]> git.itanic.dy.fi Git - rrdd/blob - onewire_parser.c
eb2ccd212b490ecead3ac58587087d1cac41d1e1
[rrdd] / onewire_parser.c
1 #include <stdio.h>
2 #include <ownetapi.h>
3
4 #include "parser.h"
5 #include "debug.h"
6 #include "string.h"
7 #include "utils.h"
8 #include "plugin.h"
9 #include "version.h"
10
11 static int parse_opts(const char *str, char *ow_path, size_t pathlen, double *offset)
12 {
13         char *endptr;
14         const char *start_str = str;
15         const char offset_str[] = "offset=";
16
17         if (!offset)
18                 return 0;
19
20         /*
21          * Skip the onewire path entry. Options begin after the first
22          * white space
23          */
24         for (; *str; str++)
25                 if (isspace(*str))
26                         break;
27
28         /* Copy the onewire path without options */
29         strncpy(ow_path, start_str, pathlen);
30         ow_path[str - start_str] = '\0';
31
32         /* Get the next non-space, which is where the argument begins */
33         for (; *str; str++)
34                 if (!isspace(*str))
35                         break;
36
37         if (strncmp(str, offset_str, sizeof(offset_str) - 1))
38                 return 0;
39         str += sizeof(offset_str) - 1;
40
41         *offset = strtod(str, &endptr);
42
43         if (str != endptr)
44                 return 1;
45
46         return 0;
47 }
48
49 static int make_uncached(char *path, size_t len)
50 {
51         char p1[32], p2[32], *p = path;
52
53         if (strstr(path, "/uncached/"))
54                 return 0;
55
56         /*
57          * Naively assume the "uncached" string can be put after the
58          * first slash
59          */
60         while (*p && *p != '/')
61                 p++;
62
63         if (!*p)
64                 return -1;
65
66         *p = 0;
67         p++;
68
69         strncpy(p1, path, sizeof(p1));
70         strncpy(p2, p, sizeof(p2));
71         snprintf(path, len, "%s/uncached/%s", p1, p2);
72
73         return 0;
74 }
75
76 static int onewire_parser(char *rrd_data, const char **parser_data)
77 {
78         OWNET_HANDLE h;
79         const char *server_addr;
80         char buf[24], *tmp;
81         int i = 1, ret;
82         int max_str = RRD_DATA_MAX_LEN;
83
84         if (!parser_data) {
85                 pr_err("No parser data available\n");
86                 return -1;
87         }
88
89         server_addr = parser_data[0];
90
91         if (!server_addr) {
92                 pr_err("Server address not specified\n");
93                 return -1;
94         }
95
96         h = OWNET_init(server_addr);
97         if (h < 0) {
98                 pr_err("Failed to connect to server %s\n", server_addr);
99                 return -1;
100         }
101
102         while (parser_data[i]) {
103                 double offset = 0, data;
104                 char *endptr;
105                 char ow_path[1024];
106                 int retries = 0;
107
108                 if (!strcmp("U", parser_data[i])) {
109 undefined:
110                         ret = snprintf(rrd_data, max_str, "U");
111                         max_str -= ret;
112                         rrd_data += ret;
113                         goto next;
114                 }
115
116                 parse_opts(parser_data[i], ow_path, sizeof(ow_path), &offset);
117
118                 while (1) {
119                         int fail, j;
120                         char *tmp2;
121
122                         pr_info("Reading data for entry %s with offset of %.2f\n",
123                                 ow_path, offset);
124                         ret = OWNET_read(h, ow_path, &tmp);
125
126                         /* Skip leading white space */
127                         tmp2 = tmp;
128                         for (j = 0; j < ret && *tmp2 == ' '; j++)
129                                 tmp2++;
130
131                         fail = !strncmp(tmp2, "85", 2);
132
133                         /*
134                          * In case of failure, retry with uncached
135                          * data. This is likely to help as it forces a
136                          * retry even if the sensor is missing from
137                          * the cache. We thread "85" also as a failure
138                          * above, as temp sensors some times report 85
139                          * under faulty conditions.
140                          */
141                         if (ret > 0 && !fail)
142                                 break;
143
144                         ret = make_uncached(ow_path, sizeof(ow_path));
145                         if (retries >= 10 || ret < 0) {
146                                 pr_err("Failed to read entry %s: %m\n",
147                                         parser_data[i]);
148                                 goto undefined;
149                         }
150                         retries++;
151                 }
152
153                 /* The data from OWNET_read appears to not be NULL terminated */
154                 memcpy(buf, tmp, MIN(ret, sizeof(buf) -1));
155                 free(tmp);
156                 buf[ret] = 0;
157
158                 data = strtod(buf, &endptr);
159
160                 if (endptr == buf) {
161                         pr_err("Failed to parse data %s\n", buf);
162                         goto undefined;
163                 }
164
165                 data += offset;
166
167                 ret = snprintf(rrd_data, max_str, "%f", data);
168                 max_str -= ret;
169                 rrd_data += ret;
170
171 next:
172                 i++;
173                 if (!parser_data[i])
174                         break;
175
176                 ret = snprintf(rrd_data, max_str, ":");
177                 max_str -= ret;
178                 rrd_data += ret;
179         }
180         rrd_data = 0;
181
182         OWNET_finish();
183         return 0;
184 }
185
186 static struct parser_info onewire_parser_info = {
187         .name = "onewire",
188         .parse = onewire_parser,
189 };
190
191 static int init_onewire_parser(void)
192 {
193         return register_parser(&onewire_parser_info);
194 }
195
196 struct plugin_info plugin_info = {
197         .name = "onewire_parser",
198         .init = init_onewire_parser,
199         .version = RRDD_VERSION,
200 };