diff options
Diffstat (limited to 'nslcd')
-rw-r--r-- | nslcd/Makefile.am | 5 | ||||
-rw-r--r-- | nslcd/daemonize.c | 191 | ||||
-rw-r--r-- | nslcd/daemonize.h | 67 | ||||
-rw-r--r-- | nslcd/nslcd.c | 41 |
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)) { |