summaryrefslogtreecommitdiff
path: root/src/pmdas/mailq/mailq.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmdas/mailq/mailq.c')
-rw-r--r--src/pmdas/mailq/mailq.c401
1 files changed, 401 insertions, 0 deletions
diff --git a/src/pmdas/mailq/mailq.c b/src/pmdas/mailq/mailq.c
new file mode 100644
index 0000000..8d82fcd
--- /dev/null
+++ b/src/pmdas/mailq/mailq.c
@@ -0,0 +1,401 @@
+/*
+ * Mailq PMDA
+ *
+ * Copyright (c) 2012,2014 Red Hat.
+ * Copyright (c) 1997-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 "pmapi.h"
+#include "impl.h"
+#include "pmda.h"
+#include "domain.h"
+#ifdef HAVE_REGEX_H
+#include <regex.h>
+#endif
+#include <sys/stat.h>
+
+/*
+ * histogram for binning messages based on queue time
+ */
+typedef struct {
+ long count; /* number in this bin */
+ time_t delay; /* in queue for at least this long (seconds) */
+} histo_t;
+
+static histo_t *histo;
+static int numhisto;
+static int queue;
+
+/*
+ * list of instances - indexes must match histo[] above
+ */
+static pmdaInstid *_delay;
+
+static char *queuedir = "/var/spool/mqueue";
+static char startdir[MAXPATHLEN];
+static char *username;
+
+static char *regexstring;
+static regex_t mq_regex;
+
+/*
+ * list of instance domains
+ */
+static pmdaIndom indomtab[] = {
+#define DELAY_INDOM 0
+ { DELAY_INDOM, 0, NULL },
+};
+
+/*
+ * all metrics supported in this PMDA - one table entry for each
+ */
+static pmdaMetric metrictab[] = {
+/* length */
+ { NULL,
+ { PMDA_PMID(0,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+/* deferred */
+ { NULL,
+ { PMDA_PMID(0,1), PM_TYPE_U32, DELAY_INDOM, PM_SEM_INSTANT,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+};
+
+static int
+mailq_histogram(char *option)
+{
+ struct timeval tv;
+ char *errmsg;
+ char *q;
+ int sts;
+
+ q = strtok(option, ",");
+ while (q != NULL) {
+ if ((sts = pmParseInterval((const char *)q, &tv, &errmsg)) < 0) {
+ pmprintf("%s: bad historgram bins argument:\n%s\n", pmProgname, errmsg);
+ free(errmsg);
+ return -EINVAL;
+ }
+ numhisto++;
+ histo = (histo_t *)realloc(histo, numhisto * sizeof(histo[0]));
+ if (histo == NULL)
+ __pmNoMem("histo", numhisto * sizeof(histo[0]), PM_FATAL_ERR);
+ histo[numhisto-1].delay = tv.tv_sec;
+ q = strtok(NULL, ",");
+ }
+ return 0;
+}
+
+static int
+compare_delay(const void *a, const void *b)
+{
+ histo_t *ha = (histo_t *)a;
+ histo_t *hb = (histo_t *)b;
+
+ return hb->delay - ha->delay;
+}
+
+/*
+ * callback provided to pmdaFetch
+ */
+static int
+mailq_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom)
+{
+ __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid);
+ int b;
+
+ if (idp->cluster == 0) {
+ if (idp->item == 0) { /* mailq.length */
+ if (inst == PM_IN_NULL)
+ atom->ul = queue;
+ else
+ return PM_ERR_INST;
+ }
+ else if (idp->item == 1) { /* mailq.deferred */
+ /* inst is unsigned, so always >= 0 */
+ for (b = 0; b < numhisto; b++) {
+ if (histo[b].delay == inst) break;
+ }
+ if (b < numhisto)
+ atom->ul = histo[b].count;
+ else
+ return PM_ERR_INST;
+ }
+ else
+ return PM_ERR_PMID;
+ }
+ else
+ return PM_ERR_PMID;
+
+ return 0;
+}
+
+/*
+ * wrapper for pmdaFetch which refreshes the metrics
+ */
+static int
+mailq_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda)
+{
+ static int warn = 0;
+ int num;
+ int i;
+ int b;
+ struct stat sbuf;
+ time_t now;
+ static time_t last_refresh = 0;
+ time_t waiting;
+ char *p;
+ struct dirent **list;
+
+ time(&now);
+
+ /* clip refresh rate to at most once per 30 seconds */
+ if (now - last_refresh > 30) {
+ last_refresh = now;
+
+ queue = 0;
+ for (b = 0; b < numhisto; b++)
+ histo[b].count = 0;
+
+ if (chdir(queuedir) < 0) {
+ if (warn == 0) {
+ __pmNotifyErr(LOG_ERR, "chdir(\"%s\") failed: %s\n",
+ queuedir, osstrerror());
+ warn = 1;
+ }
+ }
+ else {
+ if (warn == 1) {
+ __pmNotifyErr(LOG_INFO, "chdir(\"%s\") success\n", queuedir);
+ warn = 0;
+ }
+
+ num = scandir(".", &list, NULL, NULL);
+
+ for (i = 0; i < num; i++) {
+ p = list[i]->d_name;
+ /* only file names that match the regular expression */
+ if (regexstring && regexec(&mq_regex, list[i]->d_name, 0, NULL, 0))
+ continue;
+ else if (!regexstring && (*p != 'd' || *(p+1) != 'f'))
+ continue;
+ if (stat(p, &sbuf) != 0) {
+ /*
+ * ENOENT expected sometimes if sendmail is doing its job
+ */
+ if (oserror() == ENOENT)
+ continue;
+ fprintf(stderr, "stat(\"%s\"): %s\n", p, osstrerror());
+ continue;
+ }
+ if (sbuf.st_size > 0 && S_ISREG(sbuf.st_mode)) {
+ /* really in the queue */
+#if defined(HAVE_ST_MTIME_WITH_E)
+ waiting = now - sbuf.st_mtime;
+#elif defined(HAVE_ST_MTIME_WITH_SPEC)
+ waiting = now - sbuf.st_mtimespec.tv_sec;
+#else
+ waiting = now - sbuf.st_mtim.tv_sec;
+#endif
+ for (b = 0; b < numhisto; b++) {
+ if (waiting >= histo[b].delay) {
+ histo[b].count++;
+ break;
+ }
+ }
+ queue++;
+ }
+ }
+ for (i = 0; i < num; i++)
+ free(list[i]);
+ if (num > 0)
+ free(list);
+ }
+ if (chdir(startdir) < 0) {
+ __pmNotifyErr(LOG_ERR, "chdir(\"%s\") failed: %s\n",
+ startdir, osstrerror());
+ }
+ }
+
+ return pmdaFetch(numpmid, pmidlist, resp, pmda);
+}
+
+/*
+ * Initialise the agent (daemon only).
+ */
+void
+mailq_init(pmdaInterface *dp)
+{
+ if (dp->status != 0)
+ return;
+
+ __pmSetProcessIdentity(username);
+ dp->version.two.fetch = mailq_fetch;
+ pmdaSetFetchCallBack(dp, mailq_fetchCallBack);
+ pmdaInit(dp, indomtab, sizeof(indomtab)/sizeof(indomtab[0]), metrictab,
+ sizeof(metrictab)/sizeof(metrictab[0]));
+}
+
+pmdaInterface dispatch;
+
+pmLongOptions longopts[] = {
+ PMDA_OPTIONS_HEADER("Options"),
+ PMOPT_DEBUG,
+ { "binlist", 1, 'b', "TIMES", "comma-separated histogram bins times" },
+ PMDAOPT_DOMAIN,
+ PMDAOPT_LOGFILE,
+ { "regex", 1, 'r', "RE", "regular expression for matching mail file names" },
+ PMDAOPT_USERNAME,
+ PMOPT_HELP,
+ PMDA_OPTIONS_END
+};
+
+pmdaOptions opts = {
+ .short_options = "b:D:d:l:r:U:?",
+ .long_options = longopts,
+ .short_usage = "[options] [queuedir]",
+};
+
+/*
+ * Set up the agent, running as a daemon.
+ */
+int
+main(int argc, char **argv)
+{
+ int sep = __pmPathSeparator();
+ int c;
+ int i;
+ char namebuf[30];
+ char mypath[MAXPATHLEN];
+
+ __pmSetProgname(argv[0]);
+ __pmGetUsername(&username);
+
+ if (getcwd(startdir, sizeof(startdir)) == NULL) {
+ fprintf(stderr, "%s: getcwd() failed: %s\n",
+ pmProgname, pmErrStr(-oserror()));
+ exit(1);
+ }
+
+ snprintf(mypath, sizeof(mypath), "%s%c" "mailq" "%c" "help",
+ pmGetConfig("PCP_PMDAS_DIR"), sep, sep);
+ pmdaDaemon(&dispatch, PMDA_INTERFACE_2, pmProgname, MAILQ,
+ "mailq.log", mypath);
+
+ while ((c = pmdaGetOptions(argc, argv, &opts, &dispatch)) != EOF) {
+ switch (c) {
+ case 'b':
+ if (mailq_histogram(opts.optarg) < 0)
+ opts.errors++;
+ break;
+
+ case 'r':
+ regexstring = opts.optarg;
+ c = regcomp(&mq_regex, regexstring, REG_EXTENDED | REG_NOSUB);
+ if (c != 0) {
+ regerror(c, &mq_regex, mypath, sizeof(mypath));
+ pmprintf("%s: cannot compile regular expression: %s\n",
+ pmProgname, mypath);
+ opts.errors++;
+ }
+ break;
+ }
+ }
+
+ if (opts.optind == argc - 1)
+ queuedir = argv[opts.optind];
+ else if (opts.optind != argc)
+ opts.errors++;
+
+ if (opts.errors) {
+ pmdaUsageMessage(&opts);
+ exit(1);
+ }
+
+ if (opts.username)
+ username = opts.username;
+
+ if (histo == NULL) {
+ /* default histo bins, if not already done above ... */
+ numhisto = 7;
+ histo = (histo_t *)malloc(numhisto * sizeof(histo[0]));
+ if (histo == NULL) {
+ __pmNoMem("histo", numhisto * sizeof(histo[0]), PM_FATAL_ERR);
+ }
+ histo[0].delay = 7 * 24 * 3600;
+ histo[1].delay = 3 * 24 * 3600;
+ histo[2].delay = 24 * 3600;
+ histo[3].delay = 8 * 3600;
+ histo[4].delay = 4 * 3600;
+ histo[5].delay = 1 * 3600;
+ histo[6].delay = 0;
+ }
+ else {
+ /* need to add last one and sort on descending time */
+ numhisto++;
+ histo = (histo_t *)realloc(histo, numhisto * sizeof(histo[0]));
+ if (histo == NULL) {
+ __pmNoMem("histo", numhisto * sizeof(histo[0]), PM_FATAL_ERR);
+ }
+ histo[numhisto-1].delay = 0;
+ qsort(histo, numhisto, sizeof(histo[0]), compare_delay);
+ }
+
+ _delay = (pmdaInstid *)malloc(numhisto * sizeof(_delay[0]));
+ if (_delay == NULL)
+ __pmNoMem("_delay", numhisto * sizeof(_delay[0]), PM_FATAL_ERR);
+
+ for (i = 0; i < numhisto; i++) {
+ time_t tmp;
+ _delay[i].i_inst = histo[i].delay;
+ histo[i].count = 0;
+ if (histo[i].delay == 0)
+ sprintf(namebuf, "recent");
+ else if (histo[i].delay < 60)
+ sprintf(namebuf, "%d-secs", (int)histo[i].delay);
+ else if (histo[i].delay < 60 * 60) {
+ tmp = histo[i].delay / 60;
+ if (tmp <= 1)
+ sprintf(namebuf, "1-min");
+ else
+ sprintf(namebuf, "%d-mins", (int)tmp);
+ }
+ else if (histo[i].delay < 24 * 60 * 60) {
+ tmp = histo[i].delay / (60 * 60);
+ if (tmp <= 1)
+ sprintf(namebuf, "1-hour");
+ else
+ sprintf(namebuf, "%d-hours", (int)tmp);
+ }
+ else {
+ tmp = histo[i].delay / (24 * 60 * 60);
+ if (tmp <= 1)
+ sprintf(namebuf, "1-day");
+ else
+ sprintf(namebuf, "%d-days", (int)tmp);
+ }
+ _delay[i].i_name = strdup(namebuf);
+ if (_delay[i].i_name == NULL) {
+ __pmNoMem("_delay[i].i_name", strlen(namebuf), PM_FATAL_ERR);
+ }
+ }
+
+ indomtab[DELAY_INDOM].it_numinst = numhisto;
+ indomtab[DELAY_INDOM].it_set = _delay;
+
+ pmdaOpenLog(&dispatch);
+ mailq_init(&dispatch);
+ pmdaConnect(&dispatch);
+ pmdaMain(&dispatch);
+
+ exit(0);
+}