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