summaryrefslogtreecommitdiff
path: root/utils/start-stop-daemon.c
diff options
context:
space:
mode:
authorGuillem Jover <guillem@debian.org>2014-09-09 15:14:51 +0200
committerGuillem Jover <guillem@debian.org>2014-10-06 00:40:44 +0200
commit29778da537e2ff1a0f032db33dde43413b7345ef (patch)
treefdab6747ebc0aa6dc7177bffe0fe4bcd79f3cc5c /utils/start-stop-daemon.c
parentf27abd5a083c07bd367423f67cedf94b3d33b3f8 (diff)
downloaddpkg-29778da537e2ff1a0f032db33dde43413b7345ef.tar.gz
s-s-d: Do not exit from first parent before the pidfile has been created
When using the --background option combined with --make-pidfile, the parent process might end up exiting before the child's pidfile has been created, which might confuse service supervising programs. Fix the race condition by making the first parent wait for the second one, so that it can safely create the pidfile if required. Closes: #686420 Based-on-patch-by: Nir Soffer <nirs@hyperms.com>
Diffstat (limited to 'utils/start-stop-daemon.c')
-rw-r--r--utils/start-stop-daemon.c58
1 files changed, 53 insertions, 5 deletions
diff --git a/utils/start-stop-daemon.c b/utils/start-stop-daemon.c
index e4c47629d..7fd8d8f87 100644
--- a/utils/start-stop-daemon.c
+++ b/utils/start-stop-daemon.c
@@ -66,6 +66,7 @@
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
+#include <sys/wait.h>
#include <sys/ioctl.h>
#include <assert.h>
@@ -362,6 +363,33 @@ setsid(void)
#endif
static void
+wait_for_child(pid_t pid)
+{
+ pid_t child;
+ int status;
+
+ do {
+ child = waitpid(pid, &status, 0);
+ } while (child == -1 && errno == EINTR);
+
+ if (child != pid)
+ fatal("error waiting for child");
+
+ if (WIFEXITED(status)) {
+ int err = WEXITSTATUS(status);
+
+ if (err != 0)
+ fatal("child returned error exit status %d", err);
+ } else if (WIFSIGNALED(status)) {
+ int signo = WTERMSIG(status);
+
+ fatal("child was killed by signal %d", signo);
+ } else {
+ fatal("unexpected status %d waiting for child", status);
+ }
+}
+
+static void
write_pidfile(const char *filename, pid_t pid)
{
FILE *fp;
@@ -386,15 +414,30 @@ static void
daemonize(void)
{
pid_t pid;
+ sigset_t mask;
+ sigset_t oldmask;
if (quietmode < 0)
printf("Detaching to start %s...", startas);
+ /* Block SIGCHLD to allow waiting for the child process while it is
+ * performing actions, such as creating a pidfile. */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
+ if (sigprocmask(SIG_BLOCK, &mask, &oldmask) == -1)
+ fatal("cannot block SIGCHLD");
+
pid = fork();
if (pid < 0)
fatal("unable to do first fork");
- else if (pid) /* Parent. */
+ else if (pid) { /* First Parent. */
+ /* Wait for the second parent to exit, so that if we need to
+ * perform any actions there, like creating a pidfile, we do
+ * not suffer from race conditions on return. */
+ wait_for_child(pid);
+
_exit(0);
+ }
/* Create a new session. */
if (setsid() < 0)
@@ -403,8 +446,16 @@ daemonize(void)
pid = fork();
if (pid < 0)
fatal("unable to do second fork");
- else if (pid) /* Parent. */
+ else if (pid) { /* Second parent. */
+ if (mpidfile && pidfile != NULL)
+ /* User wants _us_ to make the pidfile. */
+ write_pidfile(pidfile, pid);
+
_exit(0);
+ }
+
+ if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1)
+ fatal("cannot restore signal mask");
if (quietmode < 0)
printf("done.\n");
@@ -1785,9 +1836,6 @@ do_start(int argc, char **argv)
set_io_schedule(io_sched);
if (umask_value >= 0)
umask(umask_value);
- if (mpidfile && pidfile != NULL)
- /* User wants _us_ to make the pidfile. */
- write_pidfile(pidfile, getpid());
if (changeroot != NULL) {
if (chdir(changeroot) < 0)
fatal("unable to chdir() to %s", changeroot);