LCOV - code coverage report
Current view: top level - discof/failover - fd_failover_tile.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 359 0.0 %
Date: 2026-11-11 12:40:43 Functions: 0 34 0.0 %

          Line data    Source code
       1             : #include "../../disco/topo/fd_topo.h"
       2             : #include "../../disco/topo/fd_dns_resolve.h"
       3             : #include "../../disco/keyguard/fd_keyload.h"
       4             : #include "../../disco/keyguard/fd_keyguard.h"
       5             : #include "../../disco/keyguard/fd_keyguard_client.h"
       6             : #include "../../flamenco/gossip/fd_gossip_message.h"
       7             : #include "../../ballet/base58/fd_base58.h"
       8             : #include "../../ballet/hex/fd_hex.h"
       9             : #include "../../util/fd_version.h"
      10             : #include "../../util/net/fd_ip4.h"
      11             : #include "../votor/fd_votor_tile.h"
      12             : 
      13             : #include "fd_failover_bus.h"
      14             : #include "fd_failover_channel.h"
      15             : 
      16             : #include <string.h>
      17             : #include <sys/socket.h>
      18             : #include <netinet/in.h>  /* IPPROTO_TCP */
      19             : #include <netinet/tcp.h> /* TCP_NODELAY */
      20             : #include <linux/futex.h> /* FUTEX_WAIT_BITSET */
      21             : 
      22             : #include "generated/fd_failover_tile_seccomp.h"
      23             : 
      24             : /* The failov tile owns the sockets to the other failover machine, keeps
      25             :    host networking for them and holds no private keys.  The sign tile
      26             :    signs the admission proofs and our TLS handshakes with the failover
      27             :    identity, and the admin tile does the identity switch and stays off
      28             :    the network. */
      29             : 
      30           0 : #define FD_FAILOVER_TILE_POLL_NANOS (50000L)   /* 50us */
      31           0 : #define FD_FAILOVER_TILE_PARK_NANOS (1000000L) /* 1ms */
      32             : 
      33           0 : #define FD_FAILOVER_DEADLINE_SLOTS (64UL)
      34           0 : #define FD_FAILOVER_DEADLINE_NANOS ((long)FD_FAILOVER_DEADLINE_SLOTS*(long)FD_FAILOVER_NANOS_PER_SECOND)
      35             : 
      36           0 : #define FD_FAILOVER_GOSSIP_FRESH_NANOS (15000000000L)
      37             : #define FD_FAILOVER_REQUEST_RETRIES    (3UL)
      38           0 : #define FD_FAILOVER_FORCE_WAIT_NANOS   FD_FAILOVER_GOSSIP_FRESH_NANOS
      39           0 : #define FD_FAILOVER_STEP_CNT           (8UL)
      40             : 
      41             : /* apply_control's result for a pending command */
      42           0 : #define FD_FAILOVER_CMD_PENDING (ULONG_MAX)
      43             : 
      44             : /* The current action/state of the controller */
      45           0 : #define FD_FAILOVER_ACTION_IDLE                (0UL)
      46           0 : #define FD_FAILOVER_ACTION_DEMOTE_SWITCH       (1UL)
      47           0 : #define FD_FAILOVER_ACTION_DEMOTE_WAIT_ACK     (2UL)
      48           0 : #define FD_FAILOVER_ACTION_PROMOTE_WAIT_REPLAY (3UL)
      49           0 : #define FD_FAILOVER_ACTION_PROMOTE_WAIT_ADOPT  (4UL)
      50           0 : #define FD_FAILOVER_ACTION_PROMOTE_SWITCH      (5UL)
      51           0 : #define FD_FAILOVER_ACTION_HANDOFF_WAIT_PEER   (6UL)
      52           0 : #define FD_FAILOVER_ACTION_HANDOFF_WAIT_RESULT (7UL)
      53           0 : #define FD_FAILOVER_ACTION_DEMOTE_DRAIN        (8UL)
      54           0 : #define FD_FAILOVER_ACTION_CNT                 (9UL)
      55             : 
      56             : /* Adopted vote history's source */
      57           0 : #define FD_FAILOVER_SOURCE_PEER  (0UL)
      58           0 : #define FD_FAILOVER_SOURCE_EMPTY (1UL)
      59             : 
      60             : /* Handoff result */
      61             : #define FD_FAILOVER_HANDOFF_NONE       (0UL)
      62           0 : #define FD_FAILOVER_HANDOFF_PENDING    (1UL)
      63           0 : #define FD_FAILOVER_HANDOFF_TAKEN      (2UL)
      64           0 : #define FD_FAILOVER_HANDOFF_DECLINED   (3UL)
      65           0 : #define FD_FAILOVER_HANDOFF_RESTARTED  (4UL)
      66           0 : #define FD_FAILOVER_HANDOFF_CANCELLED  (5UL)
      67           0 : #define FD_FAILOVER_HANDOFF_NOT_HOLDER (6UL)
      68           0 : #define FD_FAILOVER_HANDOFF_CNT        (7UL)
      69             : 
      70             : /* Which key we will switch to, CNT if not switching */
      71             : #define FD_FAILOVER_SWITCH_KEY_JUNK   (0UL)
      72           0 : #define FD_FAILOVER_SWITCH_KEY_STAKED (1UL)
      73           0 : #define FD_FAILOVER_SWITCH_KEY_CNT    (2UL)
      74             : 
      75             : /* The status of our DEMOTED to the handoff target */
      76           0 : #define FD_FAILOVER_DELIVERY_NONE    (0UL)
      77           0 : #define FD_FAILOVER_DELIVERY_PENDING (1UL)
      78           0 : #define FD_FAILOVER_DELIVERY_SENT    (2UL)
      79             : 
      80             : /* Our last response to a DEMOTED */
      81             : #define FD_FAILOVER_REPLY_NONE    (0UL)
      82           0 : #define FD_FAILOVER_REPLY_KEPT    (1UL)
      83           0 : #define FD_FAILOVER_REPLY_PENDING (2UL)
      84             : 
      85             : /* A serialized Alpenglow vote history. */
      86             : struct fd_failover_history {
      87             :   int   valid;                          /* holds a history */
      88             :   ulong tip;                            /* slot of its last vote */
      89             :   ulong sz;                             /* bytes used in state */
      90             :   uchar state[ FD_FAILOVER_STATE_MAX ]; /* the serialized history */
      91             : };
      92             : 
      93             : typedef struct fd_failover_history fd_failover_history_t;
      94             : 
      95             : /* A peer is a boot of the other machine.  Both authenticate as the
      96             :    identity they hand off, so only the boot id tells them apart. */
      97             : struct fd_failover_peer {
      98             :   ulong boot_id; /* the peer's boot */
      99             : };
     100             : 
     101             : typedef struct fd_failover_peer fd_failover_peer_t;
     102             : 
     103             : static inline int
     104             : is_same_peer( fd_failover_peer_t const * bound,
     105           0 :               ulong                      boot_id ) {
     106           0 :   return bound->boot_id==boot_id;
     107           0 : }
     108             : 
     109             : struct fd_failover_session {
     110             :   ulong state;            /* last seen session state */
     111             :   ulong generation;       /* catches a replacement paired during one poll */
     112             :   ulong peer_boot_id;     /* boot of the peer on this session */
     113             :   int   close_after_send; /* hang up once the queued message is sent */
     114             : };
     115             : 
     116             : typedef struct fd_failover_session fd_failover_session_t;
     117             : 
     118             : /* A handoff request to take over another machine for failover. */
     119             : struct fd_failover_request {
     120             :   ulong              id;             /* zero when none is open */
     121             :   fd_failover_peer_t peer;           /* the boot we authenticated for it, which runs the identity */
     122             :   uint               addr;           /* the address we dial, a resume may replace it */
     123             :   long               until;          /* when the request pauses, zero while paused */
     124             :   int                sent;           /* sent on this session */
     125             :   int                peer_restarted; /* paused because the authenticated member or boot changed */
     126             :   ulong              send_cnt;       /* how many times we sent it */
     127             :   int                quick;          /* use quicker timeouts for failover */
     128             : };
     129             : 
     130             : typedef struct fd_failover_request fd_failover_request_t;
     131             : 
     132             : /* A demotion. */
     133             : struct fd_failover_handoff {
     134             :   ulong              id;                                         /* id of our latest demotion */
     135             :   fd_failover_peer_t target;                                     /* peer boot it is for */
     136             :   ulong              result;                                     /* FD_FAILOVER_HANDOFF_* of the latest DEMOTED */
     137             :   ulong              delivery;                                   /* FD_FAILOVER_DELIVERY_* */
     138             :   ulong              payload_sz;                                 /* bytes used in payload */
     139             :   uchar              payload[ FD_FAILOVER_DEMOTED_PAYLOAD_MAX ]; /* our DEMOTED, resent on a new session */
     140             : };
     141             : 
     142             : typedef struct fd_failover_handoff fd_failover_handoff_t;
     143             : 
     144             : /* A promotion in progress. */
     145             : struct fd_failover_promotion {
     146             :   char                  label[ 48 ];  /* handoff ID or operator promotion, fixed for every step */
     147             :   ulong                 source;       /* FD_FAILOVER_SOURCE_* we adopt from */
     148             :   int                   from_peer;    /* the peer's DEMOTED requested it, we send the response */
     149             :   fd_failover_peer_t    peer;         /* that DEMOTED's member boot, it gets our response */
     150             :   ulong                 handoff_id;   /* and its handoff id */
     151             :   ulong                 floor;        /* coverage floor when it started */
     152             :   int                   force;        /* skip peer checks and accept incomplete or empty history */
     153             :   int                   soft;         /* demote ourselves if a peer running the identity authenticates */
     154             :   int                   holder_seen;  /* a peer running the identity authenticated after promotion started */
     155             :   int                   gave_up;      /* the identity was already given up */
     156             :   fd_failover_history_t adopt;        /* history or the bound sent to votor */
     157             :   ulong                 adopt_id;     /* id of the adoption we asked the votor for */
     158             :   int                   retry;        /* ask the votor again once replay passes retry_slot */
     159             :   ulong                 retry_slot;   /* replay slot that retry waits for */
     160             : };
     161             : 
     162             : typedef struct fd_failover_promotion fd_failover_promotion_t;
     163             : 
     164             : /* Our reply to a DEMOTED. */
     165             : struct fd_failover_reply {
     166             :   ulong              state;      /* FD_FAILOVER_REPLY_* */
     167             :   fd_failover_peer_t peer;       /* the member boot it goes to */
     168             :   ulong              handoff_id; /* the handoff it answers */
     169             :   ushort             type;       /* PROMOTE_ACK or PROMOTE_REJECTED */
     170             :   uchar              reason;     /* for a rejected reply */
     171             : };
     172             : 
     173             : typedef struct fd_failover_reply fd_failover_reply_t;
     174             : 
     175             : struct fd_failover_tx {
     176             :   ushort             type;                                       /* FD_FAILOVER_MSG_* */
     177             :   ushort             sz;                                         /* bytes used in payload */
     178             :   int                valid;                                      /* a message is waiting */
     179             :   fd_failover_peer_t to;                                         /* the member boot it goes to */
     180             :   uchar              payload[ FD_FAILOVER_DEMOTED_PAYLOAD_MAX ]; /* the encoded message */
     181             : };
     182             : 
     183             : typedef struct fd_failover_tx fd_failover_tx_t;
     184             : 
     185             : /* State of the identity switch during failover. */
     186             : struct fd_failover_id_switch {
     187             :   ulong                     request_id;     /* nonce of our latest switch request */
     188             :   ulong                     pending_key;    /* FD_FAILOVER_SWITCH_KEY_*, or CNT when idle */
     189             :   fd_failover_switch_resp_t result;         /* the committed response */
     190             :   fd_failover_switch_resp_t response;       /* uncommitted input frame */
     191             :   ulong                     response_nonce; /* nonce of the frame being consumed */
     192             :   int                       fresh;          /* a response frame arrived and is not handled yet */
     193             :   int                       overdue;        /* the switch in flight passed its deadline */
     194             :   int                       discard;        /* set-identity ran while it was in flight, discard */
     195             :   ulong                     epoch;          /* failover epoch */
     196             : };
     197             : 
     198             : typedef struct fd_failover_id_switch fd_failover_id_switch_t;
     199             : 
     200             : struct fd_failover_tile_ctx {
     201             :   /* Networking */
     202             :   fd_keyguard_client_t    keyguard_client[ 1 ]; /* signs admission proofs and TLS */
     203             :   fd_failover_hello_t     hello;        /* the pre-built HELLO we will send */
     204             :   uchar                   installed[ 32 ];    /* the identity this machine signs with now */
     205             :   uchar                   spare_pubkey[ 32 ]; /* the junk key generated at boot, a handoff installs it */
     206             :   uint                    cmd_addr;     /* failover address, 0 if from gossip */
     207             :   ushort                  cmd_port;     /* failover port, 0 if from gossip */
     208             :   fd_failover_channel_t * channel;      /* the TLS channel to the peer */
     209             :   fd_failover_session_t   session;      /* the session we last saw on the channel */
     210             :   long                    next_wake;    /* next socket poll, the park deadline */
     211             :   long                    target_since; /* our clock since we started watching gossip for the target */
     212             : 
     213             :   fd_failover_request_t request;               /* our handoff request */
     214             : 
     215             :   ulong peer_floor;
     216             :   ulong own_floor;
     217             :   int   own_voted;
     218             :   int   installed_ranked;
     219             : 
     220             :   /* Gossip */
     221             :   ulong       gossip_in_idx;
     222             :   fd_wksp_t * gossip_in_mem;
     223             :   ulong       gossip_in_chunk0;
     224             :   ulong       gossip_in_wmark;
     225             :   ulong       gossip_in_mtu;
     226             : 
     227             :   fd_ip4_port_t own_gossip;          /* our own gossip socket */
     228             :   uchar         gossip_origin[ 32 ]; /* copied in during_frag */
     229             :   fd_ip4_port_t gossip_socket;       /* copied in during_frag, zero for a remove or IPv6 */
     230             :   uint          staked_addr;         /* from a staked contact info that is not ours, 0 none */
     231             :   long          staked_seen_at;      /* our clock when that one arrived, 0 never */
     232             :   long          holder_seen_at;      /* our clock when a machine running the identity was last heard, 0 never */
     233             : 
     234             :   /* Controller */
     235             :   ulong action;        /* FD_FAILOVER_ACTION_* */
     236             :   int   stuck;         /* a transition failed or is overdue */
     237             :   int   stuck_logged;  /* the stuck value we last logged */
     238             :   ulong deadline_slot; /* replay slot past which the current state is overdue, SLOT_NULL before replay started */
     239             :   long  deadline_at;   /* our clock past which it is overdue */
     240             : 
     241             :   /* Hand-offs */
     242             :   ulong                 handoff_base; /* random base for our handoff ids */
     243             :   ulong                 handoff_cnt;  /* handoff ids issued since boot */
     244             :   fd_failover_handoff_t handoff;      /* our demotion */
     245             : 
     246             :   fd_failover_history_t   peer_history; /* stored peer history, from a DEMOTED */
     247             :   fd_failover_promotion_t promotion;    /* the promotion in progress */
     248             :   fd_failover_reply_t     reply;        /* our last response to a DEMOTED */
     249             :   fd_failover_tx_t        tx;           /* the control message waiting to go out */
     250             : 
     251             :   /* The admin tile forwards operator commands over the bus and waits for one response frame per request. */
     252             :   ulong                  admin_in_idx;
     253             :   fd_wksp_t *            admin_in_mem;
     254             :   ulong                  admin_in_chunk0;
     255             :   ulong                  admin_in_wmark;
     256             :   ulong                  admin_out_idx;
     257             :   fd_wksp_t *            admin_out_mem;
     258             :   ulong                  admin_out_chunk0;
     259             :   ulong                  admin_out_wmark;
     260             :   ulong                  admin_out_chunk;
     261             : 
     262             :   fd_failover_bus_msg_t  bus_req;       /* the operator command copied in during_frag */
     263             :   int                    bus_req_fresh; /* bus_req arrived and is not handled yet */
     264             :   fd_failover_operator_t bus_operator;  /* copied in during_frag */
     265             : 
     266             :   ulong cmd_nonce;  /* nonce of the command, 0 none */
     267             :   ulong cmd_result; /* cmd outcome once cmd_done */
     268             :   int   cmd_done;   /* cmd is done after_credit publishes it */
     269             :   ulong cmd_step_head; /* steps logged */
     270             :   ulong cmd_step_tail; /* steps sent to the admin tile or dropped */
     271             :   char  cmd_step[ FD_FAILOVER_STEP_CNT ][ FD_FAILOVER_BUS_STEP_MAX ]; /* the last steps, sent while the command waits */
     272             : 
     273             :   fd_failover_id_switch_t id_switch; /* the identity switch we asked the admin tile for */
     274             : 
     275             :   /* Votor history adoption */
     276             :   ulong                   adopt_out_idx;
     277             :   fd_wksp_t *             adopt_out_mem;
     278             :   ulong                   adopt_out_chunk0;
     279             :   ulong                   adopt_out_wmark;
     280             :   ulong                   adopt_out_chunk;
     281             :   ulong                   adopt_request_id;   /* id of our latest adoption request */
     282             :   ulong                   adopt_in_idx;
     283             :   fd_wksp_t *             adopt_in_mem;
     284             :   ulong                   adopt_in_chunk0;
     285             :   ulong                   adopt_in_wmark;
     286             : 
     287             :   fd_votor_adopt_result_t adopt_result;
     288             :   ulong                   adopt_result_id;
     289             :   int                     adopt_result_fresh; /* adopt_result arrived and is not handled yet */
     290             : 
     291             :   /* Vote history input. */
     292             :   ulong       hist_in_idx;
     293             :   fd_wksp_t * hist_in_mem;
     294             :   ulong       hist_in_chunk0;
     295             :   ulong       hist_in_wmark;
     296             : 
     297             :   int         hist_fresh;     /* a history frame arrived and is not handled yet */
     298             :   ulong       hist_seen_seq;  /* seq of the last votor_hist frag taken in, ULONG_MAX before any */
     299             : 
     300             :   /* Votor history and adoption bounds. */
     301             :   fd_votor_hist_msg_t hist;             /* the votor_hist frame copied in during_frag */
     302             :   ulong               adopt_anchor;     /* finality anchor of the history being adopted, SLOT_NULL when none */
     303             :   ulong               empty_vote_after; /* an empty history votes only past this slot, SLOT_NULL until known */
     304             :   int                 hist_warned;      /* we warned of a bad frame and no good one came since */
     305             : 
     306             :   ulong replay_slot;    /* latest replayed slot */
     307             :   ulong root_slot;      /* latest root */
     308             :   ulong last_vote_slot; /* our latest vote */
     309             : 
     310             :   fd_failover_history_t current_history; /* history of our latest vote */
     311             : 
     312             :   uchar rx[ FD_FAILOVER_PAYLOAD_MAX ]; /* the control message being decoded */
     313             : };
     314             : 
     315             : typedef struct fd_failover_tile_ctx fd_failover_tile_ctx_t;
     316             : 
     317             : /* Whether this machine currently runs the staked identity it hands off. */
     318             : static inline int
     319           0 : holds_identity( fd_failover_tile_ctx_t const * ctx ) {
     320           0 :   return fd_memeq( ctx->installed, ctx->hello.staked_pubkey, 32UL );
     321           0 : }
     322             : 
     323             : FD_FN_CONST static inline ulong
     324           0 : scratch_align( void ) {
     325           0 :   return fd_ulong_max( alignof(fd_failover_tile_ctx_t), fd_failover_channel_align() );
     326           0 : }
     327             : 
     328             : FD_FN_PURE static inline ulong
     329           0 : scratch_footprint( fd_topo_tile_t const * tile FD_PARAM_UNUSED ) {
     330           0 :   ulong l = FD_LAYOUT_INIT;
     331           0 :   l = FD_LAYOUT_APPEND( l, alignof(fd_failover_tile_ctx_t), sizeof(fd_failover_tile_ctx_t)  );
     332           0 :   l = FD_LAYOUT_APPEND( l, fd_failover_channel_align(),     fd_failover_channel_footprint() );
     333           0 :   return FD_LAYOUT_FINI( l, scratch_align() );
     334           0 : }
     335             : 
     336             : /* TLS CertificateVerify */
     337             : static void
     338             : sign_ed25519( void *      signer_ctx,
     339             :               uchar       sig[ static FD_ED25519_SIG_SZ ],
     340           0 :               uchar const msg[ static FD_TLS_CV_SIGN_SZ ] ) {
     341           0 :   fd_failover_tile_ctx_t * ctx = signer_ctx;
     342           0 :   fd_keyguard_client_sign( ctx->keyguard_client, sig, msg, FD_TLS_CV_SIGN_SZ, FD_KEYGUARD_SIGN_TYPE_ED25519 );
     343           0 : }
     344             : 
     345             : /* Pre-admission proof */
     346             : static void
     347             : sign_proof( void *      signer_ctx,
     348             :             uchar       sig[ 64 ],
     349           0 :             uchar const msg[ FD_KEYGUARD_FAILOVER_PROOF_SZ ] ) {
     350           0 :   fd_failover_tile_ctx_t * ctx = signer_ctx;
     351           0 :   fd_keyguard_client_sign( ctx->keyguard_client, sig, msg, FD_KEYGUARD_FAILOVER_PROOF_SZ, FD_KEYGUARD_SIGN_TYPE_ED25519 );
     352           0 : }
     353             : 
     354             : static void
     355             : privileged_init( fd_topo_t const *      topo,
     356           0 :                  fd_topo_tile_t const * tile ) {
     357           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     358           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     359           0 :   fd_failover_tile_ctx_t * ctx         = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_failover_tile_ctx_t), sizeof(fd_failover_tile_ctx_t)  );
     360           0 :   void *                   channel_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_failover_channel_align(),     fd_failover_channel_footprint() );
     361             :   /* Start idle with no handoff or promotion in progress. */
     362           0 :   fd_memset( ctx, 0, sizeof(fd_failover_tile_ctx_t) );
     363             :   /* No admin switch is pending at boot. */
     364           0 :   ctx->id_switch.pending_key = FD_FAILOVER_SWITCH_KEY_CNT;
     365           0 :   ctx->deadline_slot         = FD_FAILOVER_SLOT_NULL;
     366           0 :   ctx->peer_floor            = FD_FAILOVER_SLOT_NULL;
     367           0 :   ctx->own_floor             = FD_FAILOVER_SLOT_NULL;
     368           0 :   ctx->replay_slot           = FD_FAILOVER_SLOT_NULL;
     369           0 :   ctx->root_slot             = FD_FAILOVER_SLOT_NULL;
     370           0 :   ctx->last_vote_slot        = FD_FAILOVER_SLOT_NULL;
     371           0 :   ctx->hist_seen_seq         = ULONG_MAX;
     372           0 :   FD_TEST( fd_rng_secure( &ctx->handoff_base, sizeof(ulong) ) );
     373             : 
     374             :   /* The junk key generated at boot is the spare a handoff installs.
     375             :      The configured identity is the key we hand off. */
     376           0 :   uchar const * spare_pubkey = fd_keyload_load( FD_KEYLOAD_FAILOVER_JUNK, 1 );
     377           0 :   fd_memcpy( ctx->spare_pubkey, spare_pubkey, 32UL );
     378           0 :   fd_keyload_unload( spare_pubkey, 1 );
     379             : 
     380           0 :   uchar const * target_pubkey = fd_keyload_load( tile->failov.target_key_path, 1 );
     381           0 :   fd_memcpy( ctx->hello.staked_pubkey, target_pubkey, 32UL );
     382           0 :   fd_keyload_unload( target_pubkey, 1 );
     383             : 
     384           0 :   uchar const * identity_pubkey = fd_keyload_load( tile->failov.identity_key_path, 1 );
     385           0 :   fd_memcpy( ctx->installed, identity_pubkey, 32UL );
     386           0 :   fd_keyload_unload( identity_pubkey, 1 );
     387             : 
     388           0 :   if( FD_UNLIKELY( fd_memeq( ctx->spare_pubkey, ctx->hello.staked_pubkey, 32UL ) ) ) {
     389           0 :     FD_LOG_ERR(( "the failover junk key must differ from [paths.identity_key]" ));
     390           0 :   }
     391             : 
     392           0 :   ctx->adopt_anchor     = FD_FAILOVER_SLOT_NULL;
     393           0 :   ctx->empty_vote_after = FD_FAILOVER_SLOT_NULL;
     394             : 
     395           0 :   ctx->hello.version = (ushort)FD_FAILOVER_VERSION;
     396           0 :   ctx->hello.holds   = (uchar)holds_identity( ctx );
     397           0 :   ctx->hello.mode    = (uchar)FD_FAILOVER_MODE_ALPENGLOW;
     398           0 :   while( FD_UNLIKELY( !ctx->hello.boot_id ) ) FD_TEST( fd_rng_secure( &ctx->hello.boot_id, sizeof(ulong) ) );
     399             : 
     400             :   /* The git commit field with the hash bytes. */
     401           0 :   if( FD_LIKELY( strlen( fd_commit_ref_cstr )==2UL*sizeof(ctx->hello.commit) &&
     402           0 :                  fd_hex_decode( ctx->hello.commit, fd_commit_ref_cstr, sizeof(ctx->hello.commit) )!=sizeof(ctx->hello.commit) ) ) {
     403           0 :     fd_memset( ctx->hello.commit, 0, sizeof(ctx->hello.commit) );
     404           0 :   }
     405             : 
     406           0 :   ctx->own_gossip = tile->failov.gossip_addr;
     407           0 :   if( FD_UNLIKELY( tile->failov.gossip_host[ 0 ]!='\0' ) ) {
     408           0 :     if( FD_UNLIKELY( !fd_dns_resolve_address( tile->failov.gossip_host, &ctx->own_gossip.addr ) ) ) {
     409           0 :       FD_LOG_ERR(( "could not resolve [gossip.host] `%s`", tile->failov.gossip_host ));
     410           0 :     }
     411           0 :   }
     412             : 
     413           0 :   ctx->channel = fd_failover_channel_join( fd_failover_channel_new( channel_mem ) );
     414           0 :   FD_TEST( ctx->channel );
     415           0 :   fd_tls_sign_t signer = { .ctx=ctx, .sign_fn=sign_ed25519 };
     416           0 :   if( FD_UNLIKELY( fd_failover_channel_set_identity( ctx->channel, signer, (fd_failover_proof_signer_t){ .ctx=ctx, .sign_fn=sign_proof }, &ctx->hello ) ) ) {
     417           0 :     FD_LOG_ERR(( "failover TLS setup failed" ));
     418           0 :   }
     419             : 
     420           0 :   if( FD_LIKELY( tile->failov.listen ) ) fd_failover_channel_init_listener( ctx->channel, tile->failov.listen_addr, tile->failov.port );
     421           0 :   ctx->session.state = fd_failover_channel_state( ctx->channel );
     422             : 
     423           0 :   char const * role = holds_identity( ctx ) ? "primary" : "standby";
     424           0 :   if( FD_LIKELY( tile->failov.listen ) ) {
     425           0 :     FD_LOG_NOTICE(( "failover booted %s server listening on " FD_IP4_ADDR_FMT ":%u", role, FD_IP4_ADDR_FMT_ARGS( tile->failov.listen_addr ), (uint)tile->failov.port ));
     426           0 :   } else {
     427           0 :     FD_LOG_NOTICE(( "failover booted %s server not listening, [failover.enabled] is false", role ));
     428           0 :   }
     429             : 
     430           0 :   char junk_b58  [ FD_BASE58_ENCODED_32_SZ ];
     431           0 :   char staked_b58[ FD_BASE58_ENCODED_32_SZ ];
     432           0 :   fd_base58_encode_32( ctx->spare_pubkey,        NULL, junk_b58   );
     433           0 :   fd_base58_encode_32( ctx->hello.staked_pubkey, NULL, staked_b58 );
     434           0 :   FD_LOG_INFO(( "failover identity `%s`, junk key `%s`", staked_b58, junk_b58 ));
     435             : 
     436           0 :   FD_TEST( FD_SCRATCH_ALLOC_FINI( l, scratch_align() )==(ulong)scratch+scratch_footprint( tile ) );
     437           0 : }
     438             : 
     439             : static void
     440             : unprivileged_init( fd_topo_t const *      topo,
     441           0 :                    fd_topo_tile_t const * tile ) {
     442           0 :   fd_failover_tile_ctx_t * ctx = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     443             : 
     444           0 :   ctx->gossip_in_idx = ULONG_MAX;
     445           0 :   ctx->hist_in_idx   = ULONG_MAX;
     446           0 :   ctx->admin_in_idx  = ULONG_MAX;
     447           0 :   ctx->adopt_in_idx  = ULONG_MAX;
     448           0 :   ctx->target_since  = fd_failover_clock();
     449           0 :   ctx->adopt_out_idx = fd_topo_find_tile_out_link( topo, tile, "failov_votor", 0UL );
     450           0 :   if( FD_LIKELY( ctx->adopt_out_idx!=ULONG_MAX ) ) {
     451           0 :     fd_topo_link_t const * link = &topo->links[ tile->out_link_id[ ctx->adopt_out_idx ] ];
     452           0 :     ctx->adopt_out_mem    = topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ].wksp;
     453           0 :     ctx->adopt_out_chunk0 = fd_dcache_compact_chunk0( ctx->adopt_out_mem, link->dcache );
     454           0 :     ctx->adopt_out_wmark  = fd_dcache_compact_wmark ( ctx->adopt_out_mem, link->dcache, link->mtu );
     455           0 :     ctx->adopt_out_chunk  = ctx->adopt_out_chunk0;
     456           0 :   }
     457           0 :   ctx->admin_out_idx = fd_topo_find_tile_out_link( topo, tile, "failov_admin", 0UL );
     458           0 :   if( FD_LIKELY( ctx->admin_out_idx!=ULONG_MAX ) ) {
     459           0 :     fd_topo_link_t const * link = &topo->links[ tile->out_link_id[ ctx->admin_out_idx ] ];
     460           0 :     ctx->admin_out_mem    = topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ].wksp;
     461           0 :     ctx->admin_out_chunk0 = fd_dcache_compact_chunk0( ctx->admin_out_mem, link->dcache );
     462           0 :     ctx->admin_out_wmark  = fd_dcache_compact_wmark ( ctx->admin_out_mem, link->dcache, link->mtu );
     463           0 :     ctx->admin_out_chunk  = ctx->admin_out_chunk0;
     464           0 :   }
     465           0 :   for( ulong i=0UL; i<tile->in_cnt; i++ ) {
     466           0 :     fd_topo_link_t const * link = &topo->links[ tile->in_link_id[ i ] ];
     467           0 :     if( FD_LIKELY( !strcmp( link->name, "gossip_ciseen" ) ) ) {
     468           0 :       ctx->gossip_in_idx    = i;
     469           0 :       ctx->gossip_in_mem    = topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ].wksp;
     470           0 :       ctx->gossip_in_chunk0 = fd_dcache_compact_chunk0( ctx->gossip_in_mem, link->dcache );
     471           0 :       ctx->gossip_in_wmark  = fd_dcache_compact_wmark ( ctx->gossip_in_mem, link->dcache, link->mtu );
     472           0 :       ctx->gossip_in_mtu    = link->mtu;
     473           0 :       continue;
     474           0 :     }
     475           0 :     if( FD_LIKELY( !strcmp( link->name, "votor_failov" ) ) ) {
     476           0 :       ctx->adopt_in_idx    = i;
     477           0 :       ctx->adopt_in_mem    = topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ].wksp;
     478           0 :       ctx->adopt_in_chunk0 = fd_dcache_compact_chunk0( ctx->adopt_in_mem, link->dcache );
     479           0 :       ctx->adopt_in_wmark  = fd_dcache_compact_wmark ( ctx->adopt_in_mem, link->dcache, link->mtu );
     480           0 :       continue;
     481           0 :     }
     482           0 :     if( FD_LIKELY( !strcmp( link->name, "admin_failov" ) ) ) {
     483           0 :       ctx->admin_in_idx    = i;
     484           0 :       ctx->admin_in_mem    = topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ].wksp;
     485           0 :       ctx->admin_in_chunk0 = fd_dcache_compact_chunk0( ctx->admin_in_mem, link->dcache );
     486           0 :       ctx->admin_in_wmark  = fd_dcache_compact_wmark ( ctx->admin_in_mem, link->dcache, link->mtu );
     487           0 :       continue;
     488           0 :     }
     489           0 :     if( FD_LIKELY( !strcmp( link->name, "votor_hist" ) ) ) {
     490           0 :       ctx->hist_in_idx    = i;
     491           0 :       ctx->hist_in_mem    = topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ].wksp;
     492           0 :       ctx->hist_in_chunk0 = fd_dcache_compact_chunk0( ctx->hist_in_mem, link->dcache );
     493           0 :       ctx->hist_in_wmark  = fd_dcache_compact_wmark ( ctx->hist_in_mem, link->dcache, link->mtu );
     494           0 :       continue;
     495           0 :     }
     496             :     /* The keyguard client reads sign_failov itself. */
     497           0 :     if( FD_LIKELY( !strcmp( link->name, "sign_failov" ) ) ) continue;
     498           0 :     FD_LOG_ERR(( "unexpected input link name %s", link->name ));
     499           0 :   }
     500             : 
     501           0 :   ulong sign_in_idx  = fd_topo_find_tile_in_link ( topo, tile, "sign_failov", tile->kind_id );
     502           0 :   ulong sign_out_idx = fd_topo_find_tile_out_link( topo, tile, "failov_sign", tile->kind_id );
     503           0 :   FD_TEST( sign_in_idx!=ULONG_MAX && sign_out_idx!=ULONG_MAX );
     504           0 :   fd_topo_link_t const * sign_in  = &topo->links[ tile->in_link_id [ sign_in_idx  ] ];
     505           0 :   fd_topo_link_t const * sign_out = &topo->links[ tile->out_link_id[ sign_out_idx ] ];
     506             : 
     507           0 :   fd_sleep_t * sleep = NULL;
     508           0 :   if( FD_UNLIKELY( topo->sleep_obj_id!=ULONG_MAX ) ) {
     509           0 :     sleep = fd_sleep_join( fd_topo_obj_laddr( topo, topo->sleep_obj_id ) );
     510           0 :     FD_TEST( sleep );
     511           0 :   }
     512             : 
     513           0 :   if( FD_UNLIKELY( !fd_keyguard_client_join( fd_keyguard_client_new( ctx->keyguard_client,
     514           0 :                                                                      sign_out->mcache,
     515           0 :                                                                      sign_out->dcache,
     516           0 :                                                                      sign_in->mcache,
     517           0 :                                                                      sign_in->dcache,
     518           0 :                                                                      sign_out->mtu,
     519           0 :                                                                      sign_in->mtu,
     520           0 :                                                                      sleep,
     521           0 :                                                                      sign_out->id,
     522           0 :                                                                      fd_topo_find_link_consumer( topo, sign_out ) ) ) ) ) {
     523           0 :     FD_LOG_ERR(( "failed to construct keyguard" ));
     524           0 :   }
     525           0 : }
     526             : 
     527             : #include "fd_failover_controller.c"
     528             : 
     529             : static inline int
     530             : before_frag( fd_failover_tile_ctx_t * ctx,
     531             :              ulong                    in_idx,
     532             :              ulong                    seq FD_PARAM_UNUSED,
     533           0 :              ulong                    sig ) {
     534           0 :   if( FD_LIKELY( in_idx==ctx->gossip_in_idx ) ) {
     535           0 :     return sig!=FD_GOSSIP_UPDATE_TAG_CONTACT_INFO && sig!=FD_GOSSIP_UPDATE_TAG_CONTACT_INFO_REMOVE;
     536           0 :   }
     537           0 :   if( FD_LIKELY( in_idx==ctx->hist_in_idx ) ) {
     538           0 :     return 0;
     539           0 :   }
     540           0 :   if( FD_UNLIKELY( in_idx==ctx->admin_in_idx ) ) {
     541             :     /* Commands, switch responses and set-identity. */
     542           0 :     return sig!=FD_FAILOVER_BUS_REQUEST && sig!=FD_FAILOVER_BUS_SWITCH_RESP && sig!=FD_FAILOVER_BUS_OPERATOR_ACTION;
     543           0 :   }
     544           0 :   return 0;
     545           0 : }
     546             : 
     547             : static void
     548             : during_frag( fd_failover_tile_ctx_t * ctx,
     549             :              ulong                    in_idx,
     550             :              ulong                    seq FD_PARAM_UNUSED,
     551             :              ulong                    sig,
     552             :              ulong                    chunk,
     553             :              ulong                    sz,
     554           0 :              ulong                    ctl FD_PARAM_UNUSED ) {
     555           0 :   if( FD_UNLIKELY( in_idx==ctx->adopt_in_idx ) ) {
     556           0 :     if( FD_UNLIKELY( chunk<ctx->adopt_in_chunk0 || chunk>ctx->adopt_in_wmark || sz!=sizeof(fd_votor_adopt_result_t) ) ) {
     557           0 :       FD_LOG_ERR(( "chunk %lu %lu corrupt, not in range [%lu,%lu]", chunk, sz, ctx->adopt_in_chunk0, ctx->adopt_in_wmark ));
     558           0 :     }
     559           0 :     fd_memcpy( &ctx->adopt_result, fd_chunk_to_laddr_const( ctx->adopt_in_mem, chunk ), sizeof(fd_votor_adopt_result_t) );
     560           0 :     return;
     561           0 :   }
     562           0 :   if( FD_UNLIKELY( in_idx==ctx->admin_in_idx ) ) {
     563           0 :     if( FD_UNLIKELY( chunk<ctx->admin_in_chunk0 || chunk>ctx->admin_in_wmark || sz!=sizeof(fd_failover_bus_msg_t) ) ) {
     564           0 :       FD_LOG_ERR(( "chunk %lu %lu corrupt, not in range [%lu,%lu]", chunk, sz, ctx->admin_in_chunk0, ctx->admin_in_wmark ));
     565           0 :     }
     566           0 :     fd_failover_bus_msg_t const * msg = fd_chunk_to_laddr_const( ctx->admin_in_mem, chunk );
     567           0 :     if( FD_UNLIKELY( sig==FD_FAILOVER_BUS_SWITCH_RESP ) ) {
     568           0 :       fd_memcpy( &ctx->id_switch.response, msg->payload, sizeof(ctx->id_switch.response) );
     569           0 :       ctx->id_switch.response_nonce = msg->nonce;
     570           0 :       return;
     571           0 :     }
     572           0 :     if( FD_UNLIKELY( sig==FD_FAILOVER_BUS_OPERATOR_ACTION ) ) {
     573           0 :       fd_memcpy( &ctx->bus_operator, msg->payload, sizeof(ctx->bus_operator) );
     574           0 :       return;
     575           0 :     }
     576           0 :     fd_memcpy( &ctx->bus_req, msg, sizeof(fd_failover_bus_msg_t) );
     577           0 :     return;
     578           0 :   }
     579           0 :   if( FD_UNLIKELY( in_idx==ctx->hist_in_idx ) ) {
     580           0 :     ulong frame_sz = sizeof(fd_votor_hist_msg_t);
     581           0 :     if( FD_UNLIKELY( chunk<ctx->hist_in_chunk0 || chunk>ctx->hist_in_wmark || sz!=frame_sz ) ) {
     582           0 :       FD_LOG_ERR(( "chunk %lu %lu corrupt, not in range [%lu,%lu]", chunk, sz, ctx->hist_in_chunk0, ctx->hist_in_wmark ));
     583           0 :     }
     584           0 :     fd_memcpy( &ctx->hist, fd_chunk_to_laddr_const( ctx->hist_in_mem, chunk ), sizeof(fd_votor_hist_msg_t) );
     585           0 :     return;
     586           0 :   }
     587           0 :   if( FD_UNLIKELY( in_idx!=ctx->gossip_in_idx ) ) return;
     588           0 :   if( FD_UNLIKELY( chunk<ctx->gossip_in_chunk0 || chunk>ctx->gossip_in_wmark || sz>ctx->gossip_in_mtu ) ) {
     589           0 :     FD_LOG_ERR(( "chunk %lu %lu corrupt, not in range [%lu,%lu]", chunk, sz, ctx->gossip_in_chunk0, ctx->gossip_in_wmark ));
     590           0 :   }
     591             :   /* Copy here and commit in after_frag. */
     592           0 :   fd_gossip_update_message_t const * msg = fd_chunk_to_laddr_const( ctx->gossip_in_mem, chunk );
     593           0 :   fd_memcpy( ctx->gossip_origin, msg->origin, 32UL );
     594           0 :   ctx->gossip_socket.addr = 0U;
     595           0 :   ctx->gossip_socket.port = (ushort)0;
     596           0 :   if( FD_LIKELY( sig==FD_GOSSIP_UPDATE_TAG_CONTACT_INFO ) ) {
     597           0 :     fd_gossip_socket_t const * socket = &msg->contact_info->value->sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ];
     598           0 :     ctx->gossip_socket.addr = fd_uint_if  ( !socket->is_ipv6, socket->ip4,  0U        );
     599           0 :     ctx->gossip_socket.port = fd_ushort_if( !socket->is_ipv6, socket->port, (ushort)0 );
     600           0 :   }
     601           0 : }
     602             : 
     603             : /* Unknown adoption results are invalid. */
     604             : static ulong
     605           0 : validate_adopt_code( ulong result ) {
     606           0 :   return result<FD_VOTOR_ADOPT_RESULT_CNT ? result : FD_VOTOR_ADOPT_ERR_INVALID;
     607           0 : }
     608             : 
     609             : static void
     610             : after_frag( fd_failover_tile_ctx_t * ctx,
     611             :             ulong                    in_idx,
     612             :             ulong                    seq,
     613             :             ulong                    sig,
     614             :             ulong                    sz     FD_PARAM_UNUSED,
     615             :             ulong                    tsorig FD_PARAM_UNUSED,
     616             :             ulong                    tspub  FD_PARAM_UNUSED,
     617           0 :             fd_stem_context_t *      stem   FD_PARAM_UNUSED ) {
     618           0 :   if( FD_UNLIKELY( in_idx==ctx->adopt_in_idx ) ) {
     619           0 :     ctx->adopt_result.result = validate_adopt_code( ctx->adopt_result.result );
     620           0 :     ctx->adopt_result_id     = sig;
     621           0 :     ctx->adopt_result_fresh  = 1;
     622           0 :     return;
     623           0 :   }
     624           0 :   if( FD_UNLIKELY( in_idx==ctx->admin_in_idx ) ) {
     625           0 :     if( FD_UNLIKELY( sig==FD_FAILOVER_BUS_SWITCH_RESP ) ) {
     626             :       /* The drain still reads the last switch result, so we copy a
     627             :          response into it only after the stem and nonce checks pass. */
     628           0 :       if( FD_LIKELY( on_switch_resp_msg( ctx, ctx->id_switch.response_nonce ) ) ) ctx->id_switch.result = ctx->id_switch.response;
     629           0 :       if( FD_UNLIKELY( ctx->id_switch.response.result==FD_FAILOVER_SWITCH_ERR_STALE ) ) {
     630           0 :         on_admin_operator_action_msg( ctx, &ctx->id_switch.response.operator );
     631           0 :       }
     632           0 :       return;
     633           0 :     }
     634           0 :     if( FD_UNLIKELY( sig==FD_FAILOVER_BUS_OPERATOR_ACTION ) ) {
     635           0 :       on_admin_operator_action_msg( ctx, &ctx->bus_operator );
     636           0 :       return;
     637           0 :     }
     638             :     /* Send the response from after_credit, where a publish credit is available. */
     639           0 :     ctx->bus_req_fresh = 1;
     640           0 :     return;
     641           0 :   }
     642           0 :   if( FD_UNLIKELY( in_idx==ctx->hist_in_idx ) ) {
     643           0 :     ctx->hist_fresh    = 1;
     644           0 :     ctx->hist_seen_seq = seq;
     645           0 :     return;
     646           0 :   }
     647           0 :   if( FD_UNLIKELY( in_idx!=ctx->gossip_in_idx ) ) return;
     648           0 :   on_gossip_contact_info_msg( ctx, fd_failover_clock() );
     649           0 : }
     650             : 
     651             : static inline void
     652             : after_credit( fd_failover_tile_ctx_t * ctx,
     653             :               fd_stem_context_t *      stem,
     654             :               int *                    opt_poll_in FD_PARAM_UNUSED,
     655           0 :               int *                    charge_busy ) {
     656           0 :   if( FD_UNLIKELY( ctx->hist_fresh ) ) {
     657           0 :     ctx->hist_fresh = 0;
     658           0 :     on_votor_hist_msg( ctx, &ctx->hist );
     659           0 :   }
     660           0 :   step_controller( ctx, stem );
     661             : 
     662           0 :   long now = fd_failover_clock();
     663           0 :   if( FD_LIKELY( stem->now>=ctx->next_wake ) ) {
     664           0 :     int busy = 0;
     665           0 :     poll_peer( ctx, now, &busy );
     666           0 :     long poll_nanos = ctx->action!=FD_FAILOVER_ACTION_IDLE ? FD_FAILOVER_TILE_POLL_NANOS : FD_FAILOVER_TILE_PARK_NANOS;
     667           0 :     ctx->next_wake = busy ? 0L : stem->now+(long)((double)poll_nanos*fd_tempo_tick_per_ns( NULL ));
     668           0 :     if( busy ) *charge_busy = 1;
     669           0 :   }
     670           0 :   flush_pending( ctx, now );
     671           0 :   if( FD_UNLIKELY( ctx->cmd_nonce && ctx->cmd_step_tail!=ctx->cmd_step_head ) ) {
     672           0 :     if( stem->cr_avail[ ctx->admin_out_idx ]>=stem->depths[ ctx->admin_out_idx ] ) {
     673           0 :       publish_cmd_step( ctx, stem );
     674           0 :       *charge_busy = 1;
     675           0 :     }
     676           0 :   } else if( FD_UNLIKELY( ctx->cmd_done ) ) {
     677           0 :     publish_cmd_answer( ctx, stem );
     678           0 :     *charge_busy = 1;
     679           0 :   }
     680           0 :   if( FD_UNLIKELY( ctx->bus_req_fresh ) ) {
     681           0 :     ctx->bus_req_fresh = 0;
     682           0 :     serve_bus_request( ctx, stem, now );
     683           0 :     *charge_busy = 1;
     684           0 :   }
     685             : 
     686           0 :   if( FD_UNLIKELY( ctx->stuck!=ctx->stuck_logged ) ) {
     687           0 :     ctx->stuck_logged = ctx->stuck;
     688           0 :     if( !ctx->stuck ) FD_LOG_NOTICE(( "failover is no longer stuck, %s", holds_identity( ctx ) ? "this machine runs the identity" : "this machine does not run the identity" ));
     689           0 :   }
     690           0 : }
     691             : 
     692             : static inline long
     693           0 : next_deadline( fd_failover_tile_ctx_t const * ctx ) {
     694           0 :   return ctx->next_wake;
     695           0 : }
     696             : 
     697             : static ulong
     698             : populate_allowed_seccomp( fd_topo_t const *      topo,
     699             :                           fd_topo_tile_t const * tile,
     700             :                           ulong                  out_cnt,
     701           0 :                           struct sock_filter *   out ) {
     702           0 :   fd_failover_tile_ctx_t const * ctx = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     703           0 :   populate_sock_filter_policy_fd_failover_tile( out_cnt, out, (uint)fd_log_private_logfile_fd(), (uint)fd_failover_channel_listen_fd( ctx->channel ) );
     704           0 :   return sock_filter_policy_fd_failover_tile_instr_cnt;
     705           0 : }
     706             : 
     707             : static ulong
     708             : rlimit_file_cnt( fd_topo_t const *      topo FD_PARAM_UNUSED,
     709           0 :                  fd_topo_tile_t const * tile FD_PARAM_UNUSED ) {
     710           0 :   return FD_FAILOVER_CHANNEL_CANDIDATE_MAX + 1UL + 8UL;
     711           0 : }
     712             : 
     713             : static ulong
     714             : populate_allowed_fds( fd_topo_t const *      topo,
     715             :                       fd_topo_tile_t const * tile,
     716             :                       ulong                  out_fds_cnt,
     717           0 :                       int *                  out_fds ) {
     718           0 :   fd_failover_tile_ctx_t const * ctx        = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     719           0 :   int                            logfile_fd = fd_log_private_logfile_fd();
     720           0 :   int                            listen_fd  = fd_failover_channel_listen_fd( ctx->channel );
     721             : 
     722           0 :   ulong required_fds = 1UL + (ulong)(-1!=logfile_fd) + (ulong)(-1!=listen_fd);
     723           0 :   if( FD_UNLIKELY( out_fds_cnt<required_fds ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
     724             : 
     725           0 :   ulong out_cnt = 0UL;
     726           0 :   out_fds[ out_cnt++ ] = 2; /* stderr */
     727           0 :   if( FD_LIKELY(   -1!=logfile_fd ) ) out_fds[ out_cnt++ ] = logfile_fd; /* logfile */
     728           0 :   if( FD_UNLIKELY( -1!=listen_fd  ) ) out_fds[ out_cnt++ ] = listen_fd;  /* failover listener */
     729           0 :   return out_cnt;
     730           0 : }
     731             : 
     732           0 : #define STEM_BURST (3UL)
     733           0 : #define STEM_LAZY  ((long)1e6) /* 1ms */
     734             : 
     735             : #define STEM_ALWAYS_PARK 1
     736             : 
     737           0 : #define STEM_CALLBACK_CONTEXT_TYPE       fd_failover_tile_ctx_t
     738           0 : #define STEM_CALLBACK_CONTEXT_ALIGN      alignof(fd_failover_tile_ctx_t)
     739             : 
     740           0 : #define STEM_CALLBACK_NEXT_DEADLINE      next_deadline
     741           0 : #define STEM_CALLBACK_AFTER_CREDIT       after_credit
     742           0 : #define STEM_CALLBACK_BEFORE_FRAG        before_frag
     743           0 : #define STEM_CALLBACK_DURING_FRAG        during_frag
     744           0 : #define STEM_CALLBACK_AFTER_FRAG         after_frag
     745             : 
     746             : #include "../../disco/stem/fd_stem.c"
     747             : 
     748             : /* A serialized history has to fit within the DEMOTED payload. */
     749             : FD_STATIC_ASSERT( FD_VOTOR_HIST_SER_MAX<=FD_FAILOVER_ALPENGLOW_STATE_MAX, alpenglow_state_max );
     750             : 
     751             : fd_topo_run_tile_t fd_tile_failov = {
     752             :   .name                     = "failov",
     753             :   .keep_host_networking     = 1,
     754             :   .allow_connect            = 1,
     755             :   .rlimit_file_cnt_fn       = rlimit_file_cnt,
     756             :   .populate_allowed_seccomp = populate_allowed_seccomp,
     757             :   .populate_allowed_fds     = populate_allowed_fds,
     758             :   .scratch_align            = scratch_align,
     759             :   .scratch_footprint        = scratch_footprint,
     760             :   .privileged_init          = privileged_init,
     761             :   .unprivileged_init        = unprivileged_init,
     762             :   .run                      = stem_run,
     763             : };

Generated by: LCOV version 1.14