LCOV - code coverage report
Current view: top level - app/shared - fd_bootinfo.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 363 0.0 %
Date: 2026-08-04 05:20:02 Functions: 0 14 0.0 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : #include "fd_bootinfo.h"
       3             : 
       4             : #include "../../util/fd_version.h"
       5             : #include "../../util/pod/fd_pod.h"
       6             : #include "../../util/sandbox/fd_sandbox.h"
       7             : #include "../../util/shmem/fd_shmem_private.h"
       8             : #include "../../tango/tempo/fd_tempo.h"
       9             : 
      10             : #include <dirent.h>
      11             : #include <errno.h>
      12             : #include <fcntl.h>
      13             : #include <limits.h>
      14             : #include <stdio.h>
      15             : #include <string.h>
      16             : #include <sys/resource.h>
      17             : #include <sys/stat.h>
      18             : #include <unistd.h>
      19             : 
      20           0 : #define BOOTINFO_NORMAL "\033[0m"
      21           0 : #define BOOTINFO_BOLD   "\033[1m"
      22           0 : #define BOOTINFO_DIM    "\033[2m"
      23           0 : #define BOOTINFO_GREEN  "\033[32m"
      24           0 : #define BOOTINFO_YELLOW "\033[93m"
      25             : 
      26             : /* proc_start_time reads /proc/<pid>/stat field 22 (starttime, in
      27             :    clock ticks since boot).  Returns 0 on failure (0 is not a valid
      28             :    start time for any process we care about).  The comm field can
      29             :    contain spaces and parens, so parse from the last ')'. */
      30             : 
      31             : static ulong
      32           0 : proc_start_time( ulong pid ) {
      33           0 :   char path[ 64UL ];
      34           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/proc/%lu/stat", pid ) );
      35             : 
      36           0 :   char buf[ 4096UL ];
      37           0 :   int fd = open( path, O_RDONLY );
      38           0 :   if( FD_UNLIKELY( -1==fd ) ) return 0UL;
      39           0 :   long sz = read( fd, buf, sizeof(buf)-1UL );
      40           0 :   if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_WARNING(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      41           0 :   if( FD_UNLIKELY( sz<=0L ) ) return 0UL;
      42           0 :   buf[ sz ] = '\0';
      43             : 
      44           0 :   char * p = strrchr( buf, ')' );
      45           0 :   if( FD_UNLIKELY( !p ) ) return 0UL;
      46             : 
      47             :   /* fields 3 (state) through 21 follow ')', starttime is field 22 */
      48           0 :   ulong start_time = 0UL;
      49           0 :   if( FD_UNLIKELY( 1!=sscanf( p+1UL, " %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %lu", &start_time ) ) ) return 0UL;
      50           0 :   return start_time;
      51           0 : }
      52             : 
      53             : static void
      54             : bootinfo_path( char const * mount_path,
      55             :                char const * name,
      56           0 :                char *       out ) {
      57           0 :   FD_TEST( fd_cstr_printf_check( out, PATH_MAX, NULL, "%s/%s.bootinfo", mount_path, name ) );
      58           0 : }
      59             : 
      60             : /* write_file_atomic writes sz bytes at data to path via a same-dir
      61             :    .partial rename.  Returns 0 on success, -1 otherwise. */
      62             : 
      63             : static int
      64             : write_file_atomic( char const * path,
      65             :                    void const * data,
      66             :                    ulong        sz,
      67           0 :                    mode_t       mode ) {
      68           0 :   char partial_path[ PATH_MAX ];
      69           0 :   if( FD_UNLIKELY( !fd_cstr_printf_check( partial_path, sizeof(partial_path), NULL, "%s.partial", path ) ) ) return -1;
      70             : 
      71           0 :   int fd = open( partial_path, O_WRONLY|O_CREAT|O_TRUNC, mode );
      72           0 :   if( FD_UNLIKELY( -1==fd ) ) return -1;
      73             : 
      74           0 :   uchar const * p         = (uchar const *)data;
      75           0 :   ulong         remaining = sz;
      76           0 :   while( remaining ) {
      77           0 :     long written = write( fd, p, remaining );
      78           0 :     if( FD_UNLIKELY( written<=0L ) ) {
      79           0 :       if( FD_UNLIKELY( written<0L && errno==EINTR ) ) continue;
      80           0 :       if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_WARNING(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      81           0 :       if( FD_UNLIKELY( -1==unlink( partial_path ) && errno!=ENOENT ) ) FD_LOG_WARNING(( "unlink() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      82           0 :       return -1;
      83           0 :     }
      84           0 :     p += written; remaining -= (ulong)written;
      85           0 :   }
      86           0 :   if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_WARNING(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      87             : 
      88           0 :   if( FD_UNLIKELY( -1==rename( partial_path, path ) ) ) return -1;
      89           0 :   return 0;
      90           0 : }
      91             : 
      92             : void
      93           0 : fd_bootinfo_write( config_t const * config ) {
      94           0 :   fd_bootinfo_t info = {0};
      95           0 :   info.magic                 = FD_BOOTINFO_MAGIC;
      96           0 :   info.version               = FD_BOOTINFO_VERSION;
      97           0 :   info.pid                   = fd_sandbox_getpid();
      98           0 :   info.pid_start_time        = proc_start_time( info.pid );
      99           0 :   info.boot_wallclock_nanos  = fd_log_wallclock();
     100           0 :   fd_cstr_ncpy( info.commit_ref, fd_commit_ref_cstr, sizeof(info.commit_ref) );
     101           0 :   info.fd_version[ 0 ]       = fd_major_version;
     102           0 :   info.fd_version[ 1 ]       = fd_minor_version;
     103           0 :   info.fd_version[ 2 ]       = fd_patch_version;
     104           0 :   fd_cstr_ncpy( info.name, config->name, sizeof(info.name) );
     105           0 :   info.uid                   = config->uid;
     106           0 :   info.gid                   = config->gid;
     107           0 :   info.topo_layout_hash      = config->topo.layout_hash;
     108             : 
     109           0 :   ulong adminctl_obj_id = fd_pod_query_ulong( config->topo.props, "adminctl", ULONG_MAX );
     110           0 :   if( FD_LIKELY( adminctl_obj_id!=ULONG_MAX ) ) {
     111           0 :     fd_topo_obj_t const *  obj  = &config->topo.objs[ adminctl_obj_id ];
     112           0 :     fd_topo_wksp_t const * wksp = &config->topo.workspaces[ obj->wksp_id ];
     113           0 :     FD_TEST( fd_cstr_printf_check( info.adminctl_wksp_file, sizeof(info.adminctl_wksp_file), NULL, "%s_%s.wksp", config->name, wksp->name ) );
     114           0 :     info.adminctl_page_sz = wksp->page_sz;
     115           0 :     info.adminctl_offset  = obj->offset;
     116           0 :   }
     117             : 
     118           0 :   if( FD_UNLIKELY( !info.pid_start_time ) ) {
     119           0 :     FD_LOG_WARNING(( "could not read own process start time, validator discovery will be unavailable" ));
     120           0 :     return;
     121           0 :   }
     122             : 
     123             :   /* Publish the resolved config blob first so a reader that sees the
     124             :      descriptor can always recover it.  Contains paths and settings, no
     125             :      key material. */
     126           0 :   char blob_path[ PATH_MAX ];
     127           0 :   FD_TEST( fd_cstr_printf_check( blob_path, sizeof(blob_path), NULL, "%s/%s.config", config->hugetlbfs.mount_path, config->name ) );
     128           0 :   if( FD_UNLIKELY( -1==write_file_atomic( blob_path, config, sizeof(config_t), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH ) ) ) {
     129           0 :     FD_LOG_WARNING(( "write(%s) failed (%i-%s), attaching to this validator will require --config", blob_path, errno, fd_io_strerror( errno ) ));
     130           0 :   } else {
     131           0 :     FD_TEST( fd_cstr_printf_check( info.config_file, sizeof(info.config_file), NULL, "%s.config", config->name ) );
     132           0 :     info.config_sz = sizeof(config_t);
     133           0 :   }
     134             : 
     135           0 :   char path[ PATH_MAX ];
     136           0 :   bootinfo_path( config->hugetlbfs.mount_path, config->name, path );
     137             : 
     138           0 :   if( FD_UNLIKELY( -1==write_file_atomic( path, &info, sizeof(info), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH ) ) )
     139           0 :     FD_LOG_WARNING(( "write(%s) failed (%i-%s), validator discovery will be unavailable", path, errno, fd_io_strerror( errno ) ));
     140             : 
     141           0 :   char const * c_normal = fd_log_style_normal();
     142           0 :   char const * c_bold   = fd_log_style_bold();
     143           0 :   char const * c_dim    = fd_log_style_dim();
     144             : 
     145           0 :   ulong mem_gib = (fd_topo_mlock( &config->topo )+((1UL<<30UL)-1UL))>>30UL;
     146           0 :   FD_LOG_NOTICE(( "booting validator %s%s%s version %lu.%lu.%lu %s(%.11s)%s pid %lu with %s%lu%s tiles and %s%lu GiB%s memory at %s%s%s",
     147           0 :                   c_bold, info.name, c_normal,
     148           0 :                   info.fd_version[ 0 ], info.fd_version[ 1 ], info.fd_version[ 2 ],
     149           0 :                   c_dim, info.commit_ref, c_normal,
     150           0 :                   info.pid,
     151           0 :                   c_bold, config->topo.tile_cnt, c_normal,
     152           0 :                   c_bold, mem_gib, c_normal,
     153           0 :                   c_dim, config->hugetlbfs.mount_path, c_normal ));
     154           0 : }
     155             : 
     156             : void
     157           0 : fd_bootinfo_unlink( config_t const * config ) {
     158           0 :   char const * suffix[ 4 ] = { "bootinfo", "bootinfo.partial", "config", "config.partial" };
     159           0 :   for( ulong i=0UL; i<4UL; i++ ) {
     160           0 :     char path[ PATH_MAX ];
     161           0 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "%s/%s.%s", config->hugetlbfs.mount_path, config->name, suffix[ i ] ) );
     162           0 :     if( FD_UNLIKELY( -1==unlink( path ) && errno!=ENOENT ) )
     163           0 :       FD_LOG_WARNING(( "unlink(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     164           0 :   }
     165           0 : }
     166             : 
     167             : int
     168             : fd_bootinfo_path_read( char const *    path,
     169           0 :                        fd_bootinfo_t * out ) {
     170           0 :   int fd = open( path, O_RDONLY );
     171           0 :   if( FD_UNLIKELY( -1==fd ) ) return -1;
     172             : 
     173           0 :   struct stat st;
     174           0 :   if( FD_UNLIKELY( -1==fstat( fd, &st )                 ) ) goto fail;
     175           0 :   if( FD_UNLIKELY( !S_ISREG( st.st_mode )               ) ) goto fail;
     176           0 :   if( FD_UNLIKELY( st.st_uid && st.st_uid!=geteuid()    ) ) goto fail; /* only trust root or self */
     177           0 :   if( FD_UNLIKELY( st.st_mode & S_IWOTH                 ) ) goto fail;
     178             :   /* Newer builds may append fields, the v1 prefix is frozen so any
     179             :      genuine descriptor is at least this large and parses below. */
     180           0 :   if( FD_UNLIKELY( st.st_size<(long)sizeof(*out)        ) ) goto fail;
     181             : 
     182           0 :   if( FD_UNLIKELY( sizeof(*out)!=(ulong)read( fd, out, sizeof(*out) ) ) ) goto fail;
     183           0 :   if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_WARNING(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     184             : 
     185           0 :   if( FD_UNLIKELY( out->magic!=FD_BOOTINFO_MAGIC       ) ) return -1;
     186           0 :   if( FD_UNLIKELY( !out->version                       ) ) return -1;
     187           0 :   if( FD_UNLIKELY( !memchr( out->name, '\0', sizeof(out->name) )                             ) ) return -1;
     188           0 :   if( FD_UNLIKELY( !memchr( out->commit_ref, '\0', sizeof(out->commit_ref) )                 ) ) return -1;
     189           0 :   if( FD_UNLIKELY( !memchr( out->adminctl_wksp_file, '\0', sizeof(out->adminctl_wksp_file) ) ) ) return -1;
     190           0 :   if( FD_UNLIKELY( strchr( out->adminctl_wksp_file, '/' )                                    ) ) return -1;
     191           0 :   if( FD_UNLIKELY( !memchr( out->config_file, '\0', sizeof(out->config_file) )               ) ) return -1;
     192           0 :   if( FD_UNLIKELY( strchr( out->config_file, '/' )                                           ) ) return -1;
     193           0 :   return 0;
     194             : 
     195           0 : fail:
     196           0 :   if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_WARNING(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     197           0 :   return -1;
     198           0 : }
     199             : 
     200             : int
     201           0 : fd_bootinfo_live( fd_bootinfo_t const * info ) {
     202           0 :   ulong start_time = proc_start_time( info->pid );
     203           0 :   return start_time && start_time==info->pid_start_time;
     204           0 : }
     205             : 
     206             : /* mountinfo_unescape decodes \0NN octal escapes in-place. */
     207             : 
     208             : static void
     209           0 : mountinfo_unescape( char * s ) {
     210           0 :   char * w = s;
     211           0 :   while( *s ) {
     212           0 :     if( FD_UNLIKELY( s[0]=='\\' && s[1]>='0' && s[1]<='3' && s[2]>='0' && s[2]<='7' && s[3]>='0' && s[3]<='7' ) ) {
     213           0 :       *w++ = (char)( ((s[1]-'0')<<6) | ((s[2]-'0')<<3) | (s[3]-'0') );
     214           0 :       s += 4;
     215           0 :     } else {
     216           0 :       *w++ = *s++;
     217           0 :     }
     218           0 :   }
     219           0 :   *w = '\0';
     220           0 : }
     221             : 
     222             : /* candidate_mounts fills dirs with parent directories of firedancer
     223             :    shaped hugetlbfs mounts (mountpoint basename .gigantic/.huge/.normal)
     224             :    plus the default mount path.  Returns the count. */
     225             : 
     226             : static ulong
     227             : candidate_mounts( char  dirs[ FD_BOOTINFO_INSTANCE_MAX ][ PATH_MAX ],
     228           0 :                   ulong max ) {
     229           0 :   ulong cnt = 0UL;
     230             : 
     231           0 :   FILE * fp = fopen( "/proc/self/mountinfo", "r" );
     232           0 :   if( FD_LIKELY( fp ) ) {
     233           0 :     char line[ 4096UL ];
     234           0 :     while( FD_LIKELY( fgets( line, sizeof(line), fp ) ) ) {
     235             :       /* fields: id parent maj:min root mountpoint opts [optional]* - fstype src superopts */
     236           0 :       char * sep = strstr( line, " - " );
     237           0 :       if( FD_UNLIKELY( !sep ) ) continue;
     238             : 
     239           0 :       char * fstype = sep+3UL;
     240           0 :       char * fstype_end = strchr( fstype, ' ' );
     241           0 :       if( FD_UNLIKELY( !fstype_end ) ) continue;
     242           0 :       *fstype_end = '\0';
     243           0 :       if( FD_LIKELY( strcmp( fstype, "hugetlbfs" ) ) ) continue;
     244             : 
     245           0 :       *sep = '\0';
     246           0 :       char * mountpoint = NULL;
     247           0 :       char * tok = strtok( line, " " );
     248           0 :       for( ulong i=0UL; tok; i++, tok=strtok( NULL, " " ) ) {
     249           0 :         if( i==4UL ) { mountpoint = tok; break; }
     250           0 :       }
     251           0 :       if( FD_UNLIKELY( !mountpoint ) ) continue;
     252           0 :       mountinfo_unescape( mountpoint );
     253             : 
     254           0 :       char * base = strrchr( mountpoint, '/' );
     255           0 :       if( FD_UNLIKELY( !base || base==mountpoint ) ) continue;
     256           0 :       if( FD_LIKELY( strcmp( base, "/.gigantic" ) && strcmp( base, "/.huge" ) && strcmp( base, "/.normal" ) ) ) continue;
     257           0 :       *base = '\0'; /* mountpoint is now the parent dir */
     258             : 
     259           0 :       int seen = 0;
     260           0 :       for( ulong i=0UL; i<cnt; i++ ) if( !strcmp( dirs[ i ], mountpoint ) ) { seen = 1; break; }
     261           0 :       if( FD_UNLIKELY( seen ) ) continue;
     262           0 :       if( FD_UNLIKELY( cnt>=max ) ) break;
     263           0 :       strncpy( dirs[ cnt ], mountpoint, PATH_MAX-1UL );
     264           0 :       dirs[ cnt ][ PATH_MAX-1UL ] = '\0';
     265           0 :       cnt++;
     266           0 :     }
     267           0 :     if( FD_UNLIKELY( fclose( fp ) ) ) FD_LOG_WARNING(( "fclose() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     268           0 :   }
     269             : 
     270             :   /* default path, covers normal page setups with no hugetlbfs mounts */
     271           0 :   int seen = 0;
     272           0 :   for( ulong i=0UL; i<cnt; i++ ) if( !strcmp( dirs[ i ], "/mnt/.fd" ) ) { seen = 1; break; }
     273           0 :   if( FD_LIKELY( !seen && cnt<max ) ) {
     274           0 :     strncpy( dirs[ cnt ], "/mnt/.fd", PATH_MAX-1UL );
     275           0 :     cnt++;
     276           0 :   }
     277             : 
     278           0 :   return cnt;
     279           0 : }
     280             : 
     281             : ulong
     282             : fd_bootinfo_discover( fd_bootinfo_instance_t * out,
     283           0 :                       ulong                    max ) {
     284           0 :   static char dirs[ FD_BOOTINFO_INSTANCE_MAX ][ PATH_MAX ];
     285           0 :   ulong dir_cnt = candidate_mounts( dirs, FD_BOOTINFO_INSTANCE_MAX );
     286             : 
     287           0 :   ulong cnt = 0UL;
     288           0 :   for( ulong i=0UL; i<dir_cnt && cnt<max; i++ ) {
     289           0 :     DIR * dir = opendir( dirs[ i ] );
     290           0 :     if( FD_UNLIKELY( !dir ) ) continue;
     291             : 
     292           0 :     struct dirent * entry;
     293           0 :     while( FD_LIKELY( cnt<max && (entry=readdir( dir )) ) ) {
     294           0 :       char const * suffix = strstr( entry->d_name, ".bootinfo" );
     295           0 :       if( FD_LIKELY( !suffix || strcmp( suffix, ".bootinfo" ) ) ) continue;
     296             : 
     297           0 :       char path[ PATH_MAX ];
     298           0 :       if( FD_UNLIKELY( !fd_cstr_printf_check( path, sizeof(path), NULL, "%s/%s", dirs[ i ], entry->d_name ) ) ) continue;
     299             : 
     300           0 :       fd_bootinfo_t info;
     301           0 :       if( FD_UNLIKELY( -1==fd_bootinfo_path_read( path, &info ) ) ) continue;
     302             : 
     303             :       /* name must match filename */
     304           0 :       if( FD_UNLIKELY( strncmp( entry->d_name, info.name, (ulong)(suffix-entry->d_name) ) || strlen( info.name )!=(ulong)(suffix-entry->d_name) ) ) continue;
     305             : 
     306           0 :       strncpy( out[ cnt ].mount_path, dirs[ i ], PATH_MAX-1UL );
     307           0 :       out[ cnt ].mount_path[ PATH_MAX-1UL ] = '\0';
     308           0 :       out[ cnt ].info = info;
     309           0 :       out[ cnt ].live = fd_bootinfo_live( &info );
     310           0 :       cnt++;
     311           0 :     }
     312           0 :     if( FD_UNLIKELY( -1==closedir( dir ) ) ) FD_LOG_WARNING(( "closedir() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     313           0 :   }
     314             : 
     315           0 :   return cnt;
     316           0 : }
     317             : 
     318             : void
     319             : fd_bootinfo_print( fd_bootinfo_instance_t const * instances,
     320           0 :                    ulong                          cnt ) {
     321             :   /* fd_log colorize already honors NO_COLOR/TERM/--log-colorize, but is
     322             :      keyed to stderr, so additionally require stdout to be a tty. */
     323           0 :   int color = fd_log_colorize() && isatty( STDOUT_FILENO );
     324           0 :   char const * c_normal = color ? BOOTINFO_NORMAL : "";
     325           0 :   char const * c_bold   = color ? BOOTINFO_BOLD   : "";
     326           0 :   char const * c_dim    = color ? BOOTINFO_DIM    : "";
     327           0 :   char const * c_live   = color ? BOOTINFO_GREEN  : "";
     328           0 :   char const * c_stale  = color ? BOOTINFO_YELLOW : "";
     329             : 
     330           0 :   if( FD_UNLIKELY( !cnt ) ) {
     331           0 :     FD_LOG_STDOUT(( "%sno validators found%s\n", c_dim, c_normal ));
     332           0 :     return;
     333           0 :   }
     334             : 
     335           0 :   FD_LOG_STDOUT(( "%s%-16s %-8s %-7s %-10s %-12s %-12s %s%s\n", c_dim, "NAME", "PID", "STATE", "UPTIME", "VERSION", "COMMIT", "MOUNT", c_normal ));
     336           0 :   for( ulong i=0UL; i<cnt; i++ ) {
     337           0 :     fd_bootinfo_instance_t const * instance = &instances[ i ];
     338           0 :     fd_bootinfo_t const *          info     = &instance->info;
     339             : 
     340           0 :     char uptime[ 32UL ] = "-";
     341           0 :     if( FD_LIKELY( instance->live ) ) {
     342           0 :       long secs = (fd_log_wallclock()-info->boot_wallclock_nanos)/(long)1e9;
     343           0 :       if( FD_UNLIKELY( secs<0L ) ) secs = 0L;
     344           0 :       long days = secs/86400L, hours = (secs/3600L)%24L, mins = (secs/60L)%60L;
     345           0 :       if(      FD_UNLIKELY( days  ) ) FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%ldd%ldh%ldm", days, hours, mins ) );
     346           0 :       else if( FD_UNLIKELY( hours ) ) FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%ldh%ldm", hours, mins ) );
     347           0 :       else if( FD_LIKELY(   mins  ) ) FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%ldm", mins ) );
     348           0 :       else                            FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%lds", secs ) );
     349           0 :     }
     350             : 
     351           0 :     char version[ 32UL ];
     352           0 :     FD_TEST( fd_cstr_printf_check( version, sizeof(version), NULL, "%lu.%lu.%lu", info->fd_version[ 0 ], info->fd_version[ 1 ], info->fd_version[ 2 ] ) );
     353             : 
     354           0 :     char commit[ 12UL ] = {0};
     355           0 :     strncpy( commit, info->commit_ref, sizeof(commit)-1UL );
     356             : 
     357           0 :     FD_LOG_STDOUT(( "%s%-16s%s %-8lu %s%-7s%s %-10s %-12s %-12s %s%s%s\n",
     358           0 :                     c_bold, info->name, c_normal,
     359           0 :                     info->pid,
     360           0 :                     instance->live ? c_live : c_stale,
     361           0 :                     instance->live ? "live" : "stale",
     362           0 :                     c_normal,
     363           0 :                     uptime,
     364           0 :                     version,
     365           0 :                     commit,
     366           0 :                     c_dim, instance->mount_path, c_normal ));
     367           0 :   }
     368           0 : }
     369             : 
     370             : void
     371           0 : fd_bootinfo_notice( fd_bootinfo_instance_t const * instance ) {
     372           0 :   fd_bootinfo_t const * info = &instance->info;
     373             : 
     374           0 :   long secs = (fd_log_wallclock()-info->boot_wallclock_nanos)/(long)1e9;
     375           0 :   if( FD_UNLIKELY( secs<0L ) ) secs = 0L;
     376           0 :   long days = secs/86400L, hours = (secs/3600L)%24L, mins = (secs/60L)%60L;
     377           0 :   char uptime[ 32UL ];
     378           0 :   if(      FD_UNLIKELY( days  ) ) FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%ldd%ldh%ldm", days, hours, mins ) );
     379           0 :   else if( FD_UNLIKELY( hours ) ) FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%ldh%ldm", hours, mins ) );
     380           0 :   else if( FD_LIKELY(   mins  ) ) FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%ldm", mins ) );
     381           0 :   else                            FD_TEST( fd_cstr_printf_check( uptime, sizeof(uptime), NULL, "%lds", secs ) );
     382             : 
     383           0 :   char const * c_normal = fd_log_style_normal();
     384           0 :   char const * c_bold   = fd_log_style_bold();
     385           0 :   char const * c_dim    = fd_log_style_dim();
     386           0 :   char const * c_live   = fd_log_colorize() ? BOOTINFO_GREEN : "";
     387             : 
     388           0 :   FD_LOG_NOTICE(( "attached to %slive%s validator %s%s%s version %lu.%lu.%lu %s(%.11s)%s pid %lu up %s at %s%s%s",
     389           0 :                   c_live, c_normal,
     390           0 :                   c_bold, info->name, c_normal,
     391           0 :                   info->fd_version[ 0 ], info->fd_version[ 1 ], info->fd_version[ 2 ],
     392           0 :                   c_dim, info->commit_ref, c_normal,
     393           0 :                   info->pid,
     394           0 :                   uptime,
     395           0 :                   c_dim, instance->mount_path, c_normal ));
     396           0 : }
     397             : 
     398             : void
     399           0 : fd_bootinfo_adopt( config_t * config ) {
     400           0 :   if( FD_LIKELY( config->has_user_config ) ) return; /* fd_bootinfo_check_layout verifies before joining */
     401             : 
     402           0 :   fd_bootinfo_instance_t instances[ FD_BOOTINFO_INSTANCE_MAX ];
     403           0 :   ulong cnt = fd_bootinfo_discover( instances, FD_BOOTINFO_INSTANCE_MAX );
     404             : 
     405           0 :   fd_bootinfo_instance_t * match     = NULL;
     406           0 :   ulong                    live_cnt  = 0UL;
     407           0 :   for( ulong i=0UL; i<cnt; i++ ) {
     408           0 :     if( FD_UNLIKELY( !instances[ i ].live ) ) continue;
     409           0 :     match = &instances[ i ];
     410           0 :     live_cnt++;
     411           0 :   }
     412             : 
     413           0 :   if( FD_UNLIKELY( !live_cnt ) ) {
     414           0 :     if( FD_UNLIKELY( cnt ) ) fd_bootinfo_print( instances, cnt );
     415           0 :     FD_LOG_ERR(( "No running validator found. If a validator is running, pass --config with the "
     416           0 :                  "configuration file it was started with." ));
     417           0 :   }
     418             : 
     419           0 :   if( FD_UNLIKELY( live_cnt>1UL ) ) {
     420           0 :     fd_bootinfo_print( instances, cnt );
     421           0 :     FD_LOG_ERR(( "Multiple running validators found. Pass --config to select one." ));
     422           0 :   }
     423             : 
     424           0 :   fd_bootinfo_t const * info = &match->info;
     425             : 
     426           0 :   if( FD_UNLIKELY( strcmp( info->commit_ref, fd_commit_ref_cstr ) ) )
     427           0 :     FD_LOG_ERR(( "The running validator `%s` is running commit %s but this binary is commit %s.  "
     428           0 :                  "This command reads the validator's memory directly and must be run from the "
     429           0 :                  "same binary the validator is running.", info->name, info->commit_ref, fd_commit_ref_cstr ));
     430             : 
     431           0 :   if( FD_UNLIKELY( !info->config_file[ 0 ] || info->config_sz!=sizeof(config_t) ) )
     432           0 :     FD_LOG_ERR(( "The running validator `%s` did not publish a usable configuration.  Pass --config "
     433           0 :                  "with the configuration file it was started with.", info->name ));
     434             : 
     435           0 :   char path[ PATH_MAX ];
     436           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "%s/%s", match->mount_path, info->config_file ) );
     437             : 
     438           0 :   int fd = open( path, O_RDONLY );
     439           0 :   if( FD_UNLIKELY( -1==fd ) ) FD_LOG_ERR(( "open(%s) failed (%i-%s).  The validator `%s` appears to be running "
     440           0 :                                            "but its configuration could not be read.  Pass --config with the "
     441           0 :                                            "configuration file it was started with.", path, errno, fd_io_strerror( errno ), info->name ));
     442             : 
     443           0 :   struct stat st;
     444           0 :   if( FD_UNLIKELY( -1==fstat( fd, &st )               ) ) FD_LOG_ERR(( "fstat(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     445           0 :   if( FD_UNLIKELY( !S_ISREG( st.st_mode )             ) ) FD_LOG_ERR(( "`%s` is not a regular file", path ));
     446           0 :   if( FD_UNLIKELY( st.st_uid && st.st_uid!=geteuid()  ) ) FD_LOG_ERR(( "`%s` is not owned by root", path ));
     447           0 :   if( FD_UNLIKELY( st.st_mode & S_IWOTH               ) ) FD_LOG_ERR(( "`%s` is world writable", path ));
     448           0 :   if( FD_UNLIKELY( st.st_size!=(long)sizeof(config_t) ) ) FD_LOG_ERR(( "validator `%s` config has unexpected size %ld", info->name, st.st_size ));
     449             : 
     450           0 :   uchar * p         = (uchar *)config;
     451           0 :   ulong   remaining = sizeof(config_t);
     452           0 :   while( remaining ) {
     453           0 :     long got = read( fd, p, remaining );
     454           0 :     if( FD_UNLIKELY( got<=0L ) ) {
     455           0 :       if( FD_UNLIKELY( got<0L && errno==EINTR ) ) continue;
     456           0 :       FD_LOG_ERR(( "read(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     457           0 :     }
     458           0 :     p += got; remaining -= (ulong)got;
     459           0 :   }
     460           0 :   if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_WARNING(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     461             : 
     462           0 :   config->has_user_config = 0;
     463             : 
     464             :   /* Rebind process state that was derived from the default config
     465             :      before adoption. */
     466           0 :   fd_tempo_set_tick_per_ns( config->tick_per_ns_mu, config->tick_per_ns_sigma );
     467             : 
     468           0 :   ulong base_len = strlen( config->hugetlbfs.mount_path );
     469           0 :   if( FD_UNLIKELY( !base_len || base_len>=FD_SHMEM_PRIVATE_BASE_MAX ) ) FD_LOG_ERR(( "adopted invalid mount path" ));
     470           0 :   memcpy( fd_shmem_private_base, config->hugetlbfs.mount_path, base_len+1UL );
     471           0 :   fd_shmem_private_base_len = base_len;
     472             : 
     473             :   /* Permissions were checked against the default topology, which may
     474             :      mlock less than the adopted one.  Best effort raise. */
     475           0 :   struct rlimit rl = { .rlim_cur = fd_topo_mlock( &config->topo ), .rlim_max = fd_topo_mlock( &config->topo ) };
     476           0 :   if( FD_UNLIKELY( -1==setrlimit( RLIMIT_MEMLOCK, &rl ) ) )
     477           0 :     FD_LOG_INFO(( "setrlimit(RLIMIT_MEMLOCK) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     478             : 
     479           0 :   fd_bootinfo_notice( match );
     480           0 : }
     481             : 
     482             : void
     483           0 : fd_bootinfo_check_layout( config_t const * config ) {
     484           0 :   char path[ PATH_MAX ];
     485           0 :   bootinfo_path( config->hugetlbfs.mount_path, config->name, path );
     486             : 
     487           0 :   fd_bootinfo_t info;
     488           0 :   if( FD_UNLIKELY( -1==fd_bootinfo_path_read( path, &info ) ) ) return; /* no or unreadable descriptor, older validator */
     489           0 :   if( FD_UNLIKELY( !fd_bootinfo_live( &info ) ) ) return;
     490           0 :   if( FD_UNLIKELY( !info.topo_layout_hash ) ) return;
     491             : 
     492           0 :   if( FD_UNLIKELY( info.topo_layout_hash!=config->topo.layout_hash ) ) {
     493           0 :     if( FD_UNLIKELY( !strcmp( info.commit_ref, fd_commit_ref_cstr ) ) ) {
     494           0 :       FD_LOG_ERR(( "The configuration file provided to this command is not the one the "
     495           0 :                    "running validator was started with.  Rerun the command with the "
     496           0 :                    "validator's configuration file, or without --config to use the "
     497           0 :                    "running validator's configuration automatically." ));
     498           0 :     }
     499           0 :     FD_LOG_ERR(( "This binary is a different version than the running validator.  The "
     500           0 :                  "validator is running commit %s%.11s%s and this binary is commit "
     501           0 :                  "%s%.11s%s.  Rerun the command with the same binary the validator was "
     502           0 :                  "started with.",
     503           0 :                  fd_log_style_bold(), info.commit_ref, fd_log_style_normal(),
     504           0 :                  fd_log_style_bold(), fd_commit_ref_cstr, fd_log_style_normal() ));
     505           0 :   }
     506           0 : }

Generated by: LCOV version 1.14