| 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 | } |