collectd.conf.in: remove extraneous pound sign
[collectd.git] / src / disk.c
index 4a78f1b..1c3dd98 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/disk.c
- * Copyright (C) 2005-2008  Florian octo Forster
+ * Copyright (C) 2005-2012  Florian octo Forster
  * Copyright (C) 2009       Manuel Sanmartin
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -17,7 +17,7 @@
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  *   Manuel Sanmartin
  **/
 
@@ -75,6 +75,9 @@
 
 #if HAVE_IOKIT_IOKITLIB_H
 static mach_port_t io_master_port = MACH_PORT_NULL;
+/* This defaults to false for backwards compatibility. Please fix in the next
+ * major version. */
+static _Bool use_bsd_name = 0;
 /* #endif HAVE_IOKIT_IOKITLIB_H */
 
 #elif KERNEL_LINUX
@@ -85,19 +88,19 @@ typedef struct diskstats
        /* This overflows in roughly 1361 years */
        unsigned int poll_count;
 
-       counter_t read_sectors;
-       counter_t write_sectors;
+       derive_t read_sectors;
+       derive_t write_sectors;
 
-       counter_t read_bytes;
-       counter_t write_bytes;
+       derive_t read_bytes;
+       derive_t write_bytes;
 
-       counter_t read_ops;
-       counter_t write_ops;
-       counter_t read_time;
-       counter_t write_time;
+       derive_t read_ops;
+       derive_t write_ops;
+       derive_t read_time;
+       derive_t write_time;
 
-       counter_t avg_read_time;
-       counter_t avg_write_time;
+       derive_t avg_read_time;
+       derive_t avg_write_time;
 
        struct diskstats *next;
 } diskstats_t;
@@ -106,7 +109,7 @@ static diskstats_t *disklist;
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKSTAT
-#define MAX_NUMDISK 256
+#define MAX_NUMDISK 1024
 extern kstat_ctl_t *kc;
 static kstat_t *ksp[MAX_NUMDISK];
 static int numdisk = 0;
@@ -125,10 +128,19 @@ static int pnumdisk;
 # error "No applicable input method."
 #endif
 
+#if HAVE_LIBUDEV
+#include <libudev.h>
+
+static char *conf_udev_name_attr = NULL;
+static struct udev *handle_udev;
+#endif
+
 static const char *config_keys[] =
 {
        "Disk",
-       "IgnoreSelected"
+       "UseBSDName",
+       "IgnoreSelected",
+       "UdevNameAttr"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
@@ -152,6 +164,30 @@ static int disk_config (const char *key, const char *value)
       invert = 0;
     ignorelist_set_invert (ignorelist, invert);
   }
