libgluonutil: merge domain and site configs

This commit is contained in:
lemoer 2017-10-28 17:05:53 +02:00
parent a73ae6c16d
commit 0189f350f9
5 changed files with 189 additions and 4 deletions

View File

@ -16,7 +16,7 @@ define Package/libgluonutil
SECTION:=libs SECTION:=libs
CATEGORY:=Libraries CATEGORY:=Libraries
TITLE:=Gluon utility library TITLE:=Gluon utility library
DEPENDS:=+libjson-c DEPENDS:=+libjson-c +libuci
endef endef
CMAKE_OPTIONS += \ CMAKE_OPTIONS += \

View File

@ -7,14 +7,15 @@ project(libgluonutil C)
set(LIBDIR "lib${LIB_SUFFIX}") set(LIBDIR "lib${LIB_SUFFIX}")
find_package(JSON_C REQUIRED) find_package(JSON_C REQUIRED)
find_package(UCI REQUIRED)
set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS _GNU_SOURCE) set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS _GNU_SOURCE)
add_library(gluonutil SHARED libgluonutil.c) 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 COMPILE_FLAGS "-Wall -std=c99 ${JSON_C_CFLAGS_OTHER}")
set_property(TARGET gluonutil PROPERTY LINK_FLAGS "${JSON_C_LDFLAGS_OTHER}") set_property(TARGET gluonutil PROPERTY LINK_FLAGS "${JSON_C_LDFLAGS_OTHER}")
set_property(TARGET gluonutil APPEND PROPERTY INCLUDE_DIRECTORIES ${JSON_C_INCLUDE_DIR}) set_property(TARGET gluonutil APPEND PROPERTY INCLUDE_DIRECTORIES ${JSON_C_INCLUDE_DIR} ${UCI_INCLUDE_DIR})
target_link_libraries(gluonutil ${JSON_C_LIBRARIES}) target_link_libraries(gluonutil ${JSON_C_LIBRARIES} ${UCI_LIBRARIES})
install(TARGETS gluonutil install(TARGETS gluonutil
ARCHIVE DESTINATION ${LIBDIR} ARCHIVE DESTINATION ${LIBDIR}
LIBRARY DESTINATION ${LIBDIR} LIBRARY DESTINATION ${LIBDIR}

View File

@ -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)

View File

@ -31,7 +31,41 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <uci.h>
/**
* 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) { char * gluonutil_read_line(const char *filename) {
FILE *f = fopen(filename, "r"); FILE *f = fopen(filename, "r");
@ -140,7 +174,122 @@ bool gluonutil_get_node_prefix6(struct in6_addr *prefix) {
return true; 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) { 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);
} }

View File

@ -42,4 +42,18 @@ struct json_object * gluonutil_wrap_and_free_string(char *str);
struct json_object * gluonutil_load_site_config(void); 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_ */ #endif /* _LIBGLUON_LIBGLUON_H_ */