/*
- * Monitor hosts using ICMP "echo" and notify when down.
- * TODO: Write a better description.
+ * ICMPmonitor
+ *
+ * Monitors hosts using ICMP 'echo', executing a user-specified command
+ * whenever hosts change state between responsive and unresponsive.
*
* © 2019 Aaron Taylor <ataylor at subgeniuskitty dot com>
* © 1999 Vadim Zaliva <lord@crocodile.org>
* © 1989 The Regents of the University of California & Mike Muuss
+ *
* See LICENSE file for copyright and license details.
*/
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <errno.h>
+
#include "iniparser/iniparser.h"
+#define VERSION 2
+
#define MAXPACKETSIZE (65536 - 60 - 8) /* TODO: What are the magic numbers? */
#define DEFAULTDATALEN (64 - 8) /* TODO: What are the magic numbers? */
char * name;
int ping_interval;
int max_delay;
- char * upcmd;
- char * downcmd;
+ char * up_cmd;
+ char * down_cmd;
/* Calculated values */
int socket;
struct timeval last_ping_received;
struct timeval last_ping_sent;
- bool hostup;
+ bool host_up;
struct sockaddr_in dest;
- unsigned int sentpackets;
- unsigned int recvdpackets;
/* Linked list */
struct monitor_host * next;
/* Globals */
/* Since the program is based around signals, a linked list of hosts is maintained here. */
- static struct monitor_host ** hosts = NULL;
+ static struct monitor_host * hosts = NULL;
+ static int send_delay = 1;
/* Set by command line flags. */
- static bool verbose = false;
- static bool retry_down_cmd = false;
- /* TODO: Get rid of this global. */
- static int send_delay = 1;
+ static bool verbose = false;
+ static bool retry_down_cmd = false;
/*
* Checksum routine for Internet Protocol family headers
static void
pinger(int ignore)
{
- int cc, i;
+ int i;
struct icmp * icp;
- struct monitor_host * p;
+ struct monitor_host * p = hosts;
u_char outpack[MAXPACKETSIZE];
- p = hosts[0];
while (p) {
if (p->socket != -1) {
struct timeval now;
tv_sub(&now, &p->last_ping_received);
if (now.tv_sec > (p->max_delay + p->ping_interval)) {
- if ((p->hostup) || retry_down_cmd) {
+ if ((p->host_up) || retry_down_cmd) {
if (verbose) printf("INFO: Host %s stopped responding. Executing DOWN command.\n", p->name);
- p->hostup = false;
+ p->host_up = false;
if (!fork()) {
- system(p->downcmd);
+ system(p->down_cmd);
exit(EXIT_SUCCESS);
} else {
wait(NULL);
gettimeofday((struct timeval *) &outpack[8], (struct timezone *) NULL);
- cc = DEFAULTDATALEN + 8; /* skips ICMP portion */
+ int cc = DEFAULTDATALEN + 8; /* skips ICMP portion */
/* compute ICMP checksum */
icp->icmp_cksum = in_cksum((unsigned short *) icp, cc);
if (i < 0 || i != cc) {
if (i<0) fprintf(stderr, "WARN: Failed sending ICMP packet to %s.\n", p->name);
}
- p->sentpackets++;
}
}
p = p->next;
}
if (icmp->icmp_type == ICMP_ECHOREPLY && icmp->icmp_id == (getpid() & 0xFFFF) && icmp->icmp_seq == p->socket) {
- p->recvdpackets++;
memcpy(&p->last_ping_received, &tv, sizeof(tv));
delay = tv.tv_sec * 1000 + (tv.tv_usec / 1000);
if (verbose) printf("INFO: Got ICMP reply from %s in %d ms.\n", p->name, delay);
- if (!p->hostup) {
+ if (!p->host_up) {
if (verbose) printf("INFO: Host %s started responding. Executing UP command.\n", p->name);
- p->hostup = true;
+ p->host_up = true;
if (!fork()) {
- system(p->upcmd);
+ system(p->up_cmd);
exit(EXIT_SUCCESS);
} else {
wait(NULL);
struct monitor_host * p;
while (1) {
- p = hosts[0];
+ p = hosts;
FD_ZERO(&rfds);
while (p) {
if (p->socket != -1) {
- if (p->socket > maxd) maxd=p->socket;
+ if (p->socket > maxd) maxd = p->socket;
FD_SET(p->socket, &rfds);
}
p = p->next;
/* Intentionally empty. We arrive here when interrupted by a signal. No action should be taken. */
} else {
if (retval > 0) {
- p = hosts[0];
+ p = hosts;
while (p) {
if (p->socket!=-1 && FD_ISSET(p->socket, &rfds)) read_icmp_data(p);
p = p->next;
}
}
-/*
- * Parses `config_file` and creates relevant entries under the global variable `hosts`.
- */
static void
parse_config(const char * conf_file)
{
exit(EXIT_FAILURE);
}
- hosts = malloc(sizeof(struct monitor_host *) * host_count);
+ struct monitor_host * host_list_end = NULL;
for (int i=0; i < host_count; i++) {
- /* Allocate a buffer large enough to hold the full 'section:key' string. */
+ /* Allocate a reusable buffer large enough to hold the full 'section:key' string. */
int section_len = strlen(iniparser_getsecname(conf, i));
char * key_buf = malloc(section_len + 1 + MAXCONFKEYLEN + 1);
strcpy(key_buf, iniparser_getsecname(conf, i));
key_buf[section_len++] = ':';
- hosts[i] = malloc(sizeof(struct monitor_host));
- for (int k=0; k<6; k++) {
- key_buf[section_len] = '\0'; /* Reuse the section name and colon on each pass through this loop. */
- switch (k) {
- case 0:
- strncat(key_buf, "host", MAXCONFKEYLEN);
- hosts[i]->name = strdup(iniparser_getstring(conf, key_buf, NULL));
- break;
- case 1:
- strncat(key_buf, "interval", MAXCONFKEYLEN);
- hosts[i]->ping_interval = iniparser_getint(conf, key_buf, -1);
- break;
- case 2:
- strncat(key_buf, "max_delay", MAXCONFKEYLEN);
- hosts[i]->max_delay = iniparser_getint(conf, key_buf, -1);
- break;
- case 3:
- strncat(key_buf, "up_cmd", MAXCONFKEYLEN);
- hosts[i]->upcmd = strdup(iniparser_getstring(conf, key_buf, NULL));
- break;
- case 4:
- strncat(key_buf, "down_cmd", MAXCONFKEYLEN);
- hosts[i]->downcmd = strdup(iniparser_getstring(conf, key_buf, NULL));
- break;
- case 5:
- /* TODO: Parse for up/down/auto in start_condition. */
- /* TODO: Do a host up/down check if necessary. */
- hosts[i]->hostup = true;
- break;
- }
- }
- /* TODO: Do I want to make any checks for up_cmd, down_cmd, and start_condition? */
- if (hosts[i]->name == NULL || hosts[i]->ping_interval == -1 || hosts[i]->max_delay == -1) {
+ struct monitor_host * cur_host = malloc(sizeof(struct monitor_host));
+
+ key_buf[section_len] = '\0';
+ strncat(key_buf, "host", MAXCONFKEYLEN);
+ cur_host->name = strdup(iniparser_getstring(conf, key_buf, NULL));
+
+ key_buf[section_len] = '\0';
+ strncat(key_buf, "interval", MAXCONFKEYLEN);
+ cur_host->ping_interval = iniparser_getint(conf, key_buf, -1);
+
+ key_buf[section_len] = '\0';
+ strncat(key_buf, "max_delay", MAXCONFKEYLEN);
+ cur_host->max_delay = iniparser_getint(conf, key_buf, -1);
+
+ key_buf[section_len] = '\0';
+ strncat(key_buf, "up_cmd", MAXCONFKEYLEN);
+ cur_host->up_cmd = strdup(iniparser_getstring(conf, key_buf, NULL));
+
+ key_buf[section_len] = '\0';
+ strncat(key_buf, "down_cmd", MAXCONFKEYLEN);
+ cur_host->down_cmd = strdup(iniparser_getstring(conf, key_buf, NULL));
+
+ key_buf[section_len] = '\0';
+ /* TODO: Parse for up/down/auto in start_condition. */
+ /* TODO: Do a host up/down check if necessary. */
+ cur_host->host_up = true;
+
+ /* TODO: Do I want to make any checks for start_condition? */
+ if (cur_host->name == NULL || cur_host->ping_interval == -1 || cur_host->max_delay == -1) {
fprintf(stderr, "ERROR: Problems parsing section %s.\n", iniparser_getsecname(conf, i));
exit(EXIT_FAILURE);
}
- hosts[i]->sentpackets = 0;
- hosts[i]->recvdpackets = 0;
- hosts[i]->socket = -1;
- hosts[i]->next = NULL;
- if (i>0) hosts[i-1]->next = hosts[i];
- gettimeofday(&(hosts[i]->last_ping_received), (struct timezone *) NULL);
+ cur_host->socket = -1;
+ cur_host->next = NULL;
+ gettimeofday(&(cur_host->last_ping_received), (struct timezone *) NULL);
+
+ if (hosts == NULL) {
+ hosts = cur_host;
+ host_list_end = cur_host;
+ } else {
+ host_list_end->next = cur_host;
+ host_list_end = cur_host;
+ }
+
+ free(key_buf);
}
iniparser_freedict(conf);
}
get_host_addr(const char * name)
{
static int res;
- struct hostent * he;
+ struct hostent * host_ent;
if ((res = inet_addr(name)) < 0) {
- he = gethostbyname(name);
- if (!he) return -1;
- memcpy(&res, he->h_addr, he->h_length);
+ host_ent = gethostbyname(name);
+ if (!host_ent) return -1;
+ memcpy(&res, host_ent->h_addr, host_ent->h_length);
}
return(res);
}
static void
init_hosts(void)
{
- struct monitor_host * p = hosts[0];
+ struct monitor_host * p = hosts;
struct protoent * proto;
int ok = 0;
}
void
-print_usage(void)
+print_usage(char ** argv)
{
- fprintf(stderr,"Usage: icmpmonitor [-v] [-r] [-f cfgfile]\n");
+ printf( "ICMPmonitor v%d (www.subgeniuskitty.com)\n"
+ "Usage: %s [-h] [-v] [-r] -f <file>\n"
+ " -v Verbose mode. Prints message for each packet sent and received.\n"
+ " -r Repeat down_cmd every time a host fails to respond to a packet.\n"
+ " Note: Default behavior executes down_cmd only once, resetting once the host is back up.\n"
+ " -h Help (prints this message)\n"
+ " -f <file> Specify a configuration file.\n"
+ , VERSION, argv[0]);
}
void
parse_params(int argc, char ** argv)
{
int param;
- while ((param = getopt(argc, argv, "rvf:")) != -1) {
+ while ((param = getopt(argc, argv, "hrvf:")) != -1) {
switch(param) {
case 'v':
verbose = true;
case 'f':
parse_config(optarg);
break;
+ case 'h':
default:
- print_usage();
+ print_usage(argv);
exit(EXIT_FAILURE);
+ break;
}
}
if (hosts == NULL) {