18 struct owparser_state {
19 double prev_delta[20];
23 static struct owparser_state *allocate_parser_state(const char **datastr)
28 * Count how many sensor entries we need. First entry belongs
29 * to server address or mount point and last one is NULL. So
30 * the index final is the count of actual valid sensor
33 for (i = 0; datastr[i]; i++)
36 return calloc(sizeof(struct owparser_state), i);
39 static double max_glitch_delta(const struct owparser_state *s)
44 for (i = 0; i < ARRAY_SIZE(s->prev_delta); i++)
45 max_delta = max(s->prev_delta[i], max_delta);
50 static int might_be_glitch(double data, const struct owparser_state *s)
52 double max_delta, delta;
54 /* The known bad data value from the sensor */
58 max_delta = max_glitch_delta(s);
60 /* Probably no enough data yet, so no glitch detection */
65 * Simple glitch detection. If delta to previous value is more
66 * than twice as larger as any of the older delta, we might
69 delta = fabs(data - s->prev_data);
71 return delta > max_delta * 2;
74 static void update_glitch_data(double data, struct owparser_state *s)
79 for (i = 1; i < ARRAY_SIZE(s->prev_delta); i++) {
80 s->prev_delta[i - 1] = s->prev_delta[i];
81 max_delta = max(s->prev_delta[i], max_delta);
84 /* Avoid storing the first incorrect delta value */
85 if (s->prev_data || max_delta)
86 s->prev_delta[--i] = fabs(data - s->prev_data);
91 static int parse_opts(const char *str, char *ow_path, size_t pathlen,
95 const char *start_str = str;
96 const char offset_str[] = "offset=";
102 * Skip the onewire path entry. Options begin after the first
109 /* Copy the onewire path without options */
110 strncpy(ow_path, start_str, pathlen);
111 ow_path[str - start_str] = '\0';
113 /* Get the next non-space, which is where the argument begins */
118 if (strncmp(str, offset_str, sizeof(offset_str) - 1))
120 str += sizeof(offset_str) - 1;
122 *offset = strtod(str, &endptr);
130 static int make_uncached(char *path, size_t len)
133 char p1[1024], p2[1024], *p = path;
135 if (strstr(path, "/uncached/"))
138 p1[sizeof(p1) - 1] = '\0';
139 p2[sizeof(p2) - 1] = '\0';
142 * Naively assume the "uncached" string can be put after the
145 while (*p && *p != '/')
154 strncpy(p1, path, sizeof(p1) - 1);
155 strncpy(p2, p, sizeof(p2) - 1);
156 ret = snprintf(path, len, "%s/uncached/%s", p1, p2);
158 /* No actual data overflow, snprintf just couldn't fit all data in the buffer */
159 if (ret >= RRD_DATA_MAX_LEN)
160 pr_err("Buffer overlfow\n");
165 static int owfs_read(const char *mount_point, const char *path, char **res)
171 snprintf(file, sizeof(file), "%s/%s", mount_point, path);
173 fd = open(file, O_RDONLY | O_CLOEXEC);
175 pr_err("Failed to open file %s: %m\n", file);
179 ret = read(fd, result, sizeof(result));
181 pr_err("Failed to read from file %s: %m\n", file);
185 *res = strndup(result, sizeof(result));
193 static void enable_simultaneous_reading(const char *mountpoint)
195 static time_t last_simultaneous;
196 static struct mutex lock = {
197 .lock = PTHREAD_MUTEX_INITIALIZER,
200 time_t now = time(0);
207 /* Arbitrary 10 second limit between simultaneous reads */
208 if (now < last_simultaneous + 10) {
213 last_simultaneous = now;
217 * We only protect setting the variable. From now on we have
218 * 10 seconds time until we could race writing multiple times
219 * to this file. If that happens, well, can't help it..
222 strncpy(path, mountpoint, sizeof(path) - 1);
223 strncat(path, "/simultaneous/temperature", sizeof(path) - 1);
224 path[sizeof(path) - 1 ] = '\0';
226 fd = open(path, O_WRONLY);
228 pr_err("Failed to open %s for writing: %m\n", path);
232 ret = write(fd, &one, 1);
234 pr_warn("Failed to write to %s: %m\n", path);
239 static int is_mount_point(const char *str)
242 * Filesystem paths begin with a slash, everything else must
243 * be a network addresses
251 static int onewire_parser(char *rrd_data, const char **parser_data, void **s)
254 const char *server_addr, *mount_point;
255 struct owparser_state *state = *s;
258 int max_str = RRD_DATA_MAX_LEN;
259 int is_mountpoint = is_mount_point(parser_data[0]);
262 pr_err("No parser data available\n");
267 *s = state = allocate_parser_state(parser_data);
270 mount_point = parser_data[0];
273 pr_err("Server address not specified\n");
277 server_addr = parser_data[0];
280 pr_err("Server address not specified\n");
284 h = OWNET_init(server_addr);
286 pr_err("Failed to connect to server %s\n", server_addr);
291 while (parser_data[i]) {
292 double offset = 0, data, prev_data = 85;
298 if (!strcmp("U", parser_data[i])) {
300 ret = snprintf(rrd_data, max_str, "U");
306 parse_opts(parser_data[i], ow_path, sizeof(ow_path), &offset);
309 enable_simultaneous_reading(mount_point);
316 pr_info("Reading data for entry %s with offset of %.2f\n",
320 ret = owfs_read(mount_point, ow_path, &tmp);
322 ret = OWNET_read(h, ow_path, &tmp);
324 /* Skip leading white space */
326 for (j = 0; j < ret && *tmp2 == ' '; j++)
333 * Older versions of OWNET_read did not NULL
336 memcpy(buf, tmp, min(ret, sizeof(buf) -1));
339 data = strtod(buf, &endptr);
345 * If we read the almost same value as
346 * previously, it's not a glitch
348 if (glitches && prev_data != 85) {
349 double d = max_glitch_delta(&state[i]);
351 if (fabs(data - prev_data) <= d * 2)
355 if (might_be_glitch(data, &state[i]) &&
356 glitches < 4 && retries < 7) {
359 pr_info("Retrying due to a glitch: %f\n", data);
366 * In case of failure, retry with uncached
367 * data. This is likely to help as it forces a
368 * retry even if the sensor is missing from
369 * the cache. We treat "85" also as a failure,
370 * as temp sensors some times report 85 under
373 ret = make_uncached(ow_path, sizeof(ow_path));
374 if (retries >= 10 || ret < 0) {
375 pr_err("Failed to read entry %s: %m\n",
384 update_glitch_data(data, &state[i]);
387 pr_err("Failed to parse data %s\n", buf);
393 ret = snprintf(rrd_data, max_str, "%f", data);
402 ret = snprintf(rrd_data, max_str, ":");
414 static struct parser_info onewire_parser_info = {
416 .parse = onewire_parser,
419 static int init_onewire_parser(void)
421 return register_parser(&onewire_parser_info);
424 struct plugin_info plugin_info = {
425 .name = "onewire_parser",
426 .init = init_onewire_parser,
427 .version = RRDD_VERSION,