]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Replace poll(2) with pselect(2) and vfork(2)+execve(2) with
authorRoy Marples <roy@marples.name>
Sat, 10 Nov 2012 16:38:52 +0000 (16:38 +0000)
committerRoy Marples <roy@marples.name>
Sat, 10 Nov 2012 16:38:52 +0000 (16:38 +0000)
posix_spawn(3).
Now we block all our signals at startup and allow pselect to
unblock them for the duration of the call. This allows us to
manage interrupts in a fashion to guarantee a consistent
internal state.

I have added a posix_spawn compat shim for systems that lack
that call. pselect(2) has been supported on target for some time
so there is no need for a compat shim there.

bind.c
compat/posix_spawn.c [new file with mode: 0644]
compat/posix_spawn.h [new file with mode: 0644]
configure
configure.c
dhcpcd.c
dhcpcd.h
eloop.c
eloop.h
signals.c
signals.h

diff --git a/bind.c b/bind.c
index 3a00465a8ce376283b72ca76b0eb1f830eba7645..bf267e66f63b0edd85901feae9056e73bdf34988 100644 (file)
--- a/bind.c
+++ b/bind.c
@@ -59,16 +59,12 @@ pid_t
 daemonise(void)
 {
        pid_t pid;
-       sigset_t full;
-       sigset_t old;
        char buf = '\0';
        int sidpipe[2], fd;
 
        delete_timeout(handle_exit_timeout, NULL);
        if (options & DHCPCD_DAEMONISED || !(options & DHCPCD_DAEMONISE))
                return 0;
-       sigfillset(&full);
-       sigprocmask(SIG_SETMASK, &full, &old);
        /* Setup a signal pipe so parent knows when to exit. */
        if (pipe(sidpipe) == -1) {
                syslog(LOG_ERR, "pipe: %m");
@@ -96,7 +92,6 @@ daemonise(void)
                }
                break;
        default:
-               signal_reset();
                /* Wait for child to detach */
                close(sidpipe[1]);
                if (read(sidpipe[0], &buf, 1) == -1)
@@ -114,7 +109,6 @@ daemonise(void)
                exit(EXIT_SUCCESS);
        }
        options |= DHCPCD_DAEMONISED;
-       sigprocmask(SIG_SETMASK, &old, NULL);
        return pid;
 }
 #endif
