+proto_shell_if_up_cb(struct interface_user *dep, struct interface *iface,
+ enum interface_event ev)
+{
+ struct proto_shell_dependency *pdep;
+
+ if (ev != IFEV_UP && ev != IFEV_UPDATE)
+ return;
+
+ pdep = container_of(dep, struct proto_shell_dependency, dep);
+ proto_shell_update_host_dep(pdep);
+}
+
+static void
+proto_shell_if_down_cb(struct interface_user *dep, struct interface *iface,
+ enum interface_event ev)
+{
+ struct proto_shell_dependency *pdep;
+ struct proto_shell_state *state;
+
+ if (ev == IFEV_UP || ev == IFEV_UPDATE)
+ return;
+
+ pdep = container_of(dep, struct proto_shell_dependency, dep);
+ interface_remove_user(dep);
+ dep->cb = proto_shell_if_up_cb;
+ interface_add_user(dep, NULL);
+
+ state = pdep->proto;
+ if (state->sm == S_IDLE) {
+ state->proto.proto_event(&state->proto, IFPEV_LINK_LOST);
+ proto_shell_handler(&state->proto, PROTO_CMD_TEARDOWN, false);
+ }
+}
+
+static void
+proto_shell_task_finish(struct proto_shell_state *state,
+ struct netifd_process *task)
+{
+ switch (state->sm) {
+ case S_IDLE:
+ if (task == &state->proto_task)
+ state->proto.proto_event(&state->proto, IFPEV_LINK_LOST);
+ /* fall through */
+ case S_SETUP:
+ if (task == &state->proto_task)
+ proto_shell_handler(&state->proto, PROTO_CMD_TEARDOWN,
+ false);
+ else if (task == &state->script_task) {
+ if (state->renew_pending)
+ proto_shell_handler(&state->proto,
+ PROTO_CMD_RENEW, false);
+ else if (!state->handler->no_proto_task &&
+ !state->proto_task.uloop.pending &&
+ state->sm == S_SETUP)
+ proto_shell_handler(&state->proto,
+ PROTO_CMD_TEARDOWN,
+ false);
+
+ /* check up status after setup attempt by this script_task */
+ if (state->sm == S_SETUP && state->checkup_interval > 0) {
+ uloop_timeout_set(&state->checkup_timeout,
+ state->checkup_interval * 1000);
+ }
+ }
+ break;
+
+ case S_SETUP_ABORT:
+ if (state->script_task.uloop.pending ||
+ state->proto_task.uloop.pending)
+ break;
+
+ /* completed aborting all tasks, now idle */
+ uloop_timeout_cancel(&state->teardown_timeout);
+ uloop_timeout_cancel(&state->checkup_timeout);
+ state->sm = S_IDLE;
+ proto_shell_handler(&state->proto, PROTO_CMD_TEARDOWN, false);
+ break;
+
+ case S_TEARDOWN:
+ if (state->script_task.uloop.pending)
+ break;
+
+ if (state->proto_task.uloop.pending) {
+ if (!state->proto_task_killed)
+ kill(state->proto_task.uloop.pid, SIGTERM);
+ break;
+ }
+
+ /* completed tearing down all tasks, now idle */
+ uloop_timeout_cancel(&state->teardown_timeout);
+ uloop_timeout_cancel(&state->checkup_timeout);
+ state->sm = S_IDLE;
+ state->proto.proto_event(&state->proto, IFPEV_DOWN);
+ break;
+ }
+}
+
+static void
+proto_shell_teardown_timeout_cb(struct uloop_timeout *timeout)
+{
+ struct proto_shell_state *state;
+
+ state = container_of(timeout, struct proto_shell_state, teardown_timeout);
+
+ netifd_kill_process(&state->script_task);
+ netifd_kill_process(&state->proto_task);
+ proto_shell_task_finish(state, NULL);
+}
+
+static void
+proto_shell_script_cb(struct netifd_process *p, int ret)
+{
+ struct proto_shell_state *state;
+
+ state = container_of(p, struct proto_shell_state, script_task);
+ proto_shell_task_finish(state, p);
+}
+
+static void
+proto_shell_task_cb(struct netifd_process *p, int ret)
+{
+ struct proto_shell_state *state;
+
+ state = container_of(p, struct proto_shell_state, proto_task);
+
+ if (state->sm == S_IDLE || state->sm == S_SETUP)
+ state->last_error = WEXITSTATUS(ret);
+
+ proto_shell_task_finish(state, p);
+}
+
+static void