04a97cc0791e3fc65c0db2880bce28c6535da7b9
[collectd.git] / src / apple_sensors.c
1 /**
2  * collectd - src/apple_sensors.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 #include "utils_debug.h"
27
28 #define MODULE_NAME "apple_sensors"
29
30 #if HAVE_CTYPE_H
31 #  include <ctype.h>
32 #endif
33 #if HAVE_MACH_MACH_TYPES_H
34 #  include <mach/mach_types.h>
35 #endif
36 #if HAVE_MACH_MACH_INIT_H
37 #  include <mach/mach_init.h>
38 #endif
39 #if HAVE_MACH_MACH_ERROR_H
40 #  include <mach/mach_error.h>
41 #endif
42 #if HAVE_COREFOUNDATION_COREFOUNDATION_H
43 #  include <CoreFoundation/CoreFoundation.h>
44 #endif
45 #if HAVE_IOKIT_IOKITLIB_H
46 #  include <IOKit/IOKitLib.h>
47 #endif
48 #if HAVE_IOKIT_IOTYPES_H
49 #  include <IOKit/IOTypes.h>
50 #endif
51
52 #if HAVE_IOKIT_IOKITLIB_H
53 # define IOKIT_HAVE_READ 1
54 #else
55 # define IOKIT_HAVE_READ 0
56 #endif
57
58 #if IOKIT_HAVE_READ
59 static mach_port_t io_master_port;
60 #endif
61
62 static char *temperature_file = "apple_sensors/temperature-%s.rrd";
63 static char *fanspeed_file    = "apple_sensors/fanspeed-%s.rrd";
64 static char *voltage_file     = "apple_sensors/temperature-%s.rrd";
65
66 static char *ds_def[] =
67 {
68         "DS:value:GAUGE:"COLLECTD_HEARTBEAT":U:U",
69         NULL
70 };
71 static int ds_num = 1;
72
73 static void as_init (void)
74 {
75 #if IOKIT_HAVE_READ
76         kern_return_t status;
77         
78         /* FIXME: de-allocate port if it's defined */
79
80         status = IOMasterPort (MACH_PORT_NULL, &io_master_port);
81         if (status != kIOReturnSuccess)
82         {
83                 syslog (LOG_ERR, "IOMasterPort failed: %s",
84                                 mach_error_string (status));
85                 io_master_port = MACH_PORT_NULL;
86                 return;
87         }
88 #endif
89
90         return;
91 }
92
93 static void as_write (char *host, char *inst, char *val, const char *template)
94 {
95         char filename[256];
96         int  status;
97
98         status = snprintf (filename, 256, template, inst);
99         if ((status < 1) || (status >= 256))
100                 return;
101
102         rrd_update_file (host, filename, val, ds_def, ds_num);
103 }
104
105 static void temperature_write (char *host, char *inst, char *val)
106 {
107         as_write (host, inst, val, temperature_file);
108 }
109
110 static void fanspeed_write (char *host, char *inst, char *val)
111 {
112         as_write (host, inst, val, fanspeed_file);
113 }
114
115 static void voltage_write (char *host, char *inst, char *val)
116 {
117         as_write (host, inst, val, voltage_file);
118 }
119
120 #if IOKIT_HAVE_READ
121 static void as_submit (char *type, char *inst, double value)
122 {
123         char buf[128];
124
125         if (snprintf (buf, 1024, "%u:%f", (unsigned int) curtime,
126                                 value) >= 128)
127                 return;
128
129         plugin_submit (type, inst, buf);
130 }
131
132 static void as_read (void)
133 {
134         kern_return_t   status;
135         io_iterator_t   iterator;
136         io_object_t     io_obj;
137         CFMutableDictionaryRef prop_dict;
138         CFTypeRef       property;
139
140         char   type[128];
141         char   inst[128];
142         int    value_int;
143         double value_double;
144         int    i;
145
146         if (!io_master_port || (io_master_port == MACH_PORT_NULL))
147                 return;
148
149         status = IOServiceGetMatchingServices (io_master_port,
150                         IOServiceNameMatching("IOHWSensor"),
151                         &iterator);
152         if (status != kIOReturnSuccess)
153         {
154                 syslog (LOG_ERR, "IOServiceGetMatchingServices failed: %s",
155                                 mach_error_string (status));
156                 return;
157         }
158
159         while ((io_obj = IOIteratorNext (iterator)))
160         {
161                 prop_dict = NULL;
162                 status = IORegistryEntryCreateCFProperties (io_obj,
163                                 &prop_dict,
164                                 kCFAllocatorDefault,
165                                 kNilOptions);
166                 if (status != kIOReturnSuccess)
167                 {
168                         DBG ("IORegistryEntryCreateCFProperties failed: %s",
169                                         mach_error_string (status));
170                         continue;
171                 }
172
173                 /* Copy the sensor type. */
174                 property = NULL;
175                 if (!CFDictionaryGetValueIfPresent (prop_dict,
176                                         CFSTR ("type"),
177                                         &property))
178                         continue;
179                 if (CFGetTypeID (property) != CFStringGetTypeID ())
180                         continue;
181                 if (!CFStringGetCString (property,
182                                         type, 128,
183                                         kCFStringEncodingASCII))
184                         continue;
185                 type[127] = '\0';
186
187                 /* Copy the sensor location. This will be used as `instance'. */
188                 property = NULL;
189                 if (!CFDictionaryGetValueIfPresent (prop_dict,
190                                         CFSTR ("location"),
191                                         &property))
192                         continue;
193                 if (CFGetTypeID (property) != CFStringGetTypeID ())
194                         continue;
195                 if (!CFStringGetCString (property,
196                                         inst, 128,
197                                         kCFStringEncodingASCII))
198                         continue;
199                 inst[127] = '\0';
200                 for (i = 0; i < 128; i++)
201                 {
202                         if (inst[i] == '\0')
203                                 break;
204                         else if (isalnum (inst[i]))
205                                 inst[i] = (char) tolower (inst[i]);
206                         else
207                                 inst[i] = '_';
208                 }
209
210                 /* Get the actual value. Some computation, based on the `type'
211                  * is neccessary. */
212                 property = NULL;
213                 if (!CFDictionaryGetValueIfPresent (prop_dict,
214                                         CFSTR ("current-value"),
215                                         &property))
216                         continue;
217                 if (CFGetTypeID (property) != CFNumberGetTypeID ())
218                         continue;
219                 if (!CFNumberGetValue (property,
220                                         kCFNumberIntType,
221                                         &value_int))
222                         continue;
223
224                 if (strcmp (type, "temperature") == 0)
225                 {
226                         value_double = ((double) value_int) / 65536.0;
227                         strncpy (type, "apple_temperature", 128);
228                 }
229                 else if (strcmp (type, "fanspeed") == 0)
230                 {
231                         value_double = ((double) value_int) / 65536.0;
232                         strncpy (type, "apple_fanspeed", 128);
233                 }
234                 else if (strcmp (type, "voltage") == 0)
235                 {
236                         value_double = ((double) value_int) / 65536.0;
237                         strncpy (type, "apple_voltage", 128);
238                 }
239                 else
240                 {
241                         DBG ("apple_sensors: Read unknown sensor type: %s",
242                                         type);
243                         value_double = (double) value_int;
244                 }
245
246                 as_submit (type, inst, value_double);
247
248                 CFRelease (prop_dict);
249                 IOObjectRelease (io_obj);
250         } /* while (iterator) */
251
252         IOObjectRelease (iterator);
253 }
254 #else
255 # define as_read NULL
256 #endif /* IOKIT_HAVE_READ */
257
258 void module_register (void)
259 {
260         plugin_register (MODULE_NAME, as_init, as_read, NULL);
261         plugin_register ("apple_temperature", NULL, NULL, temperature_write);
262         plugin_register ("apple_fanspeed",    NULL, NULL, fanspeed_write);
263         plugin_register ("apple_voltage",     NULL, NULL, voltage_write);
264 }
265
266 #undef MODULE_NAME