+  else if (strcasecmp ("UseBSDName", key) == 0)
+  {
+#if HAVE_IOKIT_IOKITLIB_H
+    use_bsd_name = IS_TRUE (value) ? 1 : 0;
+#else
+    WARNING ("disk plugin: The \"UseBSDName\" option is only supported "
+        "on Mach / Mac OS X and will be ignored.");
+#endif
+  }
+  else if (strcasecmp ("UdevNameAttr", key) == 0)
+  {
+#if HAVE_LIBUDEV
+    if (conf_udev_name_attr != NULL)
+    {
+      free (conf_udev_name_attr);
+      conf_udev_name_attr = NULL;
+    }
+    if ((conf_udev_name_attr = strdup (value)) == NULL)
+      return (1);
+#else
+    WARNING ("disk plugin: The \"UdevNameAttr\" option is only supported "
+        "if collectd is built with libudev support");
+#endif
+  }
   else
   {
     return (-1);
@@ -212,7 +248,7 @@ static int disk_init (void)
 
 static void disk_submit (const char *plugin_instance,
                const char *type,
-               counter_t read, counter_t write)
+               derive_t read, derive_t write)
 {
        value_t values[2];
        value_list_t vl = VALUE_LIST_INIT;
@@ -221,8 +257,8 @@ static void disk_submit (const char *plugin_instance,
        if (ignorelist_match (ignorelist, plugin_instance) != 0)
          return;
 
-       values[0].counter = read;
-       values[1].counter = write;
+       values[0].derive = read;
+       values[1].derive = write;
 
        vl.values = values;
        vl.values_len = 2;
@@ -236,15 +272,86 @@ static void disk_submit (const char *plugin_instance,
 } /* void disk_submit */
 
 #if KERNEL_LINUX
+static void submit_in_progress (char const *disk_name, gauge_t in_progress)
+{
+       value_t v;
+       value_list_t vl = VALUE_LIST_INIT;
+
+       if (ignorelist_match (ignorelist, disk_name) != 0)
+         return;
+
+       v.gauge = in_progress;
+
+       vl.values = &v;
+       vl.values_len = 1;
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "disk", sizeof (vl.plugin));
+       sstrncpy (vl.plugin_instance, disk_name, sizeof (vl.plugin_instance));
+       sstrncpy (vl.type, "pending_operations", sizeof (vl.type));
+
+       plugin_dispatch_values (&vl);
+}
+
+static void submit_io_time (char const *plugin_instance, derive_t io_time, derive_t weighted_time)
+{
+       value_t values[2];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       if (ignorelist_match (ignorelist, plugin_instance) != 0)
+         return;
+
+       values[0].derive = io_time;
+       values[1].derive = weighted_time;
+
+       vl.values = values;
+       vl.values_len = 2;
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "disk", sizeof (vl.plugin));
+       sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
+       sstrncpy (vl.type, "disk_io_time", sizeof (vl.type));
+
+       plugin_dispatch_values (&vl);
+}
+
+
 static counter_t disk_calc_time_incr (counter_t delta_time, counter_t delta_ops)
 {
+       double interval = CDTIME_T_TO_DOUBLE (plugin_get_interval ());
        double avg_time = ((double) delta_time) / ((double) delta_ops);
-       double avg_time_incr = ((double) interval_g) * avg_time;
+       double avg_time_incr = interval * avg_time;
 
        return ((counter_t) (avg_time_incr + .5));
 }
 #endif
 
