handler: add mechanism to generate external device handler stubs
[project/netifd.git] / handler.c
1 /*
2 * netifd - network interface daemon
3 * Copyright (C) 2012-2013 Felix Fietkau <nbd@openwrt.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2
7 * as published by the Free Software Foundation
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15 #define _GNU_SOURCE
16 #include <glob.h>
17 #include <fcntl.h>
18 #include <stdio.h>
19
20 #include "netifd.h"
21 #include "system.h"
22 #include "handler.h"
23
24 static int
25 netifd_dir_push(int fd)
26 {
27 int prev_fd = open(".", O_RDONLY | O_DIRECTORY);
28 system_fd_set_cloexec(prev_fd);
29 if (fd >= 0)
30 if (fchdir(fd)) {}
31 return prev_fd;
32 }
33
34 static void
35 netifd_dir_pop(int prev_fd)
36 {
37 if (prev_fd < 0)
38 return;
39
40 if (fchdir(prev_fd)) {}
41 close(prev_fd);
42 }
43
44 int netifd_open_subdir(const char *name)
45 {
46 int prev_dir;
47 int ret = -1;
48
49 prev_dir = netifd_dir_push(-1);
50 if (chdir(main_path)) {
51 perror("chdir(main path)");
52 goto out;
53 }
54
55 ret = open(name, O_RDONLY | O_DIRECTORY);
56 if (ret >= 0)
57 system_fd_set_cloexec(ret);
58
59 out:
60 netifd_dir_pop(prev_dir);
61 return ret;
62 }
63
64 static void
65 netifd_init_script_handler(const char *script, json_object *obj, script_dump_cb cb)
66 {
67 json_object *tmp;
68 const char *name;
69
70 if (!json_check_type(obj, json_type_object))
71 return;
72
73 tmp = json_get_field(obj, "name", json_type_string);
74 if (!tmp)
75 return;
76
77 name = json_object_get_string(tmp);
78 cb(script, name, obj);
79 }
80
81 static void
82 netifd_init_extdev_handler(const char *config_file, json_object *obj,
83 create_extdev_handler_cb cb)
84 {
85 json_object *tmp, *cfg, *info, *stats;
86 const char *name, *ubus_name, *br_prefix = NULL;
87 bool bridge_support = true;
88 char *err_missing;
89
90 if (!json_check_type(obj, json_type_object))
91 return;
92
93 tmp = json_get_field(obj, "name", json_type_string);
94 if (!tmp) {
95 err_missing = "name";
96 goto field_missing;
97 }
98
99 name = json_object_get_string(tmp);
100
101 tmp = json_get_field(obj, "ubus_name", json_type_string);
102 if (!tmp) {
103 err_missing = "ubus_name";
104 goto field_missing;
105 }
106
107 ubus_name = json_object_get_string(tmp);
108
109 tmp = json_get_field(obj, "bridge", json_type_string);
110 if (!tmp || !strcmp(json_object_get_string(tmp), "0"))
111 bridge_support = false;
112
113 if (bridge_support) {
114 tmp = json_get_field(obj, "br-prefix", json_type_string);
115 if (!tmp)
116 br_prefix = name;
117 else
118 br_prefix = json_object_get_string(tmp);
119 }
120
121 tmp = json_get_field(obj, "config", json_type_array);
122 if (!tmp) {
123 err_missing = "config";
124 goto field_missing;
125 }
126
127 cfg = tmp;
128
129 info = json_get_field(obj, "info", json_type_array);
130 stats = json_get_field(obj, "stats", json_type_array);
131
132 cb(config_file, name, ubus_name, bridge_support, br_prefix, cfg, info, stats);
133 return;
134
135 field_missing:
136 netifd_log_message(L_WARNING, "external device handler description '%s' is"
137 "missing field '%s'\n", config_file, err_missing);
138 }
139
140 static void
141 netifd_parse_script_handler(const char *name, script_dump_cb cb)
142 {
143 struct json_tokener *tok = NULL;
144 json_object *obj;
145 static char buf[512];
146 char *start, *cmd;
147 FILE *f;
148 int len;
149
150 #define DUMP_SUFFIX " '' dump"
151
152 cmd = alloca(strlen(name) + 1 + sizeof(DUMP_SUFFIX));
153 sprintf(cmd, "%s" DUMP_SUFFIX, name);
154
155 f = popen(cmd, "r");
156 if (!f)
157 return;
158
159 do {
160 start = fgets(buf, sizeof(buf), f);
161 if (!start)
162 continue;
163
164 len = strlen(start);
165
166 if (!tok)
167 tok = json_tokener_new();
168
169 obj = json_tokener_parse_ex(tok, start, len);
170 if (obj) {
171 netifd_init_script_handler(name, obj, cb);
172 json_object_put(obj);
173 json_tokener_free(tok);
174 tok = NULL;
175 } else if (start[len - 1] == '\n') {
176 json_tokener_free(tok);
177 tok = NULL;
178 }
179 } while (!feof(f) && !ferror(f));
180
181 if (tok)
182 json_tokener_free(tok);
183
184 pclose(f);
185 }
186
187 static void
188 netifd_parse_extdev_handler(const char *path_to_file, create_extdev_handler_cb cb)
189 {
190 struct json_tokener *tok = NULL;
191 json_object *obj;
192 FILE *file;
193 int len;
194 char buf[512], *start;
195
196 file = fopen(path_to_file, "r");
197 if (!file)
198 return;
199
200 do {
201 start = fgets(buf, sizeof(buf), file);
202 if (!start)
203 continue;
204
205 len = strlen(start);
206
207 if (!tok)
208 tok = json_tokener_new();
209
210 obj = json_tokener_parse_ex(tok, start, len);
211
212 if (obj) {
213 netifd_init_extdev_handler(path_to_file, obj, cb);
214 json_object_put(obj);
215 json_tokener_free(tok);
216 tok = NULL;
217 } else if (start[len - 1] == '\n') {
218 json_tokener_free(tok);
219 tok = NULL;
220 }
221 } while (!feof(file) && !ferror(file));
222
223 if (tok)
224 json_tokener_free(tok);
225
226 fclose(file);
227 }
228
229 void netifd_init_script_handlers(int dir_fd, script_dump_cb cb)
230 {
231 glob_t g;
232 int i, prev_fd;
233
234 prev_fd = netifd_dir_push(dir_fd);
235 if (glob("./*.sh", 0, NULL, &g)) {
236 netifd_dir_pop(prev_fd);
237 return;
238 }
239
240 for (i = 0; i < g.gl_pathc; i++)
241 netifd_parse_script_handler(g.gl_pathv[i], cb);
242 netifd_dir_pop(prev_fd);
243
244 globfree(&g);
245 }
246
247 void
248 netifd_init_extdev_handlers(int dir_fd, create_extdev_handler_cb cb)
249 {
250 glob_t g;
251 int prev_fd;
252
253 prev_fd = netifd_dir_push(dir_fd);
254 glob("*.json", 0, NULL, &g);
255 for (int i = 0; i < g.gl_pathc; i++)
256 netifd_parse_extdev_handler(g.gl_pathv[i], cb);
257 netifd_dir_pop(prev_fd);
258 }
259
260 char *
261 netifd_handler_parse_config(struct uci_blob_param_list *config, json_object *obj)
262 {
263 struct blobmsg_policy *attrs;
264 char *str_buf, *str_cur;
265 char const **validate;
266 int str_len = 0;
267 int i;
268
269 config->n_params = json_object_array_length(obj);
270 attrs = calloc(1, sizeof(*attrs) * config->n_params);
271 if (!attrs)
272 return NULL;
273
274 validate = calloc(1, sizeof(char*) * config->n_params);
275 if (!validate)
276 goto error;
277
278 config->params = attrs;
279 config->validate = validate;
280 for (i = 0; i < config->n_params; i++) {
281 json_object *cur, *name, *type;
282
283 cur = json_check_type(json_object_array_get_idx(obj, i), json_type_array);
284 if (!cur)
285 goto error;
286
287 name = json_check_type(json_object_array_get_idx(cur, 0), json_type_string);
288 if (!name)
289 goto error;
290
291 type = json_check_type(json_object_array_get_idx(cur, 1), json_type_int);
292 if (!type)
293 goto error;
294
295 attrs[i].name = json_object_get_string(name);
296 attrs[i].type = json_object_get_int(type);
297 if (attrs[i].type > BLOBMSG_TYPE_LAST)
298 goto error;
299
300 str_len += strlen(attrs[i].name) + 1;
301 }
302
303 str_buf = malloc(str_len);
304 if (!str_buf)
305 goto error;
306
307 str_cur = str_buf;
308 for (i = 0; i < config->n_params; i++) {
309 const char *name = attrs[i].name;
310 char *delim;
311
312 attrs[i].name = str_cur;
313 str_cur += sprintf(str_cur, "%s", name) + 1;
314 delim = strchr(attrs[i].name, ':');
315 if (delim) {
316 *delim = '\0';
317 validate[i] = ++delim;
318 } else {
319 validate[i] = NULL;
320 }
321 }
322
323 return str_buf;
324
325 error:
326 free(attrs);
327 if (validate)
328 free(validate);
329 config->n_params = 0;
330 return NULL;
331 }