diff --git a/compat/posix_spawn.c b/compat/posix_spawn.c
new file mode 100644 (file)
index 0000000..eb0685f
--- /dev/null
@@ -0,0 +1,135 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* This implementation of posix_spawn is only suitable for the needs of dhcpcd
+ * but it could easily be extended to other applications.
+ * Also, it does rely on the system being able to modify signals safely within
+ * the vfork process which is undefined behaviour, but seems sane in testing. */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common.h"
+#include "posix_spawn.h"
+
+extern char **environ;
+
+static int
+posix_spawnattr_handle(const posix_spawnattr_t *attrp)
+{
+       struct sigaction sa;
+       int i;
+
+       if (attrp->posix_attr_flags & POSIX_SPAWN_SETSIGMASK)
+               sigprocmask(SIG_SETMASK, &attrp->posix_attr_sigmask, NULL);
+
+       if (attrp->posix_attr_flags & POSIX_SPAWN_SETSIGDEF) {
+               sa.sa_flags = 0;
+               sa.sa_handler = SIG_DFL;
+               for (i = 1; i <= _SIG_MAXSIG; i++) {
+                       if (sigismember(&attrp->posix_attr_sigdefault, i)) {
+                               if (sigaction(i, &sa, NULL) == -1)
+                                       return -1;
+                       }
+               }
+       }
+
+       return 0;
+}
+    
+int
+posix_spawn(pid_t *pid, const char * path,
+       _unused void *arg,
+       const posix_spawnattr_t *attrp,
+       char *const argv[], char *const envp[])
+{
+       pid_t p;
+       volatile int error;
+
+       error = 0;
+       p = vfork();
+       switch (p) {
+       case -1:
+               return errno;
+       case 0:
+               if (attrp) {
+                       error = posix_spawnattr_handle(attrp);
+                       if (error)
+                               _exit(127);
+               }
+               execve(path, argv, envp);
+               error = errno;
+               _exit(127);
+       default:
+               if (error != 0)
+                       waitpid(p, NULL, WNOHANG);
+               else if (pid != NULL)
+                       *pid = p;
+               return error;
+       }
+}
+
+int
+posix_spawnattr_init(posix_spawnattr_t *attr)
+{
+
+       memset(attr, 0, sizeof(*attr));
+       attr->posix_attr_flags = 0;
+       sigprocmask(0, NULL, &attr->posix_attr_sigmask);
+       sigemptyset(&attr->posix_attr_sigdefault);
+       return 0;
+}
+
+int
+posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags)
+{
+
+       attr->posix_attr_flags = flags;
+       return 0;
+}
+
+int
+posix_spawnattr_setsigmask(posix_spawnattr_t *attr, const sigset_t *sigmask)
+{
+
+       attr->posix_attr_sigmask = *sigmask;
+       return 0;
+}
+
+int
+posix_spawnattr_setsigdefault(posix_spawnattr_t *attr, const sigset_t *sigmask)
+{
+
+       attr->posix_attr_sigdefault = *sigmask;
+       return 0;
+}
diff --git a/compat/posix_spawn.h b/compat/posix_spawn.h
new file mode 100644 (file)
index 0000000..653f00a
--- /dev/null
@@ -0,0 +1,46 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef POSIX_SPAWN_H
+#define POSIX_SPAWN_H
+
+typedef struct {
+       short posix_attr_flags;
+#define POSIX_SPAWN_SETSIGDEF          0x10
+#define POSIX_SPAWN_SETSIGMASK         0x20
+       sigset_t posix_attr_sigmask;
+       sigset_t posix_attr_sigdefault;
+} posix_spawnattr_t;
+
+int posix_spawn(pid_t *, const char *, void *, const posix_spawnattr_t *,
+       char *const [], char *const []);
+int posix_spawnattr_init(posix_spawnattr_t *);
+int posix_spawnattr_setflags(posix_spawnattr_t *, short);
+int posix_spawnattr_setsigmask(posix_spawnattr_t *, const sigset_t *);
+int posix_spawnattr_setsigdefault(posix_spawnattr_t *, const sigset_t *);
+
+#endif
index bca96cfc9832dbc9b32bd9c22172b64dc884499b..233f23c18349d6142d6a997a325232139d234caf 100755 (executable)
--- a/configure
+++ b/configure
@@ -412,6 +412,31 @@ if [ "$TAILQ_FOREACH_SAFE" = no ]; then
 EOF
 fi
 
+if [ -z "$POSIX_SPAWN" ]; then
+       printf "Testing for posix_spawn ... "
+       cat <<EOF >_posix_spawn.c
+#include <spawn.h>
+#include <stdlib.h>
+int main(void) {
+       posix_spawn(NULL, NULL, NULL, NULL, NULL, NULL);
+       return 0;
+}
+EOF
+       if $XCC _posix_spawn.c -o _posix_spawn 2>/dev/null; then
+               POSIX_SPAWN=yes
+       else
+               POSIX_SPAWN=no
+       fi
+       echo "$POSIX_SPAWN"
+       rm -f _posix_spawn.c _posix_spawn
+fi
+if [ "$POSIX_SPAWN" = no ]; then
+       echo "COMPAT_SRCS+=     compat/posix_spawn.c" >>$CONFIG_MK
+       echo "#include \"compat/posix_spawn.h\"" >>$CONFIG_H
+else
+       echo "#include <spawn.h>" >>$CONFIG_H
+fi
+
 if [ -z "$SERVICECMD" ]; then
        printf "Checking for OpenRC ... "
        if [ -x /sbin/rc-service ]; then
