halapi: Add function checking all of the HAL compatibility 19/309319/9
authorYoungjae Cho <y0.cho@samsung.com>
Thu, 11 Apr 2024 03:30:37 +0000 (12:30 +0900)
committerYoungjae Cho <y0.cho@samsung.com>
Mon, 15 Apr 2024 02:27:11 +0000 (11:27 +0900)
Previously, the API provides callbacks that give responsibility to
the caller that storing compatibility result in their own way. However,
to provide a unified way of storing and loading HAL compatibility,
hal-api-common has become responsible for storing and loading
compatibility in a unified manner.

int hal_common_check_backend_compatibility_all()
  : It gets compatibility of all the hal modules. Additionally, it
    stores compatibility of all modules to backing storage if there
    hasn't been for later use.

Currently, the result is stored in /opt/etc/hal/.hal-compatibility and
the below is an example of result generated by hal-compatibility-checker:
  root:/opt/etc/hal> cat .hal-compatibility
  0:Not defined:0:Manifest hasn't specified the module
  1:HAL_MODULE_TBM:0:Manifest hasn't specified the module
  2:HAL_MODULE_TDM:0:Manifest hasn't specified the module
  ...
  26:HAL_MODULE_DEVICE_DISPLAY:2:Compatible
  27:HAL_MODULE_DEVICE_IR:0:Manifest hasn't specified the module
  28:HAL_MODULE_DEVICE_TOUCHSCREEN:1:Backend has incompatible version
  29:HAL_MODULE_DEVICE_LED:2:Compatible
  30:HAL_MODULE_DEVICE_BOARD:0:Manifest hasn't specified the module
  ...

It has foramt of
  "{index}:{module_name}:{compatibility}:{description}"

The tool hal-compatibility-checker, therefore, has changed to just call
the API, and removed callback code.

Change-Id: I4e1319bb98c81e1389e22924ae4ed70a66e46f7b
Signed-off-by: Youngjae Cho <y0.cho@samsung.com>
include/hal-common.h
src/hal-api-compatibility-checker.c
tools/hal-compatibility-checker/main.c

index 424382931cff000a8a5e4da62ded039bf62c50f5..9a9dced239ff2e1b528a160f9132648eb471cea9 100644 (file)
@@ -218,24 +218,13 @@ int hal_common_get_backend_library_names(enum hal_module module,
                                        int library_name_size);
 
 
-
 /**
- * @brief Called for every hal backend module.
- * @param[out] module HAL module
- * @param[out] compatibility Compatibility of the HAL module
- * @param[out] user_data The user data passed from the callback registration function
- */
-typedef void (*hal_common_backend_compatibility_cb) (enum hal_module module,
-       enum hal_common_backend_compatibility compatibility, void *user_data);
-
-/**
- * @brief Check version compatibility of modules specified in manifests.
- * @param[in] manifest_dir Directory of manifest files
- * @param[in] callback Callback for each specified module
- * @param[in] user_data The user data to be passed to the callback function
+ * @brief Check version compatibility of modules specified in manifests and stores its result
+ * @param[out] arr_compatibility Pointer for storing array of compatibilities about all modules. The data is owned by caller, so it is responsible for caller to free it using free()
+ * @param[out] len Pointer for retrieving length of array
+ * @return @c 0 on success, otherwise a negative error value
  */
-int hal_common_check_backend_compatibility(const char *manifest_dir,
-       hal_common_backend_compatibility_cb callback, void *user_data);
+int hal_common_check_backend_compatibility_all(enum hal_common_backend_compatibility **arr_compatibility, int *len);
 
 #ifdef __cplusplus
 }
index f0ca8123230294380cccdb002135359fa87f5e21..660e4fb9ba9e9be7b2022c3ff50c0a9846c8fcee 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <stdbool.h>
 #include <errno.h>
 #include <assert.h>
 #include <fcntl.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
 #include <glib.h>
 
 #include <hal-common.h>
 
 #include "common.h"
