Re-work the starting and stopping of services so we always know the pid so we can kill our children without the need for process groups which we cannot use as keyboard will not work in init.

This commit is contained in:
Roy Marples 2007-04-20 18:58:42 +00:00
parent a5ba34ec15
commit 35ee67f446
4 changed files with 142 additions and 47 deletions

View File

@ -445,8 +445,6 @@ static pid_t _exec_service (const char *service, const char *arg)
char *file; char *file;
char *fifo; char *fifo;
pid_t pid = -1; pid_t pid = -1;
pid_t savedpid;
int status;
char *svc; char *svc;
file = rc_resolve_service (service); file = rc_resolve_service (service);
@ -483,15 +481,16 @@ static pid_t _exec_service (const char *service, const char *arg)
free (fifo); free (fifo);
free (file); free (file);
if (pid == -1) { if (pid == -1)
eerror ("vfork: %s", strerror (errno)); eerror ("vfork: %s", strerror (errno));
return (pid);
} return (pid);
}
if (rc_is_env ("RC_PARALLEL_STARTUP", "yes")) int rc_waitpid (pid_t pid) {
return (pid); int status = 0;
pid_t savedpid = pid;
savedpid = pid;
errno = 0; errno = 0;
do { do {
pid = waitpid (savedpid, &status, 0); pid = waitpid (savedpid, &status, 0);
@ -501,8 +500,8 @@ static pid_t _exec_service (const char *service, const char *arg)
return (-1); return (-1);
} }
} while (! WIFEXITED (status) && ! WIFSIGNALED (status)); } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
return (0); return (WIFEXITED (status) ? WEXITSTATUS (status) : EXIT_FAILURE);
} }
pid_t rc_stop_service (const char *service) pid_t rc_stop_service (const char *service)

127
src/rc.c
View File