index f18e87bbb42fd76db42216f88b423167717f25cd..bbceaaae3773d076f5f73fdad80569f9667da255 100644 (file)
@@ -35,6 +35,8 @@
 #include <ctype.h>
 #include <errno.h>
 #include <signal.h>
+/* We can't include spawn.h here because it may not exist.
+ * config.h will pull it in, or our compat one. */
 #include <stdlib.h>
 #include <string.h>
 #include <syslog.h>
@@ -81,29 +83,25 @@ static int
 exec_script(char *const *argv, char *const *env)
 {
        pid_t pid;
-       sigset_t full;
-       sigset_t old;
-
-       /* OK, we need to block signals */
-       sigfillset(&full);
-       sigprocmask(SIG_SETMASK, &full, &old);
-       signal_reset();
-
-       switch (pid = vfork()) {
-       case -1:
-               syslog(LOG_ERR, "vfork: %m");
-               break;
-       case 0:
-               sigprocmask(SIG_SETMASK, &old, NULL);
-               execve(argv[0], argv, env);
-               syslog(LOG_ERR, "%s: %m", argv[0]);
-               _exit(127);
-               /* NOTREACHED */
-       }
+       posix_spawnattr_t attr;
+       sigset_t defsigs;
+       int i;
 
-       /* Restore our signals */
-       signal_setup();
-       sigprocmask(SIG_SETMASK, &old, NULL);
+       /* posix_spawn is a safe way of executing another image
+        * and changing signals back to how they should be. */
+       if (posix_spawnattr_init(&attr) == -1)
+               return -1;
+       posix_spawnattr_setflags(&attr,
+           POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF);
+       sigemptyset(&defsigs);
+       for (i = 0; i < handle_sigs[i]; i++)
+               sigaddset(&defsigs, handle_sigs[i]);
+       posix_spawnattr_setsigdefault(&attr, &defsigs);
+       posix_spawnattr_setsigmask(&attr, &dhcpcd_sigset);
+       errno = 0;
+       i = posix_spawn(&pid, argv[0], NULL, &attr, argv, env);
+       if (i)
+               return -1;
        return pid;
 }
 
@@ -423,9 +421,10 @@ run_script_reason(const struct interface *iface, const char *reason)
        env[++elen] = '\0';
 
        pid = exec_script(argv, env);
