tool: add command line tool for managing system-info 12/261712/5 accepted/tizen/unified/20210728.090615 submit/tizen/20210726.084601
authorYoungjae Cho <y0.cho@samsung.com>
Fri, 23 Jul 2021 06:06:46 +0000 (15:06 +0900)
committerYoungjae Cho <y0.cho@samsung.com>
Mon, 26 Jul 2021 06:37:37 +0000 (15:37 +0900)
USAGE
  system-into-tool [OPTION]

OPTION
  -l|--list-all                  List all system-info database entries
  -g|--get KEY [-v|--verbose]    Get value of feature KEY
                                 If -v option is given, shows result in detail
                                 ex) system-info-tool -g tizen.org/feature/display
                                     system-info-tool -g tizen.org/system/manufacturer -v
  -s|--set KEY TYPE VALUE        Add or update user-defined KEY
                                 Available TYPE: bool, int, double, string
                                 ex) system-info-tool -a tizen.org/feature/display bool 1
                                     system-info-tool -a tizen.org/system/manufacturer string Tizen
  -c|--clear KEY                 Clear user-defined KEY. This cannot clear read-only system key
  -C|--clear-all                 Clear all user-defined keys
  -h|--help                      Show this help

Change-Id: Id2f72ee242e12d90edc82797a21fdb8b097e2bc6
Signed-off-by: Youngjae Cho <y0.cho@samsung.com>
CMakeLists.txt
include/system_info_private.h
include/system_info_type.h
packaging/capi-system-info.spec
tool/CMakeLists.txt [new file with mode: 0755]
tool/system-info-tool-get.c [new file with mode: 0644]
tool/system-info-tool-get.h [new file with mode: 0644]
tool/system-info-tool-set.c [new file with mode: 0644]
tool/system-info-tool-set.h [new file with mode: 0644]
tool/system-info-tool.c [new file with mode: 0644]
tool/system-info-tool.h [new file with mode: 0644]

index dd5466ed3602343e938913c8e67bd4d3ea7d721d..5e558350bc248697f4e4036be6e852606499d2a6 100644 (file)
@@ -88,6 +88,7 @@ INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/system_info_intf.h DESTINATION
 ADD_SUBDIRECTORY(src/tizenid)
 ADD_SUBDIRECTORY(src/init_db)
 ADD_SUBDIRECTORY(test)
+ADD_SUBDIRECTORY(tool)
 
 IF(UNIX)
 
index 2454c76a05ffd2e850165c3dbf341b73070650c6..37fd5cbc0e9f5f119e5aa088c90d97a23d5c6a02 100644 (file)
@@ -76,7 +76,7 @@ static const struct runtime runtime[LANG_MAX] = {
 };
 
 #define NUM_HASH_FILE 10
-static inline unsigned long simple_hash(char *str)
+static inline unsigned long simple_hash(const char *str)
 {
        unsigned long ret = 0;
        int c;
index bbd2eab6fa5ba4a6e1067d5d86fbea9a0fd927f5..d5a71ec390206b16de88766b84a1f6dacc76fdae 100644 (file)
@@ -50,13 +50,14 @@ typedef enum {
  * @brief It is not decided if it should be opened to public.
  */
 typedef enum {
-       SYSTEM_INFO_BOOL,
+       SYSTEM_INFO_TYPE_MIN = 0,
+       SYSTEM_INFO_BOOL = SYSTEM_INFO_TYPE_MIN,
        SYSTEM_INFO_INT,
        SYSTEM_INFO_DOUBLE,
        SYSTEM_INFO_STRING,
+       SYSTEM_INFO_TYPE_MAX,
 } system_info_type_e;
 
-
 /**
  * @internal
  * @brief Enumeration for system information key.
index 80f1212dc83b3cca8b047f60aced201d42873edb..afd3aa670fc19a02acdf681a4c798250761e1e6e 100644 (file)
@@ -14,6 +14,7 @@ BuildRequires:  pkgconfig(libxml-2.0)
 BuildRequires:  pkgconfig(glib-2.0)
 BuildRequires:  pkgconfig(libtzplatform-config)
 BuildRequires:  pkgconfig(uuid)
+BuildRequires:  pkgconfig(libsyscommon)
 BuildRequires:  glibc-devel-static
 Requires: security-config
 
@@ -34,6 +35,14 @@ Requires: %{name}
 %description test
 A System Information test tool
 
+%package tool
+Summary:  System-info command line tool package
+Group:    Development/System
+Requires: %{name}
+
+%description tool
+A System Information command line tool
+
 %if 0%{?gcov:1}
 %package -n system-info-gcov
 Summary:  A System Information gcov test file
@@ -141,3 +150,6 @@ install -m 0644 gcov-obj/* %{buildroot}%{_datadir}/gcov/obj
 %files -n system-info-gcov
 %{_datadir}/gcov/obj/*
 %endif
+
+%files tool
+%{_bindir}/system-info-tool
diff --git a/tool/CMakeLists.txt b/tool/CMakeLists.txt
new file mode 100755 (executable)
index 0000000..5f44875
--- /dev/null
@@ -0,0 +1,14 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+
+INCLUDE(FindPkgConfig)
+PKG_CHECK_MODULES(TOOL_REQUIRED REQUIRED libsyscommon)
+
+SET(SYSTEM_INFO_TOOL "system-info-tool")
+FILE(GLOB SRCS "*.c")
+
+ADD_EXECUTABLE(${SYSTEM_INFO_TOOL} ${SRCS})
+SET_TARGET_PROPERTIES(${SYSTEM_INFO_TOOL} PROPERTIES COMPILE_FLAGS "-fPIE")
+ADD_DEPENDENCIES(${SYSTEM_INFO_TOOL} capi-system-info)
+TARGET_LINK_LIBRARIES(${SYSTEM_INFO_TOOL} capi-system-info ${TOOL_REQUIRED_LDFLAGS} -pie)
+
+INSTALL(TARGETS ${SYSTEM_INFO_TOOL} DESTINATION bin)
diff --git a/tool/system-info-tool-get.c b/tool/system-info-tool-get.c
new file mode 100644 (file)
index 0000000..c6662bd
--- /dev/null
@@ -0,0 +1,271 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "system-info-tool.h"
+#include "system-info-tool-get.h"
+
+void system_info_tool_get_help(void)
+{
+       printf("  -l|--list-all                  List all system-info database entries\n");
+       printf("  -g|--get KEY [-v|--verbose]    Get value of feature KEY\n");
+       printf("                                 If -v option is given, shows result in detail\n");
+       printf("                                 ex) system-info-tool -g tizen.org/feature/display\n");
+       printf("                                     system-info-tool -g tizen.org/system/manufacturer -v\n");
+}
+
+static void print_dbentry(struct dbentry *dbentry, bool found)
+{
+       const struct db *db;
+
+       if (!dbentry || !dbentry->db)
+               return;
+
+       db = dbentry->db;
+
+       if (found) {
+               printf("%s Key (%c) found: ", db->name, db->ticker);
+       } else {
+               printf("%s Key (%c) not found\n", db->name, db->ticker);
+               return;
+       }
+
+       print_value(dbentry->value);
+}
+
+int system_info_tool_get_raw(const char *searchkey, system_info_type_e searchtype,
+       enum db_e searchdb, struct dbentry *dbentry)
+{
+       char dbpath[BUFFER_MAX - 1];
+       char dbentry_format[BUFFER_MAX];
+       int retval;
+
+       if (!dbentry)
+               return -EINVAL;
+
+       if (searchdb < DB_START || searchdb >= DB_END)
+               return -EINVAL;
+
+       dbentry->db = &db[searchdb];
+
+       /* scanf format: "%127[^:]:%127[^:]:%127s %127s" */
+       snprintf(dbentry_format, sizeof(dbentry_format), "%%%d[^:]:%%%d[^:]:%%%ds %%%ds",
+               BUFFER_MAX - 1, BUFFER_MAX - 1, BUFFER_MAX - 1, BUFFER_MAX - 1);
+
+       for (int i = 0; i < NUM_HASH_FILE; ++i) {
+               __auto_fclose__ FILE *fp = NULL;
+               char line[BUFFER_MAX * 8] = {0, };
+               char tag[BUFFER_MAX - 1] = {0, };
+               char key[BUFFER_MAX - 1] = {0, };
+               char type[BUFFER_MAX - 1] = {0, };
+               char value[BUFFER_MAX - 1] = {0, };
+               const char *typestring;
+
+               snprintf(dbpath, sizeof(dbpath), "%s/%d", db[searchdb].path, i);
+               fp = fopen(dbpath, "r");
+               if (!fp)
+                       continue;
+
+               while ((fgets(line, sizeof(line), fp))) {
+                       retval = sscanf(line, dbentry_format, tag, key, type, value);
+                       if (retval != 4)
+                               continue;
+
+                       if (strncmp(key, searchkey, strlen(key) + 1))
+                               continue;
+
+                       typestring = type_to_string(searchtype);
+                       if (strncmp(type, typestring, strlen(type) + 1))
+                               continue;
+
+                       strncpy(dbentry->key, key, sizeof(dbentry->key) - 1);
+                       strncpy(dbentry->tag, tag, sizeof(dbentry->tag) - 1);
+                       dbentry->value.type = searchtype;
+                       if (strncmp(type, "bool", sizeof("bool")) == 0)
+                               dbentry->value.b = (value[runtime_env] == 'T');
+                       else if (strncmp(type, "int", sizeof("int")) == 0)
+                         dbentry->value.i = atoi(value);
+                       else if (strncmp(type, "double", sizeof("double")) == 0)
+                         dbentry->value.d = atof(value);
+                       else if (strncmp(type, "string", sizeof("string")) == 0)
+                         dbentry->value.s = strndup(value, sizeof(value));
+
+                       return 0;
+               }
+       }
+
+       return -ENOENT;
+}
+
+static int system_info_tool_get_api(const char *key, struct value *value)
+{
+       int retval;
+       int ret;
+       int match = 0;
+       int select;
+       int i;
+
+       struct cache {
+               int match;
+               struct value value;
+       } cache[SYSTEM_INFO_TYPE_MAX] = {
+               { -1, { SYSTEM_INFO_BOOL,   } },
+               { -1, { SYSTEM_INFO_INT,    } },
+               { -1, { SYSTEM_INFO_DOUBLE, } },
+               { -1, { SYSTEM_INFO_STRING, } },
+       };
+
+       retval = system_info_get_platform_bool(key, &cache[SYSTEM_INFO_BOOL].value.b);
+       if (retval == 0)
+               cache[SYSTEM_INFO_BOOL].match = ++match;
+
+       retval = system_info_get_platform_int(key, &cache[SYSTEM_INFO_INT].value.i);
+       if (retval == 0)
+               cache[SYSTEM_INFO_INT].match = ++match;
+
+       retval = system_info_get_platform_double(key, &cache[SYSTEM_INFO_DOUBLE].value.d);
+       if (retval == 0)
+               cache[SYSTEM_INFO_DOUBLE].match = ++match;
+
+       retval = system_info_get_platform_string(key, &cache[SYSTEM_INFO_STRING].value.s);
+       if (retval == 0)
+               cache[SYSTEM_INFO_STRING].match = ++match;
+
+       if (match == 1) {
+               for (i = 0; i < SYSTEM_INFO_TYPE_MAX; ++i)
+                       if (cache[i].match == match)
+                               memcpy(value, &cache[i].value, sizeof(struct value));
+               return 0;
+       } else if (match > 1) {
+               printf("There is multiple same keys with different type. Which type to search?\n");
+               for (i = 0; i < SYSTEM_INFO_TYPE_MAX; ++i)
+                       if (cache[i].match > 0) {
+                               printf(" [%d] ", cache[i].match);
+                               print_value(cache[i].value);
+                       }
+
+               printf("Select: ");
+               retval = scanf("%d", &select);
+               if (retval != 1) {
+                       printf("Invalid selection\n");
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               for (i = 0; i < SYSTEM_INFO_TYPE_MAX; ++i) {
+                       if (cache[i].match == select) {
+                               memcpy(value, &cache[i].value, sizeof(struct value));
+                               return 0;
+                       }
+               }
+
+               if (i == SYSTEM_INFO_TYPE_MAX) {
+                       printf("Invalid selection\n");
+                       ret =  -EINVAL;
+                       goto out;
+               }
+       }
+
+       printf("key=%s is not found\n", key);
+       ret = -ENOENT;
+
+out:
+       free(cache[SYSTEM_INFO_STRING].value.s);
+
+       return ret;
+}
+
+void system_info_tool_get(const char *key, bool verbose)
+{
+       struct value api;
+       int retval;
+
+       retval = system_info_tool_get_api(key, &api);
+       if (retval < 0)
+               return;
+
+       /* show each database if -v option is given */
+       if (verbose) {
+               int retplt;
+               int rethal;
+               int retusr;
+
+               /* set db to be searched */
+               __auto_free_dbentry__ struct dbentry plt = { 0, }; /* scan platform ro db */
+               __auto_free_dbentry__ struct dbentry hal = { 0, }; /* scan platform hal ro db */
+               __auto_free_dbentry__ struct dbentry usr = { 0, }; /* scan platform rw db */
+
+               retplt = system_info_tool_get_raw(key, api.type, DB_DEFAULT_RO, &plt);
+               print_dbentry(&plt, retplt == 0);
+
+               rethal = system_info_tool_get_raw(key, api.type, DB_HAL_RO, &hal);
+               print_dbentry(&hal, rethal == 0);
+
+               retusr = system_info_tool_get_raw(key, api.type, DB_DEFAULT_RW, &usr);
+               print_dbentry(&usr, retusr == 0);
+
+               /* print description how API have derived the result */
+               if (retusr == 0) {
+                       printf("Result (=%c overrides %c,%c): ", usr.db->ticker, plt.db->ticker, hal.db->ticker);
+               } else if (retplt == 0 && rethal == 0) {
+                       if (plt.value.type == SYSTEM_INFO_BOOL && hal.value.type == SYSTEM_INFO_BOOL)
+                               printf("Result (=%c && %c): ", plt.db->ticker, hal.db->ticker);
+                       else
+                               printf("Result (=%c overrides %c): ", hal.db->ticker, plt.db->ticker);
+               } else if (retplt == 0) {
+                       printf("Result (=%c): ", plt.db->ticker);
+               } else if (rethal == 0) {
+                       printf("Result (=%c): ", hal.db->ticker);
+               } else {
+                       printf("Result (error occured): ");
+               }
+       }
+
+       print_value(api);
+
+       return;
+}
+
+void system_info_tool_list_all(enum db_e dbtype)
+{
+       char dbpath[BUFFER_MAX - 1];
+       char dbentry_format[BUFFER_MAX];
+       int retval;
+
+       if (access(db[dbtype].path, F_OK) != 0)
+               return;
+
+       /* scanf format: "%127[^:]:%127[^:]:%127s %127s" */
+       snprintf(dbentry_format, sizeof(dbentry_format), "%%%d[^:]:%%%d[^:]:%%%ds %%%ds",
+               BUFFER_MAX - 1, BUFFER_MAX - 1, BUFFER_MAX - 1, BUFFER_MAX - 1);
+
+       for (int i = 0; i < NUM_HASH_FILE; ++i) {
+               __auto_fclose__ FILE *fp = NULL;
+               char line[BUFFER_MAX * 8] = {0, };
+               char tag[BUFFER_MAX - 1] = {0, };
+               char key[BUFFER_MAX - 1] = {0, };
+               char type[BUFFER_MAX - 1] = {0, };
+               char value[BUFFER_MAX - 1] = {0, };
+
+               snprintf(dbpath, sizeof(dbpath), "%s/%d", db[dbtype].path, i);
+               fp = fopen(dbpath, "r");
+               if (!fp)
+                       continue;
+
+               while ((fgets(line, sizeof(line), fp))) {
+                       retval = sscanf(line, dbentry_format, tag, key, type, value);
+                       if (retval != 4)
+                               continue;
+
+                       printf("%s: ", db[dbtype].name);
+                       printf("key=%s, ", key);
+                       printf("type=%s, ", type);
+                       if (strncmp(type, "bool", sizeof("bool")) == 0)
+                               printf("value=%d, ", (value[runtime_env] == 'T'));
+                       else
+                               printf("value=%s, ", value);
+                       printf("tag=%s\n", tag);
+               }
+       }
+}
diff --git a/tool/system-info-tool-get.h b/tool/system-info-tool-get.h
new file mode 100644 (file)
index 0000000..f5bd039
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __SYSTEM_INFO_TOOL_GET_H__
+#define __SYSTEM_INFO_TOOL_GET_H__
+
+#include <stdbool.h>
+
+void system_info_tool_get_help(void);
+void system_info_tool_get(const char *key, bool verbose);
+int system_info_tool_get_raw(const char *searchkey, system_info_type_e searchtype,
+       enum db_e searchdb, struct dbentry *dbentry);
+void system_info_tool_list_all(enum db_e dbtype);
+
+#endif
diff --git a/tool/system-info-tool-set.c b/tool/system-info-tool-set.c
new file mode 100644 (file)
index 0000000..a893b45
--- /dev/null
@@ -0,0 +1,343 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/file.h>
+
+#include "system-info-tool.h"
+#include "system-info-tool-get.h"
+#include "system-info-tool-set.h"
+
+void system_info_tool_set_help(void)
+{
+       printf("  -s|--set KEY TYPE VALUE        Add or update user-defined KEY\n");
+       printf("                                 Available TYPE: bool, int, double, string\n");
+       printf("                                 ex) system-info-tool -a tizen.org/feature/display bool 1\n");
+       printf("                                     system-info-tool -a tizen.org/system/manufacturer string Tizen\n");
+       printf("  -c|--clear KEY                 Clear user-defined KEY. This cannot clear read-only system key\n");
+       printf("  -C|--clear-all                 Clear all user-defined keys\n");
+}
+
+static int convert_raw_optargs(const char *rawtype, const char *rawvalue, struct value *out)
+{
+       system_info_type_e type = string_to_type(rawtype);
+
+       if (type == SYSTEM_INFO_TYPE_MAX) {
+               printf("Invalid type \"%s\"\n", rawtype);
+               return -EINVAL;
+       }
+
+       out->type = type;
+
+       if (type == SYSTEM_INFO_BOOL) {
+               int tmpbool;
+               if (sscanf(rawvalue, "%d", &tmpbool) == 1) {
+                       out->b = tmpbool;
+                       return 0;
+               } else if (strncasecmp(rawvalue, "true", sizeof("true")) == 0) {
+                       out->b = true;
+                       return 0;
+               } else if (strncasecmp(rawvalue, "false", sizeof("false")) == 0) {
+                       out->b = false;
+                       return 0;
+               }
+               printf("Invalid bool value \"%s\"\n", rawvalue);
+       } else if (type == SYSTEM_INFO_INT) {
+               if (sscanf(rawvalue, "%d", &out->i) == 1)
+                       return 0;
+               printf("Invalid int value \"%s\"\n", rawvalue);
+       } else if (type == SYSTEM_INFO_DOUBLE) {
+               if (sscanf(rawvalue, "%lf", &out->d) == 1)
+                       return 0;
+               printf("Invalid double value \"%s\"\n", rawvalue);
+       } else if (type == SYSTEM_INFO_STRING) {
+               out->s = strndup(rawvalue, BUFFER_MAX);
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int try_flock(FILE *fp, bool blocking)
+{
+       int fd = -1;
+       int retval;
+       int flags = LOCK_EX;
+
+       if (!fp)
+               return -EBADF;
+
+       fd = fileno(fp);
+       if (fd < 0)
+               return -errno;
+
+       if (blocking == false)
+               flags |= LOCK_NB;
+
+       retval = flock(fd, flags);
+       if (retval != 0) {
+               if (errno == EWOULDBLOCK)
+                       printf("User-defined database is now being accessed by someone. Try again\n");
+               else
+                       printf("Failed to acquire exclusive flock, %m\n");
+               return -errno;
+       }
+
+       return 0;
+}
+
+static int add_new_entry(const char *key, struct value value)
+{
+       char dbpath[BUFFER_MAX];
+       char internal_key[BUFFER_MAX] = {0, };
+       char internal_value[BUFFER_MAX] = {0, };
+       int retval;
+       __auto_fclose__ FILE *fp = NULL;
+
+       if (!key)
+               return -EINVAL;
+
+       /* make internal_key */
+       snprintf(internal_key, sizeof(internal_key), "platform:%s:%s", key, type_to_string(value.type));
+
+       /* make internal_value */
+       if (value.type == SYSTEM_INFO_BOOL) {
+               if (value.b == true)
+                       snprintf(internal_value, sizeof(internal_value), "TTTTTTTT");
+               else
+                       snprintf(internal_value, sizeof(internal_value), "FFFFFFFF");
+       } else if (value.type == SYSTEM_INFO_INT) {
+               snprintf(internal_value, sizeof(internal_value), "%d", value.i);
+       } else if (value.type == SYSTEM_INFO_DOUBLE) {
+               snprintf(internal_value, sizeof(internal_value), "%lf", value.d);
+       } else if (value.type == SYSTEM_INFO_STRING) {
+               snprintf(internal_value, sizeof(internal_value), "%s", value.s);
+       } else {
+               return -EINVAL;
+       }
+
+       snprintf(dbpath, sizeof(dbpath), "%s/%lu", SYSTEM_INFO_DB_RW_PATH, simple_hash(internal_key));
+       fp = fopen(dbpath, "a");
+       if (!fp)
+               return -errno;
+
+       retval = try_flock(fp, false);
+       if (retval < 0)
+               return retval;
+
+       retval = fprintf(fp, "%s %s\n", internal_key, internal_value);
+       if (retval < 0)
+               return -EIO;
+
+       return 0;
+}
+
+static int remove_entry(const char *key, system_info_type_e type)
+{
+       char dbpath[BUFFER_MAX];
+       char backup[BUFFER_MAX];
+       char internal_key[BUFFER_MAX];
+       char line[BUFFER_MAX * 8];
+       char readkey[BUFFER_MAX];
+       char readtype[BUFFER_MAX];
+       __auto_fclose__ FILE *fp1 = NULL;
+       __auto_fclose__ FILE *fp2 = NULL;
+       int retval;
+
+       if (!key)
+               return -EINVAL;
+
+       if (type < SYSTEM_INFO_TYPE_MIN || type >= SYSTEM_INFO_TYPE_MAX)
+               return -EINVAL;
+
+       /* make internal_key */
+       snprintf(internal_key, sizeof(internal_key), "platform:%s:%s", key, type_to_string(type));
+
+       snprintf(dbpath, sizeof(dbpath), "%s/%lu", SYSTEM_INFO_DB_RW_PATH, simple_hash(internal_key));
+       fp1 = fopen(dbpath, "r+");
+       if (!fp1)
+               return -errno;
+
+       retval = try_flock(fp1, false);
+       if (retval < 0)
+               return retval;
+
+       snprintf(backup, sizeof(backup), "%s/.backup.%lu", SYSTEM_INFO_DB_RW_PATH, simple_hash(internal_key));
+       fp2 = fopen(backup, "w+");
+       if (!fp2)
+               return -errno;
+
+       while ((fgets(line, sizeof(line), fp1))) {
+               retval = sscanf(line, "platform:%127s:%127s", readkey, readtype);
+               if (retval != 2)
+                       continue;
+
+               if (strncmp(readkey, key, sizeof(readkey)) == 0
+                       && string_to_type(readtype) == type)
+                       continue;
+
+               fprintf(fp2, "%s", line);
+       }
+
+       if (truncate(dbpath, 0) < 0)
+               printf("Failed to reset database, %m\n");
+
+       rewind(fp1);
+       rewind(fp2);
+
+       while ((fgets(line, sizeof(line), fp2)))
+               fprintf(fp1, "%s", line);
+
+       unlink(backup);
+
+       return 0;
+}
+
+static int modify_entry(const char *key, struct value value)
+{
+       int ret;
+
+       ret = remove_entry(key, value.type);
+       if (ret < 0)
+               return ret;
+
+       return add_new_entry(key, value);
+}
+
+static int system_info_tool_set_entry(const char *key, struct value value)
+{
+       __auto_free_dbentry__ struct dbentry dbentry = {0, };
+       int retval;
+
+       if (!key)
+               return -EINVAL;
+
+       retval = system_info_tool_get_raw(key, value.type, DB_DEFAULT_RW, &dbentry);
+       if (retval == 0)
+               return modify_entry(key, value);
+       else
+               return add_new_entry(key, value);
+}
+
+static int system_info_tool_init_rw_database(void)
+{
+       return system("/usr/bin/mkdir -p "SYSTEM_INFO_DB_RW_PATH);
+}
+
+int system_info_tool_set(int argc, char *argv[])
+{
+       int ret;
+       char *key;
+       struct value value;
+
+       /* argv[optind]    : key
+        * argv[optind + 1]: type
+        * argv[optind + 2]: value
+        * check if there is at least 3 arguments */
+       if (argc - optind < 3) {
+               printf("Need key, type, value\n");
+               return -EINVAL;
+       }
+
+       if (access(SYSTEM_INFO_DB_RW_PATH, F_OK) != 0) {
+               ret = system_info_tool_init_rw_database();
+               if (ret != 0)
+                       return -ENOTSUP;
+       }
+
+       key = argv[optind];
+       ret = convert_raw_optargs(argv[optind + 1], argv[optind + 2], &value);
+       if (ret < 0)
+               return ret;
+
+       return system_info_tool_set_entry(key, value);
+}
+
+int system_info_tool_clear_key(const char *key)
+{
+       int retval;
+       int ret;
+       int match = 0;
+       int i;
+       int select;
+
+       struct cache {
+               int match;
+               struct dbentry dbentry;
+       } cache[SYSTEM_INFO_TYPE_MAX] = {
+               { -1, },
+               { -1, },
+               { -1, },
+               { -1, },
+       };
+
+       for (i = 0; i < SYSTEM_INFO_TYPE_MAX; ++i) {
+               retval = system_info_tool_get_raw(key, i, DB_DEFAULT_RW, &cache[i].dbentry);
+               if (retval == 0)
+                       cache[i].match = ++match;
+       }
+
+       if (match == 1) {
+               for (i = 0; i < SYSTEM_INFO_TYPE_MAX; ++i) {
+                       if (cache[i].match == match) {
+                               ret = remove_entry(cache[i].dbentry.key, cache[i].dbentry.value.type);
+                               goto out;
+                       }
+               }
+       } else if (match > 1) {
+               printf("There is multiple same keys with different type. Which type to clear?\n");
+               for (i = 0; i < SYSTEM_INFO_TYPE_MAX; ++i)
+                       if (cache[i].match > 0) {
+                               printf(" [%d] ", cache[i].match);
+                               print_value(cache[i].dbentry.value);
+                       }
+
+               printf("Select: ");
+               retval = scanf("%d", &select);
+               if (retval != 1) {
+                       printf("Invalid selection\n");
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               for (i = 0; i < SYSTEM_INFO_TYPE_MAX; ++i) {
+                       if (cache[i].match == select) {
+                               ret = remove_entry(cache[i].dbentry.key, cache[i].dbentry.value.type);
+                               goto out;
+                       }
+               }
+       }
+
+       printf("user-defined key=%s is not found\n", key);
+       ret = -ENOENT;
+
+out:
+       if (cache[SYSTEM_INFO_STRING].match > 0)
+               free(cache[SYSTEM_INFO_STRING].dbentry.value.s);
+
+       return ret;
+}
+
+void system_info_tool_clear_all(void)
+{
+       char dbpath[BUFFER_MAX];
+       int retval;
+
+       for (int i = 0; i < NUM_HASH_FILE; ++i) {
+               __auto_fclose__ FILE *fp = NULL;
+               snprintf(dbpath, sizeof(dbpath), "%s/%d", SYSTEM_INFO_DB_RW_PATH, i);
+               fp = fopen(dbpath, "r");
+               if (!fp)
+                       continue;
+
+               retval = try_flock(fp, true);
+               if (retval < 0)
+                       continue;
+
+               if (truncate(dbpath, 0) < 0)
+                       printf("Failed to reset database, %m\n");
+               unlink(dbpath);
+       }
+}
diff --git a/tool/system-info-tool-set.h b/tool/system-info-tool-set.h
new file mode 100644 (file)
index 0000000..16f1a62
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef __SYSTEM_INFO_TOOL_SET_H__
+#define __SYSTEM_INFO_TOOL_SET_H__
+
+void system_info_tool_set_help(void);
+int system_info_tool_set(int argc, char *argv[]);
+int system_info_tool_clear_key(const char *key);
+void system_info_tool_clear_all(void);
+
+#endif
diff --git a/tool/system-info-tool.c b/tool/system-info-tool.c
new file mode 100644 (file)
index 0000000..538d533
--- /dev/null
@@ -0,0 +1,152 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <getopt.h>
+#include <libsyscommon/list.h>
+
+#include "system-info-tool.h"
+#include "system-info-tool-get.h"
+#include "system-info-tool-set.h"
+
+int runtime_env = C;
+const struct db db[DB_END] = {
+/*                       { dbpath,                     dbname,         ticker } */
+       [DB_DEFAULT_RO] = { SYSTEM_INFO_DB_RO_PATH,     "Platform",     'A'    },
+       [DB_HAL_RO]     = { SYSTEM_INFO_HAL_DB_RO_PATH, "HAL",          'B'    },
+       [DB_DEFAULT_RW] = { SYSTEM_INFO_DB_RW_PATH,     "User-defined", 'C'    },
+};
+
+static const char *__type_to_string[SYSTEM_INFO_TYPE_MAX] = {
+       [SYSTEM_INFO_BOOL]   = "bool",
+       [SYSTEM_INFO_INT]    = "int",
+       [SYSTEM_INFO_DOUBLE] = "double",
+       [SYSTEM_INFO_STRING] = "string",
+};
+
+const char *type_to_string(system_info_type_e type)
+{
+       if (type < SYSTEM_INFO_TYPE_MIN || type >= SYSTEM_INFO_TYPE_MAX)
+               return NULL;
+
+       return __type_to_string[type];
+}
+
+system_info_type_e string_to_type(const char *type)
+{
+       if (strncasecmp(type, "bool", sizeof("bool")) == 0)
+               return SYSTEM_INFO_BOOL;
+       else if (strncasecmp(type, "int", sizeof("int")) == 0)
+               return SYSTEM_INFO_INT;
+       else if (strncasecmp(type, "double", sizeof("double")) == 0)
+               return SYSTEM_INFO_DOUBLE;
+       else if (strncasecmp(type, "string", sizeof("string")) == 0)
+               return SYSTEM_INFO_STRING;
+       else
+               return SYSTEM_INFO_TYPE_MAX;
+}
+
+void print_value(struct value value)
+{
+       if (value.type == SYSTEM_INFO_BOOL)
+               printf("type=bool, value=%d\n", value.b);
+       else if (value.type == SYSTEM_INFO_INT)
+               printf("type=int, value=%d\n", value.i);
+       else if (value.type == SYSTEM_INFO_DOUBLE)
+               printf("type=double, value=%f\n", value.d);
+       else if (value.type == SYSTEM_INFO_STRING)
+               printf("type=string, value=%s\n", value.s);
+       else
+               printf("type=inavlid, error\n");
+}
+
+static void system_info_tool_show_help(void)
+{
+       printf("USAGE\n");
+       printf("  system-into-tool [OPTION]\n\n");
+       printf("OPTION\n");
+       system_info_tool_get_help();
+       system_info_tool_set_help();
+       printf("  -h|--help                      Show this help\n");
+}
+
+int main(int argc, char *argv[])
+{
+       int opt;
+       int retval;
+       char *runtime_str;
+       char *key;
+       bool verbose = false;
+       GList *keys = NULL;
+       GList *elem;
+
+       if (argc < 2) {
+               system_info_tool_show_help();
+               return 1;
+       }
+
+       runtime_str = getenv("RUNTIME_TYPE");
+       if (runtime_str) {
+               for (int i = 0; runtime[i].lang != LANG_MAX; ++i) {
+                       if (strncmp(runtime[i].runtime_type, runtime_str, strlen(runtime_str) + 1) == 0) {
+                               runtime_env = runtime[i].lang;
+                               break;
+                       }
+               }
+       }
+
+       const struct option long_option[] = {
+               { "get",       required_argument, NULL, 'g'  },
+               { "verbose",   no_argument,       NULL, 'v'  },
+               { "list-all",  no_argument,       NULL, 'l'  },
+               { "set" ,      no_argument,       NULL, 's'  }, /* requires 3 arguments */
+               { "clear",     required_argument, NULL, 'c'  },
+               { "clear-all", no_argument,       NULL, 'C'  },
+               { "help",      no_argument,       NULL, 'h'  },
+               { 0,           0,                 0,     0   },
+       };
+
+       for (;;) {
+               opt = getopt_long(argc, argv, "g:vlsc:Ch", long_option, NULL);
+               if (opt < 0)
+                       break;
+               switch (opt) {
+               case 'g':
+                       SYS_G_LIST_APPEND(keys, strdup(optarg));
+                       break;
+               case 'v':
+                       verbose = true;
+                       break;
+               case 'l':
+                       for (int i = DB_START; i < DB_END; ++i)
+                               system_info_tool_list_all(i);
+                       break;
+               case 's':
+                       retval = system_info_tool_set(argc, argv);
+                       if (retval < 0) {
+                               printf("Failed to set key\n");
+                               system_info_tool_set_help();
+                               return 1;
+                       }
+                       optind += 3; /* manually consume 3 arguments */
+                       break;
+               case 'c':
+                       system_info_tool_clear_key(optarg);
+                       break;
+               case 'C':
+                       system_info_tool_clear_all();
+                       break;
+               case 'h':
+                       system_info_tool_show_help();
+                       break;
+               default:
+                       system_info_tool_show_help();
+                       break;
+               }
+       }
+
+       SYS_G_LIST_FOREACH(keys, elem, key) {
+               system_info_tool_get(key, verbose);
+               free(key);
+       }
+
+       return 0;
+}
diff --git a/tool/system-info-tool.h b/tool/system-info-tool.h
new file mode 100644 (file)
index 0000000..7b966d2
--- /dev/null
@@ -0,0 +1,80 @@
+#ifndef __SYSTEM_INFO_TOOL_H__
+#define __SYSTEM_INFO_TOOL_H__
+
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <system_info.h>
+#include <system_info_type.h>
+#include <system_info_private.h>
+
+#define BUFFER_MAX              128
+
+enum db_e {
+       DB_START = 0,
+       DB_DEFAULT_RO = DB_START,
+       DB_HAL_RO,
+       DB_DEFAULT_RW,
+       DB_END,
+};
+
+struct db {
+       const char *path;
+       const char *name;
+       const char ticker;
+};
+extern const struct db db[DB_END];
+
+struct value {
+       system_info_type_e type;
+       union {
+               bool b;
+               int i;
+               double d;
+               char *s;
+       };
+};
+
+struct dbentry {
+       const struct db *db;
+       char tag[BUFFER_MAX];
+       char key[BUFFER_MAX];
+       struct value value;
+};
+
+extern int runtime_env;
+
+static inline void fclosep(FILE **fp)
+{
+       if (*fp)
+               fclose(*fp);
+}
+#define __auto_fclose__ __attribute__((cleanup(fclosep)))
+
+static inline void closep(int *fd)
+{
+       if (*fd >= 0)
+               close(*fd);
+}
+#define __auto_close__ __attribute__((cleanup(closep)))
+
+static inline void free_value(struct value *v)
+{
+       if (v->type == SYSTEM_INFO_STRING)
+               free(v->s);
+}
+#define __auto_free_value__ __attribute__((cleanup(free_value)))
+
+static inline void free_dbentry(struct dbentry *entry)
+{
+       if (entry->value.type == SYSTEM_INFO_STRING)
+               free(entry->value.s);
+}
+#define __auto_free_dbentry__ __attribute__((cleanup(free_dbentry)))
+
+
+const char *type_to_string(system_info_type_e type);
+system_info_type_e string_to_type(const char *type);
+void print_value(struct value value);
+
+#endif