/*
   Copyright (C) 2001  Ulric Eriksson <ulric@siag.nu>

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
   MA 02111-1307, USA.
*/

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <stdarg.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/socket.h>

#define CFG ETCDIR "/dwatch.conf"
#define TIMEOUT 5

static int foreground = 1;
static int debuglevel = 0;
static int verboselevel = 0;
static char *cfg = CFG;
static int timeout = TIMEOUT;

static void debug(char *fmt, ...)
{
        char b[4096];
        va_list ap;
        va_start(ap, fmt);
        vsnprintf(b, sizeof b, fmt, ap);
        if (foreground) {
                fprintf(stderr, "%s\n", b);
        } else {
                openlog("pen", LOG_CONS, LOG_USER);
                syslog(LOG_DEBUG, "%s\n", b);
                closelog();
        }
        va_end(ap);
}

static void error(char *fmt, ...)
{
        char b[4096];
        va_list ap;
        va_start(ap, fmt);
        vsnprintf(b, sizeof b, fmt, ap);
        if (foreground) {
                fprintf(stderr, "%s\n", b);
        } else {
                openlog("pen", LOG_CONS, LOG_USER);
                syslog(LOG_ERR, "%s\n", b);
                closelog();
        }
        va_end(ap);
        exit(1);
}

static void usage(void)
{
	printf("usage:\n"
	       "  dwatch [-d] [-f conf]\n"
	       "\n"
	       "  -d        debugging on\n"
	       "  -v        verbose\n"
	       "  -f conf   alternate configuration file [%s]\n",
	       CFG);

	exit(0);
}

static void options(int argc, char **argv)
{
	int c;

	while ((c = getopt(argc, argv, "f:dv")) != EOF) {
		switch (c) {
		case 'd':
			debuglevel++;
			break;
		case 'f':
			cfg = optarg;
			break;
		case 'v':
			verboselevel++;
			break;
		default:
			usage();
			break;
		}
	}
}

static int try_process(char *p)
{
	FILE *fp;
	char b[1024];
	if (debuglevel) debug("Scanning for process '%s'", p);
	fp = popen(PS, "r");
	if (fp == NULL) error("Can't run ps");
	while (fgets(b, sizeof b, fp)) {
		if (strstr(b, p)) {
			pclose(fp);
			return 0;
		}
	}
	pclose(fp);
	return -1;
}

static void tcp_alarm(int dummy)
{
	;
}

static int try_tcp(char *p)
{
	struct sockaddr_in serv_addr;
	int upfd;
	char *addr = strtok(p, ":"), *port = strtok(NULL, ":");
	struct hostent *h = gethostbyname(addr);
	struct in_addr a;
	struct servent *s = getservbyname(port, "tcp");
	int po;

	if (h == NULL) {
		if ((a.s_addr = inet_addr(addr)) == -1) {
			error("unknown or invalid address [%s]\n", addr);
		}
	} else {
		memcpy(&a, h->h_addr, h->h_length);
	}

	if (s == NULL) {
                po = atoi(port);
        } else {
                po = ntohs(s->s_port);
        }

	if (debuglevel) debug("Connecting to %s", p);
	upfd = socket(AF_INET, SOCK_STREAM, 0);
	if (upfd < 0) error("Error opening socket");
	memset(&serv_addr, 0, sizeof serv_addr);
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = a.s_addr;
	if (debuglevel) debug("Port '%s' = %d", port, po);
	serv_addr.sin_port = htons(po);
	signal(SIGALRM, tcp_alarm);
	alarm(timeout);
	if (connect(upfd, (struct sockaddr *) &serv_addr,
	    sizeof serv_addr) == -1) {
		if (debuglevel) debug("Connect failed");
		close(upfd);
		return -1;
	}
	if (debuglevel) debug("Successful connect");
	close(upfd);
	return 0;
}

int main(int argc, char **argv)
{
	char b[1024], directive[1024], argument[1024], command[1024];
	int n;
	FILE *fp;

	options(argc, argv);
	fp = fopen(cfg, "r");
	if (fp == NULL) error("Can't open configuration file '%s'", cfg);
	while (fgets(b, sizeof b, fp)) {
		if (b[0] == '#') continue;
		n = sscanf(b, "%s \"%[^\"]\" %[^\n]",
			   directive, argument, command);
		if (n != 3) {
			if (debuglevel) debug("Bogus conf line '%s'", b);
			continue;
		}
		if (debuglevel) {
			debug("directive: '%s'", directive);
			debug("argument: '%s'", argument);
			debug("command: '%s'", command);
		}
		switch (directive[0]) {
		case 'P':
			if (try_process(argument)) {
				if (debuglevel) debug("Running '%s'", command);
				if (verboselevel) {
					printf("No process '%s'; running '%s'\n",
						argument, command);
				}
				system(command);
				sleep(10);
			}
			break;
		case 'T':
			if (try_tcp(argument)) {
				if (debuglevel) debug("Running '%s'", command);
				if (verboselevel) {
					printf("No listener on %s, running '%s'\n",
						argument, command);
				}
				system(command);
				sleep(10);
			}
			break;
		default:
			error("Bogus directive '%s'", directive);
			break;
		}
	}
	return 0;
}



syntax highlighted by Code2HTML, v. 0.9.1