LCOV - code coverage report
Current view: top level - app/shared - fd_config.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 180 347 51.9 %
Date: 2025-03-20 12:08:36 Functions: 6 8 75.0 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : #include "fd_config.h"
       3             : 
       4             : #include "fd_config_parse.h"
       5             : #include "fd_net_util.h"
       6             : #include "fd_sys_util.h"
       7             : #include "../../ballet/toml/fd_toml.h"
       8             : #include "../../flamenco/genesis/fd_genesis_cluster.h"
       9             : 
      10             : #include <errno.h>
      11             : #include <stdlib.h> /* strtoul */
      12             : #include <sys/mman.h>
      13             : #include <fcntl.h>
      14             : #include <unistd.h>
      15             : #include <sys/utsname.h>
      16             : #include <sys/stat.h>
      17             : 
      18             : extern uchar const fdctl_default_config[];
      19             : extern ulong const fdctl_default_config_sz;
      20             : 
      21             : extern uchar const fdctl_default_firedancer_config[];
      22             : extern ulong const fdctl_default_firedancer_config_sz;
      23             : 
      24             : /* FD_TOML_POD_SZ sets the buffer size of the fd_pod that will hold the
      25             :    parsed config file content.
      26             : 
      27             :    This should be large enough to hold a Firedancer TOML file with all
      28             :    config options set. */
      29             : 
      30             : #define FD_TOML_POD_SZ (1UL<<20)
      31             : 
      32             : FD_FN_CONST static int
      33           9 : parse_log_level( char const * level ) {
      34           9 :   if( FD_UNLIKELY( !strcmp( level, "DEBUG" ) ) )    return 0;
      35           9 :   if( FD_UNLIKELY( !strcmp( level, "INFO"  ) ) )    return 1;
      36           6 :   if( FD_UNLIKELY( !strcmp( level, "NOTICE"  ) ) )  return 2;
      37           3 :   if( FD_UNLIKELY( !strcmp( level, "WARNING"  ) ) ) return 3;
      38           0 :   if( FD_UNLIKELY( !strcmp( level, "ERR" ) ) )      return 4;
      39           0 :   if( FD_UNLIKELY( !strcmp( level, "CRIT" ) ) )     return 5;
      40           0 :   if( FD_UNLIKELY( !strcmp( level, "ALERT" ) ) )    return 6;
      41           0 :   if( FD_UNLIKELY( !strcmp( level, "EMERG" ) ) )    return 7;
      42           0 :   return -1;
      43           0 : }
      44             : 
      45             : /* TODO: Rewrite this ... */
      46             : 
      47             : void
      48             : replace( char *       in,
      49             :          const char * pat,
      50          18 :          const char * sub ) {
      51          18 :   char * replace = strstr( in, pat );
      52          18 :   if( FD_LIKELY( replace ) ) {
      53           6 :     ulong pat_len = strlen( pat );
      54           6 :     ulong sub_len = strlen( sub );
      55           6 :     ulong in_len  = strlen( in );
      56           6 :     if( FD_UNLIKELY( pat_len > in_len ) ) return;
      57             : 
      58           6 :     ulong total_len = in_len - pat_len + sub_len;
      59           6 :     if( FD_UNLIKELY( total_len >= PATH_MAX ) )
      60           0 :       FD_LOG_ERR(( "configuration scratch directory path too long: `%s`", in ));
      61             : 
      62           6 :     uchar after[PATH_MAX] = {0};
      63           6 :     fd_memcpy( after, replace + pat_len, strlen( replace + pat_len ) );
      64           6 :     fd_memcpy( replace, sub, sub_len );
      65           6 :     ulong after_len = strlen( ( const char * ) after );
      66           6 :     fd_memcpy( replace + sub_len, after, after_len );
      67           6 :     in[ total_len ] = '\0';
      68           6 :   }
      69          18 : }
      70             : 
      71             : static void
      72           3 : validate_ports( config_t * result ) {
      73           3 :   char dynamic_port_range[ 32 ];
      74           3 :   fd_memcpy( dynamic_port_range, result->dynamic_port_range, sizeof(dynamic_port_range) );
      75             : 
      76           3 :   char * dash = strstr( dynamic_port_range, "-" );
      77           3 :   if( FD_UNLIKELY( !dash ) )
      78           0 :     FD_LOG_ERR(( "configuration specifies invalid [dynamic_port_range] `%s`. "
      79           3 :                  "This must be formatted like `<min>-<max>`",
      80           3 :                  result->dynamic_port_range ));
      81             : 
      82           3 :   *dash = '\0';
      83           3 :   char * endptr;
      84           3 :   ulong agave_port_min = strtoul( dynamic_port_range, &endptr, 10 );
      85           3 :   if( FD_UNLIKELY( *endptr != '\0' || agave_port_min > USHORT_MAX ) )
      86           0 :     FD_LOG_ERR(( "configuration specifies invalid [dynamic_port_range] `%s`. "
      87           3 :                  "This must be formatted like `<min>-<max>`",
      88           3 :                  result->dynamic_port_range ));
      89           3 :   ulong agave_port_max = strtoul( dash + 1, &endptr, 10 );
      90           3 :   if( FD_UNLIKELY( *endptr != '\0' || agave_port_max > USHORT_MAX ) )
      91           0 :     FD_LOG_ERR(( "configuration specifies invalid [dynamic_port_range] `%s`. "
      92           3 :                  "This must be formatted like `<min>-<max>`",
      93           3 :                  result->dynamic_port_range ));
      94           3 :   if( FD_UNLIKELY( agave_port_min > agave_port_max ) )
      95           0 :     FD_LOG_ERR(( "configuration specifies invalid [dynamic_port_range] `%s`. "
      96           3 :                  "The minimum port must be less than or equal to the maximum port",
      97           3 :                  result->dynamic_port_range ));
      98             : 
      99           3 :   if( FD_UNLIKELY( result->tiles.quic.regular_transaction_listen_port >= agave_port_min &&
     100           3 :                    result->tiles.quic.regular_transaction_listen_port < agave_port_max ) )
     101           0 :     FD_LOG_ERR(( "configuration specifies invalid [tiles.quic.transaction_listen_port] `%hu`. "
     102           3 :                  "This must be outside the dynamic port range `%s`",
     103           3 :                  result->tiles.quic.regular_transaction_listen_port,
     104           3 :                  result->dynamic_port_range ));
     105             : 
     106           3 :   if( FD_UNLIKELY( result->tiles.quic.quic_transaction_listen_port >= agave_port_min &&
     107           3 :                    result->tiles.quic.quic_transaction_listen_port < agave_port_max ) )
     108           0 :     FD_LOG_ERR(( "configuration specifies invalid [tiles.quic.quic_transaction_listen_port] `%hu`. "
     109           3 :                  "This must be outside the dynamic port range `%s`",
     110           3 :                  result->tiles.quic.quic_transaction_listen_port,
     111           3 :                  result->dynamic_port_range ));
     112             : 
     113           3 :   if( FD_UNLIKELY( result->tiles.shred.shred_listen_port >= agave_port_min &&
     114           3 :                    result->tiles.shred.shred_listen_port < agave_port_max ) )
     115           0 :     FD_LOG_ERR(( "configuration specifies invalid [tiles.shred.shred_listen_port] `%hu`. "
     116           3 :                  "This must be outside the dynamic port range `%s`",
     117           3 :                  result->tiles.shred.shred_listen_port,
     118           3 :                  result->dynamic_port_range ));
     119           3 : }
     120             : 
     121             : static void
     122             : fdctl_cfg_load_buf( config_t *   out,
     123             :                     char const * buf,
     124             :                     ulong        sz,
     125           3 :                     char const * path ) {
     126             : 
     127           3 :   static uchar pod_mem[ 1UL<<30 ];
     128           3 :   uchar * pod = fd_pod_join( fd_pod_new( pod_mem, sizeof(pod_mem) ) );
     129             : 
     130           3 :   fd_toml_err_info_t toml_err[1];
     131           3 :   uchar scratch[ 4096 ];
     132           3 :   int toml_errc = fd_toml_parse( buf, sz, pod, scratch, sizeof(scratch), toml_err );
     133           3 :   if( FD_UNLIKELY( toml_errc!=FD_TOML_SUCCESS ) ) {
     134             :     /* Override the default error messages of fd_toml for a better user
     135             :        experience */
     136           0 :     switch( toml_errc ) {
     137           0 :     case FD_TOML_ERR_POD:
     138           0 :       FD_LOG_ERR(( "Failed to parse config file (%s): ran out of buffer space while parsing (Increase FD_TOML_POD_SZ?)", path ));
     139           0 :       break;
     140           0 :     case FD_TOML_ERR_SCRATCH:
     141           0 :       FD_LOG_ERR(( "Failed to parse config file (%s) at line %lu: ran out of scratch space while parsing", path, toml_err->line ));
     142           0 :       break;
     143           0 :     case FD_TOML_ERR_KEY:
     144           0 :       FD_LOG_ERR(( "Failed to parse config file (%s) at line %lu: oversize key", path, toml_err->line ));
     145           0 :       break;
     146           0 :     case FD_TOML_ERR_DUP:
     147           0 :       FD_LOG_ERR(( "Failed to parse config file (%s) at line %lu: duplicate key", path, toml_err->line ));
     148           0 :       break;
     149           0 :     case FD_TOML_ERR_RANGE:
     150           0 :       FD_LOG_ERR(( "Failed to parse config file (%s) at line %lu: invalid value for key", path, toml_err->line ));
     151           0 :       break;
     152           0 :     case FD_TOML_ERR_PARSE:
     153           0 :       FD_LOG_ERR(( "Failed to parse config file (%s) at line %lu", path, toml_err->line ));
     154           0 :       break;
     155           0 :     default:
     156           0 :       FD_LOG_ERR(( "Failed to parse config file (%s): %s", path, fd_toml_strerror( toml_errc ) ));
     157           0 :       break;
     158           0 :     }
     159           0 :   }
     160             : 
     161           3 :   if( FD_UNLIKELY( !fdctl_pod_to_cfg( out, pod ) ) ) {
     162           0 :     FD_LOG_ERR(( "Invalid config (%s)", path ));
     163           0 :   }
     164             : 
     165           3 :   fd_pod_delete( fd_pod_leave( pod ) );
     166           3 : }
     167             : 
     168             : static void
     169             : fdctl_cfg_load_file( config_t *   out,
     170           0 :                      char const * path ) {
     171             : 
     172           0 :   int fd = open( path, O_RDONLY );
     173           0 :   if( FD_UNLIKELY( fd<0 ) ) {
     174           0 :     FD_LOG_ERR(( "open(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     175           0 :   }
     176             : 
     177           0 :   struct stat st;
     178           0 :   if( FD_UNLIKELY( fstat( fd, &st ) ) ) {
     179           0 :     FD_LOG_ERR(( "fstat(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     180           0 :   }
     181           0 :   ulong toml_sz = (ulong)st.st_size;
     182             : 
     183           0 :   if( FD_UNLIKELY( toml_sz==0UL ) ) {
     184           0 :     if( FD_UNLIKELY( 0!=close( fd ) ) ) {
     185           0 :       FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     186           0 :     }
     187           0 :     return;
     188           0 :   }
     189             : 
     190           0 :   void * mem = mmap( NULL, toml_sz, PROT_READ, MAP_PRIVATE, fd, 0 );
     191           0 :   if( FD_UNLIKELY( mem==MAP_FAILED ) ) {
     192           0 :     FD_LOG_ERR(( "mmap(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     193           0 :   }
     194             : 
     195           0 :   if( FD_UNLIKELY( 0!=close( fd ) ) ) {
     196           0 :     FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     197           0 :   }
     198             : 
     199           0 :   fdctl_cfg_load_buf( out, mem, toml_sz, path );
     200             : 
     201           0 :   if( FD_UNLIKELY( 0!=munmap( mem, toml_sz ) ) ) {
     202           0 :     FD_LOG_ERR(( "munmap(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     203           0 :   }
     204           0 : }
     205             : 
     206             : void
     207             : fdctl_cfg_from_env( int *      pargc,
     208             :                     char ***   pargv,
     209           3 :                     config_t * config ) {
     210             : 
     211           3 :   memset( config, 0, sizeof(config_t) );
     212           3 :   fdctl_cfg_load_buf( config, (char const *)fdctl_default_config, fdctl_default_config_sz, "default" );
     213             : #if FD_HAS_NO_AGAVE
     214             :   fdctl_cfg_load_buf( config, (char const *)fdctl_default_firedancer_config, fdctl_default_firedancer_config_sz, "default_firedancer" );
     215             : #endif
     216             : 
     217           3 :   const char * user_config = fd_env_strip_cmdline_cstr(
     218           3 :       pargc,
     219           3 :       pargv,
     220           3 :       "--config",
     221           3 :       "FIREDANCER_CONFIG_TOML",
     222           3 :       NULL );
     223             : 
     224           3 :   if( FD_LIKELY( user_config ) ) {
     225           0 :     fdctl_cfg_load_file( config, user_config );
     226           0 :   }
     227             : 
     228           3 :   int netns = fd_env_strip_cmdline_contains( pargc, pargv, "--netns" );
     229           3 :   if( FD_UNLIKELY( netns ) ) {
     230           0 :     config->development.netns.enabled = 1;
     231           0 :     strncpy( config->tiles.net.interface,
     232           0 :              config->development.netns.interface0,
     233           0 :              sizeof(config->tiles.net.interface) );
     234           0 :     config->tiles.net.interface[ sizeof(config->tiles.net.interface) - 1 ] = '\0';
     235           0 :   }
     236             : 
     237           3 :   if( FD_UNLIKELY( !strcmp( config->user, "" ) ) ) {
     238           3 :     const char * user = fd_sys_util_login_user();
     239           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." ));
     240           3 :     if( FD_UNLIKELY( strlen( user ) >= sizeof( config->user ) ) )
     241           0 :                               FD_LOG_ERR(( "user name `%s` is too long", user ));
     242           3 :     strncpy( config->user, user, 256 );
     243           3 :   }
     244             : 
     245           3 :   struct utsname utsname;
     246           3 :   if( FD_UNLIKELY( -1==uname( &utsname ) ) )
     247           0 :     FD_LOG_ERR(( "could not get uname (%i-%s)", errno, fd_io_strerror( errno ) ));
     248           3 :   strncpy( config->hostname, utsname.nodename, sizeof(config->hostname) );
     249           3 :   config->hostname[ sizeof(config->hostname)-1UL ] = '\0'; /* Just truncate the name if it's too long to fit */
     250             : 
     251           3 :   ulong cluster = fd_genesis_cluster_identify( config->consensus.expected_genesis_hash );
     252           3 :   config->is_live_cluster = cluster != FD_CLUSTER_UNKNOWN;
     253             : 
     254           3 :   fdctl_cfg_net_auto( config );
     255             : 
     256           3 :   if( FD_UNLIKELY( -1==fd_sys_util_user_to_uid( config->user, &config->uid, &config->gid ) ) )
     257           0 :     FD_LOG_ERR(( "configuration file wants firedancer to run as user `%s` but it does not exist", config->user ));
     258             : 
     259           3 :   if( FD_UNLIKELY( !config->uid || !config->gid ) )
     260           0 :     FD_LOG_ERR(( "firedancer cannot run as root. please specify a non-root user in the configuration file" ));
     261             : 
     262           3 :   if( FD_UNLIKELY( getuid() != 0 && config->uid != getuid() ) )
     263           0 :     FD_LOG_ERR(( "running as uid %u, but config specifies uid %u", getuid(), config->uid ));
     264           3 :   if( FD_UNLIKELY( getgid() != 0 && config->gid != getgid() ) )
     265           0 :     FD_LOG_ERR(( "running as gid %u, but config specifies gid %u", getgid(), config->gid ));
     266             : 
     267           3 :   ulong len = strlen( config->hugetlbfs.mount_path );
     268           3 :   if( FD_UNLIKELY( !len ) ) FD_LOG_ERR(( "[hugetlbfs.mount_path] must be non-empty in your configuration file" ));
     269           3 :   FD_TEST( fd_cstr_printf_check( config->hugetlbfs.gigantic_page_mount_path,
     270           3 :                                  sizeof(config->hugetlbfs.gigantic_page_mount_path),
     271           3 :                                  NULL,
     272           3 :                                  "%s/.gigantic",
     273           3 :                                  config->hugetlbfs.mount_path ) );
     274           3 :   FD_TEST( fd_cstr_printf_check( config->hugetlbfs.huge_page_mount_path,
     275           3 :                                  sizeof(config->hugetlbfs.huge_page_mount_path),
     276           3 :                                  NULL,
     277           3 :                                  "%s/.huge",
     278           3 :                                  config->hugetlbfs.mount_path ) );
     279             : 
     280           3 :   ulong max_page_sz = fd_cstr_to_shmem_page_sz( config->hugetlbfs.max_page_size );
     281           3 :   if( FD_UNLIKELY( max_page_sz!=FD_SHMEM_HUGE_PAGE_SZ && max_page_sz!=FD_SHMEM_GIGANTIC_PAGE_SZ ) ) {
     282           0 :     FD_LOG_ERR(( "[hugetlbfs.max_page_size] must be \"huge\" or \"gigantic\"" ));
     283           0 :   }
     284             : 
     285           3 :   replace( config->log.path, "{user}", config->user );
     286           3 :   replace( config->log.path, "{name}", config->name );
     287           3 :   if( FD_LIKELY( !strcmp( "auto", config->log.colorize ) ) )       config->log.colorize1 = 2;
     288           0 :   else if( FD_LIKELY( !strcmp( "true", config->log.colorize ) ) )  config->log.colorize1 = 1;
     289           0 :   else if( FD_LIKELY( !strcmp( "false", config->log.colorize ) ) ) config->log.colorize1 = 0;
     290           0 :   else FD_LOG_ERR(( "[log.colorize] must be one of \"auto\", \"true\", or \"false\"" ));
     291             : 
     292           3 :   if( FD_LIKELY( 2==config->log.colorize1 ) ) {
     293           3 :     char const * cstr = fd_env_strip_cmdline_cstr( NULL, NULL, NULL, "COLORTERM", NULL );
     294           3 :     int truecolor = cstr && !strcmp( cstr, "truecolor" );
     295             : 
     296           3 :     cstr = fd_env_strip_cmdline_cstr( NULL, NULL, NULL, "TERM", NULL );
     297           3 :     int xterm256color = cstr && !strcmp( cstr, "xterm-256color" );
     298             : 
     299           3 :     config->log.colorize1 = truecolor || xterm256color;
     300           3 :   }
     301             : 
     302           3 :   config->log.level_logfile1 = parse_log_level( config->log.level_logfile );
     303           3 :   config->log.level_stderr1  = parse_log_level( config->log.level_stderr );
     304           3 :   config->log.level_flush1   = parse_log_level( config->log.level_flush );
     305           3 :   if( FD_UNLIKELY( -1==config->log.level_logfile1 ) ) FD_LOG_ERR(( "unrecognized [log.level_logfile] `%s`", config->log.level_logfile ));
     306           3 :   if( FD_UNLIKELY( -1==config->log.level_stderr1 ) )  FD_LOG_ERR(( "unrecognized [log.level_stderr] `%s`", config->log.level_logfile ));
     307           3 :   if( FD_UNLIKELY( -1==config->log.level_flush1 ) )   FD_LOG_ERR(( "unrecognized [log.level_flush] `%s`", config->log.level_logfile ));
     308             : 
     309           3 :   replace( config->scratch_directory, "{user}", config->user );
     310           3 :   replace( config->scratch_directory, "{name}", config->name );
     311             : 
     312           3 :   if( FD_UNLIKELY( strcmp( config->ledger.path, "" ) ) ) {
     313           0 :     replace( config->ledger.path, "{user}", config->user );
     314           0 :     replace( config->ledger.path, "{name}", config->name );
     315           3 :   } else {
     316           3 :     FD_TEST( fd_cstr_printf_check( config->ledger.path, sizeof(config->ledger.path), NULL, "%s/ledger", config->scratch_directory ) );
     317           3 :   }
     318             : 
     319           3 :   if( FD_UNLIKELY( strcmp( config->snapshots.path, "" ) ) ) {
     320           0 :     replace( config->snapshots.path, "{user}", config->user );
     321           0 :     replace( config->snapshots.path, "{name}", config->name );
     322           3 :   } else {
     323           3 :     strncpy( config->snapshots.path, config->ledger.path, sizeof(config->snapshots.path) );
     324           3 :   }
     325             : 
     326           3 :   if( FD_UNLIKELY( !strcmp( config->consensus.identity_path, "" ) ) ) {
     327           3 :     FD_TEST( fd_cstr_printf_check( config->consensus.identity_path,
     328           3 :                                    sizeof(config->consensus.identity_path),
     329           3 :                                    NULL,
     330           3 :                                    "%s/identity.json",
     331           3 :                                    config->scratch_directory ) );
     332           3 :   } else {
     333           0 :     replace( config->consensus.identity_path, "{user}", config->user );
     334           0 :     replace( config->consensus.identity_path, "{name}", config->name );
     335           0 :   }
     336             : 
     337             : #if FD_HAS_NO_AGAVE
     338             :   if( FD_UNLIKELY( !strcmp( config->consensus.vote_account_path, "" ) ) ) {
     339             :     FD_TEST( fd_cstr_printf_check( config->consensus.vote_account_path,
     340             :                                    sizeof(config->consensus.vote_account_path),
     341             :                                    NULL,
     342             :                                    "%s/vote-account.json",
     343             :                                    config->scratch_directory ) );
     344             :   }
     345             : #endif
     346           3 :   replace( config->consensus.vote_account_path, "{user}", config->user );
     347           3 :   replace( config->consensus.vote_account_path, "{name}", config->name );
     348             : 
     349           3 :   for( ulong i=0UL; i<config->consensus.authorized_voter_paths_cnt; i++ ) {
     350           0 :     replace( config->consensus.authorized_voter_paths[ i ], "{user}", config->user );
     351           0 :     replace( config->consensus.authorized_voter_paths[ i ], "{name}", config->name );
     352           0 :   }
     353             : 
     354           3 :   strcpy( config->cluster, fd_genesis_cluster_name( cluster ) );
     355             : 
     356             : #if FD_HAS_NO_AGAVE
     357             :   if( FD_UNLIKELY( config->is_live_cluster && cluster!=FD_CLUSTER_TESTNET ) )
     358             :     FD_LOG_ERR(( "Attempted to start against live cluster `%s`. Firedancer is not "
     359             :                  "ready for production deployment, has not been tested, and is "
     360             :                  "missing consensus critical functionality. Joining a live Solana "
     361             :                  "cluster may destabilize the network. Please do not attempt. You "
     362             :                  "can start against the testnet cluster by specifying the testnet "
     363             :                  "entrypoints from https://docs.solana.com/clusters under "
     364             :                  "[gossip.entrypoints] in your configuration file.", fd_genesis_cluster_name( cluster ) ));
     365             : #endif /* FD_HAS_NO_AGAVE */
     366             : 
     367           3 :   if( FD_LIKELY( config->is_live_cluster) ) {
     368           0 :     if( FD_UNLIKELY( !config->development.sandbox ) )
     369           0 :       FD_LOG_ERR(( "trying to join a live cluster, but configuration disables the sandbox which is a a development only feature" ));
     370           0 :     if( FD_UNLIKELY( config->development.no_clone ) )
     371           0 :       FD_LOG_ERR(( "trying to join a live cluster, but configuration disables multiprocess which is a development only feature" ));
     372           0 :     if( FD_UNLIKELY( config->development.netns.enabled ) )
     373           0 :       FD_LOG_ERR(( "trying to join a live cluster, but configuration enables [development.netns] which is a development only feature" ));
     374           0 :     if( FD_UNLIKELY( config->development.bench.larger_max_cost_per_block ) )
     375           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" ));
     376           0 :     if( FD_UNLIKELY( config->development.bench.larger_shred_limits_per_block ) )
     377           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" ));
     378           0 :     if( FD_UNLIKELY( config->development.bench.disable_blockstore_from_slot ) )
     379           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" ));
     380           0 :     if( FD_UNLIKELY( config->development.bench.disable_status_cache ) )
     381           0 :       FD_LOG_ERR(( "trying to join a live cluster, but configuration enables [development.bench.disable_status_cache] which is a development only feature" ));
     382           0 :   }
     383             : 
     384           3 :   if( FD_UNLIKELY( config->tiles.quic.quic_transaction_listen_port != config->tiles.quic.regular_transaction_listen_port + 6 ) )
     385           0 :     FD_LOG_ERR(( "configuration specifies invalid [tiles.quic.quic_transaction_listen_port] `%hu`. "
     386           3 :                  "This must be 6 more than [tiles.quic.regular_transaction_listen_port] `%hu`",
     387           3 :                  config->tiles.quic.quic_transaction_listen_port,
     388           3 :                  config->tiles.quic.regular_transaction_listen_port ));
     389             : 
     390           3 :   if( FD_LIKELY( !strcmp( config->consensus.identity_path, "" ) ) ) {
     391           0 :     if( FD_UNLIKELY( config->is_live_cluster ) )
     392           0 :       FD_LOG_ERR(( "configuration file must specify [consensus.identity_path] when joining a live cluster" ));
     393             : 
     394           0 :     FD_TEST( fd_cstr_printf_check( config->consensus.identity_path,
     395           0 :                                    sizeof(config->consensus.identity_path),
     396           0 :                                    NULL,
     397           0 :                                    "%s/identity.json",
     398           0 :                                    config->scratch_directory ) );
     399           0 :   }
     400             : 
     401           3 :   fdctl_cfg_validate( config );
     402           3 :   validate_ports( config );
     403           3 : }
     404             : 
     405             : void
     406           3 : fdctl_cfg_net_auto( config_t * config ) {
     407             : 
     408           3 :   if( FD_UNLIKELY( !strcmp( config->tiles.net.interface, "" ) && !config->development.netns.enabled ) ) {
     409           3 :     uint ifindex;
     410           3 :     int result = fd_net_util_internet_ifindex( &ifindex );
     411           3 :     if( FD_UNLIKELY( -1==result && errno!=ENODEV ) ) FD_LOG_ERR(( "could not get network device index (%i-%s)", errno, fd_io_strerror( errno ) ));
     412           3 :     else if( FD_UNLIKELY( -1==result ) )
     413           0 :       FD_LOG_ERR(( "no network device found which routes to 8.8.8.8. If no network "
     414           3 :                    "interface is specified in the configuration file, Firedancer "
     415           3 :                    "tries to use the first network interface found which routes to "
     416           3 :                    "8.8.8.8. You can see what this is by running `ip route get 8.8.8.8` "
     417           3 :                    "You can fix this error by specifying a network interface to bind to in "
     418           3 :                    "your configuration file under [net.interface]" ));
     419             : 
     420           3 :     if( FD_UNLIKELY( !if_indextoname( ifindex, config->tiles.net.interface ) ) )
     421           0 :       FD_LOG_ERR(( "could not get name of interface with index %u", ifindex ));
     422           3 :   }
     423             : 
     424           3 :   if( FD_UNLIKELY( config->development.netns.enabled ) ) {
     425             : 
     426           0 :     if( !strcmp( config->tiles.net.interface, "" ) ) {
     427           0 :       memcpy( config->tiles.net.interface, config->development.netns.interface0, sizeof(config->tiles.net.interface) );
     428           0 :     }
     429             : 
     430           0 :     if( !strcmp( config->development.pktgen.fake_dst_ip, "" ) ) {
     431           0 :       memcpy( config->development.pktgen.fake_dst_ip, config->development.netns.interface1_addr, sizeof(config->development.netns.interface1_addr) );
     432           0 :     }
     433             : 
     434           0 :     if( FD_UNLIKELY( strcmp( config->development.netns.interface0, config->tiles.net.interface ) ) ) {
     435           0 :       FD_LOG_ERR(( "netns interface and firedancer interface are different. If you are using the "
     436           0 :                    "[development.netns] functionality to run Firedancer in a network namespace "
     437           0 :                    "for development, the configuration file must specify that "
     438           0 :                    "[development.netns.interface0] is the same as [tiles.net.interface]" ));
     439           0 :     }
     440             : 
     441           0 :     if( FD_UNLIKELY( !fd_cstr_to_ip4_addr( config->development.netns.interface0_addr, &config->tiles.net.ip_addr ) ) )
     442           0 :       FD_LOG_ERR(( "configuration specifies invalid netns IP address `%s`", config->development.netns.interface0_addr ));
     443             : 
     444           3 :   } else { /* !config->development.netns.enabled */
     445             : 
     446           3 :     if( FD_UNLIKELY( !if_nametoindex( config->tiles.net.interface ) ) )
     447           0 :       FD_LOG_ERR(( "configuration specifies network interface `%s` which does not exist", config->tiles.net.interface ));
     448           3 :     uint iface_ip;
     449           3 :     if( FD_UNLIKELY( -1==fd_net_util_if_addr( config->tiles.net.interface, &iface_ip ) ) )
     450           0 :       FD_LOG_ERR(( "could not get IP address for interface `%s`", config->tiles.net.interface ));
     451             : 
     452           3 :     if( FD_UNLIKELY( strcmp( config->gossip.host, "" ) ) ) {
     453           0 :       uint gossip_ip_addr = iface_ip;
     454           0 :       int  has_gossip_ip4 = 0;
     455           0 :       if( FD_UNLIKELY( strlen( config->gossip.host )<=15UL ) ) {
     456             :         /* Only sets gossip_ip_addr if it's a valid IPv4 address, otherwise assume it's a DNS name */
     457           0 :         has_gossip_ip4 = fd_cstr_to_ip4_addr( config->gossip.host, &gossip_ip_addr );
     458           0 :       }
     459           0 :       if( FD_UNLIKELY( !fd_ip4_addr_is_public( gossip_ip_addr ) && config->is_live_cluster && has_gossip_ip4 ) )
     460           0 :         FD_LOG_ERR(( "Trying to use [gossip.host] " FD_IP4_ADDR_FMT " for listening to incoming "
     461           0 :                      "transactions, but it is part of a private network and will not be routable "
     462           0 :                      "for other Solana network nodes.",
     463           0 :                      FD_IP4_ADDR_FMT_ARGS( iface_ip ) ));
     464           3 :     } else if( FD_UNLIKELY( !fd_ip4_addr_is_public( iface_ip ) && config->is_live_cluster ) ) {
     465           0 :       FD_LOG_ERR(( "Trying to use network interface `%s` for listening to incoming transactions, "
     466           0 :                    "but it has IPv4 address " FD_IP4_ADDR_FMT " which is part of a private network "
     467           0 :                    "and will not be routable for other Solana network nodes. If you are running "
     468           0 :                    "behind a NAT and this interface is publicly reachable, you can continue by "
     469           0 :                    "manually specifying the IP address to advertise in your configuration under "
     470           0 :                    "[gossip.host].",
     471           0 :                    config->tiles.net.interface, FD_IP4_ADDR_FMT_ARGS( iface_ip ) ));
     472           0 :     }
     473             : 
     474           3 :     config->tiles.net.ip_addr = iface_ip;
     475             : 
     476           3 :   }
     477             : 
     478           3 : }
     479             : 
     480             : int
     481           0 : fdctl_cfg_to_memfd( config_t const * config ) {
     482           0 :   int config_memfd = memfd_create( "fd_config", 0 );
     483           0 :   if( FD_UNLIKELY( -1==config_memfd ) ) FD_LOG_ERR(( "memfd_create() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     484           0 :   if( FD_UNLIKELY( -1==ftruncate( config_memfd, sizeof( config_t ) ) ) ) FD_LOG_ERR(( "ftruncate() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     485             : 
     486           0 :   uchar * bytes = mmap( NULL, sizeof( config_t ), PROT_READ | PROT_WRITE, MAP_SHARED, config_memfd, 0 );
     487           0 :   if( FD_UNLIKELY( bytes == MAP_FAILED ) ) FD_LOG_ERR(( "mmap() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     488           0 :   fd_memcpy( bytes, config, sizeof( config_t ) );
     489           0 :   if( FD_UNLIKELY( munmap( bytes, sizeof( config_t ) ) ) ) FD_LOG_ERR(( "munmap() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     490             : 
     491           0 :   return config_memfd;
     492           0 : }

Generated by: LCOV version 1.14