proto-shell: refactor code to allow specifying per-address broadcast option for ipv4
[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         netifd_kill_process(&state->script_task);
320         netifd_kill_process(&state->proto_task);
321         free(state->config);
322         free(state);
323 }
324
325 static void
326 proto_shell_parse_route_list(struct interface *iface, struct blob_attr *attr,
327                              bool v6)
328 {
329         struct blob_attr *cur;
330         int rem;
331
332         blobmsg_for_each_attr(cur, attr, rem) {
333                 if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE) {
334                         DPRINTF("Ignore wrong route type: %d\n", blobmsg_type(cur));
335                         continue;
336                 }
337
338                 interface_ip_add_route(iface, cur, v6);
339         }
340 }
341
342 static void
343 proto_shell_parse_data(struct interface *iface, struct blob_attr *attr)
344 {
345         struct blob_attr *cur;
346         int rem;
347
348         blobmsg_for_each_attr(cur, attr, rem)
349                 interface_add_data(iface, cur);
350 }
351
352 static struct device *
353 proto_shell_create_tunnel(const char *name, struct blob_attr *attr)
354 {
355         struct device *dev;
356         struct blob_buf b;
357
358         memset(&b, 0, sizeof(b));
359         blob_buf_init(&b, 0);
360         blob_put(&b, 0, blobmsg_data(attr), blobmsg_data_len(attr));
361         dev = device_create(name, &tunnel_device_type, blob_data(b.head));
362         blob_buf_free(&b);
363
364         return dev;
365 }
366
367 enum {
368         NOTIFY_ACTION,
369         NOTIFY_ERROR,
370         NOTIFY_COMMAND,
371         NOTIFY_ENV,
372         NOTIFY_SIGNAL,
373         NOTIFY_AVAILABLE,
374         NOTIFY_LINK_UP,
375         NOTIFY_IFNAME,
376         NOTIFY_ADDR_EXT,
377         NOTIFY_ROUTES,
378         NOTIFY_ROUTES6,
379         NOTIFY_TUNNEL,
380         NOTIFY_DATA,
381         NOTIFY_KEEP,
382         NOTIFY_HOST,
383         __NOTIFY_LAST
384 };
385
386 static const struct blobmsg_policy notify_attr[__NOTIFY_LAST] = {
387         [NOTIFY_ACTION] = { .name = "action", .type = BLOBMSG_TYPE_INT32 },
388         [NOTIFY_ERROR] = { .name = "error", .type = BLOBMSG_TYPE_ARRAY },
389         [NOTIFY_COMMAND] = { .name = "command", .type = BLOBMSG_TYPE_ARRAY },
390         [NOTIFY_ENV] = { .name = "env", .type = BLOBMSG_TYPE_ARRAY },
391         [NOTIFY_SIGNAL] = { .name = "signal", .type = BLOBMSG_TYPE_INT32 },
392         [NOTIFY_AVAILABLE] = { .name = "available", .type = BLOBMSG_TYPE_BOOL },
393         [NOTIFY_LINK_UP] = { .name = "link-up", .type = BLOBMSG_TYPE_BOOL },
394         [NOTIFY_IFNAME] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING },
395         [NOTIFY_ADDR_EXT] = { .name = "address-external", .type = BLOBMSG_TYPE_BOOL },
396         [NOTIFY_ROUTES] = { .name = "routes", .type = BLOBMSG_TYPE_ARRAY },
397         [NOTIFY_ROUTES6] = { .name = "routes6", .type = BLOBMSG_TYPE_ARRAY },
398         [NOTIFY_TUNNEL] = { .name = "tunnel", .type = BLOBMSG_TYPE_TABLE },
399         [NOTIFY_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE },
400         [NOTIFY_KEEP] = { .name = "keep", .type = BLOBMSG_TYPE_BOOL },
401         [NOTIFY_HOST] = { .name = "host", .type = BLOBMSG_TYPE_STRING },
402 };
403
404 static int
405 proto_shell_update_link(struct proto_shell_state *state, struct blob_attr *data, struct blob_attr **tb)
406 {
407         struct interface *iface = state->proto.iface;
408         struct blob_attr *cur;
409         struct device *dev;
410         const char *devname;
411         int dev_create = 1;
412         bool addr_ext = false;
413         bool keep = false;
414         bool up;
415
416         if (!tb[NOTIFY_LINK_UP])
417                 return UBUS_STATUS_INVALID_ARGUMENT;
418
419         up = blobmsg_get_bool(tb[NOTIFY_LINK_UP]);
420         if (!up) {
421                 state->proto.proto_event(&state->proto, IFPEV_LINK_LOST);
422                 return 0;
423         }
424
425         if ((cur = tb[NOTIFY_KEEP]) != NULL)
426                 keep = blobmsg_get_bool(cur);
427
428         if ((cur = tb[NOTIFY_ADDR_EXT]) != NULL) {
429                 addr_ext = blobmsg_get_bool(cur);
430                 if (addr_ext)
431                         dev_create = 2;
432         }
433
434         if (!tb[NOTIFY_IFNAME]) {
435                 if (!iface->main_dev.dev)
436                         return UBUS_STATUS_INVALID_ARGUMENT;
437         } else if (!keep || iface->state != IFS_UP) {
438                 keep = false;
439                 devname = blobmsg_data(tb[NOTIFY_IFNAME]);
440                 if (tb[NOTIFY_TUNNEL]) {
441                         dev = proto_shell_create_tunnel(devname,
442                                 tb[NOTIFY_TUNNEL]);
443                         if (!dev)
444                                 return UBUS_STATUS_INVALID_ARGUMENT;
445                 } else {
446                         dev = device_get(devname, dev_create);
447                         if (!dev)
448                                 return UBUS_STATUS_NOT_FOUND;
449                 }
450
451                 interface_set_l3_dev(iface, dev);
452                 device_claim(&iface->l3_dev);
453         }
454
455         if (!keep)
456                 interface_update_start(iface);
457
458         proto_apply_ip_settings(iface, data, addr_ext);
459
460         if ((cur = tb[NOTIFY_ROUTES]) != NULL)
461                 proto_shell_parse_route_list(state->proto.iface, cur, false);
462
463         if ((cur = tb[NOTIFY_ROUTES6]) != NULL)
464                 proto_shell_parse_route_list(state->proto.iface, cur, true);
465
466         interface_update_complete(state->proto.iface);
467
468         if (!keep)
469                 state->proto.proto_event(&state->proto, IFPEV_UP);
470         state->sm = S_IDLE;
471
472         if ((cur = tb[NOTIFY_DATA]))
473                 proto_shell_parse_data(state->proto.iface, cur);
474
475         return 0;
476 }
477
478 static bool
479 fill_string_list(struct blob_attr *attr, char **argv, int max)
480 {
481         struct blob_attr *cur;
482         int argc = 0;
483         int rem;
484
485         if (!attr)
486                 goto out;
487
488         blobmsg_for_each_attr(cur, attr, rem) {
489                 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
490                         return false;
491
492                 if (!blobmsg_check_attr(cur, NULL))
493                         return false;
494
495                 argv[argc++] = blobmsg_data(cur);
496                 if (argc == max - 1)
497                         return false;
498         }
499
500 out:
501         argv[argc] = NULL;
502         return true;
503 }
504
505 static int
506 proto_shell_run_command(struct proto_shell_state *state, struct blob_attr **tb)
507 {
508         static char *argv[64];
509         static char *env[32];
510
511         if (!tb[NOTIFY_COMMAND])
512                 goto error;
513
514         if (!fill_string_list(tb[NOTIFY_COMMAND], argv, ARRAY_SIZE(argv)))
515                 goto error;
516
517         if (!fill_string_list(tb[NOTIFY_ENV], env, ARRAY_SIZE(env)))
518                 goto error;
519
520         netifd_start_process((const char **) argv, (char **) env, &state->proto_task);
521
522         return 0;
523
524 error:
525         return UBUS_STATUS_INVALID_ARGUMENT;
526 }
527
528 static int
529 proto_shell_kill_command(struct proto_shell_state *state, struct blob_attr **tb)
530 {
531         unsigned int signal = ~0;
532
533         if (tb[NOTIFY_SIGNAL])
534                 signal = blobmsg_get_u32(tb[NOTIFY_SIGNAL]);
535
536         if (signal > 31)
537                 signal = SIGTERM;
538
539         if (state->proto_task.uloop.pending) {
540                 state->proto_task_killed = true;
541                 kill(state->proto_task.uloop.pid, signal);
542         }
543
544         return 0;
545 }
546
547 static int
548 proto_shell_notify_error(struct proto_shell_state *state, struct blob_attr **tb)
549 {
550         struct blob_attr *cur;
551         char *data[16];
552         int n_data = 0;
553         int rem;
554
555         if (!tb[NOTIFY_ERROR])
556                 return UBUS_STATUS_INVALID_ARGUMENT;
557
558         blobmsg_for_each_attr(cur, tb[NOTIFY_ERROR], rem) {
559                 if (n_data + 1 == ARRAY_SIZE(data))
560                         goto error;
561
562                 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
563                         goto error;
564
565                 if (!blobmsg_check_attr(cur, NULL))
566                         goto error;
567
568                 data[n_data++] = blobmsg_data(cur);
569         }
570
571         if (!n_data)
572                 goto error;
573
574         interface_add_error(state->proto.iface, state->handler->proto.name,
575                         data[0], (const char **) &data[1], n_data - 1);
576
577         return 0;
578
579 error:
580         return UBUS_STATUS_INVALID_ARGUMENT;
581 }
582
583 static int
584 proto_shell_block_restart(struct proto_shell_state *state, struct blob_attr **tb)
585 {
586         state->proto.iface->autostart = false;
587         return 0;
588 }
589
590 static int
591 proto_shell_set_available(struct proto_shell_state *state, struct blob_attr **tb)
592 {
593         if (!tb[NOTIFY_AVAILABLE])
594                 return UBUS_STATUS_INVALID_ARGUMENT;
595
596         interface_set_available(state->proto.iface, blobmsg_get_bool(tb[NOTIFY_AVAILABLE]));
597         return 0;
598 }
599
600 static int
601 proto_shell_add_host_dependency(struct proto_shell_state *state, struct blob_attr **tb)
602 {
603         struct proto_shell_dependency *dep;
604         struct blob_attr *host = tb[NOTIFY_HOST];
605
606         if (!host)
607                 return UBUS_STATUS_INVALID_ARGUMENT;
608
609         dep = calloc(1, sizeof(*dep));
610         if (!inet_pton(AF_INET, blobmsg_data(host), &dep->host)) {
611                 free(dep);
612                 return UBUS_STATUS_INVALID_ARGUMENT;
613         }
614
615         dep->proto = state;
616         dep->dep.cb = proto_shell_if_up_cb;
617         interface_add_user(&dep->dep, NULL);
618         list_add(&dep->list, &state->deps);
619         proto_shell_update_host_dep(dep);
620         if (!dep->dep.iface)
621                 return UBUS_STATUS_NOT_FOUND;
622
623         return 0;
624 }
625
626 static int
627 proto_shell_notify(struct interface_proto_state *proto, struct blob_attr *attr)
628 {
629         struct proto_shell_state *state;
630         struct blob_attr *tb[__NOTIFY_LAST];
631
632         state = container_of(proto, struct proto_shell_state, proto);
633
634         blobmsg_parse(notify_attr, __NOTIFY_LAST, tb, blob_data(attr), blob_len(attr));
635         if (!tb[NOTIFY_ACTION])
636                 return UBUS_STATUS_INVALID_ARGUMENT;
637
638         switch(blobmsg_get_u32(tb[NOTIFY_ACTION])) {
639         case 0:
640                 return proto_shell_update_link(state, attr, tb);
641         case 1:
642                 return proto_shell_run_command(state, tb);
643         case 2:
644                 return proto_shell_kill_command(state, tb);
645         case 3:
646                 return proto_shell_notify_error(state, tb);
647         case 4:
648                 return proto_shell_block_restart(state, tb);
649         case 5:
650                 return proto_shell_set_available(state, tb);
651         case 6:
652                 return proto_shell_add_host_dependency(state, tb);
653         default:
654                 return UBUS_STATUS_INVALID_ARGUMENT;
655         }
656 }
657
658 static struct interface_proto_state *
659 proto_shell_attach(const struct proto_handler *h, struct interface *iface,
660                    struct blob_attr *attr)
661 {
662         struct proto_shell_state *state;
663
664         state = calloc(1, sizeof(*state));
665         INIT_LIST_HEAD(&state->deps);
666
667         state->config = malloc(blob_pad_len(attr));
668         if (!state->config)
669                 goto error;
670
671         memcpy(state->config, attr, blob_pad_len(attr));
672         state->proto.free = proto_shell_free;
673         state->proto.notify = proto_shell_notify;
674         state->proto.cb = proto_shell_handler;
675         state->teardown_timeout.cb = proto_shell_teardown_timeout_cb;
676         state->script_task.cb = proto_shell_script_cb;
677         state->script_task.dir_fd = proto_fd.fd;
678         state->script_task.log_prefix = iface->name;
679         state->proto_task.cb = proto_shell_task_cb;
680         state->proto_task.dir_fd = proto_fd.fd;
681         state->proto_task.log_prefix = iface->name;
682         state->handler = container_of(h, struct proto_shell_handler, proto);
683
684         return &state->proto;
685
686 error:
687         free(state);
688         return NULL;
689 }
690
691 static json_object *
692 check_type(json_object *obj, json_type type)
693 {
694         if (!obj)
695                 return NULL;
696
697         if (json_object_get_type(obj) != type)
698                 return NULL;
699
700         return obj;
701 }
702
703 static inline json_object *
704 get_field(json_object *obj, const char *name, json_type type)
705 {
706         return check_type(json_object_object_get(obj, name), type);
707 }
708
709 static char *
710 proto_shell_parse_config(struct config_param_list *config, json_object *obj)
711 {
712         struct blobmsg_policy *attrs;
713         char *str_buf, *str_cur;
714         int str_len = 0;
715         int i;
716
717         config->n_params = json_object_array_length(obj);
718         attrs = calloc(1, sizeof(*attrs) * config->n_params);
719         if (!attrs)
720                 return NULL;
721
722         config->params = attrs;
723         for (i = 0; i < config->n_params; i++) {
724                 json_object *cur, *name, *type;
725
726                 cur = check_type(json_object_array_get_idx(obj, i), json_type_array);
727                 if (!cur)
728                         goto error;
729
730                 name = check_type(json_object_array_get_idx(cur, 0), json_type_string);
731                 if (!name)
732                         goto error;
733
734                 type = check_type(json_object_array_get_idx(cur, 1), json_type_int);
735                 if (!type)
736                         goto error;
737
738                 attrs[i].name = json_object_get_string(name);
739                 attrs[i].type = json_object_get_int(type);
740                 if (attrs[i].type > BLOBMSG_TYPE_LAST)
741                         goto error;
742
743                 str_len += strlen(attrs[i].name) + 1;
744         }
745
746         str_buf = malloc(str_len);
747         if (!str_buf)
748                 goto error;
749
750         str_cur = str_buf;
751         for (i = 0; i < config->n_params; i++) {
752                 const char *name = attrs[i].name;
753
754                 attrs[i].name = str_cur;
755                 str_cur += sprintf(str_cur, "%s", name) + 1;
756         }
757
758         return str_buf;
759
760 error:
761         free(attrs);
762         config->n_params = 0;
763         return NULL;
764 }
765
766 static void
767 proto_shell_add_handler(const char *script, json_object *obj)
768 {
769         struct proto_shell_handler *handler;
770         struct proto_handler *proto;
771         json_object *config, *tmp;
772         const char *name;
773         char *str;
774
775         if (!check_type(obj, json_type_object))
776                 return;
777
778         tmp = get_field(obj, "name", json_type_string);
779         if (!tmp)
780                 return;
781
782         name = json_object_get_string(tmp);
783
784         handler = calloc(1, sizeof(*handler) +
785                          strlen(script) + 1 +
786                          strlen(name) + 1);
787         if (!handler)
788                 return;
789
790         strcpy(handler->script_name, script);
791
792         str = handler->script_name + strlen(handler->script_name) + 1;
793         strcpy(str, name);
794
795         proto = &handler->proto;
796         proto->name = str;
797         proto->config_params = &handler->config;
798         proto->attach = proto_shell_attach;
799
800         tmp = get_field(obj, "no-device", json_type_boolean);
801         if (tmp && json_object_get_boolean(tmp))
802                 handler->proto.flags |= PROTO_FLAG_NODEV;
803
804         tmp = get_field(obj, "available", json_type_boolean);
805         if (tmp && json_object_get_boolean(tmp))
806                 handler->proto.flags |= PROTO_FLAG_INIT_AVAILABLE;
807
808         config = get_field(obj, "config", json_type_array);
809         if (config)
810                 handler->config_buf = proto_shell_parse_config(&handler->config, config);
811
812         DPRINTF("Add handler for script %s: %s\n", script, proto->name);
813         add_proto_handler(proto);
814 }
815
816 static void proto_shell_add_script(const char *name)
817 {
818         struct json_tokener *tok = NULL;
819         json_object *obj;
820         static char buf[512];
821         char *start, *cmd;
822         FILE *f;
823         int len;
824
825 #define DUMP_SUFFIX     " '' dump"
826
827         cmd = alloca(strlen(name) + 1 + sizeof(DUMP_SUFFIX));
828         sprintf(cmd, "%s" DUMP_SUFFIX, name);
829
830         f = popen(cmd, "r");
831         if (!f)
832                 return;
833
834         do {
835                 start = fgets(buf, sizeof(buf), f);
836                 if (!start)
837                         continue;
838
839                 len = strlen(start);
840
841                 if (!tok)
842                         tok = json_tokener_new();
843
844                 obj = json_tokener_parse_ex(tok, start, len);
845                 if (!is_error(obj)) {
846                         proto_shell_add_handler(name, obj);
847                         json_object_put(obj);
848                         json_tokener_free(tok);
849                         tok = NULL;
850                 } else if (start[len - 1] == '\n') {
851                         json_tokener_free(tok);
852                         tok = NULL;
853                 }
854         } while (!feof(f) && !ferror(f));
855
856         if (tok)
857                 json_tokener_free(tok);
858
859         pclose(f);
860 }
861
862 static void __init proto_shell_init(void)
863 {
864         glob_t g;
865         int main_fd;
866         int i;
867
868         main_fd = open(".", O_RDONLY | O_DIRECTORY);
869         if (main_fd < 0)
870                 return;
871
872         if (chdir(main_path)) {
873                 perror("chdir(main path)");
874                 goto close_cur;
875         }
876
877         if (chdir("./proto"))
878                 goto close_cur;
879
880         proto_fd.fd = open(".", O_RDONLY | O_DIRECTORY);
881         if (proto_fd.fd < 0)
882                 goto close_cur;
883
884         netifd_fd_add(&proto_fd);
885         glob("./*.sh", 0, NULL, &g);
886         for (i = 0; i < g.gl_pathc; i++)
887                 proto_shell_add_script(g.gl_pathv[i]);
888
889 close_cur:
890         fchdir(main_fd);
891         close(main_fd);
892 }