From 06368dfa151f04c6cbb0b843b7e9dc795a8193c5 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Wed, 31 Jul 2013 17:43:12 -0400 Subject: [PATCH] LP1204123 Improved per-service control (C/Perl) C opensrf no longer mantains a single master process. Instead, like Perl, each Listener process writes its own PID file and can be managed individually. Related to this, much code was cleaned up in osrf_system.c. osrf_ctl.sh now has the ability to stop/start individual services for Perl and C, using the new -s option. Examples: osrf_ctl.sh -l -a restart_c -s opensrf.math osrf_ctl.sh -l -a restart_perl -s opensrf.settings Signed-off-by: Bill Erickson Signed-off-by: Jason Stephenson --- bin/osrf_ctl.sh.in | 81 ++++- include/opensrf/osrf_system.h | 5 + src/libopensrf/opensrf.c | 70 +++- src/libopensrf/osrf_prefork.c | 1 + src/libopensrf/osrf_system.c | 594 ++++++++++++---------------------- 5 files changed, 341 insertions(+), 410 deletions(-) diff --git a/bin/osrf_ctl.sh.in b/bin/osrf_ctl.sh.in index 18838cd..78995ab 100755 --- a/bin/osrf_ctl.sh.in +++ b/bin/osrf_ctl.sh.in @@ -214,6 +214,7 @@ start_python() { Python processes are either already running, or were not correctly shut down. Now clearing any stale PID files and restarting Perl processes. EOF + rm $PID_OSRF_PYTHON; smart_clear; fi; @@ -247,37 +248,96 @@ start_perl() { Perl processes are either already running, or were not correctly shut down. Now clearing any stale PID files and restarting Perl processes. EOF + rm $PID_OSRF_PERL; smart_clear; fi; + + PL_ARGS="--verbose --pid-dir $OPT_PID_DIR --config $OPT_CONFIG --settings-startup-pause 3"; + + if [ -n "$OPT_SERVICE" ]; then + PL_ARGS="$PL_ARGS --action start --service $OPT_SERVICE"; + else + PL_ARGS="$PL_ARGS --action start_all"; + fi; - opensrf-perl.pl --verbose --pid-dir $OPT_PID_DIR \ - --config $OPT_CONFIG --action start_all --settings-startup-pause 3 - [ "$?" = "0" ] && echo "Perl Started" > $PID_OSRF_PERL; #Dummy pid file, removed when a proper shutdown happens. + echo "opensrf-perl.pl $PL_ARGS"; + opensrf-perl.pl $PL_ARGS; + + #Dummy pid file, removed when a proper shutdown happens. + [ "$?" = "0" ] && echo "Perl Started" > $PID_OSRF_PERL; return 0; } stop_perl() { echo "Stopping OpenSRF Perl"; - opensrf-perl.pl --verbose --pid-dir $OPT_PID_DIR --config $OPT_CONFIG --action stop_all + + PL_ARGS="--verbose --pid-dir $OPT_PID_DIR --config $OPT_CONFIG" + + if [ -n "$OPT_SERVICE" ]; then + PL_ARGS="$PL_ARGS --action stop --service $OPT_SERVICE"; + else + PL_ARGS="$PL_ARGS --action stop_all"; + fi; + + echo "opensrf-perl.pl $PL_ARGS"; + opensrf-perl.pl $PL_ARGS; + + # remove the dummy PID file [ -e $PID_OSRF_PERL ] && rm $PID_OSRF_PERL; sleep 1; return 0; } start_c() { + echo "Starting OpenSRF C"; + + if [ -e $PID_OSRF_C ]; then # If C is started already (or it thinks so) + cat << EOF +C processes are either already running, or were not correctly shut down. +Now clearing any stale PID files and starting C processes. +EOF + rm $PID_OSRF_C; + smart_clear; + fi; + host=$OSRF_HOSTNAME if [ "_$host" = "_" ]; then host=$(perl -MNet::Domain=hostfqdn -e 'print hostfqdn()'); fi; - do_action "start" $PID_OSRF_C "OpenSRF C (host=$host)"; - opensrf-c $host $OPT_CONFIG opensrf "$PID_OSRF_C"; + C_ARGS="-h $host -c $OPT_CONFIG -x opensrf -p $OPT_PID_DIR"; + if [ -n "$OPT_SERVICE" ]; then + C_ARGS="$C_ARGS -s $OPT_SERVICE -a start"; + else + C_ARGS="$C_ARGS -a start_all"; + fi; + + echo "opensrf-c $C_ARGS"; + opensrf-c $C_ARGS; + + #Dummy pid file, removed when a proper shutdown happens. + [ "$?" = "0" ] && echo "C Started" > $PID_OSRF_C; return 0; } stop_c() { - do_action "stop" $PID_OSRF_C "OpenSRF C"; - [ -e $PID_OSRF_C ] && rm $PID_OSRF_C; + echo "Stopping OpenSRF C"; + host=$OSRF_HOSTNAME + if [ "_$host" = "_" ]; then + host=$(perl -MNet::Domain=hostfqdn -e 'print hostfqdn()'); + fi; + + C_ARGS="-h $host -c $OPT_CONFIG -x opensrf -p $OPT_PID_DIR"; + if [ -n "$OPT_SERVICE" ]; then + C_ARGS="$C_ARGS -s $OPT_SERVICE -a stop"; + else + C_ARGS="$C_ARGS -a stop_all"; + fi; + + echo "opensrf-c $C_ARGS"; + opensrf-c $C_ARGS; + + [ -e $PID_OSRF_C ] && rm $PID_OSRF_C; sleep 1; return 0; } @@ -293,6 +353,11 @@ smart_clear() { echo "Smart clearing PID files..."; for line in $(find $OPT_PID_DIR -name *.pid -type f) do + if [ $line = $PID_OSRF_PERL -o $line = $PID_OSRF_C \ + -o $line = $PID_OSRF_PYTHON ]; then + continue; + fi; + running="false"; for p in $(cat $line) do diff --git a/include/opensrf/osrf_system.h b/include/opensrf/osrf_system.h index 328d149..18d8c85 100644 --- a/include/opensrf/osrf_system.h +++ b/include/opensrf/osrf_system.h @@ -27,6 +27,11 @@ int osrfSystemBootstrapClientResc( const char* config_file, int osrfSystemBootstrap( const char* hostname, const char* configfile, const char* contextNode ); +int osrf_system_service_ctrl( + const char* host, const char* config, + const char* context, const char* piddir, + const char* action, const char* service); + transport_client* osrfSystemGetTransportClient( void ); int osrf_system_disconnect_client(); diff --git a/src/libopensrf/opensrf.c b/src/libopensrf/opensrf.c index 5afa27e..27a13ad 100644 --- a/src/libopensrf/opensrf.c +++ b/src/libopensrf/opensrf.c @@ -1,4 +1,8 @@ #include "opensrf/osrf_system.h" +#include +#include +#include +#include /** @brief Run an OSRF server as defined by the command line and a config file. @@ -14,24 +18,61 @@ */ int main( int argc, char* argv[] ) { - if( argc < 4 ) { - fprintf(stderr, "Usage: %s \n", argv[0]); + char* host = NULL; + char* config = NULL; + char* context = NULL; + char* piddir = NULL; + char* action = NULL; + char* service = NULL; + opterr = 0; + + /* values must be strdup'ed because init_proc_title / + * set_proc_title are evil and overwrite the argv memory */ + + int c; + while ((c = getopt(argc, argv, "h:c:x:p:a:s:")) != -1) { + switch (c) { + case 'h': + host = strdup(optarg); + break; + case 'c': + config = strdup(optarg); + break; + case 'x': + context = strdup(optarg); + break; + case 'p': + piddir = strdup(optarg); + break; + case 'a': + action = strdup(optarg); + break; + case 's': + service = strdup(optarg); + break; + default: + continue; + } + } + + + if (!(host && config && context && piddir && action)) { + fprintf(stderr, "Usage: %s -h -c " + "-x -p \n", argv[0]); return 1; } - /* these must be strdup'ed because init_proc_title / set_proc_title - are evil and overwrite the argv memory */ - char* host = strdup( argv[1] ); - char* config = strdup( argv[2] ); - char* context = strdup( argv[3] ); - - if( argv[4] ) - osrfSystemSetPidFile( argv[4] ); + // prepare the proc title hack + init_proc_title(argc, argv); - init_proc_title( argc, argv ); - set_proc_title( "OpenSRF System-C" ); + // make sure the service name is valid + if (service && strlen(service) == 0) { + free(service); + service = NULL; + } - int ret = osrfSystemBootstrap( host, config, context ); + int ret = osrf_system_service_ctrl( + host, config, context, piddir, action, service); if (ret != 0) { osrfLogError( @@ -44,6 +85,9 @@ int main( int argc, char* argv[] ) { free(host); free(config); free(context); + free(piddir); + free(action); + if (service) free(service); return ret; } diff --git a/src/libopensrf/osrf_prefork.c b/src/libopensrf/osrf_prefork.c index d2537e3..cb3860f 100644 --- a/src/libopensrf/osrf_prefork.c +++ b/src/libopensrf/osrf_prefork.c @@ -369,6 +369,7 @@ static int prefork_child_init_hook( prefork_child* child ) { free( isclient ); // Remove traces of our parent's socket connection so we can have our own. + // TODO: not necessary if parent disconnects first osrfSystemIgnoreTransportClient(); // Connect to Jabber diff --git a/src/libopensrf/osrf_system.c b/src/libopensrf/osrf_system.c index f6d338c..f201a15 100644 --- a/src/libopensrf/osrf_system.c +++ b/src/libopensrf/osrf_system.c @@ -23,93 +23,18 @@ #define HOST_NAME_MAX 256 #endif -static void report_child_status( pid_t pid, int status ); -struct child_node; -typedef struct child_node ChildNode; osrfStringArray* log_protect_arr = NULL; -/** - @brief Represents a child process. -*/ -struct child_node -{ - ChildNode* pNext; /**< Linkage pointer for doubly linked list. */ - ChildNode* pPrev; /**< Linkage pointer for doubly linked list. */ - pid_t pid; /**< Process ID of the child process. */ - char* app; - char* libfile; -}; - -/** List of child processes. */ -static ChildNode* child_list; - /** Pointer to the global transport_client; i.e. our connection to Jabber. */ static transport_client* osrfGlobalTransportClient = NULL; -/** Switch to be set by signal handler */ -static volatile sig_atomic_t sig_caught; - /** Boolean: set to true when we finish shutting down. */ static int shutdownComplete = 0; -/** Name of file to which to write the process ID of the child process */ -char* pidfile_name = NULL; - -static void add_child( pid_t pid, const char* app, const char* libfile ); -static void delete_child( ChildNode* node ); -static void delete_all_children( void ); -static ChildNode* seek_child( pid_t pid ); - -/** - @brief Wait on all dead child processes so that they won't be zombies. -*/ -static void reap_children( void ) { - if( sig_caught ) { - if( SIGTERM == sig_caught || SIGINT == sig_caught ) { - osrfLogInfo( OSRF_LOG_MARK, "Killed by %s; terminating", - SIGTERM == sig_caught ? "SIGTERM" : "SIGINT" ); - } else - osrfLogInfo( OSRF_LOG_MARK, "Killed by signal %d; terminating", (int) sig_caught ); - } - - // If we caught a signal, then the signal handler already did a kill(). - // If we didn't, then do the kill() now. - if( ! sig_caught ) - kill( 0, SIGTERM ); - - sleep(1); /* Give the children a chance to die before we reap them. */ - - // Wait for each dead child. The WNOHANG option means to return immediately if - // there are no dead children, instead of waiting for them to die. It is therefore - // possible for a child still to be alive when we exit this function, either because - // it intercepted the SIGTERM and ignored it, or because it took longer to die than - // the time we gave it. - pid_t child_pid; - while( (child_pid = waitpid(-1, NULL, WNOHANG)) > 0 ) - osrfLogInfo(OSRF_LOG_MARK, "Killed child %d", child_pid); +/** Returns the full path to the pid file for the service */ +static char* get_pid_file(const char* path, const char* service); - // Remove all nodes from the list of child processes. - delete_all_children(); -} - -/** - @brief Signal handler for SIGTERM and SIGINT. - - Kill all child processes, and set a switch so that we'll know that the signal arrived. -*/ -static void handleKillSignal( int signo ) { - // First ignore SIGTERM. Otherwise we would send SIGTERM to ourself, intercept it, - // and kill() again in an endless loop. - signal( SIGTERM, SIG_IGN ); - - //Kill all child processes. This is safe to do in a signal handler, because POSIX - // specifies that kill() is reentrant. It is necessary because, if we did the kill() - // only in reap_children() (above), then there would be a narrow window of vulnerability - // in the main loop: if the signal arrives between checking sig_caught and calling wait(), - // we would wait indefinitely for a child to die on its own. - kill( 0, SIGTERM ); - sig_caught = signo; -} +static int stop_service(const char* path, const char* service); /** @brief Return a pointer to the global transport_client. @@ -125,27 +50,6 @@ transport_client* osrfSystemGetTransportClient( void ) { return osrfGlobalTransportClient; } -/** - @brief Save a copy of a file name to be used for writing a process ID. - @param name Designated file name, or NULL. - - Save a file name for later use in saving a process ID. If @a name is NULL, leave - the file name NULL. - - When the parent process spawns a child, the child becomes a daemon. The parent writes the - child's process ID to the PID file, if one has been designated, so that some other process - can retrieve the PID later and kill the daemon. -*/ -void osrfSystemSetPidFile( const char* name ) { - if( pidfile_name ) - free( pidfile_name ); - - if( name ) - pidfile_name = strdup( name ); - else - pidfile_name = NULL; -} - /** @brief Discard the global transport_client, but without disconnecting from Jabber. @@ -209,305 +113,217 @@ int osrfSystemInitCache( void ) { return 0; } -/** - @brief Launch a collection of servers, as defined by the settings server. - @param hostname Full network name of the host where the process is running; or - 'localhost' will do. - @param configfile Name of the configuration file; normally '/openils/conf/opensrf_core.xml'. - @param contextNode Name of an aggregate within the configuration file, containing the - relevant subset of configuration stuff. - @return - Zero if successful, or -1 if not. -*/ -int osrfSystemBootstrap( const char* hostname, const char* configfile, - const char* contextNode ) { - if( !(hostname && configfile && contextNode) ) - return -1; - - // Load the conguration, open the log, open a connection to Jabber - if(!osrfSystemBootstrapClientResc(configfile, contextNode, "settings_grabber" )) { - osrfLogError( OSRF_LOG_MARK, - "Unable to bootstrap for host %s from configuration file %s", - hostname, configfile ); - return -1; - } - - shutdownComplete = 0; - - // Get a list of applications to launch from the settings server - int retcode = osrf_settings_retrieve(hostname); - osrf_system_disconnect_client(); - - if( retcode ) { - osrfLogError( OSRF_LOG_MARK, - "Unable to retrieve settings for host %s from configuration file %s", - hostname, configfile ); - return -1; - } - - // Turn into a daemon. The parent forks and exits. Only the - // child returns, with the standard streams (stdin, stdout, and - // stderr) redirected to /dev/null. - daemonize(); - - jsonObject* apps = osrf_settings_host_value_object("/activeapps/appname"); - osrfStringArray* arr = osrfNewStringArray(8); - - if(apps) { - int i = 0; - - if(apps->type == JSON_STRING) { - osrfStringArrayAdd(arr, jsonObjectGetString(apps)); - - } else { - const jsonObject* app; - while( (app = jsonObjectGetIndex(apps, i++)) ) - osrfStringArrayAdd(arr, jsonObjectGetString(app)); - } - jsonObjectFree(apps); - - const char* appname = NULL; - int first_launch = 1; // Boolean - i = 0; - while( (appname = osrfStringArrayGetString(arr, i++)) ) { - - char* lang = osrf_settings_host_value("/apps/%s/language", appname); - - if(lang && !strcasecmp(lang,"c")) { - - char* libfile = osrf_settings_host_value("/apps/%s/implementation", appname); - - if(! (appname && libfile) ) { - osrfLogWarning( OSRF_LOG_MARK, "Missing appname / libfile in settings config"); - continue; - } - - osrfLogInfo( OSRF_LOG_MARK, "Launching application %s with implementation %s", - appname, libfile); - - pid_t pid; - - if( (pid = fork()) ) { // if parent - // store pid in local list for re-launching dead children... - add_child( pid, appname, libfile ); - osrfLogInfo( OSRF_LOG_MARK, "Running application child %s: process id %ld", - appname, (long) pid ); - - if( first_launch ) { - if( pidfile_name ) { - // Write our own PID to a PID file so that somebody can use it to - // send us a signal later. If we don't find any C apps to launch, - // then we will quietly exit without writing a PID file, and without - // waiting to be killed by a signal. - - FILE* pidfile = fopen( pidfile_name, "w" ); - if( !pidfile ) { - osrfLogError( OSRF_LOG_MARK, "Unable to open PID file \"%s\": %s", - pidfile_name, strerror( errno ) ); - free( pidfile_name ); - pidfile_name = NULL; - return -1; - } else { - fprintf( pidfile, "%ld\n", (long) getpid() ); - fclose( pidfile ); - } - } - first_launch = 0; - } - - } else { // if child, run the application - - osrfLogInfo( OSRF_LOG_MARK, " * Running application %s\n", appname); - if( pidfile_name ) { - free( pidfile_name ); // tidy up some debris from the parent - pidfile_name = NULL; - } - if( osrfAppRegisterApplication( appname, libfile ) == 0 ) - osrf_prefork_run(appname); - - osrfLogDebug( OSRF_LOG_MARK, "Server exiting for app %s and library %s\n", - appname, libfile ); - exit(0); - } - } // language == c - } // end while - } - - osrfStringArrayFree(arr); - - signal(SIGTERM, handleKillSignal); - signal(SIGINT, handleKillSignal); - - // Wait indefinitely for all the child processes to terminate, or for a signal to - // tell us to stop. When there are no more child processes, wait() returns an - // ECHILD error and we break out of the loop. - int status; - pid_t pid; - while( ! sig_caught ) { - pid = wait( &status ); - if( -1 == pid ) { - if( errno == ECHILD ) - osrfLogError( OSRF_LOG_MARK, "We have no more live services... exiting" ); - else if( errno != EINTR ) - osrfLogError(OSRF_LOG_MARK, "Exiting top-level system loop with error: %s", - strerror( errno ) ); - - // Since we're not being killed by a signal as usual, delete the PID file - // so that no one will try to kill us when we're already dead. - if( pidfile_name ) - remove( pidfile_name ); - break; - } else { - report_child_status( pid, status ); - } - } - - reap_children(); - osrfConfigCleanup(); - osrf_system_disconnect_client(); - osrf_settings_free_host_config(NULL); - free( pidfile_name ); - pidfile_name = NULL; - return 0; -} - -/** - @brief Report the exit status of a dead child process, then remove it from the list. - @param pid Process ID of the child. - @param status Exit status as captured by wait(). -*/ -static void report_child_status( pid_t pid, int status ) -{ - const char* app; - const char* libfile; - ChildNode* node = seek_child( pid ); - - if( node ) { - app = node->app ? node->app : "[unknown]"; - libfile = node->libfile ? node->libfile : "[none]"; - } else - app = libfile = ""; - - if( WIFEXITED( status ) ) - { - int rc = WEXITSTATUS( status ); // return code of child process - if( rc ) - osrfLogError( OSRF_LOG_MARK, "Child process %ld (app %s) exited with return code %d", - (long) pid, app, rc ); - else - osrfLogInfo( OSRF_LOG_MARK, "Child process %ld (app %s) exited normally", - (long) pid, app ); - } - else if( WIFSIGNALED( status ) ) - { - osrfLogError( OSRF_LOG_MARK, "Child process %ld (app %s) killed by signal %d", - (long) pid, app, WTERMSIG( status) ); - } - else if( WIFSTOPPED( status ) ) - { - osrfLogError( OSRF_LOG_MARK, "Child process %ld (app %s) stopped by signal %d", - (long) pid, app, (int) WSTOPSIG( status ) ); - } - - delete_child( node ); -} - -/*----------- Routines to manage list of children --*/ - -/** - @brief Add a node to the list of child processes. - @param pid Process ID of the child process. - @param app Name of the child application. - @param libfile Name of the shared library where the child process resides. -*/ -static void add_child( pid_t pid, const char* app, const char* libfile ) -{ - /* Construct new child node */ - - ChildNode* node = safe_malloc( sizeof( ChildNode ) ); - - node->pid = pid; - - if( app ) - node->app = strdup( app ); - else - node->app = NULL; - - if( libfile ) - node->libfile = strdup( libfile ); - else - node->libfile = NULL; - - /* Add new child node to the head of the list */ - - node->pNext = child_list; - node->pPrev = NULL; - - if( child_list ) - child_list->pPrev = node; - - child_list = node; -} - -/** - @brief Remove a node from the list of child processes. - @param node Pointer to the node to be removed. -*/ -static void delete_child( ChildNode* node ) { - - /* Sanity check */ - - if( ! node ) - return; - - /* Detach the node from the list */ - - if( node->pPrev ) - node->pPrev->pNext = node->pNext; - else - child_list = node->pNext; - - if( node->pNext ) - node->pNext->pPrev = node->pPrev; - - /* Deallocate the node and its payload */ - - free( node->app ); - free( node->libfile ); - free( node ); +static char* get_pid_file(const char* piddir, const char* service) { + int nsize = strlen(piddir) + strlen(service) + 6; + char pfname[nsize]; + snprintf(pfname, nsize, "%s/%s.pid", piddir, service); + pfname[nsize-1] = '\0'; + return strdup(pfname); } -/** - @brief Remove all nodes from the list of child processes, rendering it empty. -*/ -static void delete_all_children( void ) { - - while( child_list ) - delete_child( child_list ); +// TERM the process and delete the PID file +static int stop_service(const char* piddir, const char* service) { + char pidstr[16]; + char* pidfile_name = get_pid_file(piddir, service); + FILE* pidfile = fopen(pidfile_name, "r"); + + osrfLogInfo(OSRF_LOG_MARK, "Stopping service %s", service); + + if (pidfile) { + + if (fgets(pidstr, 16, pidfile) != NULL) { + long pid = atol(pidstr); + + if (pid) { + // we have a PID, now send the TERM signal the process + fprintf(stdout, + "* stopping service pid=%ld %s\n", pid, service); + kill(pid, SIGTERM); + } + + } else { + osrfLogWarning(OSRF_LOG_MARK, + "Unable to read pid file %s", pidfile_name); + } + + fclose(pidfile); + + if (unlink(pidfile_name) != 0) { + osrfLogError(OSRF_LOG_MARK, + "Unable to delete pid file %s", pidfile_name); + } + + } else { + osrfLogWarning(OSRF_LOG_MARK, + "Unable to open pidfile %s for reading", pidfile_name); + } + + free(pidfile_name); + return 0; } /** - @brief Find the node for a child process of a given process ID. - @param pid The process ID of the child process. - @return A pointer to the corresponding node if found; otherwise NULL. + @brief Launch one or more opensrf services + @param hostname Full network name of the host where the process is + running; or 'localhost' will do. + @param config Name of the configuration file; + normally '/openils/conf/opensrf_core.xml'. + @param context Name of an aggregate within the configuration file, + containing the relevant subset of configuration stuff. + @param piddir Name of the PID path the PID file directory + @param action Name of action. Options include start, start_all, stop, + and stop_all + @param service Name of the service to start/stop. If no value is + specified, all C-based services are affected + @return - Zero if successful, or -1 if not. */ -static ChildNode* seek_child( pid_t pid ) { - - /* Return a pointer to the child node for the */ - /* specified process ID, or NULL if not found */ - - ChildNode* node = child_list; - while( node ) { - if( node->pid == pid ) - break; - else - node = node->pNext; - } - return node; +// if service is null, all services are started +int osrf_system_service_ctrl( + const char* hostname, const char* config, + const char* context, const char* piddir, + const char* action, const char* service) { + + // Load the conguration, open the log, open a connection to Jabber + if (!osrfSystemBootstrapClientResc(config, context, "c_launcher")) { + osrfLogError(OSRF_LOG_MARK, + "Unable to bootstrap for host %s from configuration file %s", + hostname, config); + return -1; + } + + // Get the list of applications from the settings server + // sometimes the network / settings server is slow to get going, s + // so give it a few tries before giving up. + int j; + int retcode; + for (j = 0; j < 3; j++) { + retcode = osrf_settings_retrieve(hostname); + if (retcode == 0) break; // success + osrfLogInfo(OSRF_LOG_MARK, + "Unable to retrieve settings from settings server, retrying.."); + sleep(1); + } + + // all done talking to the network + osrf_system_disconnect_client(); + + if (retcode) { + osrfLogWarning(OSRF_LOG_MARK, "Unable to retrieve settings for " + "host %s from configuration file %s", hostname, config); + // this usually means settings server isn't running, which can happen + // for a variety of reasons. Log the problem then exit cleanly. + return 0; + } + + jsonObject* apps = osrf_settings_host_value_object("/activeapps/appname"); + + if (!apps) { + osrfLogInfo(OSRF_LOG_MARK, "OpenSRF-C found no apps to run"); + osrfConfigCleanup(); + osrf_settings_free_host_config(NULL); + } + + osrfStringArray* arr = osrfNewStringArray(8); + int i = 0; + + if(apps->type == JSON_STRING) { + osrfStringArrayAdd(arr, jsonObjectGetString(apps)); + + } else { + const jsonObject* app; + while( (app = jsonObjectGetIndex(apps, i++)) ) + osrfStringArrayAdd(arr, jsonObjectGetString(app)); + } + jsonObjectFree(apps); + + i = 0; + const char* appname = NULL; + while ((appname = osrfStringArrayGetString(arr, i++))) { + + if (!appname) { + osrfLogWarning(OSRF_LOG_MARK, + "Invalid service name at index %d", i); + continue; + } + + char* lang = osrf_settings_host_value("/apps/%s/language", appname); + + // this is not a C service, skip it. + if (!lang || strcasecmp(lang, "c")) continue; + + // caller requested a specific service, but not this one + if (service && strcmp(service, appname)) + continue; + + // stop service(s) + if (!strncmp(action, "stop", 4)) { + stop_service(piddir, appname); + continue; + } + + pid_t pid; + if ((pid = fork())) { + // parent process forks the Listener, logs the PID to stdout, + // then goes about its business + fprintf(stdout, + "* starting service pid=%ld %s\n", (long) pid, appname); + continue; + } + + // this is the top-level Listener process. It's responsible + // for managing all of the processes related to a given service. + daemonize(); + + char* libfile = osrf_settings_host_value( + "/apps/%s/implementation", appname); + + if (!libfile) { + osrfLogError(OSRF_LOG_MARK, + "Service %s has no implemention", appname); + exit(1); + } + + osrfLogInfo(OSRF_LOG_MARK, + "Launching application %s with implementation %s", + appname, libfile); + + // write the PID of our newly detached process to the PID file + // pid file name is /path/to/dir/.pid + char* pidfile_name = get_pid_file(piddir, appname); + FILE* pidfile = fopen(pidfile_name, "w"); + if (pidfile) { + osrfLogDebug(OSRF_LOG_MARK, + "Writing PID %ld for service %s", (long) getpid(), appname); + fprintf(pidfile, "%ld\n", (long) getpid()); + fclose(pidfile); + } else { + osrfLogError(OSRF_LOG_MARK, + "Unable to open PID file '%s': %s", + pidfile_name, strerror(errno)); + exit(1); + } + free(pidfile_name); + + if (osrfAppRegisterApplication(appname, libfile) == 0) + osrf_prefork_run(appname); + + osrfLogInfo(OSRF_LOG_MARK, + "Prefork Server exiting for service %s and implementation %s\n", + appname, libfile); + + exit(0); + + } // service name loop + + // main process can now go away + osrfStringArrayFree(arr); + osrfConfigCleanup(); + osrf_settings_free_host_config(NULL); + + return 0; } -/*----------- End of routines to manage list of children --*/ - /** @brief Bootstrap a generic application from info in the configuration file. @param config_file Name of the configuration file. -- 2.43.2