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 **/
26 #include "collectd.h"
28 #include "common.h"
29 #include "plugin.h"
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
56 #if !HAVE_IOKIT_IOKITLIB_H && !HAVE_IOKIT_PS_IOPOWERSOURCES_H && !KERNEL_LINUX
57 #error "No applicable input method."
58 #endif
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 */
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 */
72 static _Bool report_percent = 0;
73 static _Bool report_degraded = 0;
75 static void battery_submit2(char const *plugin_instance, /* {{{ */
76 char const *type, char const *type_instance,
77 gauge_t value) {
78 value_t values[1];
79 value_list_t vl = VALUE_LIST_INIT;
81 values[0].gauge = value;
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));
92 plugin_dispatch_values(&vl);
93 } /* }}} void battery_submit2 */
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 */
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;
108 if (report_percent) {
109 gauge_t capacity_max;
111 if (report_degraded)
112 capacity_max = capacity_design;
113 else
114 capacity_max = capacity_full;
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);
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 */
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;
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 }
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);
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 }
172 return (val_double);
173 } /* }}} double dict_get_double */
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;
184 double temp_double;
186 ps_raw = IOPSCopyPowerSourcesInfo();
187 ps_array = IOPSCopyPowerSourcesList(ps_raw);
188 ps_array_len = CFArrayGetCount(ps_array);
190 DEBUG("ps_array_len == %i", ps_array_len);
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);
196 if (ps_dict == NULL) {
197 DEBUG("IOPSGetPowerSourceDescription failed.");
198 continue;
199 }
201 if (CFGetTypeID(ps_dict) != CFDictionaryGetTypeID()) {
202 DEBUG("IOPSGetPowerSourceDescription did not return a CFDictionaryRef");
203 continue;
204 }
206 /* FIXME: Check if this is really an internal battery */
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 }
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 }
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 }
229 CFRelease(ps_array);
230 CFRelease(ps_raw);
231 } /* }}} void get_via_io_power_sources */
232 #endif /* HAVE_IOKIT_PS_IOPOWERSOURCES_H */
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;
242 CFDictionaryRef bat_root_dict;
243 CFArrayRef bat_info_arry;
244 CFIndex bat_info_arry_len;
245 CFDictionaryRef bat_info_dict;
247 double temp_double;
249 status = IOServiceGetMatchingServices(
250 kIOMasterPortDefault, IOServiceNameMatching("battery"), &iterator);
251 if (status != kIOReturnSuccess) {
252 DEBUG("IOServiceGetMatchingServices failed.");
253 return;
254 }
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 }
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);
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);
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 }
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 }
288 if (isnan(*ret_current)) {
289 temp_double = dict_get_double(bat_info_dict, "Current");
290 *ret_current = temp_double / 1000.0;
291 }
293 if (isnan(*ret_voltage)) {
294 temp_double = dict_get_double(bat_info_dict, "Voltage");
295 *ret_voltage = temp_double / 1000.0;
296 }
297 }
299 CFRelease(bat_root_dict);
300 }
302 IOObjectRelease(iterator);
303 } /* }}} void get_via_generic_iokit */
304 #endif /* HAVE_IOKIT_IOKITLIB_H */
306 static int battery_read(void) /* {{{ */
307 {
308 gauge_t current = NAN; /* Current in A */
309 gauge_t voltage = NAN; /* Voltage in V */
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 */
319 #if HAVE_IOKIT_PS_IOPOWERSOURCES_H
320 get_via_io_power_sources(&charge_rel, ¤t, &voltage);
321 #endif
322 #if HAVE_IOKIT_IOKITLIB_H
323 get_via_generic_iokit(&capacity_full, &capacity_design, ¤t, &voltage);
324 #endif
326 capacity_charged = charge_rel * capacity_full / 100.0;
327 submit_capacity("0", capacity_charged, capacity_full, capacity_design);
329 if (!isnan(current))
330 battery_submit("0", "current", current);
331 if (!isnan(voltage))
332 battery_submit("0", "voltage", voltage);
334 return (0);
335 } /* }}} int battery_read */
336 /* #endif HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H */
338 #elif KERNEL_LINUX
339 /* Reads a file which contains only a number (and optionally a trailing
340 * newline) and parses that number. */
341 static int sysfs_file_to_buffer(char const *dir, /* {{{ */
342 char const *power_supply, char const *basename,
343 char *buffer, size_t buffer_size) {
344 int status;
345 FILE *fp;
346 char filename[PATH_MAX];
348 ssnprintf(filename, sizeof(filename), "%s/%s/%s", dir, power_supply,
349 basename);
351 /* No file isn't the end of the world -- not every system will be
352 * reporting the same set of statistics */
353 if (access(filename, R_OK) != 0)
354 return ENOENT;
356 fp = fopen(filename, "r");
357 if (fp == NULL) {
358 status = errno;
359 if (status != ENOENT) {
360 char errbuf[1024];
361 WARNING("battery plugin: fopen (%s) failed: %s", filename,
362 sstrerror(status, errbuf, sizeof(errbuf)));
363 }
364 return status;
365 }
367 if (fgets(buffer, buffer_size, fp) == NULL) {
368 status = errno;
369 if (status != ENODEV) {
370 char errbuf[1024];
371 WARNING("battery plugin: fgets (%s) failed: %s", filename,
372 sstrerror(status, errbuf, sizeof(errbuf)));
373 }
374 fclose(fp);
375 return status;
376 }
378 strstripnewline(buffer);
380 fclose(fp);
381 return 0;
382 } /* }}} int sysfs_file_to_buffer */
384 /* Reads a file which contains only a number (and optionally a trailing
385 * newline) and parses that number. */
386 static int sysfs_file_to_gauge(char const *dir, /* {{{ */
387 char const *power_supply, char const *basename,
388 gauge_t *ret_value) {
389 int status;
390 char buffer[32] = "";
392 status =
393 sysfs_file_to_buffer(dir, power_supply, basename, buffer, sizeof(buffer));
394 if (status != 0)
395 return (status);
397 return (strtogauge(buffer, ret_value));
398 } /* }}} sysfs_file_to_gauge */
400 static int read_sysfs_capacity(char const *dir, /* {{{ */
401 char const *power_supply,
402 char const *plugin_instance) {
403 gauge_t capacity_charged = NAN;
404 gauge_t capacity_full = NAN;
405 gauge_t capacity_design = NAN;
406 int status;
408 status =
409 sysfs_file_to_gauge(dir, power_supply, "energy_now", &capacity_charged);
410 if (status != 0)
411 return (status);
413 status =
414 sysfs_file_to_gauge(dir, power_supply, "energy_full", &capacity_full);
415 if (status != 0)
416 return (status);
418 status = sysfs_file_to_gauge(dir, power_supply, "energy_full_design",
419 &capacity_design);
420 if (status != 0)
421 return (status);
423 submit_capacity(plugin_instance, capacity_charged * SYSFS_FACTOR,
424 capacity_full * SYSFS_FACTOR, capacity_design * SYSFS_FACTOR);
425 return (0);
426 } /* }}} int read_sysfs_capacity */
428 static int read_sysfs_callback(char const *dir, /* {{{ */
429 char const *power_supply, void *user_data) {
430 int *battery_index = user_data;
432 char const *plugin_instance;
433 char buffer[32];
434 gauge_t v = NAN;
435 _Bool discharging = 0;
436 int status;
438 /* Ignore non-battery directories, such as AC power. */
439 status =
440 sysfs_file_to_buffer(dir, power_supply, "type", buffer, sizeof(buffer));
441 if (status != 0)
442 return (0);
443 if (strcasecmp("Battery", buffer) != 0)
444 return (0);
446 (void)sysfs_file_to_buffer(dir, power_supply, "status", buffer,
447 sizeof(buffer));
448 if (strcasecmp("Discharging", buffer) == 0)
449 discharging = 1;
451 /* FIXME: This is a dirty hack for backwards compatibility: The battery
452 * plugin, for a very long time, has had the plugin_instance
453 * hard-coded to "0". So, to keep backwards compatibility, we'll use
454 * "0" for the first battery we find and the power_supply name for all
455 * following. This should be reverted in a future major version. */
456 plugin_instance = (*battery_index == 0) ? "0" : power_supply;
457 (*battery_index)++;
459 read_sysfs_capacity(dir, power_supply, plugin_instance);
461 if (sysfs_file_to_gauge(dir, power_supply, "power_now", &v) == 0) {
462 if (discharging)
463 v *= -1.0;
464 battery_submit(plugin_instance, "power", v * SYSFS_FACTOR);
465 }
466 if (sysfs_file_to_gauge(dir, power_supply, "current_now", &v) == 0) {
467 if (discharging)
468 v *= -1.0;
469 battery_submit(plugin_instance, "current", v * SYSFS_FACTOR);
470 }
472 if (sysfs_file_to_gauge(dir, power_supply, "voltage_now", &v) == 0)
473 battery_submit(plugin_instance, "voltage", v * SYSFS_FACTOR);
475 return (0);
476 } /* }}} int read_sysfs_callback */
478 static int read_sysfs(void) /* {{{ */
479 {
480 int status;
481 int battery_counter = 0;
483 if (access(SYSFS_PATH, R_OK) != 0)
484 return (ENOENT);
486 status = walk_directory(SYSFS_PATH, read_sysfs_callback,
487 /* user_data = */ &battery_counter,
488 /* include hidden */ 0);
489 return (status);
490 } /* }}} int read_sysfs */
492 static int read_acpi_full_capacity(char const *dir, /* {{{ */
493 char const *power_supply,
494 gauge_t *ret_capacity_full,
495 gauge_t *ret_capacity_design)
497 {
498 char filename[PATH_MAX];
499 char buffer[1024];
501 FILE *fh;
503 ssnprintf(filename, sizeof(filename), "%s/%s/info", dir, power_supply);
504 fh = fopen(filename, "r");
505 if (fh == NULL)
506 return (errno);
508 /* last full capacity: 40090 mWh */
509 while (fgets(buffer, sizeof(buffer), fh) != NULL) {
510 gauge_t *value_ptr;
511 int fields_num;
512 char *fields[8];
513 int index;
515 if (strncmp("last full capacity:", buffer, strlen("last full capacity:")) ==
516 0) {
517 value_ptr = ret_capacity_full;
518 index = 3;
519 } else if (strncmp("design capacity:", buffer,
520 strlen("design capacity:")) == 0) {
521 value_ptr = ret_capacity_design;
522 index = 2;
523 } else {
524 continue;
525 }
527 fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
528 if (fields_num <= index)
529 continue;
531 strtogauge(fields[index], value_ptr);
532 }
534 fclose(fh);
535 return (0);
536 } /* }}} int read_acpi_full_capacity */
538 static int read_acpi_callback(char const *dir, /* {{{ */
539 char const *power_supply, void *user_data) {
540 int *battery_index = user_data;
542 gauge_t power = NAN;
543 gauge_t voltage = NAN;
544 gauge_t capacity_charged = NAN;
545 gauge_t capacity_full = NAN;
546 gauge_t capacity_design = NAN;
547 _Bool charging = 0;
548 _Bool is_current = 0;
550 char const *plugin_instance;
551 char filename[PATH_MAX];
552 char buffer[1024];
554 FILE *fh;
556 ssnprintf(filename, sizeof(filename), "%s/%s/state", dir, power_supply);
557 fh = fopen(filename, "r");
558 if (fh == NULL) {
559 if ((errno == EAGAIN) || (errno == EINTR) || (errno == ENOENT))
560 return (0);
561 else
562 return (errno);
563 }
565 /*
566 * [11:00] <@tokkee> $ cat /proc/acpi/battery/BAT1/state
567 * [11:00] <@tokkee> present: yes
568 * [11:00] <@tokkee> capacity state: ok
569 * [11:00] <@tokkee> charging state: charging
570 * [11:00] <@tokkee> present rate: 1724 mA
571 * [11:00] <@tokkee> remaining capacity: 4136 mAh
572 * [11:00] <@tokkee> present voltage: 12428 mV
573 */
574 while (fgets(buffer, sizeof(buffer), fh) != NULL) {
575 char *fields[8];
576 int numfields;
578 numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
579 if (numfields < 3)
580 continue;
582 if ((strcmp(fields[0], "charging") == 0) &&
583 (strcmp(fields[1], "state:") == 0)) {
584 if (strcmp(fields[2], "charging") == 0)
585 charging = 1;
586 else
587 charging = 0;
588 continue;
589 }
591 /* The unit of "present rate" depends on the battery. Modern
592 * batteries export power (watts), older batteries (used to)
593 * export current (amperes). We check the fourth column and try
594 * to find old batteries this way. */
595 if ((strcmp(fields[0], "present") == 0) &&
596 (strcmp(fields[1], "rate:") == 0)) {
597 strtogauge(fields[2], &power);
599 if ((numfields >= 4) && (strcmp("mA", fields[3]) == 0))
600 is_current = 1;
601 } else if ((strcmp(fields[0], "remaining") == 0) &&
602 (strcmp(fields[1], "capacity:") == 0))
603 strtogauge(fields[2], &capacity_charged);
604 else if ((strcmp(fields[0], "present") == 0) &&
605 (strcmp(fields[1], "voltage:") == 0))
606 strtogauge(fields[2], &voltage);
607 } /* while (fgets (buffer, sizeof (buffer), fh) != NULL) */
609 fclose(fh);
611 if (!charging)
612 power *= -1.0;
614 /* FIXME: This is a dirty hack for backwards compatibility: The battery
615 * plugin, for a very long time, has had the plugin_instance
616 * hard-coded to "0". So, to keep backwards compatibility, we'll use
617 * "0" for the first battery we find and the power_supply name for all
618 * following. This should be reverted in a future major version. */
619 plugin_instance = (*battery_index == 0) ? "0" : power_supply;
620 (*battery_index)++;
622 read_acpi_full_capacity(dir, power_supply, &capacity_full, &capacity_design);
624 submit_capacity(plugin_instance, capacity_charged * PROC_ACPI_FACTOR,
625 capacity_full * PROC_ACPI_FACTOR,
626 capacity_design * PROC_ACPI_FACTOR);
628 battery_submit(plugin_instance, is_current ? "current" : "power",
629 power * PROC_ACPI_FACTOR);
630 battery_submit(plugin_instance, "voltage", voltage * PROC_ACPI_FACTOR);
632 return 0;
633 } /* }}} int read_acpi_callback */
635 static int read_acpi(void) /* {{{ */
636 {
637 int status;
638 int battery_counter = 0;
640 if (access(PROC_ACPI_PATH, R_OK) != 0)
641 return (ENOENT);
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 */
649 static int read_pmu(void) /* {{{ */
650 {
651 int i = 0;
652 /* The upper limit here is just a safeguard. If there is a system with
653 * more than 100 batteries, this can easily be increased. */
654 for (; i < 100; i++) {
655 FILE *fh;
657 char buffer[1024];
658 char filename[PATH_MAX];
659 char plugin_instance[DATA_MAX_NAME_LEN];
661 gauge_t current = NAN;
662 gauge_t voltage = NAN;
663 gauge_t charge = NAN;
665 ssnprintf(filename, sizeof(filename), PROC_PMU_PATH_FORMAT, i);
666 if (access(filename, R_OK) != 0)
667 break;
669 ssnprintf(plugin_instance, sizeof(plugin_instance), "%i", i);
671 fh = fopen(filename, "r");
672 if (fh == NULL) {
673 if (errno == ENOENT)
674 break;
675 else if ((errno == EAGAIN) || (errno == EINTR))
676 continue;
677 else
678 return (errno);
679 }
681 while (fgets(buffer, sizeof(buffer), fh) != NULL) {
682 char *fields[8];
683 int numfields;
685 numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
686 if (numfields < 3)
687 continue;
689 if (strcmp("current", fields[0]) == 0)
690 strtogauge(fields[2], ¤t);
691 else if (strcmp("voltage", fields[0]) == 0)
692 strtogauge(fields[2], &voltage);
693 else if (strcmp("charge", fields[0]) == 0)
694 strtogauge(fields[2], &charge);
695 }
697 fclose(fh);
698 fh = NULL;
700 battery_submit(plugin_instance, "charge", charge / 1000.0);
701 battery_submit(plugin_instance, "current", current / 1000.0);
702 battery_submit(plugin_instance, "voltage", voltage / 1000.0);
703 }
705 if (i == 0)
706 return (ENOENT);
707 return (0);
708 } /* }}} int read_pmu */
710 static int battery_read(void) /* {{{ */
711 {
712 int status;
714 DEBUG("battery plugin: Trying sysfs ...");
715 status = read_sysfs();
716 if (status == 0)
717 return (0);
719 DEBUG("battery plugin: Trying acpi ...");
720 status = read_acpi();
721 if (status == 0)
722 return (0);
724 DEBUG("battery plugin: Trying pmu ...");
725 status = read_pmu();
726 if (status == 0)
727 return (0);
729 ERROR("battery plugin: All available input methods failed.");
730 return (-1);
731 } /* }}} int battery_read */
732 #endif /* KERNEL_LINUX */
734 static int battery_config(oconfig_item_t *ci) {
735 for (int i = 0; i < ci->children_num; i++) {
736 oconfig_item_t *child = ci->children + i;
738 if (strcasecmp("ValuesPercentage", child->key) == 0)
739 cf_util_get_boolean(child, &report_percent);
740 else if (strcasecmp("ReportDegraded", child->key) == 0)
741 cf_util_get_boolean(child, &report_degraded);
742 else
743 WARNING("battery plugin: Ignoring unknown "
744 "configuration option \"%s\".",
745 child->key);
746 }
748 return (0);
749 } /* }}} int battery_config */
751 void module_register(void) {
752 plugin_register_complex_config("battery", battery_config);
753 plugin_register_read("battery", battery_read);
754 } /* void module_register */