From: Maciej Slodczyk Date: Wed, 20 Mar 2019 15:01:11 +0000 (+0100) Subject: Add dbus signal listener X-Git-Tag: submit/tizen/20190412.120121~6 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9daaaba4b6bd4007dfed878e65dd5ca8edaea9f9;p=platform%2Fcore%2Fsystem%2Factivationd.git Add dbus signal listener Change-Id: I72e9c1b2c022bce0969c1f37b9c993ed4232755b Signed-off-by: Maciej Slodczyk --- diff --git a/Makefile.am b/Makefile.am index 0b2fac0..340c8fc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -126,12 +126,15 @@ modules_LTLIBRARIES = \ service_restart_action.la \ unit_start_action.la \ vconf_key_changed_event.la \ + dbus_signal_event.la \ activation_dm.la \ - vconf_listener.la + vconf_listener.la \ + dbus_listener.la service_restart_action_la_SOURCES = src/action/service_restart.c unit_start_action_la_SOURCES = src/action/unit_start.c vconf_key_changed_event_la_SOURCES = src/event_types/vconf_key_changed_event.c +dbus_signal_event_la_SOURCES = src/event_types/dbus_signal_event.c activation_dm_la_SOURCES = src/decision_makers/activation_dm.c activation_dm_config_DATA = modules.conf.d/activation_eh.conf.d/activation_eh.conf activation_dm_config_DATA += modules.conf.d/activation_eh.conf.d/99-acceptance-test.conf @@ -140,6 +143,9 @@ vconf_listener_la_SOURCES = src/listeners/vconf.c vconf_listener_config_DATA = modules.conf.d/vconf_listener.conf.d/50-default.conf vconf_listener_config_DATA += modules.conf.d/vconf_listener.conf.d/99-acceptance-test.conf vconf_listener_configdir = $(modulesconfigdir)/vconf_listener.conf.d +dbus_listener_la_SOURCES = src/listeners/dbus.c +dbus_listener_config_DATA = modules.conf.d/dbus_listener.conf.d/50-default.conf +dbus_listener_configdir = $(modulesconfigdir)/dbus_listener.conf.d epcd_LDADD = $(LIBSYSTEMD_LIBS) $(GLIB_LIBS) $(JSON_C_LIBS) @@ -215,3 +221,4 @@ modulesconfigdir = $(configdir)/modules.conf.d install-data-local: $(MKDIR_P) $(DESTDIR)${vconf_listener_configdir} + $(MKDIR_P) $(DESTDIR)${dbus_listener_configdir} diff --git a/include/dbus_signal_event.h b/include/dbus_signal_event.h new file mode 100644 index 0000000..db9a072 --- /dev/null +++ b/include/dbus_signal_event.h @@ -0,0 +1,64 @@ +/* + * This file is a part of epc. + * + * Copyright © 2019 Samsung Electronics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _EPC_DBUS_SIGNAL_EVENT_H +#define _EPC_DBUS_SIGNAL_EVENT_H + +#include +#include "event.h" + +#define DBUS_SIGNAL_EVENT_ID "dbus_signal" +#define DS_EV_TIME "et" +#define DS_EV_ID "event_id" +#define DS_EV_SENDER "sender" +#define DS_EV_INTERFACE "interface" +#define DS_EV_MEMBER "member" +#define DS_EV_PATH "path" +#define DS_EV_PATH_NAMESPACE "path_namespace" +#define DS_EV_DESTINATION "destination" +#define DS_EV_PARAMS "params" + +struct dbus_signal_event { + struct epc_event event; + time_t event_time; + char *event_id; + char *sender; + char *interface; + char *member; + char *path; + char *path_namespace; + char *destination; + struct epc_object *params; +}; + +struct dbus_signal_event_data { + time_t event_time; + char *event_id; + char *sender; + char *interface; + char *member; + char *path; + char *path_namespace; + char *destination; + struct epc_object *params; +}; + +#define to_dbus_signal_event(EVENT) \ + container_of(EVENT, struct dbus_signal_event, event) + +#endif /* _EPC_DBUS_SIGNAL_EVENT_H */ diff --git a/modules.conf.d/dbus_listener.conf.d/50-default.conf b/modules.conf.d/dbus_listener.conf.d/50-default.conf new file mode 100644 index 0000000..16ceefc --- /dev/null +++ b/modules.conf.d/dbus_listener.conf.d/50-default.conf @@ -0,0 +1,5 @@ +{ + "signals":[ + {"id":"example_signal", "interface":"org.freedesktop.ExampleInterface", "member":"ExampleSignal", "path_namespace":"/org/freedesktop"} + ] +} diff --git a/packaging/activationd.spec b/packaging/activationd.spec index 780a5eb..ea30d0e 100644 --- a/packaging/activationd.spec +++ b/packaging/activationd.spec @@ -8,6 +8,7 @@ Summary: Event-based activation daemon Group: System/Monitoring Requires: event-processing-core Requires: event-processing-vconf +Requires: event-processing-dbus Requires: event-processing-activation-dm BuildRequires: pkgconfig(vconf) @@ -44,6 +45,13 @@ Requires: event-processing-core %description -n event-processing-vconf This package provides a listener for vconf events +%package -n event-processing-dbus +Summary: dbus listener module for epc +Group: System/Monitoring + +%description -n event-processing-dbus +This package provides a listener for dbus events + %package -n event-processing-activation-dm Summary: Activation decision maker Group: System/Monitoring @@ -115,6 +123,8 @@ mkdir -p %{buildroot}/%{_libdir}/epc/service.conf.d %install_module unit_start_action extra %install_module vconf_key_changed_event vconf %install_module vconf_listener vconf +%install_module dbus_signal_event dbus +%install_module dbus_listener dbus %install_module activation_dm activation-dm %files @@ -131,6 +141,9 @@ mkdir -p %{buildroot}/%{_libdir}/epc/service.conf.d %files -n event-processing-vconf -f vconf-files %{_prefix}/lib/epc/modules.conf.d/vconf_listener.conf.d/50-default.conf +%files -n event-processing-dbus -f dbus-files +%{_prefix}/lib/epc/modules.conf.d/dbus_listener.conf.d/50-default.conf + %files -n activationd-test-services %license COPYING %manifest %{name}.manifest diff --git a/src/event_types/dbus_signal_event.c b/src/event_types/dbus_signal_event.c new file mode 100644 index 0000000..8e003b1 --- /dev/null +++ b/src/event_types/dbus_signal_event.c @@ -0,0 +1,165 @@ +/* + * This file is part of epc. + * + * Copyright © 2019 Samsung Electronics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "dbus_signal_event.h" + +static int allocate_dbus_signal_event(struct epc_event_type *type, + void *data, struct epc_event **ev) +{ + struct dbus_signal_event *ds_ev; + struct dbus_signal_event_data *ds_ev_data = data; + int ret; + + ds_ev = calloc(1, sizeof(*ds_ev)); + if (!ds_ev) + return -ENOMEM; + + ret = epc_event_init_internal(type, &ds_ev->event); + if (ret) + goto cleanup; + + ds_ev->event_time = ds_ev_data->event_time; + ds_ev->event_id = ds_ev_data->event_id; + ds_ev->sender = ds_ev_data->sender; + ds_ev->interface = ds_ev_data->interface; + ds_ev->member = ds_ev_data->member; + ds_ev->path = ds_ev_data->path; + ds_ev->path_namespace = ds_ev_data->path_namespace; + ds_ev->destination = ds_ev_data->destination; + ds_ev->params = ds_ev_data->params; + + *ev = &ds_ev->event; + return 0; +cleanup: + free(ds_ev); + + return ret; +} + +static int deserialize_dbus_signal_event(struct epc_event_type *type, + struct epc_object *data, struct epc_event **ev) +{ + int ret = -EINVAL; + struct dbus_signal_event_data ds_ev_data; + struct epc_object *obj; + memset(&ds_ev_data, 0, sizeof(ds_ev_data)); + + list_for_each_entry(obj, &data->val.children, node) { + switch (obj->type) { + case TYPE_TIME_T: + if (!strcmp(DS_EV_TIME, obj->key)) + ds_ev_data.event_time = obj->val.time; + break; + case TYPE_STRING: + if (!strcmp(DS_EV_ID, obj->key)) + ds_ev_data.event_id = strdup(obj->val.s); + else if (!strcmp(DS_EV_SENDER, obj->key)) + ds_ev_data.sender = strdup(obj->val.s); + else if (!strcmp(DS_EV_INTERFACE, obj->key)) + ds_ev_data.interface = strdup(obj->val.s); + else if (!strcmp(DS_EV_MEMBER, obj->key)) + ds_ev_data.member = strdup(obj->val.s); + else if (!strcmp(DS_EV_PATH, obj->key)) + ds_ev_data.path = strdup(obj->val.s); + else if (!strcmp(DS_EV_PATH_NAMESPACE, obj->key)) + ds_ev_data.path_namespace = strdup(obj->val.s); + else if (!strcmp(DS_EV_DESTINATION, obj->key)) + ds_ev_data.destination = strdup(obj->val.s); + break; + case TYPE_OBJECT: + if (!strcmp(DS_EV_PARAMS, obj->key)) { + epc_object_ref(obj); + ds_ev_data.params = obj; + } + break; + } + } + + ret = allocate_dbus_signal_event(type, &ds_ev_data, ev); + if (ret < 0) + goto finish; + + ret = epc_event_deserialize_internal(data, type, *ev); + if (ret < 0) { + struct dbus_signal_event *ds_ev = + to_dbus_signal_event(*ev); + free(ds_ev); + goto finish; + } + ret = 0; +finish: + return ret; +} + +static void dbus_signal_event_release(struct epc_event *ev) +{ + struct dbus_signal_event *ds_ev = + to_dbus_signal_event(ev); + + free(ds_ev->event_id); + free(ds_ev->sender); + free(ds_ev->interface); + free(ds_ev->member); + free(ds_ev->path); + free(ds_ev->path_namespace); + free(ds_ev->destination); + if (ds_ev->params) + epc_object_destroy(ds_ev->params); + epc_event_cleanup_internal(&ds_ev->event); + free(ds_ev); +} + +static void dbus_signal_event_serialize(struct epc_event *ev, struct epc_object *out) +{ + struct dbus_signal_event *ds_ev = + to_dbus_signal_event(ev); + epc_event_serialize_internal(ev, out); + epc_object_append_time_t(out, DS_EV_TIME, ds_ev->event_time); + epc_object_append_string(out, DS_EV_ID, ds_ev->event_id); + if (ds_ev->sender) + epc_object_append_string(out, DS_EV_SENDER, ds_ev->sender); + if (ds_ev->interface) + epc_object_append_string(out, DS_EV_INTERFACE, ds_ev->interface); + if (ds_ev->member) + epc_object_append_string(out, DS_EV_MEMBER, ds_ev->member); + if (ds_ev->path) + epc_object_append_string(out, DS_EV_PATH, ds_ev->path); + if (ds_ev->path_namespace) + epc_object_append_string(out, DS_EV_PATH_NAMESPACE, ds_ev->path_namespace); + if (ds_ev->destination) + epc_object_append_string(out, DS_EV_DESTINATION, ds_ev->destination); + if (ds_ev->params) + epc_object_append_object(out, DS_EV_PARAMS, ds_ev->params); +} + +static struct epc_event_type dbus_signal_event_type = { + .name = DBUS_SIGNAL_EVENT_ID, + .default_ops = { + .release = dbus_signal_event_release, + .serialize = dbus_signal_event_serialize, + }, + .allocate_event = allocate_dbus_signal_event, + .deserialize_event = deserialize_dbus_signal_event, + .node = LIST_HEAD_INIT(dbus_signal_event_type.node), +}; + +EPC_EVENT_TYPE_REGISTER(dbus_signal_event_type, dbus_signal_et) diff --git a/src/listeners/dbus.c b/src/listeners/dbus.c new file mode 100644 index 0000000..159b8f1 --- /dev/null +++ b/src/listeners/dbus.c @@ -0,0 +1,398 @@ +/* + * This file is part of epc. + * + * Copyright © 2019 Samsung Electronics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "log.h" +#include "module.h" +#include "common.h" +#include "list.h" +#include "json-config.h" +#include "event_processor.h" +#include "systemd_dbus.h" +#include "dbus_signal_event.h" + +struct dbus_signal { + char *event_id; + char *sender; + char *interface; + char *member; + char *path; + char *path_namespace; + char *destination; + struct list_head node; +}; + +struct dbus_listener { + struct epc_module module; + struct list_head signals; +}; + +#define to_dbus_listener(MOD) \ + container_of(MOD, struct dbus_listener, module) + +static int append_param(struct epc_object *params, char type, int pos, const void *val) +{ + int r; + char key[64]; + + memset (key, 0, sizeof key); + + r = snprintf(key, sizeof key, "arg%d", pos); + if (r < 0) + return r; + + switch (type) { + case SD_BUS_TYPE_STRING: + r = epc_object_append_string(params, key, (char *)val); + break; + case SD_BUS_TYPE_BOOLEAN: + r = epc_object_append_bool(params, key, *(bool *)val); + break; + case SD_BUS_TYPE_DOUBLE: + r = epc_object_append_double(params, key, *(double *)val); + break; + case SD_BUS_TYPE_UINT64: + case SD_BUS_TYPE_INT64: + case SD_BUS_TYPE_UINT32: + case SD_BUS_TYPE_INT32: + r = epc_object_append_int(params, key, *(int *)val); + break; + default: + r = 0; + break; + } + if (r < 0) + return r; + return 0; +} + +static int parse_message_args(sd_bus_message *m, struct epc_object *params) +{ + int r; + char type; + const char *contents; + int pos = 0; + + while (1) { + r = sd_bus_message_peek_type(m, &type, &contents); + if (r == 0) { + break; + } else if (r < 0) { + log_error("sd_bus_message_peek_type: %s\n", strerror(-r)); + return r; + } + pos++; + + switch (type) { + case SD_BUS_TYPE_STRING: { + const char *v; + + r = sd_bus_message_read_basic(m, type, &v); + if (r < 0) + return r; + + r = append_param(params, type, pos, v); + if (r < 0) + return r; + break; + } + case SD_BUS_TYPE_BOOLEAN: + case SD_BUS_TYPE_UINT64: + case SD_BUS_TYPE_INT64: + case SD_BUS_TYPE_UINT32: + case SD_BUS_TYPE_INT32: + case SD_BUS_TYPE_DOUBLE: { + union { + int b; + uint64_t u64; + int64_t i64; + uint32_t u32; + int32_t i32; + double d; + } v; + + r = sd_bus_message_read_basic(m, type, &v); + if (r < 0) + return r; + + r = append_param(params, type, pos, &v); + if (r < 0) + return r; + break; + } + default: { + r = sd_bus_message_read_basic(m, type, NULL); + if (r < 0) + return r; + break; + } + } + + } + return 0; +} + + +static int on_dbus_signal_match(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { + struct dbus_signal *s = (struct dbus_signal *)userdata; + + struct dbus_signal_event_data ds_ev_data = {}; + struct epc_event *ev; + struct timespec ts; + int ret = 0; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) { + log_error_errno(errno, "Unable to get timestamp : %m"); + return -1; + } + + ds_ev_data.event_time = ts.tv_sec; + +#define COPY_COND(x) do { \ + if (s->x) { \ + ds_ev_data.x = strdup(s->x); \ + if (ds_ev_data.x == NULL) \ + goto finish; \ + } \ + } while (0) + + COPY_COND(event_id); + COPY_COND(sender); + COPY_COND(interface); + COPY_COND(member); + COPY_COND(path); + COPY_COND(path_namespace); + COPY_COND(destination); + +#undef COPY_COND + + if (epc_object_new(&ds_ev_data.params) < 0) + goto finish; + + ret = parse_message_args(m, ds_ev_data.params); + if (ret) { + log_error_errno(ret, "Unable to parse signal parameters: %m."); + goto finish; + } + + ret = epc_event_create(DBUS_SIGNAL_EVENT_ID, &ds_ev_data, &ev); + if (ret) { + log_error_errno(ret, "Unable to allocate an event: %m."); + goto finish; + } + + ret = event_processor_report_event(ev); + epc_event_unref(ev); + if (ret) + log_error_errno(ret, "Unable to report event: %m"); + return 0; +finish: + free(ds_ev_data.event_id); + free(ds_ev_data.sender); + free(ds_ev_data.interface); + free(ds_ev_data.member); + free(ds_ev_data.path); + free(ds_ev_data.path_namespace); + free(ds_ev_data.destination); + if (ds_ev_data.params) + epc_object_destroy(ds_ev_data.params); + + return -1; +} + +static void signal_cleanup(struct dbus_signal *s) +{ + free(s->event_id); + free(s->sender); + free(s->interface); + free(s->member); + free(s->path); + free(s->path_namespace); + free(s->destination); + free(s); +} + + +static void cleanup(struct dbus_listener *l) +{ + struct dbus_signal *s, *next; + + list_for_each_entry_safe(s, next, &l->signals, node) { + list_del(&s->node); + signal_cleanup(s); + } +} + +static int add_signal(struct dbus_listener *l, json_object *root, sd_bus *bus) +{ + int ret; + struct dbus_signal *s; + char match_buf[1024]; + int size; + char err[512]; + + s = calloc(1, sizeof(*s)); + if (!s) { + log_error("Could not allocate dbus_signal object"); + return -ENOMEM; + } + + ret = get_config_field(root, "id", &s->event_id, json_type_string); + if (ret < 0) + goto out; + + log_debug("Adding signal: %s", s->event_id); + + (void) get_config_field(root, "sender", &s->sender, json_type_string); + + ret = get_config_field(root, "interface", &s->interface, json_type_string); + if (ret < 0) + goto out; + + ret = get_config_field(root, "member", &s->member, json_type_string); + if (ret < 0) + goto out; + + (void) get_config_field(root, "path", &s->path, json_type_string); + (void) get_config_field(root, "path_namespace", &s->path_namespace, json_type_string); + + if (!s->path && !s->path_namespace) + goto out; + + (void) get_config_field(root, "destination", &s->destination, json_type_string); + + memset(match_buf, 0, sizeof match_buf); + size = 0; + + ret = snprintf(match_buf+size, sizeof match_buf-size, "type='signal',"); + if (ret < 0) + goto out; + size += ret; + +#define APPEND_MATCH(x) do { \ + ret = snprintf(match_buf+size, sizeof match_buf-size, #x "='%s',", s->x); \ + if (ret < 0) \ + goto out; \ + size += ret; \ + } while (0) + + if (s->sender) + APPEND_MATCH(sender); + APPEND_MATCH(interface); + APPEND_MATCH(member); + + if (s->path) + APPEND_MATCH(path); + else if (s->path_namespace) + APPEND_MATCH(path_namespace); + + if (s->destination) + APPEND_MATCH(destination); + +#undef APPEND_MATCH + + match_buf[size-1] = '\0'; + + ret = sd_bus_add_match(bus, NULL, match_buf, on_dbus_signal_match, s); + if (ret < 0) { + if (strerror_r(-ret, err, sizeof err)) + log_error("Failed to add signal match: %s\n", err); + goto out; + } + + list_add_tail(&s->node, &l->signals); + return 0; + +out: + signal_cleanup(s); + return ret; +} + + +static int dbus_listener_init(struct epc_module *module, + struct epc_config *config, + sd_event* loop) +{ + struct dbus_listener *listener = to_dbus_listener(module); + int ret = 0, len, i; + json_object *arr, *obj; + sd_bus *bus = NULL; + char err[512]; + + INIT_LIST_HEAD(&listener->signals); + + if (config == NULL) + return 0; + + ret = epc_acquire_systemd_bus(&bus); + if (ret < 0) { + log_error_errno(ret, "Failed to acquire the default system bus connection: %m"); + return -EINVAL; + } + + if (!json_object_object_get_ex(config->root, "signals", &arr)) { + log_error("There is no 'signals' array"); + return -EINVAL; + } + + if (!json_object_is_type(arr, json_type_array)) { + log_error("Config value is not an array"); + return -EINVAL; + } + + len = json_object_array_length(arr); + for (i = 0; i < len; ++i) { + obj = json_object_array_get_idx(arr, i); + ret = add_signal(listener, obj, bus); + if (ret < 0) { + log_error("Could not add signal"); + goto cleanup; + } + } + + ret = sd_bus_attach_event(bus, loop, 0); + if (ret < 0) { + if (strerror_r(-ret, err, sizeof err)) + log_error("Failed to attach bus to event loop: %s\n", err); + goto cleanup; + } + + return 0; + +cleanup: + cleanup(listener); + return ret; +} + +static void dbus_listener_cleanup(struct epc_module *module) +{ + struct dbus_listener *listener = to_dbus_listener(module); + cleanup(listener); +} + +struct dbus_listener dbus_listener = { + .module = { + .name = "dbus_listener", + .type = EPC_MODULE_TYPE_LISTENER, + + .init = dbus_listener_init, + .cleanup = dbus_listener_cleanup, + .node = LIST_HEAD_INIT(dbus_listener.module.node), + }, +}; + +EPC_MODULE_REGISTER(&dbus_listener.module)