Block signals to avoid fork /signal races.

This commit is contained in:
Roy Marples 2008-02-02 00:17:35 +00:00
parent fef5d0af59
commit ad04517623
7 changed files with 131 additions and 80 deletions

2
TODO
View File

@ -1,3 +1,3 @@
- stop using signal() and convert everything to sigaction() - ensure all forks block, restore and unblock signals. needs review
- add support somehow for optional translations - add support somehow for optional translations

View File

@ -34,7 +34,6 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <errno.h> #include <errno.h>
#include <signal.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
@ -134,7 +133,7 @@ bool rc_conf_yesno (const char *var);
char **env_filter (void); char **env_filter (void);
char **env_config (void); char **env_config (void);
bool service_plugable (const char *service); bool service_plugable (const char *service);
void signal_setup (int sig, void (*handler)(int)); int signal_setup (int sig, void (*handler)(int));
/* basename_c never modifies the argument. As such, if there is a trailing /* basename_c never modifies the argument. As such, if there is a trailing
* slash then an empty string is returned. */ * slash then an empty string is returned. */

View File

@ -32,6 +32,7 @@
const char librc_copyright[] = "Copyright (c) 2007-2008 Roy Marples"; const char librc_copyright[] = "Copyright (c) 2007-2008 Roy Marples";
#include "librc.h" #include "librc.h"
#include <signal.h>
#define SOFTLEVEL RC_SVCDIR "/softlevel" #define SOFTLEVEL RC_SVCDIR "/softlevel"
@ -575,6 +576,10 @@ 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;
sigset_t empty;
sigset_t full;
sigset_t old;
struct sigaction sa;
file = rc_service_resolve (service); file = rc_service_resolve (service);
if (! exists (file)) { if (! exists (file)) {
@ -593,13 +598,37 @@ static pid_t _exec_service (const char *service, const char *arg)
return (-1); return (-1);
} }
if ((pid = vfork ()) == 0) { /* We need to block signals until we have forked */
memset (&sa, 0, sizeof (sa));
sa.sa_handler = SIG_DFL;
sigemptyset (&sa.sa_mask);
sigemptyset (&empty);
sigfillset (&full);
sigprocmask (SIG_SETMASK, &full, &old);
if ((pid = fork ()) == 0) {
/* Restore default handlers */
sigaction (SIGCHLD, &sa, NULL);
sigaction (SIGHUP, &sa, NULL);
sigaction (SIGINT, &sa, NULL);
sigaction (SIGQUIT, &sa, NULL);
sigaction (SIGTERM, &sa, NULL);
sigaction (SIGUSR1, &sa, NULL);
sigaction (SIGWINCH, &sa, NULL);
/* Unmask signals */
sigprocmask (SIG_SETMASK, &empty, NULL);
/* Safe to run now */
execl (file, file, arg, (char *) NULL); execl (file, file, arg, (char *) NULL);
fprintf (stderr, "unable to exec `%s': %s\n", file, strerror (errno)); fprintf (stderr, "unable to exec `%s': %s\n",
file, strerror (errno));
unlink (fifo); unlink (fifo);
_exit (EXIT_FAILURE); _exit (EXIT_FAILURE);
} }
sigprocmask (SIG_SETMASK, &old, NULL);
free (fifo); free (fifo);
free (file); free (file);

View File

@ -432,13 +432,12 @@ bool service_plugable (const char *service)
return (allow); return (allow);
} }
void signal_setup (int sig, void (*handler)(int)) int signal_setup (int sig, void (*handler)(int))
{ {
struct sigaction sa; struct sigaction sa;
memset (&sa, 0, sizeof (sa)); memset (&sa, 0, sizeof (sa));
sa.sa_handler = handler;
sigemptyset (&sa.sa_mask); sigemptyset (&sa.sa_mask);
if (sigaction (sig, &sa, NULL) == -1) sa.sa_handler = handler;
eerrorx ("sigaction: %s", strerror (errno)); return (sigaction (sig, &sa, NULL));
} }

View File

