#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,
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
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;
}