Line data Source code
1 : #define _GNU_SOURCE
2 : #include "fd_boot.h"
3 :
4 : #include "../fd_config.h"
5 : #include "../fd_action.h"
6 : #include "../../platform/fd_file_util.h"
7 : #include "../../../disco/topo/fd_topo.h"
8 :
9 : #include <errno.h>
10 : #include <unistd.h>
11 : #include <fcntl.h>
12 : #include <sys/mman.h>
13 :
14 : extern action_t * ACTIONS[];
15 : extern fd_topo_run_tile_t * TILES[];
16 :
17 : extern int * fd_log_private_shared_lock;
18 :
19 : fd_topo_run_tile_t
20 0 : fdctl_tile_run( fd_topo_tile_t const * tile ) {
21 0 : for( ulong i=0UL; TILES[ i ]; i++ ) {
22 0 : if( !strcmp( tile->name, TILES[ i ]->name ) ) return *TILES[ i ];
23 0 : }
24 0 : FD_LOG_ERR(( "tile `%s` not found", tile->name ));
25 0 : return (fd_topo_run_tile_t){0};
26 0 : }
27 :
28 : static void
29 : copy_config_from_fd( int config_fd,
30 0 : config_t * config ) {
31 0 : uchar * bytes = mmap( NULL, sizeof( config_t ), PROT_READ, MAP_PRIVATE, config_fd, 0 );
32 0 : if( FD_UNLIKELY( bytes == MAP_FAILED ) ) FD_LOG_ERR(( "mmap() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
33 0 : fd_memcpy( config, bytes, sizeof( config_t ) );
34 0 : if( FD_UNLIKELY( munmap( bytes, sizeof( config_t ) ) ) ) FD_LOG_ERR(( "munmap() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
35 0 : if( FD_UNLIKELY( close( config_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
36 0 : }
37 :
38 : static int *
39 0 : map_log_memfd( int log_memfd ) {
40 0 : void * shmem = mmap( NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, log_memfd, (off_t)0 );
41 0 : if( FD_UNLIKELY( shmem==MAP_FAILED ) ) {
42 0 : FD_LOG_ERR(( "mmap(NULL,sizeof(int),PROT_READ|PROT_WRITE,MAP_SHARED,memfd,(off_t)0) (%i-%s); ", errno, fd_io_strerror( errno ) ));
43 0 : } else {
44 0 : if( FD_UNLIKELY( mlock( shmem, 4096 ) ) ) {
45 0 : FD_LOG_ERR(( "mlock(%p,4096) (%i-%s); unable to lock log file shared lock in memory\n", shmem, errno, fd_io_strerror( errno ) ));
46 0 : }
47 0 : }
48 0 : return shmem;
49 0 : }
50 :
51 : /* Try to allocate an anonymous page of memory in a file descriptor
52 : (memfd) for fd_log_private_shared_lock such that the log can strictly
53 : sequence messages written by clones of the caller made after the
54 : caller has finished booting the log. Must be a file descriptor so
55 : we can pass it through `execve` calls. */
56 : static int
57 0 : init_log_memfd( void ) {
58 0 : int memfd = memfd_create( "fd_log_lock_page", 0U );
59 0 : if( FD_UNLIKELY( -1==memfd) ) FD_LOG_ERR(( "memfd_create(\"fd_log_lock_page\",0) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
60 0 : if( FD_UNLIKELY( -1==ftruncate( memfd, 4096 ) ) ) FD_LOG_ERR(( "ftruncate(memfd,4096) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
61 0 : return memfd;
62 0 : }
63 :
64 : static int
65 0 : should_colorize( void ) {
66 0 : char const * cstr = fd_env_strip_cmdline_cstr( NULL, NULL, NULL, "COLORTERM", NULL );
67 0 : if( cstr && !strcmp( cstr, "truecolor" ) ) return 1;
68 :
69 0 : cstr = fd_env_strip_cmdline_cstr( NULL, NULL, NULL, "TERM", NULL );
70 0 : if( cstr && !strcmp( cstr, "xterm-256color" ) ) return 1;
71 0 : return 0;
72 0 : }
73 :
74 : void
75 : fd_main_init( int * pargc,
76 : char *** pargv,
77 : config_t * config,
78 : const char * opt_user_config_path,
79 : int is_firedancer,
80 : int is_local_cluster,
81 : char const * log_path,
82 : char const * default_config,
83 : ulong default_config_sz,
84 0 : void (* topo_init )( config_t * config ) ) {
85 0 : fd_log_enable_unclean_exit(); /* Don't call atexit handlers on FD_LOG_ERR */
86 0 : fd_log_level_core_set( 5 ); /* Don't dump core for FD_LOG_ERR during boot */
87 0 : fd_log_colorize_set( should_colorize() ); /* Colorize during boot until we can determine from config */
88 0 : fd_log_level_stderr_set( 2 ); /* Only NOTICE and above will be logged during boot until fd_log is initialized */
89 :
90 0 : int config_fd = fd_env_strip_cmdline_int( pargc, pargv, "--config-fd", NULL, -1 );
91 :
92 0 : fd_memset( config, 0, sizeof( config_t ) );
93 0 : char * thread = "";
94 0 : if( FD_UNLIKELY( config_fd >= 0 ) ) {
95 0 : copy_config_from_fd( config_fd, config );
96 : /* tick_per_ns needs to be synchronized across processes so that
97 : they can coordinate on metrics measurement. */
98 0 : fd_tempo_set_tick_per_ns( config->tick_per_ns_mu, config->tick_per_ns_sigma );
99 0 : } else {
100 0 : char * user_config = NULL;
101 0 : ulong user_config_sz = 0UL;
102 0 : if( FD_LIKELY( opt_user_config_path ) ) {
103 0 : user_config = fd_file_util_read_all( opt_user_config_path, &user_config_sz );
104 0 : if( FD_UNLIKELY( user_config==MAP_FAILED ) ) FD_LOG_ERR(( "failed to read user config file `%s` (%d-%s)", opt_user_config_path, errno, fd_io_strerror( errno ) ));
105 0 : }
106 :
107 0 : int netns = fd_env_strip_cmdline_contains( pargc, pargv, "--netns" );
108 0 : fd_config_load( is_firedancer, netns, is_local_cluster, default_config, default_config_sz, user_config, user_config_sz, opt_user_config_path, config );
109 0 : topo_init( config );
110 :
111 0 : if( FD_UNLIKELY( user_config && -1==munmap( user_config, user_config_sz ) ) ) FD_LOG_ERR(( "munmap() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
112 :
113 0 : config->log.lock_fd = init_log_memfd();
114 0 : config->log.log_fd = -1;
115 0 : thread = "main";
116 0 : if( FD_UNLIKELY( log_path ) )
117 0 : strncpy( config->log.path, log_path, sizeof( config->log.path ) - 1 );
118 0 : }
119 :
120 0 : char * shmem_args[ 3 ];
121 : /* pass in --shmem-path value from the config */
122 0 : shmem_args[ 0 ] = "--shmem-path";
123 0 : shmem_args[ 1 ] = config->hugetlbfs.mount_path;
124 0 : shmem_args[ 2 ] = NULL;
125 0 : char ** argv = shmem_args;
126 0 : int argc = 2;
127 :
128 0 : int * log_lock = map_log_memfd( config->log.lock_fd );
129 0 : ulong pid = fd_sandbox_getpid(); /* Need to read /proc since we might be in a PID namespace now */;
130 :
131 0 : log_path = config->log.path;
132 0 : if( FD_LIKELY( config->log.path[ 0 ]=='\0' ) ) log_path = NULL;
133 :
134 : /* Switch to the sandbox uid/gid for log file creation, so it's always
135 : owned by that user. */
136 :
137 0 : gid_t gid = getgid();
138 0 : uid_t uid = getuid();
139 0 : if( FD_LIKELY( !gid && setegid( config->gid ) ) ) FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
140 0 : if( FD_LIKELY( !uid && seteuid( config->uid ) ) ) FD_LOG_ERR(( "seteuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
141 :
142 0 : int boot_silent = config_fd>=0;
143 0 : fd_log_private_boot_custom( log_lock,
144 0 : 0UL,
145 0 : config->name,
146 0 : 0UL, /* Thread ID will be initialized later */
147 0 : thread, /* Thread will be initialized later */
148 0 : 0UL,
149 0 : config->hostname,
150 0 : fd_log_private_cpu_id_default(),
151 0 : NULL,
152 0 : pid,
153 0 : NULL,
154 0 : pid,
155 0 : config->uid,
156 0 : config->user,
157 0 : 1,
158 0 : config->log.colorize1,
159 0 : boot_silent ? 2 : config->log.level_logfile1,
160 0 : boot_silent ? 2 : config->log.level_stderr1,
161 0 : boot_silent ? 3 : config->log.level_flush1,
162 0 : 5,
163 0 : config->log.log_fd,
164 0 : log_path );
165 :
166 0 : if( FD_UNLIKELY( seteuid( uid ) ) ) FD_LOG_ERR(( "seteuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
167 0 : if( FD_UNLIKELY( setegid( gid ) ) ) FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
168 :
169 0 : config->log.log_fd = fd_log_private_logfile_fd();
170 0 : fd_shmem_private_boot( &argc, &argv );
171 0 : fd_tile_private_boot( 0, NULL );
172 :
173 0 : fd_log_level_logfile_set( config->log.level_logfile1 );
174 0 : fd_log_level_stderr_set( config->log.level_stderr1 );
175 0 : fd_log_level_flush_set( config->log.level_flush1 );
176 0 : }
177 :
178 : static config_t config;
179 :
180 : int
181 : fd_main( int argc,
182 : char ** _argv,
183 : int is_firedancer,
184 : char const * default_config,
185 : ulong default_config_sz,
186 0 : void (* topo_init )( config_t * config ) ) {
187 0 : char ** argv = _argv;
188 0 : argc--; argv++;
189 :
190 : /* Short circuit evaluating help and version commands so that we don't
191 : need to load and evaluate the entire config file to run them.
192 : This is useful for some operators in CI environments where, for
193 : example, they want to show the version or validate the produced
194 : binary without yet setting up the full TOML. */
195 :
196 0 : if( FD_UNLIKELY( !argc ) ) {
197 0 : for( ulong i=0UL; ACTIONS[ i ]; i++ ) {
198 0 : action_t * action = ACTIONS[ i ];
199 0 : if( FD_UNLIKELY( action->is_help ) ) {
200 0 : action->fn( NULL, NULL );
201 0 : FD_LOG_WARNING(( "no subcommand specified, exiting" ));
202 0 : return 1;
203 0 : }
204 0 : }
205 0 : }
206 :
207 : /* We need to strip away (potentially leading) cmdline flags first,
208 : since the parser assumes the action is the leading argument */
209 0 : const char * opt_user_config_path = fd_env_strip_cmdline_cstr(
210 0 : &argc,
211 0 : &argv,
212 0 : "--config",
213 0 : "FIREDANCER_CONFIG_TOML",
214 0 : NULL );
215 :
216 0 : action_t * action = NULL;
217 0 : for( ulong i=0UL; ACTIONS[ i ]; i++ ) {
218 0 : if( FD_UNLIKELY( !strcmp( argv[ 0 ], ACTIONS[ i ]->name ) ||
219 0 : (!strcmp( argv[ 0 ], "--version" ) && !strcmp( "version", ACTIONS[ i ]->name )) ||
220 0 : (!strcmp( argv[ 0 ], "--help" ) && !strcmp( "help", ACTIONS[ i ]->name ))
221 0 : ) ) {
222 0 : action = ACTIONS[ i ];
223 0 : if( FD_UNLIKELY( action->is_immediate ) ) {
224 0 : action->fn( NULL, NULL );
225 0 : return 0;
226 0 : }
227 0 : break;
228 0 : }
229 0 : }
230 :
231 0 : int is_local_cluster = action ? action->is_local_cluster : 0;
232 0 : fd_main_init( &argc, &argv, &config, opt_user_config_path, is_firedancer, is_local_cluster, NULL, default_config, default_config_sz, topo_init );
233 :
234 0 : if( FD_UNLIKELY( !action ) ) {
235 0 : for( ulong i=0UL; ACTIONS[ i ]; i++ ) {
236 0 : action_t * action = ACTIONS[ i ];
237 0 : if( FD_UNLIKELY( action->is_help ) ) {
238 0 : action->fn( NULL, NULL );
239 0 : break;
240 0 : }
241 0 : }
242 0 : FD_LOG_ERR(( "unknown subcommand `%s`", argv[ 0 ] ));
243 0 : }
244 :
245 0 : argc--; argv++;
246 :
247 0 : args_t args = {0};
248 0 : if( FD_LIKELY( action->args ) ) action->args( &argc, &argv, &args );
249 0 : if( FD_UNLIKELY( argc ) ) FD_LOG_ERR(( "unknown argument `%s`", argv[ 0 ] ));
250 :
251 0 : if( FD_LIKELY( action->perm ) ) {
252 0 : fd_cap_chk_t * chk = fd_cap_chk_join( fd_cap_chk_new( __builtin_alloca_with_align( fd_cap_chk_footprint(), FD_CAP_CHK_ALIGN ) ) );
253 :
254 0 : action->perm( &args, chk, &config );
255 :
256 0 : ulong err_cnt = fd_cap_chk_err_cnt( chk );
257 0 : if( FD_UNLIKELY( err_cnt ) ) {
258 0 : for( ulong i=0UL; i<err_cnt; i++ ) FD_LOG_WARNING(( "%s", fd_cap_chk_err( chk, i ) ));
259 :
260 0 : if( FD_LIKELY( action->permission_err ) ) FD_LOG_ERR(( action->permission_err, action->name ));
261 0 : else FD_LOG_ERR(( "insufficient permissions to execute command `%s`", action->name ));
262 0 : }
263 0 : }
264 :
265 0 : action->fn( &args, &config );
266 :
267 0 : return 0;
268 0 : }
|