+ /* We only get the charged capacity as a percentage from
+ * IOPowerSources. IOKit, on the other hand, only reports the full
+ * capacity. We use the two to calculate the current charged capacity. */
+ gauge_t charge_rel = NAN; /* Current charge in percent */
+ gauge_t capacity_charged = NAN; /* Charged capacity */
+ gauge_t capacity_full = NAN; /* Total capacity */
+ gauge_t capacity_design = NAN; /* Full design capacity */
+
+#if HAVE_IOKIT_PS_IOPOWERSOURCES_H
+ get_via_io_power_sources (&charge_rel, ¤t, &voltage);
+#endif
+#if HAVE_IOKIT_IOKITLIB_H
+ get_via_generic_iokit (&capacity_full, &capacity_design, ¤t, &voltage);
+#endif
+
+ capacity_charged = charge_rel * capacity_full / 100.0;
+ submit_capacity ("0", capacity_charged, capacity_full, capacity_design);
+
+ if (!isnan (current))
+ battery_submit ("0", "current", current);
+ if (!isnan (voltage))
+ battery_submit ("0", "voltage", voltage);
+
+ return (0);
+} /* }}} int battery_read */
+/* #endif HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H */
+
+#elif KERNEL_LINUX
+/* Reads a file which contains only a number (and optionally a trailing
+ * newline) and parses that number. */
+static int sysfs_file_to_buffer(char const *dir, /* {{{ */
+ char const *power_supply,
+ char const *basename,
+ char *buffer, size_t buffer_size)
+{
+ int status;
+ FILE *fp;
+ char filename[PATH_MAX];
+
+ ssnprintf (filename, sizeof (filename), "%s/%s/%s",
+ dir, power_supply, basename);
+
+ /* No file isn't the end of the world -- not every system will be
+ * reporting the same set of statistics */
+ if (access (filename, R_OK) != 0)
+ return ENOENT;
+
+ fp = fopen (filename, "r");
+ if (fp == NULL)
+ {
+ status = errno;
+ if (status != ENOENT)
+ {
+ char errbuf[1024];
+ WARNING ("battery plugin: fopen (%s) failed: %s", filename,
+ sstrerror (status, errbuf, sizeof (errbuf)));
+ }
+ return status;
+ }
+
+ if (fgets (buffer, buffer_size, fp) == NULL)
+ {
+ status = errno;
+ if (status != ENODEV)
+ {
+ char errbuf[1024];
+ WARNING ("battery plugin: fgets (%s) failed: %s", filename,
+ sstrerror (status, errbuf, sizeof (errbuf)));
+ }
+ fclose (fp);
+ return status;
+ }
+
+ strstripnewline (buffer);
+
+ fclose (fp);
+ return 0;
+} /* }}} int sysfs_file_to_buffer */
+
+/* Reads a file which contains only a number (and optionally a trailing
+ * newline) and parses that number. */
+static int sysfs_file_to_gauge(char const *dir, /* {{{ */
+ char const *power_supply,
+ char const *basename, gauge_t *ret_value)
+{
+ int status;
+ char buffer[32] = "";
+
+ status = sysfs_file_to_buffer (dir, power_supply, basename, buffer, sizeof (buffer));
+ if (status != 0)
+ return (status);
+
+ return (strtogauge (buffer, ret_value));
+} /* }}} sysfs_file_to_gauge */
+
+static int read_sysfs_capacity (char const *dir, /* {{{ */
+ char const *power_supply,
+ char const *plugin_instance)
+{
+ gauge_t capacity_charged = NAN;
+ gauge_t capacity_full = NAN;
+ gauge_t capacity_design = NAN;
+ int status;
+
+ status = sysfs_file_to_gauge (dir, power_supply, "energy_now", &capacity_charged);
+ if (status != 0)
+ return (status);
+
+ status = sysfs_file_to_gauge (dir, power_supply, "energy_full", &capacity_full);
+ if (status != 0)
+ return (status);
+
+ status = sysfs_file_to_gauge (dir, power_supply, "energy_full_design", &capacity_design);
+ if (status != 0)
+ return (status);
+
+ submit_capacity (plugin_instance,
+ capacity_charged * SYSFS_FACTOR,
+ capacity_full * SYSFS_FACTOR,
+ capacity_design * SYSFS_FACTOR);
+ return (0);
+} /* }}} int read_sysfs_capacity */
+
+static int read_sysfs_callback (char const *dir, /* {{{ */
+ char const *power_supply,
+ void *user_data)
+{
+ int *battery_index = user_data;
+
+ char const *plugin_instance;
+ char buffer[32];
+ gauge_t v = NAN;
+ _Bool discharging = 0;
+ int status;
+
+ /* Ignore non-battery directories, such as AC power. */
+ status = sysfs_file_to_buffer (dir, power_supply, "type", buffer, sizeof (buffer));
+ if (status != 0)
+ return (0);
+ if (strcasecmp ("Battery", buffer) != 0)
+ return (0);
+
+ (void) sysfs_file_to_buffer (dir, power_supply, "status", buffer, sizeof (buffer));
+ if (strcasecmp ("Discharging", buffer) == 0)
+ discharging = 1;
+
+ /* FIXME: This is a dirty hack for backwards compatibility: The battery
+ * plugin, for a very long time, has had the plugin_instance
+ * hard-coded to "0". So, to keep backwards compatibility, we'll use
+ * "0" for the first battery we find and the power_supply name for all
+ * following. This should be reverted in a future major version. */
+ plugin_instance = (*battery_index == 0) ? "0" : power_supply;
+ (*battery_index)++;
+
+ read_sysfs_capacity (dir, power_supply, plugin_instance);
+
+ if (sysfs_file_to_gauge (dir, power_supply, "power_now", &v) == 0)
+ {
+ if (discharging)
+ v *= -1.0;
+ battery_submit (plugin_instance, "power", v * SYSFS_FACTOR);
+ }
+ if (sysfs_file_to_gauge (dir, power_supply, "current_now", &v) == 0)
+ {
+ if (discharging)
+ v *= -1.0;
+ battery_submit (plugin_instance, "current", v * SYSFS_FACTOR);
+ }
+
+ if (sysfs_file_to_gauge (dir, power_supply, "voltage_now", &v) == 0)
+ battery_submit (plugin_instance, "voltage", v * SYSFS_FACTOR);
+
+ return (0);
+} /* }}} int read_sysfs_callback */
+
+static int read_sysfs (void) /* {{{ */
+{
+ int status;
+ int battery_counter = 0;
+
+ if (access (SYSFS_PATH, R_OK) != 0)
+ return (ENOENT);