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