/* $Id: hostmon.c,v 1.13 2005/02/09 13:47:22 wessels Exp $ */

#define USE_RFC868 1

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include <signal.h>
#include <errno.h>
#include <syslog.h>
#include <fcntl.h>
#include <netdb.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/wait.h>

#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>

#define MY_MAXFD 256
#define HTTP_SERVER_PORT 8001
#define ICMP_RTT_HIST_SZ 200
#define ICMP_MAX_PKT_SZ 4096
#define N_MSGS 20
#define STRFTIME_FMT "%a, %d %b %Y %H:%M:%S %Z"

typedef struct {
    const char *name;
    struct sockaddr_in sa;
    struct {
	int status;
	time_t last_change;
	int up_period;
	int down_period;
	struct timeval last_sent;
	int pkts_sent;
	int pkts_recv;
	unsigned short last_sent_seq;
	unsigned short last_recv_seq;
	int rtt_hist[ICMP_RTT_HIST_SZ];
	int moved;
	struct {
	    int down_time;
	    char *email;
	    time_t sent;
	} notify;
	char *other_node_down;
    } icmp;
    struct {
	unsigned short port;
	int period;
	unsigned int val;
	time_t send_time;
	time_t recv_time;
    } rfc868;
    void *next;
} Host;

typedef struct {
    void (*read) (int, void *);
    void *read_data;
} FD;

enum {
    ICMP_UNKNOWN,
    ICMP_UP,
    ICMP_DOWN
};

Host *hostMatchAddr(struct sockaddr_in *sa);
void icmpRecv(int s, void *unused);
void icmpSetStatus(Host * h);
void icmpNotify(Host * h);

static const char *const ws = " \t\r\n";
static Host *Hosts = NULL;
static Host **HostsTail = &Hosts;
static FD fds[MY_MAXFD];
static struct timeval now;
static int icmp_sock = -1;
static int ident;
static char *msgs[N_MSGS];
static char myhostname[128];

int
in_cksum(unsigned short *ptr, int size)
{
    register long sum;
    unsigned short oddbyte;
    register unsigned short answer;
    sum = 0;
    while (size > 1) {
	sum += *ptr++;
	size -= 2;
    }
    if (size == 1) {
	oddbyte = 0;
	*((unsigned char *) &oddbyte) = *(unsigned char *) ptr;
	sum += oddbyte;
    }
    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    answer = ~sum;
    return (answer);
}

int
tvsub(struct timeval *t1, struct timeval *t2)
{
    return (t2->tv_sec - t1->tv_sec) * 1000000 + (t2->tv_usec - t1->tv_usec);
}

void
reaper(int sig)
{
    int status;
    pid_t pid;
    do {
	pid = waitpid(-1, &status, WNOHANG);
    } while (pid > 0);
}

const char *
icmpStatusStr(int status)
{
    if (ICMP_UP == status)
	return "Up";
    if (ICMP_DOWN == status)
	return "Down";
    return "Unknown";
}

const char *
icmpStatusColor(int status)
{
    if (ICMP_UP == status)
	return "#00ff00";
    if (ICMP_DOWN == status)
	return "#ff0000";
    return "#ffff00";
}

void
icmpSocketCreate(void)
{
    static struct protoent *proto;
    if ((proto = getprotobyname("icmp")) == 0) {
	syslog(LOG_ERR, "unknown protocol: icmp");
	exit(1);
    }
    if ((icmp_sock = socket(PF_INET, SOCK_RAW, proto->p_proto)) < 0) {
	syslog(LOG_ERR, "socket(PF_INET, SOCK_RAW, icmp): %s", strerror(errno));
	exit(1);
    }
    syslog(LOG_DEBUG, "ICMP socket is FD %d\n", icmp_sock);
    fds[icmp_sock].read = icmpRecv;
    fds[icmp_sock].read_data = NULL;
}

