/* * Copyright 1997-2002 blahdy * Watchproc: A crude process monitor for Linux systems. * * Written by blahdy, 2002 Special thanks to DDRP @ daybologic for debugging assist. * * Changes on Watchproc ver. 1.0.1: * HDC: Get rid of extern enum and use normal enum.. extern is for variables, not * for definitions. * HDC: Deprecate ifdef_disable_logging. * HDC: Daemonize after argument checking is done. * HDC: Make -v for version work. * * To compile: gcc -o watchproc watchproc.c * To compile with debug on: gcc -g -DDEBUG -o watchproc watchproc.c * If you have humour: gcc -D_HAVE_HUMOR -o watchproc watchproc.c * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include static const char rcsid[] = "$ld: /usr/src/watchproc.c,v 1.0.1.00 2002/10/02 19:59:45 hdc Exp $"; /* "$ld: /devel/process-monitor/main.c,v 1.00.0.3 2002/04/13 02:22:54 hdc Exp $"; */ enum rturn_type { RTURN_NULL=-1, RTURN_OK=0, RTURN_NOTFOUND=100, RTURN_UNKNOWN_CAUSE=101 }; typedef struct return_data_struct { int err_code; } ret_data; static char Options[] = "n:p:v"; static char* pidfile; static char* execpath; static int panic_cnt = 0; unsigned int stfu = 30; /* number of seconds to put watchproc to silence * between is_process_dead() checks */ static const int i = 0; /* Function proto */ int log_facility (const char* procname, const char* pidfile, int condition); int is_process_dead (); void panic(void); char finder(FILE* _fileptr, char* filepath); static bool Daemonize(void); int main(int argc, char **argv){ int ch; int ifdef_pidfile; int ifdef_execpath; int ret = EXIT_SUCCESS; int set_show_version; int broken; broken = 0; set_show_version = 0; ifdef_pidfile = 0; ifdef_execpath = 0; if ( argc > 1 ){ while ( (ch = getopt(argc,argv,Options)) != -1){ switch (ch) { case 'p': ifdef_pidfile = 1; pidfile=malloc( strlen(optarg) + 1); strcpy(pidfile, optarg); break; case 'n': ifdef_execpath = 1; execpath=malloc( strlen(optarg) + 1); strcpy(execpath, optarg); break; case 'v': set_show_version = 1; break; default: broken = 1; break; } } } if ( set_show_version == 1) { puts ("Watchproc 1.0.1"); exit(0); } if ( ifdef_pidfile == 0 ){ puts("===> Bad argument: -p [path-to-pidfile] not defined."); broken = 1; } if ( ifdef_execpath == 0 ){ puts("===> Bad argument: -n [path-to-process-executable] not defined."); broken = 1; } if ( broken == 1 ) panic(); /* Drop into daemon now. */ if ( !Daemonize() ) return ret; /* start the logs */ log_facility(execpath,pidfile,0); do { if(is_process_dead() == 1){ #ifdef DEBUG printf("Warning: is_process_dead() returned int 1. Calling system for execpath.\n"); #endif system(execpath); #ifdef DEBUG printf("Warning: Condition 2 - Calling log_facility(execpath,pidfile,2);\n"); #endif log_facility(execpath,pidfile,2); } sleep(stfu); } while ( i == 0 ); return 0; } int log_facility(const char* procname, const char* pidfile, int condition){ int returncode; returncode = 0; setlogmask (LOG_UPTO (LOG_NOTICE)); openlog ("watchproc", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); switch (condition) { case 0: /* 0: Started monitoring process. */ syslog (LOG_NOTICE, "Monitoring process %s, pidfile %s.", procname, pidfile); break; case 1: /* 1: NOTICE: Monitored process died mysteriously!! */ syslog (LOG_NOTICE, "%s died, attempting restart..", procname); break; case 2: /* 2: Notice: Restarted process */ syslog (LOG_NOTICE, "Restarted process %s.",procname); break; case 3: /* 3: Warning: Someone intentionally killed the process. */ syslog (LOG_NOTICE, "%s got intentionally killed. pid file can't be found..",procname); break; case 4: /* 4: Alert: Monitoring failed! io error */ syslog (LOG_NOTICE, "Monitoring failure - io error! Monitoring HALTED."); break; default: syslog (LOG_NOTICE, "log_facility: Unknown condition."); puts("log_facility: Unknown condition."); returncode=-1; break; } closelog(); return returncode; } int is_process_dead(){ char pid[6]; char* proc_path = "/proc/"; char* proc_pid = malloc(strlen(proc_path)+strlen(pid)+1); char* proc_cmdline ="/cmdline"; char* full_path = malloc(strlen(proc_path)+strlen(pid)+strlen(proc_cmdline)+1); char* newline; FILE *leadptr; FILE *pidfile_ptr; if(finder(leadptr,pidfile) == 100){ log_facility(execpath,pidfile,3); return 1; } else { /* open up pidfile and set pid */ if ((pidfile_ptr = fopen( pidfile,"rt")) == NULL){ /* uhh... Houston.. We have a problem. */ log_facility(execpath,pidfile,4); perror("!! UNKNOWN I/O ERROR. I CRASHED ROCK HARD. !!"); fclose(pidfile_ptr); exit(1); } else { /* phew, OKay we are good to go! */ fgets(pid, 6, pidfile_ptr); newline = strchr(pid, '\n'); if ( newline ) *newline = '\0'; fclose(pidfile_ptr); } #ifdef DEBUG printf("\npid: %s\n", pid); #endif strcpy(proc_pid, proc_path); strcat(proc_pid, pid); /* combine /proc and pid */ #ifdef DEBUG printf("\nproc_pid: %s\n", proc_pid); #endif strcpy(full_path, proc_pid); strcat(full_path, proc_cmdline); /* combine /proc/pid and cmdline */ #ifdef DEBUG printf("\nfull_path: %s\n", full_path); #endif if(finder(leadptr,full_path) == 100){ #ifdef DEBUG printf("finder returned 100 with input leadptr,full_path. calling logfacility.\n"); #endif log_facility(execpath,pidfile,1); return 1; } #ifdef DEBUG printf("is_process_dead(): if(finder(leadptr,full_path) == 100) marks false, returning 0.\n"); #endif } return 0; } void panic(void){ /* panic only once. no need to repeat what was said to the user. */ if ( panic_cnt > 0 ) return; /* user does not know the syntax. tell him...*/ #ifdef _HAVE_HUMOR puts("You are a dumb ass."); #endif fputs("Usage: -v -n [path to the daemon to watch] -p [path to daemon pidfile]\n", stderr); fputs("\nMake sure your paths to daemon and pidfile are correct. If not watchproc", stderr); fputs("\nwon't do much..\n", stderr); panic_cnt++; exit(1); return; } char finder(FILE *fileptr, char * filepath){ enum rturn_type returncode=RTURN_NULL; ret_data *return_info; return_info = (ret_data * ) malloc ( sizeof(ret_data)); if ((fileptr = fopen( filepath, "rt")) == NULL) /* File does not exist. */ { returncode=RTURN_NOTFOUND; } else { fclose(fileptr); returncode=RTURN_OK; } return (char)returncode; } static bool Daemonize(){ bool ret; putc('\n', stdout); switch ( fork() ) { case -1 : { fputs("watchproc: unable to create a daemon. Abort", stderr); ret = false; break; } case 0 : { /* We are the daemon child (the living being!) */ ret = true; /* Carry on running */ break; } default : { /* Child has been created. Program should exit now */ ret = false; } } return ret; }