unit_start: Optionally wait for job finish 26/205926/2
authorPaweł Szewczyk <p.szewczyk@samsung.com>
Fri, 10 May 2019 09:57:47 +0000 (11:57 +0200)
committerPaweł Szewczyk <p.szewczyk@samsung.com>
Thu, 16 May 2019 09:45:38 +0000 (11:45 +0200)
This makes an action asynchronously wait for JobRemoved signal, meaning
start/stop action was finished.

Change-Id: I9195b5e3d0eec6421919a7bb3d49dd1399a8694f
Signed-off-by: Paweł Szewczyk <p.szewczyk@samsung.com>
src/action/unit_start.c

index 21bbd920955c3935a1ea635ecb6b57b940a3d208..40a46a72fc884c03b46cd57bf5de29ef4b04ad46 100644 (file)
 #include "systemd_dbus.h"
 #include "common.h"
 
+struct unit_action_data {
+       struct action_executed_event *exe_info;
+       bool wait_for_unit;
+       char *job;
+       sd_bus_slot *slot;
+};
+
+static int finish_action(struct unit_action_data *data, int result, const char *err_msg)
+{
+       int ret;
+
+       if (err_msg)
+               epc_object_append_string(data->exe_info->action_log, "error", err_msg);
+
+       data->exe_info->result = result;
+       ret = event_processor_report_event(&data->exe_info->event);
+       epc_event_unref(&data->exe_info->event);
+       free(data);
+
+       return ret;
+}
+
+static int on_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
+{
+       struct unit_action_data *data = userdata;
+       char *job;
+       int ret;
+
+       if (!data)
+               return -EINVAL;
+
+       ret = sd_bus_message_read(m, "uoss", NULL, &job, NULL, NULL);
+       if (ret < 0) {
+               log_error("Could not read JobRemoved signal");
+               return -EINVAL;
+       }
+
+       if (strcmp(data->job, job) == 0)
+                finish_action(data, 0, NULL);
+
+       return 0;
+}
+
 static int unit_action_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
 {
-       struct action_executed_event *exe_info = userdata;
+       struct unit_action_data *data = userdata;
+       _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
        int ret;
 
-       if (!exe_info)
+       if (!data)
                return -EINVAL;
 
-       exe_info->result = 0;
-       ret = event_processor_report_event(&exe_info->event);
-       epc_event_unref(&exe_info->event);
+       ret = epc_acquire_systemd_bus(&bus);
+       if (ret < 0)
+               return finish_action(data, ret, "Could not acquire systemd bus");
 
-       return ret;
+       if (data->wait_for_unit) {
+               ret = sd_bus_add_match(bus, &data->slot,
+                               "type='signal',sender='org.freedesktop.systemd1',"
+                               "interface='org.freedesktop.systemd1.Manager',"
+                               "member='JobRemoved',"
+                               "path_namespace='/org/freedesktop/systemd1'",
+                               on_job_removed,
+                               data);
+               if (ret < 0) {
+                       return finish_action(data, ret, "Could not register for JobRemoved signal");
+               }
+
+               return 0;
+       }
+
+       return finish_action(data, 0, NULL);
 }
 
 static int start_unit(struct epc_action *action,
@@ -46,6 +105,7 @@ static int start_unit(struct epc_action *action,
        char *unit_name = NULL;
        char *unit_action = NULL;
        int ret;
+       struct unit_action_data *data;
 
        /*
         * We are passing an event to exe_info, so there is no need to unref it
@@ -62,41 +122,45 @@ static int start_unit(struct epc_action *action,
                return 0;
        }
 
-       epc_object_get_string(dm_ev->action_data,
+       data = malloc(sizeof(*data));
+       if (!data)
+               return -ENOMEM;
+
+       data->exe_info = exe_info;
+
+       ret = epc_object_get_bool(dm_ev->action_data, "wait", &data->wait_for_unit);
+       if (ret < 0)
+               data->wait_for_unit = false;
+
+       ret = epc_object_get_string(dm_ev->action_data,
                        EPC_AD_UNIT_ACTION, &unit_action);
-       if (!unit_action) {
-               epc_object_append_string(exe_info->action_log, "error",
-                               "Unit action not specified");
-               exe_info->result = -EINVAL;
-               return 0;
-       }
+       if (!unit_action)
+               return finish_action(data, -EINVAL, "Unit action not specified");
 
        if (strcmp(unit_action, "StartUnit") &&
                        strcmp(unit_action, "StopUnit") &&
-                       strcmp(unit_action, "RestartUnit")) {
-               epc_object_append_string(exe_info->action_log, "error",
+                       strcmp(unit_action, "RestartUnit"))
+               return finish_action(data, -EINVAL,
                                "Unit action is not one of \"StartUnit\", "
                                "\"StopUnit\" or \"RestartUnit\"");
-               exe_info->result = -EINVAL;
-               return 0;
-       }
 
        ret = epc_dbus_call_systemd_simple_async(SYSTEMD_OBJ,
                        SYSTEMD_MANAGER_INTERFACE,
                        unit_action,
                        unit_action_handler,
-                       exe_info,
+                       data,
                        "ss",
                        unit_name,
                        "replace");
        if (ret < 0)
-               epc_object_append_string(exe_info->action_log, "error",
-                               "Failed to call systemd");
+               return finish_action(data, ret, "Failed to call systemd");
        else
                log_kmsg("Starting unit: %s", unit_name);
 
+       /* The result will be delayed */
        exe_info->result = -EPROBE_DEFER;
        epc_event_ref(&exe_info->event);
+
        return 0;
 }