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