/* * rstrt - child-process monitor/restart wrapper * * This program turns itself into a daemon, which in turn spawns * a child process as defined. * * Should anything happen to the child (exit, die, stop, et cetera), * the daemon will spawn another child to take its place. * * All IO is directed to a file for purposes of debugging the parent * as well as the children. * * This and other hacks can be found at: http://oddgeek.info/ * * Copyright (c) 2001 Jason A. Dour * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from the * use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software in * a product, an acknowledgment in the product documentation would be * appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not * be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. * */ /* * Version Information * * 1.0 2009.10.26 * * Revised to accomodate automatic logfile name generations based on * time/date stamp. Probably should allow for passing command via * ARGV, but hey...it's a hack. * * ooze 2001.10.15 * * First documented version. Used to peg an SSH tunnel for database * communications across an unsecured network. * */ /* * USER DEFINED VALUES * * Define the exec string, are a comma-separated, null-terminated array * of strings. Also, define the directory in which to put output files. * */ #define EXECSTRING "/usr/local/bin/someproc","someproc","--debug",NULL /* FOR PRODUCTION. */ /* #define EXECSTRING "/bin/sleep","sleep","600",NULL /* FOR TESTING. */ #define LOGFILE "/root" /* STANDARD INCLUDES */ #include #include #include #include #include #include #include #include /* CHOOSE PROPER ARGS HEADER BASED ON ANSI OR K&R COMPILER. */ #ifdef ANSIC #include #define VA_START(a,f) va_start(a,f) #elif __linux__ #include #define VA_START(a,f) va_start(a,f) #else #include #define VA_START(a,f) va_start(a) #endif /* Program name. */ #define PROGNAME "rstrt" /* Child process to be killed. */ pid_t kchld; /* Filename for logfile. */ char logfile[512]; /* Set at max length of 512 characters...this is a hack, remember? */ /* * dtstamp() * Write the date/time stamp to the logfile along with the current * process ID. */ int dtstamp () { time_t ltvar; /* time placeholder. */ struct tm *lt; /* exploded time structure. */ pid_t cpid; /* Get local time. */ time(<var); lt = localtime(<var); /* Get our process ID. */ cpid = getpid(); /* Dump date/time stamp and pid to stdout. */ printf("[%d-%.2d-%.2d %.2d:%.2d:%.2d]: (%d) ", lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec, cpid); return(0); } /* * logit(fmt, args) * Take a variable number of arguments, the first being the printf() * format string, followed by any additional arguments. */ #ifdef ANSIC int logit (char *fmt, ...) #elif __linux__ int logit (char *fmt, ...) #else int logit (fmt, va_alist) char *fmt; va_dcl #endif { va_list args; VA_START(args, fmt); dtstamp(); vprintf(fmt, args); fflush(NULL); va_end(args); return(0); } /* * cleanup(signo) * Take the trapped signal number as an argument. Then proceed to * kill the child process and shutdown. */ void cleanup (signo) int signo; { logit("SIGTERM (%d) received. Killing child (%d) and shutting down.\n", signo, kchld); if ( kchld > 0 ) kill(kchld, SIGKILL); logit("%s stopped.\n", PROGNAME); exit(15); } /* * logname() * Build log filename from timestamp. * I would like to include data from EXECSTRING, but maybe next time. */ int logname () { time_t ltvar; /* time placeholder. */ struct tm *lt; /* exploded time structure. */ /* Get local time. */ time(<var); lt = localtime(<var); sprintf(logfile, "%s/%s.%d%.2d%.2d.%.2d%.2d%.2d.log", LOGFILE, PROGNAME, lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec); return(0); } /* * daemon_init() * Turn the program into a daemon. */ int daemon_init () { pid_t pid; if ( (pid = fork()) < 0 ) { logit("Error creating daemon. EXITING."); exit(2); } else if ( pid != 0 ) exit(0); /* Okay I love you crazy parent process, buhbye! */ /* Child process becomes daemon. */ setsid(); /* Become session leader. */ chdir("/"); /* Default working directory to root filesystem. */ umask(0); /* Clear creation mask. */ return(0); } /* MAIN */ int main (argc, argv) int argc; char *argv[]; { pid_t chld; /* Child process. */ int status; /* process status. */ time_t last, current; /* time placeholder. */ /* Build initial logfile filename. */ logname(); /* Assign OUT and ERR to logfile. */ freopen(logfile,"a",stdout); freopen(logfile,"a",stderr); /* Assign null as input. */ freopen("/dev/null","r",stdin); /* Log start. */ logit("%s started\n", PROGNAME); /* Split off the daemon. */ daemon_init(); /* Log daemon init. */ logit("Daemon Initialized\n"); /* Log start of watch loop. */ logit("Entering execute/monitor loop.\n"); while ( 1 ) { /* Safety net timestamp.*/ time(&last); /* Fork! Fork! Fork! */ chld = fork(); /* FORK: Error handling. */ if ( chld < 0 ) { logit("Error: Could not fork!\n"); exit(1); } /* FORK: Child processing. */ if ( chld == 0 ) { /* Exec the command. */ logit("Starting process.\n"); execl(EXECSTRING); /* * Still here? Where's the kaboom? * There was supposed to be an earth-shattering kaboom! */ logit("Error: Could not start process!\n"); exit(127); } /* FORK: Parent processing. */ /* Wait for child to do something, then log outcome. */ if ( chld > 0 ) { /* Trap TERMs and do cleanup. */ signal(SIGTERM, cleanup); kchld = chld; waitpid(chld, &status, WUNTRACED); if (WIFEXITED(status)) { logit("Child terminated normally. (Status = %d)\n", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { logit("Child terminated abnormally. (Status = %d)%s\n", WTERMSIG(status), #ifdef WCOREDUMP WCOREDUMP(status) ? " (core file generated)" : ""); #else ""); #endif } else if (WIFSTOPPED(status)) { /* If chld is STOPped, then it cannot tunnel data. Kill it. */ kill(chld, SIGKILL); logit("Child terminated after receiving STOP signal. (Status = %d)\n", WSTOPSIG(status)); } } /* Safety net check; keep from crazy respawn. */ time(¤t); /* Get current time */ /* If the current time is less than 31 seconds from the last time... */ if ( current - last <= 30 ) { /* Bomb out. Respawning too quickly. */ logit("Error: Respawning too fast. Exiting.\n"); exit(10); } /* Generate a new logfile name. */ logname(); /* Re-attach output to new logfile. */ freopen(logfile,"a",stdout); freopen(logfile,"a",stderr); logit("%s restarting child after failure.", PROGNAME); } /* That's it! We're done. Let's go home. */ exit(0); }