LCOV - code coverage report
Current view: top level - app/shared_dev/commands - dev.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 194 0.0 %
Date: 2025-03-20 12:08:36 Functions: 0 8 0.0 %

          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 : }

Generated by: LCOV version 1.14