Add run_program() implementation

This commit is contained in:
Laurence Withers 2008-07-26 17:24:19 +00:00
parent 0015f9c660
commit 4c1a3943bd
3 changed files with 140 additions and 1 deletions

View File

@ -20,6 +20,7 @@
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
/* options for text editors

View File

@ -0,0 +1,138 @@
/* daemonitor/src/daemonitor/600_run_program.c
*
* (c)2007, Laurence Withers, <l@lwithers.me.uk>.
* 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
*/

View File

@ -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);