tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: Library support for two-phase daemonization



On Friday, at 21:02, Andreas Gustafsson wrote:
| Missing a "break;" here?

Yes, sorry. I cut too much of unrelated code...

| >   if (handler == SIG_IGN)
| >     kill(getppid(), SIGUSR1);
| 
| I think this suffers from a similar race condition as Iain's code; 
| the SIGUSR1 may be delivered to the parent before it has called
| sigprocmask().

I agree, and I must admit I had overlooked this, so thanks for the feedback.

I choosed this approach so that the program can be used according two distinct
use cases:
 - classic standalone mode, start within a shell script: daemonize with a
   command line switch and return to the shell only once the program is ready.

 - start from another unrelated "launcher" program. The launcher must of course
   implement the 'SIGUSR1' logic. In this mode, the child does not fork itself,
   but it is still able to inform it's parent about it 'ready' state. (think
   for instance about inetd, although it's not inetd in my use case).

So the pipe() approach does not really fit in my case. Or at least I don't see
an easy way to communicate a file descriptor the child.

However, I think the race that you mention can be fixed by moving the
sigprocmask() and related code before the fork(), and restoring the former mask
in the child. What do you think of the following?

#include <assert.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int
main()
{
  void (*handler)(int);

  int daemonize = 1 /* or 0, e.g. via command line switch */;

  /* daemonize */
  if (daemonize) {
    sigset_t set, oset;

    sigemptyset(&set);
    sigaddset(&set, SIGUSR1);
    sigprocmask(SIG_BLOCK, &set, &oset);
    switch(fork()) {
      case -1:
        perror("cannot fork");
        exit(2);

      case 0: /* child */ {
        sigprocmask(SIG_SETMASK, &oset, NULL);
        signal(SIGUSR1, SIG_IGN);
        break;
      }

      default: /* parent */ {
        int sig = 0;

        do { sigwait(&set, &sig); } while (sig == 0);
        assert(sig == SIGUSR1);
        _exit(0);
      }
    }
  }

  /* signal parent */
  printf("ready\n");
  handler = signal(SIGUSR1, SIG_IGN);
  if (handler == SIG_IGN)
    kill(getppid(), SIGUSR1);
  signal(SIGUSR1, handler);

  /* do some work */
  sleep(1);
  printf("done\n");
  return 0;
}


Home | Main Index | Thread Index | Old Index