LCOV - code coverage report
Current view: top level - app/fdctl - config.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 322 549 58.7 %
Date: 2024-11-13 11:58:15 Functions: 15 18 83.3 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : #include "config.h"
       3             : #include "config_parse.h"
       4             : #include "fdctl.h"
       5             : 
       6             : #include "run/topos/topos.h"
       7             : #include "run/run.h"
       8             : 
       9             : #include "../../ballet/toml/fd_toml.h"
      10             : #include "../../disco/topo/fd_topob.h"
      11             : #include "../../disco/topo/fd_pod_format.h"
      12             : #include "../../flamenco/runtime/fd_blockstore.h"
      13             : #include "../../flamenco/runtime/fd_txncache.h"
      14             : #include "../../funk/fd_funk.h"
      15             : #include "../../util/net/fd_eth.h"
      16             : #include "../../util/net/fd_ip4.h"
      17             : #include "../../util/tile/fd_tile_private.h"
      18             : 
      19             : #include <fcntl.h>
      20             : #include <pwd.h>
      21             : #include <stdio.h>
      22             : #include <stdlib.h>
      23             : #include <unistd.h>
      24             : #include <net/if.h>
      25             : #include <linux/if.h>
      26             : #include <arpa/inet.h>
      27             : #include <sys/mman.h>
      28             : #include <sys/stat.h>
      29             : #include <sys/sysinfo.h>
      30             : #include <sys/ioctl.h>
      31             : #include <sys/utsname.h>
      32             : #include <sys/wait.h>
      33             : 
      34             : void
      35             : replace( char *       in,
      36             :          const char * pat,
      37          18 :          const char * sub ) {
      38          18 :   char * replace = strstr( in, pat );
      39          18 :   if( FD_LIKELY( replace ) ) {
      40           6 :     ulong pat_len = strlen( pat );
      41           6 :     ulong sub_len = strlen( sub );
      42           6 :     ulong in_len  = strlen( in );
      43           6 :     if( FD_UNLIKELY( pat_len > in_len ) ) return;
      44             : 
      45           6 :     ulong total_len = in_len - pat_len + sub_len;
      46           6 :     if( FD_UNLIKELY( total_len >= PATH_MAX ) )
      47           0 :       FD_LOG_ERR(( "configuration scratch directory path too long: `%s`", in ));
      48             : 
      49           6 :     uchar after[PATH_MAX] = {0};
      50           6 :     fd_memcpy( after, replace + pat_len, strlen( replace + pat_len ) );
      51           6 :     fd_memcpy( replace, sub, sub_len );
      52           6 :     ulong after_len = strlen( ( const char * ) after );
      53           6 :     fd_memcpy( replace + sub_len, after, after_len );
      54           6 :     in[ total_len ] = '\0';
      55           6 :   }
      56          18 : }
      57             : 
      58             : static void
      59             : fdctl_cfg_load_buf( config_t *   out,
      60             :                     char const * buf,
      61             :                     ulong        sz,
      62           3 :                     char const * path ) {
      63             : 
      64           3 :   static uchar pod_mem[ 1UL<<30 ];
      65           3 :   uchar * pod = fd_pod_join( fd_pod_new( pod_mem, sizeof(pod_mem) ) );
      66             : 
      67           3 :   uchar scratch[ 4096 ];
      68           3 :   long toml_err = fd_toml_parse( buf, sz, pod, scratch, sizeof(scratch) );
      69           3 :   if( FD_UNLIKELY( toml_err!=FD_TOML_SUCCESS ) ) {
      70           0 :     FD_LOG_ERR(( "Invalid config (%s)", path ));
      71           0 :   }
      72             : 
      73           3 :   if( FD_UNLIKELY( !fdctl_pod_to_cfg( out, pod ) ) ) {
      74           0 :     FD_LOG_ERR(( "Invalid config (%s)", path ));
      75           0 :   }
      76             : 
      77           3 :   fd_pod_delete( fd_pod_leave( pod ) );
      78           3 : }
      79             : 
      80             : static void
      81             : fdctl_cfg_load_file( config_t *   out,
      82           0 :                      char const * path ) {
      83             : 
      84           0 :   int fd = open( path, O_RDONLY );
      85           0 :   if( FD_UNLIKELY( fd<0 ) ) {
      86           0 :     FD_LOG_ERR(( "open(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
      87           0 :   }
      88             : 
      89           0 :   struct stat st;
      90           0 :   if( FD_UNLIKELY( fstat( fd, &st ) ) ) {
      91           0 :     FD_LOG_ERR(( "fstat(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
      92           0 :   }
      93           0 :   ulong toml_sz = (ulong)st.st_size;
      94             : 
      95           0 :   void * mem = mmap( NULL, toml_sz, PROT_READ, MAP_PRIVATE, fd, 0 );
      96           0 :   if( FD_UNLIKELY( mem==MAP_FAILED ) ) {
      97           0 :     FD_LOG_ERR(( "mmap(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
      98           0 :   }
      99             : 
     100           0 :   if( FD_UNLIKELY( 0!=close( fd ) ) ) {
     101           0 :     FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     102           0 :   }
     103             : 
     104           0 :   fdctl_cfg_load_buf( out, mem, toml_sz, path );
     105             : 
     106           0 :   if( FD_UNLIKELY( 0!=munmap( mem, toml_sz ) ) ) {
     107           0 :     FD_LOG_ERR(( "munmap(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     108           0 :   }
     109           0 : }
     110             : 
     111             : static uint
     112           3 : listen_address( const char * interface ) {
     113           3 :   int fd = socket( AF_INET, SOCK_DGRAM, 0 );
     114           3 :   struct ifreq ifr = {0};
     115           3 :   ifr.ifr_addr.sa_family = AF_INET;
     116           3 :   strncpy( ifr.ifr_name, interface, IF_NAMESIZE );
     117           3 :   if( FD_UNLIKELY( ioctl( fd, SIOCGIFADDR, &ifr ) ) )
     118           0 :     FD_LOG_ERR(( "could not get IP address of interface `%s` (%i-%s)", interface, errno, fd_io_strerror( errno ) ));
     119           3 :   if( FD_UNLIKELY( close(fd) ) )
     120           0 :     FD_LOG_ERR(( "could not close socket (%i-%s)", errno, fd_io_strerror( errno ) ));
     121           3 :   return ((struct sockaddr_in *)fd_type_pun( &ifr.ifr_addr ))->sin_addr.s_addr;
     122           3 : }
     123             : 
     124             : static void
     125             : mac_address( const char * interface,
     126           3 :              uchar *      mac ) {
     127           3 :   int fd = socket( AF_INET, SOCK_DGRAM, IPPROTO_IP );
     128           3 :   struct ifreq ifr;
     129           3 :   ifr.ifr_addr.sa_family = AF_INET;
     130           3 :   strncpy( ifr.ifr_name, interface, IF_NAMESIZE );
     131           3 :   if( FD_UNLIKELY( ioctl( fd, SIOCGIFHWADDR, &ifr ) ) )
     132           0 :     FD_LOG_ERR(( "could not get MAC address of interface `%s`: (%i-%s)", interface, errno, fd_io_strerror( errno ) ));
     133           3 :   if( FD_UNLIKELY( close(fd) ) )
     134           0 :     FD_LOG_ERR(( "could not close socket (%i-%s)", errno, fd_io_strerror( errno ) ));
     135           3 :   fd_memcpy( mac, ifr.ifr_hwaddr.sa_data, 6 );
     136           3 : }
     137             : 
     138             : static void
     139           3 : username_to_id( config_t * config ) {
     140           3 :   uint * results = mmap( NULL, 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0 );
     141           3 :   if( FD_UNLIKELY( results==MAP_FAILED ) ) FD_LOG_ERR(( "mmap() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     142             : 
     143           3 :   results[ 0 ] = UINT_MAX;
     144           3 :   results[ 1 ] = UINT_MAX;
     145             : 
     146             :   /* This is extremely unfortunate.  We just want to call getpwnam but
     147             :      on various glibc it can open `/var/lib/sss/mc/passwd` and then not
     148             :      close it.  We could go and find this file descriptor and close it
     149             :      for the library, but that is a bit of a hack.  Instead we fork a
     150             :      new process to call getpwnam and then exit.
     151             : 
     152             :      We could try just reading /etc/passwd here instead, but the glibc
     153             :      getpwnam implementation does a lot of things we need, including
     154             :      potentially reading from NCSD or SSSD. */
     155             : 
     156           3 :   pid_t pid = fork();
     157           3 :   if( FD_UNLIKELY( pid == -1 ) ) FD_LOG_ERR(( "fork() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     158             : 
     159           3 :   if( FD_LIKELY( !pid ) ) {
     160           0 :     char buf[ 16384 ];
     161           0 :     struct passwd pwd;
     162           0 :     struct passwd * result;
     163           0 :     int error = getpwnam_r( config->user, &pwd, buf, sizeof(buf), &result );
     164           0 :     if( FD_UNLIKELY( error ) ) {
     165           0 :       if( FD_LIKELY( error==ENOENT || error==ESRCH ) ) FD_LOG_ERR(( "configuration file wants firedancer to run as user `%s` but it does not exist", config->user ));
     166           0 :       else FD_LOG_ERR(( "could not get user id for `%s` (%i-%s)", config->user, errno, fd_io_strerror( errno ) ));
     167           0 :     }
     168           0 :     if( FD_UNLIKELY( !result ) ) FD_LOG_ERR(( "configuration file wants firedancer to run as user `%s` but it does not exist", config->user ));
     169           0 :     results[ 0 ] = pwd.pw_uid;
     170           0 :     results[ 1 ] = pwd.pw_gid;
     171           0 :     exit_group( 0 );
     172           3 :   } else {
     173           3 :     int wstatus;
     174           3 :     if( FD_UNLIKELY( waitpid( pid, &wstatus, 0 )==-1 ) ) FD_LOG_ERR(( "waitpid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     175           3 :     if( FD_UNLIKELY( WIFSIGNALED( wstatus ) ) )
     176           0 :       FD_LOG_ERR(( "uid fetch process terminated by signal %i-%s", WTERMSIG( wstatus ), fd_io_strsignal( WTERMSIG( wstatus ) ) ));
     177           3 :     if( FD_UNLIKELY( WEXITSTATUS( wstatus ) ) )
     178           0 :       FD_LOG_ERR(( "uid fetch process exited with status %i", WEXITSTATUS( wstatus ) ));
     179           3 :   }
     180             : 
     181           3 :   if( FD_UNLIKELY( results[ 0 ]==UINT_MAX || results[ 1 ]==UINT_MAX ) ) FD_LOG_ERR(( "could not get user id for `%s`", config->user ));
     182           3 :   config->uid = results[ 0 ];
     183           3 :   config->gid = results[ 1 ];
     184             : 
     185           3 :   if( FD_UNLIKELY( munmap( results, 4096 ) ) ) FD_LOG_ERR(( "munmap() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     186           3 : }
     187             : 
     188             : FD_FN_CONST fd_topo_run_tile_t *
     189         153 : fd_topo_tile_to_config( fd_topo_tile_t const * tile ) {
     190         153 :   fd_topo_run_tile_t ** run = TILES;
     191         963 :   while( *run ) {
     192         963 :     if( FD_LIKELY( !strcmp( (*run)->name, tile->name ) ) ) return *run;
     193         810 :     run++;
     194         810 :   }
     195           0 :   FD_LOG_ERR(( "unknown tile name `%s`", tile->name ));
     196           0 : }
     197             : 
     198             : ulong
     199             : fdctl_obj_align( fd_topo_t const *     topo,
     200         342 :                  fd_topo_obj_t const * obj ) {
     201         342 :   if( FD_UNLIKELY( !strcmp( obj->name, "tile" ) ) ) {
     202          51 :     fd_topo_tile_t const * tile = NULL;
     203         459 :     for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
     204         459 :       if( FD_LIKELY( topo->tiles[ i ].tile_obj_id==obj->id ) ) {
     205          51 :         tile = &topo->tiles[ i ];
     206          51 :         break;
     207          51 :       }
     208         459 :     }
     209          51 :     fd_topo_run_tile_t * config = fd_topo_tile_to_config( tile );
     210          51 :     if( FD_LIKELY( config->scratch_align ) ) return config->scratch_align();
     211           0 :     return 1UL;
     212         291 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "mcache" ) ) ) {
     213          69 :     return fd_mcache_align();
     214         222 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "dcache" ) ) ) {
     215          66 :     return fd_dcache_align();
     216         156 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "cnc" ) ) ) {
     217           0 :     return fd_cnc_align();
     218         156 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "reasm" ) ) ) {
     219           3 :     return fd_tpu_reasm_align();
     220         153 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "fseq" ) ) ) {
     221         102 :     return fd_fseq_align();
     222         102 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "metrics" ) ) ) {
     223          51 :     return FD_METRICS_ALIGN;
     224          51 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "blockstore" ) ) ) {
     225           0 :     return fd_blockstore_align();
     226           0 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "funk" ) ) ) {
     227           0 :     return fd_funk_align();
     228           0 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "txncache" ) ) ) {
     229           0 :     return fd_txncache_align();
     230           0 :   } else {
     231           0 :     FD_LOG_ERR(( "unknown object `%s`", obj->name ));
     232           0 :     return 0UL;
     233           0 :   }
     234         342 : }
     235             : 
     236             : ulong
     237             : fdctl_obj_footprint( fd_topo_t const *     topo,
     238         342 :                      fd_topo_obj_t const * obj ) {
     239         342 :   #define VAL(name) (__extension__({                                                               \
     240         273 :       ulong __x = fd_pod_queryf_ulong( topo->props, ULONG_MAX, "obj.%lu.%s", obj->id, name );      \
     241         273 :       if( FD_UNLIKELY( __x==ULONG_MAX ) ) FD_LOG_ERR(( "obj.%lu.%s was not set", obj->id, name )); \
     242         273 :       __x; }))
     243             : 
     244         342 :   if( FD_UNLIKELY( !strcmp( obj->name, "tile" ) ) ) {
     245          51 :     fd_topo_tile_t const * tile = NULL;
     246         459 :     for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
     247         459 :       if( FD_LIKELY( topo->tiles[ i ].tile_obj_id==obj->id ) ) {
     248          51 :         tile = &topo->tiles[ i ];
     249          51 :         break;
     250          51 :       }
     251         459 :     }
     252          51 :     fd_topo_run_tile_t * config = fd_topo_tile_to_config( tile );
     253          51 :     if( FD_LIKELY( config->scratch_footprint ) ) return config->scratch_footprint( tile );
     254           0 :     return 0UL;
     255         291 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "mcache" ) ) ) {
     256          69 :     return fd_mcache_footprint( VAL("depth"), 0UL );
     257         222 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "dcache" ) ) ) {
     258          66 :     return fd_dcache_footprint( fd_dcache_req_data_sz( VAL("mtu"), VAL("depth"), VAL("burst"), 1), 0UL );
     259         156 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "cnc" ) ) ) {
     260           0 :     return fd_cnc_footprint( 0UL );
     261         156 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "reasm" ) ) ) {
     262           3 :     return fd_tpu_reasm_footprint( VAL("depth"), VAL("burst") );
     263         153 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "fseq" ) ) ) {
     264         102 :     return fd_fseq_footprint();
     265         102 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "metrics" ) ) ) {
     266          51 :     return FD_METRICS_FOOTPRINT( VAL("in_cnt"), VAL("cons_cnt") );
     267          51 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "blockstore" ) ) ) {
     268           0 :     return fd_blockstore_footprint();
     269           0 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "funk" ) ) ) {
     270           0 :     return fd_funk_footprint();
     271           0 :   } else if( FD_UNLIKELY( !strcmp( obj->name, "txncache" ) ) ) {
     272           0 :     return fd_txncache_footprint( VAL("max_rooted_slots"), VAL("max_live_slots"), VAL("max_txn_per_slot") );
     273           0 :   } else {
     274           0 :     FD_LOG_ERR(( "unknown object `%s`", obj->name ));
     275           0 :     return 0UL;
     276           0 :   }
     277         342 : #undef VAL
     278         342 : }
     279             : 
     280             : ulong
     281             : fdctl_obj_loose( fd_topo_t const *     topo,
     282         342 :                  fd_topo_obj_t const * obj ) {
     283         342 :   ulong loose = fd_pod_queryf_ulong( topo->props, ULONG_MAX, "obj.%lu.%s", obj->id, "loose" );
     284         342 :   if( loose!=ULONG_MAX ) {
     285           0 :     return loose;
     286           0 :   }
     287             : 
     288         342 :   if( FD_UNLIKELY( !strcmp( obj->name, "tile" ) ) ) {
     289          51 :     fd_topo_tile_t const * tile = NULL;
     290         459 :     for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
     291         459 :       if( FD_LIKELY( topo->tiles[ i ].tile_obj_id==obj->id ) ) {
     292          51 :         tile = &topo->tiles[ i ];
     293          51 :         break;
     294          51 :       }
     295         459 :     }
     296          51 :     fd_topo_run_tile_t * config = fd_topo_tile_to_config( tile );
     297          51 :     if( FD_LIKELY( config->loose_footprint ) ) return config->loose_footprint( tile );
     298          51 :   }
     299         342 :   return 0UL;
     300         342 : }
     301             : 
     302             : fd_topo_run_tile_t
     303           0 : fdctl_tile_run( fd_topo_tile_t * tile ) {
     304           0 :   return *fd_topo_tile_to_config( tile );
     305           0 : }
     306             : 
     307             : 
     308             : static void
     309           3 : validate_ports( config_t * result ) {
     310           3 :   char dynamic_port_range[ 32 ];
     311           3 :   fd_memcpy( dynamic_port_range, result->dynamic_port_range, sizeof(dynamic_port_range) );
     312             : 
     313           3 :   char * dash = strstr( dynamic_port_range, "-" );
     314           3 :   if( FD_UNLIKELY( !dash ) )
     315           0 :     FD_LOG_ERR(( "configuration specifies invalid [dynamic_port_range] `%s`. "
     316           3 :                  "This must be formatted like `<min>-<max>`",
     317           3 :                  result->dynamic_port_range ));
     318             : 
     319           3 :   *dash = '\0';
     320           3 :   char * endptr;
     321           3 :   ulong agave_port_min = strtoul( dynamic_port_range, &endptr, 10 );
     322           3 :   if( FD_UNLIKELY( *endptr != '\0' || agave_port_min > USHORT_MAX ) )
     323           0 :     FD_LOG_ERR(( "configuration specifies invalid [dynamic_port_range] `%s`. "
     324           3 :                  "This must be formatted like `<min>-<max>`",
     325           3 :                  result->dynamic_port_range ));
     326           3 :   ulong agave_port_max = strtoul( dash + 1, &endptr, 10 );
     327           3 :   if( FD_UNLIKELY( *endptr != '\0' || agave_port_max > USHORT_MAX ) )
     328           0 :     FD_LOG_ERR(( "configuration specifies invalid [dynamic_port_range] `%s`. "
     329           3 :                  "This must be formatted like `<min>-<max>`",
     330           3 :                  result->dynamic_port_range ));
     331           3 :   if( FD_UNLIKELY( agave_port_min > agave_port_max ) )
     332           0 :     FD_LOG_ERR(( "configuration specifies invalid [dynamic_port_range] `%s`. "
     333           3 :                  "The minimum port must be less than or equal to the maximum port",
     334           3 :                  result->dynamic_port_range ));
     335             : 
     336           3 :   if( FD_UNLIKELY( result->tiles.quic.regular_transaction_listen_port >= agave_port_min &&
     337           3 :                    result->tiles.quic.regular_transaction_listen_port < agave_port_max ) )
     338           0 :     FD_LOG_ERR(( "configuration specifies invalid [tiles.quic.transaction_listen_port] `%hu`. "
     339           3 :                  "This must be outside the dynamic port range `%s`",
     340           3 :                  result->tiles.quic.regular_transaction_listen_port,
     341           3 :                  result->dynamic_port_range ));
     342             : 
     343           3 :   if( FD_UNLIKELY( result->tiles.quic.quic_transaction_listen_port >= agave_port_min &&
     344           3 :                    result->tiles.quic.quic_transaction_listen_port < agave_port_max ) )
     345           0 :     FD_LOG_ERR(( "configuration specifies invalid [tiles.quic.quic_transaction_listen_port] `%hu`. "
     346           3 :                  "This must be outside the dynamic port range `%s`",
     347           3 :                  result->tiles.quic.quic_transaction_listen_port,
     348           3 :                  result->dynamic_port_range ));
     349             : 
     350           3 :   if( FD_UNLIKELY( result->tiles.shred.shred_listen_port >= agave_port_min &&
     351           3 :                    result->tiles.shred.shred_listen_port < agave_port_max ) )
     352           0 :     FD_LOG_ERR(( "configuration specifies invalid [tiles.shred.shred_listen_port] `%hu`. "
     353           3 :                  "This must be outside the dynamic port range `%s`",
     354           3 :                  result->tiles.shred.shred_listen_port,
     355           3 :                  result->dynamic_port_range ));
     356           3 : }
     357             : 
     358             : /* These CLUSTER_* values must be ordered from least important to most
     359             :    important network.  Eg, it's important that if a config has the
     360             :    MAINNET_BETA genesis hash, but has a bunch of entrypoints that we
     361             :    recognize as TESTNET, we classify it as MAINNET_BETA so we can be
     362             :    maximally restrictive.  This is done by a high-to-low comparison. */
     363           9 : #define FD_CONFIG_CLUSTER_UNKNOWN      (0UL)
     364           0 : #define FD_CONFIG_CLUSTER_PYTHTEST     (1UL)
     365           0 : #define FD_CONFIG_CLUSTER_TESTNET      (2UL)
     366           0 : #define FD_CONFIG_CLUSTER_DEVNET       (3UL)
     367           0 : #define FD_CONFIG_CLUSTER_PYTHNET      (4UL)
     368           0 : #define FD_CONFIG_CLUSTER_MAINNET_BETA (5UL)
     369             : 
     370             : FD_FN_PURE static ulong
     371             : determine_cluster( ulong  entrypoints_cnt,
     372             :                    char   entrypoints[16][256],
     373           3 :                    char * expected_genesis_hash ) {
     374           3 :   char const * DEVNET_GENESIS_HASH = "EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG";
     375           3 :   char const * TESTNET_GENESIS_HASH = "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY";
     376           3 :   char const * MAINNET_BETA_GENESIS_HASH = "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d";
     377           3 :   char const * PYTHTEST_GENESIS_HASH = "EkCkB7RWVrgkcpariRpd3pjf7GwiCMZaMHKUpB5Na1Ve";
     378           3 :   char const * PYTHNET_GENESIS_HASH = "GLKkBUr6r72nBtGrtBPJLRqtsh8wXZanX4xfnqKnWwKq";
     379             : 
     380           3 :   char const * DEVNET_ENTRYPOINT_URI = "devnet.solana.com";
     381           3 :   char const * TESTNET_ENTRYPOINT_URI = "testnet.solana.com";
     382           3 :   char const * MAINNET_BETA_ENTRYPOINT_URI = "mainnet-beta.solana.com";
     383           3 :   char const * PYTHTEST_ENTRYPOINT_URI = "pythtest.pyth.network";
     384           3 :   char const * PYTHNET_ENTRYPOINT_URI = "pythnet.pyth.network";
     385             : 
     386           3 :   ulong cluster = FD_CONFIG_CLUSTER_UNKNOWN;
     387           3 :   if( FD_LIKELY( expected_genesis_hash ) ) {
     388           3 :     if( FD_UNLIKELY( !strcmp( expected_genesis_hash, DEVNET_GENESIS_HASH ) ) )            cluster = FD_CONFIG_CLUSTER_DEVNET;
     389           3 :     else if( FD_UNLIKELY( !strcmp( expected_genesis_hash, TESTNET_GENESIS_HASH ) ) )      cluster = FD_CONFIG_CLUSTER_TESTNET;
     390           3 :     else if( FD_UNLIKELY( !strcmp( expected_genesis_hash, MAINNET_BETA_GENESIS_HASH ) ) ) cluster = FD_CONFIG_CLUSTER_MAINNET_BETA;
     391           3 :     else if( FD_UNLIKELY( !strcmp( expected_genesis_hash, PYTHTEST_GENESIS_HASH ) ) )     cluster = FD_CONFIG_CLUSTER_PYTHTEST;
     392           3 :     else if( FD_UNLIKELY( !strcmp( expected_genesis_hash, PYTHNET_GENESIS_HASH ) ) )      cluster = FD_CONFIG_CLUSTER_PYTHNET;
     393           3 :   }
     394             : 
     395           3 :   for( ulong i=0; i<entrypoints_cnt; i++ ) {
     396           0 :     if( FD_UNLIKELY( strstr( entrypoints[ i ], DEVNET_ENTRYPOINT_URI ) ) )            cluster = fd_ulong_max( cluster, FD_CONFIG_CLUSTER_DEVNET );
     397           0 :     else if( FD_UNLIKELY( strstr( entrypoints[ i ], TESTNET_ENTRYPOINT_URI ) ) )      cluster = fd_ulong_max( cluster, FD_CONFIG_CLUSTER_TESTNET );
     398           0 :     else if( FD_UNLIKELY( strstr( entrypoints[ i ], MAINNET_BETA_ENTRYPOINT_URI ) ) ) cluster = fd_ulong_max( cluster, FD_CONFIG_CLUSTER_MAINNET_BETA );
     399           0 :     else if( FD_UNLIKELY( strstr( entrypoints[ i ], PYTHTEST_ENTRYPOINT_URI ) ) )     cluster = fd_ulong_max( cluster, FD_CONFIG_CLUSTER_PYTHTEST );
     400           0 :     else if( FD_UNLIKELY( strstr( entrypoints[ i ], PYTHNET_ENTRYPOINT_URI ) ) )      cluster = fd_ulong_max( cluster, FD_CONFIG_CLUSTER_PYTHNET );
     401           0 :   }
     402             : 
     403           3 :   return cluster;
     404           3 : }
     405             : 
     406             : FD_FN_CONST static int
     407           9 : parse_log_level( char const * level ) {
     408           9 :   if( FD_UNLIKELY( !strcmp( level, "DEBUG" ) ) )    return 0;
     409           9 :   if( FD_UNLIKELY( !strcmp( level, "INFO"  ) ) )    return 1;
     410           6 :   if( FD_UNLIKELY( !strcmp( level, "NOTICE"  ) ) )  return 2;
     411           3 :   if( FD_UNLIKELY( !strcmp( level, "WARNING"  ) ) ) return 3;
     412           0 :   if( FD_UNLIKELY( !strcmp( level, "ERR" ) ) )      return 4;
     413           0 :   if( FD_UNLIKELY( !strcmp( level, "CRIT" ) ) )     return 5;
     414           0 :   if( FD_UNLIKELY( !strcmp( level, "ALERT" ) ) )    return 6;
     415           0 :   if( FD_UNLIKELY( !strcmp( level, "EMERG" ) ) )    return 7;
     416           0 :   return -1;
     417           0 : }
     418             : 
     419             : FD_FN_CONST static char *
     420           3 : cluster_to_cstr( ulong cluster ) {
     421           3 :   switch( cluster ) {
     422           3 :     case FD_CONFIG_CLUSTER_UNKNOWN:      return "unknown";
     423           0 :     case FD_CONFIG_CLUSTER_PYTHTEST:     return "pythtest";
     424           0 :     case FD_CONFIG_CLUSTER_TESTNET:      return "testnet";
     425           0 :     case FD_CONFIG_CLUSTER_DEVNET:       return "devnet";
     426           0 :     case FD_CONFIG_CLUSTER_PYTHNET:      return "pythnet";
     427           0 :     case FD_CONFIG_CLUSTER_MAINNET_BETA: return "mainnet-beta";
     428           0 :     default:                             return "unknown";
     429           3 :   }
     430           3 : }
     431             : 
     432             : static char *
     433           3 : default_user( void ) {
     434           3 :   char * name = getenv( "SUDO_USER" );
     435           3 :   if( FD_UNLIKELY( name ) ) return name;
     436             : 
     437           0 :   name = getenv( "LOGNAME" );
     438           0 :   if( FD_LIKELY( name ) ) return name;
     439             : 
     440           0 :   name = getenv( "USER" );
     441           0 :   if( FD_LIKELY( name ) ) return name;
     442             : 
     443           0 :   name = getenv( "LNAME" );
     444           0 :   if( FD_LIKELY( name ) ) return name;
     445             : 
     446           0 :   name = getenv( "USERNAME" );
     447           0 :   if( FD_LIKELY( name ) ) return name;
     448             : 
     449           0 :   name = getlogin();
     450           0 :   if( FD_UNLIKELY( !name && (errno==ENXIO || errno==ENOTTY) ) ) return NULL;
     451           0 :   else if( FD_UNLIKELY( !name ) ) FD_LOG_ERR(( "getlogin failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     452           0 :   return name;
     453           0 : }
     454             : 
     455             : void
     456             : fdctl_cfg_from_env( int *      pargc,
     457             :                     char ***   pargv,
     458           3 :                     config_t * config ) {
     459             : 
     460           3 :   memset( config, 0, sizeof(config_t) );
     461             : #if FD_HAS_NO_AGAVE
     462             :   static uchar pod_mem1[ 1UL<<20 ];
     463             :   static uchar pod_mem2[ 1UL<<20 ];
     464             :   uchar * pod1 = fd_pod_join( fd_pod_new( pod_mem1, sizeof(pod_mem1) ) );
     465             :   uchar * pod2 = fd_pod_join( fd_pod_new( pod_mem2, sizeof(pod_mem2) ) );
     466             :  
     467             :   uchar scratch[ 4096 ];
     468             :   long toml_err = fd_toml_parse( fdctl_default_config, fdctl_default_config_sz, pod1, scratch, sizeof(scratch) );
     469             :   if( FD_UNLIKELY( toml_err!=FD_TOML_SUCCESS ) ) FD_LOG_ERR(( "Invalid config (%s)", "default.toml" ));
     470             :   toml_err = fd_toml_parse( fdctl_default_firedancer_config, fdctl_default_firedancer_config_sz, pod2, scratch, sizeof(scratch) );
     471             :   if( FD_UNLIKELY( toml_err!=FD_TOML_SUCCESS ) ) FD_LOG_ERR(( "Invalid config (%s)", "default-firedancer.toml" ));
     472             :  
     473             :   if( FD_UNLIKELY( !fdctl_pod_to_cfg( config, pod1 ) ) ) FD_LOG_ERR(( "Invalid config (%s)", "default.toml" ));
     474             :   if( FD_UNLIKELY( !fdctl_pod_to_cfg( config, pod2 ) ) ) FD_LOG_ERR(( "Invalid config (%s)", "default-firedancer.toml" ));
     475             :   fd_pod_delete( fd_pod_leave( pod1 ) );
     476             :   fd_pod_delete( fd_pod_leave( pod2 ) );
     477             : #else
     478           3 :   fdctl_cfg_load_buf( config, (char const *)fdctl_default_config, fdctl_default_config_sz, "default" );
     479           3 : #endif
     480             : 
     481           3 :   const char * user_config = fd_env_strip_cmdline_cstr(
     482           3 :       pargc,
     483           3 :       pargv,
     484           3 :       "--config",
     485           3 :       "FIREDANCER_CONFIG_TOML",
     486           3 :       NULL );
     487             : 
     488           3 :   if( FD_LIKELY( user_config ) ) {
     489           0 :     fdctl_cfg_load_file( config, user_config );
     490           0 :   }
     491             : 
     492           3 :   int netns = fd_env_strip_cmdline_contains( pargc, pargv, "--netns" );
     493           3 :   if( FD_UNLIKELY( netns ) ) {
     494           0 :     config->development.netns.enabled = 1;
     495           0 :     strncpy( config->tiles.net.interface,
     496           0 :              config->development.netns.interface0,
     497           0 :              sizeof(config->tiles.net.interface) );
     498           0 :     config->tiles.net.interface[ sizeof(config->tiles.net.interface) - 1 ] = '\0';
     499           0 :   }
     500             : 
     501           3 :   if( FD_UNLIKELY( !strcmp( config->user, "" ) ) ) {
     502           3 :     const char * user = default_user();
     503           3 :     if( FD_UNLIKELY( !user ) ) FD_LOG_ERR(( "could not automatically determine a user to run Firedancer as. You must specify a [user] in your configuration TOML file." ));
     504           3 :     if( FD_UNLIKELY( strlen( user ) >= sizeof( config->user ) ) )
     505           0 :                               FD_LOG_ERR(( "user name `%s` is too long", user ));
     506           3 :     strncpy( config->user, user, 256 );
     507           3 :   }
     508             : 
     509           3 :   struct utsname utsname;
     510           3 :   if( FD_UNLIKELY( -1==uname( &utsname ) ) )
     511           0 :     FD_LOG_ERR(( "could not get uname (%i-%s)", errno, fd_io_strerror( errno ) ));
     512           3 :   strncpy( config->hostname, utsname.nodename, sizeof(config->hostname) );
     513           3 :   config->hostname[ sizeof(config->hostname)-1UL ] = '\0'; /* Just truncate the name if it's too long to fit */
     514             : 
     515           3 :   if( FD_UNLIKELY( !strcmp( config->tiles.net.interface, "" ) && !config->development.netns.enabled ) ) {
     516           3 :     int ifindex = internet_routing_interface();
     517           3 :     if( FD_UNLIKELY( ifindex == -1 ) )
     518           0 :       FD_LOG_ERR(( "no network device found which routes to 8.8.8.8. If no network "
     519           3 :                    "interface is specified in the configuration file, Firedancer "
     520           3 :                    "tries to use the first network interface found which routes to "
     521           3 :                    "8.8.8.8. You can see what this is by running `ip route get 8.8.8.8` "
     522           3 :                    "You can fix this error by specifying a network interface to bind to in "
     523           3 :                    "your configuration file under [net.interface]" ));
     524             : 
     525           3 :     if( FD_UNLIKELY( !if_indextoname( (uint)ifindex, config->tiles.net.interface ) ) )
     526           0 :       FD_LOG_ERR(( "could not get name of interface with index %d", ifindex ));
     527           3 :   }
     528             : 
     529           3 :   ulong cluster = determine_cluster( config->gossip.entrypoints_cnt,
     530           3 :                                      config->gossip.entrypoints,
     531           3 :                                      config->consensus.expected_genesis_hash );
     532           3 :   config->is_live_cluster = cluster != FD_CONFIG_CLUSTER_UNKNOWN;
     533             : 
     534           3 :   if( FD_UNLIKELY( config->development.netns.enabled ) ) {
     535             :     /* not currently supporting multihoming on netns */
     536           0 :     if( FD_UNLIKELY( strcmp( config->development.netns.interface0, config->tiles.net.interface ) ) )
     537           0 :       FD_LOG_ERR(( "netns interface and firedancer interface are different. If you are using the "
     538           0 :                    "[development.netns] functionality to run Firedancer in a network namespace "
     539           0 :                    "for development, the configuration file must specify that "
     540           0 :                    "[development.netns.interface0] is the same as [net.interface]" ));
     541             : 
     542           0 :     if( FD_UNLIKELY( !fd_cstr_to_ip4_addr( config->development.netns.interface0_addr, &config->tiles.net.ip_addr ) ) )
     543           0 :       FD_LOG_ERR(( "configuration specifies invalid netns IP address `%s`", config->development.netns.interface0_addr ));
     544           0 :     if( FD_UNLIKELY( !fd_cstr_to_mac_addr( config->development.netns.interface0_mac, config->tiles.net.mac_addr ) ) )
     545           0 :       FD_LOG_ERR(( "configuration specifies invalid netns MAC address `%s`", config->development.netns.interface0_mac ));
     546           3 :   } else {
     547           3 :     if( FD_UNLIKELY( !if_nametoindex( config->tiles.net.interface ) ) )
     548           0 :       FD_LOG_ERR(( "configuration specifies network interface `%s` which does not exist", config->tiles.net.interface ));
     549           3 :     uint iface_ip = listen_address( config->tiles.net.interface );
     550           3 :     if( FD_UNLIKELY( strcmp( config->gossip.host, "" ) ) ) {
     551           0 :       uint gossip_ip_addr = iface_ip;
     552           0 :       int  has_gossip_ip4 = 0;
     553           0 :       if( FD_UNLIKELY( strlen( config->gossip.host )<=15UL ) ) {
     554             :         /* Only sets gossip_ip_addr if it's a valid IPv4 address, otherwise assume it's a DNS name */
     555           0 :         has_gossip_ip4 = fd_cstr_to_ip4_addr( config->gossip.host, &gossip_ip_addr );
     556           0 :       }
     557           0 :       if ( FD_UNLIKELY( !fd_ip4_addr_is_public( gossip_ip_addr ) && config->is_live_cluster && has_gossip_ip4 ) )
     558           0 :         FD_LOG_ERR(( "Trying to use [gossip.host] " FD_IP4_ADDR_FMT " for listening to incoming "
     559           0 :                      "transactions, but it is part of a private network and will not be routable "
     560           0 :                      "for other Solana network nodes.",
     561           0 :                      FD_IP4_ADDR_FMT_ARGS( iface_ip ) ));
     562           3 :     } else if ( FD_UNLIKELY( !fd_ip4_addr_is_public( iface_ip ) && config->is_live_cluster ) ) {
     563           0 :       FD_LOG_ERR(( "Trying to use network interface `%s` for listening to incoming transactions, "
     564           0 :                    "but it has IPv4 address " FD_IP4_ADDR_FMT " which is part of a private network "
     565           0 :                    "and will not be routable for other Solana network nodes. If you are running "
     566           0 :                    "behind a NAT and this interface is publicly reachable, you can continue by "
     567           0 :                    "manually specifying the IP address to advertise in your configuration under "
     568           0 :                    "[gossip.host].",
     569           0 :                    config->tiles.net.interface, FD_IP4_ADDR_FMT_ARGS( iface_ip ) ));
     570           0 :     }
     571             : 
     572           3 :     config->tiles.net.ip_addr = iface_ip;
     573           3 :     mac_address( config->tiles.net.interface, config->tiles.net.mac_addr );
     574             : 
     575             :     /* support for multihomed hosts */
     576           3 :     ulong multi_cnt = config->tiles.net.multihome_ip_addrs_cnt;
     577           3 :     for( ulong j = 0; j < multi_cnt; ++j ) {
     578           0 :       int success = fd_cstr_to_ip4_addr( config->tiles.net.multihome_ip_addrs[j],
     579           0 :           &config->tiles.net.multihome_ip4_addrs[j] );
     580           0 :       if( !success ) {
     581           0 :         FD_LOG_ERR(( "configuration option [tiles.net.multihome_ip_addrs] "
     582           0 :                      "specifies malformed IP address `%s`",
     583           0 :                      config->tiles.net.multihome_ip_addrs[j] ));
     584           0 :       }
     585           0 :     }
     586             : 
     587             :     /* look for duplicate addresses */
     588             :     /* there's only a few, so do the O(n^2) comparison */
     589           3 :     for( ulong j = 0; j < multi_cnt; ++j ) {
     590           0 :       if( config->tiles.net.ip_addr == config->tiles.net.multihome_ip4_addrs[j] ) {
     591           0 :         FD_LOG_ERR(( "configuration option [tiles.net.multihome_ip_addrs] "
     592           0 :                      "specifies an address that matches [tiles.net.src_ip_addr]" ));
     593           0 :       }
     594           0 :       for( ulong k = j+1; k < multi_cnt; ++k ) {
     595           0 :         if( config->tiles.net.multihome_ip4_addrs[j] == config->tiles.net.multihome_ip4_addrs[k] ) {
     596           0 :           FD_LOG_ERR(( "configuration option [tiles.net.multihome_ip_addrs] "
     597           0 :                        "specifies duplicate ip addresses `%s`",
     598           0 :                        config->tiles.net.multihome_ip_addrs[j] ));
     599           0 :         }
     600           0 :       }
     601           0 :     }
     602             : 
     603           3 :   }
     604             : 
     605           3 :   username_to_id( config );
     606             : 
     607           3 :   if( config->uid == 0 || config->gid == 0 )
     608           0 :     FD_LOG_ERR(( "firedancer cannot run as root. please specify a non-root user in the configuration file" ));
     609             : 
     610           3 :   if( FD_UNLIKELY( getuid() != 0 && config->uid != getuid() ) )
     611           0 :     FD_LOG_ERR(( "running as uid %u, but config specifies uid %u", getuid(), config->uid ));
     612           3 :   if( FD_UNLIKELY( getgid() != 0 && config->gid != getgid() ) )
     613           0 :     FD_LOG_ERR(( "running as gid %u, but config specifies gid %u", getgid(), config->gid ));
     614             : 
     615           3 :   ulong len = strlen( config->hugetlbfs.mount_path );
     616           3 :   if( FD_UNLIKELY( !len ) ) FD_LOG_ERR(( "[hugetlbfs.mount_path] must be non-empty in your configuration file" ));
     617           3 :   FD_TEST( fd_cstr_printf_check( config->hugetlbfs.gigantic_page_mount_path,
     618           3 :                                  sizeof(config->hugetlbfs.gigantic_page_mount_path),
     619           3 :                                  NULL,
     620           3 :                                  "%s/.gigantic",
     621           3 :                                  config->hugetlbfs.mount_path ) );
     622           3 :   FD_TEST( fd_cstr_printf_check( config->hugetlbfs.huge_page_mount_path,
     623           3 :                                  sizeof(config->hugetlbfs.huge_page_mount_path),
     624           3 :                                  NULL,
     625           3 :                                  "%s/.huge",
     626           3 :                                  config->hugetlbfs.mount_path ) );
     627             : 
     628           3 :   replace( config->log.path, "{user}", config->user );
     629           3 :   replace( config->log.path, "{name}", config->name );
     630           3 :   if( FD_LIKELY( !strcmp( "auto", config->log.colorize ) ) )       config->log.colorize1 = 2;
     631           0 :   else if( FD_LIKELY( !strcmp( "true", config->log.colorize ) ) )  config->log.colorize1 = 1;
     632           0 :   else if( FD_LIKELY( !strcmp( "false", config->log.colorize ) ) ) config->log.colorize1 = 0;
     633           0 :   else FD_LOG_ERR(( "[log.colorize] must be one of \"auto\", \"true\", or \"false\"" ));
     634             : 
     635           3 :   if( FD_LIKELY( 2==config->log.colorize1 ) ) {
     636           3 :     char const * cstr = fd_env_strip_cmdline_cstr( NULL, NULL, NULL, "COLORTERM", NULL );
     637           3 :     int truecolor = cstr && !strcmp( cstr, "truecolor" );
     638             : 
     639           3 :     cstr = fd_env_strip_cmdline_cstr( NULL, NULL, NULL, "TERM", NULL );
     640           3 :     int xterm256color = cstr && !strcmp( cstr, "xterm-256color" );
     641             : 
     642           3 :     config->log.colorize1 = truecolor || xterm256color;
     643           3 :   }
     644             : 
     645           3 :   config->log.level_logfile1 = parse_log_level( config->log.level_logfile );
     646           3 :   config->log.level_stderr1  = parse_log_level( config->log.level_stderr );
     647           3 :   config->log.level_flush1   = parse_log_level( config->log.level_flush );
     648           3 :   if( FD_UNLIKELY( -1==config->log.level_logfile1 ) ) FD_LOG_ERR(( "unrecognized [log.level_logfile] `%s`", config->log.level_logfile ));
     649           3 :   if( FD_UNLIKELY( -1==config->log.level_stderr1 ) )  FD_LOG_ERR(( "unrecognized [log.level_stderr] `%s`", config->log.level_logfile ));
     650           3 :   if( FD_UNLIKELY( -1==config->log.level_flush1 ) )   FD_LOG_ERR(( "unrecognized [log.level_flush] `%s`", config->log.level_logfile ));
     651             : 
     652           3 :   replace( config->scratch_directory, "{user}", config->user );
     653           3 :   replace( config->scratch_directory, "{name}", config->name );
     654             : 
     655           3 :   if( FD_UNLIKELY( strcmp( config->ledger.path, "" ) ) ) {
     656           0 :     replace( config->ledger.path, "{user}", config->user );
     657           0 :     replace( config->ledger.path, "{name}", config->name );
     658           3 :   } else {
     659           3 :     FD_TEST( fd_cstr_printf_check( config->ledger.path, sizeof(config->ledger.path), NULL, "%s/ledger", config->scratch_directory ) );
     660           3 :   }
     661             : 
     662           3 :   if( FD_UNLIKELY( strcmp( config->snapshots.path, "" ) ) ) {
     663           0 :     replace( config->snapshots.path, "{user}", config->user );
     664           0 :     replace( config->snapshots.path, "{name}", config->name );
     665           3 :   } else {
     666           3 :     strncpy( config->snapshots.path, config->ledger.path, sizeof(config->snapshots.path) );
     667           3 :   }
     668             : 
     669           3 :   if( FD_UNLIKELY( !strcmp( config->consensus.identity_path, "" ) ) ) {
     670           3 :     FD_TEST( fd_cstr_printf_check( config->consensus.identity_path,
     671           3 :                                    sizeof(config->consensus.identity_path),
     672           3 :                                    NULL,
     673           3 :                                    "%s/identity.json",
     674           3 :                                    config->scratch_directory ) );
     675           3 :   } else {
     676           0 :     replace( config->consensus.identity_path, "{user}", config->user );
     677           0 :     replace( config->consensus.identity_path, "{name}", config->name );
     678           0 :   }
     679             : 
     680             : #if FD_HAS_NO_AGAVE
     681             :   if( FD_UNLIKELY( !strcmp( config->consensus.vote_account_path, "" ) ) ) {
     682             :     FD_TEST( fd_cstr_printf_check( config->consensus.vote_account_path,
     683             :                                    sizeof(config->consensus.vote_account_path),
     684             :                                    NULL,
     685             :                                    "%s/vote-account.json",
     686             :                                    config->scratch_directory ) );
     687             :   }
     688             : #endif
     689           3 :   replace( config->consensus.vote_account_path, "{user}", config->user );
     690           3 :   replace( config->consensus.vote_account_path, "{name}", config->name );
     691             : 
     692           3 :   for( ulong i=0UL; i<config->consensus.authorized_voter_paths_cnt; i++ ) {
     693           0 :     replace( config->consensus.authorized_voter_paths[ i ], "{user}", config->user );
     694           0 :     replace( config->consensus.authorized_voter_paths[ i ], "{name}", config->name );
     695           0 :   }
     696             : 
     697           3 :   strcpy( config->cluster, cluster_to_cstr( cluster ) );
     698             : 
     699             : #ifdef FD_HAS_NO_AGAVE
     700             :   if( FD_UNLIKELY( config->is_live_cluster && cluster!=FD_CONFIG_CLUSTER_TESTNET ) )
     701             :     FD_LOG_ERR(( "Attempted to start against live cluster `%s`. Firedancer is not "
     702             :                  "ready for production deployment, has not been tested, and is "
     703             :                  "missing consensus critical functionality. Joining a live Solana "
     704             :                  "cluster may destabilize the network. Please do not attempt. You "
     705             :                  "can start against the testnet cluster by specifying the testnet "
     706             :                  "entrypoints from https://docs.solana.com/clusters under "
     707             :                  "[gossip.entrypoints] in your configuration file.", cluster_to_cstr( cluster ) ));
     708             : #endif
     709             : 
     710           3 :   if( FD_LIKELY( config->is_live_cluster) ) {
     711           0 :     if( FD_UNLIKELY( !config->development.sandbox ) )
     712           0 :       FD_LOG_ERR(( "trying to join a live cluster, but configuration disables the sandbox which is a a development only feature" ));
     713           0 :     if( FD_UNLIKELY( config->development.no_clone ) )
     714           0 :       FD_LOG_ERR(( "trying to join a live cluster, but configuration disables multiprocess which is a development only feature" ));
     715           0 :     if( FD_UNLIKELY( config->development.netns.enabled ) )
     716           0 :       FD_LOG_ERR(( "trying to join a live cluster, but configuration enables [development.netns] which is a development only feature" ));
     717           0 :     if( FD_UNLIKELY( config->development.bench.larger_max_cost_per_block ) )
     718           0 :       FD_LOG_ERR(( "trying to join a live cluster, but configuration enables [development.bench.larger_max_cost_per_block] which is a development only feature" ));
     719           0 :     if( FD_UNLIKELY( config->development.bench.larger_shred_limits_per_block ) )
     720           0 :       FD_LOG_ERR(( "trying to join a live cluster, but configuration enables [development.bench.larger_shred_limits_per_block] which is a development only feature" ));
     721           0 :     if( FD_UNLIKELY( config->development.bench.disable_blockstore_from_slot ) )
     722           0 :       FD_LOG_ERR(( "trying to join a live cluster, but configuration has a non-zero value for [development.bench.disable_blockstore_from_slot] which is a development only feature" ));
     723           0 :     if( FD_UNLIKELY( config->development.bench.disable_status_cache ) )
     724           0 :       FD_LOG_ERR(( "trying to join a live cluster, but configuration enables [development.bench.disable_status_cache] which is a development only feature" ));
     725           0 :   }
     726             : 
     727           3 :   if( FD_UNLIKELY( config->tiles.quic.quic_transaction_listen_port != config->tiles.quic.regular_transaction_listen_port + 6 ) )
     728           0 :     FD_LOG_ERR(( "configuration specifies invalid [tiles.quic.quic_transaction_listen_port] `%hu`. "
     729           3 :                  "This must be 6 more than [tiles.quic.regular_transaction_listen_port] `%hu`",
     730           3 :                  config->tiles.quic.quic_transaction_listen_port,
     731           3 :                  config->tiles.quic.regular_transaction_listen_port ));
     732             : 
     733           3 :   if( FD_LIKELY( !strcmp( config->consensus.identity_path, "" ) ) ) {
     734           0 :     if( FD_UNLIKELY( config->is_live_cluster ) )
     735           0 :       FD_LOG_ERR(( "configuration file must specify [consensus.identity_path] when joining a live cluster" ));
     736             : 
     737           0 :     FD_TEST( fd_cstr_printf_check( config->consensus.identity_path,
     738           0 :                                    sizeof(config->consensus.identity_path),
     739           0 :                                    NULL,
     740           0 :                                    "%s/identity.json",
     741           0 :                                    config->scratch_directory ) );
     742           0 :   }
     743             : 
     744           3 :   fdctl_cfg_validate( config );
     745           3 :   validate_ports( config );
     746           3 :   fd_topo_initialize( config );
     747           3 : }
     748             : 
     749             : int
     750           0 : fdctl_cfg_to_memfd( config_t * config ) {
     751           0 :   int config_memfd = memfd_create( "fd_config", 0 );
     752           0 :   if( FD_UNLIKELY( -1==config_memfd ) ) FD_LOG_ERR(( "memfd_create() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     753           0 :   if( FD_UNLIKELY( -1==ftruncate( config_memfd, sizeof( config_t ) ) ) ) FD_LOG_ERR(( "ftruncate() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     754             : 
     755           0 :   uchar * bytes = mmap( NULL, sizeof( config_t ), PROT_READ | PROT_WRITE, MAP_SHARED, config_memfd, 0 );
     756           0 :   if( FD_UNLIKELY( bytes == MAP_FAILED ) ) FD_LOG_ERR(( "mmap() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     757           0 :   fd_memcpy( bytes, config, sizeof( config_t ) );
     758           0 :   if( FD_UNLIKELY( munmap( bytes, sizeof( config_t ) ) ) ) FD_LOG_ERR(( "munmap() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     759             : 
     760           0 :   return config_memfd;
     761           0 : }

Generated by: LCOV version 1.14