Rework faultd's configuration mechanism 50/163850/11
authorKonrad Kuchciak <k.kuchciak@samsung.com>
Tue, 12 Dec 2017 10:50:24 +0000 (11:50 +0100)
committerKonrad Kuchciak <k.kuchciak@samsung.com>
Tue, 9 Jan 2018 12:51:26 +0000 (13:51 +0100)
Faultd's configuration consists of two main parts,
modules configuration and service configuration.
Each of these is composed of default configuration
and then is overridden by custom/user configuration.

1. Modules

* default config:
/usr/lib/faultd/faultd.conf - main config file
/usr/lib/faultd/modules.conf.d/<mod_name>.conf.d/*.conf -
config pieces for specific module, loaded in lexicographical
order

* custom config:
/etc/faultd/faultd.conf
/etc/faultd/modules.conf.d/<mod_name>.conf.d/*.conf

2. Services

* default config:
/usr/lib/faultd/service.conf.d/<srv_name>.conf.d/*.conf

* custom config:
/etc/faultd/service.conf.d/<srv_name>.conf.d/*.conf

Change-Id: I381ad009221070c5e82f2fbbe4ac896bdf9f189b
Signed-off-by: Konrad Kuchciak <k.kuchciak@samsung.com>
20 files changed:
Makefile.am
conf.d/dbus.service.conf [deleted file]
conf.d/display-manager.service.conf [deleted file]
faultd.conf
generators/vip-generator.c
modules.conf.d/standard_fault_eh.conf.d/50-default.conf [new file with mode: 0644]
modules.conf.d/standard_reboot_eh.conf.d/50-default.conf [new file with mode: 0644]
packaging/faultd.spec
service.conf.d/dbus.service.conf.d/50-default.conf [new file with mode: 0644]
service.conf.d/display-manager.service.conf.d/50-default.conf [new file with mode: 0644]
src/core/faultd-config.c
src/core/faultd-config.h
src/core/service.c
src/faultd.c
src/util/json-config.c
src/util/json-config.h
tests/services/faultd-failed-service-with-recovery.service.conf [deleted file]
tests/services/faultd-failed-service-with-recovery.service.conf.d/50-default.conf [new file with mode: 0644]
tests/services/faultd-failed-vip-service.service.conf [deleted file]
tests/services/faultd-failed-vip-service.service.conf.d/50-default.conf [new file with mode: 0644]

index 2324694486168abf0f4753a4ff25bbb81cd3b5b9..a5fa6a3e2a9a1ad1051a8c7dbbedfc3b4b6953e5 100644 (file)
@@ -7,8 +7,12 @@ AM_CPPFLAGS = \
        -include $(top_builddir)/config.h \
        -DSYSCONFDIR=\""$(sysconfdir)"\" \
        -DDBDIR=\""$(dbdir)"\" \
-       -DFAULTD_SERVICES_CONFIG_PATH=\""${sysconfdir}/faultd/conf.d/"\" \
-       -DFAULTD_DEFAULT_CONFIG_FILE=\""${sysconfdir}/faultd/faultd.conf"\" \
+       -DFAULTD_CUSTOM_CONFIG_FILE=\""${sysconfdir}/faultd/faultd.conf"\" \
+       -DFAULTD_CUSTOM_MODULES_CONFIG_PATH=\""${sysconfdir}/faultd/modules.conf.d/"\" \
+       -DFAULTD_CUSTOM_SERVICES_CONFIG_PATH=\""${sysconfdir}/faultd/service.conf.d/"\" \
+       -DFAULTD_DEFAULT_CONFIG_FILE=\""${prefix}/lib/faultd/faultd.conf"\" \
+       -DFAULTD_DEFAULT_MODULES_CONFIG_PATH=\""${prefix}/lib/faultd/modules.conf.d/"\" \
+       -DFAULTD_DEFAULT_SERVICES_CONFIG_PATH=\""${prefix}/lib/faultd/service.conf.d/"\" \
        -DFAULTD_MODULES_PATH=\""${sysconfdir}/faultd/enabled-modules"\" \
        -I${top_srcdir}/src \
        -I${top_srcdir}/src/core \
@@ -68,8 +72,9 @@ SED_PROCESS = \
        < $< > $@ || rm $@
 
 unitdir = $(prefix)/lib/systemd/system
-configdir = $(sysconfdir)/faultd
-serviceconfigdir = $(configdir)/conf.d
+configdir = $(prefix)/lib/faultd
+serviceconfigdir = $(configdir)/service.conf.d
+modulesconfigdir = $(configdir)/modules.conf.d
 dbdir = $(localstatedir)/db/faultd
 
 nodist_unit_DATA = \
@@ -78,10 +83,6 @@ nodist_unit_DATA = \
 config_DATA = \
     faultd.conf
 
-serviceconfig_DATA = \
-    conf.d/dbus.service.conf \
-    conf.d/display-manager.service.conf
-
 sbin_PROGRAMS = faultd
 
 faultdlib_SOURCES = \
@@ -157,12 +158,22 @@ ejdb_dbadapter_la_LIBADD = $(LIBEJDB_LIBS)
 vip_fault_eh_la_SOURCES = src/decision_makers/vip_fault_dm.c
 resource_violation_eh_la_SOURCES = src/decision_makers/rv_dm.c
 standard_fault_eh_la_SOURCES = src/decision_makers/standard_fault_dm.c
+standard_fault_eh_config_DATA = modules.conf.d/standard_fault_eh.conf.d/50-default.conf
+standard_fault_eh_configdir = $(modulesconfigdir)/standard_fault_eh.conf.d
 standard_reboot_eh_la_SOURCES = src/decision_makers/standard_reboot_dm.c
+standard_reboot_eh_config_DATA = modules.conf.d/standard_reboot_eh.conf.d/50-default.conf
+standard_reboot_eh_configdir = $(modulesconfigdir)/standard_reboot_eh.conf.d
 service_restart_action_la_SOURCES = src/action/service_restart.c
 system_reboot_action_la_SOURCES = src/action/system_reboot.c
 system_reboot_to_recovery_action_la_SOURCES = src/action/system_reboot_to_recovery.c
 service_recover_action_la_SOURCES = src/action/service_recover.c
 
+dbus_service_configdir = $(serviceconfigdir)/dbus.service.conf.d
+dbus_service_config_DATA = service.conf.d/dbus.service.conf.d/50-default.conf
+
+display_manager_service_configdir = $(serviceconfigdir)/display-manager.service.conf.d
+display_manager_service_config_DATA = service.conf.d/display-manager.service.conf.d/50-default.conf
+
 faultd_LDADD = $(LIBSYSTEMD_LIBS) $(GLIB_LIBS) $(JSON_C_LIBS)
 
 %.pc: %.pc.in Makefile
@@ -189,9 +200,11 @@ testbin_DATA = \
     tests/fail-only-once.sh \
     tests/fail-always.sh
 
-serviceconfig_DATA += \
-    tests/services/faultd-failed-service-with-recovery.service.conf \
-    tests/services/faultd-failed-vip-service.service.conf
+srv_with_recovery_configdir = $(serviceconfigdir)/faultd-failed-service-with-recovery.service.conf.d
+srv_with_recovery_config_DATA = tests/services/faultd-failed-service-with-recovery.service.conf.d/50-default.conf
+
+vip_srv_configdir = $(serviceconfigdir)/faultd-failed-vip-service.service.conf.d
+vip_srv_config_DATA = tests/services/faultd-failed-vip-service.service.conf.d/50-default.conf
 
 TEST_UNIT_FILES = \
     tests/services/faultd-failed-service-always-failed.service \
@@ -247,7 +260,6 @@ generators_PROGRAMS = vip-generator
 vip_generator_SOURCES = \
     generators/vip-generator.c \
     src/util/common.c \
-    src/util/systemd_dbus.c \
     src/util/json-config.c \
     src/util/log.c
 
diff --git a/conf.d/dbus.service.conf b/conf.d/dbus.service.conf
deleted file mode 100644 (file)
index c5407cf..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-       "ServiceType":"org.tizen.faultd.service.VIP"
-}
diff --git a/conf.d/display-manager.service.conf b/conf.d/display-manager.service.conf
deleted file mode 100644 (file)
index c5407cf..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-       "ServiceType":"org.tizen.faultd.service.VIP"
-}
index b3384080a060e1b234e4a97b9ed27ec2fe3b95b4..2c63c0851048d8f7bff41ecf0f8cee05f52fd120 100644 (file)
@@ -1,8 +1,2 @@
 {
-       "standard_reboot_eh" : {
-               "n_reboots_before_recovery" : 3
-       },
-       "standard_fault_eh" : {
-               "n_fails_before_reboot" : 3
-       }
 }
index 2e6cb7d146a5a6f2379b8390c93f4b80edcf5e11..981d3572e4491cad1ab3d5fd014805e10270bd30 100644 (file)
@@ -20,7 +20,6 @@
 #include <string.h>
 #include <errno.h>
 #include <sys/stat.h>
-#include <dirent.h>
 
 #include "service.h"
 #include "json-config.h"
 
 static const char *dest_path = "/tmp/";
 
-static char *get_next_file(DIR *conf_dir)
-{
-       struct dirent *d;
-
-       while (1) {
-               d = readdir(conf_dir);
-               if (d == NULL)
-                       return NULL;
-
-               if (strcmp(d->d_name, ".") == 0 ||
-                       strcmp(d->d_name, "..") == 0) {
-
-                       continue;
-               }
-
-               return d->d_name;
-       }
-}
-
 static int create_dir(const char *path)
 {
        if (mkdir(path, 0755) != 0) {
@@ -59,20 +39,6 @@ static int create_dir(const char *path)
        return 0;
 }
 
-static char *get_service_name(char *conf_name)
-{
-       const char *suffix = ".conf";
-       int suffixlen = strlen(suffix);
-
-       int len = strlen(conf_name);
-       int prefixlen = len - suffixlen;
-
-       if (len > suffixlen && strcmp(&conf_name[prefixlen], suffix) == 0)
-               return strndup(conf_name, prefixlen);
-       else
-               return NULL;
-}
-
 static int create_vip_service_override(const char *service_name)
 {
        FILE *fp;
@@ -80,7 +46,7 @@ static int create_vip_service_override(const char *service_name)
        char *service_override;
        int ret;
 
-       ret = asprintf(&location, "%s/%s.d", dest_path, service_name);
+       ret = asprintf(&location, "%s/%s.conf.d", dest_path, service_name);
        if (ret == -1)
                return -errno;
 
@@ -107,30 +73,10 @@ static int create_vip_service_override(const char *service_name)
        return 0;
 }
 
-static void init_systemd_service(struct systemd_service *s)
-{
-       assert(s);
-
-       s->dbus_path = NULL;
-       s->service_type = NULL;
-       s->recovery_unit = NULL;
-}
-
-static void free_systemd_service(struct systemd_service *s)
-{
-       if (s) {
-               free(s->dbus_path);
-               free(s->service_type);
-               free(s->recovery_unit);
-       }
-}
-
 int main(int argc, char *argv[])
 {
-       DIR *config_dir;
        struct systemd_service s;
-       char *service_name;
-       char *service_config;
+       struct json_object *services_config = json_object_new_object();
        int ret;
 
        /*
@@ -146,48 +92,47 @@ int main(int argc, char *argv[])
        if (argc > 1)
                dest_path = argv[2];
 
-       config_dir = opendir(FAULTD_SERVICES_CONFIG_PATH);
-       if (!config_dir) {
-               log_error("Unable to open directory %s: %m", FAULTD_SERVICES_CONFIG_PATH);
-               return EXIT_FAILURE;
+       /* load services configuration into json object */
+       ret = load_config(FAULTD_DEFAULT_SERVICES_CONFIG_PATH, CONFIG_TYPE_DIR, 0,
+                                         services_config);
+       if (ret) {
+               log_error("Unable to load services config %s", FAULTD_DEFAULT_SERVICES_CONFIG_PATH);
+               json_object_put(services_config);
+               return ret;
        }
 
