int main(int argc, char *argv[]) { char *msg; int argi; struct sigaction sa; char *exthandler = NULL; char *extids = NULL; char *processor = NULL; struct sockaddr_un ctlsockaddr; int ctlsocket; /* Handle program options. */ for (argi = 1; (argi < argc); argi++) { if (strcmp(argv[argi], "--debug") == 0) { debug = 1; } else if (argnmatch(argv[argi], "--rrddir=")) { char *p = strchr(argv[argi], '='); rrddir = strdup(p+1); } else if (argnmatch(argv[argi], "--extra-script=")) { char *p = strchr(argv[argi], '='); exthandler = strdup(p+1); } else if (argnmatch(argv[argi], "--extra-tests=")) { char *p = strchr(argv[argi], '='); extids = strdup(p+1); } else if (argnmatch(argv[argi], "--processor=")) { char *p = strchr(argv[argi], '='); processor = strdup(p+1); } else if (strcmp(argv[argi], "--no-cache") == 0) { use_rrd_cache = 0; } else if (net_worker_option(argv[argi])) { /* Handled in the subroutine */ } } save_errbuf = 0; if ((rrddir == NULL) && xgetenv("XYMONRRDS")) { rrddir = strdup(xgetenv("XYMONRRDS")); } if (exthandler && extids) setup_exthandler(exthandler, extids); /* Do the network stuff if needed */ net_worker_run(ST_RRD, LOC_STICKY, update_locator_hostdata); setup_signalhandler("xymond_rrd"); memset(&sa, 0, sizeof(sa)); sa.sa_handler = sig_handler; sigaction(SIGHUP, &sa, NULL); sigaction(SIGCHLD, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); signal(SIGPIPE, SIG_DFL); /* Setup the control socket that receives cache-flush commands */ memset(&ctlsockaddr, 0, sizeof(ctlsockaddr)); sprintf(ctlsockaddr.sun_path, "%s/rrdctl.%d", xgetenv("XYMONTMP"), getpid()); unlink(ctlsockaddr.sun_path); /* In case it was accidentally left behind */ ctlsockaddr.sun_family = AF_UNIX; ctlsocket = socket(AF_UNIX, SOCK_DGRAM, 0); if (ctlsocket == -1) { errprintf("Cannot create cache-control socket (%s)\n", strerror(errno)); return 1; } fcntl(ctlsocket, F_SETFL, O_NONBLOCK); if (bind(ctlsocket, (struct sockaddr *)&ctlsockaddr, sizeof(ctlsockaddr)) == -1) { errprintf("Cannot bind to cache-control socket (%s)\n", strerror(errno)); return 1; } /* Linux obeys filesystem permissions on the socket file, so make it world-accessible */ if (chmod(ctlsockaddr.sun_path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) == -1) { errprintf("Setting permissions on cache-control socket failed: %s\n", strerror(errno)); } /* Load the RRD definitions */ load_rrddefs(); /* If we are passing data to an external processor, create the pipe to it */ setup_extprocessor(processor); running = 1; while (running) { char *eoln, *restofmsg = NULL; char *metadata[MAX_META+1]; int metacount; char *p; char *hostname = NULL, *testname = NULL, *sender = NULL, *classname = NULL, *pagepaths = NULL; xymonrrd_t *ldef = NULL; time_t tstamp; int childstat; ssize_t n; char ctlbuf[PATH_MAX]; int gotcachectlmessage; time_t now; /* See if we have any cache-control messages pending */ do { n = recv(ctlsocket, ctlbuf, sizeof(ctlbuf), 0); gotcachectlmessage = (n > 0); if (gotcachectlmessage) { /* We have a control message */ char *bol, *eol; ctlbuf[n] = '\0'; bol = ctlbuf; do { eol = strchr(bol, '\n'); if (eol) *eol = '\0'; rrdcacheflushhost(bol); if (eol) { bol = eol+1; } else bol = NULL; } while (bol && *bol); } } while (gotcachectlmessage); /* Get next message */ msg = get_xymond_message(C_LAST, argv[0], &seq, NULL); if (msg == NULL) { running = 0; continue; } now = gettimer(); if (reloadtime < now) { /* Reload configuration files */ load_hostnames(xgetenv("HOSTSCFG"), NULL, get_fqdn()); load_client_config(NULL); reloadtime = now + 600; } /* Split the message in the first line (with meta-data), and the rest */ eoln = strchr(msg, '\n'); if (eoln) { *eoln = '\0'; restofmsg = eoln+1; } /* Parse the meta-data */ metacount = 0; memset(&metadata, 0, sizeof(metadata)); p = gettok(msg, "|"); while (p && (metacount < MAX_META)) { metadata[metacount++] = p; p = gettok(NULL, "|"); } metadata[metacount] = NULL; if ((metacount >= 14) && (strncmp(metadata[0], "@@status", 8) == 0)) { /* * @@status|timestamp|sender|origin|hostname|testname|expiretime|color|testflags|\ * prevcolor|changetime|ackexpiretime|ackmessage|disableexpiretime|disablemessage|\ * clienttstamp|flapping|classname|pagepaths */ int color = parse_color(metadata[7]); switch (color) { case COL_GREEN: case COL_YELLOW: case COL_RED: case COL_BLUE: /* Blue is OK, because it only arrives here when an update is sent */ tstamp = atoi(metadata[1]); sender = metadata[2]; hostname = metadata[4]; testname = metadata[5]; classname = (metadata[17] ? metadata[17] : ""); pagepaths = (metadata[18] ? metadata[18] : ""); ldef = find_xymon_rrd(testname, metadata[8]); update_rrd(hostname, testname, restofmsg, tstamp, sender, ldef, classname, pagepaths); break; default: /* Ignore reports with purple, blue or clear - they have no data we want. */ break; } } else if ((metacount > 5) && (strncmp(metadata[0], "@@data", 6) == 0)) { /* @@data|timestamp|sender|origin|hostname|testname|classname|pagepaths */ tstamp = atoi(metadata[1]); sender = metadata[2]; hostname = metadata[4]; testname = metadata[5]; classname = (metadata[6] ? metadata[6] : ""); pagepaths = (metadata[7] ? metadata[7] : ""); ldef = find_xymon_rrd(testname, ""); update_rrd(hostname, testname, restofmsg, tstamp, sender, ldef, classname, pagepaths); } else if (strncmp(metadata[0], "@@shutdown", 10) == 0) { running = 0; continue; } else if (strncmp(metadata[0], "@@idle", 6) == 0) { /* Ignored */ continue; } else if (strncmp(metadata[0], "@@logrotate", 11) == 0) { char *fn = xgetenv("XYMONCHANNEL_LOGFILENAME"); if (fn && strlen(fn)) { freopen(fn, "a", stdout); freopen(fn, "a", stderr); } continue; } else if (strncmp(metadata[0], "@@reload", 8) == 0) { reloadtime = 0; } else if ((metacount > 3) && (strncmp(metadata[0], "@@drophost", 10) == 0)) { char hostdir[PATH_MAX]; hostname = metadata[3]; MEMDEFINE(hostdir); sprintf(hostdir, "%s/%s", rrddir, hostname); dropdirectory(hostdir, 1); MEMUNDEFINE(hostdir); } else if ((metacount > 4) && (strncmp(metadata[0], "@@droptest", 10) == 0)) { /* * Not implemented. Mappings of testnames -> rrd files is * too complex, so on the rare occasion that a single test * is deleted, they will have to delete the rrd files themselves. */ } else if ((metacount > 4) && (strncmp(metadata[0], "@@renamehost", 12) == 0)) { char oldhostdir[PATH_MAX]; char newhostdir[PATH_MAX]; char *newhostname; MEMDEFINE(oldhostdir); MEMDEFINE(newhostdir); hostname = metadata[3]; newhostname = metadata[4]; sprintf(oldhostdir, "%s/%s", rrddir, hostname); sprintf(newhostdir, "%s/%s", rrddir, newhostname); rename(oldhostdir, newhostdir); if (net_worker_locatorbased()) locator_rename_host(hostname, newhostname, ST_RRD); MEMUNDEFINE(newhostdir); MEMUNDEFINE(oldhostdir); } else if ((metacount > 5) && (strncmp(metadata[0], "@@renametest", 12) == 0)) { /* Not implemented. See "droptest". */ } /* * We fork a subprocess when processing drophost requests. * Pickup any finished child processes to avoid zombies */ while (wait3(&childstat, WNOHANG, NULL) > 0) ; } /* Flush all cached updates to disk */ errprintf("Shutting down, flushing cached updates to disk\n"); rrdcacheflushall(); errprintf("Cache flush completed\n"); /* Close the external processor */ shutdown_extprocessor(); /* Close the control socket */ close(ctlsocket); unlink(ctlsockaddr.sun_path); return 0; }
int main(int argc, char *argv[]) { char *msg; int running; int argi, seq; int recentperiod = 3600; int maxrecentcount = 5; int logdirfull = 0; int minlogspace = 5; struct sigaction sa; /* Handle program options. */ for (argi = 1; (argi < argc); argi++) { if (argnmatch(argv[argi], "--logdir=")) { clientlogdir = strchr(argv[argi], '=')+1; } else if (argnmatch(argv[argi], "--recent-period=")) { char *p = strchr(argv[argi], '='); recentperiod = 60*atoi(p+1); } else if (argnmatch(argv[argi], "--recent-count=")) { char *p = strchr(argv[argi], '='); maxrecentcount = atoi(p+1); } else if (argnmatch(argv[argi], "--minimum-free=")) { minlogspace = atoi(strchr(argv[argi], '=')+1); } else if (strcmp(argv[argi], "--debug") == 0) { /* * A global "debug" variable is available. If * it is set, then "dbgprintf()" outputs debug messages. */ debug = 1; } else if (net_worker_option(argv[argi])) { /* Handled in the subroutine */ } } if (clientlogdir == NULL) clientlogdir = xgetenv("CLIENTLOGS"); if (clientlogdir == NULL) { clientlogdir = (char *)malloc(strlen(xgetenv("XYMONVAR")) + 10); sprintf(clientlogdir, "%s/hostdata", xgetenv("XYMONVAR")); } save_errbuf = 0; /* Do the network stuff if needed */ net_worker_run(ST_HOSTDATA, LOC_STICKY, update_locator_hostdata); setup_signalhandler("xymond_hostdata"); memset(&sa, 0, sizeof(sa)); sa.sa_handler = sig_handler; signal(SIGCHLD, SIG_IGN); sigaction(SIGHUP, &sa, NULL); signal(SIGPIPE, SIG_DFL); savetimes = xtreeNew(strcasecmp); running = 1; while (running) { char *eoln, *restofmsg, *p; char *metadata[MAX_META+1]; int metacount; msg = get_xymond_message(C_CLICHG, "xymond_hostdata", &seq, NULL); if (msg == NULL) { /* * get_xymond_message will return NULL if xymond_channel closes * the input pipe. We should shutdown when that happens. */ running = 0; continue; } if (nextfscheck < gettimer()) { logdirfull = (chkfreespace(clientlogdir, minlogspace, minlogspace) != 0); if (logdirfull) errprintf("Hostdata directory %s has less than %d%% free space - disabling save of data for 5 minutes\n", clientlogdir, minlogspace); nextfscheck = gettimer() + 300; } /* Split the message in the first line (with meta-data), and the rest */ eoln = strchr(msg, '\n'); if (eoln) { *eoln = '\0'; restofmsg = eoln+1; } else { restofmsg = ""; } metacount = 0; memset(&metadata, 0, sizeof(metadata)); p = gettok(msg, "|"); while (p && (metacount < MAX_META)) { metadata[metacount++] = p; p = gettok(NULL, "|"); } metadata[metacount] = NULL; if (strncmp(metadata[0], "@@clichg", 8) == 0) { xtreePos_t handle; savetimes_t *itm; int i, recentcount; time_t now = gettimer(); char hostdir[PATH_MAX]; char fn[PATH_MAX]; FILE *fd; /* metadata[3] is the hostname */ handle = xtreeFind(savetimes, metadata[3]); if (handle != xtreeEnd(savetimes)) { itm = (savetimes_t *)xtreeData(savetimes, handle); } else { itm = (savetimes_t *)calloc(1, sizeof(savetimes_t)); itm->hostname = strdup(metadata[3]); xtreeAdd(savetimes, itm->hostname, itm); } /* See how many times we've saved the hostdata recently (within the past 'recentperiod' seconds) */ for (i=0, recentcount=0; ((i < 12) && (itm->tstamp[i] > (now - recentperiod))); i++) recentcount++; /* If it's been saved less than 'maxrecentcount' times, then save it. Otherwise just drop it */ if (!logdirfull && (recentcount < maxrecentcount)) { int written, closestatus, ok = 1; for (i = 10; (i > 0); i--) itm->tstamp[i+1] = itm->tstamp[i]; itm->tstamp[0] = now; sprintf(hostdir, "%s/%s", clientlogdir, metadata[3]); mkdir(hostdir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); sprintf(fn, "%s/%s", hostdir, metadata[4]); fd = fopen(fn, "w"); if (fd == NULL) { errprintf("Cannot create file %s: %s\n", fn, strerror(errno)); continue; } written = fwrite(restofmsg, 1, strlen(restofmsg), fd); if (written != strlen(restofmsg)) { errprintf("Cannot write hostdata file %s: %s\n", fn, strerror(errno)); closestatus = fclose(fd); /* Ignore any close errors */ ok = 0; } else { closestatus = fclose(fd); if (closestatus != 0) { errprintf("Cannot write hostdata file %s: %s\n", fn, strerror(errno)); ok = 0; } } if (!ok) remove(fn); } } /* * A "shutdown" message is sent when the master daemon * terminates. The child workers should shutdown also. */ else if (strncmp(metadata[0], "@@shutdown", 10) == 0) { running = 0; continue; } else if (strncmp(metadata[0], "@@idle", 6) == 0) { /* Ignored */ continue; } /* * A "logrotate" message is sent when the Xymon logs are * rotated. The child workers must re-open their logfiles, * typically stdin and stderr - the filename is always * provided in the XYMONCHANNEL_LOGFILENAME environment. */ else if (strncmp(metadata[0], "@@logrotate", 11) == 0) { char *fn = xgetenv("XYMONCHANNEL_LOGFILENAME"); if (fn && strlen(fn)) { reopen_file(fn, "a", stdout); reopen_file(fn, "a", stderr); } continue; } else if ((metacount > 3) && (strncmp(metadata[0], "@@drophost", 10) == 0)) { /* @@drophost|timestamp|sender|hostname */ char hostdir[PATH_MAX]; snprintf(hostdir, sizeof(hostdir), "%s/%s", clientlogdir, basename(metadata[3])); dropdirectory(hostdir, 1); } else if ((metacount > 4) && (strncmp(metadata[0], "@@renamehost", 12) == 0)) { /* @@renamehost|timestamp|sender|hostname|newhostname */ char oldhostdir[PATH_MAX], newhostdir[PATH_MAX]; snprintf(oldhostdir, sizeof(oldhostdir), "%s/%s", clientlogdir, basename(metadata[3])); snprintf(newhostdir, sizeof(newhostdir), "%s/%s", clientlogdir, basename(metadata[4])); rename(oldhostdir, newhostdir); if (net_worker_locatorbased()) locator_rename_host(metadata[3], metadata[4], ST_HOSTDATA); } else if (strncmp(metadata[0], "@@reload", 8) == 0) { /* Do nothing */ } } return 0; }