Line data Source code
1 : #define _GNU_SOURCE
2 :
3 : #include "../../platform/fd_sys_util.h"
4 : #include "../../shared/commands/configure/configure.h"
5 : #include "../../shared/commands/run/run.h"
6 : #include "../../shared/commands/watch/watch.h"
7 : #include "../../../discof/genesis/genesis_hash.h"
8 : #include "../../../disco/net/fd_net_tile.h"
9 :
10 : #include <stdio.h>
11 : #include <stdlib.h> /* setenv */
12 : #include <unistd.h>
13 : #include <sched.h>
14 : #include <fcntl.h>
15 : #include <pthread.h>
16 : #include <sys/wait.h>
17 :
18 : fd_topo_run_tile_t
19 : fdctl_tile_run( fd_topo_tile_t const * tile );
20 :
21 : void
22 : dev_cmd_args( int * pargc,
23 : char *** pargv,
24 0 : args_t * args ) {
25 0 : args->dev.parent_pipefd = -1;
26 0 : args->dev.no_watch = fd_env_strip_cmdline_contains( pargc, pargv, "--no-watch" );
27 0 : args->dev.full_watch = fd_env_strip_cmdline_contains( pargc, pargv, "--full-watch" );
28 0 : args->dev.no_configure = fd_env_strip_cmdline_contains( pargc, pargv, "--no-configure" );
29 0 : args->dev.no_init_workspaces = fd_env_strip_cmdline_contains( pargc, pargv, "--no-init-workspaces" );
30 0 : }
31 :
32 : void
33 : dev_cmd_perm( args_t * args,
34 : fd_cap_chk_t * chk,
35 0 : config_t const * config ) {
36 0 : if( FD_LIKELY( !args->dev.no_configure ) ) {
37 0 : args_t configure_args = {
38 0 : .configure.command = CONFIGURE_CMD_INIT,
39 0 : };
40 0 : for( ulong i=0UL; STAGES[ i ]; i++ )
41 0 : configure_args.configure.stages[ i ] = STAGES[ i ];
42 0 : configure_cmd_perm( &configure_args, chk, config );
43 0 : }
44 :
45 0 : run_cmd_perm( NULL, chk, config );
46 0 : }
47 :
48 : pid_t firedancer_pid, watch_pid;
49 : extern char fd_log_private_path[ 1024 ]; /* empty string on start */
50 :
51 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)
52 :
53 : extern int fd_log_private_restore_stderr;
54 :
55 : static void
56 0 : parent_signal( int sig ) {
57 0 : if( FD_LIKELY( firedancer_pid ) ) kill( firedancer_pid, SIGINT );
58 0 : if( FD_LIKELY( watch_pid ) ) kill( watch_pid, SIGKILL );
59 :
60 0 : if( FD_LIKELY( -1!=fd_log_private_restore_stderr ) ) {
61 0 : if( FD_UNLIKELY( -1==dup2( fd_log_private_restore_stderr, STDERR_FILENO ) ) ) FD_LOG_STDOUT(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
62 0 : }
63 :
64 0 : if( -1!=fd_log_private_logfile_fd() ) FD_LOG_ERR_NOEXIT(( "Received signal %s%s%s %s(%s)%s\n%sLog at \"%s\"%s", fd_log_style_bold(), fd_io_strsignal_name( sig ), fd_log_style_normal(), fd_log_style_dim(), fd_io_strsignal_desc( sig ), fd_log_style_normal(), fd_log_style_dim(), fd_log_private_path, fd_log_style_normal() ));
65 0 : else FD_LOG_ERR_NOEXIT(( "Received signal %s%s%s %s(%s)%s", fd_log_style_bold(), fd_io_strsignal_name( sig ), fd_log_style_normal(), fd_log_style_dim(), fd_io_strsignal_desc( sig ), fd_log_style_normal() ));
66 :
67 0 : if( FD_LIKELY( sig==SIGINT ) ) fd_sys_util_exit_group( 128+SIGINT );
68 0 : else fd_sys_util_exit_group( 0 );
69 0 : }
70 :
71 : static void
72 0 : install_parent_signals( void ) {
73 0 : struct sigaction sa = {
74 0 : .sa_handler = parent_signal,
75 0 : .sa_flags = 0,
76 0 : };
77 0 : if( FD_UNLIKELY( sigaction( SIGTERM, &sa, NULL ) ) )
78 0 : FD_LOG_ERR(( "sigaction(SIGTERM) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
79 0 : if( FD_UNLIKELY( sigaction( SIGINT, &sa, NULL ) ) )
80 0 : FD_LOG_ERR(( "sigaction(SIGINT) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
81 0 : }
82 :
83 : void
84 0 : update_config_for_dev( fd_config_t * config ) {
85 : /* Automatically compute the shred version from genesis if it
86 : exists and we don't know it. If it doesn't exist, we'll keep it
87 : set to zero and get from gossip. */
88 0 : char genesis_path[ PATH_MAX ];
89 0 : fd_memcpy( genesis_path, config->paths.genesis, PATH_MAX );
90 :
91 0 : ushort shred_version = 0;
92 0 : int result = read_genesis_bin( genesis_path, &shred_version, NULL );
93 0 : if( FD_UNLIKELY( -1==result && errno!=ENOENT ) ) FD_LOG_ERR(( "could not compute shred version from genesis file `%s` (%i-%s)", genesis_path, errno, fd_io_strerror( errno ) ));
94 :
95 0 : for( ulong i=0UL; i<config->layout.shred_tile_count; i++ ) {
96 0 : ulong shred_id = fd_topo_find_tile( &config->topo, "shred", i );
97 0 : if( FD_UNLIKELY( shred_id==ULONG_MAX ) ) FD_LOG_ERR(( "could not find shred tile %lu", i ));
98 0 : fd_topo_tile_t * shred = &config->topo.tiles[ shred_id ];
99 0 : if( FD_LIKELY( shred->shred.expected_shred_version==(ushort)0 ) ) {
100 0 : shred->shred.expected_shred_version = shred_version;
101 0 : }
102 0 : }
103 0 : }
104 :
105 : /* Run Firedancer entirely in a single process for development and
106 : debugging convenience. */
107 :
108 : static void
109 : run_firedancer_threaded( config_t * config,
110 0 : int init_workspaces ) {
111 0 : install_parent_signals();
112 :
113 0 : fd_topo_print_log( 0, &config->topo );
114 :
115 0 : run_firedancer_init( config, init_workspaces, 1 );
116 :
117 0 : if( 0==strcmp( config->net.provider, "xdp" ) ) {
118 0 : fd_topo_install_xdp_simple( &config->topo, config->net.bind_address_parsed );
119 0 : }
120 :
121 0 : initialize_accdb_fd( config );
122 0 : initialize_epoch_credits_fd( config );
123 0 : initialize_cost_tracker_fd( config );
124 0 : initialize_vote_stakes_fd( config );
125 0 : initialize_stake_delegations_fd( config );
126 0 : initialize_txncache_fd( config );
127 0 : initialize_store_fds( config );
128 0 : initialize_snapshot_fds( config );
129 :
130 : /* This is kind of a hack, but we have to join all the workspaces as
131 : read-write if we are running things threaded. The reason is that
132 : if one of the earlier tiles maps it in as read-only, later tiles
133 : will reuse the same cached shmem join (the key is only on shmem
134 : name, when it should be (name, mode)). */
135 :
136 0 : fd_topo_join_workspaces( &config->topo, FD_SHMEM_JOIN_MODE_READ_WRITE, config->development.core_dump_level );
137 0 : if( 0==strcmp( config->net.provider, "mlx5" ) ) {
138 0 : fd_topo_install_mlx5( &config->topo, NULL );
139 0 : }
140 0 : if( 0==strcmp( config->net.provider, "iavf" ) ) {
141 0 : fd_topo_install_iavf( &config->topo, NULL );
142 0 : }
143 0 : fd_topo_run_single_process( &config->topo, config->uid, config->gid, fdctl_tile_run );
144 0 : }
145 :
146 : void
147 : dev_cmd_fn( args_t * args,
148 0 : config_t * config ) {
149 0 : if( FD_LIKELY( !args->dev.no_configure ) ) {
150 0 : args_t configure_args = {
151 0 : .configure.command = CONFIGURE_CMD_INIT,
152 0 : };
153 0 : for( ulong i=0UL; STAGES[ i ]; i++ )
154 0 : configure_args.configure.stages[ i ] = STAGES[ i ];
155 0 : configure_cmd_fn( &configure_args, config );
156 0 : }
157 :
158 0 : update_config_for_dev( config );
159 :
160 0 : if( FD_LIKELY( args->dev.no_watch ) ) {
161 0 : if( FD_LIKELY( !config->development.no_clone ) ) run_firedancer( config, args->dev.parent_pipefd, !args->dev.no_init_workspaces );
162 0 : else {
163 0 : run_firedancer_threaded( config , !args->dev.no_init_workspaces );
164 0 : for(;;) pause();
165 0 : }
166 0 : } else {
167 0 : if( FD_LIKELY( !config->development.no_clone ) ) {
168 0 : install_parent_signals();
169 :
170 0 : int pipefd[2];
171 0 : if( FD_UNLIKELY( pipe2( pipefd, O_NONBLOCK ) ) ) FD_LOG_ERR(( "pipe2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
172 0 : initialize_workspaces(config);
173 0 : firedancer_pid = fork();
174 0 : if( !firedancer_pid ) {
175 0 : if( FD_UNLIKELY( -1==close( pipefd[0] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
176 0 : if( FD_UNLIKELY( -1==close( STDERR_FILENO ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
177 0 : if( FD_UNLIKELY( dup2( pipefd[1], STDERR_FILENO ) == -1 ) )
178 0 : FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
179 0 : if( FD_UNLIKELY( -1==close( pipefd[1] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
180 0 : if( FD_UNLIKELY( setenv( "RUST_LOG_STYLE", "always", 1 ) ) ) /* otherwise RUST_LOG will not be colorized to the pipe */
181 0 : FD_LOG_ERR(( "setenv() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
182 0 : run_firedancer( config, -1, 0 );
183 0 : } else {
184 0 : if( FD_UNLIKELY( -1==close( pipefd[1] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
185 0 : }
186 :
187 0 : args_t watch_args;
188 0 : watch_args.watch.drain_output_fd = pipefd[0];
189 0 : watch_args.watch.full = args->dev.full_watch;
190 :
191 0 : watch_pid = fork();
192 0 : if( !watch_pid ) watch_cmd_fn( &watch_args, config );
193 :
194 0 : if( FD_UNLIKELY( close( pipefd[0] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
195 :
196 0 : int wstatus;
197 0 : pid_t exited_pid = wait4( -1, &wstatus, (int)__WALL, NULL );
198 0 : if( FD_UNLIKELY( exited_pid == -1 ) ) FD_LOG_ERR(( "wait4() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
199 :
200 0 : int watch_eof = exited_pid==watch_pid && WIFEXITED( wstatus ) && !WEXITSTATUS( wstatus );
201 0 : if( FD_UNLIKELY( watch_eof ) ) {
202 0 : exited_pid = wait4( firedancer_pid, &wstatus, (int)__WALL, NULL );
203 0 : if( FD_UNLIKELY( exited_pid == -1 ) ) FD_LOG_ERR(( "wait4() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
204 0 : }
205 :
206 0 : char * exited_child = exited_pid == firedancer_pid ? "firedancer" : exited_pid == watch_pid ? "watch" : "unknown";
207 0 : int exit_code = 0;
208 0 : if( FD_UNLIKELY( !WIFEXITED( wstatus ) ) ) {
209 0 : FD_LOG_ERR_NOEXIT(( "%s%s%s exited unexpectedly with signal %d %s(%s)%s", fd_log_style_bold(), exited_child, fd_log_style_normal(), WTERMSIG( wstatus ), fd_log_style_dim(), fd_io_strsignal( WTERMSIG( wstatus ) ), fd_log_style_normal() ));
210 0 : exit_code = WTERMSIG( wstatus );
211 0 : } else {
212 0 : FD_LOG_ERR_NOEXIT(( "%s%s%s exited unexpectedly with code %d", fd_log_style_bold(), exited_child, fd_log_style_normal(), WEXITSTATUS( wstatus ) ));
213 0 : exit_code = WEXITSTATUS( wstatus );
214 0 : }
215 :
216 0 : if( FD_UNLIKELY( exited_pid==watch_pid ) ) {
217 0 : if( FD_UNLIKELY( kill( firedancer_pid, SIGKILL ) ) ) FD_LOG_ERR(( "failed to kill all processes (%i-%s)", errno, fd_io_strerror( errno ) ));
218 0 : } else if( FD_LIKELY( !watch_eof ) ) {
219 0 : if( FD_UNLIKELY( kill( watch_pid, SIGKILL ) ) ) FD_LOG_ERR(( "failed to kill all processes (%i-%s)", errno, fd_io_strerror( errno ) ));
220 0 : }
221 0 : fd_sys_util_exit_group( exit_code );
222 0 : } else {
223 0 : int pipefd[2];
224 0 : if( FD_UNLIKELY( pipe2( pipefd, O_NONBLOCK ) ) ) FD_LOG_ERR(( "pipe2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
225 :
226 0 : if( FD_LIKELY( !args->dev.no_init_workspaces ) ) initialize_workspaces( config );
227 :
228 0 : args_t watch_args;
229 0 : watch_args.watch.drain_output_fd = pipefd[0];
230 0 : watch_args.watch.full = args->dev.full_watch;
231 0 : fd_log_private_restore_stderr = dup( STDERR_FILENO );
232 0 : if( FD_UNLIKELY( fd_log_private_restore_stderr==-1 ) ) FD_LOG_ERR(( "dup() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
233 0 : if( FD_UNLIKELY( -1==dup2( pipefd[ 1 ], STDERR_FILENO ) ) ) FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
234 :
235 0 : run_firedancer_threaded( config, 0 );
236 0 : watch_cmd_fn( &watch_args, config );
237 0 : }
238 0 : }
239 0 : }
|