summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaciej Slodczyk <m.slodczyk2@partner.samsung.com>2019-04-26 15:23:45 +0200
committerPaweł Szewczyk <p.szewczyk@samsung.com>2019-04-30 16:35:49 +0200
commitc760fc6dbc91ce42dfc4774f31d713c4d0734caf (patch)
tree128d6171b47da4b3542cd84a7d5079180e5b1b60
parenteeadf741b6381385b793fbbb8aae703b10f1b389 (diff)
downloadactivationd-c760fc6dbc91ce42dfc4774f31d713c4d0734caf.tar.gz
activationd-c760fc6dbc91ce42dfc4774f31d713c4d0734caf.tar.bz2
activationd-c760fc6dbc91ce42dfc4774f31d713c4d0734caf.zip
unit control: add unit instances and wildcards support
Change-Id: Iac25e794764d79d6560ab1af39d99b71f50ffc58 Signed-off-by: Maciej Slodczyk <m.slodczyk2@partner.samsung.com>
-rw-r--r--include/unit_control_event.h3
-rw-r--r--src/decision_makers/unit_control_dm.c329
-rw-r--r--src/event_types/unit_control_event.c1
-rw-r--r--src/listeners/unit_control_api.c12
4 files changed, 323 insertions, 22 deletions
diff --git a/include/unit_control_event.h b/include/unit_control_event.h
index 40d40e9..584b270 100644
--- a/include/unit_control_event.h
+++ b/include/unit_control_event.h
@@ -20,6 +20,7 @@
#define UNIT_CONTROL_EVENT_H
#include <time.h>
+#include <systemd/sd-bus.h>
#include "event.h"
#define UC_EVENT_ID "unit_control"
@@ -29,12 +30,14 @@
struct unit_control_event {
struct epc_event event;
+ sd_bus_message *m;
char *method;
char *unit;
time_t event_time;
};
struct unit_control_event_data {
+ sd_bus_message *m;
char *method;
char *unit;
time_t event_time;
diff --git a/src/decision_makers/unit_control_dm.c b/src/decision_makers/unit_control_dm.c
index 6d8477d..077c792 100644
--- a/src/decision_makers/unit_control_dm.c
+++ b/src/decision_makers/unit_control_dm.c
@@ -18,6 +18,7 @@
#include <malloc.h>
#include <errno.h>
#include <string.h>
+#include <systemd/sd-bus.h>
#include "decision_made_event.h"
#include "action.h"
@@ -27,6 +28,7 @@
#include "common.h"
#include "database.h"
#include "json-config.h"
+#include "systemd_dbus.h"
#include "unit_control_event.h"
@@ -42,6 +44,11 @@ struct whitelist_entry {
struct list_head node;
};
+struct unit_info {
+ char *unit;
+ struct list_head node;
+};
+
static int unit_control_event_match(struct epc_event_handler *handler,
struct epc_event *ev)
{
@@ -50,8 +57,148 @@ static int unit_control_event_match(struct epc_event_handler *handler,
return 0;
}
+static int list_unit_by_pattern(const char *pattern, sd_bus_message **reply)
+{
+ assert(pattern);
+
+ __attribute__((cleanup(sd_bus_message_unrefp))) sd_bus_message *m = NULL;
+ __attribute__((cleanup(sd_bus_error_free))) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int ret;
+ sd_bus *bus = NULL;
+ char *prefix = NULL;
+ char *joined = NULL;
+
+ char *patterns[2] = {};
+
+ char *unit_states[] = {
+ "running",
+ NULL
+ };
+
+ ret = epc_acquire_systemd_bus(&bus);
+ if (ret < 0) {
+ log_error_errno(ret, "failed to acquire the default system bus connection: %m");
+ return -EINVAL;
+ }
+
+ ret = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ SYSTEMD_SERVICE,
+ SYSTEMD_OBJ,
+ SYSTEMD_MANAGER_INTERFACE,
+ "ListUnitsByPatterns");
+
+ if (ret < 0) {
+ log_error_errno(ret, "failed to issue ListUnitsByPatterns() call - %m");
+ return ret;
+ }
+
+ ret = sd_bus_message_append_strv(m, (char **)unit_states);
+ if (ret < 0) {
+ log_error_errno(ret, "failed to to setup arg 1 for ListUnitsByPatterns() call - %m");
+ return ret;
+ }
+
+ patterns[0] = (char *)pattern;
+ ret = sd_bus_message_append_strv(m, patterns);
+ if (ret < 0) {
+ log_error_errno(ret, "failed to to setup arg 2 for ListUnitsByPatterns() call - %m");
+ goto cleanup;
+ }
+
+ ret = sd_bus_call(bus, m, 0, &error, reply);
+ if (ret < 0) {
+ log_error("failed to issue ListUnitsByPatterns() call: %s\n", error.message);
+ goto cleanup;
+ }
+
+ return 0;
+cleanup:
+ free(prefix);
+ free(joined);
+ return ret;
+}
+
+static int parse_unit_info(sd_bus_message *m, char **name)
+{
+ assert(m);
+
+ char *id;
+ int ret;
+
+ ret = sd_bus_message_read(
+ m,
+ "(ssssssouso)",
+ &id,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ );
+
+ if (ret < 0) {
+ log_error_errno(ret, " failed to fetch message contents - %m");
+ return ret;
+ }
+
+ *name = id;
+ return ret;
+}
+
+static int parse_unit_list(sd_bus_message *reply, struct list_head *units)
+{
+ assert(reply);
+ assert(units);
+
+ int ret = 0;
+ char *name;
+ struct unit_info *u;
+
+ ret = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
+ if (ret < 0) {
+ log_error_errno(ret, "failed to parse ListUnitsByPatterns() response - %m");
+ return ret;
+ }
+
+ while ((ret = parse_unit_info(reply, &name)) > 0) {
+ u = calloc(1, sizeof(*u));
+ if (!u) {
+ log_error("Could not allocate unit_info object");
+ return -ENOMEM;
+ }
+ u->unit = strdup(name);
+ if (!u->unit)
+ return -ENOMEM;
+
+ list_add_tail(&u->node, units);
+ }
+
+ if (ret < 0) {
+ log_error_errno(ret, "error parsing unit info - %m");
+ return ret;
+ }
+
+ ret = sd_bus_message_exit_container(reply);
+ if (ret < 0) {
+ log_error_errno(ret, "failed to clanup parsing unit info - %m");
+ return ret;
+ }
+
+ return ret;
+}
+
static int unit_control_execute(const char *unit, const char *command, struct epc_event *ev)
{
+ assert(unit);
+ assert(command);
+ assert(ev);
+
struct dm_event_data ev_data = {
.reason = ev,
.who_made = MODULE_NAME,
@@ -59,20 +206,16 @@ static int unit_control_execute(const char *unit, const char *command, struct ep
struct epc_event *new_ev;
int ret;
- assert(unit);
- assert(command);
- assert(ev);
-
ev_data.action = EPC_ACTION_UNIT_START_ID;
ret = epc_object_new(&ev_data.action_data);
if (ret < 0) {
- log_error("Could not create data object for action");
+ log_error_errno(ret, "could not create data object for action - %m");
goto out;
}
ret = epc_fill_for_unit_start(ev_data.action_data, unit);
if (ret) {
- log_error("Unable to create event data");
+ log_error_errno(ret, "unable to create event data - %m");
goto out;
}
@@ -84,7 +227,7 @@ static int unit_control_execute(const char *unit, const char *command, struct ep
epc_fill_for_unit_action(ev_data.action_data, "RestartUnit");
else {
ret = -EINVAL;
- log_error("Could not dispatch action: %s", command);
+ log_error_errno(ret, "could not dispatch action: %s - %m", command);
goto out;
}
@@ -97,7 +240,7 @@ static int unit_control_execute(const char *unit, const char *command, struct ep
ret = event_processor_report_event(new_ev);
epc_event_unref(new_ev);
if (ret) {
- log_error("Unable to report event");
+ log_error_errno(ret, "unable to report event - %m");
goto out;
}
@@ -106,8 +249,51 @@ out:
return ret;
}
+static int unit_control_query_and_execute(const char *pattern, const char *command, struct epc_event *ev)
+{
+ assert(pattern);
+ assert(command);
+ assert(ev);
+
+ __attribute__((cleanup(sd_bus_message_unrefp))) sd_bus_message *reply = NULL;
+ int ret, error_code = 0;
+ struct list_head unit_list;
+ struct unit_info *u, *next;
+
+ INIT_LIST_HEAD(&unit_list);
+
+ ret = list_unit_by_pattern(pattern, &reply);
+ if (ret < 0)
+ return ret;
+ ret = parse_unit_list(reply, &unit_list);
+ if (ret < 0)
+ goto cleanup;
+
+ list_for_each_entry_safe(u, next, &unit_list, node) {
+ log_debug("execute action %s on unit %s", command, u->unit);
+ ret = unit_control_execute(u->unit, command, ev);
+ if (ret < 0)
+ error_code = ret;
+ free(u->unit);
+ list_del(&u->node);
+ }
+ if (error_code != 0)
+ return error_code;
+
+ return 0;
+cleanup:
+ list_for_each_entry_safe(u, next, &unit_list, node) {
+ free(u->unit);
+ list_del(&u->node);
+ }
+ return ret;
+}
+
static int match_unit(const char *unit_name, const char *unit_whitelist_rule)
{
+ assert(unit_name);
+ assert(unit_whitelist_rule);
+
char *rule_suf, *unit_suf;
rule_suf = strrchr(unit_whitelist_rule, '.');
@@ -126,36 +312,120 @@ static int match_unit(const char *unit_name, const char *unit_whitelist_rule)
return 0;
}
+/*
+ * Assuming method is not 'start', in both cases:
+ * Request: Whitelist entry:
+ * ---------------------------------------
+ * name@instance.service name@.service
+ * name@.service name@instance.service
+ *
+ * the right action to execute is equivalent to:
+ * systemctl stop|restart name@instance.service
+ *
+ * So in any case we need to pick the most precise name.
+ * Comparing string lengths seems quick and easy way to do this.
+ *
+ * Next thing is, if the proper name is a wildcard, e.g. name@.service,
+ * the way of sending a method call is to add a '*' to the service name:
+ * name@*.service
+ *
+ * User needs to free returned pointer.
+ * */
+char *construct_proper_service_name(char *requested_name, char *whitelist_name)
+{
+ assert(requested_name);
+ assert(whitelist_name);
+
+ char *name, *ret, *at;
+ if (strlen(requested_name) > strlen(whitelist_name))
+ name = requested_name;
+ else
+ name = whitelist_name;
+
+ at = strstr(name, "@.");
+
+ /* add '*' if wildcard */
+ if (at) {
+ at++; /* move the pointer after '@' */
+ ret = calloc(1, strlen(name)+ 2); /* one for \0 and one for additional asterisk*/
+ if (!ret)
+ return NULL;
+ strncpy(ret, name, at - name);
+ sprintf(ret + (at - name), "*%s", at);
+ } else {
+ ret = strdup(name);
+ }
+
+ return ret;
+}
+
static int unit_control_make_decision(struct epc_event_handler *handler)
{
+ assert(handler);
+
struct epc_event *event = pop_epc_event(&handler->event_queue);
struct unit_control_decision_maker *dm = container_of(handler,
struct unit_control_decision_maker, eh);
struct unit_control_event *ev = to_unit_control_event(event);
- int ret;
+ int ret = -EACCES;
+ int error_code = 0;
struct whitelist_entry *w;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ char *service_name;
+
+ /* cannot use start with wildcards */
+ if (!strcmp(ev->method, "start") && strstr(ev->unit, "@.")) {
+ ret = -ENOTSUP;
+ goto cleanup;
+ }
list_for_each_entry(w, &dm->whitelist, node) {
if (!match_unit(ev->unit, w->unit))
continue;
- ret = unit_control_execute(ev->unit, ev->method, event);
- if (ret < 0)
+ service_name = construct_proper_service_name(ev->unit, w->unit);
+
+ if (!service_name) {
+ error_code = -ENOMEM;
break;
+ }
+
+ log_debug("execute action %s on pattern %s", ev->method, service_name);
+
+ if (!strcmp(ev->method, "start")) /* no need to query when starting */
+ ret = unit_control_execute(service_name, ev->method, event);
+ else
+ ret = unit_control_query_and_execute(service_name, ev->method, event);
+ if (ret < 0) {
+ log_error_errno(ret, "action %s on unit %s failed (%m)", ev->method, service_name);
+ error_code = ret;
+ }
+ free(service_name);
}
+cleanup:
epc_object_unref(event);
- return 0;
+ if (error_code != 0) {
+ sd_bus_error_set_errno(&error, error_code);
+ ret = sd_bus_reply_method_error(ev->m, &error);
+ } else {
+ ret = sd_bus_reply_method_return(ev->m, "s", "ok");
+ }
+ sd_bus_message_unref(ev->m);
+ return ret;
}
static void cleanup_whitelist_entry(struct whitelist_entry *w)
{
+ assert(w);
free(w->unit);
free(w);
}
static void cleanup_whitelist(struct unit_control_decision_maker *dm)
{
+ assert(dm);
+
struct whitelist_entry *w, *next;
list_for_each_entry_safe(w, next, &dm->whitelist, node) {
@@ -166,15 +436,32 @@ static void cleanup_whitelist(struct unit_control_decision_maker *dm)
static int add_whitelist_entry(struct unit_control_decision_maker *dm, const char *unit)
{
- struct whitelist_entry *w;
+ assert(dm);
+ assert(unit);
+
+ struct whitelist_entry *w, *next;
+
+ /* check for duplicates and generalizations */
+ list_for_each_entry_safe(w, next, &dm->whitelist, node) {
+ if (match_unit(unit, w->unit)) {
+ log_debug("not adding %s to the whitelist - duplicate/more general rule %s exists", unit, w->unit);
+ return 0;
+ }
+ if (match_unit(w->unit, unit)) {
+ log_debug("add %s to the whitelist - more general rule than %s - removing %s", unit, w->unit, w->unit);
+ /* remove w */
+ list_del_init(&w->node);
+ cleanup_whitelist_entry(w);
+ }
+ }
w = calloc(1, sizeof(*w));
if (!w) {
- log_error("Could not allocate whitelist entry object");
+ log_error("could not allocate whitelist entry object");
return -ENOMEM;
}
- log_debug("Adding whitelist etry for unit %s", unit);
+ log_debug("adding whitelist entry for unit %s", unit);
w->unit = strdup(unit);
list_add_tail(&w->node, &dm->whitelist);
@@ -183,6 +470,9 @@ static int add_whitelist_entry(struct unit_control_decision_maker *dm, const cha
static int unit_control_init(struct epc_event_handler *handler, struct epc_config *config)
{
+ assert(handler);
+ assert(config);
+
struct unit_control_decision_maker *dm = container_of(handler,
struct unit_control_decision_maker, eh);
int len;
@@ -196,12 +486,12 @@ static int unit_control_init(struct epc_event_handler *handler, struct epc_confi
return 0;
if (!json_object_object_get_ex(config->root, "whitelist", &array)) {
- log_error("There is no 'whitelist' array");
+ log_error("there is no 'whitelist' array");
return -EINVAL;
}
if (!json_object_is_type(array, json_type_array)) {
- log_error("Config value is not an array");
+ log_error("config value is not an array");
return -EINVAL;
}
@@ -210,11 +500,10 @@ static int unit_control_init(struct epc_event_handler *handler, struct epc_confi
obj = json_object_array_get_idx(array, i);
ret = add_whitelist_entry(dm, json_object_get_string(obj));
if (ret < 0) {
- log_error("Could not add whitelist entry");
+ log_error_errno(ret, "could not add whitelist entry - %m");
goto cleanup;
}
}
-
return 0;
cleanup:
@@ -224,6 +513,8 @@ cleanup:
static void unit_control_cleanup(struct epc_event_handler *handler)
{
+ assert(handler);
+
struct unit_control_decision_maker *dm = container_of(handler,
struct unit_control_decision_maker, eh);
diff --git a/src/event_types/unit_control_event.c b/src/event_types/unit_control_event.c
index 5da0da7..b004c8a 100644
--- a/src/event_types/unit_control_event.c
+++ b/src/event_types/unit_control_event.c
@@ -40,6 +40,7 @@ static int allocate_uc_event(struct epc_event_type *type,
uc_ev->event_time = uc_ev_data->event_time;
uc_ev->method = uc_ev_data->method;
uc_ev->unit = uc_ev_data->unit;
+ uc_ev->m = uc_ev_data->m;
*ev = &uc_ev->event;
return 0;
diff --git a/src/listeners/unit_control_api.c b/src/listeners/unit_control_api.c
index 860d88e..d5abc5c 100644
--- a/src/listeners/unit_control_api.c
+++ b/src/listeners/unit_control_api.c
@@ -46,7 +46,7 @@ static int method_generic_callback(sd_bus_message *m, void *userdata, sd_bus_err
goto fail;
}
- /* Read the parameters */
+ /* Read parameters */
r = sd_bus_message_read(m, "s", &s);
if (r < 0) {
fprintf(stderr, "Failed to parse parameters: %s\n", strerror(-r));
@@ -73,6 +73,10 @@ static int method_generic_callback(sd_bus_message *m, void *userdata, sd_bus_err
goto fail;
}
+ /*we'll use it later for the response */
+ sd_bus_message_ref(m);
+ uc_ev_data.m = m;
+
r = epc_event_create(UC_EVENT_ID, &uc_ev_data, &ev);
if (r) {
log_error_errno(r, "Unable to allocate an event: %m.");
@@ -87,9 +91,11 @@ static int method_generic_callback(sd_bus_message *m, void *userdata, sd_bus_err
r = -EINVAL;
goto fail;
}
-
- return sd_bus_reply_method_return(m, "s", "ok");
+ return 1;
fail:
+ if (uc_ev_data.m)
+ sd_bus_message_unref(uc_ev_data.m);
+
free(uc_ev_data.method);
free(uc_ev_data.unit);
return sd_bus_error_set_errno(ret_error, -r);