Blame nslcd/daemonize.c

Packit 6bd9ab
/*
Packit 6bd9ab
   daemoninze.c - functions for properly daemonising an application
Packit 6bd9ab
Packit 6bd9ab
   Copyright (C) 2014-2015 Arthur de Jong
Packit 6bd9ab
Packit 6bd9ab
   This library is free software; you can redistribute it and/or
Packit 6bd9ab
   modify it under the terms of the GNU Lesser General Public
Packit 6bd9ab
   License as published by the Free Software Foundation; either
Packit 6bd9ab
   version 2.1 of the License, or (at your option) any later version.
Packit 6bd9ab
Packit 6bd9ab
   This library is distributed in the hope that it will be useful,
Packit 6bd9ab
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6bd9ab
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6bd9ab
   Lesser General Public License for more details.
Packit 6bd9ab
Packit 6bd9ab
   You should have received a copy of the GNU Lesser General Public
Packit 6bd9ab
   License along with this library; if not, write to the Free Software
Packit 6bd9ab
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit 6bd9ab
   02110-1301 USA
Packit 6bd9ab
*/
Packit 6bd9ab
Packit 6bd9ab
#include "config.h"
Packit 6bd9ab
Packit 6bd9ab
#include <unistd.h>
Packit 6bd9ab
#include <sys/types.h>
Packit 6bd9ab
#include <sys/stat.h>
Packit 6bd9ab
#include <fcntl.h>
Packit 6bd9ab
#include <string.h>
Packit 6bd9ab
#include <errno.h>
Packit 6bd9ab
#include <stdio.h>
Packit 6bd9ab
#include <stdlib.h>
Packit 6bd9ab
#ifdef HAVE_PTHREAD_H
Packit 6bd9ab
#include <pthread.h>
Packit 6bd9ab
#endif /* HAVE_PTHREAD_H */
Packit 6bd9ab
Packit 6bd9ab
#include "daemonize.h"
Packit 6bd9ab
#include "log.h"
Packit 6bd9ab
Packit 6bd9ab
/* the write end of a pipe that is used to signal the fact that the child
Packit 6bd9ab
   process has finished initialising (see daemonize_daemon() and
Packit 6bd9ab
   daemonize_ready() for details) */
