|
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 |
}
|