diff options
Diffstat (limited to 'src/pmdas/logger/logger.c')
-rw-r--r-- | src/pmdas/logger/logger.c | 587 |
1 files changed, 587 insertions, 0 deletions
diff --git a/src/pmdas/logger/logger.c b/src/pmdas/logger/logger.c new file mode 100644 index 0000000..98689d7 --- /dev/null +++ b/src/pmdas/logger/logger.c @@ -0,0 +1,587 @@ +/* + * Logger, a configurable log file monitoring PMDA + * + * Copyright (c) 2011-2012 Red Hat. + * Copyright (c) 2011 Nathan Scott. All Rights Reserved. + * Copyright (c) 1995,2004 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. + * + * Debug options + * APPL0 configfile processing and PMNS setup + * APPL1 loading event data from the log files + * APPL2 interaction with PMCD + */ + +#include "domain.h" +#include "event.h" +#include "util.h" +#include "pmda.h" + +/* + * Logger PMDA + * + * Metrics + * logger.numclients - number of attached clients + * logger.numlogfiles - number of monitored logfiles + * logger.param_string - string event data + * logger.perfile.{LOGFILE}.count - observed event count + * logger.perfile.{LOGFILE}.bytes - observed events size + * logger.perfile.{LOGFILE}.size - logfile size + * logger.perfile.{LOGFILE}.path - logfile path + * logger.perfile.{LOGFILE}.numclients - number of attached + * clients/logfile + * logger.perfile.{LOGFILE}.records - event records/logfile + */ + +#define DEFAULT_MAXMEM (2 * 1024 * 1024) /* 2 megabytes */ +long maxmem; + +int maxfd; +fd_set fds; +static int interval_expired; +static struct timeval interval = { 2, 0 }; +static char *username; + +static int nummetrics; +static __pmnsTree *pmns; + +typedef struct dynamic_metric_info { + int handle; + int pmid_index; + const char *help_text; +} dynamic_metric_info_t; +static dynamic_metric_info_t *dynamic_metric_infotab; + +static pmdaMetric dynamic_metrictab[] = { +/* perfile.{LOGFILE}.count */ + { NULL, /* m_user gets filled in later */ + { 0 /* pmid gets filled in later */, PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* perfile.{LOGFILE}.bytes */ + { NULL, /* m_user gets filled in later */ + { 0 /* pmid gets filled in later */, PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* perfile.{LOGFILE}.size */ + { NULL, /* m_user gets filled in later */ + { 0 /* pmid gets filled in later */, PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* perfile.{LOGFILE}.path */ + { NULL, /* m_user gets filled in later */ + { 0 /* pmid gets filled in later */, PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* perfile.{LOGFILE}.numclients */ + { NULL, /* m_user gets filled in later */ + { 0 /* pmid gets filled in later */, PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* perfile.{LOGFILE}.records */ + { NULL, /* m_user gets filled in later */ + { 0 /* pmid gets filled in later */, PM_TYPE_EVENT, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* perfile.{LOGFILE}.queuemem */ + { NULL, /* m_user gets filled in later */ + { 0 /* pmid gets filled in later */, PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +}; + +static char *dynamic_nametab[] = { +/* perfile.{LOGFILE}.count */ + "count", +/* perfile.{LOGFILE}.bytes */ + "bytes", +/* perfile.{LOGFILE}.size */ + "size", +/* perfile.{LOGFILE}.path */ + "path", +/* perfile.{LOGFILE}.numclients */ + "numclients", +/* perfile.{LOGFILE}.records */ + "records", +/* perfile.{LOGFILE}.queuemem */ + "queuemem", +}; + +static const char *dynamic_helptab[] = { +/* perfile.{LOGFILE}.count */ + "The cumulative number of events seen for this logfile.", +/* perfile.{LOGFILE}.bytes */ + "Cumulative number of bytes in events seen for this logfile.", +/* perfile.{LOGFILE}.size */ + "The current size of this logfile.", +/* perfile.{LOGFILE}.path */ + "The path for this logfile.", +/* perfile.{LOGFILE}.numclients */ + "The number of attached clients for this logfile.", +/* perfile.{LOGFILE}.records */ + "Event records for this logfile.", +/* perfile.{LOGFILE}.queuemem */ + "Amount of memory used for event data.", +}; + +static pmdaMetric static_metrictab[] = { +/* numclients */ + { NULL, + { PMDA_PMID(0,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* numlogfiles */ + { NULL, + { PMDA_PMID(0,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* param_string */ + { NULL, + { PMDA_PMID(0,2), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* perfile.maxmem */ + { NULL, /* m_user gets filled in later */ + { PMDA_PMID(0,3), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +}; + +static pmdaMetric *metrictab; + +static int +logger_profile(__pmProfile *prof, pmdaExt *pmda) +{ + pmdaEventNewClient(pmda->e_context); + return 0; +} + +static int +logger_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + pmdaEventNewClient(pmda->e_context); + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +static int +valid_pmid(unsigned int cluster, unsigned int item) +{ + if (cluster != 0 || item > nummetrics) + return PM_ERR_PMID; + return 0; +} + +static int +logger_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + int sts; + + if ((sts = valid_pmid(idp->cluster, idp->item)) < 0) + return sts; + + sts = PMDA_FETCH_STATIC; + if (idp->item < 4) { + switch (idp->item) { + case 0: /* logger.numclients */ + sts = pmdaEventClients(atom); + break; + case 1: /* logger.numlogfiles */ + atom->ul = event_logcount(); + break; + case 2: /* logger.param_string */ + sts = PMDA_FETCH_NOVALUES; + break; + case 3: /* logger.maxmem */ + atom->ull = (unsigned long long)maxmem; + break; + default: + return PM_ERR_PMID; + } + } + else { + dynamic_metric_info_t *pinfo; + int queue; + + if ((pinfo = ((mdesc != NULL) ? mdesc->m_user : NULL)) == NULL) + return PM_ERR_PMID; + queue = event_queueid(pinfo->handle); + + switch (pinfo->pmid_index) { + case 0: /* perfile.{LOGFILE}.count */ + sts = pmdaEventQueueCounter(queue, atom); + break; + case 1: /* perfile.{LOGFILE}.bytes */ + sts = pmdaEventQueueBytes(queue, atom); + break; + case 2: /* perfile.{LOGFILE}.size */ + atom->ull = event_pathsize(pinfo->handle); + break; + case 3: /* perfile.{LOGFILE}.path */ + atom->cp = (char *)event_pathname(pinfo->handle); + break; + case 4: /* perfile.{LOGFILE}.numclients */ + sts = pmdaEventQueueClients(queue, atom); + break; + case 5: /* perfile.{LOGFILE}.records */ + sts = pmdaEventQueueRecords(queue, atom, pmdaGetContext(), + event_decoder, &pinfo->handle); + break; + case 6: /* perfile.{LOGFILE}.queuemem */ + sts = pmdaEventQueueMemory(queue, atom); + break; + default: + return PM_ERR_PMID; + } + } + return sts; +} + +static int +logger_store(pmResult *result, pmdaExt *pmda) +{ + int i, j, sts; + + pmdaEventNewClient(pmda->e_context); + + for (i = 0; i < result->numpmid; i++) { + pmValueSet *vsp = result->vset[i]; + __pmID_int *idp = (__pmID_int *)&vsp->pmid; + dynamic_metric_info_t *pinfo = NULL; + void *filter; + int queueid; + + if ((sts = valid_pmid(idp->cluster, idp->item)) < 0) + return sts; + for (j = 0; j < pmda->e_nmetrics; j++) { + if (vsp->pmid == pmda->e_metrics[j].m_desc.pmid) { + pinfo = pmda->e_metrics[j].m_user; + break; + } + } + if (pinfo == NULL) + return PM_ERR_PMID; + if (pinfo->pmid_index != 5) + return PM_ERR_PERMISSION; + queueid = event_queueid(pinfo->handle); + + if (vsp->numval != 1 || vsp->valfmt != PM_VAL_SPTR) + return PM_ERR_CONV; + + sts = event_regex_alloc(vsp->vlist[0].value.pval->vbuf, &filter); + if (sts < 0) + return sts; + + sts = pmdaEventSetFilter(pmda->e_context, queueid, filter, + event_regex_apply, event_regex_release); + if (sts < 0 ) + return sts; + } + return 0; +} + +static void +logger_end_contextCallBack(int context) +{ + pmdaEventEndClient(context); +} + +static int +logger_pmid(const char *name, pmID *pmid, pmdaExt *pmda) +{ + pmdaEventNewClient(pmda->e_context); + return pmdaTreePMID(pmns, name, pmid); +} + +static int +logger_name(pmID pmid, char ***nameset, pmdaExt *pmda) +{ + pmdaEventNewClient(pmda->e_context); + return pmdaTreeName(pmns, pmid, nameset); +} + +static int +logger_children(const char *name, int traverse, char ***kids, int **sts, + pmdaExt *pmda) +{ + pmdaEventNewClient(pmda->e_context); + return pmdaTreeChildren(pmns, name, traverse, kids, sts); +} + +static int +logger_text(int ident, int type, char **buffer, pmdaExt *pmda) +{ + int numstatics = sizeof(static_metrictab)/sizeof(static_metrictab[0]); + + pmdaEventNewClient(pmda->e_context); + + if ((type & PM_TEXT_PMID) == PM_TEXT_PMID) { + /* Lookup pmid in the metric table. */ + int item = pmid_item(ident); + + /* If the PMID item was for a dynamic metric... */ + if (item >= numstatics && item < nummetrics + /* and the PMID matches... */ + && metrictab[item].m_desc.pmid == (pmID)ident + /* and we've got user data... */ + && metrictab[item].m_user != NULL) { + dynamic_metric_info_t *pinfo = metrictab[item].m_user; + + /* Return the correct help text. */ + *buffer = (char *)pinfo->help_text; + return 0; + } + } + return pmdaText(ident, type, buffer, pmda); +} + +void +logger_init(pmdaInterface *dp, const char *configfile) +{ + size_t size; + int i, j, sts, item, numloggers; + int numstatics = sizeof(static_metrictab)/sizeof(static_metrictab[0]); + int numdynamics = sizeof(dynamic_metrictab)/sizeof(dynamic_metrictab[0]); + pmdaMetric *pmetric; + char name[MAXPATHLEN * 2]; + dynamic_metric_info_t *pinfo; + + __pmSetProcessIdentity(username); + + /* Read and parse config file. */ + if ((numloggers = event_config(configfile)) < 0) + return; + + /* Create the dynamic metric info table based on the logfile table */ + size = sizeof(struct dynamic_metric_info) * numdynamics * numloggers; + if ((dynamic_metric_infotab = malloc(size)) == NULL) { + __pmNoMem("logger_init(dynamic)", size, PM_FATAL_ERR); + return; + } + pinfo = dynamic_metric_infotab; + for (i = 0; i < numloggers; i++) { + for (j = 0; j < numdynamics; j++) { + pinfo->handle = i; + pinfo->pmid_index = j; + pinfo->help_text = dynamic_helptab[j]; + pinfo++; + } + } + + /* Create the metric table based on the static and dynamic metric tables */ + nummetrics = numstatics + (numloggers * numdynamics); + size = sizeof(pmdaMetric) * nummetrics; + if ((metrictab = malloc(size)) == NULL) { + free(dynamic_metric_infotab); + __pmNoMem("logger_init(static)", size, PM_FATAL_ERR); + return; + } + memcpy(metrictab, static_metrictab, sizeof(static_metrictab)); + pmetric = &metrictab[numstatics]; + pinfo = dynamic_metric_infotab; + item = numstatics; + for (i = 0; i < numloggers; i++) { + memcpy(pmetric, dynamic_metrictab, sizeof(dynamic_metrictab)); + for (j = 0; j < numdynamics; j++) { + pmetric[j].m_desc.pmid = PMDA_PMID(0, item++); + pmetric[j].m_user = pinfo++; + } + pmetric += numdynamics; + } + + if (dp->status != 0) + return; + + dp->version.four.fetch = logger_fetch; + dp->version.four.store = logger_store; + dp->version.four.profile = logger_profile; + dp->version.four.pmid = logger_pmid; + dp->version.four.name = logger_name; + dp->version.four.children = logger_children; + dp->version.four.text = logger_text; + + pmdaSetFetchCallBack(dp, logger_fetchCallBack); + pmdaSetEndContextCallBack(dp, logger_end_contextCallBack); + + pmdaInit(dp, NULL, 0, metrictab, nummetrics); + + /* Create the dynamic PMNS tree and populate it. */ + if ((sts = __pmNewPMNS(&pmns)) < 0) { + __pmNotifyErr(LOG_ERR, "%s: failed to create new pmns: %s\n", + pmProgname, pmErrStr(sts)); + pmns = NULL; + return; + } + pmetric = &metrictab[numstatics]; + for (i = 0; i < numloggers; i++) { + const char *id = event_pmnsname(i); + for (j = 0; j < numdynamics; j++) { + snprintf(name, sizeof(name), + "logger.perfile.%s.%s", id, dynamic_nametab[j]); + __pmAddPMNSNode(pmns, pmetric[j].m_desc.pmid, name); + } + pmetric += numdynamics; + } + /* for reverse (pmid->name) lookups */ + pmdaTreeRebuildHash(pmns, (numloggers * numdynamics)); + + /* initialise the event and client tracking code */ + event_init(metrictab[2].m_desc.pmid); +} + +static void +logger_timer(int sig, void *ptr) +{ + interval_expired = 1; +} + +void +loggerMain(pmdaInterface *dispatch) +{ + fd_set readyfds; + int nready, pmcdfd; + + pmcdfd = __pmdaInFd(dispatch); + if (pmcdfd > maxfd) + maxfd = pmcdfd; + + FD_ZERO(&fds); + FD_SET(pmcdfd, &fds); + + /* arm interval timer */ + if (__pmAFregister(&interval, NULL, logger_timer) < 0) { + __pmNotifyErr(LOG_ERR, "registering event interval handler"); + exit(1); + } + + for (;;) { + memcpy(&readyfds, &fds, sizeof(readyfds)); + nready = select(maxfd+1, &readyfds, NULL, NULL, NULL); + if (pmDebug & DBG_TRACE_APPL2) + __pmNotifyErr(LOG_DEBUG, "select: nready=%d interval=%d", + nready, interval_expired); + if (nready < 0) { + if (neterror() != EINTR) { + __pmNotifyErr(LOG_ERR, "select failure: %s", netstrerror()); + exit(1); + } else if (!interval_expired) { + continue; + } + } + + __pmAFblock(); + if (nready > 0 && FD_ISSET(pmcdfd, &readyfds)) { + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "processing pmcd PDU [fd=%d]", pmcdfd); + if (__pmdaMainPDU(dispatch) < 0) { + __pmAFunblock(); + exit(1); /* fatal if we lose pmcd */ + } + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "completed pmcd PDU [fd=%d]", pmcdfd); + } + if (interval_expired) { + interval_expired = 0; + event_refresh(); + } + __pmAFunblock(); + } +} + +static void +convertUnits(char **endnum, long *maxmem) +{ + switch ((int) **endnum) { + case 'b': + case 'B': + break; + case 'k': + case 'K': + *maxmem *= 1024; + break; + case 'm': + case 'M': + *maxmem *= 1024 * 1024; + break; + case 'g': + case 'G': + *maxmem *= 1024 * 1024 * 1024; + break; + } + (*endnum)++; +} + +static void +usage(void) +{ + fprintf(stderr, + "Usage: %s [options] configfile\n\n" + "Options:\n" + " -d domain use domain (numeric) for metrics domain of PMDA\n" + " -l logfile write log into logfile rather than the default\n" + " -m memory maximum memory used per logfile (default %ld bytes)\n" + " -s interval default delay between iterations (default %d sec)\n" + " -U username user account to run under (default \"pcp\")\n", + pmProgname, maxmem, (int)interval.tv_sec); + exit(1); +} + +int +main(int argc, char **argv) +{ + static char helppath[MAXPATHLEN]; + char *endnum; + pmdaInterface desc; + long minmem; + int c, err = 0, sep = __pmPathSeparator(); + + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + minmem = getpagesize(); + maxmem = (minmem > DEFAULT_MAXMEM) ? minmem : DEFAULT_MAXMEM; + snprintf(helppath, sizeof(helppath), "%s%c" "logger" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&desc, PMDA_INTERFACE_5, pmProgname, LOGGER, + "logger.log", helppath); + + while ((c = pmdaGetOpt(argc, argv, "D:d:l:m:s:U:?", &desc, &err)) != EOF) { + switch (c) { + case 'm': + maxmem = strtol(optarg, &endnum, 10); + if (*endnum != '\0') + convertUnits(&endnum, &maxmem); + if (*endnum != '\0' || maxmem < minmem) { + fprintf(stderr, "%s: invalid max memory '%s' (min=%ld)\n", + pmProgname, optarg, minmem); + err++; + } + break; + + case 's': + if (pmParseInterval(optarg, &interval, &endnum) < 0) { + fprintf(stderr, "%s: -s requires a time interval: %s\n", + pmProgname, endnum); + free(endnum); + err++; + } + break; + + case 'U': + username = optarg; + break; + + default: + err++; + break; + } + } + + if (err || optind != argc -1) + usage(); + + pmdaOpenLog(&desc); + logger_init(&desc, argv[optind]); + pmdaConnect(&desc); + loggerMain(&desc); + event_shutdown(); + exit(0); +} |