-       while ((service_config = get_next_file(config_dir)) != NULL) {
-               init_systemd_service(&s);
+       ret = load_config(FAULTD_CUSTOM_SERVICES_CONFIG_PATH, CONFIG_TYPE_DIR, 0,
+                                         services_config);
+       if (ret) {
+               log_error("Unable to load services config %s", FAULTD_CUSTOM_SERVICES_CONFIG_PATH);
+               json_object_put(services_config);
+               return ret;
+       }
 
-               service_name = get_service_name(service_config);
-               if (service_name == NULL) {
-                       log_error("Unable to get service name from config: %s", service_config);
-                       goto cleanup;
-               }
+       /*
+        * iterate through every config node and create override unit file if
+        * the service is of VIP type
+        */
 
-               ret = asprintf(&service_config, FAULTD_SERVICES_CONFIG_PATH "%s", service_config);
-               if (ret == -1) {
-                       log_error("Unable to allocate string: %s", strerror(errno));
-                       goto cleanup;
-               }
+       json_object_object_foreach(services_config, service_name, node) {
+               s.service_type = NULL;
 
-               ret = parse_service_config(service_config, &s);
-               if (ret != 0) {
-                       log_error("Unable to parse config file: %s", service_config);
-                       free(service_config);
-                       goto cleanup;
+               ret = get_config_field(node, "ServiceType", &s.service_type, json_type_string);
+               if (ret) {
+                       log_error("Unable to get service type for %s", service_name);
+                       continue;
                }
 
-               free(service_config);
-
                if (systemd_service_is_of_type(&s, FAULTD_SERVICE_TYPE_VIP)) {
                        ret = create_vip_service_override(service_name);
-                       if (ret != 0)
+                       if (ret)
                                log_error("Unable to create override for VIP service %s: %s\n",
                                                  service_name, strerror(ret));
                }
 
-       cleanup:
-               free(service_name);
-               free_systemd_service(&s);
+               free(s.service_type);
        }
 
-       closedir(config_dir);
+       json_object_put(services_config);
        return 0;
 }
diff --git a/modules.conf.d/standard_fault_eh.conf.d/50-default.conf b/modules.conf.d/standard_fault_eh.conf.d/50-default.conf
new file mode 100644 (file)
index 0000000..6c8f689
--- /dev/null
@@ -0,0 +1,3 @@
+{
+       "n_fails_before_reboot" : 3
+}
diff --git a/modules.conf.d/standard_reboot_eh.conf.d/50-default.conf b/modules.conf.d/standard_reboot_eh.conf.d/50-default.conf
new file mode 100644 (file)
index 0000000..2d665fd
--- /dev/null
@@ -0,0 +1,3 @@
+{
+       "n_reboots_before_recovery" : 3
+}
index 355ffdfa744b3afcc8b4609ff491e20822e7d717..28f0991bfed92ba198dd73a8f7cac61f67a301a9 100644 (file)
@@ -97,14 +97,16 @@ done
 %manifest %{name}.manifest
 %{_localstatedir}/db/faultd
 %{_sbindir}/faultd
-%{_sysconfdir}/faultd/faultd.conf
-%{_sysconfdir}/faultd/conf.d/dbus.service.conf
-%{_sysconfdir}/faultd/conf.d/display-manager.service.conf
+%{_prefix}/lib/faultd/faultd.conf
+%{_prefix}/lib/faultd/service.conf.d/dbus.service.conf.d/50-default.conf
+%{_prefix}/lib/faultd/service.conf.d/display-manager.service.conf.d/50-default.conf
 %{_unitdir}/faultd.service
 %{_unitdir}/basic.target.wants/faultd.service
 %{_prefix}/lib/systemd/system-generators/vip-generator
 
 %files extra -f faultd-extra-files
+%{_prefix}/lib/faultd/modules.conf.d/standard_fault_eh.conf.d/50-default.conf
+%{_prefix}/lib/faultd/modules.conf.d/standard_reboot_eh.conf.d/50-default.conf
 
 %files test-services
 %{_bindir}/faultd/leaker
@@ -116,7 +118,7 @@ done
 %{_unitdir}/faultd-failed-vip-service.service
 %{_unitdir}/faultd-recovery-service.service
 %{_unitdir}/faultd-res-leak.service
-%{_sysconfdir}/faultd/conf.d/faultd-failed-service-with-recovery.service.conf
-%{_sysconfdir}/faultd/conf.d/faultd-failed-vip-service.service.conf
+%{_prefix}/lib/faultd/service.conf.d/faultd-failed-service-with-recovery.service.conf.d/50-default.conf
+%{_prefix}/lib/faultd/service.conf.d/faultd-failed-vip-service.service.conf.d/50-default.conf
 
 %docs_package
diff --git a/service.conf.d/dbus.service.conf.d/50-default.conf b/service.conf.d/dbus.service.conf.d/50-default.conf
new file mode 100644 (file)
index 0000000..c5407cf
--- /dev/null
@@ -0,0 +1,3 @@
+{
+       "ServiceType":"org.tizen.faultd.service.VIP"
+}
diff --git a/service.conf.d/display-manager.service.conf.d/50-default.conf b/service.conf.d/display-manager.service.conf.d/50-default.conf
new file mode 100644 (file)
index 0000000..c5407cf
--- /dev/null
@@ -0,0 +1,3 @@
+{
+       "ServiceType":"org.tizen.faultd.service.VIP"
+}
index a7819268fa93fedf1e5e222de7b6df27aec74047..8a61ab5aa630d09d0d00e8463a537849095b8b44 100644 (file)
 
 #include "common.h"
 #include "faultd-config.h"
+#include "json-config.h"
 #include "log.h"
 #include "module.h"
 
-static int parse_disable_modules(struct json_object *root)
+static struct faultd_config modules_config;
+static struct faultd_config services_config;
+
+void faultd_get_modules_config(struct faultd_config **c)
 {
-       struct json_object *node;
+       *c = &modules_config;
+}
+
+void faultd_get_services_config(struct faultd_config **c)
+{
+       *c = &services_config;
+}
+
+static int parse_disable_modules(void)
+{
+       struct json_object *root, *node;
        const char *name;
        struct faultd_module *module;
-       int i;
-       int len;
+       int len, ret;
+
+       ret = json_object_object_get_ex(modules_config.root, "disable_modules", &root);
+       if (ret == 0)
+               return 0;
 
        len = json_object_array_length(root);
 
-       for (i = 0; i < len; ++i) {
+       for (int i = 0; i < len; ++i) {
                node = json_object_array_get_idx(root, i);
                if (!node)
                        return -EINVAL;
@@ -57,33 +74,84 @@ static int parse_disable_modules(struct json_object *root)
        return 0;
 }
 
-int faultd_parse_config(const char *config_path, struct faultd_config *c)
+static int init_config(void)
 {
-       struct json_object *root;
-       struct json_object *node;
-       int ret = 0;
-       json_bool json_ret;
+       modules_config.root = json_object_new_object();
+       if (!modules_config.root) {
+                       log_error("Unable to create new json object");
+                       return -ENOMEM;
+       }
 
-       root = json_object_from_file(config_path);
-       if (is_error(root)) {
-               log_error("Could not create object from json file %s", config_path);
-               return -EINVAL;
+       services_config.root = json_object_new_object();
+       if (!services_config.root) {
+                       log_error("Unable to create new json object");
+                       json_object_put(modules_config.root);
+                       return -ENOMEM;
        }
 
-       json_ret = json_object_object_get_ex(root, "disable_modules", &node);
-       if (json_ret) {
-               ret = parse_disable_modules(node);
-               if (ret < 0) {
-                       log_error("Could not parse configuration file %s", config_path);
-                       goto out;
-               }
+       return 0;
+}
+
+static void log_config(void)
+{
+       log_debug("Modules config root:\n\n%s\n",
+                         json_object_to_json_string_ext(modules_config.root, JSON_C_TO_STRING_PRETTY));
+
+       log_debug("Services config root:\n\n%s\n",
+                         json_object_to_json_string_ext(services_config.root, JSON_C_TO_STRING_PRETTY));
+}
+
+int faultd_load_config(const char *custom_config_file)
+{
+       int ret;
+
+       ret = init_config();
+       if (ret)
+               return ret;
+
+       ret = load_config(FAULTD_DEFAULT_CONFIG_FILE, CONFIG_TYPE_FILE, 1,
+                                         modules_config.root);
+       if (ret)
+               goto cleanup;
+
+       ret = load_config(FAULTD_DEFAULT_MODULES_CONFIG_PATH, CONFIG_TYPE_DIR, 0,
+                                         modules_config.root);
+       if (ret)
+               goto cleanup;
+
+       ret = load_config(FAULTD_DEFAULT_SERVICES_CONFIG_PATH, CONFIG_TYPE_DIR, 0,
+                                         services_config.root);
+       if (ret)
+               goto cleanup;
+
+       ret = load_config(custom_config_file, CONFIG_TYPE_FILE, 1,
+                                         modules_config.root);
+       if (ret)
+               goto cleanup;
+
+       ret = load_config(FAULTD_CUSTOM_MODULES_CONFIG_PATH, CONFIG_TYPE_DIR, 1,
+                                         modules_config.root);
+       if (ret)
+               goto cleanup;
+
+       ret = load_config(FAULTD_CUSTOM_SERVICES_CONFIG_PATH, CONFIG_TYPE_DIR, 1,
+                                         services_config.root);
+       if (ret)
+               goto cleanup;
+
+       log_config();
+
+       ret = parse_disable_modules();
+       if (ret) {
+               log_error("Could not parse disable_modules section");
+               goto cleanup;
        }
 
-       c->root = root;
        return 0;
 
-out:
-       json_object_put(root);
+cleanup:
+       json_object_put(modules_config.root);
+       json_object_put(services_config.root);
        return ret;
 }
 
@@ -93,6 +161,12 @@ void faultd_config_cleanup(struct faultd_config *c)
                json_object_put(c->root);
 }
 
+void faultd_config_cleanup_global(void)
+{
+       faultd_config_cleanup(&modules_config);
+       faultd_config_cleanup(&services_config);
+}
+
 int faultd_config_get_subconf(struct faultd_config *conf, const char *key, struct faultd_config **sub)
 {
        struct faultd_config *config_node;
index 6a86315fed4488ff7cda6e50fc8c031c2d4fc72c..4b33bdd2d2c575cb468ba653d87572366cff8d3c 100644 (file)
@@ -25,8 +25,11 @@ struct faultd_config {
        struct json_object *root;
 };
 
-int faultd_parse_config(const char *config_path, struct faultd_config *c);
+void faultd_get_modules_config(struct faultd_config **c);
+void faultd_get_services_config(struct faultd_config **c);
+int faultd_load_config(const char *custom_config_file);
 void faultd_config_cleanup(struct faultd_config *c);
+void faultd_config_cleanup_global(void);
 int faultd_config_get_subconf(struct faultd_config *conf, const char *key, struct faultd_config **sub);
 
 #endif /* FAULTD_CONFIG_H */
index 77ad814f67076f21dc46399a519a7573e4bdf6bd..2515d964a876195cf54485eb47abf155d670c725 100644 (file)
@@ -32,6 +32,7 @@
 #include "common.h"
 #include "systemd_dbus.h"
 #include "json-config.h"
+#include "faultd-config.h"
 
 int systemd_service_init_by_pid(pid_t pid, struct systemd_service *s)
 {
@@ -47,7 +48,7 @@ int systemd_service_init_by_pid(pid_t pid, struct systemd_service *s)
        return systemd_service_init(n, s);
 }
 
-static int get_service_config_path(const char *dbus_path, char **config_path)
+static int get_service_name(const char *dbus_path, char **service_name)
 {
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
        _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
@@ -75,9 +76,9 @@ static int get_service_config_path(const char *dbus_path, char **config_path)
 
        log_debug("Service Id: %s", name);
 
-       ret = asprintf(config_path, FAULTD_SERVICES_CONFIG_PATH "/%s.conf", name);
-       if (ret < 0) {
-               log_error("Could not allocate service configuration path: %m");
+       *service_name = strdup(name);
+       if (*service_name == NULL) {
+               log_error("Unable to allocate string");
                return -ENOMEM;
        }
 
@@ -86,7 +87,9 @@ static int get_service_config_path(const char *dbus_path, char **config_path)
 
 int systemd_service_init(const char *dbus_path, struct systemd_service *s)
 {
-       _cleanup_free_ char *config_path = NULL;
+       _cleanup_free_ char *service_name = NULL;
+       struct faultd_config *services_config = NULL;
+       struct json_object *node;
        int ret;
 
        assert(s);
@@ -98,27 +101,26 @@ int systemd_service_init(const char *dbus_path, struct systemd_service *s)
        if (!s->dbus_path)
                return -ENOMEM;
 
-       ret = get_service_config_path(dbus_path, &config_path);
-       if (ret)
+       ret = get_service_name(dbus_path, &service_name);
+       if (ret < 0) {
+               log_error("Unable to get service name: %s", dbus_path);
                goto cleanup_service;
+       }
 
-       ret = access(config_path, F_OK | R_OK);
-       /* if there is no config file then we are done */
-       if (ret && errno == ENOENT)
+       faultd_get_services_config(&services_config);
+
+       if (!json_object_object_get_ex(services_config->root, service_name, &node)) {
+               log_debug("Service config does not contain %s node.", service_name);
                return 0;
+       }
 
-       if (ret) {
-               log_error("Failed to access service configuration path: %m");
-               ret = -errno;
+       ret = get_config_field(node, "RecoveryUnit", &s->recovery_unit, json_type_string);
+       if (ret < 0)
                goto cleanup_service;
-       }
 
-       /* file exist so we need to parse it */
-       ret = parse_service_config(config_path, s);
-       if (ret) {
-               log_error_errno(ret, "Failed to parse service configuration");
+       ret = get_config_field(node, "ServiceType", &s->service_type, json_type_string);
+       if (ret < 0)
                goto cleanup_service;
-       }
 
        return 0;
 
index bfdfea93e0d181aae684afdc15af980b86563c32..9d2c606f31515c838358891e26083cb766de2da9 100644 (file)
@@ -33,9 +33,9 @@
 #include "faultd-config.h"
 #include "common.h"
 #include "faultd-glib.h"
+#include "json-config.h"
 
 static int terminate = 0;
-static struct faultd_config config;
 
 static int signal_handler(sd_event_source *s,
                                   const struct signalfd_siginfo *si,
@@ -77,8 +77,7 @@ static int parse_argv(int ac, char *av[])
        };
 
        struct faultd_module *module;
-       const char *config_file = FAULTD_DEFAULT_CONFIG_FILE;
-       bool using_default_config = 1;
+       const char *custom_config_file = FAULTD_CUSTOM_CONFIG_FILE;
 
        while ((c = getopt_long(ac, av, "Dc:d:h", options, NULL)) >= 0) {
                switch (c) {
@@ -97,9 +96,7 @@ static int parse_argv(int ac, char *av[])
                        break;
 
                case 'c':
-                       config_file = optarg;
-                       using_default_config = 0;
-
+                       custom_config_file = optarg;
                        break;
 
                case 'h':
@@ -126,25 +123,7 @@ static int parse_argv(int ac, char *av[])
                }
        }
 
-       r = access(config_file, F_OK | R_OK);
-       if (r) {
-               if (!using_default_config || errno != ENOENT) {
-                       log_error("Could not access config file %s", config_file);
-                       return -errno;
-               }
-
-               log_debug("No config file");
-               config.root = json_object_new_object();
-               if (!config.root)
-                       return -ENOMEM;
-       } else {
-               log_debug("Using config file: %s", config_file);
-               r = faultd_parse_config(config_file, &config);
-               if (r < 0)
-                       return r;
-       }
-
-       return 0;
+       return faultd_load_config(custom_config_file);
 }
 
 int main(int ac, char *av[])
@@ -153,6 +132,7 @@ int main(int ac, char *av[])
        sd_bus *bus = NULL;
        sd_event *loop;
        sigset_t ss;
+       struct faultd_config *modules_config = NULL;
 
        rc = faultd_modules_load();
        if (rc < 0) {
@@ -201,7 +181,9 @@ int main(int ac, char *av[])
                return -1;
        }
 
-       rc = faultd_modules_init(loop, &config);
+       faultd_get_modules_config(&modules_config);
+
+       rc = faultd_modules_init(loop, modules_config);
        if (rc < 0) {
                log_error("Failed to initialize modules %d.", rc);
                return -1;
@@ -219,7 +201,7 @@ int main(int ac, char *av[])
                log_error_errno(rc, "Failed to run main loop: %m");
 
        faultd_modules_cleanup();
-       faultd_config_cleanup(&config);
+       faultd_config_cleanup_global();
 
        sd_bus_close(bus);
 
index 980eecca8320e34da700864e16e779f381a9f3e8..e7a6c9f22de89a887bd4779ee6db18e6c5c71c6f 100644 (file)
 #include <string.h>
 #include <stdio.h>
 #include <json-c/json.h>
+#include <dirent.h>
+#include <sys/stat.h>
 
 #include "json-config.h"
 #include "log.h"
 
+static int is_directory(const char *path)
+{
+       struct stat st;
+       int ret;
+
+       ret = stat(path, &st);
+       if (ret == -1) {
+               log_error("Unable to stat %s: %m", path);
+               return 0;
+       }
+       return st.st_mode & S_IFDIR;
+}
+
+static int check_suffix(const char *path, const char *suffix)
+{
+       int suffix_len = strlen(suffix);
+       int total_len = strlen(path);
+
+       if (total_len <= suffix_len)
+               return 0;
+
+       return strcmp(&path[total_len - suffix_len], suffix) == 0;
+}
+
+static int remove_last_n_chars(const char *src, size_t n,
+                                                          char *dest, size_t dest_size)
+{
+       size_t len = strlen(src) - n;
+
+       if (len + 1 > dest_size) {
+               log_error("Destination buffer is too small");
+               return -EOVERFLOW;
+       }
+
+       strncpy(dest, src, len);
+       dest[len] = 0;
+
+       return 0;
+}
+
+static int config_filter(const struct dirent *d, const char *suffix)
+{
+       if (d->d_name[0] == '.')
+               return 0;
+
+       if (check_suffix(d->d_name, suffix))
+               return 1;
+
+       return 0;
+}
+
+static int config_dir_filter(const struct dirent *d)
+{
+       return config_filter(d, ".conf.d");
+}
+
+static int config_file_filter(const struct dirent *d)
+{
+       return config_filter(d, ".conf");
+}
+
+static int json_object_object_merge(json_object *obj1, char *key, json_object *obj2_node)
+{
+       json_object *obj1_node = NULL;
+       int ret;
+
+       ret = json_object_object_get_ex(obj1, key, &obj1_node);
+
+       /* if there is no such object in obj1 - add it */
+       if (ret == 0)
+               goto json_add;
+
+       if (obj1_node == NULL) {
+               log_error("Unable to get json object");
+               return -ENOMEM;
+       }
+
+       /* if there is, but obj1 or obj2 is not json_type_object - replace it */
+       if (!json_object_is_type(obj1_node, json_type_object) ||
+               !json_object_is_type(obj2_node, json_type_object)) {
+
+               json_object_object_del(obj1, key);
+               goto json_add;
+       }
+
+       /* if obj1 and obj2 is json_object_type */
+       json_object_object_foreach(obj2_node, k, v) {
+               ret = json_object_object_merge(obj1_node, k, v);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+
+json_add:
+       json_object_get(obj2_node);
+       json_object_object_add(obj1, key, obj2_node);
+       return 0;
+}
+
+static int json_object_merge_with_file(json_object *existing_node, const char *file)
+{
+       struct json_object *new_node;
+       int ret;
+
+       new_node = json_object_from_file(file);
+       if (new_node == NULL) {
+               log_error("Could not create object from json file %s", file);
+               return -EINVAL;
+       }
+
+       json_object_object_foreach(new_node, key, val) {
+               ret = json_object_object_merge(existing_node, key, val);
+               if (ret)
+                       goto cleanup;
+       }
+
+cleanup:
+       json_object_put(new_node);
+       return ret;
+}
+
+static int assemble_config_from_dir(const char *path, json_object *config)
+{
+       int num;
+       struct dirent **list;
+       char filename[PATH_MAX];
+       int ret;
+
+       /* search for *.conf files for this module */
+       num = scandir(path, &list, config_file_filter, alphasort);
+       if (num < 0)
+               return num;
+
+       while (num--) {
+               /* get full path of the config file */
+               ret = snprintf(filename, sizeof(filename), "%s/%s", path, list[num]->d_name);
+               free(list[num]);
+               if (ret >= (int) sizeof(filename)) {
+                       log_error("Unable to copy string");
+                       goto error;
+               }
+
+               log_debug("Using config file: %s", filename);
+
+               /* merge config into existing json object */
+               ret = json_object_merge_with_file(config, filename);
+               if (ret < 0)
+                       goto error;
+       }
+
+       free(list);
+       return 0;
+
+error:
+       while (num--)
+               free(list[num]);
+       free(list);
+       return ret;
+}
+
+static int load_multidir_config(const char *path, struct json_object *c)
+{
+       int num;
+       struct dirent **list;
+       char dir_path[PATH_MAX], obj_name[PATH_MAX];
+       struct json_object *obj_config = NULL;
+       int ret;
+
+       /* search for *.conf.d directories */
+       num = scandir(path, &list, config_dir_filter, alphasort);
+       if (num < 0) {
+               /* dir doesn't exist */
+               if (errno == ENOENT) {
+                       log_debug("Config dir doesn't exist %s", path);
+                       return -ENOENT;
+               }
+
+               /* dir exists but we are unable to access it*/
+               log_error("Could not access config dir %s", path);
+               return -errno;
+       }
+
+       while (num--) {
+               /* get full path of the config dir */
+               ret = snprintf(dir_path, sizeof(dir_path), "%s/%s", path, list[num]->d_name);
+               free(list[num]);
+               if (ret >= (int) sizeof(dir_path)) {
+                       log_error("Unable to copy string");
+                       goto error;
+               }
+
+               if (!is_directory(dir_path))
+                       continue;
+
+               /* get object name out of directory path */
+               ret = remove_last_n_chars(basename(dir_path), strlen(".conf.d"),
+                                                                 obj_name, sizeof(obj_name));
+               if (ret)
+                       goto error;
+
+               /* get existing config for present object */
+               ret = json_object_object_get_ex(c, obj_name, &obj_config);
+               if (ret == 0) {
+                       obj_config = json_object_new_object();
+                       json_object_object_add(c, obj_name, obj_config);
+               }
+
+               if (obj_config == NULL) {
+                       log_error("Unable to create new json object");
+                       ret = -ENOMEM;
+                       goto error;
+               }
+
+               /* merge another config with existing one */
+               ret = assemble_config_from_dir(dir_path, obj_config);
+               if (ret < 0) {
+                       log_error("Unable to assemble config for object %s", obj_name);
+                       json_object_put(obj_config);
+                       goto error;
+               }
+       }
+
+       free(list);
+       return 0;
+
+error:
+       while (num--)
+               free(list[num]);
+       free(list);
+       return ret;
+}
+
+static int load_single_config(const char *config_path, struct json_object *c)
+{
+       int ret = 0;
+
+       ret = access(config_path, F_OK | R_OK);
+       if (ret) {
+               /* file doesn't exist */
+               if (errno == ENOENT) {
+                       log_debug("Config file doesn't exist %s", config_path);
+                       return -ENOENT;
+               }
+
+               /* file exists but we are unable to access it*/
+               log_error("Could not access config file %s", config_path);
+               return -errno;
+       }
+
+       /* file exists and we are able to access it*/
+       log_debug("Using config file: %s", config_path);
+
+       ret = json_object_merge_with_file(c, config_path);
+       if (ret) {
+               log_error("Unable to load config %s", config_path);
+               return ret;
+       }
+
+       return 0;
+}
+
+int load_config(const char *src, int type, int optional, struct json_object *c)
+{
+       int ret;
+
+       switch (type) {
+       case CONFIG_TYPE_FILE:
+               ret = load_single_config(src, c);
+               break;
+
+       case CONFIG_TYPE_DIR:
+               ret = load_multidir_config(src, c);
+               break;
+
+       default:
+               log_error("Wrong config type");
+               return -EINVAL;
+       }
+
+       if ((ret < 0 && ret != -ENOENT) || (ret == -ENOENT && !optional))
+               return ret;
+
+       return 0;
+}
+
 int get_config_field(struct json_object *root, const char *key, void *value, int type)
 {
        struct json_object *node = NULL;
@@ -74,26 +362,3 @@ int get_config_field(struct json_object *root, const char *key, void *value, int
 
        return 0;
 }
-
-int parse_service_config(const char *config_path,
-                                                struct systemd_service *s)
-{
-       struct json_object *root;
-       int ret;
-
-       root = json_object_from_file(config_path);
-       if (is_error(root)) {
-               log_error("Could not create json object from file %s", config_path);
-               return -1;
-       }
-
-       ret = get_config_field(root, "RecoveryUnit", &s->recovery_unit, json_type_string);
-       if (ret < 0)
-               goto out;
-
-       ret = get_config_field(root, "ServiceType", &s->service_type, json_type_string);
-
-out:
-       json_object_put(root);
-       return ret;
-}
index 55c2049599336a5623d3fe7e90096f72aaac6811..ed691a3bbe1bd8a1f65c6cd92fa62138d39989c4 100644 (file)
 
 #include "service.h"
 
+enum config_type {
+       CONFIG_TYPE_FILE,
+       CONFIG_TYPE_DIR
+};
+
+int load_config(const char *src, int type, int optional, struct json_object *c);
 int get_config_field(struct json_object *root, const char *key, void *value, int type);
-int parse_service_config(const char *config_path, struct systemd_service *s);
 
 #endif /* FAULTD_JSON_CONFIG_H */
diff --git a/tests/services/faultd-failed-service-with-recovery.service.conf b/tests/services/faultd-failed-service-with-recovery.service.conf
deleted file mode 100644 (file)
index a590e34..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-       "RecoveryUnit": "faultd-recovery-service.service"
-}
diff --git a/tests/services/faultd-failed-service-with-recovery.service.conf.d/50-default.conf b/tests/services/faultd-failed-service-with-recovery.service.conf.d/50-default.conf
new file mode 100644 (file)
index 0000000..a590e34
--- /dev/null
@@ -0,0 +1,3 @@
+{
+       "RecoveryUnit": "faultd-recovery-service.service"
+}
diff --git a/tests/services/faultd-failed-vip-service.service.conf b/tests/services/faultd-failed-vip-service.service.conf
deleted file mode 100644 (file)
index b84638e..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-       "ServiceType": "org.tizen.faultd.service.VIP"
-}
diff --git a/tests/services/faultd-failed-vip-service.service.conf.d/50-default.conf b/tests/services/faultd-failed-vip-service.service.conf.d/50-default.conf
new file mode 100644 (file)
index 0000000..b84638e
--- /dev/null
@@ -0,0 +1,3 @@
+{
+       "ServiceType": "org.tizen.faultd.service.VIP"
+}