**/
#include "collectd.h"
+
#include "common.h"
#include "plugin.h"
#include "utils_ignorelist.h"
#include <atasmart.h>
#include <libudev.h>
-static const char *config_keys[] =
-{
- "Disk",
- "IgnoreSelected"
-};
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#endif
+
+static const char *config_keys[] = {"Disk", "IgnoreSelected", "IgnoreSleepMode",
+ "UseSerial"};
-static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
static ignorelist_t *ignorelist = NULL;
+static int ignore_sleep_mode = 0;
+static int use_serial = 0;
-static int smart_config (const char *key, const char *value)
-{
+static int smart_config(const char *key, const char *value) {
if (ignorelist == NULL)
- ignorelist = ignorelist_create (/* invert = */ 1);
+ ignorelist = ignorelist_create(/* invert = */ 1);
if (ignorelist == NULL)
return (1);
- if (strcasecmp ("Disk", key) == 0)
- {
- ignorelist_add (ignorelist, value);
- }
- else if (strcasecmp ("IgnoreSelected", key) == 0)
- {
+ if (strcasecmp("Disk", key) == 0) {
+ ignorelist_add(ignorelist, value);
+ } else if (strcasecmp("IgnoreSelected", key) == 0) {
int invert = 1;
- if (IS_TRUE (value))
+ if (IS_TRUE(value))
invert = 0;
- ignorelist_set_invert (ignorelist, invert);
- }
- else
- {
+ ignorelist_set_invert(ignorelist, invert);
+ } else if (strcasecmp("IgnoreSleepMode", key) == 0) {
+ if (IS_TRUE(value))
+ ignore_sleep_mode = 1;
+ } else if (strcasecmp("UseSerial", key) == 0) {
+ if (IS_TRUE(value))
+ use_serial = 1;
+ } else {
return (-1);
}
return (0);
} /* int smart_config */
-static void smart_submit (const char *dev, const char *type,
- const char *type_inst, double value)
-{
- value_t values[1];
- value_list_t vl = VALUE_LIST_INIT;
-
- values[0].gauge = value;
+static void smart_submit(const char *dev, const char *type,
+ const char *type_inst, double value) {
+ value_list_t vl = VALUE_LIST_INIT;
- vl.values = values;
- vl.values_len = 1;
- sstrncpy (vl.host, hostname_g, sizeof (vl.host));
- sstrncpy (vl.plugin, "smart", sizeof (vl.plugin));
- sstrncpy (vl.plugin_instance, dev, sizeof (vl.plugin_instance));
- sstrncpy (vl.type, type, sizeof (vl.type));
- sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
+ vl.values = &(value_t){.gauge = value};
+ vl.values_len = 1;
+ sstrncpy(vl.plugin, "smart", sizeof(vl.plugin));
+ sstrncpy(vl.plugin_instance, dev, sizeof(vl.plugin_instance));
+ sstrncpy(vl.type, type, sizeof(vl.type));
+ sstrncpy(vl.type_instance, type_inst, sizeof(vl.type_instance));
- plugin_dispatch_values (&vl);
+ plugin_dispatch_values(&vl);
}
-static void smart_handle_disk_attribute(SkDisk *d, const SkSmartAttributeParsedData *a,
- void* userdata)
-{
- const char *dev = userdata;
- value_t values[4];
- value_list_t vl = VALUE_LIST_INIT;
+static void handle_attribute(SkDisk *d, const SkSmartAttributeParsedData *a,
+ void *userdata) {
+ char const *name = userdata;
+
+ if (!a->current_value_valid || !a->worst_value_valid)
+ return;
- if (!a->current_value_valid || !a->worst_value_valid) return;
- values[0].gauge = a->current_value;
- values[1].gauge = a->worst_value;
- values[2].gauge = a->threshold_valid?a->threshold:0;
- values[3].gauge = a->pretty_value;
+ value_list_t vl = VALUE_LIST_INIT;
+ value_t values[] = {
+ {.gauge = a->current_value},
+ {.gauge = a->worst_value},
+ {.gauge = a->threshold_valid ? a->threshold : 0},
+ {.gauge = a->pretty_value},
+ };
vl.values = values;
- vl.values_len = 4;
- sstrncpy (vl.host, hostname_g, sizeof (vl.host));
- sstrncpy (vl.plugin, "smart", sizeof (vl.plugin));
- sstrncpy (vl.plugin_instance, dev, sizeof (vl.plugin_instance));
- sstrncpy (vl.type, "smart_attribute", sizeof (vl.type));
- sstrncpy (vl.type_instance, a->name, sizeof (vl.type_instance));
-
- plugin_dispatch_values (&vl);
-
- if (a->threshold_valid && a->current_value <= a->threshold)
- {
- notification_t notif = { NOTIF_WARNING,
- cdtime (),
- "",
- "",
- "smart", "",
- "smart_attribute",
- "",
- NULL };
- sstrncpy (notif.host, hostname_g, sizeof (notif.host));
- sstrncpy (notif.plugin_instance, dev, sizeof (notif.plugin_instance));
- sstrncpy (notif.type_instance, a->name, sizeof (notif.type_instance));
- ssnprintf (notif.message, sizeof (notif.message),
- "attribute %s is below allowed threshold (%d < %d)",
- a->name, a->current_value, a->threshold);
- plugin_dispatch_notification (¬if);
+ vl.values_len = STATIC_ARRAY_SIZE(values);
+ sstrncpy(vl.plugin, "smart", sizeof(vl.plugin));
+ sstrncpy(vl.plugin_instance, name, sizeof(vl.plugin_instance));
+ sstrncpy(vl.type, "smart_attribute", sizeof(vl.type));
+ sstrncpy(vl.type_instance, a->name, sizeof(vl.type_instance));
+
+ plugin_dispatch_values(&vl);
+
+ if (a->threshold_valid && a->current_value <= a->threshold) {
+ notification_t notif = {NOTIF_WARNING, cdtime(), "", "", "smart", "",
+ "smart_attribute", "", NULL};
+ sstrncpy(notif.host, hostname_g, sizeof(notif.host));
+ sstrncpy(notif.plugin_instance, name, sizeof(notif.plugin_instance));
+ sstrncpy(notif.type_instance, a->name, sizeof(notif.type_instance));
+ ssnprintf(notif.message, sizeof(notif.message),
+ "attribute %s is below allowed threshold (%d < %d)", a->name,
+ a->current_value, a->threshold);
+ plugin_dispatch_notification(¬if);
}
}
-static void smart_handle_disk (const char *dev)
-{
- SkDisk *d = NULL;
- SkBool awake = FALSE;
+static void smart_read_disk(SkDisk *d, char const *name) {
SkBool available = FALSE;
- const char *shortname;
- const SkSmartParsedData *spd;
- uint64_t poweron, powercycles, badsectors, temperature;
-
- shortname = strrchr(dev, '/');
- if (!shortname) return;
- shortname++;
- if (ignorelist_match (ignorelist, shortname) != 0) {
- DEBUG ("smart plugin: ignoring %s.", dev);
+ if (sk_disk_identify_is_available(d, &available) < 0 || !available) {
+ DEBUG("smart plugin: disk %s cannot be identified.", name);
return;
}
-
- DEBUG ("smart plugin: checking SMART status of %s.",
- dev);
-
- if (sk_disk_open (dev, &d) < 0)
- {
- ERROR ("smart plugin: unable to open %s.", dev);
+ if (sk_disk_smart_is_available(d, &available) < 0 || !available) {
+ DEBUG("smart plugin: disk %s has no SMART support.", name);
return;
}
- if (sk_disk_identify_is_available (d, &available) < 0 || !available)
- {
- DEBUG ("smart plugin: disk %s cannot be identified.", dev);
- goto end;
+ if (!ignore_sleep_mode) {
+ SkBool awake = FALSE;
+ if (sk_disk_check_sleep_mode(d, &awake) < 0 || !awake) {
+ DEBUG("smart plugin: disk %s is sleeping.", name);
+ return;
+ }
}
- if (sk_disk_smart_is_available (d, &available) < 0 || !available)
- {
- DEBUG ("smart plugin: disk %s has no SMART support.", dev);
- goto end;
- }
- if (sk_disk_check_sleep_mode (d, &awake) < 0 || !awake)
- {
- DEBUG ("smart plugin: disk %s is sleeping.", dev);
- goto end;
- }
- if (sk_disk_smart_read_data (d) < 0)
- {
- ERROR ("smart plugin: unable to get SMART data for disk %s.", dev);
- goto end;
+ if (sk_disk_smart_read_data(d) < 0) {
+ ERROR("smart plugin: unable to get SMART data for disk %s.", name);
+ return;
}
- if (sk_disk_smart_parse (d, &spd) < 0)
- {
- ERROR ("smart plugin: unable to parse SMART data for disk %s.", dev);
- goto end;
+
+ if (sk_disk_smart_parse(d, &(SkSmartParsedData const *){NULL}) < 0) {
+ ERROR("smart plugin: unable to parse SMART data for disk %s.", name);
+ return;
}
/* Get some specific values */
- if (sk_disk_smart_get_power_on (d, &poweron) < 0)
- {
- WARNING ("smart plugin: unable to get milliseconds since power on for %s.",
- dev);
- }
+ uint64_t value;
+ if (sk_disk_smart_get_power_on(d, &value) >= 0)
+ smart_submit(name, "smart_poweron", "", ((gauge_t)value) / 1000.);
else
- smart_submit (shortname, "smart_poweron", "", poweron / 1000.);
+ DEBUG("smart plugin: unable to get milliseconds since power on for %s.",
+ name);
- if (sk_disk_smart_get_power_cycle (d, &powercycles) < 0)
- {
- WARNING ("smart plugin: unable to get number of power cycles for %s.",
- dev);
- }
+ if (sk_disk_smart_get_power_cycle(d, &value) >= 0)
+ smart_submit(name, "smart_powercycles", "", (gauge_t)value);
else
- smart_submit (shortname, "smart_powercycles", "", powercycles);
+ DEBUG("smart plugin: unable to get number of power cycles for %s.", name);
- if (sk_disk_smart_get_bad (d, &badsectors) < 0)
- {
- WARNING ("smart plugin: unable to get number of bad sectors for %s.",
- dev);
- }
+ if (sk_disk_smart_get_bad(d, &value) >= 0)
+ smart_submit(name, "smart_badsectors", "", (gauge_t)value);
else
- smart_submit (shortname, "smart_badsectors", "", badsectors);
+ DEBUG("smart plugin: unable to get number of bad sectors for %s.", name);
- if (sk_disk_smart_get_temperature (d, &temperature) < 0)
- {
- WARNING ("smart plugin: unable to get temperature for %s.",
- dev);
- }
+ if (sk_disk_smart_get_temperature(d, &value) >= 0)
+ smart_submit(name, "smart_temperature", "",
+ ((gauge_t)value) / 1000. - 273.15);
else
- smart_submit (shortname, "smart_temperature", "", temperature / 1000. - 273.15);
+ DEBUG("smart plugin: unable to get temperature for %s.", name);
/* Grab all attributes */
- if (sk_disk_smart_parse_attributes(d, smart_handle_disk_attribute,
- (char *)shortname) < 0)
- {
- ERROR ("smart plugin: unable to handle SMART attributes for %s.",
- dev);
+ if (sk_disk_smart_parse_attributes(d, handle_attribute, (void *)name) < 0) {
+ ERROR("smart plugin: unable to handle SMART attributes for %s.", name);
}
+}
-end:
+static void smart_handle_disk(const char *dev, const char *serial) {
+ SkDisk *d = NULL;
+ const char *name;
+
+ if (use_serial && serial) {
+ name = serial;
+ } else {
+ name = strrchr(dev, '/');
+ if (!name)
+ return;
+ name++;
+ }
+ if (ignorelist_match(ignorelist, name) != 0) {
+ DEBUG("smart plugin: ignoring %s.", dev);
+ return;
+ }
+
+ DEBUG("smart plugin: checking SMART status of %s.", dev);
+ if (sk_disk_open(dev, &d) < 0) {
+ ERROR("smart plugin: unable to open %s.", dev);
+ return;
+ }
+
+ smart_read_disk(d, name);
sk_disk_free(d);
}
-static int smart_read (void)
-{
+static int smart_read(void) {
struct udev *handle_udev;
struct udev_enumerate *enumerate;
struct udev_list_entry *devices, *dev_list_entry;
/* Use udev to get a list of disks */
handle_udev = udev_new();
- if (!handle_udev)
- {
- ERROR ("smart plugin: unable to initialize udev.");
+ if (!handle_udev) {
+ ERROR("smart plugin: unable to initialize udev.");
return (-1);
}
- enumerate = udev_enumerate_new (handle_udev);
- udev_enumerate_add_match_subsystem (enumerate, "block");
- udev_enumerate_add_match_property (enumerate, "DEVTYPE", "disk");
- udev_enumerate_scan_devices (enumerate);
- devices = udev_enumerate_get_list_entry (enumerate);
- udev_list_entry_foreach (dev_list_entry, devices)
- {
- const char *path, *devpath;
- path = udev_list_entry_get_name (dev_list_entry);
- dev = udev_device_new_from_syspath (handle_udev, path);
- devpath = udev_device_get_devnode (dev);
+ enumerate = udev_enumerate_new(handle_udev);
+ udev_enumerate_add_match_subsystem(enumerate, "block");
+ udev_enumerate_add_match_property(enumerate, "DEVTYPE", "disk");
+ udev_enumerate_scan_devices(enumerate);
+ devices = udev_enumerate_get_list_entry(enumerate);
+ udev_list_entry_foreach(dev_list_entry, devices) {
+ const char *path, *devpath, *serial;
+ path = udev_list_entry_get_name(dev_list_entry);
+ dev = udev_device_new_from_syspath(handle_udev, path);
+ devpath = udev_device_get_devnode(dev);
+ serial = udev_device_get_property_value(dev, "ID_SERIAL");
/* Query status with libatasmart */
- smart_handle_disk (devpath);
- udev_device_unref (dev);
+ smart_handle_disk(devpath, serial);
+ udev_device_unref(dev);
}
- udev_enumerate_unref (enumerate);
- udev_unref (handle_udev);
+ udev_enumerate_unref(enumerate);
+ udev_unref(handle_udev);
return (0);
} /* int smart_read */
-void module_register (void)
-{
- plugin_register_config ("smart", smart_config,
- config_keys, config_keys_num);
- plugin_register_read ("smart", smart_read);
+static int smart_init(void) {
+#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_RAWIO)
+ if (check_capability(CAP_SYS_RAWIO) != 0) {
+ if (getuid() == 0)
+ WARNING("smart plugin: Running collectd as root, but the "
+ "CAP_SYS_RAWIO capability is missing. The plugin's read "
+ "function will probably fail. Is your init system dropping "
+ "capabilities?");
+ else
+ WARNING("smart plugin: collectd doesn't have the CAP_SYS_RAWIO "
+ "capability. If you don't want to run collectd as root, try "
+ "running \"setcap cap_sys_rawio=ep\" on the collectd binary.");
+ }
+#endif
+ return (0);
+} /* int smart_init */
+
+void module_register(void) {
+ plugin_register_config("smart", smart_config, config_keys, config_keys_num);
+ plugin_register_init("smart", smart_init);
+ plugin_register_read("smart", smart_read);
} /* void module_register */