fix argument order on replacing dns lists
[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         }
459
460         if (!keep)
461                 interface_update_start(iface);
462
463         proto_apply_ip_settings(iface, data, addr_ext);
464
465         if ((cur = tb[NOTIFY_ROUTES]) != NULL)
466                 proto_shell_parse_route_list(state->proto.iface, cur, false);
467
468         if ((cur = tb[NOTIFY_ROUTES6]) != NULL)
469                 proto_shell_parse_route_list(state->proto.iface, cur, true);
470
471         if ((cur = tb[NOTIFY_DNS]))
472                 interface_add_dns_server_list(&iface->proto_ip, cur);
473
474         if ((cur = tb[NOTIFY_DNS_SEARCH]))
475                 interface_add_dns_search_list(&iface->proto_ip, cur);
476
477         interface_update_complete(state->proto.iface);
478
479         if (!keep)
480                 state->proto.proto_event(&state->proto, IFPEV_UP);
481         state->sm = S_IDLE;
482
483         if ((cur = tb[NOTIFY_DATA]))
484                 proto_shell_parse_data(state->proto.iface, cur);
485
486         return 0;
487 }
488
489 static bool
490 fill_string_list(struct blob_attr *attr, char **argv, int max)
491 {
492         struct blob_attr *cur;
493         int argc = 0;
494         int rem;
495
496         if (!attr)
497                 goto out;
498
499         blobmsg_for_each_attr(cur, attr, rem) {
500                 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
501                         return false;
502
503                 if (!blobmsg_check_attr(cur, NULL))
504                         return false;
505
506                 argv[argc++] = blobmsg_data(cur);
507                 if (argc == max - 1)
508                         return false;
509         }
510
511 out:
512         argv[argc] = NULL;
513         return true;
514 }
515
516 static int
517 proto_shell_run_command(struct proto_shell_state *state, struct blob_attr **tb)
518 {
519         static char *argv[64];
520         static char *env[32];
521
522         if (!tb[NOTIFY_COMMAND])
523                 goto error;
524
525         if (!fill_string_list(tb[NOTIFY_COMMAND], argv, ARRAY_SIZE(argv)))
526                 goto error;
527
528         if (!fill_string_list(tb[NOTIFY_ENV], env, ARRAY_SIZE(env)))
529                 goto error;
530
531         netifd_start_process((const char **) argv, (char **) env, &state->proto_task);
532
533         return 0;
534
535 error:
536         return UBUS_STATUS_INVALID_ARGUMENT;
537 }
538
539 static int
540 proto_shell_kill_command(struct proto_shell_state *state, struct blob_attr **tb)
541 {
542         unsigned int signal = ~0;
543
544         if (tb[NOTIFY_SIGNAL])
545                 signal = blobmsg_get_u32(tb[NOTIFY_SIGNAL]);
546
547         if (signal > 31)
548                 signal = SIGTERM;
549
550         if (state->proto_task.uloop.pending) {
551                 state->proto_task_killed = true;
552                 kill(state->proto_task.uloop.pid, signal);
553         }
554
555         return 0;
556 }
557
558 static int
559 proto_shell_notify_error(struct proto_shell_state *state, struct blob_attr **tb)
560 {
561         struct blob_attr *cur;
562         char *data[16];
563         int n_data = 0;
564         int rem;
565
566         if (!tb[NOTIFY_ERROR])
567                 return UBUS_STATUS_INVALID_ARGUMENT;
568
569         blobmsg_for_each_attr(cur, tb[NOTIFY_ERROR], rem) {
570                 if (n_data + 1 == ARRAY_SIZE(data))
571                         goto error;
572
573                 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
574                         goto error;
575
576                 if (!blobmsg_check_attr(cur, NULL))
577                         goto error;
578
579                 data[n_data++] = blobmsg_data(cur);
580         }
581
582         if (!n_data)
583                 goto error;
584
585         interface_add_error(state->proto.iface, state->handler->proto.name,
586                         data[0], (const char **) &data[1], n_data - 1);
587
588         return 0;
589
590 error:
591         return UBUS_STATUS_INVALID_ARGUMENT;
592 }
593
594 static int
595 proto_shell_block_restart(struct proto_shell_state *state, struct blob_attr **tb)
596 {
597         state->proto.iface->autostart = false;
598         return 0;
599 }
600
601 static int
602 proto_shell_set_available(struct proto_shell_state *state, struct blob_attr **tb)
603 {
604         if (!tb[NOTIFY_AVAILABLE])
605                 return UBUS_STATUS_INVALID_ARGUMENT;
606
607         interface_set_available(state->proto.iface, blobmsg_get_bool(tb[NOTIFY_AVAILABLE]));
608         return 0;
609 }
610
611 static int
612 proto_shell_add_host_dependency(struct proto_shell_state *state, struct blob_attr **tb)
613 {
614         struct proto_shell_dependency *dep;
615         struct blob_attr *host = tb[NOTIFY_HOST];
616
617         if (!host)
618                 return UBUS_STATUS_INVALID_ARGUMENT;
619
620         dep = calloc(1, sizeof(*dep));
621         if (!inet_pton(AF_INET, blobmsg_data(host), &dep->host)) {
622                 free(dep);
623                 return UBUS_STATUS_INVALID_ARGUMENT;
624         }
625
626         dep->proto = state;
627         dep->dep.cb = proto_shell_if_up_cb;
628         interface_add_user(&dep->dep, NULL);
629         list_add(&dep->list, &state->deps);
630         proto_shell_update_host_dep(dep);
631         if (!dep->dep.iface)
632                 return UBUS_STATUS_NOT_FOUND;
633
634         return 0;
635 }
636
637 static int
638 proto_shell_setup_failed(struct proto_shell_state *state)
639 {
640         switch (state->sm) {
641         case S_IDLE:
642                 state->proto.proto_event(&state->proto, IFPEV_LINK_LOST);
643                 /* fall through */
644         case S_SETUP:
645                 proto_shell_handler(&state->proto, PROTO_CMD_TEARDOWN, false);
646                 break;
647         default:
648                 break;
649         }
650         return 0;
651 }
652
653 static int
654 proto_shell_notify(struct interface_proto_state *proto, struct blob_attr *attr)
655 {
656         struct proto_shell_state *state;
657         struct blob_attr *tb[__NOTIFY_LAST];
658
659         state = container_of(proto, struct proto_shell_state, proto);
660
661         blobmsg_parse(notify_attr, __NOTIFY_LAST, tb, blob_data(attr), blob_len(attr));
662         if (!tb[NOTIFY_ACTION])
663                 return UBUS_STATUS_INVALID_ARGUMENT;
664
665         switch(blobmsg_get_u32(tb[NOTIFY_ACTION])) {
666         case 0:
667                 return proto_shell_update_link(state, attr, tb);
668         case 1:
669                 return proto_shell_run_command(state, tb);
670         case 2:
671                 return proto_shell_kill_command(state, tb);
672         case 3:
673                 return proto_shell_notify_error(state, tb);
674         case 4:
675                 return proto_shell_block_restart(state, tb);
676         case 5:
677                 return proto_shell_set_available(state, tb);
678         case 6:
679                 return proto_shell_add_host_dependency(state, tb);
680         case 7:
681                 return proto_shell_setup_failed(state);
682         default:
683                 return UBUS_STATUS_INVALID_ARGUMENT;
684         }
685 }
686
687 static struct interface_proto_state *
688 proto_shell_attach(const struct proto_handler *h, struct interface *iface,
689                    struct blob_attr *attr)
690 {
691         struct proto_shell_state *state;
692
693         state = calloc(1, sizeof(*state));
694         INIT_LIST_HEAD(&state->deps);
695
696         state->config = malloc(blob_pad_len(attr));
697         if (!state->config)
698                 goto error;
699
700         memcpy(state->config, attr, blob_pad_len(attr));
701         state->proto.free = proto_shell_free;
702         state->proto.notify = proto_shell_notify;
703         state->proto.cb = proto_shell_handler;
704         state->teardown_timeout.cb = proto_shell_teardown_timeout_cb;
705         state->script_task.cb = proto_shell_script_cb;
706         state->script_task.dir_fd = proto_fd.fd;
707         state->script_task.log_prefix = iface->name;
708         state->proto_task.cb = proto_shell_task_cb;
709         state->proto_task.dir_fd = proto_fd.fd;
710         state->proto_task.log_prefix = iface->name;
711         state->handler = container_of(h, struct proto_shell_handler, proto);
712
713         return &state->proto;
714
715 error:
716         free(state);
717         return NULL;
718 }
719
720 static json_object *
721 check_type(json_object *obj, json_type type)
722 {
723         if (!obj)
724                 return NULL;
725
726         if (json_object_get_type(obj) != type)
727                 return NULL;
728
729         return obj;
730 }
731
732 static inline json_object *
733 get_field(json_object *obj, const char *name, json_type type)
734 {
735         return check_type(json_object_object_get(obj, name), type);
736 }
737
738 static char *
739 proto_shell_parse_config(struct config_param_list *config, json_object *obj)
740 {
741         struct blobmsg_policy *attrs;
742         char *str_buf, *str_cur;
743         int str_len = 0;
744         int i;
745
746         config->n_params = json_object_array_length(obj);
747         attrs = calloc(1, sizeof(*attrs) * config->n_params);
748         if (!attrs)
749                 return NULL;
750
751         config->params = attrs;
752         for (i = 0; i < config->n_params; i++) {
753                 json_object *cur, *name, *type;
754
755                 cur = check_type(json_object_array_get_idx(obj, i), json_type_array);
756                 if (!cur)
757                         goto error;
758
759                 name = check_type(json_object_array_get_idx(cur, 0), json_type_string);
760                 if (!name)
761                         goto error;
762
763                 type = check_type(json_object_array_get_idx(cur, 1), json_type_int);
764                 if (!type)
765                         goto error;
766
767                 attrs[i].name = json_object_get_string(name);
768                 attrs[i].type = json_object_get_int(type);
769                 if (attrs[i].type > BLOBMSG_TYPE_LAST)
770                         goto error;
771
772                 str_len += strlen(attrs[i].name) + 1;
773         }
774
775         str_buf = malloc(str_len);
776         if (!str_buf)
777                 goto error;
778
779         str_cur = str_buf;
780         for (i = 0; i < config->n_params; i++) {
781                 const char *name = attrs[i].name;
782
783                 attrs[i].name = str_cur;
784                 str_cur += sprintf(str_cur, "%s", name) + 1;
785         }
786
787         return str_buf;
788
789 error:
790         free(attrs);
791         config->n_params = 0;
792         return NULL;
793 }
794
795 static void
796 proto_shell_add_handler(const char *script, json_object *obj)
797 {
798         struct proto_shell_handler *handler;
799         struct proto_handler *proto;
800         json_object *config, *tmp;
801         const char *name;
802         char *str;
803
804         if (!check_type(obj, json_type_object))
805                 return;
806
807         tmp = get_field(obj, "name", json_type_string);
808         if (!tmp)
809                 return;
810
811         name = json_object_get_string(tmp);
812
813         handler = calloc(1, sizeof(*handler) +
814                          strlen(script) + 1 +
815                          strlen(name) + 1);
816         if (!handler)
817                 return;
818
819         strcpy(handler->script_name, script);
820
821         str = handler->script_name + strlen(handler->script_name) + 1;
822         strcpy(str, name);
823
824         proto = &handler->proto;
825         proto->name = str;
826         proto->config_params = &handler->config;
827         proto->attach = proto_shell_attach;
828
829         tmp = get_field(obj, "no-device", json_type_boolean);
830         if (tmp && json_object_get_boolean(tmp))
831                 handler->proto.flags |= PROTO_FLAG_NODEV;
832
833         tmp = get_field(obj, "available", json_type_boolean);
834         if (tmp && json_object_get_boolean(tmp))
835                 handler->proto.flags |= PROTO_FLAG_INIT_AVAILABLE;
836
837         config = get_field(obj, "config", json_type_array);
838         if (config)
839                 handler->config_buf = proto_shell_parse_config(&handler->config, config);
840
841         DPRINTF("Add handler for script %s: %s\n", script, proto->name);
842         add_proto_handler(proto);
843 }
844
845 static void proto_shell_add_script(const char *name)
846 {
847         struct json_tokener *tok = NULL;
848         json_object *obj;
849         static char buf[512];
850         char *start, *cmd;
851         FILE *f;
852         int len;
853
854 #define DUMP_SUFFIX     " '' dump"
855
856         cmd = alloca(strlen(name) + 1 + sizeof(DUMP_SUFFIX));
857         sprintf(cmd, "%s" DUMP_SUFFIX, name);
858
859         f = popen(cmd, "r");
860         if (!f)
861                 return;
862
863         do {
864                 start = fgets(buf, sizeof(buf), f);
865                 if (!start)
866                         continue;
867
868                 len = strlen(start);
869
870                 if (!tok)
871                         tok = json_tokener_new();
872
873                 obj = json_tokener_parse_ex(tok, start, len);
874                 if (!is_error(obj)) {
875                         proto_shell_add_handler(name, obj);
876                         json_object_put(obj);
877                         json_tokener_free(tok);
878                         tok = NULL;
879                 } else if (start[len - 1] == '\n') {
880                         json_tokener_free(tok);
881                         tok = NULL;
882                 }
883         } while (!feof(f) && !ferror(f));
884
885         if (tok)
886                 json_tokener_free(tok);
887
888         pclose(f);
889 }
890
891 static void __init proto_shell_init(void)
892 {
893         glob_t g;
894         int main_fd;
895         int i;
896
897         main_fd = open(".", O_RDONLY | O_DIRECTORY);
898         if (main_fd < 0)
899                 return;
900
901         if (chdir(main_path)) {
902                 perror("chdir(main path)");
903                 goto close_cur;
904         }
905
906         if (chdir("./proto"))
907                 goto close_cur;
908
909         proto_fd.fd = open(".", O_RDONLY | O_DIRECTORY);
910         if (proto_fd.fd < 0)
911                 goto close_cur;
912
913         netifd_fd_add(&proto_fd);
914         glob("./*.sh", 0, NULL, &g);
915         for (i = 0; i < g.gl_pathc; i++)
916                 proto_shell_add_script(g.gl_pathv[i]);
917
918 close_cur:
919         fchdir(main_fd);
920         close(main_fd);
921 }