Add protocol update notifications and hotplug legacy calls
[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)
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 }