@ -143,84 +143,110 @@ int rc_waitpid (pid_t pid)
void rc_plugin_run (rc_hook_t hook, const char *value) void rc_plugin_run (rc_hook_t hook, const char *value)
{ {
plugin_t *plugin = plugins; plugin_t *plugin = plugins;
struct sigaction sa;
sigset_t empty;
sigset_t full;
sigset_t old;
/* Don't run plugins if we're in one */ /* Don't run plugins if we're in one */
if (rc_in_plugin) if (rc_in_plugin)
return; return;
/* We need to block signals until we have forked */
memset (&sa, 0, sizeof (sa));
sa.sa_handler = SIG_DFL;
sigemptyset (&sa.sa_mask);
sigemptyset (&empty);
sigfillset (&full);
while (plugin) { while (plugin) {
if (plugin->hook) { int i;
int i; int flags;
int flags; int pfd[2];
int pfd[2]; pid_t pid;
pid_t pid; char *buffer;
char *token;
char *p;
ssize_t nr;
/* We create a pipe so that plugins can affect our environment if (! plugin->hook) {
* vars, which in turn influence our scripts. */ plugin = plugin->next;
if (pipe (pfd) == -1) { continue;
eerror ("pipe: %s", strerror (errno)); }
return;
}
/* Stop any scripts from inheriting us. /* We create a pipe so that plugins can affect our environment
* This is actually quite important as without this, the splash * vars, which in turn influence our scripts. */
* plugin will probably hang when running in silent mode. */ if (pipe (pfd) == -1) {
for (i = 0; i < 2; i++) eerror ("pipe: %s", strerror (errno));
if ((flags = fcntl (pfd[i], F_GETFD, 0)) < 0 || return;
fcntl (pfd[i], F_SETFD, flags | FD_CLOEXEC) < 0) }
eerror ("fcntl: %s", strerror (errno));
/* We run the plugin in a new process so we never crash /* Stop any scripts from inheriting us.
* or otherwise affected by it */ * This is actually quite important as without this, the splash
if ((pid = fork ()) == -1) { * plugin will probably hang when running in silent mode. */
eerror ("fork: %s", strerror (errno)); for (i = 0; i < 2; i++)
return; if ((flags = fcntl (pfd[i], F_GETFD, 0)) < 0 ||
} fcntl (pfd[i], F_SETFD, flags | FD_CLOEXEC) < 0)
eerror ("fcntl: %s", strerror (errno));
if (pid == 0) { sigprocmask (SIG_SETMASK, &full, &old);
int retval;
rc_in_plugin = true; /* We run the plugin in a new process so we never crash
close (pfd[0]); * or otherwise affected by it */
rc_environ_fd = fdopen (pfd[1], "w"); if ((pid = fork ()) == -1) {
retval = plugin->hook (hook, value); eerror ("fork: %s", strerror (errno));
fclose (rc_environ_fd); break;
rc_environ_fd = NULL; }
/* Just in case the plugin sets this to false */ if (pid == 0) {
rc_in_plugin = true; int retval;
exit (retval);
} else {
char *buffer;
char *token;
char *p;
ssize_t nr;
close (pfd[1]); /* Restore default handlers */
buffer = xmalloc (sizeof (char) * BUFSIZ); sigaction (SIGCHLD, &sa, NULL);
memset (buffer, 0, BUFSIZ); sigaction (SIGHUP, &sa, NULL);
sigaction (SIGINT, &sa, NULL);
sigaction (SIGQUIT, &sa, NULL);
sigaction (SIGTERM, &sa, NULL);
sigaction (SIGUSR1, &sa, NULL);
sigaction (SIGWINCH, &sa, NULL);
sigprocmask (SIG_SETMASK, &old, NULL);
rc_in_plugin = true;
close (pfd[0]);
rc_environ_fd = fdopen (pfd[1], "w");
retval = plugin->hook (hook, value);
fclose (rc_environ_fd);
rc_environ_fd = NULL;
while ((nr = read (pfd[0], buffer, BUFSIZ)) > 0) { /* Just in case the plugin sets this to false */
p = buffer; rc_in_plugin = true;
while (*p && p - buffer < nr) { exit (retval);
token = strsep (&p, "="); }
if (token) {
unsetenv (token); sigprocmask (SIG_SETMASK, &old, NULL);
if (*p) { close (pfd[1]);
setenv (token, p, 1); buffer = xmalloc (sizeof (char) * BUFSIZ);
p += strlen (p) + 1; memset (buffer, 0, BUFSIZ);
} else
p++; while ((nr = read (pfd[0], buffer, BUFSIZ)) > 0) {
} p = buffer;
} while (*p && p - buffer < nr) {
token = strsep (&p, "=");
if (token) {
unsetenv (token);
if (*p) {
setenv (token, p, 1);
p += strlen (p) + 1;
} else
p++;
} }
free (buffer);
close (pfd[0]);
rc_waitpid (pid);
} }
} }
free (buffer);
close (pfd[0]);
rc_waitpid (pid);
plugin = plugin->next; plugin = plugin->next;
} }
} }

View File

@ -663,6 +663,7 @@ int main (int argc, char **argv)
int opt; int opt;
bool parallel; bool parallel;
int regen = 0; int regen = 0;
pid_t pid;
applet = basename_c (argv[0]); applet = basename_c (argv[0]);
atexit (cleanup); atexit (cleanup);
@ -994,7 +995,7 @@ 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) {
pid_t pid = rc_service_stop (service); pid = rc_service_stop (service);
if (pid > 0 && ! parallel) if (pid > 0 && ! parallel)
rc_waitpid (pid); rc_waitpid (pid);
continue; continue;
@ -1060,11 +1061,10 @@ int main (int argc, char **argv)
/* After all that we can finally stop the blighter! */ /* After all that we can finally stop the blighter! */
if (! found) { if (! found) {
pid_t pid; pid = rc_service_stop (service);
if ((pid = rc_service_stop (service)) > 0) { if (pid > 0) {
add_pid (pid); add_pid (pid);
if (! parallel) { if (! parallel) {
rc_waitpid (pid); rc_waitpid (pid);
remove_pid (pid); remove_pid (pid);
@ -1136,9 +1136,7 @@ int main (int argc, char **argv)
#endif #endif
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 ();
@ -1160,8 +1158,10 @@ interactive_option:
} }
} }
pid = rc_service_start (service);
/* Remember the pid if we're running in parallel */ /* Remember the pid if we're running in parallel */
if ((pid = rc_service_start (service)) > 0) { if (pid > 0) {
add_pid (pid); add_pid (pid);
if (! parallel) { if (! parallel) {

View File

@ -432,8 +432,6 @@ static bool svc_exec (const char *arg1, const char *arg2)
eerrorx ("%s: vfork: %s", service, strerror (errno)); eerrorx ("%s: vfork: %s", service, strerror (errno));
if (service_pid == 0) { if (service_pid == 0) {
if (slave_tty >= 0) { if (slave_tty >= 0) {
/* Hmmm, this shouldn't work in a vfork, but it does which is
* good for us */
close (master_tty); close (master_tty);
dup2 (slave_tty, 1); dup2 (slave_tty, 1);