#include #include #include "process.h" #include "parser.h" #include "debug.h" #include "string.h" #include "utils.h" #include "plugin.h" #include "version.h" static struct mutex server_lock = { .name = "server_addr_lock", .lock = PTHREAD_MUTEX_INITIALIZER, }; static int parse_opts(const char *str, char *ow_path, size_t pathlen, double *offset) { char *endptr; const char *start_str = str; const char offset_str[] = "offset="; if (!offset) return 0; /* * Skip the onewire path entry. Options begin after the first * white space */ for (; *str; str++) if (isspace(*str)) break; /* Copy the onewire path without options */ strncpy(ow_path, start_str, pathlen); ow_path[str - start_str] = '\0'; /* Get the next non-space, which is where the argument begins */ for (; *str; str++) if (!isspace(*str)) break; if (strncmp(str, offset_str, sizeof(offset_str) - 1)) return 0; str += sizeof(offset_str) - 1; *offset = strtod(str, &endptr); if (str != endptr) return 1; return 0; } static int make_uncached(char *path, size_t len) { char p1[32], p2[32], *p = path; if (strstr(path, "/uncached/")) return 0; /* * Naively assume the "uncached" string can be put after the * first slash */ while (*p && *p != '/') p++; if (!*p) return -1; *p = 0; p++; strncpy(p1, path, sizeof(p1)); strncpy(p2, p, sizeof(p2)); snprintf(path, len, "%s/uncached/%s", p1, p2); return 0; } static int onewire_parser(char *rrd_data, const char **parser_data) { static OWNET_HANDLE handle; static char *server_addr; char buf[24], *tmp; int i = 1, ret; int max_str = RRD_DATA_MAX_LEN; if (!parser_data) { pr_err("No parser data available\n"); return -1; } if (!parser_data[0]) { pr_err("Server address not specified\n"); return -1; } /* * No point trying to connect to the server more than * once. Also we don't know how thread safe libownet is. One * lock to protect it all. */ mutex_lock(&server_lock); /* * Keep one server connection alive at all times. This * prevents file descriptor leak with older libownet. */ if (!server_addr || handle < 0) { if (server_addr) free(server_addr); server_addr = strdup(parser_data[0]); handle = OWNET_init(server_addr); } else if (strcmp(server_addr, parser_data[0])) { OWNET_close(handle); free(server_addr); server_addr = strdup(parser_data[0]); handle = OWNET_init(server_addr); } if (handle < 0) { pr_err("Failed to connect to server %s\n", server_addr); mutex_unlock(&server_lock); return -1; } while (parser_data[i]) { double offset = 0, data; char *endptr; char ow_path[1024]; int retries = 0; if (!strcmp("U", parser_data[i])) { undefined: ret = snprintf(rrd_data, max_str, "U"); max_str -= ret; rrd_data += ret; goto next; } parse_opts(parser_data[i], ow_path, sizeof(ow_path), &offset); while (1) { int fail, j; char *tmp2; pr_info("Reading data for entry %s with offset of %.2f\n", ow_path, offset); ret = OWNET_read(handle, ow_path, &tmp); if (ret < 0) goto err; /* Skip leading white space */ tmp2 = tmp; for (j = 0; j < ret && *tmp2 == ' '; j++) tmp2++; fail = !strncmp(tmp2, "85", 2); /* * In case of failure, retry with uncached * data. This is likely to help as it forces a * retry even if the sensor is missing from * the cache. We thread "85" also as a failure * above, as temp sensors some times report 85 * under faulty conditions. */ if (ret > 0 && !fail) break; ret = make_uncached(ow_path, sizeof(ow_path)); err: if (retries >= 10 || ret < 0) { pr_err("Failed to read entry %s: %m\n", parser_data[i]); goto undefined; } retries++; } /* The data from OWNET_read appears to not be NULL terminated */ memcpy(buf, tmp, MIN(ret, sizeof(buf) -1)); free(tmp); buf[ret] = 0; data = strtod(buf, &endptr); if (endptr == buf) { pr_err("Failed to parse data %s\n", buf); goto undefined; } data += offset; ret = snprintf(rrd_data, max_str, "%f", data); max_str -= ret; rrd_data += ret; next: i++; if (!parser_data[i]) break; ret = snprintf(rrd_data, max_str, ":"); max_str -= ret; rrd_data += ret; } rrd_data = 0; mutex_unlock(&server_lock); return 0; } static struct parser_info onewire_parser_info = { .name = "onewire", .parse = onewire_parser, }; static int init_onewire_parser(void) { return register_parser(&onewire_parser_info); } struct plugin_info plugin_info = { .name = "onewire_parser", .init = init_onewire_parser, .version = RRDD_VERSION, };