battery plugin: Implement the ValuesPercentage option.
[collectd.git] / src / battery.c
1 /**
2  * collectd - src/battery.c
3  * Copyright (C) 2006-2014  Florian octo Forster
4  * Copyright (C) 2008       Michał Mirosław
5  * Copyright (C) 2014       Andy Parkins
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation; only version 2 of the License is applicable.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19  *
20  * Authors:
21  *   Florian octo Forster <octo at collectd.org>
22  *   Michał Mirosław <mirq-linux at rere.qmqm.pl>
23  *   Andy Parkins <andyp at fussylogic.co.uk>
24  **/
25
26 #include "collectd.h"
27 #include "common.h"
28 #include "plugin.h"
29
30 #include "utils_complain.h"
31
32 #if HAVE_MACH_MACH_TYPES_H
33 #  include <mach/mach_types.h>
34 #endif
35 #if HAVE_MACH_MACH_INIT_H
36 #  include <mach/mach_init.h>
37 #endif
38 #if HAVE_MACH_MACH_ERROR_H
39 #  include <mach/mach_error.h>
40 #endif
41 #if HAVE_COREFOUNDATION_COREFOUNDATION_H
42 #  include <CoreFoundation/CoreFoundation.h>
43 #endif
44 #if HAVE_IOKIT_IOKITLIB_H
45 #  include <IOKit/IOKitLib.h>
46 #endif
47 #if HAVE_IOKIT_IOTYPES_H
48 #  include <IOKit/IOTypes.h>
49 #endif
50 #if HAVE_IOKIT_PS_IOPOWERSOURCES_H
51 #  include <IOKit/ps/IOPowerSources.h>
52 #endif
53 #if HAVE_IOKIT_PS_IOPSKEYS_H
54 #  include <IOKit/ps/IOPSKeys.h>
55 #endif
56
57 #if !HAVE_IOKIT_IOKITLIB_H && !HAVE_IOKIT_PS_IOPOWERSOURCES_H && !KERNEL_LINUX
58 # error "No applicable input method."
59 #endif
60
61 #define INVALID_VALUE 47841.29
62
63 #if HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H
64         /* No global variables */
65 /* #endif HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H */
66
67 #elif KERNEL_LINUX
68 # define PROC_PMU_PATH_FORMAT "/proc/pmu/battery_%i"
69 # define PROC_ACPI_PATH "/proc/acpi/battery"
70 # define SYSFS_PATH "/sys/class/power_supply"
71 #endif /* KERNEL_LINUX */
72
73 static _Bool report_percent = 0;
74
75 static void battery_submit2 (char const *plugin_instance, /* {{{ */
76                 char const *type, char const *type_instance, gauge_t value)
77 {
78         value_t values[1];
79         value_list_t vl = VALUE_LIST_INIT;
80
81         values[0].gauge = value;
82
83         vl.values = values;
84         vl.values_len = 1;
85         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
86         sstrncpy (vl.plugin, "battery", sizeof (vl.plugin));
87         sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
88         sstrncpy (vl.type, type, sizeof (vl.type));
89         if (type_instance != NULL)
90                 sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
91
92         plugin_dispatch_values (&vl);
93 } /* }}} void battery_submit2 */
94
95 static void battery_submit (char const *plugin_instance, /* {{{ */
96                 char const *type, gauge_t value)
97 {
98         battery_submit2 (plugin_instance, type, NULL, value);
99 } /* }}} void battery_submit */
100
101 #if HAVE_IOKIT_PS_IOPOWERSOURCES_H || HAVE_IOKIT_IOKITLIB_H
102 static double dict_get_double (CFDictionaryRef dict, char *key_string) /* {{{ */
103 {
104         double      val_double;
105         long long   val_int;
106         CFNumberRef val_obj;
107         CFStringRef key_obj;
108
109         key_obj = CFStringCreateWithCString (kCFAllocatorDefault, key_string,
110                         kCFStringEncodingASCII);
111         if (key_obj == NULL)
112         {
113                 DEBUG ("CFStringCreateWithCString (%s) failed.\n", key_string);
114                 return (INVALID_VALUE);
115         }
116
117         if ((val_obj = CFDictionaryGetValue (dict, key_obj)) == NULL)
118         {
119                 DEBUG ("CFDictionaryGetValue (%s) failed.", key_string);
120                 CFRelease (key_obj);
121                 return (INVALID_VALUE);
122         }
123         CFRelease (key_obj);
124
125         if (CFGetTypeID (val_obj) == CFNumberGetTypeID ())
126         {
127                 if (CFNumberIsFloatType (val_obj))
128                 {
129                         CFNumberGetValue (val_obj,
130                                         kCFNumberDoubleType,
131                                         &val_double);
132                 }
133                 else
134                 {
135                         CFNumberGetValue (val_obj,
136                                         kCFNumberLongLongType,
137                                         &val_int);
138                         val_double = val_int;
139                 }
140         }
141         else
142         {
143                 DEBUG ("CFGetTypeID (val_obj) = %i", (int) CFGetTypeID (val_obj));
144                 return (INVALID_VALUE);
145         }
146
147         return (val_double);
148 } /* }}} double dict_get_double */
149
150 # if HAVE_IOKIT_PS_IOPOWERSOURCES_H
151 static void get_via_io_power_sources (double *ret_charge, /* {{{ */
152                 double *ret_current,
153                 double *ret_voltage)
154 {
155         CFTypeRef       ps_raw;
156         CFArrayRef      ps_array;
157         int             ps_array_len;
158         CFDictionaryRef ps_dict;
159         CFTypeRef       ps_obj;
160
161         double temp_double;
162         int i;
163
164         ps_raw       = IOPSCopyPowerSourcesInfo ();
165         ps_array     = IOPSCopyPowerSourcesList (ps_raw);
166         ps_array_len = CFArrayGetCount (ps_array);
167
168         DEBUG ("ps_array_len == %i", ps_array_len);
169
170         for (i = 0; i < ps_array_len; i++)
171         {
172                 ps_obj  = CFArrayGetValueAtIndex (ps_array, i);
173                 ps_dict = IOPSGetPowerSourceDescription (ps_raw, ps_obj);
174
175                 if (ps_dict == NULL)
176                 {
177                         DEBUG ("IOPSGetPowerSourceDescription failed.");
178                         continue;
179                 }
180
181                 if (CFGetTypeID (ps_dict) != CFDictionaryGetTypeID ())
182                 {
183                         DEBUG ("IOPSGetPowerSourceDescription did not return a CFDictionaryRef");
184                         continue;
185                 }
186
187                 /* FIXME: Check if this is really an internal battery */
188
189                 if (*ret_charge == INVALID_VALUE)
190                 {
191                         /* This is the charge in percent. */
192                         temp_double = dict_get_double (ps_dict,
193                                         kIOPSCurrentCapacityKey);
194                         if ((temp_double != INVALID_VALUE)
195                                         && (temp_double >= 0.0)
196                                         && (temp_double <= 100.0))
197                                 *ret_charge = temp_double;
198                 }
199
200                 if (*ret_current == INVALID_VALUE)
201                 {
202                         temp_double = dict_get_double (ps_dict,
203                                         kIOPSCurrentKey);
204                         if (temp_double != INVALID_VALUE)
205                                 *ret_current = temp_double / 1000.0;
206                 }
207
208                 if (*ret_voltage == INVALID_VALUE)
209                 {
210                         temp_double = dict_get_double (ps_dict,
211                                         kIOPSVoltageKey);
212                         if (temp_double != INVALID_VALUE)
213                                 *ret_voltage = temp_double / 1000.0;
214                 }
215         }
216
217         CFRelease(ps_array);
218         CFRelease(ps_raw);
219 } /* }}} void get_via_io_power_sources */
220 # endif /* HAVE_IOKIT_PS_IOPOWERSOURCES_H */
221
222 # if HAVE_IOKIT_IOKITLIB_H
223 static void get_via_generic_iokit (double *ret_charge, /* {{{ */
224                 double *ret_current,
225                 double *ret_voltage)
226 {
227         kern_return_t   status;
228         io_iterator_t   iterator;
229         io_object_t     io_obj;
230
231         CFDictionaryRef bat_root_dict;
232         CFArrayRef      bat_info_arry;
233         CFIndex         bat_info_arry_len;
234         CFIndex         bat_info_arry_pos;
235         CFDictionaryRef bat_info_dict;
236
237         double temp_double;
238
239         status = IOServiceGetMatchingServices (kIOMasterPortDefault,
240                         IOServiceNameMatching ("battery"),
241                         &iterator);
242         if (status != kIOReturnSuccess)
243         {
244                 DEBUG ("IOServiceGetMatchingServices failed.");
245                 return;
246         }
247
248         while ((io_obj = IOIteratorNext (iterator)))
249         {
250                 status = IORegistryEntryCreateCFProperties (io_obj,
251                                 (CFMutableDictionaryRef *) &bat_root_dict,
252                                 kCFAllocatorDefault,
253                                 kNilOptions);
254                 if (status != kIOReturnSuccess)
255                 {
256                         DEBUG ("IORegistryEntryCreateCFProperties failed.");
257                         continue;
258                 }
259
260                 bat_info_arry = (CFArrayRef) CFDictionaryGetValue (bat_root_dict,
261                                 CFSTR ("IOBatteryInfo"));
262                 if (bat_info_arry == NULL)
263                 {
264                         CFRelease (bat_root_dict);
265                         continue;
266                 }
267                 bat_info_arry_len = CFArrayGetCount (bat_info_arry);
268
269                 for (bat_info_arry_pos = 0;
270                                 bat_info_arry_pos < bat_info_arry_len;
271                                 bat_info_arry_pos++)
272                 {
273                         bat_info_dict = (CFDictionaryRef) CFArrayGetValueAtIndex (bat_info_arry, bat_info_arry_pos);
274
275                         /* Design capacity available in "AbsoluteMaxCapacity" */
276                         if (*ret_charge == INVALID_VALUE)
277                         {
278                                 temp_double = dict_get_double (bat_info_dict,
279                                                 "Capacity");
280                                 if (temp_double != INVALID_VALUE)
281                                         *ret_charge = temp_double / 1000.0;
282                         }
283
284                         if (*ret_current == INVALID_VALUE)
285                         {
286                                 temp_double = dict_get_double (bat_info_dict,
287                                                 "Current");
288                                 if (temp_double != INVALID_VALUE)
289                                         *ret_current = temp_double / 1000.0;
290                         }
291
292                         if (*ret_voltage == INVALID_VALUE)
293                         {
294                                 temp_double = dict_get_double (bat_info_dict,
295                                                 "Voltage");
296                                 if (temp_double != INVALID_VALUE)
297                                         *ret_voltage = temp_double / 1000.0;
298                         }
299                 }
300                 
301                 CFRelease (bat_root_dict);
302         }
303
304         IOObjectRelease (iterator);
305 } /* }}} void get_via_generic_iokit */
306 # endif /* HAVE_IOKIT_IOKITLIB_H */
307
308 static int battery_read (void) /* {{{ */
309 {
310         double current = INVALID_VALUE; /* Current in A */
311         double voltage = INVALID_VALUE; /* Voltage in V */
312
313         /* We only get the charged capacity as a percentage from
314          * IOPowerSources. IOKit, on the other hand, only reports the full
315          * capacity. We use the two to calculate the current charged capacity. */
316         double charge_rel = INVALID_VALUE; /* Current charge in percent */
317         double charge_abs = INVALID_VALUE; /* Total capacity */
318
319 #if HAVE_IOKIT_PS_IOPOWERSOURCES_H
320         get_via_io_power_sources (&charge_rel, &current, &voltage);
321 #endif
322 #if HAVE_IOKIT_IOKITLIB_H
323         get_via_generic_iokit (&charge_abs, &current, &voltage);
324 #endif
325
326         if (charge_rel != INVALID_VALUE)
327         {
328                 if (report_percent)
329                         battery_submit2 ("0", "percent", "charged", charge_rel);
330                 else if (charge_abs != INVALID_VALUE)
331                         battery_submit ("0", "charge", charge_abs * charge_rel / 100.0);
332         }
333         if (current != INVALID_VALUE)
334                 battery_submit ("0", "current", current);
335         if (voltage != INVALID_VALUE)
336                 battery_submit ("0", "voltage", voltage);
337 } /* }}} int battery_read */
338 /* #endif HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H */
339
340 #elif KERNEL_LINUX
341 /* Reads a file which contains only a number (and optionally a trailing
342  * newline) and parses that number. */
343 static int sysfs_file_to_buffer(char const *dir, /* {{{ */
344                 char const *power_supply,
345                 char const *basename,
346                 char *buffer, size_t buffer_size)
347 {
348         int status;
349         FILE *fp;
350         char filename[PATH_MAX];
351
352         ssnprintf (filename, sizeof (filename), "%s/%s/%s",
353                         dir, power_supply, basename);
354
355         /* No file isn't the end of the world -- not every system will be
356          * reporting the same set of statistics */
357         if (access (filename, R_OK) != 0)
358                 return ENOENT;
359
360         fp = fopen (filename, "r");
361         if (fp == NULL)
362         {
363                 status = errno;
364                 if (status != ENOENT)
365                 {
366                         char errbuf[1024];
367                         WARNING ("battery plugin: fopen (%s) failed: %s", filename,
368                                         sstrerror (status, errbuf, sizeof (errbuf)));
369                 }
370                 return status;
371         }
372
373         if (fgets (buffer, buffer_size, fp) == NULL)
374         {
375                 char errbuf[1024];
376                 status = errno;
377                 WARNING ("battery plugin: fgets failed: %s",
378                                 sstrerror (status, errbuf, sizeof (errbuf)));
379                 fclose (fp);
380                 return status;
381         }
382
383         strstripnewline (buffer);
384
385         fclose (fp);
386         return 0;
387 } /* }}} int sysfs_file_to_buffer */
388
389 /* Reads a file which contains only a number (and optionally a trailing
390  * newline) and parses that number. */
391 static int sysfs_file_to_gauge(char const *dir, /* {{{ */
392                 char const *power_supply,
393                 char const *basename, gauge_t *ret_value)
394 {
395         int status;
396         char buffer[32] = "";
397
398         status = sysfs_file_to_buffer (dir, power_supply, basename, buffer, sizeof (buffer));
399         if (status != 0)
400                 return (status);
401
402         return (strtogauge (buffer, ret_value));
403 } /* }}} sysfs_file_to_gauge */
404
405 static int read_sysfs_callback (char const *dir, /* {{{ */
406                 char const *power_supply,
407                 void *user_data)
408 {
409         int *battery_index = user_data;
410
411         char const *plugin_instance;
412         char buffer[32];
413         gauge_t v = NAN;
414         _Bool discharging = 0;
415         int status;
416
417         /* Ignore non-battery directories, such as AC power. */
418         status = sysfs_file_to_buffer (dir, power_supply, "type", buffer, sizeof (buffer));
419         if (status != 0)
420                 return (0);
421         if (strcasecmp ("Battery", buffer) != 0)
422                 return (0);
423
424         (void) sysfs_file_to_buffer (dir, power_supply, "status", buffer, sizeof (buffer));
425         if (strcasecmp ("Discharging", buffer) == 0)
426                 discharging = 1;
427
428         /* FIXME: This is a dirty hack for backwards compatibility: The battery
429          * plugin, for a very long time, has had the plugin_instance
430          * hard-coded to "0". So, to keep backwards compatibility, we'll use
431          * "0" for the first battery we find and the power_supply name for all
432          * following. This should be reverted in a future major version. */
433         plugin_instance = (*battery_index == 0) ? "0" : power_supply;
434         (*battery_index)++;
435
436         if (report_percent)
437         {
438                 gauge_t now = NAN;
439                 gauge_t max = NAN;
440
441                 if ((sysfs_file_to_gauge (dir, power_supply, "energy_now", &now) == 0)
442                                 && (sysfs_file_to_gauge (dir, power_supply, "energy_full", &max) == 0))
443                 {
444                         v = 100.0 * now / max;
445                         battery_submit2 (plugin_instance, "percent", "charged", v);
446                 }
447         }
448         else if (sysfs_file_to_gauge (dir, power_supply, "energy_now", &v) == 0)
449                 battery_submit (plugin_instance, "charge", v / 1000000.0);
450
451         if (sysfs_file_to_gauge (dir, power_supply, "power_now", &v) == 0)
452         {
453                 if (discharging)
454                         v *= -1.0;
455                 battery_submit (plugin_instance, "power", v / 1000000.0);
456         }
457
458         if (sysfs_file_to_gauge (dir, power_supply, "voltage_now", &v) == 0)
459                 battery_submit (plugin_instance, "voltage", v / 1000000.0);
460 #if 0
461         if (sysfs_file_to_gauge (dir, power_supply, "energy_full_design", &v) == 0)
462                 battery_submit (plugin_instance, "charge", v / 1000000.0);
463         if (sysfs_file_to_gauge (dir, power_supply, "energy_full", &v) == 0)
464                 battery_submit (plugin_instance, "charge", v / 1000000.0);
465         if (sysfs_file_to_gauge (dir, power_supply, "voltage_min_design", &v) == 0)
466                 battery_submit (plugin_instance, "voltage", v / 1000000.0);
467 #endif
468
469         return (0);
470 } /* }}} int read_sysfs_callback */
471
472 static int read_sysfs (void) /* {{{ */
473 {
474         int status;
475         int battery_counter = 0;
476
477         if (access (SYSFS_PATH, R_OK) != 0)
478                 return (ENOENT);
479
480         status = walk_directory (SYSFS_PATH, read_sysfs_callback,
481                         /* user_data = */ &battery_counter,
482                         /* include hidden */ 0);
483         return (status);
484 } /* }}} int read_sysfs */
485
486 static int read_acpi_full_capacity (char const *dir, /* {{{ */
487                 char const *power_supply,
488                 gauge_t *ret_capacity)
489 {
490         char filename[PATH_MAX];
491         char buffer[1024];
492
493         FILE *fh;
494
495         ssnprintf (filename, sizeof (filename), "%s/%s/info", dir, power_supply);
496         fh = fopen (filename, "r");
497         if ((fh = fopen (filename, "r")) == NULL)
498                 return (errno);
499
500         /* last full capacity:      40090 mWh */
501         while (fgets (buffer, sizeof (buffer), fh) != NULL)
502         {
503                 char *fields[8];
504                 int numfields;
505                 int status;
506
507                 if (strncmp ("last full capacity:", buffer, strlen ("last full capacity:")) != 0)
508                         continue;
509
510                 numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
511                 if (numfields < 5)
512                         continue;
513
514                 status = strtogauge (fields[3], ret_capacity);
515                 fclose (fh);
516                 return (status);
517         }
518
519         fclose (fh);
520         return (ENOENT);
521 } /* }}} int read_acpi_full_capacity */
522
523 static int read_acpi_callback (char const *dir, /* {{{ */
524                 char const *power_supply,
525                 void *user_data)
526 {
527         int *battery_index = user_data;
528
529         gauge_t power = NAN;
530         gauge_t voltage = NAN;
531         gauge_t charge  = NAN;
532         _Bool charging = 0;
533         _Bool is_current = 0;
534
535         char const *plugin_instance;
536         char filename[PATH_MAX];
537         char buffer[1024];
538
539         FILE *fh;
540
541         ssnprintf (filename, sizeof (filename), "%s/%s/state", dir, power_supply);
542         fh = fopen (filename, "r");
543         if ((fh = fopen (filename, "r")) == NULL)
544         {
545                 if ((errno == EAGAIN) || (errno == EINTR) || (errno == ENOENT))
546                         return (0);
547                 else
548                         return (errno);
549         }
550
551         /*
552          * [11:00] <@tokkee> $ cat /proc/acpi/battery/BAT1/state
553          * [11:00] <@tokkee> present:                 yes
554          * [11:00] <@tokkee> capacity state:          ok
555          * [11:00] <@tokkee> charging state:          charging
556          * [11:00] <@tokkee> present rate:            1724 mA
557          * [11:00] <@tokkee> remaining capacity:      4136 mAh
558          * [11:00] <@tokkee> present voltage:         12428 mV
559          */
560         while (fgets (buffer, sizeof (buffer), fh) != NULL)
561         {
562                 char *fields[8];
563                 int numfields;
564
565                 numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
566                 if (numfields < 3)
567                         continue;
568
569                 if ((strcmp (fields[0], "charging") == 0)
570                                 && (strcmp (fields[1], "state:") == 0))
571                 {
572                         if (strcmp (fields[2], "charging") == 0)
573                                 charging = 1;
574                         else
575                                 charging = 0;
576                         continue;
577                 }
578
579                 /* The unit of "present rate" depends on the battery. Modern
580                  * batteries export power (watts), older batteries (used to)
581                  * export current (amperes). We check the fourth column and try
582                  * to find old batteries this way. */
583                 if ((strcmp (fields[0], "present") == 0)
584                                 && (strcmp (fields[1], "rate:") == 0))
585                 {
586                         strtogauge (fields[2], &power);
587
588                         if ((numfields >= 4) && (strcmp ("mA", fields[3]) == 0))
589                                 is_current = 1;
590                 }
591                 else if ((strcmp (fields[0], "remaining") == 0)
592                                 && (strcmp (fields[1], "capacity:") == 0))
593                         strtogauge (fields[2], &charge);
594                 else if ((strcmp (fields[0], "present") == 0)
595                                 && (strcmp (fields[1], "voltage:") == 0))
596                         strtogauge (fields[2], &voltage);
597         } /* while (fgets (buffer, sizeof (buffer), fh) != NULL) */
598
599         fclose (fh);
600
601         if (!charging)
602                 power *= -1.0;
603
604         /* FIXME: This is a dirty hack for backwards compatibility: The battery
605          * plugin, for a very long time, has had the plugin_instance
606          * hard-coded to "0". So, to keep backwards compatibility, we'll use
607          * "0" for the first battery we find and the power_supply name for all
608          * following. This should be reverted in a future major version. */
609         plugin_instance = (*battery_index == 0) ? "0" : power_supply;
610         (*battery_index)++;
611
612         if (report_percent)
613         {
614                 gauge_t full_capacity;
615                 int status;
616
617                 status = read_acpi_full_capacity (dir, power_supply, &full_capacity);
618                 if (status == 0)
619                         battery_submit2 (plugin_instance, "percent", "charged",
620                                         100.0 * charge / full_capacity);
621         }
622         else
623         {
624                 battery_submit (plugin_instance, "charge", charge / 1000.0);
625         }
626
627         battery_submit (plugin_instance,
628                         is_current ? "current" : "power",
629                         power / 1000.0);
630         battery_submit (plugin_instance, "voltage", voltage / 1000.0);
631
632         return 0;
633 } /* }}} int read_acpi_callback */
634
635 static int read_acpi (void) /* {{{ */
636 {
637         int status;
638         int battery_counter = 0;
639
640         if (access (PROC_ACPI_PATH, R_OK) != 0)
641                 return (ENOENT);
642
643         status = walk_directory (PROC_ACPI_PATH, read_acpi_callback,
644                         /* user_data = */ &battery_counter,
645                         /* include hidden */ 0);
646         return (status);
647 } /* }}} int read_acpi */
648
649 static int read_pmu (void) /* {{{ */
650 {
651         int i;
652
653         /* The upper limit here is just a safeguard. If there is a system with
654          * more than 100 batteries, this can easily be increased. */
655         for (i = 0; i < 100; i++)
656         {
657                 FILE *fh;
658
659                 char buffer[1024];
660                 char filename[PATH_MAX];
661                 char plugin_instance[DATA_MAX_NAME_LEN];
662
663                 gauge_t current = NAN;
664                 gauge_t voltage = NAN;
665                 gauge_t charge  = NAN;
666
667                 ssnprintf (filename, sizeof (filename), PROC_PMU_PATH_FORMAT, i);
668                 if (access (filename, R_OK) != 0)
669                         break;
670
671                 ssnprintf (plugin_instance, sizeof (plugin_instance), "%i", i);
672
673                 fh = fopen (filename, "r");
674                 if (fh == NULL)
675                 {
676                         if (errno == ENOENT)
677                                 break;
678                         else if ((errno == EAGAIN) || (errno == EINTR))
679                                 continue;
680                         else
681                                 return (errno);
682                 }
683
684                 while (fgets (buffer, sizeof (buffer), fh) != NULL)
685                 {
686                         char *fields[8];
687                         int numfields;
688
689                         numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
690                         if (numfields < 3)
691                                 continue;
692
693                         if (strcmp ("current", fields[0]) == 0)
694                                 strtogauge (fields[2], &current);
695                         else if (strcmp ("voltage", fields[0]) == 0)
696                                 strtogauge (fields[2], &voltage);
697                         else if (strcmp ("charge", fields[0]) == 0)
698                                 strtogauge (fields[2], &charge);
699                 }
700
701                 fclose (fh);
702                 fh = NULL;
703
704                 battery_submit (plugin_instance, "charge", charge / 1000.0);
705                 battery_submit (plugin_instance, "current", current / 1000.0);
706                 battery_submit (plugin_instance, "voltage", voltage / 1000.0);
707         }
708
709         if (i == 0)
710                 return (ENOENT);
711         return (0);
712 } /* }}} int read_pmu */
713
714 static int battery_read (void) /* {{{ */
715 {
716         int status;
717
718         DEBUG ("battery plugin: Trying sysfs ...");
719         status = read_sysfs ();
720         if (status == 0)
721                 return (0);
722
723         DEBUG ("battery plugin: Trying acpi ...");
724         status = read_acpi ();
725         if (status == 0)
726                 return (0);
727
728         DEBUG ("battery plugin: Trying pmu ...");
729         status = read_pmu ();
730         if (status == 0)
731                 return (0);
732
733         ERROR ("battery plugin: Add available input methods failed.");
734         return (-1);
735 } /* }}} int battery_read */
736 #endif /* KERNEL_LINUX */
737
738 static int battery_config (oconfig_item_t *ci)
739 {
740         int i;
741
742         for (i = 0; i < ci->children_num; i++)
743         {
744                 oconfig_item_t *child = ci->children + i;
745
746                 if (strcasecmp ("ValuesPercentage", child->key) == 0)
747                         cf_util_get_boolean (child, &report_percent);
748                 else
749                         WARNING ("battery plugin: Ignoring unknown "
750                                         "configuration option \"%s\".",
751                                         child->key);
752         }
753
754         return (0);
755 } /* }}} int battery_config */
756
757 void module_register (void)
758 {
759         plugin_register_complex_config ("battery", battery_config);
760         plugin_register_read ("battery", battery_read);
761 } /* void module_register */