proto-shell: allow running protocols without proto_task.
[project/netifd.git] / proto-shell.c
index 7c23caa..50f1b5b 100644 (file)
@@ -44,6 +44,7 @@ struct proto_shell_handler {
        char *config_buf;
        char *script_name;
        bool init_available;
+       bool no_proto_task;
 
        struct uci_blob_param_list config;
 };
@@ -156,10 +157,22 @@ proto_shell_handler(struct interface_proto_state *proto,
        proc = &state->script_task;
 
        if (cmd == PROTO_CMD_SETUP) {
-               action = "setup";
-               state->last_error = -1;
-               proto_shell_clear_host_dep(state);
-               state->sm = S_SETUP;
+               switch (state->sm) {
+               case S_IDLE:
+                       action = "setup";
+                       state->last_error = -1;
+                       proto_shell_clear_host_dep(state);
+                       state->sm = S_SETUP;
+                       break;
+
+               case S_SETUP_ABORT:
+               case S_TEARDOWN:
+               case S_SETUP:
+                       return 0;
+
+               default:
+                       return -1;
+               }
        } else if (cmd == PROTO_CMD_RENEW) {
                if (!(handler->proto.flags & PROTO_FLAG_RENEW_AVAILABLE))
                        return 0;
@@ -172,28 +185,35 @@ proto_shell_handler(struct interface_proto_state *proto,
                state->renew_pending = false;
                action = "renew";
        } else {
-               if (state->sm == S_TEARDOWN)
-                       return 0;
-
-               state->renew_pending = false;
-               if (state->script_task.uloop.pending) {
-                       if (state->sm != S_SETUP_ABORT) {
+               switch (state->sm) {
+               case S_SETUP:
+                       if (state->script_task.uloop.pending) {
                                uloop_timeout_set(&state->teardown_timeout, 1000);
                                kill(state->script_task.uloop.pid, SIGTERM);
                                if (state->proto_task.uloop.pending)
                                        kill(state->proto_task.uloop.pid, SIGTERM);
+                               state->renew_pending = false;
                                state->sm = S_SETUP_ABORT;
+                               return 0;
                        }
+               /* fall through if no script task is running */
+               case S_IDLE:
+                       action = "teardown";
+                       state->renew_pending = false;
+                       state->sm = S_TEARDOWN;
+                       if (state->last_error >= 0) {
+                               snprintf(error_buf, sizeof(error_buf), "ERROR=%d", state->last_error);
+                               envp[j++] = error_buf;
+                       }
+                       uloop_timeout_set(&state->teardown_timeout, 5000);
+                       break;
+
+               case S_TEARDOWN:
                        return 0;
-               }
 
-               action = "teardown";
-               state->sm = S_TEARDOWN;
-               if (state->last_error >= 0) {
-                       snprintf(error_buf, sizeof(error_buf), "ERROR=%d", state->last_error);
-                       envp[j++] = error_buf;
+               default:
+                       return -1;
                }
-               uloop_timeout_set(&state->teardown_timeout, 5000);
        }
 
        D(INTERFACE, "run %s for interface '%s'\n", action, proto->iface->name);
@@ -265,9 +285,17 @@ proto_shell_task_finish(struct proto_shell_state *state,
                if (task == &state->proto_task)
                        proto_shell_handler(&state->proto, PROTO_CMD_TEARDOWN,
                                            false);
-               if (task == &state->script_task && state->renew_pending)
-                       proto_shell_handler(&state->proto, PROTO_CMD_RENEW,
-                                           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);
+               }
                break;
 
        case S_SETUP_ABORT:
@@ -506,9 +534,11 @@ proto_shell_update_link(struct proto_shell_state *state, struct blob_attr *data,
 
        interface_update_complete(state->proto.iface);
 
-       if (!keep)
-               state->proto.proto_event(&state->proto, IFPEV_UP);
-       state->sm = S_IDLE;
+       if ((state->sm != S_SETUP_ABORT) && (state->sm != S_TEARDOWN)) {
+               if (!keep)
+                       state->proto.proto_event(&state->proto, IFPEV_UP);
+               state->sm = S_IDLE;
+       }
 
        return 0;
 }
@@ -790,6 +820,12 @@ proto_shell_add_handler(const char *script, const char *name, json_object *obj)
        if (tmp && json_object_get_boolean(tmp))
                handler->proto.flags |= PROTO_FLAG_NODEV;
 
+       tmp = json_get_field(obj, "no-proto-task", json_type_boolean);
+       if (tmp && json_object_get_boolean(tmp))
+               handler->no_proto_task = true;
+       else
+               handler->no_proto_task = false;
+
        tmp = json_get_field(obj, "available", json_type_boolean);
        if (tmp && json_object_get_boolean(tmp))
                handler->proto.flags |= PROTO_FLAG_INIT_AVAILABLE;
@@ -798,6 +834,10 @@ proto_shell_add_handler(const char *script, const char *name, json_object *obj)
        if (tmp && json_object_get_boolean(tmp))
                handler->proto.flags |= PROTO_FLAG_RENEW_AVAILABLE;
 
+       tmp = json_get_field(obj, "lasterror", json_type_boolean);
+       if (tmp && json_object_get_boolean(tmp))
+               handler->proto.flags |= PROTO_FLAG_LASTERROR;
+
        config = json_get_field(obj, "config", json_type_array);
        if (config)
                handler->config_buf = netifd_handler_parse_config(&handler->config, config);