aboutsummaryrefslogtreecommitdiff
path: root/src/rc.c
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2007-04-20 18:58:42 +0000
committerRoy Marples <roy@marples.name>2007-04-20 18:58:42 +0000
commit35ee67f446277f5777a5b9f7feff3e37b315d9d0 (patch)
tree18582ab2a6ccc89bcefbfc9771c777b5ad68c7ba /src/rc.c
parenta5ba34ec151e4e18d7b732dac1517d21a1df3710 (diff)
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.
Diffstat (limited to 'src/rc.c')
-rw-r--r--src/rc.c127
1 files changed, 110 insertions, 17 deletions
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);
+ }
}
}