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