]> git.ipfire.org Git - thirdparty/collectd.git/commitdiff
interface plugin: Convert to the v6 API. 3765/head
authorFlorian Forster <octo@collectd.org>
Sat, 3 Oct 2020 06:43:54 +0000 (08:43 +0200)
committerFlorian Forster <octo@collectd.org>
Sat, 3 Oct 2020 06:51:37 +0000 (08:51 +0200)
Fixes: #3641
src/interface.c

index 4e7c5739425d117ae49c3b88d317211ad299cbdb..83573a492d08ed5dc6f6aa3bc54e86adb93edb5d 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/interface.c
- * Copyright (C) 2005-2010  Florian octo Forster
+ * Copyright (C) 2005-2020  Florian octo Forster
  * Copyright (C) 2009       Manuel Sanmartin
  *
  * This program is free software; you can redistribute it and/or modify it
 #endif /* !COLLECT_GETIFADDRS */
 #endif /* KERNEL_LINUX */
 
-#if HAVE_PERFSTAT
-static perfstat_netinterface_t *ifstat;
-static int nif;
-static int pnif;
-#endif /* HAVE_PERFSTAT */
-
 #if !HAVE_GETIFADDRS && !KERNEL_LINUX && !HAVE_LIBKSTAT &&                     \
     !HAVE_LIBSTATGRAB && !HAVE_PERFSTAT
 #error "No applicable input method."
@@ -99,10 +93,52 @@ static bool report_inactive = true;
 #define MAX_NUMIF 256
 extern kstat_ctl_t *kc;
 static kstat_t *ksp[MAX_NUMIF];
-static int numif;
+static size_t numif;
 static bool unique_name;
 #endif /* HAVE_LIBKSTAT */
 
