Example #1
0
int main(int argc, char *argv[])
{
	char		*pagedir;
	bbgen_page_t 	*p;
	dispsummary_t	*s;
	int		i;
	char		*pageset = NULL;
	char		*nssidebarfilename = NULL;
	char		*egocolumn = NULL;
	char		*csvfile = NULL;
	char		csvdelim = ',';
	int		embedded = 0;
	int		hobbitddump = 0;
	char		*envarea = NULL;
	int		do_normal_pages = 1;

	/* Setup standard header+footer (might be modified by option pageset) */
	select_headers_and_footers("bb");

	bb_color = bb2_color = bbnk_color = -1;
	pagedir = NULL;
	init_timestamp();
	fqdn = get_fqdn();

	/* Setup values from env. vars that may be overridden via commandline options */
	if (xgetenv("MKBB2COLREPEAT")) {
		int i = atoi(xgetenv("MKBB2COLREPEAT"));

		if (i > 0) maxrowsbeforeheading = i;
	}

	for (i = 1; (i < argc); i++) {
		if ( (strcmp(argv[i], "--hobbitd") == 0)       ||
		     (argnmatch(argv[i], "--purplelifetime=")) ||
		     (strcmp(argv[i], "--nopurple") == 0)      ) {
			/* Deprecated */
		}
		else if (argnmatch(argv[i], "--env=")) {
			char *lp = strchr(argv[i], '=');
			loadenv(lp+1, envarea);
		}
		else if (argnmatch(argv[i], "--area=")) {
			char *lp = strchr(argv[i], '=');
			envarea = strdup(lp+1);
		}
		else if (argnmatch(argv[i], "--hobbitddump")) {
			hobbitddump = 1;
		}

		else if (argnmatch(argv[i], "--ignorecolumns=")) {
			char *lp = strchr(argv[i], '=');
			ignorecolumns = (char *) malloc(strlen(lp)+2);
			sprintf(ignorecolumns, ",%s,", (lp+1));
		}
		else if (argnmatch(argv[i], "--nk-reds-only")) {
			nkonlyreds = 1;
		}
		else if (argnmatch(argv[i], "--bb2-ignorecolumns=")) {
			char *lp = strchr(argv[i], '=');
			bb2ignorecolumns = (char *) malloc(strlen(lp)+2);
			sprintf(bb2ignorecolumns, ",%s,", (lp+1));
		}
		else if (argnmatch(argv[i], "--bb2-colors=")) {
			char *lp = strchr(argv[i], '=') + 1;
			bb2colors = colorset(lp, (1 << COL_GREEN));
		}
		else if (argnmatch(argv[i], "--bb2-ignorepurples")) {
			bb2colors = (bb2colors & ~(1 << COL_PURPLE));
		}
		else if (argnmatch(argv[i], "--bb2-ignoredialups")) {
			bb2nodialups = 1;
		}
		else if (argnmatch(argv[i], "--includecolumns=")) {
			char *lp = strchr(argv[i], '=');
			includecolumns = (char *) malloc(strlen(lp)+2);
			sprintf(includecolumns, ",%s,", (lp+1));
		}
		else if (argnmatch(argv[i], "--eventignore=")) {
			char *lp = strchr(argv[i], '=');
			eventignorecolumns = (char *) malloc(strlen(lp)+2);
			sprintf(eventignorecolumns, ",%s,", (lp+1));
		}
		else if (argnmatch(argv[i], "--doccgi=")) {
			char *lp = strchr(argv[i], '=');
			char *url = (char *)malloc(strlen(xgetenv("CGIBINURL"))+strlen(lp+1)+2);
			sprintf(url, "%s/%s", xgetenv("CGIBINURL"), lp+1);
			setdocurl(url);
			xfree(url);
		}
		else if (argnmatch(argv[i], "--docurl=")) {
			char *lp = strchr(argv[i], '=');
			setdocurl(lp+1);
		}
		else if (argnmatch(argv[i], "--no-doc-window")) {
			/* This is a no-op now */
		}
		else if (argnmatch(argv[i], "--doc-window")) {
			setdocurl("TARGET=\"_blank\"");
		}
		else if (argnmatch(argv[i], "--htmlextension=")) {
			char *lp = strchr(argv[i], '=');
			htmlextension = strdup(lp+1);
		}
		else if (argnmatch(argv[i], "--htaccess")) {
			char *lp = strchr(argv[i], '=');
			if (lp) htaccess = strdup(lp+1);
			else htaccess = ".htaccess";
		}
		else if ((strcmp(argv[i], "--wml") == 0) || argnmatch(argv[i], "--wml=")) {
			char *lp = strchr(argv[i], '=');

			if (lp) {
				wapcolumns = (char *) malloc(strlen(lp)+2);
				sprintf(wapcolumns, ",%s,", (lp+1));
			}
			enable_wmlgen = 1;
		}
		else if (argnmatch(argv[i], "--nstab=")) {
			char *lp = strchr(argv[i], '=');

			if (strlen(lp+1) > 0) {
				nssidebarfilename = strdup(lp+1);
			}
			else errprintf("--nstab requires a filename\n");
		}
		else if (argnmatch(argv[i], "--nslimit=")) {
			char *lp = strchr(argv[i], '=');
			nssidebarcolorlimit = parse_color(lp+1);
		}
		else if (argnmatch(argv[i], "--rssversion=")) {
			char *lp = strchr(argv[i], '=');

			rssversion = strdup(lp+1);
		}
		else if (argnmatch(argv[i], "--rsslimit=")) {
			char *lp = strchr(argv[i], '=');
			rsscolorlimit = parse_color(lp+1);
		}
		else if (argnmatch(argv[i], "--rss")) {
			wantrss = 1;
		}
		else if (argnmatch(argv[i], "--rssextension=")) {
			char *lp = strchr(argv[i], '=');
			rssextension = strdup(lp+1);
		}
		else if (argnmatch(argv[i], "--reportopts=")) {
			char style[MAX_LINE_LEN];
			unsigned int rstart, rend;

			int count = sscanf(argv[i], "--reportopts=%u:%u:%d:%s", 
					   &rstart, &rend, &dynamicreport, style);
			reportstart = rstart; reportend = rend;

			if (count < 2) {
				errprintf("Invalid --reportopts option: Must have start- and end-times\n");
				return 1;
			}

			if (count < 3) dynamicreport = 1;
			if (count == 4) {
				if (strcmp(style, stylenames[STYLE_CRIT]) == 0) reportstyle = STYLE_CRIT;
				else if (strcmp(style, stylenames[STYLE_NONGR]) == 0) reportstyle = STYLE_NONGR;
				else reportstyle = STYLE_OTHER;
			}

			if (reportstart < 788918400) reportstart = 788918400;
			if (reportend > time(NULL)) reportend = time(NULL);

			if (xgetenv("BBREPWARN")) reportwarnlevel = atof(xgetenv("BBREPWARN"));
			if (xgetenv("BBREPGREEN")) reportgreenlevel = atof(xgetenv("BBREPGREEN"));

			if ((reportwarnlevel < 0.0) || (reportwarnlevel > 100.0)) reportwarnlevel = 97.0;
			if ((reportgreenlevel < 0.0) || (reportgreenlevel > 100.0)) reportgreenlevel = 99.995;

			select_headers_and_footers("bbrep");
			sethostenv_report(reportstart, reportend, reportwarnlevel, reportgreenlevel);
		}
		else if (argnmatch(argv[i], "--csv="))  {
			char *lp = strchr(argv[i], '=');
			csvfile = strdup(lp+1);
		}
		else if (argnmatch(argv[i], "--csvdelim="))  {
			char *lp = strchr(argv[i], '=');
			csvdelim = *(lp+1);
		}
		else if (argnmatch(argv[i], "--snapshot=")) {
			char *lp = strchr(argv[i], '=');

			snapshot = atol(lp+1);
			select_headers_and_footers("bbsnap");
			sethostenv_snapshot(snapshot);
		}

		else if (strcmp(argv[i], "--pages-first") == 0) {
			hostsbeforepages = 0;
		}
		else if (strcmp(argv[i], "--pages-last") == 0) {
			hostsbeforepages = 1;
		}
		else if (argnmatch(argv[i], "--subpagecolumns=")) {
			char *lp = strchr(argv[i], '=');

			subpagecolumns = atoi(lp+1);
			if (subpagecolumns < 1) subpagecolumns=1;
		}
		else if (argnmatch(argv[i], "--maxrows=")) {
			char *lp = strchr(argv[i], '=');

			maxrowsbeforeheading = atoi(lp+1);
			if (maxrowsbeforeheading < 0) maxrowsbeforeheading=0;
		}
		else if (strcmp(argv[i], "--recentgifs") == 0) {
			use_recentgifs = 1;
		}
		else if (argnmatch(argv[i], "--recentgifs=")) {
			char *lp = strchr(argv[i], '=');

			use_recentgifs = 1;
			recentgif_limit = 60*durationvalue(lp+1);
		}
		else if (strcmp(argv[i], "--sort-group-only-items") == 0) {
			sort_grouponly_items = 1;
		}
		else if (argnmatch(argv[i], "--page-title=")) {
			char *lp = strchr(argv[i], '=');

			defaultpagetitle = strdup(lp+1);
		}
		else if (argnmatch(argv[i], "--dialupskin=")) {
			char *lp = strchr(argv[i], '=');

			dialupskin = strdup(lp+1);
		}
		else if (argnmatch(argv[i], "--reverseskin=")) {
			char *lp = strchr(argv[i], '=');

			reverseskin = strdup(lp+1);
		}
		else if (strcmp(argv[i], "--pagetitle-links") == 0) {
			pagetitlelinks = 1;
		}
		else if (strcmp(argv[i], "--pagetext-headings") == 0) {
			pagetextheadings = 1;
		}
		else if (strcmp(argv[i], "--underline-headings") == 0) {
			underlineheadings = 1;
		}
		else if (strcmp(argv[i], "--no-underline-headings") == 0) {
			underlineheadings = 0;
		}
		else if (strcmp(argv[i], "--no-eventlog") == 0) {
			bb2eventlog = 0;
		}
		else if (argnmatch(argv[i], "--max-eventcount=")) {
			char *lp = strchr(argv[i], '=');

			bb2eventlogmaxcount = atoi(lp+1);
		}
		else if (argnmatch(argv[i], "--max-eventtime=")) {
			char *lp = strchr(argv[i], '=');

			bb2eventlogmaxtime = atoi(lp+1);
		}
		else if (argnmatch(argv[i], "--max-ackcount=")) {
			char *lp = strchr(argv[i], '=');

			bb2acklogmaxcount = atoi(lp+1);
		}
		else if (argnmatch(argv[i], "--max-acktime=")) {
			char *lp = strchr(argv[i], '=');

			bb2acklogmaxtime = atoi(lp+1);
		}
		else if (strcmp(argv[i], "--no-acklog") == 0) {
			bb2acklog = 0;
		}
		else if (strcmp(argv[i], "--no-pages") == 0) {
			do_normal_pages = 0;
		}

		else if (argnmatch(argv[i], "--noprop=")) {
			char *lp = strchr(argv[i], '=');
			nopropyellowdefault = (char *) malloc(strlen(lp)+2);
			sprintf(nopropyellowdefault, ",%s,", (lp+1));
			errprintf("--noprop is deprecated - use --nopropyellow instead\n");
		}
		else if (argnmatch(argv[i], "--nopropyellow=")) {
			char *lp = strchr(argv[i], '=');
			nopropyellowdefault = (char *) malloc(strlen(lp)+2);
			sprintf(nopropyellowdefault, ",%s,", (lp+1));
		}
		else if (argnmatch(argv[i], "--nopropred=")) {
			char *lp = strchr(argv[i], '=');
			nopropreddefault = (char *) malloc(strlen(lp)+2);
			sprintf(nopropreddefault, ",%s,", (lp+1));
		}
		else if (argnmatch(argv[i], "--noproppurple=")) {
			char *lp = strchr(argv[i], '=');
			noproppurpledefault = (char *) malloc(strlen(lp)+2);
			sprintf(noproppurpledefault, ",%s,", (lp+1));
		}
		else if (argnmatch(argv[i], "--nopropack=")) {
			char *lp = strchr(argv[i], '=');
			nopropackdefault = (char *) malloc(strlen(lp)+2);
			sprintf(nopropackdefault, ",%s,", (lp+1));
		}

		else if (strcmp(argv[i], "--bbpageONLY") == 0) {
			/* Deprecated */
			errprintf("--bbpageONLY is deprecated - use --pageset=NAME to generate pagesets\n");
		}
		else if (strcmp(argv[i], "--embedded") == 0) {
			embedded = 1;
		}
		else if (argnmatch(argv[i], "--pageset=")) {
			char *lp = strchr(argv[i], '=');
			pageset = strdup(lp+1);
		}
		else if (argnmatch(argv[i], "--template=")) {
			char *lp = strchr(argv[i], '=');
			lp++;
			select_headers_and_footers(lp);
		}

		else if (argnmatch(argv[i], "--purplelog=")) {
			char *lp = strchr(argv[i], '=');
			if (*(lp+1) == '/') purplelogfn = strdup(lp+1);
			else {
				purplelogfn = (char *) malloc(strlen(xgetenv("BBHOME"))+1+strlen(lp+1)+1);
				sprintf(purplelogfn, "%s/%s", xgetenv("BBHOME"), (lp+1));
			}
		}
		else if (argnmatch(argv[i], "--report=") || (strcmp(argv[i], "--report") == 0)) {
			char *lp = strchr(argv[i], '=');
			if (lp) {
				egocolumn = strdup(lp+1);
			}
			else egocolumn = "bbgen";
			timing = 1;
		}
		else if (argnmatch(argv[i], "--nklog=") || (strcmp(argv[i], "--nklog") == 0)) {
			char *lp = strchr(argv[i], '=');
			if (lp) {
				lognkstatus = strdup(lp+1);
			}
			else lognkstatus = "nk";
		}
		else if (strcmp(argv[i], "--timing") == 0) {
			timing = 1;
		}
		else if (strcmp(argv[i], "--debug") == 0) {
			debug = 1;
		}
		else if (strcmp(argv[i], "--no-update") == 0) {
			dontsendmessages = 1;
		}
		else if (strcmp(argv[i], "--version") == 0) {
			printf("bbgen version %s\n", VERSION);
			printf("\n");
			exit(0);
		}

		else if ((strcmp(argv[i], "--help") == 0) || (strcmp(argv[i], "-?") == 0)) {
			printf("bbgen for Hobbit version %s\n\n", VERSION);
			printf("Usage: %s [options] [WebpageDirectory]\n", argv[0]);
			printf("Options:\n");
			printf("    --ignorecolumns=test[,test] : Completely ignore these columns\n");
			printf("    --nk-reds-only              : Only show red statuses on the NK page\n");
			printf("    --bb2-ignorecolumns=test[,test]: Ignore these columns for the BB2 page\n");
			printf("    --bb2-ignorepurples         : Ignore all-purple hosts on BB2 page\n");
			printf("    --includecolumns=test[,test]: Always include these columns on BB2 page\n");
		        printf("    --max-eventcount=N          : Max number of events to include in eventlog\n");
		        printf("    --max-eventtime=N           : Show events that occurred within the last N minutes\n");
			printf("    --eventignore=test[,test]   : Columns to ignore in bb2 event-log display\n");
			printf("    --no-eventlog               : Do not generate the bb2 eventlog display\n");
			printf("    --no-acklog                 : Do not generate the bb2 ack-log display\n");
			printf("    --no-pages                  : Generate only the bb2 and bbnk pages\n");
			printf("    --docurl=documentation-URL  : Hostnames link to a general (dynamic) web page for docs\n");
			printf("    --no-doc-window             : Open doc-links in same window\n");
			printf("    --htmlextension=.EXT        : Sets filename extension for generated file (default: .html\n");
			printf("    --report[=COLUMNNAME]       : Send a status report about the running of bbgen\n");
			printf("    --reportopts=ST:END:DYN:STL : Run in Hobbit Reporting mode\n");
			printf("    --csv=FILENAME              : For Hobbit Reporting, output CSV file\n");
			printf("    --csvdelim=CHARACTER        : Delimiter in CSV file output (default: comma)\n");
			printf("    --snapshot=TIME             : Snapshot mode\n");
			printf("\nPage layout options:\n");
			printf("    --pages-first               : Put page- and subpage-links before hosts (default)\n");
			printf("    --pages-last                : Put page- and subpage-links after hosts\n");
			printf("    --subpagecolumns=N          : Number of columns for links to pages and subpages\n");
			printf("    --maxrows=N                 : Repeat column headings for every N hosts shown\n");
			printf("    --recentgifs                : Use xxx-recent.gif icons for newly changed tests\n");
			printf("    --sort-group-only-items     : Display group-only items in alphabetical order\n");
			printf("    --page-title=TITLE          : Set a default page title for all pages\n");
			printf("    --dialupskin=URL            : Use a different icon skin for dialup tests\n");
			printf("    --reverseskin=URL           : Use a different icon skin for reverse tests\n");
			printf("    --pagetitle-links           : Make page- and subpage-titles act as links\n");
			printf("    --pagetext-headings         : Use page texts as headings\n");
			printf("    --no-underline-headings     : Do not underline the page headings\n");
			printf("\nStatus propagation control options:\n");
			printf("    --noprop=test[,test]        : Disable upwards status propagation when YELLOW\n");
			printf("    --nopropred=test[,test]     : Disable upwards status propagation when RED or YELLOW\n");
			printf("    --noproppurple=test[,test]  : Disable upwards status propagation when PURPLE\n");
			printf("\nAlternate pageset generation support:\n");
			printf("    --pageset=SETNAME           : Generate non-standard pageset with tag SETNAME\n");
			printf("    --template=TEMPLATE         : template for header and footer files\n");
			printf("\nAlternate output formats:\n");
			printf("    --wml[=test1,test2,...]     : Generate a small (bb2-style) WML page\n");
			printf("    --nstab=FILENAME            : Generate a Netscape Sidebar feed\n");
			printf("    --nslimit=COLOR             : Minimum color to include on Netscape sidebar\n");
			printf("    --rss                       : Generate a RSS/RDF feed of alerts\n");
			printf("    --rssextension=.EXT         : Sets filename extension for RSS files (default: .rss\n");
			printf("    --rssversion={0.91|0.92|1.0|2.0} : Specify RSS/RDF version (default: 0.91)\n");
			printf("    --rsslimit=COLOR            : Minimum color to include on RSS feed\n");
			printf("\nDebugging/troubleshooting options:\n");
			printf("    --timing                    : Collect timing information\n");
			printf("    --debug                     : Debugging information\n");
			printf("    --version                   : Show version information\n");
			printf("    --purplelog=FILENAME        : Create a log of purple hosts and tests\n");
			exit(0);
		}
		else if (argnmatch(argv[i], "-")) {
			errprintf("Unknown option : %s\n", argv[i]);
		}

		else {
			/* Last argument is pagedir */
			pagedir = strdup(argv[i]);
		}
	}

	/* In case they changed the name of our column ... */
	if (egocolumn) setup_signalhandler(egocolumn);

	if (debug) {
		int i;
		printf("Command: bbgen");
		for (i=1; (i<argc); i++) printf(" '%s'", argv[i]);
		printf("\n");
		printf("Environment BBHOSTS='%s'\n", textornull(xgetenv("BBHOSTS")));
		printf("\n");
	}

	add_timestamp("Startup");

	/* Check that all needed environment vars are defined */
	envcheck(reqenv);

	/* Catch a SEGV fault */
	setup_signalhandler("bbgen");

	/* Set umask to 0022 so that the generated HTML pages have world-read access */
	umask(0022);

	if (pagedir == NULL) {
		if (xgetenv("BBWWW")) {
			pagedir = strdup(xgetenv("BBWWW"));
		}
		else {
			pagedir = (char *) malloc(strlen(xgetenv("BBHOME"))+5);
			sprintf(pagedir, "%s/www", xgetenv("BBHOME"));
		}
	}

	if (xgetenv("BBHTACCESS")) bbhtaccess = strdup(xgetenv("BBHTACCESS"));
	if (xgetenv("BBPAGEHTACCESS")) bbpagehtaccess = strdup(xgetenv("BBPAGEHTACCESS"));
	if (xgetenv("BBSUBPAGEHTACCESS")) bbsubpagehtaccess = strdup(xgetenv("BBSUBPAGEHTACCESS"));

	/*
	 * When doing alternate pagesets, disable some stuff:
	 * No WML or RSS pages.
	 */
	if (pageset || embedded || snapshot) enable_wmlgen = wantrss = 0;
	if (embedded) {
		egocolumn = htaccess = NULL;

		/*
		 * Need to have default SIGPIPE handling when doing embedded stuff.
		 * We are probably run from a CGI script or something similar.
		 */
		signal(SIGPIPE, SIG_DFL);
	}

	/* Load all data from the various files */
	load_all_links();
	add_timestamp("Load links done");
	pagehead = load_bbhosts(pageset);
	add_timestamp("Load bbhosts done");

	if (!embedded) {
		/* Remove old acknowledgements */
		delete_old_acks();
		add_timestamp("ACK removal done");
	}

	statehead = load_state(&dispsums);
	if (embedded || snapshot) dispsums = NULL;
	add_timestamp("Load STATE done");

	if (hobbitddump) {
		dump_hobbitdchk();
		return 0;
	}

	/* Calculate colors of hosts and pages */
	calc_hostcolors(bb2ignorecolumns);
	calc_pagecolors(pagehead);

	/* Topmost page (background color for bb.html) */
	for (p=pagehead; (p); p = p->next) {
		if (p->color > pagehead->color) pagehead->color = p->color;
	}
	bb_color = pagehead->color;

	if (xgetenv("SUMMARY_SET_BKG") && (strcmp(xgetenv("SUMMARY_SET_BKG"), "TRUE") == 0)) {
		/*
		 * Displayed summaries affect the BB page only, 
		 * but should not go into the color we report to
		 * others.
		 */
		for (s=dispsums; (s); s = s->next) {
			if (s->color > pagehead->color) pagehead->color = s->color;
		}
	}
	add_timestamp("Color calculation done");

	if (debug) dumpall(pagehead);

	/* Generate pages */
	if (chdir(pagedir) != 0) {
		errprintf("Cannot change to webpage directory %s\n", pagedir);
		exit(1);
	}

	if (embedded) {
		/* Just generate that one page */
		do_one_page(pagehead, NULL, 1);
		return 0;
	}

	/* The main page - bb.html and pages/subpages thereunder */
	add_timestamp("Hobbit pagegen start");
	if (reportstart && csvfile) {
		csv_availability(csvfile, csvdelim);
	}
	else if (do_normal_pages) {
		do_page_with_subs(pagehead, dispsums);
	}
	add_timestamp("Hobbit pagegen done");

	if (reportstart) {
		/* Reports end here */
		return 0;
	}

	/* The full summary page - bb2.html */
	bb2_color = do_bb2_page(nssidebarfilename, PAGE_BB2);
	add_timestamp("BB2 generation done");

	/* Reduced summary (alerts) page - bbnk.html */
	bbnk_color = do_bb2_page(NULL, PAGE_NK);
	add_timestamp("BBNK generation done");

	if (snapshot) {
		/* Snapshots end here */
		return 0;
	}

	/* Send summary notices - only once, so not on pagesets */
	if (pageset == NULL) {
		send_summaries(sumhead);
		add_timestamp("Summary transmission done");
	}

	/* Generate WML cards */
	if (enable_wmlgen) {
		do_wml_cards(pagedir);
		add_timestamp("WML generation done");
	}

	/* Need to do this before sending in our report */
	add_timestamp("Run completed");

	/* Tell about us */
	if (egocolumn) {
		char msgline[4096];
		char *timestamps;
		long bbsleep = (xgetenv("BBSLEEP") ? atol(xgetenv("BBSLEEP")) : 300);
		int color;

		/* Go yellow if it runs for too long */
		if (total_runtime() > bbsleep) {
			errprintf("WARNING: Runtime %ld longer than BBSLEEP (%ld)\n", total_runtime(), bbsleep);
		}
		color = (errbuf ? COL_YELLOW : COL_GREEN);

		combo_start();
		init_status(color);
		sprintf(msgline, "status %s.%s %s %s\n\n", xgetenv("MACHINE"), egocolumn, colorname(color), timestamp);
		addtostatus(msgline);

		sprintf(msgline, "bbgen for Hobbit version %s\n", VERSION);
		addtostatus(msgline);

		sprintf(msgline, "\nStatistics:\n Hosts               : %5d\n Status messages     : %5d\n Purple messages     : %5d\n Pages               : %5d\n", 
			hostcount, statuscount, purplecount, pagecount);
		addtostatus(msgline);

		if (errbuf) {
			addtostatus("\n\nError output:\n");
			addtostatus(errbuf);
		}

		show_timestamps(&timestamps);
		addtostatus(timestamps);

		finish_status();
		combo_end();
	}
	else show_timestamps(NULL);

	return 0;
}
Example #2
0
int main(int argc, char *argv[])
{
	char *msg;
	int seq;
	int argi;
	int alertcolors, alertinterval;
	char *configfn = NULL;
	char *checkfn = NULL;
	int checkpointinterval = 900;
	char acklogfn[PATH_MAX];
	FILE *acklogfd = NULL;
	char notiflogfn[PATH_MAX];
	FILE *notiflogfd = NULL;
	char *tracefn = NULL;
	struct sigaction sa;
	int configchanged;
	time_t lastxmit = 0;

	MEMDEFINE(acklogfn);
	MEMDEFINE(notiflogfn);

	libxymon_init(argv[0]);

	/* Dont save the error buffer */
	save_errbuf = 0;

	/* Load alert config */
	alertcolors = colorset(xgetenv("ALERTCOLORS"), ((1 << COL_GREEN) | (1 << COL_BLUE)));
	alertinterval = 60*atoi(xgetenv("ALERTREPEAT"));

	/* Create our loookup-trees */
	hostnames = xtreeNew(strcasecmp);
	testnames = xtreeNew(strcasecmp);
	locations = xtreeNew(strcasecmp);

	for (argi=1; (argi < argc); argi++) {
		if (argnmatch(argv[argi], "--config=")) {
			configfn = strdup(strchr(argv[argi], '=')+1);
		}
		else if (argnmatch(argv[argi], "--checkpoint-file=")) {
			checkfn = strdup(strchr(argv[argi], '=')+1);
		}
		else if (argnmatch(argv[argi], "--checkpoint-interval=")) {
			char *p = strchr(argv[argi], '=') + 1;
			checkpointinterval = atoi(p);
		}
		else if (argnmatch(argv[argi], "--dump-config")) {
			load_alertconfig(configfn, alertcolors, alertinterval);
			dump_alertconfig(1);
			return 0;
		}
		else if (argnmatch(argv[argi], "--cfid")) {
			include_configid = 1;
		}
		else if (argnmatch(argv[argi], "--test")) {
			char *testhost = NULL, *testservice = NULL, *testpage = NULL, 
			     *testcolor = "red", *testgroups = NULL;
			void *hinfo;
			int testdur = 0;
			FILE *logfd = NULL;
			activealerts_t *awalk = NULL;
			int paramno = 0;

			argi++; if (argi < argc) testhost = argv[argi];
			argi++; if (argi < argc) testservice = argv[argi];
			argi++; 
			while (argi < argc) {
				if (strncasecmp(argv[argi], "--duration=", 11) == 0) {
					testdur = durationvalue(strchr(argv[argi], '=')+1);
				}
				else if (strncasecmp(argv[argi], "--color=", 8) == 0) {
					testcolor = strchr(argv[argi], '=')+1;
				}
				else if (strncasecmp(argv[argi], "--group=", 8) == 0) {
					testgroups = strchr(argv[argi], '=')+1;
				}
				else if (strncasecmp(argv[argi], "--time=", 7) == 0) {
					fakestarttime = (time_t)atoi(strchr(argv[argi], '=')+1);
				}
				else {
					paramno++;
					if (paramno == 1) testdur = atoi(argv[argi]);
					else if (paramno == 2) testcolor = argv[argi];
					else if (paramno == 3) fakestarttime = (time_t) atoi(argv[argi]);
				}

				argi++;
			}

			if ((testhost == NULL) || (testservice == NULL)) {
				printf("Usage: xymond_alert --test HOST SERVICE [options]\n");
				printf("Possible options:\n\t[--duration=MINUTES]\n\t[--color=COLOR]\n\t[--group=GROUPNAME]\n\t[--time=TIMESPEC]\n");

				return 1;
			}

			load_hostnames(xgetenv("HOSTSCFG"), NULL, get_fqdn());
			hinfo = hostinfo(testhost);
			if (hinfo) {
				testpage = strdup(xmh_item(hinfo, XMH_ALLPAGEPATHS));
			}
			else {
				errprintf("Host not found in hosts.cfg - assuming it is on the top page\n");
				testpage = "";
			}

			awalk = (activealerts_t *)calloc(1, sizeof(activealerts_t));
			awalk->hostname = find_name(hostnames, testhost);
			awalk->testname = find_name(testnames, testservice);
			awalk->location = find_name(locations, testpage);
			awalk->ip = strdup("127.0.0.1");
			awalk->color = awalk->maxcolor = parse_color(testcolor);
			awalk->pagemessage = "Test of the alert configuration";
			awalk->eventstart = getcurrenttime(NULL) - testdur*60;
			awalk->groups = (testgroups ? strdup(testgroups) : NULL);
			awalk->state = A_PAGING;
			awalk->cookie = 12345;
			awalk->next = NULL;

			logfd = fopen("/dev/null", "w");
			starttrace(NULL);
			testonly = 1;

			load_alertconfig(configfn, alertcolors, alertinterval);
			load_holidays(0);
			send_alert(awalk, logfd);
			return 0;
		}
		else if (argnmatch(argv[argi], "--trace=")) {
			tracefn = strdup(strchr(argv[argi], '=')+1);
			starttrace(tracefn);
		}
		else if (net_worker_option(argv[argi])) {
			/* Handled in the subroutine */
		}
		else if (standardoption(argv[argi])) {
			if (showhelp) return 0;
		}
		else {
			errprintf("Unknown option '%s'\n", argv[argi]);
		}
	}

	/* Do the network stuff if needed */
	net_worker_run(ST_ALERT, LOC_SINGLESERVER, NULL);

	if (checkfn) {
		load_checkpoint(checkfn);
		nextcheckpoint = gettimer() + checkpointinterval;
		dbgprintf("Next checkpoint at %d, interval %d\n", (int) nextcheckpoint, checkpointinterval);
	}

	setup_signalhandler("xymond_alert");
	/* Need to handle these ourselves, so we can shutdown and save state-info */
	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = sig_handler;
	sigaction(SIGPIPE, &sa, NULL);
	sigaction(SIGTERM, &sa, NULL);
	sigaction(SIGINT,  &sa, NULL);
	sigaction(SIGCHLD, &sa, NULL);
	sigaction(SIGUSR1, &sa, NULL);
	sigaction(SIGHUP,  &sa, NULL);

	if (xgetenv("XYMONSERVERLOGS")) {
		sprintf(acklogfn, "%s/acknowledge.log", xgetenv("XYMONSERVERLOGS"));
		acklogfd = fopen(acklogfn, "a");
		sprintf(notiflogfn, "%s/notifications.log", xgetenv("XYMONSERVERLOGS"));
		notiflogfd = fopen(notiflogfn, "a");
	}

	/*
	 * The general idea here is that this loop handles receiving of alert-
	 * and ack-messages from the master daemon, and maintains a list of 
	 * host+test combinations that may have alerts going out.
	 *
	 * This module does not deal with any specific alert-configuration, 
	 * it just picks up the alert messages, maintains the list of 
	 * known tests that are in some sort of critical condition, and
	 * periodically pushes alerts to the do_alert.c module for handling.
	 *
	 * The only modification of alerts that happen here is the handling
	 * of when the next alert is due. It calls into the next_alert() 
	 * routine to learn when an alert should be repeated, and also 
	 * deals with Acknowledgments that stop alerts from going out for
	 * a period of time.
	 */
	while (running) {
		char *eoln, *restofmsg;
		char *metadata[20];
		char *p;
		int metacount;
		char *hostname = NULL, *testname = NULL;
		struct timespec timeout;
		time_t now, nowtimer;
		int anytogo;
		activealerts_t *awalk;
		int childstat;

		nowtimer = gettimer();
		if (checkfn && (nowtimer > nextcheckpoint)) {
			dbgprintf("Saving checkpoint\n");
			nextcheckpoint = nowtimer + checkpointinterval;
			save_checkpoint(checkfn);

			if (acklogfd) acklogfd = freopen(acklogfn, "a", acklogfd);
			if (notiflogfd) notiflogfd = freopen(notiflogfn, "a", notiflogfd);
		}

		timeout.tv_sec = 60; timeout.tv_nsec = 0;
		msg = get_xymond_message(C_PAGE, "xymond_alert", &seq, &timeout);
		if (msg == NULL) {
			running = 0;
			continue;
		}

		/* See what time it is - must happen AFTER the timeout */
		now = getcurrenttime(NULL);

		/* 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 = "";
		}

		/* 
		 * Now parse the meta-data into elements.
		 * We use our own "gettok()" routine which works
		 * like strtok(), but can handle empty elements.
		 */
		metacount = 0; 
		memset(&metadata, 0, sizeof(metadata));
		p = gettok(msg, "|");
		while (p && (metacount < 19)) {
			metadata[metacount] = p;
			metacount++;
			p = gettok(NULL, "|");
		}
		metadata[metacount] = NULL;

		if (metacount > 3) hostname = metadata[3];
		if (metacount > 4) testname = metadata[4];

		if ((metacount > 10) && (strncmp(metadata[0], "@@page", 6) == 0)) {
			/* @@page|timestamp|sender|hostname|testname|hostip|expiretime|color|prevcolor|changetime|location|cookie|osname|classname|grouplist|modifiers */

			int newcolor, newalertstatus, oldalertstatus;

			dbgprintf("Got page message from %s:%s\n", hostname, testname);
			traceprintf("@@page %s:%s:%s=%s\n", hostname, testname, metadata[10], metadata[7]);

			awalk = find_active(hostname, testname);
			if (awalk == NULL) {
				char *hwalk = find_name(hostnames, hostname);
				char *twalk = find_name(testnames, testname);
				char *pwalk = find_name(locations, metadata[10]);

				awalk = (activealerts_t *)calloc(1, sizeof(activealerts_t));
				awalk->hostname = hwalk;
				awalk->testname = twalk;
				awalk->location = pwalk;
				awalk->cookie = -1;
				awalk->state = A_DEAD;
				/*
				 * Use changetime here, if we restart the alert module then
				 * this gets the duration values more right than using "now".
				 * Also, define this only when a new alert arrives - we should
				 * NOT clear this when a status goes yellow->red, or if it
				 * flaps between yellow and red.
				 */
				awalk->eventstart = atoi(metadata[9]);
				add_active(awalk->hostname, awalk);
				traceprintf("New record\n");
			}

			newcolor = parse_color(metadata[7]);
			oldalertstatus = ((alertcolors & (1 << awalk->color)) != 0);
			newalertstatus = ((alertcolors & (1 << newcolor)) != 0);

			traceprintf("state %d->%d\n", oldalertstatus, newalertstatus);

			if (newalertstatus) {
				/* It's in an alert state. */
				awalk->color = newcolor;
				awalk->state = A_PAGING;

				if (newcolor > awalk->maxcolor) {
					if (awalk->maxcolor != 0) {
						/*
						 * Severity has increased (yellow -> red).
						 * Clear the repeat-interval, and set maxcolor to
						 * the new color. If it drops to yellow again,
						 * maxcolor stays at red, so a test that flaps
						 * between yellow and red will only alert on red
						 * the first time, and then follow the repeat
						 * interval.
						 */
						dbgprintf("Severity increased, cleared repeat interval: %s/%s %s->%s\n",
							awalk->hostname, awalk->testname,
							colorname(awalk->maxcolor), colorname(newcolor));
						clear_interval(awalk);
					}

					awalk->maxcolor = newcolor;
				}
			}
			else {
				/* 
				 * Send one "recovered" message out now, then go to A_DEAD.
				 * Dont update the color here - we want recoveries to go out 
				 * only if the alert color triggered an alert
				 */
				awalk->state = (newcolor == COL_BLUE) ? A_DISABLED : A_RECOVERED;
			}

			if (oldalertstatus != newalertstatus) {
				dbgprintf("Alert status changed from %d to %d\n", oldalertstatus, newalertstatus);
				clear_interval(awalk);
			}

			if (awalk->ip) xfree(awalk->ip);
			awalk->ip = strdup(metadata[5]);
			awalk->cookie = atoi(metadata[11]);
			if (awalk->osname) xfree(awalk->osname);
			awalk->osname    = (metadata[12] ? strdup(metadata[12]) : NULL);
			if (awalk->classname) xfree(awalk->classname);
			awalk->classname = (metadata[13] ? strdup(metadata[13]) : NULL);
			if (awalk->groups) xfree(awalk->groups);
			awalk->groups    = (metadata[14] ? strdup(metadata[14]) : NULL);
			if (awalk->pagemessage) xfree(awalk->pagemessage);
			if (metadata[15]) {
				/* Modifiers are more interesting than the message itself */
				awalk->pagemessage = (char *)malloc(strlen(awalk->hostname) + strlen(awalk->testname) + strlen(colorname(awalk->color)) + strlen(metadata[15]) + strlen(restofmsg) + 10);
				sprintf(awalk->pagemessage, "%s:%s %s\n%s\n%s",
					awalk->hostname, awalk->testname, colorname(awalk->color), metadata[15], restofmsg);
			}
			else {
				awalk->pagemessage = strdup(restofmsg);
			}
		}
		else if ((metacount > 5) && (strncmp(metadata[0], "@@ack", 5) == 0)) {
 			/* @@ack|timestamp|sender|hostname|testname|hostip|expiretime */

			/*
			 * An ack is handled simply by setting the next
			 * alert-time to when the ack expires.
			 */
			time_t nextalert = atoi(metadata[6]);

			dbgprintf("Got ack message from %s:%s\n", hostname, testname);
			traceprintf("@@ack: %s:%s now=%d, ackeduntil %d\n",
				     hostname, testname, (int)now, (int)nextalert);

			awalk = find_active(hostname, testname);

			if (acklogfd) {
				int cookie = (awalk ? awalk->cookie : -1);
				int color  = (awalk ? awalk->color : 0);

				fprintf(acklogfd, "%d\t%d\t%d\t%d\t%s\t%s.%s\t%s\t%s\n",
					(int)now, cookie, 
					(int)((nextalert - now) / 60), cookie,
					"np_filename_not_used", 
					hostname, testname, 
					colorname(color),
					nlencode(restofmsg));
				fflush(acklogfd);
			}

			if (awalk && (awalk->state == A_PAGING)) {
				traceprintf("Record updated\n");
				awalk->state = A_ACKED;
				awalk->nextalerttime = nextalert;
				if (awalk->ackmessage) xfree(awalk->ackmessage);
				awalk->ackmessage = strdup(restofmsg);
			}
			else {
				traceprintf("No record\n");
			}
		}
		else if ((metacount > 4) && (strncmp(metadata[0], "@@notify", 5) == 0)) {
			/* @@notify|timestamp|sender|hostname|testname|pagepath */

			char *hwalk = find_name(hostnames, hostname);
			char *twalk = find_name(testnames, testname);
			char *pwalk = find_name(locations, (metadata[5] ? metadata[5] : ""));

			awalk = (activealerts_t *)calloc(1, sizeof(activealerts_t));
			awalk->hostname = hwalk;
			awalk->testname = twalk;
			awalk->location = pwalk;
			awalk->cookie = -1;
			awalk->pagemessage = strdup(restofmsg);
			awalk->eventstart = getcurrenttime(NULL);
			awalk->state = A_NOTIFY;
			add_active(awalk->hostname, awalk);
		}
		else if ((metacount > 3) && 
			 ((strncmp(metadata[0], "@@drophost", 10) == 0) || (strncmp(metadata[0], "@@dropstate", 11) == 0))) {
			/* @@drophost|timestamp|sender|hostname */
			/* @@dropstate|timestamp|sender|hostname */
			xtreePos_t handle;

			handle = xtreeFind(hostnames, hostname);
			if (handle != xtreeEnd(hostnames)) {
				alertanchor_t *anchor = (alertanchor_t *)xtreeData(hostnames, handle);
				for (awalk = anchor->head; (awalk); awalk = awalk->next) awalk->state = A_DEAD;
			}
		}
		else if ((metacount > 4) && (strncmp(metadata[0], "@@droptest", 10) == 0)) {
			/* @@droptest|timestamp|sender|hostname|testname */

			awalk = find_active(hostname, testname);
			if (awalk) awalk->state = A_DEAD;
		}
		else if ((metacount > 4) && (strncmp(metadata[0], "@@renamehost", 12) == 0)) {
			/* @@renamehost|timestamp|sender|hostname|newhostname */

			/* 
			 * We handle rename's simply by dropping the alert. If there is still an
			 * active alert for the host, it will have to be dealt with when the next
			 * status update arrives.
			 */
			xtreePos_t handle;

			handle = xtreeFind(hostnames, hostname);
			if (handle != xtreeEnd(hostnames)) {
				alertanchor_t *anchor = (alertanchor_t *)xtreeData(hostnames, handle);
				for (awalk = anchor->head; (awalk); awalk = awalk->next) awalk->state = A_DEAD;
			}
		}
		else if ((metacount > 5) && (strncmp(metadata[0], "@@renametest", 12) == 0)) {
			/* @@renametest|timestamp|sender|hostname|oldtestname|newtestname */

			/* 
			 * We handle rename's simply by dropping the alert. If there is still an
			 * active alert for the host, it will have to be dealt with when the next
			 * status update arrives.
			 */
			awalk = find_active(hostname, testname);
			if (awalk) awalk->state = A_DEAD;
		}
		else if (strncmp(metadata[0], "@@shutdown", 10) == 0) {
			running = 0;
			errprintf("Got a shutdown message\n");
			continue;
		}
		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);

				if (tracefn) {
					stoptrace();
					starttrace(tracefn);
				}
			}
			continue;
		}
		else if (strncmp(metadata[0], "@@reload", 8) == 0) {
			/* Nothing ... right now */
		}
		else if (strncmp(metadata[0], "@@idle", 6) == 0) {
			/* Timeout */
		}

		/*
		 * When a burst of alerts happen, we get lots of alert messages
		 * coming in quickly. So lets handle them in bunches and only 
		 * do the full alert handling once every 10 secs - that lets us
		 * combine a bunch of alerts into one transmission process.
		 */
		if (nowtimer < (lastxmit+10)) continue;
		lastxmit = nowtimer;

		/* 
		 * Loop through the activealerts list and see if anything is pending.
		 * This is an optimization, we could just as well just fork off the
		 * notification child and let it handle all of it. But there is no
		 * reason to fork a child process unless it is going to do something.
		 */
		configchanged = load_alertconfig(configfn, alertcolors, alertinterval);
		configchanged += load_holidays(0);
		anytogo = 0;
		for (awalk = alistBegin(); (awalk); awalk = alistNext()) {
			int anymatch = 0;

			switch (awalk->state) {
			  case A_NORECIP:
				if (!configchanged) break;

				/* The configuration has changed - switch NORECIP -> PAGING */
				awalk->state = A_PAGING;
				clear_interval(awalk);
				/* Fall through */

			  case A_PAGING:
				if (have_recipient(awalk, &anymatch)) {
					if (awalk->nextalerttime <= now) anytogo++;
				}
				else {
					if (!anymatch) {
						awalk->state = A_NORECIP;
						cleanup_alert(awalk);
					}
				}
				break;

			  case A_ACKED:
				if (awalk->nextalerttime <= now) {
					/* An ack has expired, so drop the ack message and switch to A_PAGING */
					anytogo++;
					if (awalk->ackmessage) xfree(awalk->ackmessage);
					awalk->state = A_PAGING;
				}
				break;

			  case A_RECOVERED:
			  case A_DISABLED:
			  case A_NOTIFY:
				anytogo++;
				break;

			  case A_DEAD:
				break;
			}
		}
		dbgprintf("%d alerts to go\n", anytogo);

		if (anytogo) {
			pid_t childpid;

			childpid = fork();
			if (childpid == 0) {
				/* The child */
				start_alerts();
				for (awalk = alistBegin(); (awalk); awalk = alistNext()) {
					switch (awalk->state) {
					  case A_PAGING:
						if (awalk->nextalerttime <= now) {
							send_alert(awalk, notiflogfd);
						}
						break;

					  case A_ACKED:
						/* Cannot be A_ACKED unless the ack is still valid, so no alert. */
						break;

					  case A_RECOVERED:
					  case A_DISABLED:
					  case A_NOTIFY:
						send_alert(awalk, notiflogfd);
						break;

					  case A_NORECIP:
					  case A_DEAD:
						break;
					}
				}
				finish_alerts();

				/* Child does not continue */
				exit(0);
			}
			else if (childpid < 0) {
				errprintf("Fork failed, cannot send alerts: %s\n", strerror(errno));
			}
		}

		/* Update the state flag and the next-alert timestamp */
		for (awalk = alistBegin(); (awalk); awalk = alistNext()) {
			switch (awalk->state) {
			  case A_PAGING:
				if (awalk->nextalerttime <= now) awalk->nextalerttime = next_alert(awalk);
				break;

			  case A_NORECIP:
				break;

			  case A_ACKED:
				/* Still cannot get here except if ack is still valid */
				break;

			  case A_RECOVERED:
			  case A_DISABLED:
			  case A_NOTIFY:
				awalk->state = A_DEAD;
				/* Fall through */

			  case A_DEAD:
				cleanup_alert(awalk); 
				break;
			}
		}

		clean_all_active();

		/* Pickup any finished child processes to avoid zombies */
		while (wait3(&childstat, WNOHANG, NULL) > 0) ;
	}

	if (checkfn) save_checkpoint(checkfn);
	if (acklogfd) fclose(acklogfd);
	if (notiflogfd) fclose(notiflogfd);
	stoptrace();

	MEMUNDEFINE(notiflogfn);
	MEMUNDEFINE(acklogfn);

	if (termsig >= 0) {
		errprintf("Terminated by signal %d\n", termsig);
	}

	return 0;
}
Example #3
0
int main(int argc, char *argv[])
{
	strbuffer_t *inbuf;
	char *ackbuf;
	char *subjectline = NULL;
	char *returnpathline = NULL;
	char *fromline = NULL;
	char *firsttxtline = NULL;
	int inheaders = 1;
	char *p;
	pcre *subjexp;
	const char *errmsg;
	int errofs, result;
	int ovector[30];
	char cookie[10];
	int duration = 0;
	int argi;
	char *envarea = NULL;

	for (argi=1; (argi < argc); argi++) {
		if (strcmp(argv[argi], "--debug") == 0) {
			debug = 1;
		}
		else if (argnmatch(argv[argi], "--env=")) {
			char *p = strchr(argv[argi], '=');
			loadenv(p+1, envarea);
		}
		else if (argnmatch(argv[argi], "--area=")) {
			char *p = strchr(argv[argi], '=');
			envarea = strdup(p+1);
		}
	}

	initfgets(stdin);
	inbuf = newstrbuffer(0);
	while (unlimfgets(inbuf, stdin)) {
		sanitize_input(inbuf, 0, 0);

		if (!inheaders) {
			/* We're in the message body. Look for a "delay=N" line here. */
			if ((strncasecmp(STRBUF(inbuf), "delay=", 6) == 0) || (strncasecmp(STRBUF(inbuf), "delay ", 6) == 0)) {
				duration = durationvalue(STRBUF(inbuf)+6);
				continue;
			}
			else if ((strncasecmp(STRBUF(inbuf), "ack=", 4) == 0) || (strncasecmp(STRBUF(inbuf), "ack ", 4) == 0)) {
				/* Some systems cannot generate a subject. Allow them to ack
				 * via text in the message body. */
				subjectline = (char *)malloc(STRBUFLEN(inbuf) + 1024);
				sprintf(subjectline, "Subject: Xymon [%s]", STRBUF(inbuf)+4);
			}
			else if (*STRBUF(inbuf) && !firsttxtline) {
				/* Save the first line of the message body, but ignore blank lines */
				firsttxtline = strdup(STRBUF(inbuf));
			}

			continue;	/* We don't care about the rest of the message body */
		}

		/* See if we're at the end of the mail headers */
		if (inheaders && (STRBUFLEN(inbuf) == 0)) { inheaders = 0; continue; }

		/* Is it one of those we want to keep ? */
		if (strncasecmp(STRBUF(inbuf), "return-path:", 12) == 0) returnpathline = strdup(skipwhitespace(STRBUF(inbuf)+12));
		else if (strncasecmp(STRBUF(inbuf), "from:", 5) == 0)    fromline = strdup(skipwhitespace(STRBUF(inbuf)+5));
		else if (strncasecmp(STRBUF(inbuf), "subject:", 8) == 0) subjectline = strdup(skipwhitespace(STRBUF(inbuf)+8));
	}
	freestrbuffer(inbuf);

	/* No subject ? No deal */
	if (subjectline == NULL) {
		dbgprintf("Subject-line not found\n");
		return 1;
	}

	/* Get the alert cookie */
	subjexp = pcre_compile(".*(Xymon|Hobbit|BB)[ -]* \\[*(-*[0-9]+)[\\]!]*", PCRE_CASELESS, &errmsg, &errofs, NULL);
	if (subjexp == NULL) {
		dbgprintf("pcre compile failed - 1\n");
		return 2;
	}
	result = pcre_exec(subjexp, NULL, subjectline, strlen(subjectline), 0, 0, ovector, (sizeof(ovector)/sizeof(int)));
	if (result < 0) {
		dbgprintf("Subject line did not match pattern\n");
		return 3; /* Subject did not match what we expected */
	}
	if (pcre_copy_substring(subjectline, ovector, result, 2, cookie, sizeof(cookie)) <= 0) {
		dbgprintf("Could not find cookie value\n");
		return 4; /* No cookie */
	}
	pcre_free(subjexp);

	/* See if there's a "DELAY=" delay-value also */
	subjexp = pcre_compile(".*DELAY[ =]+([0-9]+[mhdw]*)", PCRE_CASELESS, &errmsg, &errofs, NULL);
	if (subjexp == NULL) {
		dbgprintf("pcre compile failed - 2\n");
		return 2;
	}
	result = pcre_exec(subjexp, NULL, subjectline, strlen(subjectline), 0, 0, ovector, (sizeof(ovector)/sizeof(int)));
	if (result >= 0) {
		char delaytxt[4096];
		if (pcre_copy_substring(subjectline, ovector, result, 1, delaytxt, sizeof(delaytxt)) > 0) {
			duration = durationvalue(delaytxt);
		}
	}
	pcre_free(subjexp);

	/* See if there's a "msg" text also */
	subjexp = pcre_compile(".*MSG[ =]+(.*)", PCRE_CASELESS, &errmsg, &errofs, NULL);
	if (subjexp == NULL) {
		dbgprintf("pcre compile failed - 3\n");
		return 2;
	}
	result = pcre_exec(subjexp, NULL, subjectline, strlen(subjectline), 0, 0, ovector, (sizeof(ovector)/sizeof(int)));
	if (result >= 0) {
		char msgtxt[4096];
		if (pcre_copy_substring(subjectline, ovector, result, 1, msgtxt, sizeof(msgtxt)) > 0) {
			firsttxtline = strdup(msgtxt);
		}
	}
	pcre_free(subjexp);

	/* Use the "return-path:" header if we didn't see a From: line */
	if ((fromline == NULL) && returnpathline) fromline = returnpathline;
	if (fromline) {
		/* Remove '<' and '>' from the fromline - they mess up HTML */
		while ((p = strchr(fromline, '<')) != NULL) *p = ' ';
		while ((p = strchr(fromline, '>')) != NULL) *p = ' ';
	}

	/* Setup the acknowledge message */
	if (duration == 0) duration = 60;	/* Default: Ack for 60 minutes */
	if (firsttxtline == NULL) firsttxtline = "<No cause specified>";
	ackbuf = (char *)malloc(4096 + strlen(firsttxtline) + (fromline ? strlen(fromline) : 0));
	p = ackbuf;
	p += sprintf(p, "xymondack %s %d %s", cookie, duration, firsttxtline);
	if (fromline) {
		p += sprintf(p, "\nAcked by: %s", fromline);
	}

	if (debug) {
		printf("%s\n", ackbuf);
		return 0;
	}

	sendmessage(ackbuf, NULL, XYMON_TIMEOUT, NULL);
	return 0;
}
Example #4
0
static void parse_query(void)
{
	cgidata_t *cwalk;

	/* See what kind of request this is */
	for (cwalk=cgidata; (cwalk); cwalk = cwalk->next) {
		if (nopin && (strcmp(cwalk->name, "Send_all") == 0)) {
			/* User pushed the "Send all" button */
			reqtype = ACK_MANY;
		}
		else if (nopin && (strncmp(cwalk->name, "Send_", 5) == 0)) {
			/* User pushed a specific "Send" button */
			sendnum = atoi(cwalk->name+5);
			reqtype = ACK_ONE;
		}
		else if (!nopin && (strcmp(cwalk->name, "Send") == 0)) {
			/* Old style request */
			reqtype = ACK_OLDSTYLE;
		}
	}

	for (cwalk=cgidata; (cwalk); cwalk = cwalk->next) {
		/*
		 * cwalk->name points to the name of the setting.
		 * cwalk->value points to the value (may be an empty string).
		 */
		int id = 0;
		char *acknum = NULL, *validity = NULL, *ackmsg = NULL, *period = NULL;
		char *hostname = NULL, *testname = NULL, *checked = NULL;
		char *delim;

		if (strncasecmp(cwalk->name, "NUMBER", 6) == 0) {
			if (*cwalk->value) acknum = cwalk->value;
			delim = strchr(cwalk->name, '_'); if (delim) id = atoi(delim+1);
		}
		else if (strcasecmp(cwalk->name, "DELAY_all") == 0) {
			if (*cwalk->value) validityall = cwalk->value;
		}
		else if (strcasecmp(cwalk->name, "PERIOD_all") == 0) {
			if (*cwalk->value) periodall = cwalk->value;
		}
		else if (strcasecmp(cwalk->name, "MESSAGE_all") == 0) {
			if (*cwalk->value) ackmsgall = cwalk->value;
		}
		else if (strncasecmp(cwalk->name, "DELAY", 5) == 0) {
			if (*cwalk->value) validity = cwalk->value;
			delim = strchr(cwalk->name, '_'); if (delim) id = atoi(delim+1);
		}
		else if (strncasecmp(cwalk->name, "PERIOD", 5) == 0) {
			if (*cwalk->value) period = cwalk->value;
			delim = strchr(cwalk->name, '_'); if (delim) id = atoi(delim+1);
		}
		else if (strncasecmp(cwalk->name, "MESSAGE", 7) == 0) {
			if (*cwalk->value) ackmsg = cwalk->value;
			delim = strchr(cwalk->name, '_'); if (delim) id = atoi(delim+1);
		}
		else if (strncasecmp(cwalk->name, "HOSTNAME", 8) == 0) {
			if (*cwalk->value) hostname = cwalk->value;
			delim = strchr(cwalk->name, '_'); if (delim) id = atoi(delim+1);
		}
		else if (strncasecmp(cwalk->name, "TESTNAME", 8) == 0) {
			if (*cwalk->value) testname = cwalk->value;
			delim = strchr(cwalk->name, '_'); if (delim) id = atoi(delim+1);
		}
		else if (strncasecmp(cwalk->name, "CHECKED", 7) == 0) {
			if (*cwalk->value) checked = cwalk->value;
			delim = strchr(cwalk->name, '_'); if (delim) id = atoi(delim+1);
		}

		switch (reqtype) {
		  case ACK_UNKNOWN:
			break;

		  case ACK_OLDSTYLE:
			id = 1;
			/* Fall through */
		  case ACK_ONE:
			if ((id == sendnum) || (reqtype == ACK_OLDSTYLE)) checked = "checked";
			/* Fall through */
		  case ACK_MANY:
			if (id) {
				acklist_t *awalk;

				awalk = ackhead; while (awalk && (awalk->id != id)) awalk = awalk->next;
				if (!awalk) {
					awalk = (acklist_t *)calloc(1, sizeof(acklist_t));
					awalk->id = id;
					awalk->next = NULL;

					if (!ackhead) ackhead = acktail = awalk;
					else { acktail->next = awalk; acktail = awalk; }
				}

				if (acknum) awalk->acknum = atoi(acknum);
				if (validity) awalk->validity = durationvalue(validity);
				if (ackmsg) awalk->ackmsg = strdup(ackmsg);
				if (hostname) awalk->hostname = strdup(hostname);
				if (testname) awalk->testname = strdup(testname);
				if (period) awalk->period = strdup(period);
				if (checked) awalk->checked = 1;
			}
			break;
		}
	}
}
Example #5
0
int main(int argc, char *argv[])
{
	int argi;
	char *envarea = NULL;
	int obeycookies = 1;
	char *accessfn = NULL;

	for (argi = 1; (argi < argc); argi++) {
		if (argnmatch(argv[argi], "--env=")) {
			char *p = strchr(argv[argi], '=');
			loadenv(p+1, envarea);
		}
		else if (argnmatch(argv[argi], "--area=")) {
			char *p = strchr(argv[argi], '=');
			envarea = strdup(p+1);
		}
		else if (strcmp(argv[argi], "--debug") == 0) {
			debug = 1;
		}
		else if (strcmp(argv[argi], "--no-pin") == 0) {
			nopin = 1;
		}
		else if (strcmp(argv[argi], "--no-cookies") == 0) {
			obeycookies = 0;
		}
		else if (argnmatch(argv[argi], "--access=")) {
			char *p = strchr(argv[argi], '=');
			accessfn = strdup(p+1);
		}

	}

	redirect_cgilog("ack");

	cgidata = cgi_request();
	if ( (nopin && (cgi_method == CGI_GET)) || (!nopin && (cgidata == NULL)) ) {
		/* Present the query form */
		sethostenv("", "", "", colorname(COL_RED), NULL);

		printf("Content-Type: %s\n\n", xgetenv("HTMLCONTENTTYPE"));

		if (!nopin) {
			showform(stdout, "acknowledge", "acknowledge_form", COL_RED, getcurrenttime(NULL), 
				 NULL, NULL);
		}
		else {
			char *cmd;
			char *respbuf = NULL;
			char *hostname, *pagename;
			int gotfilter = 0, filtererror = 0;
			sendreturn_t *sres = NULL;
			int col, firstcolor = 1, alertcolors = colorset(xgetenv("ALERTCOLORS"), ((1 << COL_GREEN) | (1 << COL_BLUE)));

			headfoot(stdout, "acknowledge", "", "header", COL_RED);

			cmd = (char *)malloc(1024);
			strcpy(cmd, "xymondboard fields=hostname,testname,cookie color=");
			for (col = 0; (col < COL_COUNT); col++) {
				if ((1 << col) & alertcolors) {
					if (!firstcolor) strcat(cmd, ",");
					strcat(cmd, colorname(col));
					firstcolor = 0;
				}
			}
			// printf("<!-- cmd = %s -->\n", cmd);

			if (obeycookies && !gotfilter && ((hostname = get_cookie("host")) != NULL)) {
				if (*hostname) {
					pcre *dummy;
					char *re;
					
					re = (char *)malloc(3+strlen(hostname));
					sprintf(re, "^%s$", hostname);
					dummy = compileregex(re);
					if (dummy) {
						/* Valid expression */
						freeregex(dummy);
						cmd = (char *)realloc(cmd, 1024 + strlen(cmd) + strlen(re));
						sprintf(cmd + strlen(cmd), " host=%s", re);
						gotfilter = 1;
					}
					else {
						filtererror = 1;
						printf("<p align=\"center\">Invalid hostname filter</p>\n");
					}
				}
			}

			if (obeycookies && !gotfilter && ((pagename = get_cookie("pagepath")) != NULL)) {
				if (*pagename) {
					pcre *dummy;
					char *re;

					re = (char *)malloc(8 + strlen(pagename)*2);
					sprintf(re, "%s$|^%s/.+", pagename, pagename);
					dummy = compileregex(re);
					if (dummy) {
						/* Valid expression */
						freeregex(dummy);
						cmd = (char *)realloc(cmd, 1024 + strlen(cmd) + strlen(re));
						sprintf(cmd + strlen(cmd), " page=%s", re);
						gotfilter = 1;
					}
					else {
						filtererror = 1;
						printf("<p align=\"center\">Invalid pagename filter</p>\n");
					}
				}
			}

			sres = newsendreturnbuf(1, NULL);

			if (!filtererror && (sendmessage(cmd, NULL, XYMON_TIMEOUT, sres) == XYMONSEND_OK)) {
				char *bol, *eoln;
				int first = 1;

				respbuf = getsendreturnstr(sres, 1);

				bol = respbuf;
				while (bol) {
					char *hname, *tname, *ackcode;

					eoln = strchr(bol, '\n'); if (eoln) *eoln = '\0';
					hname = tname = ackcode = NULL;
					hname = strtok(bol, "|");
					if (hname) tname = strtok(NULL, "|");
					if (tname) ackcode = strtok(NULL, "|");
					if (hname && tname && ackcode && (strcmp(hname, "summary") != 0)) {
						if (first) {
							fprintf(stdout, "<form method=\"POST\" ACTION=\"%s\">\n", getenv("SCRIPT_NAME"));
							fprintf(stdout, "<center><table cellpadding=5 summary=\"Ack data\">\n");
							fprintf(stdout, "<tr><th align=left>Host</th><th align=left>Test</th><th align=left>Duration</th><th align=left>Cause</th><th>Ack</th><th>Ack Multiple</tr>\n");
							first = 0;
						}

						generate_ackline(stdout, hname, tname, ackcode);
					}

					if (eoln) bol = eoln+1; else bol = NULL;
				}

				if (first) {
					fprintf(stdout, "<center><font size=\"+1\"><b>No active alerts</b></font></center>\n");
				}
				else {
					generate_ackline(stdout, NULL, NULL, NULL);
					fprintf(stdout, "</table></center>\n");
					fprintf(stdout, "</form>\n");
				}
			}

			freesendreturnbuf(sres);

			headfoot(stdout, "acknowledge", "", "footer", COL_RED);
		}
	}
	else if ( (nopin && (cgi_method == CGI_POST)) || (!nopin && (cgidata != NULL)) ) {
		char *xymonmsg;
		char *acking_user = "";
		acklist_t *awalk;
		strbuffer_t *response = newstrbuffer(0);
		int count = 0;


		/* We only want to accept posts from certain pages */
		{ 
			char cgisource[1024]; char *p;
			p = csp_header("acknowledge"); if (p) fprintf(stdout, "%s", p);
			snprintf(cgisource, sizeof(cgisource), "%s/%s", xgetenv("SECURECGIBINURL"), "acknowledge");
			if (!cgi_refererok(cgisource)) {
				fprintf(stdout, "Location: %s.sh?\n\n", cgisource);
				return 0;
			}
		}

		parse_query();
		if (getenv("REMOTE_USER")) {
			char *remaddr = getenv("REMOTE_ADDR");

			acking_user = (char *)malloc(1024 + strlen(getenv("REMOTE_USER")) + (remaddr ? strlen(remaddr) : 0));
			sprintf(acking_user, "\nAcked by: %s", getenv("REMOTE_USER"));
			if (remaddr) sprintf(acking_user + strlen(acking_user), " (%s)", remaddr);
		}

		/* Load the host data (for access control) */
		if (accessfn) {
			load_hostnames(xgetenv("HOSTSCFG"), NULL, get_fqdn());
			load_web_access_config(accessfn);
		}

		addtobuffer(response, "<center>\n");
		for (awalk = ackhead; (awalk); awalk = awalk->next) {
			char *msgline = (char *)malloc(1024 + (awalk->hostname ? strlen(awalk->hostname) : 0) + (awalk->testname ? strlen(awalk->testname) : 0));

			if (!awalk->checked) continue;
			if (accessfn && (!web_access_allowed(getenv("REMOTE_USER"), awalk->hostname, awalk->testname, WEB_ACCESS_CONTROL))) continue;

			if ((reqtype == ACK_ONE) && (awalk->id != sendnum)) continue;

			if (reqtype == ACK_MANY) {
				if (!awalk->ackmsg) awalk->ackmsg = ackmsgall;
				if (!awalk->validity && validityall) awalk->validity = durationvalue(validityall);
				if (periodall) awalk->period = periodall;
			}

			if (strncmp(awalk->period, "hour", 4) == 0) awalk->validity *= 60; 
			else if (strncmp(awalk->period, "day", 4) == 0) awalk->validity *= 60*24;

			count++;
			if (!awalk->ackmsg || !awalk->validity || !awalk->acknum) {
				if (awalk->hostname && awalk->testname) {
					sprintf(msgline, "<b>NO ACK</b> sent for host %s / test %s",
						htmlquoted(awalk->hostname), htmlquoted(awalk->testname));
				}
				else {
					sprintf(msgline, "<b>NO ACK</b> sent for item %d", awalk->id);
				}
				addtobuffer(response, msgline);
				addtobuffer(response, ": Duration or message not set<br>\n");
				continue;
			}

			xymonmsg = (char *)malloc(1024 + strlen(awalk->ackmsg) + strlen(acking_user));
			sprintf(xymonmsg, "xymondack %d %d %s %s", awalk->acknum, awalk->validity, awalk->ackmsg, acking_user);
			if (sendmessage(xymonmsg, NULL, XYMON_TIMEOUT, NULL) == XYMONSEND_OK) {
				if (awalk->hostname && awalk->testname) {
					sprintf(msgline, "Acknowledge sent for host %s / test %s<br>\n", 
						htmlquoted(awalk->hostname), htmlquoted(awalk->testname));
				}
				else {
					sprintf(msgline, "Acknowledge sent for code %d<br>\n", awalk->acknum);
				}
			}
			else {
				if (awalk->hostname && awalk->testname) {
					sprintf(msgline, "Failed to send acknowledge for host %s / test %s<br>\n", 
						htmlquoted(awalk->hostname), htmlquoted(awalk->testname));
				}
				else {
					sprintf(msgline, "Failed to send acknowledge for code %d<br>\n", awalk->acknum);
				}
			}

			addtobuffer(response, msgline);
			xfree(xymonmsg);
		}

		if (count == 0) addtobuffer(response, "<b>No acks requested</b>\n");

		addtobuffer(response, "</center>\n");

		fprintf(stdout, "Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE"));
	
		headfoot(stdout, "acknowledge", "", "header", COL_RED);
		fprintf(stdout, "%s", STRBUF(response));
		headfoot(stdout, "acknowledge", "", "footer", COL_RED);
	}

	return 0;
}
Example #6
0
int load_alertconfig(char *configfn, int defcolors, int defaultinterval)
{
	/* (Re)load the configuration file without leaking memory */
	static void *configfiles = NULL;
	char fn[PATH_MAX];
	FILE *fd;
	strbuffer_t *inbuf;
	char *p;
	rule_t *currule = NULL;
	recip_t *currcp = NULL, *rcptail = NULL;

	MEMDEFINE(fn);

	if (configfn) strcpy(fn, configfn); else sprintf(fn, "%s/etc/alerts.cfg", xgetenv("XYMONHOME"));

	/* First check if there were no modifications at all */
	if (configfiles) {
		if (!stackfmodified(configfiles)){
			dbgprintf("No files modified, skipping reload of %s\n", fn);
			MEMUNDEFINE(fn); 
			return 0;
		}
		else {
			stackfclist(&configfiles);
			configfiles = NULL;
		}
	}

	fd = stackfopen(fn, "r", &configfiles);
	if (!fd) { 
		errprintf("Cannot open configuration file %s: %s\n", fn, strerror(errno));
		MEMUNDEFINE(fn); 
		return 0; 
	}

	/* First, clean out the old rule set */
	while (rulehead) {
		rule_t *trule;

		if (rulehead->criteria) {
			free_criteria(rulehead->criteria);
			xfree(rulehead->criteria);
		}

		while (rulehead->recipients) {
			recip_t *trecip = rulehead->recipients;

			if (trecip->criteria) {
				recip_t *rwalk;

				/* Clear out the duplicate criteria that may exist, to avoid double-free'ing them */
				for (rwalk = trecip->next; (rwalk); rwalk = rwalk->next) {
					if (rwalk->criteria == trecip->criteria) rwalk->criteria = NULL;
				}

				free_criteria(trecip->criteria);
				xfree(trecip->criteria);
			}

			if (trecip->recipient)  xfree(trecip->recipient);
			if (trecip->scriptname) xfree(trecip->scriptname);
			rulehead->recipients = rulehead->recipients->next;
			xfree(trecip);
		}
		trule = rulehead;
		rulehead = rulehead->next;
		xfree(trule);
	}

	while (tokhead) {
		token_t *ttok;

		if (tokhead->name)  xfree(tokhead->name);
		if (tokhead->value) xfree(tokhead->value);
		ttok = tokhead;
		tokhead = tokhead->next;
		xfree(ttok);
	}

	defaultcolors = defcolors;

	MEMDEFINE(cfline);

	cfid = 0;
	inbuf = newstrbuffer(0);
	while (stackfgets(inbuf, NULL)) {
		int firsttoken = 1;
		int mailcmdactive = 0, scriptcmdactive = 0;
		recip_t *curlinerecips = NULL;

		cfid++;
		sanitize_input(inbuf, 1, 0);

		/* Skip empty lines */
		if (STRBUFLEN(inbuf) == 0) continue;

		if ((*STRBUF(inbuf) == '$') && strchr(STRBUF(inbuf), '=')) {
			/* Define a macro */
			token_t *newtok = (token_t *) malloc(sizeof(token_t));
			char *delim;

			delim = strchr(STRBUF(inbuf), '=');
			*delim = '\0';
			newtok->name = strdup(STRBUF(inbuf)+1);	/* Skip the '$' */
			newtok->value = strdup(preprocess(delim+1));
			newtok->next = tokhead;
			tokhead = newtok;
			continue;
		}

		strncpy(cfline, STRBUF(inbuf), (sizeof(cfline)-1));
		cfline[sizeof(cfline)-1] = '\0';

		/* Expand macros inside the line before parsing */
		p = strtok(preprocess(STRBUF(inbuf)), " \t");
		while (p) {
			if ((strncasecmp(p, "PAGE=", 5) == 0) || (strncasecmp(p, "PAGES=", 6) == 0)) {
				char *val;
				criteria_t *crit;

				if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
				val = strchr(p, '=')+1;
				crit = setup_criteria(&currule, &currcp);
				crit->pagespec = strdup(val);
				if (*(crit->pagespec) == '%') crit->pagespecre = compileregex(crit->pagespec+1);
				firsttoken = 0;
			}
			else if ((strncasecmp(p, "EXPAGE=", 7) == 0) || (strncasecmp(p, "EXPAGES=", 8) == 0)) {
				char *val;
				criteria_t *crit;

				if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
				val = strchr(p, '=')+1;
				crit = setup_criteria(&currule, &currcp);
				crit->expagespec = strdup(val);
				if (*(crit->expagespec) == '%') crit->expagespecre = compileregex(crit->expagespec+1);
				firsttoken = 0;
			}
			else if ((strncasecmp(p, "DISPLAYGROUP=", 13) == 0) || (strncasecmp(p, "DISPLAYGROUPS=", 14) == 0)) {
				char *val;
				criteria_t *crit;

				if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
				val = strchr(p, '=')+1;
				crit = setup_criteria(&currule, &currcp);
				crit->dgspec = strdup(val);
				if (*(crit->dgspec) == '%') crit->dgspecre = compileregex(crit->dgspec+1);
				firsttoken = 0;
			}
			else if ((strncasecmp(p, "EXDISPLAYGROUP=", 15) == 0) || (strncasecmp(p, "EXDISPLAYGROUPS=", 16) == 0)) {
				char *val;
				criteria_t *crit;

				if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
				val = strchr(p, '=')+1;
				crit = setup_criteria(&currule, &currcp);
				crit->exdgspec = strdup(val);
				if (*(crit->exdgspec) == '%') crit->exdgspecre = compileregex(crit->exdgspec+1);
				firsttoken = 0;
			}
			else if ((strncasecmp(p, "HOST=", 5) == 0) || (strncasecmp(p, "HOSTS=", 6) == 0)) {
				char *val;
				criteria_t *crit;

				if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
				val = strchr(p, '=')+1;
				crit = setup_criteria(&currule, &currcp);
				crit->hostspec = strdup(val);
				if (*(crit->hostspec) == '%') crit->hostspecre = compileregex(crit->hostspec+1);
				firsttoken = 0;
			}
			else if ((strncasecmp(p, "EXHOST=", 7) == 0) || (strncasecmp(p, "EXHOSTS=", 8) == 0)) {
				char *val;
				criteria_t *crit;

				if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
				val = strchr(p, '=')+1;
				crit = setup_criteria(&currule, &currcp);
				crit->exhostspec = strdup(val);
				if (*(crit->exhostspec) == '%') crit->exhostspecre = compileregex(crit->exhostspec+1);
				firsttoken = 0;
			}
			else if ((strncasecmp(p, "SERVICE=", 8) == 0) || (strncasecmp(p, "SERVICES=", 9) == 0)) {
				char *val;
				criteria_t *crit;

				if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
				val = strchr(p, '=')+1;
				crit = setup_criteria(&currule, &currcp);
				crit->svcspec = strdup(val);
				if (*(crit->svcspec) == '%') crit->svcspecre = compileregex(crit->svcspec+1);
				firsttoken = 0;
			}
			else if ((strncasecmp(p, "EXSERVICE=", 10) == 0) || (strncasecmp(p, "EXSERVICES=", 11) == 0)) {
				char *val;
				criteria_t *crit;

				if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
				val = strchr(p, '=')+1;
				crit = setup_criteria(&currule, &currcp);
				crit->exsvcspec = strdup(val);
				if (*(crit->exsvcspec) == '%') crit->exsvcspecre = compileregex(crit->exsvcspec+1);
				firsttoken = 0;
			}
			else if (strncasecmp(p, "CLASS=", 6) == 0) {
				char *val;
				criteria_t *crit;

				if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
				val = strchr(p, '=')+1;
				crit = setup_criteria(&currule, &currcp);
				crit->classspec = strdup(val);
				if (*(crit->classspec) == '%') crit->classspecre = compileregex(crit->classspec+1);
				firsttoken = 0;
			}
			else if (strncasecmp(p, "EXCLASS=", 8) == 0) {
				char *val;
				criteria_t *crit;

				if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
				val = strchr(p, '=')+1;
				crit = setup_criteria(&currule, &currcp);
				crit->exclassspec = strdup(val);
				if (*(crit->exclassspec) == '%') crit->exclassspecre = compileregex(crit->exclassspec+1);
				firsttoken = 0;
			}
			else if (strncasecmp(p, "GROUP=", 6) == 0) {
				char *val;
				criteria_t *crit;

				if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
				val = strchr(p, '=')+1;
				crit = setup_criteria(&currule, &currcp);
				crit->groupspec = strdup(val);
				if (*(crit->groupspec) == '%') crit->groupspecre = compileregex(crit->groupspec+1);
				firsttoken = 0;
			}
			else if (strncasecmp(p, "EXGROUP=", 8) == 0) {
				char *val;
				criteria_t *crit;

				if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
				val = strchr(p, '=')+1;
				crit = setup_criteria(&currule, &currcp);
				crit->exgroupspec = strdup(val);
				if (*(crit->exgroupspec) == '%') crit->exgroupspecre = compileregex(crit->exgroupspec+1);
				firsttoken = 0;
			}
			else if ((strncasecmp(p, "COLOR=", 6) == 0) || (strncasecmp(p, "COLORS=", 7) == 0)) {
				criteria_t *crit;
				char *c1, *c2;
				int cval, reverse = 0;

				if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
				crit = setup_criteria(&currule, &currcp);

				/* Put a value in crit->colors so we know there is an explicit color setting */
				crit->colors = (1 << 30);
				c1 = strchr(p, '=')+1;

				/*
				 * If the first colorspec is "!color", then apply the default colors and
				 * subtract colors from that.
				 */
				if (*c1 == '!') crit->colors |= defaultcolors;

				do {
					c2 = strchr(c1, ',');
					if (c2) *c2 = '\0';

					if (*c1 == '!') { reverse=1; c1++; }
					cval = (1 << parse_color(c1));

					if (reverse)
						crit->colors &= (~cval);
					else 
						crit->colors |= cval;

					if (c2) c1 = (c2+1); else c1 = NULL;
				} while (c1);
				firsttoken = 0;
			}
			else if ((strncasecmp(p, "TIME=", 5) == 0) || (strncasecmp(p, "TIMES=", 6) == 0)) {
				char *val;
				criteria_t *crit;

				if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
				val = strchr(p, '=')+1;
				crit = setup_criteria(&currule, &currcp);
				crit->timespec = strdup(val);
				firsttoken = 0;
			}
			else if (strncasecmp(p, "DURATION", 8) == 0) {
				criteria_t *crit;

				if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
				crit = setup_criteria(&currule, &currcp);
				if (*(p+8) == '>') crit->minduration = 60*durationvalue(p+9);
				else if (*(p+8) == '<') crit->maxduration = 60*durationvalue(p+9);
				else errprintf("Ignoring invalid DURATION at line %d: %s\n",cfid, p);
				firsttoken = 0;
			}
			else if (strncasecmp(p, "RECOVERED", 9) == 0) {
				criteria_t *crit;

				if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
				crit = setup_criteria(&currule, &currcp);
				crit->sendrecovered = SR_WANTED;
				firsttoken = 0;
			}
			else if (strncasecmp(p, "NORECOVERED", 11) == 0) {
				criteria_t *crit;

				if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
				crit = setup_criteria(&currule, &currcp);
				crit->sendrecovered = SR_NOTWANTED;
				firsttoken = 0;
			}
			else if (strncasecmp(p, "NOTICE", 6) == 0) {
				criteria_t *crit;

				if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
				crit = setup_criteria(&currule, &currcp);
				crit->sendnotice = SR_WANTED;
				firsttoken = 0;
			}
			else if (strncasecmp(p, "NONOTICE", 8) == 0) {
				criteria_t *crit;

				if (firsttoken) { flush_rule(currule); currule = NULL; currcp = NULL; pstate = P_NONE; }
				crit = setup_criteria(&currule, &currcp);
				crit->sendnotice = SR_NOTWANTED;
				firsttoken = 0;
			}
			else if ((pstate == P_RECIP) && (strncasecmp(p, "FORMAT=", 7) == 0)) {
				if (!currcp) errprintf("FORMAT used without a recipient (line %d), ignored\n", cfid);
				else if (strcasecmp(p+7, "TEXT") == 0) currcp->format = ALERTFORM_TEXT;
				else if (strcasecmp(p+7, "PLAIN") == 0) currcp->format = ALERTFORM_PLAIN;
				else if (strcasecmp(p+7, "SMS") == 0) currcp->format = ALERTFORM_SMS;
				else if (strcasecmp(p+7, "PAGER") == 0) currcp->format = ALERTFORM_PAGER;
				else if (strcasecmp(p+7, "SCRIPT") == 0) currcp->format = ALERTFORM_SCRIPT;
				else errprintf("Unknown FORMAT setting '%s' ignored\n", p);
				firsttoken = 0;
			}
			else if ((pstate == P_RECIP) && (strncasecmp(p, "REPEAT=", 7) == 0)) {
				if (!currcp) errprintf("REPEAT used without a recipient (line %d), ignored\n", cfid);
				else currcp->interval = 60*durationvalue(p+7);
				firsttoken = 0;
			}
			else if ((pstate == P_RECIP) && (strcasecmp(p, "STOP") == 0)) {
				if (!currcp) errprintf("STOP used without a recipient (line %d), ignored\n", cfid);
				else currcp->stoprule = 1;
				firsttoken = 0;
			}
			else if ((pstate == P_RECIP) && (strcasecmp(p, "UNMATCHED") == 0)) {
				if (!currcp) errprintf("UNMATCHED used without a recipient (line %d), ignored\n", cfid);
				else currcp->unmatchedonly = 1;
				firsttoken = 0;
			}
			else if ((pstate == P_RECIP) && (strncasecmp(p, "NOALERT", 7) == 0)) {
				if (!currcp) errprintf("NOALERT used without a recipient (line %d), ignored\n", cfid);
				else currcp->noalerts = 1;
				firsttoken = 0;
			}
			else if (currule && ((strncasecmp(p, "MAIL", 4) == 0) || mailcmdactive) ) {
				recip_t *newrcp;

				mailcmdactive = 1;
				newrcp = (recip_t *)calloc(1, sizeof(recip_t));
				newrcp->cfid = cfid;
				newrcp->method = M_MAIL;
				newrcp->format = ALERTFORM_TEXT;

				if (strncasecmp(p, "MAIL=", 5) == 0) {
					p += 5;
				}
				else if (strcasecmp(p, "MAIL") == 0) {
					p = strtok(NULL, " \t");
				}
				else {
					/* Second recipient on a rule - do nothing */
				}

				if (p) {
					newrcp->recipient = strdup(p);
					newrcp->interval = defaultinterval;
					currcp = newrcp;
					if (curlinerecips == NULL) curlinerecips = newrcp;
					pstate = P_RECIP;

					if (currule->recipients == NULL)
						currule->recipients = rcptail = newrcp;
					else {
						rcptail->next = newrcp;
						rcptail = newrcp;
					}
				}
				else {
					errprintf("Ignoring MAIL with no recipient at line %d\n", cfid);
					xfree(newrcp);
				}
				firsttoken = 0;
			}
			else if (currule && ((strncasecmp(p, "SCRIPT", 6) == 0) || scriptcmdactive)) {
				recip_t *newrcp;

				scriptcmdactive = 1;
				newrcp = (recip_t *)calloc(1, sizeof(recip_t));
				newrcp->cfid = cfid;
				newrcp->method = M_SCRIPT;
				newrcp->format = ALERTFORM_SCRIPT;

				if (strncasecmp(p, "SCRIPT=", 7) == 0) {
					p += 7;
					newrcp->scriptname = strdup(p);
					p = strtok(NULL, " \t");
				}
				else if (strcasecmp(p, "SCRIPT") == 0) {
					p = strtok(NULL, " \t");
					if (p) {
						newrcp->scriptname = strdup(p);
						p = strtok(NULL, " \t");
					}
					else {
						errprintf("Invalid SCRIPT command at line %d\n", cfid);
					}
				}
				else {
					/* A second recipient for the same script as the previous one */
					newrcp->scriptname = strdup(currcp->scriptname);
				}

				if (p) {
					newrcp->recipient = strdup(p);
					newrcp->interval = defaultinterval;
					currcp = newrcp;
					if (curlinerecips == NULL) curlinerecips = newrcp;
					pstate = P_RECIP;

					if (currule->recipients == NULL)
						currule->recipients = rcptail = newrcp;
					else {
						rcptail->next = newrcp;
						rcptail = newrcp;
					}
				}
				else {
					errprintf("Ignoring SCRIPT with no recipient at line %d\n", cfid);
					if (newrcp->scriptname) xfree(newrcp->scriptname);
					xfree(newrcp);
				}
				firsttoken = 0;
			}
			else if (currule && (strncasecmp(p, "IGNORE", 6) == 0)) {
				recip_t *newrcp;

				newrcp = (recip_t *)calloc(1, sizeof(recip_t));
				newrcp->cfid = cfid;
				newrcp->method = M_IGNORE;
				newrcp->format = ALERTFORM_NONE;
				newrcp->interval = defaultinterval;
				newrcp->stoprule = 1;
				currcp = newrcp;
				if (curlinerecips == NULL) curlinerecips = newrcp;
				pstate = P_RECIP;

				if (currule->recipients == NULL)
					currule->recipients = rcptail = newrcp;
				else {
					rcptail->next = newrcp;
					rcptail = newrcp;
				}

				firsttoken = 0;
			}
			else {
				errprintf("Ignored unknown/unexpected token '%s' at line %d\n", p, cfid);
			}

			if (p) p = strtok(NULL, " \t");
		}

		if (curlinerecips && currcp && (curlinerecips != currcp)) {
			/* We have multiple recipients on one line. Make sure criteria etc. get copied */
			recip_t *rwalk;

			/* All criteria etc. have been set on the last recipient (currcp) */
			for (rwalk = curlinerecips; (rwalk != currcp); rwalk = rwalk->next) {
				rwalk->format = currcp->format;
				rwalk->interval = currcp->interval;
				rwalk->criteria = currcp->criteria;
				rwalk->noalerts = currcp->noalerts;
			}
		}
	}

	flush_rule(currule);
	stackfclose(fd);
	freestrbuffer(inbuf);

	MEMUNDEFINE(cfline);
	MEMUNDEFINE(fn);

	return 1;
}