PageRenderTime 44ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/src/onewire.c

http://github.com/octo/collectd
C | 529 lines | 394 code | 89 blank | 46 comment | 75 complexity | a93729073ffd220242b0fee72af9cac4 MD5 | raw file
Possible License(s): GPL-2.0
  1. /**
  2. * collectd - src/onewire.c
  3. * Copyright (C) 2008 noris network AG
  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. * Florian octo Forster <octo at noris.net>
  20. **/
  21. #include "collectd.h"
  22. #include "plugin.h"
  23. #include "utils/common/common.h"
  24. #include "utils/ignorelist/ignorelist.h"
  25. #include <owcapi.h>
  26. #include <regex.h>
  27. #include <sys/time.h>
  28. #include <sys/types.h>
  29. #define OW_FAMILY_LENGTH 8
  30. #define OW_FAMILY_MAX_FEATURES 2
  31. struct ow_family_features_s {
  32. char family[OW_FAMILY_LENGTH];
  33. struct {
  34. char filename[DATA_MAX_NAME_LEN];
  35. char type[DATA_MAX_NAME_LEN];
  36. char type_instance[DATA_MAX_NAME_LEN];
  37. } features[OW_FAMILY_MAX_FEATURES];
  38. size_t features_num;
  39. };
  40. typedef struct ow_family_features_s ow_family_features_t;
  41. /* internal timing info collected in debug version only */
  42. #if COLLECT_DEBUG
  43. static struct timeval tv_begin, tv_end, tv_diff;
  44. #endif /* COLLECT_DEBUG */
  45. /* regexp to extract address (without family) and file from the owfs path */
  46. static const char *regexp_to_match =
  47. "[A-Fa-f0-9]{2}\\.([A-Fa-f0-9]{12})/([[:alnum:]]+)$";
  48. /* see http://owfs.sourceforge.net/ow_table.html for a list of families */
  49. static ow_family_features_t ow_family_features[] = {
  50. {/* DS18S20 Precision Thermometer and DS1920 ibutton */
  51. /* family = */ "10.",
  52. {{/* filename = */ "temperature",
  53. /* type = */ "temperature",
  54. /* type_instance = */ ""}},
  55. /* features_num = */ 1},
  56. {/* DS1822 Econo Thermometer */
  57. /* family = */ "22.",
  58. {{/* filename = */ "temperature",
  59. /* type = */ "temperature",
  60. /* type_instance = */ ""}},
  61. /* features_num = */ 1},
  62. {/* DS18B20 Programmable Resolution Thermometer */
  63. /* family = */ "28.",
  64. {{/* filename = */ "temperature",
  65. /* type = */ "temperature",
  66. /* type_instance = */ ""}},
  67. /* features_num = */ 1},
  68. {/* DS2436 Volts/Temp */
  69. /* family = */ "1B.",
  70. {{/* filename = */ "temperature",
  71. /* type = */ "temperature",
  72. /* type_instance = */ ""}},
  73. /* features_num = */ 1},
  74. {/* DS2438 Volts/Temp */
  75. /* family = */ "26.",
  76. {{/* filename = */ "temperature",
  77. /* type = */ "temperature",
  78. /* type_instance = */ ""}},
  79. /* features_num = */ 1}};
  80. static int ow_family_features_num = STATIC_ARRAY_SIZE(ow_family_features);
  81. static char *device_g;
  82. static cdtime_t ow_interval;
  83. static bool direct_access;
  84. static const char *config_keys[] = {"Device", "IgnoreSelected", "Sensor",
  85. "Interval"};
  86. static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
  87. static ignorelist_t *sensor_list;
  88. static bool regex_direct_initialized;
  89. static regex_t regex_direct;
  90. /**
  91. * List of onewire owfs "files" to be directly read
  92. */
  93. typedef struct direct_access_element_s {
  94. char *path; /**< The whole owfs path */
  95. char *address; /**< 1-wire address without family */
  96. char *file; /**< owfs file - e.g. temperature */
  97. struct direct_access_element_s *next; /**< Next in the list */
  98. } direct_access_element_t;
  99. static direct_access_element_t *direct_list;
  100. /* ===================================================================================
  101. */
  102. #if COLLECT_DEBUG
  103. /* Return 1 if the difference is negative, otherwise 0. */
  104. static int timeval_subtract(struct timeval *result, struct timeval *t2,
  105. struct timeval *t1) {
  106. long int diff = (t2->tv_usec + 1000000 * t2->tv_sec) -
  107. (t1->tv_usec + 1000000 * t1->tv_sec);
  108. result->tv_sec = diff / 1000000;
  109. result->tv_usec = diff % 1000000;
  110. return diff < 0;
  111. }
  112. #endif /* COLLECT_DEBUG */
  113. /* ===================================================================================
  114. */
  115. static void direct_list_element_free(direct_access_element_t *el) {
  116. if (el != NULL) {
  117. DEBUG("onewire plugin: direct_list_element_free - deleting <%s>", el->path);
  118. sfree(el->path);
  119. sfree(el->address);
  120. sfree(el->file);
  121. free(el);
  122. }
  123. }
  124. static int direct_list_insert(const char *config) {
  125. regmatch_t pmatch[3];
  126. size_t nmatch = 3;
  127. direct_access_element_t *element;
  128. DEBUG("onewire plugin: direct_list_insert <%s>", config);
  129. element = malloc(sizeof(*element));
  130. if (element == NULL) {
  131. ERROR("onewire plugin: direct_list_insert - cannot allocate element");
  132. return 1;
  133. }
  134. element->path = NULL;
  135. element->address = NULL;
  136. element->file = NULL;
  137. element->path = strdup(config);
  138. if (element->path == NULL) {
  139. ERROR("onewire plugin: direct_list_insert - cannot allocate path");
  140. direct_list_element_free(element);
  141. return 1;
  142. }
  143. DEBUG("onewire plugin: direct_list_insert - about to match %s", config);
  144. if (!regex_direct_initialized) {
  145. if (regcomp(&regex_direct, regexp_to_match, REG_EXTENDED)) {
  146. ERROR("onewire plugin: Cannot compile regex");
  147. direct_list_element_free(element);
  148. return 1;
  149. }
  150. regex_direct_initialized = true;
  151. DEBUG("onewire plugin: Compiled regex!!");
  152. }
  153. if (regexec(&regex_direct, config, nmatch, pmatch, 0)) {
  154. ERROR("onewire plugin: direct_list_insert - no regex match");
  155. direct_list_element_free(element);
  156. return 1;
  157. }
  158. if (pmatch[1].rm_so < 0) {
  159. ERROR("onewire plugin: direct_list_insert - no address regex match");
  160. direct_list_element_free(element);
  161. return 1;
  162. }
  163. element->address =
  164. strndup(config + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so);
  165. if (element->address == NULL) {
  166. ERROR("onewire plugin: direct_list_insert - cannot allocate address");
  167. direct_list_element_free(element);
  168. return 1;
  169. }
  170. DEBUG("onewire plugin: direct_list_insert - found address <%s>",
  171. element->address);
  172. if (pmatch[2].rm_so < 0) {
  173. ERROR("onewire plugin: direct_list_insert - no file regex match");
  174. direct_list_element_free(element);
  175. return 1;
  176. }
  177. element->file =
  178. strndup(config + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so);
  179. if (element->file == NULL) {
  180. ERROR("onewire plugin: direct_list_insert - cannot allocate file");
  181. direct_list_element_free(element);
  182. return 1;
  183. }
  184. DEBUG("onewire plugin: direct_list_insert - found file <%s>", element->file);
  185. element->next = direct_list;
  186. direct_list = element;
  187. return 0;
  188. }
  189. static void direct_list_free(void) {
  190. direct_access_element_t *traverse = direct_list;
  191. direct_access_element_t *tmp = NULL;
  192. ;
  193. while (traverse != NULL) {
  194. tmp = traverse;
  195. traverse = traverse->next;
  196. direct_list_element_free(tmp);
  197. tmp = NULL;
  198. }
  199. }
  200. /* ===================================================================================
  201. */
  202. static int cow_load_config(const char *key, const char *value) {
  203. if (sensor_list == NULL)
  204. sensor_list = ignorelist_create(1);
  205. if (strcasecmp(key, "Sensor") == 0) {
  206. if (direct_list_insert(value)) {
  207. DEBUG("onewire plugin: Cannot add %s to direct_list_insert.", value);
  208. if (ignorelist_add(sensor_list, value)) {
  209. ERROR("onewire plugin: Cannot add value to ignorelist.");
  210. return 1;
  211. }
  212. } else {
  213. DEBUG("onewire plugin: %s is a direct access", value);
  214. direct_access = true;
  215. }
  216. } else if (strcasecmp(key, "IgnoreSelected") == 0) {
  217. ignorelist_set_invert(sensor_list, 1);
  218. if (IS_TRUE(value))
  219. ignorelist_set_invert(sensor_list, 0);
  220. } else if (strcasecmp(key, "Device") == 0) {
  221. char *temp;
  222. temp = strdup(value);
  223. if (temp == NULL) {
  224. ERROR("onewire plugin: strdup failed.");
  225. return 1;
  226. }
  227. sfree(device_g);
  228. device_g = temp;
  229. } else if (strcasecmp("Interval", key) == 0) {
  230. double tmp;
  231. tmp = atof(value);
  232. if (tmp > 0.0)
  233. ow_interval = DOUBLE_TO_CDTIME_T(tmp);
  234. else
  235. ERROR("onewire plugin: Invalid `Interval' setting: %s", value);
  236. } else {
  237. return -1;
  238. }
  239. return 0;
  240. }
  241. static int cow_read_values(const char *path, const char *name,
  242. const ow_family_features_t *family_info) {
  243. value_list_t vl = VALUE_LIST_INIT;
  244. int success = 0;
  245. if (sensor_list != NULL) {
  246. DEBUG("onewire plugin: Checking ignorelist for `%s'", name);
  247. if (ignorelist_match(sensor_list, name) != 0)
  248. return 0;
  249. }
  250. sstrncpy(vl.plugin, "onewire", sizeof(vl.plugin));
  251. sstrncpy(vl.plugin_instance, name, sizeof(vl.plugin_instance));
  252. for (size_t i = 0; i < family_info->features_num; i++) {
  253. char *buffer;
  254. size_t buffer_size;
  255. int status;
  256. char file[4096];
  257. char *endptr;
  258. ssnprintf(file, sizeof(file), "%s/%s", path,
  259. family_info->features[i].filename);
  260. file[sizeof(file) - 1] = '\0';
  261. buffer = NULL;
  262. buffer_size = 0;
  263. DEBUG("Start reading onewire device %s", file);
  264. status = OW_get(file, &buffer, &buffer_size);
  265. if (status < 0) {
  266. ERROR("onewire plugin: OW_get (%s/%s) failed. error = %s;", path,
  267. family_info->features[i].filename, STRERRNO);
  268. return -1;
  269. }
  270. DEBUG("Read onewire device %s as %s", file, buffer);
  271. endptr = NULL;
  272. gauge_t g = strtod(buffer, &endptr);
  273. if (endptr == NULL) {
  274. ERROR("onewire plugin: Buffer is not a number: %s", buffer);
  275. continue;
  276. }
  277. sstrncpy(vl.type, family_info->features[i].type, sizeof(vl.type));
  278. sstrncpy(vl.type_instance, family_info->features[i].type_instance,
  279. sizeof(vl.type_instance));
  280. vl.values = &(value_t){.gauge = g};
  281. vl.values_len = 1;
  282. plugin_dispatch_values(&vl);
  283. success++;
  284. free(buffer);
  285. } /* for (i = 0; i < features_num; i++) */
  286. return (success > 0) ? 0 : -1;
  287. } /* int cow_read_values */
  288. /* Forward declaration so the recursion below works */
  289. static int cow_read_bus(const char *path);
  290. /*
  291. * cow_read_ds2409
  292. *
  293. * Handles:
  294. * - DS2409 - MicroLAN Coupler
  295. */
  296. static int cow_read_ds2409(const char *path) {
  297. char subpath[4096];
  298. int status;
  299. status = ssnprintf(subpath, sizeof(subpath), "%s/main", path);
  300. if ((status > 0) && (status < (int)sizeof(subpath)))
  301. cow_read_bus(subpath);
  302. status = ssnprintf(subpath, sizeof(subpath), "%s/aux", path);
  303. if ((status > 0) && (status < (int)sizeof(subpath)))
  304. cow_read_bus(subpath);
  305. return 0;
  306. } /* int cow_read_ds2409 */
  307. static int cow_read_bus(const char *path) {
  308. char *buffer;
  309. size_t buffer_size;
  310. int status;
  311. char *buffer_ptr;
  312. char *dummy;
  313. char *saveptr;
  314. char subpath[4096];
  315. status = OW_get(path, &buffer, &buffer_size);
  316. if (status < 0) {
  317. ERROR("onewire plugin: OW_get (%s) failed. error = %s;", path, STRERRNO);
  318. return -1;
  319. }
  320. DEBUG("onewire plugin: OW_get (%s) returned: %s", path, buffer);
  321. dummy = buffer;
  322. saveptr = NULL;
  323. while ((buffer_ptr = strtok_r(dummy, ",/", &saveptr)) != NULL) {
  324. int i;
  325. dummy = NULL;
  326. if (strcmp("/", path) == 0)
  327. status = ssnprintf(subpath, sizeof(subpath), "/%s", buffer_ptr);
  328. else
  329. status = ssnprintf(subpath, sizeof(subpath), "%s/%s", path, buffer_ptr);
  330. if ((status <= 0) || (status >= (int)sizeof(subpath)))
  331. continue;
  332. for (i = 0; i < ow_family_features_num; i++) {
  333. if (strncmp(ow_family_features[i].family, buffer_ptr,
  334. strlen(ow_family_features[i].family)) != 0)
  335. continue;
  336. cow_read_values(subpath,
  337. buffer_ptr + strlen(ow_family_features[i].family),
  338. ow_family_features + i);
  339. break;
  340. }
  341. if (i < ow_family_features_num)
  342. continue;
  343. /* DS2409 */
  344. if (strncmp("1F.", buffer_ptr, strlen("1F.")) == 0) {
  345. cow_read_ds2409(subpath);
  346. continue;
  347. }
  348. } /* while (strtok_r) */
  349. free(buffer);
  350. return 0;
  351. } /* int cow_read_bus */
  352. /* ===================================================================================
  353. */
  354. static int cow_simple_read(void) {
  355. value_list_t vl = VALUE_LIST_INIT;
  356. char *buffer;
  357. size_t buffer_size;
  358. int status;
  359. char *endptr;
  360. direct_access_element_t *traverse;
  361. /* traverse list and check entries */
  362. for (traverse = direct_list; traverse != NULL; traverse = traverse->next) {
  363. sstrncpy(vl.plugin, "onewire", sizeof(vl.plugin));
  364. sstrncpy(vl.plugin_instance, traverse->address, sizeof(vl.plugin_instance));
  365. status = OW_get(traverse->path, &buffer, &buffer_size);
  366. if (status < 0) {
  367. ERROR("onewire plugin: OW_get (%s) failed. status = %s;", traverse->path,
  368. STRERRNO);
  369. return -1;
  370. }
  371. DEBUG("onewire plugin: Read onewire device %s as %s", traverse->path,
  372. buffer);
  373. endptr = NULL;
  374. gauge_t g = strtod(buffer, &endptr);
  375. if (endptr == NULL) {
  376. ERROR("onewire plugin: Buffer is not a number: %s", buffer);
  377. continue;
  378. }
  379. sstrncpy(vl.type, traverse->file, sizeof(vl.type));
  380. sstrncpy(vl.type_instance, "", sizeof(""));
  381. vl.values = &(value_t){.gauge = g};
  382. vl.values_len = 1;
  383. plugin_dispatch_values(&vl);
  384. free(buffer);
  385. } /* for (traverse) */
  386. return 0;
  387. } /* int cow_simple_read */
  388. /* ===================================================================================
  389. */
  390. static int cow_read(user_data_t *ud __attribute__((unused))) {
  391. int result = 0;
  392. #if COLLECT_DEBUG
  393. gettimeofday(&tv_begin, NULL);
  394. #endif /* COLLECT_DEBUG */
  395. if (direct_access) {
  396. DEBUG("onewire plugin: Direct access read");
  397. result = cow_simple_read();
  398. } else {
  399. DEBUG("onewire plugin: Standard access read");
  400. result = cow_read_bus("/");
  401. }
  402. #if COLLECT_DEBUG
  403. gettimeofday(&tv_end, NULL);
  404. timeval_subtract(&tv_diff, &tv_end, &tv_begin);
  405. DEBUG("onewire plugin: Onewire read took us %ld.%06ld s", tv_diff.tv_sec,
  406. tv_diff.tv_usec);
  407. #endif /* COLLECT_DEBUG */
  408. return result;
  409. } /* int cow_read */
  410. static int cow_shutdown(void) {
  411. OW_finish();
  412. ignorelist_free(sensor_list);
  413. direct_list_free();
  414. if (regex_direct_initialized) {
  415. regfree(&regex_direct);
  416. }
  417. return 0;
  418. } /* int cow_shutdown */
  419. static int cow_init(void) {
  420. int status;
  421. if (device_g == NULL) {
  422. ERROR("onewire plugin: cow_init: No device configured.");
  423. return -1;
  424. }
  425. DEBUG("onewire plugin: about to init device <%s>.", device_g);
  426. status = (int)OW_init(device_g);
  427. if (status != 0) {
  428. ERROR("onewire plugin: OW_init(%s) failed: %s.", device_g, STRERRNO);
  429. return 1;
  430. }
  431. plugin_register_complex_read(/* group = */ NULL, "onewire", cow_read,
  432. ow_interval, /* user data = */ NULL);
  433. plugin_register_shutdown("onewire", cow_shutdown);
  434. return 0;
  435. } /* int cow_init */
  436. void module_register(void) {
  437. plugin_register_init("onewire", cow_init);
  438. plugin_register_config("onewire", cow_load_config, config_keys,
  439. config_keys_num);
  440. }