3e13d0fbab9c1af0b6cdbf00c3a2e16efeabb24f
[project/netifd.git] / proto-shell.c
1 /*
2 * netifd - network interface daemon
3 * Copyright (C) 2012 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 #define _GNU_SOURCE
15
16 #include <string.h>
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <glob.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <signal.h>
23
24 #include <arpa/inet.h>
25 #include <netinet/in.h>
26
27 #include <libubox/blobmsg_json.h>
28
29 #include "netifd.h"
30 #include "interface.h"
31 #include "interface-ip.h"
32 #include "proto.h"
33 #include "system.h"
34
35 static int proto_fd = -1;
36
37 enum proto_shell_sm {
38 S_IDLE,
39 S_SETUP,
40 S_SETUP_ABORT,
41 S_TEARDOWN,
42 };
43
44 struct proto_shell_handler {
45 struct list_head list;
46 struct proto_handler proto;
47 struct uci_blob_param_list config;
48 char *config_buf;
49 bool init_available;
50 char script_name[];
51 };
52
53 struct proto_shell_dependency {
54 struct list_head list;
55
56 struct proto_shell_state *proto;
57 struct interface_user dep;
58
59 union if_addr host;
60 bool v6;
61
62 char interface[];
63 };
64
65 struct proto_shell_state {
66 struct interface_proto_state proto;
67 struct proto_shell_handler *handler;
68 struct blob_attr *config;
69
70 struct uloop_timeout teardown_timeout;
71
72 struct netifd_process script_task;
73 struct netifd_process proto_task;
74
75 enum proto_shell_sm sm;
76 bool proto_task_killed;
77
78 int last_error;
79
80 struct list_head deps;
81 };
82
83 static void
84 proto_shell_check_dependencies(struct proto_shell_state *state)
85 {
86 struct proto_shell_dependency *dep;
87 bool available = true;
88
89 list_for_each_entry(dep, &state->deps, list) {
90 if (dep->dep.iface)
91 continue;
92
93 available = false;
94 break;
95 }
96
97 interface_set_available(state->proto.iface, available);
98 }
99
100 static void
101 proto_shell_if_up_cb(struct interface_user *dep, struct interface *iface,
102 enum interface_event ev);
103 static void
104 proto_shell_if_down_cb(struct interface_user *dep, struct interface *iface,
105 enum interface_event ev);
106
107 static void
108 proto_shell_update_host_dep(struct proto_shell_dependency *dep)
109 {
110 struct interface *iface = NULL;
111
112 if (dep->dep.iface)
113 goto out;
114
115 if (dep->interface[0])
116 iface = vlist_find(&interfaces, dep->interface, iface, node);
117
118 iface = interface_ip_add_target_route(&dep->host, dep->v6, iface);
119 if (!iface)
120 goto out;
121
122 interface_remove_user(&dep->dep);
123 dep->dep.cb = proto_shell_if_down_cb;
124 interface_add_user(&dep->dep, iface);
125
126 out:
127 proto_shell_check_dependencies(dep->proto);
128 }
129
130 static void
131 proto_shell_clear_host_dep(struct proto_shell_state *state)
132 {
133 struct proto_shell_dependency *dep, *tmp;
134
135 list_for_each_entry_safe(dep, tmp, &state->deps, list) {
136 interface_remove_user(&dep->dep);
137 list_del(&dep->list);
138 free(dep);
139 }
140 }
141
142 static int
143 proto_shell_handler(struct interface_proto_state *proto,
144 enum interface_proto_cmd cmd, bool force)
145 {
146 struct proto_shell_state *state;
147 struct proto_shell_handler *handler;
148 struct netifd_process *proc;
149 static char error_buf[32];
150 const char *argv[7];
151 char *envp[2];
152 const char *action;
153 char *config;
154 int ret, i = 0, j = 0;
155
156 state = container_of(proto, struct proto_shell_state, proto);
157 handler = state->handler;
158 proc = &state->script_task;
159
160 if (cmd == PROTO_CMD_SETUP) {
161 action = "setup";
162 state->last_error = -1;
163 proto_shell_clear_host_dep(state);
164 } else {
165 if (state->sm == S_TEARDOWN)
166 return 0;
167
168 if (state->script_task.uloop.pending) {
169 if (state->sm != S_SETUP_ABORT) {
170 uloop_timeout_set(&state->teardown_timeout, 1000);
171 kill(state->script_task.uloop.pid, SIGTERM);
172 if (state->proto_task.uloop.pending)
173 kill(state->proto_task.uloop.pid, SIGTERM);
174 state->sm = S_SETUP_ABORT;
175 }
176 return 0;
177 }
178
179 action = "teardown";
180 state->sm = S_TEARDOWN;
181 if (state->last_error >= 0) {
182 snprintf(error_buf, sizeof(error_buf), "ERROR=%d", state->last_error);
183 envp[j++] = error_buf;
184 }
185 uloop_timeout_set(&state->teardown_timeout, 5000);
186 }
187
188 config = blobmsg_format_json(state->config, true);
189 if (!config)
190 return -1;
191
192 argv[i++] = handler->script_name;
193 argv[i++] = handler->proto.name;
194 argv[i++] = action;
195 argv[i++] = proto->iface->name;
196 argv[i++] = config;
197 if (proto->iface->main_dev.dev)
198 argv[i++] = proto->iface->main_dev.dev->ifname;
199 argv[i] = NULL;
200 envp[j] = NULL;
201
202 ret = netifd_start_process(argv, envp, proc);
203 free(config);
204
205 return ret;
206 }
207
208 static void
209 proto_shell_if_up_cb(struct interface_user *dep, struct interface *iface,
210 enum interface_event ev)
211 {
212 struct proto_shell_dependency *pdep;
213
214 if (ev != IFEV_UP && ev != IFEV_UPDATE)
215 return;
216
217 pdep = container_of(dep, struct proto_shell_dependency, dep);
218 proto_shell_update_host_dep(pdep);
219 }
220
221 static void
222 proto_shell_if_down_cb(struct interface_user *dep, struct interface *iface,
223 enum interface_event ev)
224 {
225 struct proto_shell_dependency *pdep;
226 struct proto_shell_state *state;
227
228 if (ev == IFEV_UP || ev == IFEV_UPDATE)
229 return;
230
231 pdep = container_of(dep, struct proto_shell_dependency, dep);
232 interface_remove_user(dep);
233 dep->cb = proto_shell_if_up_cb;
234 interface_add_user(dep, NULL);
235
236 state = pdep->proto;
237 if (state->sm == S_IDLE) {
238 state->proto.proto_event(&state->proto, IFPEV_LINK_LOST);
239 proto_shell_handler(&state->proto, PROTO_CMD_TEARDOWN, false);
240 }
241 }
242
243 static void
244 proto_shell_task_finish(struct proto_shell_state *state,
245 struct netifd_process *task)
246 {
247 switch (state->sm) {
248 case S_IDLE:
249 if (task == &state->proto_task)
250 state->proto.proto_event(&state->proto, IFPEV_LINK_LOST);
251 /* fall through */
252 case S_SETUP:
253 if (task == &state->proto_task)
254 proto_shell_handler(&state->proto, PROTO_CMD_TEARDOWN,
255 false);
256 break;
257
258 case S_SETUP_ABORT:
259 if (state->script_task.uloop.pending ||
260 state->proto_task.uloop.pending)
261 break;
262
263 uloop_timeout_cancel(&state->teardown_timeout);
264 state->sm = S_IDLE;
265 proto_shell_handler(&state->proto, PROTO_CMD_TEARDOWN, false);
266 break;
267
268 case S_TEARDOWN:
269 if (state->script_task.uloop.pending)
270 break;
271
272 if (state->proto_task.uloop.pending) {
273 if (!state->proto_task_killed)
274 kill(state->proto_task.uloop.pid, SIGTERM);
275 break;
276 }
277
278 uloop_timeout_cancel(&state->teardown_timeout);
279 state->sm = S_IDLE;
280 state->proto.proto_event(&state->proto, IFPEV_DOWN);
281 break;
282 }
283 }
284
285 static void
286 proto_shell_teardown_timeout_cb(struct uloop_timeout *timeout)
287 {
288 struct proto_shell_state *state;
289
290 state = container_of(timeout, struct proto_shell_state, teardown_timeout);
291
292 netifd_kill_process(&state->script_task);
293 netifd_kill_process(&state->proto_task);
294 proto_shell_task_finish(state, NULL);
295 }
296
297 static void
298 proto_shell_script_cb(struct netifd_process *p, int ret)
299 {
300 struct proto_shell_state *state;
301
302 state = container_of(p, struct proto_shell_state, script_task);
303 proto_shell_task_finish(state, p);
304 }
305
306 static void
307 proto_shell_task_cb(struct netifd_process *p, int ret)
308 {
309 struct proto_shell_state *state;
310
311 state = container_of(p, struct proto_shell_state, proto_task);
312
313 if (state->sm == S_IDLE || state->sm == S_SETUP)
314 state->last_error = WEXITSTATUS(ret);
315
316 proto_shell_task_finish(state, p);
317 }
318
319 static void
320 proto_shell_free(struct interface_proto_state *proto)
321 {
322 struct proto_shell_state *state;
323
324 state = container_of(proto, struct proto_shell_state, proto);
325 uloop_timeout_cancel(&state->teardown_timeout);
326 proto_shell_clear_host_dep(state);
327 netifd_kill_process(&state->script_task);
328 netifd_kill_process(&state->proto_task);
329 free(state->config);
330 free(state);
331 }
332
333 static void
334 proto_shell_parse_route_list(struct interface *iface, struct blob_attr *attr,
335 bool v6)
336 {
337 struct blob_attr *cur;
338 int rem;
339
340 blobmsg_for_each_attr(cur, attr, rem) {
341 if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE) {
342 DPRINTF("Ignore wrong route type: %d\n", blobmsg_type(cur));
343 continue;
344 }
345
346 interface_ip_add_route(iface, cur, v6);
347 }
348 }
349
350 static void
351 proto_shell_parse_data(struct interface *iface, struct blob_attr *attr)
352 {
353 struct blob_attr *cur;
354 int rem;
355
356 blobmsg_for_each_attr(cur, attr, rem)
357 interface_add_data(iface, cur);
358 }
359
360 static struct device *
361 proto_shell_create_tunnel(const char *name, struct blob_attr *attr)
362 {
363 struct device *dev;
364 struct blob_buf b;
365
366 memset(&b, 0, sizeof(b));
367 blob_buf_init(&b, 0);
368 blob_put(&b, 0, blobmsg_data(attr), blobmsg_data_len(attr));
369 dev = device_create(name, &tunnel_device_type, blob_data(b.head));
370 blob_buf_free(&b);
371
372 return dev;
373 }
374
375 enum {
376 NOTIFY_ACTION,
377 NOTIFY_ERROR,
378 NOTIFY_COMMAND,
379 NOTIFY_ENV,
380 NOTIFY_SIGNAL,
381 NOTIFY_AVAILABLE,
382 NOTIFY_LINK_UP,
383 NOTIFY_IFNAME,
384 NOTIFY_ADDR_EXT,
385 NOTIFY_ROUTES,
386 NOTIFY_ROUTES6,
387 NOTIFY_TUNNEL,
388 NOTIFY_DATA,
389 NOTIFY_KEEP,
390 NOTIFY_HOST,
391 NOTIFY_DNS,
392 NOTIFY_DNS_SEARCH,
393 __NOTIFY_LAST
394 };
395
396 static const struct blobmsg_policy notify_attr[__NOTIFY_LAST] = {
397 [NOTIFY_ACTION] = { .name = "action", .type = BLOBMSG_TYPE_INT32 },
398 [NOTIFY_ERROR] = { .name = "error", .type = BLOBMSG_TYPE_ARRAY },
399 [NOTIFY_COMMAND] = { .name = "command", .type = BLOBMSG_TYPE_ARRAY },
400 [NOTIFY_ENV] = { .name = "env", .type = BLOBMSG_TYPE_ARRAY },
401 [NOTIFY_SIGNAL] = { .name = "signal", .type = BLOBMSG_TYPE_INT32 },
402 [NOTIFY_AVAILABLE] = { .name = "available", .type = BLOBMSG_TYPE_BOOL },
403 [NOTIFY_LINK_UP] = { .name = "link-up", .type = BLOBMSG_TYPE_BOOL },
404 [NOTIFY_IFNAME] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING },
405 [NOTIFY_ADDR_EXT] = { .name = "address-external", .type = BLOBMSG_TYPE_BOOL },
406 [NOTIFY_ROUTES] = { .name = "routes", .type = BLOBMSG_TYPE_ARRAY },
407 [NOTIFY_ROUTES6] = { .name = "routes6", .type = BLOBMSG_TYPE_ARRAY },
408 [NOTIFY_TUNNEL] = { .name = "tunnel", .type = BLOBMSG_TYPE_TABLE },
409 [NOTIFY_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE },
410 [NOTIFY_KEEP] = { .name = "keep", .type = BLOBMSG_TYPE_BOOL },
411 [NOTIFY_HOST] = { .name = "host", .type = BLOBMSG_TYPE_STRING },
412 [NOTIFY_DNS] = { .name = "dns", .type = BLOBMSG_TYPE_ARRAY },
413 [NOTIFY_DNS_SEARCH] = { .name = "dns_search", .type = BLOBMSG_TYPE_ARRAY },
414 };
415
416 static int
417 proto_shell_update_link(struct proto_shell_state *state, struct blob_attr *data, struct blob_attr **tb)
418 {
419 struct interface *iface = state->proto.iface;
420 struct blob_attr *cur;
421 struct device *dev;
422 const char *devname;
423 int dev_create = 1;
424 bool addr_ext = false;
425 bool keep = false;
426 bool up;
427
428 if (!tb[NOTIFY_LINK_UP])
429 return UBUS_STATUS_INVALID_ARGUMENT;
430
431 up = blobmsg_get_bool(tb[NOTIFY_LINK_UP]);
432 if (!up) {
433 state->proto.proto_event(&state->proto, IFPEV_LINK_LOST);
434 return 0;
435 }
436
437 if ((cur = tb[NOTIFY_KEEP]) != NULL)
438 keep = blobmsg_get_bool(cur);
439
440 if ((cur = tb[NOTIFY_ADDR_EXT]) != NULL) {
441 addr_ext = blobmsg_get_bool(cur);
442 if (addr_ext)
443 dev_create = 2;
444 }
445
446 if (!tb[NOTIFY_IFNAME]) {
447 if (!iface->main_dev.dev)
448 return UBUS_STATUS_INVALID_ARGUMENT;
449 } else if (!keep || iface->state != IFS_UP) {
450 keep = false;
451 devname = blobmsg_data(tb[NOTIFY_IFNAME]);
452 if (tb[NOTIFY_TUNNEL]) {
453 dev = proto_shell_create_tunnel(devname,
454 tb[NOTIFY_TUNNEL]);
455 if (!dev)
456 return UBUS_STATUS_INVALID_ARGUMENT;
457 } else {
458 dev = device_get(devname, dev_create);
459 if (!dev)
460 return UBUS_STATUS_NOT_FOUND;
461 }
462
463 interface_set_l3_dev(iface, dev);
464 device_claim(&iface->l3_dev);
465 device_set_present(dev, true);
466 }
467
468 if (!keep)
469 interface_update_start(iface);
470
471 proto_apply_ip_settings(iface, data, addr_ext);
472
473 if ((cur = tb[NOTIFY_ROUTES]) != NULL)
474 proto_shell_parse_route_list(state->proto.iface, cur, false);
475
476 if ((cur = tb[NOTIFY_ROUTES6]) != NULL)
477 proto_shell_parse_route_list(state->proto.iface, cur, true);
478
479 if ((cur = tb[NOTIFY_DNS]))
480 interface_add_dns_server_list(&iface->proto_ip, cur);
481
482 if ((cur = tb[NOTIFY_DNS_SEARCH]))
483 interface_add_dns_search_list(&iface->proto_ip, cur);
484
485 interface_update_complete(state->proto.iface);
486
487 if (!keep)
488 state->proto.proto_event(&state->proto, IFPEV_UP);
489 state->sm = S_IDLE;
490
491 if ((cur = tb[NOTIFY_DATA]))
492 proto_shell_parse_data(state->proto.iface, cur);
493
494 return 0;
495 }
496
497 static bool
498 fill_string_list(struct blob_attr *attr, char **argv, int max)
499 {
500 struct blob_attr *cur;
501 int argc = 0;
502 int rem;
503
504 if (!attr)
505 goto out;
506
507 blobmsg_for_each_attr(cur, attr, rem) {
508 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
509 return false;
510
511 if (!blobmsg_check_attr(cur, NULL))
512 return false;
513
514 argv[argc++] = blobmsg_data(cur);
515 if (argc == max - 1)
516 return false;
517 }
518
519 out:
520 argv[argc] = NULL;
521 return true;
522 }
523
524 static int
525 proto_shell_run_command(struct proto_shell_state *state, struct blob_attr **tb)
526 {
527 static char *argv[64];
528 static char *env[32];
529
530 if (!tb[NOTIFY_COMMAND])
531 goto error;
532
533 if (!fill_string_list(tb[NOTIFY_COMMAND], argv, ARRAY_SIZE(argv)))
534 goto error;
535
536 if (!fill_string_list(tb[NOTIFY_ENV], env, ARRAY_SIZE(env)))
537 goto error;
538
539 netifd_start_process((const char **) argv, (char **) env, &state->proto_task);
540
541 return 0;
542
543 error:
544 return UBUS_STATUS_INVALID_ARGUMENT;
545 }
546
547 static int
548 proto_shell_kill_command(struct proto_shell_state *state, struct blob_attr **tb)
549 {
550 unsigned int signal = ~0;
551
552 if (tb[NOTIFY_SIGNAL])
553 signal = blobmsg_get_u32(tb[NOTIFY_SIGNAL]);
554
555 if (signal > 31)
556 signal = SIGTERM;
557
558 if (state->proto_task.uloop.pending) {
559 state->proto_task_killed = true;
560 kill(state->proto_task.uloop.pid, signal);
561 }
562
563 return 0;
564 }
565
566 static int
567 proto_shell_notify_error(struct proto_shell_state *state, struct blob_attr **tb)
568 {
569 struct blob_attr *cur;
570 char *data[16];
571 int n_data = 0;
572 int rem;
573
574 if (!tb[NOTIFY_ERROR])
575 return UBUS_STATUS_INVALID_ARGUMENT;
576
577 blobmsg_for_each_attr(cur, tb[NOTIFY_ERROR], rem) {
578 if (n_data + 1 == ARRAY_SIZE(data))
579 goto error;
580
581 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
582 goto error;
583
584 if (!blobmsg_check_attr(cur, NULL))
585 goto error;
586
587 data[n_data++] = blobmsg_data(cur);
588 }
589
590 if (!n_data)
591 goto error;
592
593 interface_add_error(state->proto.iface, state->handler->proto.name,
594 data[0], (const char **) &data[1], n_data - 1);
595
596 return 0;
597
598 error:
599 return UBUS_STATUS_INVALID_ARGUMENT;
600 }
601
602 static int
603 proto_shell_block_restart(struct proto_shell_state *state, struct blob_attr **tb)
604 {
605 state->proto.iface->autostart = false;
606 return 0;
607 }
608
609 static int
610 proto_shell_set_available(struct proto_shell_state *state, struct blob_attr **tb)
611 {
612 if (!tb[NOTIFY_AVAILABLE])
613 return UBUS_STATUS_INVALID_ARGUMENT;
614
615 interface_set_available(state->proto.iface, blobmsg_get_bool(tb[NOTIFY_AVAILABLE]));
616 return 0;
617 }
618
619 static int
620 proto_shell_add_host_dependency(struct proto_shell_state *state, struct blob_attr **tb)
621 {
622 struct proto_shell_dependency *dep;
623 struct blob_attr *host = tb[NOTIFY_HOST];
624 struct blob_attr *ifname = tb[NOTIFY_IFNAME];
625 size_t ifnamelen = (ifname) ? blobmsg_data_len(ifname) : 1;
626
627 if (!host)
628 return UBUS_STATUS_INVALID_ARGUMENT;
629
630 dep = calloc(1, sizeof(*dep) + ifnamelen);
631 if (inet_pton(AF_INET, blobmsg_data(host), &dep->host) < 1) {
632 if (inet_pton(AF_INET6, blobmsg_data(host), &dep->host) < 1) {
633 free(dep);
634 return UBUS_STATUS_INVALID_ARGUMENT;
635 } else {
636 dep->v6 = true;
637 }
638 }
639
640 dep->proto = state;
641 if (ifname)
642 memcpy(dep->interface, blobmsg_data(ifname), ifnamelen);
643 else
644 dep->interface[0] = 0;
645
646 dep->dep.cb = proto_shell_if_up_cb;
647 interface_add_user(&dep->dep, NULL);
648 list_add(&dep->list, &state->deps);
649 proto_shell_update_host_dep(dep);
650 if (!dep->dep.iface)
651 return UBUS_STATUS_NOT_FOUND;
652
653 return 0;
654 }
655
656 static int
657 proto_shell_setup_failed(struct proto_shell_state *state)
658 {
659 switch (state->sm) {
660 case S_IDLE:
661 state->proto.proto_event(&state->proto, IFPEV_LINK_LOST);
662 /* fall through */
663 case S_SETUP:
664 proto_shell_handler(&state->proto, PROTO_CMD_TEARDOWN, false);
665 break;
666 default:
667 break;
668 }
669 return 0;
670 }
671
672 static int
673 proto_shell_notify(struct interface_proto_state *proto, struct blob_attr *attr)
674 {
675 struct proto_shell_state *state;
676 struct blob_attr *tb[__NOTIFY_LAST];
677
678 state = container_of(proto, struct proto_shell_state, proto);
679
680 blobmsg_parse(notify_attr, __NOTIFY_LAST, tb, blob_data(attr), blob_len(attr));
681 if (!tb[NOTIFY_ACTION])
682 return UBUS_STATUS_INVALID_ARGUMENT;
683
684 switch(blobmsg_get_u32(tb[NOTIFY_ACTION])) {
685 case 0:
686 return proto_shell_update_link(state, attr, tb);
687 case 1:
688 return proto_shell_run_command(state, tb);
689 case 2:
690 return proto_shell_kill_command(state, tb);
691 case 3:
692 return proto_shell_notify_error(state, tb);
693 case 4:
694 return proto_shell_block_restart(state, tb);
695 case 5:
696 return proto_shell_set_available(state, tb);
697 case 6:
698 return proto_shell_add_host_dependency(state, tb);
699 case 7:
700 return proto_shell_setup_failed(state);
701 default:
702 return UBUS_STATUS_INVALID_ARGUMENT;
703 }
704 }
705
706 static struct interface_proto_state *
707 proto_shell_attach(const struct proto_handler *h, struct interface *iface,
708 struct blob_attr *attr)
709 {
710 struct proto_shell_state *state;
711
712 state = calloc(1, sizeof(*state));
713 INIT_LIST_HEAD(&state->deps);
714
715 state->config = malloc(blob_pad_len(attr));
716 if (!state->config)
717 goto error;
718
719 memcpy(state->config, attr, blob_pad_len(attr));
720 state->proto.free = proto_shell_free;
721 state->proto.notify = proto_shell_notify;
722 state->proto.cb = proto_shell_handler;
723 state->teardown_timeout.cb = proto_shell_teardown_timeout_cb;
724 state->script_task.cb = proto_shell_script_cb;
725 state->script_task.dir_fd = proto_fd;
726 state->script_task.log_prefix = iface->name;
727 state->proto_task.cb = proto_shell_task_cb;
728 state->proto_task.dir_fd = proto_fd;
729 state->proto_task.log_prefix = iface->name;
730 state->handler = container_of(h, struct proto_shell_handler, proto);
731
732 return &state->proto;
733
734 error:
735 free(state);
736 return NULL;
737 }
738
739 static json_object *
740 check_type(json_object *obj, json_type type)
741 {
742 if (!obj)
743 return NULL;
744
745 if (json_object_get_type(obj) != type)
746 return NULL;
747
748 return obj;
749 }
750
751 static inline json_object *
752 get_field(json_object *obj, const char *name, json_type type)
753 {
754 return check_type(json_object_object_get(obj, name), type);
755 }
756
757 static char *
758 proto_shell_parse_config(struct uci_blob_param_list *config, json_object *obj)
759 {
760 struct blobmsg_policy *attrs;
761 char *str_buf, *str_cur;
762 int str_len = 0;
763 int i;
764
765 config->n_params = json_object_array_length(obj);
766 attrs = calloc(1, sizeof(*attrs) * config->n_params);
767 if (!attrs)
768 return NULL;
769
770 config->params = attrs;
771 for (i = 0; i < config->n_params; i++) {
772 json_object *cur, *name, *type;
773
774 cur = check_type(json_object_array_get_idx(obj, i), json_type_array);
775 if (!cur)
776 goto error;
777
778 name = check_type(json_object_array_get_idx(cur, 0), json_type_string);
779 if (!name)
780 goto error;
781
782 type = check_type(json_object_array_get_idx(cur, 1), json_type_int);
783 if (!type)
784 goto error;
785
786 attrs[i].name = json_object_get_string(name);
787 attrs[i].type = json_object_get_int(type);
788 if (attrs[i].type > BLOBMSG_TYPE_LAST)
789 goto error;
790
791 str_len += strlen(attrs[i].name) + 1;
792 }
793
794 str_buf = malloc(str_len);
795 if (!str_buf)
796 goto error;
797
798 str_cur = str_buf;
799 for (i = 0; i < config->n_params; i++) {
800 const char *name = attrs[i].name;
801
802 attrs[i].name = str_cur;
803 str_cur += sprintf(str_cur, "%s", name) + 1;
804 }
805
806 return str_buf;
807
808 error:
809 free(attrs);
810 config->n_params = 0;
811 return NULL;
812 }
813
814 static void
815 proto_shell_add_handler(const char *script, json_object *obj)
816 {
817 struct proto_shell_handler *handler;
818 struct proto_handler *proto;
819 json_object *config, *tmp;
820 const char *name;
821 char *str;
822
823 if (!check_type(obj, json_type_object))
824 return;
825
826 tmp = get_field(obj, "name", json_type_string);
827 if (!tmp)
828 return;
829
830 name = json_object_get_string(tmp);
831
832 handler = calloc_a(sizeof(*handler) + strlen(script) + 1,
833 &str, strlen(name) + 1);
834 if (!handler)
835 return;
836
837 strcpy(handler->script_name, script);
838 strcpy(str, name);
839
840 proto = &handler->proto;
841 proto->name = str;
842 proto->config_params = &handler->config;
843 proto->attach = proto_shell_attach;
844
845 tmp = get_field(obj, "no-device", json_type_boolean);
846 if (tmp && json_object_get_boolean(tmp))
847 handler->proto.flags |= PROTO_FLAG_NODEV;
848
849 tmp = get_field(obj, "available", json_type_boolean);
850 if (tmp && json_object_get_boolean(tmp))
851 handler->proto.flags |= PROTO_FLAG_INIT_AVAILABLE;
852
853 config = get_field(obj, "config", json_type_array);
854 if (config)
855 handler->config_buf = proto_shell_parse_config(&handler->config, config);
856
857 DPRINTF("Add handler for script %s: %s\n", script, proto->name);
858 add_proto_handler(proto);
859 }
860
861 static void proto_shell_add_script(const char *name)
862 {
863 struct json_tokener *tok = NULL;
864 json_object *obj;
865 static char buf[512];
866 char *start, *cmd;
867 FILE *f;
868 int len;
869
870 #define DUMP_SUFFIX " '' dump"
871
872 cmd = alloca(strlen(name) + 1 + sizeof(DUMP_SUFFIX));
873 sprintf(cmd, "%s" DUMP_SUFFIX, name);
874
875 f = popen(cmd, "r");
876 if (!f)
877 return;
878
879 do {
880 start = fgets(buf, sizeof(buf), f);
881 if (!start)
882 continue;
883
884 len = strlen(start);
885
886 if (!tok)
887 tok = json_tokener_new();
888
889 obj = json_tokener_parse_ex(tok, start, len);
890 if (!is_error(obj)) {
891 proto_shell_add_handler(name, obj);
892 json_object_put(obj);
893 json_tokener_free(tok);
894 tok = NULL;
895 } else if (start[len - 1] == '\n') {
896 json_tokener_free(tok);
897 tok = NULL;
898 }
899 } while (!feof(f) && !ferror(f));
900
901 if (tok)
902 json_tokener_free(tok);
903
904 pclose(f);
905 }
906
907 static void __init proto_shell_init(void)
908 {
909 glob_t g;
910 int main_fd;
911 int i;
912
913 main_fd = open(".", O_RDONLY | O_DIRECTORY);
914 if (main_fd < 0)
915 return;
916
917 if (chdir(main_path)) {
918 perror("chdir(main path)");
919 goto close_cur;
920 }
921
922 if (chdir("./proto"))
923 goto close_cur;
924
925 proto_fd = open(".", O_RDONLY | O_DIRECTORY);
926 if (proto_fd < 0)
927 goto close_cur;
928
929 system_fd_set_cloexec(proto_fd);
930 glob("./*.sh", 0, NULL, &g);
931 for (i = 0; i < g.gl_pathc; i++)
932 proto_shell_add_script(g.gl_pathv[i]);
933
934 close_cur:
935 fchdir(main_fd);
936 close(main_fd);
937 }