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