201280c9e1688358a80e5363df0e0afc44851c05
[collectd.git] / src / battery.c
1 /**
2  * collectd - src/battery.c
3  * Copyright (C) 2006  Florian octo Forster
4  *
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.
9  *
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.
14  *
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
18  *
19  * Authors:
20  *   Florian octo Forster <octo at verplant.org>
21  **/
22
23 #include "collectd.h"
24 #include "common.h"
25 #include "plugin.h"
26
27 #define MODULE_NAME "battery"
28 #define BUFSIZE 512
29
30 #if defined(KERNEL_LINUX)
31 # define BATTERY_HAVE_READ 1
32 #else
33 # define BATTERY_HAVE_READ 0
34 #endif
35
36 #define INVALID_VALUE 47841.29
37
38 static char *battery_current_file = "battery-%s/current.rrd";
39 static char *battery_voltage_file = "battery-%s/voltage.rrd";
40 static char *battery_charge_file  = "battery-%s/charge.rrd";
41
42 static char *ds_def_current[] =
43 {
44         "DS:current:GAUGE:"COLLECTD_HEARTBEAT":U:U",
45         NULL
46 };
47 static int ds_num_current = 1;
48
49 static char *ds_def_voltage[] =
50 {
51         "DS:voltage:GAUGE:"COLLECTD_HEARTBEAT":U:U",
52         NULL
53 };
54 static int ds_num_voltage = 1;
55
56 static char *ds_def_charge[] =
57 {
58         "DS:charge:GAUGE:"COLLECTD_HEARTBEAT":0:U",
59         NULL
60 };
61 static int ds_num_charge = 1;
62
63 #if BATTERY_HAVE_READ
64 static int   battery_pmu_num = 0;
65 static char *battery_pmu_file = "/proc/pmu/battery_%i";
66 #endif
67
68 static void battery_init (void)
69 {
70 #if BATTERY_HAVE_READ
71         int len;
72         char filename[BUFSIZE];
73
74         for (battery_pmu_num = 0; ; battery_pmu_num++)
75         {
76                 len = snprintf (filename, BUFSIZE, battery_pmu_file, battery_pmu_num);
77
78                 if ((len >= BUFSIZE) || (len < 0))
79                         break;
80
81                 if (access (filename, R_OK))
82                         break;
83         }
84 #endif
85
86         return;
87 }
88
89 static void battery_current_write (char *host, char *inst, char *val)
90 {
91         char filename[BUFSIZE];
92         int len;
93
94         len = snprintf (filename, BUFSIZE, battery_current_file, inst);
95         if ((len >= BUFSIZE) || (len < 0))
96                 return;
97
98         rrd_update_file (host, filename, val,
99                         ds_def_current, ds_num_current);
100 }
101
102 static void battery_voltage_write (char *host, char *inst, char *val)
103 {
104         char filename[BUFSIZE];
105         int len;
106
107         len = snprintf (filename, BUFSIZE, battery_voltage_file, inst);
108         if ((len >= BUFSIZE) || (len < 0))
109                 return;
110
111         rrd_update_file (host, filename, val,
112                         ds_def_voltage, ds_num_voltage);
113 }
114
115 static void battery_charge_write (char *host, char *inst, char *val)
116 {
117         char filename[BUFSIZE];
118         int len;
119
120         len = snprintf (filename, BUFSIZE, battery_charge_file, inst);
121         if ((len >= BUFSIZE) || (len < 0))
122                 return;
123
124         rrd_update_file (host, filename, val,
125                         ds_def_charge, ds_num_charge);
126 }
127
128 #if BATTERY_HAVE_READ
129 static void battery_submit (char *inst, double current, double voltage, double charge)
130 {
131         int len;
132         char buffer[BUFSIZE];
133
134         if (current != INVALID_VALUE)
135         {
136                 len = snprintf (buffer, BUFSIZE, "N:%.3f", current);
137
138                 if ((len > 0) && (len < BUFSIZE))
139                         plugin_submit ("battery_current", inst, buffer);
140         }
141         else
142         {
143                 plugin_submit ("battery_current", inst, "N:U");
144         }
145
146         if (voltage != INVALID_VALUE)
147         {
148                 len = snprintf (buffer, BUFSIZE, "N:%.3f", voltage);
149
150                 if ((len > 0) && (len < BUFSIZE))
151                         plugin_submit ("battery_voltage", inst, buffer);
152         }
153         else
154         {
155                 plugin_submit ("battery_voltage", inst, "N:U");
156         }
157
158         if (charge != INVALID_VALUE)
159         {
160                 len = snprintf (buffer, BUFSIZE, "N:%.3f", charge);
161
162                 if ((len > 0) && (len < BUFSIZE))
163                         plugin_submit ("battery_charge", inst, buffer);
164         }
165         else
166         {
167                 plugin_submit ("battery_charge", inst, "N:U");
168         }
169 }
170
171 static void battery_read (void)
172 {
173 #ifdef KERNEL_LINUX
174         FILE *fh;
175         char buffer[BUFSIZE];
176         char filename[BUFSIZE];
177         
178         char *fields[8];
179         int numfields;
180
181         int i;
182         int len;
183
184         for (i = 0; i < battery_pmu_num; i++)
185         {
186                 char    batnum_str[BUFSIZE];
187                 double  current = INVALID_VALUE;
188                 double  voltage = INVALID_VALUE;
189                 double  charge  = INVALID_VALUE;
190                 double *valptr = NULL;
191
192                 len = snprintf (filename, BUFSIZE, battery_pmu_file, i);
193                 if ((len >= BUFSIZE) || (len < 0))
194                         continue;
195
196                 len = snprintf (batnum_str, BUFSIZE, "%i", i);
197                 if ((len >= BUFSIZE) || (len < 0))
198                         continue;
199
200                 if ((fh = fopen (filename, "r")) == NULL)
201                         continue;
202
203                 while (fgets (buffer, BUFSIZE, fh) != NULL)
204                 {
205                         numfields = strsplit (buffer, fields, 8);
206
207                         if (numfields < 3)
208                                 continue;
209
210                         if (strcmp ("current", fields[0]) == 0)
211                                 valptr = &current;
212                         else if (strcmp ("voltage", fields[0]) == 0)
213                                 valptr = &voltage;
214                         else if (strcmp ("charge", fields[0]) == 0)
215                                 valptr = &charge;
216                         else
217                                 valptr = NULL;
218
219                         if (valptr != NULL)
220                         {
221                                 char *endptr;
222
223                                 endptr = NULL;
224                                 errno  = 0;
225
226                                 *valptr = strtod (fields[2], &endptr) / 1000.0;
227
228                                 if ((fields[2] == endptr) || (errno != 0))
229                                         *valptr = INVALID_VALUE;
230                         }
231                 }
232
233                 if ((current != INVALID_VALUE)
234                                 || (voltage != INVALID_VALUE)
235                                 || (charge  != INVALID_VALUE))
236                         battery_submit (batnum_str, current, voltage, charge);
237
238                 fclose (fh);
239                 fh = NULL;
240         }
241
242         if (access ("/proc/acpi/battery", R_OK | X_OK) == 0)
243         {
244                 double  current = INVALID_VALUE;
245                 double  voltage = INVALID_VALUE;
246                 double  charge  = INVALID_VALUE;
247                 double *valptr = NULL;
248                 int charging = 0;
249
250                 struct dirent *ent;
251                 DIR *dh;
252
253                 if ((dh = opendir ("/proc/acpi/battery")) == NULL)
254                 {
255                         syslog (LOG_ERR, "Cannot open `/proc/acpi/battery': %s", strerror (errno));
256                         return;
257                 }
258
259                 while ((ent = readdir (dh)) != NULL)
260                 {
261                         if (ent->d_name[0] == '.')
262                                 continue;
263
264                         len = snprintf (filename, BUFSIZE, "/proc/acpi/battery/%s/state", ent->d_name);
265                         if ((len >= BUFSIZE) || (len < 0))
266                                 continue;
267
268                         if ((fh = fopen (filename, "r")) == NULL)
269                         {
270                                 syslog (LOG_ERR, "Cannot open `%s': %s", filename, strerror (errno));
271                                 continue;
272                         }
273
274                         /*
275                          * [11:00] <@tokkee> $ cat /proc/acpi/battery/BAT1/state
276                          * [11:00] <@tokkee> present:                 yes
277                          * [11:00] <@tokkee> capacity state:          ok
278                          * [11:00] <@tokkee> charging state:          charging
279                          * [11:00] <@tokkee> present rate:            1724 mA
280                          * [11:00] <@tokkee> remaining capacity:      4136 mAh
281                          * [11:00] <@tokkee> present voltage:         12428 mV
282                          */
283                         while (fgets (buffer, BUFSIZE, fh) != NULL)
284                         {
285                                 numfields = strsplit (buffer, fields, 8);
286
287                                 if (numfields < 3)
288                                         continue;
289
290                                 if ((strcmp (fields[0], "present") == 0)
291                                                 && (strcmp (fields[1], "rate:") == 0))
292                                         valptr = &current;
293                                 else if ((strcmp (fields[0], "remaining") == 0)
294                                                 && (strcmp (fields[1], "capacity:") == 0))
295                                         valptr = &charge;
296                                 else if ((strcmp (fields[0], "present") == 0)
297                                                 && (strcmp (fields[1], "voltage:") == 0))
298                                         valptr = &voltage;
299                                 else
300                                         valptr = NULL;
301
302                                 if ((strcmp (fields[0], "charging") == 0)
303                                                 && (strcmp (fields[1], "state:") == 0))
304                                 {
305                                         if (strcmp (fields[2], "charging") == 0)
306                                                 charging = 1;
307                                         else
308                                                 charging = 0;
309                                 }
310
311                                 if (valptr != NULL)
312                                 {
313                                         char *endptr;
314
315                                         endptr = NULL;
316                                         errno  = 0;
317
318                                         *valptr = strtod (fields[2], &endptr) / 1000.0;
319
320                                         if ((fields[2] == endptr) || (errno != 0))
321                                                 *valptr = INVALID_VALUE;
322                                 }
323                         }
324
325                         if ((current != INVALID_VALUE) && (charging == 0))
326                                         current *= -1;
327
328                         if ((current != INVALID_VALUE)
329                                         || (voltage != INVALID_VALUE)
330                                         || (charge  != INVALID_VALUE))
331                                 battery_submit (ent->d_name, current, voltage, charge);
332
333                         fclose (fh);
334                 }
335
336                 closedir (dh);
337         }
338 #endif /* KERNEL_LINUX */
339 }
340 #else
341 # define battery_read NULL
342 #endif /* BATTERY_HAVE_READ */
343
344 void module_register (void)
345 {
346         plugin_register (MODULE_NAME, battery_init, battery_read, NULL);
347         plugin_register ("battery_current", NULL, NULL, battery_current_write);
348         plugin_register ("battery_voltage", NULL, NULL, battery_voltage_write);
349         plugin_register ("battery_charge",  NULL, NULL, battery_charge_write);
350 }
351
352 #undef BUFSIZE
353 #undef MODULE_NAME