diff --git a/Makefile b/Makefile index 8ce869b66624..ce88a881b3ce 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,7 @@ CCAN_OBJS := \ ccan-crypto-sha256.o \ ccan-crypto-shachain.o \ ccan-crypto-siphash24.o \ + ccan-daemonize.o \ ccan-err.o \ ccan-fdpass.o \ ccan-htable.o \ @@ -97,6 +98,7 @@ CCAN_HEADERS := \ $(CCANDIR)/ccan/crypto/sha256/sha256.h \ $(CCANDIR)/ccan/crypto/shachain/shachain.h \ $(CCANDIR)/ccan/crypto/siphash24/siphash24.h \ + $(CCANDIR)/ccan/daemonize/daemonize.h \ $(CCANDIR)/ccan/endian/endian.h \ $(CCANDIR)/ccan/err/err.h \ $(CCANDIR)/ccan/fdpass/fdpass.h \ @@ -450,6 +452,8 @@ ccan-opt-parse.o: $(CCANDIR)/ccan/opt/parse.c $(CC) $(CFLAGS) -c -o $@ $< ccan-opt-usage.o: $(CCANDIR)/ccan/opt/usage.c $(CC) $(CFLAGS) -c -o $@ $< +ccan-daemonize.o: $(CCANDIR)/ccan/daemonize/daemonize.c + $(CC) $(CFLAGS) -c -o $@ $< ccan-err.o: $(CCANDIR)/ccan/err/err.c $(CC) $(CFLAGS) -c -o $@ $< ccan-noerr.o: $(CCANDIR)/ccan/noerr/noerr.c diff --git a/ccan/ccan/daemonize/LICENSE b/ccan/ccan/daemonize/LICENSE new file mode 120000 index 000000000000..2354d12945d3 --- /dev/null +++ b/ccan/ccan/daemonize/LICENSE @@ -0,0 +1 @@ +../../licenses/BSD-MIT \ No newline at end of file diff --git a/ccan/ccan/daemonize/_info b/ccan/ccan/daemonize/_info new file mode 100644 index 000000000000..0c4bc98063ca --- /dev/null +++ b/ccan/ccan/daemonize/_info @@ -0,0 +1,54 @@ +#include "config.h" +#include +#include + +/** + * daemonize - routine to turn a process into a well-behaved daemon. + * + * Daemons should detach themselves thoroughly from the process which launched + * them, and not prevent any filesystems from being unmounted. daemonize() + * helps with the process. + * + * Example: + * #include + * #include + * #include + * #include + * #include + * + * static void usage(const char *name) + * { + * errx(1, "Usage: %s [--daemonize]\n", name); + * } + * + * // Wait for a minute, possibly as a daemon. + * int main(int argc, char *argv[]) + * { + * if (argc != 1) { + * if (argc == 2 && streq(argv[1], "--daemonize")) { + * if (!daemonize()) + * err(1, "Failed to become daemon"); + * } else + * usage(argv[1]); + * } + * sleep(60); + * exit(0); + * } + * + * License: BSD-MIT + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + return 0; + } + + if (strcmp(argv[1], "libs") == 0) { + return 0; + } + + return 1; +} diff --git a/ccan/ccan/daemonize/daemonize.c b/ccan/ccan/daemonize/daemonize.c new file mode 100644 index 000000000000..bd32ecbbaeeb --- /dev/null +++ b/ccan/ccan/daemonize/daemonize.c @@ -0,0 +1,45 @@ +/* Licensed under BSD-MIT - see LICENSE file for details */ +#include +#include +#include +#include +#include +#include + +/* This code is based on Stevens Advanced Programming in the UNIX + * Environment. */ +bool daemonize(void) +{ + pid_t pid; + + /* Separate from our parent via fork, so init inherits us. */ + if ((pid = fork()) < 0) + return false; + /* use _exit() to avoid triggering atexit() processing */ + if (pid != 0) + _exit(0); + + /* Don't hold files open. */ + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + + /* Many routines write to stderr; that can cause chaos if used + * for something else, so set it here. */ + if (open("/dev/null", O_WRONLY) != 0) + return false; + if (dup2(0, STDERR_FILENO) != STDERR_FILENO) + return false; + close(0); + + /* Session leader so ^C doesn't whack us. */ + if (setsid() == (pid_t)-1) + return false; + /* Move off any mount points we might be in. */ + if (chdir("/") != 0) + return false; + + /* Discard our parent's old-fashioned umask prejudices. */ + umask(0); + return true; +} diff --git a/ccan/ccan/daemonize/daemonize.h b/ccan/ccan/daemonize/daemonize.h new file mode 100644 index 000000000000..1f473eae22bf --- /dev/null +++ b/ccan/ccan/daemonize/daemonize.h @@ -0,0 +1,21 @@ +/* Licensed under BSD-MIT - see LICENSE file for details */ +#ifndef CCAN_DAEMONIZE_H +#define CCAN_DAEMONIZE_H +#include + +/** + * daemonize - turn this process into a daemon. + * + * This routine forks us off to become a daemon. It returns false on failure + * (eg. fork(), chdir or open failed) and sets errno. + * + * Side effects for programmers to be aware of: + * - PID changes (our parent exits, we become child of init) + * - stdin and stdout file descriptors are closed + * - stderr is reopened to /dev/null so you don't reuse it + * - Current working directory changes to / + * - Umask is set to 0. + */ +bool daemonize(void); + +#endif /* CCAN_DAEMONIZE_H */ diff --git a/ccan/ccan/daemonize/test/run.c b/ccan/ccan/daemonize/test/run.c new file mode 100644 index 000000000000..73f021118a8c --- /dev/null +++ b/ccan/ccan/daemonize/test/run.c @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +struct child_data { + pid_t pid; + pid_t ppid; + bool in_root_dir; + int read_from_stdin, write_to_stdout, write_to_stderr; +}; + +int main(int argc, char *argv[]) +{ + int fds[2]; + struct child_data daemonized; + pid_t pid; + + plan_tests(5); + + if (pipe(fds) != 0) + err(1, "Failed pipe"); + + /* Since daemonize forks and parent exits, we need to fork + * that parent. */ + pid = fork(); + if (pid == -1) + err(1, "Failed fork"); + + if (pid == 0) { + char buffer[2]; + pid = getpid(); + daemonize(); + /* Keep valgrind happy about uninitialized bytes. */ + memset(&daemonized, 0, sizeof(daemonized)); + daemonized.pid = getpid(); + daemonized.in_root_dir = (getcwd(buffer, 2) != NULL); + daemonized.read_from_stdin + = read(STDIN_FILENO, buffer, 1) == -1 ? errno : 0; + daemonized.write_to_stdout + = write(STDOUT_FILENO, buffer, 1) == -1 ? errno : 0; + if (write(STDERR_FILENO, buffer, 1) != 1) { + daemonized.write_to_stderr = errno; + if (daemonized.write_to_stderr == 0) + daemonized.write_to_stderr = -1; + } else + daemonized.write_to_stderr = 0; + + /* Make sure parent exits. */ + while (getppid() == pid) + sleep(1); + daemonized.ppid = getppid(); + if (write(fds[1], &daemonized, sizeof(daemonized)) + != sizeof(daemonized)) + exit(1); + exit(0); + } + + if (read(fds[0], &daemonized, sizeof(daemonized)) != sizeof(daemonized)) + err(1, "Failed read"); + + ok1(daemonized.pid != pid); +#if 0 /* Believe it or not, this fails under Ubuntu 13.10 (Upstart) */ + ok1(daemonized.ppid == 1); +#endif + ok1(daemonized.in_root_dir); + ok1(daemonized.read_from_stdin == EBADF); + ok1(daemonized.write_to_stdout == EBADF); + ok1(daemonized.write_to_stderr == 0); + + return exit_status(); +}