+metric_family_t receive_bytes = {
+    .name = "node_network_receive_bytes_total",
+    .help = "Network device statistic receive_bytes.",
+    .type = METRIC_TYPE_COUNTER,
+};
+metric_family_t receive_drop = {
+    .name = "node_network_receive_drop_total",
+    .help = "Network device statistic receive_drop.",
+    .type = METRIC_TYPE_COUNTER,
+};
+metric_family_t receive_errs = {
+    .name = "node_network_receive_errs_total",
+    .help = "Network device statistic receive_errs.",
+    .type = METRIC_TYPE_COUNTER,
+};
+metric_family_t receive_packets = {
+    .name = "node_network_receive_packets_total",
+    .help = "Network device statistic receive_packets.",
+    .type = METRIC_TYPE_COUNTER,
+};
+
+metric_family_t transmit_bytes = {
+    .name = "node_network_transmit_bytes_total",
+    .help = "Network device statistic transmit_bytes.",
+    .type = METRIC_TYPE_COUNTER,
+};
+metric_family_t transmit_drop = {
+    .name = "node_network_transmit_drop_total",
+    .help = "Network device statistic transmit_drop.",
+    .type = METRIC_TYPE_COUNTER,
+};
+metric_family_t transmit_errs = {
+    .name = "node_network_transmit_errs_total",
+    .help = "Network device statistic transmit_errs.",
+    .type = METRIC_TYPE_COUNTER,
+};
+metric_family_t transmit_packets = {
+    .name = "node_network_transmit_packets_total",
+    .help = "Network device statistic transmit_packets.",
+    .type = METRIC_TYPE_COUNTER,
+};
+
 static int interface_config(const char *key, const char *value) {
   if (ignorelist == NULL)
     ignorelist = ignorelist_create(/* invert = */ 1);
@@ -158,86 +194,70 @@ static int interface_init(void) {
 } /* int interface_init */
 #endif /* HAVE_LIBKSTAT */
 
-static void if_submit(const char *dev, const char *type, derive_t rx,
-                      derive_t tx) {
-  value_list_t vl = VALUE_LIST_INIT;
-  value_t values[] = {
-      {.derive = rx},
-      {.derive = tx},
-  };
-
-  if (ignorelist_match(ignorelist, dev) != 0)
-    return;
-
-  vl.values = values;
-  vl.values_len = STATIC_ARRAY_SIZE(values);
-  sstrncpy(vl.plugin, "interface", sizeof(vl.plugin));
-  sstrncpy(vl.plugin_instance, dev, sizeof(vl.plugin_instance));
-  sstrncpy(vl.type, type, sizeof(vl.type));
-
-  plugin_dispatch_values(&vl);
-} /* void if_submit */
-
-static int interface_read(void) {
+static int if_read_internal(void) {
 #if KERNEL_LINUX
-  FILE *fh;
-  char buffer[1024];
-  derive_t incoming, outgoing;
-  char *device;
-
-  char *dummy;
-  char *fields[16];
-  int numfields;
-
-  if ((fh = fopen("/proc/net/dev", "r")) == NULL) {
-    WARNING("interface plugin: fopen: %s", STRERRNO);
-    return -1;
+  FILE *fh = fopen("/proc/net/dev", "r");
+  if (fh == NULL) {
+    WARNING("interface plugin: fopen(\"/proc/net/dev\"): %s", STRERRNO);
+    return errno;
   }
 
-  while (fgets(buffer, 1024, fh) != NULL) {
-    if (!(dummy = strchr(buffer, ':')))
+  char buffer[1024];
+  while (fgets(buffer, sizeof(buffer), fh) != NULL) {
+    char *dummy = strchr(buffer, ':');
+    if (dummy == NULL) {
       continue;
-    dummy[0] = '\0';
+    }
+    dummy[0] = 0;
     dummy++;
 
-    device = buffer;
+    char *device = buffer;
     while (device[0] == ' ')
       device++;
 
-    if (device[0] == '\0')
+    if ((strlen(device) == 0) || ignorelist_match(ignorelist, device)) {
       continue;
+    }
 
-    numfields = strsplit(dummy, fields, 16);
+    char *fields[16];
+    int numfields = strsplit(dummy, fields, STATIC_ARRAY_SIZE(fields));
 
-    if (numfields < 11)
+    if (numfields < 11) {
       continue;
+    }
 
-    incoming = atoll(fields[1]);
-    outgoing = atoll(fields[9]);
-    if (!report_inactive && incoming == 0 && outgoing == 0)
+    counter_t rx = (counter_t)atoll(fields[1]);
+    counter_t tx = (counter_t)atoll(fields[9]);
+    if (!report_inactive && (rx == 0) && (tx == 0)) {
       continue;
+    }
 
-    if_submit(device, "if_packets", incoming, outgoing);
-
-    incoming = atoll(fields[0]);
-    outgoing = atoll(fields[8]);
-    if_submit(device, "if_octets", incoming, outgoing);
-
-    incoming = atoll(fields[2]);
-    outgoing = atoll(fields[10]);
-    if_submit(device, "if_errors", incoming, outgoing);
+    struct {
+      metric_family_t *fam;
+      char const *value;
+    } metrics[] = {
+        {&receive_bytes, fields[0]},  {&receive_packets, fields[1]},
+        {&receive_errs, fields[2]},   {&receive_drop, fields[3]},
+        {&transmit_bytes, fields[8]}, {&transmit_packets, fields[9]},
+        {&transmit_errs, fields[10]}, {&transmit_drop, fields[11]},
+    };
+
+    for (size_t i = 0; i < STATIC_ARRAY_SIZE(metrics); i++) {
+      errno = 0;
+      counter_t v = (counter_t)strtoull(metrics[i].value, NULL, 0);
+      if (errno != 0) {
+        continue;
+      }
 
-    incoming = atoll(fields[3]);
-    outgoing = atoll(fields[11]);
-    if_submit(device, "if_dropped", incoming, outgoing);
+      metric_family_append(metrics[i].fam, "device", device,
+                           (value_t){.counter = v}, NULL);
+    }
   }
 
   fclose(fh);
   /* #endif KERNEL_LINUX */
 
 #elif HAVE_GETIFADDRS
-  struct ifaddrs *if_list;
-
 /* Darwin/Mac OS X and possible other *BSDs */
 #if HAVE_STRUCT_IF_DATA
 #define IFA_DATA if_data
@@ -261,26 +281,41 @@ static int interface_read(void) {
 #error "No suitable type for `struct ifaddrs->ifa_data' found."
 #endif
 
-  struct IFA_DATA *if_data;
-
+  struct ifaddrs *if_list = NULL;
   if (getifaddrs(&if_list) != 0)
     return -1;
 
   for (struct ifaddrs *if_ptr = if_list; if_ptr != NULL;
        if_ptr = if_ptr->ifa_next) {
-    if (if_ptr->ifa_addr != NULL && if_ptr->ifa_addr->sa_family == AF_LINK) {
-      if_data = (struct IFA_DATA *)if_ptr->ifa_data;
+    if ((if_ptr->ifa_addr == NULL) ||
+        (if_ptr->ifa_addr->sa_family != AF_LINK)) {
+      continue;
+    }
+    if (ignorelist_match(ignorelist, if_ptr->ifa_name)) {
+      continue;
+    }
 
-      if (!report_inactive && if_data->IFA_RX_PACKT == 0 &&
-          if_data->IFA_TX_PACKT == 0)
-        continue;
+    struct IFA_DATA *if_data = (struct IFA_DATA *)if_ptr->ifa_data;
+    if (!report_inactive && if_data->IFA_RX_PACKT == 0 &&
+        if_data->IFA_TX_PACKT == 0) {
+      continue;
+    }
 
-      if_submit(if_ptr->ifa_name, "if_octets", if_data->IFA_RX_BYTES,
-                if_data->IFA_TX_BYTES);
-      if_submit(if_ptr->ifa_name, "if_packets", if_data->IFA_RX_PACKT,
-                if_data->IFA_TX_PACKT);
-      if_submit(if_ptr->ifa_name, "if_errors", if_data->IFA_RX_ERROR,
-                if_data->IFA_TX_ERROR);
+    struct {
+      metric_family_t *fam;
+      counter_t value;
+    } metrics[] = {
+        {&receive_bytes, (counter_t)if_data->IFA_RX_BYTES},
+        {&receive_packets, (counter_t)if_data->IFA_RX_PACKT},
+        {&receive_errs, (counter_t)if_data->IFA_RX_ERROR},
+        {&transmit_bytes, (counter_t)if_data->IFA_TX_BYTES},
+        {&transmit_packets, (counter_t)if_data->IFA_TX_PACKT},
+        {&transmit_errs, (counter_t)if_data->IFA_TX_ERROR},
+    };
+
+    for (size_t i = 0; i < STATIC_ARRAY_SIZE(metrics); i++) {
+      metric_family_append(metrics[i].fam, "device", if_ptr->ifa_name,
+                           (value_t){.counter = metrics[i].value}, NULL);
     }
   }
 
@@ -288,106 +323,143 @@ static int interface_read(void) {
   /* #endif HAVE_GETIFADDRS */
 
 #elif HAVE_LIBKSTAT
-  derive_t rx;
-  derive_t tx;
-  char iname[DATA_MAX_NAME_LEN];
-
-  if (kc == NULL)
+  if (kc == NULL) {
     return -1;
+  }
 
-  for (int i = 0; i < numif; i++) {
-    if (kstat_read(kc, ksp[i], NULL) == -1)
+  for (size_t i = 0; i < numif; i++) {
+    if (kstat_read(kc, ksp[i], NULL) == -1) {
       continue;
+    }
 
-    if (unique_name)
+    char iname[128];
+    if (unique_name) {
       ssnprintf(iname, sizeof(iname), "%s_%d_%s", ksp[i]->ks_module,
                 ksp[i]->ks_instance, ksp[i]->ks_name);
-    else
+    } else {
       sstrncpy(iname, ksp[i]->ks_name, sizeof(iname));
+    }
 
-    /* try to get 64bit counters */
-    rx = get_kstat_value(ksp[i], "ipackets64");
-    tx = get_kstat_value(ksp[i], "opackets64");
-    /* or fallback to 32bit */
-    if (rx == -1LL)
-      rx = get_kstat_value(ksp[i], "ipackets");
-    if (tx == -1LL)
-      tx = get_kstat_value(ksp[i], "opackets");
-    if (!report_inactive && rx == 0 && tx == 0)
-      continue;
-    if ((rx != -1LL) || (tx != -1LL))
-      if_submit(iname, "if_packets", rx, tx);
-
-    /* try to get 64bit counters */
-    rx = get_kstat_value(ksp[i], "rbytes64");
-    tx = get_kstat_value(ksp[i], "obytes64");
-    /* or fallback to 32bit */
-    if (rx == -1LL)
-      rx = get_kstat_value(ksp[i], "rbytes");
-    if (tx == -1LL)
-      tx = get_kstat_value(ksp[i], "obytes");
-    if ((rx != -1LL) || (tx != -1LL))
-      if_submit(iname, "if_octets", rx, tx);
-
-    /* no 64bit error counters yet */
-    rx = get_kstat_value(ksp[i], "ierrors");
-    tx = get_kstat_value(ksp[i], "oerrors");
-    if ((rx != -1LL) || (tx != -1LL))
-      if_submit(iname, "if_errors", rx, tx);
+    struct {
+      metric_family_t *fam;
+      char const *name;
+      char const *fallback_name;
+    } metrics[] = {
+        {&receive_bytes, "rbytes64", "rbytes"},
+        {&receive_packets, "ipackets64", "ipackets"},
+        {&receive_errs, "ierrors"},
+        {&transmit_bytes, "obytes64", "obytes"},
+        {&transmit_packets, "opackets64", "opackets"},
+        {&transmit_errs, "oerrors"},
+    };
+
+    for (size_t j = 0; j < STATIC_ARRAY_SIZE(metrics); j++) {
+      long long value = get_kstat_value(ksp[i], metrics[j].name);
+      if ((value == -1) && (metrics[j].fallback_name != NULL)) {
+        value = get_kstat_value(ksp[i], metrics[j].fallback_name);
+      }
+      if (value == -1) {
+        continue;
+      }
+      metric_family_append(metrics[i].fam, "device", iname,
+                           (value_t){.counter = (counter_t)value}, NULL);
+    }
   }
-    /* #endif HAVE_LIBKSTAT */
+  /* #endif HAVE_LIBKSTAT */
 
 #elif defined(HAVE_LIBSTATGRAB)
-  sg_network_io_stats *ios;
-  int num;
-
-  ios = sg_get_network_io_stats(&num);
+  int num = 0;
+  sg_network_io_stats *ios = sg_get_network_io_stats(&num);
 
   for (int i = 0; i < num; i++) {
-    if (!report_inactive && ios[i].rx == 0 && ios[i].tx == 0)
+    if (!report_inactive && ios[i].rx == 0 && ios[i].tx == 0) {
       continue;
-    if_submit(ios[i].interface_name, "if_octets", ios[i].rx, ios[i].tx);
+    }
+
+    metric_family_append(&receive_bytes, "device", ios[i].interface_name,
+                         (value_t){.counter = (counter_t)ios[i].rx}, NULL);
+    metric_family_append(&transmit_bytes, "device", ios[i].interface_name,
+                         (value_t){.counter = (counter_t)ios[i].tx}, NULL);
   }
-    /* #endif HAVE_LIBSTATGRAB */
+  /* #endif HAVE_LIBSTATGRAB */
 
 #elif defined(HAVE_PERFSTAT)
-  perfstat_id_t id;
-  int ifs;
-
-  if ((nif = perfstat_netinterface(NULL, NULL, sizeof(perfstat_netinterface_t),
-                                   0)) < 0) {
+  int num =
+      perfstat_netinterface(NULL, NULL, sizeof(perfstat_netinterface_t), 0);
+  if (num < 0) {
     WARNING("interface plugin: perfstat_netinterface: %s", STRERRNO);
     return -1;
   }
+  if (num == 0) {
+    return 0;
+  }
 
-  if (pnif != nif || ifstat == NULL) {
-    free(ifstat);
-    ifstat = malloc(nif * sizeof(*ifstat));
+  perfstat_netinterface_t *ifstat =
+      calloc(num, sizeof(perfstat_netinterface_t));
+  if (ifstat == NULL) {
+    return errno;
   }
-  pnif = nif;
 
-  id.name[0] = '\0';
-  if ((ifs = perfstat_netinterface(&id, ifstat, sizeof(perfstat_netinterface_t),
-                                   nif)) < 0) {
-    WARNING("interface plugin: perfstat_netinterface (interfaces=%d): %s", nif,
-            STRERRNO);
+  perfstat_id_t id = {
+      .name = {0},
+  };
+  int status =
+      perfstat_netinterface(&id, ifstat, sizeof(perfstat_netinterface_t), num);
+  if (status < 0) {
+    ERROR("interface plugin: perfstat_netinterface (interfaces=%d): %s", num,
+          STRERRNO);
+    sfree(ifstat);
     return -1;
   }
 
-  for (int i = 0; i < ifs; i++) {
-    if (!report_inactive && ifstat[i].ipackets == 0 && ifstat[i].opackets == 0)
+  for (int i = 0; i < num; i++) {
+    if (!report_inactive && ifstat[i].ipackets == 0 &&
+        ifstat[i].opackets == 0) {
       continue;
+    }
 
-    if_submit(ifstat[i].name, "if_octets", ifstat[i].ibytes, ifstat[i].obytes);
-    if_submit(ifstat[i].name, "if_packets", ifstat[i].ipackets,
-              ifstat[i].opackets);
-    if_submit(ifstat[i].name, "if_errors", ifstat[i].ierrors,
-              ifstat[i].oerrors);
+    struct {
+      metric_family_t *fam;
+      counter_t value;
+    } metrics[] = {
+        {&receive_bytes, (counter_t)ifstat[i].ibytes},
+        {&receive_packets, (counter_t)ifstat[i].ipackets},
+        {&receive_errs, (counter_t)ifstat[i].ierrors},
+        {&transmit_bytes, (counter_t)ifstat[i].obytes},
+        {&transmit_packets, (counter_t)ifstat[i].opackets},
+        {&transmit_errs, (counter_t)ifstat[i].oerrors},
+    };
+
+    for (size_t j = 0; j < STATIC_ARRAY_SIZE(metrics); j++) {
+      metric_family_append(metrics[j].fam, "device", ifstat[i].name,
+                           (value_t){.counter = metrics[j].value}, NULL);
+    }
   }
+
+  sfree(ifstat);
 #endif /* HAVE_PERFSTAT */
 
   return 0;
-} /* int interface_read */
+} /* int if_read_internal */
+
+static int if_read(void) {
+  metric_family_t *families[] = {
+      &receive_bytes,  &receive_drop,  &receive_errs,  &receive_packets,
+      &transmit_bytes, &transmit_drop, &transmit_errs, &transmit_packets,
+  };
+
+  int status = if_read_internal();
+
+  for (size_t i = 0; i < STATIC_ARRAY_SIZE(families); i++) {
+    if (status == 0) {
+      plugin_dispatch_metric_family(families[i]);
+    }
+
+    metric_family_metric_reset(families[i]);
+  }
+
+  return status;
+}
 
 void module_register(void) {
   plugin_register_config("interface", interface_config, config_keys,
@@ -395,5 +467,5 @@ void module_register(void) {
 #if HAVE_LIBKSTAT
   plugin_register_init("interface", interface_init);
 #endif
-  plugin_register_read("interface", interface_read);
+  plugin_register_read("interface", if_read);
 } /* void module_register */