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