Merge pull request #1830 from rubenk/move-collectd-header
[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
24 #include "common.h"
25 #include "plugin.h"
26 #include "configfile.h"
27 #include "utils_ignorelist.h"
28
29 #if !KERNEL_LINUX
30 # error "This module is for Linux only."
31 #endif
32
33 static const char *config_keys[] = {
34         "Device",
35         "IgnoreSelected",
36         "ForceUseProcfs"
37 };
38
39 static const char *const dirname_sysfs = "/sys/class/thermal";
40 static const char *const dirname_procfs = "/proc/acpi/thermal_zone";
41
42 static _Bool force_procfs = 0;
43 static ignorelist_t *device_list;
44
45 enum dev_type {
46         TEMP = 0,
47         COOLING_DEV
48 };
49
50 static void thermal_submit (const char *plugin_instance, enum dev_type dt,
51                 gauge_t value)
52 {
53         value_list_t vl = VALUE_LIST_INIT;
54         value_t v;
55
56         v.gauge = value;
57         vl.values = &v;
58
59         vl.values_len = 1;
60
61         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
62         sstrncpy (vl.plugin, "thermal", sizeof(vl.plugin));
63         if (plugin_instance != NULL)
64                 sstrncpy (vl.plugin_instance, plugin_instance,
65                                 sizeof (vl.plugin_instance));
66         sstrncpy (vl.type,
67                         (dt == TEMP) ? "temperature" : "gauge",
68                         sizeof (vl.type));
69
70         plugin_dispatch_values (&vl);
71 }
72
73 static int thermal_sysfs_device_read (const char __attribute__((unused)) *dir,
74                 const char *name, void __attribute__((unused)) *user_data)
75 {
76         char filename[256];
77         char data[1024];
78         int len;
79         _Bool success = 0;
80
81         if (device_list && ignorelist_match (device_list, name))
82                 return -1;
83
84         len = ssnprintf (filename, sizeof (filename),
85                         "%s/%s/temp", dirname_sysfs, name);
86         if ((len < 0) || ((size_t) len >= sizeof (filename)))
87                 return -1;
88
89         len = (ssize_t) read_file_contents (filename, data, sizeof(data));
90         if (len > 1 && data[--len] == '\n') {
91                 char *endptr = NULL;
92                 double temp;
93
94                 data[len] = 0;
95                 errno = 0;
96                 temp = strtod (data, &endptr) / 1000.0;
97
98                 if (endptr == data + len && errno == 0) {
99                         thermal_submit(name, TEMP, temp);
100                         success = 1;
101                 }
102         }
103
104         len = ssnprintf (filename, sizeof (filename),
105                         "%s/%s/cur_state", dirname_sysfs, name);
106         if ((len < 0) || ((size_t) len >= sizeof (filename)))
107                 return -1;
108
109         len = (ssize_t) read_file_contents (filename, data, sizeof(data));
110         if (len > 1 && data[--len] == '\n') {
111                 char *endptr = NULL;
112                 double state;
113
114                 data[len] = 0;
115                 errno = 0;
116                 state = strtod (data, &endptr);
117
118                 if (endptr == data + len && errno == 0) {
119                         thermal_submit(name, COOLING_DEV, state);
120                         success = 1;
121                 }
122         }
123
124         return (success ? 0 : -1);
125 }
126
127 static int thermal_procfs_device_read (const char __attribute__((unused)) *dir,
128                 const char *name, void __attribute__((unused)) *user_data)
129 {
130         const char str_temp[] = "temperature:";
131         char filename[256];
132         char data[1024];
133         int len;
134
135         if (device_list && ignorelist_match (device_list, name))
136                 return -1;
137
138         /**
139          * rechot ~ # cat /proc/acpi/thermal_zone/THRM/temperature
140          * temperature:             55 C
141          */
142         
143         len = ssnprintf (filename, sizeof (filename),
144                         "%s/%s/temperature", dirname_procfs, name);
145         if ((len < 0) || ((size_t) len >= sizeof (filename)))
146                 return -1;
147
148         len = (ssize_t) read_file_contents (filename, data, sizeof(data));
149         if ((len > 0) && ((size_t) len > sizeof(str_temp))
150                         && (data[--len] == '\n')
151                         && (! strncmp(data, str_temp, sizeof(str_temp)-1))) {
152                 char *endptr = NULL;
153                 double temp;
154                 double factor, add;
155                 
156                 if (data[--len] == 'C') {
157                         add = 0;
158                         factor = 1.0;
159                 } else if (data[len] == 'F') {
160                         add = -32;
161                         factor = 5.0/9.0;
162                 } else if (data[len] == 'K') {
163                         add = -273.15;
164                         factor = 1.0;
165                 } else
166                         return -1;
167
168                 while (len > 0 && data[--len] == ' ')
169                         ;
170                 data[len + 1] = 0;
171
172                 while (len > 0 && data[--len] != ' ')
173                         ;
174                 ++len;
175
176                 errno = 0;
177                 temp = (strtod (data + len, &endptr) + add) * factor;
178
179                 if (endptr != data + len && errno == 0) {
180                         thermal_submit(name, TEMP, temp);
181                         return 0;
182                 }
183         }
184
185         return -1;
186 }
187
188 static int thermal_config (const char *key, const char *value)
189 {
190         if (device_list == NULL)
191                 device_list = ignorelist_create (1);
192
193         if (strcasecmp (key, "Device") == 0)
194         {
195                 if (ignorelist_add (device_list, value))
196                 {
197                         ERROR ("thermal plugin: "
198                                         "Cannot add value to ignorelist.");
199                         return 1;
200                 }
201         }
202         else if (strcasecmp (key, "IgnoreSelected") == 0)
203         {
204                 ignorelist_set_invert (device_list, 1);
205                 if (IS_TRUE (value))
206                         ignorelist_set_invert (device_list, 0);
207         }
208         else if (strcasecmp (key, "ForceUseProcfs") == 0)
209         {
210                 force_procfs = 0;
211                 if (IS_TRUE (value))
212                         force_procfs = 1;
213         }
214         else
215         {
216                 return -1;
217         }
218
219         return 0;
220 }
221
222 static int thermal_sysfs_read (void)
223 {
224         return walk_directory (dirname_sysfs, thermal_sysfs_device_read,
225                         /* user_data = */ NULL, /* include hidden */ 0);
226 }
227
228 static int thermal_procfs_read (void)
229 {
230         return walk_directory (dirname_procfs, thermal_procfs_device_read,
231                         /* user_data = */ NULL, /* include hidden */ 0);
232 }
233
234 static int thermal_init (void)
235 {
236         int ret = -1;
237
238         if (!force_procfs && access (dirname_sysfs, R_OK | X_OK) == 0) {
239                 ret = plugin_register_read ("thermal", thermal_sysfs_read);
240         } else if (access (dirname_procfs, R_OK | X_OK) == 0) {
241                 ret = plugin_register_read ("thermal", thermal_procfs_read);
242         }
243
244         return ret;
245 }
246
247 static int thermal_shutdown (void)
248 {
249         ignorelist_free (device_list);
250
251         return 0;
252 }
253
254 void module_register (void)
255 {
256         plugin_register_config ("thermal", thermal_config,
257                         config_keys, STATIC_ARRAY_SIZE(config_keys));
258         plugin_register_init ("thermal", thermal_init);
259         plugin_register_shutdown ("thermal", thermal_shutdown);
260 }
261