LCOV - code coverage report
Current view: top level - app/shared - fd_config.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 289 552 52.4 %
Date: 2026-08-31 04:29:48 Functions: 11 13 84.6 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : #include "fd_config.h"
       3             : #include "fd_config_auto.h"
       4             : #include "fd_config_private.h"
       5             : 
       6             : #include "../platform/fd_net_util.h"
       7             : #include "../platform/fd_sys_util.h"
       8             : #include "../../ballet/toml/fd_toml.h"
       9             : #include "../../disco/genesis/fd_genesis_cluster.h"
      10             : #include "../../discof/genesis/fd_genesi_tile.h"
      11             : #include "../../disco/net/fd_net_tile.h"
      12             : #include "../../discof/restore/utils/fd_ssarchive.h"
      13             : 
      14             : #include <unistd.h>
      15             : #include <errno.h>
      16             : #include <stdlib.h> /* strtoul */
      17             : #include <sys/utsname.h>
      18             : #include <sys/mman.h>
      19             : 
      20             : /* TODO: Rewrite this ... */
      21             : 
      22             : static inline void
      23             : replace( char *       in,
      24             :          const char * pat,
      25          18 :          const char * sub ) {
      26          18 :   char * replace = strstr( in, pat );
      27          18 :   if( FD_LIKELY( replace ) ) {
      28           6 :     ulong pat_len = strlen( pat );
      29           6 :     ulong sub_len = strlen( sub );
      30           6 :     ulong in_len  = strlen( in );
      31           6 :     if( FD_UNLIKELY( pat_len > in_len ) ) return;
      32             : 
      33           6 :     ulong total_len = in_len - pat_len + sub_len;
      34           6 :     if( FD_UNLIKELY( total_len >= PATH_MAX ) )
      35           0 :       FD_LOG_ERR(( "configuration scratch directory path too long: `%s`", in ));
      36             : 
      37           6 :     uchar after[PATH_MAX] = {0};
      38           6 :     fd_memcpy( after, replace + pat_len, strlen( replace + pat_len ) );
      39           6 :     fd_memcpy( replace, sub, sub_len );
      40           6 :     ulong after_len = strlen( ( const char * ) after );
      41           6 :     fd_memcpy( replace + sub_len, after, after_len );
      42           6 :     in[ total_len ] = '\0';
      43           6 :   }
      44          18 : }
      45             : 
      46             : 
      47             : FD_FN_CONST static inline int
      48           6 : parse_log_level( char const * level ) {
      49           6 :   if( FD_UNLIKELY( !strcmp( level, "DEBUG" ) ) )    return 0;
      50           6 :   if( FD_UNLIKELY( !strcmp( level, "INFO"  ) ) )    return 1;
      51           3 :   if( FD_UNLIKELY( !strcmp( level, "NOTICE"  ) ) )  return 2;
      52           0 :   if( FD_UNLIKELY( !strcmp( level, "WARNING"  ) ) ) return 3;
      53           0 :   if( FD_UNLIKELY( !strcmp( level, "ERR" ) ) )      return 4;
      54           0 :   if( FD_UNLIKELY( !strcmp( level, "CRIT" ) ) )     return 5;
      55           0 :   if( FD_UNLIKELY( !strcmp( level, "ALERT" ) ) )    return 6;
      56           0 :   if( FD_UNLIKELY( !strcmp( level, "EMERG" ) ) )    return 7;
      57           0 :   return -1;
      58           0 : }
      59             : 
      60             : FD_FN_CONST static inline int
      61           3 : parse_core_dump_level( char const * level ) {
      62           3 :   if( FD_UNLIKELY( !strcmp( level, "disabled" ) ) )       return FD_TOPO_CORE_DUMP_LEVEL_DISABLED;
      63           0 :   if( FD_UNLIKELY( !strcmp( level, "minimal"  ) ) )       return FD_TOPO_CORE_DUMP_LEVEL_MINIMAL;
      64           0 :   if( FD_UNLIKELY( !strcmp( level, "regular"  ) ) )       return FD_TOPO_CORE_DUMP_LEVEL_REGULAR;
      65           0 :   if( FD_UNLIKELY( !strcmp( level, "full"     ) ) )       return FD_TOPO_CORE_DUMP_LEVEL_FULL;
      66           0 :   return -1;
      67           0 : }
      68             : 
      69             : void
      70             : fd_config_load_buf( fd_config_t * out,
      71             :                     char const *  buf,
      72             :                     ulong         sz,
      73           3 :                     char const *  path ) {
      74           3 :   static uchar pod_mem[ 1UL<<26 ];
      75           3 :   uchar * pod = fd_pod_join( fd_pod_new( pod_mem, sizeof(pod_mem) ) );
      76             : 
      77           3 :   fd_toml_err_info_t toml_err[1];
      78           3 :   uchar scratch[ 4096 ];
      79           3 :   int toml_errc = fd_toml_parse( buf, sz, pod, scratch, sizeof(scratch), toml_err );
      80           3 :   if( FD_UNLIKELY( toml_errc!=FD_TOML_SUCCESS ) ) {
      81           0 :     switch( toml_errc ) {
      82           0 :     case FD_TOML_ERR_POD:
      83           0 :       FD_LOG_ERR(( "Failed to parse config file (%s): ran out of buffer space while parsing", path ));
      84           0 :       break;
      85           0 :     case FD_TOML_ERR_SCRATCH:
      86           0 :       FD_LOG_ERR(( "Failed to parse config file (%s) at line %lu: ran out of scratch space while parsing", path, toml_err->line ));
      87           0 :       break;
      88           0 :     case FD_TOML_ERR_KEY:
      89           0 :       FD_LOG_ERR(( "Failed to parse config file (%s) at line %lu: oversize key", path, toml_err->line ));
      90           0 :       break;
      91           0 :     case FD_TOML_ERR_DUP:
      92           0 :       FD_LOG_ERR(( "Failed to parse config file (%s) at line %lu: duplicate key", path, toml_err->line ));
      93           0 :       break;
      94           0 :     case FD_TOML_ERR_RANGE:
      95           0 :       FD_LOG_ERR(( "Failed to parse config file (%s) at line %lu: invalid value for key", path, toml_err->line ));
      96           0 :       break;
      97           0 :     case FD_TOML_ERR_PARSE:
      98           0 :       FD_LOG_ERR(( "Failed to parse config file (%s) at line %lu", path, toml_err->line ));
      99           0 :       break;
     100           0 :     default:
     101           0 :       FD_LOG_ERR(( "Failed to parse config file (%s): %s", path, fd_toml_strerror( toml_errc ) ));
     102           0 :       break;
     103           0 :     }
     104           0 :   }
     105             : 
     106           3 :   if( FD_UNLIKELY( !fd_config_extract_pod( pod, out ) ) ) FD_LOG_ERR(( "Failed to parse config file (%s): there are unrecognized keys logged above", path ));
     107             : 
     108           3 :   fd_pod_delete( fd_pod_leave( pod ) );
     109           3 : }
     110             : 
     111             : static void
     112           3 : fd_config_fillf( fd_config_t * config ) {
     113           3 :   if( FD_UNLIKELY( strcmp( config->paths.accounts, "" ) ) ) {
     114           0 :     replace( config->paths.accounts, "{user}", config->user );
     115           0 :     replace( config->paths.accounts, "{name}", config->name );
     116           3 :   } else {
     117           3 :     FD_TEST( fd_cstr_printf_check( config->paths.accounts, sizeof(config->paths.accounts), NULL, "%s/accounts.db", config->paths.base ) );
     118           3 :   }
     119             : 
     120           3 :   if( FD_UNLIKELY( strcmp( config->paths.shredb, "" ) ) ) {
     121           0 :     replace( config->paths.shredb, "{user}", config->user );
     122           0 :     replace( config->paths.shredb, "{name}", config->name );
     123           3 :   } else {
     124           3 :     FD_TEST( fd_cstr_printf_check( config->paths.shredb, sizeof(config->paths.shredb), NULL, "%s/shreds.db", config->paths.base ) );
     125           3 :   }
     126             : 
     127           3 :   if( FD_UNLIKELY( strcmp( config->paths.guidb, "" ) ) ) {
     128           0 :     replace( config->paths.guidb, "{user}", config->user );
     129           0 :     replace( config->paths.guidb, "{name}", config->name );
     130           3 :   } else {
     131           3 :     FD_TEST( fd_cstr_printf_check( config->paths.guidb, sizeof(config->paths.guidb), NULL, "%s/gui.db", config->paths.base ) );
     132           3 :   }
     133             : 
     134           3 :   for( ulong i=0UL; i<config->firedancer.paths.authorized_voter_paths_cnt; i++ ) {
     135           0 :     replace( config->firedancer.paths.authorized_voter_paths[ i ], "{user}", config->user );
     136           0 :     replace( config->firedancer.paths.authorized_voter_paths[ i ], "{name}", config->name );
     137           0 :   }
     138           3 : }
     139             : 
     140             : static void
     141           0 : fd_config_fillh( fd_config_t * config ) {
     142           0 :   if( FD_UNLIKELY( strcmp( config->frankendancer.paths.accounts_path, "" ) ) ) {
     143           0 :     replace( config->frankendancer.paths.accounts_path, "{user}", config->user );
     144           0 :     replace( config->frankendancer.paths.accounts_path, "{name}", config->name );
     145           0 :   }
     146             : 
     147           0 :   if( FD_UNLIKELY( strcmp( config->frankendancer.paths.ledger, "" ) ) ) {
     148           0 :     replace( config->frankendancer.paths.ledger, "{user}", config->user );
     149           0 :     replace( config->frankendancer.paths.ledger, "{name}", config->name );
     150           0 :   } else {
     151           0 :     FD_TEST( fd_cstr_printf_check( config->frankendancer.paths.ledger, sizeof(config->frankendancer.paths.ledger), NULL, "%s/ledger", config->paths.base ) );
     152           0 :   }
     153             : 
     154           0 :   if( FD_UNLIKELY( strcmp( config->frankendancer.snapshots.path, "" ) ) ) {
     155           0 :     replace( config->frankendancer.snapshots.path, "{user}", config->user );
     156           0 :     replace( config->frankendancer.snapshots.path, "{name}", config->name );
     157           0 :   } else {
     158           0 :     strncpy( config->frankendancer.snapshots.path, config->frankendancer.paths.ledger, sizeof(config->frankendancer.snapshots.path) );
     159           0 :   }
     160             : 
     161           0 :   for( ulong i=0UL; i<config->frankendancer.paths.authorized_voter_paths_cnt; i++ ) {
     162           0 :     replace( config->frankendancer.paths.authorized_voter_paths[ i ], "{user}", config->user );
     163           0 :     replace( config->frankendancer.paths.authorized_voter_paths[ i ], "{name}", config->name );
     164           0 :   }
     165             : 
     166           0 :   if( FD_UNLIKELY( config->tiles.quic.quic_transaction_listen_port!=config->tiles.quic.regular_transaction_listen_port+6 ) )
     167           0 :     FD_LOG_ERR(( "configuration specifies invalid [tiles.quic.quic_transaction_listen_port] `%hu`. "
     168           0 :                  "This must be 6 more than [tiles.quic.regular_transaction_listen_port] `%hu`",
     169           0 :                  config->tiles.quic.quic_transaction_listen_port,
     170           0 :                  config->tiles.quic.regular_transaction_listen_port ));
     171             : 
     172           0 :   char dynamic_port_range[ 32 ];
     173           0 :   fd_memcpy( dynamic_port_range, config->frankendancer.dynamic_port_range, sizeof(dynamic_port_range) );
     174             : 
     175           0 :   char * dash = strstr( dynamic_port_range, "-" );
     176           0 :   if( FD_UNLIKELY( !dash ) )
     177           0 :     FD_LOG_ERR(( "configuration specifies invalid [dynamic_port_range] `%s`. "
     178           0 :                  "This must be formatted like `<min>-<max>`",
     179           0 :                  config->frankendancer.dynamic_port_range ));
     180             : 
     181           0 :   *dash = '\0';
     182           0 :   char * endptr;
     183           0 :   ulong agave_port_min = strtoul( dynamic_port_range, &endptr, 10 );
     184           0 :   if( FD_UNLIKELY( *endptr != '\0' || agave_port_min > USHORT_MAX ) )
     185           0 :     FD_LOG_ERR(( "configuration specifies invalid [dynamic_port_range] `%s`. "
     186           0 :                  "This must be formatted like `<min>-<max>`",
     187           0 :                  config->frankendancer.dynamic_port_range ));
     188           0 :   ulong agave_port_max = strtoul( dash + 1, &endptr, 10 );
     189           0 :   if( FD_UNLIKELY( *endptr != '\0' || agave_port_max > USHORT_MAX ) )
     190           0 :     FD_LOG_ERR(( "configuration specifies invalid [dynamic_port_range] `%s`. "
     191           0 :                  "This must be formatted like `<min>-<max>`",
     192           0 :                  config->frankendancer.dynamic_port_range ));
     193           0 :   if( FD_UNLIKELY( agave_port_min > agave_port_max ) )
     194           0 :     FD_LOG_ERR(( "configuration specifies invalid [dynamic_port_range] `%s`. "
     195           0 :                  "The minimum port must be less than or equal to the maximum port",
     196           0 :                  config->frankendancer.dynamic_port_range ));
     197             : 
     198           0 :   if( FD_UNLIKELY( config->tiles.quic.regular_transaction_listen_port >= agave_port_min &&
     199           0 :                    config->tiles.quic.regular_transaction_listen_port < agave_port_max ) )
     200           0 :     FD_LOG_ERR(( "configuration specifies invalid [tiles.quic.transaction_listen_port] `%hu`. "
     201           0 :                  "This must be outside the dynamic port range `%s`",
     202           0 :                  config->tiles.quic.regular_transaction_listen_port,
     203           0 :                  config->frankendancer.dynamic_port_range ));
     204             : 
     205           0 :   if( FD_UNLIKELY( config->tiles.quic.quic_transaction_listen_port >= agave_port_min &&
     206           0 :                    config->tiles.quic.quic_transaction_listen_port < agave_port_max ) )
     207           0 :     FD_LOG_ERR(( "configuration specifies invalid [tiles.quic.quic_transaction_listen_port] `%hu`. "
     208           0 :                  "This must be outside the dynamic port range `%s`",
     209           0 :                  config->tiles.quic.quic_transaction_listen_port,
     210           0 :                  config->frankendancer.dynamic_port_range ));
     211             : 
     212           0 :   if( FD_UNLIKELY( config->tiles.shred.shred_listen_port >= agave_port_min &&
     213           0 :                    config->tiles.shred.shred_listen_port < agave_port_max ) )
     214           0 :     FD_LOG_ERR(( "configuration specifies invalid [tiles.shred.shred_listen_port] `%hu`. "
     215           0 :                  "This must be outside the dynamic port range `%s`",
     216           0 :                  config->tiles.shred.shred_listen_port,
     217           0 :                  config->frankendancer.dynamic_port_range ));
     218           0 : }
     219             : 
     220             : static void
     221           3 : fd_config_fill_net( fd_config_t * config ) {
     222           3 :   if( FD_UNLIKELY( !strcmp( config->net.interface, "" ) ) ) {
     223           3 :     uint ifindex;
     224           3 :     int result = fd_net_util_internet_ifindex( &ifindex );
     225           3 :     if( FD_UNLIKELY( -1==result && errno!=ENODEV ) ) FD_LOG_ERR(( "could not get network device index (%i-%s)", errno, fd_io_strerror( errno ) ));
     226           3 :     else if( FD_UNLIKELY( -1==result ) )
     227           0 :       FD_LOG_ERR(( "no network device found which routes to 8.8.8.8. If no network "
     228           3 :                    "interface is specified in the configuration file, Firedancer "
     229           3 :                    "tries to use the first network interface found which routes to "
     230           3 :                    "8.8.8.8. You can see what this is by running `ip route get 8.8.8.8` "
     231           3 :                    "You can fix this error by specifying a network interface to bind to in "
     232           3 :                    "your configuration file under [net.interface]" ));
     233             : 
     234           3 :     if( FD_UNLIKELY( !if_indextoname( ifindex, config->net.interface ) ) )
     235           0 :       FD_LOG_ERR(( "could not get name of interface with index %u", ifindex ));
     236           3 :   }
     237             : 
     238           3 :   if( FD_UNLIKELY( !if_nametoindex( config->net.interface ) ) )
     239           0 :     FD_LOG_ERR(( "configuration specifies network interface `%s` which does not exist", config->net.interface ));
     240           3 :   uint iface_ip;
     241           3 :   if( FD_UNLIKELY( -1==fd_net_util_if_addr( config->net.interface, &iface_ip ) ) )
     242           0 :     FD_LOG_ERR(( "could not get IP address for interface `%s`", config->net.interface ));
     243             : 
     244           3 :   if( FD_UNLIKELY( config->is_firedancer ) ) {
     245           3 :     if( FD_UNLIKELY( strcmp( config->firedancer.gossip.host, "" ) ) ) {
     246           0 :       uint gossip_ip_addr = iface_ip;
     247           0 :       int  has_gossip_ip4 = 0;
     248           0 :       if( FD_UNLIKELY( strlen( config->firedancer.gossip.host )<=15UL ) ) {
     249             :         /* Only sets gossip_ip_addr if it's a valid IPv4 address, otherwise assume it's a DNS name */
     250           0 :         has_gossip_ip4 = fd_cstr_to_ip4_addr( config->firedancer.gossip.host, &gossip_ip_addr );
     251           0 :       }
     252           0 :       if( FD_UNLIKELY( !fd_ip4_addr_is_public( gossip_ip_addr ) && config->is_live_cluster && has_gossip_ip4 ) )
     253           0 :         FD_LOG_ERR(( "Trying to use [gossip.host] " FD_IP4_ADDR_FMT " for listening to incoming "
     254           0 :                      "transactions, but it is part of a private network and will not be routable "
     255           0 :                      "for other Solana network nodes.", FD_IP4_ADDR_FMT_ARGS( gossip_ip_addr ) ));
     256           3 :     } else if( FD_UNLIKELY( !fd_ip4_addr_is_public( iface_ip ) && config->is_live_cluster ) ) {
     257           0 :       FD_LOG_ERR(( "Trying to use network interface `%s` for listening to incoming transactions, "
     258           0 :                    "but it has IPv4 address " FD_IP4_ADDR_FMT " which is part of a private network "
     259           0 :                    "and will not be routable for other Solana network nodes. If you are running "
     260           0 :                    "behind a NAT and this interface is publicly reachable, you can continue by "
     261           0 :                    "manually specifying the IP address to advertise in your configuration under "
     262           0 :                    "[gossip.host].", config->net.interface, FD_IP4_ADDR_FMT_ARGS( iface_ip ) ));
     263           0 :     }
     264           3 :   }
     265             : 
     266           3 :   config->net.ip_addr = iface_ip;
     267           3 : }
     268             : 
     269             : void
     270             : fd_config_fill( fd_config_t * config,
     271             :                 int           is_local_cluster,
     272           3 :                 int           dev ) {
     273           3 :   struct utsname utsname;
     274           3 :   if( FD_UNLIKELY( -1==uname( &utsname ) ) )
     275           0 :     FD_LOG_ERR(( "could not get uname (%i-%s)", errno, fd_io_strerror( errno ) ));
     276           3 :   fd_cstr_ncpy( config->hostname, utsname.nodename, sizeof(config->hostname) ); /* Just truncate the name if it's too long to fit */
     277             : 
     278           3 :   ulong cluster;
     279           3 :   if( FD_UNLIKELY( !config->is_firedancer ) ) cluster = fd_genesis_cluster_identify( config->frankendancer.consensus.expected_genesis_hash );
     280           3 :   else                                        cluster = fd_genesis_cluster_identify( config->consensus.expected_genesis_hash );
     281           3 :   config->is_live_cluster = cluster!=FD_CLUSTER_UNKNOWN;
     282           3 :   strcpy( config->cluster, fd_genesis_cluster_name( cluster ) );
     283             : 
     284           3 :   if( FD_UNLIKELY( !strcmp( config->user, "" ) ) ) {
     285           3 :     const char * user = fd_sys_util_login_user();
     286           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." ));
     287           3 :     if( FD_UNLIKELY( strlen( user )>=sizeof( config->user ) ) )                                FD_LOG_ERR(( "user name `%s` is too long", user ));
     288           3 :     strncpy( config->user, user, sizeof(config->user) );
     289           3 :   }
     290             : 
     291           3 :   if( FD_UNLIKELY( -1==fd_sys_util_user_to_uid( config->user, &config->uid, &config->gid ) ) ) FD_LOG_ERR(( "configuration file wants firedancer to run as user `%s` but it does not exist", config->user ));
     292           3 :   if( FD_UNLIKELY( !config->uid || !config->gid ) )                                            FD_LOG_ERR(( "firedancer cannot run as root. please specify a non-root user in the configuration file" ));
     293           3 :   if( FD_UNLIKELY( getuid()!=0U && config->uid!=getuid() ) )                                   FD_LOG_ERR(( "running as uid %u, but config specifies uid %u", getuid(), config->uid ));
     294           3 :   if( FD_UNLIKELY( getgid()!=0U && config->gid!=getgid() ) )                                   FD_LOG_ERR(( "running as gid %u, but config specifies gid %u", getgid(), config->gid ));
     295             : 
     296           3 :   FD_TEST( fd_cstr_printf_check( config->hugetlbfs.gigantic_page_mount_path,
     297           3 :     sizeof(config->hugetlbfs.gigantic_page_mount_path),
     298           3 :     NULL,
     299           3 :     "%s/.gigantic",
     300           3 :     config->hugetlbfs.mount_path ) );
     301           3 :   FD_TEST( fd_cstr_printf_check( config->hugetlbfs.huge_page_mount_path,
     302           3 :     sizeof(config->hugetlbfs.huge_page_mount_path),
     303           3 :     NULL,
     304           3 :     "%s/.huge",
     305           3 :     config->hugetlbfs.mount_path ) );
     306             : 
     307           3 :   ulong max_page_sz = fd_cstr_to_shmem_page_sz( config->hugetlbfs.max_page_size );
     308           3 :   if( FD_UNLIKELY( max_page_sz!=FD_SHMEM_HUGE_PAGE_SZ && max_page_sz!=FD_SHMEM_GIGANTIC_PAGE_SZ ) ) FD_LOG_ERR(( "[hugetlbfs.max_page_size] must be \"huge\" or \"gigantic\"" ));
     309             : 
     310           3 :   replace( config->log.path, "{user}", config->user );
     311           3 :   replace( config->log.path, "{name}", config->name );
     312             : 
     313           3 :   if( FD_LIKELY( !strcmp( "auto", config->log.colorize ) ) )       config->log.colorize1 = 2;
     314           0 :   else if( FD_LIKELY( !strcmp( "true", config->log.colorize ) ) )  config->log.colorize1 = 1;
     315           0 :   else if( FD_LIKELY( !strcmp( "false", config->log.colorize ) ) ) config->log.colorize1 = 0;
     316           0 :   else  FD_LOG_ERR(( "[log.colorize] must be one of \"auto\", \"true\", or \"false\"" ));
     317             : 
     318           3 :   if( FD_LIKELY( 2==config->log.colorize1 ) ) {
     319           3 :     config->log.colorize1 = fd_log_should_colorize();
     320           3 :   }
     321             : 
     322           3 :   config->log.level_logfile1 = parse_log_level( config->log.level_logfile );
     323           3 :   config->log.level_stderr1  = parse_log_level( config->log.level_stderr );
     324           3 :   if( FD_UNLIKELY( !strcmp( config->log.level_flush, "NONE" ) ) ) config->log.level_flush1 = 8;
     325           0 :   else                                                            config->log.level_flush1 = parse_log_level( config->log.level_flush );
     326           3 :   if( FD_UNLIKELY( -1==config->log.level_logfile1 ) ) FD_LOG_ERR(( "unrecognized [log.level_logfile] `%s`", config->log.level_logfile ));
     327           3 :   if( FD_UNLIKELY( -1==config->log.level_stderr1 ) )  FD_LOG_ERR(( "unrecognized [log.level_stderr] `%s`", config->log.level_stderr ));
     328           3 :   if( FD_UNLIKELY( -1==config->log.level_flush1 ) )   FD_LOG_ERR(( "unrecognized [log.level_flush] `%s`", config->log.level_flush ));
     329             : 
     330           3 :   config->development.core_dump_level = parse_core_dump_level( config->development.core_dump );
     331           3 :   if( FD_UNLIKELY( -1==config->development.core_dump_level ) ) FD_LOG_ERR(( "unrecognized [development.core_dump] `%s`", config->development.core_dump ));
     332             : 
     333           3 :   replace( config->paths.base, "{user}", config->user );
     334           3 :   replace( config->paths.base, "{name}", config->name );
     335             : 
     336           3 :   if( FD_UNLIKELY( !strcmp( config->paths.identity_key, "" ) ) ) {
     337             :     /* Development binaries generate an identity key on boot. */
     338           3 :     if( FD_UNLIKELY( config->is_live_cluster && !dev ) ) FD_LOG_ERR(( "configuration file must specify [consensus.identity_path] when joining a live cluster" ));
     339             : 
     340           3 :     FD_TEST( fd_cstr_printf_check( config->paths.identity_key,
     341           3 :                                    sizeof(config->paths.identity_key),
     342           3 :                                    NULL,
     343           3 :                                    "%s/identity.json",
     344           3 :                                    config->paths.base ) );
     345           3 :   } else {
     346           0 :     replace( config->paths.identity_key, "{user}", config->user );
     347           0 :     replace( config->paths.identity_key, "{name}", config->name );
     348           0 :   }
     349             : 
     350           3 :   replace( config->paths.vote_account, "{user}", config->user );
     351           3 :   replace( config->paths.vote_account, "{name}", config->name );
     352             : 
     353           3 :   if( FD_UNLIKELY( strcmp( config->paths.snapshots, "" ) ) ) {
     354           0 :     replace( config->paths.snapshots, "{user}", config->user );
     355           0 :     replace( config->paths.snapshots, "{name}", config->name );
     356           3 :   } else {
     357           3 :     FD_TEST( fd_cstr_printf_check( config->paths.snapshots, sizeof(config->paths.snapshots), NULL, "%s/snapshots", config->paths.base ) );
     358           3 :   }
     359             : 
     360           3 :   if( FD_UNLIKELY( strcmp( config->paths.genesis, "" ) ) ) {
     361           0 :     replace( config->paths.genesis, "{user}", config->user );
     362           0 :     replace( config->paths.genesis, "{name}", config->name );
     363           3 :   } else {
     364           3 :     FD_TEST( fd_cstr_printf_check( config->paths.genesis, sizeof(config->paths.genesis), NULL, "%s/genesis.bin", config->paths.base ) );
     365           3 :   }
     366             : 
     367           3 :   long ts = -fd_log_wallclock();
     368           3 :   config->is_dev = dev;
     369             : 
     370           3 :   config->tick_per_ns_mu = dev ? fd_tempo_tick_per_ns_dev( &config->tick_per_ns_sigma )
     371           3 :                                : fd_tempo_tick_per_ns    ( &config->tick_per_ns_sigma );
     372           3 :   FD_LOG_INFO(( "calibrating fd_tempo tick_per_ns took %ld ms", (fd_log_wallclock()+ts)/(1000L*1000L) ));
     373             : 
     374           3 :   if( 0!=strcmp( config->net.bind_address, "" ) ) {
     375           0 :     if( FD_UNLIKELY( !fd_cstr_to_ip4_addr( config->net.bind_address, &config->net.bind_address_parsed ) ) ) {
     376           0 :       FD_LOG_ERR(( "`net.bind_address` is not a valid IPv4 address" ));
     377           0 :     }
     378           0 :   }
     379             : 
     380           3 :   if(      FD_LIKELY( !strcmp( config->tiles.pack.schedule_strategy, "perf"     ) ) ) config->tiles.pack.schedule_strategy_enum = 0;
     381           3 :   else if( FD_LIKELY( !strcmp( config->tiles.pack.schedule_strategy, "balanced" ) ) ) config->tiles.pack.schedule_strategy_enum = 1;
     382           0 :   else if( FD_LIKELY( !strcmp( config->tiles.pack.schedule_strategy, "revenue"  ) ) ) {
     383           0 :     FD_LOG_ERR(( "the revenue scheduler has been removed.  Please update [tiles.pack.schedule_strategy]" ));
     384           0 :   }
     385           0 :   else FD_LOG_ERR(( "[tiles.pack.schedule_strategy] %s not recognized", config->tiles.pack.schedule_strategy ));
     386             : 
     387           3 :   fd_config_fill_net( config );
     388             : 
     389           3 :   if( FD_UNLIKELY( config->is_firedancer ) ) {
     390           3 :     fd_config_fillf( config );
     391           3 :   } else {
     392           0 :     fd_config_fillh( config );
     393           0 :   }
     394             : 
     395           3 :   fd_config_auto( config );
     396           3 :   if( FD_UNLIKELY( !strcmp( config->net.provider, "auto" ) ) ) {
     397           0 :     FD_LOG_ERR(( "failed to resolve automatic network provider" ));
     398           0 :   }
     399           3 :   fd_config_validate( config );
     400             : 
     401           3 :   if( FD_LIKELY( config->is_live_cluster) ) {
     402           0 :     if( FD_UNLIKELY( !config->development.sandbox ) )                            FD_LOG_ERR(( "trying to join a live cluster, but configuration disables the sandbox which is a development only feature" ));
     403           0 :     if( FD_UNLIKELY( config->development.no_clone ) )                            FD_LOG_ERR(( "trying to join a live cluster, but configuration disables multiprocess which is a development only feature" ));
     404           0 :     if( FD_UNLIKELY( config->development.bench.larger_max_cost_per_block ) )     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" ));
     405           0 :     if( FD_UNLIKELY( config->development.bench.larger_shred_limits_per_block ) ) 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" ));
     406           0 :     if( FD_UNLIKELY( config->development.bench.disable_blockstore_from_slot ) )  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" ));
     407           0 :     if( FD_UNLIKELY( config->development.bench.disable_status_cache ) )          FD_LOG_ERR(( "trying to join a live cluster, but configuration enables [development.bench.disable_status_cache] which is a development only feature" ));
     408           0 :   }
     409             : 
     410             :   /* When running a local cluster, some options are overridden by default
     411             :      to make starting and running in development environments a little
     412             :      easier and less strict. */
     413           3 :   if( FD_UNLIKELY( is_local_cluster ) ) {
     414           0 :     if( FD_LIKELY( !strcmp( config->paths.vote_account, "" ) ) ) {
     415           0 :       FD_TEST( fd_cstr_printf_check( config->paths.vote_account,
     416           0 :                                      sizeof( config->paths.vote_account ),
     417           0 :                                      NULL,
     418           0 :                                      "%s/vote-account.json",
     419           0 :                                      config->paths.base ) );
     420           0 :     }
     421             : 
     422           0 :     strncpy( config->cluster, "development", sizeof(config->cluster) );
     423             : 
     424           0 :     if( FD_UNLIKELY( !config->is_firedancer ) ) {
     425             :       /* By default only_known is true for validators to ensure secure
     426             :         snapshot download, but in development it doesn't matter and
     427             :         often the developer does not provide known peers. */
     428           0 :       config->frankendancer.rpc.only_known = 0;
     429             : 
     430             :       /* When starting from a new genesis block, this needs to be off else
     431             :         the validator will get stuck forever. */
     432           0 :       config->frankendancer.consensus.wait_for_vote_to_start_leader = 0;
     433             : 
     434             :       /* We have to wait until we get a snapshot before we can join a
     435             :         second validator to this one, so make this smaller than the
     436             :         default.  */
     437           0 :       config->frankendancer.snapshots.full_snapshot_interval_slots = fd_uint_min( config->frankendancer.snapshots.full_snapshot_interval_slots, 200U );
     438           0 :     }
     439           0 :   }
     440             : 
     441           3 :   if( FD_UNLIKELY( config->is_firedancer && strcmp( config->firedancer.consensus.wait_for_supermajority_with_bank_hash, "" ) && (!config->consensus.expected_shred_version || config->consensus.wait_for_vote_to_start_leader) ) ) {
     442           0 :     FD_LOG_ERR(( "Config option [consensus.wait_for_supermajority_with_bank_hash] requires consensus.expected_shred_version!=0 and consensus.wait_for_vote_to_start_leader==false." ));
     443           0 :   }
     444             : 
     445           3 : }
     446             : 
     447         282 : #define CFG_HAS_NON_EMPTY( key ) do {                  \
     448         282 :   if( !strnlen( config->key, sizeof(config->key) ) ) { \
     449           0 :     FD_LOG_ERR(( "missing `%s`", #key ));              \
     450           0 :   }                                                    \
     451         282 : } while(0)
     452             : 
     453         630 : #define CFG_HAS_NON_ZERO( key ) do {                           \
     454         630 :   if( !config->key ) { FD_LOG_ERR(( "missing `%s`", #key )); } \
     455         630 : } while(0)
     456             : 
     457          24 : #define CFG_HAS_POW2( key ) do {                       \
     458          24 :   ulong value = (ulong)( config -> key );              \
     459          24 :   if( !value || !fd_ulong_is_pow2( value ) ) {         \
     460           0 :     FD_LOG_ERR(( "`%s` must be a power of 2", #key )); \
     461           0 :   }                                                    \
     462          24 : } while(0)
     463             : 
     464             : static void
     465           9 : fd_config_validatef( fd_configf_t const * config ) {
     466           9 :   CFG_HAS_NON_ZERO( layout.sign_tile_count );
     467           9 :   CFG_HAS_NON_ZERO( layout.resolv_tile_count );
     468           9 :   CFG_HAS_NON_ZERO( layout.execle_tile_count );
     469           9 :   CFG_HAS_NON_ZERO( layout.snapzp_tile_count );
     470           9 :   CFG_HAS_NON_ZERO( layout.snapsv_tile_count );
     471           9 :   CFG_HAS_NON_ZERO( layout.snapsv_io_worker_count );
     472           9 :   CFG_HAS_NON_ZERO( layout.snapdc_tile_count );
     473           9 :   if( FD_UNLIKELY( config->layout.sign_tile_count < 2 ) ) {
     474           0 :     FD_LOG_ERR(( "layout.sign_tile_count must be >= 2" ));
     475           0 :   }
     476             : 
     477           9 :   if( FD_UNLIKELY( config->snapshots.sources.gossip.allow_any && config->snapshots.sources.gossip.allow_list_cnt>0UL ) ) {
     478           0 :     FD_LOG_ERR(( "`snapshots.sources.gossip` has an explicit list of %lu allowed peer(s) in `allow_list` "
     479           0 :                  "but also allows any peer with `allow_any=true`. `allow_list` has no effect and may "
     480           0 :                  "give a false sense of security. Please modify one of the two options and restart.",
     481           0 :                  config->snapshots.sources.gossip.allow_list_cnt ));
     482           0 :   }
     483           9 :   for( ulong i=0UL; i<config->snapshots.sources.gossip.allow_list_cnt; i++ ) {
     484           0 :     for( ulong j=0UL; j<config->snapshots.sources.gossip.block_list_cnt; j++ ) {
     485           0 :       if( FD_UNLIKELY( 0==strcmp( config->snapshots.sources.gossip.allow_list[ i ], config->snapshots.sources.gossip.block_list[ j ] ) ) ) {
     486           0 :         FD_LOG_ERR(( "`snapshots.sources.gossip` has repeated public key `%s` in both allow[%lu] and block[%lu] lists.  "
     487           0 :                      "Please modify one of the two options and restart.", config->snapshots.sources.gossip.allow_list[ i ], i, j ));
     488             : 
     489           0 :       }
     490           0 :     }
     491           0 :   }
     492             : 
     493           9 :   if( FD_UNLIKELY( config->snapshots.server.enabled && !config->layout.enable_snapshot_production ) ) {
     494           0 :     FD_LOG_ERR(( "[snapshots.server].enabled=true requires [layout].enable_snapshot_production=true" ));
     495           0 :   }
     496             : 
     497           9 :   CFG_HAS_NON_ZERO( snapshots.wait_for_peers_timeout_seconds );
     498           9 :   if( FD_UNLIKELY( config->snapshots.server.idle_timeout_millis<100UL ||
     499           9 :                    config->snapshots.server.idle_timeout_millis>=60000UL ) ) {
     500           0 :     FD_LOG_ERR(( "`snapshots.server.idle_timeout_millis` must be in [100,60000)" ));
     501           0 :   }
     502           9 :   if( FD_UNLIKELY( config->snapshots.server.send_timeout_millis<100UL ||
     503           9 :                    config->snapshots.server.send_timeout_millis>=60000UL ) ) {
     504           0 :     FD_LOG_ERR(( "`snapshots.server.send_timeout_millis` must be in [100,60000)" ));
     505           0 :   }
     506             : 
     507           9 :   if( FD_UNLIKELY( config->snapshots.max_full_snapshots_to_keep>FD_SSARCHIVE_MAX_ENTRIES ) ) {
     508           0 :     FD_LOG_ERR(( "`snapshots.max_full_snapshots_to_keep` is %u but must be at most %lu",
     509           0 :                  config->snapshots.max_full_snapshots_to_keep, FD_SSARCHIVE_MAX_ENTRIES ));
     510           0 :   }
     511           9 :   if( FD_UNLIKELY( config->snapshots.max_incremental_snapshots_to_keep>FD_SSARCHIVE_MAX_ENTRIES ) ) {
     512           0 :     FD_LOG_ERR(( "`snapshots.max_incremental_snapshots_to_keep` is %u but must be at most %lu",
     513           0 :                  config->snapshots.max_incremental_snapshots_to_keep, FD_SSARCHIVE_MAX_ENTRIES ));
     514           0 :   }
     515             : 
     516           9 :   CFG_HAS_NON_ZERO( accounts.max_accounts   );
     517           9 :   CFG_HAS_NON_ZERO( accounts.cache_size_gib );
     518             : 
     519           9 :   CFG_HAS_NON_ZERO( development.genesis.max_file_size_mib );
     520           9 :   if( FD_UNLIKELY( config->development.genesis.max_file_size_mib>FD_GENESIS_MAX_FILE_SIZE_MIB ) ) {
     521           3 :     FD_LOG_ERR(( "`development.genesis.max_file_size_mib` must be at most %lu", FD_GENESIS_MAX_FILE_SIZE_MIB ));
     522           3 :   }
     523             : 
     524           6 :   CFG_HAS_NON_ZERO( runtime.program_cache.mean_cache_entry_size );
     525           6 :   CFG_HAS_NON_ZERO( runtime.program_cache.heap_size_mib );
     526           6 :   if( config->runtime.program_cache.mean_cache_entry_size < 4096 ) { FD_LOG_ERR(( "`%s` must be >= 4096", "runtime.program_cache.mean_cache_entry_size" )); }
     527           6 :   if( config->runtime.program_cache.heap_size_mib < 32 ) { FD_LOG_ERR(( "`%s` must be >= 32", "runtime.program_cache.heap_size_mib" )); }
     528           6 : }
     529             : 
     530             : static void
     531          12 : fd_config_validateh( fd_configh_t const * config ) {
     532          12 :   CFG_HAS_NON_EMPTY( dynamic_port_range );
     533             : 
     534          12 :   CFG_HAS_NON_EMPTY( ledger.snapshot_archive_format );
     535             : 
     536          12 :   CFG_HAS_NON_ZERO( snapshots.full_snapshot_interval_slots );
     537          12 :   CFG_HAS_NON_ZERO( snapshots.incremental_snapshot_interval_slots );
     538          12 :   CFG_HAS_NON_ZERO( snapshots.minimum_snapshot_download_speed );
     539          12 :   CFG_HAS_NON_ZERO( snapshots.maximum_snapshot_download_abort );
     540             : 
     541          12 :   CFG_HAS_NON_EMPTY( layout.agave_affinity );
     542          12 :   CFG_HAS_NON_ZERO ( layout.resolh_tile_count );
     543          12 :   CFG_HAS_NON_ZERO ( layout.bank_tile_count );
     544          12 : }
     545             : 
     546             : void
     547          21 : fd_config_validate( fd_config_t const * config ) {
     548          21 :   if( FD_LIKELY( config->is_firedancer ) ) {
     549           9 :     fd_config_validatef( &config->firedancer );
     550          12 :   } else {
     551          12 :     fd_config_validateh( &config->frankendancer );
     552          12 :   }
     553             : 
     554          21 :   CFG_HAS_NON_EMPTY( name );
     555          21 :   CFG_HAS_NON_EMPTY( paths.base );
     556             : 
     557          21 :   CFG_HAS_NON_EMPTY( log.colorize );
     558          21 :   CFG_HAS_NON_EMPTY( log.level_logfile );
     559          21 :   CFG_HAS_NON_EMPTY( log.level_stderr );
     560          21 :   CFG_HAS_NON_EMPTY( log.level_flush );
     561             : 
     562          21 :   CFG_HAS_NON_EMPTY( layout.affinity );
     563          21 :   CFG_HAS_NON_EMPTY( layout.blocklist_cores );
     564          21 :   CFG_HAS_NON_ZERO ( layout.net_tile_count );
     565          21 :   CFG_HAS_NON_ZERO ( layout.quic_tile_count );
     566          21 :   CFG_HAS_NON_ZERO ( layout.verify_tile_count );
     567          21 :   CFG_HAS_NON_ZERO ( layout.shred_tile_count );
     568             : 
     569          21 :   CFG_HAS_NON_EMPTY( hugetlbfs.mount_path );
     570          21 :   CFG_HAS_NON_EMPTY( hugetlbfs.max_page_size );
     571             : 
     572          21 :   CFG_HAS_NON_ZERO( net.ingress_buffer_size );
     573          21 :   if( 0==strcmp( config->net.provider, "xdp" ) ) {
     574           6 :     if( 0!=strcmp( config->net.xdp.xdp_mode, "skb"     ) &&
     575           6 :         0!=strcmp( config->net.xdp.xdp_mode, "drv"     ) &&
     576           6 :         0!=strcmp( config->net.xdp.xdp_mode, "auto"    ) &&
     577           6 :         0!=strcmp( config->net.xdp.xdp_mode, "default" ) ) {
     578           0 :       FD_LOG_ERR(( "invalid `net.xdp.xdp_mode`: \"%s\"; must be \"skb\", \"drv\", \"auto\" or \"default\"",
     579           0 :                    config->net.xdp.xdp_mode ));
     580           0 :     }
     581             : 
     582           6 :     if( 0!=strcmp( config->net.xdp.poll_mode, "prefbusy" ) &&
     583           6 :         0!=strcmp( config->net.xdp.poll_mode, "softirq"  ) &&
     584           6 :         0!=strcmp( config->net.xdp.poll_mode, "auto"     ) ) {
     585           0 :       FD_LOG_ERR(( "invalid `net.xdp.poll_mode`: \"%s\"; must be \"prefbusy\", \"softirq\" or \"auto\"",
     586           0 :                    config->net.xdp.poll_mode ));
     587           0 :     }
     588             : 
     589           6 :     CFG_HAS_POW2     ( net.xdp.xdp_rx_queue_size );
     590           6 :     CFG_HAS_POW2     ( net.xdp.xdp_tx_queue_size );
     591           6 :     if( 0!=strcmp( config->net.xdp.rss_queue_mode, "dedicated" ) &&
     592           6 :         0!=strcmp( config->net.xdp.rss_queue_mode, "simple"    ) &&
     593           6 :         0!=strcmp( config->net.xdp.rss_queue_mode, "auto"      ) ) {
     594           0 :       FD_LOG_ERR(( "invalid `net.xdp.rss_queue_mode`: \"%s\"; must be \"simple\", \"dedicated\" or \"auto\"",
     595           0 :                    config->net.xdp.rss_queue_mode ));
     596           0 :     }
     597          15 :   } else if( 0==strcmp( config->net.provider, "mlx5" ) ) {
     598           3 :     CFG_HAS_POW2( net.mlx5.rx_queue_size );
     599           3 :     CFG_HAS_POW2( net.mlx5.tx_queue_size );
     600           3 :     if( FD_UNLIKELY( config->net.mlx5.rx_queue_size<=FD_MLX5_BATCH_SIZE ||
     601           3 :                      config->net.mlx5.tx_queue_size< FD_MLX5_BATCH_SIZE ) ) {
     602           0 :       FD_LOG_ERR(( "invalid mlx5 queue depth: RX must exceed %u and TX must be at least %u",
     603           0 :                    FD_MLX5_BATCH_SIZE, FD_MLX5_BATCH_SIZE ));
     604           0 :     }
     605           3 :     if( FD_UNLIKELY( config->net.mlx5.rx_queue_size>FD_MLX5_QUEUE_DEPTH_MAX ||
     606           3 :                      config->net.mlx5.tx_queue_size>FD_MLX5_QUEUE_DEPTH_MAX ) ) {
     607           0 :       FD_LOG_ERR(( "invalid mlx5 queue depth: RX and TX must not exceed %u", FD_MLX5_QUEUE_DEPTH_MAX ));
     608           0 :     }
     609          12 :   } else if( 0==strcmp( config->net.provider, "socket" ) ) {
     610           0 :     CFG_HAS_NON_ZERO( net.socket.receive_buffer_size );
     611           0 :     CFG_HAS_NON_ZERO( net.socket.send_buffer_size );
     612          12 :   } else if( 0==strcmp( config->net.provider, "auto" ) ) {
     613             :     /* "auto" is resolved after interface discovery in fd_config_fill(). */
     614           9 :   } else {
     615           3 :     FD_LOG_ERR(( "invalid `net.provider`: \"%s\"; must be \"auto\", \"xdp\", \"socket\" or \"mlx5\"",
     616           3 :                  config->net.provider ));
     617           3 :   }
     618             : 
     619          18 :   CFG_HAS_NON_ZERO( tiles.netlink.max_routes           );
     620          18 :   CFG_HAS_NON_ZERO( tiles.netlink.max_peer_routes      );
     621          18 :   CFG_HAS_NON_ZERO( tiles.netlink.max_neighbors        );
     622             : 
     623          18 :   CFG_HAS_NON_ZERO( tiles.quic.max_concurrent_connections );
     624          18 :   CFG_HAS_NON_ZERO( tiles.quic.txn_reassembly_count );
     625          18 :   CFG_HAS_NON_ZERO( tiles.quic.max_concurrent_handshakes );
     626          18 :   CFG_HAS_NON_ZERO( tiles.quic.idle_timeout_millis );
     627             : 
     628          18 :   CFG_HAS_NON_ZERO( tiles.verify.signature_cache_size );
     629          18 :   CFG_HAS_NON_ZERO( tiles.verify.receive_buffer_size );
     630             : 
     631          18 :   CFG_HAS_NON_ZERO( tiles.dedup.signature_cache_size );
     632             : 
     633          18 :   CFG_HAS_NON_ZERO( tiles.pack.max_pending_transactions );
     634             : 
     635          18 :   CFG_HAS_NON_ZERO( tiles.shred.max_pending_shred_sets );
     636             : 
     637          18 :   if( config->is_firedancer ) {
     638           6 :     CFG_HAS_POW2( tiles.repair.slot_max );
     639           6 :   }
     640             : 
     641          18 :   if( FD_UNLIKELY( config->tiles.bundle.keepalive_interval_millis <    3000 ||
     642          18 :                    config->tiles.bundle.keepalive_interval_millis > 3600000 ) ) {
     643           0 :     FD_LOG_ERR(( "`tiles.bundle.keepalive_interval_millis` must be in range [3000, 3,600,000]" ));
     644           0 :   }
     645             : 
     646          18 :   CFG_HAS_NON_EMPTY( development.core_dump );
     647             : 
     648          18 :   CFG_HAS_NON_ZERO( development.genesis.target_tick_duration_micros );
     649          18 :   CFG_HAS_NON_ZERO( development.genesis.ticks_per_slot );
     650          18 :   CFG_HAS_NON_ZERO( development.genesis.fund_initial_accounts );
     651          18 :   CFG_HAS_NON_ZERO( development.genesis.fund_initial_amount_lamports );
     652             : 
     653          18 :   CFG_HAS_NON_ZERO ( development.bench.benchg_tile_count );
     654          18 :   CFG_HAS_NON_ZERO ( development.bench.benchs_tile_count );
     655          18 :   CFG_HAS_NON_EMPTY( development.bench.affinity );
     656             : 
     657          18 :   CFG_HAS_NON_ZERO( development.bundle.ssl_heap_size_mib );
     658          18 : }
     659             : 
     660             : #undef CFG_HAS_NON_EMPTY
     661             : #undef CFG_HAS_NON_ZERO
     662             : #undef CFG_HAS_POW2
     663             : 
     664             : void
     665             : fd_config_load( int           is_firedancer,
     666             :                 int           is_local_cluster,
     667             :                 char const *  default_config,
     668             :                 ulong         default_config_sz,
     669             :                 char const *  override_config,
     670             :                 char const *  override_config_path,
     671             :                 ulong         override_config_sz,
     672             :                 char const *  user_config,
     673             :                 ulong         user_config_sz,
     674             :                 char const *  user_config_path,
     675             :                 fd_config_t * config,
     676           3 :                 int           dev ) {
     677           3 :   memset( config, 0, sizeof(config_t) );
     678           3 :   config->is_firedancer = is_firedancer;
     679           3 :   config->boot_timestamp_nanos = fd_log_wallclock();
     680             : 
     681           3 :   fd_config_load_buf( config, default_config, default_config_sz, "default.toml" );
     682           3 :   fd_config_validate( config );
     683           3 :   if( FD_UNLIKELY( override_config ) ) {
     684           0 :     fd_config_load_buf( config, override_config, override_config_sz, override_config_path );
     685           0 :     fd_config_validate( config );
     686           0 :   }
     687           3 :   if( FD_LIKELY( user_config ) ) {
     688           0 :     fd_config_load_buf( config, user_config, user_config_sz, user_config_path );
     689           0 :     fd_config_validate( config );
     690           0 :     if( FD_UNLIKELY( user_config_sz>sizeof(config->user_config)-1UL ) ) {
     691           0 :       if( FD_UNLIKELY( config->telemetry && config->tiles.event.url[ 0 ] ) ) {
     692           0 :         FD_LOG_ERR(( "config file (%s) is too large (%lu bytes, max %lu) to report in telemetry", user_config_path, user_config_sz, sizeof(config->user_config)-1UL ));
     693           0 :       }
     694           0 :     } else {
     695           0 :       fd_memcpy( config->user_config, user_config, user_config_sz );
     696           0 :       config->user_config[ user_config_sz ] = '\0';
     697           0 :       config->user_config_len = user_config_sz;
     698           0 :     }
     699           0 :   }
     700             : 
     701           3 :   fd_config_fill( config, is_local_cluster, dev );
     702           3 : }
     703             : 
     704             : int
     705           0 : fd_config_to_memfd( fd_config_t const * config ) {
     706           0 :   int config_memfd = memfd_create( "fd_config", 0 );
     707           0 :   if( FD_UNLIKELY( -1==config_memfd ) ) return -1;
     708           0 :   if( FD_UNLIKELY( -1==ftruncate( config_memfd, sizeof( config_t ) ) ) ) {
     709           0 :     if( FD_UNLIKELY( close( config_memfd ) ) ) FD_LOG_WARNING(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     710           0 :     return -1;
     711           0 :   }
     712             : 
     713           0 :   uchar * bytes = mmap( NULL, sizeof( config_t ), PROT_READ|PROT_WRITE, MAP_SHARED, config_memfd, 0 );
     714           0 :   if( FD_UNLIKELY( bytes==MAP_FAILED ) ) {
     715           0 :     if( FD_UNLIKELY( close( config_memfd ) ) ) FD_LOG_WARNING(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     716           0 :     return -1;
     717           0 :   }
     718             : 
     719           0 :   fd_memcpy( bytes, config, sizeof( config_t ) );
     720           0 :   if( FD_UNLIKELY( munmap( bytes, sizeof( config_t ) ) ) ) {
     721           0 :     FD_LOG_WARNING(( "munmap() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     722           0 :     return -1;
     723           0 :   }
     724             : 
     725           0 :   return config_memfd;
     726           0 : }

Generated by: LCOV version 1.14