diff options
Diffstat (limited to 'src/pmdas/weblog/pmda.c')
-rw-r--r-- | src/pmdas/weblog/pmda.c | 1205 |
1 files changed, 1205 insertions, 0 deletions
diff --git a/src/pmdas/weblog/pmda.c b/src/pmdas/weblog/pmda.c new file mode 100644 index 0000000..6c1967f --- /dev/null +++ b/src/pmdas/weblog/pmda.c @@ -0,0 +1,1205 @@ +/* + * Web PMDA, based on generic driver for a daemon-based PMDA + * + * Copyright (c) 2012 Red Hat. + * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "weblog.h" +#include "domain.h" +#if defined(HAVE_REGEX_H) +#include <regex.h> +#endif +#if defined(HAVE_SYS_WAIT_H) +#include <sys/wait.h> +#endif +#if defined(HAVE_SCHED_H) +#include <sched.h> +#endif + +#ifdef IS_SOLARIS +#define CLONE_VM 0x00000100 +#elif !defined(CLONE_VM) +#define CLONE_VM 0x0 +#endif + +#ifndef HAVE_SPROC +int sproc (void (*entry) (void *), int flags, void *arg); +#endif + +/* path to the configuration file */ +static char *configFileName = (char*)0; + +/* line number of configuration file */ +static int line = 0; + +/* number of errors in configuration file */ +static int err = 0; + +/* configured for num of servers */ +__uint32_t wl_numServers = 0; + +/* number of active servers */ +__uint32_t wl_numActive = 0; + +/* check logs every 15 seconds by default */ +__uint32_t wl_refreshDelay = 15; + +/* re-open logs if unchanged in this number of seconds */ +__uint32_t wl_chkDelay = 20; + +/* max servers per sproc */ +__uint32_t wl_sprocThresh = 80; + +/* number of sprocs spawned */ +__uint32_t wl_numSprocs = 0; + +/* number of regex parsed */ +__uint32_t wl_numRegex = 0; + +/* list of web servers */ +WebServer *wl_servers = (WebServer*)0; + +/* list of regular expressions */ +WebRegex *wl_regexTable = (WebRegex*)0; + +/* instance table of web servers */ +pmdaInstid *wl_serverInst = (pmdaInstid*)0; + +/* list of sprocs spawned from the main process */ +WebSproc *wl_sproc; + +/* default name for log file */ +char *wl_logFile = "weblog.log"; + +/* default path to help file */ +char wl_helpFile[MAXPATHLEN]; + +/* default user name for PMDA */ +char *wl_username; + +/* + * Usage Information + */ + +void +usage(void) +{ + fprintf(stderr, + "Usage: %s [options] configfile\n\ +\n\ +Options\n\ + -C check configuration and exit\n\ + -d domain PMDA domain number\n\ + -h helpfile get help text from helpfile rather than default path\n\ + -i port expect PMCD to connect on given inet port (number or name)\n\ + -l logfile redirect diagnostics and trace output (default weblog.log)\n\ + -n idlesec number of seconds of weblog inactivity before checking for\n\ + log rotation\n\ + -p expect PMCD to supply stdin/stdout (pipe)\n\ + -S num number of web servers per sproc\n\ + -t delay maximum number of seconds between reading weblog files\n\ + -u socket expect PMCD to connect on given unix domain socket\n\ + -U username user account to run under (default \"pcp\")\n\ + -6 port expect PMCD to connect on given ipv6 port (number or name)\n\ +\n\ +If none of the -i, -p or -u options are given, the configuration file is\n\ +checked and then %s terminates.\n", pmProgname, pmProgname); + exit(1); +} + +void +logmessage(int priority, const char *format, ...) +{ + va_list arglist; + char buffer[2048]; + char *level; + char *p; + time_t now; + + buffer[0] = '\0'; + time(&now); + + switch (priority) { + case LOG_EMERG : + level = "Emergency"; + break; + case LOG_ALERT : + level = "Alert"; + break; + case LOG_CRIT : + level = "Critical"; + break; + case LOG_ERR : + level = "Error"; + break; + case LOG_WARNING : + level = "Warning"; + break; + case LOG_NOTICE : + level = "Notice"; + break; + case LOG_INFO : + level = "Info"; + break; + case LOG_DEBUG : + level = "Debug"; + break; + default: + level = "???"; + break; + } + + va_start (arglist, format); + vsnprintf (buffer, sizeof(buffer), format, arglist); + for (p = buffer; *p; p++); + if (*(--p) == '\n') *p = '\0'; + fprintf (stderr, "[%.19s] %s(%" FMT_PID ") %s: %s\n", ctime(&now), pmProgname, getpid(), level, buffer) ; + va_end (arglist) ; +} + +/* + * Errors message during parsing of config file + */ + +static void +yyerror(char *s) +{ + fprintf(stderr, "[%s:%d] Error: %s\n", configFileName, line, s); + err++; +} + +/* + * Warning message during parsing of config file + */ + +static void +yywarn(char *s) +{ + fprintf(stderr, "[%s:%d] Warning: %s\n", configFileName, line, s); +} + +/* + * skip remaining characters on this line + */ + +static void +skip_to_eol(FILE *f) +{ + int c; + + while ((c = fgetc(f)) != EOF) { + if (c == '\n') + return; + } + return; +} + +/* + * Are we at the end of the line (sucks up spaces and tabs which may preceed + * EOL) + */ + +static void +check_to_eol(FILE *f) +{ + int c; + int i = 0; + + while ((c = fgetc(f)) != EOF) { + if (c == '\n') + break; + if (c == ' ' || c == '\t') + continue; + i++; + } + if (i) + yywarn("additional words in line, ignored"); + + return; +} + +/* + * Get a word. A word if any text until a whitespace + */ + +static int +getword(FILE *f, char *buf, int len) +{ + int c; + char *bend = &buf[len-1]; + + while ((c = fgetc(f)) != EOF) { + if (c == ' ' || c == '\t') + continue; + ungetc(c, f); + break; + } + + while ((c = fgetc(f)) != EOF) { + if (c == ' ' || c == '\t') + break; + if (c == '\n') { + ungetc(c, f); + break; + } + if (buf < bend) { + *buf++ = c; + continue; + } + else { + yyerror("word too long, remainder of line ignored"); + return -1; + } + } + *buf = '\0'; + + return c == EOF ? 0 : 1; +} + +/* + * Get the next line from buffer + */ + +static int +get_to_eol(FILE *f, char *buf, int len) +{ + int c; + char *bend = &buf[len-1]; + + while ((c = fgetc(f)) != EOF) { + if (c == ' ' || c == '\t') + continue; + ungetc(c, f); + break; + } + + while ((c = fgetc(f)) != EOF) { + if (c == '\n') + break; + if (buf < bend) { + *buf++ = c; + continue; + } + else { + yyerror("list of words too long, remainder of line ignored"); + return -1; + } + } + *buf = '\0'; + return c == EOF ? 0 : 1; +} + +/* + * Replacement for pmdaMainLoop + * Has a select loop on pipe from PMCD, reads in PDUs and acts on them + * appropriately. + */ + +static void +receivePDUs(pmdaInterface *dispatch) +{ + int nfds = 0; + time_t interval = 0; + int sts = 0; + struct timeval timeout; + fd_set rfds; + + + FD_ZERO(&rfds); + nfds = fileno(stdin)+1; + + for (;;) { + + FD_SET(fileno(stdin), &rfds); + __pmtimevalNow(&timeout); + timeout.tv_usec = 0; + interval = (time_t)wl_refreshDelay - (timeout.tv_sec % (time_t)wl_refreshDelay); + timeout.tv_sec = interval; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, "Select set for %d seconds\n", + interval); +#endif + + sts = select(nfds, &rfds, (fd_set*)0, (fd_set*)0, &timeout); + if (sts < 0) { + logmessage(LOG_ERR, "Error on fetch select: %s", netstrerror()); + exit(1); + } + + if (sts == 0) { + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, "Select timed out\n"); +#endif + + refreshAll(); + continue; + } + + if (__pmdaMainPDU(dispatch) < 0){ + exit(1); + } + + if (interval == 0) { + refreshAll(); + } + + } +} + +/* + * Catch an SPROC dying, report what we know, and exit + * -- when main exits, other sprocs will get SIGHUP and exit quietly + */ +static void +onchld(int dummy) +{ + int done; + int waitStatus; + int sprocNum; + + while ((done = waitpid(-1, &waitStatus, WNOHANG)) > 0) { + for (sprocNum = 1; + wl_sproc[sprocNum].pid != done && sprocNum <= wl_numSprocs; + sprocNum++); + + if (sprocNum > wl_numSprocs) + { + logmessage(LOG_INFO, + "Unexpected child process (pid=%d) died!\n", + done); + continue; + } + + if (WIFEXITED(waitStatus)) { + + if (WEXITSTATUS(waitStatus) == 0) + logmessage(LOG_INFO, + "Sproc %d (pid=%d) exited normally\n", + sprocNum, done); + else + logmessage(LOG_INFO, + "Sproc %d (pid=%d) exited with status = %d\n", + sprocNum, done, WEXITSTATUS(waitStatus)); + } + else if (WIFSIGNALED(waitStatus)) { + +#ifdef WCOREDUMP + if (WCOREDUMP(waitStatus)) + logmessage(LOG_INFO, + "Sproc %d (pid=%d) received signal = %d and dumped core\n", + sprocNum, done, WTERMSIG(waitStatus)); +#endif + logmessage(LOG_INFO, + "Sproc %d (pid=%d) received signal = %d\n", + sprocNum, done, WTERMSIG(waitStatus)); + } + else { + logmessage(LOG_INFO, + "Sproc %d (pid=%d) died, reason unknown\n", + sprocNum, done); + } + + logmessage(LOG_INFO, + "Sproc %d managed servers %d to %d\n", + sprocNum, + wl_sproc[sprocNum].firstServer, + wl_sproc[sprocNum].lastServer); + } + + logmessage(LOG_INFO, "Main process exiting\n"); + exit(0); +} + +/* + * Parse command line args and the configuration file. Also sets up and fires + * off the required sprocs + */ + +int +main(int argc, char **argv) +{ + WebServer *server = (WebServer *)0; + WebSproc *proc = (WebSproc *)0; + + char *endnum = (char*)0; + char buf1[FILENAME_MAX]; + char buf2[FILENAME_MAX]; + char emess[120]; + char *pstart, *pend; + char argsDone, argFound; + char *err_msg; + + int i = 0; + int argCount = 0; + int checkOnly = 0; + int sts = 0; + int sep = __pmPathSeparator(); + int n = 0; + int serverTableSize = 0; + int regexTableSize = 0; + + FILE *configFile = (FILE*)0; + FILE *tmpFp = (FILE*)0; + + pmdaInterface desc; + struct timeval delta; + + struct { + int *argPos; + char *argString; + } regexargs[2]; + +#ifdef PCP_DEBUG + struct timeval start; + struct timeval end; + double startTime; +#endif + + __pmSetProgname(argv[0]); + __pmGetUsername(&wl_username); + +#ifdef PCP_DEBUG + __pmtimevalNow(&start); +#endif + + wl_isDSO = 0; + + snprintf(wl_helpFile, sizeof(wl_helpFile), "%s%c" "weblog" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&desc, PMDA_INTERFACE_2, pmProgname, WEBSERVER, + wl_logFile, wl_helpFile); + + while ((n = pmdaGetOpt(argc, argv, "CD:d:h:i:l:n:pS:t:u:U:6:?", + &desc, &err)) != EOF) { + switch (n) { + + case 'C': + checkOnly = 1; + break; + + case 'S': + wl_sprocThresh = (int)strtol(optarg, &endnum, 10); + if (*endnum != '\0') { + fprintf(stderr, "%s: -S requires numeric argument\n", + pmProgname); + err++; + } + break; + + case 'n': + if (pmParseInterval(optarg, &delta, &err_msg) < 0) { + (void)fprintf(stderr, + "%s: -n requires a time interval: %s\n", + err_msg, pmProgname); + free(err_msg); + err++; + } + else { + wl_chkDelay = delta.tv_sec; + } + break; + + case 't': + if (pmParseInterval(optarg, &delta, &err_msg) < 0) { + (void)fprintf(stderr, + "%s: -t requires a time interval: %s\n", + err_msg, pmProgname); + free(err_msg); + err++; + } + else { + wl_refreshDelay = delta.tv_sec; + } + break; + + case 'U': + wl_username = optarg; + break; + + default: + fprintf(stderr, "%s: Unknown option \"-%c\"", pmProgname, (char)n); + err++; + break; + } + } + + if (err || optind != argc-1) { + usage(); + } + + line = 0; + configFileName = argv[optind]; + configFile = fopen(configFileName, "r"); + + if (configFile == (FILE*)0) { + fprintf(stderr, "Unable to open config file %s\n", configFileName); + usage(); + } + + if (checkOnly == 0) { + /* + * if doing more than just parsing, force errors from here + * on into the logfile + */ + pmdaOpenLog(&desc); + __pmSetProcessIdentity(wl_username); + } + + /* + * Parse the configuration file + */ + + /* These settings should be reflected below */ + regexargs[0].argString = strdup("method"); + regexargs[1].argString = strdup("size"); + + while(!feof(configFile)) { + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts == 0) { + /* End of File */ + break; + } + + line++; + + if (sts < 0) { + /* error, reported in getword() */ + skip_to_eol(configFile); + continue; + } + + if (buf1[0] == '\0' || buf1[0] == '#') { + /* comment, or nothing in the line, next line please */ + skip_to_eol(configFile); + continue; + } + + if (strcasecmp(buf1, "regex_posix") == 0) { + /* + * Parse a regex specification + */ + + if (wl_numRegex == regexTableSize) { + regexTableSize += 2; + wl_regexTable = (WebRegex*)realloc(wl_regexTable, + regexTableSize * sizeof(WebRegex)); + if (wl_regexTable == (WebRegex*)0) { + __pmNoMem("main.wl_regexInst", + (wl_numRegex + 1) * sizeof(WebRegex), + PM_FATAL_ERR); + } + } + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract regex name"); + skip_to_eol(configFile); + continue; + } + + wl_regexTable[wl_numRegex].name = strdup(buf1); + + if (wl_numRegex) { + for (n = 0; n < wl_numRegex; n++) { + if (strcmp(wl_regexTable[n].name, + wl_regexTable[wl_numRegex].name) == 0) { + + snprintf(emess, sizeof(emess), "duplicate regex name (%s)", + wl_regexTable[wl_numRegex].name); + yyerror(emess); + break; + } + } + if (n < wl_numRegex) { + skip_to_eol(configFile); + continue; + } + } + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract regex match parameters"); + skip_to_eol(configFile); + continue; + } + + regexargs[0].argPos = &(wl_regexTable[wl_numRegex].methodPos); + regexargs[1].argPos = &(wl_regexTable[wl_numRegex].sizePos); + wl_regexTable[wl_numRegex].methodPos = 0; + wl_regexTable[wl_numRegex].sizePos = 0; + wl_regexTable[wl_numRegex].sizePos = 0; + wl_regexTable[wl_numRegex].s_statusPos = 0; + + pstart = buf1; + argCount = 0; + do { + argFound = 0; + argsDone = 1; + argCount++; + for(pend = pstart; *pend; pend++) { + if(*pend == ',') { + *pend = '\0'; + argsDone = 0; + break; + } + } + for(i = 0; i < sizeof(regexargs) / sizeof(regexargs[0]); i++) { + if(strcmp(pstart, regexargs[i].argString) == 0) { + *regexargs[i].argPos = argCount; + argFound = 1; + break; + } + } + if(!argFound) { + /* not the old method,size style */ + switch(pstart[0]) { + case '1': + wl_regexTable[wl_numRegex].methodPos = argCount; + argFound = 1; + break; + case '2': + wl_regexTable[wl_numRegex].sizePos = argCount; + argFound = 1; + break; + case '3': + wl_regexTable[wl_numRegex].c_statusPos = argCount; + argFound = 1; + break; + case '4': + wl_regexTable[wl_numRegex].s_statusPos = argCount; + argFound = 1; + break; + case '-': + wl_regexTable[wl_numRegex].methodPos = argCount++; + wl_regexTable[wl_numRegex].sizePos = argCount; + argFound = 1; + argsDone = 1; + break; + default: + break; + } + } + pstart = pend + 1; + } while(argsDone == 0 && argFound != 0); + + if(argFound == 0) { + yyerror("invalid keyword in regex match parameters"); + skip_to_eol(configFile); + continue; + } + + sts = get_to_eol(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract regex"); + else + skip_to_eol(configFile); + continue; + } + + wl_regexTable[wl_numRegex].regex = malloc(sizeof(*wl_regexTable[wl_numRegex].regex)); + if(wl_regexTable[wl_numRegex].regex == NULL) { + __pmNoMem("main.wl_regex", + sizeof(*wl_regexTable[wl_numRegex].regex), + PM_FATAL_ERR); + } + + if (regcomp(wl_regexTable[wl_numRegex].regex, buf1, REG_EXTENDED) != 0) { + yyerror("unable to compile regex"); + continue; + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + logmessage(LOG_DEBUG, "%d regex %s: %s\n", + wl_numRegex, wl_regexTable[wl_numRegex].name, buf1); +#endif + + wl_regexTable[wl_numRegex].posix_regexp = 1; + wl_numRegex++; + } +#ifdef NON_POSIX_REGEX + else if (strcasecmp(buf1, "regex") == 0) { + /* + * Parse a regex specification + */ + + if (wl_numRegex == regexTableSize) { + regexTableSize += 2; + wl_regexTable = (WebRegex*)realloc(wl_regexTable, + regexTableSize * sizeof(WebRegex)); + if (wl_regexTable == (WebRegex*)0) { + __pmNoMem("main.wl_regexInst", + (wl_numRegex + 1) * sizeof(WebRegex), + PM_FATAL_ERR); + } + } + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract regex name"); + skip_to_eol(configFile); + continue; + } + + wl_regexTable[wl_numRegex].name = strdup(buf1); + + if (wl_numRegex) { + for (n = 0; n < wl_numRegex; n++) { + if (strcmp(wl_regexTable[n].name, + wl_regexTable[wl_numRegex].name) == 0) { + + snprintf(emess, sizeof(emess), "duplicate regex name (%s)", + wl_regexTable[wl_numRegex].name); + yyerror(emess); + break; + } + } + if (n < wl_numRegex) { + skip_to_eol(configFile); + continue; + } + } + + sts = get_to_eol(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract regex"); + else + skip_to_eol(configFile); + continue; + } + + if(strstr(buf1, "$2") != NULL && strstr(buf1, "$3") != NULL ) { + /* + * extended caching server format + * + * although these aren't used in the non-regex code, they + * are a good enough placeholder until server->counts.extendedp + * is set below + */ + wl_regexTable[wl_numRegex].c_statusPos = 1; + wl_regexTable[wl_numRegex].s_statusPos = 1; + + } + wl_regexTable[wl_numRegex].np_regex = regcmp(buf1, (char*)0); + + if (wl_regexTable[wl_numRegex].np_regex == (char*)0) { + yyerror("unable to compile regex"); + continue; + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + logmessage(LOG_DEBUG, "%d NON POSIX regex %s: %s\n", + wl_numRegex, wl_regexTable[wl_numRegex].name, buf1); +#endif + + wl_regexTable[wl_numRegex].posix_regexp = 0; + wl_numRegex++; + } +#endif + else if (strcasecmp(buf1, "server") == 0) { + /* + * Parse a server specification + */ + + if (wl_numServers == serverTableSize) { + serverTableSize += 4; + wl_serverInst = (pmdaInstid*)realloc(wl_serverInst, + serverTableSize * sizeof(pmdaInstid)); + if (wl_serverInst == (pmdaInstid*)0) { + __pmNoMem("main.wl_serverInst", + (wl_numServers + 1) * sizeof(pmdaInstid), + PM_FATAL_ERR); + } + + wl_servers = (WebServer*)realloc(wl_servers, + serverTableSize * sizeof(WebServer)); + if (wl_servers == (WebServer*)0) { + __pmNoMem("main.wl_servers", + (wl_numServers + 1) * sizeof(WebServer), + PM_FATAL_ERR); + } + } + + /* Get server name */ + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract server name"); + skip_to_eol(configFile); + continue; + } + + if (wl_numServers) { + for (n = 0; n < wl_numServers; n++) { + if (strcmp(buf1, wl_serverInst[n].i_name) == 0) { + snprintf(emess, sizeof(emess), "duplicate server name (%s)", buf1); + yyerror(emess); + break; + } + } + if (n < wl_numServers) { + skip_to_eol(configFile); + continue; + } + } + + wl_serverInst[wl_numServers].i_name = strdup(buf1); + wl_serverInst[wl_numServers].i_inst = wl_numServers; + + server = &(wl_servers[wl_numServers]); + memset(server, 0, sizeof(*server)); + server->access.filePtr = -1; + server->error.filePtr = -1; + + /* Get server active flag */ + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract active flag"); + skip_to_eol(configFile); + continue; + } + + if (strcasecmp(buf1, "on") == 0) { + server->counts.active = 1; + } + else if (strcasecmp(buf1, "off") == 0) { + server->counts.active = 0; + } + else { + yyerror("illegal active flag"); + skip_to_eol(configFile); + continue; + } + + /* Get access log regex and file name */ + + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract access log regex"); + skip_to_eol(configFile); + continue; + } + + sts = getword(configFile, buf2, sizeof(buf2)); + + if (sts <= 0 || buf2[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract access log name"); + skip_to_eol(configFile); + continue; + } + + for (n = 0; n < wl_numRegex; n++) + if (strcmp(buf1, wl_regexTable[n].name) == 0) + break; + + if (n == wl_numRegex) { + snprintf(emess, sizeof(emess), "access log regex \"%s\" not defined", buf1); + yyerror(emess); + skip_to_eol(configFile); + continue; + } else if(wl_regexTable[n].c_statusPos > 0 && + wl_regexTable[n].s_statusPos > 0) { + /* common extended format or one that uses the same codes */ + server->counts.extendedp = 1; + if(strcmp(wl_regexTable[n].name, "SQUID") == 0) { + /* + * default squid format - uses text codes not numerics + * so it *has* to be a special case + */ + server->counts.extendedp = 2; + } + } + + server->access.format = n; + server->access.fileName = strdup(buf2); + + if (server->counts.active) { + tmpFp = fopen(server->access.fileName, "r"); + if (tmpFp == (FILE*)0) { + snprintf(emess, sizeof(emess), "cannot open access log \"%s\"", buf2); + yywarn(emess); + server->access.filePtr = -1; + } + else + fclose(tmpFp); + } + + /* Get error log regex and file name */ + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract error log regex"); + skip_to_eol(configFile); + continue; + } + + sts = getword(configFile, buf2, sizeof(buf2)); + + if (sts <= 0 || buf2[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract error log name"); + skip_to_eol(configFile); + continue; + } + + for (n = 0; n < wl_numRegex; n++) + if (strcmp(buf1, wl_regexTable[n].name) == 0) + break; + + if (n == wl_numRegex) { + snprintf(emess, sizeof(emess), "error log regex \"%s\" not defined", buf1); + yyerror(emess); + skip_to_eol(configFile); + continue; + } + + server->error.format = n; + server->error.fileName = strdup(buf2); + + if (server->counts.active) { + tmpFp = fopen(server->error.fileName, "r"); + if (tmpFp == (FILE*)0) { + snprintf(emess, sizeof(emess), "cannot open error log \"%s\"", buf2); + yywarn(emess); + server->error.filePtr = -1; + } + else + fclose(tmpFp); + } + + check_to_eol(configFile); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + logmessage(LOG_DEBUG, "%d Server %s, %d, %d, %s, %d, %s\n", + wl_numServers, + wl_serverInst[wl_numServers].i_name, + server->counts.active, + server->access.format, + server->access.fileName, + server->error.format, + server->error.fileName); + } +#endif + + if (server->counts.active) + wl_numActive++; + + wl_numServers++; + } + else { + snprintf(emess, sizeof(emess), "illegal keyword \"%s\"", buf1); + yyerror(emess); + skip_to_eol(configFile); + continue; + } + } + + if (wl_numServers == 0) { + yyerror("no servers were specified in the configuration file!"); + } + + fclose(configFile); + + if (checkOnly || err) { + /* errors, or parse only, no PMCD communication option */ + exit(err); + } + + wl_indomTable[0].it_numinst = wl_numServers; + wl_indomTable[0].it_set = wl_serverInst; + + web_init(&desc); + pmdaConnect(&desc); + + /* catch any sprocs dying */ + + signal(SIGCHLD, onchld); + + /* fire off all the sprocs that we need */ + + wl_numSprocs = (wl_numServers-1) / wl_sprocThresh; + wl_sproc = (WebSproc*)malloc((wl_numSprocs+1) * sizeof(WebSproc)); + if (wl_sproc == NULL) { + logmessage(LOG_ERR, + "wl_numServers = %d, wl_sprocThresh = %d", + wl_numServers, + wl_sprocThresh); + __pmNoMem("main.wl_sproc", + (wl_numSprocs+1) * sizeof(WebSproc), + PM_FATAL_ERR); + } + + + for (n = 0; n <= wl_numSprocs; n++) + { + proc = &wl_sproc[n]; + proc->pid = -1; + proc->methodStr = (char *)0; + proc->sizeStr = (char *)0; + proc->c_statusStr = (char *)0; + proc->s_statusStr = (char *)0; + proc->strLength = 0; + } + + if (wl_numSprocs) { + + for (n=1; n<=wl_numSprocs; n++) { + proc = &wl_sproc[n]; + + sts = pipe1(proc->inFD); + if (sts) { + logmessage(LOG_ERR, + "Cannot allocate fileDes 1 for sproc[%d]", + n); + exit(1); + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, + "Creating in pipe (in=%d, out=%d) for sproc %d\n", + proc->inFD[0], + proc->inFD[1], + n); +#endif + + sts = pipe1(proc->outFD); + if (sts) { + logmessage(LOG_ERR, + "Cannot allocate fileDes 2 for sproc[%d]", + n); + exit(1); + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, + "Creating out pipe (in=%d, out=%d) for sproc %d\n", + proc->outFD[0], + proc->outFD[1], + n); +#endif + + proc->firstServer = (n)*wl_sprocThresh; + if (n != wl_numSprocs) + proc->lastServer = proc->firstServer + + wl_sprocThresh - 1; + else + proc->lastServer = wl_numServers - 1; + + logmessage(LOG_INFO, + "Creating sproc [%d] for servers %d to %d\n", + n, proc->firstServer, proc->lastServer); + + proc->id = n; + +#ifndef HAVE_SPROC + proc->pid = sproc(sprocMain, CLONE_VM, (void*)(&proc->id)); +#else + proc->pid = sproc(sprocMain, PR_SADDR, (void*)(&proc->id)); +#endif + + if (proc->pid < 0) { + logmessage(LOG_ERR, "main: error creating sproc %d: %s\n", + n, osstrerror()); + exit(1); + } + +#ifdef PCP_DEBUG + if(pmDebug & DBG_TRACE_APPL0) { + logmessage(LOG_INFO, + "main: created sproc %d: pid %" FMT_PID "\n", + n, + proc->pid); + } +#endif + + /* close off unwanted pipes */ + + if(close(proc->inFD[0]) < 0) { + logmessage(LOG_WARNING, + "main: pipe close(fd=%d) failed: %s\n", + proc->inFD[0], osstrerror()); + } + if(close(proc->outFD[1]) < 0) { + logmessage(LOG_WARNING, + "main: pipe close(fd=%d) failed: %s\n", + proc->outFD[1], osstrerror()); + } + } + } + + wl_sproc[0].firstServer = 0; + wl_sproc[0].lastServer = (wl_numServers <= wl_sprocThresh) ? + wl_numServers - 1 : wl_sprocThresh - 1; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + logmessage(LOG_DEBUG, + "Main process will monitor servers 0 to %d\n", + wl_sproc[0].lastServer); +#endif + + for (n=0; n <= wl_sproc[0].lastServer; n++) { + if (wl_servers[n].counts.active) { + openLogFile(&(wl_servers[n].access)); + openLogFile(&(wl_servers[n].error)); + } + } + +#ifdef PCP_DEBUG + __pmtimevalNow(&end); + startTime = (end.tv_sec - start.tv_sec) + + ((end.tv_usec - start.tv_usec) / 1000000.0); + if (pmDebug & DBG_TRACE_APPL0) + logmessage(LOG_DEBUG, "Agent started in %f seconds", startTime); +#endif + + receivePDUs(&desc); + + logmessage(LOG_INFO, "Connection to PMCD closed by PMCD\n"); + logmessage(LOG_INFO, "Last fetch took %d msec\n", wl_catchupTime); + logmessage(LOG_INFO, "Exiting...\n"); + + return 0; +} |