Source

watchproc / watchproc.c

/*
 * Copyright 1997-2002 Haesu C. All rights reserved.
 * Watchproc: A process monitor for Linux systems.
 *
 * Written by Haesu <haesu@towardex.com>. Special thanks to DDRP <ddrp@daybologic.com>
 * for his assistance in debugging is_process_dead(); :-)
 *
 * 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
 *
 * TowardEX Linux 1.0 (Build 1.0 Stable 8 DAKN)
 * Copyright (c) 2001-2002 TowardEX Technologies
 * All rights reserved.
 *
 *                            TowardEX Public License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is furnished
 * to do so, subject to the following conditions:
 *
 *      a) Redistributions of the Software whether in binary form, or source
 *         code, must retain the above copyright notice, this list of
 *         conditions and the following disclaimer in the documentation and/or
 *         other materials provided with the distribution..
 *      b) The Software is not modified, copied, distributed, merged, or
 *         published with intention of destroying another party's property, or
 *         being used to violate any applicable law, regulation, treaty or
 *         tariff.
 *      c) Neither the name of the TowardEX Technologies nor the names of its
 *         contributors may be used to endorse or promote products derived from
 *         this Software without specific prior written permission.
 *      d) The Software is not modified, copied, distributed, merged, published,
 *         or utilized in any way to infringe any intellectual property rights
 *         of others. This license (the "License") hereby warrants that any
 *         use of the Software including but not limited to, modification, copy,
 *         distribution, merge, publish, and derivation shall not violate any
 *         proprietary right or the Digital Millenium Copyright Act of 1998 (the
 *         "DMCA"). Furthermore, the License warrants that any use of the Software
 *         shall not violate other applicable copyright regulations.
 *      e) In addition to the above clause, the License is hereby terminated upon
 *         all commercial organizations and institutions whom are in faith of
 *         using any clause from the DMCA against the public, or in faith of using
 *         any other regulations in effort to restrict the public freedom of speech
 *         rights mandated by the First Amendment of the United States Constitution.
 *         The License does not enforce, nor endorse any parties to violate the DMCA,
 *         or proprietary rights of the others. As stated in the Clause D of the
 *         License, any parties that violate the DMCA or other proprietary rights
 *         shall not possess any right to use the Software.
 *
 * 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <syslog.h>
#include <stdbool.h>

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 haesu 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);
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];
	const char* proc_path = "/proc/";
	char* proc_pid = malloc(strlen(proc_path)+strlen(pid)+1); 
	const 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;
}