From: SangYoun Kwak Date: Mon, 10 Mar 2025 07:58:47 +0000 (+0900) Subject: hal-backend-service: Change to create socket, service and header dynamically using... X-Git-Tag: accepted/tizen/unified/20250322.012724~6 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F38%2F320938%2F11;p=platform%2Fhal%2Fapi%2Fcommon.git hal-backend-service: Change to create socket, service and header dynamically using json config file Services/sockets, stub proc names can be vary, so they should be changed easily. To accomplish this, script and template file(ends with .in) are added to create socket, service and header file dynamically, by the information in the json config file. hal-backend-service-config.json file contains service informations, thread informations. Service information contains service_name(which will be service name and stub proc name), thread informations. Thread information contains hal module names to be used. Below is an example of json config file: { "process": [ { "type": "systemd", "property": { "name": "hal-backend-service-1" }, "thread": [ { "hal_module_name": [ "device_display", "device_led" ] }, { "hal_module_name": [ "abc" ] } ] }, { "type": "systemd", "property": { "name": "hal-backend-service-2" }, "thread": [ { "hal_module_name": [ "codec" ] } ] } ] } Based on the parsed information from the json file, service and socket files are created. Also, a header file which maps hal module(enum) to the stub proc name. To get the stub proc name form the proxy, a function is added to get them. Please be notified that name in the property section does not have to be same with stub_proc_name. name is used to define stub_proc_name since it is unique. stub_proc_name can be other value if it is unique. Change-Id: I13b03196f3cd64b3404cd2946d540e67fd6afeec Signed-off-by: SangYoun Kwak --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 77f8cae..0553a4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,8 @@ SET(SRCS src/hal-api-conf.c src/hal-api-compatibility-checker.c src/hal-api-compatibility-checker-object.c - src/hal-api-compatibility-checker-parser.c) + src/hal-api-compatibility-checker-parser.c + src/hal-api-backend-service.c) ADD_LIBRARY( ${PROJECT_NAME} SHARED ${SRCS}) TARGET_LINK_LIBRARIES( ${PROJECT_NAME} ${pkgs_LDFLAGS} -ldl -Wl,-z,nodelete,--no-undefined) @@ -70,13 +71,6 @@ INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/packaging/${PROJECT_NAME}.pc DESTINATION ${LIBDIR}/pkgconfig) -if (${ENABLE_HAL_BACKEND_SERVICE}) -INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/packaging/ DESTINATION lib/systemd/system - FILES_MATCHING - PATTERN "hal-backend-service.*" - ) -endif() - ADD_SUBDIRECTORY(tests) ADD_SUBDIRECTORY(tools/lshal) diff --git a/hal-backend-service/CMakeLists.txt b/hal-backend-service/CMakeLists.txt index 35868d3..dfeeb64 100644 --- a/hal-backend-service/CMakeLists.txt +++ b/hal-backend-service/CMakeLists.txt @@ -33,3 +33,8 @@ MESSAGE("${PROJECT_NAME}") ADD_EXECUTABLE(${PROJECT_NAME} ${src}) TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${gtest_LDFLAGS} ${gtest_pkgs_LDFLAGS} -ldl -L${LIBDIR}/hal) INSTALL(TARGETS ${PROJECT_NAME} DESTINATION /usr/bin/) + +INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ DESTINATION lib/systemd/system + FILES_MATCHING PATTERN "hal-backend-service-*.service") +INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ DESTINATION lib/systemd/system + FILES_MATCHING PATTERN "hal-backend-service-*.socket") diff --git a/hal-backend-service/config/hal-backend-service-config.json b/hal-backend-service/config/hal-backend-service-config.json new file mode 100644 index 0000000..ee4bdf2 --- /dev/null +++ b/hal-backend-service/config/hal-backend-service-config.json @@ -0,0 +1,15 @@ +{ + "process": [ + { + "type": "systemd", + "property": { + "name": "hal-backend-service-device" + }, + "thread": [ + { "hal_module_name": [ "device_display" ] }, + { "hal_module_name": [ "device_led" ] }, + { "hal_module_name": [ "device_battery" ] } + ] + } + ] +} diff --git a/hal-backend-service/config_types.py b/hal-backend-service/config_types.py new file mode 100644 index 0000000..d93f99e --- /dev/null +++ b/hal-backend-service/config_types.py @@ -0,0 +1,167 @@ +import errno + +class InvalidJsonStructException(Exception): + errno = errno.EINVAL + +class InvalidJsonValueException(Exception): + errno = errno.EINVAL + +class JsonConfigEntity: + def get_name(self): + return None + def get_hal_module_names(self): + return None + def map_json_obj(self, json_obj): + return None + +class ThreadConfig(JsonConfigEntity): + hal_module_names: list + + def __init__(self, json_obj): + self.map_json_obj(json_obj) + + def __str__(self): + result = "" + result += f"\t\tThread:\n" + result += f"\t\t\thal module names: " + ", ".join(self.hal_module_names) + "\n" + return result + + def map_json_obj(self, json_obj): + if type(json_obj) != dict: + raise InvalidJsonStructException("Invalid json struct: thread should be a json object") + if "hal_module_name" not in json_obj: + raise InvalidJsonStructException("Invalid json struct: thread should have \"hal_module_name\"") + + hal_module_name_obj = json_obj["hal_module_name"] + if type(hal_module_name_obj) != list: + raise InvalidJsonStructException("Invalid json struct: hal_module_name should be a list") + + if any(filter(lambda x: type(x) != str, hal_module_name_obj)): + raise InvalidJsonStructException("Invalid json struct: hal_module_name should be a list of strings") + + self.hal_module_names = [ str(obj) for obj in hal_module_name_obj ] + +class ThreadsConfig(JsonConfigEntity): + threads: list + + def __init__(self, json_obj): + self.map_json_obj(json_obj) + + def __str__(self): + result = "" + result += f"\tThreads:\n" + for thread in self.threads: + result += f"{str(thread)}" + return result + + def map_json_obj(self, json_obj): + if type(json_obj) != list: + raise InvalidJsonStructException("Invalid json struct: thread should be a list") + + threads = list() + + for obj in json_obj: + threads.append(ThreadConfig(obj)) + + self.threads = threads + + def get_hal_module_names(self): + hal_module_names = list() + + for thread in self.threads: + hal_module_names += thread.hal_module_names + + return hal_module_names + +class HalBackendServiceSystemdPropertyConfig(JsonConfigEntity): + name: str + + def __init__(self, json_obj): + self.map_json_obj(json_obj) + + def __str__(self): + result = "" + result += f"\tProperties:\n" + result += f"\t\tname: {self.name}\n" + return result + + def map_json_obj(self, json_obj): + if type(json_obj) != dict: + raise InvalidJsonStructException("Invalid json struct: systemd property should be a json object") + if "name" not in json_obj: + raise InvalidJsonStructException("Invalid json struct: systemd property should have \"name\"") + + name_obj = json_obj["name"] + if type(name_obj) != str: + raise InvalidJsonStructException("Invalid json struct: systemd name should be a string") + + service_name_prefix = "hal-backend-service-" + if not name_obj.startswith(service_name_prefix): + raise InvalidJsonValueException(f"Invalid json value: systemd name should start with {service_name_prefix}") + + self.name = name_obj + +class HalBackendServiceSystemdConfig(JsonConfigEntity): + properties: HalBackendServiceSystemdPropertyConfig + thread_config: ThreadsConfig + + def __init__(self, json_obj): + self.type = "systemd" + self.map_json_obj(json_obj) + + def __str__(self): + result = "" + result += f"Service:\n" + result += f"{str(self.properties)}" + result += f"{str(self.thread_config)}" + return result + + def map_json_obj(self, json_obj): + if type(json_obj) != dict: + raise InvalidJsonStructException("Invalid json struct: systemd process should be a json object") + if "property" not in json_obj: + raise InvalidJsonStructException("Invalid json struct: systemd process should have \"properties\"") + if "thread" not in json_obj: + raise InvalidJsonStructException("Invalid json struct: systemd process should have \"thread\"") + + property_obj = json_obj["property"] + self.properties = HalBackendServiceSystemdPropertyConfig(property_obj) + + thread_obj = json_obj["thread"] + self.thread_config = ThreadsConfig(thread_obj) + + def get_name(self): + return self.properties.name + + def get_hal_module_names(self): + return self.thread_config.get_hal_module_names() + +class HalBackendServiceConfig(JsonConfigEntity): + processes: list + type_classes = { "systemd": HalBackendServiceSystemdConfig } + + def __init__(self, json_obj): + self.map_json_obj(json_obj) + + def __str__(self): + return '\n'.join([ str(process) for process in self.processes ]) + + def map_json_obj(self, json_obj): + if "process" not in json_obj: + raise InvalidJsonStructException("No process element") + + process_obj = json_obj["process"] + if type(process_obj) != list: + raise InvalidJsonStructException(f"Invalid json struct: \"process\" should be a list, not {type(process_obj)}") + + processes = list() + + for obj in process_obj: + if "type" not in obj: + raise InvalidJsonStructException(f"Invalid json struct: process should have \"type\"") + process_type = obj["type"] + if process_type not in self.type_classes: + raise InvalidJsonValueException(f"Invalid json value: unknown type({process_type})") + processes.append(self.type_classes[process_type](obj)) + + self.processes = processes diff --git a/hal-backend-service/hal-api-backend-service-list.h.in b/hal-backend-service/hal-api-backend-service-list.h.in new file mode 100644 index 0000000..7023fe2 --- /dev/null +++ b/hal-backend-service/hal-api-backend-service-list.h.in @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +static const char *g_hal_module_stub_proc_name[] = { + [HAL_MODULE_UNKNOWN] = NULL, +@HAL_MODULE_STUB_PROC_NAME_MAP@ + [HAL_MODULE_END] = NULL, +}; diff --git a/hal-backend-service/hal-backend-service-generator.py b/hal-backend-service/hal-backend-service-generator.py new file mode 100755 index 0000000..b1d9c72 --- /dev/null +++ b/hal-backend-service/hal-backend-service-generator.py @@ -0,0 +1,232 @@ +#!/usr/bin/python3 + +import errno +import json + +from getopt import getopt, GetoptError +from sys import exit, argv + +from config_types import * + +def log(message): + message_lines = message.split('\n') + for line in message_lines: + print(f"[hal-backend-service-generator] {line}") + +def parse_hal_backend_service_configs(config_file_path): + with open(config_file_path, "r") as f: + json_object = json.load(f) + return HalBackendServiceConfig(json_object) + +def check_hal_backend_service_configs_duplications(configs): + names_set = set() + hal_module_names_set = set() + + for config in configs.processes: + name = config.get_name() + if name not in names_set: + names_set.add(name) + continue + raise InvalidJsonValueException(f"Invalid json value: duplicated name({name})") + + for config in configs.processes: + for hal_module_name in config.get_hal_module_names(): + if hal_module_name not in hal_module_names_set: + hal_module_names_set.add(hal_module_name) + continue + raise InvalidJsonValueException(f"Invalid json value: duplicated hal module name({hal_module_name})") + +def create_empty_hal_backend_service_list_header(prototype_path, header_file_path): + with open(prototype_path, "r") as f: + prototype = f.read() + + module_name_map = prototype.replace("@HAL_MODULE_STUB_PROC_NAME_MAP@", "") + + with open(header_file_path, "w") as f: + f.write(module_name_map) + +def create_hal_backend_service_list_header(prototype_path, configs, header_file_path): + with open(prototype_path, "r") as f: + prototype = f.read() + + module_name_map_list = list() + for config in configs.processes: + name = config.get_name() + for hal_module_name in config.get_hal_module_names(): + hal_module_enum = f"HAL_MODULE_{hal_module_name.upper()}" + stub_proc_name = f"d::{name}" + module_name_map_list.append(f"\t[{hal_module_enum}] = \"{stub_proc_name}\",") + module_name_map = prototype.replace("@HAL_MODULE_STUB_PROC_NAME_MAP@", '\n'.join(module_name_map_list)) + + with open(header_file_path, "w") as f: + f.write(module_name_map) + +def create_hal_backend_service_service(prototype_path, configs, install_dir): + with open(prototype_path, "r") as f: + prototype = f.read() + + for config in configs.processes: + if config.type != "systemd": + continue + service_name = config.get_name() + service_file_name = f"{service_name}.service" + stub_proc_name = f"d::{service_name}" + threads = ' '.join(map(lambda thread: ','.join(thread.hal_module_names), config.thread_config.threads)) + threads = threads.replace('_', '-') + + service_content = prototype.replace("@SERVICE_NAME@", service_name)\ + .replace("@PROC_NAME@", stub_proc_name)\ + .replace("@THREADS@", threads) + + with open(f"{install_dir}/{service_file_name}", "w") as f: + f.write(service_content) + +def create_hal_backend_service_socket(prototype_path, configs, install_dir): + with open(prototype_path, "r") as f: + prototype = f.read() + + for config in configs.processes: + if config.type != "systemd": + continue + service_name = config.get_name() + socket_file_name = f"{service_name}.socket" + stub_proc_name = f"d::{service_name}" + listen_streams = list() + + for thread_config in config.thread_config.threads: + for hal_module_name in thread_config.hal_module_names: + listen_streams.append(f"ListenStream=/run/aul/rpcport/.{stub_proc_name}::{hal_module_name}") + + socket_content = prototype.replace("@SERVICE_NAME@", service_name)\ + .replace("@LISTEN_STREAMS@", '\n'.join(listen_streams)) + + with open(f"{install_dir}/{socket_file_name}", "w") as f: + f.write(socket_content) + +def print_usage(exec_name): + print(f"usage: {exec_name}