Add run_program() implementation
This commit is contained in:
parent
0015f9c660
commit
4c1a3943bd
|
@ -20,6 +20,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
/* options for text editors
|
/* 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 */
|
/* run process */
|
||||||
while(1) {
|
while(1) {
|
||||||
run_program(argv + optind, argc - optind);
|
run_program(argv[optind] /* path */, argv + optind + 1 /* argv[], null terminated */);
|
||||||
|
|
||||||
if(restart_interval) {
|
if(restart_interval) {
|
||||||
LOG(LOG_NOTICE, "Sleeping for %d seconds.", restart_interval);
|
LOG(LOG_NOTICE, "Sleeping for %d seconds.", restart_interval);
|
||||||
|
|
Loading…
Reference in New Issue