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