From 4c1a3943bd8aa8de4fb420922caff5d14a8e8a28 Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Sat, 26 Jul 2008 17:24:19 +0000 Subject: [PATCH] Add run_program() implementation --- src/daemonitor/000_TopSource.c | 1 + src/daemonitor/600_run_program.c | 138 +++++++++++++++++++++++++++++++ src/daemonitor/999_main.c | 2 +- 3 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 src/daemonitor/600_run_program.c diff --git a/src/daemonitor/000_TopSource.c b/src/daemonitor/000_TopSource.c index 782395c..a76b773 100644 --- a/src/daemonitor/000_TopSource.c +++ b/src/daemonitor/000_TopSource.c @@ -20,6 +20,7 @@ #include #include #include +#include #include /* options for text editors diff --git a/src/daemonitor/600_run_program.c b/src/daemonitor/600_run_program.c new file mode 100644 index 0000000..0325bf0 --- /dev/null +++ b/src/daemonitor/600_run_program.c @@ -0,0 +1,138 @@ +/* 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. +*/ + + + +/* PROGRAM EXECUTION AND MONITORING ROUTINES ****************************************************** + * + * This file provides the run_program() routine, which actually runs and waits on the child + * process. `program_pid' is set to the PID of the child, which allows it to be sent signals. + */ + + + +/* PUBLIC API ************************************************************************************/ + + + +/* run_program() + * Runs the program with the specified arguments. Logs the start and end of the process, including + * exit status. Waits on the process to exit. A single call to this function is a single run. If + * the program cannot be run, for whatever reason, that is logged and the function returns. + */ +void run_program(const char* exe_path, char** argv); + + + +/* 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 ********************************************************************************/ + + + +/* environ + * As per environ(7), this variable must be declared by the user program. It is used to specify + * the environment for our child process. + */ +extern char **environ; + + + +/* 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. + */ +void crash(void) __attribute__((noreturn)); +void crash(void) +{ + /* raise SIGKILL; failing that, call abort(); failing that, _exit() */ + raise(SIGKILL); /* quick + dirty + unblockable crash */ + abort(); /* unblocks and raises SIGABRT */ + _exit(127); +} + + + +/* run_program() + * fork() then wait(), logging the results of each step. + */ +void run_program(const char* exe_path, char** argv) +{ + pid_t pid; + siginfo_t si; + + pid = fork(); + + /* deal with errors */ + if(pid == (pid_t)-1) { + LOG(LOG_ERR, "Could not create new child process (%m)."); + return; + } + + /* child -- execute new process */ + if(pid == 0) { + execve(exe_path, argv, environ); + LOG(LOG_ERR, "Could not execute `%s' (%m).", exe_path); + crash(); + } + + /* parent -- wait(2) on child */ + LOG(LOG_NOTICE, "Created child process with PID %lu.", (unsigned long)pid); + + wait_again: + /* wait on child */ + si.si_pid = 0; + if(waitid(P_PID, pid, &si, WEXITED) == -1) { + switch(errno) { + case EINTR: + break; /* signal processed */ + + default: + LOG(LOG_ALERT, "waitid() returned error `%m' for PID %lu -- should not be possible.", + (unsigned long)pid); + crash(); + } + goto wait_again; /* go again */ + } + + /* check that child did in fact exit */ + if(si.si_pid != pid) { + LOG(LOG_ALERT, "waitid() returned results for PID %lu, but we were expecting %lu.", + (unsigned long)si.si_pid, (unsigned long)pid); + crash(); + } + + /* report how the child exited */ + switch(si.si_code) { + case CLD_EXITED: + LOG(LOG_NOTICE, "Child process %lu exited with status %d.", + (unsigned long)pid, si.si_status); + break; + + case CLD_KILLED: + LOG(LOG_NOTICE, "Child process %lu killed by signal %d (%s).", + (unsigned long)pid, si.si_status, strsignal(si.si_status)); + break; + + default: + LOG(LOG_WARNING, "Received si_code 0x%X from waitid(); ignoring it.", si.si_code); + goto wait_again; + } +} + + + +/* 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 2f26243..6616232 100644 --- a/src/daemonitor/999_main.c +++ b/src/daemonitor/999_main.c @@ -132,7 +132,7 @@ int main(int argc, char* argv[]) /* run process */ while(1) { - run_program(argv + optind, argc - optind); + run_program(argv[optind] /* path */, argv + optind + 1 /* argv[], null terminated */); if(restart_interval) { LOG(LOG_NOTICE, "Sleeping for %d seconds.", restart_interval);