#define SIZE_ICMP_HDR   8
#define DEF_DATALEN     56
void
icmpSend(Host * h)
{
    static unsigned char send_pkt[ICMP_MAX_PKT_SZ];
    struct icmp *icp;
    int icmp_pktsize = DEF_DATALEN + SIZE_ICMP_HDR;
    int x;
    memset(send_pkt, '\0', sizeof(send_pkt));
    icp = (struct icmp *) send_pkt;
    icp->icmp_type = ICMP_ECHO;
    icp->icmp_code = 0;
    icp->icmp_cksum = 0;
    icp->icmp_id = ident;
    icp->icmp_seq = (++h->icmp.last_sent_seq);
    memcpy(send_pkt + SIZE_ICMP_HDR, &now, sizeof(now));
    h->icmp.last_sent = now;
    h->icmp.pkts_sent++;
    icp->icmp_cksum = in_cksum((unsigned short *) icp, icmp_pktsize);
    if (-1 == icmp_sock)
	icmpSocketCreate();
    syslog(LOG_DEBUG, "send %4d ICMP bytes  to  %s", icmp_pktsize, inet_ntoa(h->sa.sin_addr));
    x = sendto(icmp_sock,
	send_pkt,
	icmp_pktsize,
	0,
	(struct sockaddr *) &h->sa,
	sizeof(h->sa));
    if (x < 0)
	syslog(LOG_ERR, "sendto: %s: %s", inet_ntoa(h->sa.sin_addr), strerror(errno));
}

void
icmpRecv(int s, void *unused)
{
    static unsigned char recv_pkt[ICMP_MAX_PKT_SZ];
    static struct sockaddr_in from;
    int fromlen = sizeof(from);
    struct timeval tv;
    struct timeval tv1;
    struct icmp *icp;
    struct ip *ip;
    int iphdrlen;
    int n;
    int rtt;
    Host *h;
    assert(s == icmp_sock);
    n = recvfrom(s,
	recv_pkt,
	sizeof(recv_pkt),
	0,
	(struct sockaddr *) &from,
	&fromlen);
    gettimeofday(&tv, 0);
    ip = (struct ip *) recv_pkt;
#if defined(__osf__) && __STDC__==1
#if BYTE_ORDER == BIG_ENDIAN
    iphdrlen = (ip->ip_vhl >> 4) << 2;
#endif
#if BYTE_ORDER == LITTLE_ENDIAN
    iphdrlen = (ip->ip_vhl & 0xF) << 2;
#endif
#else
    iphdrlen = ip->ip_hl << 2;
#endif
    syslog(LOG_DEBUG, "recv %4d ICMP bytes from %s", n, inet_ntoa(from.sin_addr));
    if (n < iphdrlen + SIZE_ICMP_HDR) {
	syslog(LOG_ERR, "packet too short (%d bytes) from %s",
	    n, inet_ntoa(from.sin_addr));
	return;
    }
    n -= iphdrlen;
    icp = (struct icmp *) &recv_pkt[iphdrlen];
    if (icp->icmp_type != ICMP_ECHOREPLY)
	return;
    if (icp->icmp_id != ident)
	return;
    if ((h = hostMatchAddr(&from)) == NULL)
	return;
    memcpy(&tv1, &icp->icmp_data[0], sizeof(tv1));
    rtt = tvsub(&tv1, &tv);
    h->icmp.rtt_hist[0] = rtt;
    icmpSetStatus(h);
    h->icmp.last_recv_seq = icp->icmp_seq;
    memmove(&h->icmp.rtt_hist[1], &h->icmp.rtt_hist[0],
	(ICMP_RTT_HIST_SZ - 1) * sizeof(h->icmp.rtt_hist[0]));
    h->icmp.moved = 1;
}

double
icmpPktLoss(Host * h)
{
    int sent = 0;
    int recv = 0;
    int i;
    for (i = 1; i < ICMP_RTT_HIST_SZ; i++) {
	if (h->icmp.rtt_hist[i] < 0)
	    break;
	sent++;
	if (h->icmp.rtt_hist[i] > 0)
	    recv++;
    }
    if (0 == sent)
	return -1.0;
    return 1.0 - (double) recv / (double) sent;
}

int
icmpRttAvg(Host * h)
{
    int rttsum = 0;
    int recv = 0;
    int i;
    for (i = 1; i < ICMP_RTT_HIST_SZ; i++) {
	if (h->icmp.rtt_hist[i] < 0)
	    break;
	if (0 == h->icmp.rtt_hist[i])
	    continue;
	recv++;
	rttsum += h->icmp.rtt_hist[i];
    }
    if (0 == recv)
	return -100;
    return rttsum / recv;
}

