From ff43e389fd744022e3e68e803e454950379593ce Mon Sep 17 00:00:00 2001 From: Lebbeous Fogle-Weekley Date: Tue, 4 Feb 2014 17:56:07 -0500 Subject: [PATCH] LP#1286198: Teach osrf_router to (optionally) write its own PID files Also, tiny bit of noise squelching on osrf_control/opensrf-perl.pl Signed-off-by: Lebbeous Fogle-Weekley Signed-off-by: Mike Rylander Signed-off-by: Galen Charlton --- bin/opensrf-perl.pl.in | 19 +++----- include/opensrf/utils.h | 1 + src/libopensrf/utils.c | 20 +++++++- src/router/osrf_router_main.c | 89 +++++++++++++++++++++++++++++++++-- 4 files changed, 110 insertions(+), 19 deletions(-) diff --git a/bin/opensrf-perl.pl.in b/bin/opensrf-perl.pl.in index c95e2b3..20b1829 100755 --- a/bin/opensrf-perl.pl.in +++ b/bin/opensrf-perl.pl.in @@ -288,20 +288,11 @@ sub do_diagnostic { sub do_start_router { - `opensrf_router $opt_config routers`; - - sleep 2; # give the router time to fork - my @pids = `ps -C opensrf_router -o pid=`; - s/^\s*|\n//g for @pids; my $pidfile = get_pid_file('router'); - open(PF, '>', $pidfile) or die "Cannot open $pidfile: $!\n"; - foreach (@pids) { - chomp; - msg("starting service pid=$_ router"); - print PF "$_\n"; - } - close PF; + `opensrf_router $opt_config routers $pidfile`; + + sleep 2; # give the router time to fork (probably not need now but w/e) } # stop a specific service @@ -762,7 +753,9 @@ do_init() and verify_services($opt_service) if $opt_start_services or $opt_restart or $opt_restart_all or - $opt_restart_services) and $opt_service ne 'router'; + $opt_restart_services) and ( + not defined $opt_service or $opt_service ne 'router' + ); # starting services. do_init() handled above do_start($opt_service) if $opt_start; diff --git a/include/opensrf/utils.h b/include/opensrf/utils.h index 2f9c786..2276dd6 100644 --- a/include/opensrf/utils.h +++ b/include/opensrf/utils.h @@ -280,6 +280,7 @@ extern "C" { int init_proc_title( int argc, char* argv[] ); int set_proc_title( const char* format, ... ); +int daemonizeWithCallback( void (*)(pid_t, int), int ); int daemonize( void ); void* safe_malloc(int size); diff --git a/src/libopensrf/utils.c b/src/libopensrf/utils.c index 52e8a68..6628c8c 100644 --- a/src/libopensrf/utils.c +++ b/src/libopensrf/utils.c @@ -645,12 +645,14 @@ char* uescape( const char* string, int size, int full_escape ) { /** @brief Become a proper daemon. + @param callback Ptr to a function. From parent, called with child PID as first argument, and the next argument to *this* function as the second argument. From child, called with -1, -1 (yes pid_t is signed) + @param callback_arg An integer argument passed as the second argument to the callback function from the parent process post-fork() @return 0 if successful, or -1 if not. Call fork(). The parent exits. The child moves to the root directory, detaches from the terminal, and redirects the standard streams (stdin, stdout, stderr) to /dev/null. */ -int daemonize( void ) { +int daemonizeWithCallback( void (*callback)(pid_t, int), int callback_arg ) { pid_t f = fork(); if (f == -1) { @@ -659,6 +661,9 @@ int daemonize( void ) { } else if (f == 0) { // We're in the child now... + if( callback ) + callback( -1, -1 ); + // Change directories. Otherwise whatever directory // we're in couldn't be deleted until the program // terminated -- possibly causing some inconvenience. @@ -676,10 +681,23 @@ int daemonize( void ) { return 0; } else { // We're in the parent... + if( callback ) + callback( f, callback_arg ); + _exit(0); } } +/** + @brief Become a proper daemon. + @return 0 if successful, or -1 if not. + + See daemonizeWithCallback() for details. +*/ +int daemonize( void ) { + return daemonizeWithCallback( NULL, 0 ); +} + /** @brief Determine whether a string represents a decimal integer. diff --git a/src/router/osrf_router_main.c b/src/router/osrf_router_main.c index 202e99a..5e51062 100644 --- a/src/router/osrf_router_main.c +++ b/src/router/osrf_router_main.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "opensrf/utils.h" #include "opensrf/log.h" @@ -30,7 +31,14 @@ static osrfRouter* router = NULL; static volatile sig_atomic_t stop_signal = 0; -static void setupRouter( const jsonObject* configChunk ); +static void setupRouter( const jsonObject* configChunk, int configPos ); + +/* I think it's important these following things not be static */ +pid_t* daemon_pid_list; +size_t daemon_pid_list_size; + + +void storeRouterDaemonPid( pid_t, int ); /** @brief Respond to signal by setting a switch that will interrupt the main loop. @@ -58,12 +66,14 @@ int main( int argc, char* argv[] ) { if( argc < 3 ) { osrfLogError( OSRF_LOG_MARK, - "Usage: %s ", argv[0] ); + "Usage: %s [pid_file]", + argv[0] ); exit( EXIT_FAILURE ); } const char* config_file = argv[1]; const char* context = argv[2]; + char* pid_file = argc >= 4 ? strdup(argv[3]) : NULL; /* Get a set of router definitions from a config file */ @@ -87,11 +97,35 @@ int main( int argc, char* argv[] ) { init_proc_title( argc, argv ); set_proc_title( "OpenSRF Router" ); + /* Set up some shared memory so after some forking(), our children + * can tell us about all our grandchildren's PIDs, and we can write + * them to a file. + */ + daemon_pid_list_size = configInfo->size * sizeof(pid_t); + daemon_pid_list = mmap( + NULL, daemon_pid_list_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_SHARED, -1 , 0 + ); + + if ( daemon_pid_list == MAP_FAILED ) { + osrfLogError( + OSRF_LOG_MARK, + "mmap() for router daemon PID list failed: %s", + strerror(errno) + ); + exit( EXIT_FAILURE ); + } + + memset( daemon_pid_list, 0, daemon_pid_list_size ); + /* Spawn child process(es) */ int rc = EXIT_SUCCESS; int parent = 1; // boolean int i; + + FILE *pid_fp; + for(i = 0; i < configInfo->size; i++) { const jsonObject* configChunk = jsonObjectGetIndex( configInfo, i ); if( ! jsonObjectGetKeyConst( configChunk, "transport" ) ) @@ -107,8 +141,10 @@ int main( int argc, char* argv[] ) { // typo or other such error, making it look spurious. In that case, well, too bad. continue; } + if(fork() == 0) { /* create a new child to run this router instance */ - setupRouter(configChunk); + free( pid_file ); /* we don't need this as a child */ + setupRouter(configChunk, i); parent = 0; break; /* We're a child; don't spawn any more children here */ } @@ -150,6 +186,43 @@ int main( int argc, char* argv[] ) { rc = EXIT_FAILURE; } } + + /* If rc is still EXIT_SUCCESS after the preceding loop, + * all our children have spawned grandchildren and will have + * reported their IDs via our shared memory daemon_pid_list + * buffer by now. + * + * A note about that list: it's going to have one pid_t-sized + * slot for every configInfo chunk in the config. Commonly, + * this code sees empty chunks or chunks that don't correspond + * to full router configs, so the slots for these unused chunks + * get left at zero. We skip those zeros both when writing the + * PID file and when reporting to the log. + * */ + if ( rc == EXIT_SUCCESS ) { + if ( pid_file ) { + if ( (pid_fp = fopen(pid_file, "w")) ) { + for (i = 0; i < configInfo->size; i++) { + if ( daemon_pid_list[i] > 0 ) + fprintf(pid_fp, "%d\n", daemon_pid_list[i]); + } + fclose(pid_fp); + } else { + osrfLogWarning(OSRF_LOG_MARK, + "Tried to write PID file at %s but couldn't: %s", + pid_file, strerror(errno)); + } + free( pid_file ); + } + +// for (i = 0; i < configInfo->size; i++) { +// if ( daemon_pid_list[i] > 0 ) { +// osrfLogInfo(OSRF_LOG_MARK, +// "Reporting a grandchild with PID %d", daemon_pid_list[i]); +// } +// } + } + munmap( daemon_pid_list, daemon_pid_list_size ); } if( stop_signal ) { @@ -166,11 +239,12 @@ int main( int argc, char* argv[] ) { /** @brief Configure and run a child process. @param configChunk Pointer to a subset of the loaded configuration. + @param configPos Index of configChunk in its original containing array, doubling as index for storing PID of child from daemonization (the original process' grandchild) Configure oneself, daemonize, and then call osrfRouterRun() to go into a near-endless loop. Return when interrupted by a signal, or when something goes wrong. */ -static void setupRouter( const jsonObject* configChunk ) { +static void setupRouter( const jsonObject* configChunk, int configPos ) { const jsonObject* transport_cfg = jsonObjectGetKeyConst( configChunk, "transport" ); @@ -266,7 +340,7 @@ static void setupRouter( const jsonObject* configChunk ) { // Done configuring? Let's get to work. - daemonize(); + daemonizeWithCallback( storeRouterDaemonPid, configPos ); osrfRouterRun( router ); osrfRouterFree(router); @@ -275,3 +349,8 @@ static void setupRouter( const jsonObject* configChunk ) { return; } + +void storeRouterDaemonPid( pid_t p, int i ) { + if( i != -1 ) + daemon_pid_list[i] = p; +} -- 2.43.2