2 * collectd - src/onewire.c
3 * Copyright (C) 2008 noris network AG
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.
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.
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
19 * Florian octo Forster <octo at noris.net>
26 #include "utils_ignorelist.h"
31 #include <sys/types.h>
33 #define OW_FAMILY_LENGTH 8
34 #define OW_FAMILY_MAX_FEATURES 2
35 struct ow_family_features_s {
36 char family[OW_FAMILY_LENGTH];
38 char filename[DATA_MAX_NAME_LEN];
39 char type[DATA_MAX_NAME_LEN];
40 char type_instance[DATA_MAX_NAME_LEN];
41 } features[OW_FAMILY_MAX_FEATURES];
44 typedef struct ow_family_features_s ow_family_features_t;
46 /* internal timing info collected in debug version only */
48 static struct timeval tv_begin, tv_end, tv_diff;
49 #endif /* COLLECT_DEBUG */
51 /* regexp to extract address (without family) and file from the owfs path */
52 static const char *regexp_to_match =
53 "[A-Fa-f0-9]{2}\\.([A-Fa-f0-9]{12})/([[:alnum:]]+)$";
55 /* see http://owfs.sourceforge.net/ow_table.html for a list of families */
56 static ow_family_features_t ow_family_features[] = {
57 {/* DS18S20 Precision Thermometer and DS1920 ibutton */
59 {{/* filename = */ "temperature",
60 /* type = */ "temperature",
61 /* type_instance = */ ""}},
62 /* features_num = */ 1},
63 {/* DS1822 Econo Thermometer */
65 {{/* filename = */ "temperature",
66 /* type = */ "temperature",
67 /* type_instance = */ ""}},
68 /* features_num = */ 1},
69 {/* DS18B20 Programmable Resolution Thermometer */
71 {{/* filename = */ "temperature",
72 /* type = */ "temperature",
73 /* type_instance = */ ""}},
74 /* features_num = */ 1},
75 {/* DS2436 Volts/Temp */
77 {{/* filename = */ "temperature",
78 /* type = */ "temperature",
79 /* type_instance = */ ""}},
80 /* features_num = */ 1},
81 {/* DS2438 Volts/Temp */
83 {{/* filename = */ "temperature",
84 /* type = */ "temperature",
85 /* type_instance = */ ""}},
86 /* features_num = */ 1}};
87 static int ow_family_features_num = STATIC_ARRAY_SIZE(ow_family_features);
89 static char *device_g = NULL;
90 static cdtime_t ow_interval = 0;
91 static _Bool direct_access = 0;
93 static const char *config_keys[] = {"Device", "IgnoreSelected", "Sensor",
95 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
97 static ignorelist_t *sensor_list;
99 static _Bool regex_direct_initialized = 0;
100 static regex_t regex_direct;
103 * List of onewire owfs "files" to be directly read
105 typedef struct direct_access_element_s {
106 char *path; /**< The whole owfs path */
107 char *address; /**< 1-wire address without family */
108 char *file; /**< owfs file - e.g. temperature */
109 struct direct_access_element_s *next; /**< Next in the list */
110 } direct_access_element_t;
112 static direct_access_element_t *direct_list = NULL;
114 /* ===================================================================================
118 /* Return 1 if the difference is negative, otherwise 0. */
119 static int timeval_subtract(struct timeval *result, struct timeval *t2,
120 struct timeval *t1) {
121 long int diff = (t2->tv_usec + 1000000 * t2->tv_sec) -
122 (t1->tv_usec + 1000000 * t1->tv_sec);
123 result->tv_sec = diff / 1000000;
124 result->tv_usec = diff % 1000000;
128 #endif /* COLLECT_DEBUG */
130 /* ===================================================================================
133 static void direct_list_element_free(direct_access_element_t *el) {
135 DEBUG("onewire plugin: direct_list_element_free - deleting <%s>", el->path);
143 static int direct_list_insert(const char *config) {
144 regmatch_t pmatch[3];
146 direct_access_element_t *element;
148 DEBUG("onewire plugin: direct_list_insert <%s>", config);
150 element = malloc(sizeof(*element));
151 if (element == NULL) {
152 ERROR("onewire plugin: direct_list_insert - cannot allocate element");
155 element->path = NULL;
156 element->address = NULL;
157 element->file = NULL;
159 element->path = strdup(config);
160 if (element->path == NULL) {
161 ERROR("onewire plugin: direct_list_insert - cannot allocate path");
162 direct_list_element_free(element);
166 DEBUG("onewire plugin: direct_list_insert - about to match %s", config);
168 if (!regex_direct_initialized) {
169 if (regcomp(®ex_direct, regexp_to_match, REG_EXTENDED)) {
170 ERROR("onewire plugin: Cannot compile regex");
171 direct_list_element_free(element);
174 regex_direct_initialized = 1;
175 DEBUG("onewire plugin: Compiled regex!!");
178 if (regexec(®ex_direct, config, nmatch, pmatch, 0)) {
179 ERROR("onewire plugin: direct_list_insert - no regex match");
180 direct_list_element_free(element);
184 if (pmatch[1].rm_so < 0) {
185 ERROR("onewire plugin: direct_list_insert - no address regex match");
186 direct_list_element_free(element);
190 strndup(config + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so);
191 if (element->address == NULL) {
192 ERROR("onewire plugin: direct_list_insert - cannot allocate address");
193 direct_list_element_free(element);
196 DEBUG("onewire plugin: direct_list_insert - found address <%s>",
199 if (pmatch[2].rm_so < 0) {
200 ERROR("onewire plugin: direct_list_insert - no file regex match");
201 direct_list_element_free(element);
205 strndup(config + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so);
206 if (element->file == NULL) {
207 ERROR("onewire plugin: direct_list_insert - cannot allocate file");
208 direct_list_element_free(element);
211 DEBUG("onewire plugin: direct_list_insert - found file <%s>", element->file);
213 element->next = direct_list;
214 direct_list = element;
219 static void direct_list_free(void) {
220 direct_access_element_t *traverse = direct_list;
221 direct_access_element_t *tmp = NULL;
224 while (traverse != NULL) {
226 traverse = traverse->next;
227 direct_list_element_free(tmp);
232 /* ===================================================================================
235 static int cow_load_config(const char *key, const char *value) {
236 if (sensor_list == NULL)
237 sensor_list = ignorelist_create(1);
239 if (strcasecmp(key, "Sensor") == 0) {
240 if (direct_list_insert(value)) {
241 DEBUG("onewire plugin: Cannot add %s to direct_list_insert.", value);
243 if (ignorelist_add(sensor_list, value)) {
244 ERROR("onewire plugin: Cannot add value to ignorelist.");
248 DEBUG("onewire plugin: %s is a direct access", value);
251 } else if (strcasecmp(key, "IgnoreSelected") == 0) {
252 ignorelist_set_invert(sensor_list, 1);
254 ignorelist_set_invert(sensor_list, 0);
255 } else if (strcasecmp(key, "Device") == 0) {
257 temp = strdup(value);
259 ERROR("onewire plugin: strdup failed.");
264 } else if (strcasecmp("Interval", key) == 0) {
268 ow_interval = DOUBLE_TO_CDTIME_T(tmp);
270 ERROR("onewire plugin: Invalid `Interval' setting: %s", value);
278 static int cow_read_values(const char *path, const char *name,
279 const ow_family_features_t *family_info) {
280 value_list_t vl = VALUE_LIST_INIT;
283 if (sensor_list != NULL) {
284 DEBUG("onewire plugin: Checking ignorelist for `%s'", name);
285 if (ignorelist_match(sensor_list, name) != 0)
289 sstrncpy(vl.plugin, "onewire", sizeof(vl.plugin));
290 sstrncpy(vl.plugin_instance, name, sizeof(vl.plugin_instance));
292 for (size_t i = 0; i < family_info->features_num; i++) {
301 snprintf(file, sizeof(file), "%s/%s", path,
302 family_info->features[i].filename);
303 file[sizeof(file) - 1] = 0;
307 DEBUG("Start reading onewire device %s", file);
308 status = OW_get(file, &buffer, &buffer_size);
310 ERROR("onewire plugin: OW_get (%s/%s) failed. error = %s;", path,
311 family_info->features[i].filename,
312 sstrerror(errno, errbuf, sizeof(errbuf)));
315 DEBUG("Read onewire device %s as %s", file, buffer);
318 gauge_t g = strtod(buffer, &endptr);
319 if (endptr == NULL) {
320 ERROR("onewire plugin: Buffer is not a number: %s", buffer);
324 sstrncpy(vl.type, family_info->features[i].type, sizeof(vl.type));
325 sstrncpy(vl.type_instance, family_info->features[i].type_instance,
326 sizeof(vl.type_instance));
328 vl.values = &(value_t){.gauge = g};
331 plugin_dispatch_values(&vl);
335 } /* for (i = 0; i < features_num; i++) */
337 return (success > 0) ? 0 : -1;
338 } /* int cow_read_values */
340 /* Forward declaration so the recursion below works */
341 static int cow_read_bus(const char *path);
347 * - DS2409 - MicroLAN Coupler
349 static int cow_read_ds2409(const char *path) {
353 status = snprintf(subpath, sizeof(subpath), "%s/main", path);
354 if ((status > 0) && (status < (int)sizeof(subpath)))
355 cow_read_bus(subpath);
357 status = snprintf(subpath, sizeof(subpath), "%s/aux", path);
358 if ((status > 0) && (status < (int)sizeof(subpath)))
359 cow_read_bus(subpath);
362 } /* int cow_read_ds2409 */
364 static int cow_read_bus(const char *path) {
375 status = OW_get(path, &buffer, &buffer_size);
377 ERROR("onewire plugin: OW_get (%s) failed. error = %s;", path,
378 sstrerror(errno, errbuf, sizeof(errbuf)));
381 DEBUG("onewire plugin: OW_get (%s) returned: %s", path, buffer);
385 while ((buffer_ptr = strtok_r(dummy, ",/", &saveptr)) != NULL) {
390 if (strcmp("/", path) == 0)
391 status = snprintf(subpath, sizeof(subpath), "/%s", buffer_ptr);
393 status = snprintf(subpath, sizeof(subpath), "%s/%s", path, buffer_ptr);
394 if ((status <= 0) || (status >= (int)sizeof(subpath)))
397 for (i = 0; i < ow_family_features_num; i++) {
398 if (strncmp(ow_family_features[i].family, buffer_ptr,
399 strlen(ow_family_features[i].family)) != 0)
402 cow_read_values(subpath,
403 buffer_ptr + strlen(ow_family_features[i].family),
404 ow_family_features + i);
407 if (i < ow_family_features_num)
411 if (strncmp("1F.", buffer_ptr, strlen("1F.")) == 0) {
412 cow_read_ds2409(subpath);
415 } /* while (strtok_r) */
419 } /* int cow_read_bus */
421 /* ===================================================================================
424 static int cow_simple_read(void) {
425 value_list_t vl = VALUE_LIST_INIT;
431 direct_access_element_t *traverse;
433 /* traverse list and check entries */
434 for (traverse = direct_list; traverse != NULL; traverse = traverse->next) {
435 sstrncpy(vl.plugin, "onewire", sizeof(vl.plugin));
436 sstrncpy(vl.plugin_instance, traverse->address, sizeof(vl.plugin_instance));
438 status = OW_get(traverse->path, &buffer, &buffer_size);
440 ERROR("onewire plugin: OW_get (%s) failed. status = %s;", traverse->path,
441 sstrerror(errno, errbuf, sizeof(errbuf)));
444 DEBUG("onewire plugin: Read onewire device %s as %s", traverse->path,
448 gauge_t g = strtod(buffer, &endptr);
449 if (endptr == NULL) {
450 ERROR("onewire plugin: Buffer is not a number: %s", buffer);
454 sstrncpy(vl.type, traverse->file, sizeof(vl.type));
455 sstrncpy(vl.type_instance, "", sizeof(""));
457 vl.values = &(value_t){.gauge = g};
460 plugin_dispatch_values(&vl);
462 } /* for (traverse) */
465 } /* int cow_simple_read */
467 /* ===================================================================================
470 static int cow_read(user_data_t *ud __attribute__((unused))) {
474 gettimeofday(&tv_begin, NULL);
475 #endif /* COLLECT_DEBUG */
478 DEBUG("onewire plugin: Direct access read");
479 result = cow_simple_read();
481 DEBUG("onewire plugin: Standard access read");
482 result = cow_read_bus("/");
486 gettimeofday(&tv_end, NULL);
487 timeval_subtract(&tv_diff, &tv_end, &tv_begin);
488 DEBUG("onewire plugin: Onewire read took us %ld.%06ld s", tv_diff.tv_sec,
490 #endif /* COLLECT_DEBUG */
495 static int cow_shutdown(void) {
497 ignorelist_free(sensor_list);
501 if (regex_direct_initialized) {
502 regfree(®ex_direct);
506 } /* int cow_shutdown */
508 static int cow_init(void) {
512 if (device_g == NULL) {
513 ERROR("onewire plugin: cow_init: No device configured.");
517 DEBUG("onewire plugin: about to init device <%s>.", device_g);
518 status = (int)OW_init(device_g);
520 ERROR("onewire plugin: OW_init(%s) failed: %s.", device_g,
521 sstrerror(errno, errbuf, sizeof(errbuf)));
525 plugin_register_complex_read(/* group = */ NULL, "onewire", cow_read,
526 ow_interval, /* user data = */ NULL);
527 plugin_register_shutdown("onewire", cow_shutdown);
532 void module_register(void) {
533 plugin_register_init("onewire", cow_init);
534 plugin_register_config("onewire", cow_load_config, config_keys,