Commit | Line | Data |
---|---|---|
c5de9e27 AT |
1 | /* |
2 | * Copyright (c) 1989 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * This code is derived from software contributed to Berkeley by | |
6 | * Mike Muuss. | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * 1. Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * 2. Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in the | |
15 | * documentation and/or other materials provided with the distribution. | |
16 | * 3. All advertising materials mentioning features or use of this software | |
17 | * must display the following acknowledgement: | |
18 | * This product includes software developed by the University of | |
19 | * California, Berkeley and its contributors. | |
20 | * 4. Neither the name of the University nor the names of its contributors | |
21 | * may be used to endorse or promote products derived from this software | |
22 | * without specific prior written permission. | |
23 | * | |
24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
34 | * SUCH DAMAGE. | |
35 | */ | |
36 | ||
37 | /* | |
38 | * ICMPMONITOR.C | |
39 | * | |
40 | * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, | |
41 | * monitor several hosts, and notify admin if some of them are down. | |
42 | * | |
43 | * Author - | |
44 | * Vadim Zaliva <lord@crocodile.org> | |
45 | * | |
46 | * Status - | |
47 | * Public Domain. Distribution Unlimited. | |
48 | */ | |
49 | ||
50 | char copyright[] = | |
51 | "@(#) Copyright (c) 1989 The Regents of the University of California.\n" | |
52 | "All rights reserved.\n"; | |
53 | ||
54 | char rcsid[] = "$Id: icmpmonitor.c,v 1.8 2004/05/28 01:33:07 lord Exp $"; | |
55 | ||
56 | #include <sys/param.h> | |
57 | #include <stdio.h> | |
58 | #include <stdlib.h> | |
59 | #ifdef HAVE_SYSLOG_H | |
60 | # include <syslog.h> | |
61 | #endif | |
62 | #include <stdarg.h> | |
63 | #include <signal.h> | |
64 | #include <string.h> | |
65 | #ifdef HAVE_SYS_TIME_H | |
66 | # include <sys/time.h> | |
67 | #endif | |
68 | #include <sys/socket.h> | |
69 | #include <sys/types.h> | |
70 | #ifdef HAVE_FCNTL_H | |
71 | # include <fcntl.h> | |
72 | #endif | |
73 | #ifdef HAVE_SYS_FCNTL_H | |
74 | # include <sys/fcntl.h> | |
75 | #endif | |
76 | #ifdef HAVE_UNISTD_H | |
77 | # include <unistd.h> | |
78 | #endif | |
79 | ||
80 | #include <netinet/in.h> | |
81 | #include <arpa/inet.h> | |
82 | #include <netdb.h> | |
83 | #include <netinet/in_systm.h> | |
84 | #include <netinet/ip.h> | |
85 | #include <netinet/ip_icmp.h> | |
86 | ||
87 | /* Workaround for broken ICMP header on Slackware 4.x */ | |
88 | #ifdef _LINUX_ICMP_H | |
89 | # warning "Broken Slackware 4.x 'netinet/ip_icmp.h' header detected. Using replacement 'struct icmp' definition." | |
90 | # define ICMP_MINLEN 8 | |
91 | struct icmp | |
92 | { | |
93 | u_int8_t icmp_type; | |
94 | u_int8_t icmp_code; | |
95 | u_int16_t icmp_cksum; | |
96 | union | |
97 | { | |
98 | struct ih_idseq | |
99 | { | |
100 | u_int16_t icd_id; | |
101 | u_int16_t icd_seq; | |
102 | } ih_idseq; | |
103 | } icmp_hun; | |
104 | ||
105 | # define icmp_id icmp_hun.ih_idseq.icd_id | |
106 | # define icmp_seq icmp_hun.ih_idseq.icd_seq | |
107 | ||
108 | union { | |
109 | u_int8_t id_data[1]; | |
110 | } icmp_dun; | |
111 | ||
112 | # define icmp_data icmp_dun.id_data | |
113 | ||
114 | }; | |
115 | #endif /* _LINUX_ICMP_H */ | |
116 | ||
117 | #include <stddef.h> | |
118 | #include <errno.h> | |
119 | ||
120 | #include "cfg.h" | |
121 | ||
122 | /* defines */ | |
123 | ||
124 | /* #define DEBUG */ | |
125 | ||
126 | #ifndef nil | |
127 | # define nil NULL | |
128 | #endif | |
129 | ||
130 | /* return codes */ | |
131 | #define RET_OK 0 | |
132 | #define RET_NO_HOSTS 1 | |
133 | #define RET_INIT_ERROR 2 | |
134 | #define RET_BAD_CFG 3 | |
135 | #define RET_BAD_OPT 4 | |
136 | ||
137 | #define MAXPACKET (65536 - 60 - 8) /* max packet size */ | |
138 | #define DEFDATALEN (64 - 8) /* default data length */ | |
139 | ||
140 | #define VERSION "ICMPmonitor v1.2 by lord@crocodile.org" | |
141 | #define MAX_LOG_MSG_SIZE 4096 | |
142 | ||
143 | # define icmphdr icmp | |
144 | ||
145 | /* typedefs */ | |
146 | typedef struct monitor_host | |
147 | { | |
148 | /* following are coming from cfg */ | |
149 | char *name; | |
150 | int ping_interval; | |
151 | int max_delay; | |
152 | char *upcmd; | |
153 | char *downcmd; | |
154 | ||
155 | /* following values are calculated */ | |
156 | int socket; | |
157 | struct timeval last_ping_received; | |
158 | struct timeval last_ping_sent; | |
159 | int up; | |
160 | int down; | |
161 | struct sockaddr_in dest; | |
162 | ||
163 | unsigned int sentpackets ; | |
164 | unsigned int recvdpackets; | |
165 | ||
166 | /* linked list */ | |
167 | struct monitor_host *next; | |
168 | } monitor_host_t; | |
169 | ||
170 | /* protos */ | |
171 | static void logopen(void); | |
172 | static void logclose(void); | |
173 | static void log(int type, char *format, ...); | |
174 | static int gethostaddr(const char *name); | |
175 | static void read_hosts(const char *cfg_file_name); | |
176 | static void init_hosts(void); | |
177 | static void get_response(void); | |
178 | static void pinger(int); | |
179 | static int in_cksum(u_short *addr, int len); | |
180 | static void read_icmp_data(monitor_host_t *p); | |
181 | static void tvsub(struct timeval *out, struct timeval *in); | |
182 | static void done(int code); | |
183 | static void start_daemon(void); | |
184 | static int gcd(int x, int y); | |
185 | ||
186 | /* globals */ | |
187 | ||
188 | static monitor_host_t **hosts = nil; | |
189 | static int isDaemon = 0; | |
190 | static int isVerbose = 0; | |
191 | static int keepBanging = 0; | |
192 | static unsigned short ident; | |
193 | static int send_delay = 1; | |
194 | ||
195 | int main(int ac, char **av) | |
196 | { | |
197 | extern char* optarg; | |
198 | extern int optind; | |
199 | char *cfgfile=nil; | |
200 | int param; | |
201 | ||
202 | logopen(); | |
203 | log(LOG_INFO, VERSION " is starting."); | |
204 | ||
205 | while((param = getopt(ac, av, "rvdf:")) != -1) | |
206 | switch(param) | |
207 | { | |
208 | case 'v': | |
209 | isVerbose = 1; | |
210 | break; | |
211 | case 'd': | |
212 | isDaemon = 1; | |
213 | break; | |
214 | case 'r': | |
215 | keepBanging = 1; | |
216 | break; | |
217 | case 'f': | |
218 | cfgfile=strdup(optarg); | |
219 | break; | |
220 | default: | |
221 | fprintf(stderr,"Usage: icmpmonitor [-d] [-v] [-r] [-f cfgfile]\n"); | |
222 | done(RET_BAD_OPT); | |
223 | } | |
224 | ||
225 | if(!cfgfile) | |
226 | { | |
227 | log(LOG_WARNING,"No cfg file specified. Assuming 'icmpmonitor.cfg'"); | |
228 | cfgfile="icmpmonitor.cfg"; | |
229 | } | |
230 | ||
231 | read_hosts(cfgfile); /* we do this before becoming daemon, | |
232 | to be able process relative path */ | |
233 | ||
234 | if(isDaemon) | |
235 | start_daemon(); | |
236 | ||
237 | init_hosts(); | |
238 | ||
239 | ident=getpid() & 0xFFFF; | |
240 | ||
241 | (void)signal(SIGALRM, pinger); | |
242 | alarm(send_delay); | |
243 | ||
244 | get_response(); | |
245 | ||
246 | done(RET_OK); | |
247 | } | |
248 | ||
249 | ||
250 | /* | |
251 | * in_cksum -- | |
252 | * Checksum routine for Internet Protocol family headers (C Version) | |
253 | */ | |
254 | static int | |
255 | in_cksum(u_short *addr, int len) | |
256 | { | |
257 | register int nleft = len; | |
258 | register u_short *w = addr; | |
259 | register int sum = 0; | |
260 | u_short answer = 0; | |
261 | ||
262 | /* | |
263 | * Our algorithm is simple, using a 32 bit accumulator (sum), we add | |
264 | * sequential 16 bit words to it, and at the end, fold back all the | |
265 | * carry bits from the top 16 bits into the lower 16 bits. | |
266 | */ | |
267 | while (nleft > 1) { | |
268 | sum += *w++; | |
269 | nleft -= 2; | |
270 | } | |
271 | ||
272 | /* mop up an odd byte, if necessary */ | |
273 | if (nleft == 1) { | |
274 | *(u_char *)(&answer) = *(u_char *)w ; | |
275 | sum += answer; | |
276 | } | |
277 | ||
278 | /* add back carry outs from top 16 bits to low 16 bits */ | |
279 | sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ | |
280 | sum += (sum >> 16); /* add carry */ | |
281 | answer = ~sum; /* truncate to 16 bits */ | |
282 | return(answer); | |
283 | } | |
284 | ||
285 | /* | |
286 | * pinger -- | |
287 | * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet | |
288 | * will be added on by the kernel. The ID field is our UNIX process ID, | |
289 | * and the sequence number is an ascending integer. The first 8 bytes | |
290 | * of the data portion are used to hold a UNIX "timeval" struct in VAX | |
291 | * byte-order, to compute the round-trip time. | |
292 | */ | |
293 | static void pinger(int ignore) | |
294 | { | |
295 | register struct icmphdr *icp; | |
296 | register int cc; | |
297 | int i; | |
298 | monitor_host_t *p; | |
299 | u_char outpack[MAXPACKET]; | |
300 | ||
301 | p=hosts[0]; | |
302 | while(p) | |
303 | { | |
304 | if(p->socket!=-1) | |
305 | { | |
306 | struct timeval now; | |
307 | ||
308 | (void)gettimeofday(&now,(struct timezone *)NULL); | |
309 | tvsub(&now, &p->last_ping_received); | |
310 | ||
311 | if(now.tv_sec > (p->max_delay+p->ping_interval)) | |
312 | { | |
313 | p->up=0; | |
314 | if((!p->down) || keepBanging) | |
315 | { | |
316 | p->down = 1; | |
317 | ||
318 | if(isVerbose) | |
319 | log(LOG_INFO,"Host %s in down. Executing DOWN command",p->name); | |
320 | if(!fork()) | |
321 | { | |
322 | system(p->downcmd); | |
323 | exit(0); | |
324 | } else | |
325 | { | |
326 | wait(nil); | |
327 | } | |
328 | } | |
329 | } | |
330 | ||
331 | (void)gettimeofday(&now,(struct timezone *)NULL); | |
332 | tvsub(&now, &p->last_ping_sent); | |
333 | ||
334 | if(now.tv_sec > p->ping_interval) | |
335 | { | |
336 | /* Time to send ping */ | |
337 | ||
338 | icp = (struct icmphdr *)outpack; | |
339 | icp->icmp_type = ICMP_ECHO; | |
340 | icp->icmp_code = 0; | |
341 | icp->icmp_cksum = 0; | |
342 | icp->icmp_seq = p->socket; | |
343 | icp->icmp_id = ident; | |
344 | ||
345 | if(isVerbose) | |
346 | log(LOG_INFO,"Sending ICMP packet to %s.",p->name); | |
347 | ||
348 | (void)gettimeofday((struct timeval *)&outpack[8], | |
349 | (struct timezone *)NULL); | |
350 | ||
351 | cc = DEFDATALEN + 8; /* skips ICMP portion */ | |
352 | ||
353 | /* compute ICMP checksum here */ | |
354 | icp->icmp_cksum = in_cksum((u_short *)icp, cc); | |
355 | ||
356 | i = sendto(p->socket, (char *)outpack, cc, 0, (const struct sockaddr *)(&p->dest), | |
357 | sizeof(struct sockaddr)); | |
358 | ||
359 | (void)gettimeofday(&p->last_ping_sent, | |
360 | (struct timezone *)NULL); | |
361 | ||
362 | if(i<0 || i!=cc) | |
363 | { | |
364 | if(i<0) | |
365 | log(LOG_WARNING,"Sending ICMP packet to %s failed.",p->name); | |
366 | } | |
367 | p->sentpackets++; | |
368 | } | |
369 | } | |
370 | p=p->next; | |
371 | ||
372 | } | |
373 | ||
374 | (void)signal(SIGALRM, pinger); /* restore handler */ | |
375 | alarm(send_delay); | |
376 | } | |
377 | ||
378 | static void get_response(void) | |
379 | { | |
380 | fd_set rfds; | |
381 | int retval; | |
382 | monitor_host_t *p; | |
383 | int maxd=-1; | |
384 | ||
385 | while(1) | |
386 | { | |
387 | p=hosts[0]; | |
388 | FD_ZERO(&rfds); | |
389 | while(p) | |
390 | { | |
391 | if(p->socket != -1) | |
392 | { | |
393 | if(p->socket > maxd) | |
394 | maxd=p->socket; | |
395 | FD_SET(p->socket, &rfds); | |
396 | } | |
397 | p=p->next; | |
398 | } | |
399 | ||
400 | retval = select(maxd+1, &rfds, nil, nil, nil); | |
401 | if(retval<0) | |
402 | { | |
403 | /* we get her in case we are interrupted by signal. | |
404 | it's ok. */ | |
405 | } | |
406 | else | |
407 | { | |
408 | if(retval>0) | |
409 | { | |
410 | /* log(LOG_DEBUG,"ICMP data is available now."); */ | |
411 | p=hosts[0]; | |
412 | while(p) | |
413 | { | |
414 | if(p->socket!=-1 && FD_ISSET(p->socket, &rfds)) | |
415 | { | |
416 | /* Read data */ | |
417 | read_icmp_data(p); | |
418 | } | |
419 | p=p->next; | |
420 | } | |
421 | } else | |
422 | { | |
423 | log(LOG_DEBUG,"select returns 0."); /* TODO */ | |
424 | } | |
425 | } | |
426 | } | |
427 | } | |
428 | ||
429 | static void read_icmp_data(monitor_host_t *p) | |
430 | { | |
431 | socklen_t fromlen ; | |
432 | struct sockaddr_in from ; | |
433 | int cc ; | |
434 | struct ip *ip ; | |
435 | struct icmp *icmp ; | |
436 | int iphdrlen ; | |
437 | int delay ; | |
438 | struct timeval tv ; | |
439 | unsigned char buf[MAXPACKET]; /* read buffer */ | |
440 | ||
441 | (void)gettimeofday(&tv, (struct timezone *)NULL); | |
442 | ||
443 | fromlen = sizeof(from); | |
444 | if((cc = recvfrom(p->socket, buf, sizeof(buf), 0, | |
445 | (struct sockaddr *)&from, &fromlen)) < 0) | |
446 | { | |
447 | if(errno != EINTR) | |
448 | log(LOG_WARNING,"Error reading ICMP data from %s.",p->name); | |
449 | return; | |
450 | } | |
451 | ||
452 | /* log(LOG_DEBUG,"Got %d bytes of ICMP data from %s.",cc, p->name); */ | |
453 | ||
454 | /* check IP header actual len */ | |
455 | ip = (struct ip *)buf ; | |
456 | iphdrlen = ip->ip_hl<<2 ; | |
457 | icmp = (struct icmp *) (buf+iphdrlen) ; | |
458 | ||
459 | if(cc < iphdrlen+ICMP_MINLEN) | |
460 | { | |
461 | log(LOG_WARNING,"Received short packet from %s.",p->name); | |
462 | return; | |
463 | } | |
464 | ||
465 | if(icmp->icmp_type == ICMP_ECHOREPLY && | |
466 | icmp->icmp_id == ident && | |
467 | icmp->icmp_seq == p->socket) | |
468 | { | |
469 | p->recvdpackets++; | |
470 | ||
471 | memcpy(&p->last_ping_received, &tv, sizeof(tv)); | |
472 | ||
473 | tvsub(&tv, (struct timeval *) &icmp->icmp_data[0]); | |
474 | delay=tv.tv_sec*1000+(tv.tv_usec/1000); | |
475 | ||
476 | if(isVerbose) | |
477 | log(LOG_INFO,"Got ICMP reply from %s in %d ms.",p->name,delay); | |
478 | p->down=0; | |
479 | if(!p->up) | |
480 | { | |
481 | p->up=1; | |
482 | if(isVerbose) | |
483 | log(LOG_INFO,"Host %s in now up. Executing UP command",p->name); | |
484 | if(!fork()) | |
485 | { | |
486 | system(p->upcmd); | |
487 | exit(0); | |
488 | } else | |
489 | { | |
490 | wait(nil); | |
491 | } | |
492 | } | |
493 | } else | |
494 | { | |
495 | /* | |
496 | log(LOG_DEBUG,"ICMP packet of type %d from %s. Ident=%d",icmp->icmp_type, | |
497 | p->name, | |
498 | icmp->icmp_id | |
499 | ); | |
500 | */ | |
501 | } | |
502 | } | |
503 | ||
504 | static void read_hosts(const char *cfg_file_name) | |
505 | { | |
506 | int i,n=0; | |
507 | struct Cfg *cfg; | |
508 | ||
509 | if((cfg=readcfg(cfg_file_name))==NULL) | |
510 | { | |
511 | log(LOG_ERR,"Error reading cfg. Exiting."); | |
512 | done(RET_BAD_CFG); | |
513 | } | |
514 | ||
515 | if(cfg->nelements) | |
516 | { | |
517 | hosts=malloc(sizeof(monitor_host_t *)*cfg->nelements); | |
518 | for(i=0;i<cfg->nelements;i++) | |
519 | { | |
520 | if(cfg->dict[i]->nvalues<4) | |
521 | { | |
522 | log(LOG_ERR,"Not enough fields in record %d of cfg file. Got %d.",n, cfg->dict[i]->nvalues+1); | |
523 | done(RET_BAD_CFG); | |
524 | } else if(cfg->dict[i]->nvalues>5) | |
525 | { | |
526 | log(LOG_ERR,"Too many fields in record %d of cfg file. Got %d.",n, cfg->dict[i]->nvalues+1); | |
527 | done(RET_BAD_CFG); | |
528 | } | |
529 | ||
530 | hosts[n]=malloc(sizeof(monitor_host_t)); | |
531 | hosts[n]->name = strdup(cfg->dict[i]->name); | |
532 | hosts[n]->ping_interval = atoi (cfg->dict[i]->value[0]); | |
533 | hosts[n]->max_delay = atoi (cfg->dict[i]->value[1]); | |
534 | hosts[n]->upcmd = strdup(cfg->dict[i]->value[2]); | |
535 | hosts[n]->downcmd = strdup(cfg->dict[i]->value[3]); | |
536 | ||
537 | if(cfg->dict[i]->nvalues==4) | |
538 | { | |
539 | hosts[n]->down = 0; | |
540 | hosts[n]->up = 1; | |
541 | } else if(strcmp(cfg->dict[i]->value[4], "up")==0) | |
542 | { | |
543 | hosts[n]->down = 0; | |
544 | hosts[n]->up = 1; | |
545 | } else if(strcmp(cfg->dict[i]->value[4], "down")==0) | |
546 | { | |
547 | hosts[n]->down = 1; | |
548 | hosts[n]->up = 0; | |
549 | } else if(strcmp(cfg->dict[i]->value[4], "auto")==0) | |
550 | { | |
551 | hosts[n]->down = 1; | |
552 | hosts[n]->up = 1; | |
553 | } else if(strcmp(cfg->dict[i]->value[4], "none")==0) | |
554 | { | |
555 | hosts[n]->down = 0; | |
556 | hosts[n]->up = 0; | |
557 | } else | |
558 | { | |
559 | log(LOG_ERR,"Illegal value %s in record %n for startup condition.", cfg->dict[i]->value[4], n); | |
560 | done(RET_BAD_CFG); | |
561 | } | |
562 | hosts[n]->sentpackets = 0; | |
563 | hosts[n]->recvdpackets = 0; | |
564 | ||
565 | hosts[n]->socket = -1; | |
566 | hosts[n]->next = nil; | |
567 | if(n>0) | |
568 | hosts[n-1]->next=hosts[n]; | |
569 | (void)gettimeofday(&(hosts[n]->last_ping_received), (struct timezone *)NULL); | |
570 | ||
571 | n++; | |
572 | } | |
573 | } | |
574 | ||
575 | freecfg(cfg); | |
576 | ||
577 | if(n<=0) | |
578 | { | |
579 | log(LOG_ERR,"No hosts defined in cfg file, exiting."); | |
580 | done(RET_NO_HOSTS); | |
581 | } | |
582 | else | |
583 | log(LOG_DEBUG,"%d host(s) found in cfg file,", n); | |
584 | ||
585 | } | |
586 | ||
587 | static int gethostaddr(const char *name) | |
588 | { | |
589 | static int res; | |
590 | struct hostent *he; | |
591 | ||
592 | if((res=inet_addr(name))<0) | |
593 | { | |
594 | he=gethostbyname(name); | |
595 | if(!he) | |
596 | return -1; | |
597 | memcpy( &res , he->h_addr , he->h_length ); | |
598 | } | |
599 | return(res); | |
600 | } | |
601 | ||
602 | static void init_hosts(void) | |
603 | { | |
604 | monitor_host_t *p=hosts[0]; | |
605 | struct protoent *proto; | |
606 | int ok=0; | |
607 | ||
608 | if((proto=getprotobyname("icmp"))==nil) | |
609 | { | |
610 | log(LOG_ERR,"Unknown protocol: icmp. Exiting."); | |
611 | done(RET_INIT_ERROR); | |
612 | } | |
613 | ||
614 | while(p) | |
615 | { | |
616 | log(LOG_DEBUG,"resolving host %s", p->name); | |
617 | ||
618 | bzero(&p->dest,sizeof(p->dest)); | |
619 | p->dest.sin_family=AF_INET; | |
620 | if((p->dest.sin_addr.s_addr=gethostaddr(p->name))<=0) | |
621 | { | |
622 | log(LOG_ERR,"Can't resolve host. Skipping client %s.",p->name); | |
623 | p->socket=-1; | |
624 | } else | |
625 | { | |
626 | if((p->socket=socket(AF_INET,SOCK_RAW,proto->p_proto))<0) | |
627 | { | |
628 | log(LOG_ERR,"Can't create socket. Skipping client %s.",p->name); | |
629 | p->socket=-1; | |
630 | } else | |
631 | { | |
632 | if(ok==0) | |
633 | send_delay = p->ping_interval; | |
634 | else | |
635 | send_delay = gcd(send_delay, p->ping_interval); | |
636 | ok++; | |
637 | } | |
638 | } | |
639 | p=p->next; | |
640 | } | |
641 | ||
642 | if(!ok) | |
643 | { | |
644 | log(LOG_ERR,"No hosts left to process, exiting."); | |
645 | done(RET_NO_HOSTS); | |
646 | } | |
647 | } | |
648 | ||
649 | /* | |
650 | * tvsub -- | |
651 | * Subtract 2 timeval structs: out = out - in. Out is assumed to | |
652 | * be >= in. | |
653 | */ | |
654 | static void | |
655 | tvsub(register struct timeval *out, register struct timeval *in) | |
656 | { | |
657 | if ((out->tv_usec -= in->tv_usec) < 0) { | |
658 | --out->tv_sec; | |
659 | out->tv_usec += 1000000; | |
660 | } | |
661 | out->tv_sec -= in->tv_sec; | |
662 | } | |
663 | ||
664 | void done(int code) | |
665 | { | |
666 | logclose(); | |
667 | exit(code); | |
668 | } | |
669 | ||
670 | void start_daemon(void) | |
671 | { | |
672 | if(fork()) | |
673 | exit(0); | |
674 | ||
675 | chdir("/"); | |
676 | umask(0); | |
677 | (void) close(0); | |
678 | (void) close(1); | |
679 | (void) close(2); | |
680 | (void) open("/", O_RDONLY); | |
681 | (void) dup2(0, 1); | |
682 | (void) dup2(0, 2); | |
683 | setsid(); | |
684 | } | |
685 | ||
686 | static void logopen(void) | |
687 | { | |
688 | #if HAVE_OPENLOG | |
689 | if(isDaemon) | |
690 | openlog("icmpmonitor", LOG_PID| LOG_CONS|LOG_NOWAIT, LOG_USER); | |
691 | #else | |
692 | log(LOG_WARNING,"Compiled without syslog. Syslog can't be used."); | |
693 | #endif | |
694 | } | |
695 | ||
696 | static void logclose(void) | |
697 | { | |
698 | #if HAVE_CLOSELOG | |
699 | if(isDaemon) | |
700 | closelog(); | |
701 | #endif | |
702 | } | |
703 | ||
704 | /** | |
705 | * This function should be used as central logging facility. | |
706 | * 'type' argument should be one of following: | |
707 | * | |
708 | * LOG_EMERG system is unusable | |
709 | * LOG_ALERT action must be taken immediately | |
710 | * LOG_CRIT critical conditions | |
711 | * LOG_ERR error conditions | |
712 | * LOG_WARNING warning conditions | |
713 | * LOG_NOTICE normal but significant condition | |
714 | * LOG_INFO informational | |
715 | * LOG_DEBUG debug-level messages | |
716 | */ | |
717 | static void log(int type, char *format, ...) | |
718 | { | |
719 | va_list ap; | |
720 | ||
721 | #ifndef DEBUG | |
722 | if(type==LOG_DEBUG) | |
723 | return; | |
724 | #endif | |
725 | ||
726 | va_start(ap, format); | |
727 | ||
728 | if(isDaemon) | |
729 | { | |
730 | char buffer[MAX_LOG_MSG_SIZE]; | |
731 | ||
732 | #if HAVE_VSNPRINTF | |
733 | (void)vsnprintf(buffer, MAX_LOG_MSG_SIZE, format, ap); | |
734 | #else | |
735 | # if HAVE_VSPRINTF | |
736 | # warning "Using VSPRINTF. Buffer overflow could happen!" | |
737 | (void)vsprintf(buffer, format, ap); | |
738 | # else | |
739 | # error "Your standard libabry have neither vsnprintf nor vsprintf defined. One of them is reqired!" | |
740 | # endif | |
741 | #endif | |
742 | #if HAVE_SYSLOG | |
743 | syslog(type,buffer); | |
744 | #endif | |
745 | } else | |
746 | { | |
747 | (void) fprintf(stderr, "icmpmonitor[%d]:", (int)getpid()); | |
748 | (void) vfprintf(stderr, format, ap); | |
749 | (void) fprintf(stderr, "\n"); | |
750 | } | |
751 | va_end(ap); | |
752 | } | |
753 | ||
754 | static int gcd(int x, int y) | |
755 | { | |
756 | while(x!=y) | |
757 | { | |
758 | if(x<y) | |
759 | y-=x; | |
760 | else | |
761 | x-=y; | |
762 | } | |
763 | return x; | |
764 | } |