-fPIE \
-rdynamic \
$(GLIB_CFLAGS) \
+ $(VCONF_CFLAGS) \
-D"EPC_MODNAME_T=$(call name_fix,$(modname))"
AM_LDFLAGS = \
+ $(VCONF_LIBS) \
-Wl,--gc-sections \
-Wl,--as-needed \
-avoid-version \
modulesdir = $(pkglibdir)/available-modules
modules_LTLIBRARIES = \
service_restart_action.la \
- unit_start_action.la
+ unit_start_action.la \
+ vconf_key_changed_event.la \
+ activation_dm.la \
+ vconf_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
+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
+activation_dm_configdir = $(modulesconfigdir)/activation_eh.conf.d
+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
epcd_LDADD = $(LIBSYSTEMD_LIBS) $(GLIB_LIBS) $(JSON_C_LIBS)
$(MKDIR_P) $(DESTDIR)$(sysconfdir)/epc/available-modules
$(MKDIR_P) $(DESTDIR)$(sysconfdir)/epc/enabled-modules
-SUBDIRS = activationd
+
+bin_PROGRAMS = activationd_acceptance_checker
+
+activationd_acceptance_checker_SOURCES = \
+ tests/activationd_acceptance_checker.c
+
+activationd_acceptance_checker_CFLAGS = $(AM_CFLAGS)
+activationd_acceptance_checker_LDFLAGS = $(VCONF_LIBS) $(LIBSYSTEMD_LIBS)
+
+bin_PROGRAMS += activationd_acceptance_service
+
+activationd_acceptance_service_SOURCES = \
+ tests/activationd_acceptance_service.c
+
+activationd_acceptance_service_CFLAGS = $(AM_CFLAGS)
+activationd_acceptance_service_LDFLAGS =
+
+bin_SCRIPTS = tests/activationd_acceptance_test
+
+unitdir = $(prefix)/lib/systemd/system
+unit_DATA = \
+ tests/activationd-acceptance-test.service
+
+configdir = $(prefix)/lib/epc
+modulesconfigdir = $(configdir)/modules.conf.d
+
+install-data-local:
+ $(MKDIR_P) $(DESTDIR)${vconf_listener_configdir}
+++ /dev/null
-AM_LDFLAGS = $(VCONF_LIBS) \
- -Wl,--gc-sections \
- -Wl,--as-needed \
- -avoid-version \
- -module \
- -shared \
- -pie
-
-AM_CPPFLAGS = -I${top_srcdir}/include \
- -I${top_srcdir}/activationd/include
-
-basetarget = $(basename $(notdir $@))
-modname = $(basetarget)
-name_fix = $(subst $(comma),_,$(subst -,_,$1))
-
-AM_CFLAGS = \
- -Wall \
- -Wchar-subscripts \
- -Wformat-security \
- -Wmissing-declarations \
- -Wmissing-prototypes \
- -Wnested-externs \
- -Wpointer-arith \
- -Wshadow \
- -Wsign-compare \
- -Wstrict-prototypes \
- -Wtype-limits \
- -D_GNU_SOURCE=1 \
- -fPIE \
- -rdynamic \
- $(GLIB_CFLAGS) \
- $(VCONF_CFLAGS) \
- $(LIBSYSTEMD_CFLAGS) \
- -D"EPC_MODNAME_T=$(call name_fix,$(modname))"
-
-modulesdir = $(pkglibdir)/available-modules
-modules_LTLIBRARIES = \
- vconf_key_changed_event.la \
- activation_dm.la \
- vconf_listener.la
-
-vconf_key_changed_event_la_SOURCES = event_types/vconf_key_changed_event.c
-activation_dm_la_SOURCES = 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
-activation_dm_configdir = $(modulesconfigdir)/activation_eh.conf.d
-vconf_listener_la_SOURCES = 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
-
-bin_PROGRAMS = activationd_acceptance_checker
-
-activationd_acceptance_checker_SOURCES = \
- tests/activationd_acceptance_checker.c
-
-activationd_acceptance_checker_CFLAGS = $(AM_CFLAGS)
-activationd_acceptance_checker_LDFLAGS = $(VCONF_LIBS) $(LIBSYSTEMD_LIBS)
-
-bin_PROGRAMS += activationd_acceptance_service
-
-activationd_acceptance_service_SOURCES = \
- tests/activationd_acceptance_service.c
-
-activationd_acceptance_service_CFLAGS = $(AM_CFLAGS)
-activationd_acceptance_service_LDFLAGS =
-
-bin_SCRIPTS = tests/activationd_acceptance_test
-
-unitdir = $(prefix)/lib/systemd/system
-unit_DATA = \
- configs/activationd-acceptance-test.service
-
-configdir = $(prefix)/lib/epc
-modulesconfigdir = $(configdir)/modules.conf.d
-
-install-data-local:
- $(MKDIR_P) $(DESTDIR)${vconf_listener_configdir}
+++ /dev/null
-[Unit]
-Description=activationd acceptance tests service
-
-[Service]
-Type=simple
-ExecStart=/usr/bin/activationd_acceptance_service
+++ /dev/null
-/*
- * This file is part of activationd.
- *
- * 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 <malloc.h>
-#include <errno.h>
-
-#include "decision_made_event.h"
-#include "action.h"
-#include "event_processor.h"
-#include "log.h"
-#include "service.h"
-#include "common.h"
-#include "database.h"
-#include "json-config.h"
-
-#include "vconf_key_changed_event.h"
-
-#define MODULE_NAME "activation_decision_maker"
-
-struct activation_decision_maker {
- struct epc_event_handler eh;
- struct list_head rules;
-};
-
-struct rule {
- const char *event;
- void *ev_data;
- bool match_oldval;
- bool match_newval;
- const char *action;
- const char *target;
-
- struct list_head node;
-};
-
-static int activation_event_match(struct epc_event_handler *handler,
- struct epc_event *ev)
-{
- if (strcmp(ev->type->name, VCONF_KEY_CHANGED_EVENT_ID) == 0)
- return 1;
-
- return 0;
-}
-
-static int vconf_field_cmp(struct vkc_value *a, struct vkc_value *b)
-{
- if (a->type != b->type)
- return 0;
-
- switch (a->type){
- case VKC_BOOL:
- return a->value.b == b->value.b;
- case VKC_INT:
- return a->value.i == b->value.i;
- case VKC_DOUBLE:
- return a->value.d == b->value.d;
- case VKC_STRING:
- return strcmp(a->value.s, b->value.s) == 0;
- }
-}
-
-static int vconf_rule_match(struct rule *r, struct epc_event *event)
-{
- struct vconf_key_changed_event *ev = to_vconf_key_changed_event(event);
- struct vconf_key_changed_event_data *ev_data = r->ev_data;
-
- if (ev_data->key_name && strcmp(ev_data->key_name, ev->key_name))
- return 0;
-
- if (r->match_oldval && !vconf_field_cmp(&ev_data->oldval, &ev->oldval))
- return 0;
-
- if (r->match_newval && !vconf_field_cmp(&ev_data->newval, &ev->newval))
- return 0;
-
- return 1;
-}
-
-static int execute_rule(struct rule *r, struct epc_event *ev)
-{
- struct dm_event_data ev_data = {
- .reason = ev,
- .who_made = MODULE_NAME,
- };
- struct epc_event *new_ev;
- int ret;
-
- 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");
- goto out;
- }
-
- ret = epc_fill_for_unit_start(ev_data.action_data, r->target);
- if (ret) {
- log_error("Unable to create event data");
- goto out;
- }
- epc_fill_for_unit_action(ev_data.action_data, r->action);
-
- ret = epc_event_create(DECISION_MADE_EVENT_ID, &ev_data, &new_ev);
- if (ret) {
- log_error("Unable to create event");
- goto out;
- }
-
- ret = event_processor_report_event(new_ev);
- epc_event_unref(new_ev);
- if (ret) {
- log_error("Unable to report event");
- goto out;
- }
-
-out:
- epc_object_unref(ev_data.action_data);
- return ret;
-}
-
-static int activation_make_decision(struct epc_event_handler *handler)
-{
- struct epc_event *ev = pop_epc_event(&handler->event_queue);
- struct activation_decision_maker *dm = container_of(handler,
- struct activation_decision_maker, eh);
- int ret;
- struct rule *r;
-
- list_for_each_entry(r, &dm->rules, node) {
- if (strcmp(r->event, ev->type->name))
- continue;
-
- if (!strcmp(ev->type->name, VCONF_KEY_CHANGED_EVENT_ID) == 0 ||
- !vconf_rule_match(r, ev))
- continue;
-
- ret = execute_rule(r, ev);
- if (ret < 0)
- break;
- }
-
- epc_object_unref(ev);
- return 0;
-}
-
-static int parse_vconf_field(json_object *root, const char *key, struct vkc_value *value)
-{
- struct json_object *node = NULL;
- enum json_type type;
- char *str;
-
- if (!json_object_object_get_ex(root, key, &node)) {
- log_debug("Config does not contain %s parameter", key);
- return -1;
- }
-
- type = json_object_get_type(node);
-
- switch (type) {
- case json_type_boolean:
- value->type = VKC_BOOL;
- value->value.b = json_object_get_boolean(node);
- break;
- case json_type_double:
- value->type = VKC_DOUBLE;
- value->value.d = json_object_get_double(node);
- break;
- case json_type_int:
- value->type = VKC_INT;
- value->value.i = json_object_get_int(node);
- break;
- case json_type_string:
- value->type = VKC_STRING;
- str = json_object_get_string(node);
- if (!str)
- goto err;
-
- value->value.s = strdup(str);
- break;
- case json_type_array:
- case json_type_object:
- log_error("Field %s must have simple type", key);
- return -EINVAL;
- break;
- case json_type_null:
- return -EINVAL;
- break;
- }
-
- return 0;
-
-err:
- return -1;
-}
-
-static int parse_vconf_data(json_object *root, struct rule *r)
-{
- struct vconf_key_changed_event_data *evd;
- int ret;
-
- evd = calloc(1, sizeof(*evd));
- if (!evd)
- return -ENOMEM;
-
- (void) get_config_field(root, "key_name", &evd->key_name, json_type_string);
- ret = parse_vconf_field(root, "oldval", &evd->oldval);
- r->match_oldval = ret >= 0;
- ret = parse_vconf_field(root, "newval", &evd->newval);
- r->match_newval = ret >= 0;
-
- r->ev_data = evd;
-
- return 0;
-}
-
-static void rule_cleanup(struct rule *r)
-{
- free(r->ev_data);
-}
-
-static void clean_rules(struct activation_decision_maker *dm)
-{
- struct rule *r, *next;
-
- list_for_each_entry_safe(r, next, &dm->rules, node) {
- list_del_init(&r->node);
- rule_cleanup(r);
- free(r);
- }
-}
-
-static int add_rule(struct activation_decision_maker *dm, json_object *root)
-{
- int ret;
- struct rule *r;
- char *str;
-
- r = calloc(1, sizeof(*r));
- if (!r) {
- log_error("Could not allocate rule object");
- return -ENOMEM;
- }
-
- ret = get_config_field(root, "event", &r->event, json_type_string);
- if (ret < 0)
- goto out;
-
- log_debug("Adding rule for event %s", r->event);
-
- if (strcmp(r->event, VCONF_KEY_CHANGED_EVENT_ID) == 0) {
- parse_vconf_data(root, r);
- } else {
- log_warning("Event type %s is not supported by activationd module\n", r->event);
- goto out;
- }
-
- ret = get_config_field(root, "action", &r->action, json_type_string);
- if (ret < 0)
- goto out;
-
- ret = get_config_field(root, "target", &r->target, json_type_string);
- if (ret < 0)
- goto out;
-
- list_add_tail(&r->node, &dm->rules);
-
- return 0;
-
-out:
- rule_cleanup(r);
- free(r);
- return ret;
-}
-
-static int activation_init(struct epc_event_handler *handler, struct epc_config *config)
-{
- struct activation_decision_maker *dm = container_of(handler,
- struct activation_decision_maker, eh);
- int len;
- int i;
- int ret;
- json_object *array, *obj;
-
- INIT_LIST_HEAD(&dm->rules);
-
- if (config == NULL)
- return 0;
-
- if (!json_object_object_get_ex(config->root, "rules", &array)) {
- log_error("There are no 'rules' array");
- return -EINVAL;
- }
-
- if (!json_object_is_type(array, json_type_array)) {
- log_error("Config value is not an array");
- return -EINVAL;
- }
-
- len = json_object_array_length(array);
- for (i = 0; i < len; ++i) {
- obj = json_object_array_get_idx(array, i);
- ret = add_rule(dm, obj);
- if (ret < 0) {
- log_error("Could not add rule");
- goto cleanup;
- }
- }
-
- return 0;
-
-cleanup:
- clean_rules(dm);
- return ret;
-};
-
-static int activation_cleanup(struct epc_event_handler *handler)
-{
- struct activation_decision_maker *dm = container_of(handler,
- struct activation_decision_maker, eh);
-
- clean_rules(dm);
-}
-
-static struct activation_decision_maker activation_dm = {
- .eh = {
- .name = MODULE_NAME,
- .init = activation_init,
- .cleanup = activation_cleanup,
- .event_match = activation_event_match,
- .handle_event = activation_make_decision,
-
- .node = LIST_HEAD_INIT(activation_dm.eh.node),
- },
-};
-
-EPC_EVENT_HANDLER_REGISTER(activation_dm.eh, activation_eh,
- EPC_MODULE_TYPE_DECISION_MAKER)
+++ /dev/null
-/*
- * 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 <stdio.h>
-#include <errno.h>
-#include <malloc.h>
-
-#include "vconf_key_changed_event.h"
-
-static int allocate_vkc_event(struct epc_event_type *type,
- void *data, struct epc_event **ev)
-{
- struct vconf_key_changed_event *vkc_ev;
- struct vconf_key_changed_event_data *vkc_ev_data = data;
- int ret;
-
- vkc_ev = calloc(1, sizeof(*vkc_ev));
- if (!vkc_ev)
- return -ENOMEM;
-
- ret = epc_event_init_internal(type, &vkc_ev->event);
- if (ret)
- goto cleanup;
-
- vkc_ev->event_time = vkc_ev_data->event_time;
- vkc_ev->key_name = vkc_ev_data->key_name;
- vkc_ev->oldval = vkc_ev_data->oldval;
- vkc_ev->newval = vkc_ev_data->newval;
-
- *ev = &vkc_ev->event;
- return 0;
-cleanup:
- free(vkc_ev);
-
- return ret;
-}
-
-static int deserialize_vkc_event(struct epc_event_type *type,
- struct epc_object *data, struct epc_event **ev)
-{
- int ret = -EINVAL;
- struct vconf_key_changed_event_data vkc_ev_data;
- struct epc_object *obj;
- memset(&vkc_ev_data, 0, sizeof(vkc_ev_data));
-
- list_for_each_entry(obj, &data->val.children, node) {
- const bool oldval_match = !strcmp(VKC_EV_OLDVAL, obj->key);
- const bool newval_match = !strcmp(VKC_EV_NEWVAL, obj->key);
-
- switch (obj->type) {
- case TYPE_TIME_T:
- if (!strcmp(VKC_EV_TIME, obj->key))
- vkc_ev_data.event_time = obj->val.time;
- break;
- case TYPE_BOOL:
- if (oldval_match) {
- vkc_ev_data.oldval.value.b = obj->val.b;
- vkc_ev_data.oldval.type = VKC_BOOL;
- } else if (newval_match) {
- vkc_ev_data.newval.value.b = obj->val.b;
- vkc_ev_data.newval.type = VKC_BOOL;
- }
- break;
- case TYPE_INT:
- if (oldval_match) {
- vkc_ev_data.oldval.value.i = obj->val.i;
- vkc_ev_data.oldval.type = VKC_INT;
- } else if (newval_match) {
- vkc_ev_data.newval.value.i = obj->val.i;
- vkc_ev_data.newval.type = VKC_INT;
- }
- break;
- case TYPE_DOUBLE:
- if (oldval_match) {
- vkc_ev_data.oldval.value.d = obj->val.d;
- vkc_ev_data.oldval.type = VKC_DOUBLE;
- } else if (newval_match) {
- vkc_ev_data.newval.value.d = obj->val.d;
- vkc_ev_data.newval.type = VKC_DOUBLE;
- }
- break;
- case TYPE_STRING:
- if (!strcmp(VKC_EV_NAME, obj->key)) {
- vkc_ev_data.key_name = obj->val.s;
- } else if (oldval_match) {
- vkc_ev_data.oldval.value.s = obj->val.s;
- vkc_ev_data.oldval.type = VKC_STRING;
- } else if (newval_match) {
- vkc_ev_data.newval.value.s = obj->val.s;
- vkc_ev_data.newval.type = VKC_STRING;
- }
- break;
- }
- }
-
- ret = allocate_vkc_event(type, &vkc_ev_data, ev);
- if (ret < 0)
- goto finish;
-
- ret = epc_event_deserialize_internal(data, type, *ev);
- if (ret < 0) {
- struct vconf_key_changed_event *vkc_ev =
- to_vconf_key_changed_event(*ev);
- free(vkc_ev);
- goto finish;
- }
- ret = 0;
-finish:
- return ret;
-}
-
-static void vkc_event_release(struct epc_event *ev)
-{
- struct vconf_key_changed_event *vkc_ev =
- to_vconf_key_changed_event(ev);
-
- if (vkc_ev->key_name)
- free(vkc_ev->key_name);
- if (vkc_ev->oldval.type == VKC_STRING)
- free(vkc_ev->oldval.value.s);
- if (vkc_ev->newval.type == VKC_STRING)
- free(vkc_ev->newval.value.s);
-
- epc_event_cleanup_internal(&vkc_ev->event);
- free(vkc_ev);
-}
-
-static void vkc_event_serialize(struct epc_event *ev, struct epc_object *out)
-{
- struct vconf_key_changed_event *vkc_ev =
- to_vconf_key_changed_event(ev);
- epc_event_serialize_internal(ev, out);
- epc_object_append_string(out, VKC_EV_NAME, vkc_ev->key_name);
-
- switch (vkc_ev->oldval.type) {
- case VKC_BOOL:
- epc_object_append_bool(out, VKC_EV_OLDVAL, vkc_ev->oldval.value.b);
- break;
- case VKC_INT:
- epc_object_append_int(out, VKC_EV_OLDVAL, vkc_ev->oldval.value.i);
- break;
- case VKC_DOUBLE:
- epc_object_append_double(out, VKC_EV_OLDVAL, vkc_ev->oldval.value.d);
- break;
- case VKC_STRING:
- epc_object_append_string(out, VKC_EV_OLDVAL, vkc_ev->oldval.value.s);
- break;
- }
-
- switch (vkc_ev->newval.type) {
- case VKC_BOOL:
- epc_object_append_bool(out, VKC_EV_NEWVAL, vkc_ev->newval.value.b);
- break;
- case VKC_INT:
- epc_object_append_int(out, VKC_EV_NEWVAL, vkc_ev->newval.value.i);
- break;
- case VKC_DOUBLE:
- epc_object_append_double(out, VKC_EV_NEWVAL, vkc_ev->newval.value.d);
- break;
- case VKC_STRING:
- epc_object_append_string(out, VKC_EV_NEWVAL, vkc_ev->newval.value.s);
- break;
- }
- epc_object_append_time_t(out, VKC_EV_TIME, vkc_ev->event_time);
-}
-
-static struct epc_event_type vconf_key_changed_event_type = {
- .name = VCONF_KEY_CHANGED_EVENT_ID,
- .default_ops = {
- .release = vkc_event_release,
- .serialize = vkc_event_serialize,
- },
- .allocate_event = allocate_vkc_event,
- .deserialize_event = deserialize_vkc_event,
- .node = LIST_HEAD_INIT(vconf_key_changed_event_type.node),
-};
-
-EPC_EVENT_TYPE_REGISTER(vconf_key_changed_event_type, vconf_key_changed_et)
+++ /dev/null
-/*
- * 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_VCONF_KEY_CHANGED_EVENT_H
-#define _EPC_VCONF_KEY_CHANGED_EVENT_H
-
-#include <time.h>
-#include "event.h"
-
-#define VCONF_KEY_CHANGED_EVENT_ID "vconf_key_changed"
-#define VKC_EV_TIME "et"
-#define VKC_EV_NAME "name"
-#define VKC_EV_OLDVAL "oldval"
-#define VKC_EV_NEWVAL "newval"
-
-enum vkc_type {
- VKC_BOOL,
- VKC_INT,
- VKC_DOUBLE,
- VKC_STRING
-};
-
-struct vkc_value {
- enum vkc_type type;
- union {
- bool b;
- int i;
- double d;
- char *s;
- } value;
-};
-
-struct vconf_key_changed_event {
- struct epc_event event;
- char *key_name;
- struct vkc_value oldval;
- struct vkc_value newval;
- time_t event_time;
-};
-
-struct vconf_key_changed_event_data {
- char *key_name;
- struct vkc_value oldval;
- struct vkc_value newval;
- time_t event_time;
-};
-
-#define to_vconf_key_changed_event(EVENT) \
- container_of(EVENT, struct vconf_key_changed_event, event)
-
-#endif /* _EPC_VCONF_KEY_CHANGED_EVENT_H */
+++ /dev/null
-/*
- * 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 <vconf.h>
-#include <glib.h>
-#include <time.h>
-
-#include "log.h"
-#include "module.h"
-#include "common.h"
-#include "list.h"
-#include "json-config.h"
-#include "event_processor.h"
-#include "vconf_key_changed_event.h"
-
-struct vconf_kv {
- char *name;
- struct vkc_value val;
- struct list_head node;
-};
-
-struct vconf_listener {
- struct epc_module module;
- struct list_head keys;
-};
-
-#define to_vconf_listener(MOD) \
- container_of(MOD, struct vconf_listener, module)
-
-static struct vconf_kv *find_vconf_kv(struct vconf_listener *l, const char *name)
-{
- struct vconf_kv *k = NULL;
-
- list_for_each_entry(k, &l->keys, node) {
- if (!strcmp(k->name, name))
- return k;
- }
- return NULL;
-}
-
-static int copy_vkc_value(struct vkc_value *from, struct vkc_value *to)
-{
- to->type = from->type;
- switch (from->type){
- case VKC_BOOL:
- to->value.b = from->value.b;
- break;
- case VKC_INT:
- to->value.i = from->value.i;
- break;
- case VKC_DOUBLE:
- to->value.d = from->value.d;
- break;
- case VKC_STRING:
- to->value.s = strdup(from->value.s);
- if (!to->value.s)
- return -ENOMEM;
- break;
- }
- return 0;
-}
-
-static int set_object_value(struct vconf_kv *k, keynode_t *node)
-{
- assert (k);
- assert (node);
- int ret = 0;
-
- switch(vconf_keynode_get_type(node)) {
- case VCONF_TYPE_INT:{
- int value = vconf_keynode_get_int(node);
- k->val.value.i = value;
- k->val.type = VKC_INT;
- }break;
- case VCONF_TYPE_BOOL:{
- int value = vconf_keynode_get_bool(node);
- if (value == -1) {
- ret = -1;
- break;
- }
- k->val.value.b = value;
- k->val.type = VKC_BOOL;
- }break;
- case VCONF_TYPE_DOUBLE:{
- double value = vconf_keynode_get_dbl(node);
- k->val.value.d = value;
- k->val.type = VKC_DOUBLE;
- }break;
- case VCONF_TYPE_STRING:{
- char *value = vconf_keynode_get_str(node);
- if (!value) {
- ret = -1;
- break;
- }
- k->val.value.s = strdup(value);
- if (!k->val.value.s)
- return -1;
- k->val.type = VKC_STRING;
- }break;
- default:
- ret = -1;
- break;
- }
-
- return ret;
-}
-
-static void on_vconf_key_changed(keynode_t *key, void *data)
-{
- struct vconf_listener *l = (struct vconf_listener *)data;
- char *name = NULL;
- struct vconf_key_changed_event_data vkc_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;
- }
-
- name = vconf_keynode_get_name(key);
- if (!name) {
- log_debug("vconf: key name not found (key=%s)", name);
- return;
- }
-
- struct vconf_kv *k = find_vconf_kv(l, name);
- if (!k) {
- log_debug("vconf: key-value pair not found (key=%s)", name);
- return;
- }
-
- vkc_ev_data.event_time = ts.tv_sec;
- vkc_ev_data.key_name = strdup(name);
- if (vkc_ev_data.key_name == NULL)
- goto finish;
-
- if (copy_vkc_value(&k->val, &vkc_ev_data.oldval) < 0)
- goto finish;
-
- if (k->val.type == VKC_STRING) {
- free(k->val.value.s);
- k->val.value.s = NULL;
- }
-
- if (set_object_value(k, key) < 0)
- goto finish;
-
- if (copy_vkc_value(&k->val, &vkc_ev_data.newval) < 0)
- goto finish;
-
- ret = epc_event_create(VCONF_KEY_CHANGED_EVENT_ID, &vkc_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;
-finish:
- free(vkc_ev_data.key_name);
- if (vkc_ev_data.oldval.type == VKC_STRING)
- free(vkc_ev_data.oldval.value.s);
- if (vkc_ev_data.newval.type == VKC_STRING)
- free(vkc_ev_data.newval.value.s);
-
- return;
-}
-
-static int add_key(struct vconf_listener *l, const char *key, keynode_t *node)
-{
- int ret = 0;
- struct vconf_kv *k;
-
- assert (l);
- assert (key);
- //assert (node); /* it's ok to be null */
-
- k = (struct vconf_kv*)malloc(sizeof(struct vconf_kv));
- if (k == NULL) {
- ret = -ENOMEM;
- goto cleanup;
- }
-
- k->name = strdup(key);
- if (k->name == NULL) {
- ret = -ENOMEM;
- goto cleanup;
- }
-
- if (node != NULL && set_object_value(k, node) < 0) {
- ret = -EINVAL;
- goto cleanup;
- }
-
- list_add_tail(&k->node, &l->keys);
- if (vconf_notify_key_changed(key, on_vconf_key_changed, l) < 0)
- log_error("vconf_notify_key_changed() failed (%s)", key);
-
- return 0;
-
-cleanup:
- if (k) {
- free(k->name);
- free(k);
- }
- return ret;
-}
-
-static void cleanup(struct vconf_listener *l)
-{
- struct vconf_kv *k, *next;
-
- list_for_each_entry_safe(k, next, &l->keys, node) {
- list_del(&k->node);
- free(k->name);
- if (k->val.type == VKC_STRING)
- free(k->val.value.s);
- free(k);
- }
-}
-
-static int vconf_listener_init(struct epc_module *module,
- struct epc_config *config,
- sd_event* event)
-{
- struct vconf_listener *listener = to_vconf_listener(module);
- int ret = 0;
- json_object *arr, *val;
- keylist_t *klist;
- keynode_t *knode;
-
- INIT_LIST_HEAD(&listener->keys);
-
- if (!json_object_object_get_ex(config->root, "keys", &arr)) {
- log_error("Config does not contain 'keys' parameter.");
- ret = -EINVAL;
- goto cleanup;
- }
-
- if (!json_object_is_type(arr, json_type_array)) {
- log_error("Config value is not an array");
- ret = -EINVAL;
- goto cleanup;
- }
-
- for (size_t i = 0; i < json_object_array_length(arr); i++) {
- knode = NULL;
- val = json_object_array_get_idx(arr, i);
- if (val == NULL) {
- log_error("vconf: no specified key (%zu)\n", i);
- ret = -EINVAL;
- goto cleanup;
- }
-
- klist = vconf_keylist_new();
- if (!klist) {
- log_error("vconf: could not create keylist for %s\n", json_object_get_string(val));
- continue;
- }
-
- ret = vconf_get(klist, json_object_get_string(val), VCONF_GET_ALL);
- if (ret < 0) {
- log_debug("vconf: keylist does not contain key %s\n", json_object_get_string(val));
- goto free_list;
- }
-
- ret = vconf_keylist_lookup(klist, json_object_get_string(val), &knode);
- if (ret <= 0) {
- log_debug("vconf_keylist_lookup failed for %s\n", json_object_get_string(val));
- //goto free_list;
- }
-
- ret = add_key(listener, json_object_get_string(val), knode);
- if (ret < 0)
- log_error("vconf: could not add key\n");
-
- free_list:
- vconf_keylist_free(klist);
- }
- return 0;
-
-cleanup:
- cleanup(listener);
- return ret;
-}
-
-static void vconf_listener_cleanup(struct epc_module *module)
-{
- struct vconf_listener *listener = to_vconf_listener(module);
- cleanup(listener);
-}
-
-struct vconf_listener vconf_listener = {
- .module = {
- .name = "vconf_listener",
- .type = EPC_MODULE_TYPE_LISTENER,
-
- .init = vconf_listener_init,
- .cleanup = vconf_listener_cleanup,
- .node = LIST_HEAD_INIT(vconf_listener.module.node),
- },
-};
-
-EPC_MODULE_REGISTER(&vconf_listener.module)
+++ /dev/null
-/*
- * 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 <systemd/sd-bus.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/timerfd.h>
-#include <vconf.h>
-
-#include "common.h"
-#include "systemd_dbus.h"
-
-#define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties"
-#define SERVICE_NAME "activationd-acceptance-test.service"
-#define VCONF_ACTIVATION_KEY "file/activationd/acceptance"
-
-enum unit_state {
- UNKNOWN,
- ACTIVE,
- INACTIVE
-};
-
-struct action {
- const char *action;
- enum unit_state expected_states[2];
- int state_count;
- int current_state;
-};
-
-struct test_sequence {
- struct action actions[3];
- int current_action;
- int ok;
-};
-
-
-struct test_data {
- sd_bus *bus;
- sd_event *loop;
- struct test_sequence test;
- enum unit_state state;
- enum unit_state prev_state;
- char *path;
-};
-
-static const char *from_unit_state(enum unit_state state)
-{
- switch (state) {
- case UNKNOWN:
- return "unknown";
- case ACTIVE:
- return "active";
- default:
- return "inactive";
- }
-}
-
-static enum unit_state to_unit_state(const char *state)
-{
- if (!strncmp (state, "active", 6))
- return ACTIVE;
- else if (!strncmp (state, "inactive", 8) || !strncmp (state, "deactivating", 12))
- return INACTIVE;
- else
- return UNKNOWN;
-}
-
-static int set_vconf_key(const char *val)
-{
- keylist_t *kl = NULL;
- kl = vconf_keylist_new();
- if (!kl) {
- fprintf(stderr, "error creating vconf keylists\n");
- return -1;
- }
-
- vconf_keylist_add_str(kl, VCONF_ACTIVATION_KEY, val);
- if (vconf_set(kl) < 0) {
- fprintf(stderr, "error saving vconf key\n");
- vconf_keylist_free(kl);
- return -1;
- }
-
- vconf_keylist_free(kl);
- return 0;
-}
-
-static int test_step(struct test_data *data)
-{
- struct test_sequence *test = &data->test;
- if (test->current_action == ARRAY_SIZE(test->actions)) {
- sd_event_exit(data->loop, EXIT_SUCCESS);
- test->ok = 1;
- return 0;
- }
-
- if (set_vconf_key(test->actions[test->current_action].action))
- return -1;
- return 0;
-}
-
-static int check_test_result(struct test_data *data)
-{
- struct action *a = &data->test.actions[data->test.current_action];
- enum unit_state exp_state = a->expected_states[a->current_state];
-
- if (data->test.ok == 1)
- return 0;
-
- if (data->state != exp_state) {
- data->test.ok = 1;
- fprintf(stderr, "test failed (%s instead of %s at step %d of %s)\n",
- from_unit_state(data->state),
- from_unit_state(exp_state),
- a->current_state,
- a->action);
-
- sd_event_exit(data->loop, EXIT_FAILURE);
- return -1;
- }
- a->current_state++;
- if (a->current_state == a->state_count) {
- data->test.current_action++;
- if (test_step(data) < 0)
- return -1;
- }
- return 0;
-}
-
-
-static int check_unit_state(struct test_data *data, const char *state)
-{
- enum unit_state new_state;
-
- new_state = to_unit_state(state);
- if (data->state != new_state) {
- data->prev_state = data->state;
- data->state = new_state;
- check_test_result(data);
- }
- return 0;
-}
-
-
-static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
- int r;
- struct test_data *data = (struct test_data*)userdata;
- const char *iface;
- const char *member;
- const char *value = NULL;
- const char *contents;
-
- r = sd_bus_message_read(m, "s", &iface);
- if (r < 0) {
- fprintf(stderr, "Failed to parse PropertiesChanged\n");
- return r;
- }
-
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
- if (r < 0)
- return r;
-
- while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
- char type;
-
- r = sd_bus_message_read(m, "s", &member);
- if (r < 0)
- return r;
-
- r = sd_bus_message_peek_type(m, &type, &contents);
- if (r < 0)
- return r;
-
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
- if (r < 0)
- return r;
-
- if (strcmp(member, "ActiveState") == 0) {
- r = sd_bus_message_read(m, "s", &value);
- if (r < 0)
- return r;
- } else {
- r = sd_bus_message_skip(m, contents);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return r;
-
- if (value)
- check_unit_state(data, value);
- return 0;
-}
-
-static void cleanup(struct test_data *data)
-{
- free(data->path);
- if (!data->bus)
- return;
- sd_bus_flush(data->bus);
- sd_bus_unref(data->bus);
-}
-
-static int signal_handler(sd_event_source *s,
- const struct signalfd_siginfo *si,
- void *userdata)
-{
- sd_event *loop = userdata;
- sd_event_exit(loop, EXIT_FAILURE);
- return 0;
-}
-
-static int start_test(sd_event_source *s, uint64_t usec, void *userdata)
-{
- struct test_data *data = (struct test_data*)userdata;
- return test_step(data);
-}
-
-static int timeout_handler(sd_event_source *s, uint64_t usec, void *userdata)
-{
- struct test_data *data = (struct test_data*)userdata;
- fprintf(stderr, "test timed out\n");
- sd_event_exit(data->loop, EXIT_FAILURE);
- return 0;
-}
-
-static int call_subscribe(struct test_data *data)
-{
- __attribute__((cleanup(sd_bus_message_unrefp))) sd_bus_message *reply = NULL;
- __attribute__((cleanup(sd_bus_error_free))) sd_bus_error error = SD_BUS_ERROR_NULL;
- int ret;
-
- ret = sd_bus_call_method(data->bus,
- SYSTEMD_SERVICE,
- SYSTEMD_OBJ,
- SYSTEMD_MANAGER_INTERFACE,
- "Subscribe",
- &error,
- &reply,
- "");
-
- if (ret < 0) {
- fprintf(stderr, "Failed to issue Subscribe() call: %s\n", error.message);
- return -1;
- }
- return 0;
-}
-
-static int call_load_unit(struct test_data *data)
-{
- __attribute__((cleanup(sd_bus_message_unrefp))) sd_bus_message *reply = NULL;
- __attribute__((cleanup(sd_bus_error_free))) sd_bus_error error = SD_BUS_ERROR_NULL;
- int ret;
- const char *path = NULL;
- char err[512];
-
- ret = sd_bus_call_method(data->bus,
- SYSTEMD_SERVICE,
- SYSTEMD_OBJ,
- SYSTEMD_MANAGER_INTERFACE,
- "LoadUnit",
- &error,
- &reply,
- "s",
- SERVICE_NAME);
-
- if (ret < 0) {
- fprintf(stderr, "Failed to issue LoadUnit() call: %s\n", error.message);
- return -1;
- }
-
- ret = sd_bus_message_read(reply, "o", &path);
- if (ret < 0) {
- if (strerror_r(-ret, err, sizeof err))
- fprintf(stderr, "Failed to read reply: %s\n", err);
- return -1;
- }
-
- if (!path) {
- fprintf(stderr, "ould not get object path\n");
- return -1;
- }
- data->path = strdup(path);
- return 0;
-}
-int main(int argc, char *argv[])
-{
- struct test_data data = {
- .bus = NULL,
- .loop = NULL,
- .state = UNKNOWN,
- .prev_state = UNKNOWN,
- .path = NULL,
- .test = {
- .actions = {
- {"START", {ACTIVE, UNKNOWN}, 1, 0},
- {"RELOAD", {INACTIVE, ACTIVE}, 2, 0},
- {"STOP", {INACTIVE, UNKNOWN}, 1, 0}
- },
- .current_action = 0,
- .ok = 0
- }
- };
-
- int r;
- uint64_t now;
- sigset_t ss;
- char buf[512];
- char err[512];
-
- r = sd_bus_open_system(&data.bus);
- if (r < 0) {
- if (strerror_r(-r, err, sizeof err))
- fprintf(stderr, "Failed to read reply: %s\n", err);
- goto fail;
- }
-
- r = call_subscribe(&data);
- if (r < 0)
- goto fail;
-
- r = call_load_unit(&data);
- if (r < 0)
- goto fail;
-
- r = snprintf(buf, sizeof buf, "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged', path='%s'", data.path);
- if (r < 0) {
- if (strerror_r(-r, err, sizeof err))
- fprintf(stderr, "Failed to create match rule: %s\n", err);
- goto fail;
- }
-
- r = sd_bus_add_match(data.bus, NULL, buf, on_properties_changed, &data);
- if (r < 0) {
- if (strerror_r(-r, err, sizeof err))
- fprintf(stderr, "Failed to add PropertiesChanged match: %s\n", err);
- goto fail;
- }
-
- r = sd_event_new(&data.loop);
- if (r < 0) {
- if (strerror_r(-r, err, sizeof err))
- fprintf(stderr, "Failed to create main loop: %s\n", err);
- goto fail;
- }
-
- sigemptyset(&ss);
- sigaddset(&ss, SIGINT);
- sigaddset(&ss, SIGTERM);
- r = sigprocmask(SIG_BLOCK, &ss, NULL);
- if (r < 0) {
- if (strerror_r(-r, err, sizeof err))
- fprintf(stderr, "Failed to change signal mask: %s\n", err);
- goto fail;
- }
-
- r = sd_event_add_signal(data.loop, NULL, SIGINT, signal_handler, data.loop);
- if (r < 0) {
- if (strerror_r(-r, err, sizeof err))
- fprintf(stderr, "Failed to register SIGINT handler: %s\n", err);
- goto fail;
- }
-
- r = sd_event_add_signal(data.loop, NULL, SIGTERM, signal_handler, data.loop);
- if (r < 0) {
- if (strerror_r(-r, err, sizeof err))
- fprintf(stderr, "Failed to register SIGTERM handler: %s\n", err);
- goto fail;
- }
-
- r = sd_bus_attach_event(data.bus, data.loop, 0);
- if (r < 0) {
- if (strerror_r(-r, err, sizeof err))
- fprintf(stderr, "Failed to attach bus to event loop: %s\n", err);
- goto fail;
- }
-
- r = sd_event_add_time(data.loop, NULL, CLOCK_REALTIME, 0, 0, start_test, &data);
- if (r < 0) {
- if (strerror_r(-r, err, sizeof err))
- fprintf(stderr, "Failed to attach timer to event loop: %s\n", err);
- goto fail;
- }
-
- sd_event_now(data.loop, CLOCK_REALTIME, &now);
-
- r = sd_event_add_time(data.loop, NULL, CLOCK_REALTIME, now+10000000, 0, timeout_handler, &data);
- if (r < 0) {
- if (strerror_r(-r, err, sizeof err))
- fprintf(stderr, "Failed to attach timer to event loop: %s\n", err);
- goto fail;
- }
-
- r = sd_event_loop(data.loop);
- if (r < 0) {
- if (strerror_r(-r, err, sizeof err))
- fprintf(stderr, "Failed to run main loop: %s\n", err);
- goto fail;
- }
-
- cleanup(&data);
- return r;
-
-fail:
- cleanup(&data);
- return EXIT_FAILURE;
-}
+++ /dev/null
-/*
- * 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 <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <signal.h>
-
-static void exit_handler(int signo)
-{
- exit(EXIT_SUCCESS);
-}
-
-int main(int argc, char *argv[])
-{
- if (signal(SIGINT, exit_handler) == SIG_ERR ||
- signal(SIGQUIT, exit_handler) == SIG_ERR ||
- signal(SIGTERM, exit_handler) == SIG_ERR) {
- perror("catching SIGNAL");
- exit(EXIT_FAILURE);
- }
-
- printf("service started, do nothing...\n");
- while (1)
- sleep(10);
-
- exit_handler(0);
-}
-
+++ /dev/null
-#!/bin/sh
-
-EPCD_PID=-1
-
-cleanup() {
- systemctl stop activationd-acceptance-test
- [ $EPCD_PID -ne -1 ] && kill $EPCD_PID > /dev/null 2>&1
- vconftool unset file/activationd/acceptance
- exit 0
-}
-
-trap cleanup 0
-
-vconftool -f set -t string file/activationd/acceptance foo
-
-# assumes there's no epcd service running
-epcd > /dev/null 2>&1 &
-EPCD_PID=$!
-sleep 2
-
-/usr/bin/activationd_acceptance_checker
-
-if [ "$?" -eq "0" ]; then
- echo "ok"
-else
- echo "fail"
-fi
-
AC_CONFIG_HEADERS(config.h)
AC_CONFIG_FILES([
- Makefile activationd/Makefile
+ Makefile
])
LT_INIT
--- /dev/null
+/*
+ * 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_VCONF_KEY_CHANGED_EVENT_H
+#define _EPC_VCONF_KEY_CHANGED_EVENT_H
+
+#include <time.h>
+#include "event.h"
+
+#define VCONF_KEY_CHANGED_EVENT_ID "vconf_key_changed"
+#define VKC_EV_TIME "et"
+#define VKC_EV_NAME "name"
+#define VKC_EV_OLDVAL "oldval"
+#define VKC_EV_NEWVAL "newval"
+
+enum vkc_type {
+ VKC_BOOL,
+ VKC_INT,
+ VKC_DOUBLE,
+ VKC_STRING
+};
+
+struct vkc_value {
+ enum vkc_type type;
+ union {
+ bool b;
+ int i;
+ double d;
+ char *s;
+ } value;
+};
+
+struct vconf_key_changed_event {
+ struct epc_event event;
+ char *key_name;
+ struct vkc_value oldval;
+ struct vkc_value newval;
+ time_t event_time;
+};
+
+struct vconf_key_changed_event_data {
+ char *key_name;
+ struct vkc_value oldval;
+ struct vkc_value newval;
+ time_t event_time;
+};
+
+#define to_vconf_key_changed_event(EVENT) \
+ container_of(EVENT, struct vconf_key_changed_event, event)
+
+#endif /* _EPC_VCONF_KEY_CHANGED_EVENT_H */
Source1001: %{name}.manifest
Summary: Event-based activation daemon
Group: System/Monitoring
+Requires: event-processing-core
+Requires: event-processing-vconf
+Requires: event-processing-activation-dm
+BuildRequires: pkgconfig(vconf)
%define with_activationd_glib_support 1
-BuildRequires: pkgconfig(vconf)
-
%description
Activationd allows starting systemd units based on various system events.
%description -n event-processing-extra
Event processing extra modules
+%package -n event-processing-vconf
+Summary: vconf listener module for epc
+Group: System/Monitoring
+BuildRequires: pkgconfig(vconf)
+Requires: event-processing-core
+
+%description -n event-processing-vconf
+This package provides a listener for vconf events
+
+%package -n event-processing-activation-dm
+Summary: Activation decision maker
+Group: System/Monitoring
+Requires: event-processing-core
+
+%description -n event-processing-activation-dm
+This package provides a decision maker for activating systemd units based on various system events
+
%package -n activationd-test-services
Summary: activationd test services
Group: System/Monitoring
+BuildRequires: pkgconfig(vconf)
+Requires: event-processing-core
%description -n activationd-test-services
Services used to test activationd functionality
%make_install
mkdir -p %{buildroot}/%{_unitdir}/basic.target.wants
-ln -s ../epc.service %{buildroot}/%{_unitdir}/basic.target.wants
+ln -s epc.service %{buildroot}/%{_unitdir}/basic.target.wants
ln -s %{_sbindir}/epcd %{buildroot}/%{_sbindir}/epcctl
%define moduleconfdir %{_sysconfdir}/epc/available-modules/
%define enabled_moduledir %{_sysconfdir}/epc/enabled-modules/
-touch epc-files
-#for mod in <core_modules>
-#do
-# ln -s %{moduledir}/${mod}.so %{buildroot}/%{moduleconfdir}/${mod}.so;
-# ln -s ../available-modules/${mod}.so %{buildroot}/%{enabled_moduledir}/${mod}.so;
-# echo %{moduledir}/${mod}.so >> activationd-files;
-# echo %{moduleconfdir}/${mod}.so >> activationd-files;
-# echo %{enabled_moduledir}/${mod}.so >> activationd-files;
-#done
-
-for mod in service_restart_action \
- unit_start_action
-do
- ln -s %{moduledir}/${mod}.so %{buildroot}/%{moduleconfdir}/${mod}.so;
- ln -s ../available-modules/${mod}.so %{buildroot}/%{enabled_moduledir}/${mod}.so;
- echo %{moduledir}/${mod}.so >> extra-files;
- echo %{moduleconfdir}/${mod}.so >> extra-files;
- echo %{enabled_moduledir}/${mod}.so >> extra-files;
-done
-
-echo /usr/lib/epc/service.conf.d/ >> activationd-files;
-
-for mod in vconf_listener \
- vconf_key_changed_event \
- activation_dm
-do
- ln -s %{moduledir}/${mod}.so %{buildroot}/%{moduleconfdir}/${mod}.so;
- ln -s ../available-modules/${mod}.so %{buildroot}/%{enabled_moduledir}/${mod}.so;
- echo %{moduledir}/${mod}.so >> activationd-files;
- echo %{moduleconfdir}/${mod}.so >> activationd-files;
- echo %{enabled_moduledir}/${mod}.so >> activationd-files;
-done
+mkdir -p %{buildroot}/%{moduledir}
+mkdir -p %{buildroot}/%{moduleconfdir}
+mkdir -p %{buildroot}/%{enabled_moduledir}
+mkdir -p %{buildroot}/%{_libdir}/epc/service.conf.d
+
+# install_module <module> <package>
+%define install_module() \
+ ln -s %{moduledir}/%1.so %{buildroot}/%{moduleconfdir}/%1.so; \
+ ln -s ../available-modules/%1.so %{buildroot}/%{enabled_moduledir}/%1.so; \
+ echo %{moduledir}/%1.so >> %2-files; \
+ echo %{moduleconfdir}/%1.so >> %2-files; \
+ echo %{enabled_moduledir}/%1.so >> %2-files;
+
+%install_module service_restart_action extra
+%install_module unit_start_action extra
+%install_module vconf_key_changed_event vconf
+%install_module vconf_listener vconf
+%install_module activation_dm activation-dm
+
+%files
%files -n event-processing-devel
%license COPYING
%{_includedir}/*
%{_libdir}/pkgconfig/*
-%files -f activationd-files
+%files -n event-processing-activation-dm -f activation-dm-files
%license COPYING
-%{_prefix}/lib/epc/modules.conf.d/vconf_listener.conf.d/50-default.conf
%{_prefix}/lib/epc/modules.conf.d/activation_eh.conf.d/activation_eh.conf
+%files -n event-processing-vconf -f vconf-files
+%{_prefix}/lib/epc/modules.conf.d/vconf_listener.conf.d/50-default.conf
+
%files -n activationd-test-services
%license COPYING
%manifest %{name}.manifest
%{_prefix}/lib/epc/modules.conf.d/vconf_listener.conf.d/99-acceptance-test.conf
%{_prefix}/lib/epc/modules.conf.d/activation_eh.conf.d/99-acceptance-test.conf
-%files -n event-processing-core -f epc-files
+%files -n event-processing-core
%license COPYING
%manifest %{name}.manifest
-%{_localstatedir}/db/epc
%{_sbindir}/epcd
%{_sbindir}/epcctl
%{_prefix}/lib/epc/epc.conf
%{_unitdir}/epc.service
%{_unitdir}/basic.target.wants/epc.service
%{_prefix}/lib/epc/modules.conf.d
+%{_prefix}/lib/epc/service.conf.d
%files -n event-processing-extra -f extra-files
--- /dev/null
+/*
+ * This file is part of activationd.
+ *
+ * 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 <malloc.h>
+#include <errno.h>
+
+#include "decision_made_event.h"
+#include "action.h"
+#include "event_processor.h"
+#include "log.h"
+#include "service.h"
+#include "common.h"
+#include "database.h"
+#include "json-config.h"
+
+#include "vconf_key_changed_event.h"
+
+#define MODULE_NAME "activation_decision_maker"
+
+struct activation_decision_maker {
+ struct epc_event_handler eh;
+ struct list_head rules;
+};
+
+struct rule {
+ const char *event;
+ void *ev_data;
+ bool match_oldval;
+ bool match_newval;
+ const char *action;
+ const char *target;
+
+ struct list_head node;
+};
+
+static int activation_event_match(struct epc_event_handler *handler,
+ struct epc_event *ev)
+{
+ if (strcmp(ev->type->name, VCONF_KEY_CHANGED_EVENT_ID) == 0)
+ return 1;
+
+ return 0;
+}
+
+static int vconf_field_cmp(struct vkc_value *a, struct vkc_value *b)
+{
+ if (a->type != b->type)
+ return 0;
+
+ switch (a->type){
+ case VKC_BOOL:
+ return a->value.b == b->value.b;
+ case VKC_INT:
+ return a->value.i == b->value.i;
+ case VKC_DOUBLE:
+ return a->value.d == b->value.d;
+ case VKC_STRING:
+ return strcmp(a->value.s, b->value.s) == 0;
+ }
+}
+
+static int vconf_rule_match(struct rule *r, struct epc_event *event)
+{
+ struct vconf_key_changed_event *ev = to_vconf_key_changed_event(event);
+ struct vconf_key_changed_event_data *ev_data = r->ev_data;
+
+ if (ev_data->key_name && strcmp(ev_data->key_name, ev->key_name))
+ return 0;
+
+ if (r->match_oldval && !vconf_field_cmp(&ev_data->oldval, &ev->oldval))
+ return 0;
+
+ if (r->match_newval && !vconf_field_cmp(&ev_data->newval, &ev->newval))
+ return 0;
+
+ return 1;
+}
+
+static int execute_rule(struct rule *r, struct epc_event *ev)
+{
+ struct dm_event_data ev_data = {
+ .reason = ev,
+ .who_made = MODULE_NAME,
+ };
+ struct epc_event *new_ev;
+ int ret;
+
+ 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");
+ goto out;
+ }
+
+ ret = epc_fill_for_unit_start(ev_data.action_data, r->target);
+ if (ret) {
+ log_error("Unable to create event data");
+ goto out;
+ }
+ epc_fill_for_unit_action(ev_data.action_data, r->action);
+
+ ret = epc_event_create(DECISION_MADE_EVENT_ID, &ev_data, &new_ev);
+ if (ret) {
+ log_error("Unable to create event");
+ goto out;
+ }
+
+ ret = event_processor_report_event(new_ev);
+ epc_event_unref(new_ev);
+ if (ret) {
+ log_error("Unable to report event");
+ goto out;
+ }
+
+out:
+ epc_object_unref(ev_data.action_data);
+ return ret;
+}
+
+static int activation_make_decision(struct epc_event_handler *handler)
+{
+ struct epc_event *ev = pop_epc_event(&handler->event_queue);
+ struct activation_decision_maker *dm = container_of(handler,
+ struct activation_decision_maker, eh);
+ int ret;
+ struct rule *r;
+
+ list_for_each_entry(r, &dm->rules, node) {
+ if (strcmp(r->event, ev->type->name))
+ continue;
+
+ if (!strcmp(ev->type->name, VCONF_KEY_CHANGED_EVENT_ID) == 0 ||
+ !vconf_rule_match(r, ev))
+ continue;
+
+ ret = execute_rule(r, ev);
+ if (ret < 0)
+ break;
+ }
+
+ epc_object_unref(ev);
+ return 0;
+}
+
+static int parse_vconf_field(json_object *root, const char *key, struct vkc_value *value)
+{
+ struct json_object *node = NULL;
+ enum json_type type;
+ char *str;
+
+ if (!json_object_object_get_ex(root, key, &node)) {
+ log_debug("Config does not contain %s parameter", key);
+ return -1;
+ }
+
+ type = json_object_get_type(node);
+
+ switch (type) {
+ case json_type_boolean:
+ value->type = VKC_BOOL;
+ value->value.b = json_object_get_boolean(node);
+ break;
+ case json_type_double:
+ value->type = VKC_DOUBLE;
+ value->value.d = json_object_get_double(node);
+ break;
+ case json_type_int:
+ value->type = VKC_INT;
+ value->value.i = json_object_get_int(node);
+ break;
+ case json_type_string:
+ value->type = VKC_STRING;
+ str = json_object_get_string(node);
+ if (!str)
+ goto err;
+
+ value->value.s = strdup(str);
+ break;
+ case json_type_array:
+ case json_type_object:
+ log_error("Field %s must have simple type", key);
+ return -EINVAL;
+ break;
+ case json_type_null:
+ return -EINVAL;
+ break;
+ }
+
+ return 0;
+
+err:
+ return -1;
+}
+
+static int parse_vconf_data(json_object *root, struct rule *r)
+{
+ struct vconf_key_changed_event_data *evd;
+ int ret;
+
+ evd = calloc(1, sizeof(*evd));
+ if (!evd)
+ return -ENOMEM;
+
+ (void) get_config_field(root, "key_name", &evd->key_name, json_type_string);
+ ret = parse_vconf_field(root, "oldval", &evd->oldval);
+ r->match_oldval = ret >= 0;
+ ret = parse_vconf_field(root, "newval", &evd->newval);
+ r->match_newval = ret >= 0;
+
+ r->ev_data = evd;
+
+ return 0;
+}
+
+static void rule_cleanup(struct rule *r)
+{
+ free(r->ev_data);
+}
+
+static void clean_rules(struct activation_decision_maker *dm)
+{
+ struct rule *r, *next;
+
+ list_for_each_entry_safe(r, next, &dm->rules, node) {
+ list_del_init(&r->node);
+ rule_cleanup(r);
+ free(r);
+ }
+}
+
+static int add_rule(struct activation_decision_maker *dm, json_object *root)
+{
+ int ret;
+ struct rule *r;
+ char *str;
+
+ r = calloc(1, sizeof(*r));
+ if (!r) {
+ log_error("Could not allocate rule object");
+ return -ENOMEM;
+ }
+
+ ret = get_config_field(root, "event", &r->event, json_type_string);
+ if (ret < 0)
+ goto out;
+
+ log_debug("Adding rule for event %s", r->event);
+
+ if (strcmp(r->event, VCONF_KEY_CHANGED_EVENT_ID) == 0) {
+ parse_vconf_data(root, r);
+ } else {
+ log_warning("Event type %s is not supported by activationd module\n", r->event);
+ goto out;
+ }
+
+ ret = get_config_field(root, "action", &r->action, json_type_string);
+ if (ret < 0)
+ goto out;
+
+ ret = get_config_field(root, "target", &r->target, json_type_string);
+ if (ret < 0)
+ goto out;
+
+ list_add_tail(&r->node, &dm->rules);
+
+ return 0;
+
+out:
+ rule_cleanup(r);
+ free(r);
+ return ret;
+}
+
+static int activation_init(struct epc_event_handler *handler, struct epc_config *config)
+{
+ struct activation_decision_maker *dm = container_of(handler,
+ struct activation_decision_maker, eh);
+ int len;
+ int i;
+ int ret;
+ json_object *array, *obj;
+
+ INIT_LIST_HEAD(&dm->rules);
+
+ if (config == NULL)
+ return 0;
+
+ if (!json_object_object_get_ex(config->root, "rules", &array)) {
+ log_error("There are no 'rules' array");
+ return -EINVAL;
+ }
+
+ if (!json_object_is_type(array, json_type_array)) {
+ log_error("Config value is not an array");
+ return -EINVAL;
+ }
+
+ len = json_object_array_length(array);
+ for (i = 0; i < len; ++i) {
+ obj = json_object_array_get_idx(array, i);
+ ret = add_rule(dm, obj);
+ if (ret < 0) {
+ log_error("Could not add rule");
+ goto cleanup;
+ }
+ }
+
+ return 0;
+
+cleanup:
+ clean_rules(dm);
+ return ret;
+};
+
+static int activation_cleanup(struct epc_event_handler *handler)
+{
+ struct activation_decision_maker *dm = container_of(handler,
+ struct activation_decision_maker, eh);
+
+ clean_rules(dm);
+}
+
+static struct activation_decision_maker activation_dm = {
+ .eh = {
+ .name = MODULE_NAME,
+ .init = activation_init,
+ .cleanup = activation_cleanup,
+ .event_match = activation_event_match,
+ .handle_event = activation_make_decision,
+
+ .node = LIST_HEAD_INIT(activation_dm.eh.node),
+ },
+};
+
+EPC_EVENT_HANDLER_REGISTER(activation_dm.eh, activation_eh,
+ EPC_MODULE_TYPE_DECISION_MAKER)
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <errno.h>
+#include <malloc.h>
+
+#include "vconf_key_changed_event.h"
+
+static int allocate_vkc_event(struct epc_event_type *type,
+ void *data, struct epc_event **ev)
+{
+ struct vconf_key_changed_event *vkc_ev;
+ struct vconf_key_changed_event_data *vkc_ev_data = data;
+ int ret;
+
+ vkc_ev = calloc(1, sizeof(*vkc_ev));
+ if (!vkc_ev)
+ return -ENOMEM;
+
+ ret = epc_event_init_internal(type, &vkc_ev->event);
+ if (ret)
+ goto cleanup;
+
+ vkc_ev->event_time = vkc_ev_data->event_time;
+ vkc_ev->key_name = vkc_ev_data->key_name;
+ vkc_ev->oldval = vkc_ev_data->oldval;
+ vkc_ev->newval = vkc_ev_data->newval;
+
+ *ev = &vkc_ev->event;
+ return 0;
+cleanup:
+ free(vkc_ev);
+
+ return ret;
+}
+
+static int deserialize_vkc_event(struct epc_event_type *type,
+ struct epc_object *data, struct epc_event **ev)
+{
+ int ret = -EINVAL;
+ struct vconf_key_changed_event_data vkc_ev_data;
+ struct epc_object *obj;
+ memset(&vkc_ev_data, 0, sizeof(vkc_ev_data));
+
+ list_for_each_entry(obj, &data->val.children, node) {
+ const bool oldval_match = !strcmp(VKC_EV_OLDVAL, obj->key);
+ const bool newval_match = !strcmp(VKC_EV_NEWVAL, obj->key);
+
+ switch (obj->type) {
+ case TYPE_TIME_T:
+ if (!strcmp(VKC_EV_TIME, obj->key))
+ vkc_ev_data.event_time = obj->val.time;
+ break;
+ case TYPE_BOOL:
+ if (oldval_match) {
+ vkc_ev_data.oldval.value.b = obj->val.b;
+ vkc_ev_data.oldval.type = VKC_BOOL;
+ } else if (newval_match) {
+ vkc_ev_data.newval.value.b = obj->val.b;
+ vkc_ev_data.newval.type = VKC_BOOL;
+ }
+ break;
+ case TYPE_INT:
+ if (oldval_match) {
+ vkc_ev_data.oldval.value.i = obj->val.i;
+ vkc_ev_data.oldval.type = VKC_INT;
+ } else if (newval_match) {
+ vkc_ev_data.newval.value.i = obj->val.i;
+ vkc_ev_data.newval.type = VKC_INT;
+ }
+ break;
+ case TYPE_DOUBLE:
+ if (oldval_match) {
+ vkc_ev_data.oldval.value.d = obj->val.d;
+ vkc_ev_data.oldval.type = VKC_DOUBLE;
+ } else if (newval_match) {
+ vkc_ev_data.newval.value.d = obj->val.d;
+ vkc_ev_data.newval.type = VKC_DOUBLE;
+ }
+ break;
+ case TYPE_STRING:
+ if (!strcmp(VKC_EV_NAME, obj->key)) {
+ vkc_ev_data.key_name = obj->val.s;
+ } else if (oldval_match) {
+ vkc_ev_data.oldval.value.s = obj->val.s;
+ vkc_ev_data.oldval.type = VKC_STRING;
+ } else if (newval_match) {
+ vkc_ev_data.newval.value.s = obj->val.s;
+ vkc_ev_data.newval.type = VKC_STRING;
+ }
+ break;
+ }
+ }
+
+ ret = allocate_vkc_event(type, &vkc_ev_data, ev);
+ if (ret < 0)
+ goto finish;
+
+ ret = epc_event_deserialize_internal(data, type, *ev);
+ if (ret < 0) {
+ struct vconf_key_changed_event *vkc_ev =
+ to_vconf_key_changed_event(*ev);
+ free(vkc_ev);
+ goto finish;
+ }
+ ret = 0;
+finish:
+ return ret;
+}
+
+static void vkc_event_release(struct epc_event *ev)
+{
+ struct vconf_key_changed_event *vkc_ev =
+ to_vconf_key_changed_event(ev);
+
+ if (vkc_ev->key_name)
+ free(vkc_ev->key_name);
+ if (vkc_ev->oldval.type == VKC_STRING)
+ free(vkc_ev->oldval.value.s);
+ if (vkc_ev->newval.type == VKC_STRING)
+ free(vkc_ev->newval.value.s);
+
+ epc_event_cleanup_internal(&vkc_ev->event);
+ free(vkc_ev);
+}
+
+static void vkc_event_serialize(struct epc_event *ev, struct epc_object *out)
+{
+ struct vconf_key_changed_event *vkc_ev =
+ to_vconf_key_changed_event(ev);
+ epc_event_serialize_internal(ev, out);
+ epc_object_append_string(out, VKC_EV_NAME, vkc_ev->key_name);
+
+ switch (vkc_ev->oldval.type) {
+ case VKC_BOOL:
+ epc_object_append_bool(out, VKC_EV_OLDVAL, vkc_ev->oldval.value.b);
+ break;
+ case VKC_INT:
+ epc_object_append_int(out, VKC_EV_OLDVAL, vkc_ev->oldval.value.i);
+ break;
+ case VKC_DOUBLE:
+ epc_object_append_double(out, VKC_EV_OLDVAL, vkc_ev->oldval.value.d);
+ break;
+ case VKC_STRING:
+ epc_object_append_string(out, VKC_EV_OLDVAL, vkc_ev->oldval.value.s);
+ break;
+ }
+
+ switch (vkc_ev->newval.type) {
+ case VKC_BOOL:
+ epc_object_append_bool(out, VKC_EV_NEWVAL, vkc_ev->newval.value.b);
+ break;
+ case VKC_INT:
+ epc_object_append_int(out, VKC_EV_NEWVAL, vkc_ev->newval.value.i);
+ break;
+ case VKC_DOUBLE:
+ epc_object_append_double(out, VKC_EV_NEWVAL, vkc_ev->newval.value.d);
+ break;
+ case VKC_STRING:
+ epc_object_append_string(out, VKC_EV_NEWVAL, vkc_ev->newval.value.s);
+ break;
+ }
+ epc_object_append_time_t(out, VKC_EV_TIME, vkc_ev->event_time);
+}
+
+static struct epc_event_type vconf_key_changed_event_type = {
+ .name = VCONF_KEY_CHANGED_EVENT_ID,
+ .default_ops = {
+ .release = vkc_event_release,
+ .serialize = vkc_event_serialize,
+ },
+ .allocate_event = allocate_vkc_event,
+ .deserialize_event = deserialize_vkc_event,
+ .node = LIST_HEAD_INIT(vconf_key_changed_event_type.node),
+};
+
+EPC_EVENT_TYPE_REGISTER(vconf_key_changed_event_type, vconf_key_changed_et)
--- /dev/null
+/*
+ * 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 <vconf.h>
+#include <glib.h>
+#include <time.h>
+
+#include "log.h"
+#include "module.h"
+#include "common.h"
+#include "list.h"
+#include "json-config.h"
+#include "event_processor.h"
+#include "vconf_key_changed_event.h"
+
+struct vconf_kv {
+ char *name;
+ struct vkc_value val;
+ struct list_head node;
+};
+
+struct vconf_listener {
+ struct epc_module module;
+ struct list_head keys;
+};
+
+#define to_vconf_listener(MOD) \
+ container_of(MOD, struct vconf_listener, module)
+
+static struct vconf_kv *find_vconf_kv(struct vconf_listener *l, const char *name)
+{
+ struct vconf_kv *k = NULL;
+
+ list_for_each_entry(k, &l->keys, node) {
+ if (!strcmp(k->name, name))
+ return k;
+ }
+ return NULL;
+}
+
+static int copy_vkc_value(struct vkc_value *from, struct vkc_value *to)
+{
+ to->type = from->type;
+ switch (from->type){
+ case VKC_BOOL:
+ to->value.b = from->value.b;
+ break;
+ case VKC_INT:
+ to->value.i = from->value.i;
+ break;
+ case VKC_DOUBLE:
+ to->value.d = from->value.d;
+ break;
+ case VKC_STRING:
+ to->value.s = strdup(from->value.s);
+ if (!to->value.s)
+ return -ENOMEM;
+ break;
+ }
+ return 0;
+}
+
+static int set_object_value(struct vconf_kv *k, keynode_t *node)
+{
+ assert (k);
+ assert (node);
+ int ret = 0;
+
+ switch(vconf_keynode_get_type(node)) {
+ case VCONF_TYPE_INT:{
+ int value = vconf_keynode_get_int(node);
+ k->val.value.i = value;
+ k->val.type = VKC_INT;
+ }break;
+ case VCONF_TYPE_BOOL:{
+ int value = vconf_keynode_get_bool(node);
+ if (value == -1) {
+ ret = -1;
+ break;
+ }
+ k->val.value.b = value;
+ k->val.type = VKC_BOOL;
+ }break;
+ case VCONF_TYPE_DOUBLE:{
+ double value = vconf_keynode_get_dbl(node);
+ k->val.value.d = value;
+ k->val.type = VKC_DOUBLE;
+ }break;
+ case VCONF_TYPE_STRING:{
+ char *value = vconf_keynode_get_str(node);
+ if (!value) {
+ ret = -1;
+ break;
+ }
+ k->val.value.s = strdup(value);
+ if (!k->val.value.s)
+ return -1;
+ k->val.type = VKC_STRING;
+ }break;
+ default:
+ ret = -1;
+ break;
+ }
+
+ return ret;
+}
+
+static void on_vconf_key_changed(keynode_t *key, void *data)
+{
+ struct vconf_listener *l = (struct vconf_listener *)data;
+ char *name = NULL;
+ struct vconf_key_changed_event_data vkc_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;
+ }
+
+ name = vconf_keynode_get_name(key);
+ if (!name) {
+ log_debug("vconf: key name not found (key=%s)", name);
+ return;
+ }
+
+ struct vconf_kv *k = find_vconf_kv(l, name);
+ if (!k) {
+ log_debug("vconf: key-value pair not found (key=%s)", name);
+ return;
+ }
+
+ vkc_ev_data.event_time = ts.tv_sec;
+ vkc_ev_data.key_name = strdup(name);
+ if (vkc_ev_data.key_name == NULL)
+ goto finish;
+
+ if (copy_vkc_value(&k->val, &vkc_ev_data.oldval) < 0)
+ goto finish;
+
+ if (k->val.type == VKC_STRING) {
+ free(k->val.value.s);
+ k->val.value.s = NULL;
+ }
+
+ if (set_object_value(k, key) < 0)
+ goto finish;
+
+ if (copy_vkc_value(&k->val, &vkc_ev_data.newval) < 0)
+ goto finish;
+
+ ret = epc_event_create(VCONF_KEY_CHANGED_EVENT_ID, &vkc_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;
+finish:
+ free(vkc_ev_data.key_name);
+ if (vkc_ev_data.oldval.type == VKC_STRING)
+ free(vkc_ev_data.oldval.value.s);
+ if (vkc_ev_data.newval.type == VKC_STRING)
+ free(vkc_ev_data.newval.value.s);
+
+ return;
+}
+
+static int add_key(struct vconf_listener *l, const char *key, keynode_t *node)
+{
+ int ret = 0;
+ struct vconf_kv *k;
+
+ assert (l);
+ assert (key);
+ //assert (node); /* it's ok to be null */
+
+ k = (struct vconf_kv*)malloc(sizeof(struct vconf_kv));
+ if (k == NULL) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ k->name = strdup(key);
+ if (k->name == NULL) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ if (node != NULL && set_object_value(k, node) < 0) {
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ list_add_tail(&k->node, &l->keys);
+ if (vconf_notify_key_changed(key, on_vconf_key_changed, l) < 0)
+ log_error("vconf_notify_key_changed() failed (%s)", key);
+
+ return 0;
+
+cleanup:
+ if (k) {
+ free(k->name);
+ free(k);
+ }
+ return ret;
+}
+
+static void cleanup(struct vconf_listener *l)
+{
+ struct vconf_kv *k, *next;
+
+ list_for_each_entry_safe(k, next, &l->keys, node) {
+ list_del(&k->node);
+ free(k->name);
+ if (k->val.type == VKC_STRING)
+ free(k->val.value.s);
+ free(k);
+ }
+}
+
+static int vconf_listener_init(struct epc_module *module,
+ struct epc_config *config,
+ sd_event* event)
+{
+ struct vconf_listener *listener = to_vconf_listener(module);
+ int ret = 0;
+ json_object *arr, *val;
+ keylist_t *klist;
+ keynode_t *knode;
+
+ INIT_LIST_HEAD(&listener->keys);
+
+ if (!json_object_object_get_ex(config->root, "keys", &arr)) {
+ log_error("Config does not contain 'keys' parameter.");
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ if (!json_object_is_type(arr, json_type_array)) {
+ log_error("Config value is not an array");
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ for (size_t i = 0; i < json_object_array_length(arr); i++) {
+ knode = NULL;
+ val = json_object_array_get_idx(arr, i);
+ if (val == NULL) {
+ log_error("vconf: no specified key (%zu)\n", i);
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ klist = vconf_keylist_new();
+ if (!klist) {
+ log_error("vconf: could not create keylist for %s\n", json_object_get_string(val));
+ continue;
+ }
+
+ ret = vconf_get(klist, json_object_get_string(val), VCONF_GET_ALL);
+ if (ret < 0) {
+ log_debug("vconf: keylist does not contain key %s\n", json_object_get_string(val));
+ goto free_list;
+ }
+
+ ret = vconf_keylist_lookup(klist, json_object_get_string(val), &knode);
+ if (ret <= 0) {
+ log_debug("vconf_keylist_lookup failed for %s\n", json_object_get_string(val));
+ //goto free_list;
+ }
+
+ ret = add_key(listener, json_object_get_string(val), knode);
+ if (ret < 0)
+ log_error("vconf: could not add key\n");
+
+ free_list:
+ vconf_keylist_free(klist);
+ }
+ return 0;
+
+cleanup:
+ cleanup(listener);
+ return ret;
+}
+
+static void vconf_listener_cleanup(struct epc_module *module)
+{
+ struct vconf_listener *listener = to_vconf_listener(module);
+ cleanup(listener);
+}
+
+struct vconf_listener vconf_listener = {
+ .module = {
+ .name = "vconf_listener",
+ .type = EPC_MODULE_TYPE_LISTENER,
+
+ .init = vconf_listener_init,
+ .cleanup = vconf_listener_cleanup,
+ .node = LIST_HEAD_INIT(vconf_listener.module.node),
+ },
+};
+
+EPC_MODULE_REGISTER(&vconf_listener.module)
--- /dev/null
+[Unit]
+Description=activationd acceptance tests service
+
+[Service]
+Type=simple
+ExecStart=/usr/bin/activationd_acceptance_service
--- /dev/null
+/*
+ * 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 <systemd/sd-bus.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/timerfd.h>
+#include <vconf.h>
+
+#include "common.h"
+#include "systemd_dbus.h"
+
+#define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties"
+#define SERVICE_NAME "activationd-acceptance-test.service"
+#define VCONF_ACTIVATION_KEY "file/activationd/acceptance"
+
+enum unit_state {
+ UNKNOWN,
+ ACTIVE,
+ INACTIVE
+};
+
+struct action {
+ const char *action;
+ enum unit_state expected_states[2];
+ int state_count;
+ int current_state;
+};
+
+struct test_sequence {
+ struct action actions[3];
+ int current_action;
+ int ok;
+};
+
+
+struct test_data {
+ sd_bus *bus;
+ sd_event *loop;
+ struct test_sequence test;
+ enum unit_state state;
+ enum unit_state prev_state;
+ char *path;
+};
+
+static const char *from_unit_state(enum unit_state state)
+{
+ switch (state) {
+ case UNKNOWN:
+ return "unknown";
+ case ACTIVE:
+ return "active";
+ default:
+ return "inactive";
+ }
+}
+
+static enum unit_state to_unit_state(const char *state)
+{
+ if (!strncmp (state, "active", 6))
+ return ACTIVE;
+ else if (!strncmp (state, "inactive", 8) || !strncmp (state, "deactivating", 12))
+ return INACTIVE;
+ else
+ return UNKNOWN;
+}
+
+static int set_vconf_key(const char *val)
+{
+ keylist_t *kl = NULL;
+ kl = vconf_keylist_new();
+ if (!kl) {
+ fprintf(stderr, "error creating vconf keylists\n");
+ return -1;
+ }
+
+ vconf_keylist_add_str(kl, VCONF_ACTIVATION_KEY, val);
+ if (vconf_set(kl) < 0) {
+ fprintf(stderr, "error saving vconf key\n");
+ vconf_keylist_free(kl);
+ return -1;
+ }
+
+ vconf_keylist_free(kl);
+ return 0;
+}
+
+static int test_step(struct test_data *data)
+{
+ struct test_sequence *test = &data->test;
+ if (test->current_action == ARRAY_SIZE(test->actions)) {
+ sd_event_exit(data->loop, EXIT_SUCCESS);
+ test->ok = 1;
+ return 0;
+ }
+
+ if (set_vconf_key(test->actions[test->current_action].action))
+ return -1;
+ return 0;
+}
+
+static int check_test_result(struct test_data *data)
+{
+ struct action *a = &data->test.actions[data->test.current_action];
+ enum unit_state exp_state = a->expected_states[a->current_state];
+
+ if (data->test.ok == 1)
+ return 0;
+
+ if (data->state != exp_state) {
+ data->test.ok = 1;
+ fprintf(stderr, "test failed (%s instead of %s at step %d of %s)\n",
+ from_unit_state(data->state),
+ from_unit_state(exp_state),
+ a->current_state,
+ a->action);
+
+ sd_event_exit(data->loop, EXIT_FAILURE);
+ return -1;
+ }
+ a->current_state++;
+ if (a->current_state == a->state_count) {
+ data->test.current_action++;
+ if (test_step(data) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+
+static int check_unit_state(struct test_data *data, const char *state)
+{
+ enum unit_state new_state;
+
+ new_state = to_unit_state(state);
+ if (data->state != new_state) {
+ data->prev_state = data->state;
+ data->state = new_state;
+ check_test_result(data);
+ }
+ return 0;
+}
+
+
+static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+ int r;
+ struct test_data *data = (struct test_data*)userdata;
+ const char *iface;
+ const char *member;
+ const char *value = NULL;
+ const char *contents;
+
+ r = sd_bus_message_read(m, "s", &iface);
+ if (r < 0) {
+ fprintf(stderr, "Failed to parse PropertiesChanged\n");
+ return r;
+ }
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
+ char type;
+
+ r = sd_bus_message_read(m, "s", &member);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_peek_type(m, &type, &contents);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
+ if (r < 0)
+ return r;
+
+ if (strcmp(member, "ActiveState") == 0) {
+ r = sd_bus_message_read(m, "s", &value);
+ if (r < 0)
+ return r;
+ } else {
+ r = sd_bus_message_skip(m, contents);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ if (value)
+ check_unit_state(data, value);
+ return 0;
+}
+
+static void cleanup(struct test_data *data)
+{
+ free(data->path);
+ if (!data->bus)
+ return;
+ sd_bus_flush(data->bus);
+ sd_bus_unref(data->bus);
+}
+
+static int signal_handler(sd_event_source *s,
+ const struct signalfd_siginfo *si,
+ void *userdata)
+{
+ sd_event *loop = userdata;
+ sd_event_exit(loop, EXIT_FAILURE);
+ return 0;
+}
+
+static int start_test(sd_event_source *s, uint64_t usec, void *userdata)
+{
+ struct test_data *data = (struct test_data*)userdata;
+ return test_step(data);
+}
+
+static int timeout_handler(sd_event_source *s, uint64_t usec, void *userdata)
+{
+ struct test_data *data = (struct test_data*)userdata;
+ fprintf(stderr, "test timed out\n");
+ sd_event_exit(data->loop, EXIT_FAILURE);
+ return 0;
+}
+
+static int call_subscribe(struct test_data *data)
+{
+ __attribute__((cleanup(sd_bus_message_unrefp))) sd_bus_message *reply = NULL;
+ __attribute__((cleanup(sd_bus_error_free))) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int ret;
+
+ ret = sd_bus_call_method(data->bus,
+ SYSTEMD_SERVICE,
+ SYSTEMD_OBJ,
+ SYSTEMD_MANAGER_INTERFACE,
+ "Subscribe",
+ &error,
+ &reply,
+ "");
+
+ if (ret < 0) {
+ fprintf(stderr, "Failed to issue Subscribe() call: %s\n", error.message);
+ return -1;
+ }
+ return 0;
+}
+
+static int call_load_unit(struct test_data *data)
+{
+ __attribute__((cleanup(sd_bus_message_unrefp))) sd_bus_message *reply = NULL;
+ __attribute__((cleanup(sd_bus_error_free))) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int ret;
+ const char *path = NULL;
+ char err[512];
+
+ ret = sd_bus_call_method(data->bus,
+ SYSTEMD_SERVICE,
+ SYSTEMD_OBJ,
+ SYSTEMD_MANAGER_INTERFACE,
+ "LoadUnit",
+ &error,
+ &reply,
+ "s",
+ SERVICE_NAME);
+
+ if (ret < 0) {
+ fprintf(stderr, "Failed to issue LoadUnit() call: %s\n", error.message);
+ return -1;
+ }
+
+ ret = sd_bus_message_read(reply, "o", &path);
+ if (ret < 0) {
+ if (strerror_r(-ret, err, sizeof err))
+ fprintf(stderr, "Failed to read reply: %s\n", err);
+ return -1;
+ }
+
+ if (!path) {
+ fprintf(stderr, "ould not get object path\n");
+ return -1;
+ }
+ data->path = strdup(path);
+ return 0;
+}
+int main(int argc, char *argv[])
+{
+ struct test_data data = {
+ .bus = NULL,
+ .loop = NULL,
+ .state = UNKNOWN,
+ .prev_state = UNKNOWN,
+ .path = NULL,
+ .test = {
+ .actions = {
+ {"START", {ACTIVE, UNKNOWN}, 1, 0},
+ {"RELOAD", {INACTIVE, ACTIVE}, 2, 0},
+ {"STOP", {INACTIVE, UNKNOWN}, 1, 0}
+ },
+ .current_action = 0,
+ .ok = 0
+ }
+ };
+
+ int r;
+ uint64_t now;
+ sigset_t ss;
+ char buf[512];
+ char err[512];
+
+ r = sd_bus_open_system(&data.bus);
+ if (r < 0) {
+ if (strerror_r(-r, err, sizeof err))
+ fprintf(stderr, "Failed to read reply: %s\n", err);
+ goto fail;
+ }
+
+ r = call_subscribe(&data);
+ if (r < 0)
+ goto fail;
+
+ r = call_load_unit(&data);
+ if (r < 0)
+ goto fail;
+
+ r = snprintf(buf, sizeof buf, "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged', path='%s'", data.path);
+ if (r < 0) {
+ if (strerror_r(-r, err, sizeof err))
+ fprintf(stderr, "Failed to create match rule: %s\n", err);
+ goto fail;
+ }
+
+ r = sd_bus_add_match(data.bus, NULL, buf, on_properties_changed, &data);
+ if (r < 0) {
+ if (strerror_r(-r, err, sizeof err))
+ fprintf(stderr, "Failed to add PropertiesChanged match: %s\n", err);
+ goto fail;
+ }
+
+ r = sd_event_new(&data.loop);
+ if (r < 0) {
+ if (strerror_r(-r, err, sizeof err))
+ fprintf(stderr, "Failed to create main loop: %s\n", err);
+ goto fail;
+ }
+
+ sigemptyset(&ss);
+ sigaddset(&ss, SIGINT);
+ sigaddset(&ss, SIGTERM);
+ r = sigprocmask(SIG_BLOCK, &ss, NULL);
+ if (r < 0) {
+ if (strerror_r(-r, err, sizeof err))
+ fprintf(stderr, "Failed to change signal mask: %s\n", err);
+ goto fail;
+ }
+
+ r = sd_event_add_signal(data.loop, NULL, SIGINT, signal_handler, data.loop);
+ if (r < 0) {
+ if (strerror_r(-r, err, sizeof err))
+ fprintf(stderr, "Failed to register SIGINT handler: %s\n", err);
+ goto fail;
+ }
+
+ r = sd_event_add_signal(data.loop, NULL, SIGTERM, signal_handler, data.loop);
+ if (r < 0) {
+ if (strerror_r(-r, err, sizeof err))
+ fprintf(stderr, "Failed to register SIGTERM handler: %s\n", err);
+ goto fail;
+ }
+
+ r = sd_bus_attach_event(data.bus, data.loop, 0);
+ if (r < 0) {
+ if (strerror_r(-r, err, sizeof err))
+ fprintf(stderr, "Failed to attach bus to event loop: %s\n", err);
+ goto fail;
+ }
+
+ r = sd_event_add_time(data.loop, NULL, CLOCK_REALTIME, 0, 0, start_test, &data);
+ if (r < 0) {
+ if (strerror_r(-r, err, sizeof err))
+ fprintf(stderr, "Failed to attach timer to event loop: %s\n", err);
+ goto fail;
+ }
+
+ sd_event_now(data.loop, CLOCK_REALTIME, &now);
+
+ r = sd_event_add_time(data.loop, NULL, CLOCK_REALTIME, now+10000000, 0, timeout_handler, &data);
+ if (r < 0) {
+ if (strerror_r(-r, err, sizeof err))
+ fprintf(stderr, "Failed to attach timer to event loop: %s\n", err);
+ goto fail;
+ }
+
+ r = sd_event_loop(data.loop);
+ if (r < 0) {
+ if (strerror_r(-r, err, sizeof err))
+ fprintf(stderr, "Failed to run main loop: %s\n", err);
+ goto fail;
+ }
+
+ cleanup(&data);
+ return r;
+
+fail:
+ cleanup(&data);
+ return EXIT_FAILURE;
+}
--- /dev/null
+/*
+ * 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 <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+
+static void exit_handler(int signo)
+{
+ exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+ if (signal(SIGINT, exit_handler) == SIG_ERR ||
+ signal(SIGQUIT, exit_handler) == SIG_ERR ||
+ signal(SIGTERM, exit_handler) == SIG_ERR) {
+ perror("catching SIGNAL");
+ exit(EXIT_FAILURE);
+ }
+
+ printf("service started, do nothing...\n");
+ while (1)
+ sleep(10);
+
+ exit_handler(0);
+}
+
--- /dev/null
+#!/bin/sh
+
+EPCD_PID=-1
+
+cleanup() {
+ systemctl stop activationd-acceptance-test
+ [ $EPCD_PID -ne -1 ] && kill $EPCD_PID > /dev/null 2>&1
+ vconftool unset file/activationd/acceptance
+ exit 0
+}
+
+trap cleanup 0
+
+vconftool -f set -t string file/activationd/acceptance foo
+
+# assumes there's no epcd service running
+epcd > /dev/null 2>&1 &
+EPCD_PID=$!
+sleep 2
+
+/usr/bin/activationd_acceptance_checker
+
+if [ "$?" -eq "0" ]; then
+ echo "ok"
+else
+ echo "fail"
+fi
+