LCOV - code coverage report
Current view: top level - app/shared/commands/run - run.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 1162 0.0 %
Date: 2026-11-11 12:40:43 Functions: 0 33 0.0 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : #include "run.h"
       3             : #include "../../../../flamenco/accdb/fd_accdb.h"
       4             : #include "../../../../flamenco/stakes/fd_epoch_credits.h"
       5             : #include "../../../../flamenco/runtime/fd_cost_tracker_store.h"
       6             : #include "../../../../flamenco/stakes/fd_vote_stakes.h"
       7             : #include "../../../../flamenco/stakes/fd_stake_delegations.h"
       8             : #include "../../../../flamenco/runtime/fd_txncache.h"
       9             : #include "../../../../disco/store/fd_store.h"
      10             : 
      11             : #include <sys/wait.h>
      12             : #include "generated/main_seccomp.h"
      13             : #if defined(__aarch64__) || defined(__riscv)
      14             : #include "generated/pidns_no_poll_seccomp.h"
      15             : #else
      16             : #include "generated/pidns_seccomp.h"
      17             : #endif
      18             : 
      19             : #include "../../fd_bootinfo.h"
      20             : #include "../../../platform/fd_sys_util.h"
      21             : #include "../../../platform/fd_file_util.h"
      22             : #include "../../../platform/fd_net_util.h"
      23             : #include "../../../../disco/net/fd_net_tile.h"
      24             : #include "../../../../discof/backup/fd_backup.h"
      25             : #include "../../../../discof/backup/fd_snap_pool.h"
      26             : #include "../../../../discof/restore/utils/fd_ssarchive.h"
      27             : #include "../../../../disco/waker/fd_waker.h"
      28             : #include "../../../../disco/sleep/fd_sleep.h"
      29             : #include "../../../../util/pod/fd_pod_format.h"
      30             : #include "../../../../discof/restore/utils/fd_wfs.h"
      31             : #include "../../../../disco/keyguard/fd_keyload.h"
      32             : 
      33             : #include "../configure/configure.h"
      34             : #include "../configure/fd_cpu_isolation.h"
      35             : #include "../../../../disco/topo/fd_cpu_topo.h"
      36             : 
      37             : #include <dirent.h>
      38             : #include <sched.h>
      39             : #include <stdio.h>
      40             : #include <stdlib.h> /* getenv */
      41             : #include <poll.h>
      42             : #include <unistd.h>
      43             : #include <errno.h>
      44             : #include <fcntl.h>
      45             : #include <sys/prctl.h>
      46             : #include <sys/resource.h>
      47             : #include <sys/mman.h>
      48             : #include <sys/stat.h>
      49             : #include <linux/capability.h>
      50             : 
      51             : #if defined(__x86_64__)
      52             : #include <cpuid.h>
      53             : #endif
      54             : 
      55             : #include "../../../../util/tile/fd_tile_private.h"
      56             : 
      57             : extern fd_topo_obj_callbacks_t * CALLBACKS[];
      58             : 
      59           0 : #define NAME "run"
      60             : 
      61             : void
      62             : run_cmd_perm( args_t *         args,
      63             :               fd_cap_chk_t *   chk,
      64           0 :               config_t const * config ) {
      65           0 :   (void)args;
      66             : 
      67           0 :   ulong mlock_limit = fd_topo_mlock_max_tile( &config->topo );
      68             : 
      69           0 :   fd_cap_chk_raise_rlimit( chk, NAME, RLIMIT_MEMLOCK, mlock_limit, "call `rlimit(2)` to increase `RLIMIT_MEMLOCK` so all memory can be locked with `mlock(2)`" );
      70           0 :   fd_cap_chk_raise_rlimit( chk, NAME, RLIMIT_NICE,    40,          "call `setpriority(2)` to increase thread priorities" );
      71           0 :   fd_cap_chk_raise_rlimit( chk, NAME, RLIMIT_NOFILE,  CONFIGURE_NR_OPEN_FILES,
      72           0 :                                                                        "call `rlimit(2)  to increase `RLIMIT_NOFILE` to allow more open files" );
      73           0 :   fd_cap_chk_cap(          chk, NAME, CAP_NET_RAW,                 "call `socket(2)` to bind to a raw socket for use by XDP" );
      74           0 :   fd_cap_chk_cap(          chk, NAME, CAP_SYS_ADMIN,               "call `bpf(2)` with the `BPF_OBJ_GET` command to initialize XDP" );
      75           0 :   if( fd_sandbox_requires_cap_sys_admin( config->uid, config->gid ) )
      76           0 :     fd_cap_chk_cap(        chk, NAME, CAP_SYS_ADMIN,               "call `unshare(2)` with `CLONE_NEWUSER` to sandbox the process in a user namespace" );
      77           0 :   if( FD_LIKELY( getuid() != config->uid ) )
      78           0 :     fd_cap_chk_cap(        chk, NAME, CAP_SETUID,                  "call `setresuid(2)` to switch uid to the sandbox user" );
      79           0 :   if( FD_LIKELY( getgid()!=config->gid ) )
      80           0 :     fd_cap_chk_cap(        chk, NAME, CAP_SETGID,                  "call `setresgid(2)` to switch gid to the sandbox user" );
      81           0 :   if( FD_UNLIKELY( config->tiles.metric.prometheus_listen_port<1024 ) )
      82           0 :     fd_cap_chk_cap(        chk, NAME, CAP_NET_BIND_SERVICE,        "call `bind(2)` to bind to a privileged port for serving metrics" );
      83           0 :   if( FD_UNLIKELY( config->tiles.gui.gui_listen_port<1024 ) )
      84           0 :     fd_cap_chk_cap(        chk, NAME, CAP_NET_BIND_SERVICE,        "call `bind(2)` to bind to a privileged port for serving the GUI" );
      85           0 :   if( FD_UNLIKELY( config->firedancer.failover.enabled && config->firedancer.failover.listen_port<1024 ) )
      86           0 :     fd_cap_chk_cap(        chk, NAME, CAP_NET_BIND_SERVICE,        "call `bind(2)` to bind to a privileged port for the failover listener" );
      87           0 : }
      88             : 
      89             : struct pidns_clone_args {
      90             :   config_t const * config;
      91             :   int *            pipefd;
      92             :   int              closefd;
      93             : };
      94             : 
      95             : extern char fd_log_private_path[ 1024 ]; /* empty string on start */
      96             : 
      97             : static pid_t pid_namespace;
      98             : 
      99           0 : #define FD_LOG_ERR_NOEXIT(a) do { long _fd_log_msg_now = fd_log_wallclock(); fd_log_private_1( 4, _fd_log_msg_now, __FILE__, __LINE__, __func__, fd_log_private_0 a ); } while(0)
     100             : 
     101             : static void
     102           0 : parent_signal( int sig ) {
     103           0 :   if( FD_LIKELY( pid_namespace ) ) kill( pid_namespace, SIGKILL );
     104             : 
     105           0 :   if( -1!=fd_log_private_logfile_fd() ) FD_LOG_ERR_NOEXIT(( "Received signal %s%s%s %s(%s)%s\n%sLog at \"%s\"%s", fd_log_style_bold(), fd_io_strsignal_name( sig ), fd_log_style_normal(), fd_log_style_dim(), fd_io_strsignal_desc( sig ), fd_log_style_normal(), fd_log_style_dim(), fd_log_private_path, fd_log_style_normal() ));
     106           0 :   else                                  FD_LOG_ERR_NOEXIT(( "Received signal %s%s%s %s(%s)%s",                fd_log_style_bold(), fd_io_strsignal_name( sig ), fd_log_style_normal(), fd_log_style_dim(), fd_io_strsignal_desc( sig ), fd_log_style_normal() ));
     107             : 
     108           0 :   if( FD_LIKELY( sig==SIGINT ) ) fd_sys_util_exit_group( 128+SIGINT );
     109           0 :   else                           fd_sys_util_exit_group( 0          );
     110           0 : }
     111             : 
     112             : static void
     113           0 : install_parent_signals( void ) {
     114           0 :   struct sigaction sa = {
     115           0 :     .sa_handler = parent_signal,
     116           0 :     .sa_flags   = 0,
     117           0 :   };
     118           0 :   if( FD_UNLIKELY( sigaction( SIGTERM, &sa, NULL ) ) )
     119           0 :     FD_LOG_ERR(( "sigaction(SIGTERM) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     120           0 :   if( FD_UNLIKELY( sigaction( SIGINT, &sa, NULL ) ) )
     121           0 :     FD_LOG_ERR(( "sigaction(SIGINT) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     122             : 
     123           0 :   sa.sa_handler = SIG_IGN;
     124           0 :   if( FD_UNLIKELY( sigaction( SIGUSR1, &sa, NULL ) ) )
     125           0 :     FD_LOG_ERR(( "sigaction(SIGUSR1) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     126           0 :   if( FD_UNLIKELY( sigaction( SIGUSR2, &sa, NULL ) ) )
     127           0 :     FD_LOG_ERR(( "sigaction(SIGUSR2) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     128           0 : }
     129             : 
     130             : void *
     131           0 : create_clone_stack( void ) {
     132           0 :   ulong mmap_sz = FD_TILE_PRIVATE_STACK_SZ + 2UL*FD_SHMEM_NORMAL_PAGE_SZ;
     133           0 :   uchar * stack = (uchar *)mmap( NULL, mmap_sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, (off_t)0 );
     134           0 :   if( FD_UNLIKELY( stack==MAP_FAILED ) )
     135           0 :     FD_LOG_ERR(( "mmap() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     136             : 
     137             :   /* Make space for guard lo and guard hi */
     138           0 :   if( FD_UNLIKELY( munmap( stack, FD_SHMEM_NORMAL_PAGE_SZ ) ) )
     139           0 :     FD_LOG_ERR(( "munmap failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     140           0 :   stack += FD_SHMEM_NORMAL_PAGE_SZ;
     141           0 :   if( FD_UNLIKELY( munmap( stack + FD_TILE_PRIVATE_STACK_SZ, FD_SHMEM_NORMAL_PAGE_SZ ) ) )
     142           0 :     FD_LOG_ERR(( "munmap failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     143             : 
     144             :   /* Create the guard regions in the extra space */
     145           0 :   void * guard_lo = (void *)(stack - FD_SHMEM_NORMAL_PAGE_SZ );
     146           0 :   if( FD_UNLIKELY( mmap( guard_lo, FD_SHMEM_NORMAL_PAGE_SZ, PROT_NONE,
     147           0 :                          MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, (off_t)0 )!=guard_lo ) )
     148           0 :     FD_LOG_ERR(( "mmap failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     149             : 
     150           0 :   void * guard_hi = (void *)(stack + FD_TILE_PRIVATE_STACK_SZ);
     151           0 :   if( FD_UNLIKELY( mmap( guard_hi, FD_SHMEM_NORMAL_PAGE_SZ, PROT_NONE,
     152           0 :                          MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, (off_t)0 )!=guard_hi ) )
     153           0 :     FD_LOG_ERR(( "mmap failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     154             : 
     155           0 :   return stack;
     156           0 : }
     157             : 
     158             : static int
     159           0 : cgroup_procs_write( char const * path ) {
     160           0 :   int fd = open( path, O_WRONLY );
     161           0 :   if( FD_UNLIKELY( fd<0 ) ) {
     162           0 :     if( FD_LIKELY( errno==ENOENT ) ) return 0;
     163           0 :     FD_LOG_ERR(( "open(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     164           0 :   }
     165             : 
     166           0 :   char pid[ 32 ];
     167           0 :   ulong pid_len;
     168           0 :   FD_TEST( fd_cstr_printf_check( pid, sizeof(pid), &pid_len, "%ld", (long)getpid() ) );
     169           0 :   if( FD_UNLIKELY( write( fd, pid, pid_len )!=(long)pid_len ) )
     170           0 :     FD_LOG_ERR(( "write(%s,\"%s\") failed (%i-%s)", path, pid, errno, fd_io_strerror( errno ) ));
     171           0 :   if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "close(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     172           0 :   return 1;
     173           0 : }
     174             : 
     175             : struct spawn_cgroup {
     176             :   int  probed;    /* isolation cgroup existence checked, original saved */
     177             :   int  present;   /* isolation cgroup exists */
     178             :   int  joined;    /* currently a member of the isolation cgroup */
     179             :   char isolation[ PATH_MAX ];
     180             :   char original[ PATH_MAX ];
     181             : };
     182             : 
     183             : static void
     184             : join_isolation_cgroup( char const *          name,
     185           0 :                        struct spawn_cgroup * cg ) {
     186           0 :   if( FD_UNLIKELY( !cg->probed ) ) {
     187           0 :     cg->probed = 1;
     188           0 :     FD_TEST( fd_cstr_printf_check( cg->isolation, sizeof(cg->isolation), NULL, "/sys/fs/cgroup/%s/cgroup.procs", name ) );
     189             : 
     190             :     /* The cpuset stage not being configured (no cgroup) is the common
     191             :        case and must be decided FIRST: on cgroup v1-only or hybrid
     192             :        hosts /proc/self/cgroup does not have the v2 format, and
     193             :        validating it before knowing the stage is even in use would
     194             :        turn every tile launch on such hosts into a fatal error. */
     195           0 :     cg->present = !access( cg->isolation, F_OK );
     196           0 :     if( FD_LIKELY( !cg->present ) ) return;
     197             : 
     198             :     /* Remember where we came from.  The v2 entry in /proc/self/cgroup
     199             :        is the line "0::<path>"; on a pure v2 hierarchy it is the only
     200             :        line, but on hybrid systems v1 controller lines precede it, so
     201             :        search rather than assume. */
     202           0 :     char buf[ 4096 ];
     203           0 :     int fd = open( "/proc/self/cgroup", O_RDONLY );
     204           0 :     if( FD_UNLIKELY( fd<0 ) ) FD_LOG_ERR(( "open(/proc/self/cgroup) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     205           0 :     long n = read( fd, buf, sizeof(buf)-1UL );
     206           0 :     if( FD_UNLIKELY( n<0L ) ) FD_LOG_ERR(( "read(/proc/self/cgroup) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     207           0 :     if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "close(/proc/self/cgroup) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     208           0 :     buf[ n ] = '\0';
     209             : 
     210           0 :     char * line = buf;
     211           0 :     while( line && strncmp( line, "0::", 3UL ) ) {
     212           0 :       line = strchr( line, '\n' );
     213           0 :       if( FD_LIKELY( line ) ) line++;
     214           0 :     }
     215           0 :     if( FD_UNLIKELY( !line || !line[ 0 ] ) )
     216           0 :       FD_LOG_ERR(( "no cgroup v2 entry in /proc/self/cgroup while the cpuset isolation cgroup `/sys/fs/cgroup/%s` "
     217           0 :                    "exists. Remove it with `%s configure fini cpuset`", name, FD_BINARY_NAME ));
     218           0 :     char * nl = strchr( line, '\n' ); if( FD_LIKELY( nl ) ) *nl = '\0';
     219           0 :     FD_TEST( fd_cstr_printf_check( cg->original, PATH_MAX, NULL,
     220           0 :                                    "/sys/fs/cgroup%s/cgroup.procs", line+3UL ) );
     221           0 :   }
     222             : 
     223           0 :   if( FD_UNLIKELY( !cg->present || cg->joined ) ) return;
     224           0 :   cg->joined = cgroup_procs_write( cg->isolation );
     225           0 : }
     226             : 
     227             : static void
     228           0 : leave_isolation_cgroup( struct spawn_cgroup * cg ) {
     229           0 :   if( FD_LIKELY( !cg->joined ) ) return;
     230           0 :   if( FD_UNLIKELY( !cgroup_procs_write( cg->original ) ) ) FD_LOG_ERR(( "could not return to original cgroup `%s`", cg->original ));
     231           0 :   cg->joined = 0;
     232           0 : }
     233             : 
     234             : static pid_t
     235             : execve_tile( char const *           name,
     236             :              fd_topo_tile_t const * tile,
     237             :              fd_cpuset_t const *    float_cpu_set,
     238             :              fd_cpuset_t const *    cpuless_cpu_set,
     239             :              int                    floating_priority,
     240             :              int                    config_memfd,
     241             :              int                    pipefd,
     242           0 :              struct spawn_cgroup *  cg ) {
     243           0 :   FD_CPUSET_DECL( cpu_set );
     244           0 :   if( FD_LIKELY( tile->cpu_idx!=ULONG_MAX ) ) {
     245             :     /* Join the cpuset isolation cgroup (if configured) and set the
     246             :        thread affinity before we clone the new process, to ensure
     247             :        kernel first touch happens on the desired thread.  The child
     248             :        inherits both. */
     249           0 :     join_isolation_cgroup( name, cg );
     250           0 :     if( FD_UNLIKELY( tile->floats ) ) {
     251             :       /* the floating CPUs on this tile's NUMA node: memory was placed
     252             :          by cpu_idx */
     253           0 :       ulong numa_idx = fd_shmem_numa_idx( tile->cpu_idx );
     254           0 :       for( ulong cpu=0UL; cpu<FD_TILE_MAX; cpu++ )
     255           0 :         if( fd_cpuset_test( float_cpu_set, cpu ) && fd_shmem_numa_idx( cpu )==numa_idx ) fd_cpuset_insert( cpu_set, cpu );
     256           0 :       if( FD_UNLIKELY( !fd_cpuset_cnt( cpu_set ) ) ) fd_memcpy( cpu_set, float_cpu_set, fd_cpuset_footprint() );
     257           0 :     }
     258           0 :     if( FD_UNLIKELY( !fd_cpuset_cnt( cpu_set ) ) ) fd_cpuset_insert( cpu_set, tile->cpu_idx );
     259           0 :     if( FD_UNLIKELY( -1==setpriority( PRIO_PROCESS, 0, -19 ) ) ) FD_LOG_ERR(( "setpriority() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     260           0 :   } else {
     261           0 :     leave_isolation_cgroup( cg );
     262           0 :     fd_cpuset_copy( cpu_set, cpuless_cpu_set );
     263           0 :     if( FD_UNLIKELY( -1==setpriority( PRIO_PROCESS, 0, floating_priority ) ) ) FD_LOG_ERR(( "setpriority() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     264           0 :   }
     265             : 
     266           0 :   if( FD_UNLIKELY( fd_cpuset_setaffinity( 0, cpu_set ) ) ) {
     267           0 :     if( FD_LIKELY( errno==EINVAL ) ) {
     268           0 :       FD_LOG_ERR(( "Unable to set the thread affinity for tile %s:%lu on cpu %lu. It is likely that the affinity "
     269           0 :                    "you have specified for this tile in [layout.affinity] of your configuration file contains a "
     270           0 :                    "CPU (%lu) which does not exist on this machine.",
     271           0 :                    tile->name, tile->kind_id, tile->cpu_idx, tile->cpu_idx ));
     272           0 :     } else {
     273           0 :       FD_LOG_ERR(( "sched_setaffinity failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     274           0 :     }
     275           0 :   }
     276             : 
     277             :   /* Clear CLOEXEC on the side of the pipe we want to pass to the tile. */
     278           0 :   if( FD_UNLIKELY( -1==fcntl( pipefd, F_SETFD, 0 ) ) ) FD_LOG_ERR(( "fcntl(F_SETFD,0) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     279           0 :   pid_t child = fork();
     280           0 :   if( FD_UNLIKELY( -1==child ) ) FD_LOG_ERR(( "fork() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     281           0 :   if( FD_LIKELY( !child ) ) {
     282           0 :     if( !strcmp( tile->name, "sign" ) && tile->sign.failover_junk_fd!=-1 ) {
     283           0 :       if( FD_UNLIKELY( -1==fcntl( tile->sign.failover_junk_fd, F_SETFD, 0 ) ) )
     284           0 :         FD_LOG_ERR(( "passing the failover junk key pipe failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     285           0 :     }
     286             : 
     287           0 :     char _current_executable_path[ PATH_MAX ];
     288           0 :     FD_TEST( -1!=fd_file_util_self_exe( _current_executable_path ) );
     289             : 
     290           0 :     char kind_id[ 32 ], config_fd[ 32 ], pipe_fd[ 32 ];
     291           0 :     FD_TEST( fd_cstr_printf_check( kind_id,   sizeof( kind_id ),   NULL, "%lu", tile->kind_id ) );
     292           0 :     FD_TEST( fd_cstr_printf_check( config_fd, sizeof( config_fd ), NULL, "%d",  config_memfd ) );
     293           0 :     FD_TEST( fd_cstr_printf_check( pipe_fd,   sizeof( pipe_fd ),   NULL, "%d",  pipefd ) );
     294           0 :     char const * args[ 9 ] = { _current_executable_path, "run1", tile->name, kind_id, "--pipe-fd", pipe_fd, "--config-fd", config_fd, NULL };
     295           0 :     if( FD_UNLIKELY( -1==execve( _current_executable_path, (char **)args, NULL ) ) ) FD_LOG_ERR(( "execve() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     296           0 :   } else {
     297           0 :     if( FD_UNLIKELY( -1==fcntl( pipefd, F_SETFD, FD_CLOEXEC ) ) ) FD_LOG_ERR(( "fcntl(F_SETFD,FD_CLOEXEC) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     298           0 :     return child;
     299           0 :   }
     300           0 :   return 0;
     301           0 : }
     302             : 
     303             : int
     304           0 : main_pid_namespace( void * _args ) {
     305           0 :   struct pidns_clone_args * args = _args;
     306           0 :   if( FD_UNLIKELY( close( args->pipefd[ 0 ] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     307           0 :   if( FD_UNLIKELY( -1!=args->closefd ) ) {
     308           0 :     if( FD_UNLIKELY( close( args->closefd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     309           0 :   }
     310             : 
     311           0 :   config_t const * config = args->config;
     312             : 
     313           0 :   fd_log_thread_set( "pidns" );
     314           0 :   ulong pid = fd_sandbox_getpid(); /* Need to read /proc again.. we got a new PID from clone */
     315           0 :   fd_log_private_group_id_set( pid );
     316           0 :   fd_log_private_thread_id_set( pid );
     317           0 :   fd_log_private_stack_discover( FD_TILE_PRIVATE_STACK_SZ,
     318           0 :                                  &fd_tile_private_stack0, &fd_tile_private_stack1 );
     319             : 
     320           0 :   if( FD_UNLIKELY( !config->development.sandbox ) ) {
     321             :     /* If no sandbox, then there's no actual PID namespace so we can't
     322             :        wait() grandchildren for the exit code.  Do this as a workaround. */
     323           0 :     if( FD_UNLIKELY( -1==prctl( PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0 ) ) )
     324           0 :       FD_LOG_ERR(( "prctl(PR_SET_CHILD_SUBREAPER) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     325           0 :   }
     326             : 
     327             :   /* Save the current affinity, it will be restored after creating any child tiles */
     328           0 :   FD_CPUSET_DECL( floating_cpu_set );
     329           0 :   if( FD_UNLIKELY( fd_cpuset_getaffinity( 0, floating_cpu_set ) ) )
     330           0 :     FD_LOG_ERR(( "fd_cpuset_getaffinity failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     331             : 
     332           0 :   FD_CPUSET_DECL( float_cpu_set );
     333           0 :   FD_CPUSET_DECL( cpuless_cpu_set );
     334           0 :   static fd_topo_cpus_t cpus[1];
     335           0 :   fd_topo_cpus_init( cpus );
     336           0 :   fd_topo_cpus_floating( cpus, &config->topo, floating_cpu_set, float_cpu_set, cpuless_cpu_set );
     337           0 :   int any_floats = 0;
     338           0 :   for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) any_floats |= config->topo.tiles[ i ].floats;
     339             : 
     340           0 :   pid_t child_pids[ FD_TOPO_MAX_TILES+1 ];
     341           0 :   ulong actual_pids[ FD_TOPO_MAX_TILES+1 ];
     342           0 :   for( ulong i=0UL; i<FD_TOPO_MAX_TILES+1; i++ ) actual_pids[ i ] = ULONG_MAX;
     343           0 :   char  child_names[ FD_TOPO_MAX_TILES+1 ][ 32 ];
     344           0 :   ulong child_idxs[ FD_TOPO_MAX_TILES+1 ];
     345           0 :   struct pollfd fds[ FD_TOPO_MAX_TILES+2 ];
     346             : 
     347           0 :   int config_memfd = fd_config_to_memfd( config );
     348           0 :   if( FD_UNLIKELY( -1==config_memfd ) ) FD_LOG_ERR(( "fd_config_to_memfd() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     349             : 
     350           0 :   int need_iavf = 0==strcmp( config->net.provider, "iavf" );
     351           0 :   fd_iavf_fds_t iavf_fds = { .member_cnt=0UL };
     352           0 :   for( ulong i=0UL; i<FD_IAVF_MEMBER_MAX; i++ ) {
     353           0 :     iavf_fds.container_fd[ i ] = -1;
     354           0 :     iavf_fds.group_fd    [ i ] = -1;
     355           0 :     iavf_fds.device_fd   [ i ] = -1;
     356           0 :   }
     357           0 :   if( need_iavf ) {
     358           0 :     fd_topo_install_iavf( (fd_topo_t *)&config->topo, &iavf_fds );
     359           0 :   }
     360             : 
     361           0 :   int need_mlx5 = 0==strcmp( config->net.provider, "mlx5" );
     362           0 :   fd_mlx5_fds_t mlx5_fds = { .cmd_fd=-1, .async_fd=-1 };
     363           0 :   if( need_mlx5 ) {
     364           0 :     fd_topo_install_mlx5( (fd_topo_t *)&config->topo, &mlx5_fds );
     365           0 :   }
     366             : 
     367           0 :   ulong child_cnt = 0UL;
     368             : 
     369           0 :   errno = 0;
     370           0 :   int save_priority = getpriority( PRIO_PROCESS, 0 );
     371           0 :   if( FD_UNLIKELY( -1==save_priority && errno ) ) FD_LOG_ERR(( "getpriority() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     372             : 
     373           0 :   int need_xdp = 0==strcmp( config->net.provider, "xdp" );
     374           0 :   fd_xdp_fds_t xdp_fds[ FD_TOPO_XDP_FDS_MAX ];
     375           0 :   uint         xdp_fds_cnt = FD_TOPO_XDP_FDS_MAX;
     376           0 :   if( need_xdp ) {
     377           0 :     fd_topo_install_xdp( &config->topo, xdp_fds, &xdp_fds_cnt, config->net.bind_address_parsed, 0 );
     378           0 :   }
     379             : 
     380           0 :   initialize_accdb_fd( config );
     381           0 :   initialize_epoch_credits_fd( config );
     382           0 :   initialize_cost_tracker_fd( config );
     383           0 :   initialize_vote_stakes_fd( config );
     384           0 :   initialize_stake_delegations_fd( config );
     385           0 :   initialize_txncache_fd( config );
     386           0 :   initialize_store_fds( config );
     387           0 :   ulong store_obj_id = fd_pod_query_ulong( config->topo.props, "store", ULONG_MAX );
     388           0 :   int   has_store     = store_obj_id!=ULONG_MAX;
     389           0 :   ulong snap_max                = 0UL;
     390           0 :   ulong snap_retained_max       = 0UL;
     391           0 :   int   snapshot_upload_enabled = 0;
     392           0 :   int   snapshot_dio_enabled    = 0;
     393           0 :     snap_max                = initialize_snapshot_fds( config );
     394           0 :     snap_retained_max       = (ulong)config->firedancer.snapshots.max_full_snapshots_to_keep +
     395           0 :                               (ulong)config->firedancer.snapshots.max_incremental_snapshots_to_keep;
     396           0 :     snapshot_upload_enabled = fd_topo_find_tile( &config->topo, "snapsv", 0UL )!=ULONG_MAX;
     397           0 :     snapshot_dio_enabled    = fd_topo_find_tile( &config->topo, "snapzp", 0UL )!=ULONG_MAX;
     398             : 
     399           0 :   ulong waker_client_cnt = 0UL;
     400           0 :   for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
     401           0 :     ulong idx = config->topo.tiles[ i ].waker_client_idx;
     402           0 :     if( FD_UNLIKELY( idx!=ULONG_MAX ) ) waker_client_cnt = fd_ulong_max( waker_client_cnt, idx+1UL );
     403           0 :   }
     404           0 :   fd_waker_install( waker_client_cnt );
     405           0 :   fd_sleep_eventfd_install( &config->topo );
     406             : 
     407           0 :   struct spawn_cgroup spawn_cg = {0};
     408             : 
     409           0 :   for( ulong pass=0UL; pass<2UL; pass++ ) {
     410           0 :     for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
     411           0 :       fd_topo_tile_t const * tile = &config->topo.tiles[ i ];
     412           0 :       if( FD_UNLIKELY( (tile->cpu_idx!=ULONG_MAX)!=pass ) ) continue;
     413             : 
     414           0 :       if( need_xdp ) {
     415           0 :         if( FD_UNLIKELY( strcmp( tile->name, "net" ) ) ) {
     416           0 :           for( uint i=0U; i<xdp_fds_cnt; i++ ) {
     417             :             /* close XDP related file descriptors */
     418           0 :             if( FD_UNLIKELY( -1==fcntl( xdp_fds[i].xsk_map_fd,   F_SETFD, FD_CLOEXEC ) ) ) FD_LOG_ERR(( "fcntl(F_SETFD,FD_CLOEXEC) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     419           0 :             if( FD_UNLIKELY( -1==fcntl( xdp_fds[i].prog_link_fd, F_SETFD, FD_CLOEXEC ) ) ) FD_LOG_ERR(( "fcntl(F_SETFD,FD_CLOEXEC) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     420           0 :           }
     421           0 :         } else {
     422           0 :           for( uint i=0U; i<xdp_fds_cnt; i++ ) {
     423           0 :             if( FD_UNLIKELY( -1==fcntl( xdp_fds[i].xsk_map_fd,   F_SETFD, 0 ) ) ) FD_LOG_ERR(( "fcntl(F_SETFD,0) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     424           0 :             if( FD_UNLIKELY( -1==fcntl( xdp_fds[i].prog_link_fd, F_SETFD, 0 ) ) ) FD_LOG_ERR(( "fcntl(F_SETFD,0) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     425           0 :           }
     426           0 :         }
     427           0 :       }
     428             : 
     429           0 :       if( need_iavf ) {
     430           0 :         int const fd_flags = 0!=strcmp( tile->name, "iavf" ) ? FD_CLOEXEC : 0;
     431           0 :         for( ulong i=0UL; i<iavf_fds.member_cnt; i++ ) {
     432           0 :           if( FD_UNLIKELY( -1==fcntl( iavf_fds.container_fd[ i ], F_SETFD, fd_flags ) ) ) FD_LOG_ERR(( "fcntl(F_SETFD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     433           0 :           if( FD_UNLIKELY( -1==fcntl( iavf_fds.group_fd    [ i ], F_SETFD, fd_flags ) ) ) FD_LOG_ERR(( "fcntl(F_SETFD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     434           0 :           if( FD_UNLIKELY( -1==fcntl( iavf_fds.device_fd   [ i ], F_SETFD, fd_flags ) ) ) FD_LOG_ERR(( "fcntl(F_SETFD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     435           0 :         }
     436           0 :       }
     437             : 
     438           0 :       if( need_mlx5 ) {
     439           0 :         int const fd_flags = strcmp( tile->name, "mlx5" ) ? FD_CLOEXEC : 0;
     440           0 :         if( FD_UNLIKELY( -1==fcntl( mlx5_fds.cmd_fd, F_SETFD, fd_flags ) ) ) {
     441           0 :           FD_LOG_ERR(( "fcntl(F_SETFD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     442           0 :         }
     443           0 :         if( FD_UNLIKELY( -1==fcntl( mlx5_fds.async_fd, F_SETFD, fd_flags ) ) ) {
     444           0 :           FD_LOG_ERR(( "fcntl(F_SETFD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     445           0 :         }
     446           0 :         for( ulong i=0UL; i<mlx5_fds.rx_comp_channel_fd_cnt; i++ ) {
     447           0 :           int const rx_comp_channel_fd_flags = fd_flags || i!=tile->kind_id ? FD_CLOEXEC : 0;
     448           0 :           if( FD_UNLIKELY( -1==fcntl( mlx5_fds.rx_comp_channel_fd[ i ],
     449           0 :                                      F_SETFD, rx_comp_channel_fd_flags ) ) ) {
     450           0 :             FD_LOG_ERR(( "fcntl(F_SETFD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     451           0 :           }
     452           0 :         }
     453           0 :       }
     454             : 
     455           0 :         int tile_uses_accdb    = 0;
     456           0 :         int tile_uses_accdb_ro = 0;
     457           0 :         for( ulong i=0UL; i<tile->uses_obj_cnt; i++ ) {
     458           0 :           fd_topo_obj_t const * obj = &config->topo.objs[ tile->uses_obj_id[ i ] ];
     459           0 :           if( FD_UNLIKELY( !strcmp( obj->name, "accdb" ) ) ) {
     460           0 :             if( FD_UNLIKELY( tile->uses_obj_mode[ i ]==FD_SHMEM_JOIN_MODE_READ_ONLY ) ) tile_uses_accdb_ro = 1;
     461           0 :             else                                                                        tile_uses_accdb    = 1;
     462           0 :             break;
     463           0 :           }
     464           0 :         }
     465             : 
     466             :         /* The gui joins the accdb shmem read-only (for partition stats)
     467             :            but never reads account data from the on-disk file, so it does
     468             :            not need the accounts.db fd.  Withhold it to keep the gui at
     469             :            least privilege. */
     470           0 :         if( FD_UNLIKELY( !strcmp( tile->name, "gui" ) ) ) tile_uses_accdb_ro = 0;
     471           0 :         if( FD_UNLIKELY( !strcmp( tile->name, "snapmk" ) ) ) { tile_uses_accdb = 0; tile_uses_accdb_ro = 1; }
     472             : 
     473           0 :         if( FD_UNLIKELY( tile_uses_accdb ) ) {
     474           0 :           if( FD_UNLIKELY( -1==fcntl( FD_ACCDB_FD_RW, F_SETFD, 0 ) ) ) FD_LOG_ERR(( "fcntl(F_SETFD,0) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     475           0 :         } else {
     476           0 :           if( FD_UNLIKELY( -1==fcntl( FD_ACCDB_FD_RW, F_SETFD, FD_CLOEXEC ) ) ) FD_LOG_ERR(( "fcntl(F_SETFD,FD_CLOEXEC) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     477           0 :         }
     478             : 
     479           0 :         if( FD_UNLIKELY( tile_uses_accdb_ro ) ) {
     480           0 :           if( FD_UNLIKELY( -1==fcntl( FD_ACCDB_FD_RO, F_SETFD, 0 ) ) ) FD_LOG_ERR(( "fcntl(F_SETFD,0) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     481           0 :         } else {
     482           0 :           if( FD_UNLIKELY( -1==fcntl( FD_ACCDB_FD_RO, F_SETFD, FD_CLOEXEC ) ) ) FD_LOG_ERR(( "fcntl(F_SETFD,FD_CLOEXEC) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     483           0 :         }
     484             : 
     485           0 :         ulong txncache_obj_id    = fd_pod_query_ulong( config->topo.props, "txncache", ULONG_MAX );
     486           0 :         int   tile_uses_txncache = 0;
     487           0 :         for( ulong i=0UL; i<tile->uses_obj_cnt; i++ ) tile_uses_txncache |= tile->uses_obj_id[ i ]==txncache_obj_id;
     488           0 :         if( FD_UNLIKELY( fcntl( FD_TXNCACHE_FD, F_SETFD, tile_uses_txncache ? 0 : FD_CLOEXEC )<0 ) )
     489           0 :           FD_LOG_ERR(( "fcntl(FD_TXNCACHE_FD,F_SETFD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     490             : 
     491           0 :         if( FD_LIKELY( has_store ) ) {
     492           0 :           int tile_uses_store = 0;
     493           0 :           for( ulong i=0UL; i<tile->uses_obj_cnt; i++ ) tile_uses_store |= tile->uses_obj_id[ i ]==store_obj_id;
     494           0 :           int tile_uses_store_rw = tile_uses_store &&
     495           0 :                                    (!strcmp( tile->name, "shred" ) ||
     496           0 :                                     !strcmp( tile->name, "backt" ) ||
     497           0 :                                     !strcmp( tile->name, "rserve" ));
     498           0 :           int tile_uses_store_ro = tile_uses_store && !strcmp( tile->name, "replay" );
     499           0 :           if( FD_UNLIKELY( fcntl( FD_STORE_FD_RW, F_SETFD, tile_uses_store_rw ? 0 : FD_CLOEXEC )<0 ) )
     500           0 :             FD_LOG_ERR(( "fcntl(FD_STORE_FD_RW,F_SETFD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     501           0 :           if( FD_UNLIKELY( fcntl( FD_STORE_FD_RO, F_SETFD, tile_uses_store_ro ? 0 : FD_CLOEXEC )<0 ) )
     502           0 :             FD_LOG_ERR(( "fcntl(FD_STORE_FD_RO,F_SETFD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     503           0 :         }
     504             : 
     505           0 :         if( FD_UNLIKELY( -1==fcntl( FD_EPOCH_CREDITS_FD, F_SETFD, !strcmp( tile->name, "replay" ) ? 0 : FD_CLOEXEC ) ) )
     506           0 :           FD_LOG_ERR(( "fcntl(F_SETFD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     507             : 
     508           0 :         if( FD_UNLIKELY( -1==fcntl( FD_COST_TRACKER_FD, F_SETFD, !strcmp( tile->name, "replay" ) ? 0 : FD_CLOEXEC ) ) )
     509           0 :           FD_LOG_ERR(( "fcntl(F_SETFD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     510             : 
     511           0 :         int tile_uses_vote_stakes = !strcmp( tile->name, "replay" ) || !strcmp( tile->name, "execle" ) ||
     512           0 :                                     !strcmp( tile->name, "execrp" ) || !strcmp( tile->name, "snapmk" );
     513           0 :         if( FD_UNLIKELY( -1==fcntl( FD_VOTE_STAKES_FD, F_SETFD, tile_uses_vote_stakes ? 0 : FD_CLOEXEC ) ) )
     514           0 :           FD_LOG_ERR(( "fcntl(F_SETFD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     515             : 
     516           0 :         int tile_uses_stake_spill = !strcmp( tile->name, "replay" ) || !strcmp( tile->name, "execle" ) ||
     517           0 :                                     !strcmp( tile->name, "execrp" ) || !strcmp( tile->name, "snapin" );
     518           0 :         if( FD_UNLIKELY( -1==fcntl( FD_STAKE_DELEGATIONS_FD, F_SETFD, tile_uses_stake_spill ? 0 : FD_CLOEXEC ) ) )
     519           0 :           FD_LOG_ERR(( "fcntl(F_SETFD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     520             : 
     521           0 :         int tile_uses_snap_fd     = !strcmp( tile->name, "snapmk" );
     522           0 :         int tile_uses_snap_dio_fd = !strcmp( tile->name, "snapzp" );
     523           0 :         int tile_uses_snap_rd_fd  = !strcmp( tile->name, "snapsv" );
     524           0 :         int tile_uses_snap_all_fd = !strcmp( tile->name, "snapct" );
     525           0 :         for( ulong j=0UL; j<snap_max; j++ ) {
     526           0 :           int retained = j<snap_retained_max;
     527           0 :           if( FD_UNLIKELY( -1==fcntl( FD_SNAP_FD( j ), F_SETFD, tile_uses_snap_all_fd || ( tile_uses_snap_fd && retained ) ? 0 : FD_CLOEXEC ) ) )
     528           0 :             FD_LOG_ERR(( "fcntl(F_SETFD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     529           0 :           if( snapshot_dio_enabled ) {
     530           0 :             if( FD_UNLIKELY( -1==fcntl( FD_SNAP_DIO_FD( j ), F_SETFD, tile_uses_snap_dio_fd && retained ? 0 : FD_CLOEXEC ) ) )
     531           0 :               FD_LOG_ERR(( "fcntl(F_SETFD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     532           0 :           }
     533           0 :           if( snapshot_upload_enabled ) {
     534           0 :             if( FD_UNLIKELY( -1==fcntl( FD_SNAP_RO_FD( j ), F_SETFD, tile_uses_snap_rd_fd && retained ? 0 : FD_CLOEXEC ) ) )
     535           0 :               FD_LOG_ERR(( "fcntl(F_SETFD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     536           0 :           }
     537           0 :         }
     538             : 
     539           0 :       int is_waker = !strcmp( tile->name, "waker" );
     540           0 :       int outer_entitled = is_waker || tile->waker_client_idx!=ULONG_MAX;
     541           0 :       if( FD_UNLIKELY( -1==fcntl( FD_WAKER_OUTER_FD, F_SETFD, outer_entitled ? 0 : FD_CLOEXEC ) ) ) FD_LOG_ERR(( "fcntl(F_SETFD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     542           0 :       for( ulong j=0UL; j<waker_client_cnt; j++ ) {
     543           0 :         int inner_entitled = is_waker || tile->waker_client_idx==j;
     544           0 :         if( FD_UNLIKELY( -1==fcntl( FD_WAKER_INNER_FD( j ), F_SETFD, inner_entitled ? 0 : FD_CLOEXEC ) ) ) FD_LOG_ERR(( "fcntl(F_SETFD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     545           0 :       }
     546             : 
     547           0 :       int is_mwaitx = !strcmp( tile->name, "mwaitx" );
     548           0 :       for( ulong j=0UL; j<config->topo.tile_cnt; j++ ) {
     549           0 :         if( FD_LIKELY( !config->topo.tiles[ j ].sleep_eventfd ) ) continue;
     550             : 
     551           0 :         int eventfd_entitled = is_mwaitx || tile->id==j;
     552           0 :         if( FD_UNLIKELY( -1==fcntl( FD_SLEEP_EVENTFD( j ), F_SETFD, eventfd_entitled ? 0 : FD_CLOEXEC ) ) ) FD_LOG_ERR(( "fcntl(F_SETFD) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     553           0 :       }
     554             : 
     555           0 :       int pipefd[ 2 ];
     556           0 :       if( FD_UNLIKELY( pipe2( pipefd, O_CLOEXEC ) ) ) FD_LOG_ERR(( "pipe2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     557           0 :       fds[ child_cnt ] = (struct pollfd){ .fd = pipefd[ 0 ], .events = 0 };
     558             : 
     559           0 :       int floating_priority = ( any_floats && !strcmp( tile->name, "waker" ) ) ? -19 : save_priority;
     560           0 :       child_pids[ child_cnt ] = execve_tile( config->name, tile, float_cpu_set, cpuless_cpu_set, floating_priority, config_memfd, pipefd[ 1 ], &spawn_cg );
     561           0 :       child_idxs[ child_cnt ] = i;
     562           0 :       if( FD_UNLIKELY( close( pipefd[ 1 ] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     563           0 :       strncpy( child_names[ child_cnt ], tile->name, 32 );
     564           0 :       child_cnt++;
     565           0 :     }
     566           0 :   }
     567             : 
     568           0 :   leave_isolation_cgroup( &spawn_cg );
     569             : 
     570             :   /* Obtain the actual grandchild PID from the pipe */
     571           0 :   for( ulong i=0UL; i<child_cnt; i++ ) {
     572           0 :     if( FD_UNLIKELY( actual_pids[ i ]!=ULONG_MAX ) ) continue;
     573           0 :     FD_TEST( 8UL==read( fds[ i ].fd, &actual_pids[ i ], 8UL ) );
     574           0 :   }
     575             : 
     576           0 :   if( FD_UNLIKELY( -1==setpriority( PRIO_PROCESS, 0, save_priority ) ) ) FD_LOG_ERR(( "setpriority() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     577           0 :   if( FD_UNLIKELY( fd_cpuset_setaffinity( 0, floating_cpu_set ) ) )
     578           0 :     FD_LOG_ERR(( "fd_cpuset_setaffinity failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     579             : 
     580           0 :   for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
     581           0 :     fd_topo_tile_t const * tile = &config->topo.tiles[ i ];
     582           0 :     if( strcmp( tile->name, "sign" ) || tile->sign.failover_junk_fd==-1 ) continue;
     583           0 :     if( FD_UNLIKELY( close( tile->sign.failover_junk_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     584           0 :   }
     585             : 
     586           0 :   if( FD_UNLIKELY( close( config_memfd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     587           0 :   if( need_xdp ) {
     588           0 :     for( uint i=0U; i<xdp_fds_cnt; i++ ) {
     589           0 :       if( FD_UNLIKELY( close( xdp_fds[i].xsk_map_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     590           0 :       if( FD_UNLIKELY( close( xdp_fds[i].prog_link_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     591           0 :     }
     592           0 :   }
     593             : 
     594           0 :     if( FD_UNLIKELY( -1==close( FD_ACCDB_FD_RW ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     595           0 :     if( FD_UNLIKELY( -1==close( FD_ACCDB_FD_RO ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     596           0 :     if( FD_UNLIKELY( -1==close( FD_TXNCACHE_FD ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     597           0 :     if( FD_LIKELY( has_store ) ) {
     598           0 :       if( FD_UNLIKELY( -1==close( FD_STORE_FD_RW ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     599           0 :       if( FD_UNLIKELY( -1==close( FD_STORE_FD_RO ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     600           0 :     }
     601           0 :     if( FD_UNLIKELY( -1==close( FD_EPOCH_CREDITS_FD ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     602           0 :     if( FD_UNLIKELY( -1==close( FD_COST_TRACKER_FD ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     603           0 :     if( FD_UNLIKELY( -1==close( FD_VOTE_STAKES_FD ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     604           0 :     if( FD_UNLIKELY( -1==close( FD_STAKE_DELEGATIONS_FD ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     605           0 :     for( ulong j=0UL; j<snap_max; j++ ) {
     606           0 :       if( FD_UNLIKELY( -1==close( FD_SNAP_FD( j ) ) ) )     FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     607           0 :       if( snapshot_dio_enabled )
     608           0 :         if( FD_UNLIKELY( -1==close( FD_SNAP_DIO_FD( j ) ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     609           0 :       if( snapshot_upload_enabled )
     610           0 :         if( FD_UNLIKELY( -1==close( FD_SNAP_RO_FD( j ) ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     611           0 :     }
     612             : 
     613           0 :   if( FD_UNLIKELY( -1==close( FD_WAKER_OUTER_FD ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     614           0 :   for( ulong j=0UL; j<waker_client_cnt; j++ ) {
     615           0 :     if( FD_UNLIKELY( -1==close( FD_WAKER_INNER_FD( j ) ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     616           0 :   }
     617           0 :   for( ulong j=0UL; j<config->topo.tile_cnt; j++ ) {
     618           0 :     if( FD_LIKELY( !config->topo.tiles[ j ].sleep_eventfd ) ) continue;
     619           0 :     if( FD_UNLIKELY( -1==close( FD_SLEEP_EVENTFD( j ) ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     620           0 :   }
     621             : 
     622           0 :   int allow_fds[ 4+FD_TOPO_MAX_TILES ];
     623           0 :   ulong allow_fds_cnt = 0;
     624           0 :   allow_fds[ allow_fds_cnt++ ] = 2; /* stderr */
     625           0 :   if( FD_LIKELY( fd_log_private_logfile_fd()!=-1 ) )
     626           0 :     allow_fds[ allow_fds_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
     627           0 :   allow_fds[ allow_fds_cnt++ ] = args->pipefd[ 1 ]; /* write end of main pipe */
     628           0 :   for( ulong i=0UL; i<child_cnt; i++ )
     629           0 :     allow_fds[ allow_fds_cnt++ ] = fds[ i ].fd; /* read end of child pipes */
     630           0 :   if( need_iavf ) {
     631           0 :     for( ulong i=0UL; i<iavf_fds.member_cnt; i++ ) {
     632           0 :       if( FD_UNLIKELY( -1==close( iavf_fds.container_fd[ i ] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     633           0 :       if( FD_UNLIKELY( -1==close( iavf_fds.group_fd    [ i ] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     634           0 :       if( FD_UNLIKELY( -1==close( iavf_fds.device_fd   [ i ] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     635           0 :     }
     636           0 :   }
     637           0 :   if( need_mlx5 ) {
     638           0 :     if( FD_UNLIKELY( -1==close( mlx5_fds.cmd_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     639           0 :     if( FD_UNLIKELY( -1==close( mlx5_fds.async_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     640           0 :     for( ulong i=0UL; i<mlx5_fds.rx_comp_channel_fd_cnt; i++ ) {
     641           0 :       if( FD_UNLIKELY( -1==close( mlx5_fds.rx_comp_channel_fd[ i ] ) ) )
     642           0 :         FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     643           0 :     }
     644           0 :   }
     645             : 
     646           0 :   struct sock_filter seccomp_filter[ 128UL ];
     647           0 :   unsigned int instr_cnt;
     648             :   #if defined(__aarch64__) || defined(__riscv)
     649             :   populate_sock_filter_policy_pidns_no_poll( 128UL, seccomp_filter, (uint)fd_log_private_logfile_fd() );
     650             :   instr_cnt = sock_filter_policy_pidns_no_poll_instr_cnt;
     651             :   #else
     652           0 :   populate_sock_filter_policy_pidns( 128UL, seccomp_filter, (uint)fd_log_private_logfile_fd() );
     653           0 :   instr_cnt = sock_filter_policy_pidns_instr_cnt;
     654           0 :   #endif
     655             : 
     656           0 :   if( FD_LIKELY( config->development.sandbox ) ) {
     657           0 :     fd_sandbox_enter( config->uid,
     658           0 :                       config->gid,
     659           0 :                       0,
     660           0 :                       0,
     661           0 :                       0,
     662           0 :                       0,
     663           0 :                       0,
     664           0 :                       1UL+child_cnt, /* RLIMIT_NOFILE needs to be set to the nfds argument of poll() */
     665           0 :                       0UL,
     666           0 :                       0UL,
     667           0 :                       0UL,
     668           0 :                       allow_fds_cnt,
     669           0 :                       allow_fds,
     670           0 :                       instr_cnt,
     671           0 :                       seccomp_filter );
     672           0 :   } else {
     673           0 :     fd_sandbox_switch_uid_gid( config->uid, config->gid );
     674           0 :   }
     675             : 
     676             :   /* Reap child process PIDs so they don't show up in `ps` etc.  All of
     677             :      these children should have exited immediately after clone(2)'ing
     678             :      another child with a huge page based stack. */
     679           0 :   for( ulong i=0UL; i<child_cnt; i++ ) {
     680           0 :     int wstatus;
     681           0 :     int exited_pid = wait4( child_pids[ i ], &wstatus, (int)__WALL, NULL );
     682           0 :     if( FD_UNLIKELY( -1==exited_pid ) ) {
     683           0 :       FD_LOG_ERR(( "pidns wait4() failed (%i-%s) %lu %hu", errno, fd_io_strerror( errno ), i, fds[i].revents ));
     684           0 :     } else if( FD_UNLIKELY( child_pids[ i ]!=exited_pid ) ) {
     685           0 :       FD_LOG_ERR(( "pidns wait4() returned unexpected pid %d %d", child_pids[ i ], exited_pid ));
     686           0 :     } else if( FD_UNLIKELY( !WIFEXITED( wstatus ) ) ) {
     687           0 :       FD_LOG_ERR_NOEXIT(( "tile %lu (%s) exited while booting with signal %d (%s)", i, child_names[ i ], WTERMSIG( wstatus ), fd_io_strsignal( WTERMSIG( wstatus ) ) ));
     688           0 :       fd_sys_util_exit_group( WTERMSIG( wstatus ) ? WTERMSIG( wstatus ) : 1 );
     689           0 :     }
     690           0 :     if( FD_UNLIKELY( WEXITSTATUS( wstatus ) ) ) {
     691           0 :       FD_LOG_ERR_NOEXIT(( "tile %lu (%s) exited while booting with code %d", i, child_names[ i ], WEXITSTATUS( wstatus ) ));
     692           0 :       fd_sys_util_exit_group( WEXITSTATUS( wstatus ) ? WEXITSTATUS( wstatus ) : 1 );
     693           0 :     }
     694           0 :   }
     695             : 
     696           0 :   fds[ child_cnt ] = (struct pollfd){ .fd = args->pipefd[ 1 ], .events = 0 };
     697           0 :   strncpy( child_names[ child_cnt ], "parent", 32UL );
     698           0 :   child_idxs[ child_cnt ] = ULONG_MAX;
     699             : 
     700             :   /* We are now the init process of the pid namespace.  If the init
     701             :      process dies, all children are terminated.  If any child dies, we
     702             :      terminate the init process, which will cause the kernel to
     703             :      terminate all other children bringing all of our processes down as
     704             :      a group.  The parent process will also die if this process dies,
     705             :      due to getting SIGHUP on the pipe. */
     706           0 :   while( 1 ) {
     707           0 :     if( FD_UNLIKELY( -1==poll( fds, 1UL+child_cnt, (int)-1 ) ) ) FD_LOG_ERR(( "poll() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     708             : 
     709             :     /* Parent process died, probably SIGINT, exit gracefully. */
     710           0 :     if( FD_UNLIKELY( fds[ child_cnt ].revents ) ) fd_sys_util_exit_group( 0 );
     711             : 
     712             :     /* Child process died, reap it to figure out exit code. */
     713           0 :     int wstatus;
     714           0 :     int exited_pid = wait4( -1, &wstatus, (int)__WALL | (int)WNOHANG, NULL );
     715           0 :     if( FD_UNLIKELY( -1==exited_pid ) ) {
     716           0 :       FD_LOG_ERR(( "pidns wait4() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     717           0 :     } else if( FD_UNLIKELY( !exited_pid ) ) {
     718             :       /* Spurious wakeup, no child actually dead yet. */
     719           0 :       continue;
     720           0 :     }
     721             : 
     722             :     /* Now find the tile corresponding to that PID */
     723           0 :     FD_TEST( exited_pid>0 );
     724           0 :     int found = 0;
     725           0 :     for( ulong i=0UL; i<child_cnt; i++ ) {
     726           0 :       if( FD_LIKELY( actual_pids[ i ]!=(ulong)exited_pid ) ) continue;
     727             : 
     728           0 :       found = 1;
     729           0 :       fds[ i ].fd = -1; /* Don't poll on this tile anymore */
     730             : 
     731           0 :       char * tile_name = child_names[ i ];
     732           0 :       ulong  tile_idx = child_idxs[ i ];
     733           0 :       ulong  tile_id = config->topo.tiles[ tile_idx ].kind_id;
     734             : 
     735           0 :       if( FD_UNLIKELY( !WIFEXITED( wstatus ) ) ) {
     736           0 :         FD_LOG_ERR_NOEXIT(( "tile %s%s:%lu%s exited with signal %d %s(%s)%s", fd_log_style_bold(), tile_name, tile_id, fd_log_style_normal(), WTERMSIG( wstatus ), fd_log_style_dim(), fd_io_strsignal( WTERMSIG( wstatus ) ), fd_log_style_normal() ));
     737           0 :         fd_sys_util_exit_group( WTERMSIG( wstatus ) ? WTERMSIG( wstatus ) : 1 );
     738           0 :       } else {
     739           0 :         int exit_code = WEXITSTATUS( wstatus );
     740           0 :         if( FD_LIKELY( !exit_code && tile_idx!=ULONG_MAX && config->topo.tiles[ tile_idx ].allow_shutdown ) ) {
     741           0 :           found = 1;
     742           0 :           FD_LOG_INFO(( "tile %s:%lu exited gracefully with code %d", tile_name, tile_id, exit_code ));
     743           0 :         } else {
     744           0 :           FD_LOG_ERR_NOEXIT(( "tile %s%s:%lu%s exited with code %d", fd_log_style_bold(), tile_name, tile_id, fd_log_style_normal(), exit_code ));
     745           0 :           fd_sys_util_exit_group( exit_code ? exit_code : 1 );
     746           0 :         }
     747           0 :       }
     748           0 :     }
     749             : 
     750           0 :     if( FD_UNLIKELY( !found ) ) FD_LOG_ERR(( "wait4() returned unexpected pid %d", exited_pid ));
     751           0 :   }
     752             : 
     753           0 :   return 0;
     754           0 : }
     755             : 
     756             : int
     757             : clone_firedancer( config_t const * config,
     758             :                   int              close_fd,
     759           0 :                   int *            out_pipe ) {
     760             :   /* This pipe is here just so that the child process knows when the
     761             :      parent has died (it will get a HUP). */
     762           0 :   int pipefd[2];
     763           0 :   if( FD_UNLIKELY( pipe2( pipefd, O_CLOEXEC | O_NONBLOCK ) ) ) FD_LOG_ERR(( "pipe2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     764             : 
     765             :   /* clone into a pid namespace */
     766           0 :   int flags = config->development.sandbox ? CLONE_NEWPID : 0;
     767           0 :   struct pidns_clone_args args = { .config = config, .closefd = close_fd, .pipefd = pipefd, };
     768             : 
     769           0 :   void * stack = create_clone_stack();
     770             : 
     771           0 :   int pid_namespace = clone( main_pid_namespace, (uchar *)stack + FD_TILE_PRIVATE_STACK_SZ, flags, &args );
     772           0 :   if( FD_UNLIKELY( pid_namespace<0 ) ) FD_LOG_ERR(( "clone() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     773           0 :   for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
     774           0 :     fd_topo_tile_t const * tile = &config->topo.tiles[ i ];
     775           0 :     if( strcmp( tile->name, "sign" ) || tile->sign.failover_junk_fd==-1 ) continue;
     776           0 :     if( FD_UNLIKELY( close( tile->sign.failover_junk_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     777           0 :   }
     778             : 
     779           0 :   if( FD_UNLIKELY( close( pipefd[ 1 ] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     780             : 
     781           0 :   *out_pipe = pipefd[ 0 ];
     782           0 :   return pid_namespace;
     783           0 : }
     784             : 
     785             : static void
     786             : workspace_path( config_t const *       config,
     787             :                 fd_topo_wksp_t const * wksp,
     788           0 :                 char                   out[ PATH_MAX ] ) {
     789           0 :   char const * mount_path;
     790           0 :   switch( wksp->page_sz ) {
     791           0 :     case FD_SHMEM_HUGE_PAGE_SZ:
     792           0 :       mount_path = config->hugetlbfs.huge_page_mount_path;
     793           0 :       break;
     794           0 :     case FD_SHMEM_GIGANTIC_PAGE_SZ:
     795           0 :       mount_path = config->hugetlbfs.gigantic_page_mount_path;
     796           0 :       break;
     797           0 :     case FD_SHMEM_NORMAL_PAGE_SZ:
     798           0 :       mount_path = config->hugetlbfs.normal_page_mount_path;
     799           0 :       break;
     800           0 :     default:
     801           0 :       FD_LOG_ERR(( "invalid page size %lu", wksp->page_sz ));
     802           0 :   }
     803             : 
     804           0 :   FD_TEST( fd_cstr_printf_check( out, PATH_MAX, NULL, "%s/%s_%s.wksp", mount_path, config->name, wksp->name ) );
     805           0 : }
     806             : 
     807             : static void
     808             : warn_unknown_files( config_t const * config,
     809           0 :                     ulong            mount_type ) {
     810           0 :   char const * mount_path;
     811           0 :   switch( mount_type ) {
     812           0 :     case 0UL:
     813           0 :       mount_path = config->hugetlbfs.huge_page_mount_path;
     814           0 :       break;
     815           0 :     case 1UL:
     816           0 :       mount_path = config->hugetlbfs.gigantic_page_mount_path;
     817           0 :       break;
     818           0 :     default:
     819           0 :       FD_LOG_ERR(( "invalid mount type %lu", mount_type ));
     820           0 :   }
     821             : 
     822             :   /* Check if there are any files in mount_path */
     823           0 :   DIR * dir = opendir( mount_path );
     824           0 :   if( FD_UNLIKELY( !dir ) ) {
     825           0 :     if( FD_UNLIKELY( errno!=ENOENT ) ) FD_LOG_ERR(( "error opening `%s` (%i-%s)", mount_path, errno, fd_io_strerror( errno ) ));
     826           0 :     return;
     827           0 :   }
     828             : 
     829           0 :   struct dirent * entry;
     830           0 :   for(;;) {
     831           0 :     errno = 0;
     832           0 :     entry = readdir( dir );
     833           0 :     if( FD_UNLIKELY( !entry ) ) break;
     834           0 :     if( FD_UNLIKELY( !strcmp( entry->d_name, ".") || !strcmp( entry->d_name, ".." ) ) ) continue;
     835             : 
     836           0 :     char entry_path[ PATH_MAX ];
     837           0 :     FD_TEST( fd_cstr_printf_check( entry_path, PATH_MAX, NULL, "%s/%s", mount_path, entry->d_name ));
     838             : 
     839           0 :     int known_file = 0;
     840           0 :     for( ulong i=0UL; i<config->topo.wksp_cnt; i++ ) {
     841           0 :       fd_topo_wksp_t const * wksp = &config->topo.workspaces[ i ];
     842             : 
     843           0 :       char expected_path[ PATH_MAX ];
     844           0 :       workspace_path( config, wksp, expected_path );
     845             : 
     846           0 :       if( !strcmp( entry_path, expected_path ) ) {
     847           0 :         known_file = 1;
     848           0 :         break;
     849           0 :       }
     850           0 :     }
     851             : 
     852           0 :     if( mount_type==0UL ) {
     853           0 :       for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
     854           0 :         fd_topo_tile_t const * tile = &config->topo.tiles [ i ];
     855             : 
     856           0 :         char expected_path[ PATH_MAX ];
     857           0 :         FD_TEST( fd_cstr_printf_check( expected_path, PATH_MAX, NULL, "%s/%s_stack_%s%lu", config->hugetlbfs.huge_page_mount_path, config->name, tile->name, tile->kind_id ) );
     858             : 
     859           0 :         if( !strcmp( entry_path, expected_path ) ) {
     860           0 :           known_file = 1;
     861           0 :           break;
     862           0 :         }
     863           0 :       }
     864           0 :     }
     865             : 
     866           0 :     if( FD_UNLIKELY( !known_file ) ) FD_LOG_WARNING(( "unknown file `%s` found in `%s`", entry->d_name, mount_path ));
     867           0 :   }
     868             : 
     869           0 :   if( FD_UNLIKELY( errno ) ) FD_LOG_ERR(( "error reading dir `%s` (%i-%s)", mount_path, errno, fd_io_strerror( errno ) ));
     870           0 :   if( FD_UNLIKELY( closedir( dir ) ) ) FD_LOG_ERR(( "error closing `%s` (%i-%s)", mount_path, errno, fd_io_strerror( errno ) ));
     871           0 : }
     872             : 
     873             : void
     874           0 : initialize_workspaces( config_t * config ) {
     875             :   /* Switch to non-root uid/gid for workspace creation.  Permissions
     876             :      checks are still done as the current user. */
     877           0 :   uint gid = getgid();
     878           0 :   uint uid = getuid();
     879           0 :   if( FD_LIKELY( gid!=config->gid && -1==setegid( config->gid ) ) )
     880           0 :     FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     881           0 :   if( FD_LIKELY( uid!=config->uid && -1==seteuid( config->uid ) ) )
     882           0 :     FD_LOG_ERR(( "seteuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     883             : 
     884           0 :   for( ulong i=0UL; i<config->topo.wksp_cnt; i++ ) {
     885           0 :     fd_topo_wksp_t * wksp = &config->topo.workspaces[ i ];
     886             : 
     887           0 :     char path[ PATH_MAX ];
     888           0 :     workspace_path( config, wksp, path );
     889             : 
     890           0 :     struct stat st;
     891           0 :     int result = stat( path, &st );
     892             : 
     893           0 :     int update_existing;
     894           0 :     if( FD_UNLIKELY( !result && config->is_live_cluster && !config->is_dev ) ) {
     895           0 :       if( FD_UNLIKELY( -1==unlink( path ) && errno!=ENOENT ) ) FD_LOG_ERR(( "unlink() failed when trying to create workspace `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     896           0 :       update_existing = 0;
     897           0 :     } else if( FD_UNLIKELY( !result ) ) {
     898             :       /* Creating all of the workspaces is very expensive because the
     899             :          kernel has to zero out all of the pages.  There can be tens or
     900             :          hundreds of gigabytes of zeroing to do.
     901             : 
     902             :          What would be really nice is if the kernel let us create huge
     903             :          pages without zeroing them, but it's not possible.  The
     904             :          ftruncate and fallocate calls do not support this type of
     905             :          resize with the hugetlbfs filesystem.
     906             : 
     907             :          Instead.. to prevent repeatedly doing this zeroing every time
     908             :          we start the validator, we have a small hack here to re-use the
     909             :          workspace files if they exist. */
     910           0 :       update_existing = 1;
     911           0 :     } else if( FD_LIKELY( result && errno==ENOENT ) ) {
     912           0 :       update_existing = 0;
     913           0 :     } else {
     914           0 :       FD_LOG_ERR(( "stat failed when trying to create workspace `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     915           0 :     }
     916             : 
     917           0 :     if( FD_UNLIKELY( -1==fd_topo_create_workspace( &config->topo, wksp, update_existing ) ) ) {
     918           0 :       FD_TEST( errno==ENOMEM );
     919             : 
     920           0 :       warn_unknown_files( config, wksp->page_sz!=FD_SHMEM_HUGE_PAGE_SZ );
     921             : 
     922           0 :       char path[ PATH_MAX ];
     923           0 :       workspace_path( config, wksp, path );
     924           0 :       FD_LOG_ERR(( "ENOMEM-Out of memory when trying to create workspace `%s` at `%s` "
     925           0 :                    "with %lu %s pages. Firedancer reserves enough memory for all of its workspaces "
     926           0 :                    "during the `hugetlbfs` configure step, so it is likely you have unknown files "
     927           0 :                    "left over in this directory which are consuming memory, or another program on "
     928           0 :                    "the system is using pages from the same mount.",
     929           0 :                    wksp->name, path, wksp->page_cnt, fd_shmem_page_sz_to_cstr( wksp->page_sz ) ));
     930           0 :     }
     931           0 :     fd_topo_join_workspace( &config->topo, wksp, FD_SHMEM_JOIN_MODE_READ_WRITE, 0 );
     932           0 :     fd_topo_wksp_new( &config->topo, wksp, CALLBACKS );
     933           0 :     fd_topo_leave_workspace( &config->topo, wksp );
     934           0 :   }
     935             : 
     936           0 :   if( FD_UNLIKELY( seteuid( uid ) ) ) FD_LOG_ERR(( "seteuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     937           0 :   if( FD_UNLIKELY( setegid( gid ) ) ) FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     938           0 : }
     939             : 
     940             : void
     941           0 : initialize_stacks( config_t const * config ) {
     942             : # if FD_HAS_MSAN
     943             :   /* MSan calls an external symbolizer using fork() on crashes, which is
     944             :      incompatible with Firedancer's MAP_SHARED stacks. */
     945             :   (void)config;
     946             :   return;
     947             : # endif
     948             : 
     949             :   /* Switch to non-root uid/gid for workspace creation.  Permissions
     950             :      checks are still done as the current user. */
     951           0 :   uint gid = getgid();
     952           0 :   uint uid = getuid();
     953           0 :   if( FD_LIKELY( gid!=config->gid && -1==setegid( config->gid ) ) )
     954           0 :     FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     955           0 :   if( FD_LIKELY( uid!=config->uid && -1==seteuid( config->uid ) ) )
     956           0 :     FD_LOG_ERR(( "seteuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     957             : 
     958           0 :   for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
     959           0 :     fd_topo_tile_t const * tile = &config->topo.tiles[ i ];
     960             : 
     961           0 :     char path[ PATH_MAX ];
     962           0 :     FD_TEST( fd_cstr_printf_check( path, PATH_MAX, NULL, "%s/%s_stack_%s%lu", config->hugetlbfs.huge_page_mount_path, config->name, tile->name, tile->kind_id ) );
     963             : 
     964           0 :     struct stat st;
     965           0 :     int result = stat( path, &st );
     966             : 
     967           0 :     int update_existing;
     968           0 :     if( FD_UNLIKELY( !result && config->is_live_cluster ) ) {
     969           0 :       if( FD_UNLIKELY( -1==unlink( path ) && errno!=ENOENT ) ) FD_LOG_ERR(( "unlink() failed when trying to create stack workspace `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     970           0 :       update_existing = 0;
     971           0 :     } else if( FD_UNLIKELY( !result ) ) {
     972             :       /* See above note about zeroing out pages. */
     973           0 :       update_existing = 1;
     974           0 :     } else if( FD_LIKELY( result && errno==ENOENT ) ) {
     975           0 :       update_existing = 0;
     976           0 :     } else {
     977           0 :       FD_LOG_ERR(( "stat failed when trying to create workspace `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     978           0 :     }
     979             : 
     980             :     /* TODO: Use a better CPU idx for the stack if tile is floating */
     981           0 :     ulong stack_cpu_idx = 0UL;
     982           0 :     if( FD_LIKELY( tile->cpu_idx<65535UL ) ) stack_cpu_idx = tile->cpu_idx;
     983             : 
     984           0 :     char name[ PATH_MAX ];
     985           0 :     FD_TEST( fd_cstr_printf_check( name, PATH_MAX, NULL, "%s_stack_%s%lu", config->name, tile->name, tile->kind_id ) );
     986             : 
     987           0 :     ulong sub_page_cnt[ 1 ] = { 6 };
     988           0 :     ulong sub_cpu_idx [ 1 ] = { stack_cpu_idx };
     989           0 :     int err;
     990           0 :     if( FD_UNLIKELY( update_existing ) ) {
     991           0 :       err = fd_shmem_update_multi( name, FD_SHMEM_HUGE_PAGE_SZ, 1, sub_page_cnt, sub_cpu_idx, S_IRUSR | S_IWUSR ); /* logs details */
     992           0 :     } else {
     993           0 :       err = fd_shmem_create_multi( name, FD_SHMEM_HUGE_PAGE_SZ, 1, sub_page_cnt, sub_cpu_idx, S_IRUSR | S_IWUSR ); /* logs details */
     994           0 :     }
     995           0 :     if( FD_UNLIKELY( err && errno==ENOMEM ) ) {
     996           0 :       warn_unknown_files( config, 0UL );
     997             : 
     998           0 :       char path[ PATH_MAX ];
     999           0 :       FD_TEST( fd_cstr_printf_check( path, PATH_MAX, NULL, "%s/%s_stack_%s%lu", config->hugetlbfs.huge_page_mount_path, config->name, tile->name, tile->kind_id ) );
    1000           0 :       FD_LOG_ERR(( "ENOMEM-Out of memory when trying to create huge page stack for tile `%s` at `%s`. "
    1001           0 :                    "Firedancer reserves enough memory for all of its stacks during the `hugetlbfs` configure "
    1002           0 :                    "step, so it is likely you have unknown files left over in this directory which are "
    1003           0 :                    "consuming memory, or another program on the system is using pages from the same mount.",
    1004           0 :                    tile->name, path ));
    1005           0 :     } else if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fd_shmem_create_multi failed" ));
    1006           0 :   }
    1007             : 
    1008           0 :   if( FD_UNLIKELY( seteuid( uid ) ) ) FD_LOG_ERR(( "seteuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1009           0 :   if( FD_UNLIKELY( setegid( gid ) ) ) FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1010           0 : }
    1011             : 
    1012             : void
    1013           0 : fdctl_check_configure( config_t const * config ) {
    1014           0 : # if defined(__x86_64__)
    1015           0 :   uint eax, ebx, ecx, edx;
    1016           0 :   if( FD_LIKELY( __get_cpuid_count( 7U, 0U, &eax, &ebx, &ecx, &edx ) ) ) {
    1017           0 :     int cpu_avx512 = ((ebx>>16)&1U) && ((ebx>>21)&1U); /* avx512f, avx512ifma */
    1018           0 :     int cpu_gfni   = (int)((ecx>>8)&1U);
    1019           0 :     if( FD_UNLIKELY( (cpu_avx512 && !FD_HAS_AVX512) || (cpu_gfni && !FD_HAS_GFNI) ) )
    1020           0 :       FD_LOG_WARNING(( "This CPU supports%s%s but the binary was built without them, so it runs slower code paths. "
    1021           0 :                        "Rebuild on this host with MACHINE=native, or with a MACHINE matching this CPU.",
    1022           0 :                        (cpu_avx512 && !FD_HAS_AVX512) ? " AVX-512" : "", (cpu_gfni && !FD_HAS_GFNI) ? " GFNI" : "" ));
    1023           0 :   }
    1024           0 : # endif
    1025             : 
    1026           0 :   configure_result_t check = fd_cfg_stage_hugetlbfs.check( config, FD_CONFIGURE_CHECK_TYPE_RUN );
    1027           0 :   if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) )
    1028           0 :     FD_LOG_ERR(( "Huge pages are not configured correctly: %s. You can run `%s configure init hugetlbfs` "
    1029           0 :                  "to create the mounts correctly. This must be done after every system restart before running "
    1030           0 :                  "Firedancer.", check.message, FD_BINARY_NAME ));
    1031             : 
    1032           0 :   if( FD_LIKELY( 0==strcmp( config->net.provider, "xdp" ) ) ) {
    1033           0 :     if( fd_cfg_stage_bonding.enabled( config ) ) {
    1034           0 :       check = fd_cfg_stage_bonding.check( config, FD_CONFIGURE_CHECK_TYPE_RUN );
    1035           0 :       if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) )
    1036           0 :         FD_LOG_ERR(( "Bonded network device is not configured correctly: %s. You can run `%s configure init bonding` "
    1037           0 :                     "to configure the bonding driver.", check.message, FD_BINARY_NAME ));
    1038           0 :     }
    1039             : 
    1040           0 :     check = fd_cfg_stage_ethtool_channels.check( config, FD_CONFIGURE_CHECK_TYPE_RUN );
    1041           0 :     if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) )
    1042           0 :       FD_LOG_ERR(( "Network %s. You can run `%s configure init ethtool-channels` to set the number of channels on the "
    1043           0 :                   "network device correctly.", check.message, FD_BINARY_NAME ));
    1044             : 
    1045           0 :     check = fd_cfg_stage_ethtool_offloads.check( config, FD_CONFIGURE_CHECK_TYPE_RUN );
    1046           0 :     if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) )
    1047           0 :       FD_LOG_ERR(( "Network %s. You can run `%s configure init ethtool-offloads` to disable features "
    1048           0 :                   "as required.", check.message, FD_BINARY_NAME ));
    1049             : 
    1050           0 :     check = fd_cfg_stage_ethtool_loopback.check( config, FD_CONFIGURE_CHECK_TYPE_RUN );
    1051           0 :     if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) )
    1052           0 :       FD_LOG_ERR(( "Network %s. You can run `%s configure init ethtool-loopback` to disable tx-udp-segmentation "
    1053           0 :                   "on the loopback device.", check.message, FD_BINARY_NAME ));
    1054           0 :   }
    1055             : 
    1056           0 :   if( FD_LIKELY( 0==strcmp( config->net.provider, "mlx5" ) ) ) {
    1057           0 :     check = fd_cfg_stage_mlx5.check( config, FD_CONFIGURE_CHECK_TYPE_RUN );
    1058           0 :     if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) )
    1059           0 :       FD_LOG_ERR(( "Network %s. You can run `%s configure init mlx5` to configure mlx5 network devices and load the ib_uverbs kernel module.",
    1060           0 :                    check.message, FD_BINARY_NAME ));
    1061           0 :   }
    1062             : 
    1063           0 :   if( FD_LIKELY( 0==strcmp( config->net.provider, "iavf" ) ) ) {
    1064           0 :     check = fd_cfg_stage_iavf.check( config, FD_CONFIGURE_CHECK_TYPE_RUN );
    1065           0 :     if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) )
    1066           0 :       FD_LOG_ERR(( "Network %s. You can run `%s configure init iavf` to configure the VFs and PF filters and load vfio-pci.",
    1067           0 :                    check.message, FD_BINARY_NAME ));
    1068           0 :   }
    1069             : 
    1070           0 :   check = fd_cfg_stage_sysctl.check( config, FD_CONFIGURE_CHECK_TYPE_RUN );
    1071           0 :   if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) )
    1072           0 :     FD_LOG_ERR(( "Kernel parameters are not configured correctly: %s. You can run `%s configure init sysctl` "
    1073           0 :                  "to set kernel parameters correctly.", check.message, FD_BINARY_NAME ));
    1074             : 
    1075             :   /* hyperthreads, nohz-full and rcu-nocbs are check-only stages: they
    1076             :      emit warnings themselves and always return OK, so there is no
    1077             :      result to act on (and no init to point the operator at). */
    1078           0 :   (void)fd_cfg_stage_hyperthreads.check( config, FD_CONFIGURE_CHECK_TYPE_RUN );
    1079           0 :   (void)fd_cfg_stage_nohz_full.check( config, FD_CONFIGURE_CHECK_TYPE_RUN );
    1080           0 :   (void)fd_cfg_stage_rcu_nocbs.check( config, FD_CONFIGURE_CHECK_TYPE_RUN );
    1081             : 
    1082             :   /* kworkers and cpuset are optional (but recommended) hardening: an
    1083             :      unconfigured stage only warns.  A PARTIALLY_CONFIGURED cpuset is
    1084             :      fatal however: the isolation cgroup exists but covers the wrong
    1085             :      CPUs (e.g. stale from a previous [layout.affinity]), and the tile
    1086             :      launcher would join it and then fail to pin with a confusing
    1087             :      EINVAL.  Fail up front with the fix instead. */
    1088           0 :   if( FD_UNLIKELY( fd_cfg_stage_kworkers.enabled( config ) ) ) {
    1089           0 :     check = fd_cfg_stage_kworkers.check( config, FD_CONFIGURE_CHECK_TYPE_RUN );
    1090           0 :     if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) )
    1091           0 :       FD_LOG_WARNING(( "Kernel workqueues may steal CPU time from Firedancer tiles: %s. For lower jitter, run "
    1092           0 :                        "`%s configure init kworkers`.", check.message, FD_BINARY_NAME ));
    1093           0 :   }
    1094             : 
    1095             :   /* Floating tiles need a scheduler domain to be balanced across their
    1096             :      CPUs; isolcpus= removes it, and every floater would stay on the
    1097             :      CPU it was forked on. */
    1098           0 :   FD_CPUSET_DECL( isolated );
    1099           0 :   if( FD_LIKELY( fd_cpu_isolation_read_list( "/sys/devices/system/cpu/isolated", isolated ) ) ) {
    1100           0 :     for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
    1101           0 :       fd_topo_tile_t const * tile = &config->topo.tiles[ i ];
    1102           0 :       if( FD_UNLIKELY( tile->floats && fd_cpuset_test( isolated, tile->cpu_idx ) ) )
    1103           0 :         FD_LOG_ERR(( "tile %s:%lu floats on CPU %lu, which the isolcpus= boot parameter removed from the kernel scheduler. "
    1104           0 :                      "Floating tiles need their CPUs scheduled: drop them from isolcpus= and isolate them with "
    1105           0 :                      "`%s configure init cpuset` instead.", tile->name, tile->kind_id, tile->cpu_idx, FD_BINARY_NAME ));
    1106           0 :     }
    1107           0 :   }
    1108             : 
    1109           0 :   FD_CPUSET_DECL( partition );
    1110           0 :   fd_cpu_isolation_partition_cpus( partition, &config->topo );
    1111           0 :   FD_CPUSET_DECL( housekeeping );
    1112           0 :   fd_cpuset_subtract( housekeeping, fd_cpu_isolation_online_cpus( housekeeping ), partition );
    1113           0 :   if( FD_UNLIKELY( fd_cpuset_cnt( partition ) && fd_cpuset_cnt( housekeeping )<2UL ) ) {
    1114           0 :     char list[ FD_CPU_ISOLATION_LIST_MAX ];
    1115           0 :     FD_LOG_WARNING(( "only %lu online CPU(s) (%s) remain outside Firedancer tile CPUs and their isolated hyperthread "
    1116           0 :                      "siblings for the kernel, interrupts and other processes. Consider reducing tile counts or "
    1117           0 :                      "adding to [layout.blocklist_cores].", fd_cpuset_cnt( housekeeping ),
    1118           0 :                      fd_cpu_isolation_format_list( list, sizeof(list), housekeeping ) ));
    1119           0 :   }
    1120             : 
    1121           0 :   if( FD_LIKELY( fd_cfg_stage_cpuset.enabled( config ) ) ) {
    1122           0 :     check = fd_cfg_stage_cpuset.check( config, FD_CONFIGURE_CHECK_TYPE_RUN );
    1123           0 :     if( FD_UNLIKELY( check.result==CONFIGURE_PARTIALLY_CONFIGURED ) )
    1124           0 :       FD_LOG_ERR(( "The CPU isolation cgroup exists but does not match the topology: %s. Tiles would fail to pin "
    1125           0 :                    "to their CPUs. Run `%s configure init cpuset` to fix it, or `%s configure fini cpuset` to "
    1126           0 :                    "remove it.", check.message, FD_BINARY_NAME, FD_BINARY_NAME ));
    1127           0 :     else if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) )
    1128           0 :       FD_LOG_WARNING(( "Firedancer tile CPUs are not isolated from other processes: %s. For lower jitter, run "
    1129           0 :                        "`%s configure init cpuset`.", check.message, FD_BINARY_NAME ));
    1130           0 :   }
    1131             : 
    1132           0 :   fd_cpu_isolation_check_cgroup( config );
    1133           0 : }
    1134             : 
    1135             : /* Each Sign tile gets its own one-shot pipe for receiving the ephemeral
    1136             :    generated junk key. */
    1137             : static void FD_FN_SENSITIVE
    1138           0 : failover_junk_prepare( config_t * config ) {
    1139           0 :   if( fd_topo_find_tile( &config->topo, "failov", 0UL )==ULONG_MAX ) return;
    1140           0 :   uchar const * key = fd_keyload_generate();
    1141           0 :   fd_memcpy( config->topo.failover_junk, key+32UL, 32UL );
    1142           0 :   for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
    1143           0 :     fd_topo_tile_t * tile = &config->topo.tiles[ i ];
    1144           0 :     if( strcmp( tile->name, "sign" ) ) continue;
    1145           0 :     FD_TEST( tile->sign.failover_enabled && tile->sign.failover_junk_fd==-1 );
    1146           0 :     int pipefd[ 2 ];
    1147           0 :     if( FD_UNLIKELY( pipe2( pipefd, O_CLOEXEC ) ) ) FD_LOG_ERR(( "pipe2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1148           0 :     for( ulong off=0UL; off<64UL; ) {
    1149           0 :       long n = write( pipefd[ 1 ], key+off, 64UL-off );
    1150           0 :       if( FD_UNLIKELY( n==-1L && errno==EINTR ) ) continue;
    1151           0 :       if( FD_UNLIKELY( n<=0L ) ) FD_LOG_ERR(( "could not deliver the failover junk key" ));
    1152           0 :       off += (ulong)n;
    1153           0 :     }
    1154           0 :     if( FD_UNLIKELY( close( pipefd[ 1 ] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1155           0 :     tile->sign.failover_junk_fd = pipefd[ 0 ];
    1156           0 :   }
    1157           0 :   fd_keyload_unload( key, 0 );
    1158           0 : }
    1159             : 
    1160             : void
    1161             : run_firedancer_init( config_t * config,
    1162             :                      int        init_workspaces,
    1163           0 :                      int        check_configure ) {
    1164           0 :   struct stat st;
    1165           0 :   int err = stat( config->paths.identity_key, &st );
    1166           0 :   if( FD_UNLIKELY( -1==err && errno==ENOENT ) ) FD_LOG_ERR(( "[paths.identity_key] key does not exist `%s`. You can generate an identity key at this path by running `%s keys new %s --config <toml>`", config->paths.identity_key, FD_BINARY_NAME, config->paths.identity_key ));
    1167           0 :   else if( FD_UNLIKELY( -1==err ) )             FD_LOG_ERR(( "could not stat [paths.identity_key] `%s` (%i-%s)", config->paths.identity_key, errno, fd_io_strerror( errno ) ));
    1168             : 
    1169             :   /* Failover needs a fresh in-memory junk key. */
    1170           0 :   failover_junk_prepare( config );
    1171             : 
    1172             :   /* FIXME: fdctl_check_configure unconditionally checks for network
    1173             :             stack prerequisites even if the command being run does not
    1174             :             require networking.  Hack around that here for now. */
    1175           0 :   if( check_configure ) fdctl_check_configure( config );
    1176           0 :   if( FD_LIKELY( init_workspaces ) ) initialize_workspaces( config );
    1177           0 :   initialize_stacks( config );
    1178           0 :   fd_bootinfo_write( config );
    1179           0 : }
    1180             : 
    1181             : void
    1182           0 : initialize_accdb_fd( config_t const * config ) {
    1183             :   /* O_TRUNC of a previous run's accounts.db frees all its extents
    1184             :      synchronously.  In development skip the truncate to keep reboots
    1185             :      fast. */
    1186           0 :   int oflags = O_RDWR|O_CREAT|O_NOATIME;
    1187           0 :   if( FD_LIKELY( !config->is_dev ) ) oflags |= O_TRUNC;
    1188             : 
    1189           0 :   int accounts_fd = open( config->paths.accounts, oflags, S_IRUSR|S_IWUSR );
    1190           0 :   if( FD_UNLIKELY( -1==accounts_fd ) ) FD_LOG_ERR(( "failed to open accounts.db (%i-%s)", errno, fd_io_strerror( errno ) ));
    1191           0 :   if( FD_UNLIKELY( -1==dup2( accounts_fd, FD_ACCDB_FD_RW ) ) ) FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1192           0 :   if( FD_UNLIKELY( -1==close( accounts_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1193             : 
    1194             :   /* Read-only fd for tiles (e.g. rpc) that consume accdb but must not
    1195             :      be able to mutate the on-disk file.  Reopen via /proc/self/fd to
    1196             :      guarantee it refers to the same inode as the RW fd, avoiding any
    1197             :      race where the file at the path could be replaced between opens. */
    1198           0 :   char proc_path[ PATH_MAX ];
    1199           0 :   FD_TEST( fd_cstr_printf_check( proc_path, sizeof(proc_path), NULL, "/proc/self/fd/%d", FD_ACCDB_FD_RW ) );
    1200           0 :   int accounts_ro_fd = open( proc_path, O_RDONLY|O_NOATIME );
    1201           0 :   if( FD_UNLIKELY( -1==accounts_ro_fd ) ) FD_LOG_ERR(( "failed to open accounts.db read-only (%i-%s)", errno, fd_io_strerror( errno ) ));
    1202           0 :   if( FD_UNLIKELY( -1==dup2( accounts_ro_fd, FD_ACCDB_FD_RO ) ) ) FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1203           0 :   if( FD_UNLIKELY( -1==close( accounts_ro_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1204           0 : }
    1205             : 
    1206             : void
    1207           0 : initialize_epoch_credits_fd( config_t const * config ) {
    1208           0 :   char const * spill_path = config->paths.epoch_credits;
    1209           0 :   int spill_fd = open( spill_path, O_RDWR|O_CREAT|O_TRUNC|O_NOATIME, S_IRUSR|S_IWUSR );
    1210           0 :   if( FD_UNLIKELY( -1==spill_fd ) ) FD_LOG_ERR(( "failed to open %s (%i-%s)", spill_path, errno, fd_io_strerror( errno ) ));
    1211           0 :   if( FD_UNLIKELY( -1==unlink( spill_path ) ) ) FD_LOG_ERR(( "unlink(%s) failed (%i-%s)", spill_path, errno, fd_io_strerror( errno ) ));
    1212             : 
    1213           0 :   if( FD_LIKELY( spill_fd!=FD_EPOCH_CREDITS_FD ) ) {
    1214           0 :     if( FD_UNLIKELY( -1==dup2( spill_fd, FD_EPOCH_CREDITS_FD ) ) ) FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1215           0 :     if( FD_UNLIKELY( -1==close( spill_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1216           0 :   }
    1217           0 : }
    1218             : 
    1219             : void
    1220           0 : initialize_cost_tracker_fd( config_t const * config ) {
    1221           0 :   char const * spill_path = config->paths.cost_tracker;
    1222           0 :   int spill_fd = open( spill_path, O_RDWR|O_CREAT|O_TRUNC|O_NOATIME, S_IRUSR|S_IWUSR );
    1223           0 :   if( FD_UNLIKELY( -1==spill_fd ) ) FD_LOG_ERR(( "failed to open %s (%i-%s)", spill_path, errno, fd_io_strerror( errno ) ));
    1224           0 :   if( FD_UNLIKELY( -1==unlink( spill_path ) ) ) FD_LOG_ERR(( "unlink(%s) failed (%i-%s)", spill_path, errno, fd_io_strerror( errno ) ));
    1225             : 
    1226           0 :   if( FD_LIKELY( spill_fd!=FD_COST_TRACKER_FD ) ) {
    1227           0 :     if( FD_UNLIKELY( -1==dup2( spill_fd, FD_COST_TRACKER_FD ) ) ) FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1228           0 :     if( FD_UNLIKELY( -1==close( spill_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1229           0 :   }
    1230           0 : }
    1231             : 
    1232             : void
    1233           0 : initialize_vote_stakes_fd( config_t const * config ) {
    1234           0 :   char const * spill_path = config->paths.vote_stakes;
    1235           0 :   int spill_fd = open( spill_path, O_RDWR|O_CREAT|O_TRUNC|O_NOATIME, S_IRUSR|S_IWUSR );
    1236           0 :   if( FD_UNLIKELY( -1==spill_fd ) ) FD_LOG_ERR(( "failed to open %s (%i-%s)", spill_path, errno, fd_io_strerror( errno ) ));
    1237           0 :   if( FD_UNLIKELY( -1==unlink( spill_path ) ) ) FD_LOG_ERR(( "unlink(%s) failed (%i-%s)", spill_path, errno, fd_io_strerror( errno ) ));
    1238             : 
    1239           0 :   if( FD_LIKELY( spill_fd!=FD_VOTE_STAKES_FD ) ) {
    1240           0 :     if( FD_UNLIKELY( -1==dup2( spill_fd, FD_VOTE_STAKES_FD ) ) ) FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1241           0 :     if( FD_UNLIKELY( -1==close( spill_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1242           0 :   }
    1243           0 : }
    1244             : 
    1245             : void
    1246           0 : initialize_stake_delegations_fd( config_t const * config ) {
    1247           0 :   char const * spill_path = config->paths.stake_delegations;
    1248           0 :   int spill_fd = open( spill_path, O_RDWR|O_CREAT|O_TRUNC|O_NOATIME, S_IRUSR|S_IWUSR );
    1249           0 :   if( FD_UNLIKELY( -1==spill_fd ) ) FD_LOG_ERR(( "failed to open %s (%i-%s)", spill_path, errno, fd_io_strerror( errno ) ));
    1250           0 :   if( FD_UNLIKELY( -1==unlink( spill_path ) ) ) FD_LOG_ERR(( "unlink(%s) failed (%i-%s)", spill_path, errno, fd_io_strerror( errno ) ));
    1251             : 
    1252           0 :   if( FD_LIKELY( spill_fd!=FD_STAKE_DELEGATIONS_FD ) ) {
    1253           0 :     if( FD_UNLIKELY( -1==dup2( spill_fd, FD_STAKE_DELEGATIONS_FD ) ) ) FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1254           0 :     if( FD_UNLIKELY( -1==close( spill_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1255           0 :   }
    1256           0 : }
    1257             : 
    1258             : void
    1259           0 : initialize_txncache_fd( config_t const * config ) {
    1260           0 :   char const * spill_path = config->paths.txncache;
    1261           0 :   int spill_fd = open( spill_path, O_RDWR|O_CREAT|O_TRUNC|O_NOATIME|O_DIRECT, S_IRUSR|S_IWUSR );
    1262           0 :   if( FD_UNLIKELY( -1==spill_fd ) ) FD_LOG_ERR(( "failed to open %s (%i-%s)", spill_path, errno, fd_io_strerror( errno ) ));
    1263           0 :   if( FD_UNLIKELY( -1==unlink( spill_path ) ) ) FD_LOG_ERR(( "unlink(%s) failed (%i-%s)", spill_path, errno, fd_io_strerror( errno ) ));
    1264             : 
    1265           0 :   if( FD_LIKELY( spill_fd!=FD_TXNCACHE_FD ) ) {
    1266           0 :     if( FD_UNLIKELY( -1==dup2( spill_fd, FD_TXNCACHE_FD ) ) ) FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1267           0 :     if( FD_UNLIKELY( -1==close( spill_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1268           0 :   }
    1269           0 : }
    1270             : 
    1271             : void
    1272           0 : initialize_store_fds( config_t const * config ) {
    1273           0 :   fd_topo_t const * topo = &config->topo;
    1274           0 :   ulong store_obj_id = fd_pod_query_ulong( topo->props, "store", ULONG_MAX );
    1275           0 :   if( FD_UNLIKELY( store_obj_id==ULONG_MAX ) ) return;
    1276             : 
    1277           0 :   char const * path = fd_pod_queryf_cstr( topo->props, NULL, "obj.%lu.disk_path", store_obj_id );
    1278           0 :   ulong fec_max = fd_pod_queryf_ulong( topo->props, 0UL, "obj.%lu.fec_max", store_obj_id );
    1279           0 :   ulong fec_data_max = fd_pod_queryf_ulong( topo->props, 0UL, "obj.%lu.fec_data_max", store_obj_id );
    1280           0 :   ulong shred_storage_gib = fd_pod_queryf_ulong( topo->props, ULONG_MAX, "obj.%lu.shred_storage_gib", store_obj_id );
    1281           0 :   ulong payload_slot_sz = fd_store_payload_slot_sz( fec_data_max );
    1282           0 :   ulong wire_off;
    1283           0 :   if( FD_UNLIKELY( !path || !fec_max || !payload_slot_sz || shred_storage_gib>FD_SHREDB_MAX_SIZE_GIB ||
    1284           0 :                    __builtin_umull_overflow( fec_max, payload_slot_sz, &wire_off ) ) )
    1285           0 :     FD_LOG_ERR(( "invalid Store backing-file configuration" ));
    1286             : 
    1287           0 :   int store_fd = fd_store_file_create( path, wire_off, fd_shredb_max_shreds( shred_storage_gib ) );
    1288           0 :   if( FD_UNLIKELY( store_fd<0 ) )
    1289           0 :     FD_LOG_ERR(( "failed to create Store backing file `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
    1290           0 :   if( FD_LIKELY( store_fd!=FD_STORE_FD_RW ) ) {
    1291           0 :     if( FD_UNLIKELY( dup2( store_fd, FD_STORE_FD_RW )<0 ) ) FD_LOG_ERR(( "dup2(Store RW) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1292           0 :     if( FD_UNLIKELY( close( store_fd ) ) ) FD_LOG_ERR(( "close(Store RW source) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1293           0 :   }
    1294             : 
    1295             :   /* Reopen through procfs so the read-only descriptor is guaranteed to
    1296             :      name the same inode even if the configured path is replaced. */
    1297           0 :   char proc_path[ PATH_MAX ];
    1298           0 :   FD_TEST( fd_cstr_printf_check( proc_path, sizeof(proc_path), NULL, "/proc/self/fd/%d", FD_STORE_FD_RW ) );
    1299           0 :   int store_ro_fd = open( proc_path, O_RDONLY|O_NOATIME );
    1300           0 :   if( FD_UNLIKELY( store_ro_fd<0 ) )
    1301           0 :     FD_LOG_ERR(( "failed to open Store backing file read-only (%i-%s)", errno, fd_io_strerror( errno ) ));
    1302           0 :   if( FD_LIKELY( store_ro_fd!=FD_STORE_FD_RO ) ) {
    1303           0 :     if( FD_UNLIKELY( dup2( store_ro_fd, FD_STORE_FD_RO )<0 ) ) FD_LOG_ERR(( "dup2(Store RO) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1304           0 :     if( FD_UNLIKELY( close( store_ro_fd ) ) ) FD_LOG_ERR(( "close(Store RO source) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1305           0 :   }
    1306           0 : }
    1307             : 
    1308             : /* Snapshot production prep
    1309             :    (On startup, reconcile with files in snapshot dirs, and drop old
    1310             :    files.) */
    1311             : 
    1312             : static int
    1313           0 : matches_partial_snap_filename( char const * name ) {
    1314           0 :   if( FD_UNLIKELY( !strcmp( name, ".snapshot.tar.bz2-partial" ) ||
    1315           0 :                    !strcmp( name, ".incremental-snapshot.tar.bz2-partial" ) ) ) return 1;
    1316             : 
    1317           0 :   if( *name=='.' ) name++;
    1318           0 :   char const * p;
    1319           0 :   if( !strncmp( name, "snapshot-x", 10UL ) ) p = name+10UL;
    1320           0 :   else if( !strncmp( name, "snapshot", 8UL ) ) p = name+8UL;
    1321           0 :   else return 0;
    1322           0 :   if( *p<'0' || *p>'9' ) return 0;
    1323           0 :   while( *p>='0' && *p<='9' ) p++;
    1324           0 :   return 0==strcmp( p, ".partial" );
    1325           0 : }
    1326             : 
    1327             : static int
    1328             : snap_inode_desc( void const * _a,
    1329           0 :                  void const * _b ) {
    1330           0 :   fd_backup_inode_t const * a = (fd_backup_inode_t const *)_a;
    1331           0 :   fd_backup_inode_t const * b = (fd_backup_inode_t const *)_b;
    1332           0 :   ulong a_slot = a->incr_slot==ULONG_MAX ? a->full_slot : a->incr_slot;
    1333           0 :   ulong b_slot = b->incr_slot==ULONG_MAX ? b->full_slot : b->incr_slot;
    1334           0 :   if( FD_LIKELY( a_slot!=b_slot ) ) return a_slot>b_slot ? -1 : 1;
    1335           0 :   return 0;
    1336           0 : }
    1337             : 
    1338             : static void
    1339             : drop_old_snaps( int                 snap_dir_fd,
    1340             :                 char const *        snap_dir,
    1341             :                 fd_backup_inode_t * snaps,
    1342             :                 ulong *             snap_cnt_p,
    1343           0 :                 ulong               snap_max ) {
    1344           0 :   ulong snap_cnt = *snap_cnt_p;
    1345           0 :   if( snap_cnt<=snap_max ) return;
    1346             :   /* drop oldest */
    1347           0 :   qsort( snaps, snap_cnt, sizeof(fd_backup_inode_t), snap_inode_desc );
    1348           0 :   for( ulong i=snap_max; i<snap_cnt; i++ ) {
    1349           0 :     int err = unlinkat( snap_dir_fd, snaps[ i ].name, 0 );
    1350           0 :     if( FD_UNLIKELY( err && errno!=ENOENT ) ) {
    1351           0 :       FD_LOG_WARNING(( "unlinkat(%s/%s) failed (%i-%s)", snap_dir, snaps[ i ].name, errno, fd_io_strerror( errno ) ));
    1352           0 :     } else if( FD_LIKELY( !err ) ) {
    1353           0 :       FD_LOG_INFO(( "deleted old snapshot `%s/%s`", snap_dir, snaps[ i ].name ));
    1354           0 :     }
    1355           0 :   }
    1356           0 :   *snap_cnt_p = snap_max;
    1357           0 : }
    1358             : 
    1359             : static void
    1360             : snap_check_fd( char const * snap_dir,
    1361             :                char const * name,
    1362           0 :                int          snap_fd ) {
    1363           0 :   struct stat st;
    1364           0 :   if( FD_UNLIKELY( -1==fstat( snap_fd, &st ) ) )
    1365           0 :     FD_LOG_ERR(( "fstat(%s/%s) failed (%i-%s)", snap_dir, name, errno, fd_io_strerror( errno ) ));
    1366             : 
    1367           0 :   if( FD_UNLIKELY( !S_ISREG( st.st_mode ) ) )
    1368           0 :     FD_LOG_ERR(( "snapshot `%s/%s` is not a regular file", snap_dir, name ));
    1369           0 : }
    1370             : 
    1371             : static void
    1372             : snap_reperm_fd( char const * snap_dir,
    1373             :                 char const * name,
    1374             :                 int          snap_fd,
    1375             :                 uint         uid,
    1376           0 :                 uint         gid ) {
    1377           0 :   if( FD_UNLIKELY( -1==fchown( snap_fd, uid, gid ) ) )
    1378           0 :     FD_LOG_ERR(( "fchown(%s/%s) failed (%i-%s)", snap_dir, name, errno, fd_io_strerror( errno ) ));
    1379             : 
    1380           0 :   if( FD_UNLIKELY( -1==fchmod( snap_fd, S_IRUSR|S_IWUSR ) ) )
    1381           0 :     FD_LOG_ERR(( "fchmod(%s/%s) failed (%i-%s)", snap_dir, name, errno, fd_io_strerror( errno ) ));
    1382           0 : }
    1383             : 
    1384             : ulong
    1385           0 : initialize_snapshot_fds( config_t const * config ) {
    1386             : 
    1387           0 :   int download_enabled = fd_topo_find_tile( &config->topo, "snapct", 0UL )!=ULONG_MAX &&
    1388           0 :                          (config->firedancer.snapshots.sources.gossip.allow_any ||
    1389           0 :                           config->firedancer.snapshots.sources.gossip.allow_list_cnt ||
    1390           0 :                           config->firedancer.snapshots.sources.servers_cnt);
    1391           0 :   int upload_enabled = fd_topo_find_tile( &config->topo, "snapsv", 0UL )!=ULONG_MAX;
    1392           0 :   int dio_enabled    = fd_topo_find_tile( &config->topo, "snapzp", 0UL )!=ULONG_MAX;
    1393           0 :   fd_snap_pool_layout_t layout = fd_snap_pool_layout( config->firedancer.snapshots.max_full_snapshots_to_keep,
    1394           0 :                                                       config->firedancer.snapshots.max_incremental_snapshots_to_keep,
    1395           0 :                                                       fd_wfs_needs_incr( config->firedancer.snapshots.incremental_snapshots,
    1396           0 :                                                                          config->firedancer.consensus.wait_for_supermajority_at_slot,
    1397           0 :                                                                          !strcmp( config->firedancer.consensus.wait_for_supermajority_with_bank_hash, "" ),
    1398           0 :                                                                          (ulong)config->consensus.expected_shred_version ),
    1399           0 :                                                       download_enabled );
    1400           0 :   ulong snap_full_max     = layout.full_max;
    1401           0 :   ulong snap_incr_max     = layout.incr_max;
    1402           0 :   ulong retained_snap_max = layout.retained_max;
    1403           0 :   ulong snap_max          = layout.max;
    1404           0 :   if( FD_UNLIKELY( !snap_max ) ) return 0UL;
    1405             : 
    1406           0 :   char const * snap_dir = config->paths.snapshots;
    1407           0 :   int dir_fd = open( snap_dir, O_RDONLY|O_DIRECTORY|O_CLOEXEC );
    1408           0 :   if( FD_UNLIKELY( -1==dir_fd ) ) FD_LOG_ERR(( "open(%s) failed (%i-%s)", snap_dir, errno, fd_io_strerror( errno ) ));
    1409             : 
    1410             :   /* This is additionally validated in fd_config_validatef() */
    1411           0 :   FD_CHECK_ERR( snap_max<=FD_SNAP_MAX, "[snapshots.max_{full,incremental}_snapshots_to_keep] is set too high" );
    1412           0 :   fd_backup_inode_t snap_full[ FD_SNAP_MAX ];
    1413           0 :   fd_backup_inode_t snap_incr[ FD_SNAP_MAX ];
    1414           0 :   fd_backup_inode_t scratch[ 2 ] = {
    1415           0 :     { .full_slot=ULONG_MAX, .incr_slot=ULONG_MAX },
    1416           0 :     { .full_slot=ULONG_MAX, .incr_slot=ULONG_MAX }
    1417           0 :   };
    1418           0 :   ulong             snap_full_cnt = 0UL;
    1419           0 :   ulong             snap_incr_cnt = 0UL;
    1420           0 :   FD_STATIC_ASSERT( sizeof(snap_full)+sizeof(snap_incr) < 2UL<<20, "stack overflow" );
    1421             : 
    1422             :   /* First, reconcile with the existing snapshots */
    1423           0 :   int dir_fd2 = dup( dir_fd );
    1424           0 :   if( FD_UNLIKELY( -1==dir_fd2 ) ) FD_LOG_ERR(( "dup(%s) failed (%i-%s)", snap_dir, errno, fd_io_strerror( errno ) ));
    1425           0 :   DIR * dir = fdopendir( dir_fd2 );
    1426           0 :   if( FD_UNLIKELY( !dir ) ) FD_LOG_ERR(( "fdopendir(%s) failed (%i-%s)", snap_dir, errno, fd_io_strerror( errno ) ));
    1427           0 :   struct dirent * entry;
    1428           0 :   for(;;) {
    1429           0 :     errno = 0;
    1430           0 :     entry = readdir( dir );
    1431           0 :     if( FD_UNLIKELY( !entry ) ) break;
    1432           0 :     if( FD_UNLIKELY( !strcmp( entry->d_name, ".") || !strcmp( entry->d_name, ".." ) ) ) continue;
    1433             : 
    1434             :     /* delete partial files */
    1435           0 :     if( FD_UNLIKELY( matches_partial_snap_filename( entry->d_name ) ) ) {
    1436           0 :       if( FD_UNLIKELY( -1==unlinkat( dir_fd, entry->d_name, 0 ) && errno!=ENOENT ) )
    1437           0 :         FD_LOG_ERR(( "unlinkat(%s/%s) failed (%i-%s)", snap_dir, entry->d_name, errno, fd_io_strerror( errno ) ));
    1438           0 :       continue;
    1439           0 :     }
    1440             : 
    1441             :     /* decode file name */
    1442           0 :     int is_zstd;
    1443           0 :     ulong entry_full_slot, entry_incremental_slot;
    1444           0 :     uchar decoded_hash[ FD_HASH_FOOTPRINT ];
    1445           0 :     if( FD_UNLIKELY( -1==fd_ssarchive_parse_filename( entry->d_name, &entry_full_slot, &entry_incremental_slot, decoded_hash, &is_zstd ) ) ) continue;
    1446           0 :     if( FD_UNLIKELY( strlen( entry->d_name )>=FD_SNAP_NAME_MAX ) ) continue;
    1447           0 :     fd_backup_inode_t inode = (fd_backup_inode_t){
    1448           0 :       .full_slot = entry_full_slot,
    1449           0 :       .incr_slot = entry_incremental_slot
    1450           0 :     };
    1451           0 :     fd_cstr_ncpy( inode.name, entry->d_name, FD_SNAP_NAME_MAX );
    1452             : 
    1453             :     /* register snapshot (and lazily delete snaps if there are too many)
    1454             :        the lazy drop must free a slot for the append below, even if
    1455             :        retention is configured at the hard capacity */
    1456           0 :     if( entry_incremental_slot==ULONG_MAX ) {
    1457           0 :       if( FD_UNLIKELY( snap_full_cnt>=FD_SNAP_MAX ) ) {
    1458           0 :         drop_old_snaps( dir_fd, snap_dir, snap_full, &snap_full_cnt, fd_ulong_min( snap_full_max, FD_SNAP_MAX-1UL ) );
    1459           0 :       }
    1460           0 :       snap_full[ snap_full_cnt++ ] = inode;
    1461           0 :     } else {
    1462           0 :       if( FD_UNLIKELY( snap_incr_cnt>=FD_SNAP_MAX ) ) {
    1463           0 :         drop_old_snaps( dir_fd, snap_dir, snap_incr, &snap_incr_cnt, fd_ulong_min( snap_incr_max, FD_SNAP_MAX-1UL ) );
    1464           0 :       }
    1465           0 :       snap_incr[ snap_incr_cnt++ ] = inode;
    1466           0 :     }
    1467           0 :   }
    1468           0 :   if( FD_UNLIKELY( errno ) ) FD_LOG_ERR(( "readdir(%s) failed (%i-%s)", snap_dir, errno, fd_io_strerror( errno ) ));
    1469           0 :   if( FD_UNLIKELY( -1==closedir( dir ) ) ) FD_LOG_ERR(( "closedir(%s) failed (%i-%s)", snap_dir, errno, fd_io_strerror( errno ) ));
    1470             : 
    1471             :   /* FIXME this should be smart enough to drop incremental snapshots
    1472             :            that no longer have a matching full snapshot. */
    1473           0 :   drop_old_snaps( dir_fd, snap_dir, snap_full, &snap_full_cnt, snap_full_max );
    1474           0 :   drop_old_snaps( dir_fd, snap_dir, snap_incr, &snap_incr_cnt, snap_incr_max );
    1475             : 
    1476             :   /* zero pad */
    1477           0 :   for( ulong i=snap_full_cnt; i<snap_full_max; i++ ) snap_full[ i ] = (fd_backup_inode_t){ .full_slot=ULONG_MAX, .incr_slot=ULONG_MAX };
    1478           0 :   for( ulong i=snap_incr_cnt; i<snap_incr_max; i++ ) snap_incr[ i ] = (fd_backup_inode_t){ .full_slot=ULONG_MAX, .incr_slot=ULONG_MAX };
    1479             : 
    1480           0 :   for( ulong i=0UL; i<snap_max; i++ ) {
    1481           0 :     fd_backup_inode_t * inode;
    1482           0 :     if(      i<snap_full_max     ) inode = &snap_full[ i ];
    1483           0 :     else if( i<retained_snap_max ) inode = &snap_incr[ i-snap_full_max ];
    1484           0 :     else                           inode = &scratch [ i-retained_snap_max ];
    1485             : 
    1486           0 :     int snap_fd;
    1487           0 :     if( FD_UNLIKELY( !inode->name[ 0 ] ) ) {
    1488           0 :       fd_snap_pool_partial_name( inode->name, (uint)i );
    1489           0 :       snap_fd = openat( dir_fd, inode->name, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC|O_NOFOLLOW, S_IRUSR|S_IWUSR );
    1490           0 :       if( FD_UNLIKELY( -1==snap_fd ) ) FD_LOG_ERR(( "openat(%s/%s) failed (%i-%s)", snap_dir, inode->name, errno, fd_io_strerror( errno ) ));
    1491           0 :     } else {
    1492           0 :       snap_fd = openat( dir_fd, inode->name, O_RDWR|O_CLOEXEC|O_NOFOLLOW );
    1493           0 :       if( FD_UNLIKELY( -1==snap_fd ) ) FD_LOG_ERR(( "openat(%s/%s) failed (%i-%s)", snap_dir, inode->name, errno, fd_io_strerror( errno ) ));
    1494           0 :       snap_check_fd( snap_dir, inode->name, snap_fd );
    1495           0 :     }
    1496             : 
    1497           0 :     snap_reperm_fd( snap_dir, inode->name, snap_fd, config->uid, config->gid );
    1498             : 
    1499           0 :     if( FD_UNLIKELY( -1==dup2( snap_fd, FD_SNAP_FD( i ) ) ) ) FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1500           0 :     if( FD_UNLIKELY( -1==close( snap_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1501             : 
    1502           0 :     if( dio_enabled ) {
    1503           0 :       int dio_fd = openat( dir_fd, inode->name, O_WRONLY|O_DIRECT );
    1504           0 :       if( FD_UNLIKELY( -1==dio_fd ) ) FD_LOG_ERR(( "openat(%s/%s, O_DIRECT) failed (%i-%s)", snap_dir, inode->name, errno, fd_io_strerror( errno ) ));
    1505           0 :       if( FD_UNLIKELY( -1==dup2( dio_fd, FD_SNAP_DIO_FD( i ) ) ) ) FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1506           0 :       if( FD_UNLIKELY( -1==close( dio_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1507           0 :     }
    1508             : 
    1509           0 :     if( upload_enabled ) {
    1510           0 :       int ro_fd = openat( dir_fd, inode->name, O_RDONLY|O_NOFOLLOW );
    1511           0 :       if( FD_UNLIKELY( -1==ro_fd ) ) FD_LOG_ERR(( "openat(%s/%s, O_RDONLY) failed (%i-%s)", snap_dir, inode->name, errno, fd_io_strerror( errno ) ));
    1512           0 :       if( FD_UNLIKELY( -1==dup2( ro_fd, FD_SNAP_RO_FD( i ) ) ) ) FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1513           0 :       if( FD_UNLIKELY( -1==close( ro_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1514           0 :     }
    1515           0 :   }
    1516             : 
    1517           0 :   if( FD_UNLIKELY( -1==close( dir_fd ) ) ) FD_LOG_ERR(( "close(%s) failed (%i-%s)", snap_dir, errno, fd_io_strerror( errno ) ));
    1518           0 :   return snap_max;
    1519           0 : }
    1520             : 
    1521             : /* The boot sequence is a little bit involved...
    1522             : 
    1523             :    A process tree is created that looks like,
    1524             : 
    1525             :    + main
    1526             :    +-- pidns
    1527             :        +-- tile 0
    1528             :        +-- tile 1
    1529             :        ...
    1530             : 
    1531             :    What we want is that if any process in the tree dies, all other
    1532             :    processes will also die.  This is done as follows,
    1533             : 
    1534             :     (a) pidns is the init process of a PID namespace, so if it dies the
    1535             :         kernel will terminate the child processes.
    1536             : 
    1537             :     (b) main is the parent of pidns, so it can issue a waitpid() on the
    1538             :         child PID, and when it completes terminate itself.
    1539             : 
    1540             :     (c) pidns is the parent of tiles, so it could issue a waitpid() of
    1541             :         -1 to wait for any of them to terminate, but how would it know
    1542             :         if main has died?
    1543             : 
    1544             :     (d) main creates a pipe, and passes the write end to pidns.  If main
    1545             :         dies, the pipe will be closed, and pidns will get a HUP on the
    1546             :         read end.  Then pidns creates a pipe per child and passes the
    1547             :         write end to the child.  If any of the children die, the pipe
    1548             :         will be closed, and pidns will get a HUP on the read end.
    1549             : 
    1550             :         Then pidns can call poll() on both the write end of the main
    1551             :         pipe and the read end of all the child pipes.  If any of them
    1552             :         raises SIGHUP, then pidns knows that the parent or a child has
    1553             :         died, and it can terminate itself, which due to (a) and (b)
    1554             :         will kill all other processes. */
    1555             : void
    1556             : run_firedancer( config_t * config,
    1557             :                 int        parent_pipefd,
    1558           0 :                 int        init_workspaces ) {
    1559             :   /* dump the topology we are using to the output log */
    1560           0 :   fd_topo_print_log( 0, &config->topo );
    1561             : 
    1562           0 :   run_firedancer_init( config, init_workspaces, 1 );
    1563             : 
    1564           0 :   if( FD_UNLIKELY( close( 0 ) ) ) FD_LOG_ERR(( "close(0) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1565           0 :   if( FD_UNLIKELY( fd_log_private_logfile_fd()!=1 && close( 1 ) ) ) FD_LOG_ERR(( "close(1) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
    1566             : 
    1567           0 :   int pipefd;
    1568           0 :   pid_namespace = clone_firedancer( config, parent_pipefd, &pipefd );
    1569             : 
    1570             :   /* Print the location of the logfile on SIGINT or SIGTERM, and also
    1571             :      kill the child.  They are connected by a pipe which the child is
    1572             :      polling so we don't strictly need to kill the child, but its helpful
    1573             :      to do that before printing the log location line, else it might
    1574             :      get interleaved due to timing windows in the shutdown. */
    1575           0 :   install_parent_signals();
    1576             : 
    1577           0 :   struct sock_filter seccomp_filter[ 128UL ];
    1578           0 :   populate_sock_filter_policy_main( 128UL, seccomp_filter, (uint)fd_log_private_logfile_fd(), (uint)pid_namespace );
    1579             : 
    1580           0 :   int allow_fds[ 4 ];
    1581           0 :   ulong allow_fds_cnt = 0;
    1582           0 :   allow_fds[ allow_fds_cnt++ ] = 2; /* stderr */
    1583           0 :   if( FD_LIKELY( fd_log_private_logfile_fd()!=-1 ) )
    1584           0 :     allow_fds[ allow_fds_cnt++ ] = fd_log_private_logfile_fd(); /* logfile, or maybe stdout */
    1585           0 :   allow_fds[ allow_fds_cnt++ ] = pipefd; /* read end of main pipe */
    1586           0 :   if( FD_UNLIKELY( parent_pipefd!=-1 ) )
    1587           0 :     allow_fds[ allow_fds_cnt++ ] = parent_pipefd; /* write end of parent pipe */
    1588             : 
    1589           0 :   if( FD_LIKELY( config->development.sandbox ) ) {
    1590           0 :     fd_sandbox_enter( config->uid,
    1591           0 :                       config->gid,
    1592           0 :                       0,
    1593           0 :                       0,
    1594           0 :                       0,
    1595           0 :                       1, /* Keep controlling terminal for main so it can receive Ctrl+C */
    1596           0 :                       0,
    1597           0 :                       0UL,
    1598           0 :                       0UL,
    1599           0 :                       0UL,
    1600           0 :                       0UL,
    1601           0 :                       allow_fds_cnt,
    1602           0 :                       allow_fds,
    1603           0 :                       sock_filter_policy_main_instr_cnt,
    1604           0 :                       seccomp_filter );
    1605           0 :   } else {
    1606           0 :     fd_sandbox_switch_uid_gid( config->uid, config->gid );
    1607           0 :   }
    1608             : 
    1609             :   /* the only clean way to exit is SIGINT or SIGTERM on this parent process,
    1610             :      so if wait4() completes, it must be an error */
    1611           0 :   int wstatus;
    1612           0 :   if( FD_UNLIKELY( -1==wait4( pid_namespace, &wstatus, (int)__WALL, NULL ) ) )
    1613           0 :     FD_LOG_ERR(( "main wait4() failed (%i-%s)\nLog at \"%s\"", errno, fd_io_strerror( errno ), fd_log_private_path ));
    1614             : 
    1615           0 :   if( FD_UNLIKELY( WIFSIGNALED( wstatus ) ) ) fd_sys_util_exit_group( WTERMSIG( wstatus ) ? WTERMSIG( wstatus ) : 1 );
    1616           0 :   else fd_sys_util_exit_group( WEXITSTATUS( wstatus ) ? WEXITSTATUS( wstatus ) : 1 );
    1617           0 : }
    1618             : 
    1619             : void
    1620             : run_cmd_fn( args_t *   args FD_PARAM_UNUSED,
    1621           0 :             config_t * config ) {
    1622           0 :   #define CHECK_PORT_NON_ZERO( field ) \
    1623           0 :     if( FD_UNLIKELY( config->field==0 ) ) { \
    1624           0 :       FD_LOG_ERR(( #field " is not set in your configuration file. Please set it to a non-zero value." )); \
    1625           0 :     }
    1626             : 
    1627           0 :   if( FD_UNLIKELY( !config->gossip.entrypoints_cnt && !config->development.bootstrap ) )
    1628           0 :     FD_LOG_ERR(( "No entrypoints specified in configuration file under [gossip.entrypoints], but "
    1629           0 :                  "at least one is needed to determine how to connect to the Solana cluster. If "
    1630           0 :                  "you want to start a new cluster in a development environment, use `fddev` instead "
    1631           0 :                  "of `fdctl`. If you want to use an existing genesis, set [development.bootstrap] "
    1632           0 :                  "to \"true\" in the configuration file." ));
    1633             : 
    1634           0 :   for( ulong i=0; i<config->gossip.entrypoints_cnt; i++ ) {
    1635           0 :     if( FD_UNLIKELY( !strcmp( config->gossip.entrypoints[ i ], "" ) ) )
    1636           0 :       FD_LOG_ERR(( "One of the entrypoints in your configuration file under [gossip.entrypoints] is "
    1637           0 :                    "empty. Please remove the empty entrypoint or set it correctly. "));
    1638           0 :   }
    1639             : 
    1640           0 :   CHECK_PORT_NON_ZERO( gossip.port );
    1641           0 :   CHECK_PORT_NON_ZERO( tiles.quic.quic_transaction_listen_port );
    1642           0 :   CHECK_PORT_NON_ZERO( tiles.quic.regular_transaction_listen_port );
    1643           0 :   CHECK_PORT_NON_ZERO( tiles.shred.shred_listen_port );
    1644           0 :   CHECK_PORT_NON_ZERO( tiles.metric.prometheus_listen_port );
    1645           0 :   CHECK_PORT_NON_ZERO( tiles.gui.gui_listen_port );
    1646             : 
    1647           0 :   #undef CHECK_PORT_NON_ZERO
    1648             : 
    1649           0 :   run_firedancer( config, -1, 1 );
    1650           0 : }
    1651             : 
    1652             : static void
    1653           0 : run1_args_help( fd_action_help_t * help ) {
    1654           0 :   fd_action_help_arg( help, "<tile-name>", NULL,   "Type of tile to run (e.g. `net`, `quic`, `replay`).  A tile is a single\n"
    1655           0 :                                                   "thread pinned to a CPU core that performs one part of the validator's work" );
    1656           0 :   fd_action_help_arg( help, "<kind-id>",   NULL,   "Zero-based index selecting which instance of that tile type to run\n"
    1657           0 :                                                   "when the topology has more than one" );
    1658           0 :   fd_action_help_arg( help, "--pipe-fd",   "<fd>", "Internal use: file descriptor over which the parent supervisor process\n"
    1659           0 :                                                   "communicates with this tile (default -1, standalone)" );
    1660           0 : }
    1661             : 
    1662             : action_t fd_action_run1 = {
    1663             :   .name        = "run1",
    1664             :   .args        = run1_cmd_args,
    1665             :   .fn          = run1_cmd_fn,
    1666             :   .perm        = NULL,
    1667             :   .description = "Start up a single Firedancer tile",
    1668             :   .detail      = "Runs one tile of the validator topology in the current process.  A tile is a\n"
    1669             :                  "single thread pinned to a CPU core that performs one part of the validator's\n"
    1670             :                  "work.  This is primarily an internal command used by `run` to spawn individual\n"
    1671             :                  "tiles; most operators should use `run` instead.",
    1672             :   .usage       = "run1 <tile-name> <kind-id> [OPTIONS]",
    1673             :   .args_help   = run1_args_help,
    1674             : };
    1675             : 
    1676             : action_t fd_action_run = {
    1677             :   .name           = "run",
    1678             :   .args           = NULL,
    1679             :   .fn             = run_cmd_fn,
    1680             :   .require_config = 1,
    1681             :   .perm           = run_cmd_perm,
    1682             :   .description    = "Start up a Firedancer validator",
    1683             :   .detail         = "Boots and runs the full validator described by the configuration file.  This\n"
    1684             :                     "is the main command operators use to run Firedancer.  It must be started with\n"
    1685             :                     "sufficient privileges to perform boot-time setup, after which it drops\n"
    1686             :                     "privileges to the configured user.",
    1687             :   .usage          = "run [OPTIONS]",
    1688             :   .permission_err = "insufficient permissions to execute command `%s`. It is recommended "
    1689             :                     "to start Firedancer as the root user, but you can also start it "
    1690             :                     "with the missing capabilities listed above. The program only needs "
    1691             :                     "to start with elevated permissions to do privileged operations at "
    1692             :                     "boot, and will immediately drop permissions and switch to the user "
    1693             :                     "specified in your configuration file once they are complete. Firedancer "
    1694             :                     "will not execute outside of the boot process as root, and will refuse "
    1695             :                     "to start if it cannot drop privileges. Firedancer needs to be started "
    1696             :                     "privileged to configure high performance networking with XDP.",
    1697             : };

Generated by: LCOV version 1.14