diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
commit | 47e6e7c84f008a53061e661f31ae96629bc694ef (patch) | |
tree | 648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/pmdas/linux/proc_net_snmp.c | |
download | pcp-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.c | 367 |
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; +} |