Line data Source code
1 : #ifndef HEADER_fd_src_discof_admin_fd_adminctl_h 2 : #define HEADER_fd_src_discof_admin_fd_adminctl_h 3 : 4 : #include "../../util/fd_util_base.h" 5 : 6 : /* fd_adminctl_t provides APIs for out-of-band command-and-control 7 : signals to the firedancer process via the admin tile. It provides a 8 : ring of shared-memory command slots between command processes and the 9 : admin tile. 10 : 11 : Each slot is owned by one CAS word that packs state, process pid, 12 : request sequence number, and reservation timestamp. Command 13 : processes send commands to the main app process via a reserve and 14 : publish scheme. A call to fd_adminctl_reserve claims a command slot; 15 : the command process then has exclusive write access to writing the 16 : command payload. The fd_adminctl_publish publishes the command to 17 : the app process and effectively transfers ownership of the slot to 18 : the app process. The app process will poll for a command via 19 : fd_adminctl_poll, process the command, and send back a result via 20 : fd_adminctl_complete. The command process receives the result via 21 : fd_adminctl_wait which blocks on the result. At this point, the slot 22 : is free again and is free to be claimed by another command process. 23 : 24 : If a command process dies while it has ownership of a slot (after 25 : a reservation has been made, but before a publish OR while a command 26 : result is available but not consumed), then other command processes 27 : are free to claim the slot. If for some reason the command process 28 : is hung, the caller will be responsible for cleaning up and killing 29 : the process. 30 : 31 : All input into fd_adminctl_t must be trusted. If the adminctl memory 32 : layout changes, adminctl magic must be updated. */ 33 : 34 252 : #define FD_ADMINCTL_CMD_IDLE (0UL) 35 3 : #define FD_ADMINCTL_CMD_ADD_AUTH_VOTER (1UL) 36 0 : #define FD_ADMINCTL_CMD_SET_IDENTITY (2UL) 37 0 : #define FD_ADMINCTL_CMD_GET_IDENTITY (3UL) 38 0 : #define FD_ADMINCTL_CMD_REMOVE_ALL_AUTH_VOTERS (4UL) 39 0 : #define FD_ADMINCTL_CMD_SNAP_CREATE (5UL) 40 1605 : #define FD_ADMINCTL_CMD_FAILOVER_PROMOTE (6UL) 41 : 42 30 : #define FD_ADMINCTL_ALIGN (8UL) 43 84 : #define FD_ADMINCTL_PAYLOAD_MAX (32768UL) 44 627 : #define FD_ADMINCTL_SLOT_CNT (4UL) 45 156 : #define FD_ADMINCTL_STEP_MAX (256UL) /* a step line, with its NUL */ 46 0 : #define FD_ADMINCTL_STEP_CNT (16UL) /* step lines a slot keeps for its command process */ 47 : 48 : /* Shared command result codes. */ 49 603 : #define FD_ADMINCTL_RESULT_SUCCESS (0UL) 50 42 : #define FD_ADMINCTL_RESULT_UNKNOWN_COMMAND (1UL) 51 0 : #define FD_ADMINCTL_RESULT_ABI_VERSION_MISMATCH (2UL) 52 18 : #define FD_ADMINCTL_RESULT_ABI_SIZE_MISMATCH (3UL) 53 0 : #define FD_ADMINCTL_RESULT_UNSUPPORTED (4UL) 54 : 55 : /* App-specific command result codes. 56 : NOTE: It is important these codes start at least 5UL. */ 57 : 58 0 : #define FD_ADD_AUTHORIZED_VOTER_RESULT_KEYPAIR_MISMATCH (0x1001UL) 59 0 : #define FD_ADD_AUTHORIZED_VOTER_RESULT_MAX_AUTH_VOTERS (0x1002UL) 60 0 : #define FD_ADD_AUTHORIZED_VOTER_RESULT_DUPLICATE_AUTH_VOTER (0x1003UL) 61 3 : #define FD_ADD_AUTHORIZED_VOTER_RESULT_FAILOVER_IDENTITY (0x1004UL) 62 : 63 0 : #define FD_SNAPSHOT_CREATE_RESULT_BUSY (0x2001UL) 64 0 : #define FD_SNAPSHOT_CREATE_RESULT_NOT_READY (0x2003UL) 65 0 : #define FD_SNAPSHOT_CREATE_RESULT_SLOT_IN_PAST (0x2004UL) 66 : 67 3 : #define FD_SET_IDENTITY_RESULT_KEYPAIR_MISMATCH (0x3001UL) 68 0 : #define FD_SET_IDENTITY_RESULT_INVALID_VOTE_HISTORY (0x3002UL) 69 0 : #define FD_SET_IDENTITY_RESULT_SAME_IDENTITY (0x3003UL) 70 9 : #define FD_SET_IDENTITY_RESULT_AUTHORIZED_VOTER (0x3004UL) 71 : 72 : /* Failover command results. */ 73 3 : #define FD_FAILOVER_CONTROL_RESULT_BUSY (0x4001UL) 74 12 : #define FD_FAILOVER_CONTROL_RESULT_NOT_HOLDER (0x4002UL) 75 3 : #define FD_FAILOVER_CONTROL_RESULT_IN_PROGRESS (0x4003UL) 76 18 : #define FD_FAILOVER_CONTROL_RESULT_HANDOFF_ABORTED (0x4004UL) 77 0 : #define FD_FAILOVER_CONTROL_RESULT_PEER_HOLDS (0x4005UL) 78 3 : #define FD_FAILOVER_CONTROL_RESULT_NO_FINAL_HISTORY (0x4006UL) 79 3 : #define FD_FAILOVER_CONTROL_RESULT_NO_HOLDER_ADDRESS (0x4007UL) 80 12 : #define FD_FAILOVER_CONTROL_RESULT_REPLAY_BEHIND (0x4008UL) 81 27 : #define FD_FAILOVER_CONTROL_RESULT_TIMEOUT (0x4009UL) 82 15 : #define FD_FAILOVER_CONTROL_RESULT_PEER_RESTARTED (0x400AUL) 83 6 : #define FD_FAILOVER_CONTROL_RESULT_ADOPTION_FAILED (0x400BUL) 84 0 : #define FD_FAILOVER_CONTROL_RESULT_SWITCH_FAILED (0x400CUL) 85 6 : #define FD_FAILOVER_CONTROL_RESULT_HOLDS_OTHER (0x400DUL) 86 36 : #define FD_FAILOVER_CONTROL_RESULT_IDENTITY_CHANGED (0x400EUL) 87 27 : #define FD_FAILOVER_CONTROL_RESULT_HOLDER_LIVE (0x400FUL) 88 3 : #define FD_FAILOVER_CONTROL_RESULT_NO_PEER (0x4010UL) 89 3 : #define FD_FAILOVER_CONTROL_RESULT_ALREADY_HOLDS (0x4011UL) 90 240 : #define FD_FAILOVER_CONTROL_RESULT_HANDED_OFF (0x4012UL) 91 : 92 : struct fd_adminctl_add_auth_voter_v1 { 93 : ulong version; /* ==FD_ADMINCTL_ADD_AUTH_VOTER_PAYLOAD_VERSION */ 94 : uchar keypair[ 64UL ]; 95 : }; 96 : typedef struct fd_adminctl_add_auth_voter_v1 fd_adminctl_add_auth_voter_t; 97 3 : #define FD_ADMINCTL_ADD_AUTH_VOTER_PAYLOAD_VERSION (1UL) 98 : 99 : struct fd_adminctl_snap_create_v1 { 100 : ulong version; /* ==FD_ADMINCTL_SNAP_CREATE_PAYLOAD_VERSION */ 101 : ulong slot; /* 0 to create a snapshot of the published root 102 : immediately, else stop rooting at the first rooted 103 : slot >= this slot and snapshot it */ 104 : }; 105 : typedef struct fd_adminctl_snap_create_v1 fd_adminctl_snap_create_t; 106 0 : #define FD_ADMINCTL_SNAP_CREATE_PAYLOAD_VERSION (1UL) 107 : 108 : struct fd_adminctl_set_identity_v4 { 109 : ulong version; /* ==FD_ADMINCTL_SET_IDENTITY_PAYLOAD_VERSION */ 110 : uchar keypair[ 64UL ]; 111 : ulong vote_history_slot; /* scanned by the command from the vote history file, 0 if none was provided */ 112 : }; 113 : typedef struct fd_adminctl_set_identity_v4 fd_adminctl_set_identity_t; 114 : FD_STATIC_ASSERT( sizeof(fd_adminctl_set_identity_t)<=FD_ADMINCTL_PAYLOAD_MAX, set_identity ); 115 0 : #define FD_ADMINCTL_SET_IDENTITY_PAYLOAD_VERSION (4UL) 116 : 117 : struct fd_adminctl_get_identity_req_v1 { 118 : ulong version; /* ==FD_ADMINCTL_GET_IDENTITY_PAYLOAD_VERSION */ 119 : }; 120 : typedef struct fd_adminctl_get_identity_req_v1 fd_adminctl_get_identity_req_t; 121 : 122 : struct fd_adminctl_get_identity_resp_v1 { 123 : ulong version; /* ==FD_ADMINCTL_GET_IDENTITY_PAYLOAD_VERSION */ 124 : uchar identity_pubkey[ 32UL ]; 125 : }; 126 : typedef struct fd_adminctl_get_identity_resp_v1 fd_adminctl_get_identity_resp_t; 127 0 : #define FD_ADMINCTL_GET_IDENTITY_PAYLOAD_VERSION (1UL) 128 : 129 : struct fd_adminctl_remove_all_auth_voters_v1 { 130 : ulong version; /* ==FD_ADMINCTL_REMOVE_ALL_AUTH_VOTERS_PAYLOAD_VERSION */ 131 : }; 132 : typedef struct fd_adminctl_remove_all_auth_voters_v1 fd_adminctl_remove_all_auth_voters_t; 133 0 : #define FD_ADMINCTL_REMOVE_ALL_AUTH_VOTERS_PAYLOAD_VERSION (1UL) 134 : 135 : /* Failover commands, forwarded to the failover tile. */ 136 644 : #define FD_ADMINCTL_FAILOVER_CMD_HANDOFF (0UL) 137 1297 : #define FD_ADMINCTL_FAILOVER_CMD_PROMOTE (1UL) 138 60 : #define FD_ADMINCTL_FAILOVER_CMD_PREFLIGHT (2UL) 139 39 : #define FD_ADMINCTL_FAILOVER_CMD_CNT (3UL) 140 : 141 542 : #define FD_ADMINCTL_FAILOVER_FLAG_YES (1UL) 142 809 : #define FD_ADMINCTL_FAILOVER_FLAG_FORCE (2UL) 143 1270 : #define FD_ADMINCTL_FAILOVER_FLAG_TAKEOVER (4UL) 144 : 145 : /* The command as the operator typed it. */ 146 : static inline char const * 147 : fd_adminctl_failover_cmd_typed( ulong cmd, 148 27 : ulong flags ) { 149 27 : if( FD_UNLIKELY( cmd>=FD_ADMINCTL_FAILOVER_CMD_CNT ) ) return "unknown"; 150 27 : if( FD_LIKELY( !( flags & FD_ADMINCTL_FAILOVER_FLAG_TAKEOVER ) ) ) return "failover promote"; 151 12 : return ( flags & FD_ADMINCTL_FAILOVER_FLAG_FORCE ) ? "failover takeover --force" : "failover takeover"; 152 27 : } 153 : 154 : struct fd_adminctl_failover_req_v1 { 155 : ulong version; /* ==FD_ADMINCTL_FAILOVER_PAYLOAD_VERSION */ 156 : ulong cmd; /* FD_ADMINCTL_FAILOVER_CMD_* */ 157 : ulong flags; /* FD_ADMINCTL_FAILOVER_FLAG_* */ 158 : uint addr; /* HANDOFF only, the machine running the identity to dial in network byte order, 0 to find it in gossip */ 159 : ushort port; /* HANDOFF only, the port to dial it on in host byte order, 0 for the default port */ 160 : uchar target[ 32 ]; /* PREFLIGHT: the identity the keypair names. PROMOTE: the identity the command's handoff was for, zero for whatever the target is; another is refused IDENTITY_CHANGED */ 161 : }; 162 : typedef struct fd_adminctl_failover_req_v1 fd_adminctl_failover_req_t; 163 : 164 : /* Optional promote payload. */ 165 : struct fd_adminctl_failover_prepare_v1 { 166 : fd_adminctl_failover_req_t req; 167 : uchar keypair[ 64 ]; 168 : }; 169 : typedef struct fd_adminctl_failover_prepare_v1 fd_adminctl_failover_prepare_t; 170 : 171 : struct fd_adminctl_failover_resp_v1 { 172 : ulong version; /* ==FD_ADMINCTL_FAILOVER_PAYLOAD_VERSION */ 173 : uchar identity[ 32 ]; 174 : ulong slot; /* on success the last vote cast before hand-over, ULONG_MAX for none */ 175 : }; 176 : typedef struct fd_adminctl_failover_resp_v1 fd_adminctl_failover_resp_t; 177 246 : #define FD_ADMINCTL_FAILOVER_PAYLOAD_VERSION (1UL) 178 : 179 : FD_STATIC_ASSERT( sizeof(fd_adminctl_failover_req_t )==64UL, failover_req_v1_layout ); 180 : FD_STATIC_ASSERT( sizeof(fd_adminctl_failover_resp_t)==48UL, failover_resp_v1_layout ); 181 : 182 : typedef struct fd_adminctl_private fd_adminctl_t; 183 : 184 : FD_PROTOTYPES_BEGIN 185 : 186 : FD_FN_CONST ulong 187 : fd_adminctl_align( void ); 188 : 189 : FD_FN_CONST ulong 190 : fd_adminctl_footprint( void ); 191 : 192 : void * 193 : fd_adminctl_new( void * shmem ); 194 : 195 : fd_adminctl_t * 196 : fd_adminctl_join( void * shadminctl ); 197 : 198 : /* fd_adminctl_reserve claims a command slot and returns an identifier 199 : for the command reservation (index into the command buffer). Returns 200 : ULONG_MAX if every slot is busy or reserved. If a reservation is 201 : successful, payload_out receives a pointer into shared memory valid 202 : until publish completes or the reservation is abandoned by process 203 : death. */ 204 : 205 : ulong 206 : fd_adminctl_reserve( fd_adminctl_t * adminctl, 207 : void ** payload_out, 208 : ulong * payload_max_out ); 209 : 210 : /* fd_adminctl_publish validates the reservation and publishes the 211 : command to the admin tile. This should only be called by a command 212 : process after a successful reservation. After this function is 213 : called, the ownership of the slot is transferred to the app process. 214 : If a publish is made after a reservation, it will always succeed. */ 215 : 216 : void 217 : fd_adminctl_publish( fd_adminctl_t * adminctl, 218 : ulong slot_id, 219 : ulong cmd_id, 220 : ulong payload_sz ); 221 : 222 : /* fd_adminctl_wait waits for the command identified by slot_id to 223 : complete and returns the command result. This command should only be 224 : called by a command process after a successful publish. After the 225 : function returns, the command slot will be reclaimed. The command 226 : process result is returned. */ 227 : 228 : ulong 229 : fd_adminctl_wait( fd_adminctl_t * adminctl, 230 : ulong slot_id ); 231 : 232 : /* fd_adminctl_wait_steps is fd_adminctl_wait_response that also calls 233 : step_fn with each step line the admin tile adds while the command 234 : runs, in order. A command process that falls more than 235 : FD_ADMINCTL_STEP_CNT-1 lines behind skips the oldest. */ 236 : 237 : typedef void (* fd_adminctl_step_fn_t)( char const * step ); 238 : 239 : ulong 240 : fd_adminctl_wait_steps( fd_adminctl_t * adminctl, 241 : ulong slot_id, 242 : void * resp, 243 : ulong resp_max, 244 : ulong * resp_sz_out, 245 : fd_adminctl_step_fn_t step_fn ); 246 : 247 : /* fd_adminctl_wait_response is fd_adminctl_wait for commands that also 248 : return a response payload. Before the slot is reclaimed, up to 249 : resp_max bytes of the response payload are copied into resp and the 250 : response payload size is stored in resp_sz_out. */ 251 : 252 : ulong 253 : fd_adminctl_wait_response( fd_adminctl_t * adminctl, 254 : ulong slot_id, 255 : void * resp, 256 : ulong resp_max, 257 : ulong * resp_sz_out ); 258 : 259 : /* fd_adminctl_poll checks a command slot at a time and returns the 260 : command id and payload if a command is available. The command is now 261 : ready to be processed by the app process. If no command is 262 : available, the function returns FD_ADMINCTL_CMD_IDLE. Under the 263 : hood, it checks one slot at a time and advances the poll cursor. 264 : This function should be called repeatedly by only the main app 265 : process. */ 266 : 267 : ulong 268 : fd_adminctl_poll( fd_adminctl_t * adminctl, 269 : ulong * slot_id_out, 270 : void ** payload_out, 271 : ulong * payload_sz_out ); 272 : 273 : /* fd_adminctl_complete publishes the result for the command that has 274 : finished being processed. It should only be called by the app 275 : process after the command has been processed. */ 276 : 277 : void 278 : fd_adminctl_complete( fd_adminctl_t * adminctl, 279 : ulong slot_id, 280 : ulong result ); 281 : 282 : /* fd_adminctl_complete_response is fd_adminctl_complete for commands 283 : that also return a response payload. The request payload is zeroed 284 : and replaced with the resp_sz bytes at resp before the result is 285 : published. */ 286 : 287 : void 288 : fd_adminctl_complete_response( fd_adminctl_t * adminctl, 289 : ulong slot_id, 290 : ulong result, 291 : void const * resp, 292 : ulong resp_sz ); 293 : 294 : /* fd_adminctl_step adds a step line to the command being processed in 295 : slot_id, for its command process to show. Only the app process 296 : calls it, between poll and complete. The line is truncated to 297 : FD_ADMINCTL_STEP_MAX-1 bytes. */ 298 : 299 : void 300 : fd_adminctl_step( fd_adminctl_t * adminctl, 301 : ulong slot_id, 302 : char const * step ); 303 : 304 : FD_PROTOTYPES_END 305 : 306 : #endif /* HEADER_fd_src_discof_admin_fd_adminctl_h */