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