+#include "hal-api-list.h"
 #include "hal-api-compatibility-checker-object.h"
 #include "hal-api-compatibility-checker-parser.h"
 #include "hal-api-compatibility-checker-util.h"
 
-struct callback_data {
-       hal_common_backend_compatibility_cb func;
-       void *user_data;
+#define HAL_COMMON_DEFAULT_HAL_INFO_INI                        "/hal/etc/hal-info.ini"
+#define HAL_COMMON_DEFAULT_BACKEND_COMPATIBILITY_PATH          "/opt/etc/hal/.hal-backend-compatibility"
+
+#define COMPAT_INFO_MODULE_NAME_MAX            64
+#define COMPAT_INFO_COMPAT_DESC_MAX            64
+#define COMPAT_INFO_FILE_ALIGN_BYTE            ((COMPAT_INFO_MODULE_NAME_MAX + COMPAT_INFO_COMPAT_DESC_MAX) + 32)
+
+struct compatibility_info {
+       enum hal_module module;
+       char module_name[COMPAT_INFO_MODULE_NAME_MAX];
+       enum hal_common_backend_compatibility compat;
+       char compat_desc[COMPAT_INFO_COMPAT_DESC_MAX];
 };
 
+static struct compatibility_info g_compatibility_info[HAL_MODULE_END];
+static bool g_compatibility_info_loaded = false;
+
+static void set_compatibility_info_compat(enum hal_module module,
+       enum hal_common_backend_compatibility compat, const char *desc)
+{
+       if (module < HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END)
+               return;
+
+       g_compatibility_info[module].compat = compat;
+
+       if (!desc)
+               return;
+
+       snprintf(g_compatibility_info[module].compat_desc,
+               COMPAT_INFO_COMPAT_DESC_MAX, "%s", desc);
+}
+
+static void init_compatibility_info(enum hal_module module)
+{
+       if (module < HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END)
+               return;
+
+       g_compatibility_info[module].module = module;
+       snprintf(g_compatibility_info[module].module_name, COMPAT_INFO_MODULE_NAME_MAX,
+               "%s", g_hal_module_info[module].module_name ? : "Not defined");
+       set_compatibility_info_compat(module, HAL_COMMON_BACKEND_COMPATIBILITY_UNKNOWN,
+               "Manifest hasn't specified the module");
+}
+
+static int write_compatibility_info(int fd, enum hal_module module)
+{
+       char buffer[COMPAT_INFO_FILE_ALIGN_BYTE] = { 0 , };
+       struct compatibility_info *ci = NULL;
+       ssize_t ret;
+       int len = 0;
+
+       if (fd == -1)
+               return -EINVAL;
+
+       if (module < HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END)
+               return -EINVAL;
+
+       ci = &g_compatibility_info[module];
+
+       len = snprintf(buffer, sizeof(buffer), "%d:%s:%d:%s\n",
+               ci->module, ci->module_name, ci->compat, ci->compat_desc);
+
+       ret = write(fd, buffer, len);
+       if (ret == -1)
+               return -errno;
+
+       return 0;
+}
+
 static int parse_directory(const char *manifest_dir, halcc_manifest *manifest)
 {
        DIR *dir;
@@ -45,7 +114,7 @@ static int parse_directory(const char *manifest_dir, halcc_manifest *manifest)
 
        dir = opendir(manifest_dir);
        if (!dir) {
-               printf("Failed to opendir '%s', %m\n", manifest_dir);
+               _E("Failed to opendir '%s', %m\n", manifest_dir);
                return -errno;
        }
 
@@ -60,13 +129,13 @@ static int parse_directory(const char *manifest_dir, halcc_manifest *manifest)
 
                fd = openat(dfd, entry->d_name, O_RDONLY);
                if (fd < 0) {
-                       printf("Failed to openat(), %m\n");
+                       _E("Failed to openat(), %m\n");
                        continue;
                }
 
                ret = halcc_parse_fd(fd, manifest);
                if (ret < 0) {
-                       printf("Failed to parse xml, ret=%d\n", ret);
+                       _E("Failed to parse xml, ret=%d\n", ret);
                        close(fd);
                        continue;
                }
@@ -79,105 +148,218 @@ static int parse_directory(const char *manifest_dir, halcc_manifest *manifest)
        return 0;
 }
 
-static enum hal_common_backend_compatibility is_compatible(int platform_major, int platform_minor,
+static enum hal_common_backend_compatibility is_compatible(int manifest_major, int manifest_minor,
        int backend_major, int backend_minor)
 {
-       if (platform_major != backend_major)
+       if (manifest_major != backend_major)
                return HAL_COMMON_BACKEND_COMPATIBILITY_INCOMPATIBLE;
 
-       if (platform_minor < backend_minor)
+       if (manifest_minor < backend_minor)
                return HAL_COMMON_BACKEND_COMPATIBILITY_INCOMPATIBLE;
 
        return HAL_COMMON_BACKEND_COMPATIBILITY_COMPATIBLE;
 }
 
-static void foreach_hal(void *data, void *user_data)
+static void make_compatibility_info(void *data, void *user_data)
 {
-       struct callback_data *callback_data = NULL;
        enum hal_module module;
        halcc_hal *hal;
        const char *hal_name = NULL;
-       int major, minor;
+       int manifest_major, manifest_minor;
+       int backend_major, backend_minor;
        halcc_dependency_state_e state;
-       enum hal_common_backend_compatibility compatible = HAL_COMMON_BACKEND_COMPATIBILITY_INCOMPATIBLE;
+       int ret;
+       enum hal_common_backend_compatibility compat = HAL_COMMON_BACKEND_COMPATIBILITY_INCOMPATIBLE;
 
        hal = (halcc_hal *) data;
-       callback_data = (struct callback_data *) user_data;
 
        assert(hal);
-       assert(callback_data);
-       assert(callback_data->func);
 
        if (halcc_hal_get_name(hal, &hal_name) < 0 || hal_name == NULL)
                return;
 
-       if (halcc_hal_get_version(hal, &major, &minor, NULL) < 0)
-               return;
-
-       if (halcc_hal_get_dependency_state(hal, &state) != 0)
-               return;
-
-       if (state != HALCC_DEPENDENCY_STATE_SUCCESS)
-               return;
-
        for (module = HAL_MODULE_UNKNOWN; module < HAL_MODULE_END; ++module) {
                char module_name[128] = { 0 , };
-               int backend_major, backend_minor;
-               int ret;
 
                ret = hal_common_get_backend_module_name(module, module_name, sizeof(module_name));
                if (ret < 0)
                        continue;
 
                ret = strncmp(hal_name, module_name, strlen(hal_name) + 1);
-               if (ret != 0)
-                       continue;
+               if (ret == 0)
+                       break;
+       }
 
-               ret = hal_common_get_backend_version(module, &backend_major, &backend_minor);
-               if (ret != 0)
-                       continue;
+       if (module >= HAL_MODULE_END) {
+               _E("Unknown hal module: %s\n", hal_name);
+               return;
+       }
 
-               compatible = is_compatible(major, minor, backend_major, backend_minor);
-               break;
+       if (halcc_hal_get_dependency_state(hal, &state) != 0) {
+               set_compatibility_info_compat(module,
+                       HAL_COMMON_BACKEND_COMPATIBILITY_INCOMPATIBLE,
+                       "Manifest failed to resolve dependency");
+               return;
        }
 
-       if (module >= HAL_MODULE_END) {
-               printf("Unknown hal module: %s\n", hal_name);
+       if (halcc_hal_get_version(hal, &manifest_major, &manifest_minor, NULL) < 0) {
+               set_compatibility_info_compat(module,
+                       HAL_COMMON_BACKEND_COMPATIBILITY_INCOMPATIBLE,
+                       "Manifest has invalid version");
                return;
        }
 
-       printf("%s: %s\n", hal_name, compatible ? "Compatible" : "Incompatible");
+       ret = hal_common_get_backend_version(module, &backend_major, &backend_minor);
+       if (ret != 0) {
+               set_compatibility_info_compat(module,
+                       HAL_COMMON_BACKEND_COMPATIBILITY_INCOMPATIBLE,
+                       "Backend has invalid version");
+               return;
+       }
 
-       callback_data->func(module, compatible, callback_data->user_data);
+       compat = is_compatible(manifest_major, manifest_minor, backend_major, backend_minor);
+       if (compat == HAL_COMMON_BACKEND_COMPATIBILITY_COMPATIBLE)
+               set_compatibility_info_compat(module,
+                       HAL_COMMON_BACKEND_COMPATIBILITY_COMPATIBLE,
+                       "Compatible");
+       else
+               set_compatibility_info_compat(module,
+                       HAL_COMMON_BACKEND_COMPATIBILITY_INCOMPATIBLE,
+                       "Backend has incompatible version");
 }
 
-EXPORT
-int hal_common_check_backend_compatibility(const char *hal_api_manifest_dir,
-       hal_common_backend_compatibility_cb callback, void *user_data)
+static int get_tizen_hal_version(int *major, int *minor)
 {
-       halcc_manifest *manifest = NULL;
-       struct callback_data callback_data = { 0 , };
-       int ret = 0;
+       FILE *fp = NULL;
+       char *line = NULL;
+       size_t len = 0;
+       int found = 0;
 
-       if (!hal_api_manifest_dir)
+       assert(major);
+       assert(minor);
+
+       fp = fopen(HAL_COMMON_DEFAULT_HAL_INFO_INI, "r");
+       if (!fp) {
+               _E("Failed to open %s, %m\n", HAL_COMMON_DEFAULT_HAL_INFO_INI);
+               return -errno;
+       }
+
+       while (getline(&line, &len, fp) != EOF) {
+               if (fscanf(fp, "Model=Tizen%d.%d", major, minor) == 2) {
+                       found = 1;
+                       break;
+               }
+       }
+
+       fclose(fp);
+       fp = NULL;
+       free(line);
+       line = NULL;
+
+       return found ? 0 : -EINVAL;
+}
+
+static int build_compatibility_manifest_dir(char *manifest_dir, int len)
+{
+       int manifest_major = 0;
+       int manifest_minor = 0;
+       int ret;
+
+       if (!manifest_dir || len <= 0)
                return -EINVAL;
 
-       if (!callback)
+       ret = get_tizen_hal_version(&manifest_major, &manifest_minor);
+       if (ret != 0) {
+               _E("Failed to get hal version, ret=%d\n", ret);
+               return ret;
+       }
+
+       ret = snprintf(manifest_dir, len, "/etc/hal/%d.%d", manifest_major, manifest_minor);
+       if (ret >= len) {
+               _E("Buffer is too small for manifest directory\n");
+               return -EOVERFLOW;
+       }
+
+       return 0;
+}
+
+static int set_owner(int fd)
+{
+       static uid_t uid_system_fw = -1;
+       static gid_t gid_system_fw = -1;
+
+       if (fd == -1)
                return -EINVAL;
 
+       if (uid_system_fw == -1) {
+               struct passwd *p = getpwnam("system_fw");
+               if (!p)
+                       return -errno;
+
+               uid_system_fw = p->pw_uid;
+       }
+
+       if (gid_system_fw == -1) {
+               struct group *g = getgrnam("system_fw");
+               if (!g)
+                       return -errno;
+
+               gid_system_fw = g->gr_gid;
+       }
+
+       return fchown(fd, uid_system_fw, gid_system_fw);
+}
+
+static void store_backend_compatibility_to_storage(void)
+{
+       int fd = -1;
+       int ret;
+
+       assert(g_compatibility_info_loaded);
+
+       fd = open(HAL_COMMON_DEFAULT_BACKEND_COMPATIBILITY_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+       if (fd == -1)
+               return;
+
+       ret = set_owner(fd); // system_fw:system_fw
+       if (ret != 0) {
+               close(fd);
+               return;
+       }
+
+       for (enum hal_module module = 0; module < HAL_MODULE_END; ++module)
+               write_compatibility_info(fd, module);
+
+       close(fd);
+}
+
+static int load_backend_compatibility_from_manifest(void)
+{
+       char manifest_dir[64] = { 0, };
+       halcc_manifest *manifest = NULL;
+       int ret = 0;
+
+       ret = build_compatibility_manifest_dir(manifest_dir, sizeof(manifest_dir));
+       if (ret != 0)
+               return ret;
+
        ret = halcc_manifest_new(&manifest);
        if (ret < 0)
                return ret;
 
-       ret = parse_directory(hal_api_manifest_dir, manifest);
+       ret = parse_directory(manifest_dir, manifest);
        if (ret < 0)
                goto out;
 
        halcc_manifest_validate_hal_dependency(manifest);
 
-       callback_data = (struct callback_data) { callback, user_data };
+       g_compatibility_info_loaded = false;
+
+       for (enum hal_module module = 0; module < HAL_MODULE_END; ++module)
+               init_compatibility_info(module);
+       halcc_manifest_foreach_hal(manifest, make_compatibility_info, NULL);
 
-       halcc_manifest_foreach_hal(manifest, foreach_hal, &callback_data);
+       g_compatibility_info_loaded = true;
 
 out:
        if (manifest)
@@ -185,3 +367,87 @@ out:
 
        return ret;
 }
+
+static int load_backend_compatibility_from_storage(void)
+{
+       FILE *fp = NULL;
+       char *line = NULL;
+       size_t len = 0;
+       enum hal_module module;
+       int ret;
+
+       if (g_compatibility_info_loaded)
+               return 0;
+
+       fp = fopen(HAL_COMMON_DEFAULT_BACKEND_COMPATIBILITY_PATH, "r");
+       if (!fp)
+               return -errno;
+
+       for (module = 0; module < HAL_MODULE_END; ++module)
+               init_compatibility_info(module);
+
+       module = 0;
+       while (getline(&line, &len, fp) != EOF) {
+               struct compatibility_info *ci = NULL;
+
+               if (module >= HAL_MODULE_END)
+                       break;
+
+               ci = &g_compatibility_info[module++];
+               ret = sscanf(line, "%d:%63[^:]:%d:%63s",
+                       (int *) &ci->module, ci->module_name, (int *) &ci->compat, ci->compat_desc);
+               if (ret != 4)
+                       _E("Failed to load %d module", module);
+       }
+
+       free(line);
+       line = NULL;
+       fclose(fp);
+       fp = NULL;
+
+       g_compatibility_info_loaded = true;
+
+       return 0;
+}
+
+EXPORT
+int hal_common_check_backend_compatibility_all(enum hal_common_backend_compatibility **arr_compatibility, int *len)
+{
+       enum hal_common_backend_compatibility *arr = NULL;
+
+       if (g_compatibility_info_loaded)
+               goto load_success;
+
+       load_backend_compatibility_from_storage();
+       if (g_compatibility_info_loaded)
+               goto load_success;
+
+       load_backend_compatibility_from_manifest();
+       if (g_compatibility_info_loaded)
+               goto load_success;
+
+       return -ENODATA;
+
+load_success:
+       if (access(HAL_COMMON_DEFAULT_BACKEND_COMPATIBILITY_PATH, F_OK) != 0)
+               store_backend_compatibility_to_storage();
+
+       if (!arr_compatibility)
+               return 0;
+
+       if (!len)
+               return -EINVAL;
+
+       arr = calloc(HAL_MODULE_END, sizeof(enum hal_common_backend_compatibility));
+       if (!arr)
+               return -ENOMEM;
+
+       for (int i = 0; i < HAL_MODULE_END; ++i)
+               arr[i] = g_compatibility_info[i].compat;
+
+       *len = HAL_MODULE_END;
+       *arr_compatibility = arr;
+       arr = NULL;
+
+       return 0;
+}
index 13f345defcc67f9d4ae1773b49eb4aaf5b37fd0b..8ec6790c5b3fbca60f9fbcad27cc9a318d28dd6f 100644 (file)
@@ -39,7 +39,6 @@
 enum {
        OPT_START = 0,
        OPT_HELP = OPT_START,
-       OPT_PLATFORM,
        OPT_SKIP_IF_RESULT_EXIST,
        OPT_REDIRECT_ALL,
        OPT_REDIRECT_STDOUT,
@@ -51,9 +50,6 @@ static const struct option long_option[] = {
        [OPT_HELP]
            = { "help",                 no_argument,            NULL,   'h' },
 
-       [OPT_PLATFORM]
-           = { "platform",             required_argument,      NULL,   'p' },
-
        [OPT_SKIP_IF_RESULT_EXIST]
            = { "skip-if-result-exist", optional_argument,      NULL,   0 },
 
@@ -99,22 +95,6 @@ static int get_tizen_hal_version(int *major, int *minor)
        return found ? 0 : -EINVAL;
 }
 
-static const char* default_platform_manifest_dir(void)
-{
-       static char dirpath[HCC_BUF_MAX] = { 0 , };
-       int major;
-       int minor;
-       int ret;
-
-       ret = get_tizen_hal_version(&major, &minor);
-       if (ret != 0)
-               return NULL;
-
-       snprintf(dirpath, sizeof(dirpath), DEFAULT_PLATFORM_MANIFEST_DIR"/%d.%d", major, minor);
-
-       return dirpath;
-}
-
 // check result is exist, return true on exist.
 static bool result_exist(const char *dir)
 {
@@ -161,10 +141,6 @@ static void show_help(void)
                "\t-h, --help\n"
                "\t\tshow this help.\n"
                "\n"
-               "\t-p, --platform=DIRECTORY\n"
-               "\t\tspecify directory that holds platform manifest xml\n"
-               "\t\twhen it is not specified, use default path: %s\n"
-               "\n"
                "\t--skip-if-result-exist[=DIRECTORY]\n"
                "\t\tskip compatibility check if there exists a result of compatibility.\n"
                "\t\tif DIRECTORY is given, locate a result based on the given DIRECTORY.\n"
@@ -177,16 +153,9 @@ static void show_help(void)
                "\n"
                "\t--redirect-stderr=FILE|dlog\n"
                "\t\tredirect stderr to FILE or dlog with tag "LOG_TAG_HAL_COMPATIBILITY_CHECKER".\n"
-               , default_platform_manifest_dir()
          );
 }
 
-static void compatibility_cb(enum hal_module module,
-       enum hal_common_backend_compatibility is_compatible, void *user_data)
-{
-       // do something
-}
-
 int main(int argc, char *argv[])
 {
        int opt;
@@ -199,7 +168,7 @@ int main(int argc, char *argv[])
        const char *errfile = NULL;
 
        for (;;) {
-               opt = getopt_long(argc, argv, "hp:", long_option, &index);
+               opt = getopt_long(argc, argv, "h", long_option, &index);
                if (opt == -1)
                        break;
 
@@ -207,9 +176,6 @@ int main(int argc, char *argv[])
                case 'h':
                        help = 1;
                        break;
-               case 'p':
-                       platform_manifest_dir = optarg;
-                       break;
                case 0: // long-only options
                {
                        switch (index) {
@@ -254,10 +220,8 @@ int main(int argc, char *argv[])
                return 0;
        }
 
-       if (!platform_manifest_dir)
-               platform_manifest_dir = default_platform_manifest_dir();
-
-       hal_common_check_backend_compatibility(platform_manifest_dir, compatibility_cb, NULL);
+       // Not for getting result, but for storing result to backing storage
+       hal_common_check_backend_compatibility_all(NULL, NULL);
 
        return 0;
 }