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