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