LCOV - code coverage report
Current view: top level - app/shared/commands/run - run.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 597 0.0 %
Date: 2025-03-20 12:08:36 Functions: 0 18 0.0 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : #include "run.h"
       3             : 
       4             : #include <sys/wait.h>
       5             : #include "generated/main_seccomp.h"
       6             : #if defined(__aarch64__)
       7             : #include "generated/pidns.arm64_seccomp.h"
       8             : #else
       9             : #include "generated/pidns_seccomp.h"
      10             : #endif
      11             : 
      12             : #include "../../fd_sys_util.h"
      13             : #include "../../fd_file_util.h"
      14             : #include "../../fd_net_util.h"
      15             : 
      16             : #include "../../../../disco/metrics/fd_metrics.h"
      17             : #include "../../../../disco/topo/fd_pod_format.h"
      18             : #include "../../../../disco/keyguard/fd_keyswitch.h"
      19             : #include "../../../../waltz/xdp/fd_xdp1.h"
      20             : #if FD_HAS_NO_AGAVE
      21             : #include "../../../../flamenco/runtime/fd_blockstore.h"
      22             : #include "../../../../flamenco/runtime/fd_txncache.h"
      23             : #include "../../../../flamenco/runtime/fd_runtime.h"
      24             : #endif
      25             : #include "../../../../funk/fd_funk.h"
      26             : #include "../../../../waltz/ip/fd_fib4.h"
      27             : #include "../../../../waltz/mib/fd_dbl_buf.h"
      28             : #undef FD_MAP_FLAG_BLOCKING
      29             : #include "../../../../waltz/neigh/fd_neigh4_map.h"
      30             : #include "../configure/configure.h"
      31             : 
      32             : #include <dirent.h>
      33             : #include <sched.h>
      34             : #include <stdio.h>
      35             : #include <stdlib.h> /* getenv */
      36             : #include <poll.h>
      37             : #include <unistd.h>
      38             : #include <errno.h>
      39             : #include <fcntl.h>
      40             : #include <sys/prctl.h>
      41             : #include <sys/resource.h>
      42             : #include <sys/mman.h>
      43             : #include <sys/stat.h>
      44             : #include <linux/capability.h>
      45             : #include <linux/unistd.h>
      46             : 
      47             : #include "../../../../util/tile/fd_tile_private.h"
      48             : 
      49           0 : #define NAME "run"
      50             : 
      51             : void
      52             : run_cmd_perm( args_t *         args,
      53             :               fd_cap_chk_t *   chk,
      54           0 :               config_t const * config ) {
      55           0 :   (void)args;
      56             : 
      57           0 :   ulong mlock_limit = fd_topo_mlock_max_tile( &config->topo );
      58             : 
      59           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)`" );
      60           0 :   fd_cap_chk_raise_rlimit( chk, NAME, RLIMIT_NICE,    40,          "call `setpriority(2)` to increase thread priorities" );
      61           0 :   fd_cap_chk_raise_rlimit( chk, NAME, RLIMIT_NOFILE,  CONFIGURE_NR_OPEN_FILES,
      62           0 :                                                                        "call `rlimit(2)  to increase `RLIMIT_NOFILE` to allow more open files for Agave" );
      63           0 :   fd_cap_chk_cap(          chk, NAME, CAP_NET_RAW,                 "call `socket(2)` to bind to a raw socket for use by XDP" );
      64           0 :   fd_cap_chk_cap(          chk, NAME, CAP_SYS_ADMIN,               "call `bpf(2)` with the `BPF_OBJ_GET` command to initialize XDP" );
      65           0 :   if( fd_sandbox_requires_cap_sys_admin( config->uid, config->gid ) )
      66           0 :     fd_cap_chk_cap(        chk, NAME, CAP_SYS_ADMIN,               "call `unshare(2)` with `CLONE_NEWUSER` to sandbox the process in a user namespace" );
      67           0 :   if( FD_LIKELY( getuid() != config->uid ) )
      68           0 :     fd_cap_chk_cap(        chk, NAME, CAP_SETUID,                  "call `setresuid(2)` to switch uid to the sandbox user" );
      69           0 :   if( FD_LIKELY( getgid()!=config->gid ) )
      70           0 :     fd_cap_chk_cap(        chk, NAME, CAP_SETGID,                  "call `setresgid(2)` to switch gid to the sandbox user" );
      71           0 :   if( FD_UNLIKELY( config->development.netns.enabled ) )
      72           0 :     fd_cap_chk_cap(        chk, NAME, CAP_SYS_ADMIN,               "call `setns(2)` to enter a network namespace" );
      73           0 :   if( FD_UNLIKELY( config->tiles.metric.prometheus_listen_port<1024 ) )
      74           0 :     fd_cap_chk_cap(        chk, NAME, CAP_NET_BIND_SERVICE,        "call `bind(2)` to bind to a privileged port for serving metrics" );
      75           0 :   if( FD_UNLIKELY( config->tiles.gui.gui_listen_port<1024 ) )
      76           0 :     fd_cap_chk_cap(        chk, NAME, CAP_NET_BIND_SERVICE,        "call `bind(2)` to bind to a privileged port for serving the GUI" );
      77           0 : }
      78             : 
      79             : struct pidns_clone_args {
      80             :   config_t const * config;
      81             :   int *            pipefd;
      82             :   int              closefd;
      83             : };
      84             : 
      85             : extern char fd_log_private_path[ 1024 ]; /* empty string on start */
      86             : 
      87             : static pid_t pid_namespace;
      88             : 
      89           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)
      90             : 
      91             : extern int * fd_log_private_shared_lock;
      92             : 
      93             : static void
      94           0 : parent_signal( int sig ) {
      95           0 :   if( FD_LIKELY( pid_namespace ) ) kill( pid_namespace, SIGKILL );
      96             : 
      97             :   /* A pretty gross hack.  For the local process, clear the lock so that
      98             :      we can always print the messages without waiting on another process,
      99             :      particularly if one of those processes might have just died.  The
     100             :      signal handler is re-entrant so this also avoids a deadlock since
     101             :      the log lock is not re-entrant. */
     102           0 :   int lock = 0;
     103           0 :   fd_log_private_shared_lock = &lock;
     104             : 
     105           0 :   if( -1!=fd_log_private_logfile_fd() ) FD_LOG_ERR_NOEXIT(( "Received signal %s\nLog at \"%s\"", fd_io_strsignal( sig ), fd_log_private_path ));
     106           0 :   else                                  FD_LOG_ERR_NOEXIT(( "Received signal %s",                fd_io_strsignal( sig ) ));
     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             : 
     159             : static int
     160             : execve_agave( int config_memfd,
     161           0 :                     int pipefd ) {
     162           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 ) ));
     163           0 :   pid_t child = fork();
     164           0 :   if( FD_UNLIKELY( -1==child ) ) FD_LOG_ERR(( "fork() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     165           0 :   if( FD_LIKELY( !child ) ) {
     166           0 :     char _current_executable_path[ PATH_MAX ];
     167           0 :     FD_TEST( -1!=fd_file_util_self_exe( _current_executable_path ) );
     168             : 
     169           0 :     char config_fd[ 32 ];
     170           0 :     FD_TEST( fd_cstr_printf_check( config_fd, sizeof( config_fd ), NULL, "%d", config_memfd ) );
     171           0 :     char * args[ 5 ] = { _current_executable_path, "run-agave", "--config-fd", config_fd, NULL };
     172             : 
     173           0 :     char * envp[] = { NULL, NULL };
     174           0 :     char * google_creds = getenv( "GOOGLE_APPLICATION_CREDENTIALS" );
     175           0 :     char provide_creds[ PATH_MAX+30UL ];
     176           0 :     if( FD_UNLIKELY( google_creds ) ) {
     177           0 :       FD_TEST( fd_cstr_printf_check( provide_creds, sizeof( provide_creds ), NULL, "GOOGLE_APPLICATION_CREDENTIALS=%s", google_creds ) );
     178           0 :       envp[ 0 ] = provide_creds;
     179           0 :     }
     180             : 
     181           0 :     if( FD_UNLIKELY( -1==execve( _current_executable_path, args, envp ) ) ) FD_LOG_ERR(( "execve() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     182           0 :   } else {
     183           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 ) ));
     184           0 :     return child;
     185           0 :   }
     186           0 :   return 0;
     187           0 : }
     188             : 
     189             : static pid_t
     190             : execve_tile( fd_topo_tile_t const * tile,
     191             :              fd_cpuset_t const *    floating_cpu_set,
     192             :              int                    floating_priority,
     193             :              int                    config_memfd,
     194           0 :              int                    pipefd ) {
     195           0 :   FD_CPUSET_DECL( cpu_set );
     196           0 :   if( FD_LIKELY( tile->cpu_idx!=ULONG_MAX ) ) {
     197             :     /* set the thread affinity before we clone the new process to ensure
     198             :         kernel first touch happens on the desired thread. */
     199           0 :     fd_cpuset_insert( cpu_set, tile->cpu_idx );
     200           0 :     if( FD_UNLIKELY( -1==setpriority( PRIO_PROCESS, 0, -19 ) ) ) FD_LOG_ERR(( "setpriority() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     201           0 :   } else {
     202           0 :     fd_memcpy( cpu_set, floating_cpu_set, fd_cpuset_footprint() );
     203           0 :     if( FD_UNLIKELY( -1==setpriority( PRIO_PROCESS, 0, floating_priority ) ) ) FD_LOG_ERR(( "setpriority() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     204           0 :   }
     205             : 
     206           0 :   if( FD_UNLIKELY( fd_cpuset_setaffinity( 0, cpu_set ) ) ) {
     207           0 :     if( FD_LIKELY( errno==EINVAL ) ) {
     208           0 :       FD_LOG_ERR(( "Unable to set the thread affinity for tile %s:%lu on cpu %lu. It is likely that the affinity "
     209           0 :                    "you have specified for this tile in [layout.affinity] of your configuration file contains a "
     210           0 :                    "CPU (%lu) which does not exist on this machine.",
     211           0 :                    tile->name, tile->kind_id, tile->cpu_idx, tile->cpu_idx ));
     212           0 :     } else {
     213           0 :       FD_LOG_ERR(( "sched_setaffinity failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     214           0 :     }
     215           0 :   }
     216             : 
     217             :   /* Clear CLOEXEC on the side of the pipe we want to pass to the tile. */
     218           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 ) ));
     219           0 :   pid_t child = fork();
     220           0 :   if( FD_UNLIKELY( -1==child ) ) FD_LOG_ERR(( "fork() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     221           0 :   if( FD_LIKELY( !child ) ) {
     222           0 :     char _current_executable_path[ PATH_MAX ];
     223           0 :     FD_TEST( -1!=fd_file_util_self_exe( _current_executable_path ) );
     224             : 
     225           0 :     char kind_id[ 32 ], config_fd[ 32 ], pipe_fd[ 32 ];
     226           0 :     FD_TEST( fd_cstr_printf_check( kind_id,   sizeof( kind_id ),   NULL, "%lu", tile->kind_id ) );
     227           0 :     FD_TEST( fd_cstr_printf_check( config_fd, sizeof( config_fd ), NULL, "%d",  config_memfd ) );
     228           0 :     FD_TEST( fd_cstr_printf_check( pipe_fd,   sizeof( pipe_fd ),   NULL, "%d",  pipefd ) );
     229           0 :     char const * args[ 9 ] = { _current_executable_path, "run1", tile->name, kind_id, "--pipe-fd", pipe_fd, "--config-fd", config_fd, NULL };
     230           0 :     if( FD_UNLIKELY( -1==execve( _current_executable_path, (char **)args, NULL ) ) ) FD_LOG_ERR(( "execve() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     231           0 :   } else {
     232           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 ) ));
     233           0 :     return child;
     234           0 :   }
     235           0 :   return 0;
     236           0 : }
     237             : 
     238             : extern int * fd_log_private_shared_lock;
     239             : 
     240             : int
     241           0 : main_pid_namespace( void * _args ) {
     242           0 :   struct pidns_clone_args * args = _args;
     243           0 :   if( FD_UNLIKELY( close( args->pipefd[ 0 ] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     244           0 :   if( FD_UNLIKELY( -1!=args->closefd ) ) {
     245           0 :     if( FD_UNLIKELY( close( args->closefd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     246           0 :   }
     247             : 
     248           0 :   config_t const * config = args->config;
     249             : 
     250           0 :   fd_log_thread_set( "pidns" );
     251           0 :   ulong pid = fd_sandbox_getpid(); /* Need to read /proc again.. we got a new PID from clone */
     252           0 :   fd_log_private_group_id_set( pid );
     253           0 :   fd_log_private_thread_id_set( pid );
     254           0 :   fd_log_private_stack_discover( FD_TILE_PRIVATE_STACK_SZ,
     255           0 :                                  &fd_tile_private_stack0, &fd_tile_private_stack1 );
     256             : 
     257           0 :   if( FD_UNLIKELY( !config->development.sandbox ) ) {
     258             :     /* If no sandbox, then there's no actual PID namespace so we can't
     259             :        wait() grandchildren for the exit code.  Do this as a workaround. */
     260           0 :     if( FD_UNLIKELY( -1==prctl( PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0 ) ) )
     261           0 :       FD_LOG_ERR(( "prctl(PR_SET_CHILD_SUBREAPER) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     262           0 :   }
     263             : 
     264             :   /* Save the current affinity, it will be restored after creating any child tiles */
     265           0 :   FD_CPUSET_DECL( floating_cpu_set );
     266           0 :   if( FD_UNLIKELY( fd_cpuset_getaffinity( 0, floating_cpu_set ) ) )
     267           0 :     FD_LOG_ERR(( "fd_cpuset_getaffinity failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     268             : 
     269           0 :   pid_t child_pids[ FD_TOPO_MAX_TILES+1 ];
     270           0 :   char  child_names[ FD_TOPO_MAX_TILES+1 ][ 32 ];
     271           0 :   struct pollfd fds[ FD_TOPO_MAX_TILES+2 ];
     272             : 
     273           0 :   int config_memfd = fdctl_cfg_to_memfd( config );
     274             : 
     275           0 :   if( FD_UNLIKELY( config->development.debug_tile ) ) {
     276           0 :     fd_log_private_shared_lock[1] = 1;
     277           0 :   }
     278             : 
     279           0 :   ulong child_cnt = 0UL;
     280           0 :   if( FD_LIKELY( !config->development.no_agave ) ) {
     281           0 :     int pipefd[ 2 ];
     282           0 :     if( FD_UNLIKELY( pipe2( pipefd, O_CLOEXEC ) ) ) FD_LOG_ERR(( "pipe2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     283           0 :     fds[ child_cnt ] = (struct pollfd){ .fd = pipefd[ 0 ], .events = 0 };
     284           0 :     child_pids[ child_cnt ] = execve_agave( config_memfd, pipefd[ 1 ] );
     285           0 :     if( FD_UNLIKELY( close( pipefd[ 1 ] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     286           0 :     strncpy( child_names[ child_cnt ], "agave", 32 );
     287           0 :     child_cnt++;
     288           0 :   }
     289             : 
     290           0 :   if( FD_UNLIKELY( config->development.netns.enabled ) ) {
     291           0 :     if( FD_UNLIKELY( -1==fd_net_util_netns_enter( config->tiles.net.interface, NULL ) ) )
     292           0 :       FD_LOG_ERR(( "failed to enter network namespace `%s` (%i-%s)", config->tiles.net.interface, errno, fd_io_strerror( errno ) ));
     293           0 :   }
     294             : 
     295           0 :   errno = 0;
     296           0 :   int save_priority = getpriority( PRIO_PROCESS, 0 );
     297           0 :   if( FD_UNLIKELY( -1==save_priority && errno ) ) FD_LOG_ERR(( "getpriority() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     298             : 
     299           0 :   int need_xdp = 0==strcmp( config->development.net.provider, "xdp" );
     300           0 :   fd_xdp_fds_t xdp_fds = {0};
     301           0 :   if( need_xdp ) {
     302           0 :     xdp_fds = fd_topo_install_xdp( &config->topo );
     303           0 :   }
     304             : 
     305           0 :   for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
     306           0 :     fd_topo_tile_t const * tile = &config->topo.tiles[ i ];
     307           0 :     if( FD_UNLIKELY( tile->is_agave ) ) continue;
     308             : 
     309           0 :     if( need_xdp ) {
     310           0 :       if( FD_UNLIKELY( strcmp( tile->name, "net" ) ) ) {
     311             :         /* close XDP related file descriptors */
     312           0 :         if( FD_UNLIKELY( -1==fcntl( xdp_fds.xsk_map_fd,   F_SETFD, FD_CLOEXEC ) ) ) FD_LOG_ERR(( "fcntl(F_SETFD,FD_CLOEXEC) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     313           0 :         if( FD_UNLIKELY( -1==fcntl( xdp_fds.prog_link_fd, F_SETFD, FD_CLOEXEC ) ) ) FD_LOG_ERR(( "fcntl(F_SETFD,FD_CLOEXEC) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     314           0 :       } else {
     315           0 :         if( FD_UNLIKELY( -1==fcntl( xdp_fds.xsk_map_fd,   F_SETFD, 0 ) ) ) FD_LOG_ERR(( "fcntl(F_SETFD,0) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     316           0 :         if( FD_UNLIKELY( -1==fcntl( xdp_fds.prog_link_fd, F_SETFD, 0 ) ) ) FD_LOG_ERR(( "fcntl(F_SETFD,0) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     317           0 :       }
     318           0 :     }
     319             : 
     320           0 :     int pipefd[ 2 ];
     321           0 :     if( FD_UNLIKELY( pipe2( pipefd, O_CLOEXEC ) ) ) FD_LOG_ERR(( "pipe2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     322           0 :     fds[ child_cnt ] = (struct pollfd){ .fd = pipefd[ 0 ], .events = 0 };
     323           0 :     child_pids[ child_cnt ] = execve_tile( tile, floating_cpu_set, save_priority, config_memfd, pipefd[ 1 ] );
     324           0 :     if( FD_UNLIKELY( close( pipefd[ 1 ] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     325           0 :     strncpy( child_names[ child_cnt ], tile->name, 32 );
     326           0 :     child_cnt++;
     327           0 :   }
     328             : 
     329           0 :   if( FD_UNLIKELY( -1==setpriority( PRIO_PROCESS, 0, save_priority ) ) ) FD_LOG_ERR(( "setpriority() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     330           0 :   if( FD_UNLIKELY( fd_cpuset_setaffinity( 0, floating_cpu_set ) ) )
     331           0 :     FD_LOG_ERR(( "fd_cpuset_setaffinity failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     332             : 
     333           0 :   if( FD_UNLIKELY( close( config_memfd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     334           0 :   if( FD_UNLIKELY( close( config->log.lock_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     335           0 :   if( need_xdp ) {
     336           0 :     if( FD_UNLIKELY( close( xdp_fds.xsk_map_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     337           0 :     if( FD_UNLIKELY( close( xdp_fds.prog_link_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     338           0 :   }
     339             : 
     340           0 :   int allow_fds[ 4+FD_TOPO_MAX_TILES ];
     341           0 :   ulong allow_fds_cnt = 0;
     342           0 :   allow_fds[ allow_fds_cnt++ ] = 2; /* stderr */
     343           0 :   if( FD_LIKELY( fd_log_private_logfile_fd()!=-1 ) )
     344           0 :     allow_fds[ allow_fds_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
     345           0 :   allow_fds[ allow_fds_cnt++ ] = args->pipefd[ 1 ]; /* write end of main pipe */
     346           0 :   for( ulong i=0; i<child_cnt; i++ )
     347           0 :     allow_fds[ allow_fds_cnt++ ] = fds[ i ].fd; /* read end of child pipes */
     348             : 
     349           0 :   struct sock_filter seccomp_filter[ 128UL ];
     350           0 :   populate_sock_filter_policy_pidns( 128UL, seccomp_filter, (uint)fd_log_private_logfile_fd() );
     351             : 
     352           0 :   if( FD_LIKELY( config->development.sandbox ) ) {
     353           0 :     fd_sandbox_enter( config->uid,
     354           0 :                       config->gid,
     355           0 :                       0,
     356           0 :                       0,
     357           0 :                       0,
     358           0 :                       0,
     359           0 :                       1UL+child_cnt, /* RLIMIT_NOFILE needs to be set to the nfds argument of poll() */
     360           0 :                       0UL,
     361           0 :                       0UL,
     362           0 :                       allow_fds_cnt,
     363           0 :                       allow_fds,
     364           0 :                       sock_filter_policy_pidns_instr_cnt,
     365           0 :                       seccomp_filter );
     366           0 :   } else {
     367           0 :     fd_sandbox_switch_uid_gid( config->uid, config->gid );
     368           0 :   }
     369             : 
     370             :   /* The supervsior process should not share the log lock, because a
     371             :      child process might die while holding it and we still need to
     372             :      reap and print errors. */
     373           0 :   int lock = 0;
     374           0 :   fd_log_private_shared_lock = &lock;
     375             : 
     376             :   /* Reap child process PIDs so they don't show up in `ps` etc.  All of
     377             :      these children should have exited immediately after clone(2)'ing
     378             :      another child with a huge page based stack. */
     379           0 :   for( ulong i=0; i<child_cnt; i++ ) {
     380           0 :     int wstatus;
     381           0 :     int exited_pid = wait4( child_pids[ i ], &wstatus, (int)__WALL, NULL );
     382           0 :     if( FD_UNLIKELY( -1==exited_pid ) ) {
     383           0 :       FD_LOG_ERR(( "pidns wait4() failed (%i-%s) %lu %hu", errno, fd_io_strerror( errno ), i, fds[i].revents ));
     384           0 :     } else if( FD_UNLIKELY( child_pids[ i ]!=exited_pid ) ) {
     385           0 :       FD_LOG_ERR(( "pidns wait4() returned unexpected pid %d %d", child_pids[ i ], exited_pid ));
     386           0 :     } else if( FD_UNLIKELY( !WIFEXITED( wstatus ) ) ) {
     387           0 :       FD_LOG_ERR_NOEXIT(( "tile %lu (%s) exited while booting with signal %d (%s)\n", i, child_names[ i ], WTERMSIG( wstatus ), fd_io_strsignal( WTERMSIG( wstatus ) ) ));
     388           0 :       fd_sys_util_exit_group( WTERMSIG( wstatus ) ? WTERMSIG( wstatus ) : 1 );
     389           0 :     }
     390           0 :     if( FD_UNLIKELY( WEXITSTATUS( wstatus ) ) ) {
     391           0 :       FD_LOG_ERR_NOEXIT(( "tile %lu (%s) exited while booting with code %d\n", i, child_names[ i ], WEXITSTATUS( wstatus ) ));
     392           0 :       fd_sys_util_exit_group( WEXITSTATUS( wstatus ) ? WEXITSTATUS( wstatus ) : 1 );
     393           0 :     }
     394           0 :   }
     395             : 
     396           0 :   fds[ child_cnt ] = (struct pollfd){ .fd = args->pipefd[ 1 ], .events = 0 };
     397             : 
     398             :   /* We are now the init process of the pid namespace.  If the init
     399             :      process dies, all children are terminated.  If any child dies, we
     400             :      terminate the init process, which will cause the kernel to
     401             :      terminate all other children bringing all of our processes down as
     402             :      a group.  The parent process will also die if this process dies,
     403             :      due to getting SIGHUP on the pipe. */
     404           0 :   while( 1 ) {
     405           0 :     if( FD_UNLIKELY( -1==poll( fds, 1+child_cnt, -1 ) ) ) FD_LOG_ERR(( "poll() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     406             : 
     407           0 :     for( ulong i=0UL; i<1UL+child_cnt; i++ ) {
     408           0 :       if( FD_UNLIKELY( fds[ i ].revents ) ) {
     409             :         /* Must have been POLLHUP, POLLERR and POLLNVAL are not possible. */
     410           0 :         if( FD_UNLIKELY( i==child_cnt ) ) {
     411             :           /* Parent process died, probably SIGINT, exit gracefully. */
     412           0 :           fd_sys_util_exit_group( 0 );
     413           0 :         }
     414             : 
     415           0 :         char * tile_name = child_names[ i ];
     416           0 :         ulong  tile_idx = 0UL;
     417           0 :         if( FD_LIKELY( i>0UL ) ) tile_idx = config->development.no_agave ? i : i-1UL;
     418           0 :         ulong  tile_id = config->topo.tiles[ tile_idx ].kind_id;
     419             : 
     420             :         /* Child process died, reap it to figure out exit code. */
     421           0 :         int wstatus;
     422           0 :         int exited_pid = wait4( -1, &wstatus, (int)__WALL | (int)WNOHANG, NULL );
     423           0 :         if( FD_UNLIKELY( -1==exited_pid ) ) {
     424           0 :           FD_LOG_ERR(( "pidns wait4() failed (%i-%s) %lu %hu", errno, fd_io_strerror( errno ), i, fds[ i ].revents ));
     425           0 :         } else if( FD_UNLIKELY( !exited_pid ) ) {
     426             :           /* Spurious wakeup, no child actually dead yet. */
     427           0 :           continue;
     428           0 :         }
     429             : 
     430           0 :         if( FD_UNLIKELY( !WIFEXITED( wstatus ) ) ) {
     431           0 :           FD_LOG_ERR_NOEXIT(( "tile %s:%lu exited with signal %d (%s)", tile_name, tile_id, WTERMSIG( wstatus ), fd_io_strsignal( WTERMSIG( wstatus ) ) ));
     432           0 :           fd_sys_util_exit_group( WTERMSIG( wstatus ) ? WTERMSIG( wstatus ) : 1 );
     433           0 :         } else {
     434           0 :           FD_LOG_ERR_NOEXIT(( "tile %s:%lu exited with code %d", tile_name, tile_id, WEXITSTATUS( wstatus ) ));
     435           0 :           fd_sys_util_exit_group( WEXITSTATUS( wstatus ) ? WEXITSTATUS( wstatus ) : 1 );
     436           0 :         }
     437           0 :       }
     438           0 :     }
     439           0 :   }
     440           0 :   return 0;
     441           0 : }
     442             : 
     443             : int
     444             : clone_firedancer( config_t const * config,
     445             :                   int              close_fd,
     446           0 :                   int *            out_pipe ) {
     447             :   /* This pipe is here just so that the child process knows when the
     448             :      parent has died (it will get a HUP). */
     449           0 :   int pipefd[2];
     450           0 :   if( FD_UNLIKELY( pipe2( pipefd, O_CLOEXEC | O_NONBLOCK ) ) ) FD_LOG_ERR(( "pipe2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     451             : 
     452             :   /* clone into a pid namespace */
     453           0 :   int flags = config->development.sandbox ? CLONE_NEWPID : 0;
     454           0 :   struct pidns_clone_args args = { .config = config, .closefd = close_fd, .pipefd = pipefd, };
     455             : 
     456           0 :   void * stack = create_clone_stack();
     457             : 
     458           0 :   int pid_namespace = clone( main_pid_namespace, (uchar *)stack + FD_TILE_PRIVATE_STACK_SZ, flags, &args );
     459           0 :   if( FD_UNLIKELY( pid_namespace<0 ) ) FD_LOG_ERR(( "clone() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     460             : 
     461           0 :   if( FD_UNLIKELY( close( pipefd[ 1 ] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     462             : 
     463           0 :   *out_pipe = pipefd[ 0 ];
     464           0 :   return pid_namespace;
     465           0 : }
     466             : 
     467             : static void
     468             : workspace_path( config_t const *       config,
     469             :                 fd_topo_wksp_t const * wksp,
     470           0 :                 char                   out[ PATH_MAX ] ) {
     471           0 :   char const * mount_path;
     472           0 :   switch( wksp->page_sz ) {
     473           0 :     case FD_SHMEM_HUGE_PAGE_SZ:
     474           0 :       mount_path = config->hugetlbfs.huge_page_mount_path;
     475           0 :       break;
     476           0 :     case FD_SHMEM_GIGANTIC_PAGE_SZ:
     477           0 :       mount_path = config->hugetlbfs.gigantic_page_mount_path;
     478           0 :       break;
     479           0 :     default:
     480           0 :       FD_LOG_ERR(( "invalid page size %lu", wksp->page_sz ));
     481           0 :   }
     482             : 
     483           0 :   FD_TEST( fd_cstr_printf_check( out, PATH_MAX, NULL, "%s/%s_%s.wksp", mount_path, config->name, wksp->name ) );
     484           0 : }
     485             : 
     486             : static void
     487             : warn_unknown_files( config_t const * config,
     488           0 :                     ulong            mount_type ) {
     489           0 :   char const * mount_path;
     490           0 :   switch( mount_type ) {
     491           0 :     case 0UL:
     492           0 :       mount_path = config->hugetlbfs.huge_page_mount_path;
     493           0 :       break;
     494           0 :     case 1UL:
     495           0 :       mount_path = config->hugetlbfs.gigantic_page_mount_path;
     496           0 :       break;
     497           0 :     default:
     498           0 :       FD_LOG_ERR(( "invalid mount type %lu", mount_type ));
     499           0 :   }
     500             : 
     501             :   /* Check if there are any files in mount_path */
     502           0 :   DIR * dir = opendir( mount_path );
     503           0 :   if( FD_UNLIKELY( !dir ) ) {
     504           0 :     if( FD_UNLIKELY( errno!=ENOENT ) ) FD_LOG_ERR(( "error opening `%s` (%i-%s)", mount_path, errno, fd_io_strerror( errno ) ));
     505           0 :     return;
     506           0 :   }
     507             : 
     508           0 :   struct dirent * entry;
     509           0 :   while(( FD_LIKELY( entry = readdir( dir ) ) )) {
     510           0 :     if( FD_UNLIKELY( !strcmp( entry->d_name, ".") || !strcmp( entry->d_name, ".." ) ) ) continue;
     511             : 
     512           0 :     char entry_path[ PATH_MAX ];
     513           0 :     FD_TEST( fd_cstr_printf_check( entry_path, PATH_MAX, NULL, "%s/%s", mount_path, entry->d_name ));
     514             : 
     515           0 :     int known_file = 0;
     516           0 :     for( ulong i=0UL; i<config->topo.wksp_cnt; i++ ) {
     517           0 :       fd_topo_wksp_t const * wksp = &config->topo.workspaces[ i ];
     518             : 
     519           0 :       char expected_path[ PATH_MAX ];
     520           0 :       workspace_path( config, wksp, expected_path );
     521             : 
     522           0 :       if( !strcmp( entry_path, expected_path ) ) {
     523           0 :         known_file = 1;
     524           0 :         break;
     525           0 :       }
     526           0 :     }
     527             : 
     528           0 :     if( mount_type==0UL ) {
     529           0 :       for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
     530           0 :         fd_topo_tile_t const * tile = &config->topo.tiles [ i ];
     531             : 
     532           0 :         char expected_path[ PATH_MAX ];
     533           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 ) );
     534             : 
     535           0 :         if( !strcmp( entry_path, expected_path ) ) {
     536           0 :           known_file = 1;
     537           0 :           break;
     538           0 :         }
     539           0 :       }
     540           0 :     }
     541             : 
     542           0 :     if( FD_UNLIKELY( !known_file ) ) FD_LOG_WARNING(( "unknown file `%s` found in `%s`", entry->d_name, mount_path ));
     543           0 :   }
     544             : 
     545           0 :   if( FD_UNLIKELY( closedir( dir ) ) ) FD_LOG_ERR(( "error closing `%s` (%i-%s)", mount_path, errno, fd_io_strerror( errno ) ));
     546           0 : }
     547             : 
     548             : static void
     549             : fdctl_obj_new( fd_topo_t const *     topo,
     550           0 :                fd_topo_obj_t const * obj ) {
     551           0 :   #define VAL(name) (__extension__({                                                               \
     552           0 :       ulong __x = fd_pod_queryf_ulong( topo->props, ULONG_MAX, "obj.%lu.%s", obj->id, name );      \
     553           0 :       if( FD_UNLIKELY( __x==ULONG_MAX ) ) FD_LOG_ERR(( "obj.%lu.%s was not set", obj->id, name )); \
     554           0 :       __x; }))
     555             : 
     556           0 :   void * laddr = fd_topo_obj_laddr( topo, obj->id );
     557             : 
     558           0 :   if( FD_UNLIKELY( !strcmp( obj->name, "tile" ) ) ) {
     559             :     /* No need to do anything, tiles don't have a new. */
     560           0 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "mcache" ) ) ) {
     561           0 :     FD_TEST( fd_mcache_new( laddr, VAL("depth"), 0UL, 0UL ) );
     562           0 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "dcache" ) ) ) {
     563           0 :     FD_TEST( fd_dcache_new( laddr, fd_dcache_req_data_sz( VAL("mtu"), VAL("depth"), VAL("burst"), 1 ), 0UL ) );
     564           0 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "cnc" ) ) ) {
     565           0 :     FD_TEST( fd_cnc_new( laddr, 0UL, 0, fd_tickcount() ) );
     566           0 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "fseq" ) ) ) {
     567           0 :     FD_TEST( fd_fseq_new( laddr, ULONG_MAX ) );
     568           0 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "metrics" ) ) ) {
     569           0 :     FD_TEST( fd_metrics_new( laddr, VAL("in_cnt"), VAL("cons_cnt") ) );
     570           0 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "opaque" ) ) ) {
     571           0 :     fd_memset( laddr, 0, VAL("footprint") );
     572           0 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "ulong" ) ) ) {
     573           0 :     *(ulong*)laddr = 0;
     574           0 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "dbl_buf" ) ) ) {
     575           0 :     FD_TEST( fd_dbl_buf_new( laddr, VAL("mtu"), 1UL ) );
     576           0 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "funk" ) ) ) {
     577           0 :     FD_TEST( fd_funk_new( laddr, VAL("wksp_tag"), VAL("seed"), VAL("txn_max"), VAL("rec_max") ) );
     578           0 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "neigh4_hmap" ) ) )  {
     579           0 :     FD_TEST( fd_neigh4_hmap_new( laddr, VAL("ele_max"), VAL("lock_cnt"), VAL("probe_max"), VAL("seed") ) );
     580           0 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "fib4" ) ) ) {
     581           0 :     FD_TEST( fd_fib4_new( laddr, VAL("route_max") ) );
     582           0 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "keyswitch" ) ) ) {
     583           0 :     FD_TEST( fd_keyswitch_new( laddr, FD_KEYSWITCH_STATE_UNLOCKED ) );
     584             : #if FD_HAS_NO_AGAVE
     585             :   } else if( FD_UNLIKELY( !strcmp( obj->name, "replay_pub" ) ) ) {
     586             :     FD_TEST( fd_runtime_public_new( laddr ) );
     587             :   } else if( FD_UNLIKELY( !strcmp( obj->name, "blockstore" ) ) ) {
     588             :     FD_TEST( fd_blockstore_new( laddr, VAL("wksp_tag"), VAL("seed"), VAL("shred_max"), VAL("block_max"), VAL("idx_max"), VAL("txn_max") ) );
     589             :   } else if( FD_UNLIKELY( !strcmp( obj->name, "txncache" ) ) ) {
     590             :     FD_TEST( fd_txncache_new( laddr, VAL("max_rooted_slots"), VAL("max_live_slots"), VAL("max_txn_per_slot"), FD_TXNCACHE_DEFAULT_MAX_CONSTIPATED_SLOTS ) );
     591             : #endif /* FD_HAS_NO_AGAVE */
     592           0 :   } else {
     593           0 :     FD_LOG_ERR(( "unknown object `%s`", obj->name ));
     594           0 :   }
     595           0 : #undef VAL
     596           0 : }
     597             : 
     598             : void
     599           0 : initialize_workspaces( config_t * config ) {
     600             :   /* Switch to non-root uid/gid for workspace creation.  Permissions
     601             :      checks are still done as the current user. */
     602           0 :   uint gid = getgid();
     603           0 :   uint uid = getuid();
     604           0 :   if( FD_LIKELY( gid!=config->gid && -1==setegid( config->gid ) ) )
     605           0 :     FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     606           0 :   if( FD_LIKELY( uid!=config->uid && -1==seteuid( config->uid ) ) )
     607           0 :     FD_LOG_ERR(( "seteuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     608             : 
     609           0 :   for( ulong i=0UL; i<config->topo.wksp_cnt; i++ ) {
     610           0 :     fd_topo_wksp_t * wksp = &config->topo.workspaces[ i ];
     611             : 
     612           0 :     char path[ PATH_MAX ];
     613           0 :     workspace_path( config, wksp, path );
     614             : 
     615           0 :     struct stat st;
     616           0 :     int result = stat( path, &st );
     617             : 
     618           0 :     int update_existing;
     619           0 :     if( FD_UNLIKELY( !result && config->is_live_cluster ) ) {
     620           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 ) ));
     621           0 :       update_existing = 0;
     622           0 :     } else if( FD_UNLIKELY( !result ) ) {
     623             :       /* Creating all of the workspaces is very expensive because the
     624             :          kernel has to zero out all of the pages.  There can be tens or
     625             :          hundreds of gigabytes of zeroing to do.
     626             : 
     627             :          What would be really nice is if the kernel let us create huge
     628             :          pages without zeroing them, but it's not possible.  The
     629             :          ftruncate and fallocate calls do not support this type of
     630             :          resize with the hugetlbfs filesystem.
     631             : 
     632             :          Instead.. to prevent repeatedly doing this zeroing every time
     633             :          we start the validator, we have a small hack here to re-use the
     634             :          workspace files if they exist. */
     635           0 :       update_existing = 1;
     636           0 :     } else if( FD_LIKELY( result && errno==ENOENT ) ) {
     637           0 :       update_existing = 0;
     638           0 :     } else {
     639           0 :       FD_LOG_ERR(( "stat failed when trying to create workspace `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     640           0 :     }
     641             : 
     642           0 :     if( FD_UNLIKELY( -1==fd_topo_create_workspace( &config->topo, wksp, update_existing ) ) ) {
     643           0 :       FD_TEST( errno==ENOMEM );
     644             : 
     645           0 :       warn_unknown_files( config, wksp->page_sz!=FD_SHMEM_HUGE_PAGE_SZ );
     646             : 
     647           0 :       char path[ PATH_MAX ];
     648           0 :       workspace_path( config, wksp, path );
     649           0 :       FD_LOG_ERR(( "ENOMEM-Out of memory when trying to create workspace `%s` at `%s` "
     650           0 :                    "with %lu %s pages. Firedancer reserves enough memory for all of its workspaces "
     651           0 :                    "during the `hugetlbfs` configure step, so it is likely you have unknown files "
     652           0 :                    "left over in this directory which are consuming memory, or another program on "
     653           0 :                    "the system is using pages from the same mount.",
     654           0 :                    wksp->name, path, wksp->page_cnt, fd_shmem_page_sz_to_cstr( wksp->page_sz ) ));
     655           0 :     }
     656           0 :     fd_topo_join_workspace( &config->topo, wksp, FD_SHMEM_JOIN_MODE_READ_WRITE );
     657           0 :     fd_topo_wksp_apply( &config->topo, wksp, fdctl_obj_new );
     658           0 :     fd_topo_leave_workspace( &config->topo, wksp );
     659           0 :   }
     660             : 
     661           0 :   if( FD_UNLIKELY( seteuid( uid ) ) ) FD_LOG_ERR(( "seteuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     662           0 :   if( FD_UNLIKELY( setegid( gid ) ) ) FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     663           0 : }
     664             : 
     665             : void
     666           0 : initialize_stacks( config_t const * config ) {
     667             :   /* Switch to non-root uid/gid for workspace creation.  Permissions
     668             :      checks are still done as the current user. */
     669           0 :   uint gid = getgid();
     670           0 :   uint uid = getuid();
     671           0 :   if( FD_LIKELY( gid!=config->gid && -1==setegid( config->gid ) ) )
     672           0 :     FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     673           0 :   if( FD_LIKELY( uid!=config->uid && -1==seteuid( config->uid ) ) )
     674           0 :     FD_LOG_ERR(( "seteuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     675             : 
     676           0 :   for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
     677           0 :     fd_topo_tile_t const * tile = &config->topo.tiles[ i ];
     678             : 
     679           0 :     char path[ PATH_MAX ];
     680           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 ) );
     681             : 
     682           0 :     int result = unlink( path );
     683           0 :     if( -1==result && errno!=ENOENT ) FD_LOG_ERR(( "unlink() failed when trying to create stack `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     684             : 
     685             :     /* TODO: Use a better CPU idx for the stack if tile is floating */
     686           0 :     ulong stack_cpu_idx = 0UL;
     687           0 :     if( FD_LIKELY( tile->cpu_idx<65535UL ) ) stack_cpu_idx = tile->cpu_idx;
     688             : 
     689           0 :     char name[ PATH_MAX ];
     690           0 :     FD_TEST( fd_cstr_printf_check( name, PATH_MAX, NULL, "%s_stack_%s%lu", config->name, tile->name, tile->kind_id ) );
     691             : 
     692           0 :     ulong sub_page_cnt[ 1 ] = { 6 };
     693           0 :     ulong sub_cpu_idx [ 1 ] = { stack_cpu_idx };
     694           0 :     int err = fd_shmem_create_multi( name, FD_SHMEM_HUGE_PAGE_SZ, 1, sub_page_cnt, sub_cpu_idx, S_IRUSR | S_IWUSR ); /* logs details */
     695           0 :     if( FD_UNLIKELY( err && errno==ENOMEM ) ) {
     696           0 :       warn_unknown_files( config, 0UL );
     697             : 
     698           0 :       char path[ PATH_MAX ];
     699           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 ) );
     700           0 :       FD_LOG_ERR(( "ENOMEM-Out of memory when trying to create huge page stack for tile `%s` at `%s`. "
     701           0 :                    "Firedancer reserves enough memory for all of its stacks during the `hugetlbfs` configure "
     702           0 :                    "step, so it is likely you have unknown files left over in this directory which are "
     703           0 :                    "consuming memory, or another program on the system is using pages from the same mount.",
     704           0 :                    tile->name, path ));
     705           0 :     } else if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fd_shmem_create_multi failed" ));
     706           0 :   }
     707             : 
     708           0 :   if( FD_UNLIKELY( seteuid( uid ) ) ) FD_LOG_ERR(( "seteuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     709           0 :   if( FD_UNLIKELY( setegid( gid ) ) ) FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     710           0 : }
     711             : 
     712             : extern configure_stage_t fd_cfg_stage_hugetlbfs;
     713             : extern configure_stage_t fd_cfg_stage_ethtool_channels;
     714             : extern configure_stage_t fd_cfg_stage_ethtool_gro;
     715             : extern configure_stage_t fd_cfg_stage_ethtool_loopback;
     716             : extern configure_stage_t fd_cfg_stage_sysctl;
     717             : extern configure_stage_t fd_cfg_stage_hyperthreads;
     718             : 
     719             : void
     720           0 : fdctl_check_configure( config_t const * config ) {
     721           0 :   configure_result_t check = fd_cfg_stage_hugetlbfs.check( config );
     722           0 :   if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) )
     723           0 :     FD_LOG_ERR(( "Huge pages are not configured correctly: %s. You can run `fdctl configure init hugetlbfs` "
     724           0 :                  "to create the mounts correctly. This must be done after every system restart before running "
     725           0 :                  "Firedancer.", check.message ));
     726             : 
     727           0 :   if( FD_LIKELY( !config->development.netns.enabled && 0==strcmp( config->development.net.provider, "xdp" ) ) ) {
     728           0 :     check = fd_cfg_stage_ethtool_channels.check( config );
     729           0 :     if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) )
     730           0 :       FD_LOG_ERR(( "Network %s. You can run `fdctl configure init ethtool-channels` to set the number of channels on the "
     731           0 :                   "network device correctly.", check.message ));
     732             : 
     733           0 :     check = fd_cfg_stage_ethtool_gro.check( config );
     734           0 :     if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) )
     735           0 :       FD_LOG_ERR(( "Network %s. You can run `fdctl configure init ethtool-gro` to disable generic-receive-offload "
     736           0 :                   "as required.", check.message ));
     737             : 
     738           0 :     check = fd_cfg_stage_ethtool_loopback.check( config );
     739           0 :     if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) )
     740           0 :       FD_LOG_ERR(( "Network %s. You can run `fdctl configure init ethtool-loopback` to disable tx-udp-segmentation "
     741           0 :                   "on the loopback device.", check.message ));
     742           0 :   }
     743             : 
     744           0 :   check = fd_cfg_stage_sysctl.check( config );
     745           0 :   if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) )
     746           0 :     FD_LOG_ERR(( "Kernel parameters are not configured correctly: %s. You can run `fdctl configure init sysctl` "
     747           0 :                  "to set kernel parameters correctly.", check.message ));
     748             : 
     749           0 :   check = fd_cfg_stage_hyperthreads.check( config );
     750           0 :   if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) )
     751           0 :     FD_LOG_ERR(( "Hyperthreading is not configured correctly: %s. You can run `fdctl configure init hyperthreads` "
     752           0 :                  "to configure hyperthreading correctly.", check.message ));
     753           0 : }
     754             : 
     755             : void
     756             : run_firedancer_init( config_t * config,
     757           0 :                      int        init_workspaces ) {
     758           0 :   struct stat st;
     759           0 :   int err = stat( config->consensus.identity_path, &st );
     760           0 :   if( FD_UNLIKELY( -1==err && errno==ENOENT ) ) FD_LOG_ERR(( "[consensus.identity_path] key does not exist `%s`. You can generate an identity key at this path by running `fdctl keys new identity --config <toml>`", config->consensus.identity_path ));
     761           0 :   else if( FD_UNLIKELY( -1==err ) )             FD_LOG_ERR(( "could not stat [consensus.identity_path] `%s` (%i-%s)", config->consensus.identity_path, errno, fd_io_strerror( errno ) ));
     762             : 
     763           0 :   for( ulong i=0UL; i<config->consensus.authorized_voter_paths_cnt; i++ ) {
     764           0 :     err = stat( config->consensus.authorized_voter_paths[ i ], &st );
     765           0 :     if( FD_UNLIKELY( -1==err && errno==ENOENT ) ) FD_LOG_ERR(( "[consensus.authorized_voter_paths] key does not exist `%s`", config->consensus.authorized_voter_paths[ i ] ));
     766           0 :     else if( FD_UNLIKELY( -1==err ) )             FD_LOG_ERR(( "could not stat [consensus.authorized_voter_paths] `%s` (%i-%s)", config->consensus.authorized_voter_paths[ i ], errno, fd_io_strerror( errno ) ));
     767           0 :   }
     768             : 
     769           0 :   fdctl_check_configure( config );
     770           0 :   if( FD_LIKELY( init_workspaces ) ) initialize_workspaces( config );
     771           0 :   initialize_stacks( config );
     772           0 : }
     773             : 
     774             : void
     775             : fdctl_setup_netns( config_t * config,
     776           0 :                    int        stay ) {
     777           0 :   if( !config->development.netns.enabled ) return;
     778             : 
     779           0 :   int original_netns_;
     780           0 :   int * original_netns = stay ? NULL : &original_netns_;
     781           0 :   if( FD_UNLIKELY( -1==fd_net_util_netns_enter( config->tiles.net.interface, original_netns ) ) )
     782           0 :     FD_LOG_ERR(( "failed to enter network namespace `%s` (%i-%s)", config->tiles.net.interface, errno, fd_io_strerror( errno ) ));
     783             : 
     784           0 :   if( 0==strcmp( config->development.net.provider, "xdp" ) ) {
     785           0 :     fd_cfg_stage_ethtool_channels.init( config );
     786           0 :     fd_cfg_stage_ethtool_gro     .init( config );
     787           0 :     fd_cfg_stage_ethtool_loopback.init( config );
     788           0 :   }
     789             : 
     790           0 :   if( FD_UNLIKELY( original_netns && -1==fd_net_util_netns_restore( original_netns_ ) ) )
     791           0 :     FD_LOG_ERR(( "failed to restore network namespace (fd=%d) (%i-%s)", original_netns_, errno, fd_io_strerror( errno ) ));
     792           0 : }
     793             : 
     794             : /* The boot sequence is a little bit involved...
     795             : 
     796             :    A process tree is created that looks like,
     797             : 
     798             :    + main
     799             :    +-- pidns
     800             :        +-- agave
     801             :        +-- tile 0
     802             :        +-- tile 1
     803             :        ...
     804             : 
     805             :    What we want is that if any process in the tree dies, all other
     806             :    processes will also die.  This is done as follows,
     807             : 
     808             :     (a) pidns is the init process of a PID namespace, so if it dies the
     809             :         kernel will terminate the child processes.
     810             : 
     811             :     (b) main is the parent of pidns, so it can issue a waitpid() on the
     812             :         child PID, and when it completes terminate itself.
     813             : 
     814             :     (c) pidns is the parent of agave and the tiles, so it could
     815             :         issue a waitpid() of -1 to wait for any of them to terminate,
     816             :         but how would it know if main has died?
     817             : 
     818             :     (d) main creates a pipe, and passes the write end to pidns.  If main
     819             :         dies, the pipe will be closed, and pidns will get a HUP on the
     820             :         read end.  Then pidns creates a pipe per child and passes the
     821             :         write end to the child.  If any of the children die, the pipe
     822             :         will be closed, and pidns will get a HUP on the read end.
     823             : 
     824             :         Then pidns can call poll() on both the write end of the main
     825             :         pipe and the read end of all the child pipes.  If any of them
     826             :         raises SIGHUP, then pidns knows that the parent or a child has
     827             :         died, and it can terminate itself, which due to (a) and (b)
     828             :         will kill all other processes. */
     829             : void
     830             : run_firedancer( config_t * config,
     831             :                 int        parent_pipefd,
     832           0 :                 int        init_workspaces ) {
     833             :   /* dump the topology we are using to the output log */
     834           0 :   fd_topo_print_log( 0, &config->topo );
     835             : 
     836           0 :   run_firedancer_init( config, init_workspaces );
     837             : 
     838           0 : #if defined(__x86_64__)
     839             : 
     840           0 : #ifndef SYS_landlock_create_ruleset
     841           0 : #define SYS_landlock_create_ruleset 444
     842           0 : #endif
     843             : 
     844           0 : #ifndef LANDLOCK_CREATE_RULESET_VERSION
     845           0 : #define LANDLOCK_CREATE_RULESET_VERSION (1U << 0)
     846           0 : #endif
     847             : 
     848           0 : #endif
     849           0 :   long abi = syscall( SYS_landlock_create_ruleset, NULL, 0, LANDLOCK_CREATE_RULESET_VERSION );
     850           0 :   if( -1L==abi && (errno==ENOSYS || errno==EOPNOTSUPP ) ) {
     851           0 :     FD_LOG_WARNING(( "The Landlock access control system is not supported by your Linux kernel. Firedancer uses landlock to "
     852           0 :                      "provide an additional layer of security to the sandbox, but it is not required." ));
     853           0 :   }
     854             : 
     855           0 :   if( FD_UNLIKELY( close( 0 ) ) ) FD_LOG_ERR(( "close(0) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     856           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 ) ));
     857             : 
     858           0 :   int pipefd;
     859           0 :   pid_namespace = clone_firedancer( config, parent_pipefd, &pipefd );
     860             : 
     861             :   /* Print the location of the logfile on SIGINT or SIGTERM, and also
     862             :      kill the child.  They are connected by a pipe which the child is
     863             :      polling so we don't strictly need to kill the child, but its helpful
     864             :      to do that before printing the log location line, else it might
     865             :      get interleaved due to timing windows in the shutdown. */
     866           0 :   install_parent_signals();
     867             : 
     868           0 :   if( FD_UNLIKELY( close( config->log.lock_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     869             : 
     870           0 :   struct sock_filter seccomp_filter[ 128UL ];
     871           0 :   populate_sock_filter_policy_main( 128UL, seccomp_filter, (uint)fd_log_private_logfile_fd(), (uint)pid_namespace );
     872             : 
     873           0 :   int allow_fds[ 4 ];
     874           0 :   ulong allow_fds_cnt = 0;
     875           0 :   allow_fds[ allow_fds_cnt++ ] = 2; /* stderr */
     876           0 :   if( FD_LIKELY( fd_log_private_logfile_fd()!=-1 ) )
     877           0 :     allow_fds[ allow_fds_cnt++ ] = fd_log_private_logfile_fd(); /* logfile, or maybe stdout */
     878           0 :   allow_fds[ allow_fds_cnt++ ] = pipefd; /* read end of main pipe */
     879           0 :   if( FD_UNLIKELY( parent_pipefd!=-1 ) )
     880           0 :     allow_fds[ allow_fds_cnt++ ] = parent_pipefd; /* write end of parent pipe */
     881             : 
     882           0 :   if( FD_LIKELY( config->development.sandbox ) ) {
     883           0 :     fd_sandbox_enter( config->uid,
     884           0 :                       config->gid,
     885           0 :                       0,
     886           0 :                       0,
     887           0 :                       1, /* Keep controlling terminal for main so it can receive Ctrl+C */
     888           0 :                       0,
     889           0 :                       0UL,
     890           0 :                       0UL,
     891           0 :                       0UL,
     892           0 :                       allow_fds_cnt,
     893           0 :                       allow_fds,
     894           0 :                       sock_filter_policy_main_instr_cnt,
     895           0 :                       seccomp_filter );
     896           0 :   } else {
     897           0 :     fd_sandbox_switch_uid_gid( config->uid, config->gid );
     898           0 :   }
     899             : 
     900             :   /* The supervsior process should not share the log lock, because a
     901             :      child process might die while holding it and we still need to
     902             :      reap and print errors. */
     903           0 :   int lock = 0;
     904           0 :   fd_log_private_shared_lock = &lock;
     905             : 
     906             :   /* the only clean way to exit is SIGINT or SIGTERM on this parent process,
     907             :      so if wait4() completes, it must be an error */
     908           0 :   int wstatus;
     909           0 :   if( FD_UNLIKELY( -1==wait4( pid_namespace, &wstatus, (int)__WALL, NULL ) ) )
     910           0 :     FD_LOG_ERR(( "main wait4() failed (%i-%s)\nLog at \"%s\"", errno, fd_io_strerror( errno ), fd_log_private_path ));
     911             : 
     912           0 :   if( FD_UNLIKELY( WIFSIGNALED( wstatus ) ) ) fd_sys_util_exit_group( WTERMSIG( wstatus ) ? WTERMSIG( wstatus ) : 1 );
     913           0 :   else fd_sys_util_exit_group( WEXITSTATUS( wstatus ) ? WEXITSTATUS( wstatus ) : 1 );
     914           0 : }
     915             : 
     916             : void
     917             : run_cmd_fn( args_t *   args FD_PARAM_UNUSED,
     918           0 :             config_t * config ) {
     919           0 :   if( FD_UNLIKELY( !config->gossip.entrypoints_cnt && !config->development.bootstrap ) )
     920           0 :     FD_LOG_ERR(( "No entrypoints specified in configuration file under [gossip.entrypoints], but "
     921           0 :                  "at least one is needed to determine how to connect to the Solana cluster. If "
     922           0 :                  "you want to start a new cluster in a development environment, use `fddev` instead "
     923           0 :                  "of `fdctl`. If you want to use an existing genesis, set [development.bootstrap] "
     924           0 :                  "to \"true\" in the configuration file." ));
     925             : 
     926           0 :   for( ulong i=0; i<config->gossip.entrypoints_cnt; i++ ) {
     927           0 :     if( FD_UNLIKELY( !strcmp( config->gossip.entrypoints[ i ], "" ) ) )
     928           0 :       FD_LOG_ERR(( "One of the entrypoints in your configuration file under [gossip.entrypoints] is "
     929           0 :                    "empty. Please remove the empty entrypoint or set it correctly. "));
     930           0 :   }
     931             : 
     932           0 :   run_firedancer( config, -1, 1 );
     933           0 : }

Generated by: LCOV version 1.14