Packit 6bd9ab
static int daemonizefd = -1;
Packit 6bd9ab
Packit 6bd9ab
void daemonize_closefds(void)
Packit 6bd9ab
{
Packit 6bd9ab
  int i;
Packit 6bd9ab
  /* close all file descriptors (except stdin/out/err) */
Packit 6bd9ab
  i = sysconf(_SC_OPEN_MAX) - 1;
Packit 6bd9ab
  /* if the system does not have OPEN_MAX just close the first 32 and
Packit 6bd9ab
     hope we closed enough */
Packit 6bd9ab
  if (i < 0)
Packit 6bd9ab
    i = 32;
Packit 6bd9ab
  for (; i > 3; i--)
Packit 6bd9ab
    close(i);
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
void daemonize_redirect_stdio(void)
Packit 6bd9ab
{
Packit 6bd9ab
  /* close stdin, stdout and stderr */
Packit 6bd9ab
  (void)close(0);   /* stdin */
Packit 6bd9ab
  (void)close(1);   /* stdout */
Packit 6bd9ab
  (void)close(2);   /* stderr */
Packit 6bd9ab
  /* reconnect to /dev/null */
Packit 6bd9ab
  (void)open("/dev/null", O_RDWR);  /* stdin, fd=0 */
Packit 6bd9ab
  (void)dup(0);     /* stdout, fd=1 */
Packit 6bd9ab
  (void)dup(0);     /* stderr, fd=2 */
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* try to fill the buffer until EOF or error */
Packit 6bd9ab
static int read_response(int fd, char *buffer, size_t bufsz)
Packit 6bd9ab
{
Packit 6bd9ab
  int rc;
Packit 6bd9ab
  size_t r = 0;
Packit 6bd9ab
  while (r < bufsz)
Packit 6bd9ab
  {
Packit 6bd9ab
    rc = read(fd, buffer + r, bufsz - r);
Packit 6bd9ab
    if (rc == 0)
Packit 6bd9ab
      break;
Packit 6bd9ab
    else if (rc > 0)
Packit 6bd9ab
      r += rc;
Packit 6bd9ab
    else if ((errno == EINTR) || (errno == EAGAIN))
Packit 6bd9ab
      continue; /* ignore these errors and try again */
Packit 6bd9ab
    else
Packit 6bd9ab
    {
Packit 6bd9ab
      log_log(LOG_ERR, "read_response(): read() failed: %s", strerror(errno));
Packit 6bd9ab
      return -1;
Packit 6bd9ab
    }
Packit 6bd9ab
  }
Packit 6bd9ab
  return r;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* The parent process calling daemonize_daemon() will end up here on success */
Packit 6bd9ab
static int wait_for_response(int fd)
Packit 6bd9ab
{
Packit 6bd9ab
  int i, l, rc;
Packit 6bd9ab
  char buffer[1024];
Packit 6bd9ab
  /* read return code */
Packit 6bd9ab
  errno = 0;
Packit 6bd9ab
  i = read_response(fd, (void *)&rc, sizeof(int));
Packit 6bd9ab
  log_log(LOG_DEBUG, "DEBUG: wait_for_response(): i=%d, rc=%d", i, rc);
Packit 6bd9ab
  if (i != sizeof(int))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "wait_for_response(): read_response() returned %d (expected %d)",
Packit 6bd9ab
            i, (int)sizeof(int));
Packit 6bd9ab
    if (errno == 0)
Packit 6bd9ab
#ifdef ENODATA
Packit 6bd9ab
      errno = ENODATA;
Packit 6bd9ab
#else
Packit 6bd9ab
      errno = ENOATTR;
Packit 6bd9ab
#endif
Packit 6bd9ab
    return -1;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* read string length */
Packit 6bd9ab
  i = read_response(fd, (void *)&l, sizeof(int));
Packit 6bd9ab
  log_log(LOG_DEBUG, "DEBUG: wait_for_response(): i=%d, l=%d", i, l);
Packit 6bd9ab
  if ((i != sizeof(int)) || (l <= 0))
Packit 6bd9ab
    _exit(rc);
Packit 6bd9ab
  /* read string */
Packit 6bd9ab
  if ((size_t)l > (sizeof(buffer) - 1))
Packit 6bd9ab
    l = sizeof(buffer) - 1;
Packit 6bd9ab
  i = read_response(fd, buffer, l);
Packit 6bd9ab
  buffer[l] = '\0';
Packit 6bd9ab
  if (i == l)
Packit 6bd9ab
    fprintf(stderr, "%s", buffer);
Packit 6bd9ab
  _exit(rc);
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
static void closefd(void)
Packit 6bd9ab
{
Packit 6bd9ab
  if (daemonizefd >= 0)
Packit 6bd9ab
  {
Packit 6bd9ab
    close(daemonizefd);
Packit 6bd9ab
    daemonizefd = -1;
Packit 6bd9ab
  }
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
int daemonize_daemon(void)
Packit 6bd9ab
{
Packit 6bd9ab
  int pipefds[2];
Packit 6bd9ab
  int i;
Packit 6bd9ab
  /* set up a pipe for communication */
Packit 6bd9ab
  if (pipe(pipefds) < 0)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "pipe() failed: %s", strerror(errno));
Packit 6bd9ab
    return -1;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* set O_NONBLOCK on the write end to ensure that a call to
Packit 6bd9ab
     daemonize_ready() will not lock the application */
Packit 6bd9ab
  if ((i = fcntl(pipefds[1], F_GETFL, 0)) < 0)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "fcntl() failed: %s", strerror(errno));
Packit 6bd9ab
    close(pipefds[0]);
Packit 6bd9ab
    close(pipefds[1]);
Packit 6bd9ab
    return -1;
Packit 6bd9ab
  }
Packit 6bd9ab
  if (fcntl(pipefds[1], F_SETFL, i | O_NONBLOCK) < 0)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "fcntl() failed: %s", strerror(errno));
Packit 6bd9ab
    close(pipefds[0]);
Packit 6bd9ab
    close(pipefds[1]);
Packit 6bd9ab
    return -1;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* fork() and exit() to detach from the parent process */
Packit 6bd9ab
  switch (fork())
Packit 6bd9ab
  {
Packit 6bd9ab
    case 0:
Packit 6bd9ab
      /* we are the child, close read end of pipe */
Packit 6bd9ab
      close(pipefds[0]);
Packit 6bd9ab
      break;
Packit 6bd9ab
    case -1:
Packit 6bd9ab
      /* we are the parent, but have an error */
Packit 6bd9ab
      log_log(LOG_ERR, "fork() failed: %s", strerror(errno));
Packit 6bd9ab
      close(pipefds[0]);
Packit 6bd9ab
      close(pipefds[1]);
Packit 6bd9ab
      return -1;
Packit 6bd9ab
    default:
Packit 6bd9ab
      /* we are the parent, close write end and wait for information */
Packit 6bd9ab
      close(pipefds[1]);
Packit 6bd9ab
      return wait_for_response(pipefds[0]);
Packit 6bd9ab
  }
Packit 6bd9ab
  /* become process leader */
Packit 6bd9ab
  if (setsid() < 0)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "setsid() failed: %s", strerror(errno));