void
icmpSetStatus(Host * h)
{
    char buf[256];
    char tbuf[256];
    int newstatus;
    int i = h->icmp.last_sent_seq - h->icmp.last_recv_seq;
    if (i > 20)
	newstatus = ICMP_DOWN;
    else if (0 == h->icmp.last_recv_seq)
	newstatus = ICMP_UNKNOWN;
    else if (i > 10)
	newstatus = ICMP_UNKNOWN;
    else
	newstatus = ICMP_UP;
    if (newstatus == h->icmp.status)
	return;
    if (msgs[N_MSGS - 1])
	free(msgs[N_MSGS - 1]);
    memmove(&msgs[1], &msgs[0], (N_MSGS - 1) * sizeof(msgs[0]));
    strftime(tbuf, 256, STRFTIME_FMT, localtime(&now.tv_sec));
    snprintf(buf, 256, "%s: %s changed from %s to %s\n",
	tbuf,
	h->name,
	icmpStatusStr(h->icmp.status),
	icmpStatusStr(newstatus));
    msgs[0] = strdup(buf);
    h->icmp.status = newstatus;
    h->icmp.last_change = now.tv_sec;
}

void
icmpNotify(Host * h)
{
    char cmd[256];
    FILE *fp;
    if (ICMP_DOWN != h->icmp.status)
	return;
    if (0 == h->icmp.notify.down_time)
	return;
    if (NULL == h->icmp.notify.email)
	return;
    if ((now.tv_sec - h->icmp.last_change) < h->icmp.notify.down_time)
	return;
    if (h->icmp.notify.sent > h->icmp.last_change)
	return;
    memset(cmd, '\0', 256);
    snprintf(cmd, 256, "/usr/sbin/sendmail %s", h->icmp.notify.email);
    fp = popen(cmd, "w");
    if (NULL == fp) {
	syslog(LOG_ERR, "popen: %s", strerror(errno));
	return;
    }
    fprintf(fp, "From: hostmon@%s\n", myhostname);
    fprintf(fp, "To: %s\n", h->icmp.notify.email);
    fprintf(fp, "Subject: %s is DOWN from %s\n", h->name, myhostname);
    fclose(fp);
    h->icmp.notify.sent = now.tv_sec;
}

int
parseHost(char *buf, void *data)
{
    Host *h = data;
    char *t;
    if (NULL == (t = strtok(buf, ws)))
	return 1;
    if (0 == strcmp(t, "}")) {
	int i;
	for (i = 0; i < ICMP_RTT_HIST_SZ; i++)
	    h->icmp.rtt_hist[i] = -1;
	*HostsTail = h;
	HostsTail = (Host **) & h->next;
	return 1;
    } else if (0 == strcmp(t, "ip-address")) {
	if (NULL == (t = strtok(NULL, ws)))
	    return 1;
	h->sa.sin_addr.s_addr = inet_addr(t);
    } else if (0 == strcmp(t, "icmp-up-period")) {
	if (NULL == (t = strtok(NULL, ws)))
	    return 1;
	h->icmp.up_period = atoi(t);
    } else if (0 == strcmp(t, "icmp-down-period")) {
	if (NULL == (t = strtok(NULL, ws)))
	    return 1;
	h->icmp.down_period = atoi(t);
    } else if (0 == strcmp(t, "icmp-notify-down-time")) {
	if (NULL == (t = strtok(NULL, ws)))
	    return 1;
	h->icmp.notify.down_time = atoi(t);
    } else if (0 == strcmp(t, "icmp-notify-email")) {
	if (NULL == (t = strtok(NULL, ws)))
	    return 1;
	h->icmp.notify.email = strdup(t);
    } else if (0 == strcmp(t, "if-other-node-down")) {
	if (NULL == (t = strtok(NULL, ws)))
	    return 1;
	h->icmp.other_node_down = strdup(t);
    } else if (0 == strcmp(t, "rfc868-port")) {
	if (NULL == (t = strtok(NULL, ws)))
	    return 1;
	h->rfc868.port = atoi(t);
    } else if (0 == strcmp(t, "rfc868-period")) {
	if (NULL == (t = strtok(NULL, ws)))
	    return 1;
	h->rfc868.period = atoi(t);
    }
    return 0;
}

int
parseConfig(const char *cf)
{
    FILE *fp = fopen(cf, "r");
    static char buf[512];
    char *t;
    int (*parseFunc) (char *, void *) = NULL;
    void *parseData = NULL;
    if (NULL == fp) {
	perror(cf);
	return 1;
    }
    while (fgets(buf, 512, fp)) {
	if (parseFunc) {
	    int x = parseFunc(buf, parseData);
	    if (1 == x)
		parseFunc = parseData = NULL;
	}
	if (NULL == (t = strtok(buf, ws)))
	    continue;;
	if (0 == strcmp(t, "host")) {
	    Host *h = calloc(1, sizeof(*h));
	    assert(h);
	    if (NULL == (t = strtok(NULL, ws))) {
		fprintf(stderr, "expected host name\n");
		break;
	    }
	    h->name = strdup(t);
	    if (NULL == (t = strtok(NULL, ws))) {
		fprintf(stderr, "expected open bracket, got NULL\n");
		break;
	    }
	    if (strcmp(t, "{")) {
		fprintf(stderr, "expected open bracket, got %s\n", t);
		break;
	    }
	    parseFunc = parseHost;
	    parseData = h;
	}
    }
    return 0;
}

