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,17 +143,36 @@ 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;
if (! plugin->hook) {
plugin = plugin->next;
continue;
}
/* We create a pipe so that plugins can affect our environment /* We create a pipe so that plugins can affect our environment
* vars, which in turn influence our scripts. */ * vars, which in turn influence our scripts. */
@ -170,16 +189,28 @@ void rc_plugin_run (rc_hook_t hook, const char *value)
fcntl (pfd[i], F_SETFD, flags | FD_CLOEXEC) < 0) fcntl (pfd[i], F_SETFD, flags | FD_CLOEXEC) < 0)
eerror ("fcntl: %s", strerror (errno)); eerror ("fcntl: %s", strerror (errno));
sigprocmask (SIG_SETMASK, &full, &old);
/* We run the plugin in a new process so we never crash /* We run the plugin in a new process so we never crash
* or otherwise affected by it */ * or otherwise affected by it */
if ((pid = fork ()) == -1) { if ((pid = fork ()) == -1) {
eerror ("fork: %s", strerror (errno)); eerror ("fork: %s", strerror (errno));
return; break;
} }
if (pid == 0) { if (pid == 0) {
int retval; int retval;
/* 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);
sigprocmask (SIG_SETMASK, &old, NULL);
rc_in_plugin = true; rc_in_plugin = true;
close (pfd[0]); close (pfd[0]);
rc_environ_fd = fdopen (pfd[1], "w"); rc_environ_fd = fdopen (pfd[1], "w");
@ -190,12 +221,9 @@ void rc_plugin_run (rc_hook_t hook, const char *value)
/* Just in case the plugin sets this to false */ /* Just in case the plugin sets this to false */
rc_in_plugin = true; rc_in_plugin = true;
exit (retval); exit (retval);
} else { }
char *buffer;
char *token;
char *p;
ssize_t nr;
sigprocmask (SIG_SETMASK, &old, NULL);
close (pfd[1]); close (pfd[1]);
buffer = xmalloc (sizeof (char) * BUFSIZ); buffer = xmalloc (sizeof (char) * BUFSIZ);
memset (buffer, 0, BUFSIZ); memset (buffer, 0, BUFSIZ);
@ -219,8 +247,6 @@ void rc_plugin_run (rc_hook_t hook, const char *value)
close (pfd[0]); close (pfd[0]);
rc_waitpid (pid); 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);
@ -1137,8 +1137,6 @@ 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 ();
@ -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);