Packit 6bd9ab
    close(pipefds[1]);
Packit 6bd9ab
    _exit(EXIT_FAILURE);
Packit 6bd9ab
  }
Packit 6bd9ab
  /* fork again so we cannot allocate a pty */
Packit 6bd9ab
  switch (fork())
Packit 6bd9ab
  {
Packit 6bd9ab
    case 0:
Packit 6bd9ab
      /* we are the child */
Packit 6bd9ab
      break;
Packit 6bd9ab
    case -1:
Packit 6bd9ab
      /* we are the parent, but have an error */
Packit 6bd9ab
      log_log(LOG_ERR, "fork() failed: %s", strerror(errno));
Packit 6bd9ab
      close(pipefds[1]);
Packit 6bd9ab
      _exit(EXIT_FAILURE);
Packit 6bd9ab
    default:
Packit 6bd9ab
      /* we are the parent and we're done */
Packit 6bd9ab
      close(pipefds[1]);
Packit 6bd9ab
      _exit(EXIT_SUCCESS);
Packit 6bd9ab
  }
Packit 6bd9ab
  daemonizefd = pipefds[1];
Packit 6bd9ab
  /* close the file descriptor on exec (ignore errors) */
Packit 6bd9ab
  fcntl(daemonizefd, F_SETFD, FD_CLOEXEC);
Packit 6bd9ab
#ifdef HAVE_PTHREAD_ATFORK
Packit 6bd9ab
  /* handle any other forks by closing daemonizefd first */
Packit 6bd9ab
  (void)pthread_atfork(NULL, NULL, closefd);
Packit 6bd9ab
#endif /* HAVE_PTHREAD_ATFORK */
Packit 6bd9ab
  return 0;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
void daemonize_ready(int status, const char *message)
Packit 6bd9ab
{
Packit 6bd9ab
  int l;
Packit 6bd9ab
  if (daemonizefd >= 0)
Packit 6bd9ab
  {
Packit 6bd9ab
    /* we ignore any errors writing */
Packit 6bd9ab
    (void)write(daemonizefd, &status, sizeof(int));
Packit 6bd9ab
    if ((message == NULL) || (message[0] == '\0'))
Packit 6bd9ab
    {
Packit 6bd9ab
      l = 0;
Packit 6bd9ab
      (void)write(daemonizefd, &l, sizeof(int));
Packit 6bd9ab
    }
Packit 6bd9ab
    else
Packit 6bd9ab
    {
Packit 6bd9ab
      l = strlen(message);
Packit 6bd9ab
      (void)write(daemonizefd, &l, sizeof(int));
Packit 6bd9ab
      (void)write(daemonizefd, message, l);
Packit 6bd9ab
    }
Packit 6bd9ab
    (void)close(daemonizefd);
Packit 6bd9ab
    daemonizefd = -1;
Packit 6bd9ab
  }
Packit 6bd9ab
}