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