Host *
hostMatchAddr(struct sockaddr_in * sa)
{
    Host *h;
    for (h = Hosts; h; h = h->next) {
	if (h->sa.sin_addr.s_addr == sa->sin_addr.s_addr)
	    return h;
    }
    return NULL;
}

Host *
hostMatchName(const char *name)
{
    Host *h;
    for (h = Hosts; h; h = h->next) {
	if (0 == strcasecmp(h->name, name))
	    return h;
    }
    return NULL;
}

int
hostNeedsPing(Host * h)
{
    time_t last = h->icmp.last_sent.tv_sec;
    time_t delta;
    if (h->icmp.last_sent_seq == h->icmp.last_recv_seq)
	delta = h->icmp.up_period;
    else
	delta = h->icmp.down_period;
    if (now.tv_sec > last + delta)
	return 1;
    return 0;
}

void
hostPing(Host * h)
{
    if (!h->icmp.moved)
	memmove(&h->icmp.rtt_hist[1], &h->icmp.rtt_hist[0],
	    (ICMP_RTT_HIST_SZ - 1) * sizeof(h->icmp.rtt_hist[0]));
    h->icmp.moved = 0;
    h->icmp.rtt_hist[0] = 0;
    icmpSetStatus(h);
    icmpNotify(h);
    icmpSend(h);
}

/* RFC868 SECTION */
#if USE_RFC868

static int rfc868_sock = -1;

static void
rfc868Send(Host * h)
{
    struct sockaddr_in S;
    static char buf[1];
    if (rfc868_sock < 0)
	return;
    if (h->rfc868.period < 1)
	return;
    memcpy(&S, &h->sa, sizeof(S));
    S.sin_port = htons(h->rfc868.port);
    S.sin_family = AF_INET;
    syslog(LOG_DEBUG, "send %4d TIME bytes  to  %s", 1, inet_ntoa(S.sin_addr));
    sendto(rfc868_sock,
	buf,
	1,
	0,
	(struct sockaddr *) &S,
	sizeof(S));
    h->rfc868.send_time = now.tv_sec;
}

static void
rfc868Recv(int s, void *unused)
{
    int x;
    static char buf[32];
    static struct sockaddr_in from;
    int fromlen = sizeof(from);
    unsigned int val;
    Host *h;
    assert(s == rfc868_sock);
    x = recvfrom(rfc868_sock,
	buf,
	32,
	0,
	(struct sockaddr *) &from,
	&fromlen);
    syslog(LOG_DEBUG, "recv %4d TIME bytes from %s", x, inet_ntoa(from.sin_addr));
    if (x < 4)
	return;
    h = hostMatchAddr(&from);
    if (NULL == h)
	return;
    memcpy(&val, buf, 4);
    h->rfc868.val = ntohl(val);
    h->rfc868.recv_time = now.tv_sec;
}

static void
rfc868Init(void)
{
    struct sockaddr_in S;
    int x;
    rfc868_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (rfc868_sock < 0) {
	syslog(LOG_ALERT, "RFC868 socket: %s", strerror(errno));
	return;
    }
    syslog(LOG_DEBUG, "TIME socket is FD %d\n", rfc868_sock);
    memset(&S, '\0', sizeof(S));
    S.sin_family = AF_INET;
    S.sin_addr.s_addr = INADDR_ANY;
    S.sin_port = 0;
    x = bind(rfc868_sock, (struct sockaddr *) &S, sizeof(S));
    if (x < 0) {
	syslog(LOG_ALERT, "bind: %s", strerror(errno));
	close(rfc868_sock);
	rfc868_sock = -1;
	return;
    }
    syslog(LOG_DEBUG, "TIME socket bound to %s\n", inet_ntoa(S.sin_addr));
    fds[rfc868_sock].read = rfc868Recv;
    fds[rfc868_sock].read_data = NULL;
}

