2 * collectd - src/battery.c
3 * Copyright (C) 2006 Florian octo Forster
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 * Florian octo Forster <octo at verplant.org>
26 #include "utils_debug.h"
28 #define MODULE_NAME "battery"
31 #if HAVE_MACH_MACH_TYPES_H
32 # include <mach/mach_types.h>
34 #if HAVE_MACH_MACH_INIT_H
35 # include <mach/mach_init.h>
37 #if HAVE_MACH_MACH_ERROR_H
38 # include <mach/mach_error.h>
40 #if HAVE_COREFOUNDATION_COREFOUNDATION_H
41 # include <CoreFoundation/CoreFoundation.h>
43 #if HAVE_IOKIT_IOKITLIB_H
44 # include <IOKit/IOKitLib.h>
46 #if HAVE_IOKIT_IOTYPES_H
47 # include <IOKit/IOTypes.h>
49 #if HAVE_IOKIT_PS_IOPOWERSOURCES_H
50 # include <IOKit/ps/IOPowerSources.h>
52 #if HAVE_IOKIT_PS_IOPSKEYS_H
53 # include <IOKit/ps/IOPSKeys.h>
56 #if HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H || KERNEL_LINUX
57 # define BATTERY_HAVE_READ 1
59 # define BATTERY_HAVE_READ 0
62 #define INVALID_VALUE 47841.29
64 static char *battery_current_file = "battery-%s/current.rrd";
65 static char *battery_voltage_file = "battery-%s/voltage.rrd";
66 static char *battery_charge_file = "battery-%s/charge.rrd";
68 static char *ds_def_current[] =
70 "DS:current:GAUGE:"COLLECTD_HEARTBEAT":U:U",
73 static int ds_num_current = 1;
75 static char *ds_def_voltage[] =
77 "DS:voltage:GAUGE:"COLLECTD_HEARTBEAT":U:U",
80 static int ds_num_voltage = 1;
82 static char *ds_def_charge[] =
84 "DS:charge:GAUGE:"COLLECTD_HEARTBEAT":0:U",
87 static int ds_num_charge = 1;
89 #if HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H
90 /* No global variables */
91 /* #endif HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H */
94 static int battery_pmu_num = 0;
95 static char *battery_pmu_file = "/proc/pmu/battery_%i";
96 #endif /* KERNEL_LINUX */
98 static void battery_init (void)
100 #if HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H
101 /* No init neccessary */
102 /* #endif HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H */
106 char filename[BUFSIZE];
108 for (battery_pmu_num = 0; ; battery_pmu_num++)
110 len = snprintf (filename, BUFSIZE, battery_pmu_file, battery_pmu_num);
112 if ((len >= BUFSIZE) || (len < 0))
115 if (access (filename, R_OK))
118 #endif /* KERNEL_LINUX */
123 static void battery_current_write (char *host, char *inst, char *val)
125 char filename[BUFSIZE];
128 len = snprintf (filename, BUFSIZE, battery_current_file, inst);
129 if ((len >= BUFSIZE) || (len < 0))
132 rrd_update_file (host, filename, val,
133 ds_def_current, ds_num_current);
136 static void battery_voltage_write (char *host, char *inst, char *val)
138 char filename[BUFSIZE];
141 len = snprintf (filename, BUFSIZE, battery_voltage_file, inst);
142 if ((len >= BUFSIZE) || (len < 0))
145 rrd_update_file (host, filename, val,
146 ds_def_voltage, ds_num_voltage);
149 static void battery_charge_write (char *host, char *inst, char *val)
151 char filename[BUFSIZE];
154 len = snprintf (filename, BUFSIZE, battery_charge_file, inst);
155 if ((len >= BUFSIZE) || (len < 0))
158 rrd_update_file (host, filename, val,
159 ds_def_charge, ds_num_charge);
162 #if BATTERY_HAVE_READ
163 static void battery_submit (char *inst, double current, double voltage, double charge)
166 char buffer[BUFSIZE];
168 if (current != INVALID_VALUE)
170 len = snprintf (buffer, BUFSIZE, "N:%.3f", current);
172 if ((len > 0) && (len < BUFSIZE))
173 plugin_submit ("battery_current", inst, buffer);
177 plugin_submit ("battery_current", inst, "N:U");
180 if (voltage != INVALID_VALUE)
182 len = snprintf (buffer, BUFSIZE, "N:%.3f", voltage);
184 if ((len > 0) && (len < BUFSIZE))
185 plugin_submit ("battery_voltage", inst, buffer);
189 plugin_submit ("battery_voltage", inst, "N:U");
192 if (charge != INVALID_VALUE)
194 len = snprintf (buffer, BUFSIZE, "N:%.3f", charge);
196 if ((len > 0) && (len < BUFSIZE))
197 plugin_submit ("battery_charge", inst, buffer);
201 plugin_submit ("battery_charge", inst, "N:U");
205 double dict_get_double (CFDictionaryRef dict, char *key_string)
212 key_obj = CFStringCreateWithCString (kCFAllocatorDefault, key_string,
213 kCFStringEncodingASCII);
216 DBG ("CFStringCreateWithCString (%s) failed.\n", key_string);
217 return (INVALID_VALUE);
220 if ((val_obj = CFDictionaryGetValue (dict, key_obj)) == NULL)
222 DBG ("CFDictionaryGetValue (%s) failed.\n", key_string);
224 return (INVALID_VALUE);
228 if (CFGetTypeID (val_obj) == CFNumberGetTypeID ())
230 if (CFNumberIsFloatType (val_obj))
232 CFNumberGetValue (val_obj,
238 CFNumberGetValue (val_obj,
239 kCFNumberLongLongType,
241 val_double = val_int;
246 DBG ("CFGetTypeID (val_obj) = %i\n", (int) CFGetTypeID (val_obj));
247 return (INVALID_VALUE);
253 #if HAVE_IOKIT_PS_IOPOWERSOURCES_H
254 static void get_via_io_power_sources (double *ret_charge,
261 CFDictionaryRef ps_dict;
267 ps_raw = IOPSCopyPowerSourcesInfo ();
268 ps_array = IOPSCopyPowerSourcesList (ps_raw);
269 ps_array_len = CFArrayGetCount (ps_array);
271 DBG ("ps_array_len == %i", ps_array_len);
273 for (i = 0; i < ps_array_len; i++)
275 ps_obj = CFArrayGetValueAtIndex (ps_array, i);
276 ps_dict = IOPSGetPowerSourceDescription (ps_raw, ps_obj);
280 DBG ("IOPSGetPowerSourceDescription failed.");
284 if (CFGetTypeID (ps_dict) != CFDictionaryGetTypeID ())
286 DBG ("IOPSGetPowerSourceDescription did not return a CFDictionaryRef");
290 /* FIXME: Check if this is really an internal battery */
292 if (*ret_charge == INVALID_VALUE)
294 double current_charge;
297 current_charge = dict_get_double (ps_dict,
298 kIOPSCurrentCapacityKey);
299 total_charge = dict_get_double (ps_dict,
300 kIOPSMaxCapacityKey);
302 if ((current_charge != INVALID_VALUE)
303 && (total_charge != INVALID_VALUE)
304 && (current_charge >= 0.0)
305 && (current_charge <= 100.0))
306 *ret_charge = total_charge * current_charge / 100.0;
309 if (*ret_current == INVALID_VALUE)
311 temp_double = dict_get_double (ps_dict,
313 if (temp_double != INVALID_VALUE)
314 *ret_current = temp_double / 1000.0;
317 if (*ret_voltage == INVALID_VALUE)
319 temp_double = dict_get_double (ps_dict,
321 if (temp_double != INVALID_VALUE)
322 *ret_voltage = temp_double / 1000.0;
329 #endif /* HAVE_IOKIT_PS_IOPOWERSOURCES_H */
331 #if HAVE_IOKIT_IOKITLIB_H
332 static void get_via_generic_iokit (double *ret_charge,
336 kern_return_t status;
337 io_iterator_t iterator;
340 CFDictionaryRef bat_root_dict;
341 CFArrayRef bat_info_arry;
342 CFIndex bat_info_arry_len;
343 CFIndex bat_info_arry_pos;
344 CFDictionaryRef bat_info_dict;
348 status = IOServiceGetMatchingServices (kIOMasterPortDefault,
349 IOServiceNameMatching ("battery"),
351 if (status != kIOReturnSuccess)
353 DBG ("IOServiceGetMatchingServices failed.");
357 while ((io_obj = IOIteratorNext (iterator)))
359 status = IORegistryEntryCreateCFProperties (io_obj,
360 (CFMutableDictionaryRef *) &bat_root_dict,
363 if (status != kIOReturnSuccess)
365 DBG ("IORegistryEntryCreateCFProperties failed.");
369 bat_info_arry = (CFArrayRef) CFDictionaryGetValue (bat_root_dict,
370 CFSTR ("IOBatteryInfo"));
371 if (bat_info_arry == NULL)
373 CFRelease (bat_root_dict);
376 bat_info_arry_len = CFArrayGetCount (bat_info_arry);
378 for (bat_info_arry_pos = 0;
379 bat_info_arry_pos < bat_info_arry_len;
382 bat_info_dict = (CFDictionaryRef) CFArrayGetValueAtIndex (bat_info_arry, bat_info_arry_pos);
384 if (*ret_current == INVALID_VALUE)
386 temp_double = dict_get_double (bat_info_dict,
388 if (temp_double != INVALID_VALUE)
389 *ret_current = temp_double / 1000.0;
392 if (*ret_voltage == INVALID_VALUE)
394 temp_double = dict_get_double (bat_info_dict,
396 if (temp_double != INVALID_VALUE)
397 *ret_voltage = temp_double / 1000.0;
401 CFRelease (bat_root_dict);
404 IOObjectRelease (iterator);
406 #endif /* HAVE_IOKIT_IOKITLIB_H */
408 static void battery_read (void)
410 #if HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H
411 double charge = INVALID_VALUE;
412 double current = INVALID_VALUE;
413 double voltage = INVALID_VALUE;
415 #if HAVE_IOKIT_PS_IOPOWERSOURCES_H
416 get_via_io_power_sources (&charge, ¤t, &voltage);
419 #if HAVE_IOKIT_IOKITLIB_H
420 if ((charge == INVALID_VALUE)
421 || (current == INVALID_VALUE)
422 || (voltage == INVALID_VALUE))
423 get_via_generic_iokit (&charge, ¤t, &voltage);
426 if ((charge != INVALID_VALUE)
427 || (current != INVALID_VALUE)
428 || (voltage != INVALID_VALUE))
429 battery_submit ("battery", current, voltage, charge);
430 /* #endif HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H */
434 char buffer[BUFSIZE];
435 char filename[BUFSIZE];
443 for (i = 0; i < battery_pmu_num; i++)
445 char batnum_str[BUFSIZE];
446 double current = INVALID_VALUE;
447 double voltage = INVALID_VALUE;
448 double charge = INVALID_VALUE;
449 double *valptr = NULL;
451 len = snprintf (filename, BUFSIZE, battery_pmu_file, i);
452 if ((len >= BUFSIZE) || (len < 0))
455 len = snprintf (batnum_str, BUFSIZE, "%i", i);
456 if ((len >= BUFSIZE) || (len < 0))
459 if ((fh = fopen (filename, "r")) == NULL)
462 while (fgets (buffer, BUFSIZE, fh) != NULL)
464 numfields = strsplit (buffer, fields, 8);
469 if (strcmp ("current", fields[0]) == 0)
471 else if (strcmp ("voltage", fields[0]) == 0)
473 else if (strcmp ("charge", fields[0]) == 0)
485 *valptr = strtod (fields[2], &endptr) / 1000.0;
487 if ((fields[2] == endptr) || (errno != 0))
488 *valptr = INVALID_VALUE;
492 if ((current != INVALID_VALUE)
493 || (voltage != INVALID_VALUE)
494 || (charge != INVALID_VALUE))
495 battery_submit (batnum_str, current, voltage, charge);
501 if (access ("/proc/acpi/battery", R_OK | X_OK) == 0)
503 double current = INVALID_VALUE;
504 double voltage = INVALID_VALUE;
505 double charge = INVALID_VALUE;
506 double *valptr = NULL;
512 if ((dh = opendir ("/proc/acpi/battery")) == NULL)
514 syslog (LOG_ERR, "Cannot open `/proc/acpi/battery': %s", strerror (errno));
518 while ((ent = readdir (dh)) != NULL)
520 if (ent->d_name[0] == '.')
523 len = snprintf (filename, BUFSIZE, "/proc/acpi/battery/%s/state", ent->d_name);
524 if ((len >= BUFSIZE) || (len < 0))
527 if ((fh = fopen (filename, "r")) == NULL)
529 syslog (LOG_ERR, "Cannot open `%s': %s", filename, strerror (errno));
534 * [11:00] <@tokkee> $ cat /proc/acpi/battery/BAT1/state
535 * [11:00] <@tokkee> present: yes
536 * [11:00] <@tokkee> capacity state: ok
537 * [11:00] <@tokkee> charging state: charging
538 * [11:00] <@tokkee> present rate: 1724 mA
539 * [11:00] <@tokkee> remaining capacity: 4136 mAh
540 * [11:00] <@tokkee> present voltage: 12428 mV
542 while (fgets (buffer, BUFSIZE, fh) != NULL)
544 numfields = strsplit (buffer, fields, 8);
549 if ((strcmp (fields[0], "present") == 0)
550 && (strcmp (fields[1], "rate:") == 0))
552 else if ((strcmp (fields[0], "remaining") == 0)
553 && (strcmp (fields[1], "capacity:") == 0))
555 else if ((strcmp (fields[0], "present") == 0)
556 && (strcmp (fields[1], "voltage:") == 0))
561 if ((strcmp (fields[0], "charging") == 0)
562 && (strcmp (fields[1], "state:") == 0))
564 if (strcmp (fields[2], "charging") == 0)
577 *valptr = strtod (fields[2], &endptr) / 1000.0;
579 if ((fields[2] == endptr) || (errno != 0))
580 *valptr = INVALID_VALUE;
584 if ((current != INVALID_VALUE) && (charging == 0))
587 if ((current != INVALID_VALUE)
588 || (voltage != INVALID_VALUE)
589 || (charge != INVALID_VALUE))
590 battery_submit (ent->d_name, current, voltage, charge);
597 #endif /* KERNEL_LINUX */
600 # define battery_read NULL
601 #endif /* BATTERY_HAVE_READ */
603 void module_register (void)
605 plugin_register (MODULE_NAME, battery_init, battery_read, NULL);
606 plugin_register ("battery_current", NULL, NULL, battery_current_write);
607 plugin_register ("battery_voltage", NULL, NULL, battery_voltage_write);
608 plugin_register ("battery_charge", NULL, NULL, battery_charge_write);