diff --git a/interfaces/innerkits/include/init_socket_api.h b/interfaces/innerkits/include/init_socket_api.h new file mode 100755 index 0000000000000000000000000000000000000000..420264d147bf965ee58dc44f2b2a07dd856af2a8 --- /dev/null +++ b/interfaces/innerkits/include/init_socket_api.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef INIT_SOCKET_API_H +#define INIT_SOCKET_API_H + +#define OHOS_SOCKET_DIR "/dev/unix/socket" +#define OHOS_SOCKET_ENV_PREFIX "OHOS_SOCKET_" +// parameter is service name +int GetControlSocket(const char *name); + +#endif \ No newline at end of file diff --git a/interfaces/innerkits/socket/BUILD.gn b/interfaces/innerkits/socket/BUILD.gn new file mode 100755 index 0000000000000000000000000000000000000000..d2c9ca81e5040fce4ad13de007c6e471af731f0b --- /dev/null +++ b/interfaces/innerkits/socket/BUILD.gn @@ -0,0 +1,27 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# 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. + +import("//build/ohos.gni") +ohos_static_library("libsocket") { + sources = [ + "//base/startup/init_lite/interfaces/innerkits/socket/init_socket_api.c", + ] + + include_dirs = [ + "//base/startup/init_lite/interfaces/innerkits/include", + ] + + deps = [ + ] +} + diff --git a/interfaces/innerkits/socket/init_socket_api.c b/interfaces/innerkits/socket/init_socket_api.c new file mode 100755 index 0000000000000000000000000000000000000000..1e32b61cf643e939f6e450cc7cdea2051009144a --- /dev/null +++ b/interfaces/innerkits/socket/init_socket_api.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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. + */ + +#include "init_socket_api.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define N_DEC 10 +#define MAX_SOCKET_ENV_PREFIX_LEN 64 +#define MAX_SOCKET_DIR_LEN 128 + +static int GetControlFromEnv(char *path) +{ + if (path == NULL) { + return -1; + } + printf("GetControlFromEnv path is %s \n", path); + const char *val = getenv(path); + if (val == NULL) { + printf("test GetControlFromEnv val is null %d\n", errno); + return -1; + } + errno = 0; + int fd = strtol(val, NULL, N_DEC); + if (errno) { + return -1; + } + printf("test GetControlFromEnv fd is %d \n", fd); + if (fcntl(fd, F_GETFD) < 0) { + printf("test GetControlFromEnv errno %d \n", errno); + return -1; + } + return fd; +} + +int GetControlSocket(const char *name) +{ + if (name == NULL) { + return -1; + } + char path[MAX_SOCKET_ENV_PREFIX_LEN] = {0}; + snprintf(path, sizeof(path), OHOS_SOCKET_ENV_PREFIX"%s", name); + printf("test GetControlSocket path is %s \n", path); + int fd = GetControlFromEnv(path); + if (fd < 0) { + printf("GetControlFromEnv fail \n"); + return -1; + } + struct sockaddr_un addr; + socklen_t addrlen = sizeof(addr); + int ret = getsockname(fd, (struct sockaddr*)&addr, &addrlen); + if (ret < 0) { + printf("test GetControlSocket errno %d \n", errno); + return -1; + } + char sockDir[MAX_SOCKET_DIR_LEN] = {0}; + snprintf(sockDir, sizeof(sockDir), OHOS_SOCKET_DIR"/%s", name); + printf("test sockDir %s \n", sockDir); + printf("addr.sun_path %s \n", addr.sun_path); + if (strncmp(sockDir, addr.sun_path, strlen(sockDir)) == 0) { + return fd; + } + return -1; +} diff --git a/ohos.build b/ohos.build new file mode 100755 index 0000000000000000000000000000000000000000..c6cdbf243676ed456a75ccd691441b85613a6645 --- /dev/null +++ b/ohos.build @@ -0,0 +1,10 @@ +{ + "subsystem": "startup", + "parts": { + "init": { + "module_list": [ + "//base/startup/init_lite/services:startup_init" + ] + } + } +} diff --git a/services/BUILD.gn b/services/BUILD.gn index 286dadf956a3d7eea7fccce55f271a6564aad512..4ba148f7a628a29d5b5647006c5fffbe5d2515e7 100644 --- a/services/BUILD.gn +++ b/services/BUILD.gn @@ -25,12 +25,17 @@ if (defined(ohos_lite)) { "src/init_read_cfg.c", "src/init_service.c", "src/init_service_manager.c", + "src/init_import.c", "src/init_signal_handler.c", + "src/init_utils.c", + "src/init_service_socket.c", "src/main.c", + "src/init_capability.c", ] include_dirs = [ - "include", + "//base/startup/init_lite/services/include", + "//base/startup/init_lite/services/property/include", "//third_party/cJSON", "//third_party/bounds_checking_function/include", "//base/startup/syspara_lite/interfaces/kits", @@ -74,7 +79,6 @@ if (defined(ohos_lite)) { } } else { import("//build/ohos.gni") - ohos_executable("updaterueventd") { sources = [ "src/list.c", @@ -86,37 +90,66 @@ if (defined(ohos_lite)) { ] deps = [ "//third_party/bounds_checking_function:libsec_static" ] install_enable = true - part_name = "updater" + part_name = "init" } - ohos_executable("updaterinit") { + ohos_executable("init") { sources = [ "src/device.c", "src/init_adapter.c", "src/init_cmds.c", "src/init_jobs.c", + "src/init_log.c", "src/init_read_cfg.c", "src/init_service.c", "src/init_service_manager.c", + "src/init_import.c", "src/init_signal_handler.c", + "src/init_utils.c", + "src/init_service_socket.c", "src/main.c", + "src/init_capability.c", ] include_dirs = [ - "include", + "//base/startup/init_lite/services/include/property", + "//base/startup/init_lite/services/include/trigger", + "//base/startup/init_lite/services/include", + "//base/startup/init_lite/services/property/include", "//third_party/cJSON", "//third_party/bounds_checking_function/include", ] deps = [ + "//base/startup/init_lite/services/property:propertyserver", + "//base/startup/init_lite/services/property:propertyclient", + "//base/startup/init_lite/services/trigger:triggerservice", "//third_party/bounds_checking_function:libsec_static", "//third_party/cJSON:cjson_static", ] + if (use_musl) { + deps += [ + "//third_party/mksh:sh", + "//third_party/toybox:toybox" + ] + } install_enable = true - part_name = "updater" + part_name = "init" } ohos_prebuilt_etc("init.cfg") { source = "//device/hisilicon/hi3516dv300/updater/init.cfg" - relative_install_dir = "init" - subsystem_name = "updater" + part_name = "init" + } + + group("startup_init") { + deps = [ + ":init.cfg", + ":init", + ":updaterueventd", + "//base/startup/init_lite/services/trigger:triggerservice", + "//base/startup/init_lite/services/property:propertyserver", + "//base/startup/init_lite/services/property:propertyclient", + "//base/startup/init_lite/services/property:setparam", + "//base/startup/init_lite/services/property:getparam" + ] } } diff --git a/services/include/device.h b/services/include/device.h index 6739b2bc78280535403a06546405950ba2656f38..aebe7e07ea8a13c5fa560bc970dea597dfafab25 100755 --- a/services/include/device.h +++ b/services/include/device.h @@ -21,9 +21,11 @@ extern "C" { #endif #endif +#include void MountBasicFs(); void CreateDeviceNode(); +int MakeSocketDir(const char *path, mode_t mode); #ifdef __cplusplus #if __cplusplus diff --git a/services/include/init_adapter.h b/services/include/init_adapter.h index d6376df403dfb4b45bd829d6db4dc59af273ba56..c79ed05f9e73c80bdb264e1504a4d399af54ee0b 100755 --- a/services/include/init_adapter.h +++ b/services/include/init_adapter.h @@ -16,10 +16,10 @@ #ifndef BASE_STARTUP_INITLITE_ADAPTER_H #define BASE_STARTUP_INITLITE_ADAPTER_H -#ifdef __LINUX__ -#include -#else +#ifdef OHOS_LITE #include +#else +#include #endif #ifdef __cplusplus @@ -28,7 +28,7 @@ extern "C" { #endif #endif -#ifdef __LINUX__ +#ifndef OHOS_LITE /* Control the ambient capability set */ #ifndef PR_CAP_AMBIENT #define PR_CAP_AMBIENT 47 diff --git a/services/include/init_capability.h b/services/include/init_capability.h new file mode 100755 index 0000000000000000000000000000000000000000..842959e73e0cc043949640656e435d7aa01e8d26 --- /dev/null +++ b/services/include/init_capability.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef INIT_CAPABILITY_H +#define INIT_CAPABILITY_H + +#include "cJSON.h" +#include "init_service.h" + +int GetServiceCaps(const cJSON* curArrItem, Service* curServ); + +#endif + diff --git a/services/include/init_cmds.h b/services/include/init_cmds.h index da4b2b29303c20e14c6be76975e3846f91b7a4c7..d25d7384960f15b3489465207aba9a081108ec1d 100644 --- a/services/include/init_cmds.h +++ b/services/include/init_cmds.h @@ -23,9 +23,10 @@ extern "C" { #endif #define MAX_CMD_NAME_LEN 10 -#define MAX_CMD_CONTENT_LEN 128 -#define MAX_CMD_CNT_IN_ONE_JOB 30 - +#define MAX_CMD_CONTENT_LEN 256 +#define MAX_CMD_CNT_IN_ONE_JOB 200 +#define MAX_COPY_BUF_SIZE 256 +#define DEFAULT_COPY_ARGS_CNT 2 // one cmd line typedef struct { char name[MAX_CMD_NAME_LEN + 1]; @@ -35,6 +36,8 @@ typedef struct { void ParseCmdLine(const char* cmdStr, CmdLine* resCmd); void DoCmd(const CmdLine* curCmd); +void DoCmdByName(const char *name, const char *cmdContent); +const char *GetMatchCmd(const char *cmdStr); #ifdef __cplusplus #if __cplusplus } diff --git a/services/include/init_import.h b/services/include/init_import.h new file mode 100755 index 0000000000000000000000000000000000000000..48b6c01dd4ec79018b48140176110913bf140d59 --- /dev/null +++ b/services/include/init_import.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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. + */ + #ifndef BASE_STARTUP_INITLITE_IMPORT_H + #define BASE_STARTUP_INITLITE_IMPORT_H + #include "cJSON.h" + void ParseAllImports(cJSON *root); + #endif \ No newline at end of file diff --git a/services/include/init_jobs.h b/services/include/init_jobs.h index 5a6a6b3b75f21096a9660b586203f224b5c11bb5..7c7ffaed5cd53b53715f2317d128f7a9594f1e48 100644 --- a/services/include/init_jobs.h +++ b/services/include/init_jobs.h @@ -25,7 +25,7 @@ extern "C" { #endif #endif -#define MAX_JOB_NAME_LEN 32 +#define MAX_JOB_NAME_LEN 64 // one job, could have many cmd lines typedef struct { @@ -37,7 +37,7 @@ typedef struct { void ParseAllJobs(const cJSON* fileRoot); void DoJob(const char* jobName); void ReleaseAllJobs(); - +void DumpAllJobs(); #ifdef __cplusplus #if __cplusplus } diff --git a/services/include/init_log.h b/services/include/init_log.h new file mode 100755 index 0000000000000000000000000000000000000000..0a352b5c92e0fe86495e986817e346fb1c368ca0 --- /dev/null +++ b/services/include/init_log.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef INIT_LOG_H +#define INIT_LOG_H +#include +#include +#include + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +typedef enum StatupLogLevel { + STARTUP_DEBUG = 0, + STARTUP_INFO, + STARTUP_WARN, + STARTUP_ERROR, + STARTUP_FATAL +} StatupLogLevel; + +#define __FILE_NAME__ (strrchr((__FILE__), '/') ? strrchr((__FILE__), '/') + 1 : (__FILE__)) + +#ifdef OHOS_LITE +#define INIT_LOGE(format, ...) printf("%s %d: "format, __FILE_NAME__, __LINE__, ##__VA_ARGS__) +#define INIT_LOGW(format, ...) printf("%s %d: "format, __FILE_NAME__, __LINE__, ##__VA_ARGS__) +#define INIT_LOGI(format, ...) printf("%s %d: "format, __FILE_NAME__, __LINE__, ##__VA_ARGS__) +#define INIT_LOGD(format, ...) printf("%s %d: "format, __FILE_NAME__, __LINE__, ##__VA_ARGS__) +#else +#define INIT_LOGE(format, ...) printf("%s %d: "format, __FILE_NAME__, __LINE__, ##__VA_ARGS__) +#define INIT_LOGW(format, ...) printf("%s %d: "format, __FILE_NAME__, __LINE__, ##__VA_ARGS__) +#define INIT_LOGI(format, ...) printf("%s %d: "format, __FILE_NAME__, __LINE__, ##__VA_ARGS__) +#define INIT_LOGD(format, ...) printf("%s %d: "format, __FILE_NAME__, __LINE__, ##__VA_ARGS__) +void InitLog(int logLevel, const char *fileName, int line, const char *fmt, ...); +void Logger(StatupLogLevel level, const char *format, ...); +#endif + + +#define INIT_ERROR_CHECK(ret, statement, format, ...) \ + if (!(ret)) { \ + INIT_LOGE(format, ##__VA_ARGS__); \ + statement; \ + } + +#define INIT_CHECK_ONLY_RETURN(ret, statement) \ + if (!(ret)) { \ + statement; \ + } + +#ifdef SUPPORT_HILOG +#include "hilog/log.h" + +static constexpr OHOS::HiviewDFX::HiLogLabel STARTUP_LABEL = {LOG_CORE, 0, "STARTUP"}; + +StatupLogLevel level_; +int JudgeLevel(const StatupLogLevel level) { return return; } + +#define STARTUP_LOG(LEVEL, LABEL, Level, fmt, ...) \ + Logger(__FILE_NAME__, (__LINE__), fmt, ##__VA_ARGS__); \ + if (JudgeLevel(StatupLogLevel::LEVEL)) \ + OHOS::HiviewDFX::HiLog::Level(STARTUP_LABEL, "[%{public}s(%{public}d)] " fmt, \ + __FILE_NAME__, __LINE__, ##__VA_ARGS__) +#else +#define STARTUP_LOG(LEVEL, LABEL, Level, fmt, ...) \ + printf("[%s][%s:%d] " fmt "\n", LABEL, __FILE_NAME__, __LINE__, ##__VA_ARGS__); +#endif + +#define STARTUP_LOGI(LABEL, fmt, ...) STARTUP_LOG(STARTUP_INFO, LABEL, Info, fmt, ##__VA_ARGS__) +#define STARTUP_LOGE(LABEL, fmt, ...) STARTUP_LOG(STARTUP_ERROR, LABEL, Error, fmt, ##__VA_ARGS__) + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif + +#endif // INIT_LOG_H diff --git a/services/include/init_read_cfg.h b/services/include/init_read_cfg.h index c596128127076889cd03892406c40be50305d887..5f61c6009a9e4ae57eff189cd6d1ebd594a3b3ed 100644 --- a/services/include/init_read_cfg.h +++ b/services/include/init_read_cfg.h @@ -22,9 +22,12 @@ extern "C" { #endif #endif -#define INIT_CONFIGURATION_FILE "/etc/init.cfg" +#define INIT_CONFIGURATION_FILE "/init.cfg" +#define MAX_PATH_ARGS_CNT 20 +#define MAX_ONE_ARG_LEN 200 // max length of one param/path void InitReadCfg(); +void ParseInitCfg(const char *configFile); #ifdef __cplusplus #if __cplusplus diff --git a/services/include/init_service.h b/services/include/init_service.h index b6c7cfb328818d24c2cfc5e834edf78423286a99..ba4e046744cfbd2f80f6f2c243434e275bf68e3c 100755 --- a/services/include/init_service.h +++ b/services/include/init_service.h @@ -17,6 +17,8 @@ #define BASE_STARTUP_INITLITE_SERVICE_H #include +#include "init_cmds.h" +#include "init_service_socket.h" #ifdef __cplusplus #if __cplusplus @@ -34,8 +36,11 @@ extern "C" { #define SERVICE_ATTR_NEED_RESTART 0x004 // will restart in the near future #define SERVICE_ATTR_NEED_STOP 0x008 // will stop in reap #define SERVICE_ATTR_IMPORTANT 0x010 // will reboot if it crash +#define SERVICE_ATTR_CRITICAL 0x020 // critical, will reboot if it crash 4 times in 4 minutes +#define SERVICE_ATTR_DISABLED 0x040 // disabled #define MAX_SERVICE_NAME 32 +#define MAX_WRITEPID_FILES 100 #define CAP_NUM 2 @@ -43,12 +48,17 @@ extern "C" { typedef struct { uid_t uID; - gid_t *gIDs; - unsigned int gidsCnt; + gid_t *gIDArray; + int gIDCnt; unsigned int *caps; unsigned int capsCnt; } Perms; +struct OnRestartCmd { + CmdLine *cmdLine; + int cmdNum; +}; + typedef struct { char name[MAX_SERVICE_NAME + 1]; char** pathArgs; @@ -56,8 +66,13 @@ typedef struct { int pid; int crashCnt; time_t firstCrashTime; + int criticalCrashCnt; // count for critical + time_t firstCriticalCrashTime; // record for critical + char *writepidFiles[MAX_WRITEPID_FILES]; unsigned int attribute; Perms servPerm; + struct OnRestartCmd *onRestart; + struct ServiceSocket *socketCfg; } Service; int ServiceStart(Service *service); diff --git a/services/include/init_service_manager.h b/services/include/init_service_manager.h index 37e97d7af825e28114fab530ff6e2afa2e8cd5b7..0040f74df6bf3d8cd0e4f675283126b5098bf215 100644 --- a/services/include/init_service_manager.h +++ b/services/include/init_service_manager.h @@ -17,6 +17,7 @@ #define BASE_STARTUP_INITLITE_SERVICEMANAGER_H #include "init_service.h" +#include "cJSON.h" #ifdef __cplusplus #if __cplusplus @@ -24,10 +25,23 @@ extern "C" { #endif #endif +#define UID_STR_IN_CFG "uid" +#define GID_STR_IN_CFG "gid" +#define ONCE_STR_IN_CFG "once" +#define IMPORTANT_STR_IN_CFG "importance" +#define BIN_SH_NOT_ALLOWED "/bin/sh" +#define CRITICAL_STR_IN_CFG "critical" +#define DISABLED_STR_IN_CFG "disabled" + +#define MAX_SERVICES_CNT_IN_FILE 100 + void RegisterServices(Service* services, int servicesCnt); void StartServiceByName(const char* serviceName); +void StopServiceByName(const char* serviceName); void StopAllServices(); void ReapServiceByPID(int pid); +void ParseAllServices(const cJSON* fileRoot); +void DumpAllServices(); #ifdef __cplusplus #if __cplusplus diff --git a/services/include/init_service_socket.h b/services/include/init_service_socket.h new file mode 100755 index 0000000000000000000000000000000000000000..9fe5e1a80c4cac546f88c4b5981928b1aefafccb --- /dev/null +++ b/services/include/init_service_socket.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef INIT_SERVICE_SOCKET_ +#define INIT_SERVICE_SOCKET_ +#include +#include +#include +#include +#include + +struct ServiceSocket; +struct ServiceSocket +{ + char *name; // service name + int type; // socket type + uid_t uid; // uid + gid_t gid; // gid + bool passcred; // setsocketopt + mode_t perm; // Setting permissions + struct ServiceSocket *next; +}; + +int DoCreateSocket(struct ServiceSocket *sockopt); + +#endif diff --git a/services/include/init_utils.h b/services/include/init_utils.h new file mode 100755 index 0000000000000000000000000000000000000000..dfe3734f82f27f3a7773728644e2fcf9de52a13f --- /dev/null +++ b/services/include/init_utils.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef INIT_UTILS_H +#define INIT_UTILS_H + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +struct CmdArgs { + int argc; + char **argv; +}; + +struct CmdArgs* GetCmd(const char *cmdContent, const char *delim); +void FreeCmd(struct CmdArgs **cmd); +int DecodeUid(const char *name); +void CheckAndCreateDir(const char *fileName); +char* ReadFileToBuf(const char *configFile); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif +#endif // INIT_UTILS_H diff --git a/services/include/list.h b/services/include/list.h index ee2220cc078053150768ea0735f1b57a40c1b646..4aaf9dcf65bc02fe7b4e416c011995719c7bbb78 100755 --- a/services/include/list.h +++ b/services/include/list.h @@ -1,10 +1,10 @@ /* - * Copyright (c) 2020 Huawei Device Co., Ltd. + * Copyright (c) 2021 Huawei Device Co., Ltd. * 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 + * 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, @@ -22,10 +22,13 @@ extern "C" { #endif #endif -struct ListNode { +typedef struct ListNode { struct ListNode *next; struct ListNode *prev; -}; +} ListNode; + +#define ListEmpty(node) ((node).next == &(node) && (node).prev == &(node)) +#define ListEntry(ptr, type, member) (type *)((char *)(ptr) - offsetof(type, member)) void ListInit(struct ListNode *list); void ListAddTail(struct ListNode *list, struct ListNode *item); diff --git a/services/include/property/property.h b/services/include/property/property.h new file mode 100755 index 0000000000000000000000000000000000000000..537641b6334cbeadd0374ba2fd451303556b4539 --- /dev/null +++ b/services/include/property/property.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2020 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef BASE_STARTUP_PROPERTY_H +#define BASE_STARTUP_PROPERTY_H +#include +#include +#include + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +#define PROPERTY_VALUE_LEN_MAX 96 +typedef u_int32_t PropertyHandle; + +typedef struct { + u_int32_t serial; + PropertyHandle handle; + char value[PROPERTY_VALUE_LEN_MAX]; +} PropertyCacheNode; + +typedef const char *(*PropertyEvaluatePtr)(u_int32_t cacheCount, PropertyCacheNode *node); + +typedef struct { + pthread_mutex_t lock; + u_int32_t serial; + u_int32_t cacheCount; + PropertyEvaluatePtr evaluate; + PropertyCacheNode *cacheNode; +} PropertyCache; + +/** + * 对外接口 + * 设置属性,主要用于其他进程使用,通过管道修改属性。 + * + */ +int SystemSetParameter(const char *name, const char *value); + +/** + * 对外接口 + * 查询属性,主要用于其他进程使用,需要给定足够的内存保存属性。 + * 如果 value == null,获取value的长度 + * 否则value的大小认为是len + * + */ +int SystemGetParameter(const char *name, char *value, unsigned int *len); + +/** + * 对外接口 + * 查询属性,主要用于其他进程使用,需要给定足够的内存保存属性。 + * 如果 value == null,获取value的长度 + * 否则value的大小认为是len + * + */ +int SystemGetParameterName(PropertyHandle handle, char *name, unsigned int len); + +/** + * 对外接口 + * 遍历所有属性。 + * + */ +int SystemTraversalParameter(void (*traversalParameter)(PropertyHandle handle, void* cookie), void* cookie); + +/** + * 对外接口 + * 获取属性值。 + * + */ +int SystemGetParameterValue(PropertyHandle handle, char *value, unsigned int *len); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif +#endif \ No newline at end of file diff --git a/services/include/trigger/trigger.h b/services/include/trigger/trigger.h new file mode 100755 index 0000000000000000000000000000000000000000..d974ccfce1c58b446a4975db04ccb6fd5bd542af --- /dev/null +++ b/services/include/trigger/trigger.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef BASE_STARTUP_TRIGER_H +#define BASE_STARTUP_TRIGER_H +#include +#include "cJSON.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +typedef enum { + EVENT_PROPERTY, // 属性修改事件 + EVENT_BOOT +} EventType; + +void PostTrigger(EventType type, void *content, u_int32_t contentLen); + +void PostPropertyTrigger(const char *name, const char *value); + +void StartTriggerService(); + +int ParseTriggerConfig(cJSON *fileRoot); + +void DoTriggerExec(const char *content); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif +#endif \ No newline at end of file diff --git a/services/property/BUILD.gn b/services/property/BUILD.gn new file mode 100755 index 0000000000000000000000000000000000000000..92c6a37a503217c36c11cf6b7945b4f7c777b7cb --- /dev/null +++ b/services/property/BUILD.gn @@ -0,0 +1,106 @@ +# Copyright (c) 2020 Huawei Device Co., Ltd. +# 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. + +import("//build/ohos.gni") + +ohos_static_library("propertyserver") { + sources = [ + "manager/property_manager.c", + "manager/property_trie.c", + "manager/property_cache.c", + "service/property_service.c", + "service/property_persist.c", + "//base/startup/init_lite/services/src/init_utils.c", + ] + + include_dirs = [ + "include", + "//base/startup/init_lite/services/include/property", + "//base/startup/init_lite/services/include/trigger", + "//base/startup/init_lite/services/include", + "//third_party/libuv/include", + "//third_party/cJSON", + ] + + deps = [ + "//third_party/libuv:uv_static", + "//base/startup/init_lite/services/trigger:triggerservice", + "//third_party/bounds_checking_function:libsec_static", + ] + part_name = "init" + subsystem_name = "startup" +} + +ohos_static_library("propertyclient") { + sources = [ + "manager/property_manager.c", + "client/property_request.c", + "manager/property_trie.c", + "manager/property_cache.c", + "//base/startup/init_lite/services/src/init_utils.c", + ] + + include_dirs = [ + "include", + "//base/startup/init_lite/services/include/property", + "//base/startup/init_lite/services/include/trigger", + "//base/startup/init_lite/services/include", + "//third_party/libuv/include", + "//third_party/cJSON", + ] + + deps = [ + "//third_party/libuv:uv_static", + "//third_party/bounds_checking_function:libsec_static", + ] + part_name = "init" + subsystem_name = "startup" +} + +ohos_executable("getparam") { + sources = [ + "cmd/property_get.c", + ] + include_dirs = [ + "include", + "//base/startup/init_lite/services/include/property", + "//base/startup/init_lite/services/include/trigger", + "//base/startup/init_lite/services/include", + ] + deps = [ + "//base/startup/init_lite/services/property:propertyclient", + "//third_party/bounds_checking_function:libsec_static", + "//third_party/cJSON:cjson_static", + ] + install_enable = true + part_name = "init" +} + +ohos_executable("setparam") { + sources = [ + "cmd/property_set.c", + ] + include_dirs = [ + "include", + "//base/startup/init_lite/services/include/property", + "//base/startup/init_lite/services/include/trigger", + "//base/startup/init_lite/services/include", + ] + deps = [ + "//base/startup/init_lite/services/property:propertyclient", + "//third_party/bounds_checking_function:libsec_static", + "//third_party/cJSON:cjson_static", + ] + install_enable = true + part_name = "init" +} diff --git a/services/property/client/property_request.c b/services/property/client/property_request.c new file mode 100755 index 0000000000000000000000000000000000000000..22e55fb84ad6d38368add421d6e79a91d16529b8 --- /dev/null +++ b/services/property/client/property_request.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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. + */ + +#include "property_request.h" + +#include +#include +#include + +#include "property_manager.h" +#include "uv.h" + +#define LABEL "Client" +#define BUFFER_SIZE 200 +#define PropertyEntry(ptr, type, member) (type *)((char *)(ptr) - offsetof(type, member)) + +static PropertyWorkSpace g_propertyWorkSpaceReadOnly = {ATOMIC_VAR_INIT(0), {}, {}, {}}; + +static void OnWrite(uv_write_t* req, int status) +{ + PROPERTY_LOGI("OnWrite status %d", status); +} + +static void OnReceiveAlloc(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buf) +{ + // 这里需要按实际回复大小申请内存,不需要大内存 + buf->base = (char *)malloc(sizeof(ResponseMsg)); + buf->len = suggestedSize; + PROPERTY_LOGI("OnReceiveAlloc handle %p %zu", handle, suggestedSize); +} + +static void OnReceiveResponse(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) +{ + RequestNode *req = PropertyEntry(handle, RequestNode, handle); + PROPERTY_LOGI("OnReceiveResponse %p", handle); + if (nread <= 0 || buf == NULL || buf->base == NULL) { + free(buf->base); + uv_close((uv_handle_t*)handle, NULL); + uv_stop(req->loop); + return; + } + ResponseMsg *response = (ResponseMsg *)(buf->base); + PROPERTY_CHECK(response != NULL, return, "The response is null"); + PROPERTY_LOGI("OnReceiveResponse %p cmd %d result: %d", handle, response->type, response->result); + switch (response->type) { + case SET_PROPERTY: + req->result = response->result; + break; + default: + PROPERTY_LOGE("not supported the command: %d", response->type); + break; + } + + PROPERTY_LOGE("Close handle %p", handle); + free(buf->base); + uv_close((uv_handle_t*)handle, NULL); + uv_stop(req->loop); +} + +static void OnConnection(uv_connect_t *connect, int status) +{ + PROPERTY_CHECK(status >= 0, return, "Failed to conntect status %s", uv_strerror(status)); + uv_write_t wr; + RequestNode *request = PropertyEntry(connect, RequestNode, connect); + PROPERTY_LOGI("Connect to server handle %p", &(request->handle)); + uv_buf_t buf = uv_buf_init((char*)&request->msg, request->msg.contentSize + sizeof(request->msg)); + int ret = uv_write2(&wr, (uv_stream_t*)&(request->handle), &buf, 1, (uv_stream_t*)&(request->handle), OnWrite); + PROPERTY_CHECK(ret >= 0, return, "Failed to uv_write2 porperty"); + + // read result + ret = uv_read_start((uv_stream_t*)&(request->handle), OnReceiveAlloc, OnReceiveResponse); + PROPERTY_CHECK(ret >= 0, return, "Failed to uv_read_start response"); +} + +static int StartRequest(int cmd, RequestNode *request) +{ + PROPERTY_CHECK(request != NULL, return -1, "Invalid request"); + request->result = -1; + request->msg.type = cmd; + request->loop = uv_loop_new(); + uv_pipe_init(request->loop, &request->handle, 1); + uv_pipe_connect(&request->connect, &request->handle, PIPE_NAME, OnConnection); + uv_run(request->loop, UV_RUN_DEFAULT); + int result = request->result; + free(request); + return result; +} + +int SystemSetParameter(const char *name, const char *value) +{ + PROPERTY_CHECK(name != NULL && value != NULL, return -1, "Invalid param"); + int ret = CheckPropertyName(name, 0); + PROPERTY_CHECK(ret == 0, return ret, "Illegal property name"); + + PROPERTY_LOGI("StartRequest %s", name); + u_int32_t msgSize = sizeof(RequestMsg) + strlen(name) + strlen(value) + 2; + RequestNode *request = (RequestNode *)malloc(sizeof(RequestNode) + msgSize); + PROPERTY_CHECK(request != NULL, return -1, "Failed to malloc for connect"); + + // 带字符串结束符 + int contentSize = BuildPropertyContent(request->msg.content, msgSize - sizeof(RequestMsg), name, value); + PROPERTY_CHECK(contentSize > 0, return -1, "Failed to copy porperty"); + request->msg.contentSize = contentSize; + return StartRequest(SET_PROPERTY, request); +} + +int SystemGetParameter(const char *name, char *value, unsigned int *len) +{ + PROPERTY_CHECK(name != NULL && len != NULL, return -1, "The name or value is null"); + InitPropertyWorkSpace(&g_propertyWorkSpaceReadOnly, 1, NULL); + + PropertyHandle handle = 0; + int ret = ReadPropertyWithCheck(&g_propertyWorkSpaceReadOnly, name, &handle); + PROPERTY_CHECK(ret == 0, return ret, "Can not get param for %s", name); + return ReadPropertyValue(&g_propertyWorkSpaceReadOnly, handle, value, len); +} + +int SystemGetParameterName(PropertyHandle handle, char *name, unsigned int len) +{ + PROPERTY_CHECK(name != NULL && handle != 0, return -1, "The name is null"); + InitPropertyWorkSpace(&g_propertyWorkSpaceReadOnly, 1, NULL); + return ReadPropertyName(&g_propertyWorkSpaceReadOnly, handle, name, len); +} + +int SystemGetParameterValue(PropertyHandle handle, char *value, unsigned int *len) +{ + PROPERTY_CHECK(len != NULL && handle != 0, return -1, "The value is null"); + InitPropertyWorkSpace(&g_propertyWorkSpaceReadOnly, 1, NULL); + return ReadPropertyValue(&g_propertyWorkSpaceReadOnly, handle, value, len); +} + +int SystemTraversalParameter(void (*traversalParameter)(PropertyHandle handle, void* cookie), void* cookie) +{ + PROPERTY_CHECK(traversalParameter != NULL, return -1, "The param is null"); + InitPropertyWorkSpace(&g_propertyWorkSpaceReadOnly, 1, NULL); + return TraversalProperty(&g_propertyWorkSpaceReadOnly, traversalParameter, cookie); +} + +const char *SystemDetectPropertyChange(PropertyCache *cache, + PropertyEvaluatePtr evaluate, u_int32_t count, const char *properties[][2]) +{ + PROPERTY_CHECK(cache != NULL && evaluate != NULL && properties != NULL, return NULL, "The param is null"); + return DetectPropertyChange(&g_propertyWorkSpaceReadOnly, cache, evaluate, count, properties); +} diff --git a/services/property/cmd/property_get.c b/services/property/cmd/property_get.c new file mode 100755 index 0000000000000000000000000000000000000000..f2d49d7be6e7adacd668cc90eb129487c11a56a4 --- /dev/null +++ b/services/property/cmd/property_get.c @@ -0,0 +1,54 @@ +/* +* Copyright (c) 2021 Huawei Device Co., Ltd. +* 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. +*/ + +#include +#include +#include "property.h" + +#define HELP_PARAM "--help" +#define BUFFER_SIZE 256 + +static void ProcessParam(PropertyHandle handle, void* cookie) +{ + SystemGetParameterName(handle, (char*)cookie, BUFFER_SIZE); + u_int32_t size = BUFFER_SIZE; + SystemGetParameterValue(handle, ((char*)cookie) + BUFFER_SIZE, &size); + printf("\t%s=%s \n", (char*)cookie, ((char*)cookie) + BUFFER_SIZE); +} + +int main(int argc, char* argv[]) +{ + if (argc == 1) { // 显示所有的记录 + char value[BUFFER_SIZE + BUFFER_SIZE] = {0}; + SystemTraversalParameter(ProcessParam, (void*)value); + return 0; + } + if (argc == 2 && strncmp(argv[1], HELP_PARAM, strlen(HELP_PARAM)) == 0) { // 显示帮助 + printf("usage: getprop NAME VALUE\n"); + return 0; + } + if (argc != 2) { + printf("usage: getprop NAME VALUE\n"); + return 0; + } + char value[BUFFER_SIZE] = {0}; + u_int32_t size = BUFFER_SIZE; + int ret = SystemGetParameter(argv[1], value, &size); + if (ret == 0) { + printf("getparm %s %s \n", argv[1], value); + } else { + printf("getparm %s %s fail\n", argv[1], value); + } +} diff --git a/services/property/cmd/property_set.c b/services/property/cmd/property_set.c new file mode 100755 index 0000000000000000000000000000000000000000..173a2e6aaa27dc160977e17629cab49039f95554 --- /dev/null +++ b/services/property/cmd/property_set.c @@ -0,0 +1,39 @@ +/* +* Copyright (c) 2021 Huawei Device Co., Ltd. +* 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. +*/ + +#include +#include + +#include "property.h" + +#define HELP_PARAM "--help" + +int main(int argc, char* argv[]) +{ + if (argc == 1 || argc > 3) { + printf("setparm: Need 2 arguments (see \"setparm --help\")\n"); + return 0; + } + if (argc == 2 && strncmp(argv[1], HELP_PARAM, strlen(HELP_PARAM)) == 0) { + printf("usage: setprop NAME VALUE\n"); + return 0; + } + int ret = SystemSetParameter(argv[1], argv[2]); + if (ret == 0) { + printf("setparm %s %s success\n", argv[1], argv[2]); + } else { + printf("setparm %s %s fail\n", argv[1], argv[2]); + } +} diff --git a/services/property/include/property_manager.h b/services/property/include/property_manager.h new file mode 100755 index 0000000000000000000000000000000000000000..8324b08cb42d78d0fe8a5f7570a6507f8417d71c --- /dev/null +++ b/services/property/include/property_manager.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2020 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef BASE_STARTUP_PROPERTY_MANAGER_H +#define BASE_STARTUP_PROPERTY_MANAGER_H +#include +#include + +#include "init_log.h" +#include "property.h" +#include "property_trie.h" +#include "securec.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +typedef enum { + PROPERTY_CODE_INVALID_PARAM = 100, + PROPERTY_CODE_INVALID_NAME, + PROPERTY_CODE_INVALID_VALUE, + PROPERTY_CODE_REACHED_MAX, + PROPERTY_CODE_PERMISSION_DENIED, + PROPERTY_CODE_READ_ONLY_PROPERTY, + PROPERTY_CODE_NOT_SUPPORT, + PROPERTY_CODE_ERROR_MAP_FILE, + PROPERTY_CODE_NOT_FOUND_PROP, + PROPERTY_CODE_NOT_INIT +} PROPERTY_CODE; + +#define IS_READY_ONLY(name) strncmp((name), "ro.", strlen("ro.")) == 0 +#define LABEL_STRING_LEN 128 + +#ifdef STARTUP_LOCAL +#define PIPE_NAME "/tmp/propertyservice.sock" +#define PROPERTY_STORAGE_PATH "/media/sf_ubuntu/test/__properties__/property_storage" +#define PROPERTY_PERSIST_PATH "/media/sf_ubuntu/test/property/persist_properties.property" +#define PROPERTY_INFO_PATH "/media/sf_ubuntu/test/__properties__/property_info" +#else +#define PIPE_NAME "/dev/unix/socket/PropertyService" +#define PROPERTY_STORAGE_PATH "/dev/__properties__/property_storage" +#define PROPERTY_PERSIST_PATH "/data/property/persist_properties.property" +#define PROPERTY_INFO_PATH "/dev/__properties__/property_info" +#endif + +#define SUBSTR_INFO_NAME 0 +#define SUBSTR_INFO_LABEL 1 +#define SUBSTR_INFO_TYPE 2 +#define SUBSTR_INFO_MAX 4 + +#define WORKSPACE_FLAGS_INIT 0x01 +#define WORKSPACE_FLAGS_LOADED 0x02 + +#define PROPERTY_LOGI(fmt, ...) STARTUP_LOGI(LABEL, fmt, ##__VA_ARGS__) +#define PROPERTY_LOGE(fmt, ...) STARTUP_LOGE(LABEL, fmt, ##__VA_ARGS__) + +#define PROPERTY_CHECK(retCode, exper, ...) \ + if (!(retCode)) { \ + PROPERTY_LOGE(__VA_ARGS__); \ + exper; \ + } + +#define futex(addr1, op, val, rel, addr2, val3) \ + syscall(SYS_futex, addr1, op, val, rel, addr2, val3) +#define futex_wait_always(addr1) \ + syscall(SYS_futex, addr1, FUTEX_WAIT, *(int*)(addr1), 0, 0, 0) +#define futex_wake_single(addr1) \ + syscall(SYS_futex, addr1, FUTEX_WAKE, 1, 0, 0, 0) + +typedef struct UserCred { + pid_t pid; + uid_t uid; + gid_t gid; +} UserCred; + +typedef struct { + char label[LABEL_STRING_LEN]; + UserCred cred; +} PropertySecurityLabel; + +typedef struct PropertyAuditData { + const UserCred *cr; + const char *name; +} PropertyAuditData; + +typedef struct { + atomic_uint_least32_t flags; + WorkSpace propertyLabelSpace; + WorkSpace propertySpace; + PropertySecurityLabel label; +} PropertyWorkSpace; + +typedef struct { + atomic_uint_least32_t flags; + WorkSpace persistWorkSpace; +} PropertyPersistWorkSpace; + +typedef struct { + char value[128]; +} SubStringInfo; + +int InitPropertyWorkSpace(PropertyWorkSpace *workSpace, int onlyRead, const char *context); +void ClosePropertyWorkSpace(PropertyWorkSpace *workSpace); + +int ReadPropertyWithCheck(PropertyWorkSpace *workSpace, const char *name, PropertyHandle *handle); +int ReadPropertyValue(PropertyWorkSpace *workSpace, PropertyHandle handle, char *value, u_int32_t *len); +int ReadPropertyName(PropertyWorkSpace *workSpace, PropertyHandle handle, char *name, u_int32_t len); +u_int32_t ReadPropertySerial(PropertyWorkSpace *workSpace, PropertyHandle handle); + +int AddProperty(WorkSpace *workSpace, const char *name, const char *value); +int WriteProperty(WorkSpace *workSpace, const char *name, const char *value); +int WritePropertyWithCheck(PropertyWorkSpace *workSpace, + const PropertySecurityLabel *srcLabel, const char *name, const char *value); +int WritePropertyInfo(PropertyWorkSpace *workSpace, SubStringInfo *info, int subStrNumber); + +int CheckPropertyValue(WorkSpace *workSpace, const TrieDataNode *node, const char *name, const char *value); +int CheckPropertyName(const char *name, int propInfo); +int CanReadProperty(PropertyWorkSpace *workSpace, u_int32_t labelIndex, const char *name); +int CanWriteProperty(PropertyWorkSpace *workSpace, + const PropertySecurityLabel *srcLabel, const TrieDataNode *node, const char *name, const char *value); + +int CheckMacPerms(PropertyWorkSpace *workSpace, + const PropertySecurityLabel *srcLabel, const char *name, u_int32_t labelIndex); +int CheckControlPropertyPerms(PropertyWorkSpace *workSpace, + const PropertySecurityLabel *srcLabel, const char *name, const char *value); + +int GetSubStringInfo(const char *buff, u_int32_t buffLen, char delimiter, SubStringInfo *info, int subStrNumber); +int BuildPropertyContent(char *content, u_int32_t contentSize, const char *name, const char *value); +PropertyWorkSpace *GetPropertyWorkSpace(); + +typedef void (*TraversalParamPtr)(PropertyHandle handle, void* context); +typedef struct { + TraversalParamPtr traversalParamPtr; + void *context; +} PropertyTraversalContext; +int TraversalProperty(PropertyWorkSpace *workSpace, TraversalParamPtr walkFunc, void *cookie); + +int InitPersistPropertyWorkSpace(const char *context); +int RefreshPersistProperties(PropertyWorkSpace *workSpace, const char *context); +void ClosePersistPropertyWorkSpace(); +int WritePersistProperty(const char *name, const char *value); + +const char *DetectPropertyChange(PropertyWorkSpace *workSpace, PropertyCache *cache, + PropertyEvaluatePtr evaluate, u_int32_t count, const char *properties[][2]); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif +#endif \ No newline at end of file diff --git a/services/property/include/property_request.h b/services/property/include/property_request.h new file mode 100755 index 0000000000000000000000000000000000000000..4b85810c41e53892e89b208d087354d827a4652a --- /dev/null +++ b/services/property/include/property_request.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2020 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef BASE_STARTUP_PROPERTY_REQUEST_H +#define BASE_STARTUP_PROPERTY_REQUEST_H + +#include +#include "property.h" +#include "property_manager.h" + +#include "uv.h" +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +typedef enum RequestType { + SET_PROPERTY, + GET_PROPERTY, +} RequestType; + +typedef struct { + PropertySecurityLabel securitylabel; + RequestType type; + int contentSize; + char content[0]; +} RequestMsg; + +typedef struct { + RequestType type; + int result; + int contentSize; + char content[0]; +} ResponseMsg; + +typedef struct { + uv_loop_t *loop; + uv_connect_t connect; + uv_pipe_t handle; + int result; + RequestMsg msg; +} RequestNode; + +typedef struct { + uv_write_t writer; + ResponseMsg msg; +} ResponseNode; + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif +#endif \ No newline at end of file diff --git a/services/property/include/property_service.h b/services/property/include/property_service.h new file mode 100755 index 0000000000000000000000000000000000000000..f5925f72a51bd5fb01b7c1cc2325d8174a22b96d --- /dev/null +++ b/services/property/include/property_service.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2020 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef BASE_STARTUP_PROPERTY_SERVICE_H +#define BASE_STARTUP_PROPERTY_SERVICE_H +#include +#include "property.h" +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +/** + * Init 接口 + * 初始化属性服务 + * + */ +void InitPropertyService(); + +/** + * Init 接口 + * 加载默认的属性值 + * + */ +int LoadDefaultProperty(const char *fileName); + +/** + * Init 接口 + * 安全使用,加载属性的信息,包括selinux label 等 + * + */ +int LoadPropertyInfo(const char *fileName); + +/** + * Init 接口 + * 启动属性服务,在main启动的最后调用,阻赛当前线程 + * + */ +int StartPropertyService(); + +/** + * Init 接口 + * 设置属性,主要用于其他进程使用,通过管道修改属性 + * + */ +int SystemWriteParameter(const char *name, const char *value); + +/** + * Init 接口 + * 查询属性。 + * + */ +int SystemReadParameter(const char *name, char *value, unsigned int *len); + +/** + * Init 接口 + * 遍历属性。 + * + */ +int SystemTraversalParameters(void (*traversalParameter)(PropertyHandle handle, void* cookie), void* cookie); + +/** + * Init 接口 + * 加载默认属性。 + * + */ +int LoadPersistProperties(); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif +#endif \ No newline at end of file diff --git a/services/property/include/property_trie.h b/services/property/include/property_trie.h new file mode 100755 index 0000000000000000000000000000000000000000..fe9d86a9163d2476343714499748f5aff1e70bb6 --- /dev/null +++ b/services/property/include/property_trie.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2020 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef BASE_STARTUP_PROPERTY_TRIE_H +#define BASE_STARTUP_PROPERTY_TRIE_H +#include +#include +#include +#include +#include + +#include "init_log.h" +#include "property.h" +#include "securec.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +#define PROPERTY_WORKSPACE_MAX 64*1024 + +#define TRIE_SERIAL_DIRTY_OFFSET 1 +#define TRIE_SERIAL_KEY_LEN_OFFSET 24 +#define TRIE_SERIAL_DATA_LEN_OFFSET 16 + +#define FILENAME_LEN_MAX 255 +#define TRIE_DATA_LEN_MAX 128 + +#define NODE_INDEX unsigned int + +#define TRIE_NODE_HEADER \ + atomic_uint_least32_t serial; \ + NODE_INDEX left; \ + NODE_INDEX right; + +#define DATA_ENTRY_KEY_LEN(entry) (entry)->dataLength >> TRIE_SERIAL_KEY_LEN_OFFSET +#define DATA_ENTRY_DATA_LEN(entry) (((entry)->dataLength >> TRIE_SERIAL_DATA_LEN_OFFSET) & 0x00FF) +#define DATA_ENTRY_DIRTY(serial) ((serial) & 1) + +#define FUTEX_WAIT 0 +#define FUTEX_WAKE 1 +#define __futex(ftx, op, value, timeout, bitset) \ + syscall(__NR_futex, ftx, op, value, timeout, NULL, bitset) +#define futex_wake(ftx, count) __futex(ftx, FUTEX_WAKE, count, NULL, 0) +#define futex_wait(ftx, value, timeout) __futex(ftx, FUTEX_WAIT, value, timeout, 0) + +typedef struct { + TRIE_NODE_HEADER; + char key[0]; +} TrieNode; + +typedef struct { + TRIE_NODE_HEADER; + NODE_INDEX child; + NODE_INDEX labelIndex; + NODE_INDEX dataIndex; + char key[0]; +} TrieDataNode; + +typedef struct { + atomic_uint_least32_t serial; + atomic_uint_least32_t dataLength; + char data[0]; +} DataEntry; + +typedef struct { + atomic_uint_least32_t serial; + u_int32_t currOffset; + u_int32_t firstNode; + u_int32_t dataSize; + u_int32_t reserved_[28]; + char data[0]; +} WorkArea; + +struct WorkSpace_; +typedef u_int32_t (*AllocTrieNodePtr)(struct WorkSpace_ *workSpace, const char *key, u_int32_t keyLen); +typedef int (*CompareTrieNodePtr)(TrieNode *node, const char *key2, u_int32_t key2Len); + +typedef struct WorkSpace_ { + char fileName[FILENAME_LEN_MAX + 1]; + WorkArea *area; + TrieNode *rootNode; + AllocTrieNodePtr allocTrieNode; + CompareTrieNodePtr compareTrieNode; +} WorkSpace; + +u_int32_t GetWorkSpaceSerial(WorkSpace *workSpace); +int CompareTrieNode(TrieNode *node, const char *key, u_int32_t keyLen); +u_int32_t AllocateTrieNode(WorkSpace *workSpace, const char *key, u_int32_t keyLen); +int CompareTrieDataNode(TrieNode *node, const char *key, u_int32_t keyLen); +u_int32_t AllocateTrieDataNode(WorkSpace *workSpace, const char *key, u_int32_t keyLen); + +u_int32_t GetTrieNodeOffset(WorkSpace *workSpace, const TrieNode *current); +TrieNode *GetTrieNode(WorkSpace *workSpace, NODE_INDEX *index); +u_int32_t GetTrieKeyLen(TrieNode *current); +void SaveIndex(NODE_INDEX *index, u_int32_t offset); +TrieDataNode *AddTrieDataNode(WorkSpace *workSpace, const char *key, u_int32_t keyLen); +TrieDataNode *AddToSubTrie(WorkSpace *workSpace, TrieDataNode *dataNode, const char *key, u_int32_t keyLen); +TrieDataNode *FindSubTrie(WorkSpace *workSpace, TrieDataNode *dataNode, const char *key, u_int32_t keyLen); +TrieDataNode *FindTrieDataNode(WorkSpace *workSpace, const char *key, u_int32_t keyLen, int matchPrefix); + +TrieNode *AddTrieNode(WorkSpace *workSpace, TrieNode *root, const char *key, u_int32_t keyLen); +TrieNode *FindTrieNode(WorkSpace *workSpace, TrieNode *tree, const char *key, u_int32_t keyLen); + +int InitWorkSpace_(WorkSpace *workSpace, int mode, int prot, u_int32_t spaceSize, int readOnly); +int InitPersistWorkSpace(const char *fileName, WorkSpace *workSpace); +int InitWorkSpace(const char *fileName, WorkSpace *workSpace, int onlyRead); +void CloseWorkSpace(WorkSpace *workSpace); + +typedef int (*TraversalTrieNodePtr)(WorkSpace *workSpace, TrieNode *node, void *cookie); +int TraversalTrieNode(WorkSpace *workSpace, TrieNode *root, TraversalTrieNodePtr walkFunc, void *cookie); +int TraversalTrieDataNode(WorkSpace *workSpace, TrieDataNode *current, TraversalTrieNodePtr walkFunc, void *cookie); + +u_int32_t AddData(WorkSpace *workSpace, const char *key, u_int32_t keyLen, const char *value, u_int32_t valueLen); +int UpdateDataValue(DataEntry *entry, const char *value); +int GetDataName(const DataEntry *entry, char *name, u_int32_t len); +int GetDataValue(const DataEntry *entry, char *value, u_int32_t len); +u_int32_t GetDataSerial(const DataEntry *entry); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif +#endif // BASE_STARTUP_PROPERTY_TRIE_H \ No newline at end of file diff --git a/services/property/manager/property_cache.c b/services/property/manager/property_cache.c new file mode 100755 index 0000000000000000000000000000000000000000..82f6e236e73f4d6dfc8a7aaa3c9edbf6098de25e --- /dev/null +++ b/services/property/manager/property_cache.c @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2020 Huawei Device Co., Ltd. + * 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. + */ +#include "property.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "property_manager.h" + +#define LABEL "Manager" +#define MAX_PROPERT_IN_WATCH 5 +#define NORMAL_MEMORY_FOR_PROPERTY_CACHE 4 * 1024 +static WorkSpace g_workSpace; +static pthread_mutex_t cacheLock = PTHREAD_MUTEX_INITIALIZER; + +static int InitNormalMemory(WorkSpace *workSpace, u_int32_t spaceSize) +{ + PROPERTY_CHECK(workSpace != NULL, return -1, "Invalid param"); + if (workSpace->area != NULL) { + return 0; + } + + void *areaAddr = (void *)mmap(NULL, spaceSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_POPULATE | MAP_ANON, -1, 0); + PROPERTY_CHECK(areaAddr != MAP_FAILED, return -1, "Failed to map memory error %s", strerror(errno)); + workSpace->area = (WorkArea*)areaAddr; + atomic_init(&workSpace->area->serial, 0); + workSpace->area->dataSize = spaceSize; + workSpace->area->currOffset = sizeof(WorkArea); + PROPERTY_LOGI("InitNormalMemory success, currOffset %u firstNode %u dataSize %u", + workSpace->area->currOffset, workSpace->area->firstNode, workSpace->area->dataSize); + return 0; +} + +static PropertyCacheNode *AllocPropertyCacheNode(WorkSpace *workSpace, u_int32_t size) +{ + PROPERTY_CHECK(workSpace != NULL, return 0, "Invalid param"); + PROPERTY_CHECK((workSpace->area->currOffset + size) < workSpace->area->dataSize, return 0, + "Failed to allocate currOffset %d, dataSize %d", workSpace->area->currOffset, workSpace->area->dataSize); + PropertyCacheNode *cache = (PropertyCacheNode *)(workSpace->area->data + workSpace->area->currOffset); + workSpace->area->currOffset += size; + return cache; +} + +static int CreatePropertyCache(PropertyCache *cache, PropertyWorkSpace *workSpace, PropertyEvaluatePtr evaluate) +{ + PROPERTY_CHECK(cache != NULL && evaluate != NULL, return -1, "Invalid param"); + if (cache->cacheNode != NULL) { + return 0; + } + int ret = InitNormalMemory(&g_workSpace, NORMAL_MEMORY_FOR_PROPERTY_CACHE); + PROPERTY_CHECK(ret == 0, return -1, "Failed to init normal memory"); + pthread_mutex_init(&cache->lock, NULL); + cache->serial = GetWorkSpaceSerial(&workSpace->propertySpace); + cache->cacheCount = 0; + cache->evaluate = evaluate; + cache->cacheNode = (PropertyCacheNode *)AllocPropertyCacheNode(&g_workSpace, + sizeof(PropertyCache) * MAX_PROPERT_IN_WATCH); + PROPERTY_CHECK(cache->cacheNode != NULL, return -1, "Failed to malloc memory"); + return 0; +} + +static int AddPropertyNode(PropertyCache *cache, PropertyWorkSpace *workSpace, const char *name, const char *defValue) +{ + PROPERTY_CHECK(cache != NULL && name != NULL, return -1, "Invalid param"); + PROPERTY_CHECK(cache->cacheCount < MAX_PROPERT_IN_WATCH, return -1, "Full property in cache"); + + PropertyCacheNode *cacheNode = &cache->cacheNode[cache->cacheCount++]; + int ret = memcpy_s(cacheNode->value, sizeof(cacheNode->value), defValue, strlen(defValue)); + PROPERTY_CHECK(ret == 0, return -1, "Failed to copy default value"); + + ret = ReadPropertyWithCheck(workSpace, name, &cacheNode->handle); + PROPERTY_CHECK(ret == 0, return -1, "Failed to read property"); + cacheNode->serial = ReadPropertySerial(workSpace, cacheNode->handle); + return ret; +} + +static int CheckCacheNode(PropertyWorkSpace *workSpace, PropertyCacheNode *cacheNode) +{ + return cacheNode && ReadPropertySerial(workSpace, cacheNode->handle) != cacheNode->serial; +} + +static void RefreshCacheNode(PropertyWorkSpace *workSpace, PropertyCacheNode *cacheNode) +{ + cacheNode->serial = ReadPropertySerial(workSpace, cacheNode->handle); + u_int32_t len = sizeof(cacheNode->value); + ReadPropertyValue(workSpace, cacheNode->handle, cacheNode->value, &len); +} + +static const char *TestPropertyCache(PropertyCache *cache, PropertyWorkSpace *workSpace) +{ + int changeDetected; + if (pthread_mutex_trylock(&cache->lock)) { + return cache->evaluate(cache->cacheCount, cache->cacheNode); + } + if (GetWorkSpaceSerial(&workSpace->propertySpace) != cache->serial) { + changeDetected = 1; + } + for (u_int32_t i = 0; (i < cache->cacheCount) && changeDetected == 0; i++) { + changeDetected = CheckCacheNode(workSpace, &cache->cacheNode[i]); + } + if (changeDetected) { + for (u_int32_t i = 0; i < cache->cacheCount; i++) { + RefreshCacheNode(workSpace, &cache->cacheNode[i]); + } + cache->serial = GetWorkSpaceSerial(&workSpace->propertySpace); + } + pthread_mutex_unlock(&cache->lock); + + return cache->evaluate(cache->cacheCount, cache->cacheNode); +} + +const char *DetectPropertyChange(PropertyWorkSpace *workSpace, PropertyCache *cache, + PropertyEvaluatePtr evaluate, u_int32_t count, const char *properties[][2]) +{ + pthread_mutex_lock(&cacheLock); + while (cache->cacheCount == 0) { + int ret = CreatePropertyCache(cache, workSpace, evaluate); + PROPERTY_CHECK(ret == 0, break, "Failed to create cache"); + for (u_int32_t i = 0; i < count; i++) { + ret = AddPropertyNode(cache, workSpace, properties[i][0], properties[i][1]); + PROPERTY_CHECK(ret == 0, break, "Failed to add property cache"); + } + PROPERTY_CHECK(ret == 0, break, "Failed to add property cache"); + } + pthread_mutex_unlock(&cacheLock); + return TestPropertyCache(cache, workSpace); +} diff --git a/services/property/manager/property_manager.c b/services/property/manager/property_manager.c new file mode 100755 index 0000000000000000000000000000000000000000..64b6229086020589358aaa6e017b0a885f910c36 --- /dev/null +++ b/services/property/manager/property_manager.c @@ -0,0 +1,532 @@ +/* + * Copyright (c) 2020 Huawei Device Co., Ltd. + * 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. + */ + +#include "property_manager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LABEL "Manager" + +#ifdef PROPERTY_SUPPORT_SELINUX +static int SelinuxAuditCallback(void *data, + __attribute__((unused))security_class_t class, char *msgBuf, size_t msgSize) +{ + PropertyAuditData *auditData = (PropertyAuditData*)(data); + PROPERTY_CHECK(auditData != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + PROPERTY_CHECK(auditData->name != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + PROPERTY_CHECK(auditData->cr != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + snprintf(msgBuf, msgSize, "property=%s pid=%d uid=%d gid=%d", + auditData->name, auditData->cr->pid, auditData->cr->uid, auditData->cr->gid); + return 0; +} +#endif + +int InitPropertyWorkSpace(PropertyWorkSpace *workSpace, int onlyRead, const char *context) +{ + u_int32_t flags = atomic_load_explicit(&workSpace->flags, memory_order_relaxed); + PROPERTY_LOGI("InitPropertyWorkSpace flags %x", flags); + if ((flags & WORKSPACE_FLAGS_INIT) == WORKSPACE_FLAGS_INIT) { + return 0; + } + +#ifdef PROPERTY_SUPPORT_SELINUX + union selinux_callback cb; + cb.func_audit = SelinuxAuditCallback; + selinux_set_callback(SELINUX_CB_AUDIT, cb); +#endif + +#ifdef PROPERTY_SUPPORT_SELINUX + if (context && fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0) != 0) { + PROPERTY_LOGI("fsetxattr context %s fail", context); + } +#endif + PROPERTY_CHECK(workSpace != NULL, return PROPERTY_CODE_INVALID_NAME, "Invalid param"); + workSpace->propertySpace.compareTrieNode = CompareTrieDataNode; + workSpace->propertySpace.allocTrieNode = AllocateTrieDataNode; + int ret = InitWorkSpace(PROPERTY_STORAGE_PATH, &workSpace->propertySpace, onlyRead); + + // 保存selinux 使用的属性标签值 + workSpace->propertyLabelSpace.compareTrieNode = CompareTrieNode; // 必须先设置 + workSpace->propertyLabelSpace.allocTrieNode = AllocateTrieNode; + ret |= InitWorkSpace(PROPERTY_INFO_PATH, &workSpace->propertyLabelSpace, onlyRead); + atomic_store_explicit(&workSpace->flags, WORKSPACE_FLAGS_INIT, memory_order_release); + return ret; +} + +void ClosePropertyWorkSpace(PropertyWorkSpace *workSpace) +{ + CloseWorkSpace(&workSpace->propertySpace); + CloseWorkSpace(&workSpace->propertyLabelSpace); + atomic_store_explicit(&workSpace->flags, 0, memory_order_release); +} + +int WritePropertyInfo(PropertyWorkSpace *workSpace, SubStringInfo *info, int subStrNumber) +{ + PROPERTY_CHECK(workSpace != NULL && info != NULL && subStrNumber > SUBSTR_INFO_NAME, + return PROPERTY_CODE_INVALID_PARAM, "Failed to check param"); + const char *name = info[SUBSTR_INFO_NAME].value; + char *label = NULL; + char *type = NULL; + if (subStrNumber >= SUBSTR_INFO_LABEL) { + label = info[SUBSTR_INFO_LABEL].value; + } else { + label = "u:object_r:default_prop:s0"; + } + if (subStrNumber >= SUBSTR_INFO_TYPE) { + type = info[SUBSTR_INFO_TYPE].value; + } else { + type = "string"; + } + + int ret = CheckPropertyName(name, 1); + PROPERTY_CHECK(ret == 0, return ret, "Illegal property name %s", name); + + // 先保存标签值 + TrieNode *node = AddTrieNode(&workSpace->propertyLabelSpace, + workSpace->propertyLabelSpace.rootNode, label, strlen(label)); + PROPERTY_CHECK(node != NULL, return PROPERTY_CODE_REACHED_MAX, "Failed to add label"); + u_int32_t offset = GetTrieNodeOffset(&workSpace->propertyLabelSpace, node); + + TrieDataNode *dataNode = AddTrieDataNode(&workSpace->propertySpace, name, strlen(name)); + PROPERTY_CHECK(dataNode != NULL, return PROPERTY_CODE_REACHED_MAX, "Failed to add node %s", name); + TrieNode *entry = (TrieNode *)GetTrieNode(&workSpace->propertyLabelSpace, &dataNode->labelIndex); + if (entry != 0) { // 已经存在label + PROPERTY_LOGE("Has been set label %s old label %s new label: %s", name, entry->key, label); + } + SaveIndex(&dataNode->labelIndex, offset); + return 0; +} + +int AddProperty(WorkSpace *workSpace, const char *name, const char *value) +{ + PROPERTY_CHECK(workSpace != NULL && name != NULL && value != NULL, + return PROPERTY_CODE_INVALID_PARAM, "Failed to check param"); + + TrieDataNode *node = AddTrieDataNode(workSpace, name, strlen(name)); + PROPERTY_CHECK(node != NULL, return PROPERTY_CODE_REACHED_MAX, "Failed to add node"); + DataEntry *entry = (DataEntry *)GetTrieNode(workSpace, &node->dataIndex); + if (entry == NULL) { + u_int32_t offset = AddData(workSpace, name, strlen(name), value, strlen(value)); + PROPERTY_CHECK(offset != 0, return PROPERTY_CODE_REACHED_MAX, "Failed to allocate name %s", name); + SaveIndex(&node->dataIndex, offset); + } + PROPERTY_LOGI("AddProperty trie %p dataIndex %u name %s", node, node->dataIndex, name); + atomic_store_explicit(&workSpace->area->serial, + atomic_load_explicit(&workSpace->area->serial, memory_order_relaxed) + 1, + memory_order_release); + futex_wake(&workSpace->area->serial, INT_MAX); + return 0; +} + +int UpdateProperty(WorkSpace *workSpace, u_int32_t *dataIndex, const char *name, const char *value) +{ + PROPERTY_CHECK(workSpace != NULL && name != NULL && value != NULL, + return PROPERTY_CODE_INVALID_PARAM, "Failed to check param"); + + DataEntry *entry = (DataEntry *)GetTrieNode(workSpace, dataIndex); + if (entry == NULL) { + PROPERTY_LOGE("Failed to update property value %s %u", name, *dataIndex); + return -1; + } + u_int32_t keyLen = DATA_ENTRY_KEY_LEN(entry); + PROPERTY_CHECK(keyLen == strlen(name), return PROPERTY_CODE_INVALID_NAME, "Failed to check name len %s", name); + + u_int32_t serial = atomic_load_explicit(&entry->serial, memory_order_relaxed); + serial |= 1; + atomic_store_explicit(&entry->serial, serial | 1, memory_order_release); + atomic_thread_fence(memory_order_release); + + int ret = UpdateDataValue(entry, value); + if (ret != 0) { + PROPERTY_LOGE("Failed to update property value %s %s", name, value); + } + atomic_store_explicit(&entry->serial, serial + 1, memory_order_release); + futex_wake(&entry->serial, INT_MAX); + + atomic_store_explicit(&workSpace->area->serial, + atomic_load_explicit(&workSpace->area->serial, memory_order_relaxed) + 1, memory_order_release); + futex_wake(&workSpace->area->serial, INT_MAX); + return ret; +} + +DataEntry *FindProperty(WorkSpace *workSpace, const char *name) +{ + PROPERTY_CHECK(workSpace != NULL, return NULL, "Failed to check param"); + PROPERTY_CHECK(name != NULL, return NULL, "Invalid param size"); + + TrieDataNode *node = FindTrieDataNode(workSpace, name, strlen(name), 0); + if (node != NULL) { + return (DataEntry *)GetTrieNode(workSpace, &node->dataIndex); + } + return NULL; +} + +int WritePropertyWithCheck(PropertyWorkSpace *workSpace, + const PropertySecurityLabel *srcLabel, const char *name, const char *value) +{ + PROPERTY_CHECK(workSpace != NULL && srcLabel != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + PROPERTY_CHECK(value != NULL && name != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + + u_int32_t flags = atomic_load_explicit(&workSpace->flags, memory_order_relaxed); + if ((flags & WORKSPACE_FLAGS_INIT) != WORKSPACE_FLAGS_INIT) { + return PROPERTY_CODE_NOT_INIT; + } + + int ret = CheckPropertyName(name, 0); + PROPERTY_CHECK(ret == 0, return ret, "Illegal property name %s", name); + + // 取最长匹配的属性的label + TrieDataNode *propertInfo = FindTrieDataNode(&workSpace->propertySpace, name, strlen(name), 1); + ret = CanWriteProperty(workSpace, srcLabel, propertInfo, name, value); + PROPERTY_CHECK(ret == 0, return ret, "Permission to write property %s", name); + + return WriteProperty(&workSpace->propertySpace, name, value); +} + +int WriteProperty(WorkSpace *workSpace, const char *name, const char *value) +{ + PROPERTY_CHECK(workSpace != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + PROPERTY_CHECK(value != NULL && name != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + + TrieDataNode *node = FindTrieDataNode(workSpace, name, strlen(name), 0); + int ret = CheckPropertyValue(workSpace, node, name, value); + PROPERTY_CHECK(ret == 0, return ret, "Invalid value %s %s", name, value); + + if (node != NULL) { + return UpdateProperty(workSpace, &node->dataIndex, name, value); + } + return AddProperty(workSpace, name, value); +} + +int ReadPropertyWithCheck(PropertyWorkSpace *workSpace, const char *name, PropertyHandle *handle) +{ + PROPERTY_CHECK(handle != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + PROPERTY_CHECK(workSpace != NULL && name != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + u_int32_t flags = atomic_load_explicit(&workSpace->flags, memory_order_relaxed); + if ((flags & WORKSPACE_FLAGS_INIT) != WORKSPACE_FLAGS_INIT) { + return PROPERTY_CODE_NOT_INIT; + } + + *handle = 0; + // 取最长匹配 + TrieDataNode *propertyInfo = FindTrieDataNode(&workSpace->propertySpace, name, strlen(name), 1); + int ret = CanReadProperty(workSpace, propertyInfo == NULL ? 0 : propertyInfo->labelIndex, name); + PROPERTY_CHECK(ret == 0, return ret, "Permission to read property %s", name); + + // 查找结点 + TrieDataNode *node = FindTrieDataNode(&workSpace->propertySpace, name, strlen(name), 0); + if (node != NULL && node->dataIndex != 0) { + PROPERTY_LOGI("ReadPropertyWithCheck trie %p dataIndex %u name %s", node, node->dataIndex, name); + *handle = node->dataIndex; + return 0; + } + return PROPERTY_CODE_NOT_FOUND_PROP; +} + +int ReadPropertyValue(PropertyWorkSpace *workSpace, PropertyHandle handle, char *value, u_int32_t *len) +{ + PROPERTY_CHECK(workSpace != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + u_int32_t flags = atomic_load_explicit(&workSpace->flags, memory_order_relaxed); + if ((flags & WORKSPACE_FLAGS_INIT) != WORKSPACE_FLAGS_INIT) { + return PROPERTY_CODE_NOT_INIT; + } + DataEntry *entry = (DataEntry *)GetTrieNode(&workSpace->propertySpace, &handle); + if (entry == NULL) { + return -1; + } + + if (value == NULL) { + *len = DATA_ENTRY_DATA_LEN(entry);; + return 0; + } + + while (1) { + u_int32_t serial = GetDataSerial(entry); + int ret = GetDataValue(entry, value, *len); + PROPERTY_CHECK(ret == 0, return ret, "Failed to get value"); + atomic_thread_fence(memory_order_acquire); + if (serial == atomic_load_explicit(&(entry->serial), memory_order_relaxed)) { + return 0; + } + } + return 0; +} + +int ReadPropertyName(PropertyWorkSpace *workSpace, PropertyHandle handle, char *name, u_int32_t len) +{ + PROPERTY_CHECK(workSpace != NULL && name != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + DataEntry *entry = (DataEntry *)GetTrieNode(&workSpace->propertySpace, &handle); + if (entry == NULL) { + return -1; + } + return GetDataValue(entry, name, len); +} + +u_int32_t ReadPropertySerial(PropertyWorkSpace *workSpace, PropertyHandle handle) +{ + PROPERTY_CHECK(workSpace != NULL, return 0, "Invalid param"); + DataEntry *entry = (DataEntry *)GetTrieNode(&workSpace->propertySpace, &handle); + if (entry == NULL) { + return 0; + } + return GetDataSerial(entry); +} + +int CheckControlPropertyPerms(PropertyWorkSpace *workSpace, + const PropertySecurityLabel *srcLabel, const char *name, const char *value) +{ + PROPERTY_CHECK(srcLabel != NULL && name != NULL && value != NULL, + return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + + char * ctrlName[] = { + "ctl.start", "ctl.stop", "ctl.restart" + }; + size_t size1 = strlen("ctl.") + strlen(value); + size_t size2 = strlen(name) + strlen(value) + 1; + size_t size = ((size1 > size2) ? size1 : size2) + 1; + char *legacyName = (char*)malloc(size); + PROPERTY_CHECK(legacyName != NULL, return PROPERTY_CODE_INVALID_PARAM, "Failed to alloc memory"); + + // We check the legacy method first but these properties are dontaudit, so we only log an audit + // if the newer method fails as well. We only do this with the legacy ctl. properties. + for (size_t i = 0; i < sizeof(ctrlName) / sizeof(char*); i++) { + if (strcmp(name, ctrlName[i]) == 0) { + // The legacy permissions model is that ctl. properties have their name ctl. and + // their value is the name of the service to apply that action to. Permissions for these + // actions are based on the service, so we must create a fake name of ctl. to + // check permissions. + int n = snprintf_s(legacyName, size, size, "ctl.%s", value); + PROPERTY_CHECK(n > 0, free(legacyName); return PROPERTY_CODE_INVALID_PARAM, "Failed to snprintf value"); + legacyName[n] = '\0'; + + TrieDataNode *node = FindTrieDataNode(&workSpace->propertySpace, legacyName, strlen(legacyName), 1); + int ret = CheckMacPerms(workSpace, srcLabel, legacyName, node == NULL ? 0 : node->labelIndex); + if (ret == 0) { + free(legacyName); + return 0; + } + break; + } + } + int n = snprintf_s(legacyName, size, size, "%s$%s", name, value); + PROPERTY_CHECK(n > 0, free(legacyName); return PROPERTY_CODE_INVALID_PARAM, "Failed to snprintf value"); + + TrieDataNode *node = FindTrieDataNode(&workSpace->propertySpace, name, strlen(name), 1); + int ret = CheckMacPerms(workSpace, srcLabel, name, node == NULL ? 0 : node->labelIndex); + free(legacyName); + return ret; +} + +int CheckPropertyName(const char *name, int propInfo) +{ + size_t nameLen = strlen(name); + if (nameLen >= PROPERTY_VALUE_LEN_MAX) { + return PROPERTY_CODE_INVALID_NAME; + } + + if (nameLen < 1 || name[0] == '.' || (!propInfo && name[nameLen - 1] == '.')) { + PROPERTY_LOGE("CheckPropertyName %s %d", name, propInfo); + return PROPERTY_CODE_INVALID_NAME; + } + + /* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */ + /* Don't allow ".." to appear in a property name */ + for (size_t i = 0; i < nameLen; i++) { + if (name[i] == '.') { + if (name[i - 1] == '.') { + return PROPERTY_CODE_INVALID_NAME; + } + continue; + } + if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') { + continue; + } + if (isalnum(name[i])) { + continue; + } + return PROPERTY_CODE_INVALID_NAME; + } + return 0; +} + +int CheckPropertyValue(WorkSpace *workSpace, const TrieDataNode *node, const char *name, const char *value) +{ + if (IS_READY_ONLY(name)) { + if (node != NULL && node->dataIndex != 0) { + PROPERTY_LOGE("Read-only property was already set %s", name); + return PROPERTY_CODE_READ_ONLY_PROPERTY; + } + } else { + // 限制非read only的属性,防止属性值修改后,原空间不能保存 + PROPERTY_CHECK(strlen(value) < PROPERTY_VALUE_LEN_MAX, + return PROPERTY_CODE_INVALID_VALUE, "Illegal property value"); + } + return 0; +} + +int CheckMacPerms(PropertyWorkSpace *workSpace, + const PropertySecurityLabel *srcLabel, const char *name, u_int32_t labelIndex) +{ +#ifdef PROPERTY_SUPPORT_SELINUX + PropertyAuditData auditData; + auditData.name = name; + auditData.cr = &srcLabel->cred; + + int ret = 0; + TrieNode *node = (TrieNode *)GetTrieNode(&workSpace->propertyLabelSpace, &labelIndex); + if (node != 0) { // 已经存在label + ret = selinux_check_access(srcLabel, node->key, "property_service", "set", &auditData); + } else { + ret = selinux_check_access(srcLabel, "u:object_r:default_prop:s0", "property_service", "set", &auditData); + } + return ret == 0 ? 0 : PROPERTY_CODE_PERMISSION_DENIED; +#else + return 0; +#endif +} + +int CanWriteProperty(PropertyWorkSpace *workSpace, + const PropertySecurityLabel *srcLabel, const TrieDataNode *node, const char *name, const char *value) +{ + PROPERTY_CHECK(workSpace != NULL && name != NULL && value != NULL && srcLabel != NULL, + return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + + if (strncmp(name, "ctl.", strlen("ctl.")) == 0) { // 处理ctrl TODO + return CheckControlPropertyPerms(workSpace, srcLabel, name, value); + } + + int ret = CheckMacPerms(workSpace, srcLabel, name, node == NULL ? 0 : node->labelIndex); + PROPERTY_CHECK(ret == 0, return ret, "SELinux permission check failed"); + return 0; +} + +int CanReadProperty(PropertyWorkSpace *workSpace, u_int32_t labelIndex, const char *name) +{ + PROPERTY_CHECK(workSpace != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + PROPERTY_CHECK(name != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); +#ifdef PROPERTY_SUPPORT_SELINUX + PropertyAuditData auditData; + auditData.name = name; + UserCred cr = {.pid = 0, .uid = 0, .gid = 0}; + auditData.cr = &cr; + + int ret = 0; + TrieNode *node = (TrieNode *)GetTrieNode(&workSpace->propertyLabelSpace, &labelIndex); + if (node != 0) { // 已经存在label + ret = selinux_check_access(&workSpace->context, node->key, "property_service", "read", &auditData); + } else { + ret = selinux_check_access(&workSpace->context, "selinux_check_access", "file", "read", &auditData); + } + return ret == 0 ? 0 : PROPERTY_CODE_PERMISSION_DENIED; +#else + return 0; +#endif +} + +int GetSubStringInfo(const char *buff, u_int32_t buffLen, char delimiter, SubStringInfo *info, int subStrNumber) +{ + size_t i = 0; + // 去掉开始的空格 + for (; i < strlen(buff); i++) { + if (!isspace(buff[i])) { + break; + } + } + // 过滤掉注释 + if (buff[i] == '#') { + return -1; + } + // 分割字符串 + int curr = 0; + int valueCurr = 0; + for (; i < buffLen; i++) { + if (buff[i] == '\n' || buff[i] == '\r') { + break; + } + if (buff[i] == delimiter && valueCurr != 0) { + info[curr].value[valueCurr] = '\0'; + valueCurr = 0; + curr++; + } else if (isspace(buff[i])) { // 无效字符,进行过滤 + continue; + } else { + if ((valueCurr + 1) >= (int)sizeof(info[curr].value)) { + continue; + } + info[curr].value[valueCurr++] = buff[i]; + } + if (curr >= subStrNumber) { + break; + } + } + if (valueCurr > 0) { + info[curr].value[valueCurr] = '\0'; + valueCurr = 0; + curr++; + } + return curr; +} + +int BuildPropertyContent(char *content, u_int32_t contentSize, const char *name, const char *value) +{ + PROPERTY_CHECK(name != NULL && value != NULL && content != NULL, return -1, "Invalid param"); + u_int32_t nameLen = (u_int32_t)strlen(name); + u_int32_t valueLen = (u_int32_t)strlen(value); + PROPERTY_CHECK(contentSize >= (nameLen + valueLen + 2), return -1, "Invalid content size %u", contentSize); + + int offset = 0; + int ret = memcpy_s(content + offset, contentSize - offset, name, nameLen); + offset += nameLen; + ret |= memcpy_s(content + offset, contentSize - offset, "=", 1); + offset += 1; + ret |= memcpy_s(content + offset, contentSize - offset, value, valueLen); + offset += valueLen; + content[offset] = '\0'; + PROPERTY_CHECK(ret == 0, return -1, "Failed to copy porperty"); + offset += 1; + return offset; +} + +int ProcessPropertTraversal(WorkSpace *workSpace, TrieNode *node, void *cookie) +{ + PropertyTraversalContext *context = (PropertyTraversalContext *)cookie; + TrieDataNode *current = (TrieDataNode *)node; + if (current == NULL) { + return 0; + } + if (current->dataIndex == 0) { + return 0; + } + context->traversalParamPtr(current->dataIndex, context->context); + return 0; +} + +int TraversalProperty(PropertyWorkSpace *workSpace, TraversalParamPtr walkFunc, void *cookie) +{ + PropertyTraversalContext context = { + walkFunc, cookie + }; + return TraversalTrieDataNode(&workSpace->propertySpace, + (TrieDataNode *)workSpace->propertySpace.rootNode, ProcessPropertTraversal, &context); +} \ No newline at end of file diff --git a/services/property/manager/property_trie.c b/services/property/manager/property_trie.c new file mode 100755 index 0000000000000000000000000000000000000000..a7995a9ae8b677515e8459e98b62aa4dc8923b40 --- /dev/null +++ b/services/property/manager/property_trie.c @@ -0,0 +1,536 @@ +/* + * Copyright (c) 2020 Huawei Device Co., Ltd. + * 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. + */ + +#include "property_trie.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "init_utils.h" +#include "property.h" +#include "property_manager.h" + +#define LABEL "Manager" + +int InitWorkSpace(const char *fileName, WorkSpace *workSpace, int onlyRead) +{ + PROPERTY_CHECK(fileName != NULL, return PROPERTY_CODE_INVALID_NAME, "Invalid param"); + PROPERTY_CHECK(workSpace != NULL, return PROPERTY_CODE_INVALID_NAME, "Invalid param"); + if (workSpace->area != NULL) { + return 0; + } + + int ret = memcpy_s(workSpace->fileName, FILENAME_LEN_MAX, fileName, strlen(fileName)); + PROPERTY_CHECK(ret == 0, return PROPERTY_CODE_INVALID_NAME, "Copy file %s fail ", fileName); + int openMode = 0; + int prot = PROT_READ; + if (onlyRead) { + openMode = O_RDONLY; + } else { + openMode = O_CREAT | O_RDWR | O_TRUNC; + prot = PROT_READ | PROT_WRITE; + } + ret = InitWorkSpace_(workSpace, openMode, prot, PROPERTY_WORKSPACE_MAX, onlyRead); + PROPERTY_CHECK(ret == 0, return ret, "Failed to init workspace %s", workSpace->fileName); + return ret; +} + +int InitPersistWorkSpace(const char *fileName, WorkSpace *workSpace) +{ + PROPERTY_CHECK(fileName != NULL, return PROPERTY_CODE_INVALID_NAME, "Invalid param"); + PROPERTY_CHECK(workSpace != NULL, return PROPERTY_CODE_INVALID_NAME, "Invalid param"); + if (workSpace->area != NULL) { + return 0; + } + + int ret = memcpy_s(workSpace->fileName, FILENAME_LEN_MAX, fileName, strlen(fileName)); + PROPERTY_CHECK(ret == 0, return PROPERTY_CODE_INVALID_NAME, "Copy file %s fail ", fileName); + + int flag = (access(fileName, F_OK) == 0) ? 1 : 0; + int openMode = (flag == 0) ? (O_CREAT | O_RDWR | O_TRUNC) : O_RDWR; + int prot = PROT_READ | PROT_WRITE; + ret = InitWorkSpace_(workSpace, openMode, prot, PROPERTY_WORKSPACE_MAX, flag); + PROPERTY_CHECK(ret == 0, return ret, "Failed to init workspace %s", workSpace->fileName); + return ret; +} + +int InitWorkSpace_(WorkSpace *workSpace, int mode, int prot, u_int32_t spaceSize, int readOnly) +{ + PROPERTY_CHECK(workSpace != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid fileName"); + PROPERTY_CHECK(workSpace->allocTrieNode != NULL, + return PROPERTY_CODE_INVALID_PARAM, "Invalid param %s", workSpace->fileName); + PROPERTY_CHECK(workSpace->compareTrieNode != NULL, + return PROPERTY_CODE_INVALID_PARAM, "Invalid param %s", workSpace->fileName); + PROPERTY_LOGI("InitWorkSpace %s ", workSpace->fileName); + CheckAndCreateDir(workSpace->fileName); + + int fd = open(workSpace->fileName, mode, 00777); //0444); + PROPERTY_CHECK(fd >= 0, return PROPERTY_CODE_INVALID_NAME, + "Open file %s fail error %s", workSpace->fileName, strerror(errno)); + + if (!readOnly) { + lseek(fd, spaceSize, SEEK_SET); + write(fd, "", 1); + } + void *areaAddr = (void *)mmap(NULL, spaceSize, prot, MAP_SHARED, fd, 0); + PROPERTY_CHECK(areaAddr != MAP_FAILED, close(fd); return PROPERTY_CODE_ERROR_MAP_FILE, + "Failed to map memory error %s", strerror(errno)); + close(fd); + + if (!readOnly) { + workSpace->area = (WorkArea*)areaAddr; + atomic_init(&workSpace->area->serial, 0); + workSpace->area->dataSize = spaceSize; + workSpace->area->currOffset = sizeof(WorkArea); + // 创建一个key为#的节点 + u_int32_t offset = workSpace->allocTrieNode(workSpace, "#", 1); + workSpace->area->firstNode = offset; + workSpace->rootNode = GetTrieNode(workSpace, &offset); + } else { + workSpace->area = (WorkArea*)areaAddr; + workSpace->rootNode = GetTrieNode(workSpace, &workSpace->area->firstNode); + } + PROPERTY_LOGI("InitWorkSpace success, currOffset %u firstNode %u dataSize %u", + workSpace->area->currOffset, workSpace->area->firstNode, workSpace->area->dataSize); + return 0; +} + +void CloseWorkSpace(WorkSpace *workSpace) +{ + PROPERTY_CHECK(workSpace != NULL && workSpace->area != NULL, return, "The workspace is null"); + munmap((char *)workSpace->area, workSpace->area->dataSize); + workSpace->area = NULL; +} + +u_int32_t GetWorkSpaceSerial(WorkSpace *workSpace) +{ + PROPERTY_CHECK(workSpace != NULL && workSpace->area != NULL, return 0, "The workspace is null"); + return (u_int32_t)workSpace->area->serial; +} + +u_int32_t AllocateTrieNode(WorkSpace *workSpace, const char *key, u_int32_t keyLen) +{ + u_int32_t len = keyLen + sizeof(TrieNode) + 1; + PROPERTY_CHECK((workSpace->area->currOffset + len) < workSpace->area->dataSize, return 0, + "Failed to allocate currOffset %d, dataSize %d", workSpace->area->currOffset, workSpace->area->dataSize); + TrieNode *node = (TrieNode*)(workSpace->area->data + workSpace->area->currOffset + len); + + atomic_init(&node->serial, ATOMIC_VAR_INIT(keyLen << TRIE_SERIAL_KEY_LEN_OFFSET)); + int ret = memcpy_s(node->key, keyLen, key, keyLen); + PROPERTY_CHECK(ret == 0, return 0, "Failed to copy key"); + node->key[keyLen] = '\0'; + node->left = 0; + node->right = 0; + u_int32_t offset = workSpace->area->currOffset; + workSpace->area->currOffset += len; + return offset; +} + +u_int32_t AllocateTrieDataNode(WorkSpace *workSpace, const char *key, u_int32_t keyLen) +{ + u_int32_t len = keyLen + sizeof(TrieDataNode) + 1; + PROPERTY_CHECK((workSpace->area->currOffset + len) < workSpace->area->dataSize, return 0, + "Failed to allocate currOffset %d, dataSize %d", workSpace->area->currOffset, workSpace->area->dataSize); + TrieDataNode *node = (TrieDataNode*)(workSpace->area->data + workSpace->area->currOffset); + + atomic_init(&node->serial, ATOMIC_VAR_INIT(keyLen << TRIE_SERIAL_KEY_LEN_OFFSET)); + int ret = memcpy_s(node->key, keyLen, key, keyLen); + PROPERTY_CHECK(ret == 0, return 0, "Failed to copy key"); + node->key[keyLen] = '\0'; + node->left = 0; + node->right = 0; + node->child = 0; + node->dataIndex = 0; + node->labelIndex = 0; + u_int32_t offset = workSpace->area->currOffset; + workSpace->area->currOffset += len; + return offset; +} + +TrieNode *GetTrieNode(WorkSpace *workSpace, NODE_INDEX *index) +{ + if (index == NULL) { + return NULL; + } + u_int32_t offset = *index; // atomic_load_explicit(¤t->children, memory_order_relaxed); + if (offset == 0 || offset > workSpace->area->dataSize) { + return NULL; + } + return (TrieNode*)(workSpace->area->data + offset); +} + +u_int32_t GetTrieKeyLen(TrieNode *current) +{ + return (current)->serial >> TRIE_SERIAL_KEY_LEN_OFFSET; +} + +u_int32_t GetTrieNodeOffset(WorkSpace *workSpace, const TrieNode *current) +{ + return (((char *)current) - workSpace->area->data); +} + +void SaveIndex(NODE_INDEX *index, u_int32_t offset) +{ + // atomic_store_explicit(¤t->children, new_offset, memory_order_release); + *index = offset; +} + +int CompareTrieDataNode(TrieNode *node, const char *key, u_int32_t keyLen) +{ + TrieDataNode *data = (TrieDataNode *)node; + u_int32_t len = GetTrieKeyLen((TrieNode *)data); + if (len > keyLen) { + return -1; + } else if (len < keyLen) { + return 1; + } + return strncmp(data->key, key, keyLen); +} + +int CompareTrieNode(TrieNode *node, const char *key, u_int32_t keyLen) +{ + u_int32_t len = GetTrieKeyLen(node); + if (len > keyLen) { + return -1; + } else if (len < keyLen) { + return 1; + } + return strncmp(node->key, key, keyLen); +} + +static void GetNextKey(const char **remainingKey, int *hasDot, char **subKey, u_int32_t *subKeyLen) +{ + *subKey = strchr(*remainingKey, '.'); + if (*subKey != NULL) { + if ((*subKey)[0] == '.') { + *hasDot = 1; + } + *subKeyLen = *subKey - *remainingKey; + } else { + *subKeyLen = strlen(*remainingKey); + } +} + +TrieDataNode *AddTrieDataNode(WorkSpace *workSpace, const char *key, u_int32_t keyLen) +{ + PROPERTY_CHECK(workSpace->allocTrieNode != NULL, return NULL, "Invalid param %s", key); + PROPERTY_CHECK(workSpace->compareTrieNode != NULL, return NULL, "Invalid param %s", key); + + const char *remainingKey = key; + TrieDataNode *current = (TrieDataNode *)workSpace->rootNode; + while (1) { + int hasDot = 0; + u_int32_t subKeyLen = 0; + char *subKey = NULL; + GetNextKey(&remainingKey, &hasDot, &subKey, &subKeyLen); + if (!subKeyLen) { + return NULL; + } + u_int32_t offset = subKey == NULL ? strlen(key) : subKey - key; + + if (current->child != 0) { // 如果child存在,则检查是否匹配 + TrieDataNode *next = (TrieDataNode*)GetTrieNode(workSpace, ¤t->child); + if (next != NULL && workSpace->compareTrieNode((TrieNode*)next, remainingKey, subKeyLen) == 0) { + current = next; + } else { // 不匹配,需要建立子树 + current = (TrieDataNode*)AddToSubTrie(workSpace, current, key, offset); + } + } else if (hasDot) { + u_int32_t offset = workSpace->allocTrieNode(workSpace, remainingKey, subKeyLen); + PROPERTY_CHECK(offset != 0, return NULL, "Failed to allocate key %s", key); + SaveIndex(¤t->child, offset); + current = (TrieDataNode*)GetTrieNode(workSpace, ¤t->child); + } else { + current = (TrieDataNode*)AddToSubTrie(workSpace, current, key, offset); + } + if (current == NULL) { + return NULL; + } + if (subKey == NULL || strcmp(subKey, ".") == 0) { + break; + } + remainingKey = subKey + 1; + } + return current; +} + +TrieDataNode *AddToSubTrie(WorkSpace *workSpace, TrieDataNode *dataNode, const char *key, u_int32_t keyLen) +{ + TrieDataNode *root = NULL; + int ret = workSpace->compareTrieNode((TrieNode *)dataNode, key, keyLen); + if (ret <= 0) { + root = (TrieDataNode *)GetTrieNode(workSpace, &dataNode->left); + if (root == NULL) { + u_int32_t offset = workSpace->allocTrieNode(workSpace, key, keyLen); + PROPERTY_CHECK(offset != 0, return NULL, "Failed to allocate key %s", key); + SaveIndex(&dataNode->left, offset); + return (TrieDataNode *)GetTrieNode(workSpace, &dataNode->left); + } + } else { + root = (TrieDataNode *)GetTrieNode(workSpace, &dataNode->right); + if (root == NULL) { + u_int32_t offset = workSpace->allocTrieNode(workSpace, key, keyLen); + PROPERTY_CHECK(offset != 0, return NULL, "Failed to allocate key %s", key); + SaveIndex(&dataNode->right, offset); + return (TrieDataNode *)GetTrieNode(workSpace, &dataNode->right); + } + } + return (TrieDataNode*)AddTrieNode(workSpace, (TrieNode*)root, key, keyLen); +} + +TrieNode *AddTrieNode(WorkSpace *workSpace, TrieNode *root, const char *key, u_int32_t keyLen) +{ + PROPERTY_CHECK(root != NULL, return NULL, "Invalid param %s", key); + TrieNode *current = root; + TrieNode *next = NULL; + while (1) { + if (current == NULL) { + return NULL; + } + int ret = workSpace->compareTrieNode(current, key, keyLen); + if (ret == 0) { + return current; + } + if (ret < 0) { + next = GetTrieNode(workSpace, ¤t->left); + if (next == NULL) { + u_int32_t offset = workSpace->allocTrieNode(workSpace, key, keyLen); + PROPERTY_CHECK(offset != 0, return NULL, "Failed to allocate key %s", key); + SaveIndex(¤t->left, offset); + return GetTrieNode(workSpace, ¤t->left); + } + } else { + next = GetTrieNode(workSpace, ¤t->right); + if (next == NULL) { + u_int32_t offset = workSpace->allocTrieNode(workSpace, key, keyLen); + PROPERTY_CHECK(offset != 0, return NULL, "Failed to allocate key %s", key); + SaveIndex(¤t->right, offset); + return GetTrieNode(workSpace, ¤t->right); + } + } + current = next; + } + return current; +} + +TrieDataNode *FindTrieDataNode(WorkSpace *workSpace, const char *key, u_int32_t keyLen, int matchPrefix) +{ + PROPERTY_CHECK(workSpace->allocTrieNode != NULL, return NULL, "Invalid param %s", key); + PROPERTY_CHECK(workSpace->compareTrieNode != NULL, return NULL, "Invalid param %s", key); + + const char *remainingKey = key; + TrieDataNode *matchNode = (TrieDataNode *)workSpace->rootNode; + TrieDataNode *current = (TrieDataNode *)workSpace->rootNode; + while (1) { + int hasDot = 0; + u_int32_t subKeyLen = 0; + char *subKey = NULL; + GetNextKey(&remainingKey, &hasDot, &subKey, &subKeyLen); + if (!subKeyLen) { + return matchPrefix ? matchNode : NULL; + } + u_int32_t offset = subKey == NULL ? strlen(key) : subKey - key; + + if (current->child != 0) { // 如果child存在,则检查是否匹配 + TrieDataNode *next = (TrieDataNode*)GetTrieNode(workSpace, ¤t->child); + if (next != NULL && workSpace->compareTrieNode((TrieNode*)next, remainingKey, subKeyLen) == 0) { + current = next; + } else { // 不匹配,搜索子树 + current = (TrieDataNode*)FindSubTrie(workSpace, current, key, offset); + } + } else { + current = (TrieDataNode*)FindSubTrie(workSpace, current, key, offset); + } + if (current == NULL) { + return matchPrefix ? matchNode : NULL; + } + matchNode = current; + if (subKey == NULL || strcmp(subKey, ".") == 0) { + break; + } + remainingKey = subKey + 1; + } + return matchPrefix ? matchNode : current; +} + +TrieDataNode *FindSubTrie(WorkSpace *workSpace, TrieDataNode *dataNode, const char *key, u_int32_t keyLen) +{ + TrieDataNode *root = NULL; + int ret = workSpace->compareTrieNode((TrieNode*)dataNode, key, keyLen); + if (ret <= 0) { + root = (TrieDataNode *)GetTrieNode(workSpace, &dataNode->left); + if (root == NULL) { + return NULL; + } + } else { + root = (TrieDataNode *)GetTrieNode(workSpace, &dataNode->right); + if (root == NULL) { + return NULL; + } + } + return (TrieDataNode*)FindTrieNode(workSpace, (TrieNode*)root, key, keyLen); +} + +TrieNode *FindTrieNode(WorkSpace *workSpace, TrieNode *root, const char *key, u_int32_t keyLen) +{ + PROPERTY_CHECK(root != NULL, return NULL, "Invalid param %s", key); + TrieNode *current = root; + TrieNode *next = NULL; + while (1) { + if (current == NULL) { + return NULL; + } + int ret = workSpace->compareTrieNode(current, key, keyLen); + if (ret == 0) { + return current; + } + if (ret < 0) { + next = GetTrieNode(workSpace, ¤t->left); + } else { + next = GetTrieNode(workSpace, ¤t->right); + } + if (next == NULL) { + return NULL; + } + current = next; + } + return current; +} + +int TraversalTrieDataNode(WorkSpace *workSpace, TrieDataNode *current, TraversalTrieNodePtr walkFunc, void* cookie) +{ + PROPERTY_CHECK(walkFunc != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + PROPERTY_CHECK(workSpace != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + if (current == NULL) { + return 0; + } + + while (1) { + TrieDataNode *child = NULL; + // 显示子树 + TraversalTrieDataNode(workSpace, (TrieDataNode*)GetTrieNode(workSpace, ¤t->left), walkFunc, cookie); + TraversalTrieDataNode(workSpace, (TrieDataNode*)GetTrieNode(workSpace, ¤t->right), walkFunc, cookie); + walkFunc(workSpace, (TrieNode *)current, cookie); + + if (current->child != 0) { // 如果child存在,则检查是否匹配 + child = (TrieDataNode*)GetTrieNode(workSpace, ¤t->child); + } + if (child == NULL) { + return 0; + } + current = child; + } + return 0; +} + +int TraversalTrieNode(WorkSpace *workSpace, TrieNode *root, TraversalTrieNodePtr walkFunc, void* cookie) +{ + PROPERTY_CHECK(workSpace != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + PROPERTY_CHECK(walkFunc != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + if (root == NULL) { + return 0; + } + TraversalTrieNode(workSpace, GetTrieNode(workSpace, &root->left), walkFunc, cookie); + TraversalTrieNode(workSpace, GetTrieNode(workSpace, &root->right), walkFunc, cookie); + walkFunc(workSpace, (TrieNode *)root, cookie); + return 0; +} + +u_int32_t AddData(WorkSpace *workSpace, const char *key, u_int32_t keyLen, const char *value, u_int32_t valueLen) +{ + PROPERTY_CHECK(workSpace != NULL, return 0, "Invalid param"); + PROPERTY_CHECK(key != NULL && value != NULL, return 0, "Invalid param"); + u_int32_t realLen = sizeof(DataEntry) + 1 + 1; + if (valueLen > PROPERTY_VALUE_LEN_MAX) { // value超过最大时,只能是只读属性,保存最大字符串 + realLen = keyLen + valueLen; + } else { + realLen = keyLen + PROPERTY_VALUE_LEN_MAX; // 属性保存,预留最大属性值长度 + } + PROPERTY_CHECK((workSpace->area->currOffset + realLen) < workSpace->area->dataSize, return 0, + "Failed to allocate currOffset %d, dataSize %d", workSpace->area->currOffset, workSpace->area->dataSize); + + DataEntry *node = (DataEntry*)(workSpace->area->data + workSpace->area->currOffset); + u_int32_t dataLength = keyLen << TRIE_SERIAL_KEY_LEN_OFFSET | valueLen << TRIE_SERIAL_DATA_LEN_OFFSET; + atomic_init(&node->serial, ATOMIC_VAR_INIT(0)); + atomic_init(&node->dataLength, ATOMIC_VAR_INIT(dataLength)); + + int ret = memcpy_s(node->data, keyLen, key, keyLen); + ret |= memcpy_s(node->data + keyLen + 1, valueLen, value, valueLen); + PROPERTY_CHECK(ret == 0, return 0, "Failed to copy key"); + node->data[keyLen] = '='; + node->data[keyLen + 1 + valueLen] = '\0'; + u_int32_t offset = workSpace->area->currOffset; + workSpace->area->currOffset += realLen; + return offset; +} + +int UpdateDataValue(DataEntry *entry, const char *value) +{ + PROPERTY_CHECK(entry != NULL && value != NULL, return PROPERTY_CODE_INVALID_PARAM, "Failed to check param"); + int ret = PROPERTY_CODE_INVALID_VALUE; + u_int32_t keyLen = DATA_ENTRY_KEY_LEN(entry); + u_int32_t valueLen = strlen(value); + u_int32_t oldLen = DATA_ENTRY_DATA_LEN(entry); + if (oldLen < PROPERTY_VALUE_LEN_MAX && valueLen < PROPERTY_VALUE_LEN_MAX) { + PROPERTY_LOGE("Old value %s new value %s", entry->data + keyLen + 1, value); + ret = memcpy_s(entry->data + keyLen + 1, PROPERTY_VALUE_LEN_MAX, value, valueLen + 1); + PROPERTY_CHECK(ret == 0, return PROPERTY_CODE_INVALID_VALUE, "Failed to copy value"); + u_int32_t dataLength = keyLen << TRIE_SERIAL_KEY_LEN_OFFSET | valueLen << TRIE_SERIAL_DATA_LEN_OFFSET; + atomic_store_explicit(&entry->dataLength, dataLength, memory_order_release); + } + return ret; +} + +u_int32_t GetDataSerial(const DataEntry *entry) +{ + u_int32_t serial = atomic_load_explicit(&entry->serial, memory_order_acquire); + while (DATA_ENTRY_DIRTY(serial)) { + futex_wait(&entry->serial, serial, NULL); + serial = atomic_load_explicit(&entry->serial, memory_order_acquire); + } + return serial; +} + +int GetDataName(const DataEntry *entry, char *name, u_int32_t len) +{ + PROPERTY_CHECK(entry != NULL && name != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + u_int32_t keyLen = DATA_ENTRY_KEY_LEN(entry); + PROPERTY_CHECK(len > keyLen, return -1, "Invalid param size"); + int ret = memcpy_s(name, len, entry->data, keyLen); + PROPERTY_CHECK(ret == 0, return PROPERTY_CODE_INVALID_PARAM, "Failed to copy name"); + name[keyLen] = '\0'; + return ret; +} + +int GetDataValue(const DataEntry *entry, char *value, u_int32_t len) +{ + PROPERTY_CHECK(entry != NULL && value != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + u_int32_t keyLen = DATA_ENTRY_KEY_LEN(entry); + u_int32_t valueLen = DATA_ENTRY_DATA_LEN(entry); + PROPERTY_CHECK(len > valueLen, return PROPERTY_CODE_INVALID_PARAM, "Invalid value len %u %u", len, valueLen); + int ret = memcpy_s(value, len, entry->data + keyLen + 1, valueLen); + PROPERTY_CHECK(ret == 0, return PROPERTY_CODE_INVALID_PARAM, "Failed to copy value"); + value[valueLen] = '\0'; + return ret; +} diff --git a/services/property/service/property_persist.c b/services/property/service/property_persist.c new file mode 100755 index 0000000000000000000000000000000000000000..39dd410ec7a27b6818925311f02a55718bfa3854 --- /dev/null +++ b/services/property/service/property_persist.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2020 Huawei Device Co., Ltd. + * 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. + */ +#include "property.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "property_manager.h" +#include "property_trie.h" + +#define LABEL "Manager" +#define MAX_BUFF 256 + +typedef struct { + WorkSpace *workSpace; + WorkSpace *persistWorkSpace; + char *buffer; +} PersistContext; + +static PropertyPersistWorkSpace g_persistWorkSpace = {ATOMIC_VAR_INIT(0), }; + +static int ProcessPropertTraversal(WorkSpace *workSpace, TrieNode *node, void *cookie) +{ + PROPERTY_CHECK(workSpace != 0 && node != NULL && cookie != NULL, return -1, "Invalid param"); + TrieDataNode *current = (TrieDataNode *)node; + if (current == NULL || current->dataIndex == 0) { + return 0; + } + DataEntry *entry = (DataEntry *)GetTrieNode(workSpace, ¤t->dataIndex); + if (entry == NULL) { + return -1; + } + PersistContext *persistContext = (PersistContext *)cookie; + int ret = GetDataName(entry, persistContext->buffer, MAX_BUFF); + if (strncmp(persistContext->buffer, "persist.", strlen("persist.")) != 0) { + return 0; + } + ret |= GetDataValue(entry, persistContext->buffer + MAX_BUFF, MAX_BUFF); + if (ret == 0) { // 只支持新建 + PROPERTY_LOGI("Insert new persist property from normal property %s", persistContext->buffer); + ret = AddProperty(persistContext->persistWorkSpace, persistContext->buffer, persistContext->buffer + MAX_BUFF); + } + PROPERTY_CHECK(ret == 0, return ret, "Failed to add persist property"); + return ret; +} + +static int ProcessPersistPropertTraversal(WorkSpace *workSpace, TrieNode *node, void *cookie) +{ + TrieDataNode *current = (TrieDataNode *)node; + if (current == NULL || current->dataIndex == 0) { + return 0; + } + DataEntry *entry = (DataEntry *)GetTrieNode(workSpace, ¤t->dataIndex); + if (entry == NULL) { + return -1; + } + PersistContext *persistContext = (PersistContext *)cookie; + int ret = GetDataName(entry, persistContext->buffer, MAX_BUFF); + ret |= GetDataValue(entry, persistContext->buffer + MAX_BUFF, MAX_BUFF); + if (ret == 0) { + PROPERTY_LOGI("update normal property %s %s from persist property ", + persistContext->buffer, persistContext->buffer + MAX_BUFF); + ret = WriteProperty(persistContext->workSpace, persistContext->buffer, persistContext->buffer + MAX_BUFF); + } + PROPERTY_CHECK(ret == 0, return ret, "Failed to add persist property"); + return ret; +} + +int InitPersistPropertyWorkSpace(const char *context) +{ + u_int32_t flags = atomic_load_explicit(&g_persistWorkSpace.flags, memory_order_relaxed); + PROPERTY_LOGI("InitPersistPropertyWorkSpace flags %x", flags); + if ((flags & WORKSPACE_FLAGS_INIT) == WORKSPACE_FLAGS_INIT) { + return 0; + } + g_persistWorkSpace.persistWorkSpace.compareTrieNode = CompareTrieDataNode; + g_persistWorkSpace.persistWorkSpace.allocTrieNode = AllocateTrieDataNode; + int ret = InitPersistWorkSpace(PROPERTY_PERSIST_PATH, &g_persistWorkSpace.persistWorkSpace); + atomic_store_explicit(&g_persistWorkSpace.flags, WORKSPACE_FLAGS_INIT, memory_order_release); + return ret; +} + +int RefreshPersistProperties(PropertyWorkSpace *workSpace, const char *context) +{ + u_int32_t flags = atomic_load_explicit(&g_persistWorkSpace.flags, memory_order_relaxed); + if ((flags & WORKSPACE_FLAGS_INIT) != WORKSPACE_FLAGS_INIT) { + int ret = InitPersistPropertyWorkSpace(context); + PROPERTY_CHECK(ret == 0, return ret, "Failed to init persist property"); + flags = atomic_load_explicit(&g_persistWorkSpace.flags, memory_order_relaxed); + } + + if ((flags & WORKSPACE_FLAGS_LOADED) == WORKSPACE_FLAGS_LOADED) { + return 0; + } + + // 申请临时的缓存,用于数据读取 + char *buffer = (char *)malloc(MAX_BUFF + MAX_BUFF); + PROPERTY_CHECK(buffer != NULL, return -1, "Failed to malloc memory for property"); + PersistContext persistContext = { + &workSpace->propertySpace, &g_persistWorkSpace.persistWorkSpace, buffer + }; + + // 遍历当前的属性,并把persist的写入 + int ret = TraversalTrieDataNode(&workSpace->propertySpace, + (TrieDataNode *)workSpace->propertySpace.rootNode, ProcessPropertTraversal, &persistContext); + + // 修改默认属性值 + ret = TraversalTrieDataNode(&g_persistWorkSpace.persistWorkSpace, + (TrieDataNode *)g_persistWorkSpace.persistWorkSpace.rootNode, ProcessPersistPropertTraversal, &persistContext); + + atomic_store_explicit(&g_persistWorkSpace.flags, flags | WORKSPACE_FLAGS_LOADED, memory_order_release); + free(buffer); + return ret; +} + +void ClosePersistPropertyWorkSpace() +{ + CloseWorkSpace(&g_persistWorkSpace.persistWorkSpace); + atomic_store_explicit(&g_persistWorkSpace.flags, 0, memory_order_release); +} + +int WritePersistProperty(const char *name, const char *value) +{ + PROPERTY_CHECK(value != NULL && name != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + + u_int32_t flags = atomic_load_explicit(&g_persistWorkSpace.flags, memory_order_relaxed); + if ((flags & WORKSPACE_FLAGS_LOADED) != WORKSPACE_FLAGS_LOADED) { + return 0; + } + + if (strncmp(name, "persist.", strlen("persist.")) != 0) { + return 0; + } + return WriteProperty(&g_persistWorkSpace.persistWorkSpace, name, value); +} diff --git a/services/property/service/property_service.c b/services/property/service/property_service.c new file mode 100755 index 0000000000000000000000000000000000000000..8cdf02c747aad6b8df5ca9dfe69198ca56292f43 --- /dev/null +++ b/services/property/service/property_service.c @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2020 Huawei Device Co., Ltd. + * 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. + */ + +#include "property_service.h" + +#include +#include +#include +#include + +#include "property.h" +#include "property_manager.h" +#include "property_request.h" +#include "trigger.h" + +#include "uv.h" + +#define BUFFER_SIZE 256 +#define LABEL "Server" + +static char *g_initContext = ""; +static PropertyWorkSpace g_propertyWorkSpace = {ATOMIC_VAR_INIT(0), {}, {}, {}}; + +void InitPropertyService() +{ + int ret = InitPropertyWorkSpace(&g_propertyWorkSpace, 0, g_initContext); + PROPERTY_CHECK(ret == 0, return, "Init propert workspace fail"); +} + +int LoadDefaultProperty(const char *fileName) +{ + u_int32_t flags = atomic_load_explicit(&g_propertyWorkSpace.flags, memory_order_relaxed); + if ((flags & WORKSPACE_FLAGS_INIT) != WORKSPACE_FLAGS_INIT) { + return PROPERTY_CODE_NOT_INIT; + } + + FILE *fp = fopen(fileName, "r"); + PROPERTY_CHECK(fp != NULL, return -1, "Open file %s fail", fileName); + char buff[BUFFER_SIZE]; + SubStringInfo *info = malloc(sizeof(SubStringInfo) * (SUBSTR_INFO_LABEL + 1)); + while(fgets(buff, BUFFER_SIZE, fp) != NULL) { + int subStrNumber = GetSubStringInfo(buff, strlen(buff), '\n', info, SUBSTR_INFO_LABEL + 1); + if (subStrNumber <= SUBSTR_INFO_LABEL) { + continue; + } + + if (strncmp(info[0].value, "ctl.", strlen("ctl.")) == 0) { + PROPERTY_LOGE("Do not set ctl. properties from init %s", info[0].value); + continue; + } + if (strcmp(info[0].value, "selinux.restorecon_recursive") == 0) { + PROPERTY_LOGE("Do not set selinux.restorecon_recursive from init %s", info[0].value); + continue; + } + int ret = CheckPropertyName(info[0].value, 0); + PROPERTY_CHECK(ret == 0, continue, "Illegal property name %s", info[0].value); + + ret = WriteProperty(&g_propertyWorkSpace.propertySpace, info[0].value, info[1].value); + PROPERTY_CHECK(ret == 0, continue, "Failed to set property %d %s", ret, buff); + + ret = WritePersistProperty(info[0].value, info[1].value); + PROPERTY_CHECK(ret == 0, continue, "Failed to set persist property %d %s", ret, buff); + } + fclose(fp); + free(info); + PROPERTY_LOGI("LoadDefaultProperty proterty success %s", fileName); + return 0; +} + +int LoadPropertyInfo(const char *fileName) +{ + u_int32_t flags = atomic_load_explicit(&g_propertyWorkSpace.flags, memory_order_relaxed); + if ((flags & WORKSPACE_FLAGS_INIT) != WORKSPACE_FLAGS_INIT) { + return PROPERTY_CODE_NOT_INIT; + } + FILE *fp = fopen(fileName, "r"); + PROPERTY_CHECK(fp != NULL, return -1, "Open file %s fail", fileName); + SubStringInfo *info = malloc(sizeof(SubStringInfo) * SUBSTR_INFO_MAX); + char buff[BUFFER_SIZE]; + int propInfoCount = 0; + while(fgets(buff, BUFFER_SIZE, fp) != NULL) { + int subStrNumber = GetSubStringInfo(buff, strlen(buff), ' ', info, SUBSTR_INFO_MAX); + if (subStrNumber <= 0) { + continue; + } + int ret = WritePropertyInfo(&g_propertyWorkSpace, info, subStrNumber); + PROPERTY_CHECK(ret == 0, continue, "Failed to write property info %d %s", ret, buff); + propInfoCount++; + } + fclose(fp); + free(info); + PROPERTY_LOGI("Load proterty info %d success %s", propInfoCount, fileName); + return 0; +} + +static int ProcessPropertySet(RequestMsg *msg) +{ + PROPERTY_CHECK(msg != NULL, return PROPERTY_CODE_INVALID_PARAM, "Failed to check param"); + + SubStringInfo info[3]; + int ret = GetSubStringInfo(msg->content, msg->contentSize, '=', info, sizeof(info)/sizeof(info[0])); + PROPERTY_CHECK(ret >= 2, return ret, "Failed to get name from content %s", msg->content); + + PROPERTY_LOGI("ProcessPropertySet name %s value: %s", info[0].value, info[1].value); + ret = WritePropertyWithCheck(&g_propertyWorkSpace, &msg->securitylabel, info[0].value, info[1].value); + PROPERTY_CHECK(ret == 0, return ret, "Failed to set property %d name %s %s", ret, info[0].value, info[1].value); + + ret = WritePersistProperty(info[0].value, info[1].value); + PROPERTY_CHECK(ret == 0, return ret, "Failed to set property"); + + // notify event to process trigger + PostTrigger(EVENT_PROPERTY, msg->content, msg->contentSize); + return 0; +} + +static void OnClose(uv_handle_t *handle) +{ + free(handle); +} + +static void OnReceiveAlloc(uv_handle_t *handle, size_t suggestedSize, uv_buf_t* buf) +{ + // 这里需要按实际消息的大小申请内存,取最大消息的长度 + buf->base = (char *)malloc(sizeof(RequestMsg) + BUFFER_SIZE * 2); + buf->len = suggestedSize; +} + +static void OnWriteResponse(uv_write_t *req, int status) +{ + // 发送成功,释放请求内存 + PROPERTY_LOGI("OnWriteResponse status %d", status); + ResponseNode *node = (ResponseNode*)req; + free(node); +} + +static void SendResponse(uv_stream_t *handle, RequestType type, int result, void *content, int size) +{ + int ret = 0; + // 申请整块内存,用于回复数据和写请求 + ResponseNode *response = (ResponseNode *)malloc(sizeof(ResponseNode) + size); + PROPERTY_CHECK(response != NULL, return, "Failed to alloc memory for response"); + response->msg.type = type; + response->msg.contentSize = size; + response->msg.result = result; + if (content != NULL && size != 0) { + ret = memcpy_s(response->msg.content, size, content, size); + PROPERTY_CHECK(ret == 0, return, "Failed to copy content"); + } + uv_buf_t buf = uv_buf_init((char *)&response->msg, sizeof(response->msg) + size); + ret = uv_write2(&response->writer, handle, &buf, 1, handle, OnWriteResponse); + PROPERTY_CHECK(ret >= 0, return, "Failed to uv_write2 ret %s", uv_strerror(ret)); +} + +static void OnReceiveRequest(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) +{ + if (nread <= 0 || buf == NULL || buf->base == NULL) { + uv_close((uv_handle_t*)handle, OnClose); + free(buf->base); + return; + } + int freeHandle = 1; + RequestMsg *msg = (RequestMsg *)buf->base; + switch (msg->type) { + case SET_PROPERTY: { + freeHandle = 0; + int ret = ProcessPropertySet(msg); + SendResponse(handle, SET_PROPERTY, ret, NULL, 0); + break; + } + default: + PROPERTY_LOGE("not supported the command: %d", msg->type); + break; + } + free(buf->base); + uv_close((uv_handle_t*)handle, OnClose); +} + +static void RemoveSocket(int sig) +{ + uv_fs_t req; + uv_fs_unlink(uv_default_loop(), &req, PIPE_NAME, NULL); + ClosePropertyWorkSpace(&g_propertyWorkSpace); + ClosePersistPropertyWorkSpace(); + uv_stop(uv_default_loop()); + exit(0); +} + +static void OnConnection(uv_stream_t *server, int status) +{ + PROPERTY_CHECK(status >= 0, return, "Error status %d", status); + PROPERTY_CHECK(server != NULL, return, "Error server"); + uv_pipe_t *stream = (uv_pipe_t*)malloc(sizeof(uv_pipe_t)); + PROPERTY_CHECK(stream != NULL, return, "Failed to alloc stream"); + + int ret = uv_pipe_init(uv_default_loop(), (uv_pipe_t*)stream, 1); + PROPERTY_CHECK(ret == 0, free(stream); return, "Failed to uv_pipe_init %d", ret); + + stream->data = server; + ret = uv_accept(server, (uv_stream_t *)stream); + PROPERTY_CHECK(ret == 0, uv_close((uv_handle_t*)stream, NULL); free(stream); + return, "Failed to uv_accept %d", ret); + + ret = uv_read_start((uv_stream_t *)stream, OnReceiveAlloc, OnReceiveRequest); + PROPERTY_CHECK(ret == 0, uv_close((uv_handle_t*)stream, NULL); free(stream); + return, "Failed to uv_read_start %d", ret); +} + +int StartPropertyService() +{ + PROPERTY_LOGI("StartPropertyService."); + uv_fs_t req; + uv_fs_unlink(uv_default_loop(), &req, PIPE_NAME, NULL); + + signal(SIGINT, RemoveSocket); + + uv_pipe_t pipeServer; + int ret = uv_pipe_init(uv_default_loop(), &pipeServer, 0); + PROPERTY_CHECK(ret == 0, return ret, "Failed to uv_pipe_init %d", ret); + ret = uv_pipe_bind(&pipeServer, PIPE_NAME); + PROPERTY_CHECK(ret == 0, return ret, "Failed to uv_pipe_bind %d %s", ret, uv_err_name(ret)); + ret = uv_listen((uv_stream_t*)&pipeServer, SOMAXCONN, OnConnection); + PROPERTY_CHECK(ret == 0, return ret, "Failed to uv_listen %d %s", ret, uv_err_name(ret)); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + PROPERTY_LOGI("Start service exit."); + return 0; +} + +int SystemWriteParameter(const char *name, const char *value) +{ + PROPERTY_CHECK(name != NULL && value != NULL, return -1, "The name is null"); + PROPERTY_LOGI("SystemWriteParameter name %s value: %s", name, value); + int ret = WritePropertyWithCheck(&g_propertyWorkSpace, &g_propertyWorkSpace.label, name, value); + if (ret == 0) { + ret = WritePersistProperty(name, value); + PROPERTY_CHECK(ret == 0, return ret, "Failed to set property"); + } else { + PROPERTY_LOGE("Failed to set property %d name %s %s", ret, name, value); + } + // notify event to process trigger + PostPropertyTrigger(name, value); + return 0; +} + +int SystemReadParameter(const char *name, char *value, unsigned int *len) +{ + PROPERTY_CHECK(name != NULL && len != NULL, return -1, "The name is null"); + PropertyHandle handle = 0; + int ret = ReadPropertyWithCheck(&g_propertyWorkSpace, name, &handle); + if (ret == 0) { + ret = ReadPropertyValue(&g_propertyWorkSpace, handle, value, len); + } + return ret; +} + +int SystemTraversalParameters(void (*traversalParameter)(PropertyHandle handle, void* cookie), void* cookie) +{ + PROPERTY_CHECK(traversalParameter != NULL, return -1, "The param is null"); + return TraversalProperty(&g_propertyWorkSpace, traversalParameter, cookie); +} + +PropertyWorkSpace *GetPropertyWorkSpace() +{ + return &g_propertyWorkSpace; +} + +int LoadPersistProperties() +{ + return RefreshPersistProperties(&g_propertyWorkSpace, g_initContext); +} diff --git a/services/src/device.c b/services/src/device.c index 84013a2cbf376ed2ab91a7b59036ff5ec844edf2..af969a83d236e150c89c6a966a16650b2060f7ac 100755 --- a/services/src/device.c +++ b/services/src/device.c @@ -38,6 +38,11 @@ void MountBasicFs() if (mount("sysfs", "/sys", "sysfs", 0, NULL) != 0) { printf("Mount sysfs failed. %s\n", strerror(errno)); } +#ifndef __LITEOS__ + if (mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL) != 0) { + printf("Mount selinuxfs failed. %s\n", strerror(errno)); + } +#endif } void CreateDeviceNode() @@ -56,3 +61,19 @@ void CreateDeviceNode() printf("Create /dev/urandom device node failed. %s\n", strerror(errno)); } } + +int MakeSocketDir(const char *path, mode_t mode) +{ + int rc = mkdir("/dev/unix/", mode); + if (rc < 0 && errno != EEXIST) { + printf("Create %s failed. %d\n", path, errno); + return -1; + } + rc = mkdir("/dev/unix/socket/", mode); + if (rc < 0 && errno != EEXIST) { + printf("Create %s failed. %d\n", path, errno); + return -1; + } + return rc; +} + diff --git a/services/src/init_adapter.c b/services/src/init_adapter.c index 34c46408b42119df54b41760bb7c8d0ccc9de1e6..5aa53c5dceea379a612db84fba034bfa7bc59fa8 100644 --- a/services/src/init_adapter.c +++ b/services/src/init_adapter.c @@ -20,8 +20,10 @@ #include #include #include -#ifdef __LINUX__ +#if ((defined __LINUX__) || (!defined OHOS_LITE)) #include +#endif +#ifdef __LINUX__ #include "init_signal_handler.h" #endif @@ -35,9 +37,9 @@ void RebootSystem() int KeepCapability() { -#ifdef __LINUX__ +#if ((defined __LINUX__) || (!defined OHOS_LITE)) if (prctl(PR_SET_SECUREBITS, SECBIT_NO_SETUID_FIXUP | SECBIT_NO_SETUID_FIXUP_LOCKED)) { - printf("[Init] prctl failed\n"); + printf("[Init] prctl PR_SET_SECUREBITS failed: %d\n", errno); return -1; } #endif @@ -46,9 +48,9 @@ int KeepCapability() int SetAmbientCapability(int cap) { -#ifdef __LINUX__ +#if ((defined __LINUX__) || (!defined OHOS_LITE)) if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0)) { - printf("[Init] prctl PR_CAP_AMBIENT failed\n"); + printf("[Init] prctl PR_CAP_AMBIENT failed: %d\n", errno); return -1; } #endif diff --git a/services/src/init_capability.c b/services/src/init_capability.c new file mode 100755 index 0000000000000000000000000000000000000000..14cdbc66c29818cac87347e6ae8710fd7a447066 --- /dev/null +++ b/services/src/init_capability.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2020 Huawei Device Co., Ltd. + * 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. + */ + +#include "init_capability.h" + +#include +#include +#include +#include +#include +#ifdef OHOS_LITE +#include +#else +#include +#endif +#include +#include + +#include "init_log.h" +#include "init_perms.h" + +#define MAX_CAPS_CNT_FOR_ONE_SERVICE 100 + +struct CapStrCapNum { + char *capStr; + int CapNum; +}; + +static struct CapStrCapNum g_capStrCapNum[] = { + {"CHOWN", CAP_CHOWN}, + {"DAC_OVERRIDE", CAP_DAC_OVERRIDE}, + {"DAC_READ_SEARCH", CAP_DAC_READ_SEARCH}, + {"FOWNER", CAP_FOWNER}, + {"FSETID", CAP_FSETID}, + {"KILL", CAP_KILL}, + {"SETGID", CAP_SETGID}, + {"SETUID", CAP_SETUID}, + {"SETPCAP", CAP_SETPCAP}, + {"LINUX_IMMUTABLE", CAP_LINUX_IMMUTABLE}, + {"NET_BIND_SERVICE", CAP_NET_BIND_SERVICE}, + {"NET_BROADCAST", CAP_NET_BROADCAST}, + {"NET_ADMIN", CAP_NET_ADMIN}, + {"NET_RAW", CAP_NET_RAW}, + {"IPC_LOCK", CAP_IPC_LOCK}, + {"IPC_OWNER", CAP_IPC_OWNER}, + {"SYS_MODULE", CAP_SYS_MODULE}, + {"SYS_RAWIO", CAP_SYS_RAWIO}, + {"SYS_CHROOT", CAP_SYS_CHROOT}, + {"SYS_PTRACE", CAP_SYS_PTRACE}, + {"SYS_PACCT", CAP_SYS_PACCT}, + {"SYS_ADMIN", CAP_SYS_ADMIN}, + {"SYS_BOOT", CAP_SYS_BOOT}, + {"SYS_NICE", CAP_SYS_NICE}, + {"SYS_RESOURCE", CAP_SYS_RESOURCE}, + {"SYS_TIME", CAP_SYS_TIME}, + {"SYS_TTY_CONFIG", CAP_SYS_TTY_CONFIG}, + {"MKNOD", CAP_MKNOD}, + {"LEASE", CAP_LEASE}, + {"AUDIT_WRITE", CAP_AUDIT_WRITE}, + {"AUDIT_CONTROL", CAP_AUDIT_CONTROL}, + {"SETFCAP", CAP_SETFCAP}, + {"MAC_OVERRIDE", CAP_MAC_OVERRIDE}, + {"MAC_ADMIN", CAP_MAC_ADMIN}, + {"SYSLOG", CAP_SYSLOG}, + {"WAKE_ALARM", CAP_WAKE_ALARM}, + {"BLOCK_SUSPEND", CAP_BLOCK_SUSPEND}, + {"AUDIT_READ", CAP_AUDIT_READ}, +}; + +static int GetServiceStringCaps(const cJSON* filedJ, Service* curServ) // string form +{ + unsigned int i = 0; + for (; i < curServ->servPerm.capsCnt; ++i) { + if (cJSON_GetArrayItem(filedJ, i) == NULL || !cJSON_GetStringValue(cJSON_GetArrayItem(filedJ, i)) + || strlen(cJSON_GetStringValue(cJSON_GetArrayItem(filedJ, i))) <= 0) { // check all errors + INIT_LOGE("[Init] GetServiceStringCaps, parse item[%d] as string, error.\n", i); + break; + } + char* fieldStr = cJSON_GetStringValue(cJSON_GetArrayItem(filedJ, i)); + int mapSize = sizeof(g_capStrCapNum) / sizeof(struct CapStrCapNum); // search + int j = 0; + for (; j < mapSize; j++) { + if (!strcmp(fieldStr, g_capStrCapNum[j].capStr)) { + break; + } + } + if (j < mapSize) { + curServ->servPerm.caps[i] = g_capStrCapNum[j].CapNum; + } else { + INIT_LOGE("[Init] GetServiceStringCaps, fieldStr = %s, error.\n", fieldStr); + break; + } + if (curServ->servPerm.caps[i] > CAP_LAST_CAP && curServ->servPerm.caps[i] != FULL_CAP) { + // resources will be released by function: ReleaseServiceMem + INIT_LOGE("[Init] GetServiceStringCaps, cap = %d, error.\n", curServ->servPerm.caps[i]); + return SERVICE_FAILURE; + } + } + int ret = i == curServ->servPerm.capsCnt ? SERVICE_SUCCESS : SERVICE_FAILURE; + return ret; +} + +int GetServiceCaps(const cJSON* curArrItem, Service* curServ) +{ + curServ->servPerm.capsCnt = 0; + curServ->servPerm.caps = NULL; + cJSON* filedJ = cJSON_GetObjectItem(curArrItem, "caps"); + if (filedJ == NULL) { + INIT_LOGE("[Init] GetServiceCaps, caps is not found. but maybe ok.\n"); + return SERVICE_SUCCESS; + } + if (!cJSON_IsArray(filedJ)) { + INIT_LOGE("[Init] GetServiceCaps, caps is not a list, error.\n"); + return SERVICE_FAILURE; + } + // caps array does not exist, means do not need any capability + int capsCnt = cJSON_GetArraySize(filedJ); + if (capsCnt <= 0) { + return SERVICE_SUCCESS; + } + if (capsCnt > MAX_CAPS_CNT_FOR_ONE_SERVICE) { + INIT_LOGE("[Init] GetServiceCaps, too many caps[cnt %d] for one service, should not exceed %d.\n", + capsCnt, MAX_CAPS_CNT_FOR_ONE_SERVICE); + return SERVICE_FAILURE; + } + curServ->servPerm.caps = (unsigned int*)malloc(sizeof(unsigned int) * capsCnt); + if (curServ->servPerm.caps == NULL) { + INIT_LOGE("[Init] GetServiceCaps, malloc error.\n"); + return SERVICE_FAILURE; + } + curServ->servPerm.capsCnt = capsCnt; + int i = 0; + for (; i < capsCnt; ++i) { // number form + cJSON* capJ = cJSON_GetArrayItem(filedJ, i); + if (!cJSON_IsNumber(capJ) || cJSON_GetNumberValue(capJ) < 0) { + // resources will be released by function: ReleaseServiceMem + INIT_LOGE("[Init] GetServiceCaps, capJ is not a number or capJ < 0, error.\n"); + break; + } + curServ->servPerm.caps[i] = (unsigned int)cJSON_GetNumberValue(capJ); + if (curServ->servPerm.caps[i] > CAP_LAST_CAP && curServ->servPerm.caps[i] != FULL_CAP) { // CAP_LAST_CAP = 37 + // resources will be released by function: ReleaseServiceMem + INIT_LOGE("[Init] GetServiceCaps, caps = %d, error.\n", curServ->servPerm.caps[i]); + return SERVICE_FAILURE; + } + } + if (i == capsCnt) { + return SERVICE_SUCCESS; + } + int ret = GetServiceStringCaps(filedJ, curServ); + return ret; +} + diff --git a/services/src/init_cmds.c b/services/src/init_cmds.c index b79c561823120e208274e35f2106afaaa9dc4eff..19485623275b47400ba3fc4d810a5715e9d48f35 100644 --- a/services/src/init_cmds.c +++ b/services/src/init_cmds.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Huawei Device Co., Ltd. + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. * 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 @@ -23,17 +23,24 @@ #include #include #include +#include +#include #include -#ifndef OHOS_LITE #include +#ifndef OHOS_LITE #include -#include #endif - +#include +#include "init_jobs.h" +#include "init_log.h" #include "init_service_manager.h" +#include "init_utils.h" #include "securec.h" +#ifndef OHOS_LITE +#include "property_service.h" +#include "trigger.h" +#endif -#define MODE_LEN 4 // for chmod mode, format 0xxx #define DEFAULT_DIR_MODE 0755 // mkdir, default mode #define SPACES_CNT_IN_CMD_MAX 10 // mount, max number of spaces in cmdline #define SPACES_CNT_IN_CMD_MIN 2 // mount, min number of spaces in cmdline @@ -43,6 +50,9 @@ #define LOADCFG_MAX_LOOP 20 // loadcfg, to prevent to be trapped in infite loop #define OCTAL_TYPE 8 // 8 means octal to decimal #define MAX_BUFFER 256 +#define AUTHORITY_MAX_SIZE 128 +#define CONVERT_MICROSEC_TO_SEC(x) ((x) / 1000 / 1000) + static const char *g_supportCfg[] = { "/etc/patch.cfg", "/patch/fstab.cfg", @@ -54,8 +64,21 @@ static const char* g_supportedCmds[] = { "chmod ", "chown ", "mount ", + "export ", "loadcfg ", "insmod ", + "rm ", + "rmdir ", + "write ", + "exec ", + "mknode ", + "makedev ", + "symlink ", + "stop ", + "trigger ", + "reset ", + "copy ", + "load_persist_props " }; void ParseCmdLine(const char* cmdStr, CmdLine* resCmd) @@ -88,93 +111,165 @@ void ParseCmdLine(const char* cmdStr, CmdLine* resCmd) } if (!foundAndSucceed) { + printf("[Init][Debug], Cannot parse command: %s\n", cmdStr); (void)memset_s(resCmd, sizeof(*resCmd), 0, sizeof(*resCmd)); } } static void DoStart(const char* cmdContent) { + printf("[init][Debug] DoStart %s \n", cmdContent); StartServiceByName(cmdContent); } -static void DoMkDir(const char* cmdContent) +static void DoStop(const char* cmdContent) { - mode_t mode = DEFAULT_DIR_MODE; - if (mkdir(cmdContent, mode) != 0) { - if (errno != EEXIST) { - printf("[Init] DoMkDir, failed for %s, err %d.\n", cmdContent, errno); - } - } + StopServiceByName(cmdContent); } -static void DoChmod(const char* cmdContent) +static void DoReset(const char* cmdContent) { - // format: 0xxx /xxx/xxx/xxx - if (cmdContent[0] != '0' || cmdContent[MODE_LEN] != ' ' || strlen(cmdContent) <= MODE_LEN + 1) { - printf("[Init] DoChmod, bad format for %s.\n", cmdContent); - return; - } + INIT_LOGE("[init][Debug] DoReset %s \n", cmdContent); + DoStop(cmdContent); + DoStart(cmdContent); +} - for (size_t i = 1; i < MODE_LEN; ++i) { - if (cmdContent[i] > '7' || cmdContent[i] < '0') { - printf("[Init] DoChmod, bad mode format for %s.\n", cmdContent); - return; - } +static void DoCopy(const char* cmdContent) +{ + int srcFd = -1; + int dstFd = -1; + int rdLen = 0; + int rtLen = 0; + char buf[MAX_COPY_BUF_SIZE] = {0}; + mode_t mode = 0; + struct stat fileStat = {0}; + struct CmdArgs *ctx = GetCmd(cmdContent, " "); + if (ctx == NULL || ctx->argv == NULL || ctx->argc != DEFAULT_COPY_ARGS_CNT) { + INIT_LOGE("[Init] DoCopy failed.\n"); + goto out; + } + srcFd = open(ctx->argv[0], O_RDONLY); + INIT_ERROR_CHECK(srcFd >= 0, goto out, "[Init] copy open %s fail %d! \n", ctx->argv[0], errno); + INIT_ERROR_CHECK(stat(ctx->argv[0], &fileStat) == 0, goto out, "[Init] stat fail \n"); + mode = fileStat.st_mode; + dstFd = open(ctx->argv[1], O_WRONLY | O_TRUNC | O_CREAT, mode); + INIT_ERROR_CHECK(dstFd >= 0, goto out, "[Init] copy open %s fail %d! \n", ctx->argv[1], errno); + while ((rdLen = read(srcFd, buf, sizeof(buf) - 1)) > 0) { + rtLen = write(dstFd, buf, rdLen); + INIT_ERROR_CHECK(rtLen == rdLen, goto out, "[Init] write %s file fail %d! \n", ctx->argv[1], errno); + } + fsync(dstFd); +out: + FreeCmd(&ctx); + ctx = NULL; + close(srcFd); + srcFd = -1; + close(dstFd); + dstFd = -1; + return; +} + +static void DoChown(const char* cmdContent) +{ + // format: chown owner group /xxx/xxx/xxx + struct CmdArgs *ctx = GetCmd(cmdContent, " "); + if (ctx == NULL || ctx->argv == NULL || ctx->argc != 3) { + INIT_LOGE("[Init] DoChown failed.\n"); + goto out; + } + + uid_t owner = (uid_t)-1; + gid_t group = (gid_t)-1; + if (isalpha(ctx->argv[0][0])) { + owner = DecodeUid(ctx->argv[0]); + INIT_ERROR_CHECK(owner != (uid_t)-1, goto out, "[Init] DoChown decode owner failed.\n"); + } else { + owner = strtoul(ctx->argv[0], NULL, 0); } - const char* pathBeginStr = cmdContent + MODE_LEN + 1; // after space - mode_t mode = strtoul(cmdContent, NULL, OCTAL_TYPE); - if (mode == 0) { - printf("[Init] DoChmod, strtoul failed for %s, er %d.\n", cmdContent, errno); - return; + if (isalpha(ctx->argv[1][0])) { + group = DecodeUid(ctx->argv[1]); + INIT_ERROR_CHECK(group != (gid_t)-1, goto out, "[Init] DoChown decode group failed.\n"); + } else { + group = strtoul(ctx->argv[1], NULL, 0); } - if (chmod(pathBeginStr, mode) != 0) { - printf("[Init] DoChmod, failed for %s, err %d.\n", cmdContent, errno); + int pathPos = 2; + if (chown(ctx->argv[pathPos], owner, group) != 0) { + INIT_LOGE("[Init] DoChown, failed for %s, err %d.\n", cmdContent, errno); } +out: + FreeCmd(&ctx); + return; } -static void DoChown(const char* cmdContent) +static void DoMkDir(const char* cmdContent) { - // format: owner group /xxx/xxx/xxx - size_t firstSpace = 0; - size_t secondSpace = 0; - size_t strLen = strlen(cmdContent); - for (size_t i = 0; i < strLen; ++i) { - if (cmdContent[i] == ' ') { - if (i == 0) { - printf("[Init] DoChown, bad format for %s.\n", cmdContent); - return; - } - if (firstSpace == 0) { - firstSpace = i; - } else { - secondSpace = i; - break; + // format: mkdir /xxx/xxx/xxx or mkdir /xxx/xxx/xxx mode owner group + struct CmdArgs *ctx = GetCmd(cmdContent, " "); + if (ctx == NULL || ctx->argv == NULL || ctx->argc < 1) { + INIT_LOGE("[Init] DoMkDir failed.\n"); + goto out; + } + + mode_t mode = DEFAULT_DIR_MODE; + for (size_t i = 0; i < strlen(ctx->argv[0]); ++i) { + if (ctx->argv[0][i] == '/') { + ctx->argv[0][i] = '\0'; + if (access(ctx->argv[0], 0) != 0 ) { + mkdir(ctx->argv[0], mode); } + ctx->argv[0][i]='/'; } } - - if (secondSpace <= firstSpace || firstSpace + 1 == secondSpace || secondSpace == strLen - 1) { - printf("[Init] DoChown, bad format for %s.\n", cmdContent); - return; + if (access(ctx->argv[0], 0) != 0) { + if (mkdir(ctx->argv[0], mode) != 0 && errno != EEXIST) { + INIT_LOGE("[Init] DoMkDir %s failed, err %d.\n", ctx->argv[0], errno); + goto out; + } } - // only numbers valid - for (size_t i = 0; i < secondSpace; ++i) { - if (i != firstSpace && !isdigit(cmdContent[i])) { - printf("[Init] DoChown, bad format for %s.\n", cmdContent); - return; + if (ctx->argc > 1) { + mode = strtoul(ctx->argv[1], NULL, OCTAL_TYPE); + if (chmod(ctx->argv[0], mode) != 0) { + printf("[Init] DoMkDir failed for %s, err %d.\n", cmdContent, errno); + } + int ownerPos = 2; + int groupPos = 3; + char chownCmdContent[AUTHORITY_MAX_SIZE] = { 0 }; + if (snprintf_s(chownCmdContent, AUTHORITY_MAX_SIZE, AUTHORITY_MAX_SIZE - 1, "%s %s %s", + ctx->argv[ownerPos], ctx->argv[groupPos], ctx->argv[0]) == -1) { + INIT_LOGE("[Init] DoMkDir snprintf failed.\n"); + goto out; } + DoChown(chownCmdContent); + } +out: + FreeCmd(&ctx); + return; +} + +static void DoChmod(const char* cmdContent) +{ + // format: chmod xxxx /xxx/xxx/xxx + struct CmdArgs *ctx = GetCmd(cmdContent, " "); + if (ctx == NULL || ctx->argv == NULL || ctx->argc != 2) { + INIT_LOGE("[Init] DoChmod failed.\n"); + goto out; } - uid_t owner = strtoul(cmdContent, NULL, 0); - const char* groupBegin = cmdContent + firstSpace + 1; - gid_t group = strtoul(groupBegin, NULL, 0); - const char *path = cmdContent + secondSpace + 1; - if (chown(path, owner, group) != 0) { - printf("[Init] DoChown, failed for %s, err %d.\n", cmdContent, errno); + mode_t mode = strtoul(ctx->argv[0], NULL, OCTAL_TYPE); + if (mode == 0) { + INIT_LOGE("[Init] DoChmod, strtoul failed for %s, er %d.\n", cmdContent, errno); + goto out; + } + + if (chmod(ctx->argv[1], mode) != 0) { + printf("[Init] DoChmod, failed for %s, err %d.\n", cmdContent, errno); } +out: + FreeCmd(&ctx); + return; } static char* CopySubStr(const char* srcStr, size_t startPos, size_t endPos) @@ -206,7 +301,23 @@ static char* CopySubStr(const char* srcStr, size_t startPos, size_t endPos) return retStr; } -static int GetMountFlag(unsigned long* mountflags, const char* targetStr) +static void WaitForFile(const char *source) +{ + struct stat sourceInfo; + unsigned int waitTime = 500000; + int maxCount = 10; // 10 means that sleep 10 times, 500ms at a time + int count = 0; + do { + usleep(waitTime); + count++; + } while ((stat(source, &sourceInfo) < 0) && (errno == ENOENT) && (count < maxCount)); + if (count == maxCount) { + INIT_LOGE("[Init] wait for file:%s failed after %d.\n", source, maxCount * CONVERT_MICROSEC_TO_SEC(waitTime)); + } + return; +} + +static int GetMountFlag(unsigned long* mountflags, const char* targetStr, const char *source) { if (targetStr == NULL) { return 0; @@ -220,6 +331,10 @@ static int GetMountFlag(unsigned long* mountflags, const char* targetStr) (*mountflags) |= MS_NOSUID; } else if (strncmp(targetStr, "rdonly", strlen("rdonly")) == 0) { (*mountflags) |= MS_RDONLY; + } else if (strncmp(targetStr, "noatime", strlen("noatime")) == 0) { + (*mountflags) |= MS_NOATIME; + } else if (strncmp(targetStr, "wait", strlen("wait")) == 0) { + WaitForFile(source); } else { return 0; } @@ -296,7 +411,7 @@ static void DoMount(const char* cmdContent) while (indexOffset < spaceCnt) { size_t tmpStrEndPos = (indexOffset == spaceCnt - 1) ? strLen : spacePosArr[indexOffset + 1]; char* tmpStr = CopySubStr(cmdContent, spacePosArr[indexOffset] + 1, tmpStrEndPos); - int ret = GetMountFlag(&mountflags, tmpStr); + int ret = GetMountFlag(&mountflags, tmpStr, source); free(tmpStr); tmpStr = NULL; @@ -487,30 +602,291 @@ static void DoLoadCfg(const char *path) fclose(fp); } +static void DoWrite(const char *cmdContent) +{ + // format: write path content + struct CmdArgs *ctx = GetCmd(cmdContent, " "); + int writeCmdNumber = 2; + if (ctx == NULL || ctx->argv == NULL || ctx->argc != writeCmdNumber) { + printf("[Init] DoWrite: invalid arguments\n"); + goto out; + } + + int fd = open(ctx->argv[0], O_WRONLY | O_CREAT | O_NOFOLLOW | O_CLOEXEC, S_IRWXU | + S_IRGRP | S_IROTH); + if (fd == -1) { + printf("[Init] DoWrite: open %s failed: %d\n", ctx->argv[0], errno); + goto out; + } + + size_t ret = write(fd, ctx->argv[1], strlen(ctx->argv[1])); + if (ret < 0) { + printf("[Init] DoWrite: write to file %s failed: %d\n", ctx->argv[0], errno); + close(fd); + goto out; + } + close(fd); +out: + FreeCmd(&ctx); + return; +} + +static void DoRmdir(const char *cmdContent) +{ + // format: rmdir path + struct CmdArgs *ctx = GetCmd(cmdContent, " "); + if (ctx == NULL || ctx->argv == NULL || ctx->argc != 1) { + INIT_LOGE("[Init] DoRmdir: invalid arguments\n"); + goto out; + } + + int ret = rmdir(ctx->argv[0]); + if (ret == -1) { + INIT_LOGE("[Init] DoRmdir: remove %s failed: %d.\n", ctx->argv[0], errno); + goto out; + } +out: + FreeCmd(&ctx); + return; +} + +static void DoRm(const char *cmdContent) +{ + // format: rm /xxx/xxx/xxx + struct CmdArgs *ctx = GetCmd(cmdContent, " "); + if (ctx == NULL || ctx->argv == NULL || ctx->argc != 1) { + printf("[Init] DoRm: invalid arguments\n"); + goto out; + } + int ret = unlink(ctx->argv[0]); + if (ret == -1) { + INIT_LOGE("[Init] DoRm: unlink %s failed: %d.\n", ctx->argv[0], errno); + goto out; + } +out: + FreeCmd(&ctx); + return; +} + +static void DoExport(const char *cmdContent) +{ + // format: export xxx /xxx/xxx/xxx + struct CmdArgs *ctx = GetCmd(cmdContent, " "); + if (ctx == NULL || ctx->argv == NULL || ctx->argc != 2) { + printf("[Init] DoExport: invalid arguments\n"); + goto out; + } + int ret = setenv(ctx->argv[0], ctx->argv[1], 1); + if (ret != 0) { + INIT_LOGE("[Init] DoExport: set %s with %s failed: %d\n", ctx->argv[0], ctx->argv[1], errno); + goto out; + } +out: + FreeCmd(&ctx); + return; +} + +static void DoExec(const char *cmdContent) +{ + // format: exec /xxx/xxx/xxx xxx + struct CmdArgs *ctx = GetCmd(cmdContent, " "); + if (ctx == NULL || ctx->argv == NULL) { + INIT_LOGE("[Init] DoExec: invalid arguments\n"); + goto out; + } + pid_t pid = fork(); + if (pid == 0) { +#ifdef OHOS_LITE + int ret = execve(ctx->argv[0], ctx->argv, NULL); +#else + int ret = execv(ctx->argv[0], ctx->argv); +#endif + if (ret == -1) { + INIT_LOGE("[Init] DoExec: execute \"%s\" failed: %d.\n", cmdContent, errno); + goto out; + } + } else { + int status = 0; + waitpid(pid, &status, 0); + INIT_LOGI("[Init] DoExec done.\n"); + } +out: + FreeCmd(&ctx); + return; +} + +#ifndef __LITEOS__ +static void DoSymlink(const char *cmdContent) +{ + // format: symlink /xxx/xxx/xxx /xxx/xxx/xxx + struct CmdArgs *ctx = GetCmd(cmdContent, " "); + int symlinkCmdNumber = 2; + if (ctx == NULL || ctx->argv == NULL || ctx->argc != symlinkCmdNumber) { + INIT_LOGE("[Init] DoSymlink: invalid arguments.\n"); + goto out; + } + + int ret = symlink(ctx->argv[0], ctx->argv[1]); + if (ret != 0) { + INIT_LOGE("[Init] DoSymlink: link %s to %s failed: %d\n", ctx->argv[0], ctx->argv[1], errno); + goto out; + } +out: + FreeCmd(&ctx); + return; +} + +static mode_t GetDeviceMode(const char *deviceStr) +{ + switch (*deviceStr) { + case 'b': + case 'B': + return S_IFBLK; + case 'c': + case 'C': + return S_IFCHR; + case 'f': + case 'F': + return S_IFIFO; + default: + return -1; + } +} + +static void DoMakeNode(const char *cmdContent) +{ + // format: mknod path b 0644 1 9 + struct CmdArgs *ctx = GetCmd(cmdContent, " "); + int mkNodeCmdNumber = 5; + int deviceTypePos = 1; + int authorityPos = 2; + int majorDevicePos = 3; + int minorDevicePos = 4; + int decimal = 10; + int octal = 8; + if (ctx == NULL || ctx->argv == NULL || ctx->argc != mkNodeCmdNumber) { + INIT_LOGE("[Init] DoMakeNode: invalid arguments\n"); + goto out; + } + + if (!access(ctx->argv[1], F_OK)) { + INIT_LOGE("[Init] DoMakeNode failed, path has not sexisted\n"); + goto out; + } + mode_t deviceMode = GetDeviceMode(ctx->argv[deviceTypePos]); + unsigned int major = strtoul(ctx->argv[majorDevicePos], NULL, decimal); + unsigned int minor = strtoul(ctx->argv[minorDevicePos], NULL, decimal); + mode_t authority = strtoul(ctx->argv[authorityPos], NULL, octal); + + int ret = mknod(ctx->argv[0], deviceMode | authority, makedev(major, minor)); + if (ret != 0) { + INIT_LOGE("[Init] DoMakeNode: path: %s failed: %d\n", ctx->argv[0], errno); + goto out; + } +out: + FreeCmd(&ctx); + return; +} + +static void DoMakeDevice(const char *cmdContent) +{ + // format: makedev major minor + struct CmdArgs *ctx = GetCmd(cmdContent, " "); + int makeDevCmdNumber = 2; + int decimal = 10; + if (ctx == NULL || ctx->argv == NULL || ctx->argc != makeDevCmdNumber) { + INIT_LOGE("[Init] DoMakedevice: invalid arugments\n"); + goto out; + } + unsigned int major = strtoul(ctx->argv[0], NULL, decimal); + unsigned int minor = strtoul(ctx->argv[1], NULL, decimal); + dev_t deviceId = makedev(major, minor); + if (deviceId < 0) { + INIT_LOGE("[Init] DoMakedevice \" %s \" failed :%d \n", cmdContent, errno); + goto out; + } +out: + FreeCmd(&ctx); + return; +} +#endif // __LITEOS__ + void DoCmd(const CmdLine* curCmd) { - if (curCmd == NULL) { + // null curCmd or empty command, just quit. + if (curCmd == NULL || curCmd->name[0] == '\0') { return; } + // INIT_LOGE("curCmd->name:%s, curCmd->cmdContent:%s\n", curCmd->name, curCmd->cmdContent); + DoCmdByName(curCmd->name, curCmd->cmdContent); +} - if (strncmp(curCmd->name, "start ", strlen("start ")) == 0) { - DoStart(curCmd->cmdContent); - } else if (strncmp(curCmd->name, "mkdir ", strlen("mkdir ")) == 0) { - DoMkDir(curCmd->cmdContent); - } else if (strncmp(curCmd->name, "chmod ", strlen("chmod ")) == 0) { - DoChmod(curCmd->cmdContent); - } else if (strncmp(curCmd->name, "chown ", strlen("chown ")) == 0) { - DoChown(curCmd->cmdContent); - } else if (strncmp(curCmd->name, "mount ", strlen("mount ")) == 0) { - DoMount(curCmd->cmdContent); - } else if (strncmp(curCmd->name, "loadcfg ", strlen("loadcfg ")) == 0) { - DoLoadCfg(curCmd->cmdContent); +void DoCmdByName(const char *name, const char *cmdContent) +{ + if (name == NULL || cmdContent == NULL) { + return; + } + if (strncmp(name, "start ", strlen("start ")) == 0) { + DoStart(cmdContent); + } else if (strncmp(name, "mkdir ", strlen("mkdir ")) == 0) { + DoMkDir(cmdContent); + } else if (strncmp(name, "stop ", strlen("stop ")) == 0) { + DoStop(cmdContent); + } else if (strncmp(name, "reset ", strlen("reset ")) == 0) { + DoReset(cmdContent); + } else if (strncmp(name, "copy ", strlen("copy ")) == 0) { + DoCopy(cmdContent); + } else if (strncmp(name, "chmod ", strlen("chmod ")) == 0) { + DoChmod(cmdContent); + } else if (strncmp(name, "chown ", strlen("chown ")) == 0) { + DoChown(cmdContent); + } else if (strncmp(name, "mount ", strlen("mount ")) == 0) { + DoMount(cmdContent); + } else if (strncmp(name, "write ", strlen("write ")) == 0) { + DoWrite(cmdContent); + } else if (strncmp(name, "rmdir ", strlen("rmdir ")) == 0) { + DoRmdir(cmdContent); + } else if (strncmp(name, "rm ", strlen("rm ")) == 0) { + DoRm(cmdContent); + } else if (strncmp(name, "export ", strlen("export ")) == 0) { + DoExport(cmdContent); + } else if (strncmp(name, "exec ", strlen("exec ")) == 0) { + DoExec(cmdContent); +#ifndef __LITEOS__ + } else if (strncmp(name, "symlink ", strlen("symlink ")) == 0) { + DoSymlink(cmdContent); + } else if (strncmp(name, "makedev ", strlen("makedev ")) == 0) { + DoMakeDevice(cmdContent); + } else if (strncmp(name, "mknode ", strlen("mknode ")) == 0) { + DoMakeNode(cmdContent); +#endif + } else if (strncmp(name, "loadcfg ", strlen("loadcfg ")) == 0) { + DoLoadCfg(cmdContent); #ifndef OHOS_LITE - } else if (strncmp(curCmd->name, "insmod ", strlen("insmod ")) == 0) { - DoInsmod(curCmd->cmdContent); + } else if (strncmp(name, "insmod ", strlen("insmod ")) == 0) { + DoInsmod(cmdContent); + } else if (strncmp(name, "trigger ", strlen("trigger ")) == 0) { + printf("[Init][Debug], ready to trigger job: %s\n", name); + DoTriggerExec(cmdContent); + } else if (strncmp(name, "load_persist_props ", strlen("load_persist_props ")) == 0) { + LoadPersistProperties(); #endif } else { - printf("[Init] DoCmd, unknown cmd name %s.\n", curCmd->name); + printf("[Init] DoCmd, unknown cmd name %s.\n", name); } } +const char *GetMatchCmd(const char *cmdStr) +{ + if (cmdStr == NULL) { + return NULL; + } + size_t supportCmdCnt = sizeof(g_supportedCmds) / sizeof(g_supportedCmds[0]); + for (size_t i = 0; i < supportCmdCnt; ++i) { + size_t curCmdNameLen = strlen(g_supportedCmds[i]); + if (strncmp(g_supportedCmds[i], cmdStr, curCmdNameLen) == 0) { + return g_supportedCmds[i]; + } + } + return NULL; +} diff --git a/services/src/init_import.c b/services/src/init_import.c new file mode 100755 index 0000000000000000000000000000000000000000..c2eac20a49cfcbbd6323979c7b19f74d50e84734 --- /dev/null +++ b/services/src/init_import.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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. + */ + +#include "init_import.h" +#include +#include "cJSON.h" +#include "init_read_cfg.h" + +#define IMPORT_ARR_NAME_IN_JSON "import" + +void ParseAllImports(cJSON *root) +{ + cJSON *importAttr = cJSON_GetObjectItemCaseSensitive(root, IMPORT_ARR_NAME_IN_JSON); + + if (!cJSON_IsArray(importAttr)) { + printf("[Init] ParseAllImports, import item is not array!\n"); + return; + } + int importAttrSize = cJSON_GetArraySize(importAttr); + + for (int i = 0; i < importAttrSize; i++) { + cJSON *importItem = cJSON_GetArrayItem(importAttr, i); + if (!cJSON_IsString(importItem)) { + printf("[Init] Invalid type of import item. should be string\n"); + return; + } + char *importFile = cJSON_GetStringValue(importItem); + if (importFile == NULL) { + printf("[Init] cannot get import config file\n"); + return; + } + printf("[Init] [Debug], ready to import %s...\n", importFile); + ParseInitCfg(importFile); + } + printf("[Init] [Debug], parse import file done\n"); + return; +} \ No newline at end of file diff --git a/services/src/init_jobs.c b/services/src/init_jobs.c index e41e623c817880b2777c75cfb41868953712b2ff..97bea7983a561981fdab9f65a19ebe50c81eb0a7 100644 --- a/services/src/init_jobs.c +++ b/services/src/init_jobs.c @@ -24,17 +24,31 @@ #define JOBS_ARR_NAME_IN_JSON "jobs" #define CMDS_ARR_NAME_IN_JSON "cmds" -#define MAX_JOBS_COUNT 10 +#define MAX_JOBS_COUNT 100 -static const char* g_supportedJobs[] = { - "pre-init", - "init", - "post-init", -}; +// static const char* g_supportedJobs[] = { +// "pre-init", +// "init", +// "post-init", +// }; static Job* g_jobs = NULL; static int g_jobCnt = 0; +void DumpAllJobs() +{ + printf("[Init][Debug], Ready to dump all jobs:\n"); + for (int i = 0; i < g_jobCnt; i++) { + printf("\t[Init], job name: %s\n", g_jobs[i].name); + printf("\t[Init], list all commands:\n"); + for (int j = 0; j < g_jobs[i].cmdLinesCnt; j++) { + printf("\t\t[Init], command name : %s, command options: %s\n", + g_jobs[i].cmdLines[j].name, g_jobs[i].cmdLines[j].cmdContent); + } + } + printf("[Init][Debug], To dump all jobs finished\n"); +} + static int GetJobName(const cJSON* jobItem, Job* resJob) { char* jobNameStr = cJSON_GetStringValue(cJSON_GetObjectItem(jobItem, "name")); @@ -42,37 +56,46 @@ static int GetJobName(const cJSON* jobItem, Job* resJob) return 0; } - size_t supportJobCnt = sizeof(g_supportedJobs) / sizeof(g_supportedJobs[0]); - for (size_t i = 0; i < supportJobCnt; ++i) { - if (strlen(g_supportedJobs[i]) == strlen(jobNameStr) && - strncmp(g_supportedJobs[i], jobNameStr, strlen(g_supportedJobs[i])) == 0) { - if (memcpy_s(resJob->name, MAX_JOB_NAME_LEN, jobNameStr, strlen(jobNameStr)) != EOK) { - return 0; - } - resJob->name[strlen(jobNameStr)] = '\0'; - return 1; - } + //size_t supportJobCnt = sizeof(g_supportedJobs) / sizeof(g_supportedJobs[0]); + //for (size_t i = 0; i < supportJobCnt; ++i) { + // if (strlen(g_supportedJobs[i]) == strlen(jobNameStr) && + // strncmp(g_supportedJobs[i], jobNameStr, strlen(g_supportedJobs[i])) == 0) { + // if (memcpy_s(resJob->name, MAX_JOB_NAME_LEN, jobNameStr, strlen(jobNameStr)) != EOK) { + // return 0; + // } + // resJob->name[strlen(jobNameStr)] = '\0'; + // return 1; + // } + //} + if (memcpy_s(resJob->name, MAX_JOB_NAME_LEN, jobNameStr, strlen(jobNameStr)) != EOK) { + printf("[Init], Get job name \"%s\" failed\n", jobNameStr); + return 0; } - return 0; + resJob->name[strlen(jobNameStr)] = '\0'; + return 1; } static void ParseJob(const cJSON* jobItem, Job* resJob) { if (!GetJobName(jobItem, resJob)) { + printf("[Init][Debug], get JobName failed\n"); (void)memset_s(resJob, sizeof(*resJob), 0, sizeof(*resJob)); return; } cJSON* cmdsItem = cJSON_GetObjectItem(jobItem, CMDS_ARR_NAME_IN_JSON); if (!cJSON_IsArray(cmdsItem)) { + printf("[Init][Debug], job %s is not an arrary\n", resJob->name); return; } int cmdLinesCnt = cJSON_GetArraySize(cmdsItem); if (cmdLinesCnt <= 0) { // empty job, no cmd + printf("[Init][Debug], empty job \"%s\"\n", resJob->name); return; } + printf("[Init][Debug], job = %s, cmdLineCnt = %d\n", resJob->name, cmdLinesCnt); if (cmdLinesCnt > MAX_CMD_CNT_IN_ONE_JOB) { printf("[Init] ParseAllJobs, too many cmds[cnt %d] in one job, it should not exceed %d.\n", cmdLinesCnt, MAX_CMD_CNT_IN_ONE_JOB); @@ -81,6 +104,7 @@ static void ParseJob(const cJSON* jobItem, Job* resJob) resJob->cmdLines = (CmdLine*)malloc(cmdLinesCnt * sizeof(CmdLine)); if (resJob->cmdLines == NULL) { + printf("[Init][Debug], allocate memory for command line failed\n"); return; } @@ -117,13 +141,14 @@ void ParseAllJobs(const cJSON* fileRoot) return; } - Job* retJobs = (Job*)malloc(sizeof(Job) * jobArrSize); + Job* retJobs = (Job*)realloc(g_jobs, sizeof(Job) * (g_jobCnt + jobArrSize)); if (retJobs == NULL) { printf("[Init] ParseAllJobs, malloc failed! job arrSize %d.\n", jobArrSize); return; } - if (memset_s(retJobs, sizeof(Job) * jobArrSize, 0, sizeof(Job) * jobArrSize) != EOK) { + Job* tmp = retJobs + g_jobCnt; + if (memset_s(tmp, sizeof(Job) * jobArrSize, 0, sizeof(Job) * jobArrSize) != EOK) { printf("[Init] ParseAllJobs, memset_s failed.\n"); free(retJobs); retJobs = NULL; @@ -132,10 +157,10 @@ void ParseAllJobs(const cJSON* fileRoot) for (int i = 0; i < jobArrSize; ++i) { cJSON* jobItem = cJSON_GetArrayItem(jobArr, i); - ParseJob(jobItem, &(retJobs[i])); + ParseJob(jobItem, &(tmp[i])); } g_jobs = retJobs; - g_jobCnt = jobArrSize; + g_jobCnt += jobArrSize; } void DoJob(const char* jobName) @@ -145,13 +170,15 @@ void DoJob(const char* jobName) return; } + printf("[Init][Debug], Call job with name %s\n", jobName); for (int i = 0; i < g_jobCnt; ++i) { if (strncmp(jobName, g_jobs[i].name, strlen(g_jobs[i].name)) == 0) { CmdLine* cmdLines = g_jobs[i].cmdLines; for (int j = 0; j < g_jobs[i].cmdLinesCnt; ++j) { DoCmd(&(cmdLines[j])); } - break; + // Walk through all jobs + // break; } } } diff --git a/services/src/init_log.c b/services/src/init_log.c new file mode 100755 index 0000000000000000000000000000000000000000..bcde16fbfaf2b121a73438deafaf80a5b3cf4a0c --- /dev/null +++ b/services/src/init_log.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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. + */ + +#include "init_log.h" +#include +#include +#include +#include +#include +#include +#include +#include "securec.h" + +#define UNUSED(x) (void)(x) +#define MAX_FORMAT_SIZE 1024 +#define MAX_LOG_SIZE 2048 +#define BASE_YEAR 1900 + +void InitLog(int logLevel, const char *fileName, int line, const char *fmt, ...) +{ + UNUSED(logLevel); + va_list vargs; + va_start(vargs, fmt); + + char tmpFmt[MAX_FORMAT_SIZE]; + if (vsnprintf_s(tmpFmt, MAX_FORMAT_SIZE, MAX_FORMAT_SIZE, fmt, vargs) == -1) { + return; + } + time_t logTime; + time(&logTime); + struct tm *t = gmtime(&logTime); + char logInfo[MAX_LOG_SIZE]; + if (snprintf_s(logInfo, MAX_LOG_SIZE, MAX_LOG_SIZE, "%d-%d-%d %d:%d %s %d : %s", (t->tm_year + BASE_YEAR), + (t->tm_mon + 1), t->tm_mday, t->tm_hour, t->tm_min, fileName, line, tmpFmt) == -1) { + return; + } + + int fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC | O_APPEND ); + if (fd < 1) { + printf("xxxxxxxxxxxxxxx open failed. %d\n", errno); + return; + } + if (write(fd, logInfo, strlen(logInfo)) < -1) { + printf("xxxxxxxxxxxxxxx write failed.%d\n", errno); + return; + } + va_end(vargs); +} + diff --git a/services/src/init_read_cfg.c b/services/src/init_read_cfg.c index 4d6cbd3d45c370fb9d4ba0fa191a84ed91549dfa..97566985a8149110eb3d33e7cf1e489f0a820d7c 100644 --- a/services/src/init_read_cfg.c +++ b/services/src/init_read_cfg.c @@ -16,6 +16,7 @@ #include "init_read_cfg.h" #include +#include #include #include #include @@ -23,9 +24,16 @@ #include #include +#include "init_import.h" #include "init_jobs.h" +#include "init_log.h" #include "init_perms.h" #include "init_service_manager.h" +#include "init_utils.h" + +#ifndef OHOS_LITE +#include "trigger.h" +#endif #include "securec.h" #ifndef __LINUX__ #ifdef OHOS_LITE @@ -33,368 +41,91 @@ #endif #endif -static const long MAX_JSON_FILE_LEN = 102400; // max init.cfg size 100KB -static const int MAX_PATH_ARGS_CNT = 20; // max path and args count -static const int MAX_ONE_ARG_LEN = 64; // max length of one param/path -#define MAX_SERVICES_CNT_IN_FILE 100 -#define MAX_CAPS_CNT_FOR_ONE_SERVICE 100 -#define UID_STR_IN_CFG "uid" -#define GID_STR_IN_CFG "gid" -#define ONCE_STR_IN_CFG "once" -#define IMPORTANT_STR_IN_CFG "importance" -#define BIN_SH_NOT_ALLOWED "/bin/sh" - -static char* ReadFileToBuf() -{ - char* buffer = NULL; - FILE* fd = NULL; - struct stat fileStat = {0}; - do { - if (stat(INIT_CONFIGURATION_FILE, &fileStat) != 0 || - fileStat.st_size <= 0 || fileStat.st_size > MAX_JSON_FILE_LEN) { - break; - } - - fd = fopen(INIT_CONFIGURATION_FILE, "r"); - if (fd == NULL) { - break; - } - - buffer = (char*)malloc(fileStat.st_size + 1); - if (buffer == NULL) { - break; - } - - if (fread(buffer, fileStat.st_size, 1, fd) != 1) { - free(buffer); - buffer = NULL; - break; - } - buffer[fileStat.st_size] = '\0'; - } while (0); - - if (fd != NULL) { - fclose(fd); - fd = NULL; - } - return buffer; -} - -static cJSON* GetArrItem(const cJSON* fileRoot, int* arrSize, const char* arrName) +#define FILE_NAME_MAX_SIZE 100 +static void ParseInitCfgContents(cJSON *root) { - cJSON* arrItem = cJSON_GetObjectItemCaseSensitive(fileRoot, arrName); - if (!cJSON_IsArray(arrItem)) { - printf("[Init] GetArrItem, item %s is not an array!\n", arrName); - return NULL; - } - - *arrSize = cJSON_GetArraySize(arrItem); - if (*arrSize <= 0) { - return NULL; - } - return arrItem; -} + // parse services + ParseAllServices(root); -static int IsForbidden(const char* fieldStr) -{ - size_t fieldLen = strlen(fieldStr); - size_t forbidStrLen = strlen(BIN_SH_NOT_ALLOWED); - if (fieldLen == forbidStrLen) { - if (strncmp(fieldStr, BIN_SH_NOT_ALLOWED, fieldLen) == 0) { - return 1; - } - return 0; - } else if (fieldLen > forbidStrLen) { - // "/bin/shxxxx" is valid but "/bin/sh xxxx" is invalid - if (strncmp(fieldStr, BIN_SH_NOT_ALLOWED, forbidStrLen) == 0) { - if (fieldStr[forbidStrLen] == ' ') { - return 1; - } - } - return 0; - } else { - return 0; - } -} - -static void ReleaseServiceMem(Service* curServ) -{ - if (curServ->pathArgs != NULL) { - for (int i = 0; i < curServ->pathArgsCnt; ++i) { - if (curServ->pathArgs[i] != NULL) { - free(curServ->pathArgs[i]); - curServ->pathArgs[i] = NULL; - } - } - free(curServ->pathArgs); - curServ->pathArgs = NULL; - } - curServ->pathArgsCnt = 0; - - if (curServ->servPerm.caps != NULL) { - free(curServ->servPerm.caps); - curServ->servPerm.caps = NULL; - } - if (curServ->servPerm.gIDs != NULL) { - free(curServ->servPerm.gIDs); - curServ->servPerm.gIDs = NULL; - } - curServ->servPerm.capsCnt = 0; - curServ->servPerm.gidsCnt = 0; -} - -static int GetServiceName(const cJSON* curArrItem, Service* curServ) -{ - char* fieldStr = cJSON_GetStringValue(cJSON_GetObjectItem(curArrItem, "name")); - if (fieldStr == NULL) { - return SERVICE_FAILURE; - } + // parse jobs + ParseAllJobs(root); - size_t strLen = strlen(fieldStr); - if (strLen == 0 || strLen > MAX_SERVICE_NAME) { - return SERVICE_FAILURE; - } +#ifndef OHOS_LITE + ParseTriggerConfig(root); +#endif - if (memcpy_s(curServ->name, MAX_SERVICE_NAME, fieldStr, strLen) != EOK) { - return SERVICE_FAILURE; - } - curServ->name[strLen] = '\0'; - return SERVICE_SUCCESS; + // parse imports + ParseAllImports(root); } -static int GetServicePathAndArgs(const cJSON* curArrItem, Service* curServ) +void ParseInitCfg(const char *configFile) { - cJSON* pathItem = cJSON_GetObjectItem(curArrItem, "path"); - if (!cJSON_IsArray(pathItem)) { - return SERVICE_FAILURE; - } - - int arrSize = cJSON_GetArraySize(pathItem); - if (arrSize <= 0 || arrSize > MAX_PATH_ARGS_CNT) { // array size invalid - return SERVICE_FAILURE; - } - - curServ->pathArgs = (char**)malloc((arrSize + 1) * sizeof(char*)); - if (curServ->pathArgs == NULL) { - return SERVICE_FAILURE; - } - for (int i = 0; i < arrSize + 1; ++i) { - curServ->pathArgs[i] = NULL; - } - curServ->pathArgsCnt = arrSize + 1; - - for (int i = 0; i < arrSize; ++i) { - char* curParam = cJSON_GetStringValue(cJSON_GetArrayItem(pathItem, i)); - if (curParam == NULL || strlen(curParam) > MAX_ONE_ARG_LEN) { - // resources will be released by function: ReleaseServiceMem - return SERVICE_FAILURE; - } - - if (i == 0 && IsForbidden(curParam)) { - // resources will be released by function: ReleaseServiceMem - return SERVICE_FAILURE; - } - - size_t paramLen = strlen(curParam); - curServ->pathArgs[i] = (char*)malloc(paramLen + 1); - if (curServ->pathArgs[i] == NULL) { - // resources will be released by function: ReleaseServiceMem - return SERVICE_FAILURE; - } - - if (memcpy_s(curServ->pathArgs[i], paramLen + 1, curParam, paramLen) != EOK) { - // resources will be released by function: ReleaseServiceMem - return SERVICE_FAILURE; - } - curServ->pathArgs[i][paramLen] = '\0'; + if (configFile == NULL || *configFile == '\0') { + printf("[Init] Invalid config file\n"); + return; } - return SERVICE_SUCCESS; -} -static int GetServiceNumber(const cJSON* curArrItem, Service* curServ, const char* targetField) -{ - cJSON* filedJ = cJSON_GetObjectItem(curArrItem, targetField); - if (!cJSON_IsNumber(filedJ)) { - return SERVICE_FAILURE; - } + char *fileBuf = ReadFileToBuf(configFile); + //printf("[Init] start dump config file: \n"); + //printf("%s", fileBuf); + //printf("[Init] end dump config file: \n"); - int value = (int)cJSON_GetNumberValue(filedJ); - if (value < 0) { - return SERVICE_FAILURE; - } + cJSON* fileRoot = cJSON_Parse(fileBuf); + free(fileBuf); + fileBuf = NULL; - if (strncmp(targetField, UID_STR_IN_CFG, strlen(UID_STR_IN_CFG)) == 0) { - curServ->servPerm.uID = value; - } else if (strncmp(targetField, ONCE_STR_IN_CFG, strlen(ONCE_STR_IN_CFG)) == 0) { - if (value != 0) { - curServ->attribute |= SERVICE_ATTR_ONCE; - } - } else if (strncmp(targetField, IMPORTANT_STR_IN_CFG, strlen(IMPORTANT_STR_IN_CFG)) == 0) { - if (value != 0) { - curServ->attribute |= SERVICE_ATTR_IMPORTANT; - } - } else { - return SERVICE_FAILURE; + if (fileRoot == NULL) { + printf("[Init] InitReadCfg, parse failed! please check file %s format.\n", configFile); + return; } - return SERVICE_SUCCESS; + ParseInitCfgContents(fileRoot); + // Release JSON object + cJSON_Delete(fileRoot); + return; } -static int GetServiceCaps(const cJSON* curArrItem, Service* curServ) +static void ReadCfgs(const char *dirPath) { - curServ->servPerm.capsCnt = 0; - curServ->servPerm.caps = NULL; - - cJSON* filedJ = cJSON_GetObjectItem(curArrItem, "caps"); - if (!cJSON_IsArray(filedJ)) { - return SERVICE_FAILURE; - } - - // caps array does not exist, means do not need any capability - int capsCnt = cJSON_GetArraySize(filedJ); - if (capsCnt <= 0) { - return SERVICE_SUCCESS; - } - - if (capsCnt > MAX_CAPS_CNT_FOR_ONE_SERVICE) { - printf("[Init] GetServiceCaps, too many caps[cnt %d] for one service, should not exceed %d.\n", - capsCnt, MAX_CAPS_CNT_FOR_ONE_SERVICE); - return SERVICE_FAILURE; - } - - curServ->servPerm.caps = (unsigned int*)malloc(sizeof(unsigned int) * capsCnt); - if (curServ->servPerm.caps == NULL) { - return SERVICE_FAILURE; + DIR *pDir = opendir(dirPath); + if (pDir == NULL) { + INIT_LOGE("[Init], ParseCfgs open cfg dir :%s failed.%d\n", dirPath, errno); + return; } - - for (int i = 0; i < capsCnt; ++i) { - cJSON* capJ = cJSON_GetArrayItem(filedJ, i); - if (!cJSON_IsNumber(capJ) || cJSON_GetNumberValue(capJ) < 0) { - // resources will be released by function: ReleaseServiceMem - return SERVICE_FAILURE; - } - curServ->servPerm.caps[i] = (unsigned int)cJSON_GetNumberValue(capJ); - if (curServ->servPerm.caps[i] > CAP_LAST_CAP && curServ->servPerm.caps[i] != FULL_CAP) { - // resources will be released by function: ReleaseServiceMem - return SERVICE_FAILURE; + struct dirent *dp; + while ((dp = readdir(pDir)) != NULL) { + char fileName[FILE_NAME_MAX_SIZE]; + if (snprintf_s(fileName, FILE_NAME_MAX_SIZE, FILE_NAME_MAX_SIZE - 1, "%s/%s", dirPath, dp->d_name) == -1) { + INIT_LOGE("[Init], ParseCfgs snprintf_s failed.\n"); + closedir(pDir); + return; + } + struct stat st; + if (stat(fileName, &st) == 0) { + if (strstr(dp->d_name, ".cfg") == NULL) { + continue; + } + INIT_LOGE("[Init], fileName :%s.\n", fileName); + ParseInitCfg(fileName); } } - curServ->servPerm.capsCnt = capsCnt; - return SERVICE_SUCCESS; + closedir(pDir); + return; } -static int GetServiceGids(const cJSON* curArrItem, Service* curServ) +static void ParseOtherCfgs() { - curServ->servPerm.gidsCnt = 0; - curServ->servPerm.gIDs = NULL; - int gidsCnt; - cJSON* filedJ = cJSON_GetObjectItem(curArrItem, "gid"); - if (cJSON_IsArray(filedJ)) { - gidsCnt = cJSON_GetArraySize(filedJ); - if (gidsCnt <= 0) { - // gids array does not exist, means do not need any group - return SERVICE_SUCCESS; - } - curServ->servPerm.gIDs = (unsigned int*)malloc(sizeof(unsigned int) * gidsCnt); - if (curServ->servPerm.gIDs == NULL) { - return SERVICE_FAILURE; - } - for (int i = 0; i < gidsCnt; ++i) { - cJSON* gidJ = cJSON_GetArrayItem(filedJ, i); - if (!cJSON_IsNumber(gidJ) || cJSON_GetNumberValue(gidJ) < 0) { - // resources will be released by function: ReleaseServiceMem - return SERVICE_FAILURE; - } - curServ->servPerm.gIDs[i] = (unsigned int)cJSON_GetNumberValue(gidJ); - } - } else { - int value = (int)cJSON_GetNumberValue(filedJ); - if (value < 0) { - return SERVICE_FAILURE; - } - gidsCnt = 1; - curServ->servPerm.gIDs = (unsigned int*)malloc(sizeof(unsigned int)); - if (curServ->servPerm.gIDs == NULL) { - return SERVICE_FAILURE; - } - curServ->servPerm.gIDs[0] = (unsigned int)value; - } - curServ->servPerm.gidsCnt = gidsCnt; - return SERVICE_SUCCESS; -} - -static void ParseAllServices(const cJSON* fileRoot) -{ - int servArrSize = 0; - cJSON* serviceArr = GetArrItem(fileRoot, &servArrSize, SERVICES_ARR_NAME_IN_JSON); - if (serviceArr == NULL) { - printf("[Init] InitReadCfg, get array %s failed.\n", SERVICES_ARR_NAME_IN_JSON); - return; - } - - if (servArrSize > MAX_SERVICES_CNT_IN_FILE) { - printf("[Init] InitReadCfg, too many services[cnt %d] detected, should not exceed %d.\n", - servArrSize, MAX_SERVICES_CNT_IN_FILE); - return; - } - - Service* retServices = (Service*)malloc(sizeof(Service) * servArrSize); - if (retServices == NULL) { - printf("[Init] InitReadCfg, malloc for %s arr failed! %d.\n", SERVICES_ARR_NAME_IN_JSON, servArrSize); - return; - } - - if (memset_s(retServices, sizeof(Service) * servArrSize, 0, sizeof(Service) * servArrSize) != EOK) { - free(retServices); - retServices = NULL; - return; - } - - for (int i = 0; i < servArrSize; ++i) { - cJSON* curItem = cJSON_GetArrayItem(serviceArr, i); - if (GetServiceName(curItem, &retServices[i]) != SERVICE_SUCCESS || - GetServicePathAndArgs(curItem, &retServices[i]) != SERVICE_SUCCESS || - GetServiceNumber(curItem, &retServices[i], UID_STR_IN_CFG) != SERVICE_SUCCESS || - GetServiceGids(curItem, &retServices[i]) != SERVICE_SUCCESS || - GetServiceNumber(curItem, &retServices[i], ONCE_STR_IN_CFG) != SERVICE_SUCCESS || - GetServiceNumber(curItem, &retServices[i], IMPORTANT_STR_IN_CFG) != SERVICE_SUCCESS || - GetServiceCaps(curItem, &retServices[i]) != SERVICE_SUCCESS) { - // release resources if it fails - ReleaseServiceMem(&retServices[i]); - retServices[i].attribute |= SERVICE_ATTR_INVALID; - printf("[Init] InitReadCfg, parse information for service %d failed.\n", i); - continue; - } - } - RegisterServices(retServices, servArrSize); + ReadCfgs("/system/etc/init"); + return; } void InitReadCfg() { - // read configuration file in json format - char* fileBuf = ReadFileToBuf(); - if (fileBuf == NULL) { - printf("[Init] InitReadCfg, read file %s failed! err %d.\n", INIT_CONFIGURATION_FILE, errno); - return; - } - - cJSON* fileRoot = cJSON_Parse(fileBuf); - free(fileBuf); - fileBuf = NULL; - - if (fileRoot == NULL) { - printf("[Init] InitReadCfg, parse failed! please check file %s format.\n", INIT_CONFIGURATION_FILE); - return; - } - - // parse services - ParseAllServices(fileRoot); - - // parse jobs - ParseAllJobs(fileRoot); - - // release memory - cJSON_Delete(fileRoot); + ParseInitCfg(INIT_CONFIGURATION_FILE); + ParseOtherCfgs(); + printf("[init], Parse init config file done.\n"); + DumpAllServices(); + // DumpAllJobs(); // do jobs DoJob("pre-init"); #ifndef __LINUX__ diff --git a/services/src/init_service.c b/services/src/init_service.c index 3974ea8b8a27e5e34155fd90e2d0b186366639e2..52d516b1c6b1c6e9f5721d513d7d8b7ec560f1dc 100644 --- a/services/src/init_service.c +++ b/services/src/init_service.c @@ -19,12 +19,16 @@ #include #include #include +#include #include #include #include #include "init_adapter.h" +#include "init_cmds.h" +#include "init_log.h" #include "init_perms.h" +#include "init_service_socket.h" #define CAP_NUM 2 @@ -33,6 +37,13 @@ static const int CRASH_TIME_LIMIT = 240; // maximum number of crashes within time CRASH_TIME_LIMIT for one service static const int CRASH_COUNT_LIMIT = 4; +// 240 seconds, 4 minutes +static const int CRITICAL_CRASH_TIME_LIMIT = 240; +// maximum number of crashes within time CRITICAL_CRASH_TIME_LIMIT for one service +static const int CRITICAL_CRASH_COUNT_LIMIT = 4; +static const int MAX_PID_STRING_LENGTH = 50; + + static int SetAllAmbientCapability() { for (int i = 0; i <= CAP_LAST_CAP; ++i) { @@ -48,12 +59,17 @@ static int SetPerms(const Service *service) if (KeepCapability() != 0) { return SERVICE_FAILURE; } - if (setgroups(service->servPerm.gidsCnt, service->servPerm.gIDs) != 0) { + + if (setgroups(service->servPerm.gIDCnt, service->servPerm.gIDArray) != 0) { + INIT_LOGE("[Init] SetPerms, setgroups failed. errno = %d, gIDCnt=%d\n", errno, service->servPerm.gIDCnt); return SERVICE_FAILURE; } - if (setuid(service->servPerm.uID) != 0) { - return SERVICE_FAILURE; + if (service->servPerm.uID != 0) { + if (setuid(service->servPerm.uID) != 0) { + printf("[Init] setuid of service: %s failed, uid = %d\n", service->name, service->servPerm.uID); + return SERVICE_FAILURE; + } } // umask call always succeeds and return the previous mask value which is not needed here @@ -79,6 +95,7 @@ static int SetPerms(const Service *service) } if (capset(&capHeader, capData) != 0) { + printf("[Init][Debug], capset faild for service: %s, error: %d\n", service->name, errno); return SERVICE_FAILURE; } for (unsigned int i = 0; i < service->servPerm.capsCnt; ++i) { @@ -86,6 +103,7 @@ static int SetPerms(const Service *service) return SetAllAmbientCapability(); } if (SetAmbientCapability(service->servPerm.caps[i]) != 0) { + printf("[Init][Debug], SetAmbientCapability faild for service: %s\n", service->name); return SERVICE_FAILURE; } } @@ -112,19 +130,56 @@ int ServiceStart(Service *service) service->name, service->pathArgs[0]); return SERVICE_FAILURE; } - + int ret = 0; int pid = fork(); if (pid == 0) { + if (service->socketCfg != NULL) { // start socket service + printf("[init] Create socket \n"); + ret = DoCreateSocket(service->socketCfg); + if (ret < 0) { + return SERVICE_FAILURE; + } + } // permissions if (SetPerms(service) != SERVICE_SUCCESS) { - printf("[Init] service %s exit! set perms failed! err %d.\n", service->name, errno); + INIT_LOGE("[Init] service %s exit! set perms failed! err %d.\n", service->name, errno); _exit(0x7f); // 0x7f: user specified } + char pidString[MAX_PID_STRING_LENGTH]; // writepid + pid_t childPid = getpid(); + if (snprintf(pidString, MAX_PID_STRING_LENGTH, "%d", childPid) <= 0) { + INIT_LOGE("[Init] start service writepid sprintf failed.\n"); + return SERVICE_FAILURE; + } + for (int i = 0; i < MAX_WRITEPID_FILES; i++) { + if (service->writepidFiles[i] == NULL) { + continue; + } + FILE *fd = fopen(service->writepidFiles[i], "wb"); + if (fd == NULL) { + INIT_LOGE("[Init] start service writepidFiles %s invalid.\n", service->writepidFiles[i]); + continue; + } + if (fwrite(pidString, 1, strlen(pidString), fd) != strlen(pidString)) { + INIT_LOGE("[Init] start service writepid error.file:%s pid:%s\n", service->writepidFiles[i], pidString); + } + fclose(fd); + printf("[Init] ServiceStart writepid filename=%s, childPid=%s, ok\n", service->writepidFiles[i], pidString); + } + printf("[init] service->name is %s \n", service->name); +#ifndef OHOS_LITE + // L2 Can not be reset env + if (execv(service->pathArgs[0], service->pathArgs) != 0) { + printf("[Init] service %s execve failed! err %d.\n", service->name, errno); + } +#else char* env[] = {"LD_LIBRARY_PATH=/storage/app/libs", NULL}; if (execve(service->pathArgs[0], service->pathArgs, env) != 0) { printf("[Init] service %s execve failed! err %d.\n", service->name, errno); } +#endif + _exit(0x7f); // 0x7f: user specified } else if (pid < 0) { printf("[Init] start service %s fork failed!\n", service->name); @@ -158,6 +213,45 @@ int ServiceStop(Service *service) return SERVICE_SUCCESS; } +// the service need to be restarted, if it crashed more than 4 times in 4 minutes +void CheckCritical(Service *service) +{ + if (service->attribute & SERVICE_ATTR_CRITICAL) { // critical + // crash time and count check + time_t curTime = time(NULL); + if (service->criticalCrashCnt == 0) { + service->firstCriticalCrashTime = curTime; + ++service->criticalCrashCnt; + } else if (difftime(curTime, service->firstCriticalCrashTime) > CRITICAL_CRASH_TIME_LIMIT) { + service->firstCriticalCrashTime = curTime; + service->criticalCrashCnt = 1; + } else { + ++service->criticalCrashCnt; + if (service->criticalCrashCnt > CRITICAL_CRASH_COUNT_LIMIT) { + INIT_LOGE("[Init] reap critical service %s, crash too many times! Need reboot!\n", service->name); + RebootSystem(); + } + } + } +} + +static int ExecRestartCmd(const Service *service) +{ + printf("[init] ExecRestartCmd \n"); + if ((service == NULL) || (service->onRestart == NULL) || (service->onRestart->cmdLine == NULL)) { + return SERVICE_FAILURE; + } + + for (int i = 0; i < service->onRestart->cmdNum; i++) { + printf("[init] SetOnRestart cmdLine->name %s cmdLine->cmdContent %s \n", service->onRestart->cmdLine[i].name, + service->onRestart->cmdLine[i].cmdContent); + DoCmd(&service->onRestart->cmdLine[i]); + } + free(service->onRestart->cmdLine); + free(service->onRestart); + return SERVICE_SUCCESS; +} + void ServiceReap(Service *service) { if (service == NULL) { @@ -166,7 +260,6 @@ void ServiceReap(Service *service) } service->pid = -1; - // stopped by system-init itself, no need to restart even if it is not one-shot service if (service->attribute & SERVICE_ATTR_NEED_STOP) { service->attribute &= (~SERVICE_ATTR_NEED_STOP); @@ -203,9 +296,17 @@ void ServiceReap(Service *service) } } - int ret = ServiceStart(service); + CheckCritical(service); + int ret = 0; + if (service->onRestart != NULL) { + ret = ExecRestartCmd(service); + if (ret != SERVICE_SUCCESS) { + printf("[Init] SetOnRestart fail \n"); + } + } + ret = ServiceStart(service); if (ret != SERVICE_SUCCESS) { - printf("[Init] reap service %s start failed!\n", service->name); + INIT_LOGE("[Init] reap service %s start failed!\n", service->name); } service->attribute &= (~SERVICE_ATTR_NEED_RESTART); diff --git a/services/src/init_service_manager.c b/services/src/init_service_manager.c index 2a7cbcbbe4cc13e82cac5accd1d0639b1e907bbf..5606a80882ff29abc309fc5ecd702d3dc67ee3f4 100755 --- a/services/src/init_service_manager.c +++ b/services/src/init_service_manager.c @@ -1,91 +1,785 @@ -/* - * Copyright (c) 2020 Huawei Device Co., Ltd. - * 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. - */ - -#include "init_service_manager.h" - -#include -#include -#include - -#include "init_adapter.h" -#include "init_jobs.h" - - -// All serivce processes that init will fork+exec. -static Service* g_services = NULL; -static int g_servicesCnt = 0; - -void RegisterServices(Service* services, int servicesCnt) -{ - g_services = services; - g_servicesCnt = servicesCnt; -} - -static int FindServiceByName(const char* servName) -{ - if (servName == NULL) { - return -1; - } - - for (int i = 0; i < g_servicesCnt; ++i) { - if (strlen(g_services[i].name) == strlen(servName) && - strncmp(g_services[i].name, servName, strlen(g_services[i].name)) == 0) { - return i; - } - } - return -1; -} - -void StartServiceByName(const char* servName) -{ - // find service by name - int servIdx = FindServiceByName(servName); - if (servIdx < 0) { - printf("[Init] StartServiceByName, cannot find service %s.\n", servName); - return; - } - - if (ServiceStart(&g_services[servIdx]) != SERVICE_SUCCESS) { - printf("[Init] StartServiceByName, service %s start failed!\n", g_services[servIdx].name); - } - - return; -} - -void StopAllServices() -{ - for (int i = 0; i < g_servicesCnt; i++) { - if (ServiceStop(&g_services[i]) != SERVICE_SUCCESS) { - printf("[Init] StopAllServices, service %s stop failed!\n", g_services[i].name); - } - } -} - -void ReapServiceByPID(int pid) -{ - for (int i = 0; i < g_servicesCnt; i++) { - if (g_services[i].pid == pid) { - if (g_services[i].attribute & SERVICE_ATTR_IMPORTANT) { - // important process exit, need to reboot system - g_services[i].pid = -1; - StopAllServices(); - RebootSystem(); - } - ServiceReap(&g_services[i]); - break; - } - } -} - +/* + * Copyright (c) 2020 Huawei Device Co., Ltd. + * 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. + */ + +#include "init_service_manager.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cJSON.h" +#include "init_adapter.h" +#include "init_jobs.h" +#include "init_log.h" +#include "init_perms.h" +#include "init_read_cfg.h" +#include "init_capability.h" +#include "init_service_socket.h" +#include "init_utils.h" +#include "securec.h" + +// All serivce processes that init will fork+exec. +static Service* g_services = NULL; +static int g_servicesCnt = 0; + +void DumpAllServices() +{ + printf("[Init] Ready to dump all services:\n"); + printf("[Init] total service number: %d\n", g_servicesCnt); + for (int i = 0; i < g_servicesCnt; i++) { + printf("\tservice name: [%s]\n", g_services[i].name); + printf("\tpath :"); + for (int j = 0; j < g_services[i].pathArgsCnt; j++) { + printf(" %s", g_services[i].pathArgs[j]); + } + printf("\n"); + } + printf("[Init] Dump all services finished\n"); +} + +void RegisterServices(Service* services, int servicesCnt) +{ + g_services = services; + g_servicesCnt += servicesCnt; +} + +static void ReleaseServiceMem(Service* curServ) +{ + if (curServ->pathArgs != NULL) { + for (int i = 0; i < curServ->pathArgsCnt; ++i) { + if (curServ->pathArgs[i] != NULL) { + free(curServ->pathArgs[i]); + curServ->pathArgs[i] = NULL; + } + } + free(curServ->pathArgs); + curServ->pathArgs = NULL; + } + curServ->pathArgsCnt = 0; + + if (curServ->servPerm.caps != NULL) { + free(curServ->servPerm.caps); + curServ->servPerm.caps = NULL; + } + curServ->servPerm.capsCnt = 0; + + for (int i = 0; i < MAX_WRITEPID_FILES; i++) { + if (curServ->writepidFiles[i] != NULL) { + free(curServ->writepidFiles[i]); + curServ->writepidFiles[i] = NULL; + } + } + + if (curServ->servPerm.gIDArray != NULL) { + free(curServ->servPerm.gIDArray); + curServ->servPerm.gIDArray = NULL; + } + curServ->servPerm.gIDCnt = 0; +} + +static int GetServiceName(const cJSON* curArrItem, Service* curServ) +{ + char* fieldStr = cJSON_GetStringValue(cJSON_GetObjectItem(curArrItem, "name")); + if (fieldStr == NULL) { + INIT_LOGE("[init] GetServiceName cJSON_GetStringValue error\n"); + return SERVICE_FAILURE; + } + + size_t strLen = strlen(fieldStr); + if (strLen == 0 || strLen > MAX_SERVICE_NAME) { + INIT_LOGE("[init] GetServiceName strLen = %d, error\n", strLen); + return SERVICE_FAILURE; + } + + if (memcpy_s(curServ->name, MAX_SERVICE_NAME, fieldStr, strLen) != EOK) { + INIT_LOGE("[init] GetServiceName memcpy_s error\n"); + return SERVICE_FAILURE; + } + curServ->name[strLen] = '\0'; + return SERVICE_SUCCESS; +} + +static int IsForbidden(const char* fieldStr) +{ + size_t fieldLen = strlen(fieldStr); + size_t forbidStrLen = strlen(BIN_SH_NOT_ALLOWED); + if (fieldLen == forbidStrLen) { + if (strncmp(fieldStr, BIN_SH_NOT_ALLOWED, fieldLen) == 0) { + return 1; + } + return 0; + } else if (fieldLen > forbidStrLen) { + // "/bin/shxxxx" is valid but "/bin/sh xxxx" is invalid + if (strncmp(fieldStr, BIN_SH_NOT_ALLOWED, forbidStrLen) == 0) { + if (fieldStr[forbidStrLen] == ' ') { + return 1; + } + } + return 0; + } else { + return 0; + } +} + +// TODO: move this function to common files +static cJSON* GetArrItem(const cJSON* fileRoot, int* arrSize, const char* arrName) +{ + cJSON* arrItem = cJSON_GetObjectItemCaseSensitive(fileRoot, arrName); + if (!cJSON_IsArray(arrItem)) { + printf("[Init] GetArrItem, item %s is not an array!\n", arrName); + return NULL; + } + + *arrSize = cJSON_GetArraySize(arrItem); + if (*arrSize <= 0) { + return NULL; + } + return arrItem; +} + +static int GetWritepidStrings(const cJSON *curArrItem, Service *curServ) // writepid +{ + int writepidCnt = 0; + cJSON* filedJ = GetArrItem(curArrItem, &writepidCnt, "writepid"); + if (writepidCnt <= 0) { // not item is ok. + return SERVICE_SUCCESS; + } + + if (writepidCnt > MAX_WRITEPID_FILES) { + INIT_LOGE("[Init] GetWritepidStrings, too many writepid[cnt %d] for one service, should not exceed %d.\n", + writepidCnt, MAX_WRITEPID_FILES); + return SERVICE_FAILURE; + } + + for (int i = 0; i < writepidCnt; ++i) { + if (!cJSON_GetArrayItem(filedJ, i) || !cJSON_GetStringValue(cJSON_GetArrayItem(filedJ, i)) + || strlen(cJSON_GetStringValue(cJSON_GetArrayItem(filedJ, i))) <= 0) { // check all errors + INIT_LOGE("[Init] GetWritepidStrings, parse item[%d] error.\n", i); + return SERVICE_FAILURE; + } + + char *fieldStr = cJSON_GetStringValue(cJSON_GetArrayItem(filedJ, i)); + size_t strLen = strlen(fieldStr); + curServ->writepidFiles[i] = (char *)malloc(sizeof(char) * strLen + 1); + if (curServ->writepidFiles[i] == NULL) { + INIT_LOGE("[Init] GetWritepidStrings, malloc item[%d] error.\n", i); + return SERVICE_FAILURE; + } + if (memcpy_s(curServ->writepidFiles[i], strLen + 1, fieldStr, strLen) != EOK) { + INIT_LOGE("[Init] GetWritepidStrings, memcpy_s error.\n"); + return SERVICE_FAILURE; + } + curServ->writepidFiles[i][strLen] = '\0'; + } + + return SERVICE_SUCCESS; +} + +static int GetGidOneItem(const cJSON *curArrItem, Service *curServ) // gid one item +{ + cJSON* filedJ = cJSON_GetObjectItem(curArrItem, GID_STR_IN_CFG); + if (filedJ == NULL) { + INIT_LOGE("[Init] GetGidOneItem, gid is not an item.\n"); + return SERVICE_FAILURE; // not found + } + curServ->servPerm.gIDCnt = 1; + curServ->servPerm.gIDArray = (gid_t *)malloc(sizeof(gid_t)); + if (curServ->servPerm.gIDArray == NULL) { + INIT_LOGE("[Init] GetGidOneItem, can't malloc, error.\n"); + return SERVICE_FAILURE; + } + + if (cJSON_IsString(filedJ)) { + char* fieldStr = cJSON_GetStringValue(filedJ); + gid_t gID = DecodeUid(fieldStr); + if (gID == (gid_t)(-1)) { + INIT_LOGE("[Init] GetGidOneItem, DecodeUid %s error.\n", fieldStr); + return SERVICE_FAILURE; + } + curServ->servPerm.gIDArray[0] = gID; + return SERVICE_SUCCESS; + } + + if (cJSON_IsNumber(filedJ)) { + gid_t gID = (int)cJSON_GetNumberValue(filedJ); + if (gID < 0) { + INIT_LOGE("[Init] GetGidOneItem, gID = %d error.\n", gID); + return SERVICE_FAILURE; + } + curServ->servPerm.gIDArray[0] = gID; + return SERVICE_SUCCESS; + } + + INIT_LOGE("[Init] GetGidOneItem, this gid is neither a string nor a number, error.\n"); + return SERVICE_FAILURE; +} + +static int GetGidArray(const cJSON *curArrItem, Service *curServ) // gid array +{ + int gIDCnt = 0; + cJSON* filedJ = GetArrItem(curArrItem, &gIDCnt, GID_STR_IN_CFG); // "gid" must have 1 item. + if (gIDCnt <= 0) { // not a array, but maybe a item? + INIT_LOGE("[Init] GetGidArray, gid is not a list.\n"); + return GetGidOneItem(curArrItem, curServ); + } + + if (gIDCnt > NGROUPS_MAX + 1) { + INIT_LOGE("[Init] GetGidArray, too many gids[cnt %d] for one service, should not exceed %d.\n", + gIDCnt, NGROUPS_MAX + 1); + return SERVICE_FAILURE; + } + + curServ->servPerm.gIDArray = (gid_t *)malloc(sizeof(gid_t) * gIDCnt); + if (curServ->servPerm.gIDArray == NULL) { + INIT_LOGE("[init] GetGidArray malloc error\n"); + return SERVICE_FAILURE; + } + curServ->servPerm.gIDCnt = gIDCnt; + int i = 0; + for (; i < gIDCnt; ++i) { + if (cJSON_GetArrayItem(filedJ, i) == NULL || !cJSON_GetStringValue(cJSON_GetArrayItem(filedJ, i)) + || strlen(cJSON_GetStringValue(cJSON_GetArrayItem(filedJ, i))) <= 0) { // check all errors + INIT_LOGE("[Init] GetGidArray, parse item[%d] as string, error.\n", i); + break; + } + char* fieldStr = cJSON_GetStringValue(cJSON_GetArrayItem(filedJ, i)); + gid_t gID = DecodeUid(fieldStr); + if ((gID) == (gid_t)(-1)) { + INIT_LOGE("[Init] GetGidArray, DecodeUid item[%d] error.\n", i); + return SERVICE_FAILURE; + } + curServ->servPerm.gIDArray[i] = gID; + } + if (i == gIDCnt) { + return SERVICE_SUCCESS; + } + for (i = 0; i < gIDCnt; ++i) { + if (cJSON_GetArrayItem(filedJ, i) == NULL || !cJSON_IsNumber(cJSON_GetArrayItem(filedJ, i))) { + INIT_LOGE("[Init] GetGidArray, parse item[%d] as number, error.\n", i); + break; + } + gid_t gID = (int)cJSON_GetNumberValue(cJSON_GetArrayItem(filedJ, i)); + if (gID < 0) { + INIT_LOGE("[init] GetGidArray gID = %d, error\n", gID); + break; + } + curServ->servPerm.gIDArray[i] = gID; + } + int ret = i == gIDCnt ? SERVICE_SUCCESS : SERVICE_FAILURE; + return ret; +} + +static int GetServicePathAndArgs(const cJSON* curArrItem, Service* curServ) +{ + cJSON* pathItem = cJSON_GetObjectItem(curArrItem, "path"); + if (!cJSON_IsArray(pathItem)) { + INIT_LOGE("[init] GetServicePathAndArgs path item not found or not a array\n"); + return SERVICE_FAILURE; + } + + int arrSize = cJSON_GetArraySize(pathItem); + if (arrSize <= 0 || arrSize > MAX_PATH_ARGS_CNT) { // array size invalid + INIT_LOGE("[init] GetServicePathAndArgs arrSize = %d, error\n", arrSize); + return SERVICE_FAILURE; + } + + curServ->pathArgs = (char**)malloc((arrSize + 1) * sizeof(char*)); + if (curServ->pathArgs == NULL) { + INIT_LOGE("[init] GetServicePathAndArgs malloc 1 error\n"); + return SERVICE_FAILURE; + } + for (int i = 0; i < arrSize + 1; ++i) { + curServ->pathArgs[i] = NULL; + } + curServ->pathArgsCnt = arrSize + 1; + + for (int i = 0; i < arrSize; ++i) { + char* curParam = cJSON_GetStringValue(cJSON_GetArrayItem(pathItem, i)); + if (curParam == NULL || strlen(curParam) > MAX_ONE_ARG_LEN) { + // resources will be released by function: ReleaseServiceMem + if (curParam == NULL) { + INIT_LOGE("[init] GetServicePathAndArgs curParam == NULL, error\n"); + } else { + INIT_LOGE("[init] GetServicePathAndArgs strlen = %d, error\n", strlen(curParam)); + } + return SERVICE_FAILURE; + } + + if (i == 0 && IsForbidden(curParam)) { + // resources will be released by function: ReleaseServiceMem + INIT_LOGE("[init] GetServicePathAndArgs i == 0 && IsForbidden, error\n"); + return SERVICE_FAILURE; + } + + size_t paramLen = strlen(curParam); + curServ->pathArgs[i] = (char*)malloc(paramLen + 1); + if (curServ->pathArgs[i] == NULL) { + // resources will be released by function: ReleaseServiceMem + INIT_LOGE("[init] GetServicePathAndArgs i == 0 && IsForbidden, error\n"); + return SERVICE_FAILURE; + } + + if (memcpy_s(curServ->pathArgs[i], paramLen + 1, curParam, paramLen) != EOK) { + // resources will be released by function: ReleaseServiceMem + INIT_LOGE("[init] GetServicePathAndArgs malloc 2 error.\n"); + return SERVICE_FAILURE; + } + curServ->pathArgs[i][paramLen] = '\0'; + } + return SERVICE_SUCCESS; +} + +static int GetServiceNumber(const cJSON* curArrItem, Service* curServ, const char* targetField) +{ + cJSON* filedJ = cJSON_GetObjectItem(curArrItem, targetField); + if (filedJ == NULL && (strncmp(targetField, CRITICAL_STR_IN_CFG, strlen(CRITICAL_STR_IN_CFG)) == 0 + || strncmp(targetField, DISABLED_STR_IN_CFG, strlen(DISABLED_STR_IN_CFG)) == 0 + || strncmp(targetField, ONCE_STR_IN_CFG, strlen(ONCE_STR_IN_CFG)) == 0 + || strncmp(targetField, IMPORTANT_STR_IN_CFG, strlen(IMPORTANT_STR_IN_CFG)) == 0)) { + return SERVICE_SUCCESS; // not found "critical","disabled","once","importance" item is ok + } + + if (!cJSON_IsNumber(filedJ)) { + INIT_LOGE("[Init] GetServiceNumber, %s is null or is not a number, error.\n", targetField); + return SERVICE_FAILURE; + } + + int value = (int)cJSON_GetNumberValue(filedJ); + if (value < 0) { + INIT_LOGE("[Init] GetServiceNumber, value = %d, error.\n", value); + return SERVICE_FAILURE; + } + + if (strncmp(targetField, ONCE_STR_IN_CFG, strlen(ONCE_STR_IN_CFG)) == 0) { + if (value != 0) { + curServ->attribute |= SERVICE_ATTR_ONCE; + } + } else if (strncmp(targetField, IMPORTANT_STR_IN_CFG, strlen(IMPORTANT_STR_IN_CFG)) == 0) { + if (value != 0) { + curServ->attribute |= SERVICE_ATTR_IMPORTANT; + } + } else if (strncmp(targetField, CRITICAL_STR_IN_CFG, strlen(CRITICAL_STR_IN_CFG)) == 0) { // set critical + curServ->attribute &= ~SERVICE_ATTR_CRITICAL; + if (value == 1) { + curServ->attribute |= SERVICE_ATTR_CRITICAL; + } + } else if (strncmp(targetField, DISABLED_STR_IN_CFG, strlen(DISABLED_STR_IN_CFG)) == 0) { // set disabled + curServ->attribute &= ~SERVICE_ATTR_DISABLED; + if (value == 1) { + curServ->attribute |= SERVICE_ATTR_DISABLED; + } + } else { + INIT_LOGE("[Init] GetServiceNumber, item = %s, not expected, error.\n", targetField); + return SERVICE_FAILURE; + } + return SERVICE_SUCCESS; +} + +static int GetUidStringNumber(const cJSON *curArrItem, Service *curServ) +{ + cJSON* filedJ = cJSON_GetObjectItem(curArrItem, UID_STR_IN_CFG); + if (filedJ == NULL) { + INIT_LOGE("[Init] GetUidStringNumber, %s not found, error.\n", UID_STR_IN_CFG); + return SERVICE_FAILURE; // not found + } + + if (cJSON_IsString(filedJ)) { + char* fieldStr = cJSON_GetStringValue(filedJ); + int uID = DecodeUid(fieldStr); + if (uID < 0) { + INIT_LOGE("[Init] GetUidStringNumber, DecodeUid %s error.\n", fieldStr); + return SERVICE_FAILURE; + } + curServ->servPerm.uID = uID; + return SERVICE_SUCCESS; + } + + if (cJSON_IsNumber(filedJ)) { + int uID = (int)cJSON_GetNumberValue(filedJ); + if (uID < 0) { + INIT_LOGE("[Init] GetUidStringNumber, uID = %d error.\n", uID); + return SERVICE_FAILURE; + } + curServ->servPerm.uID = uID; + return SERVICE_SUCCESS; + } + + INIT_LOGE("[Init] GetUidStringNumber, this uid is neither a string nor a number, error.\n"); + return SERVICE_FAILURE; +} + +static int SplitString(char *srcPtr, char **dstPtr) +{ + if (!srcPtr) { + return -1; + } + char *buf = NULL; + dstPtr[0] = strtok_r(srcPtr, " ", &buf); + int i = 0; + while (dstPtr[i]) + { + i++; + dstPtr[i] = strtok_r(NULL, " ", &buf); + } + dstPtr[i] = "\0"; + int num = i; + for (int j = 0; j < num; j++) + { + printf("dstPtr[%d] is %s \n", j, dstPtr[j]); + } + return num; +} + +#define MAX_SOCK_NAME_LEN 16 +#define SOCK_OPT_NUMS 6 +enum SockOptionTab +{ + SERVICE_SOCK_NAME = 0, + SERVICE_SOCK_TYPE, + SERVICE_SOCK_PERM, + SERVICE_SOCK_UID, + SERVICE_SOCK_GID, + SERVICE_SOCK_SETOPT +}; + +static int ParseServiceSocket(char **opt, const int optNum, struct ServiceSocket *sockopt) +{ + printf("[init] ParseServiceSocket\n"); + if (optNum != SOCK_OPT_NUMS) { + return -1; + } + sockopt->name = (char *)calloc(MAX_SOCK_NAME_LEN, sizeof(char)); + if (sockopt->name == NULL) { + return -1; + } + if (opt[SERVICE_SOCK_NAME] == NULL) { + return -1; + } + printf("[init] ParseServiceSocket SERVICE_SOCK_NAME is %s \n", opt[SERVICE_SOCK_NAME]); + int ret = memcpy_s(sockopt->name, MAX_SOCK_NAME_LEN, opt[SERVICE_SOCK_NAME], MAX_SOCK_NAME_LEN - 1); + if (ret != 0) { + return -1; + } + + if (opt[SERVICE_SOCK_TYPE] == NULL) { + return -1; + } + printf("[init] ParseServiceSocket SERVICE_SOCK_TYPE is %s \n", opt[SERVICE_SOCK_TYPE]); + sockopt->type = + strncmp(opt[SERVICE_SOCK_TYPE], "stream", strlen(opt[SERVICE_SOCK_TYPE])) == 0 ? SOCK_STREAM : + (strncmp(opt[SERVICE_SOCK_TYPE], "dgram", strlen(opt[SERVICE_SOCK_TYPE])) == 0 ? SOCK_DGRAM : SOCK_SEQPACKET); + + if (opt[SERVICE_SOCK_PERM] == NULL) { + return -1; + } + printf("[init] ParseServiceSocket SERVICE_SOCK_PERM is %s \n", opt[SERVICE_SOCK_PERM]); + sockopt->perm = strtoul(opt[SERVICE_SOCK_PERM], 0, 8); //a??a8???? + + if (opt[SERVICE_SOCK_UID] == NULL) { + return -1; + } + printf("[init] ParseServiceSocket SERVICE_SOCK_UID is %s \n", opt[SERVICE_SOCK_UID]); + int uuid = DecodeUid(opt[SERVICE_SOCK_UID]); + if (uuid < 0) { + return -1; + } + sockopt->uid = uuid; + printf("[init] ParseServiceSocket uuid is %d \n", uuid); + + if (opt[SERVICE_SOCK_GID] == NULL) { + return -1; + } + printf("[init] ParseServiceSocket SERVICE_SOCK_GID is %s \n", opt[SERVICE_SOCK_GID]); + int ggid = DecodeUid(opt[SERVICE_SOCK_GID]); + if (ggid < 0) { + return -1; + } + sockopt->gid = ggid; + printf("[init] ParseServiceSocket ggid is %d \n", ggid); + + if (opt[SERVICE_SOCK_SETOPT] == NULL) { + return -1; + } + printf("[init] ParseServiceSocket SERVICE_SOCK_SETOPT is %s \n", opt[SERVICE_SOCK_SETOPT]); + sockopt->passcred = strncmp(opt[SERVICE_SOCK_SETOPT], "passcred", strlen(opt[SERVICE_SOCK_SETOPT])) == 0 ? true : false; + sockopt->next = NULL; + return 0; +} + +static int GetServiceSocket(const cJSON* curArrItem, Service* curServ) +{ + printf("[init] GetServiceSocket \n"); + cJSON* filedJ = cJSON_GetObjectItem(curArrItem, "socket"); + if (!cJSON_IsArray(filedJ)) { + return SERVICE_FAILURE; + } + + int sockCnt = cJSON_GetArraySize(filedJ); + if (sockCnt <= 0) { + return SERVICE_FAILURE; + } + printf("[init] GetServiceSocket sockCnt is %d \n", sockCnt); + curServ->socketCfg = NULL; + for (int i = 0; i < sockCnt; ++i) { + cJSON* sockJ = cJSON_GetArrayItem(filedJ, i); + if (!cJSON_IsString(sockJ) || !cJSON_GetStringValue(sockJ)) { + return SERVICE_FAILURE; + } + char *sockStr = cJSON_GetStringValue(sockJ); + char *tmpStr[SOCK_OPT_NUMS] = {NULL,}; + int num = SplitString(sockStr, tmpStr); + if (num != SOCK_OPT_NUMS) { + return SERVICE_FAILURE; + } + printf("[init] GetServiceSocket num is %d \n", num); + struct ServiceSocket *socktmp = (struct ServiceSocket *)calloc(1, sizeof(struct ServiceSocket)); + if (!socktmp) { + return SERVICE_FAILURE; + } + printf("[init] ParseServiceSocket\n"); + int ret = ParseServiceSocket(tmpStr, SOCK_OPT_NUMS, socktmp); + if (ret < 0) { + return SERVICE_FAILURE; + } + if (curServ->socketCfg == NULL) { + curServ->socketCfg = socktmp; + } else { + socktmp->next = curServ->socketCfg->next; + curServ->socketCfg->next = socktmp; + } + } + return SERVICE_SUCCESS; +} + +static int GetServiceOnRestart(const cJSON* curArrItem, Service* curServ) +{ + printf("[init] GetServiceOnRestart \n"); + cJSON* filedJ = cJSON_GetObjectItem(curArrItem, "onrestart"); + if (!cJSON_IsArray(filedJ)) { + return SERVICE_FAILURE; + } + int cmdCnt = cJSON_GetArraySize(filedJ); + if (cmdCnt <= 0) { + return SERVICE_FAILURE; + } + curServ->onRestart = (struct OnRestartCmd *)calloc(1, sizeof(struct OnRestartCmd)); + if (curServ->onRestart == NULL) { + return SERVICE_FAILURE; + } + curServ->onRestart->cmdLine = (CmdLine *)calloc(cmdCnt, sizeof(CmdLine)); + if (curServ->onRestart->cmdLine == NULL) { + return SERVICE_FAILURE; + } + curServ->onRestart->cmdNum = cmdCnt; + for (int i = 0; i < cmdCnt; ++i) { + cJSON* cmdJ = cJSON_GetArrayItem(filedJ, i); + if (!cJSON_IsString(cmdJ) || !cJSON_GetStringValue(cmdJ)) { + return SERVICE_FAILURE; + } + char *cmdStr = cJSON_GetStringValue(cmdJ); + ParseCmdLine(cmdStr, &curServ->onRestart->cmdLine[i]); + printf("[init] SetOnRestart cmdLine->name %s cmdLine->cmdContent %s \n", curServ->onRestart->cmdLine[i].name, + curServ->onRestart->cmdLine[i].cmdContent); + } + return SERVICE_SUCCESS; +} + +static int CheckServiceKeyName(const cJSON* curService) +{ + char *cfgServiceKeyList[] = {"name", "path", "uid", "gid", "once", + "importance", "caps", "disabled", "writepid", "critical", "socket", + }; + if (curService == NULL) { + return SERVICE_FAILURE; + } + cJSON *child = curService->child; + if (child == NULL) { + return SERVICE_FAILURE; + } + while (child) { + int i = 0; + int keyListSize = sizeof(cfgServiceKeyList) / sizeof(char *); + for (; i < keyListSize; i++) { + if (!strcmp(child->string, cfgServiceKeyList[i])) { + break; + } + } + if(i < keyListSize) { + child = child->next; + } else { + INIT_LOGE("[Init] CheckServiceKeyName, key name %s is not found. error.\n", child->string); + return SERVICE_FAILURE; + } + } + return SERVICE_SUCCESS; +} + +void ParseAllServices(const cJSON* fileRoot) +{ + int servArrSize = 0; + cJSON* serviceArr = GetArrItem(fileRoot, &servArrSize, SERVICES_ARR_NAME_IN_JSON); + if (serviceArr == NULL) { + printf("[Init] InitReadCfg, get array %s failed.\n", SERVICES_ARR_NAME_IN_JSON); + return; + } + + printf("[Init] servArrSize is %d \n", servArrSize); + if (servArrSize > MAX_SERVICES_CNT_IN_FILE) { + printf("[Init] InitReadCfg, too many services[cnt %d] detected, should not exceed %d.\n", + servArrSize, MAX_SERVICES_CNT_IN_FILE); + return; + } + + Service* retServices = (Service*)realloc(g_services, sizeof(Service) * (g_servicesCnt + servArrSize)); + if (retServices == NULL) { + printf("[Init] InitReadCfg, realloc for %s arr failed! %d.\n", SERVICES_ARR_NAME_IN_JSON, servArrSize); + return; + } + // Skip already saved services, + Service* tmp = retServices + g_servicesCnt; + if (memset_s(tmp, sizeof(Service) * servArrSize, 0, sizeof(Service) * servArrSize) != EOK) { + free(retServices); + retServices = NULL; + return; + } + + for (int i = 0; i < servArrSize; ++i) { + cJSON* curItem = cJSON_GetArrayItem(serviceArr, i); + if (CheckServiceKeyName(curItem) != SERVICE_SUCCESS) { + ReleaseServiceMem(&tmp[i]); + tmp[i].attribute |= SERVICE_ATTR_INVALID; + continue; + } + tmp[i].servPerm.uID = (uid_t)(-1); // set 0xffffffff as init value + int ret1 = GetServiceName(curItem, &tmp[i]); + int ret2 = GetServicePathAndArgs(curItem, &tmp[i]); + int ret3 = GetUidStringNumber(curItem, &tmp[i]); // uid in string or number form + int ret4 = GetGidArray(curItem, &tmp[i]); // gid array + int ret5 = GetServiceNumber(curItem, &tmp[i], ONCE_STR_IN_CFG); + int ret6 = GetServiceNumber(curItem, &tmp[i], IMPORTANT_STR_IN_CFG); + int ret7 = GetServiceNumber(curItem, &tmp[i], CRITICAL_STR_IN_CFG); // critical + int ret8 = GetServiceNumber(curItem, &tmp[i], DISABLED_STR_IN_CFG); // disabled + int ret9 = GetWritepidStrings(curItem, &tmp[i]); // writepid + int reta = GetServiceCaps(curItem, &tmp[i]); + int retAll = ret1 | ret2 | ret3 | ret4 | ret5 | ret6 | ret7 | ret8 | ret9 | reta; + if (retAll != SERVICE_SUCCESS) { + // release resources if it fails + ReleaseServiceMem(&tmp[i]); + tmp[i].attribute |= SERVICE_ATTR_INVALID; + printf("[Init] InitReadCfg, parse information for service %d failed. ", i); + printf("service name = [%s]\n", tmp[i].name); + printf("ret1=%d,ret2=%d,ret3=%d,ret4=%d,ret5=%d,ret6=%d,ret7=%d,ret8=%d,ret9=%d,reta=%d,", + ret1, ret2, ret3, ret4, ret5, ret6, ret7, ret8, ret9, reta); + continue; + } else { + printf("[init] ParseAllServices ParseAllServices Service[%d] name=%s, uid=%d, critical=%d, disabled=%d\n", + i, tmp[i].name, tmp[i].servPerm.uID, tmp[i].attribute & SERVICE_ATTR_CRITICAL ? 1 : 0, + tmp[i].attribute & SERVICE_ATTR_DISABLED ? 1 : 0); + for(int j = 0; j < tmp[i].servPerm.gIDCnt; j++) { + printf("\t\tgIDArray[%d] = %d\n", j, tmp[i].servPerm.gIDArray[j]); + } + for(int j = 0; j < MAX_WRITEPID_FILES; j++) { + if(tmp[i].writepidFiles[j]) + printf("\t\twritepidFiles[%d] = %s\n", j, tmp[i].writepidFiles[j]); + } + } + if (GetServiceSocket(curItem, &tmp[i]) != SERVICE_SUCCESS) { + // free list + } + if (GetServiceOnRestart(curItem, &tmp[i]) != SERVICE_SUCCESS) { + // free + } + } + // Increase service counter. + RegisterServices(retServices, servArrSize); +} + +static int FindServiceByName(const char* servName) +{ + if (servName == NULL) { + return -1; + } + + for (int i = 0; i < g_servicesCnt; ++i) { + if (strlen(g_services[i].name) == strlen(servName) && + strncmp(g_services[i].name, servName, strlen(g_services[i].name)) == 0) { + return i; + } + } + return -1; +} + +void StartServiceByName(const char* servName) +{ + // find service by name + int servIdx = FindServiceByName(servName); + if (servIdx < 0) { + printf("[Init] StartServiceByName, cannot find service %s.\n", servName); + return; + } + + if (ServiceStart(&g_services[servIdx]) != SERVICE_SUCCESS) { + printf("[Init] StartServiceByName, service %s start failed!\n", g_services[servIdx].name); + } + + return; +} + +void StopServiceByName(const char* servName) +{ + // find service by name + int servIdx = FindServiceByName(servName); + if (servIdx < 0) { + INIT_LOGE("[Init] StopServiceByName, cannot find service %s.\n", servName); + return; + } + + if (ServiceStop(&g_services[servIdx]) != SERVICE_SUCCESS) { + INIT_LOGE("[Init] StopServiceByName, service %s start failed!\n", g_services[servIdx].name); + } + + return; +} + +void StopAllServices() +{ + for (int i = 0; i < g_servicesCnt; i++) { + if (ServiceStop(&g_services[i]) != SERVICE_SUCCESS) { + printf("[Init] StopAllServices, service %s stop failed!\n", g_services[i].name); + } + } +} + +void ReapServiceByPID(int pid) +{ + for (int i = 0; i < g_servicesCnt; i++) { + if (g_services[i].pid == pid) { + if (g_services[i].attribute & SERVICE_ATTR_IMPORTANT) { + // important process exit, need to reboot system + g_services[i].pid = -1; + StopAllServices(); + RebootSystem(); + } + ServiceReap(&g_services[i]); + break; + } + } +} + + diff --git a/services/src/init_service_socket.c b/services/src/init_service_socket.c new file mode 100755 index 0000000000000000000000000000000000000000..df565ca26a1402e2db4815f306bb203b0728b20c --- /dev/null +++ b/services/src/init_service_socket.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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. + */ + +#include "init_service_socket.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HOS_SOCKET_DIR "/dev/unix/socket" +#define HOS_SOCKET_ENV_PREFIX "OHOS_SOCKET_" + +static int CreateSocket(struct ServiceSocket *sockopt) +{ + if (!sockopt || !sockopt->name) { + return -1; + } + printf("[init] CreateSocket\n"); + int sockFd = socket(PF_UNIX, sockopt->type, 0); + if (sockFd < 0) { + printf("[init] socket fail %d \n", errno); + return -1; + } + + struct sockaddr_un addr; + bzero(&addr,sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof(addr.sun_path), HOS_SOCKET_DIR"/%s", + sockopt->name); + printf("[init] addr.sun_path is %s \n", addr.sun_path); + if (access(addr.sun_path, F_OK)) { + printf("[init] %s already exist, remove it\n", addr.sun_path); + unlink(addr.sun_path); + } + if (sockopt->passcred) { + int on = 1; + if (setsockopt(sockFd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) { + unlink(addr.sun_path); + close(sockFd); + return -1; + } + } + + if (bind(sockFd, (struct sockaddr *)&addr, sizeof(addr))) { + printf("[Init] Create socket for service %s failed: %d\n", sockopt->name, errno); + unlink(addr.sun_path); + close(sockFd); + return -1; + } + printf("[init] bind socket success\n"); + if (lchown(addr.sun_path, sockopt->uid, sockopt->gid)) { + unlink(addr.sun_path); + close(sockFd); + printf("[init] lchown fail %d \n", errno); + return -1; + } + + if (fchmodat(AT_FDCWD, addr.sun_path, sockopt->perm, AT_SYMLINK_NOFOLLOW)) { + unlink(addr.sun_path); + close(sockFd); + printf("[Init] fchmodat fail %d \n", errno); + return -1; + } + printf("[Init] CreateSocket success \n"); + return sockFd; +} + +static int SetSocketEnv(int fd, char *name) +{ + printf("[init] SetSocketEnv\n"); + char pubName[64] = {0}; + char val[16] = {0}; + snprintf(pubName, sizeof(pubName), HOS_SOCKET_ENV_PREFIX"%s", name); + printf("[init] pubName is %s \n", pubName); + snprintf(val, sizeof(val), "%d", fd); + printf("[init] val is %s \n", val); + int ret = setenv(pubName, val, 1); + if (ret < 0) { + printf("[init] setenv fail %d \n", errno); + return -1; + } + fcntl(fd, F_SETFD, 0); + return 0; +} + +int DoCreateSocket(struct ServiceSocket *sockopt) +{ + if (!sockopt) { + return -1; + } + printf("[init] DoCreateSocket \n"); + struct ServiceSocket *tmpSock = sockopt; + while (tmpSock) { + printf("[init] tmpSock %p \n", tmpSock); + int fd = CreateSocket(tmpSock); + if (fd < 0) { + return -1; + } + int ret = SetSocketEnv(fd, tmpSock->name); + if (ret < 0) { + return -1; + } + tmpSock = tmpSock->next; + } + return 0; +} + +int GetControlFromEnv(char *path) +{ + if (path == NULL) { + return -1; + } + char *cp = path; + while (*cp) { + if (!isalnum(*cp)) *cp = '_'; + ++cp; + } + const char *val = getenv(path); + if (val == NULL) { + return -1; + } + errno = 0; + int fd = strtol(val, NULL, 10); + if (errno) { + return -1; + } + if (fcntl(fd, F_GETFD) < 0) { + return -1; + } + return fd; +} + +int GetControlSocket(const char *name) +{ + if (name == NULL) { + return -1; + } + char path[128] = {0}; + snprintf(path, sizeof(path), HOS_SOCKET_ENV_PREFIX"%s", name); + int fd = GetControlFromEnv(path); + + struct sockaddr_un addr; + socklen_t addrlen = sizeof(addr); + int ret = getsockname(fd, (struct sockaddr*)&addr, &addrlen); + if (ret < 0) { + return -1; + } + char sockDir[128] = {0}; + snprintf(sockDir, sizeof(sockDir), HOS_SOCKET_DIR"/%s", name); + if (strncmp(sockDir, addr.sun_path, strlen(sockDir)) == 0) { + return fd; + } + return -1; +} + diff --git a/services/src/init_utils.c b/services/src/init_utils.c new file mode 100755 index 0000000000000000000000000000000000000000..e100a68d1029ff1d4209d29b48ea63cb46352cca --- /dev/null +++ b/services/src/init_utils.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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. + */ +#include "init_utils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "init_cmds.h" +#include "init_log.h" +#include "init_utils.h" +#include "securec.h" + +#define MAX_BUF_SIZE 1024 +#ifdef STARTUP_UT +#define LOG_FILE_NAME "/media/sf_ubuntu/test/log.txt" +#else +#define LOG_FILE_NAME "/data/startup_log.txt" +#endif +#define MAX_BUFFER 256 +#define MAX_EACH_CMD_LENGTH 30 +#define MAX_JSON_FILE_LEN 102400 // max init.cfg size 100KB + +struct CmdArgs* GetCmd(const char *cmdContent, const char *delim) +{ + struct CmdArgs *ctx = (struct CmdArgs *)malloc(sizeof(struct CmdArgs)); + INIT_CHECK_ONLY_RETURN(ctx != NULL, return NULL); + + ctx->argv = (char**)malloc(sizeof(char*) * MAX_CMD_NAME_LEN); + INIT_CHECK_ONLY_RETURN(ctx->argv != NULL, FreeCmd(&ctx); return NULL); + + char tmpCmd[MAX_BUFFER]; + INIT_CHECK_ONLY_RETURN(strncpy_s(tmpCmd, strlen(cmdContent) + 1, cmdContent, strlen(cmdContent)) == EOK, + FreeCmd(&ctx); + return NULL); + tmpCmd[strlen(cmdContent)] = '\0'; + + char *buffer = NULL; + char *token = strtok_r(tmpCmd, delim, &buffer); + ctx->argc = 0; + while (token != NULL) { + ctx->argv[ctx->argc] = calloc(sizeof(char *), MAX_EACH_CMD_LENGTH); + INIT_CHECK_ONLY_RETURN(ctx->argv[ctx->argc] != NULL, FreeCmd(&ctx); return NULL); + + INIT_CHECK_ONLY_RETURN(strncpy_s(ctx->argv[ctx->argc], strlen(cmdContent) + 1, token, strlen(token)) == EOK, + FreeCmd(&ctx); + return NULL); + if (ctx->argc > MAX_CMD_NAME_LEN - 1) { + INIT_LOGE("[Init] GetCmd failed, max cmd number is 10.\n"); + FreeCmd(&ctx); + return NULL; + } + token = strtok_r(NULL, delim, &buffer); + ctx->argc += 1; + } + ctx->argv[ctx->argc] = NULL; + return ctx; +} + +void FreeCmd(struct CmdArgs **cmd) +{ + struct CmdArgs *tmpCmd = *cmd; + INIT_CHECK_ONLY_RETURN(tmpCmd != NULL, return); + for (int i = 0; i < tmpCmd->argc; ++i) { + INIT_CHECK_ONLY_RETURN(tmpCmd->argv[i] == NULL, free(tmpCmd->argv[i])); + } + INIT_CHECK_ONLY_RETURN(tmpCmd->argv == NULL, free(tmpCmd->argv)); + free(tmpCmd); + return; +} + +void Logger(StatupLogLevel level, const char *format, ...) +{ + FILE* pFile = fopen(LOG_FILE_NAME, "a"); + static char *logLeveInfo[] = { "VERBOSE", "INFO", "WARN", "ERROR", "FATAL" }; + if (level >= sizeof(logLeveInfo) / sizeof(char*) || pFile == NULL) { + return; + } + time_t t; + struct tm *localTimer; + time(&t); + localTimer = localtime (&t); + fprintf(pFile, "[%d/%d/%d %d:%d:%d][%s]", localTimer->tm_year + 1900, localTimer->tm_mon, localTimer->tm_mday, + localTimer->tm_hour, localTimer->tm_min, localTimer->tm_sec, logLeveInfo[level]); + + va_list list; + va_start(list, format); + vfprintf(pFile, format, list); + va_end(list); + + fprintf(pFile, "%s", " \n"); + fflush(pFile); + fclose(pFile); +} + +int DecodeUid(const char *name) +{ + if (isalpha(name[0])) { + struct passwd *pwd = getpwnam(name); + if (!pwd) { + return -1; + } + return pwd->pw_uid; + } else if (isdigit(name[0])) { + uid_t result = strtoul(name, 0, 10); + return result; + } else { + return -1; + } +} + +void CheckAndCreateDir(const char *fileName) +{ + char *path = strndup(fileName, strrchr(fileName, '/') - fileName); + if (path != NULL && access(path, F_OK) != 0) { + mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + } + free(path); +} + +char* ReadFileToBuf(const char *configFile) +{ + char* buffer = NULL; + FILE* fd = NULL; + struct stat fileStat = {0}; + if (configFile == NULL || *configFile == '\0') { + return NULL; + } + + do { + if (stat(configFile, &fileStat) != 0 || + fileStat.st_size <= 0 || fileStat.st_size > MAX_JSON_FILE_LEN) { + break; + } + fd = fopen(configFile, "r"); + if (fd == NULL) { + break; + } + buffer = (char*)malloc(fileStat.st_size + 1); + if (buffer == NULL) { + break; + } + + if (fread(buffer, fileStat.st_size, 1, fd) != 1) { + free(buffer); + buffer = NULL; + break; + } + buffer[fileStat.st_size] = '\0'; + } while (0); + + if (fd != NULL) { + fclose(fd); + fd = NULL; + } + return buffer; +} \ No newline at end of file diff --git a/services/src/main.c b/services/src/main.c index 40ab4241076b031b2acdd33311375bb6250eb3ac..b25aea509fc95b1fa95673d0afe28d635f44657e 100755 --- a/services/src/main.c +++ b/services/src/main.c @@ -33,6 +33,8 @@ #ifndef OHOS_LITE #include "device.h" +#include "property.h" +#include "property_service.h" #endif static const pid_t INIT_PROCESS_PID = 1; @@ -82,6 +84,9 @@ int main(int argc, char * const argv[]) // 2. Mount basic filesystem and create common device node. MountBasicFs(); CreateDeviceNode(); + MakeSocketDir("/dev/unix/socket/", 0755); + + InitPropertyService(); #endif // 3. signal register @@ -107,6 +112,12 @@ int main(int argc, char * const argv[]) // 5. read configuration file and do jobs InitReadCfg(); + LoadDefaultProperty("/system/build.prop"); + LoadDefaultProperty("/system/buildz.prop"); + LoadDefaultProperty("/vendor/build.prop"); + LoadDefaultProperty("/vendor/default.prop"); + LoadDefaultProperty("/vendor/odm/etc/build.prop"); + LoadDefaultProperty("/system/etc/prop.default"); #ifdef OHOS_DEBUG struct timespec tmCfg; if (clock_gettime(CLOCK_REALTIME, &tmCfg) != 0) { @@ -121,6 +132,9 @@ int main(int argc, char * const argv[]) #endif printf("[Init] main, entering wait.\n"); +#ifndef OHOS_LITE + StartPropertyService(); +#endif while (1) { // pause only returns when a signal was caught and the signal-catching function returned. // pause only returns -1, no need to process the return value. diff --git a/services/test/unittest/common/BUILD.gn b/services/test/unittest/common/BUILD.gn index a692ab44a0803cf9c4abed127124bd93d64d7f53..0d8da09fe21c5cbb2ab123facc2fdec84da116c2 100644 --- a/services/test/unittest/common/BUILD.gn +++ b/services/test/unittest/common/BUILD.gn @@ -34,6 +34,7 @@ if (defined(ohos_lite)) { "//base/startup/init_lite/services/src/init_cmds.c", "//base/startup/init_lite/services/src/init_jobs.c", "//base/startup/init_lite/services/src/init_service.c", + "//base/startup/init_lite/services/src/init_utils.c", "//base/startup/init_lite/services/src/init_service_manager.c", "//base/startup/init_lite/services/src/init_signal_handler.c", "cmd_func_test.cpp", diff --git a/services/test/unittest/common/cmd_func_test.cpp b/services/test/unittest/common/cmd_func_test.cpp index 676403b7e43f6c15fa6be53364fabab29840db5d..7e3cc721b2535eefac8a45a129dc879a78c6daaa 100644 --- a/services/test/unittest/common/cmd_func_test.cpp +++ b/services/test/unittest/common/cmd_func_test.cpp @@ -48,7 +48,7 @@ const uid_t CFG_FILE_UID = 0; const gid_t CFG_FILE_GID = 0; const mode_t CFG_FILE_MODE = S_IRUSR; const int JOBS_IN_FILE_COUNT = 3; // pre-init, init, post-init -const int MAX_SERVICES_CNT_IN_FILE = 100; +const int MAX_SERVICES_COUNT_IN_FILE = 100; const int MAX_CAPS_CNT_FOR_ONE_SERVICE = 100; const unsigned int MAX_CAPABILITY_VALUE = 4294967295; // 0xFFFFFFFF const unsigned int MAX_JSON_FILE_LEN = 102400; // max init.cfg size 100KB @@ -642,7 +642,7 @@ static void CheckServices(const cJSON* fileRoot) int servArrSize = 0; cJSON* serviceArr = GetArrItem(fileRoot, servArrSize, SERVICE_ARR_NAME_IN_JSON); EXPECT_TRUE(serviceArr != nullptr); - EXPECT_TRUE(servArrSize <= MAX_SERVICES_CNT_IN_FILE); + EXPECT_TRUE(servArrSize <= MAX_SERVICES_COUNT_IN_FILE); for (int i = 0; i < servArrSize; ++i) { cJSON* curItem = cJSON_GetArrayItem(serviceArr, i); diff --git a/services/trigger/BUILD.gn b/services/trigger/BUILD.gn new file mode 100755 index 0000000000000000000000000000000000000000..8a6ac55146a9312795fd61bbe8f5b983d122d1b7 --- /dev/null +++ b/services/trigger/BUILD.gn @@ -0,0 +1,40 @@ +# Copyright (c) 2020 Huawei Device Co., Ltd. +# 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. + +import("//build/ohos.gni") + +ohos_static_library("triggerservice") { + sources = [ + "trigger_manager.c", + "trigger_processor.c", + "trigger_checker.c", + "//base/startup/init_lite/services/src/init_utils.c", + ] + + include_dirs = [ + ".", + "//base/startup/init_lite/services/include/property", + "//base/startup/init_lite/services/include/trigger", + "//base/startup/init_lite/services/include", + "//base/startup/init_lite/services/property/include", + "//third_party/libuv/include", + "//third_party/cJSON", + ] + + deps = [ + "//third_party/libuv:uv_static", + "//third_party/bounds_checking_function:libsec_static", + ] + part_name = "startup" + subsystem_name = "startup" +} \ No newline at end of file diff --git a/services/trigger/trigger_checker.c b/services/trigger/trigger_checker.c new file mode 100755 index 0000000000000000000000000000000000000000..948c64607dd67ee1b4157a96a5d0df155357d993 --- /dev/null +++ b/services/trigger/trigger_checker.c @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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. + */ + +#include "trigger_checker.h" +#include +#include "trigger_manager.h" +#include "property_service.h" + +// 申请整块能存作为计算的节点 +int CalculatorInit(LogicCalculator *calculator, int dataNumber, int dataUnit, int needCondition) +{ + TRIGGER_CHECK(calculator != NULL, return -1, "Invalid param"); + int dataSize = dataUnit * dataNumber; + if (needCondition) { + dataSize += 5 * SUPPORT_DATA_BUFFER_MAX; + } + calculator->data = (char *)malloc(dataSize); + TRIGGER_CHECK(calculator->data != NULL, return -1, "Failed to malloc for calculator"); + calculator->dataNumber = dataNumber; + calculator->endIndex = 0; + calculator->dataUnit = dataUnit; + + dataSize = dataUnit * dataNumber; + calculator->conditionName = calculator->data + dataSize; + dataSize += SUPPORT_DATA_BUFFER_MAX; + calculator->conditionContent = calculator->data + dataSize; + dataSize += SUPPORT_DATA_BUFFER_MAX; + calculator->inputName = calculator->data + dataSize; + dataSize += SUPPORT_DATA_BUFFER_MAX; + calculator->inputContent = calculator->data + dataSize; + dataSize += SUPPORT_DATA_BUFFER_MAX; + calculator->readContent = calculator->data + dataSize; + return 0; +} + +void CalculatorFree(LogicCalculator *calculator) +{ + TRIGGER_CHECK(calculator != NULL, return, "Invalid param"); + free(calculator->data); + calculator->data = NULL; +} + +static void CalculatorClear(LogicCalculator *calculator) +{ + TRIGGER_CHECK(calculator != NULL, return, "Invalid param"); + calculator->endIndex = 0; +} + +static int CalculatorPushChar(LogicCalculator *calculator, char data) +{ + TRIGGER_CHECK(calculator != NULL, return -1, "Invalid param"); + TRIGGER_CHECK(calculator->endIndex < calculator->dataNumber, return -1, "More data for calculator support"); + TRIGGER_CHECK(sizeof(char) == calculator->dataUnit, return -1, "More data for calculator support"); + calculator->data[calculator->endIndex++] = data; + return 0; +} + +static int CalculatorPopChar(LogicCalculator *calculator, char *data) +{ + TRIGGER_CHECK(calculator != NULL, return -1, "Invalid param"); + TRIGGER_CHECK(calculator->endIndex < calculator->dataNumber, return -1, "More data for calculator support"); + if (calculator->endIndex == 0) { + return -1; + } + *data = calculator->data[--calculator->endIndex]; + return 0; +} + +static int CalculatorPush(LogicCalculator *calculator, void *data) +{ + TRIGGER_CHECK(calculator != NULL, return -1, "Invalid param"); + TRIGGER_CHECK(calculator->endIndex < calculator->dataNumber, return -1, "More data for calculator support"); + char *tmpData = (calculator->data + calculator->dataUnit * calculator->endIndex); + int ret = memcpy_s(tmpData, calculator->dataUnit, data, calculator->dataUnit); + TRIGGER_CHECK(ret == 0, return -1, "Failed to copy logic data"); + calculator->endIndex++; + return 0; +} + +static int CalculatorPop(LogicCalculator *calculator, void *data) +{ + TRIGGER_CHECK(calculator != NULL || data == NULL, return -1, "Invalid param"); + TRIGGER_CHECK(calculator->endIndex < calculator->dataNumber, return -1, "More data for calculator support"); + if (calculator->endIndex == 0) { + return -1; + } + char *tmpData = calculator->data + calculator->dataUnit * (calculator->endIndex - 1); + int ret = memcpy_s(data, calculator->dataUnit, tmpData, calculator->dataUnit); + TRIGGER_CHECK(ret == 0, return -1, "Failed to copy logic data"); + calculator->endIndex--; + return 0; +} + +static int CalculatorLength(const LogicCalculator *calculator) +{ + TRIGGER_CHECK(calculator != NULL, return 0, "Invalid param"); + return calculator->endIndex; +} + +static int PrefixAdd(char *prefix, u_int32_t *prefixIndex, u_int32_t prefixLen, char op) +{ + if ((*prefixIndex + 3) >= prefixLen) { + return -1; + } + prefix[(*prefixIndex)++] = ' '; + prefix[(*prefixIndex)++] = op; + prefix[(*prefixIndex)++] = ' '; + return 0; +} + +static int HandleOperationOr(LogicCalculator *calculator, char *prefix, u_int32_t *prefixIndex, u_int32_t prefixLen) +{ + int ret = 0; + char e; + prefix[(*prefixIndex)++] = ' '; + if(CalculatorLength(calculator) == 0) { + CalculatorPushChar(calculator, '|'); + } else { + do { + CalculatorPopChar(calculator, &e); + if (e == '(') { + CalculatorPushChar(calculator, e); + } else { + ret = PrefixAdd(prefix, prefixIndex, prefixLen, e); + TRIGGER_CHECK(ret == 0, return -1, "Invalid prefix"); + } + } while (CalculatorLength(calculator) > 0 && e != '('); + CalculatorPushChar(calculator, '|'); + } + return 0; +} + +static int ComputeSubCondition(LogicCalculator *calculator, LogicData *data, const char *condition) +{ + if (!LOGIC_DATA_TEST_FLAG(data, LOGIC_DATA_FLAGS_ORIGINAL)) { + return LOGIC_DATA_TEST_FLAG(data, LOGIC_DATA_FLAGS_TRUE); + } + // 解析条件 + int ret = GetValueFromContent(condition + data->startIndex, + data->endIndex - data->startIndex, 0, calculator->conditionName, SUPPORT_DATA_BUFFER_MAX); + TRIGGER_CHECK(ret == 0, return -1, "Failed parse content name"); + ret = GetValueFromContent(condition + data->startIndex, data->endIndex - data->startIndex, + strlen(calculator->conditionName) + 1, calculator->conditionContent, SUPPORT_DATA_BUFFER_MAX); + TRIGGER_CHECK(ret == 0, return -1, "Failed parse content value"); + TRIGGER_LOGI("ComputeSubCondition subcondition \'%s\' \'%s\'", + calculator->conditionName, calculator->conditionContent); + + // check name + if (strcmp(calculator->conditionName, calculator->inputName) == 0) { + if (strcmp(calculator->conditionContent, "*") == 0) { + return 1; + } + if (strcmp(calculator->conditionContent, calculator->inputContent) == 0) { + return 1; + } + } else { + u_int32_t len = SUPPORT_DATA_BUFFER_MAX; + ret = SystemReadParameter(calculator->conditionName, calculator->readContent, &len); + if (ret == 0 && strcmp(calculator->conditionContent, calculator->readContent) == 0) { + return 1; + } + } + return 0; +} + +int GetValueFromContent(const char *content, u_int32_t contentSize, u_int32_t start, char *value, u_int32_t valueSize) +{ + u_int32_t contentIndex = start; + u_int32_t currIndex = 0; + while (contentIndex < contentSize && currIndex < valueSize) { + if (isspace(content[contentIndex])) { + contentIndex++; + continue; + } + if (content[contentIndex] == '=') { + value[currIndex++] = '\0'; + return 0; + } + value[currIndex++] = content[contentIndex++]; + } + if (currIndex < valueSize) { + value[currIndex] = '\0'; + return 0; + } + return -1; +} + +int ComputeCondition(LogicCalculator *calculator, const char *condition) +{ + u_int32_t currIndex = 0; + u_int32_t start = 0; + int noneOper = 1; + CalculatorClear(calculator); + LogicData data1 = {}; + LogicData data2 = {}; + while (currIndex < strlen(condition)) { + if (condition[currIndex] == '|' || condition[currIndex] == '&') { + noneOper = 0; + int ret = CalculatorPop(calculator, (void*)&data2); + ret |= CalculatorPop(calculator, (void*)&data1); + TRIGGER_CHECK(ret == 0, return -1, "Failed to pop data"); + + ret = ComputeSubCondition(calculator, &data1, condition); + data1.flags = 0; + if (condition[currIndex] == '|' && ret == 1) { + LOGIC_DATA_SET_FLAG(&data1, LOGIC_DATA_FLAGS_TRUE); + } else if (condition[currIndex] == '|' || ret == 1) { + if (ComputeSubCondition(calculator, &data2, condition) == 1) { + LOGIC_DATA_SET_FLAG(&data1, LOGIC_DATA_FLAGS_TRUE); + } + } + ret = CalculatorPush(calculator, (void*)&data1); + TRIGGER_CHECK(ret == 0, return -1, "Failed to push data"); + start = currIndex + 1; // 跳过符号 + } else if (isspace(condition[currIndex])) { + if (start == currIndex) { + start = ++currIndex; + continue; + } + data1.flags = LOGIC_DATA_FLAGS_ORIGINAL; + data1.startIndex = start; + data1.endIndex = currIndex; + int ret = CalculatorPush(calculator, (void*)&data1); + TRIGGER_CHECK(ret == 0, return -1, "Failed to push data"); + start = currIndex + 1; + } + currIndex++; + } + if (noneOper) { + data1.flags = LOGIC_DATA_FLAGS_ORIGINAL; + data1.startIndex = start; + data1.endIndex = strlen(condition); + } else { + int ret = CalculatorPop(calculator, &data1); + TRIGGER_CHECK(ret == 0, return -1, "Invalid calculator"); + } + return ComputeSubCondition(calculator, &data1, condition); +} + +int ConvertInfixToPrefix(const char *condition, char *prefix, u_int32_t prefixLen) +{ + char e; + int ret = 0; + u_int32_t curr = 0; + u_int32_t prefixIndex = 0; + LogicCalculator calculator; + CalculatorInit(&calculator, 100, 1, 0); + + while (curr < strlen(condition)) { + if (condition[curr] == ')') { + CalculatorPopChar(&calculator, &e); + while (e != '(') { + ret = PrefixAdd(prefix, &prefixIndex, prefixLen, e); + TRIGGER_CHECK(ret == 0, CalculatorFree(&calculator); return -1, "Invalid prefix"); + ret = CalculatorPopChar(&calculator, &e); + TRIGGER_CHECK(ret == 0, CalculatorFree(&calculator); return -1, "Invalid calculator"); + } + } else if (condition[curr] == '|') { + TRIGGER_CHECK(condition[curr + 1] == '|', CalculatorFree(&calculator); return -1, "Invalid condition"); + ret = HandleOperationOr(&calculator, prefix, &prefixIndex, prefixLen); + TRIGGER_CHECK(ret == 0, CalculatorFree(&calculator); return -1, "Invalid prefix"); + curr++; + } else if (condition[curr] == '&') { + TRIGGER_CHECK(condition[curr + 1] == '&', CalculatorFree(&calculator); return -1, "Invalid condition"); + prefix[prefixIndex++] = ' '; + CalculatorPushChar(&calculator, condition[curr]); + curr++; + } else if (condition[curr] == '(') { + CalculatorPushChar(&calculator, condition[curr]); + } else { + prefix[prefixIndex++] = condition[curr]; + } + curr++; + TRIGGER_CHECK(prefixIndex < prefixLen, CalculatorFree(&calculator); return -1, "Invalid prefixIndex"); + } + + while (CalculatorLength(&calculator) > 0) { + CalculatorPopChar(&calculator, &e); + ret = PrefixAdd(prefix, &prefixIndex, prefixLen, e); + TRIGGER_CHECK(ret == 0, CalculatorFree(&calculator); + return -1, "Invalid prefix %u %u", prefixIndex, prefixLen); + } + prefix[prefixIndex] = '\0'; + CalculatorFree(&calculator); + return 0; +} \ No newline at end of file diff --git a/services/trigger/trigger_checker.h b/services/trigger/trigger_checker.h new file mode 100755 index 0000000000000000000000000000000000000000..e3b9a3ed202e026c8280926901e8390b4f5bdf41 --- /dev/null +++ b/services/trigger/trigger_checker.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef STARTUP_TRIGER_CHECKER_H +#define STARTUP_TRIGER_CHECKER_H + +#include +#include +#include + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +#define SUPPORT_DATA_BUFFER_MAX 128 +#define CONDITION_EXTEND_LEN 32 + +#define LOGIC_DATA_FLAGS_ORIGINAL 0x1 +#define LOGIC_DATA_FLAGS_TRUE 0x08 + +#define LOGIC_DATA_TEST_FLAG(data, flag) (((data)->flags & flag) == (flag)) +#define LOGIC_DATA_SET_FLAG(data, flag) (data)->flags |= (flag) +#define LOGIC_DATA_CLEAR_FLAG(data, flag) (data)->flags &= ~(flag) + +typedef struct { + u_int32_t flags; + u_int32_t startIndex; + u_int32_t endIndex; +} LogicData; + +typedef struct { + int dataNumber; + int endIndex; + int dataUnit; + char *conditionName; + char *conditionContent; + char *inputName; + char *inputContent; + char *readContent; + char *data; +} LogicCalculator; + +int CalculatorInit(LogicCalculator *calculator, int dataNumber, int dataUnit, int needCondition); +void CalculatorFree(LogicCalculator *calculator); +int ConvertInfixToPrefix(const char *condition, char *prefix, u_int32_t prefixLen); +int ComputeCondition(LogicCalculator *calculator, const char *condition); +int GetValueFromContent(const char *content, u_int32_t contentSize, u_int32_t start, char *value, u_int32_t valueSize); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif +#endif // STARTUP_TRIGER_CHECKER_H \ No newline at end of file diff --git a/services/trigger/trigger_manager.c b/services/trigger/trigger_manager.c new file mode 100755 index 0000000000000000000000000000000000000000..cd47145af4af51521acf9fcad17728eb7b0400b4 --- /dev/null +++ b/services/trigger/trigger_manager.c @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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. + */ + +#include "trigger_manager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "init_cmds.h" +#include "init_utils.h" +#include "trigger_checker.h" + +#define TRIGGER_AREA_SPACE 1024*64 +#define TRIGGER_EXECUTE_QUEUE 64 +#define BUFFER_SIZE 256 +#define CHECK_INDEX_VALID(workSpace, index) \ + (u_int32_t)(index) < sizeof((workSpace)->header) / sizeof((workSpace)->header[0]) + +#ifdef STARTUP_LOCAL +#define TRIGGER_PATH "/media/sf_ubuntu/test/__trigger__/trigger" +#else +#define TRIGGER_PATH "/dev/__trigger__/trigger" +#endif + +int InitTriggerWorkSpace(TriggerWorkSpace *workSpace) +{ + TRIGGER_CHECK(workSpace != NULL, return -1, "Invalid parm"); + if (workSpace->area != NULL) { + return 0; + } + CheckAndCreateDir(TRIGGER_PATH); + int fd = open(TRIGGER_PATH, O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0444); + TRIGGER_CHECK(fd >= 0, return -1, "Open file fail error %s", strerror(errno)); + lseek(fd, TRIGGER_AREA_SPACE, SEEK_SET); + write(fd, "", 1); + + void *areaAddr = (void *)mmap(NULL, TRIGGER_AREA_SPACE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + TRIGGER_CHECK(areaAddr != MAP_FAILED, close(fd); return -1, + "Failed to map memory error %s", strerror(errno)); + close(fd); + + // 第一部分做执行队列 + workSpace->executeQueue.executeQueue = (u_int32_t *)areaAddr; + workSpace->executeQueue.queueCount = TRIGGER_EXECUTE_QUEUE; + workSpace->executeQueue.startIndex = 0; + workSpace->executeQueue.endIndex = 0; + pthread_mutex_init(&workSpace->executeQueue.mutex, NULL); + + // 动态数据保存 + workSpace->area = (TriggerArea *)(areaAddr + TRIGGER_EXECUTE_QUEUE * sizeof(u_int32_t)); + atomic_init(&workSpace->area->serial, ATOMIC_VAR_INIT(0)); + workSpace->area->dataSize = TRIGGER_AREA_SPACE - sizeof(TriggerArea) - TRIGGER_EXECUTE_QUEUE * sizeof(u_int32_t); + workSpace->area->currOffset = sizeof(TriggerArea) + TRIGGER_EXECUTE_QUEUE * sizeof(u_int32_t); + for (size_t i = 0; i < sizeof(workSpace->header) / sizeof(workSpace->header[0]); i++) { + atomic_init(&workSpace->header[i].firstTrigger, ATOMIC_VAR_INIT(0)); + atomic_init(&workSpace->header[i].lastTrigger, ATOMIC_VAR_INIT(0)); + } + return 0; +} + +static CommandNode *GetCmdByIndex(TriggerWorkSpace *workSpace, TriggerNode *trigger, u_int32_t index) +{ + if (index == 0 || index == (u_int32_t)-1) { + return NULL; + } + u_int32_t size = sizeof(CommandNode) + 2; + TRIGGER_CHECK((index + size) < workSpace->area->dataSize, + return NULL, "Invalid index for cmd %u", index); + return (CommandNode *)(workSpace->area->data + index); +} + +static u_int32_t AddCommand(TriggerWorkSpace *workSpace, TriggerNode *trigger, const char *cmdName, const char *content) +{ + TRIGGER_CHECK(workSpace != NULL && trigger != NULL, return 0, "list is null"); + TRIGGER_CHECK(content != NULL, return 0, "command is null"); + u_int32_t size = sizeof(CommandNode) + strlen(cmdName) + 1; + size += (content == NULL) ? 1 : strlen(content) + 1; + TRIGGER_CHECK((workSpace->area->currOffset + size) < workSpace->area->dataSize, + return 0, "Not enough memory for cmd %u %u", size, workSpace->area->currOffset); + + CommandNode *node = (CommandNode *)(workSpace->area->data + workSpace->area->currOffset); + TRIGGER_CHECK(node != NULL, return 0, "Failed to alloc memory for command"); + + int ret = memcpy_s(node->name, sizeof(node->name) - 1, cmdName, strlen(cmdName)); + TRIGGER_CHECK(ret == 0, return 0, "Failed to copy command"); + node->name[strlen(cmdName)] = '\0'; + if (content != NULL) { + ret = memcpy_s(node->content, size, content, strlen(content)); + node->content[strlen(content)] = '\0'; + TRIGGER_CHECK(ret == 0, return 0, "Failed to copy command"); + } else { + node->content[0] = '\0'; + } + + u_int32_t offset = workSpace->area->currOffset; + atomic_init(&node->next, ATOMIC_VAR_INIT(0)); + // 插入队列 + if (trigger->firstCmd == 0) { + atomic_store_explicit(&trigger->firstCmd, offset, memory_order_release); + atomic_store_explicit(&trigger->lastCmd, offset, memory_order_release); + } else { + CommandNode *lastNode = GetCmdByIndex(workSpace, trigger, trigger->lastCmd); + if (lastNode != NULL) { + atomic_store_explicit(&lastNode->next, offset, memory_order_release); + } + atomic_store_explicit(&trigger->lastCmd, offset, memory_order_release); + } + workSpace->area->currOffset += size; + return offset; +} + +static TriggerNode *GetTriggerByIndex(TriggerWorkSpace *workSpace, u_int32_t index) +{ + if (index == 0 || index == (u_int32_t)-1) { + return NULL; + } + u_int32_t size = sizeof(TriggerNode) + 1; + TRIGGER_CHECK((index + size) < workSpace->area->dataSize, + return NULL, "Invalid index for trigger %u", index); + return (TriggerNode *)(workSpace->area->data + index); +} + +static u_int32_t AddTrigger(TriggerWorkSpace *workSpace, int type, const char *name, const char *condition) +{ + TRIGGER_CHECK(workSpace != NULL && name != NULL, return 0, "list is null"); + const char *tmpCond = condition; + if (type == TRIGGER_BOOT && condition == NULL) { + tmpCond = name; + } + u_int32_t conditionSize = (tmpCond == NULL) ? 1 : strlen(tmpCond) + 1 + CONDITION_EXTEND_LEN; + TRIGGER_CHECK((workSpace->area->currOffset + sizeof(TriggerNode) + conditionSize) < workSpace->area->dataSize, + return -1, "Not enough memory for cmd"); + + TriggerNode *node = (TriggerNode *)(workSpace->area->data + workSpace->area->currOffset); + TRIGGER_CHECK(node != NULL, return 0, "Failed to alloc memory for trigger"); + node->type = type; + int ret = memcpy_s(node->name, sizeof(node->name) - 1, name, strlen(name)); + TRIGGER_CHECK(ret == 0, return 0, "Failed to memcpy_s for trigger"); + node->name[strlen(name)] = '\0'; + + if (tmpCond != NULL) { + if (type == TRIGGER_PROPERTY) { + ret = ConvertInfixToPrefix(tmpCond, node->condition, conditionSize); + TRIGGER_CHECK(ret == 0, return 0, "Failed to memcpy_s for trigger"); + } else { + ret = memcpy_s(node->condition, strlen(tmpCond) + 1, tmpCond, strlen(tmpCond)); + TRIGGER_CHECK(ret == 0, return 0, "Failed to memcpy_s for trigger"); + node->condition[strlen(tmpCond)] = '\0'; + } + } else { + node->condition[0] = '\0'; + } + + u_int32_t offset = workSpace->area->currOffset; + atomic_init(&node->serial, ATOMIC_VAR_INIT(0)); + atomic_init(&node->next, ATOMIC_VAR_INIT(0)); + atomic_init(&node->firstCmd, ATOMIC_VAR_INIT(0)); + atomic_init(&node->lastCmd, ATOMIC_VAR_INIT(0)); + + // 插入到trigger队列中 + if (workSpace->header[type].firstTrigger == 0) { + atomic_store_explicit(&workSpace->header[type].firstTrigger, offset, memory_order_release); + atomic_store_explicit(&workSpace->header[type].lastTrigger, offset, memory_order_release); + } else { + TriggerNode *lastNode = GetTriggerByIndex(workSpace, workSpace->header[type].lastTrigger); + if (lastNode != NULL) { + atomic_store_explicit(&lastNode->next, offset, memory_order_release); + } + atomic_store_explicit(&workSpace->header[type].lastTrigger, offset, memory_order_release); + } + workSpace->area->currOffset += conditionSize + sizeof(TriggerNode); + return offset; +} + +static int GetTriggerIndex(const char *type) +{ + if (strncmp("property", type, strlen("property")) == 0) { + return TRIGGER_PROPERTY; + } + static const char *triggerType[] = { + "boot", "early-init", "init", "early-init", "late-init", + "early-fs", "post-fs", "late-fs", "post-fs-data", + "nonencrypted", + "firmware_mounts_complete", + "load_persist_props_action" + }; + for (size_t i = 0; i < sizeof(triggerType) / sizeof(char*); i++) { + if (strncmp(triggerType[i], type, strlen(triggerType[i])) == 0) { + return TRIGGER_BOOT; + } + } + return TRIGGER_UNKNOW; +} + +static int CheckBootTriggerMatch(TriggerNode *trigger, void *content, u_int32_t contentSize) +{ + if (strncmp(trigger->name, (char *)content, contentSize) == 0) { + return 1; + } + return 0; +} + +int ParseTrigger(TriggerWorkSpace *workSpace, cJSON *triggerItem) +{ + TRIGGER_CHECK(triggerItem != NULL, return -1, "Invalid file"); + TRIGGER_CHECK(workSpace != NULL, return -1, "Failed to create trigger list"); + + char *name = cJSON_GetStringValue(cJSON_GetObjectItem(triggerItem, "name")); + TRIGGER_CHECK(name != NULL, return -1, "Can not get name from cfg"); + char *condition = cJSON_GetStringValue(cJSON_GetObjectItem(triggerItem, "condition")); + + int index = GetTriggerIndex(name); + TRIGGER_CHECK(CHECK_INDEX_VALID(workSpace, index), return -1, "Failed to get trigger index"); + + u_int32_t offset = AddTrigger(workSpace, index, name, condition); + //TRIGGER_LOGE("AddTrigger %u %s %u", offset, name, workSpace->area->currOffset); + TRIGGER_CHECK(offset > 0, return -1, "Failed to create trigger %s", name); + TriggerNode *trigger = GetTriggerByIndex(workSpace, offset); + + // 添加命令行 + cJSON* cmdItems = cJSON_GetObjectItem(triggerItem, CMDS_ARR_NAME_IN_JSON); + TRIGGER_CHECK(cJSON_IsArray(cmdItems), return -1, "Command item must be array"); + int cmdLinesCnt = cJSON_GetArraySize(cmdItems); + TRIGGER_CHECK(cmdLinesCnt > 0, return -1, "Command array size must positive"); + + for (int i = 0; i < cmdLinesCnt; ++i) { + char *cmdLineStr = cJSON_GetStringValue(cJSON_GetArrayItem(cmdItems, i)); + TRIGGER_CHECK(cmdLinesCnt > 0, continue, "Command is null"); + + size_t cmdLineLen = strlen(cmdLineStr); + const char *matchCmd = GetMatchCmd(cmdLineStr); + if (matchCmd == NULL && strncmp(cmdLineStr, TRIGGER_CMD, strlen(TRIGGER_CMD)) == 0) { + matchCmd = TRIGGER_CMD; + } + TRIGGER_CHECK(matchCmd != NULL, continue, "Command not support %s", cmdLineStr); + size_t matchLen = strlen(matchCmd); + if (matchLen == cmdLineLen) { + offset = AddCommand(workSpace, trigger, matchCmd, NULL); + } else { + offset = AddCommand(workSpace, trigger, matchCmd, cmdLineStr + matchLen); + } + //TRIGGER_LOGE("AddCommand %u %s %u", offset, cmdLineStr, workSpace->area->currOffset); + TRIGGER_CHECK(offset > 0, continue, "Failed to add command %s", cmdLineStr); + } + return 0; +} + +int ExecuteTrigger(TriggerWorkSpace *workSpace, TriggerNode *trigger, CMD_EXECUTE cmdExecuter) +{ + TRIGGER_CHECK(workSpace != NULL && trigger != NULL && cmdExecuter != NULL, return -1, "Invalid param"); + TRIGGER_LOGI("ExecuteCmds trigger %s", trigger->name); + CommandNode *cmd = GetCmdByIndex(workSpace, trigger, trigger->firstCmd); + while (cmd != NULL) { + cmdExecuter(trigger, cmd->name, cmd->content); + cmd = GetCmdByIndex(workSpace, trigger, cmd->next); + } + return 0; +} + +int ExecuteQueuePush(TriggerWorkSpace *workSpace, TriggerNode *trigger, u_int32_t triggerIndex) +{ + TRIGGER_CHECK(workSpace != NULL, return -1, "Invalid area"); + pthread_mutex_lock(&workSpace->executeQueue.mutex); + u_int32_t index = workSpace->executeQueue.endIndex++ % workSpace->executeQueue.queueCount; + workSpace->executeQueue.executeQueue[index] = triggerIndex; + pthread_mutex_unlock(&workSpace->executeQueue.mutex); + return 0; +} + +TriggerNode *ExecuteQueuePop(TriggerWorkSpace *workSpace) +{ + if (workSpace->executeQueue.endIndex <= workSpace->executeQueue.startIndex) { + return NULL; + } + pthread_mutex_lock(&workSpace->executeQueue.mutex); + u_int32_t currIndex = workSpace->executeQueue.startIndex % workSpace->executeQueue.queueCount; + u_int32_t triggerIndex = workSpace->executeQueue.executeQueue[currIndex]; + workSpace->executeQueue.executeQueue[currIndex] = 0; + workSpace->executeQueue.startIndex++; + pthread_mutex_unlock(&workSpace->executeQueue.mutex); + return GetTriggerByIndex(workSpace, triggerIndex); +} + +int ExecuteQueueSize(TriggerWorkSpace *workSpace) +{ + TRIGGER_CHECK(workSpace != NULL, return 0, "Invalid param"); + return workSpace->executeQueue.endIndex - workSpace->executeQueue.startIndex; +} + +int CheckTrigger(TriggerWorkSpace *workSpace, + int type, void *content, u_int32_t contentSize, TRIGGER_CHECK_DONE triggerExecuter) +{ + static TRIGGER_MATCH triggerCheckMatch[TRIGGER_MAX] = { + CheckBootTriggerMatch, NULL, NULL + }; + TRIGGER_LOGI("ExecuteTrigger check content %s", (char*)content); + TRIGGER_CHECK(workSpace != NULL, return -1, "Trigger not init"); + TRIGGER_CHECK(CHECK_INDEX_VALID(workSpace, type), return -1, "Invalid type %d", type); + TRIGGER_CHECK((u_int32_t)type < sizeof(triggerCheckMatch) / sizeof(triggerCheckMatch[0]), + return -1, "Failed to get check function"); + TRIGGER_CHECK(triggerCheckMatch[type] != NULL, return 0, "Failed to get check function"); + u_int32_t index = workSpace->header[type].firstTrigger; + TriggerNode *trigger = GetTriggerByIndex(workSpace, workSpace->header[type].firstTrigger); + while (trigger != NULL) { + if (triggerCheckMatch[type](trigger, content, contentSize) == 1) { // 等于1 则认为匹配 + triggerExecuter(trigger, index); + } + index = trigger->next; + trigger = GetTriggerByIndex(workSpace, trigger->next); + } + return 0; +} + +int CheckPropertyTrigger(TriggerWorkSpace *workSpace, + const char *content, u_int32_t contentSize, TRIGGER_CHECK_DONE triggerExecuter) +{ + TRIGGER_CHECK(workSpace != NULL && content != NULL && triggerExecuter != NULL, + return -1, "Failed arg for property trigger"); + LogicCalculator calculator; + CalculatorInit(&calculator, 100, sizeof(LogicData), 1); + + // 先解析content + int ret = GetValueFromContent(content, contentSize, 0, calculator.inputName, SUPPORT_DATA_BUFFER_MAX); + TRIGGER_CHECK(ret == 0, CalculatorFree(&calculator); return -1, "Failed parse content name"); + ret = GetValueFromContent(content, contentSize, + strlen(calculator.inputName) + 1, calculator.inputContent, SUPPORT_DATA_BUFFER_MAX); + TRIGGER_CHECK(ret == 0, CalculatorFree(&calculator); return -1, "Failed parse content value"); + TRIGGER_LOGI("CheckPropertyTrigger content %s ", content); + + u_int32_t index = workSpace->header[TRIGGER_PROPERTY].firstTrigger; + TriggerNode *trigger = GetTriggerByIndex(workSpace, workSpace->header[TRIGGER_PROPERTY].firstTrigger); + while (trigger != NULL) { + TRIGGER_LOGI("CheckPropertyTrigger name:%s condition \'%s\' ", trigger->name, trigger->condition); + ret = ComputeCondition(&calculator, trigger->condition); + if (ret == 1) { + triggerExecuter(trigger, index); + } + index = trigger->next; + trigger = GetTriggerByIndex(workSpace, trigger->next); + } + CalculatorFree(&calculator); + return 0; +} + +TriggerNode *GetTriggerByName(TriggerWorkSpace *workSpace, const char *triggerName, u_int32_t *triggerIndex) +{ + TRIGGER_CHECK(workSpace != NULL && triggerName != NULL, return NULL, "Invalid param"); + for (size_t i = 0; i < sizeof(workSpace->header) / sizeof(workSpace->header[0]); i++) { + u_int32_t index = workSpace->header[i].firstTrigger; + TriggerNode *trigger = GetTriggerByIndex(workSpace, workSpace->header[i].firstTrigger); + while (trigger != NULL) { + if (strcmp(triggerName, trigger->name) == 0) { + *triggerIndex = index; + return trigger; + } + index = trigger->next; + trigger = GetTriggerByIndex(workSpace, trigger->next); + } + } + return NULL; +} \ No newline at end of file diff --git a/services/trigger/trigger_manager.h b/services/trigger/trigger_manager.h new file mode 100755 index 0000000000000000000000000000000000000000..0fc47633893c332e1d51740a200e526d3cb9137c --- /dev/null +++ b/services/trigger/trigger_manager.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef STARTUP_TRIGER_MANAGER_H +#define STARTUP_TRIGER_MANAGER_H + +#include +#include +#include +#include + +#include "cJSON.h" +#include "init_log.h" +#include "list.h" +#include "property_service.h" +#include "securec.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +#define TRIGGER_LOGI(fmt, ...) STARTUP_LOGI("Trigger", fmt, ##__VA_ARGS__) +#define TRIGGER_LOGE(fmt, ...) STARTUP_LOGE("Trigger", fmt, ##__VA_ARGS__) + +#define TRIGGER_CHECK(retCode, exper, ...) \ + if (!(retCode)) { \ + TRIGGER_LOGE(__VA_ARGS__); \ + exper; \ + } + +#define COND_STACK_SIZE 20 +#define TRIGGER_CMD "trigger " +#define TRIGGER_ARR_NAME_IN_JSON "jobs" +#define CMDS_ARR_NAME_IN_JSON "cmds" + +typedef struct { + char data[COND_STACK_SIZE]; + int top; +} CONDITION_STACK; + +/* +on boot + exec /system/bin/sleep 4 + start telephony_sa +*/ +typedef enum { + TRIGGER_BOOT = 0, + TRIGGER_PROPERTY, + TRIGGER_UNKNOW, + TRIGGER_MAX +}TriggerType; + +#define MAX_TRIGGER_CMD_NAME_LEN 16 +#define MAX_TRIGGER_NAME_LEN 64 +#define MAX_TRIGGER_TYPE_LEN 16 + +// Command对象列表,主要存储每个triger需要执行那些Command操作。 +typedef struct CommandNode { + atomic_uint_least32_t next; + char name[MAX_TRIGGER_CMD_NAME_LEN]; + char content[0]; +} CommandNode; + +typedef struct { + atomic_uint_least32_t serial; + atomic_uint_least32_t next; + atomic_uint_least32_t firstCmd; + atomic_uint_least32_t lastCmd; + int type; + char name[MAX_TRIGGER_NAME_LEN]; + char condition[0]; +} TriggerNode; + +typedef struct { + atomic_uint_least32_t serial; + u_int32_t dataSize; + u_int32_t startSize; + u_int32_t currOffset; + char data[0]; +} TriggerArea; + +typedef struct { + atomic_uint_least32_t firstTrigger; + atomic_uint_least32_t lastTrigger; +} TriggerHeader; + +typedef struct { + u_int32_t *executeQueue; + u_int32_t queueCount; + u_int32_t startIndex; + u_int32_t endIndex; + pthread_mutex_t mutex; +} TriggerExecuteQueue; + +typedef struct TriggerWorkSpace { + TriggerExecuteQueue executeQueue; + TriggerHeader header[TRIGGER_MAX]; + TriggerArea *area; +} TriggerWorkSpace; + +int InitTriggerWorkSpace(TriggerWorkSpace *workSpace); +int ParseTrigger(TriggerWorkSpace *workSpace, cJSON *triggerItem); + +typedef int (*TRIGGER_MATCH)(TriggerNode *trigger, void *content, u_int32_t contentSize); +typedef int (*TRIGGER_CHECK_DONE) (TriggerNode *trigger, u_int32_t index); +typedef int (*CMD_EXECUTE) (TriggerNode *trigger, const char *cmdName, const char *command); + +TriggerNode *GetTriggerByName(TriggerWorkSpace *workSpace, const char *triggerName, u_int32_t *triggerIndex); +int ExecuteTrigger(TriggerWorkSpace *workSpace, TriggerNode *trigger, CMD_EXECUTE cmdExecuter); +int CheckTrigger(TriggerWorkSpace *workSpace, + int type, void *content, u_int32_t contentSize, TRIGGER_CHECK_DONE triggerExecuter); +int CheckPropertyTrigger(TriggerWorkSpace *workSpace, + const char *content, u_int32_t contentSize, TRIGGER_CHECK_DONE triggerExecuter); + +TriggerNode *ExecuteQueuePop(TriggerWorkSpace *workSpace); +int ExecuteQueuePush(TriggerWorkSpace *workSpace, TriggerNode *trigger, u_int32_t index); +int ExecuteQueueSize(TriggerWorkSpace *workSpace); + +#define TRIGGER_NODE_IN_QUEUE(trigger) \ + (atomic_load_explicit(&(trigger)->serial, memory_order_relaxed) & 0x01) +#define TRIGGER_NODE_SET_QUEUE_FLAG(trigger) \ + atomic_store_explicit(&(trigger)->serial, (trigger)->serial | 0x01, memory_order_relaxed) +#define TRIGGER_NODE_CLEAR_QUEUE_FLAG(trigger) \ + atomic_store_explicit(&(trigger)->serial, (trigger)->serial & ~0x01, memory_order_relaxed) + +TriggerWorkSpace *GetTriggerWorkSpace(); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif +#endif // STARTUP_TRIGER_MANAGER_H \ No newline at end of file diff --git a/services/trigger/trigger_processor.c b/services/trigger/trigger_processor.c new file mode 100755 index 0000000000000000000000000000000000000000..83e16c015d2dea10831645f2a03a58d6d9cd1206 --- /dev/null +++ b/services/trigger/trigger_processor.c @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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. + */ + +#include "trigger_processor.h" +#include +#include + +#include "init_cmds.h" +#include "property_manager.h" +#include "trigger_checker.h" +#include "uv.h" + +#define SYS_POWER_CTRL "sys.powerctrl." +static int g_triggerServiceStart = 0; +static TriggerWorkSpace g_triggerWorkSpace = {}; + +static int DoCmdExecute(TriggerNode *trigger, const char *cmdName, const char *command) +{ + TRIGGER_CHECK(trigger != NULL && cmdName != NULL && command != NULL, return -1, "Invalid param"); + if (strncmp(cmdName, TRIGGER_CMD, strlen(TRIGGER_CMD)) == 0) { + u_int32_t triggerIndex = 0; + TriggerNode *node = GetTriggerByName(&g_triggerWorkSpace, command, &triggerIndex); + if (node != NULL && !TRIGGER_NODE_IN_QUEUE(node)) { // 不在队列中 + TRIGGER_LOGI("DoCmdExecute trigger %s", node->name); + TRIGGER_NODE_SET_QUEUE_FLAG(node); + ExecuteQueuePush(&g_triggerWorkSpace, node, triggerIndex); + } + return 0; + } + TRIGGER_LOGI("DoCmdExecute trigger %s cmd %s", trigger->name, cmdName); + DoCmdByName(cmdName, command); + return 0; +} + +static int DoTiggerCheckResult(TriggerNode *trigger, u_int32_t triggerIndex) +{ + // 直接执行,不需要添加队列 + if (!g_triggerServiceStart) { + TRIGGER_LOGI("DoTiggerExecute trigger %s", trigger->name); + ExecuteTrigger(&g_triggerWorkSpace, trigger, DoCmdExecute); + TRIGGER_NODE_CLEAR_QUEUE_FLAG(trigger); + } + + // 已经在队列中了,则不执行 TODO + if (TRIGGER_NODE_IN_QUEUE(trigger)) { + TRIGGER_LOGI("DoTiggerExecute trigger %s has been waiting execute", trigger->name); + return 0; + } + + if (trigger->type == TRIGGER_BOOT) { // 立刻执行 + TRIGGER_LOGI("DoTiggerExecute trigger %s", trigger->name); + ExecuteTrigger(&g_triggerWorkSpace, trigger, DoCmdExecute); + TRIGGER_NODE_CLEAR_QUEUE_FLAG(trigger); + } else { + TRIGGER_NODE_SET_QUEUE_FLAG(trigger); + TRIGGER_LOGI("DoTiggerExecute trigger %s", trigger->name); + ExecuteQueuePush(&g_triggerWorkSpace, trigger, triggerIndex); + } + return 0; +} + +void ExecuteQueueWork() +{ + int executeCount = 0; + TriggerNode *trigger = ExecuteQueuePop(&g_triggerWorkSpace); + while (trigger != NULL) { + ExecuteTrigger(&g_triggerWorkSpace, trigger, DoCmdExecute); + TRIGGER_NODE_CLEAR_QUEUE_FLAG(trigger); + executeCount++; + if (executeCount > 5) { + break; + } + trigger = ExecuteQueuePop(&g_triggerWorkSpace); + } +} + +static void CheckTriggers(int type, void *content, u_int32_t contentLen) +{ + switch (type) { + case EVENT_PROPERTY: { + CheckPropertyTrigger(&g_triggerWorkSpace, content, contentLen, DoTiggerCheckResult); + break; + } + case EVENT_BOOT: { + CheckTrigger(&g_triggerWorkSpace, TRIGGER_BOOT, content, contentLen, DoTiggerCheckResult); + break; + } + default: + TRIGGER_LOGI("CheckTriggers: %d", type); + break; + } +} + +static void ProcessAfterEvent(uv_work_t *req, int status) +{ + free(req); + ExecuteQueueWork(); +} + +static void ProcessEvent(uv_work_t *req) +{ + TriggerDataEvent *event = (TriggerDataEvent *)req; + CheckTriggers(event->type, event->content, event->contentSize); +} + +static const char *GetCmdInfo(const char *content, u_int32_t contentSize, char **cmdParam) +{ + char cmd[MAX_CMD_NAME_LEN + 1] = { 0 }; + int ret = GetValueFromContent(content, contentSize, 0, cmd, sizeof(cmd)); + TRIGGER_CHECK(ret == 0, return NULL, "Failed parse cmd"); + u_int32_t cmdLen = strlen(cmd); + TRIGGER_CHECK(cmdLen < MAX_CMD_NAME_LEN, return NULL, "Failed parse cmd"); + cmd[cmdLen] = ' '; + cmd[cmdLen + 1] = '\0'; + *cmdParam = content + cmdLen + 1; + return GetMatchCmd(cmd); +} + +static void SendTriggerEvent(TriggerDataEvent *event) +{ + int ctrlSize = strlen(SYS_POWER_CTRL); + if (strncmp(event->content, SYS_POWER_CTRL, ctrlSize) == 0) { + char *cmdParam = NULL; + const char *matchCmd = GetCmdInfo(event->content + ctrlSize, event->contentSize - ctrlSize, &cmdParam); + if (matchCmd != NULL) { + DoCmdByName(matchCmd, cmdParam); + } else { + TRIGGER_LOGE("SendTriggerEvent cmd %s not found", event->content); + } + free(event); + return; + } + if (event->type == EVENT_BOOT || g_triggerServiceStart == 0) { + CheckTriggers(EVENT_PROPERTY, event->content, event->contentSize); + free(event); + } else { + uv_queue_work(uv_default_loop(), &event->request, ProcessEvent, ProcessAfterEvent); + } +} + +void PostPropertyTrigger(const char *name, const char *value) +{ + TRIGGER_CHECK(name != NULL && value != NULL, return, "Invalid param"); + TRIGGER_LOGI("PostPropertyTrigger %s ", name); + int contentLen = strlen(name) + strlen(value) + 2; + TriggerDataEvent *event = (TriggerDataEvent *)malloc(sizeof(TriggerDataEvent) + contentLen); + TRIGGER_CHECK(event != NULL, return, "Failed to alloc memory"); + event->type = EVENT_PROPERTY; + event->request.data = (char*)event + sizeof(uv_work_t); + event->contentSize = BuildPropertyContent(event->content, contentLen, name, value); + TRIGGER_CHECK(event->contentSize > 0, return, "Failed to copy porperty"); + SendTriggerEvent(event); + TRIGGER_LOGI("PostPropertyTrigger %s success", name); +} + +void PostTrigger(EventType type, void *content, u_int32_t contentLen) +{ + TRIGGER_LOGI("PostTrigger %d", type); + TRIGGER_CHECK(content != NULL && contentLen > 0, return, "Invalid param"); + if (type == EVENT_BOOT || g_triggerServiceStart == 0) { + CheckTriggers(type, content, contentLen); + } else { + TriggerDataEvent *event = (TriggerDataEvent *)malloc(sizeof(TriggerDataEvent) + contentLen + 1); + TRIGGER_CHECK(event != NULL, return, "Failed to alloc memory"); + event->type = type; + event->request.data = (char*)event + sizeof(uv_work_t); + event->contentSize = contentLen; + memcpy_s(event->content, contentLen, content, contentLen); + event->content[contentLen] = '\0'; + SendTriggerEvent(event); + } + TRIGGER_LOGI("PostTrigger %d success", type); +} + +void StartTriggerService() +{ + TRIGGER_LOGI("StartTriggerService "); + g_triggerServiceStart = 1; +} + +int ParseTriggerConfig(cJSON *fileRoot) +{ + TRIGGER_CHECK(fileRoot != NULL, return -1, "Invalid file"); + int ret = InitTriggerWorkSpace(&g_triggerWorkSpace); + TRIGGER_CHECK(ret == 0, return -1, "Failed to init trigger"); + + cJSON *triggers = cJSON_GetObjectItemCaseSensitive(fileRoot, TRIGGER_ARR_NAME_IN_JSON); + TRIGGER_CHECK(cJSON_IsArray(triggers), return -1, "Trigger item must array"); + + int size = cJSON_GetArraySize(triggers); + TRIGGER_CHECK(size > 0, return -1, "Trigger array size must positive"); + + for (int i = 0; i < size; ++i) { + cJSON *item = cJSON_GetArrayItem(triggers, i); + ParseTrigger(&g_triggerWorkSpace, item); + } + return 0; +} + +void DoTriggerExec(const char *content) +{ + TRIGGER_CHECK(content != NULL, return, "Invalid trigger content"); + u_int32_t triggerIndex = 0; + TriggerNode *trigger = GetTriggerByName(&g_triggerWorkSpace, content, &triggerIndex); + if (trigger != NULL) { + ExecuteTrigger(&g_triggerWorkSpace, trigger, DoCmdExecute); + } +} + +TriggerWorkSpace *GetTriggerWorkSpace() +{ + return &g_triggerWorkSpace; +} \ No newline at end of file diff --git a/services/trigger/trigger_processor.h b/services/trigger/trigger_processor.h new file mode 100755 index 0000000000000000000000000000000000000000..94ed8ff28ce6952516562018548d317070071f85 --- /dev/null +++ b/services/trigger/trigger_processor.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef BASE_STARTUP_EVENT_MANAGER_H +#define BASE_STARTUP_EVENT_MANAGER_H + +#include + +#include "property.h" +#include "trigger.h" +#include "trigger_manager.h" +#include "uv.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +typedef struct TriggerEvent { + uv_work_t request; + EventType type; +} TriggerEvent; + +typedef struct { + uv_work_t request; + EventType type; + u_int32_t contentSize; + char content[0]; +} TriggerDataEvent; + +typedef struct TriggerExecute { + TriggerEvent event; + TriggerNode *trigger; +} TriggerExecute; + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif +#endif \ No newline at end of file