diff --git a/src/librc.c b/src/librc.c index 2a240a83..560359e1 100644 --- a/src/librc.c +++ b/src/librc.c @@ -445,8 +445,6 @@ static pid_t _exec_service (const char *service, const char *arg) char *file; char *fifo; pid_t pid = -1; - pid_t savedpid; - int status; char *svc; file = rc_resolve_service (service); @@ -483,15 +481,16 @@ static pid_t _exec_service (const char *service, const char *arg) free (fifo); free (file); - if (pid == -1) { + if (pid == -1) eerror ("vfork: %s", strerror (errno)); - return (pid); - } + + return (pid); +} - if (rc_is_env ("RC_PARALLEL_STARTUP", "yes")) - return (pid); +int rc_waitpid (pid_t pid) { + int status = 0; + pid_t savedpid = pid; - savedpid = pid; errno = 0; do { pid = waitpid (savedpid, &status, 0); @@ -501,8 +500,8 @@ static pid_t _exec_service (const char *service, const char *arg) return (-1); } } while (! WIFEXITED (status) && ! WIFSIGNALED (status)); - - return (0); + + return (WIFEXITED (status) ? WEXITSTATUS (status) : EXIT_FAILURE); } pid_t rc_stop_service (const char *service) diff --git a/src/rc.c b/src/rc.c index 4910b733..fcb660a4 100644 --- a/src/rc.c +++ b/src/rc.c @@ -65,8 +65,17 @@ static char *tmp = NULL; struct termios *termios_orig = NULL; +typedef struct pidlist +{ + pid_t pid; + struct pidlist *next; +} pidlist_t; +static pidlist_t *service_pids = NULL; + static void cleanup (void) { + pidlist_t *pl = service_pids; + rc_plugin_unload (); if (termios_orig) { @@ -74,6 +83,12 @@ static void cleanup (void) free (termios_orig); } + while (pl) { + pidlist_t *p = pl->next; + free (pl); + pl = p; + } + rc_strlist_free (env); rc_strlist_free (newenv); rc_strlist_free (coldplugged_services); @@ -371,6 +386,7 @@ static void sulogin (bool cont) execl ("/sbin/halt", "/sbin/halt", "-f", (char *) NULL); eerrorx ("%s: unable to exec `/sbin/halt': %s", applet, strerror (errno)); } +#endif if (cont) { int status = 0; @@ -380,22 +396,29 @@ static void sulogin (bool cont) eerrorx ("%s: vfork: %s", applet, strerror (errno)); if (pid == 0) { newenv = rc_filter_env (); - execl ("/sbin/sulogin", "/sbin/sulogin", +#ifdef __linux__ + execle ("/sbin/sulogin", "/sbin/sulogin", getenv ("CONSOLE"), (char *) NULL, newenv); eerror ("%s: unable to exec `/sbin/sulogin': %s", applet, strerror (errno)); +#else + execle ("/bin/sh", "/bin/sh", (char *) NULL, newenv); + eerror ("%s: unable to exec `/bin/sh': %s", applet, + strerror (errno)); +#endif _exit (EXIT_FAILURE); } waitpid (pid, &status, 0); } else { +#ifdef __linux newenv = rc_filter_env (); - execl ("/sbin/sulogin", "/sbin/sulogin", + execle ("/sbin/sulogin", "/sbin/sulogin", getenv ("CONSOLE"), (char *) NULL, newenv); eerrorx ("%s: unable to exec `/sbin/sulogin': %s", applet, strerror (errno)); - } #else - exit (cont ? EXIT_FAILURE : EXIT_SUCCESS); + exit (EXIT_SUCCESS); #endif + } } static void single_user (void) @@ -448,14 +471,64 @@ static void wait_for_services () select (0, NULL, NULL, NULL, &tv); } +static void add_pid (pid_t pid) +{ + pidlist_t *sp = service_pids; + if (sp) { + while (sp->next) + sp = sp->next; + sp->next = rc_xmalloc (sizeof (pidlist_t)); + sp = sp->next; + } else + sp = service_pids = rc_xmalloc (sizeof (pidlist_t)); + memset (sp, 0, sizeof (pidlist_t)); + sp->pid = pid; +} + +static void remove_pid (pid_t pid) +{ + pidlist_t *last = NULL; + pidlist_t *pl; + + for (pl = service_pids; pl; pl = pl->next) { + if (pl->pid == pid) { + if (last) + last->next = pl->next; + else + service_pids = pl->next; + free (pl); + break; + } + last = pl; + } +} + static void handle_signal (int sig) { int serrno = errno; char signame[10] = { '\0' }; char *run; char *prev; + pidlist_t *pl; + pid_t pid; + int status = 0; switch (sig) { + case SIGCHLD: + do { + pid = waitpid (-1, &status, WNOHANG); + if (pid < 0) { + if (errno != ECHILD) + eerror ("waitpid: %s", strerror (errno)); + return; + } + } while (! WIFEXITED (status) && ! WIFSIGNALED (status)); + + /* Remove that pid from our list */ + if (pid > 0) + remove_pid (pid); + break; + case SIGINT: if (! signame[0]) snprintf (signame, sizeof (signame), "SIGINT"); @@ -469,17 +542,21 @@ static void handle_signal (int sig) case SIGUSR1: eerror ("rc: Aborting!"); /* Kill any running services we have started */ - signal (SIGTERM, SIG_IGN); - killpg (getpgrp (), SIGTERM); - + + signal (SIGCHLD, SIG_IGN); + for (pl = service_pids; pl; pl = pl->next) + kill (pl->pid, SIGTERM); + /* Notify plugins we are aborting */ rc_plugin_run (rc_hook_abort, "rc"); - + + /* Only drop into single user mode if we're booting */ run = getenv ("RUNLEVEL"); prev = getenv ("PREVLEVEL"); - /* Only drop into single user mode if we're booting */ if ((prev && strcmp (prev, "S") == 0) || - (run && strcmp (run, "S") == 0)) + (run && + (strcmp (run, "S") == 0 || + strcmp (run, "1") == 0))) single_user (); exit (EXIT_FAILURE); @@ -793,6 +870,9 @@ int main (int argc, char **argv) /* Export our current softlevel */ runlevel = rc_get_runlevel (); + /* Now we start handling our children */ + signal (SIGCHLD, handle_signal); + /* If we're in the default runlevel and ksoftlevel exists, we should use that instead */ if (newlevel && @@ -996,8 +1076,9 @@ int main (int argc, char **argv) /* We always stop the service when in these runlevels */ if (going_down) { - rc_stop_service (service); - continue; + pid_t pid = rc_stop_service (service); + if (pid > 0 && ! rc_is_env ("RC_PARALLEL_STARTUP", "yes")) + rc_waitpid (pid); } /* If we're in the start list then don't bother stopping us */ @@ -1058,15 +1139,17 @@ int main (int argc, char **argv) deporder = NULL; /* After all that we can finally stop the blighter! */ - if (! found) - rc_stop_service (service); + if (! found) { + pid_t pid = rc_stop_service (service); + if (pid > 0 && ! rc_is_env ("RC_PARALLEL_STARTUP", "yes")) + rc_waitpid (pid); + } } rc_strlist_free (types); types = NULL; /* Wait for our services to finish */ - if (rc_is_env ("RC_PARALLEL_STARTUP", "yes")) - wait_for_services (); + wait_for_services (); /* Notify the plugins we have finished */ rc_plugin_run (rc_hook_runlevel_stop_out, runlevel); @@ -1117,6 +1200,8 @@ int main (int argc, char **argv) STRLIST_FOREACH (start_services, service, i) { if (rc_service_state (service, rc_service_stopped)) { + pid_t pid; + if (! interactive) interactive = want_interactive (); @@ -1137,7 +1222,15 @@ interactive_option: default: goto interactive_option; } } - rc_start_service (service); + + /* Remember the pid if we're running in parallel */ + if ((pid = rc_start_service (service))) + add_pid (pid); + + if (! rc_is_env ("RC_PARALLEL_STARTUP", "yes")) { + rc_waitpid (pid); + remove_pid (pid); + } } } diff --git a/src/rc.h b/src/rc.h index aa49903e..c4b651be 100644 --- a/src/rc.h +++ b/src/rc.h @@ -44,6 +44,7 @@ bool rc_service_state (const char *service, rc_service_state_t state); bool rc_mark_service (const char *service, rc_service_state_t state); pid_t rc_stop_service (const char *service); pid_t rc_start_service (const char *service); +int rc_waitpid (pid_t pid); void rc_schedule_start_service (const char *service, const char *service_to_start); char **rc_services_scheduled_by (const char *service); diff --git a/src/runscript.c b/src/runscript.c index c82c5f9a..73333605 100644 --- a/src/runscript.c +++ b/src/runscript.c @@ -9,7 +9,6 @@ #define APPLET "runscript" #include -#include #include #include #include @@ -17,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +49,7 @@ static bool sighup = false; static char *ibsave = NULL; static bool in_background = false; static rc_hook_t hook_out = 0; +static pid_t service_pid = 0; extern char **environ; @@ -108,6 +109,8 @@ static void handle_signal (int sig) return; } } while (! WIFEXITED (status) && ! WIFSIGNALED (status)); + if (pid == service_pid) + service_pid = 0; break; case SIGINT: @@ -119,6 +122,9 @@ static void handle_signal (int sig) case SIGQUIT: if (! signame[0]) snprintf (signame, sizeof (signame), "SIGQUIT"); + /* Send the signal to our children too */ + if (service_pid > 0) + kill (service_pid, sig); eerrorx ("%s: caught %s, aborting", applet, signame); default: @@ -259,18 +265,17 @@ static void cleanup (void) static bool svc_exec (const char *service, const char *arg1, const char *arg2) { - int status = 0; - pid_t pid; + bool retval; /* We need to disable our child signal handler now so we block until our script returns. */ signal (SIGCHLD, NULL); - pid = vfork(); + service_pid = vfork(); - if (pid == -1) + if (service_pid == -1) eerrorx ("%s: vfork: %s", service, strerror (errno)); - if (pid == 0) { + if (service_pid == 0) { if (rc_exists (RC_SVCDIR "runscript.sh")) { execl (RC_SVCDIR "runscript.sh", service, service, arg1, arg2, (char *) NULL); @@ -286,21 +291,13 @@ static bool svc_exec (const char *service, const char *arg1, const char *arg2) } } - do { - if (waitpid (pid, &status, 0) < 0) { - if (errno != ECHILD) - eerror ("waitpid: %s", strerror (errno)); - break; - } - } while (! WIFEXITED (status) && ! WIFSIGNALED (status)); + retval = rc_waitpid (service_pid) == 0 ? true : false; + service_pid = 0; /* Done, so restore the signal handler */ signal (SIGCHLD, handle_signal); - if (WIFEXITED (status)) - return (WEXITSTATUS (status) ? false : true); - - return (false); + return (retval); } static rc_service_state_t svc_status (const char *service) @@ -467,8 +464,11 @@ static void svc_start (const char *service, bool deps) services = rc_get_depends (deptree, types, svclist, softlevel, depoptions); STRLIST_FOREACH (services, svc, i) - if (rc_service_state (svc, rc_service_stopped)) - rc_start_service (svc); + if (rc_service_state (svc, rc_service_stopped)) { + pid_t pid = rc_start_service (svc); + if (! rc_is_env ("RC_PARALLEL_STARTUP", "yes")) + rc_waitpid (pid); + } rc_strlist_free (services); } @@ -669,7 +669,9 @@ static void svc_stop (const char *service, bool deps) if (rc_service_state (svc, rc_service_started) || rc_service_state (svc, rc_service_inactive)) { - rc_stop_service (svc); + pid_t pid = rc_stop_service (svc); + if (! rc_is_env ("RC_PARALLEL_STARTUP", "yes")) + rc_waitpid (pid); tmplist = rc_strlist_add (tmplist, svc); } }