@ -65,8 +65,17 @@ static char *tmp = NULL;
struct termios *termios_orig = 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) static void cleanup (void)
{ {
pidlist_t *pl = service_pids;
rc_plugin_unload (); rc_plugin_unload ();
if (termios_orig) { if (termios_orig) {
@ -74,6 +83,12 @@ static void cleanup (void)
free (termios_orig); free (termios_orig);
} }
while (pl) {
pidlist_t *p = pl->next;
free (pl);
pl = p;
}
rc_strlist_free (env); rc_strlist_free (env);
rc_strlist_free (newenv); rc_strlist_free (newenv);
rc_strlist_free (coldplugged_services); rc_strlist_free (coldplugged_services);
@ -371,6 +386,7 @@ static void sulogin (bool cont)
execl ("/sbin/halt", "/sbin/halt", "-f", (char *) NULL); execl ("/sbin/halt", "/sbin/halt", "-f", (char *) NULL);
eerrorx ("%s: unable to exec `/sbin/halt': %s", applet, strerror (errno)); eerrorx ("%s: unable to exec `/sbin/halt': %s", applet, strerror (errno));
} }
#endif
if (cont) { if (cont) {
int status = 0; int status = 0;
@ -380,22 +396,29 @@ static void sulogin (bool cont)
eerrorx ("%s: vfork: %s", applet, strerror (errno)); eerrorx ("%s: vfork: %s", applet, strerror (errno));
if (pid == 0) { if (pid == 0) {
newenv = rc_filter_env (); newenv = rc_filter_env ();
execl ("/sbin/sulogin", "/sbin/sulogin", #ifdef __linux__
execle ("/sbin/sulogin", "/sbin/sulogin",
getenv ("CONSOLE"), (char *) NULL, newenv); getenv ("CONSOLE"), (char *) NULL, newenv);
eerror ("%s: unable to exec `/sbin/sulogin': %s", applet, eerror ("%s: unable to exec `/sbin/sulogin': %s", applet,
strerror (errno)); 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); _exit (EXIT_FAILURE);
} }
waitpid (pid, &status, 0); waitpid (pid, &status, 0);
} else { } else {
#ifdef __linux
newenv = rc_filter_env (); newenv = rc_filter_env ();
execl ("/sbin/sulogin", "/sbin/sulogin", execle ("/sbin/sulogin", "/sbin/sulogin",
getenv ("CONSOLE"), (char *) NULL, newenv); getenv ("CONSOLE"), (char *) NULL, newenv);
eerrorx ("%s: unable to exec `/sbin/sulogin': %s", applet, strerror (errno)); eerrorx ("%s: unable to exec `/sbin/sulogin': %s", applet, strerror (errno));
}
#else #else
exit (cont ? EXIT_FAILURE : EXIT_SUCCESS); exit (EXIT_SUCCESS);
#endif #endif
}
} }
static void single_user (void) static void single_user (void)
@ -448,14 +471,64 @@ static void wait_for_services ()
select (0, NULL, NULL, NULL, &tv); 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) static void handle_signal (int sig)
{ {
int serrno = errno; int serrno = errno;
char signame[10] = { '\0' }; char signame[10] = { '\0' };
char *run; char *run;
char *prev; char *prev;
pidlist_t *pl;
pid_t pid;
int status = 0;
switch (sig) { 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: case SIGINT:
if (! signame[0]) if (! signame[0])
snprintf (signame, sizeof (signame), "SIGINT"); snprintf (signame, sizeof (signame), "SIGINT");
@ -469,17 +542,21 @@ static void handle_signal (int sig)
case SIGUSR1: case SIGUSR1:
eerror ("rc: Aborting!"); eerror ("rc: Aborting!");
/* Kill any running services we have started */ /* 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 */ /* Notify plugins we are aborting */
rc_plugin_run (rc_hook_abort, "rc"); rc_plugin_run (rc_hook_abort, "rc");
/* Only drop into single user mode if we're booting */
run = getenv ("RUNLEVEL"); run = getenv ("RUNLEVEL");
prev = getenv ("PREVLEVEL"); prev = getenv ("PREVLEVEL");
/* Only drop into single user mode if we're booting */
if ((prev && strcmp (prev, "S") == 0) || if ((prev && strcmp (prev, "S") == 0) ||
(run && strcmp (run, "S") == 0)) (run &&
(strcmp (run, "S") == 0 ||
strcmp (run, "1") == 0)))
single_user (); single_user ();
exit (EXIT_FAILURE); exit (EXIT_FAILURE);
@ -793,6 +870,9 @@ int main (int argc, char **argv)
/* Export our current softlevel */ /* Export our current softlevel */
runlevel = rc_get_runlevel (); 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 /* If we're in the default runlevel and ksoftlevel exists, we should use
that instead */ that instead */
if (newlevel && if (newlevel &&
@ -996,8 +1076,9 @@ int main (int argc, char **argv)
/* We always stop the service when in these runlevels */ /* We always stop the service when in these runlevels */
if (going_down) { if (going_down) {
rc_stop_service (service); pid_t pid = rc_stop_service (service);
continue; 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 */ /* 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; deporder = NULL;
/* After all that we can finally stop the blighter! */ /* After all that we can finally stop the blighter! */
if (! found) if (! found) {
rc_stop_service (service); pid_t pid = rc_stop_service (service);
if (pid > 0 && ! rc_is_env ("RC_PARALLEL_STARTUP", "yes"))
rc_waitpid (pid);
}
} }
rc_strlist_free (types); rc_strlist_free (types);
types = NULL; types = NULL;
/* Wait for our services to finish */ /* 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 */ /* Notify the plugins we have finished */
rc_plugin_run (rc_hook_runlevel_stop_out, runlevel); 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) { STRLIST_FOREACH (start_services, service, i) {
if (rc_service_state (service, rc_service_stopped)) { if (rc_service_state (service, rc_service_stopped)) {
pid_t pid;
if (! interactive) if (! interactive)
interactive = want_interactive (); interactive = want_interactive ();
@ -1137,7 +1222,15 @@ interactive_option:
default: goto 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);
}
} }
} }

View File

@ -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); bool rc_mark_service (const char *service, rc_service_state_t state);
pid_t rc_stop_service (const char *service); pid_t rc_stop_service (const char *service);
pid_t rc_start_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, void rc_schedule_start_service (const char *service,
const char *service_to_start); const char *service_to_start);
char **rc_services_scheduled_by (const char *service); char **rc_services_scheduled_by (const char *service);

