1 /*
2    Copyright (C) 2001  Ulric Eriksson <ulric@siag.nu>
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 59 Temple Place - Suite 330, Boston,
17    MA 02111-1307, USA.
18 */
19 
20 #include <sys/types.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <netdb.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <signal.h>
28 #include <stdarg.h>
29 #include <unistd.h>
30 #include <syslog.h>
31 #include <sys/socket.h>
32 
33 #define CFG ETCDIR "/dwatch.conf"
34 #define TIMEOUT 5
35 
36 static int foreground = 1;
37 static int debuglevel = 0;
38 static int verboselevel = 0;
39 static char *cfg = CFG;
40 static int timeout = TIMEOUT;
41 
debug(char * fmt,...)42 static void debug(char *fmt, ...)
43 {
44         char b[4096];
45         va_list ap;
46         va_start(ap, fmt);
47         vsnprintf(b, sizeof b, fmt, ap);
48         if (foreground) {
49                 fprintf(stderr, "%s\n", b);
50         } else {
51                 openlog("pen", LOG_CONS, LOG_USER);
52                 syslog(LOG_DEBUG, "%s\n", b);
53                 closelog();
54         }
55         va_end(ap);
56 }
57 
error(char * fmt,...)58 static void error(char *fmt, ...)
59 {
60         char b[4096];
61         va_list ap;
62         va_start(ap, fmt);
63         vsnprintf(b, sizeof b, fmt, ap);
64         if (foreground) {
65                 fprintf(stderr, "%s\n", b);
66         } else {
67                 openlog("pen", LOG_CONS, LOG_USER);
68                 syslog(LOG_ERR, "%s\n", b);
69                 closelog();
70         }
71         va_end(ap);
72         exit(1);
73 }
74 
usage(void)75 static void usage(void)
76 {
77 	printf("usage:\n"
78 	       "  dwatch [-d] [-f conf]\n"
79 	       "\n"
80 	       "  -d        debugging on\n"
81 	       "  -v        verbose\n"
82 	       "  -f conf   alternate configuration file [%s]\n",
83 	       CFG);
84 
85 	exit(0);
86 }
87 
options(int argc,char ** argv)88 static void options(int argc, char **argv)
89 {
90 	int c;
91 
92 	while ((c = getopt(argc, argv, "f:dv")) != EOF) {
93 		switch (c) {
94 		case 'd':
95 			debuglevel++;
96 			break;
97 		case 'f':
98 			cfg = optarg;
99 			break;
100 		case 'v':
101 			verboselevel++;
102 			break;
103 		default:
104 			usage();
105 			break;
106 		}
107 	}
108 }
109 
try_process(char * p)110 static int try_process(char *p)
111 {
112 	FILE *fp;
113 	char b[1024];
114 	if (debuglevel) debug("Scanning for process '%s'", p);
115 	fp = popen(PS, "r");
116 	if (fp == NULL) error("Can't run ps");
117 	while (fgets(b, sizeof b, fp)) {
118 		if (strstr(b, p)) {
119 			pclose(fp);
120 			return 0;
121 		}
122 	}
123 	pclose(fp);
124 	return -1;
125 }
126 
tcp_alarm(int dummy)127 static void tcp_alarm(int dummy)
128 {
129 	;
130 }
131 
try_tcp(char * p)132 static int try_tcp(char *p)
133 {
134 	struct sockaddr_in serv_addr;
135 	int upfd;
136 	char *addr = strtok(p, ":"), *port = strtok(NULL, ":");
137 	struct hostent *h = gethostbyname(addr);
138 	struct in_addr a;
139 	struct servent *s = getservbyname(port, "tcp");
140 	int po;
141 
142 	if (h == NULL) {
143 		if ((a.s_addr = inet_addr(addr)) == -1) {
144 			error("unknown or invalid address [%s]\n", addr);
145 		}
146 	} else {
147 		memcpy(&a, h->h_addr, h->h_length);
148 	}
149 
150 	if (s == NULL) {
151                 po = atoi(port);
152         } else {
153                 po = ntohs(s->s_port);
154         }
155 
156 	if (debuglevel) debug("Connecting to %s", p);
157 	upfd = socket(AF_INET, SOCK_STREAM, 0);
158 	if (upfd < 0) error("Error opening socket");
159 	memset(&serv_addr, 0, sizeof serv_addr);
160 	serv_addr.sin_family = AF_INET;
161 	serv_addr.sin_addr.s_addr = a.s_addr;
162 	if (debuglevel) debug("Port '%s' = %d", port, po);
163 	serv_addr.sin_port = htons(po);
164 	signal(SIGALRM, tcp_alarm);
165 	alarm(timeout);
166 	if (connect(upfd, (struct sockaddr *) &serv_addr,
167 	    sizeof serv_addr) == -1) {
168 		if (debuglevel) debug("Connect failed");
169 		close(upfd);
170 		return -1;
171 	}
172 	if (debuglevel) debug("Successful connect");
173 	close(upfd);
174 	return 0;
175 }
176 
main(int argc,char ** argv)177 int main(int argc, char **argv)
178 {
179 	char b[1024], directive[1024], argument[1024], command[1024];
180 	int n;
181 	FILE *fp;
182 
183 	options(argc, argv);
184 	fp = fopen(cfg, "r");
185 	if (fp == NULL) error("Can't open configuration file '%s'", cfg);
186 	while (fgets(b, sizeof b, fp)) {
187 		if (b[0] == '#') continue;
188 		n = sscanf(b, "%s \"%[^\"]\" %[^\n]",
189 			   directive, argument, command);
190 		if (n != 3) {
191 			if (debuglevel) debug("Bogus conf line '%s'", b);
192 			continue;
193 		}
194 		if (debuglevel) {
195 			debug("directive: '%s'", directive);
196 			debug("argument: '%s'", argument);
197 			debug("command: '%s'", command);
198 		}
199 		switch (directive[0]) {
200 		case 'P':
201 			if (try_process(argument)) {
202 				if (debuglevel) debug("Running '%s'", command);
203 				if (verboselevel) {
204 					printf("No process '%s'; running '%s'\n",
205 						argument, command);
206 				}
207 				system(command);
208 				sleep(10);
209 			}
210 			break;
211 		case 'T':
212 			if (try_tcp(argument)) {
213 				if (debuglevel) debug("Running '%s'", command);
214 				if (verboselevel) {
215 					printf("No listener on %s, running '%s'\n",
216 						argument, command);
217 				}
218 				system(command);
219 				sleep(10);
220 			}
221 			break;
222 		default:
223 			error("Bogus directive '%s'", directive);
224 			break;
225 		}
226 	}
227 	return 0;
228 }
229 
230