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