View File

@ -9,7 +9,6 @@
#define APPLET "runscript" #define APPLET "runscript"
#include <sys/types.h> #include <sys/types.h>
#include <sys/signal.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <dlfcn.h> #include <dlfcn.h>
@ -17,6 +16,7 @@
#include <getopt.h> #include <getopt.h>
#include <libgen.h> #include <libgen.h>
#include <limits.h> #include <limits.h>
#include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -49,6 +49,7 @@ static bool sighup = false;
static char *ibsave = NULL; static char *ibsave = NULL;
static bool in_background = false; static bool in_background = false;
static rc_hook_t hook_out = 0; static rc_hook_t hook_out = 0;
static pid_t service_pid = 0;
extern char **environ; extern char **environ;
@ -108,6 +109,8 @@ static void handle_signal (int sig)
return; return;
} }
} while (! WIFEXITED (status) && ! WIFSIGNALED (status)); } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
if (pid == service_pid)
service_pid = 0;
break; break;
case SIGINT: case SIGINT:
@ -119,6 +122,9 @@ static void handle_signal (int sig)
case SIGQUIT: case SIGQUIT:
if (! signame[0]) if (! signame[0])
snprintf (signame, sizeof (signame), "SIGQUIT"); 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); eerrorx ("%s: caught %s, aborting", applet, signame);
default: default:
@ -259,18 +265,17 @@ static void cleanup (void)
static bool svc_exec (const char *service, const char *arg1, const char *arg2) static bool svc_exec (const char *service, const char *arg1, const char *arg2)
{ {
int status = 0; bool retval;
pid_t pid;
/* We need to disable our child signal handler now so we block /* We need to disable our child signal handler now so we block
until our script returns. */ until our script returns. */
signal (SIGCHLD, NULL); signal (SIGCHLD, NULL);
pid = vfork(); service_pid = vfork();
if (pid == -1) if (service_pid == -1)
eerrorx ("%s: vfork: %s", service, strerror (errno)); eerrorx ("%s: vfork: %s", service, strerror (errno));
if (pid == 0) { if (service_pid == 0) {
if (rc_exists (RC_SVCDIR "runscript.sh")) { if (rc_exists (RC_SVCDIR "runscript.sh")) {
execl (RC_SVCDIR "runscript.sh", service, service, arg1, arg2, execl (RC_SVCDIR "runscript.sh", service, service, arg1, arg2,
(char *) NULL); (char *) NULL);
@ -286,21 +291,13 @@ static bool svc_exec (const char *service, const char *arg1, const char *arg2)
} }
} }
do { retval = rc_waitpid (service_pid) == 0 ? true : false;
if (waitpid (pid, &status, 0) < 0) {
if (errno != ECHILD)
eerror ("waitpid: %s", strerror (errno));
break;
}
} while (! WIFEXITED (status) && ! WIFSIGNALED (status));
service_pid = 0;
/* Done, so restore the signal handler */ /* Done, so restore the signal handler */
signal (SIGCHLD, handle_signal); signal (SIGCHLD, handle_signal);
if (WIFEXITED (status)) return (retval);
return (WEXITSTATUS (status) ? false : true);
return (false);
} }
static rc_service_state_t svc_status (const char *service) 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, services = rc_get_depends (deptree, types, svclist,
softlevel, depoptions); softlevel, depoptions);
STRLIST_FOREACH (services, svc, i) STRLIST_FOREACH (services, svc, i)
if (rc_service_state (svc, rc_service_stopped)) if (rc_service_state (svc, rc_service_stopped)) {
rc_start_service (svc); pid_t pid = rc_start_service (svc);
if (! rc_is_env ("RC_PARALLEL_STARTUP", "yes"))
rc_waitpid (pid);
}
rc_strlist_free (services); 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) || if (rc_service_state (svc, rc_service_started) ||
rc_service_state (svc, rc_service_inactive)) 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); tmplist = rc_strlist_add (tmplist, svc);
} }
} }