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