Add run_program() implementation
This commit is contained in:
parent
0015f9c660
commit
4c1a3943bd
|
@ -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
|
||||
|
|
|
@ -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
|
||||
*/
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue