add multi-plugin functionality 68/221468/26
authorWiktor Gerstenstein <w.gerstenste@partner.samsung.com>
Wed, 22 Jan 2020 14:39:38 +0000 (15:39 +0100)
committerKarol Lewandowski <k.lewandowsk@samsung.com>
Tue, 4 Feb 2020 14:09:26 +0000 (14:09 +0000)
Change-Id: Iacef58acb8b2e9dae93a7f2bd63813c82a1ce992

CMakeLists.txt
packaging/capi-system-info.spec
src/system_info_external.c
src/test/CMakeLists.txt
src/test/system_info_external_plugin_test.sh [new file with mode: 0755]
src/testplugin/CMakeLists.txt [new file with mode: 0755]
src/testplugin/plugin_test.c [new file with mode: 0644]

index 0914c6997fd8d2996cc3c56379e81f99128c07b5..894d242d18678664a39f0f044b36546852c3ce9a 100644 (file)
@@ -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)
 
index 4e141f2103f960a5965e4246c60405b22d33b656..d0605f4c07c17150bf6ae07ee8b8efa1afedec9f 100644 (file)
@@ -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
index 414380c4d7efaac753a844934813c6c16e65e33b..100e88646e8a64439ea77c4f35bb48d5f22de193 100644 (file)
 #include <string.h>
 #include <unistd.h>
 #include <dlfcn.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
 
 #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
index 6c0905f065d0a038452957f07ce84b58a71f9bcd..d602a0176ea0ca413f7903629ab06512abec43bf 100755 (executable)
@@ -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 (executable)
index 0000000..e34142e
--- /dev/null
@@ -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 (executable)
index 0000000..8f9d45a
--- /dev/null
@@ -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 (file)
index 0000000..5824b80
--- /dev/null
@@ -0,0 +1,62 @@
+#include <string.h>
+#include <system_info_intf.h>
+
+#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;
+}