summaryrefslogtreecommitdiff
path: root/src/pmdas/linux/proc_net_snmp.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
committerIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
commit47e6e7c84f008a53061e661f31ae96629bc694ef (patch)
tree648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/pmdas/linux/proc_net_snmp.c
downloadpcp-debian/3.9.10.tar.gz
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmdas/linux/proc_net_snmp.c')
-rw-r--r--src/pmdas/linux/proc_net_snmp.c367
1 files changed, 367 insertions, 0 deletions
diff --git a/src/pmdas/linux/proc_net_snmp.c b/src/pmdas/linux/proc_net_snmp.c
new file mode 100644
index 0000000..431d984
--- /dev/null
+++ b/src/pmdas/linux/proc_net_snmp.c
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2013-2014 Red Hat.
+ * Copyright (c) 2000,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.
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+#include "pmda.h"
+#include "indom.h"
+#include "proc_net_snmp.h"
+
+extern proc_net_snmp_t _pm_proc_net_snmp;
+extern pmdaInstid _pm_proc_net_snmp_indom_id[];
+static char *proc_net_snmp_icmpmsg_names;
+
+typedef struct {
+ const char *field;
+ __uint64_t *offset;
+} snmp_fields_t;
+
+snmp_fields_t ip_fields[] = {
+ { .field = "Forwarding",
+ .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_FORWARDING] },
+ { .field = "DefaultTTL",
+ .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_DEFAULTTTL] },
+ { .field = "InReceives",
+ .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_INRECEIVES] },
+ { .field = "InHdrErrors",
+ .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_INHDRERRORS] },
+ { .field = "InAddrErrors",
+ .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_INADDRERRORS] },
+ { .field = "ForwDatagrams",
+ .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_FORWDATAGRAMS] },
+ { .field = "InUnknownProtos",
+ .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_INUNKNOWNPROTOS] },
+ { .field = "InDiscards",
+ .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_INDISCARDS] },
+ { .field = "InDelivers",
+ .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_INDELIVERS] },
+ { .field = "OutRequests",
+ .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_OUTREQUESTS] },
+ { .field = "OutDiscards",
+ .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_OUTDISCARDS] },
+ { .field = "OutNoRoutes",
+ .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_OUTNOROUTES] },
+ { .field = "ReasmTimeout",
+ .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_REASMTIMEOUT] },
+ { .field = "ReasmReqds",
+ .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_REASMREQDS] },
+ { .field = "ReasmOKs",
+ .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_REASMOKS] },
+ { .field = "ReasmFails",
+ .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_REASMFAILS] },
+ { .field = "FragOKs",
+ .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_FRAGOKS] },
+ { .field = "FragFails",
+ .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_FRAGFAILS] },
+ { .field = "FragCreates",
+ .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_FRAGCREATES] },
+ { .field = NULL, .offset = NULL }
+};
+
+snmp_fields_t icmp_fields[] = {
+ { .field = "InMsgs",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INMSGS] },
+ { .field = "InErrors",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INERRORS] },
+ { .field = "InCsumErrors",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INCSUMERRORS] },
+ { .field = "InDestUnreachs",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INDESTUNREACHS] },
+ { .field = "InTimeExcds",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INTIMEEXCDS] },
+ { .field = "InParmProbs",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INPARMPROBS] },
+ { .field = "InSrcQuenchs",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INSRCQUENCHS] },
+ { .field = "InRedirects",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INREDIRECTS] },
+ { .field = "InEchos",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INECHOS] },
+ { .field = "InEchoReps",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INECHOREPS] },
+ { .field = "InTimestamps",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INTIMESTAMPS] },
+ { .field = "InTimestampReps",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INTIMESTAMPREPS] },
+ { .field = "InAddrMasks",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INADDRMASKS] },
+ { .field = "InAddrMaskReps",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INADDRMASKREPS] },
+ { .field = "OutMsgs",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTMSGS] },
+ { .field = "OutErrors",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTERRORS] },
+ { .field = "OutDestUnreachs",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTDESTUNREACHS] },
+ { .field = "OutTimeExcds",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTTIMEEXCDS] },
+ { .field = "OutParmProbs",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTPARMPROBS] },
+ { .field = "OutSrcQuenchs",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTSRCQUENCHS] },
+ { .field = "OutRedirects",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTREDIRECTS] },
+ { .field = "OutEchos",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTECHOS] },
+ { .field = "OutEchoReps",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTECHOREPS] },
+ { .field = "OutTimestamps",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTTIMESTAMPS] },
+ { .field = "OutTimestampReps",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTTIMESTAMPREPS] },
+ { .field = "OutAddrMasks",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTADDRMASKS] },
+ { .field = "OutAddrMaskReps",
+ .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTADDRMASKREPS] },
+ { .field = NULL, .offset = NULL }
+};
+
+snmp_fields_t icmpmsg_fields[] = {
+ { .field = "InType%u",
+ .offset = &_pm_proc_net_snmp.icmpmsg[_PM_SNMP_ICMPMSG_INTYPE] },
+ { .field = "OutType%u",
+ .offset = &_pm_proc_net_snmp.icmpmsg[_PM_SNMP_ICMPMSG_OUTTYPE] },
+ { .field = NULL, .offset = NULL }
+};
+
+snmp_fields_t tcp_fields[] = {
+ { .field = "RtoAlgorithm",
+ .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_RTOALGORITHM] },
+ { .field = "RtoMin",
+ .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_RTOMIN] },
+ { .field = "RtoMax",
+ .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_RTOMAX] },
+ { .field = "MaxConn",
+ .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_MAXCONN] },
+ { .field = "ActiveOpens",
+ .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_ACTIVEOPENS] },
+ { .field = "PassiveOpens",
+ .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_PASSIVEOPENS] },
+ { .field = "AttemptFails",
+ .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_ATTEMPTFAILS] },
+ { .field = "EstabResets",
+ .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_ESTABRESETS] },
+ { .field = "CurrEstab",
+ .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_CURRESTAB] },
+ { .field = "InSegs",
+ .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_INSEGS] },
+ { .field = "OutSegs",
+ .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_OUTSEGS] },
+ { .field = "RetransSegs",
+ .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_RETRANSSEGS] },
+ { .field = "InErrs",
+ .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_INERRS] },
+ { .field = "OutRsts",
+ .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_OUTRSTS] },
+ { .field = "InCsumErrors",
+ .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_INCSUMERRORS] },
+ { .field = NULL, .offset = NULL }
+};
+
+snmp_fields_t udp_fields[] = {
+ { .field = "InDatagrams",
+ .offset = &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_INDATAGRAMS] },
+ { .field = "NoPorts",
+ .offset = &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_NOPORTS] },
+ { .field = "InErrors",
+ .offset = &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_INERRORS] },
+ { .field = "OutDatagrams",
+ .offset = &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_OUTDATAGRAMS] },
+ { .field = "RcvbufErrors",
+ .offset = &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_RECVBUFERRORS] },
+ { .field = "SndbufErrors",
+ .offset = &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_SNDBUFERRORS] },
+ { .field = "InCsumErrors",
+ .offset = &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_INCSUMERRORS] },
+ { .field = NULL, .offset = NULL }
+};
+
+snmp_fields_t udplite_fields[] = {
+ { .field = "InDatagrams",
+ .offset = &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_INDATAGRAMS] },
+ { .field = "NoPorts",
+ .offset = &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_NOPORTS] },
+ { .field = "InErrors",
+ .offset = &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_INERRORS] },
+ { .field = "OutDatagrams",
+ .offset = &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_OUTDATAGRAMS] },
+ { .field = "RcvbufErrors",
+ .offset = &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_RECVBUFERRORS] },
+ { .field = "SndbufErrors",
+ .offset = &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_SNDBUFERRORS] },
+ { .field = "InCsumErrors",
+ .offset = &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_INCSUMERRORS] },
+ { .field = NULL, .offset = NULL }
+};
+
+static void
+get_fields(snmp_fields_t *fields, char *header, char *buffer)
+{
+ int i, j, count;
+ char *p, *indices[SNMP_MAX_COLUMNS];
+
+ /* first get pointers to each of the column headings */
+ strtok(header, " ");
+ for (i = 0; i < SNMP_MAX_COLUMNS; i++) {
+ if ((p = strtok(NULL, " \n")) == NULL)
+ break;
+ indices[i] = p;
+ }
+ count = i;
+
+ /*
+ * Extract values via back-referencing column headings.
+ * "i" is the last found index, which we use for a bit
+ * of optimisation for the (common) in-order maps case
+ * (where "in order" means in the order defined by the
+ * passed in "fields" table which typically matches the
+ * kernel - but may be out-of-order for older kernels).
+ */
+ strtok(buffer, " ");
+ for (i = j = 0; j < count && fields[i].field; j++, i++) {
+ if ((p = strtok(NULL, " \n")) == NULL)
+ break;
+ if (strcmp(fields[i].field, indices[j]) == 0) {
+ *fields[i].offset = strtoull(p, NULL, 10);
+ } else {
+ for (i = 0; fields[i].field; i++) {
+ if (strcmp(fields[i].field, indices[j]) != 0)
+ continue;
+ *fields[i].offset = strtoull(p, NULL, 10);
+ break;
+ }
+ if (fields[i].field == NULL) /* not found, ignore */
+ i = 0;
+ }
+ }
+}
+
+static void
+get_ordinal_fields(snmp_fields_t *fields, char *header, char *buffer,
+ unsigned limit)
+{
+ int i, j, count;
+ unsigned int inst;
+ char *p, *indices[SNMP_MAX_COLUMNS];
+
+ strtok(header, " ");
+ for (i = 0; i < SNMP_MAX_COLUMNS; i++) {
+ if ((p = strtok(NULL, " \n")) == NULL)
+ break;
+ indices[i] = p;
+ }
+ count = i;
+
+ strtok(buffer, " ");
+ for (j = 0; j < count; j++) {
+ if ((p = strtok(NULL, " \n")) == NULL)
+ break;
+ for (i = 0; fields[i].field; i++) {
+ if (sscanf(indices[j], fields[i].field, &inst) != 1)
+ continue;
+ if (inst >= limit)
+ continue;
+ *(fields[i].offset + inst) = strtoull(p, NULL, 10);
+ break;
+ }
+ }
+}
+
+#define SNMP_IP_OFFSET(ii, pp) (int64_t *)((char *)pp + \
+ (__psint_t)ip_fields[ii].offset - (__psint_t)&_pm_proc_net_snmp.ip)
+#define SNMP_ICMP_OFFSET(ii, pp) (int64_t *)((char *)pp + \
+ (__psint_t)icmp_fields[ii].offset - (__psint_t)&_pm_proc_net_snmp.icmp)
+#define SNMP_ICMPMSG_OFFSET(ii, nn, pp) (int64_t *)((char *)pp + \
+ (__psint_t)(icmpmsg_fields[ii].offset + nn) - (__psint_t)&_pm_proc_net_snmp.icmpmsg)
+#define SNMP_TCP_OFFSET(ii, pp) (int64_t *)((char *)pp + \
+ (__psint_t)tcp_fields[ii].offset - (__psint_t)&_pm_proc_net_snmp.tcp)
+#define SNMP_UDP_OFFSET(ii, pp) (int64_t *)((char *)pp + \
+ (__psint_t)udp_fields[ii].offset - (__psint_t)&_pm_proc_net_snmp.udp)
+#define SNMP_UDPLITE_OFFSET(ii, pp) (int64_t *)((char *)pp + \
+ (__psint_t)udplite_fields[ii].offset - (__psint_t)&_pm_proc_net_snmp.udplite)
+
+static void
+init_refresh_proc_net_snmp(proc_net_snmp_t *snmp)
+{
+ pmdaIndom *idp;
+ char *s;
+ int i, n;
+
+ /* initially, all marked as "no value available" */
+ for (i = 0; ip_fields[i].field != NULL; i++)
+ *(SNMP_IP_OFFSET(i, snmp->ip)) = -1;
+ for (i = 0; icmp_fields[i].field != NULL; i++)
+ *(SNMP_ICMP_OFFSET(i, snmp->icmp)) = -1;
+ for (i = 0; tcp_fields[i].field != NULL; i++)
+ *(SNMP_TCP_OFFSET(i, snmp->tcp)) = -1;
+ for (i = 0; udp_fields[i].field != NULL; i++)
+ *(SNMP_UDP_OFFSET(i, snmp->udp)) = -1;
+ for (i = 0; udplite_fields[i].field != NULL; i++)
+ *(SNMP_UDPLITE_OFFSET(i, snmp->udplite)) = -1;
+ for (i = 0; icmpmsg_fields[i].field != NULL; i++)
+ for (n = 0; n < NR_ICMPMSG_COUNTERS; n++)
+ *(SNMP_ICMPMSG_OFFSET(i, n, snmp->icmpmsg)) = -1;
+
+ /* only need to allocate and setup the names once */
+ if (proc_net_snmp_icmpmsg_names)
+ return;
+ i = NR_ICMPMSG_COUNTERS * SNMP_MAX_ICMPMSG_TYPESTR;
+ proc_net_snmp_icmpmsg_names = malloc(i);
+ if (!proc_net_snmp_icmpmsg_names)
+ return;
+ s = proc_net_snmp_icmpmsg_names;
+ for (n = 0; n < NR_ICMPMSG_COUNTERS; n++) {
+ sprintf(s, "Type%u", n);
+ _pm_proc_net_snmp_indom_id[n].i_name = s;
+ _pm_proc_net_snmp_indom_id[n].i_inst = n;
+ s += SNMP_MAX_ICMPMSG_TYPESTR;
+ }
+ idp = PMDAINDOM(ICMPMSG_INDOM);
+ idp->it_numinst = NR_ICMPMSG_COUNTERS;
+ idp->it_set = _pm_proc_net_snmp_indom_id;
+}
+
+int
+refresh_proc_net_snmp(proc_net_snmp_t *snmp)
+{
+ char buf[MAXPATHLEN];
+ char header[1024];
+ FILE *fp;
+
+ init_refresh_proc_net_snmp(snmp);
+ if ((fp = linux_statsfile("/proc/net/snmp", buf, sizeof(buf))) == NULL)
+ return -oserror();
+ while (fgets(header, sizeof(header), fp) != NULL) {
+ if (fgets(buf, sizeof(buf), fp) != NULL) {
+ if (strncmp(buf, "Ip:", 3) == 0)
+ get_fields(ip_fields, header, buf);
+ else if (strncmp(buf, "Icmp:", 5) == 0)
+ get_fields(icmp_fields, header, buf);
+ else if (strncmp(buf, "IcmpMsg:", 8) == 0)
+ get_ordinal_fields(icmpmsg_fields, header, buf,
+ NR_ICMPMSG_COUNTERS);
+ else if (strncmp(buf, "Tcp:", 4) == 0)
+ get_fields(tcp_fields, header, buf);
+ else if (strncmp(buf, "Udp:", 4) == 0)
+ get_fields(udp_fields, header, buf);
+ else if (strncmp(buf, "UdpLite:", 8) == 0)
+ get_fields(udplite_fields, header, buf);
+ else
+ fprintf(stderr, "Error: unrecognised snmp row: %s", buf);
+ }
+ }
+ fclose(fp);
+ return 0;
+}