Let plugin_dispatch_values() set value_list.time in case of 'now'.
[collectd.git] / src / thermal.c
1 /**
2  * collectd - src/thermal.c
3  * Copyright (C) 2008  Michał Mirosław
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; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Michał Mirosław <mirq-linux at rere.qmqm.pl>
20  **/
21
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
25 #include "configfile.h"
26 #include "utils_ignorelist.h"
27
28 #if !KERNEL_LINUX
29 # error "This module is for Linux only."
30 #endif
31
32 const char *const dirname_sysfs = "/sys/class/thermal";
33 const char *const dirname_procfs = "/proc/acpi/thermal_zone";
34
35 static char force_procfs = 0;
36 static ignorelist_t *device_list;
37 static value_list_t vl_temp_template = VALUE_LIST_STATIC;
38 static value_list_t vl_state_template = VALUE_LIST_STATIC;
39
40 enum dev_type {
41         TEMP = 0,
42         COOLING_DEV
43 };
44
45 static void thermal_submit (const char *plugin_instance, enum dev_type dt,
46                 gauge_t value)
47 {
48         value_list_t vl = (dt == TEMP) ? vl_temp_template : vl_state_template;
49         value_t vt;
50
51         vt.gauge = value;
52
53         vl.values = &vt;
54         sstrncpy (vl.plugin, "thermal", sizeof(vl.plugin));
55         sstrncpy (vl.plugin_instance, plugin_instance,
56                         sizeof(vl.plugin_instance));
57         sstrncpy (vl.type, (dt == TEMP) ? "temperature" : "gauge",
58                         sizeof (vl.type));
59
60         plugin_dispatch_values (&vl);
61 }
62
63 static int thermal_sysfs_device_read (const char *dir, const char *name,
64                 void *user_data)
65 {
66         char filename[256];
67         char data[1024];
68         int len;
69         int ok = 0;
70
71         if (device_list && ignorelist_match (device_list, name))
72                 return -1;
73
74         len = snprintf (filename, sizeof (filename), "%s/%s/temp", dirname_sysfs, name);
75         if ((len < 0) || ((unsigned int)len >= sizeof (filename)))
76                 return -1;
77
78         len = read_file_contents (filename, data, sizeof(data));
79         if (len > 1 && data[--len] == '\n') {
80                 char *endptr = NULL;
81                 double temp;
82
83                 data[len] = 0;
84                 errno = 0;
85                 temp = strtod (data, &endptr) / 1000.0;
86
87                 if (endptr == data + len && errno == 0) {
88                         thermal_submit(name, TEMP, temp);
89                         ++ok;
90                 }
91         }
92
93         len = snprintf (filename, sizeof (filename), "%s/%s/cur_state", dirname_sysfs, name);
94         if ((len < 0) || ((unsigned int)len >= sizeof (filename)))
95                 return -1;
96
97         len = read_file_contents (filename, data, sizeof(data));
98         if (len > 1 && data[--len] == '\n') {
99                 char *endptr = NULL;
100                 double state;
101
102                 data[len] = 0;
103                 errno = 0;
104                 state = strtod (data, &endptr);
105
106                 if (endptr == data + len && errno == 0) {
107                         thermal_submit(name, COOLING_DEV, state);
108                         ++ok;
109                 }
110         }
111
112         return ok ? 0 : -1;
113 }
114
115 static int thermal_procfs_device_read (const char *dir, const char *name,
116                 void *user_data)
117 {
118         const char str_temp[] = "temperature:";
119         char filename[256];
120         char data[1024];
121         int len;
122
123         if (device_list && ignorelist_match (device_list, name))
124                 return -1;
125
126         /**
127          * rechot ~ # cat /proc/acpi/thermal_zone/THRM/temperature
128          * temperature:             55 C
129          */
130         
131         len = snprintf (filename, sizeof (filename), "%s/%s/temperature", dirname_procfs, name);
132         if ((len < 0) || ((unsigned int)len >= sizeof (filename)))
133                 return -1;
134
135         len = read_file_contents (filename, data, sizeof(data));
136         if (len > sizeof(str_temp) && data[--len] == '\n' && !strncmp(data, str_temp, sizeof(str_temp)-1)) {
137                 char *endptr = NULL;
138                 double temp;
139                 double celsius, add;
140                 
141                 if (data[--len] == 'C') {
142                         add = 0;
143                         celsius = 1;
144                 } else if (data[len] == 'F') {
145                         add = -32;
146                         celsius = 5/9;
147                 } else if (data[len] == 'K') {
148                         add = -273.15;
149                         celsius = 1;
150                 } else
151                         return -1;
152
153                 while (len > 0 && data[--len] == ' ')
154                         ;
155                 data[len + 1] = 0;
156
157                 while (len > 0 && data[--len] != ' ')
158                         ;
159                 ++len;
160
161                 errno = 0;
162                 temp = (strtod (data + len, &endptr) + add) * celsius;
163
164                 if (endptr != data + len && errno == 0) {
165                         thermal_submit(name, TEMP, temp);
166                         return 0;
167                 }
168         }
169
170         return -1;
171 }
172
173 static const char *config_keys[] = {
174         "Device",
175         "IgnoreSelected",
176         "ForceUseProcfs"
177 };
178
179 static int thermal_config (const char *key, const char *value)
180 {
181         if (device_list == NULL)
182                 device_list = ignorelist_create (1);
183
184         if (strcasecmp (key, "Device") == 0)
185         {
186                 if (ignorelist_add (device_list, value))
187                 {
188                         ERROR ("thermal plugin: "
189                                         "Cannot add value to ignorelist.");
190                         return 1;
191                 }
192         }
193         else if (strcasecmp (key, "IgnoreSelected") == 0)
194         {
195                 ignorelist_set_invert (device_list, 1);
196                 if ((strcasecmp (value, "True") == 0)
197                                 || (strcasecmp (value, "Yes") == 0)
198                                 || (strcasecmp (value, "On") == 0))
199                         ignorelist_set_invert (device_list, 0);
200         }
201         else if (strcasecmp (key, "ForceUseProcfs") == 0)
202         {
203                 force_procfs = 0;
204                 if ((strcasecmp (value, "True") == 0)
205                                 || (strcasecmp (value, "Yes") == 0)
206                                 || (strcasecmp (value, "On") == 0))
207                         force_procfs = 1;
208         }
209         else
210         {
211                 return -1;
212         }
213
214         return 0;
215 }
216
217 static int thermal_sysfs_read (void)
218 {
219         return walk_directory (dirname_sysfs, thermal_sysfs_device_read,
220                         /* user_data = */ NULL);
221 }
222
223 static int thermal_procfs_read (void)
224 {
225         return walk_directory (dirname_procfs, thermal_procfs_device_read,
226                         /* user_data = */ NULL);
227 }
228
229 static int thermal_init (void)
230 {
231         int ret = -1;
232
233         if (!force_procfs && access (dirname_sysfs, R_OK | X_OK) == 0) {
234                 ret = plugin_register_read ("thermal", thermal_sysfs_read);
235         } else if (access (dirname_procfs, R_OK | X_OK) == 0) {
236                 ret = plugin_register_read ("thermal", thermal_procfs_read);
237         }
238
239         if (!ret) {
240                 vl_temp_template.values_len = 1;
241                 vl_temp_template.interval = interval_g;
242                 sstrncpy (vl_temp_template.host, hostname_g,
243                         sizeof(vl_temp_template.host));
244                 sstrncpy (vl_temp_template.plugin, "thermal",
245                         sizeof(vl_temp_template.plugin));
246                 sstrncpy (vl_temp_template.type_instance, "temperature",
247                         sizeof(vl_temp_template.type_instance));
248
249                 vl_state_template = vl_temp_template;
250                 sstrncpy (vl_state_template.type_instance, "cooling_state",
251                         sizeof(vl_state_template.type_instance));
252         }
253
254         return ret;
255 }
256
257 static int thermal_shutdown (void)
258 {
259         ignorelist_free (device_list);
260
261         return 0;
262 }
263
264 void module_register (void)
265 {
266         plugin_register_config ("thermal", thermal_config,
267                         config_keys, STATIC_ARRAY_SIZE(config_keys));
268         plugin_register_init ("thermal", thermal_init);
269         plugin_register_shutdown ("thermal", thermal_shutdown);
270 }
271