+#if HAVE_LIBUDEV
+/**
+ * Attempt to provide an rename disk instance from an assigned udev attribute.
+ *
+ * On success, it returns a strduped char* to the desired attribute value.
+ * Otherwise it returns NULL.
+ */
+
+static char *disk_udev_attr_name (struct udev *udev, char *disk_name, const char *attr)
+{
+       struct udev_device *dev;
+       const char *prop;
+       char *output = NULL;
+
+       dev = udev_device_new_from_subsystem_sysname (udev, "block", disk_name);
+       if (dev != NULL)
+       {
+               prop = udev_device_get_property_value (dev, attr);
+               if (prop) {
+                       output = strdup (prop);
+                       DEBUG ("disk plugin: renaming %s => %s", disk_name, output);
+               }
+               udev_device_unref (dev);
+       }
+       return output;
+}
+#endif
+
 #if HAVE_IOKIT_IOKITLIB_H
 static signed long long dict_get_value (CFDictionaryRef dict, const char *key)
 {
@@ -288,142 +395,112 @@ static int disk_read (void)
        io_registry_entry_t     disk;
        io_registry_entry_t     disk_child;
        io_iterator_t           disk_list;
-       CFDictionaryRef         props_dict;
+       CFMutableDictionaryRef  props_dict, child_dict;
        CFDictionaryRef         stats_dict;
-       CFDictionaryRef         child_dict;
-       kern_return_t           status;
+       CFStringRef             tmp_cf_string_ref;
+       kern_return_t           status;
 
-       signed long long read_ops;
-       signed long long read_byt;
-       signed long long read_tme;
-       signed long long write_ops;
-       signed long long write_byt;
-       signed long long write_tme;
+       signed long long read_ops, read_byt, read_tme;
+       signed long long write_ops, write_byt, write_tme;
 
-       int  disk_major;
-       int  disk_minor;
-       char disk_name[64];
+       int  disk_major, disk_minor;
+       char disk_name[DATA_MAX_NAME_LEN];
+       char child_disk_name_bsd[DATA_MAX_NAME_LEN], props_disk_name_bsd[DATA_MAX_NAME_LEN];
 
        /* Get the list of all disk objects. */
-       if (IOServiceGetMatchingServices (io_master_port,
-                               IOServiceMatching (kIOBlockStorageDriverClass),
-                               &disk_list) != kIOReturnSuccess)
-       {
+       if (IOServiceGetMatchingServices (io_master_port, IOServiceMatching (kIOBlockStorageDriverClass), &disk_list) != kIOReturnSuccess) {
                ERROR ("disk plugin: IOServiceGetMatchingServices failed.");
                return (-1);
        }
 
-       while ((disk = IOIteratorNext (disk_list)) != 0)
-       {
+       while ((disk = IOIteratorNext (disk_list)) != 0) {
                props_dict = NULL;
                stats_dict = NULL;
                child_dict = NULL;
 
-               /* `disk_child' must be released */
-               if ((status = IORegistryEntryGetChildEntry (disk, kIOServicePlane, &disk_child))
-                               != kIOReturnSuccess)
-               {
-                       /* This fails for example for DVD/CD drives.. */
+               /* get child of disk entry and corresponding property dictionary */
+               if ((status = IORegistryEntryGetChildEntry (disk, kIOServicePlane, &disk_child)) != kIOReturnSuccess) {
+                       /* This fails for example for DVD/CD drives, which we want to ignore anyway */
                        DEBUG ("IORegistryEntryGetChildEntry (disk) failed: 0x%08x", status);
                        IOObjectRelease (disk);
                        continue;
                }
-
-               /* We create `props_dict' => we need to release it later */
-               if (IORegistryEntryCreateCFProperties (disk,
-                                       (CFMutableDictionaryRef *) &props_dict,
-                                       kCFAllocatorDefault,
-                                       kNilOptions)
-                               != kIOReturnSuccess)
-               {
-                       ERROR ("disk-plugin: IORegistryEntryCreateCFProperties failed.");
+               if (IORegistryEntryCreateCFProperties (disk_child, (CFMutableDictionaryRef *) &child_dict, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess || child_dict == NULL) {
+                       ERROR ("disk plugin: IORegistryEntryCreateCFProperties (disk_child) failed.");
                        IOObjectRelease (disk_child);
                        IOObjectRelease (disk);
                        continue;
                }
 
-               if (props_dict == NULL)
-               {
-                       DEBUG ("IORegistryEntryCreateCFProperties (disk) failed.");
-                       IOObjectRelease (disk_child);
-                       IOObjectRelease (disk);
-                       continue;
+               /* extract name and major/minor numbers */
+               memset (child_disk_name_bsd, 0, sizeof (child_disk_name_bsd));
+               tmp_cf_string_ref = (CFStringRef) CFDictionaryGetValue (child_dict, CFSTR(kIOBSDNameKey));
+               if (tmp_cf_string_ref) {
+                       assert (CFGetTypeID (tmp_cf_string_ref) == CFStringGetTypeID ());
+                       CFStringGetCString (tmp_cf_string_ref, child_disk_name_bsd, sizeof (child_disk_name_bsd), kCFStringEncodingUTF8);
                }
+               disk_major = (int) dict_get_value (child_dict, kIOBSDMajorKey);
+               disk_minor = (int) dict_get_value (child_dict, kIOBSDMinorKey);
+               DEBUG ("disk plugin: child_disk_name_bsd=\"%s\" major=%d minor=%d", child_disk_name_bsd, disk_major, disk_minor);
+               CFRelease (child_dict);
+               IOObjectRelease (disk_child);
 
-               stats_dict = (CFDictionaryRef) CFDictionaryGetValue (props_dict,
-                               CFSTR (kIOBlockStorageDriverStatisticsKey));
-
-               if (stats_dict == NULL)
-               {
-                       DEBUG ("CFDictionaryGetValue (%s) failed.",
-                                       kIOBlockStorageDriverStatisticsKey);
-                       CFRelease (props_dict);
-                       IOObjectRelease (disk_child);
+               /* get property dictionary of the disk entry itself */
+               if (IORegistryEntryCreateCFProperties (disk, (CFMutableDictionaryRef *) &props_dict, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess || props_dict == NULL) {
+                       ERROR ("disk-plugin: IORegistryEntryCreateCFProperties failed.");
                        IOObjectRelease (disk);
                        continue;
                }
 
-               if (IORegistryEntryCreateCFProperties (disk_child,
-                                       (CFMutableDictionaryRef *) &child_dict,
-                                       kCFAllocatorDefault,
-                                       kNilOptions)
-                               != kIOReturnSuccess)
-               {
-                       DEBUG ("IORegistryEntryCreateCFProperties (disk_child) failed.");
-                       IOObjectRelease (disk_child);
-                       CFRelease (props_dict);
-                       IOObjectRelease (disk);
-                       continue;
+               /* extract name and stats dictionary */
+               memset (props_disk_name_bsd, 0, sizeof (props_disk_name_bsd));
+               tmp_cf_string_ref = (CFStringRef) CFDictionaryGetValue (props_dict, CFSTR(kIOBSDNameKey));
+               if (tmp_cf_string_ref) {
+                       assert (CFGetTypeID (tmp_cf_string_ref) == CFStringGetTypeID ());
+                       CFStringGetCString (tmp_cf_string_ref, props_disk_name_bsd, sizeof (props_disk_name_bsd), kCFStringEncodingUTF8);
                }
-
-               /* kIOBSDNameKey */
-               disk_major = (int) dict_get_value (child_dict,
-                               kIOBSDMajorKey);
-               disk_minor = (int) dict_get_value (child_dict,
-                               kIOBSDMinorKey);
-               read_ops  = dict_get_value (stats_dict,
-                               kIOBlockStorageDriverStatisticsReadsKey);
-               read_byt  = dict_get_value (stats_dict,
-                               kIOBlockStorageDriverStatisticsBytesReadKey);
-               read_tme  = dict_get_value (stats_dict,
-                               kIOBlockStorageDriverStatisticsTotalReadTimeKey);
-               write_ops = dict_get_value (stats_dict,
-                               kIOBlockStorageDriverStatisticsWritesKey);
-               write_byt = dict_get_value (stats_dict,
-                               kIOBlockStorageDriverStatisticsBytesWrittenKey);
-               /* This property describes the number of nanoseconds spent
-                * performing writes since the block storage driver was
-                * instantiated. It is one of the statistic entries listed
-                * under the top-level kIOBlockStorageDriverStatisticsKey
-                * property table. It has an OSNumber value. */
-               write_tme = dict_get_value (stats_dict,
-                               kIOBlockStorageDriverStatisticsTotalWriteTimeKey);
-
-               if (ssnprintf (disk_name, sizeof (disk_name),
-                               "%i-%i", disk_major, disk_minor) >= sizeof (disk_name))
-               {
-                       DEBUG ("snprintf (major, minor) failed.");
-                       CFRelease (child_dict);
-                       IOObjectRelease (disk_child);
+               stats_dict = (CFDictionaryRef) CFDictionaryGetValue (props_dict, CFSTR (kIOBlockStorageDriverStatisticsKey));
+               if (stats_dict == NULL) {
+                       ERROR ("disk plugin: CFDictionaryGetValue (%s) failed.", kIOBlockStorageDriverStatisticsKey);
                        CFRelease (props_dict);
                        IOObjectRelease (disk);
                        continue;
                }
-               DEBUG ("disk_name = %s", disk_name);
+               DEBUG ("disk plugin: props_disk_name_bsd=\"%s\"", props_disk_name_bsd);
+
+               /* choose name */
+               if (use_bsd_name) {
+                       if (child_disk_name_bsd[0] != 0)
+                               sstrncpy (disk_name, child_disk_name_bsd, sizeof (disk_name));
+                       else if (props_disk_name_bsd[0] != 0)
+                               sstrncpy (disk_name, props_disk_name_bsd, sizeof (disk_name));
+                       else {
+                               ERROR ("disk plugin: can't find bsd disk name.");
+                               ssnprintf (disk_name, sizeof (disk_name), "%i-%i", disk_major, disk_minor);
+                       }
+               }
+               else
+                       ssnprintf (disk_name, sizeof (disk_name), "%i-%i", disk_major, disk_minor);
+
+               /* extract the stats */
+               read_ops  = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsReadsKey);
+               read_byt  = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsBytesReadKey);
+               read_tme  = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsTotalReadTimeKey);
+               write_ops = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsWritesKey);
+               write_byt = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsBytesWrittenKey);
+               write_tme = dict_get_value (stats_dict, kIOBlockStorageDriverStatisticsTotalWriteTimeKey);
+               CFRelease (props_dict);
+               IOObjectRelease (disk);
 
+               /* and submit */
+               DEBUG ("disk plugin: disk_name = \"%s\"", disk_name);
                if ((read_byt != -1LL) || (write_byt != -1LL))
                        disk_submit (disk_name, "disk_octets", read_byt, write_byt);
                if ((read_ops != -1LL) || (write_ops != -1LL))
                        disk_submit (disk_name, "disk_ops", read_ops, write_ops);
                if ((read_tme != -1LL) || (write_tme != -1LL))
-                       disk_submit (disk_name, "disk_time",
-                                       read_tme / 1000,
-                                       write_tme / 1000);
+                       disk_submit (disk_name, "disk_time", read_tme / 1000, write_tme / 1000);
 
-               CFRelease (child_dict);
-               IOObjectRelease (disk_child);
-               CFRelease (props_dict);
-               IOObjectRelease (disk);
        }
        IOObjectRelease (disk_list);
 /* #endif HAVE_IOKIT_IOKITLIB_H */
@@ -438,15 +515,18 @@ static int disk_read (void)
 
        int minor = 0;
 
-       counter_t read_sectors  = 0;
-       counter_t write_sectors = 0;
-
-       counter_t read_ops      = 0;
-       counter_t read_merged   = 0;
-       counter_t read_time     = 0;
-       counter_t write_ops     = 0;
-       counter_t write_merged  = 0;
-       counter_t write_time    = 0;
+       derive_t read_sectors  = 0;
+       derive_t write_sectors = 0;
+
+       derive_t read_ops      = 0;
+       derive_t read_merged   = 0;
+       derive_t read_time     = 0;
+       derive_t write_ops     = 0;
+       derive_t write_merged  = 0;
+       derive_t write_time    = 0;
+       gauge_t in_progress    = NAN;
+       derive_t io_time       = 0;
+       derive_t weighted_time = 0;
        int is_disk = 0;
 
        diskstats_t *ds, *pre_ds;
@@ -464,9 +544,15 @@ static int disk_read (void)
                fieldshift = 1;
        }
 
+#if HAVE_LIBUDEV
+       handle_udev = udev_new();
+#endif
+
        while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
                char *disk_name;
+               char *output_name;
+               char *alt_name;
 
                numfields = strsplit (buffer, fields, 32);
 
@@ -522,6 +608,11 @@ static int disk_read (void)
                                read_time    = atoll (fields[6 + fieldshift]);
                                write_merged = atoll (fields[8 + fieldshift]);
                                write_time   = atoll (fields[10+ fieldshift]);
+
+                               in_progress = atof (fields[11 + fieldshift]);
+
+                               io_time       = atof (fields[12 + fieldshift]);
+                               weighted_time = atof (fields[13 + fieldshift]);
                        }
                }
                else
@@ -531,8 +622,8 @@ static int disk_read (void)
                }
 
                {
-                       counter_t diff_read_sectors;
-                       counter_t diff_write_sectors;
+                       derive_t diff_read_sectors;
+                       derive_t diff_write_sectors;
 
                /* If the counter wraps around, it's only 32 bits.. */
                        if (read_sectors < ds->read_sectors)
@@ -555,18 +646,18 @@ static int disk_read (void)
                /* Calculate the average time an io-op needs to complete */
                if (is_disk)
                {
-                       counter_t diff_read_ops;
-                       counter_t diff_write_ops;
-                       counter_t diff_read_time;
-                       counter_t diff_write_time;
+                       derive_t diff_read_ops;
+                       derive_t diff_write_ops;
+                       derive_t diff_read_time;
+                       derive_t diff_write_time;
 
                        if (read_ops < ds->read_ops)
                                diff_read_ops = 1 + read_ops
                                        + (UINT_MAX - ds->read_ops);
                        else
                                diff_read_ops = read_ops - ds->read_ops;
-                       DEBUG ("disk plugin: disk_name = %s; read_ops = %llu; "
-                                       "ds->read_ops = %llu; diff_read_ops = %llu;",
+                       DEBUG ("disk plugin: disk_name = %s; read_ops = %"PRIi64"; "
+                                       "ds->read_ops = %"PRIi64"; diff_read_ops = %"PRIi64";",
                                        disk_name,
                                        read_ops, ds->read_ops, diff_read_ops);
 
@@ -618,25 +709,45 @@ static int disk_read (void)
                        continue;
                }
 
+               output_name = disk_name;
+
+#if HAVE_LIBUDEV
+               alt_name = disk_udev_attr_name (handle_udev, disk_name,
+                               conf_udev_name_attr);
+#else
+               alt_name = NULL;
+#endif
+               if (alt_name != NULL)
+                       output_name = alt_name;
+
                if ((ds->read_bytes != 0) || (ds->write_bytes != 0))
-                       disk_submit (disk_name, "disk_octets",
+                       disk_submit (output_name, "disk_octets",
                                        ds->read_bytes, ds->write_bytes);
 
                if ((ds->read_ops != 0) || (ds->write_ops != 0))
-                       disk_submit (disk_name, "disk_ops",
+                       disk_submit (output_name, "disk_ops",
                                        read_ops, write_ops);
 
                if ((ds->avg_read_time != 0) || (ds->avg_write_time != 0))
-                       disk_submit (disk_name, "disk_time",
+                       disk_submit (output_name, "disk_time",
                                        ds->avg_read_time, ds->avg_write_time);
 
                if (is_disk)
                {
-                       disk_submit (disk_name, "disk_merged",
+                       disk_submit (output_name, "disk_merged",
                                        read_merged, write_merged);
+                       submit_in_progress (output_name, in_progress);
+                       submit_io_time (output_name, io_time, weighted_time);
                } /* if (is_disk) */
+
+               /* release udev-based alternate name, if allocated */
+               free(alt_name);
        } /* while (fgets (buffer, sizeof (buffer), fh) != NULL) */
 
+#if HAVE_LIBUDEV
+       udev_unref(handle_udev);
+#endif
+
        fclose (fh);
 /* #endif defined(KERNEL_LINUX) */
 
@@ -691,7 +802,12 @@ static int disk_read (void)
 
 #elif defined(HAVE_LIBSTATGRAB)
        sg_disk_io_stats *ds;
-       int disks, counter;
+# if HAVE_LIBSTATGRAB_0_90
+       size_t disks;
+# else
+       int disks;
+#endif
+       int counter;
        char name[DATA_MAX_NAME_LEN];
        
        if ((ds = sg_get_disk_io_stats(&disks)) == NULL)
@@ -706,12 +822,12 @@ static int disk_read (void)
 /* #endif defined(HAVE_LIBSTATGRAB) */
 
 #elif defined(HAVE_PERFSTAT)
-       counter_t read_sectors;
-       counter_t write_sectors;
-       counter_t read_time;
-       counter_t write_time;
-       counter_t read_ops;
-       counter_t write_ops;
+       derive_t read_sectors;
+       derive_t write_sectors;
+       derive_t read_time;
+       derive_t write_time;
+       derive_t read_ops;
+       derive_t write_ops;
        perfstat_id_t firstpath;
        int rnumdisk;
        int i;