From: Wiktor Gerstenstein Date: Wed, 22 Jan 2020 14:39:38 +0000 (+0100) Subject: add multi-plugin functionality X-Git-Tag: submit/tizen/20200204.171703~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=e343942e33cc8f09b7c3de1fb603a6bed5d253d4;p=platform%2Fcore%2Fapi%2Fsystem-info.git add multi-plugin functionality Change-Id: Iacef58acb8b2e9dae93a7f2bd63813c82a1ce992 --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 0914c69..894d242 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ ADD_DEFINITIONS("-DTIZEN_ID_PATH=\"${TIZEN_ID_PATH}\"") ADD_DEFINITIONS("-DLIBPATH=\"${LIB_INSTALL_DIR}\"") ADD_DEFINITIONS("-DSYSTEM_INFO_DB_RO_PATH=\"${DB_RO_PATH}\"") ADD_DEFINITIONS("-DSYSTEM_INFO_DB_RW_PATH=\"${DB_RW_PATH}\"") +ADD_DEFINITIONS(-D_GNU_SOURCE) # to make scandirat() available SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -Wl,--rpath=/usr/lib") @@ -85,6 +86,7 @@ INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/system_info_intf.h DESTINATION ADD_SUBDIRECTORY(src/tizenid) ADD_SUBDIRECTORY(src/init_db) ADD_SUBDIRECTORY(src/test) +ADD_SUBDIRECTORY(src/testplugin) IF(UNIX) diff --git a/packaging/capi-system-info.spec b/packaging/capi-system-info.spec index 4e141f2..d0605f4 100644 --- a/packaging/capi-system-info.spec +++ b/packaging/capi-system-info.spec @@ -27,10 +27,18 @@ Requires: %{name} = %{version}-%{release} %description devel +%package testplugin +Summary: System-info test plugin package +Group: Development/System + +%description testplugin +A System Information test plugins + %package test Summary: System-info test package Group: Development/System Requires: %{name} +Requires: %{name}-testplugin %description test A System Information library test tool @@ -123,8 +131,15 @@ install -m 0644 gcov-obj/* %{buildroot}%{_datadir}/gcov/obj %{_libdir}/pkgconfig/*.pc %{_libdir}/libcapi-system-info.so +%files testplugin +%{_libdir}/libsystem_info_plugins/libsystem_info_test_plugin.so +%{_libdir}/libsystem_info_plugins/libsystem_info_test_plugin2.so + %files test %{_bindir}/system_info_test +%{_bindir}/system_info_external_plugin_test.sh +%attr(0755,root,-) %{_bindir}/system_info_external_plugin_test.sh + %if 0%{?gcov:1} %files -n system-info-gcov diff --git a/src/system_info_external.c b/src/system_info_external.c index 414380c..100e886 100644 --- a/src/system_info_external.c +++ b/src/system_info_external.c @@ -23,49 +23,132 @@ #include #include #include +#include +#include +#include #define BUF_MAX 256 +#define EXTERNAL_PLUGINS_DIR "/usr/lib/libsystem_info_plugins" #define EXTERNAL_SYSTEM_INFO "/usr/lib/libsystem-info-external-plugin.so" - //LCOV_EXCL_START -int external_get_value(const char *tag, const char *key, - const char *type, char **val) +static int plugin_filter(const struct dirent *de) +{ + if (NULL == de) + return 0; + + if (de->d_type != DT_REG && de->d_type != DT_LNK) + return 0; + + return 1; +} + +void external_free_plugin_names(char **tab, int count) +{ + for (int i = 0; i < count; ++i) + free(tab[i]); + free(tab); +} + +char **external_get_plugin_names(size_t *plugin_count) +{ + char **tab = NULL; + struct dirent **entries = NULL; + int count = 0; + const int dir_fd = open(EXTERNAL_PLUGINS_DIR, O_DIRECTORY | O_RDONLY); + if (dir_fd >= 0) { + count = scandirat(dir_fd, EXTERNAL_PLUGINS_DIR, &entries, plugin_filter, alphasort); + if (!entries || count < 0) { + count = 0; + _E("Unable to get additional plugins from directory " EXTERNAL_PLUGINS_DIR ": %m"); + } + close(dir_fd); + } + + int tab_count = count + 1; + tab = (char **)calloc(sizeof(char *), tab_count); + if (!tab) + goto out_free_entries; + + tab[0] = strdup(EXTERNAL_SYSTEM_INFO); + if (!tab[0]) { + _E("strdup failed for alloc plugin path " EXTERNAL_SYSTEM_INFO ", stop listing"); + external_free_plugin_names(tab, 0); + tab = NULL; + goto out_free_entries; + } + + for (int ei = 0, ti = 1; ei < count; ++ei, ++ti) + if (asprintf(&tab[ti], EXTERNAL_PLUGINS_DIR "/%s", entries[ei]->d_name) < 0) { + _E("asprintf failed for alloc plugin path (" EXTERNAL_PLUGINS_DIR "/%s), stop listing", entries[ei]->d_name); + external_free_plugin_names(tab, ti); + tab = NULL; + goto out_free_entries; + } + + if (plugin_count) + *plugin_count = tab_count; + +out_free_entries: + for (int i = 0; i < count; ++i) + free(entries[i]); + free(entries); + + return tab; +} + +void *external_open_plugin(const char *so_path, const system_info_external_plugin_interface **plugin) { + if (!plugin) + return NULL; + void *handle = NULL; - int ret; - char buf[BUF_MAX]; - const system_info_external_plugin_interface *plugin; const system_info_external_plugin_interface *(*system_info_get_external_plugin_interface)(void); - - if (access(EXTERNAL_SYSTEM_INFO, F_OK) != 0) { - _E("No external system info library"); - return SYSTEM_INFO_ERROR_IO_ERROR; + if (access(so_path, F_OK) != 0) { + _E("No external system info library at path '%s'", so_path); + return NULL; } - handle = dlopen(EXTERNAL_SYSTEM_INFO, RTLD_NOW); + handle = dlopen(so_path, RTLD_NOW); if (!handle) { - _E("dlopen(%s) failed(%s)", EXTERNAL_SYSTEM_INFO, dlerror()); - ret = SYSTEM_INFO_ERROR_IO_ERROR; - goto out; + _E("dlopen(%s) failed(%s)", so_path, dlerror()); + goto out_err; } system_info_get_external_plugin_interface = dlsym(handle, "system_info_get_external_plugin_interface"); if (!system_info_get_external_plugin_interface) { _E("dlsym failed(%s)", dlerror()); - ret = SYSTEM_INFO_ERROR_IO_ERROR; - goto out; + goto out_err; } - plugin = system_info_get_external_plugin_interface(); - if (!plugin) { + *plugin = system_info_get_external_plugin_interface(); + if (!*plugin) { _E("Failed to get plugin"); - ret = SYSTEM_INFO_ERROR_IO_ERROR; - goto out; + goto out_err; } + return handle; + +out_err: + if (handle) + dlclose(handle); + + return NULL; +} + +int external_get_plugin_value(const char *so_path, const char *tag, const char *key, + const char *type, char **val) +{ + int ret; + char buf[BUF_MAX]; + const system_info_external_plugin_interface *plugin = NULL; + void *handle = external_open_plugin(so_path, &plugin); + + if (!handle) + return SYSTEM_INFO_ERROR_IO_ERROR; + if (plugin->get_value_external == NULL) { _E("Invalid plugin function"); ret = SYSTEM_INFO_ERROR_IO_ERROR; @@ -73,8 +156,8 @@ int external_get_value(const char *tag, const char *key, } ret = plugin->get_value_external(tag, key, type, buf, sizeof(buf)); - if (ret < 0) { - _E("Failed to get value from plugin (ret:%d)", ret); + if (ret < 0) { // TODO: add special value when plugin does not provide the key + _I("Key %s is not provided by '%s' plugin (ret:%d)", key, so_path, ret); ret = SYSTEM_INFO_ERROR_IO_ERROR; goto out; } @@ -95,40 +178,33 @@ out: return ret; } -int external_get_type(const char *tag, const char *key, char **type) +int external_get_value(const char *tag, const char *key, const char *type, char **val) { - void *handle = NULL; - int ret; - char buf[BUF_MAX]; - const system_info_external_plugin_interface *plugin; - const system_info_external_plugin_interface *(*system_info_get_external_plugin_interface)(void); + size_t plugin_count = 0; + int ret = SYSTEM_INFO_ERROR_IO_ERROR; + char **tab = external_get_plugin_names(&plugin_count); + for (size_t i = 0; i < plugin_count && ret != SYSTEM_INFO_ERROR_NONE; ++i) + ret = external_get_plugin_value(tab[i], tag, key, type, val); - if (access(EXTERNAL_SYSTEM_INFO, F_OK) != 0) { - _E("No external system info library"); - return SYSTEM_INFO_ERROR_IO_ERROR; - } + external_free_plugin_names(tab, plugin_count); - handle = dlopen(EXTERNAL_SYSTEM_INFO, RTLD_NOW); - if (!handle) { - _E("dlopen(%s) failed(%s)", EXTERNAL_SYSTEM_INFO, dlerror()); - ret = SYSTEM_INFO_ERROR_IO_ERROR; - goto out; - } + if (ret != SYSTEM_INFO_ERROR_NONE) + _E("Key value (%s) not obtained by any plugin.", key); - system_info_get_external_plugin_interface = dlsym(handle, "system_info_get_external_plugin_interface"); - if (!system_info_get_external_plugin_interface) { - _E("dlsym failed(%s)", dlerror()); - ret = SYSTEM_INFO_ERROR_IO_ERROR; - goto out; - } + return ret; +} - plugin = system_info_get_external_plugin_interface(); - if (!plugin) { - _E("Failed to get plugin"); - ret = SYSTEM_INFO_ERROR_IO_ERROR; - goto out; - } +int external_get_plugin_type(const char *so_path, const char *tag, const char *key, + const char *type, char **val) +{ + int ret; + char buf[BUF_MAX]; + const system_info_external_plugin_interface *plugin = NULL; + void *handle = external_open_plugin(so_path, &plugin); + + if (!handle) + return SYSTEM_INFO_ERROR_IO_ERROR; if (plugin->get_type_external == NULL) { _E("Invalid plugin function"); @@ -137,14 +213,14 @@ int external_get_type(const char *tag, const char *key, char **type) } ret = plugin->get_type_external(tag, key, buf, sizeof(buf)); - if (ret < 0) { - _E("Failed to get value from plugin (ret:%d)", ret); + if (ret < 0) { // TODO: add special value when plugin does not provide the key + _I("Key %s is not provided by '%s' plugin (ret:%d)", key, so_path, ret); ret = SYSTEM_INFO_ERROR_IO_ERROR; goto out; } - *type = strdup(buf); - if (*type == NULL) { + *val = strdup(buf); + if (*val == NULL) { _E("strdup() failed"); ret = SYSTEM_INFO_ERROR_OUT_OF_MEMORY; goto out; @@ -158,4 +234,22 @@ out: return ret; } + +int external_get_type(const char *tag, const char *key, char **type) +{ + size_t plugin_count = 0; + int ret = SYSTEM_INFO_ERROR_IO_ERROR; + + char **tab = external_get_plugin_names(&plugin_count); + for (size_t i = 0; i < plugin_count && ret != SYSTEM_INFO_ERROR_NONE; ++i) + ret = external_get_plugin_type(tab[i], tag, key, NULL, type); + + external_free_plugin_names(tab, plugin_count); + + if (ret != SYSTEM_INFO_ERROR_NONE) + _E("Key type (%s) not obtained by any plugin.", key); + + return ret; +} + //LCOV_EXCL_STOP diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 6c0905f..d602a01 100755 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -20,3 +20,5 @@ ADD_DEPENDENCIES(${SYSTEM_INFO_TEST} ${fw_name}) TARGET_LINK_LIBRARIES(${SYSTEM_INFO_TEST} ${SYSTEM_INFO_TEST_pkgs_LDFLAGS} "-L${CMAKE_SOURCE_DIR} -lcapi-system-info") INSTALL(TARGETS ${SYSTEM_INFO_TEST} DESTINATION bin) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/src/test/system_info_external_plugin_test.sh DESTINATION bin) + diff --git a/src/test/system_info_external_plugin_test.sh b/src/test/system_info_external_plugin_test.sh new file mode 100755 index 0000000..e34142e --- /dev/null +++ b/src/test/system_info_external_plugin_test.sh @@ -0,0 +1,136 @@ +#!/bin/bash + +function test_key_type_string_cmd() { + cmd_result="$(system_info_test -g custom -t string -k sysinfotest_ks)" + name="'$FUNCNAME'" +} + +function test_string_not_int_cmd() { + cmd_result="$(system_info_test -g custom -t int -k sysinfotest_ks)" + name="'$FUNCNAME'" +} + +function test_string_not_double_cmd() { + cmd_result="$(system_info_test -g custom -t double -k sysinfotest_ks)" + name="'$FUNCNAME'" +} + +function test_string_not_bool_cmd() { + cmd_result="$(system_info_test -g custom -t bool -k sysinfotest_ks)" + name="'$FUNCNAME'" +} + +function test_key_type_int_cmd() { + cmd_result="$(system_info_test -g custom -t int -k sysinfotest_ki)" + name="'$FUNCNAME'" +} + +function test_key_type_double_cmd() { + cmd_result="$(system_info_test -g custom -t double -k sysinfotest_kd)" + name="'$FUNCNAME'" +} + +function test_key_type_bool_cmd() { + cmd_result="$(system_info_test -g custom -t bool -k sysinfotest_kb)" + name="'$FUNCNAME'" +} + +function test_bool_not_string_cmd() { + cmd_result="$(system_info_test -g custom -t string -k sysinfotest_kb)" + name="'$FUNCNAME'" +} + +function test_bool_not_int_cmd() { + cmd_result="$(system_info_test -g custom -t int -k sysinfotest_kb)" + name="'$FUNCNAME'" +} + +function test_bool_not_double_cmd() { + cmd_result="$(system_info_test -g custom -t double -k sysinfotest_kb)" + name="'$FUNCNAME'" +} + +function test_second_plugin_str_cmd() { + cmd_result="$(system_info_test -g custom -t string -k sysinfotest_vks_custom)" + name="'$FUNCNAME'" +} + +function test_right_plugin_str_cmd() { + cmd_result="$(system_info_test -g custom -t string -k sysinfotest_kvs)" + name="'$FUNCNAME'" +} + +function get_cmd_result() { + check_result="$(echo $1 | cut -d'(' -f6 | cut -d')' -f1)" +} + +function check_file_exists() { + cmd_result="$(test -s $1 && echo 'exists' || echo 'not')" + name="'$FUNCNAME' $1" +} + +function check_command_exists() { + command_exists="$(type -p system_info_test)" +} + +function check_result() { + local test_cmd=$1 + local expected_result=$2 + + $test_cmd + get_cmd_result "$cmd_result" + + #check if a variable is a number (formating cmd output) + re='^[+-]?[0-9]+([.][0-9]+)?$' + if [[ $check_result =~ $re ]] ; then + check_result="$(printf '%.2f' $check_result)" + if [[ $expected_result =~ $re ]] ; then + expected_result="$(printf '%.2f' $expected_result)" + fi + fi + + sign='=' + nsign='!' + if [[ $# -ge 3 && $3 == '!' ]]; then + nsign=$sign + sign=$3 + fi + if [[ $check_result == '' ]]; then + check_result='EMPTY_RESULT' + fi + if [[ ( $check_result != 'EMPTY_RESULT' && ( $sign == '!' && "$check_result" != "$expected_result" ) || ( "$check_result" == "$expected_result" ) ) ]]; then + echo -e '\033[32m[' ' OK ' ']\e[0m' "$name:\t" "$sign('$expected_result')" + else + echo -e '\033[31m[' 'FAIL' ']\e[0m' "$name:\t" "('\033[31m$check_result\e[0m'" "$nsign=" "'\033[32m$expected_result\e[0m')" + return_code=1 + fi +} + +check_command_exists result +if [ -z "$command_exists" ]; then + exit 1 +fi + +return_code=0 + +PLUGIN_FILE='/usr/lib/libsystem_info_plugins/libsystem_info_test_plugin.so' +check_result "check_file_exists $PLUGIN_FILE" 'exists' +PLUGIN_FILE='/usr/lib/libsystem_info_plugins/libsystem_info_test_plugin2.so' +check_result "check_file_exists $PLUGIN_FILE" 'exists' + +check_result test_key_type_string_cmd 'plugin' +check_result test_string_not_int_cmd 'ERROR : -22' +check_result test_string_not_double_cmd 'ERROR : -22' +check_result test_string_not_bool_cmd 'ERROR : -22' +check_result test_key_type_int_cmd '1' +check_result test_key_type_double_cmd '2.0' +check_result test_key_type_bool_cmd '1' +check_result test_bool_not_string_cmd 'ERROR : -22' +check_result test_bool_not_int_cmd 'ERROR : -22' +check_result test_bool_not_double_cmd 'ERROR : -22' +check_result test_second_plugin_str_cmd 'various key' +check_result test_right_plugin_str_cmd 'first_in_lexicographical_order' +check_result test_right_plugin_str_cmd 'second_in_lexicographical_order' '!' +check_result test_right_plugin_str_cmd 'ERROR : -22' '!' + +exit $return_code diff --git a/src/testplugin/CMakeLists.txt b/src/testplugin/CMakeLists.txt new file mode 100755 index 0000000..8f9d45a --- /dev/null +++ b/src/testplugin/CMakeLists.txt @@ -0,0 +1,13 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +SET(SI_PLUGIN "system_info_test_plugin") + +SET(SRCS ${CMAKE_SOURCE_DIR}/src/testplugin/plugin_test.c) + +INCLUDE_DIRECTORIES(include) + +ADD_LIBRARY(${SI_PLUGIN} MODULE ${SRCS}) +SET_TARGET_PROPERTIES(${SI_PLUGIN} PROPERTIES COMPILE_FLAGS -DPLUGIN_FIRST) +ADD_LIBRARY(${SI_PLUGIN}2 MODULE ${SRCS}) + +INSTALL(TARGETS ${SI_PLUGIN} DESTINATION ${LIB_INSTALL_DIR}/libsystem_info_plugins) +INSTALL(TARGETS ${SI_PLUGIN}2 DESTINATION ${LIB_INSTALL_DIR}/libsystem_info_plugins) diff --git a/src/testplugin/plugin_test.c b/src/testplugin/plugin_test.c new file mode 100644 index 0000000..5824b80 --- /dev/null +++ b/src/testplugin/plugin_test.c @@ -0,0 +1,62 @@ +#include +#include + +#include "system_info_type.h" + +#define VALUES_COUNT (sizeof value_descriptors / sizeof *value_descriptors) + +#ifdef PLUGIN_FIRST + #define PLUGIN_STRING_VALUE "first_in_lexicographical_order" + #define PLUGIN_STRING_KEY "" +#else + #define PLUGIN_STRING_VALUE "second_in_lexicographical_order" + #define PLUGIN_STRING_KEY "_custom" +#endif + +const struct entity { + const char *key; + const char *type; + const char *value; +} value_descriptors[] = { + { "sysinfotest_kvs", TYPE_STR, PLUGIN_STRING_VALUE }, + { "sysinfotest_vks" PLUGIN_STRING_KEY, TYPE_STR, "various key" }, + { "sysinfotest_ks", TYPE_STR, "plugin" }, + { "sysinfotest_ki", TYPE_INT, "1" }, + { "sysinfotest_kd", TYPE_DBL, "2.0" }, + { "sysinfotest_kb", TYPE_BOOL, "true" } +}; + +static int get_value(const char *tag, const char *key, const char *type, char *buf, unsigned int len) +{ + if (key && type) + for (size_t i = 0; i < VALUES_COUNT; ++i) + if (!strcmp(key, value_descriptors[i].key) && !strcmp(type, value_descriptors[i].type)) { + if (!strncpy(buf, value_descriptors[i].value, len)) + return SYSTEM_INFO_ERROR_INVALID_PARAMETER; + return SYSTEM_INFO_ERROR_NONE; + } + return SYSTEM_INFO_ERROR_IO_ERROR; +} + +static int get_type(const char *tag, const char *key, char *buf, unsigned int len) +{ + if (key) + for (size_t i = 0; i < VALUES_COUNT; ++i) + if (!strcmp(key, value_descriptors[i].key)) { + if (!strncpy(buf, value_descriptors[i].type, len)) + return SYSTEM_INFO_ERROR_INVALID_PARAMETER; + return SYSTEM_INFO_ERROR_NONE; + } + return SYSTEM_INFO_ERROR_IO_ERROR; +} + +const system_info_external_plugin_interface intf = { + .get_value_external = get_value, + .get_type_external = get_type, +}; + +__attribute__ ((visibility ("default"))) +const system_info_external_plugin_interface *system_info_get_external_plugin_interface(void) +{ + return &intf; +}