static int
rfc868NeedsQuery(Host * h)
{
    if (h->rfc868.period < 1)
	return 0;
    if ((now.tv_sec - h->rfc868.send_time) > h->rfc868.period)
	return 1;
    return 0;
}

static char *
rfc868Asctime(Host * h)
{
    time_t when;
    struct tm *lt;
    static char when_str[64];
    if (0 == h->rfc868.recv_time)
	return "&nbsp;";
    if (now.tv_sec - h->rfc868.recv_time > 300)
	return "unknown";
    if (now.tv_sec - h->rfc868.recv_time > 3600)
	return "&nbsp;";
    when = h->rfc868.val - (unsigned int) 2208988800 + (now.tv_sec - h->rfc868.recv_time);
    lt = localtime(&when);
    assert(NULL != lt);
    strftime(when_str, 64, "%v %T", lt);
    return when_str;
}

static char *
rfc868Color(Host * h)
{
    time_t when;
    if (0 == h->rfc868.recv_time)
	return "#ff0000;";
    when = h->rfc868.val - (unsigned int) 2208988800 + (now.tv_sec - h->rfc868.recv_time);
    if (abs(now.tv_sec - when) > 30)
	return "#ff0000";
    if (abs(now.tv_sec - when) > 3)
	return "#ffff00";
    return "#000000";
}
#endif

/* ============== */

int
otherNodeIsDown(Host *h)
{
    Host *other;
    if (NULL == h->icmp.other_node_down)
	return 1;
    other = hostMatchName(h->icmp.other_node_down);
    if (NULL == other)
	return 1;
    if (ICMP_DOWN == other->icmp.status)
	return 1;
    return 0;
}

void
checkHosts(void)
{
    Host *h;
    for (h = Hosts; h; h = h->next) {
	if (!otherNodeIsDown(h))
	    continue;
	if (hostNeedsPing(h)) {
	    hostPing(h);
	    break;
	}
#if USE_RFC868
	if (rfc868NeedsQuery(h)) {
	    rfc868Send(h);
	    break;
	}
#endif
    }
}

void
checkSockets(void)
{
    fd_set R;
    struct timeval to;
    int x;
    int maxx = 0;
    to.tv_sec = 1;
    to.tv_usec = 0;
    FD_ZERO(&R);
    for (x = 0; x < MY_MAXFD; x++) {
	if (NULL == fds[x].read)
	    continue;
	FD_SET(x, &R);
	if (x > maxx)
	    maxx = x;
    }
    x = select(maxx + 1, &R, NULL, NULL, &to);
    if (0 == x)
	return;
    if (x < 0) {
	if (EINTR != errno)
	    perror("select");
	return;
    }
    for (x = 0; x < MY_MAXFD; x++) {
	if (NULL == fds[x].read)
	    continue;
	if (!FD_ISSET(x, &R))
	    continue;
	fds[x].read(x, fds[x].read_data);
    }
}

void
run(void)
{
    for (;;) {
	gettimeofday(&now, 0);
	checkHosts();
	checkSockets();
    }
}

void
htmlOutput(int s)
{
    char buf[1024];
    int x;
    Host *h;
    read(s, buf, 1024);
    x = snprintf(buf, 1024,
	"HTTP/1.0 200 Ok\r\n"
	"Cache-control: no-cache\r\n"
	"Content-type: text/html\r\n"
	"\r\n");
    write(s, buf, x);
    x = snprintf(buf, 1024,
	"<head>\n"
	"<meta http-equiv=\"refresh\" content=\"30\">\n"
	"</head>\n"
	"<body>\n");
    write(s, buf, x);
    x = snprintf(buf, 1024,
	"<p>\n"
	"<table border=\"1\" cellpadding=\"5\">\n");
    write(s, buf, x);
    x = snprintf(buf, 1024,
	"<tr>\n"
	"<th rowspan=\"2\">Host</th>\n"
	"<th colspan=\"3\">ICMP</th>\n"
	"<th rowspan=\"2\">Time</th>\n"
	"</tr>\n");
    write(s, buf, x);
    x = snprintf(buf, 1024,
	"<tr>\n"
	"<th>Status</th>\n"
	"<th>RTT (msec)</th>\n"
	"<th>Loss (%%)</th>\n"
	"</tr>\n");
    write(s, buf, x);
    for (h = Hosts; h; h = h->next) {
	if (!otherNodeIsDown(h))
	    continue;
	x = snprintf(buf, 1024,
	    "<tr>\n"
	    "<td>%s</td>\n"
	    "<td align=\"center\"><font color=\"%s\">%s</font></td>\n"
	    "<td align=\"right\">%d</td>\n"
	    "<td align=\"right\">%4.1f</td>\n"
	    "<td align=\"center\"><small><tt><font color=\"%s\">%s</font></tt></small></td>\n"
	    "</tr>\n",
	    h->name,
	    icmpStatusColor(h->icmp.status),
	    icmpStatusStr(h->icmp.status),
	    icmpRttAvg(h) / 1000,
	    100.0 * icmpPktLoss(h),
#if USE_RFC868
	    rfc868Color(h),
	    rfc868Asctime(h));
#else
	    "", "");
#endif
	write(s, buf, x);
    }
    x = snprintf(buf, 1024,
	"</table>\n");
    write(s, buf, x);
    x = snprintf(buf, 1024,
	"\n"
	"<p><small><pre>\n");
    write(s, buf, x);
    for (x = 0; x < N_MSGS; x++)
	if (msgs[x])
	    write(s, msgs[x], strlen(msgs[x]));
    x = snprintf(buf, 1024,
	"</pre></small>\n"
	"</body>\n");
    write(s, buf, x);
}

