aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/librc.c19
-rw-r--r--src/rc.c127
-rw-r--r--src/rc.h1
-rw-r--r--src/runscript.c42
4 files changed, 142 insertions, 47 deletions
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 <sys/types.h>
-#include <sys/signal.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <dlfcn.h>
@@ -17,6 +16,7 @@
#include <getopt.h>
#include <libgen.h>
#include <limits.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -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);
}
}