-       if (pid == -1)
+       if (pid == -1) {
+               syslog(LOG_ERR, "exec_script: %m");
                status = -1;
-       else if (pid != 0) {
+       else if (pid != 0) {
                /* Wait for the script to finish */
                while (waitpid(pid, &status, 0) == -1) {
                        if (errno != EINTR) {
index a75545c5821a421d125eb90444108dae1caa77c9..7bee966b5c2f3e66ca69dc8c8973ba98aef472f8 100644 (file)
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -92,15 +92,17 @@ char **ifav = NULL;
 int ifdc = 0;
 char **ifdv = NULL;
 
-static char **margv;
-static int margc;
-static struct if_options *if_options;
-static char **ifv;
-static int ifc;
+sigset_t dhcpcd_sigset;
+
 static char *cffile;
+static struct if_options *if_options;
 static char *pidfile;
 static int linkfd = -1, ipv6rsfd = -1, ipv6nsfd = -1;
 static uint8_t *packet;
+static char **ifv;
+static int ifc;
+static char **margv;
+static int margc;
 
 struct dhcp_op {
        uint8_t value;
@@ -1538,13 +1540,11 @@ reconf_reboot(int action, int argc, char **argv, int oi)
        sort_interfaces();
 }
 
-/* ARGSUSED */
-static void
-handle_signal(_unused void *arg)
+void
+handle_signal(int sig)
 {
        struct interface *ifp;
        struct if_options *ifo;
-       int sig = signal_read();
        int do_release, i;
 
        do_release = 0;
@@ -1809,7 +1809,7 @@ main(int argc, char **argv)
 {
        struct interface *iface;
        uint16_t family = 0;
-       int opt, oi = 0, signal_fd, sig = 0, i, control_fd;
+       int opt, oi = 0, sig = 0, i, control_fd;
        size_t len;
        pid_t pid;
        struct timespec ts;
@@ -2059,11 +2059,15 @@ main(int argc, char **argv)
        eloop_init();
 #endif
 
-       if ((signal_fd = signal_init()) == -1)
+       /* This blocks all signals we're interested in.
+        * eloop uses pselect(2) so that the signals are unblocked
+        * when we're testing fd's.
+        * This allows us to ensure a consistent state is maintained
+        * regardless of when we are interrupted .*/
+       if (signal_setup(handle_signal, &dhcpcd_sigset) == -1) {
+               syslog(LOG_ERR, "signal_setup: %m");
                exit(EXIT_FAILURE);
-       if (signal_setup() == -1)
-               exit(EXIT_FAILURE);
-       add_event(signal_fd, handle_signal, NULL);
+       }
 
        if (options & DHCPCD_MASTER) {
                if (start_control() == -1)
@@ -2196,6 +2200,6 @@ main(int argc, char **argv)
        for (iface = ifaces; iface; iface = iface->next)
                add_timeout_sec(0, start_interface, iface);
 
-       start_eloop();
+       start_eloop(&dhcpcd_sigset);
        exit(EXIT_SUCCESS);
 }
index a04b7a8186332c90a78a19c75f18697cef045da2..bfcba8c4769cee2538c226ae19979980d36e1397 100644 (file)
--- a/dhcpcd.h
+++ b/dhcpcd.h
@@ -120,6 +120,7 @@ struct interface {
 };
 
 extern char vendor[VENDORCLASSID_MAX_LEN];
+extern sigset_t dhcpcd_sigset;
 extern int pidfd;
 extern int ifac;
 extern char **ifav;
@@ -135,6 +136,7 @@ void handle_hwaddr(const char *, unsigned char *, size_t);
 void handle_ifa(int, const char *,
     struct in_addr *, struct in_addr *, struct in_addr *);
 void handle_exit_timeout(void *);
+void handle_signal(int);
 void start_interface(void *);
 void start_discover(void *);
 void start_request(void *);
diff --git a/eloop.c b/eloop.c
index 51fe0ac4e5a1a4d6c53fb8fb35be6394bb44b8f3..457e0d7e4506ed9948b97b106d728f3455f30d0f 100644 (file)
--- a/eloop.c
+++ b/eloop.c
@@ -1,6 +1,6 @@
 /* 
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
 #include <errno.h>
 #include <limits.h>
 #include <poll.h>
+#include <signal.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <syslog.h>
 
 #include "common.h"
+#include "dhcpcd.h"
 #include "eloop.h"
 
 static struct timeval now;
@@ -56,9 +58,6 @@ static struct timeout {
 } *timeouts;
 static struct timeout *free_timeouts;
 
-static struct pollfd *fds;
-static size_t fds_len;
-
 void
 add_event(int fd, void (*callback)(void *), void *arg)
 {
@@ -274,7 +273,6 @@ cleanup(void)
                free(free_timeouts);
                free_timeouts = t;
        }
-       free(fds);
 }
 
 void
@@ -286,18 +284,20 @@ eloop_init(void)
 #endif
 
 _noreturn void
-start_eloop(void)
+start_eloop(const sigset_t *cursigs)
 {
-       int msecs, n;
-       nfds_t nfds, i;
+       int n, max_fd;
+       fd_set read_fds, error_fds;
        struct event *e;
        struct timeout *t;
        struct timeval tv;
+       struct timespec ts;
+       const struct timespec *tsp;
 
        for (;;) {
-               /* Run all timeouts first.
-                * When we have one that has not yet occured,
-                * calculate milliseconds until it does for use in poll. */
+               get_monotonic(&now);
+
+               /* Run all timeouts first */
                if (timeouts) {
                        if (timercmp(&now, &timeouts->when, >)) {
                                t = timeouts;
@@ -308,61 +308,40 @@ start_eloop(void)
                                continue;
                        }
                        timersub(&timeouts->when, &now, &tv);
-                       if (tv.tv_sec > INT_MAX / 1000 ||
-                           (tv.tv_sec == INT_MAX / 1000 &&
-                               (tv.tv_usec + 999) / 1000 > INT_MAX % 1000))
-                               msecs = INT_MAX;
-                       else
-                               msecs = tv.tv_sec * 1000 +
-                                   (tv.tv_usec + 999) / 1000;
+                       ts.tv_sec = tv.tv_sec;
+                       ts.tv_nsec = tv.tv_usec * 1000;
+                       tsp = &ts;
                } else
-                       /* No timeouts, so wait forever. */
-                       msecs = -1;
-
-               /* Allocate memory for our pollfds as and when needed.
-                * We don't bother shrinking it. */
-               nfds = 0;
-               for (e = events; e; e = e->next)
-                       nfds++;
-               if (msecs == -1 && nfds == 0) {
+                       /* No timeouts, so wait forever */
+                       tsp = NULL;
+
+               max_fd = -1;
+               FD_ZERO(&read_fds);
+               FD_ZERO(&error_fds);
+               for (e = events; e; e = e->next) {
+                       FD_SET(e->fd, &read_fds);
+                       if (e->fd > max_fd)
+                               max_fd = e->fd;
+               }
+               if (tsp == NULL && max_fd == -1) {
                        syslog(LOG_ERR, "nothing to do");
                        exit(EXIT_FAILURE);
                }
-               if (nfds > fds_len) {
-                       free(fds);
-                       /* Allocate 5 more than we need for future use */
-                       fds_len = nfds + 5;
-                       fds = xmalloc(sizeof(*fds) * fds_len);
-               }
-               nfds = 0;
-               for (e = events; e; e = e->next) {
-                       fds[nfds].fd = e->fd;
-                       fds[nfds].events = POLLIN;
-                       fds[nfds].revents = 0;
-                       nfds++;
-               }
-               n = poll(fds, nfds, msecs);
+
+               n = pselect(max_fd + 1, &read_fds, NULL, &error_fds,
+                   tsp, cursigs);
                if (n == -1) {
-                       if (errno == EAGAIN || errno == EINTR) {
-                               get_monotonic(&now);
+                       if (errno == EINTR)
                                continue;
-                       }
-                       syslog(LOG_ERR, "poll: %m");
+                       syslog(LOG_ERR, "pselect: %m");
                        exit(EXIT_FAILURE);
                }
-
-               /* Get the now time and process any triggered events. */
-               get_monotonic(&now);
-               if (n == 0)
-                       continue;
-               for (i = 0; i < nfds; i++) {
-                       if (!(fds[i].revents & (POLLIN | POLLHUP)))
-                               continue;
+               
+               /* Process any triggered events. */
+               if (n) {
                        for (e = events; e; e = e->next) {
-                               if (e->fd == fds[i].fd) {
+                               if (FD_ISSET(e->fd, &read_fds))
                                        e->callback(e->arg);
-                                       break;
-                               }
                        }
                }
        }
diff --git a/eloop.h b/eloop.h
index 23c3626c0fd9b9b5796009862eff15a196743733..df06dce654d337cc9cad5c48c3ccb8175f3672f3 100644 (file)
--- a/eloop.h
+++ b/eloop.h
@@ -1,6 +1,6 @@
 /* 
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
@@ -28,6 +28,7 @@
 #ifndef ELOOP_H
 #define ELOOP_H
 
+#include <signal.h>
 #include <time.h>
 
 #ifndef ELOOP_QUEUE
@@ -47,6 +48,6 @@ void add_q_timeout_tv(int queue, const struct timeval *, void (*)(void *),
 void delete_q_timeout(int, void (*)(void *), void *);
 void delete_q_timeouts(int, void *, void (*)(void *), ...);
 void eloop_init(void);
-void start_eloop(void);
+void start_eloop(const sigset_t *);
 
 #endif
index fd3b0c3c52cd134235eb5731ed95a5c1ff24a891..e1ce69cb0c20741a741d9dc07bd47090ecf2dcad 100644 (file)
--- a/signals.c
+++ b/signals.c
@@ -1,6 +1,6 @@
 /* 
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2009 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
@@ -25,9 +25,6 @@
  * SUCH DAMAGE.
  */
 
-#include <sys/types.h>
-#include <sys/socket.h>
-
 #include <errno.h>
 #include <signal.h>
 #include <string.h>
 #include "common.h"
 #include "signals.h"
 
-static int signal_pipe[2];
-
-static const int handle_sigs[] = {
+const int handle_sigs[] = {
        SIGALRM,
        SIGHUP,
        SIGINT,
        SIGPIPE,
        SIGTERM,
        SIGUSR1,
+       0
 };
 
-static void
-signal_handler(int sig)
-{
-       int serrno = errno;
-
-       if (write(signal_pipe[1], &sig, sizeof(sig)) != sizeof(sig))
-               syslog(LOG_ERR, "failed to write signal %d: %m", sig);
-       /* Restore errno */
-       errno = serrno;
-}
-
-/* Read a signal from the signal pipe. Returns 0 if there is
- * no signal, -1 on error (and sets errno appropriately), and
- * your signal on success */
-int
-signal_read(void)
-{
-       int sig = -1;
-       char buf[16];
-       ssize_t bytes;
-
-       memset(buf, 0, sizeof(buf));
-       bytes = read(signal_pipe[0], buf, sizeof(buf));
-       if (bytes >= 0 && (size_t)bytes >= sizeof(sig))
-               memcpy(&sig, buf, sizeof(sig));
-       return sig;
-}
-
-/* Call this before doing anything else. Sets up the socket pair
- * and installs the signal handler */
-int
-signal_init(void)
-{
-       if (pipe(signal_pipe) == -1)
-               return -1;
-       /* Don't block on read */
-       if (set_nonblock(signal_pipe[0]) == -1)
-               return -1;
-       /* Stop any scripts from inheriting us */
-       if (set_cloexec(signal_pipe[0]) == -1)
-               return -1;
-       if (set_cloexec(signal_pipe[1]) == -1)
-               return -1;
-       return signal_pipe[0];
-}
-
 static int
-signal_handle(void (*func)(int))
+signal_handle(void (*func)(int), sigset_t *oldset)
 {
        unsigned int i;
        struct sigaction sa;
+       sigset_t newset;
 
        memset(&sa, 0, sizeof(sa));
        sa.sa_handler = func;
        sigemptyset(&sa.sa_mask);
 
-       for (i = 0; i < sizeof(handle_sigs) / sizeof(handle_sigs[0]); i++)
+       for (i = 0; handle_sigs[i]; i++) {
                if (sigaction(handle_sigs[i], &sa, NULL) == -1)
                        return -1;
+               if (oldset)
+                       sigaddset(&newset, handle_sigs[i]);
+       }
+       if (oldset)
+               return sigprocmask(SIG_BLOCK, &newset, oldset);
        return 0;
 }
 
 int
-signal_setup(void)
+signal_setup(void (*func)(int), sigset_t *oldset)
 {
-       return signal_handle(signal_handler);
+
+       return signal_handle(func, oldset);
 }
 
 int
 signal_reset(void)
 {
-       return signal_handle(SIG_DFL);
-}
 
+       return signal_handle(SIG_DFL, NULL);
+}
index 7098cfb1bdc52998547a8e2c2d5ec97a9550e0c2..bd141329adcaa5530a28972c5178b9c4d3bc2168 100644 (file)
--- a/signals.h
+++ b/signals.h
@@ -1,6 +1,6 @@
 /* 
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
  * SUCH DAMAGE.
  */
 
-#ifndef SIGNAL_H
-#define SIGNAL_H
+#ifndef SIGNALS_H
+#define SIGNALS_H
+
+extern const int handle_sigs[];
 
 int signal_init(void);
-int signal_setup(void);
+int signal_setup(void (*)(int), sigset_t *);
 int signal_reset(void);
 int signal_read(void);
+int signal_block(int);
 
 #endif