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