From 45bbf6bf1ce9ccc3564b29d0c511d0ae1634bc45 Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Mon, 6 Apr 2009 15:00:13 +0000 Subject: [PATCH 1/8] Upgrade build system --- make.sh | 10 +++++----- run-test.sh | 17 ++++++----------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/make.sh b/make.sh index 4704369..499cbcc 100755 --- a/make.sh +++ b/make.sh @@ -1,7 +1,7 @@ -#!/bin/sh +#!/bin/bash # daemonitor/make.sh # -# (c)2006-2007, Laurence Withers, . +# (c)2009, Laurence Withers, . # Released under the GNU GPLv3. See file COPYING or # http://www.gnu.org/copyleft/gpl.html for details. # @@ -15,19 +15,19 @@ then echo "Configuration file not found???" exit 1 fi -source "config" # don't fail on error, since last command in config might return false +source "./config" # don't fail on error, since last command in config might return false # Get version information -source version || exit 1 +source "./version" || exit 1 VERSION="${VERMAJOR}.${VERMINOR}.${VERMICRO}" # Get standard functions [ -z "${VERBOSE}" ] && VERBOSE="0" -source scripts/functions.sh || exit 1 +source "./scripts/functions.sh" || exit 1 # List of directories which will be emptied by clean. diff --git a/run-test.sh b/run-test.sh index f937df4..074457c 100755 --- a/run-test.sh +++ b/run-test.sh @@ -1,7 +1,7 @@ -#!/bin/sh +#!/bin/bash # daemonitor/test.sh # -# (c)2007, Laurence Withers, . +# (c)2009, Laurence Withers, . # Released under the GNU GPLv3. See file COPYING or # http://www.gnu.org/copyleft/gpl.html for details. # @@ -21,12 +21,7 @@ run_test() { return 1 fi - if [ $# -eq 0 ] - then - LD_LIBRARY_PATH="obj" ${EXE} || return 1 - else - LD_LIBRARY_PATH="obj" ${EXE} "$*" || return 1 - fi + LD_LIBRARY_PATH="obj" "${EXE}" "$@" || return 1 return 0 } @@ -38,10 +33,10 @@ print_tests() { echo "---------------------------------------------------------------------" for EXE in obj/tests/* do - [ -x ${EXE} ] || continue - NAME=$(echo ${EXE} | sed 's,obj/tests/,,') + [ -x "${EXE}" ] || continue + NAME="$(echo "${EXE}" | sed 's,obj/tests/,,')" echo -ne "${NAME}\t" - LD_LIBRARY_PATH="obj" ${EXE} --print-summary + LD_LIBRARY_PATH="obj" "${EXE}" --print-summary done } From 4a50315f12762372321d5d9fe3361424e28526ed Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Mon, 6 Apr 2009 15:00:26 +0000 Subject: [PATCH 2/8] Bump version --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index efb8d0b..6d353e9 100644 --- a/version +++ b/version @@ -11,7 +11,7 @@ # expected to be in 'major.minor.micro' format. VERMAJOR=1 VERMINOR=0 -VERMICRO=1 +VERMICRO=2 # kate: replace-trailing-space-save true; space-indent true; tab-width 4; # vim: expandtab:ts=4:sw=4:syntax=sh From f611c2acf7992bb07ddeefaa486f5cf9c226e91b Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Tue, 2 Oct 2012 09:54:56 +0000 Subject: [PATCH 3/8] Update build system --- README | 10 ++++++---- config | 7 +++---- make.sh | 9 ++++----- run-test.sh | 16 ++++++++-------- scripts/.gitignore | 1 + scripts/functions.sh | 7 +++---- scripts/paths | 7 +++---- src/daemonitor/build.app | 3 +-- src/daemonitor/build.install-app | 1 - src/daemonitor/build.monolithic | 1 - version | 9 ++++----- 11 files changed, 33 insertions(+), 38 deletions(-) diff --git a/README b/README index 41e1684..4c9c22a 100644 --- a/README +++ b/README @@ -1,8 +1,10 @@ daemonitor -============================================================================== -(c)2007, Laurence Withers, . -Released under the GNU GPLv3. See file COPYING or -http://www.gnu.org/copyleft/gpl.html for details. +======================================================================== +Copyright: ©2007–2012, Güralp Systems Limited +Author: Laurence Withers +License: GPLv3 + +See file COPYING for detail license information. Really Quick Instructions ------------------------- diff --git a/config b/config index ca23c9e..246b047 100644 --- a/config +++ b/config @@ -1,10 +1,9 @@ # daemonitor/config -# kate: replace-trailing-space-save true; space-indent true; tab-width 4; # vim: syntax=sh:expandtab:ts=4:sw=4 # -# (c)2007, Laurence Withers, . -# Released under the GNU GPLv3. See file COPYING or -# http://www.gnu.org/copyleft/gpl.html for details. +# Copyright: ©2007–2012, Güralp Systems Limited +# Author: Laurence Withers +# License: GPLv3 # # This file contains options used to build daemonitor. diff --git a/make.sh b/make.sh index 499cbcc..e33fc32 100755 --- a/make.sh +++ b/make.sh @@ -1,9 +1,9 @@ #!/bin/bash # daemonitor/make.sh -# -# (c)2009, Laurence Withers, . -# Released under the GNU GPLv3. See file COPYING or -# http://www.gnu.org/copyleft/gpl.html for details. +# +# Copyright: ©2007–2012, Güralp Systems Limited +# Author: Laurence Withers +# License: GPLv3 # @@ -292,5 +292,4 @@ done exit 0 -# kate: replace-trailing-space-save true; space-indent true; tab-width 4; # vim: expandtab:ts=4:sw=4 diff --git a/run-test.sh b/run-test.sh index 074457c..c54a0e4 100755 --- a/run-test.sh +++ b/run-test.sh @@ -1,9 +1,9 @@ #!/bin/bash # daemonitor/test.sh -# -# (c)2009, Laurence Withers, . -# Released under the GNU GPLv3. See file COPYING or -# http://www.gnu.org/copyleft/gpl.html for details. +# +# Copyright: ©2007–2012, Güralp Systems Limited +# Author: Laurence Withers +# License: GPLv3 # # Running this script on its own will display a summary of all the @@ -21,7 +21,7 @@ run_test() { return 1 fi - LD_LIBRARY_PATH="obj" "${EXE}" "$@" || return 1 + LD_LIBRARY_PATH="obj:${LD_LIBRARY_PATH}" "${EXE}" "$@" || return 1 return 0 } @@ -36,11 +36,12 @@ print_tests() { [ -x "${EXE}" ] || continue NAME="$(echo "${EXE}" | sed 's,obj/tests/,,')" echo -ne "${NAME}\t" - LD_LIBRARY_PATH="obj" "${EXE}" --print-summary + LD_LIBRARY_PATH="obj:${LD_LIBRARY_PATH}" "${EXE}" --print-summary done } + # Main script if [ $# -eq 0 ] then @@ -48,7 +49,6 @@ then exit 0 fi -run_test $* +run_test "$@" -# kate: replace-trailing-space-save true; space-indent true; tab-width 4; # vim: expandtab:ts=4:sw=4 diff --git a/scripts/.gitignore b/scripts/.gitignore index edcbbeb..b9fcf56 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -7,6 +7,7 @@ build.docs.none build.files.none build.firmware.gpasm build.firmware.sdcc +build.header.c build.lib.c build.lib.c++ build.make.none diff --git a/scripts/functions.sh b/scripts/functions.sh index d16f446..81c05f1 100644 --- a/scripts/functions.sh +++ b/scripts/functions.sh @@ -1,8 +1,8 @@ # daemonitor/scripts/functions.sh # -# (c)2007, Laurence Withers, . -# Released under the GNU GPLv3. See file COPYING or -# http://www.gnu.org/copyleft/gpl.html for details. +# Copyright: ©2007–2012, Güralp Systems Limited +# Author: Laurence Withers, +# License: GPLv3 # # Common functions @@ -63,5 +63,4 @@ do_cmd_redir() { fi } -# kate: replace-trailing-space-save true; space-indent true; tab-width 4; # vim: expandtab:ts=4:sw=4 diff --git a/scripts/paths b/scripts/paths index 484627c..8f905a4 100644 --- a/scripts/paths +++ b/scripts/paths @@ -1,8 +1,8 @@ # daemonitor/scripts/paths # -# (c)2007, Laurence Withers, . -# Released under the GNU GPLv3. See file COPYING or -# http://www.gnu.org/copyleft/gpl.html for details. +# Copyright: ©2007–2012, Güralp Systems Limited +# Author: Laurence Withers, +# License: GPLv3 # # Default path setup. Not meant for editing; use environment variables # to override values if needed. @@ -60,5 +60,4 @@ fi [ -z "${WEBDIR}" ] && WEBDIR="${SRVDIR}/http" [ -z "${CGIDIR}" ] && CGIDIR="${WEBDIR}/cgi-bin" -# kate: replace-trailing-space-save true; space-indent true; tab-width 4; # vim: syntax=sh:expandtab:ts=4:sw=4 diff --git a/src/daemonitor/build.app b/src/daemonitor/build.app index 41d452e..4d47da9 100644 --- a/src/daemonitor/build.app +++ b/src/daemonitor/build.app @@ -6,7 +6,7 @@ if [ -z ${daemonitor_BUILT} ] then daemonitor="obj/daemonitor" - EXTRAS="-D_GNU_SOURCE" + EXTRAS="-std=gnu99 -D_GNU_SOURCE -DAPP_NAME=\"daemonitor\"" echo "Building application ${daemonitor}..." @@ -37,5 +37,4 @@ then fi -# kate: replace-trailing-space-save true; space-indent true; tab-width 4; # vim: syntax=sh:expandtab:ts=4:sw=4 diff --git a/src/daemonitor/build.install-app b/src/daemonitor/build.install-app index c4a44be..ee06605 100644 --- a/src/daemonitor/build.install-app +++ b/src/daemonitor/build.install-app @@ -8,5 +8,4 @@ echo "Installing binaries into '${SBINDIR}'" install_file "${daemonitor}" "${SBINDIR}" 0755 || return 1 print_success "Done" -# kate: replace-trailing-space-save true; space-indent true; tab-width 4; # vim: syntax=sh:expandtab:ts=4:sw=4 diff --git a/src/daemonitor/build.monolithic b/src/daemonitor/build.monolithic index 8db384e..0f3e681 100644 --- a/src/daemonitor/build.monolithic +++ b/src/daemonitor/build.monolithic @@ -14,5 +14,4 @@ then MONOLITHIC_DOC="${MONOLITHIC_DOC} ${SRC}" fi -# kate: replace-trailing-space-save true; space-indent true; tab-width 4; # vim: syntax=sh:expandtab:ts=4:sw=4 diff --git a/version b/version index 6d353e9..eee5d05 100644 --- a/version +++ b/version @@ -1,8 +1,8 @@ # daemonitor/version -# -# (c)2007, Laurence Withers, . -# Released under the GNU GPLv3. See file COPYING or -# http://www.gnu.org/copyleft/gpl.html for details. +# +# Copyright: ©2007–2012, Güralp Systems Limited +# Author: Laurence Withers +# License: GPLv3 # @@ -13,5 +13,4 @@ VERMAJOR=1 VERMINOR=0 VERMICRO=2 -# kate: replace-trailing-space-save true; space-indent true; tab-width 4; # vim: expandtab:ts=4:sw=4:syntax=sh From b26bd15791256a4da0b4d3563ef5bb1a384ea0c3 Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Tue, 2 Oct 2012 18:50:23 +0000 Subject: [PATCH 4/8] Treat SIGINT similarly to SIGTERM Treat SIGINT similarly to SIGTERM; exit when it is received. This gives the desired behaviour of having a foreground daemonitor be stopped by pressing ctrl-C on its controlling tty. --- src/daemonitor/700_signal.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/daemonitor/700_signal.c b/src/daemonitor/700_signal.c index 469c896..e47edd1 100644 --- a/src/daemonitor/700_signal.c +++ b/src/daemonitor/700_signal.c @@ -1,9 +1,9 @@ /* daemonitor/src/daemonitor/700_signal.c * - * (c)2007, Laurence Withers, . - * Released under the GNU GPLv3. See file COPYING or - * http://www.gnu.org/copyleft/gpl.html for details. -*/ + * Copyright: ©2007–2012, Laurence Withers. + * Author: Laurence Withers + * License: GPLv3 + */ @@ -72,8 +72,9 @@ void signal_setup(void) sigdelset(&ss, SIGSEGV); sigdelset(&ss, SIGBUS); - /* we'd like to deal with SIGTERM specially */ + /* we'd like to deal with SIGTERM/SIGINT specially */ sigdelset(&ss, SIGTERM); + sigdelset(&ss, SIGINT); /* don't block SIGCHLD or wait(2) won't work */ sigdelset(&ss, SIGCHLD); @@ -90,9 +91,10 @@ void signal_setup(void) sigaction(SIGBUS, &sa, 0); sigaction(SIGCHLD, &sa, 0); - /* install our own handler for SIGTERM */ + /* install our own handler for SIGTERM/SIGINT */ sa.sa_handler = sigterm_handler; sigaction(SIGTERM, &sa, 0); + sigaction(SIGINT, &sa, 0); } From b17eed94661a6a6d9bd1a43c4545c0e255f95ab3 Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Tue, 2 Oct 2012 18:52:11 +0000 Subject: [PATCH 5/8] SIGTERM handler: use _exit(2), not exit(3) exit(3) is not async-signal safe (presumably due to atexit(3) processing), so use _exit(2) instead. This was clearly an omission from the original code which even had a comment to the effect that _exit(2) needed to be used. In all likelihood this never caused any problems as atexit(3) functionality is not used. --- src/daemonitor/700_signal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/daemonitor/700_signal.c b/src/daemonitor/700_signal.c index e47edd1..f6d2346 100644 --- a/src/daemonitor/700_signal.c +++ b/src/daemonitor/700_signal.c @@ -45,7 +45,7 @@ void sigterm_handler(int signum __attribute__((unused))) /* uck -- we can't call exit() because it's not async-signal-safe, so we must call _exit() and * clean up manually, with no logging */ unlink(pidfile_filename); - exit(0); + _exit(0); } From c55693ce1c33dad3ce1909f0cc7ec2c5287bafa9 Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Tue, 2 Oct 2012 19:03:51 +0000 Subject: [PATCH 6/8] Add respawn checking feature Add a feature which allows arbitrary checks to be performed whenever the child process is about to be respawned. Currently implemented checks allow running of a command (via system(3)) or testing the existence of a file. This was developed to be used to stop respawns of a daemon that was started in response to a udev hotplug event, but it is generally applicable to many other scenarios. --- src/daemonitor/800_checks.c | 186 ++++++++++++++++++++++++++++++++++++ src/daemonitor/999_main.c | 21 ++-- 2 files changed, 201 insertions(+), 6 deletions(-) create mode 100644 src/daemonitor/800_checks.c diff --git a/src/daemonitor/800_checks.c b/src/daemonitor/800_checks.c new file mode 100644 index 0000000..1f7f11b --- /dev/null +++ b/src/daemonitor/800_checks.c @@ -0,0 +1,186 @@ +/* daemonitor/src/daemonitor/800_checks.c + * + * Copyright: ©2012, Laurence Withers. + * Author: Laurence Withers + * License: GPLv3 + */ + + + +/* RESPAWN CHECKS ************************************************************** + * + * This file implements functionality for various checks that may be carried + * out whenever the child process needs to be respawned. An arbitrary number of + * checks may be registered; if any single check fails, the entire test counts + * as failing. The result of the checks is found by calling run_checks(). + * + * A failed check will log details at LOG_NOTICE (or above). + */ + + + +/* PUBLIC API *****************************************************************/ + + + +/* check_command_register() + * cmd: A command (passed to system(3)) to run. If it exits with code 0, the + * check passes. + */ +void check_command_register(const char* cmd); + + + +/* check_file_register() + * path: A file to test. If it exists, the check passes. + */ +void check_file_register(const char* cmd); + + + +/* run_checks() + * Returns 0 if all checks succeeded (including when none are registered) and + * non-0 if any check failed. + */ +int run_checks(void); + + + +/* IMPLEMENTATION *************************************************************/ + + + +enum check_type { + check_type_command, + check_type_file, +}; + + + +/* struct check + * check_head + * A linked list of the checks that are to be run. + */ +struct check { + enum check_type type; + + /* can be the argument for system(3) or the path to check for a file */ + const char* arg; + + /* linked list structure */ + struct check* next; +}; + +struct check* check_head = 0; + + + +/* check_generic_register() + * Instantiates a ‘struct check’ and adds it to the list at ‘check_head’. + */ +void +check_generic_register(enum check_type type, const char* arg) +{ + struct check* c; + + c = malloc(sizeof(*c)); + c->type = type; + c->arg = arg; + c->next = check_head; + check_head = c; +} + + + +void +check_command_register(const char* cmd) +{ + check_generic_register(check_type_command, cmd); +} + + + +int +check_command_run(const struct check* iter) +{ + int ret; + + ret = system(iter->arg); + if(ret == -1) { + LOG(LOG_ERR, "Unable to run check command \"%s\": %m.", iter->arg); + return 0; /* don't exit just because system(3) failed */ + } + + if(WIFSIGNALED(ret)) { + LOG(LOG_WARNING, "Check command \"%s\" killed by signal (%s). Exiting.", + iter->arg, strsignal(WTERMSIG(ret))); + return -1; + } + + if(!WIFEXITED(ret)) { + LOG(LOG_WARNING, "Unrecognised exit code 0x%X from check command " + "\"%s\".", ret, iter->arg); + return 0; + } + + if(WEXITSTATUS(ret)) { + LOG(LOG_NOTICE, "Check command \"%s\" signalled failure (exit code " + "%d). Exiting.", iter->arg, WEXITSTATUS(ret)); + return -1; + } + + return 0; +} + + + +void +check_file_register(const char* path) +{ + check_generic_register(check_type_file, path); +} + + + +int +check_file_run(const struct check* iter) +{ + int ret; + + ret = access(iter->arg, F_OK); + if(ret) { + LOG(LOG_NOTICE, "Check file \"%s\" does not exist. Exiting.", + iter->arg); + return -1; + } + + return 0; +} + + + +int +run_checks(void) +{ + const struct check* iter; + + for(iter = check_head; iter; iter = iter->next) { + switch(iter->type) { + case check_type_command: + if(check_command_run(iter)) return -1; + break; + + case check_type_file: + if(check_file_run(iter)) return -1; + break; + } + } + + return 0; +} + + + +/* options for text editors +vim: expandtab:ts=4:sw=4 +*/ diff --git a/src/daemonitor/999_main.c b/src/daemonitor/999_main.c index 7474a0e..b4819b2 100644 --- a/src/daemonitor/999_main.c +++ b/src/daemonitor/999_main.c @@ -1,9 +1,9 @@ /* daemonitor/src/daemonitor/999_main.c * - * (c)2007, Laurence Withers, . - * Released under the GNU GPLv3. See file COPYING or - * http://www.gnu.org/copyleft/gpl.html for details. -*/ + * Copyright: ©2007–2012, Laurence Withers. + * Author: Laurence Withers + * License: GPLv3 + */ @@ -37,6 +37,10 @@ void usage(void) " " DEFAULT_RESTART_INTERVAL_S "). May be 0 to restart immediately.\n" " -E, --environment Name of file containing environment variables for\n" " child process, one NAME=VALUE entry per line.\n" + " -C, --check-command Runs via /bin/sh before restarting a service\n" + " that has exited. Stops if fails.\n" + " -F, --check-file Checks that exists before restarting a service\n" + " that has exited. Stops if it does not exist.\n" "", stderr); } @@ -53,6 +57,8 @@ struct option options[] = { { "pidfile", required_argument, 0, 'p' }, { "restart-interval", required_argument, 0, 'R' }, { "environment", required_argument, 0, 'E' }, + { "check-command", required_argument, 0, 'C' }, + { "check-file", required_argument, 0, 'F' }, { 0, 0, 0, 0 } }; @@ -61,7 +67,7 @@ struct option options[] = { /* optstr * The list of short commandline options for getopt(3). Sorted alphabetically, lowercase first. */ -const char* optstr = "dhl:p:E:R:V"; +const char* optstr = "dhl:p:C:E:F:R:V"; @@ -90,6 +96,8 @@ int main(int argc, char* argv[]) case 'p': pidfile_argument = optarg; break; case 'R': restart_argument = optarg; break; case 'E': set_environment_file(optarg); break; + case 'C': check_command_register(optarg); break; + case 'F': check_file_register(optarg); break; } } opts_done: @@ -150,6 +158,8 @@ int main(int argc, char* argv[]) LOG(LOG_NOTICE, "Sleeping for %d seconds.", restart_interval); safe_sleep_fixed(restart_interval, 0); } + + if(run_checks()) return 0; } return 0; @@ -158,6 +168,5 @@ int main(int argc, char* argv[]) /* options for text editors -kate: replace-trailing-space-save true; space-indent true; tab-width 4; vim: expandtab:ts=4:sw=4 */ From 639944b905f1c2557b96108052709eccdd5191b4 Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Tue, 2 Oct 2012 19:55:54 +0000 Subject: [PATCH 7/8] Simplify daemon and always close file descriptors Use daemon(3) to daemonise rather than writing our own code. Since we use the mode of daemon(3) which closes the standard 3 file descriptors, if the user requests logging to one of those we dup(2) the original first. We must also take care to always close any newly-opened file descriptors before execve(2) of the child. --- src/daemonitor/200_log.c | 24 +++-- src/daemonitor/400_daemon.c | 160 ------------------------------- src/daemonitor/600_run_program.c | 47 +++++++-- src/daemonitor/999_main.c | 11 ++- 4 files changed, 64 insertions(+), 178 deletions(-) delete mode 100644 src/daemonitor/400_daemon.c diff --git a/src/daemonitor/200_log.c b/src/daemonitor/200_log.c index b82ecea..f337b48 100644 --- a/src/daemonitor/200_log.c +++ b/src/daemonitor/200_log.c @@ -1,9 +1,9 @@ /* daemonitor/src/daemonitor/200_log.c * - * (c)2007, Laurence Withers, . - * Released under the GNU GPLv3. See file COPYING or - * http://www.gnu.org/copyleft/gpl.html for details. -*/ + * Copyright: ©2007–2012, Laurence Withers. + * Author: Laurence Withers + * License: GPLv3 + */ @@ -170,16 +170,25 @@ void log_func_syslog(int level, const char* fmt, ...) /* log_destination_set() * Set `log_func' function pointer. */ -void log_destination_set(enum log_destination_t dest) +void +log_destination_set(enum log_destination_t dest) { switch(dest) { case log_destination_stdout: - log_func_fd = STDOUT_FILENO; + log_func_fd = dup(STDOUT_FILENO); + if(log_func_fd == -1) { + printf("dup() failed: %m."); + exit(1); + } log_func = log_func_file; break; case log_destination_stderr: - log_func_fd = STDERR_FILENO; + log_func_fd = dup(STDERR_FILENO); + if(log_func_fd == -1) { + fprintf(stderr, "dup() failed: %m."); + exit(1); + } log_func = log_func_file; break; @@ -210,6 +219,5 @@ int log_destination_set_file(const char* filename) /* options for text editors -kate: replace-trailing-space-save true; space-indent true; tab-width 4; vim: expandtab:ts=4:sw=4 */ diff --git a/src/daemonitor/400_daemon.c b/src/daemonitor/400_daemon.c deleted file mode 100644 index 7d9540d..0000000 --- a/src/daemonitor/400_daemon.c +++ /dev/null @@ -1,160 +0,0 @@ -/* daemonitor/src/daemonitor/400_daemon.c - * - * (c)2007, Laurence Withers, . - * Released under the GNU GPLv3. See file COPYING or - * http://www.gnu.org/copyleft/gpl.html for details. -*/ - - - -/* DAEMON ROUTINES ******************************************************************************** - * - * This file provides the daemonise() routine, which makes the process a daemon (fork() + setsid() - * so that the process lives in the background, also opens /dev/console). It also provides the - * close_file_descriptors() routine, which clears all file descriptors but 0, 1 and 2. - */ - - - -/* PUBLIC API ************************************************************************************/ - - - -/* daemonise() - * Performs a fork() and setsid() so that the process lives in the background. It closes stdin, - * stdout and stderr and opens /dev/null and/or /dev/console instead, as appropriate. On error, - * this function exits the program. - */ -void daemonise(void); - - - -/* close_file_descriptors() - * Closes all file descriptors but stdin, stdout and stderr. Should be called whether we are - * daemonising or not. - */ -void close_file_descriptors(void); - - - -/* IMPLEMENTATION ********************************************************************************/ - - - -void daemonise(void) -{ - int fd; - pid_t pid; - - /* close stdin and reopen it as /dev/null */ - TEMP_FAILURE_RETRY( close(STDIN_FILENO) ); - fd = open("/dev/null", O_RDONLY); - switch(fd) { - case -1: - LOG(LOG_CRIT, "Could not open `/dev/null' (%m)."); - exit(1); - - case STDIN_FILENO: - break; - - default: - LOG(LOG_CRIT, "Opened `/dev/null' for stdin, but got file descriptor %d instead.", fd); - exit(1); - } - - /* close stdout and reopen it as /dev/console if possible, or /dev/null if not */ - TEMP_FAILURE_RETRY( close(STDOUT_FILENO) ); - fd = open("/dev/console", O_WRONLY | O_NOCTTY); - if(fd == -1) { - LOG(LOG_WARNING, "Could not open `/dev/console' for stdout (%m)."); - fd = open("/dev/null", O_WRONLY); - } - switch(fd) { - case -1: - LOG(LOG_CRIT, "Could not open `/dev/null' (%m)."); - exit(1); - - case STDOUT_FILENO: - break; - - default: - LOG(LOG_CRIT, "Opened `/dev/null' for stdout, but got file descriptor %d instead.", fd); - exit(1); - } - - /* close stderr and reopen it as /dev/console if possible, or /dev/null if not */ - TEMP_FAILURE_RETRY( close(STDERR_FILENO) ); - fd = open("/dev/console", O_WRONLY | O_NOCTTY); - if(fd == -1) { - LOG(LOG_WARNING, "Could not open `/dev/console' for stderr (%m)."); - /* force fd closed again, in case glibc kindly connected it to syslog for us */ - TEMP_FAILURE_RETRY( close(STDERR_FILENO) ); - fd = open("/dev/null", O_WRONLY); - } - switch(fd) { - case -1: - LOG(LOG_CRIT, "Could not open `/dev/null' (%m)."); - exit(1); - - case STDERR_FILENO: - break; - - default: - LOG(LOG_CRIT, "Opened `/dev/null' for stderr, but got file descriptor %d instead.", fd); - exit(1); - } - - /* daemonise */ - pid = fork(); - if(pid == (pid_t)-1) { - LOG(LOG_CRIT, "Could not fork daemonised process (%m)."); - exit(1); - } - - if(pid) { - /* parent */ - _exit(0); /* don't call atexit() et al. in parent */ - } - - - setsid(); - LOG(LOG_NOTICE, "Daemonised, PID %lu.", (unsigned long)getpid()); -} - - - -/* close_file_descriptors() - * Adapted from libgslutil (GPL-3). - */ -void close_file_descriptors(void) -{ - DIR* d; - struct dirent* de; - int i, end, dfd; - - d = opendir("/proc/self/fd"); - if(!d) { - /* backup method */ - for(i = STDERR_FILENO + 1, end = getdtablesize(); i < end; ++i) { - close(i); - } - return; - } - dfd = dirfd(d); - - while( (de = readdir(d)) ) { - if(de->d_name[0] == '.') continue; - i = strtol(de->d_name, 0, 10); - if(i == dfd) continue; /* skip file descriptor associated with `DIR* d' */ - if(i <= STDERR_FILENO) continue; /* leave stdin, stdout, stderr alone */ - TEMP_FAILURE_RETRY( close(i) ); - } - closedir(d); -} - - - -/* options for text editors -kate: replace-trailing-space-save true; space-indent true; tab-width 4; -vim: expandtab:ts=4:sw=4 -*/ diff --git a/src/daemonitor/600_run_program.c b/src/daemonitor/600_run_program.c index 638eaf1..a85720c 100644 --- a/src/daemonitor/600_run_program.c +++ b/src/daemonitor/600_run_program.c @@ -1,9 +1,9 @@ /* daemonitor/src/daemonitor/600_run_program.c * - * (c)2007, Laurence Withers, . - * Released under the GNU GPLv3. See file COPYING or - * http://www.gnu.org/copyleft/gpl.html for details. -*/ + * Copyright: ©2007–2012, Laurence Withers. + * Author: Laurence Withers + * License: GPLv3 + */ @@ -45,8 +45,8 @@ volatile int sigterm_received = 0; /* close_file_descriptors() - * Closes all file descriptors but stdin, stdout and stderr. Should be called whether we are - * daemonising or not. + * Closes all file descriptors but stdin, stdout and stderr (which it leaves + * unchanged). */ void close_file_descriptors(void); @@ -56,6 +56,38 @@ void close_file_descriptors(void); +/* close_file_descriptors() + * Adapted from libgslutil (GPL-3). + */ +void +close_file_descriptors(void) +{ + DIR* d; + struct dirent* de; + int i, end, dfd; + + d = opendir("/proc/self/fd"); + if(!d) { + /* backup method */ + for(i = STDERR_FILENO + 1, end = getdtablesize(); i < end; ++i) { + close(i); + } + return; + } + dfd = dirfd(d); + + while( (de = readdir(d)) ) { + if(de->d_name[0] == '.') continue; + i = strtol(de->d_name, 0, 10); + if(i == dfd) continue; /* skip file descriptor associated with `DIR* d' */ + if(i <= STDERR_FILENO) continue; /* leave stdin, stdout, stderr alone */ + TEMP_FAILURE_RETRY( close(i) ); + } + closedir(d); +} + + + /* crash() * Crash the program. This is better than just calling exit() because it doesn't clean up. We * should leave our PID file lying around to look like we crashed. This function is paranoid. @@ -113,6 +145,8 @@ void run_program(const char* exe_path, char** argv) /* child -- execute new process */ if(pid == 0) { + close_file_descriptors(); + /* clear the child's signal mask */ sigemptyset(&ss); sigprocmask(SIG_SETMASK, &ss, 0); @@ -203,6 +237,5 @@ void run_program(const char* exe_path, char** argv) /* options for text editors -kate: replace-trailing-space-save true; space-indent true; tab-width 4; vim: expandtab:ts=4:sw=4 */ diff --git a/src/daemonitor/999_main.c b/src/daemonitor/999_main.c index b4819b2..98e46f7 100644 --- a/src/daemonitor/999_main.c +++ b/src/daemonitor/999_main.c @@ -79,7 +79,7 @@ int main(int argc, char* argv[]) { char* endp; const char* log_argument = 0, * pidfile_argument = 0, * restart_argument = 0; - int daemon = 0, restart_interval = DEFAULT_RESTART_INTERVAL; + int do_daemon = 0, restart_interval = DEFAULT_RESTART_INTERVAL; /* safety */ close_file_descriptors(); /* leaves stdout etc. open */ @@ -91,7 +91,7 @@ int main(int argc, char* argv[]) case '?': LOG_ANYWHERE("Invalid commandline options."); return 1; case 'h': usage(); return 1; case 'V': fputs("daemonitor " VERSION "\n", stderr); return 1; - case 'd': ++daemon; break; + case 'd': ++do_daemon; break; case 'l': log_argument = optarg; break; case 'p': pidfile_argument = optarg; break; case 'R': restart_argument = optarg; break; @@ -136,7 +136,12 @@ int main(int argc, char* argv[]) if(load_child_environ()) return 1; /* daemonise? */ - if(daemon) daemonise(); + if(do_daemon) { + if(daemon(0, 0)) { + LOG(LOG_CRIT, "daemon() failed: %m."); + return 1; + } + } /* write a PID file */ if(pidfile_argument) { From b2aadf503b89ed3c4db82d4892ed7c5153fa92cb Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Sun, 10 Nov 2013 12:27:57 +0000 Subject: [PATCH 8/8] Bump version --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index eee5d05..de0b85d 100644 --- a/version +++ b/version @@ -11,6 +11,6 @@ # expected to be in 'major.minor.micro' format. VERMAJOR=1 VERMINOR=0 -VERMICRO=2 +VERMICRO=3 # vim: expandtab:ts=4:sw=4:syntax=sh