Line data Source code
1 : #define _GNU_SOURCE
2 :
3 : #include "configure/genesis_hash.h"
4 :
5 : #include "../../shared/fd_sys_util.h"
6 : #include "../../shared/commands/configure/configure.h"
7 : #include "../../shared/commands/run/run.h"
8 : #include "../../shared/commands/monitor/monitor.h"
9 :
10 : #include <stdio.h>
11 : #include <unistd.h>
12 : #include <sched.h>
13 : #include <fcntl.h>
14 : #include <pthread.h>
15 : #include <sys/wait.h>
16 :
17 : fd_topo_run_tile_t
18 : fdctl_tile_run( fd_topo_tile_t const * tile );
19 :
20 : void
21 : dev_cmd_args( int * pargc,
22 : char *** pargv,
23 0 : args_t * args ) {
24 0 : args->dev.parent_pipefd = -1;
25 0 : args->dev.monitor = fd_env_strip_cmdline_contains( pargc, pargv, "--monitor" );
26 0 : args->dev.no_configure = fd_env_strip_cmdline_contains( pargc, pargv, "--no-configure" );
27 0 : args->dev.no_init_workspaces = fd_env_strip_cmdline_contains( pargc, pargv, "--no-init-workspaces" );
28 0 : args->dev.no_agave = fd_env_strip_cmdline_contains( pargc, pargv, "--no-agave" ) ||
29 0 : fd_env_strip_cmdline_contains( pargc, pargv, "--no-solana" );
30 0 : const char * debug_tile = fd_env_strip_cmdline_cstr( pargc, pargv, "--debug-tile", NULL, NULL );
31 0 : if( FD_UNLIKELY( debug_tile ) )
32 0 : strncpy( args->dev.debug_tile, debug_tile, sizeof( args->dev.debug_tile ) - 1 );
33 0 : }
34 :
35 : void
36 : dev_cmd_perm( args_t * args,
37 : fd_cap_chk_t * chk,
38 0 : config_t const * config ) {
39 0 : if( FD_LIKELY( !args->dev.no_configure ) ) {
40 0 : args_t configure_args = {
41 0 : .configure.command = CONFIGURE_CMD_INIT,
42 0 : };
43 0 : for( ulong i=0; i<CONFIGURE_STAGE_COUNT; i++ )
44 0 : configure_args.configure.stages[ i ] = STAGES[ i ];
45 0 : configure_cmd_perm( &configure_args, chk, config );
46 0 : }
47 :
48 0 : run_cmd_perm( NULL, chk, config );
49 0 : }
50 :
51 : pid_t firedancer_pid, monitor_pid;
52 : extern char fd_log_private_path[ 1024 ]; /* empty string on start */
53 :
54 0 : #define FD_LOG_ERR_NOEXIT(a) do { long _fd_log_msg_now = fd_log_wallclock(); fd_log_private_1( 4, _fd_log_msg_now, __FILE__, __LINE__, __func__, fd_log_private_0 a ); } while(0)
55 :
56 : extern int * fd_log_private_shared_lock;
57 :
58 : static void
59 0 : parent_signal( int sig ) {
60 0 : if( FD_LIKELY( firedancer_pid ) ) kill( firedancer_pid, SIGINT );
61 0 : if( FD_LIKELY( monitor_pid ) ) kill( monitor_pid, SIGKILL );
62 :
63 : /* Same hack as in run.c, see comments there. */
64 0 : int lock = 0;
65 0 : fd_log_private_shared_lock = &lock;
66 :
67 0 : if( -1!=fd_log_private_logfile_fd() ) FD_LOG_ERR_NOEXIT(( "Received signal %s\nLog at \"%s\"", fd_io_strsignal( sig ), fd_log_private_path ));
68 0 : else FD_LOG_ERR_NOEXIT(( "Received signal %s", fd_io_strsignal( sig ) ));
69 :
70 0 : if( FD_LIKELY( sig==SIGINT ) ) fd_sys_util_exit_group( 128+SIGINT );
71 0 : else fd_sys_util_exit_group( 0 );
72 0 : }
73 :
74 : static void
75 0 : install_parent_signals( void ) {
76 0 : struct sigaction sa = {
77 0 : .sa_handler = parent_signal,
78 0 : .sa_flags = 0,
79 0 : };
80 0 : if( FD_UNLIKELY( sigaction( SIGTERM, &sa, NULL ) ) )
81 0 : FD_LOG_ERR(( "sigaction(SIGTERM) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
82 0 : if( FD_UNLIKELY( sigaction( SIGINT, &sa, NULL ) ) )
83 0 : FD_LOG_ERR(( "sigaction(SIGINT) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
84 0 : }
85 :
86 :
87 : void
88 0 : update_config_for_dev( config_t * config ) {
89 : /* By default only_known is true for validators to ensure secure
90 : snapshot download, but in development it doesn't matter and
91 : often the developer does not provide known peers. */
92 0 : config->rpc.only_known = 0;
93 :
94 : /* When starting from a new genesis block, this needs to be off else the
95 : validator will get stuck forever. */
96 0 : config->consensus.wait_for_vote_to_start_leader = 0;
97 :
98 : /* We have to wait until we get a snapshot before we can join a second
99 : validator to this one, so make this smaller than the default. */
100 0 : config->snapshots.full_snapshot_interval_slots = fd_uint_min( config->snapshots.full_snapshot_interval_slots, 200U );
101 :
102 : /* Automatically compute the shred version from genesis if it
103 : exists and we don't know it. If it doesn't exist, we'll keep it
104 : set to zero and get from gossip. */
105 0 : char genesis_path[ PATH_MAX ];
106 0 : FD_TEST( fd_cstr_printf_check( genesis_path, PATH_MAX, NULL, "%s/genesis.bin", config->ledger.path ) );
107 0 : ushort shred_version = compute_shred_version( genesis_path, NULL );
108 0 : for( ulong i=0UL; i<config->layout.shred_tile_count; i++ ) {
109 0 : ulong shred_id = fd_topo_find_tile( &config->topo, "shred", i );
110 0 : if( FD_UNLIKELY( shred_id==ULONG_MAX ) ) FD_LOG_ERR(( "could not find shred tile %lu", i ));
111 0 : fd_topo_tile_t * shred = &config->topo.tiles[ shred_id ];
112 0 : if( FD_LIKELY( shred->shred.expected_shred_version==(ushort)0 ) ) {
113 0 : shred->shred.expected_shred_version = shred_version;
114 0 : }
115 0 : }
116 0 : ulong store_id = fd_topo_find_tile( &config->topo, "storei", 0 );
117 0 : if( FD_UNLIKELY( store_id!=ULONG_MAX ) ) {
118 0 : fd_topo_tile_t * storei = &config->topo.tiles[ store_id ];
119 0 : if( FD_LIKELY( storei->store_int.expected_shred_version==(ushort)0 ) ) {
120 0 : storei->store_int.expected_shred_version = shred_version;
121 0 : }
122 0 : }
123 :
124 0 : if( FD_LIKELY( !strcmp( config->consensus.vote_account_path, "" ) ) ) {
125 0 : FD_TEST( fd_cstr_printf_check( config->consensus.vote_account_path,
126 0 : sizeof( config->consensus.vote_account_path ),
127 0 : NULL,
128 0 : "%s/vote-account.json",
129 0 : config->scratch_directory ) );
130 : /* If using bundles, pack and poh need the vote account too */
131 0 : if( FD_UNLIKELY( config->tiles.bundle.enabled ) ) {
132 0 : ulong pack_id = fd_topo_find_tile( &config->topo, "pack", 0 );
133 0 : fd_topo_tile_t * pack_topo = &config->topo.tiles[ pack_id ];
134 0 : memcpy( pack_topo->pack.bundle.vote_account_path, config->consensus.vote_account_path, sizeof(pack_topo->pack.bundle.vote_account_path) );
135 :
136 0 : ulong poh_id = fd_topo_find_tile( &config->topo, "poh", 0 );
137 0 : fd_topo_tile_t * poh_topo = &config->topo.tiles[ poh_id ];
138 0 : memcpy( poh_topo->poh.bundle.vote_account_path, config->consensus.vote_account_path, sizeof(poh_topo->poh.bundle.vote_account_path) );
139 0 : }
140 0 : }
141 :
142 0 : ulong gui_idx = fd_topo_find_tile( &config->topo, "gui", 0UL );
143 0 : if( FD_LIKELY( gui_idx!=ULONG_MAX ) ) {
144 0 : fd_topo_tile_t * gui = &config->topo.tiles[ gui_idx ];
145 0 : gui->gui.is_voting = 1;
146 0 : if( FD_LIKELY( !strcmp( gui->gui.cluster, "unknown" ) ) ) {
147 0 : strcpy( gui->gui.cluster, "development" );
148 0 : }
149 0 : }
150 0 : }
151 :
152 : #if !FD_HAS_NO_AGAVE
153 :
154 : static void *
155 0 : agave_main1( void * args ) {
156 0 : agave_boot( args );
157 0 : return NULL;
158 0 : }
159 :
160 : #endif
161 :
162 : /* Run Firedancer entirely in a single process for development and
163 : debugging convenience. */
164 :
165 : static void
166 0 : run_firedancer_threaded( config_t * config , int init_workspaces) {
167 0 : install_parent_signals();
168 :
169 0 : fd_topo_print_log( 0, &config->topo );
170 :
171 0 : run_firedancer_init( config, init_workspaces );
172 0 : fdctl_setup_netns( config, 1 );
173 :
174 0 : if( FD_UNLIKELY( config->development.debug_tile ) ) {
175 0 : fd_log_private_shared_lock[ 1 ] = 1;
176 0 : }
177 :
178 : /* This is kind of a hack, but we have to join all the workspaces as read-write
179 : if we are running things threaded. The reason is that if one of the earlier
180 : tiles maps it in as read-only, later tiles will reuse the same cached shmem
181 : join (the key is only on shmem name, when it should be (name, mode)). */
182 :
183 0 : if( 0==strcmp( config->development.net.provider, "xdp" ) ) {
184 0 : fd_xdp_fds_t fds = fd_topo_install_xdp( &config->topo );
185 0 : (void)fds;
186 0 : }
187 :
188 0 : fd_topo_join_workspaces( &config->topo, FD_SHMEM_JOIN_MODE_READ_WRITE );
189 0 : fd_topo_run_single_process( &config->topo, 2, config->uid, config->gid, fdctl_tile_run, NULL );
190 :
191 0 : #if !FD_HAS_NO_AGAVE
192 0 : if( FD_LIKELY( !config->development.no_agave ) ) {
193 0 : pthread_t pthread;
194 0 : if( FD_UNLIKELY( pthread_create( &pthread, NULL, agave_main1, config ) ) ) FD_LOG_ERR(( "pthread_create() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
195 0 : if( FD_UNLIKELY( pthread_setname_np( pthread, "fdSolMain" ) ) ) FD_LOG_ERR(( "pthread_setname_np() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
196 0 : }
197 0 : #endif
198 :
199 : /* None of the threads will ever exit, they just abort the process, so sleep forever. */
200 0 : for(;;) pause();
201 0 : }
202 :
203 : void
204 : dev_cmd_fn( args_t * args,
205 0 : config_t * config ) {
206 0 : if( FD_LIKELY( !args->dev.no_configure ) ) {
207 0 : args_t configure_args = {
208 0 : .configure.command = CONFIGURE_CMD_INIT,
209 0 : };
210 0 : for( ulong i=0; i<CONFIGURE_STAGE_COUNT; i++ )
211 0 : configure_args.configure.stages[ i ] = STAGES[ i ];
212 0 : configure_cmd_fn( &configure_args, config );
213 0 : }
214 :
215 0 : update_config_for_dev( config );
216 0 : if( FD_UNLIKELY( args->dev.no_agave ) ) config->development.no_agave = 1;
217 :
218 0 : if( FD_UNLIKELY( strcmp( "", args->dev.debug_tile ) ) ) {
219 0 : if( FD_LIKELY( config->development.sandbox ) ) {
220 0 : FD_LOG_WARNING(( "disabling sandbox to debug tile `%s`", args->dev.debug_tile ));
221 0 : config->development.sandbox = 0;
222 0 : }
223 :
224 0 : if( !strcmp( args->dev.debug_tile, "solana" ) ||
225 0 : !strcmp( args->dev.debug_tile, "agave" ) ) {
226 0 : config->development.debug_tile = UINT_MAX; /* Sentinel value representing Agave */
227 0 : } else {
228 0 : ulong tile_id = fd_topo_find_tile( &config->topo, args->dev.debug_tile, 0UL );
229 0 : if( FD_UNLIKELY( tile_id==ULONG_MAX ) ) FD_LOG_ERR(( "--debug-tile `%s` not present in topology", args->dev.debug_tile ));
230 0 : config->development.debug_tile = 1U+(uint)tile_id;
231 0 : }
232 0 : }
233 :
234 0 : if( FD_LIKELY( !args->dev.monitor ) ) {
235 0 : if( FD_LIKELY( !config->development.no_clone ) ) run_firedancer( config, args->dev.parent_pipefd, !args->dev.no_init_workspaces );
236 0 : else run_firedancer_threaded( config , !args->dev.no_init_workspaces);
237 0 : } else {
238 0 : install_parent_signals();
239 :
240 0 : int pipefd[2];
241 0 : if( FD_UNLIKELY( pipe2( pipefd, O_NONBLOCK ) ) ) FD_LOG_ERR(( "pipe2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
242 0 : initialize_workspaces(config);
243 0 : firedancer_pid = fork();
244 0 : if( !firedancer_pid ) {
245 0 : if( FD_UNLIKELY( close( pipefd[0] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
246 0 : if( FD_UNLIKELY( dup2( pipefd[1], STDERR_FILENO ) == -1 ) )
247 0 : FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
248 0 : if( FD_UNLIKELY( close( pipefd[1] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
249 0 : if( FD_UNLIKELY( setenv( "RUST_LOG_STYLE", "always", 1 ) ) ) /* otherwise RUST_LOG will not be colorized to the pipe */
250 0 : FD_LOG_ERR(( "setenv() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
251 0 : if( FD_LIKELY( !config->development.no_clone ) ) run_firedancer( config, -1, 0 );
252 0 : else run_firedancer_threaded( config , 0);
253 0 : } else {
254 0 : if( FD_UNLIKELY( close( pipefd[1] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
255 0 : }
256 :
257 0 : args_t monitor_args;
258 0 : int argc = 0;
259 0 : char * argv[] = { NULL };
260 0 : char ** pargv = (char**)argv;
261 0 : monitor_cmd_args( &argc, &pargv, &monitor_args );
262 0 : monitor_args.monitor.drain_output_fd = pipefd[0];
263 :
264 0 : monitor_pid = fork();
265 0 : if( !monitor_pid ) monitor_cmd_fn( &monitor_args, config );
266 0 : if( FD_UNLIKELY( close( pipefd[0] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
267 :
268 0 : int wstatus;
269 0 : pid_t exited_pid = wait4( -1, &wstatus, (int)__WALL, NULL );
270 0 : if( FD_UNLIKELY( exited_pid == -1 ) ) FD_LOG_ERR(( "wait4() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
271 :
272 0 : char * exited_child = exited_pid == firedancer_pid ? "firedancer" : exited_pid == monitor_pid ? "monitor" : "unknown";
273 0 : int exit_code = 0;
274 0 : if( FD_UNLIKELY( !WIFEXITED( wstatus ) ) ) {
275 0 : FD_LOG_ERR(( "%s exited unexpectedly with signal %d (%s)", exited_child, WTERMSIG( wstatus ), fd_io_strsignal( WTERMSIG( wstatus ) ) ));
276 0 : exit_code = WTERMSIG( wstatus );
277 0 : } else {
278 0 : FD_LOG_ERR(( "%s exited unexpectedly with code %d", exited_child, WEXITSTATUS( wstatus ) ));
279 0 : if( FD_UNLIKELY( exited_pid == monitor_pid && !WEXITSTATUS( wstatus ) ) ) exit_code = EXIT_FAILURE;
280 0 : else exit_code = WEXITSTATUS( wstatus );
281 0 : }
282 :
283 0 : if( FD_UNLIKELY( exited_pid == monitor_pid ) ) {
284 0 : if( FD_UNLIKELY( kill( firedancer_pid, SIGKILL ) ) )
285 0 : FD_LOG_ERR(( "failed to kill all processes (%i-%s)", errno, fd_io_strerror( errno ) ));
286 0 : } else {
287 0 : if( FD_UNLIKELY( kill( monitor_pid, SIGKILL ) ) )
288 0 : FD_LOG_ERR(( "failed to kill all processes (%i-%s)", errno, fd_io_strerror( errno ) ));
289 0 : }
290 0 : fd_sys_util_exit_group( exit_code );
291 0 : }
292 0 : }
|