Arthur de Jong

Open Source / Free Software developer

summaryrefslogtreecommitdiffstats
path: root/nslcd
diff options
context:
space:
mode:
Diffstat (limited to 'nslcd')
-rw-r--r--nslcd/Makefile.am5
-rw-r--r--nslcd/daemonize.c191
-rw-r--r--nslcd/daemonize.h67
-rw-r--r--nslcd/nslcd.c41
4 files changed, 288 insertions, 16 deletions
diff --git a/nslcd/Makefile.am b/nslcd/Makefile.am
index 4346f21..a17b4ce 100644
--- a/nslcd/Makefile.am
+++ b/nslcd/Makefile.am
@@ -1,7 +1,7 @@
# Makefile.am - use automake to generate Makefile.in
#
-# Copyright (C) 2006, 2007 West Consulting
-# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Arthur de Jong
+# Copyright (C) 2006-2007 West Consulting
+# Copyright (C) 2006-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
@@ -26,6 +26,7 @@ AM_CFLAGS = $(PTHREAD_CFLAGS)
nslcd_SOURCES = nslcd.c ../nslcd.h ../common/nslcd-prot.h \
../compat/attrs.h \
log.c log.h \
+ daemonize.c daemonize.h \
common.c common.h \
myldap.c myldap.h \
cfg.c cfg.h \
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;
+ }
+}
diff --git a/nslcd/daemonize.h b/nslcd/daemonize.h
new file mode 100644
index 0000000..8eb9885
--- /dev/null
+++ b/nslcd/daemonize.h
@@ -0,0 +1,67 @@
+/*
+ daemonize.h - definition of functions for 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
+*/
+
+#ifndef NSLCD__DAEMONINZE_H
+#define NSLCD__DAEMONINZE_H 1
+
+/*
+ To properly run as a daemon an application should:
+
+ - close all open file descriptors (see daemonize_closefds() for that)
+ - (re)set proper signal handlers and signal mask
+ - sanitise the environment
+ - fork() / setsid() / fork() to detach from terminal, become process
+ leader and run in the background (see daemonize_demon() for that)
+ - reconnect stdin/stdout/stderr to /dev/null (see
+ daemonize_redirect_stdio() for that)
+ - set the umask to a reasonable value
+ - chdir(/) to avoid locking any mounts
+ - drop privileges as appropriate
+ - chroot() if appropriate
+ - create and lock a pidfile
+ - exit the starting process if initialisation is complete (see
+ daemonize_ready() for that)
+*/
+
+/* This closes all open file descriptors, except stdin, stdout and stderr. */
+void daemonize_closefds(void);
+
+/* Redirect stdio, stdin and stderr to /dev/null. */
+void daemonize_redirect_stdio(void);
+
+/* Detach from the controlling terminal and run in the background. This
+ function does:
+ - double fork and exit first child
+ - in the first child call setsid() to detach from any terminal and
+ create an independent session
+ - keep the parent process waiting until a call to daemonize_ready() is
+ done by the deamon process
+ This function returns either an error which indicates that the
+ daemonizing failed for some reason (usually sets errno), or returns
+ without error indicating that the process has been daemonized. */
+int daemonize_daemon(void);
+
+/* Signal that the original parent may exit because the service has been
+ initialised. The status indicates the exit code of the original process and
+ message, if not NULL or an empty string, is printed to stderr. */
+void daemonize_ready(int status, const char *message);
+
+#endif /* not NSLCD__DAEMONINZE_H */
diff --git a/nslcd/nslcd.c b/nslcd/nslcd.c
index b5881c4..bbe6547 100644
--- a/nslcd/nslcd.c
+++ b/nslcd/nslcd.c
@@ -2,7 +2,7 @@
nslcd.c - ldap local connection daemon
Copyright (C) 2006 West Consulting
- Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Arthur de Jong
+ Copyright (C) 2006-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
@@ -53,7 +53,6 @@
#ifndef HAVE_GETOPT_LONG
#include "compat/getopt_long.h"
#endif /* not HAVE_GETOPT_LONG */
-#include "compat/daemon.h"
#include <dlfcn.h>
#include <libgen.h>
#include <limits.h>
@@ -65,6 +64,7 @@
#include "compat/attrs.h"
#include "compat/getpeercred.h"
#include "compat/socket.h"
+#include "daemonize.h"
/* read timeout is half a second because clients should send their request
quickly, write timeout is 60 seconds because clients could be taking some
@@ -117,7 +117,7 @@ static void display_version(FILE *fp)
{
fprintf(fp, "%s\n", PACKAGE_STRING);
fprintf(fp, "Written by Luke Howard and Arthur de Jong.\n\n");
- fprintf(fp, "Copyright (C) 1997-2013 Luke Howard, Arthur de Jong and West Consulting\n"
+ fprintf(fp, "Copyright (C) 1997-2014 Luke Howard, Arthur de Jong and West Consulting\n"
"This is free software; see the source for copying conditions. There is NO\n"
"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
}
@@ -649,13 +649,7 @@ int main(int argc, char *argv[])
struct timespec ts;
#endif /* HAVE_PTHREAD_TIMEDJOIN_NP */
/* 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);
+ daemonize_closefds();
/* parse the command line */
parse_cmdline(argc, argv);
/* clean the environment */
@@ -695,22 +689,34 @@ int main(int argc, char *argv[])
exit(EXIT_FAILURE);
}
}
+ /* change directory */
+ if (chdir("/") != 0)
+ {
+ log_log(LOG_ERR, "chdir failed: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
/* normal check for pidfile locked */
if (is_locked(NSLCD_PIDFILE))
{
- log_log(LOG_ERR, "daemon may already be active, cannot acquire lock (%s): %s",
+ log_log(LOG_ERR, "nslcd may already be active, cannot acquire lock (%s): %s",
NSLCD_PIDFILE, strerror(errno));
exit(EXIT_FAILURE);
}
/* daemonize */
- if ((!nslcd_debugging) && (!nslcd_nofork) && (daemon(0, 0) < 0))
+ if ((!nslcd_debugging) && (!nslcd_nofork))
{
- log_log(LOG_ERR, "unable to daemonize: %s", strerror(errno));
- exit(EXIT_FAILURE);
+ if (daemonize_daemon() != 0)
+ {
+ log_log(LOG_ERR, "unable to daemonize: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
}
/* intilialize logging */
if (!nslcd_debugging)
+ {
+ daemonize_redirect_stdio();
log_startlogging();
+ }
/* write pidfile */
create_pidfile(NSLCD_PIDFILE);
/* log start */
@@ -719,6 +725,7 @@ int main(int argc, char *argv[])
if (atexit(exithandler))
{
log_log(LOG_ERR, "atexit() failed: %s", strerror(errno));
+ daemonize_ready(EXIT_FAILURE, "atexit() failed\n");
exit(EXIT_FAILURE);
}
/* create socket */
@@ -760,6 +767,7 @@ int main(int argc, char *argv[])
{
log_log(LOG_ERR, "cannot setgid(%d): %s",
(int)nslcd_cfg->gid, strerror(errno));
+ daemonize_ready(EXIT_FAILURE, "cannot setgid()\n");
exit(EXIT_FAILURE);
}
log_log(LOG_DEBUG, "setgid(%d) done", (int)nslcd_cfg->gid);
@@ -771,6 +779,7 @@ int main(int argc, char *argv[])
{
log_log(LOG_ERR, "cannot setuid(%d): %s",
(int)nslcd_cfg->uid, strerror(errno));
+ daemonize_ready(EXIT_FAILURE, "cannot setuid()\n");
exit(EXIT_FAILURE);
}
log_log(LOG_DEBUG, "setuid(%d) done", (int)nslcd_cfg->uid);
@@ -792,6 +801,7 @@ int main(int argc, char *argv[])
if (nslcd_threads == NULL)
{
log_log(LOG_CRIT, "main(): malloc() failed to allocate memory");
+ daemonize_ready(EXIT_FAILURE, "malloc() failed to allocate memory\n");
exit(EXIT_FAILURE);
}
for (i = 0; i < nslcd_cfg->threads; i++)
@@ -800,6 +810,7 @@ int main(int argc, char *argv[])
{
log_log(LOG_ERR, "unable to start worker thread %d: %s",
i, strerror(errno));
+ daemonize_ready(EXIT_FAILURE, "unable to start worker thread\n");
exit(EXIT_FAILURE);
}
}
@@ -813,6 +824,8 @@ int main(int argc, char *argv[])
install_sighandler(SIGTERM, sig_handler);
install_sighandler(SIGUSR1, sig_handler);
install_sighandler(SIGUSR2, SIG_IGN);
+ /* signal the starting process to exit because we can provide services now */
+ daemonize_ready(EXIT_SUCCESS, NULL);
/* wait until we received a signal */
while ((nslcd_receivedsignal == 0) || (nslcd_receivedsignal == SIGUSR1))
{