diff --git a/package/libgluonutil/Makefile b/package/libgluonutil/Makefile index 489a9ba9..2536727c 100644 --- a/package/libgluonutil/Makefile +++ b/package/libgluonutil/Makefile @@ -16,7 +16,7 @@ define Package/libgluonutil SECTION:=libs CATEGORY:=Libraries TITLE:=Gluon utility library - DEPENDS:=+libjson-c + DEPENDS:=+libjson-c +libuci endef CMAKE_OPTIONS += \ diff --git a/package/libgluonutil/src/CMakeLists.txt b/package/libgluonutil/src/CMakeLists.txt index ba92a3e1..ac8c0c49 100644 --- a/package/libgluonutil/src/CMakeLists.txt +++ b/package/libgluonutil/src/CMakeLists.txt @@ -7,14 +7,15 @@ project(libgluonutil C) set(LIBDIR "lib${LIB_SUFFIX}") find_package(JSON_C REQUIRED) +find_package(UCI REQUIRED) set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS _GNU_SOURCE) add_library(gluonutil SHARED libgluonutil.c) set_property(TARGET gluonutil PROPERTY COMPILE_FLAGS "-Wall -std=c99 ${JSON_C_CFLAGS_OTHER}") set_property(TARGET gluonutil PROPERTY LINK_FLAGS "${JSON_C_LDFLAGS_OTHER}") -set_property(TARGET gluonutil APPEND PROPERTY INCLUDE_DIRECTORIES ${JSON_C_INCLUDE_DIR}) -target_link_libraries(gluonutil ${JSON_C_LIBRARIES}) +set_property(TARGET gluonutil APPEND PROPERTY INCLUDE_DIRECTORIES ${JSON_C_INCLUDE_DIR} ${UCI_INCLUDE_DIR}) +target_link_libraries(gluonutil ${JSON_C_LIBRARIES} ${UCI_LIBRARIES}) install(TARGETS gluonutil ARCHIVE DESTINATION ${LIBDIR} LIBRARY DESTINATION ${LIBDIR} diff --git a/package/libgluonutil/src/FindUCI.cmake b/package/libgluonutil/src/FindUCI.cmake new file mode 100644 index 00000000..820bc09b --- /dev/null +++ b/package/libgluonutil/src/FindUCI.cmake @@ -0,0 +1,21 @@ +# UCI_FOUND - true if library and headers were found +# UCI_INCLUDE_DIRS - include directories +# UCI_LIBRARIES - library directories + +find_package(PkgConfig) +pkg_check_modules(PC_UCI QUIET uci) + +find_path(UCI_INCLUDE_DIR uci.h + HINTS ${PC_UCI_INCLUDEDIR} ${PC_UCI_INCLUDE_DIRS}) + +find_library(UCI_LIBRARY NAMES uci libuci + HINTS ${PC_UCI_LIBDIR} ${PC_UCI_LIBRARY_DIRS}) + +set(UCI_LIBRARIES ${UCI_LIBRARY}) +set(UCI_INCLUDE_DIRS ${UCI_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(UCI DEFAULT_MSG UCI_LIBRARY UCI_INCLUDE_DIR) + +mark_as_advanced(UCI_INCLUDE_DIR UCI_LIBRARY) diff --git a/package/libgluonutil/src/libgluonutil.c b/package/libgluonutil/src/libgluonutil.c index db46b9c9..36eecbc2 100644 --- a/package/libgluonutil/src/libgluonutil.c +++ b/package/libgluonutil/src/libgluonutil.c @@ -31,7 +31,41 @@ #include #include #include +#include +#include +/** + * Merges two JSON objects + * + * On conflicts, object a will be preferred. + * + * Internally, this functions merges all entries from object a into object b, + * so merging a small object a with a big object b is faster than vice-versa. + */ +static struct json_object * merge_json(struct json_object *a, struct json_object *b) { + if (!json_object_is_type(a, json_type_object) || !json_object_is_type(b, json_type_object)) { + json_object_put(b); + return a; + } + + json_object_object_foreach(a, key, val_a) { + struct json_object *val_b; + + json_object_get(val_a); + + if (!json_object_object_get_ex(b, key, &val_b)) { + json_object_object_add(b, key, val_a); + continue; + } + + json_object_get(val_b); + + json_object_object_add(b, key, merge_json(val_a, val_b)); + } + + json_object_put(a); + return b; +} char * gluonutil_read_line(const char *filename) { FILE *f = fopen(filename, "r"); @@ -140,7 +174,122 @@ bool gluonutil_get_node_prefix6(struct in6_addr *prefix) { return true; } +char * get_selected_domain_code(struct json_object * base) { + char * domain_path_fmt = "/lib/gluon/domains/%s.json"; + char domain_path[strlen(domain_path_fmt) + 256]; + const char * domain_code; + + struct uci_context *ctx = uci_alloc_context(); + if (!ctx) + goto uci_fail; + + ctx->flags &= ~UCI_FLAG_STRICT; + + struct uci_package *p; + if (uci_load(ctx, "gluon", &p)) + goto uci_fail; + + struct uci_section *s = uci_lookup_section(ctx, p, "system"); + if (!s) + goto uci_fail; + + domain_code = uci_lookup_option_string(ctx, s, "domain_code"); + + if (!domain_code) + goto uci_fail; + + snprintf(domain_path, sizeof domain_path, domain_path_fmt, domain_code); + + if (access(domain_path, R_OK) != -1) { + // ${domain_code}.conf exists and is accessible + char * domain_code_cpy = strndup(domain_code, 256); // copy before free + uci_free_context(ctx); + return domain_code_cpy; + } + +uci_fail: + if (ctx) + uci_free_context(ctx); + + json_object * default_domain_code; + + // it's okay to pass base == NULL to json_object_object_get_ex() + if (!json_object_object_get_ex(base, "default_domain_code", &default_domain_code)) + return NULL; + + domain_code = json_object_get_string(default_domain_code); + + if (!domain_code) + return NULL; + + // the gluon build environment should ensure, that this filename exists, + // but to be sure, we check here again. + snprintf(domain_path, sizeof domain_path, domain_path_fmt, domain_code); + + if (access(domain_path, R_OK) == -1) + return NULL; + + // create a copy so site could be freed before domain_code + return strndup(domain_code, 256); +} + +/** + * Get selected domain code + * + * - If NULL is passed to the site parameter, internally only the base part + * (without domain config) is loaded, which is more efficient than calling + * gluonutil_load_site_config() for this job only. Nevertheless if you already + * have an instance of a site object then you should pass it here. + * - Returned domain code string has to be freed after use + * - Returns NULL in case of error + * - If a domain code is returned, it's ensured that the corresponding config + * in /lib/gluon/domains/ exists. + */ +char * gluonutil_get_selected_domain_code(struct json_object * site) { + if (site) + // If the user already has allocated a whole site object, it makes no sense + // to load the base object. Taking the site object (merged from domain and + // base) should be fine here. + return get_selected_domain_code(site); + + // load base + struct json_object * base = json_object_from_file("/lib/gluon/site.json"); + + if (!base) + return NULL; + + return get_selected_domain_code(base); +} struct json_object * gluonutil_load_site_config(void) { - return json_object_from_file("/lib/gluon/site.json"); + // load base + struct json_object * base = json_object_from_file("/lib/gluon/site.json"); + + if (!base) + return NULL; + + // load domain + char * domain_path_fmt = "/lib/gluon/domains/%s.json"; + char domain_path[strlen(domain_path_fmt) + 256]; + char * domain_code = get_selected_domain_code(base); + + if (!domain_code) { + // something went horribly wrong here + json_object_put(base); // free base + return NULL; + } + + snprintf(domain_path, sizeof domain_path, domain_path_fmt, domain_code); + + free(domain_code); + + struct json_object * domain = json_object_from_file(domain_path); + + if (!domain) { + json_object_put(base); + return NULL; + } + + // finally merge them + return merge_json(domain, base); } diff --git a/package/libgluonutil/src/libgluonutil.h b/package/libgluonutil/src/libgluonutil.h index b2f90d69..d464005c 100644 --- a/package/libgluonutil/src/libgluonutil.h +++ b/package/libgluonutil/src/libgluonutil.h @@ -42,4 +42,18 @@ struct json_object * gluonutil_wrap_and_free_string(char *str); struct json_object * gluonutil_load_site_config(void); +/** + * Get selected domain code + * + * - If NULL is passed to the site parameter, internally only the base part + * (without domain config) is loaded, which is more efficient than calling + * gluonutil_load_site_config() for this job only. Nevertheless if you already + * have an instance of a site object then you should pass it here. + * - Returned domain code string has to be freed after use + * - Returns NULL in case of error + * - If a domain code is returned, it's ensured that the corresponding config + * in /lib/gluon/domains/ exists. + */ +char * gluonutil_get_selected_domain_code(struct json_object * site); + #endif /* _LIBGLUON_LIBGLUON_H_ */