void dropdirectory(char *dirfn, int background) { DIR *dirfd; struct dirent *de; char fn[PATH_MAX]; struct stat st; pid_t childpid = 0; if (background) { /* Caller wants us to run as a background task. */ childpid = fork(); } MEMDEFINE(fn); if (childpid == 0) { dbgprintf("Starting to remove directory %s\n", dirfn); dirfd = opendir(dirfn); if (dirfd) { while ( (de = readdir(dirfd)) != NULL ) { sprintf(fn, "%s/%s", dirfn, de->d_name); if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..") && (stat(fn, &st) == 0)) { if (S_ISREG(st.st_mode)) { dbgprintf("Removing file %s\n", fn); unlink(fn); } else if (S_ISDIR(st.st_mode)) { dbgprintf("Recurse into %s\n", fn); dropdirectory(fn, 0); /* Don't background the recursive calls! */ } } } closedir(dirfd); } dbgprintf("Removing directory %s\n", dirfn); rmdir(dirfn); if (background) { /* Background task just exits */ exit(0); } } else if (childpid < 0) { errprintf("Could not fork child to remove directory %s\n", dirfn); } MEMUNDEFINE(fn); }
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; }
void trim_logs(time_t cutoff) { filelist_t *fwalk; DIR *ldir = NULL, *sdir = NULL; struct dirent *sent, *lent; time_t ltime; char fn1[PATH_MAX], fn2[PATH_MAX]; int itemno = 0; /* We have a list of directories to trim, so process them */ for (fwalk = flhead; (fwalk); fwalk = fwalk->next) { dbgprintf("Processing %s\n", fwalk->fname); itemno++; if (progressinfo && ((itemno % progressinfo) == 0)) showprogress(itemno); switch (fwalk->ftype) { case F_DROPIT: /* It's an orphan, and we want to delete it */ dropdirectory(fwalk->fname, 0); break; case F_PURGELOGS: sdir = opendir(fwalk->fname); if (sdir == NULL) { errprintf("Cannot process directory %s: %s\n", fwalk->fname, strerror(errno)); break; } while ((sent = readdir(sdir)) != NULL) { int allgone = 1; if (*(sent->d_name) == '.') continue; sprintf(fn1, "%s/%s", fwalk->fname, sent->d_name); ldir = opendir(fn1); if (ldir == NULL) { errprintf("Cannot process directory %s: %s\n", fn1, strerror(errno)); continue; } while ((lent = readdir(ldir)) != NULL) { if (*(lent->d_name) == '.') continue; ltime = logtime(lent->d_name); if ((ltime > 0) && (ltime < cutoff)) { sprintf(fn2, "%s/%s", fn1, lent->d_name); if (unlink(fn2) == -1) { errprintf("Failed to unlink %s: %s\n", fn2, strerror(errno)); } } else allgone = 0; } closedir(ldir); /* Is it empty ? Then remove it */ if (allgone) rmdir(fn1); } closedir(sdir); break; default: break; } } }
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; }
int main(int argc, char *argv[]) { time_t starttime = time(NULL); char *histdir = NULL; char *histlogdir = NULL; char *msg; int argi, seq; int save_allevents = 1; int save_hostevents = 1; int save_statusevents = 1; int save_histlogs = 1; FILE *alleventsfd = NULL; int running = 1; struct sigaction sa; char newcol2[3]; char oldcol2[3]; MEMDEFINE(newcol2); MEMDEFINE(oldcol2); /* Dont save the error buffer */ save_errbuf = 0; if (xgetenv("BBALLHISTLOG")) save_allevents = (strcmp(xgetenv("BBALLHISTLOG"), "TRUE") == 0); if (xgetenv("BBHOSTHISTLOG")) save_hostevents = (strcmp(xgetenv("BBHOSTHISTLOG"), "TRUE") == 0); if (xgetenv("SAVESTATUSLOG")) save_histlogs = (strcmp(xgetenv("SAVESTATUSLOG"), "TRUE") == 0); for (argi = 1; (argi < argc); argi++) { if (argnmatch(argv[argi], "--histdir=")) { histdir = strchr(argv[argi], '=')+1; } else if (argnmatch(argv[argi], "--histlogdir=")) { histlogdir = strchr(argv[argi], '=')+1; } else if (argnmatch(argv[argi], "--debug")) { debug = 1; } } if (xgetenv("BBHIST") && (histdir == NULL)) { histdir = strdup(xgetenv("BBHIST")); } if (histdir == NULL) { errprintf("No history directory given, aborting\n"); return 1; } if (save_histlogs && (histlogdir == NULL) && xgetenv("BBHISTLOGS")) { histlogdir = strdup(xgetenv("BBHISTLOGS")); } if (save_histlogs && (histlogdir == NULL)) { errprintf("No history-log directory given, aborting\n"); return 1; } if (save_allevents) { char alleventsfn[PATH_MAX]; MEMDEFINE(alleventsfn); sprintf(alleventsfn, "%s/allevents", histdir); alleventsfd = fopen(alleventsfn, "a"); if (alleventsfd == NULL) { errprintf("Cannot open the all-events file '%s'\n", alleventsfn); } setvbuf(alleventsfd, (char *)NULL, _IOLBF, 0); MEMUNDEFINE(alleventsfn); } /* For picking up lost children */ setup_signalhandler("hobbitd_history"); memset(&sa, 0, sizeof(sa)); sa.sa_handler = sig_handler; sigaction(SIGCHLD, &sa, NULL); signal(SIGPIPE, SIG_DFL); while (running) { char *metadata[20] = { NULL, }; int metacount; char *p; char *statusdata = ""; char *hostname, *hostnamecommas, *testname, *dismsg; time_t tstamp, lastchg, disabletime, clienttstamp; int tstamp_i, lastchg_i; int newcolor, oldcolor; int downtimeactive; struct tm tstamptm; int trend; int childstat; /* Pickup any finished child processes to avoid zombies */ while (wait3(&childstat, WNOHANG, NULL) > 0) ; msg = get_hobbitd_message(C_STACHG, "hobbitd_history", &seq, NULL); if (msg == NULL) { running = 0; continue; } p = strchr(msg, '\n'); if (p) { *p = '\0'; statusdata = msg_data(p+1); } metacount = 0; memset(&metadata, 0, sizeof(metadata)); p = gettok(msg, "|"); while (p && (metacount < 20)) { metadata[metacount++] = p; p = gettok(NULL, "|"); } if ((metacount > 9) && (strncmp(metadata[0], "@@stachg", 8) == 0)) { /* @@stachg#seq|timestamp|sender|origin|hostname|testname|expiretime|color|prevcolor|changetime|disabletime|disablemsg|downtimeactive|clienttstamp */ sscanf(metadata[1], "%d.%*d", &tstamp_i); tstamp = tstamp_i; hostname = metadata[4]; testname = metadata[5]; newcolor = parse_color(metadata[7]); oldcolor = parse_color(metadata[8]); lastchg = atoi(metadata[9]); disabletime = atoi(metadata[10]); dismsg = metadata[11]; downtimeactive = (atoi(metadata[12]) > 0); clienttstamp = atoi(metadata[13]); if (newcolor == -1) { errprintf("Bad message: newcolor is unknown '%s'\n", metadata[7]); continue; } p = hostnamecommas = strdup(hostname); while ((p = strchr(p, '.')) != NULL) *p = ','; if (save_statusevents) { char statuslogfn[PATH_MAX]; int logexists; FILE *statuslogfd; char oldcol[100]; char timestamp[40]; struct stat st; MEMDEFINE(statuslogfn); MEMDEFINE(oldcol); MEMDEFINE(timestamp); sprintf(statuslogfn, "%s/%s.%s", histdir, hostnamecommas, testname); stat(statuslogfn, &st); statuslogfd = fopen(statuslogfn, "r+"); logexists = (statuslogfd != NULL); *oldcol = '\0'; if (logexists) { /* * There is a fair chance hobbitd has not been * running all the time while this system was monitored. * So get the time of the latest status change from the file, * instead of relying on the "lastchange" value we get * from hobbitd. This is also needed when migrating from * standard bbd to hobbitd. */ off_t pos = -1; char l[1024]; int gotit; MEMDEFINE(l); fseeko(statuslogfd, 0, SEEK_END); if (ftello(statuslogfd) > 512) { /* Go back 512 from EOF, and skip to start of a line */ fseeko(statuslogfd, -512, SEEK_END); gotit = (fgets(l, sizeof(l)-1, statuslogfd) == NULL); } else { /* Read from beginning of file */ fseeko(statuslogfd, 0, SEEK_SET); gotit = 0; } while (!gotit) { off_t tmppos = ftello(statuslogfd); time_t dur; int dur_i; if (fgets(l, sizeof(l)-1, statuslogfd)) { /* Sun Oct 10 06:49:42 2004 red 1097383782 602 */ if ((strlen(l) > 24) && (sscanf(l+24, " %s %d %d", oldcol, &lastchg_i, &dur_i) == 2) && (parse_color(oldcol) != -1)) { /* * Record the start location of the line */ pos = tmppos; lastchg = lastchg_i; dur = dur_i; } } else { gotit = 1; } } if (pos == -1) { /* * Couldnt find anything in the log. * Take lastchg from the timestamp of the logfile, * and just append the data. */ lastchg = st.st_mtime; fseeko(statuslogfd, 0, SEEK_END); } else { /* * lastchg was updated above. * Seek to where the last line starts. */ fseeko(statuslogfd, pos, SEEK_SET); } MEMUNDEFINE(l); } else { /* * Logfile does not exist. */ lastchg = tstamp; statuslogfd = fopen(statuslogfn, "a"); if (statuslogfd == NULL) { errprintf("Cannot open status historyfile '%s' : %s\n", statuslogfn, strerror(errno)); } } if (strcmp(oldcol, colorname(newcolor)) == 0) { /* We wont update history unless the color did change. */ if ((time(NULL) - starttime) > 300) { errprintf("Will not update %s - color unchanged (%s)\n", statuslogfn, oldcol); } if (hostnamecommas) xfree(hostnamecommas); if (statuslogfd) fclose(statuslogfd); MEMUNDEFINE(statuslogfn); MEMUNDEFINE(oldcol); MEMUNDEFINE(timestamp); continue; } if (statuslogfd) { if (logexists) { struct tm oldtm; /* Re-print the old record, now with the final duration */ memcpy(&oldtm, localtime(&lastchg), sizeof(oldtm)); strftime(timestamp, sizeof(timestamp), "%a %b %e %H:%M:%S %Y", &oldtm); fprintf(statuslogfd, "%s %s %d %d\n", timestamp, oldcol, (int)lastchg, (int)(tstamp - lastchg)); } /* And the new record. */ memcpy(&tstamptm, localtime(&tstamp), sizeof(tstamptm)); strftime(timestamp, sizeof(timestamp), "%a %b %e %H:%M:%S %Y", &tstamptm); fprintf(statuslogfd, "%s %s %d", timestamp, colorname(newcolor), (int)tstamp); fclose(statuslogfd); } MEMUNDEFINE(statuslogfn); MEMUNDEFINE(oldcol); MEMUNDEFINE(timestamp); } if (save_histlogs) { char *hostdash; char fname[PATH_MAX]; FILE *histlogfd; MEMDEFINE(fname); p = hostdash = strdup(hostname); while ((p = strchr(p, '.')) != NULL) *p = '_'; sprintf(fname, "%s/%s", histlogdir, hostdash); mkdir(fname, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); p = fname + sprintf(fname, "%s/%s/%s", histlogdir, hostdash, testname); mkdir(fname, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); p += sprintf(p, "/%s", histlogtime(tstamp)); histlogfd = fopen(fname, "w"); if (histlogfd) { /* * When a host gets disabled or goes purple, the status * message data is not changed - so it will include a * wrong color as the first word of the message. * Therefore we need to fixup this so it matches the * newcolor value. */ int txtcolor = parse_color(statusdata); char *origstatus = statusdata; if (txtcolor != -1) { fprintf(histlogfd, "%s", colorname(newcolor)); statusdata += strlen(colorname(txtcolor)); } if (dismsg && *dismsg) nldecode(dismsg); if (disabletime > 0) { fprintf(histlogfd, " Disabled until %s\n%s\n\n", ctime(&disabletime), (dismsg ? dismsg : "")); fprintf(histlogfd, "Status message when disabled follows:\n\n"); statusdata = origstatus; } else if (dismsg && *dismsg) { fprintf(histlogfd, " Planned downtime: %s\n\n", dismsg); fprintf(histlogfd, "Original status message follows:\n\n"); statusdata = origstatus; } fwrite(statusdata, strlen(statusdata), 1, histlogfd); fprintf(histlogfd, "Status unchanged in 0.00 minutes\n"); fprintf(histlogfd, "Message received from %s\n", metadata[2]); if (clienttstamp) fprintf(histlogfd, "Client data ID %d\n", (int) clienttstamp); fclose(histlogfd); } else { errprintf("Cannot create histlog file '%s' : %s\n", fname, strerror(errno)); } xfree(hostdash); MEMUNDEFINE(fname); } strncpy(oldcol2, ((oldcolor >= 0) ? colorname(oldcolor) : "-"), 2); strncpy(newcol2, colorname(newcolor), 2); newcol2[2] = oldcol2[2] = '\0'; if (oldcolor == -1) trend = -1; /* we dont know how bad it was */ else if (newcolor > oldcolor) trend = 2; /* It's getting worse */ else if (newcolor < oldcolor) trend = 1; /* It's getting better */ else trend = 0; /* Shouldn't happen ... */ if (save_hostevents) { char hostlogfn[PATH_MAX]; FILE *hostlogfd; MEMDEFINE(hostlogfn); sprintf(hostlogfn, "%s/%s", histdir, hostname); hostlogfd = fopen(hostlogfn, "a"); if (hostlogfd) { fprintf(hostlogfd, "%s %d %d %d %s %s %d\n", testname, (int)tstamp, (int)lastchg, (int)(tstamp - lastchg), newcol2, oldcol2, trend); fclose(hostlogfd); } else { errprintf("Cannot open host logfile '%s' : %s\n", hostlogfn, strerror(errno)); } MEMUNDEFINE(hostlogfn); } if (save_allevents) { fprintf(alleventsfd, "%s %s %d %d %d %s %s %d\n", hostname, testname, (int)tstamp, (int)lastchg, (int)(tstamp - lastchg), newcol2, oldcol2, trend); fflush(alleventsfd); } xfree(hostnamecommas); } else if ((metacount > 3) && ((strncmp(metadata[0], "@@drophost", 10) == 0))) { /* @@drophost|timestamp|sender|hostname */ hostname = metadata[3]; if (save_histlogs) { char *hostdash; char testdir[PATH_MAX]; MEMDEFINE(testdir); /* Remove all directories below the host-specific histlog dir */ p = hostdash = strdup(hostname); while ((p = strchr(p, '.')) != NULL) *p = '_'; sprintf(testdir, "%s/%s", histlogdir, hostdash); dropdirectory(testdir, 1); xfree(hostdash); MEMUNDEFINE(testdir); } if (save_hostevents) { char hostlogfn[PATH_MAX]; struct stat st; MEMDEFINE(hostlogfn); sprintf(hostlogfn, "%s/%s", histdir, hostname); if ((stat(hostlogfn, &st) == 0) && S_ISREG(st.st_mode)) { unlink(hostlogfn); } MEMUNDEFINE(hostlogfn); } if (save_statusevents) { DIR *dirfd; struct dirent *de; char *hostlead; char statuslogfn[PATH_MAX]; struct stat st; MEMDEFINE(statuslogfn); /* Remove bbvar/hist/host,name.* */ p = hostnamecommas = strdup(hostname); while ((p = strchr(p, '.')) != NULL) *p = ','; hostlead = malloc(strlen(hostname) + 2); strcpy(hostlead, hostnamecommas); strcat(hostlead, "."); dirfd = opendir(histdir); if (dirfd) { while ((de = readdir(dirfd)) != NULL) { if (strncmp(de->d_name, hostlead, strlen(hostlead)) == 0) { sprintf(statuslogfn, "%s/%s", histdir, de->d_name); if ((stat(statuslogfn, &st) == 0) && S_ISREG(st.st_mode)) { unlink(statuslogfn); } } } closedir(dirfd); } xfree(hostlead); xfree(hostnamecommas); MEMUNDEFINE(statuslogfn); } } else if ((metacount > 4) && ((strncmp(metadata[0], "@@droptest", 10) == 0))) { /* @@droptest|timestamp|sender|hostname|testname */ hostname = metadata[3]; testname = metadata[4]; if (save_histlogs) { char *hostdash; char testdir[PATH_MAX]; MEMDEFINE(testdir); p = hostdash = strdup(hostname); while ((p = strchr(p, '.')) != NULL) *p = '_'; sprintf(testdir, "%s/%s/%s", histlogdir, hostdash, testname); dropdirectory(testdir, 1); xfree(hostdash); MEMUNDEFINE(testdir); } if (save_statusevents) { char *hostnamecommas; char statuslogfn[PATH_MAX]; struct stat st; MEMDEFINE(statuslogfn); p = hostnamecommas = strdup(hostname); while ((p = strchr(p, '.')) != NULL) *p = ','; sprintf(statuslogfn, "%s/%s.%s", histdir, hostnamecommas, testname); if ((stat(statuslogfn, &st) == 0) && S_ISREG(st.st_mode)) unlink(statuslogfn); xfree(hostnamecommas); MEMUNDEFINE(statuslogfn); } } else if ((metacount > 4) && ((strncmp(metadata[0], "@@renamehost", 12) == 0))) { /* @@renamehost|timestamp|sender|hostname|newhostname */ char *newhostname; hostname = metadata[3]; newhostname = metadata[4]; if (save_histlogs) { char *hostdash; char *newhostdash; char olddir[PATH_MAX]; char newdir[PATH_MAX]; MEMDEFINE(olddir); MEMDEFINE(newdir); p = hostdash = strdup(hostname); while ((p = strchr(p, '.')) != NULL) *p = '_'; p = newhostdash = strdup(newhostname); while ((p = strchr(p, '.')) != NULL) *p = '_'; sprintf(olddir, "%s/%s", histlogdir, hostdash); sprintf(newdir, "%s/%s", histlogdir, newhostdash); rename(olddir, newdir); xfree(newhostdash); xfree(hostdash); MEMUNDEFINE(newdir); MEMUNDEFINE(olddir); } if (save_hostevents) { char hostlogfn[PATH_MAX]; char newhostlogfn[PATH_MAX]; MEMDEFINE(hostlogfn); MEMDEFINE(newhostlogfn); sprintf(hostlogfn, "%s/%s", histdir, hostname); sprintf(newhostlogfn, "%s/%s", histdir, newhostname); rename(hostlogfn, newhostlogfn); MEMUNDEFINE(hostlogfn); MEMUNDEFINE(newhostlogfn); } if (save_statusevents) { DIR *dirfd; struct dirent *de; char *hostlead; char *newhostnamecommas; char statuslogfn[PATH_MAX]; char newlogfn[PATH_MAX]; MEMDEFINE(statuslogfn); MEMDEFINE(newlogfn); p = hostnamecommas = strdup(hostname); while ((p = strchr(p, '.')) != NULL) *p = ','; hostlead = malloc(strlen(hostname) + 2); strcpy(hostlead, hostnamecommas); strcat(hostlead, "."); p = newhostnamecommas = strdup(newhostname); while ((p = strchr(p, '.')) != NULL) *p = ','; dirfd = opendir(histdir); if (dirfd) { while ((de = readdir(dirfd)) != NULL) { if (strncmp(de->d_name, hostlead, strlen(hostlead)) == 0) { char *testname = strchr(de->d_name, '.'); sprintf(statuslogfn, "%s/%s", histdir, de->d_name); sprintf(newlogfn, "%s/%s%s", histdir, newhostnamecommas, testname); rename(statuslogfn, newlogfn); } } closedir(dirfd); } xfree(newhostnamecommas); xfree(hostlead); xfree(hostnamecommas); MEMUNDEFINE(statuslogfn); MEMUNDEFINE(newlogfn); } } else if ((metacount > 5) && (strncmp(metadata[0], "@@renametest", 12) == 0)) { /* @@renametest|timestamp|sender|hostname|oldtestname|newtestname */ char *newtestname; hostname = metadata[3]; testname = metadata[4]; newtestname = metadata[5]; if (save_histlogs) { char *hostdash; char olddir[PATH_MAX]; char newdir[PATH_MAX]; MEMDEFINE(olddir); MEMDEFINE(newdir); p = hostdash = strdup(hostname); while ((p = strchr(p, '.')) != NULL) *p = '_'; sprintf(olddir, "%s/%s/%s", histlogdir, hostdash, testname); sprintf(newdir, "%s/%s/%s", histlogdir, hostdash, newtestname); rename(olddir, newdir); xfree(hostdash); MEMUNDEFINE(newdir); MEMUNDEFINE(olddir); } if (save_statusevents) { char *hostnamecommas; char statuslogfn[PATH_MAX]; char newstatuslogfn[PATH_MAX]; MEMDEFINE(statuslogfn); MEMDEFINE(newstatuslogfn); p = hostnamecommas = strdup(hostname); while ((p = strchr(p, '.')) != NULL) *p = ','; sprintf(statuslogfn, "%s/%s.%s", histdir, hostnamecommas, testname); sprintf(newstatuslogfn, "%s/%s.%s", histdir, hostnamecommas, newtestname); rename(statuslogfn, newstatuslogfn); xfree(hostnamecommas); MEMUNDEFINE(newstatuslogfn); MEMUNDEFINE(statuslogfn); } } else if (strncmp(metadata[0], "@@shutdown", 10) == 0) { running = 0; } else if (strncmp(metadata[0], "@@logrotate", 11) == 0) { char *fn = xgetenv("HOBBITCHANNEL_LOGFILENAME"); if (fn && strlen(fn)) { freopen(fn, "a", stdout); freopen(fn, "a", stderr); } continue; } } MEMUNDEFINE(newcol2); MEMUNDEFINE(oldcol2); fclose(alleventsfd); return 0; }