void
httpServerAccept(int fd, void *unused)
{
    int s;
    s = accept(fd, NULL, NULL);
    if (s < 0) {
	perror("accept");
	return;
    }
    if (fork()) {
	/* parent */
	struct sigaction sa;
	memset(&sa, '\0', sizeof(sa));
	sa.sa_handler = reaper;
	sa.sa_flags = SA_NODEFER;
	sigemptyset(&sa.sa_mask);
	if (sigaction(SIGCHLD, &sa, NULL) < 0)
	    perror("sigaction SIGCHLD");
	close(s);
	return;
    }
    htmlOutput(s);
    close(s);
    _exit(0);
}

void
httpServerCreate(void)
{
    int s;
    int x;
    struct sockaddr_in S;
    s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (s < 0) {
	perror("socket");
	return;
    }
    x = 1;
    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)) < 0)
	perror("setsockopt SO_REUSEADDR");
    memset(&S, '\0', sizeof(S));
    S.sin_family = AF_INET;
    S.sin_addr.s_addr = INADDR_ANY;
    S.sin_port = htons(HTTP_SERVER_PORT);
    while (bind(s, (struct sockaddr *) &S, sizeof(S)) < 0) {
	static int count = 0;
	fprintf(stderr, "[%d] ", ++count);
	perror("bind");
	if (count == 120)
	    break;
	sleep(1 << count);
    }
    listen(s, 10);
    fds[s].read = httpServerAccept;
    fds[s].read_data = NULL;
}

void
writePid(void)
{
    FILE *fp = fopen("/var/run/hostmon.pid", "w");
    if (NULL == fp)
	return;
    fprintf(fp, "%d\n", (int) getpid());
    fclose(fp);
}

void
daemonize(void)
{
    pid_t pid;
    int i;
    if ((pid = fork()) < 0)
	syslog(LOG_ALERT, "fork failed: %s", strerror(errno));
    else if (pid > 0)
	exit(0);
    if (setsid() < 0)
	syslog(LOG_ALERT, "setsid failed: %s", strerror(errno));
#ifdef TIOCNOTTY
    if ((i = open("/dev/tty", O_RDWR)) >= 0) {
	ioctl(i, TIOCNOTTY, NULL);
	close(i);
    }
#endif
    i = open("/dev/null", O_RDWR);
    dup2(i, 0);
    dup2(i, 1);
    dup2(i, 2);
    for (i = 3; i < MY_MAXFD; i++)
	close(i);
}

int
main(int argc, char *argv[])
{
    char *config = "/usr/local/etc/hostmon.conf";
    if (argc > 1)
	config = strdup(argv[1]);
    if (parseConfig(config))
	return 1;
    memset(msgs, '\0', sizeof(msgs));
    openlog(argv[0], LOG_PID | LOG_NDELAY, LOG_DAEMON);
    daemonize();
    /*
     * need to reopen syslog because paranoid daemonize() closes all
     * FDs
     */
    closelog();
    openlog(argv[0], LOG_PID | LOG_NDELAY, LOG_DAEMON);
    writePid();
    ident = getpid() & 0xffff;
    gethostname(myhostname, 128);
    strtok(myhostname, ".");
    httpServerCreate();
#if USE_RFC868
    rfc868Init();
#endif
    run();
    return 0;
}
