/* * SIPB service monitor. * * Nickolai Zeldovich * February 2002 */ #include #include #include "sipbmon.h" static const str confpath = "/afs/sipb.mit.edu/user/kolya/sipbmon/sipbmon.cf"; static int check_timeout = 30; static int check_period = 60; static int status_port = -1; static int webstat_port = 8080; static struct sm_service *service_head; struct service_type { str name; cbsv cb; }; static struct service_type services[] = { { "tcp", NULL }, { "www-toplevel", wrap (&check_www, "/") }, { "www-afs", wrap (&check_www, "/afs/sipb/") }, { "news-reader", wrap (&check_news) }, { "pksd", wrap (&check_pgp) }, { NULL, NULL }, }; static void service_done (struct sm_service *svc, bool up) { assert (svc->active); if (svc->notify_cb && svc->up != up) svc->notify_cb (svc, up); if (up) svc->errmsg = ""; if (svc->tcb) { timecb_remove (svc->tcb); svc->tcb = NULL; } if (svc->tcpconn) { tcpconn_abort (svc->tcpconn); svc->tcpconn = NULL; } if (svc->fd >= 0) { fdcb (svc->fd, selread, NULL); fdcb (svc->fd, selwrite, NULL); close (svc->fd); svc->fd = -1; } svc->up = up; svc->active = 0; } void service_up (struct sm_service *svc) { service_done (svc, true); } void service_down (struct sm_service *svc, str err) { svc->errmsg = err; service_done (svc, false); } static void service_timeout (struct sm_service *svc) { assert (svc->tcb != NULL); svc->tcb = NULL; service_down (svc, "request timed out"); } static void svc_write_done (struct sm_service *svc, cbv cb, bool ok) { if (ok) cb (); else service_down (svc, str ("write failed: ") << strerror (errno)); } void net_write (int fd, suio *s, cbb cb) { fdcb (fd, selwrite, NULL); if (s->resid ()) { int res = s->output (fd); if (res < 0) cb (false); } if (s->resid ()) fdcb (fd, selwrite, wrap (&net_write, fd, s, cb)); else cb (true); } void svc_write (struct sm_service *svc, str tosend, cbv cb) { net_write (svc->fd, strbuf (tosend).tosuio (), wrap (&svc_write_done, svc, cb)); } static void do_read (struct sm_service *svc, strbuf rb, cbbsb cb) { char buf[1024]; fdcb (svc->fd, selread, NULL); int cc = read (svc->fd, buf, sizeof (buf)); if (cc < 0 && errno != EAGAIN) { service_down (svc, str ("read failed: ") << strerror (errno)); return; } if (cc >= 0) { bool eof = (cc == 0); rb << str(buf, cc); if (cb (rb, eof)) fdcb (svc->fd, selread, wrap (&do_read, svc, rb, cb)); } } void svc_read (struct sm_service *svc, cbbsb cb) { fdcb (svc->fd, selread, wrap (&do_read, svc, strbuf (), cb)); } static void connected_cb (struct sm_service *svc, int s) { assert (svc->active); svc->tcpconn = NULL; if (s < 0) { service_down (svc, strerror (errno)); return; } svc->fd = s; if (svc->conn_cb) svc->conn_cb (svc); else service_up (svc); } static void start_check () { struct sm_service *svc = service_head; while (svc) { if (!svc->active) { svc->tcb = delaycb (check_timeout, wrap (&service_timeout, svc)); svc->tcpconn = tcpconn (svc->host, svc->port, wrap (connected_cb, svc)); svc->active = 1; } svc = svc->next; } delaycb (check_period, 0, wrap (&start_check)); } static int add_service (str name, str host, int port) { int i = 0; while (services[i].name) { if (services[i].name == name) { service_head = New sm_service (name, host, port, services[i].cb, wrap (¬ify_zephyr), service_head); return 1; } i++; } return 0; } static void load_config (str cf) { parseargs pa (cf); bool errors = false; int line; vec av; while (pa.getline (&av, &line)) { if (!strcasecmp (av[0], "recheck")) { if (av.size () != 2 || !convertint (av[1], &check_period)) { errors = true; warn << cf << ":" << line << ": usage: recheck \n"; } } else if (!strcasecmp (av[0], "timeout")) { if (av.size () != 2 || !convertint (av[1], &check_timeout)) { errors = true; warn << cf << ":" << line << ": usage: timeout \n"; } } else if (!strcasecmp (av[0], "statusport")) { if (av.size () != 2 || !convertint (av[1], &status_port)) { errors = true; warn << cf << ":" << line << ": usage: statusport \n"; } } else if (!strcasecmp (av[0], "service")) { str name, host; int port; if (av.size () != 4 || !convertint (av[3], &port)) { errors = true; warn << cf << ":" << line << ": usage: service \n"; } else { name = av[1]; host = av[2]; if (!add_service (name, host, port)) { errors = true; warn << cf << ":" << line << ": unknown service: " << name << "\n"; } } } else { errors = true; warn << cf << ":" << line << ": unknown directive: " << av[0] << "\n"; } } if (errors) fatal << "errors in config file\n"; } int main () { load_config (confpath); delaycb (0, 0, wrap (&start_check)); if (status_port > 0) status_init (status_port, service_head); if (webstat_port > 0) web_status_init (webstat_port, service_head); amain (); return 0; }