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..09f78ac39a9391bce899a3727c39de694bb31447 100644 --- a/services/BUILD.gn +++ b/services/BUILD.gn @@ -25,12 +25,16 @@ 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", "//third_party/cJSON", "//third_party/bounds_checking_function/include", "//base/startup/syspara_lite/interfaces/kits", @@ -74,7 +78,6 @@ if (defined(ohos_lite)) { } } else { import("//build/ohos.gni") - ohos_executable("updaterueventd") { sources = [ "src/list.c", @@ -86,7 +89,7 @@ if (defined(ohos_lite)) { ] deps = [ "//third_party/bounds_checking_function:libsec_static" ] install_enable = true - part_name = "updater" + part_name = "init" } ohos_executable("updaterinit") { @@ -95,28 +98,49 @@ if (defined(ohos_lite)) { "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", "//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", ] 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", + ":updaterinit", + ":updaterueventd", + "//base/startup/init_lite/services/trigger:triggerservice", + "//base/startup/init_lite/services/property:propertyserver", + "//base/startup/init_lite/services/property:propertyclient" + ] } } 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_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..a3374b283817e926e07c592300f415828cf7df5f 100644 --- a/services/include/init_cmds.h +++ b/services/include/init_cmds.h @@ -23,8 +23,8 @@ 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 // one cmd line typedef struct { @@ -35,6 +35,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..55003d4ea02aa057357f17a2a563252e5d496ff5 --- /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); \ + 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..2e3412dde827534cafc895349c865b83424951b0 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 64 // 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..5446057f6c65de43a4e2f814357f02f89607ea1f --- /dev/null +++ b/services/include/init_utils.h @@ -0,0 +1,28 @@ +/* + * 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 + +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); + +#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..e9103756e601431545e5c4347098706a7e8a0258 --- /dev/null +++ b/services/include/property/property.h @@ -0,0 +1,85 @@ +/* + * 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 + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +#define KEY_SIZE 128 // max property Key size. +#define CONTEXT_STRING_LEN 128 + +typedef struct UserCred { + pid_t pid; + uid_t uid; + gid_t gid; +} UserCred; + +typedef struct { + char context[CONTEXT_STRING_LEN]; + UserCred cred; +} PropertySecurityContext; + +typedef struct { + char name[KEY_SIZE]; + char value[KEY_SIZE]; +} Property; + +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; + +/** + * 对外接口 + * 设置属性,主要用于其他进程使用,此处会调用Socket通信,将数据发送到Init进程propertyServer服务。 + * + */ +int SetProperty(const char *name, const char *value); + +/** + * 对外接口 + * 查询属性,主要用于其他进程使用,此处通过mmap映射,直接将属性内存映射到进程空间,直接访问。 + * + */ +int GetProperty(const char *name, char **value); + +/** + * 内部接口,用于启动属性服务 + */ +int StartPropertyService(); + +/** + * 内部接口,用于从文件中初始化默认属性 + */ +void InitPropertyService(char **fileName, int argc); +#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..ce1e0aac9aece72bef056e8c98d1d460f18e0000 --- /dev/null +++ b/services/include/trigger/trigger.h @@ -0,0 +1,45 @@ +/* + * 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_ACTION, // Action事件,主要指一些无需属性修改就能触发的事件 + EVENT_BOOT, + EVENT_EXECUTE +} EventType; + +void PostTrigger(EventType type, void *content); + +void StartTriggerService(); + +int ParseTriggerConfig(cJSON *fileRoot); + +#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..ae39ff11a7d5052c749c9c145caf08db6df967c6 --- /dev/null +++ b/services/property/BUILD.gn @@ -0,0 +1,66 @@ +# 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", + "service/property_service.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_shared_library("propertyclient") { + install_enable = true + + sources = [ + "manager/property_manager.c", + "client/property_request.c", + "//base/startup/init_lite/services/src/init_utils.c", + "//base/startup/init_lite/services/src/list.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" +} diff --git a/services/property/client/property_request.c b/services/property/client/property_request.c new file mode 100755 index 0000000000000000000000000000000000000000..e04d5c983252f264c51f6967a7f025ce9f6c7700 --- /dev/null +++ b/services/property/client/property_request.c @@ -0,0 +1,158 @@ +/* + * 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 = {NULL, NULL}; + +typedef struct { + uv_loop_t *loop; + uv_connect_t connect; + uv_pipe_t handle; + int result; + RequestMsg msg; +} RequestNode; + +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 *)MEMALLOC(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 %lx", handle, pthread_self()); + if (nread <= 0 || buf == NULL || buf->base == NULL) { + MEMFREE(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 %lx", handle, response->type, response->result, pthread_self()); + 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 %lx", handle, pthread_self()); + MEMFREE(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"); + 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.msgSize); + 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, void *content, int size) +{ + PROPERTY_LOGI("StartRequest %d %lx", cmd, pthread_self()); + RequestNode *request = (RequestNode *)MEMALLOC(sizeof(RequestNode) + size); + PROPERTY_CHECK(request != NULL, return -1, "Failed to malloc for connect"); + + request->result = -1; + request->msg.type = cmd; + request->msg.msgSize = size + sizeof(request->msg); + if (size > 0 && content != NULL) { + int ret = memcpy_s(request->msg.content, size, content, size); + PROPERTY_CHECK(ret == 0, MEMFREE(request); return -1, "Failed to copy porperty"); + } + + 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); + + PROPERTY_LOGI("Finish request %d handle %p result %d %lx", cmd, &request->handle, request->result, pthread_self()); + int result = request->result; + MEMFREE(request); + return result; +} + +int SetProperty(const char *name, const char *value) +{ + PROPERTY_CHECK(name != NULL && value != NULL, return -1, "Invalid param"); + PROPERTY_CHECK(strlen(value) < KEY_SIZE, return PROPERTY_CODE_INVALID_VALUE, "Illegal property value"); + int ret = CheckPropertyName(name); + PROPERTY_CHECK(ret == 0, return ret, "Illegal property name"); + + Property prop; + ret = memcpy_s(prop.name, sizeof(prop.name), name, strlen(name)); + PROPERTY_CHECK(ret == 0, return -1, "Failed to copy porperty"); + ret = memcpy_s(prop.value, sizeof(prop.value), value, strlen(value)); + PROPERTY_CHECK(ret == 0, return -1, "Failed to copy porperty"); + prop.name[strlen(name)] = '\0'; + prop.value[strlen(value)] = '\0'; + + PROPERTY_LOGI("SetProperty %s %s %lx", name, value, pthread_self()); + ret = StartRequest(SET_PROPERTY, (void*)&prop, sizeof(prop)); + PROPERTY_CHECK(ret == 0, return ret, "Failed to start request"); + PROPERTY_LOGI("SetProperty %s %s %lx finish", name, value, pthread_self()); + return ret; +} + +int GetProperty(const char *name, char **value) +{ + PROPERTY_CHECK(name != NULL, return -1, "The name is null"); + PROPERTY_CHECK(strlen(name) < KEY_SIZE, return -1, "Invalid param size"); + + if (g_propertyWorkSpaceReadOnly.area == NULL) { + InitPropertyWorkSpaceReadOnly(PROPERTY_STORAGE_PATH, &g_propertyWorkSpaceReadOnly); + } + *value = NULL; + Property *prop = PropertyGet(&g_propertyWorkSpaceReadOnly, name); + if (prop) { + PROPERTY_LOGI("GetProperty, name %s %s", prop->name, prop->value); + *value = strdup(prop->value); + return 0; + } + return -1; +} \ No newline at end of file diff --git a/services/property/include/property_manager.h b/services/property/include/property_manager.h new file mode 100755 index 0000000000000000000000000000000000000000..50f852ea0305b5bb7469db9cdb0588c0b414f335 --- /dev/null +++ b/services/property/include/property_manager.h @@ -0,0 +1,127 @@ +/* + * 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 "securec.h" + + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +#define MEMALLOC malloc +#define MEMFREE free + +#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; \ + } + +typedef struct PropertyAuditData { + const UserCred *cr; + const char *name; +} PropertyAuditData; + +// 内存中每个属性存储节点 +typedef struct { + int serial; + unsigned int index; // 相对于起始位置的偏移值,单位为PropertyNode大小 + Property prop; + unsigned int left; // 左子树偏移值 + unsigned int right; // 右子树偏移值 +} PropertyNode; + +// 内存中属性工作区域详情 +typedef struct { + int serial; + int used; // 已经使用的属性数量 + int sum; // 支持的属性总数量 + unsigned int propertyOffset; // PropertyNode位置偏移值 +} PropertyArea; + +// 内存中mmap映射的工作区域 +typedef struct { + PropertyArea *area; // 指向工作区域元数据信息 + PropertyNode *propertySpace; // 指向工作区域属性存储位置 +} PropertyWorkSpace; + +/** + * 解除指定的内存空间与文件映射 + * + */ +void CloseWorkSpace(PropertyWorkSpace *workSpace); + +/** + * 初始化属性管理服务使用的工作空间 + * + */ +int InitPropertyWorkSpace(const char *fileName, PropertyWorkSpace *workSpace); + +/** + * 初始化其他进程属性查询使用的工作空间 + * + */ +int InitPropertyWorkSpaceReadOnly(const char *fileName, PropertyWorkSpace *workSpace); + +/** + * 初始化属性管理服务持久化存储属性使用的工作空间 + * + */ +int InitPersistWorkSpace(const char *fileName, PropertyWorkSpace *workSpace); + +/** + * 检查属性名字是否合法 + */ +int CheckPropertyName(const char *name); + +/** + * 检查属性是否允许修改 + */ +int CanSetProperty(PropertyWorkSpace *workSpace, + const PropertySecurityContext *context, const char *name, const char *value); + +/** + * 检查对用的属性是否允许读操作 + */ +int CanReadProperty(PropertyWorkSpace *workSpace, const PropertySecurityContext *context, const char *name); + +/** + * 根据名字获取对应的属性值 + */ +Property *PropertyGet(PropertyWorkSpace *workSpace, const char *name); + +/** + * 保存属性值 + */ +int PropertySet(PropertyWorkSpace *workSpace, const char *name, const char *value); + +#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..d733f68b7f8591a8d29af28d26c3b7493df8448d --- /dev/null +++ b/services/property/include/property_request.h @@ -0,0 +1,63 @@ +/* + * 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" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +#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" +#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" +#endif + +typedef enum RequestType { + SET_PROPERTY, + GET_PROPERTY +} RequestType; + +typedef struct { + PropertySecurityContext context; + RequestType type; + int msgSize; // 从type开始的整个消息的长度 + char content[0]; +} RequestMsg; + +typedef struct { + RequestType type; + int msgSize; + int result; + char content[0]; +} ResponseMsg; + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif +#endif diff --git a/services/property/include/property_service.h b/services/property/include/property_service.h new file mode 100755 index 0000000000000000000000000000000000000000..613dd89238c4984e7feb50afc46345fd0f33d989 --- /dev/null +++ b/services/property/include/property_service.h @@ -0,0 +1,51 @@ +/* + * 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" +#include "property_manager.h" +#include "property_request.h" +#include "uv.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +typedef struct { + uv_write_t writer; + ResponseMsg msg; +} ResponseNode; + +void OnReceiveAlloc(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buf); +void OnWriteResponse(uv_write_t* req, int status); +// libuv中,在接受到数据时触发调用,此处主要完成属性的修改,添加属性修改事件到Event队列,以触发属性相关的Triger执行。 +void OnReceiveRequest(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf); +void RemoveSocket(int sig); +void OnConnection(uv_stream_t *server, int status); +void *RunPipeServer(void *args); +int ProcessPropertySet(PropertyWorkSpace *workSpace, + const PropertySecurityContext *context, const char *name, const char *value); +void SetDefaultProperty(PropertyWorkSpace *workSpace, const char *fileName); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif +#endif \ No newline at end of file diff --git a/services/property/manager/property_manager.c b/services/property/manager/property_manager.c new file mode 100755 index 0000000000000000000000000000000000000000..06e0e2ef7dd25cc8082c9112a04371302ddc39b6 --- /dev/null +++ b/services/property/manager/property_manager.c @@ -0,0 +1,377 @@ +/* + * 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" +#define PROPERTY_SUM_IN_AREA 1024 + +static unsigned int g_propertyAreaSize = sizeof(PropertyArea) + sizeof(Property) * (PROPERTY_SUM_IN_AREA + 1); + +static int CheckMacPerms(const PropertySecurityContext *context, const char *name, const char *value) +{ + PROPERTY_CHECK(context != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + PROPERTY_CHECK(name != NULL && value != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + + PropertyAuditData auditData; + auditData.name = name; + auditData.cr = &context->cred; + /*if (selinux_check_access(context->context, value, "property_service", "set", &auditData) != 0) { + return PROPERTY_CODE_PERMISSION_DENIED; + }*/ + return 0; +} + +static int CheckControlPropertyPerms(PropertyWorkSpace *workSpace, + const PropertySecurityContext *context, const char *name, const char *value) +{ + PROPERTY_CHECK(context != 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*)MEMALLOC(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, MEMFREE(legacyName); return PROPERTY_CODE_INVALID_PARAM, "Failed to snprintf value"); + legacyName[n] = '\0'; + Property *oldProp = PropertyGet(workSpace, legacyName); + if (CheckMacPerms(context, legacyName, (oldProp == NULL) ? "" : oldProp->value) == 0) { + return 0; + } + break; + } + } + int n = snprintf_s(legacyName, size, size, "%s$%s", name, value); + PROPERTY_CHECK(n > 0, MEMFREE(legacyName); return PROPERTY_CODE_INVALID_PARAM, "Failed to snprintf value"); + legacyName[n] = '\0'; + Property *oldProp = PropertyGet(workSpace, legacyName); + int ret = CheckMacPerms(context, legacyName, (oldProp == NULL) ? "" : oldProp->value); + MEMFREE(legacyName); + return ret; +} + +static void InitPropertyNode(PropertyNode *node, const char *key, const char *value) +{ + node->serial = 0; + node->left = 0; + node->right = 0; + int ret = memcpy_s(node->prop.name, sizeof(node->prop.name), key, strlen(key)); + PROPERTY_CHECK(ret == 0, return, "Failed to copy porperty"); + ret = memcpy_s(node->prop.value, sizeof(node->prop.value), value, strlen(value)); + PROPERTY_CHECK(ret == 0, return, "Failed to copy porperty"); + (node->prop.name)[strlen(key)] = '\0'; + (node->prop.value)[strlen(value)] = '\0'; +} + +static PropertyNode *AllocateProperty(PropertyWorkSpace *workSpace) +{ + PROPERTY_CHECK(workSpace->area->used < workSpace->area->sum, + return NULL, "Failed to allocate used %d, sum %d", workSpace->area->used, workSpace->area->sum); + PropertyNode *newPropAddr = workSpace->propertySpace + workSpace->area->used; + newPropAddr->index = workSpace->area->used; + workSpace->area->used++; + return newPropAddr; +} + +static 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); +} + +/** + * 内存中要映射的文件空间初始化,完成文件与内存空间映射 + * type = -1 Init the PropertyWorkSpace 初始化内存空间,支持读写操作 + * type = 0 Init the PersitWorkSpace 初始化持久化存储空间 + * type = 1 Init the ReadOnlyWorkSpace 初始化只读空间 + * falg = 0 create and set size 初始化空间时设置大小,主要用于指定的文件不存在或者为空,限定存储空间大小。 + * flag = 1 no set size. 初始化空间时不设置大小,大小使用原始文件大小。 + */ +static int InitWorkSpace(const char *fileName, PropertyWorkSpace *workSpace, int type, int flag) +{ + PROPERTY_CHECK(fileName != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid fileName"); + if (workSpace->area != NULL) { + return 0; + } + PROPERTY_LOGI("InitWorkSpace %s type: %x %x", fileName, type, flag); + CheckAndCreateDir(fileName); + + int openMode = 0; + int mmapMode = PROT_READ; + if (type == -1) { // 支持读写操作 + openMode = O_CREAT | O_RDWR | O_TRUNC; + mmapMode = PROT_READ | PROT_WRITE; + } else if (type == 0) { // 持久化存储空间 + openMode = O_CREAT | O_RDWR; + mmapMode = PROT_READ | PROT_WRITE; + } else if (type == 1) { // 只读空间 + openMode = O_RDONLY; + mmapMode = PROT_READ; + } else { + PROPERTY_LOGE("the type is not supported"); + return PROPERTY_CODE_NOT_SUPPORT; + } + + int fd = open(fileName, openMode, 00777); + PROPERTY_CHECK(fd >= 0, return PROPERTY_CODE_INVALID_NAME, + "Open file %s fail error %s", fileName, strerror(errno)); + if (flag == 0) { + lseek(fd, g_propertyAreaSize, SEEK_SET); + write(fd, "", 1); + } else if (openMode != O_RDONLY) { + struct stat statbuf; + stat(fileName, &statbuf); + if (statbuf.st_size < g_propertyAreaSize) { // 修改扩容后,增加文件大小 + lseek(fd, g_propertyAreaSize, SEEK_SET); + write(fd, "", 1); + } + } + + PROPERTY_LOGI("begin to mmap %s", fileName); + void *areaAddr = (void *)mmap(NULL, g_propertyAreaSize, mmapMode, MAP_SHARED, fd, 0); + PROPERTY_CHECK(areaAddr != NULL, close(fd); return PROPERTY_CODE_ERROR_MAP_FILE, + "Failed to map memory error %s", strerror(errno)); + workSpace->area = (PropertyArea*)areaAddr; + if (flag == 0) { + workSpace->area->serial = 0; + workSpace->area->used = 0; + workSpace->area->sum = PROPERTY_SUM_IN_AREA; + workSpace->area->propertyOffset = sizeof(PropertyArea); + } + if (openMode != O_RDONLY) { + workSpace->area->sum = PROPERTY_SUM_IN_AREA; + } + + workSpace->propertySpace = (PropertyNode*)((char*)areaAddr + workSpace->area->propertyOffset); + PROPERTY_LOGI("init %s space success, serial: %d, used: %d, sum: %d, offset: %d fileSize: %d", fileName, + workSpace->area->serial, workSpace->area->used, workSpace->area->sum, + workSpace->area->propertyOffset, g_propertyAreaSize); + close(fd); + return 0; +} + +static PropertyNode *InsertNode(PropertyWorkSpace *workSpace, PropertyNode *tree, const char *key, const char *value) +{ + if (tree == NULL) { + tree = AllocateProperty(workSpace); + PROPERTY_CHECK(tree != NULL, return NULL, "Failed to allocate"); + InitPropertyNode(tree, key, value); + PROPERTY_LOGI("Insert Value: %s, %s", key, value); + return tree; + } + + int ret = strcmp(tree->prop.name, key); + PropertyNode *child = NULL; + if(ret > 0) { + if (tree->left > 0) { + child = workSpace->propertySpace + tree->left; + } + child = InsertNode(workSpace, child, key, value); + if (child != NULL) { + tree->left = child->index; + } else { + return NULL; + } + } else if (ret < 0) { + if (tree->right > 0) { + child = workSpace->propertySpace + tree->right; + } + child = InsertNode(workSpace, child, key, value); + if (child != NULL) { + tree->right = child->index; + } else { + return NULL; + } + } else { + PROPERTY_LOGI("Update %s value %s to %s ", key, tree->prop.value, value); + ret = memcpy_s(tree->prop.value, sizeof(tree->prop.value), value, strlen(value)); + PROPERTY_CHECK(ret == 0, return NULL, "Failed to copy porperty"); + (tree->prop.value)[strlen(value)] = '\0'; + } + return tree; +} + +static PropertyNode *FindNode(PropertyWorkSpace *workSpace, PropertyNode *tree, const char *key) +{ + PROPERTY_CHECK(tree != NULL, return NULL, "Invalid tree to find"); + int ret = strcmp(tree->prop.name, key); + if (ret == 0) { + PROPERTY_LOGI("Found key: %s index %u", key, tree->index); + return tree; + } else if (ret < 0) { + if (tree->right > 0) { + PropertyNode *rightChild = workSpace->propertySpace + tree->right; + return FindNode(workSpace, rightChild, key); + } + } else { + if (tree->left > 0) { + PropertyNode *leftChild = workSpace->propertySpace + tree->left; + return FindNode(workSpace, leftChild, key); + } + } + return NULL; +} + +int CheckPropertyName(const char *name) +{ + size_t nameLen = strlen(name); + if (nameLen >= KEY_SIZE) { + return PROPERTY_CODE_INVALID_NAME; + } + + if (nameLen < 1 || name[0] == '.' || name[nameLen - 1] == '.') { + 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; +} + +void CloseWorkSpace(PropertyWorkSpace *workSpace) +{ + PROPERTY_CHECK(workSpace != NULL && workSpace->area != NULL, return, "The workspace is null"); + unsigned int size = sizeof(PropertyArea) + sizeof(Property) * PROPERTY_SUM_IN_AREA + 1; + munmap((char *)workSpace->area, size); + workSpace->area = NULL; +} + +int InitPropertyWorkSpace(const char *fileName, PropertyWorkSpace *workSpace) +{ + return InitWorkSpace(fileName, workSpace, -1, 0); +} + +int InitPersistWorkSpace(const char *fileName, PropertyWorkSpace *workSpace) +{ + int flag = (access(fileName, F_OK) == 0) ? 1 : 0; + return InitWorkSpace(fileName, workSpace, 0, flag); +} + +int InitPropertyWorkSpaceReadOnly(const char *fileName, PropertyWorkSpace *workSpace) +{ + return InitWorkSpace(fileName, workSpace, 1, 1); +} + +int PropertySet(PropertyWorkSpace *workSpace, const char *name, const char *value) +{ + PROPERTY_CHECK(workSpace != NULL && workSpace->area != NULL && name != NULL && value != NULL, + return PROPERTY_CODE_INVALID_PARAM, "Failed to check param"); + + PropertyNode *node = InsertNode(workSpace, workSpace->propertySpace, name, value); + if (node == NULL) { + return PROPERTY_CODE_REACHED_MAX; + } + msync((char *)workSpace->area, g_propertyAreaSize, MAP_SHARED); + return 0; +} + +Property *PropertyGet(PropertyWorkSpace *workSpace, const char *name) +{ + PROPERTY_CHECK(workSpace != NULL && workSpace->area != NULL, return NULL, "Failed to check param"); + PROPERTY_CHECK(name != NULL && strlen(name) < KEY_SIZE, return NULL, "Invalid param size"); + + PropertyNode *node = FindNode(workSpace, workSpace->propertySpace, name); + if (node != NULL) { + return &node->prop; + } + return NULL; +} + +int CanSetProperty(PropertyWorkSpace *workSpace, + const PropertySecurityContext *context, const char *name, const char *value) +{ + PROPERTY_CHECK(context != NULL && name != NULL && value != NULL, + return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + + Property *oldProp = PropertyGet(workSpace, name); + if (oldProp != NULL && strncmp(name, "ro.", strlen("ro.")) == 0) { + PROPERTY_LOGE("Read-only property was already set %s", name); + return PROPERTY_CODE_READ_ONLY_PROPERTY; + } + + if (strncmp(name, "ctl.", strlen("ctl.")) == 0) { // 处理ctrl TODO + return CheckControlPropertyPerms(workSpace, context, name, value); + } + + int ret = CheckMacPerms(context, name, (oldProp == NULL) ? "" : oldProp->value); + PROPERTY_CHECK(ret == 0, return ret, "SELinux permission check failed %s", name); + + /* TODO 是否检查 + if (type == nullptr || !CheckType(type, value)) { + *error = StringPrintf("Property type check failed, value doesn't match expected type '%s'", + (type ?: "(null)")); + return PROP_ERROR_INVALID_VALUE; + } + */ + return 0; +} + +int CanReadProperty(PropertyWorkSpace *workSpace, const PropertySecurityContext *context, const char *name) +{ + PROPERTY_CHECK(context != NULL && name != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param"); + PropertyNode *node = FindNode(workSpace, workSpace->propertySpace, name); + PROPERTY_CHECK(node != NULL, return -1, "Property not found %s", name); + + PropertyAuditData auditData; + auditData.name = name; + UserCred cr = {.pid = 0, .uid = 0, .gid = 0}; + auditData.cr = &cr; + + return 0; //selinux_check_access(context, node->prop.value, "file", "read", &auditData); +} \ No newline at end of file diff --git a/services/property/service/property_service.c b/services/property/service/property_service.c new file mode 100755 index 0000000000000000000000000000000000000000..261690ddaf0895db4a86c1ae13d4d1680f66cffc --- /dev/null +++ b/services/property/service/property_service.c @@ -0,0 +1,254 @@ +/* + * 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 "selinux.h" + +#include "uv.h" + +#define BUFFER_SIZE 256 +#define LABEL "Server" + +static char *g_initContext = ""; +static PropertyWorkSpace g_persistWorkSpace = {NULL, NULL}; +static PropertyWorkSpace g_propertyWorkSpace = {NULL, NULL}; + +/* +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; +} +*/ + +void OnReceiveAlloc(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buf) +{ + // 这里需要按实际消息的大小申请内存,取最大消息的长度 + buf->base = (char *)MEMALLOC(sizeof(RequestMsg) + sizeof(Property)); + buf->len = suggestedSize; + PROPERTY_LOGI("OnReceiveAlloc buf->base %p %zu", buf->base, suggestedSize); +} + +void OnWriteResponse(uv_write_t* req, int status) +{ + // 发送成功,释放请求内存 + PROPERTY_LOGI("OnWriteResponse status %d", status); + ResponseNode *node = (ResponseNode*)req; + MEMFREE(node); +} + +void SendResponse(uv_stream_t *handle, RequestType type, int result, void *content, int size) +{ + int ret = 0; + // 申请整块内存,用于回复数据和写请求 + ResponseNode *response = (ResponseNode *)MEMALLOC(sizeof(ResponseNode) + size); + PROPERTY_CHECK(response != NULL, return, "Failed to alloc memory for response"); + response->msg.type = type; + response->msg.msgSize = sizeof(response->msg) + 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)); +} + +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, NULL); + MEMFREE(buf->base); + MEMFREE(handle); + return; + } + int freeHandle = 1; + RequestMsg *msg = (RequestMsg *)buf->base; + PROPERTY_LOGI("OnReceiveRequest %p cmd %d %lu", handle, msg->type, pthread_self()); + switch (msg->type) { + case SET_PROPERTY: { + PROPERTY_CHECK((size_t)msg->msgSize > sizeof(Property), break, "Failed to check msg size %d", msg->msgSize); + freeHandle = 0; + Property *prop = (Property *)(msg->content); + //Property *oldProp = PropertyGet(&g_propertyWorkSpace, prop->name); + int ret = ProcessPropertySet(&g_propertyWorkSpace, &msg->context, prop->name, prop->value); + SendResponse(handle, SET_PROPERTY, ret, NULL, 0); + // notify event to process trigger + if (ret == 0 /*&& (oldProp == NULL || strcmp(oldProp->value, prop->value) != 0)*/) { + PostTrigger(EVENT_PROPERTY, (void*)prop); + } + break; + } + default: + PROPERTY_LOGE("not supported the command: %d", msg->type); + break; + } + + MEMFREE(buf->base); + if (freeHandle) { + uv_close((uv_handle_t*)handle, NULL); + MEMFREE(handle); + } +} + +void RemoveSocket(int sig) +{ + uv_fs_t req; + uv_fs_unlink(uv_default_loop(), &req, PIPE_NAME, NULL); + CloseWorkSpace(&g_propertyWorkSpace); + CloseWorkSpace(&g_persistWorkSpace); + uv_stop(uv_default_loop()); + exit(0); +} + +void OnConnection(uv_stream_t *server, int status) +{ + PROPERTY_CHECK(status >= 0, return, "Error status %d", status); + PROPERTY_CHECK(server != NULL, return, "Error server"); + PROPERTY_LOGI("OnConnection "); + + uv_pipe_t *stream = (uv_pipe_t*)MEMALLOC(sizeof(uv_pipe_t)); + PROPERTY_CHECK(stream != NULL, return, "Failed to alloc stream"); + PROPERTY_LOGI("OnConnection %p", stream); + + int ret = uv_pipe_init(uv_default_loop(), (uv_pipe_t*)stream, 1); + PROPERTY_CHECK(ret == 0, MEMFREE(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); MEMFREE(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); MEMFREE(stream); + return, "Failed to uv_read_start %d", ret); +} + +int StartPropertyService() +{ + /*union selinux_callback cb; + cb.func_audit = SelinuxAuditCallback; + selinux_set_callback(SELINUX_CB_AUDIT, cb); + */ + + 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)); + + PROPERTY_LOGI("Start service success. %lu", pthread_self()); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + PROPERTY_LOGI("Start service exit."); + return 0; +} + +void InitPropertyService(char **fileName, int argc) +{ + InitPersistWorkSpace(PROPERTY_PERSIST_PATH, &g_persistWorkSpace); + InitPropertyWorkSpace(PROPERTY_STORAGE_PATH, &g_propertyWorkSpace); + for (int i = 0; i < argc; i++) { + SetDefaultProperty(&g_propertyWorkSpace, fileName[i]); + } +} + +void SetDefaultProperty(PropertyWorkSpace *workSpace, const char *fileName) +{ + PROPERTY_CHECK(workSpace != NULL && workSpace->area != NULL && fileName != NULL, return, "The workspace is null"); + FILE *fp = fopen(fileName, "r"); + PROPERTY_CHECK(fp != NULL, return, "Open file %s fail", fileName); + + UserCred cr = {.pid = 1, .uid = 0, .gid = 0}; + PropertySecurityContext context; + memcpy_s(&context.cred, sizeof(context.cred), &cr, sizeof(cr)); + memcpy_s(&context.context, sizeof(context.context), g_initContext, strlen(g_initContext)); + + char buff[BUFFER_SIZE]; + while(fgets(buff, BUFFER_SIZE, fp) != NULL) { + char *key = strtok(buff, "="); + char *value = strtok(NULL, "="); + if (key == NULL || value == NULL) { + continue; + } + PROPERTY_LOGI("Set default property %s = %s", key, value); + + if (strncmp(key, "ctl.", strlen("ctl.")) == 0) { + PROPERTY_LOGE("Do not set ctl. properties from init %s", key); + continue; + } + if (strcmp(key, "selinux.restorecon_recursive") == 0) { + PROPERTY_LOGE("Do not set selinux.restorecon_recursive from init %s", key); + continue; + } + + int ret = ProcessPropertySet(workSpace, &context, key, value); + PROPERTY_CHECK(ret == 0, continue, "Failed to set property %d", ret); + } + fclose(fp); + PROPERTY_LOGI("Get default proterty success"); +} + +int ProcessPropertySet(PropertyWorkSpace *workSpace, + const PropertySecurityContext *context, const char *name, const char *value) +{ + PROPERTY_CHECK(workSpace != NULL && workSpace->area != NULL, + return PROPERTY_CODE_INVALID_PARAM, "Failed to check param"); + PROPERTY_CHECK(name != NULL && value != NULL, return PROPERTY_CODE_INVALID_PARAM, "Failed to check param"); + PROPERTY_CHECK(strlen(value) < KEY_SIZE, return PROPERTY_CODE_INVALID_VALUE, "Illegal property value"); + int ret = CheckPropertyName(name); + PROPERTY_CHECK(ret == 0, return ret, "Illegal property name"); + + PROPERTY_LOGI("ProcessPropertySet name %s %s", name, value); + ret = CanSetProperty(workSpace, context, name, value); + if (ret == PROPERTY_CODE_READ_ONLY_PROPERTY) { + return 0; + } + PROPERTY_CHECK(ret == 0, return ret, "Can set property ret %d", ret); + + ret = PropertySet(workSpace, name, value); + PROPERTY_CHECK(ret == 0, return ret, "Failed to set property"); + + if (strncmp(name, "persist.", strlen("persist.")) == 0) { + ret = PropertySet(&g_persistWorkSpace, name, value); + PROPERTY_CHECK(ret == 0, return ret, "Failed to set property"); + } + return 0; +} 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_capability.c b/services/src/init_capability.c new file mode 100755 index 0000000000000000000000000000000000000000..a39bc2ff9b31538fc181c49bf8be4cd52f31a8be --- /dev/null +++ b/services/src/init_capability.c @@ -0,0 +1,157 @@ +/* + * 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 +#include +#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 { + break; + } + if (curServ->servPerm.caps[i] > CAP_LAST_CAP && curServ->servPerm.caps[i] != FULL_CAP) { + // resources will be released by function: ReleaseServiceMem + 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 + 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 + 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..48011f4bd7225973fe9414dea0746b2914848418 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,20 @@ #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" -#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 +46,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 +60,18 @@ static const char* g_supportedCmds[] = { "chmod ", "chown ", "mount ", + "export ", "loadcfg ", "insmod ", + "rm ", + "rmdir ", + "write ", + "exec ", + "mknode ", + "makedev ", + "symlink ", + "stop ", + "trigger ", }; void ParseCmdLine(const char* cmdStr, CmdLine* resCmd) @@ -88,93 +104,123 @@ 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 DoChown(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; - } - - 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; - } + // 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 +252,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 +282,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 +362,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 +553,285 @@ 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, "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); #endif - } else { - printf("[Init] DoCmd, unknown cmd name %s.\n", curCmd->name); + } else if (strncmp(name, "trigger ", strlen("trigger ")) == 0) { + printf("[Init][Debug], ready to trigger job: %s\n", name); + DoJob(cmdContent); + }else { + 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..a64c67b5dc302759fee510c42c49783e69850596 100644 --- a/services/src/init_read_cfg.c +++ b/services/src/init_read_cfg.c @@ -23,9 +23,13 @@ #include #include +#include "init_import.h" #include "init_jobs.h" #include "init_perms.h" #include "init_service_manager.h" +#ifndef OHOS_LITE +#include "trigger.h" +#endif #include "securec.h" #ifndef __LINUX__ #ifdef OHOS_LITE @@ -34,28 +38,24 @@ #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() +static 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(INIT_CONFIGURATION_FILE, &fileStat) != 0 || + if (stat(configFile, &fileStat) != 0 || fileStat.st_size <= 0 || fileStat.st_size > MAX_JSON_FILE_LEN) { break; } - fd = fopen(INIT_CONFIGURATION_FILE, "r"); + fd = fopen(configFile, "r"); if (fd == NULL) { break; } @@ -80,321 +80,54 @@ static char* ReadFileToBuf() return buffer; } -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 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; - } - - size_t strLen = strlen(fieldStr); - if (strLen == 0 || strLen > MAX_SERVICE_NAME) { - return SERVICE_FAILURE; - } - - if (memcpy_s(curServ->name, MAX_SERVICE_NAME, fieldStr, strLen) != EOK) { - return SERVICE_FAILURE; - } - curServ->name[strLen] = '\0'; - return SERVICE_SUCCESS; -} - -static int GetServicePathAndArgs(const cJSON* curArrItem, Service* curServ) -{ - 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'; - } - 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; - } - - int value = (int)cJSON_GetNumberValue(filedJ); - if (value < 0) { - return SERVICE_FAILURE; - } - - 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; - } - return SERVICE_SUCCESS; -} - -static int GetServiceCaps(const cJSON* curArrItem, Service* curServ) +static void ParseInitCfgContents(cJSON *root) { - curServ->servPerm.capsCnt = 0; - curServ->servPerm.caps = NULL; - - cJSON* filedJ = cJSON_GetObjectItem(curArrItem, "caps"); - if (!cJSON_IsArray(filedJ)) { - return SERVICE_FAILURE; - } + // parse services + ParseAllServices(root); - // 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; - } + // parse jobs + ParseAllJobs(root); - 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; - } - } - curServ->servPerm.capsCnt = capsCnt; - return SERVICE_SUCCESS; -} +#ifndef OHOS_LITE + ParseTriggerConfig(root); +#endif -static int GetServiceGids(const cJSON* curArrItem, Service* curServ) -{ - 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; + // parse imports + ParseAllImports(root); } -static void ParseAllServices(const cJSON* fileRoot) +void ParseInitCfg(const char *configFile) { - 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); + if (configFile == NULL || *configFile == '\0') { + printf("[Init] Invalid config file\n"); 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); -} - -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; - } + char *fileBuf = ReadFileToBuf(configFile); + //printf("[Init] start dump config file: \n"); + //printf("%s", fileBuf); + //printf("[Init] end dump config file: \n"); 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); + printf("[Init] InitReadCfg, parse failed! please check file %s format.\n", configFile); return; } - - // parse services - ParseAllServices(fileRoot); - - // parse jobs - ParseAllJobs(fileRoot); - - // release memory + ParseInitCfgContents(fileRoot); + // Release JSON object cJSON_Delete(fileRoot); + return; +} +void InitReadCfg() +{ + ParseInitCfg(INIT_CONFIGURATION_FILE); + 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..4a64a212d220d4a0e3df578ac48801f9660d8a5b 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,11 +59,14 @@ 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) { + printf("[Init][Debug], setuid of service: %s failed, uid = %d\n", service->name, service->servPerm.uID); return SERVICE_FAILURE; } @@ -79,6 +93,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 +101,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 +128,56 @@ int ServiceStart(Service *service) service->name, service->pathArgs[0]); return SERVICE_FAILURE; } - + int ret = 0; int pid = fork(); if (pid == 0) { // 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("@@@@@@@@@@ writepid filename=%s, childPid=%s, ok\n", service->writepidFiles[i], pidString); + } + if (service->socketCfg != NULL) { // start socket service + printf("[init] ServiceStart DoCreateSocket \n"); + ret = DoCreateSocket(service->socketCfg); + if (ret < 0) { + return SERVICE_FAILURE; + } + } + 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 +211,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 +258,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 +294,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..29e0efeaa226b27366560216b0b01742932d708d 100755 --- a/services/src/init_service_manager.c +++ b/services/src/init_service_manager.c @@ -14,23 +14,676 @@ */ #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; + 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) { + return SERVICE_FAILURE; + } + + size_t strLen = strlen(fieldStr); + if (strLen == 0 || strLen > MAX_SERVICE_NAME) { + return SERVICE_FAILURE; + } + + if (memcpy_s(curServ->name, MAX_SERVICE_NAME, fieldStr, strLen) != EOK) { + 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) { + 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) { + 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) { + 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)) { + 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'; + } + 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)) { + return SERVICE_FAILURE; + } + + int value = (int)cJSON_GetNumberValue(filedJ); + if (value < 0) { + 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 { + 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); + + 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 + if (GetServiceName(curItem, &tmp[i]) != SERVICE_SUCCESS || + GetServicePathAndArgs(curItem, &tmp[i]) != SERVICE_SUCCESS || + GetUidStringNumber(curItem, &tmp[i]) != SERVICE_SUCCESS || // uid in string or number form + GetGidArray(curItem, &tmp[i]) != SERVICE_SUCCESS || // gid array + GetServiceNumber(curItem, &tmp[i], ONCE_STR_IN_CFG) != SERVICE_SUCCESS || + GetServiceNumber(curItem, &tmp[i], IMPORTANT_STR_IN_CFG) != SERVICE_SUCCESS || + GetServiceNumber(curItem, &tmp[i], CRITICAL_STR_IN_CFG) != SERVICE_SUCCESS || // critical + GetServiceNumber(curItem, &tmp[i], DISABLED_STR_IN_CFG) != SERVICE_SUCCESS || // disabled + GetWritepidStrings(curItem, &tmp[i]) != SERVICE_SUCCESS || // writepid + GetServiceCaps(curItem, &tmp[i]) != 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); + continue; + } else { + printf("@@@@@@@@@@@ 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) @@ -64,6 +717,22 @@ void StartServiceByName(const char* servName) 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++) { @@ -89,3 +758,4 @@ void ReapServiceByPID(int pid) } } + diff --git a/services/src/init_service_socket.c b/services/src/init_service_socket.c new file mode 100755 index 0000000000000000000000000000000000000000..b6e61acb61513a3856d3e77f88231d651e7c2389 --- /dev/null +++ b/services/src/init_service_socket.c @@ -0,0 +1,171 @@ +/* + * 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] access fail %d \n", errno); + unlink(addr.sun_path); // 删除该文件 + } + if (sockopt->passcred) { + int on = 1; + if (setsockopt(sockFd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) { + unlink(addr.sun_path); + return -1; + } + } + + if (bind(sockFd, (struct sockaddr *)&addr, sizeof(addr))) { + perror("bind"); + unlink(addr.sun_path); + return -1; + } + printf("[init] bind socket success \n"); + if (lchown(addr.sun_path, sockopt->uid, sockopt->gid)) { + unlink(addr.sun_path); + 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); + printf("[init] fchmodat fail %d \n", errno); + return -1; + } + if (access(addr.sun_path, F_OK) == 0) { + printf("addr.sun_path : %s is exist \n", addr.sun_path); + } + 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..c36cb53b11f7ea02bfe81ffb70dabe142e6d4af5 --- /dev/null +++ b/services/src/init_utils.c @@ -0,0 +1,121 @@ +/* + * 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 "init_cmds.h" +#include "init_log.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 + +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; + } +} \ No newline at end of file diff --git a/services/src/main.c b/services/src/main.c index 40ab4241076b031b2acdd33311375bb6250eb3ac..992b215d79e363701095b9845876a10c998ca616 100755 --- a/services/src/main.c +++ b/services/src/main.c @@ -33,6 +33,7 @@ #ifndef OHOS_LITE #include "device.h" +#include "property.h" #endif static const pid_t INIT_PROCESS_PID = 1; @@ -82,6 +83,11 @@ int main(int argc, char * const argv[]) // 2. Mount basic filesystem and create common device node. MountBasicFs(); CreateDeviceNode(); + MakeSocketDir("/dev/unix/socket/", 0755); + char *propFilename[] = {"/system/build.prop", "/system/buildz.prop", "/vendor/build.prop", "/vendor/default.prop", + "/vendor/odm/etc/build.prop", "/system/etc/prop.default"}; + int propFileNum = 6; + InitPropertyService(propFilename, propFileNum); #endif // 3. signal register @@ -121,6 +127,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..ac878e9335ee67ec195f44ab080114f2eb33a7b0 --- /dev/null +++ b/services/trigger/BUILD.gn @@ -0,0 +1,39 @@ +# 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", + "//base/startup/init_lite/services/src/init_utils.c", + "//base/startup/init_lite/services/src/list.c", + ] + + include_dirs = [ + ".", + "//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 = "startup" + subsystem_name = "startup" +} \ 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..b35cb2724fecb2a0c06a8a08f5372bfa44a7c340 --- /dev/null +++ b/services/trigger/trigger_manager.c @@ -0,0 +1,385 @@ +/* + * 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 "init_cmds.h" + +#define BUFFER_SIZE 256 +static TriggerList *g_triggerList = NULL; +#define TRIGGER_ARR_NAME_IN_JSON "jobs" +#define CMDS_ARR_NAME_IN_JSON "cmds" + +int AddCommand(TriggerNode *trigger, const char *cmdName, const char *content) +{ + TRIGGER_CHECK(trigger != NULL, return -1, "list is null"); + TRIGGER_CHECK(content != NULL, return -1, "command is null"); + CommandNode *node = (CommandNode *)MEMALLOC(sizeof(CommandNode) + strlen(content) + 1); + TRIGGER_CHECK(node != NULL, return -1, "Failed to alloc memory for command"); + int ret = memcpy_s(node->content, strlen(content) + 1, content, strlen(content) + 1); + ret |= memcpy_s(node->name, sizeof(node->name) - 1, cmdName, strlen(cmdName)); + node->name[strlen(cmdName)] = '\0'; + TRIGGER_CHECK(ret == 0, MEMFREE(node); return -1, "Failed to copy command"); + + ListInit(&node->node); + ListAddTail(&trigger->commands, &node->node); + // TRIGGER_LOGI("AddCommand %s %s to %s", cmdName, content, trigger->name); + return 0; +} + +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", + }; + 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; +} + +void FreeTrigger(TriggerNode *trigger) +{ + TRIGGER_CHECK(trigger != NULL, return, "trigger is null"); + ListNode *node = trigger->commands.next; + while (node != NULL && node != &trigger->commands) { + CommandNode *cmd = ListEntry(node, CommandNode, node); + node = node->next; + + ListRemove(&cmd->node); + MEMFREE(cmd); + } + if (!ListEmpty(trigger->node)) { + ListRemove(&trigger->node); + } + MEMFREE(trigger); +} + +TriggerNode *CreateTrigger(int type, const char *name, const char *condition) +{ + TRIGGER_LOGI("CreateTrigger name: %s, condition: %s", name, condition); + TRIGGER_CHECK(name != NULL, return NULL, "Param is null"); + size_t len = ((condition != NULL) ? strlen(condition) + 1 : 1) + sizeof(TriggerNode); + TriggerNode *node = (TriggerNode *)MEMALLOC(len); + TRIGGER_CHECK(node != NULL, return NULL, "Failed to alloc memory for trigger"); + memset_s(node, len, 0, len); + ListInit(&node->node); + ListInit(&node->commands); + node->type = type; + int ret = memcpy_s(node->name, sizeof(node->name), name, strlen(name)); + TRIGGER_CHECK(ret == 0, MEMFREE(node); return NULL, "Failed to memcpy_s for trigger"); + node->name[strlen(name)] = '\0'; + if (condition != NULL) { + ret = memcpy_s(node->condition, strlen(condition) + 1, condition, strlen(condition)); + TRIGGER_CHECK(ret == 0, MEMFREE(node); return NULL, "Failed to memcpy_s for trigger"); + node->condition[strlen(condition)] = '\0'; + } else { + node->condition[0] = '\0'; + } + atomic_flag_clear(&node->executeFlags); + return node; +} + +TriggerList *CreateTriggerList(int count) +{ + TRIGGER_CHECK(count > 0, return NULL, "count is 0"); + TriggerList *list = (TriggerList *)MEMALLOC(sizeof(TriggerList) + sizeof(ListNode) * count); + TRIGGER_CHECK(list != NULL, return NULL, "Failed to alloc list memory for trigger"); + + list->count = count; + for (int i = 0; i < count; i++) { + ListInit(&list->triggers[i]); + } + return list; +} + +/* +int ComputerCondition(const char *condition, const Property *prop) +{ + CONDITION_STACK stack; + stack.top = 0; + size_t startIndex = 0; + size_t index = 0; + while (index < strlen(condition)) { + if (condition[index] == '(') { + stack.data[stack.top++] = condition[index]; + index++; + startIndex = index; + } else if (condition[index] == ')') { + if (stack.data[stack.top]) { + int ret = ComputerSubCondition(condition, startIndex, index, prop); + stack.data[stack.top++] = (ret == 1) ? '1' : '0'; + } + index++; + startIndex = index; + + // 弹出栈,直到栈底或者'(' + ComputerStackCondition(&stack); + } else if (strncmp(condition + index, "||", 2) == 0) { + int ret = ComputerSubCondition(condition, startIndex, index, prop); + stack.data[stack.top++] = (ret == 1) ? '1' : '0'; + stack.data[stack.top++] = '|'; + index += 2; + startIndex = index; + } else if (strncmp(condition + index, "&&", 2) == 0) { + int ret = ComputerSubCondition(condition, startIndex, index, prop); + stack.data[stack.top++] = (ret == 1) ? '1' : '0'; + stack.data[stack.top++] = '&'; + index += 2; + startIndex = index; + } else { + index++; + } + } + if (startIndex != index) { + int ret = ComputerSubCondition(condition, startIndex, index, prop); + stack.data[stack.top++] = (ret == 1) ? '1' : '0'; + } + if (stack.top > 1) { + ComputerStackCondition(&stack); + } + if (stack.top == 1) { + return stack.data[stack.top] == '1'; + } + return -1; +} + +int ComputerStackCondition(CONDITION_STACK *stack) +{ + CONDITION_STACK tmpStack; + char top = stack->data[--stack->top]; + do { + if (top == '(') { + break; + } + tmpStack.data[tmpStack.top++] = top; + if (stack->top <= 0) { + break; + } + top = stack->data[--stack->top]; + } while (1); + + // 第一个是操作数 + char right = tmpStack.data[--tmpStack.top]; + if (right != '0' && right != '1') { + return -1; + } + if (tmpStack.top <= 0) { + stack->data[stack->top++] = right; + break; + } + do { + char op = tmpStack.data[--tmpStack.top]; + TRIGGER_CHECK(tmpStack.top > 0, return -1, "Failed to get op"); + + char left = tmpStack.data[--tmpStack.top]; + TRIGGER_CHECK(left == '0' || left == '1', return -1, "Failed to get left"); + + if (op == '|') { + if (right == '1' || left == '1') { + stack->data[stack->top++] = '1'; + } else { + stack->data[stack->top++] = '0'; + } + } else if (op == '&') { + if (right == '1' && left == '1') { + stack->data[stack->top++] = '1'; + } else { + stack->data[stack->top++] = '0'; + } + } else { + return -1; + } + if (tmpStack.top <= 0) { + break; + } + } while (1); +} +*/ +int ComputerSubCondition(const char *subcondition, size_t startIndex, size_t endIndex, const Property *prop) +{ + char *condition = MEMALLOC(endIndex - startIndex + 1); //strndup(subcondition + startIndex, endIndex - startIndex); + TRIGGER_CHECK(condition != NULL, return 0, "Failed to alloc new string"); + size_t index = startIndex; + char *tmp = condition; + while (index < endIndex) { + if (subcondition[index] != ' ') { + *tmp = subcondition[index]; + tmp++; + } + index++; + } + *tmp = '\0'; + char *key = strtok(condition, "="); + char *value = strtok(NULL, "="); + if (key == NULL || value == NULL) { + free(condition); + return 0; + } + size_t keyLen = strlen(key); + size_t propNameLen = strlen(prop->name); + // check name + if (keyLen != propNameLen || strncmp(prop->name, key, keyLen) != 0) { + free(condition); + return 0; + } + if (strcmp(value, "*") == 0) { + free(condition); + return 1; + } + size_t valueLen = strlen(value); + size_t propValueLen = strlen(prop->value); + if (valueLen == propValueLen && strncmp(prop->value, value, valueLen) == 0) { + free(condition); + return 1; + } + free(condition); + return 0; +} + +int CheckPropertyTriggerMatch(TriggerNode *trigger, void *content) +{ + TRIGGER_CHECK(content != NULL, return 0, "Failed arg for property trigger"); + Property *prop = (Property *)content; + TRIGGER_LOGI("CheckPropertyTriggerMatch: %s, condition %s", trigger->name, trigger->condition); + TRIGGER_LOGI("property Key: %s, Value: %s", prop->name, prop->value); + if (strlen(trigger->condition) == 0) { + return 0; + } + int ret = ComputerSubCondition(trigger->condition, 0, strlen(trigger->condition), prop); + TRIGGER_LOGI("ComputerCondition result: %d", ret); + return ret; +} + +int CheckBootTriggerMatch(TriggerNode *trigger, void *content) +{ + if (strcmp(trigger->name, (char *)content) == 0) { + return 1; + } + return 0; +} + +int CheckTriggerMatch(TriggerNode *trigger, void *content) +{ + static TRIGGER_MATCH triggerCheckMatch[TRIGGER_MAX] = { + CheckBootTriggerMatch, + CheckPropertyTriggerMatch, + }; + TRIGGER_CHECK(trigger->type < TRIGGER_MAX, return 0, "Failed to get trigger index"); + return triggerCheckMatch[trigger->type](trigger, content); +} + +int ExecuteCmds(TriggerNode *trigger, CMD_EXECUTE cmdExecuter) +{ + TRIGGER_LOGI("ExecuteCmds %s", trigger->name); + ListNode *node = trigger->commands.next; + while (node != NULL && node != &trigger->commands) { + CommandNode *cmd = ListEntry(node, CommandNode, node); + node = node->next; + TRIGGER_LOGI("ExecuteCmds cmd %s arg %s", cmd->name, cmd->content); + cmdExecuter(trigger, cmd->name, cmd->content); + } + return 0; +} + +int ExecuteTrigger(int type, void *content, TRIGGER_EXECUTE triggerExecuter) +{ + TRIGGER_CHECK(type < g_triggerList->count, return -1, "Failed to get trigger type"); + ListNode *node = g_triggerList->triggers[type].next; + while (node != NULL && node != &g_triggerList->triggers[type]) { + TriggerNode *trigger = ListEntry(node, TriggerNode, node); + node = node->next; + if (!CheckTriggerMatch(trigger, content)) { + continue; + } + triggerExecuter(trigger); + } + return 0; +} + +int ParseTrigger(cJSON *triggerItem) +{ + TRIGGER_CHECK(triggerItem != NULL, return -1, "Invalid file"); + if (g_triggerList == NULL) { + g_triggerList = CreateTriggerList(TRIGGER_MAX); + } + TRIGGER_CHECK(g_triggerList != 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(index < g_triggerList->count, return -1, "Failed to get trigger index"); + TriggerNode *trigger = CreateTrigger(index, name, condition); + TRIGGER_CHECK(trigger != NULL, return -1, "Failed to create trigger"); + ListAddTail(&g_triggerList->triggers[index], &trigger->node); + + // 添加命令行 + 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); + 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) { + AddCommand(trigger, matchCmd, ""); + } else { + AddCommand(trigger, matchCmd, cmdLineStr + matchLen); + } + } + return 0; +} + +int ParseTriggerConfig(cJSON *fileRoot) +{ + TRIGGER_CHECK(fileRoot != NULL, return -1, "Invalid file"); + + 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(item); + } + return 0; +} diff --git a/services/trigger/trigger_manager.h b/services/trigger/trigger_manager.h new file mode 100755 index 0000000000000000000000000000000000000000..6d71de1f7492dbbcab8e66826d47c61e5c17aebb --- /dev/null +++ b/services/trigger/trigger_manager.h @@ -0,0 +1,118 @@ +/* + * 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.h" +#include "securec.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +#define MEMALLOC malloc +#define MEMFREE free + +#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 " + +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 32 +#define MAX_TRIGGER_TYPE_LEN 16 + +// Command对象列表,主要存储每个triger需要执行那些Command操作。 +typedef struct CommandNode { + ListNode node; + char name[MAX_TRIGGER_CMD_NAME_LEN]; + char content[0]; +} CommandNode, CommandList; + +typedef struct { + ListNode node; + ListNode commands; + atomic_flag executeFlags; + int type; + char name[MAX_TRIGGER_NAME_LEN]; + char condition[0]; +} TriggerNode; + +typedef struct TriggerList { + int count; + ListNode triggers[0]; +} TriggerList; + +typedef int (*TRIGGER_MATCH)(TriggerNode *trigger, void *content); +typedef int (*TRIGGER_EXECUTE) (TriggerNode *trigger); +typedef int (*CMD_EXECUTE) (TriggerNode *trigger, const char *cmdName, const char *command); + +void FreeTrigger(TriggerNode *trigger); +TriggerNode *CreateTrigger(int type, const char *name, const char *condition); +TriggerList *CreateTriggerList(int count); +int CheckPropertyTriggerMatch(TriggerNode *trigger, void *content); +int CheckBootTriggerMatch(TriggerNode *trigger, void *content); +int CheckTriggerMatch(TriggerNode *trigger, void *content); +int AddCommand(TriggerNode *trigger, const char *cmdName, const char *command); +int GetTriggerIndex(const char *type); + +int ExecuteCmds(TriggerNode *trigger, CMD_EXECUTE cmdExecuter); +int ExecuteTrigger(int type, void *content, TRIGGER_EXECUTE triggerExecuter); + +int ComputerSubCondition(const char *condition, size_t startIndex, size_t endIndex, const Property *prop); +int ComputerStackCondition(CONDITION_STACK *stack); +int ComputerCondition(const char *condition, const Property *prop); + +#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..2cf6574e71cd43d962a0bdd0dac29bf09478d4dd --- /dev/null +++ b/services/trigger/trigger_processor.c @@ -0,0 +1,132 @@ +/* + * 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 "uv.h" + +int DoCmdExecute(TriggerNode *trigger, const char *cmdName, const char *command) +{ + if (strncmp(cmdName, TRIGGER_CMD, strlen(TRIGGER_CMD)) == 0) { + if (!atomic_flag_test_and_set(&trigger->executeFlags)) { + TRIGGER_LOGI("DoTiggerExecute trigger %s", trigger->name); + PostTrigger(EVENT_EXECUTE, (void*)trigger); + } + } + DoCmdByName(cmdName, command); + return 0; +} + +int DoTiggerExecute(TriggerNode *trigger) +{ + // 立刻执行 + if (trigger->type == TRIGGER_BOOT) { + ExecuteCmds(trigger, DoCmdExecute); + atomic_flag_clear(&trigger->executeFlags); + } else if (!atomic_flag_test_and_set(&trigger->executeFlags)) { + TRIGGER_LOGI("DoTiggerExecute trigger %s", trigger->name); + PostTrigger(EVENT_EXECUTE, (void*)trigger); + } else { + TRIGGER_LOGI("DoTiggerExecute trigger %s has been waiting execute", trigger->name); + } + return 0; +} + +void EventProcess(uv_work_t *req) +{ +} + +void AfterEventProcess(uv_work_t *req, int status) +{ + TriggerEvent *event = (TriggerEvent *)req; + TRIGGER_LOGI("EventProcess: %d status %d %lu", event->type, status, pthread_self()); + switch (event->type) { + case EVENT_PROPERTY: + ExecuteTrigger(TRIGGER_PROPERTY, (void*)(Property*)(event + 1), DoTiggerExecute); + break; + case EVENT_ACTION: + break; + case EVENT_BOOT: + ExecuteTrigger(TRIGGER_BOOT, (void*)(Property*)(event + 1), DoTiggerExecute); + break; + case EVENT_EXECUTE: { + TriggerNode *trigger = ((TriggerExecute*)req)->trigger; + ExecuteCmds(trigger, DoCmdExecute); + atomic_flag_clear(&trigger->executeFlags); + break; + } + default: + TRIGGER_LOGE("unsupported type %d", event->type); + break; + } + MEMFREE(event); +} + +void PostTrigger(EventType type, void *content) +{ + TRIGGER_LOGI("PostTrigger event: %d", type); + TriggerEvent *event = NULL; + switch (type) { + case EVENT_PROPERTY: { + event = (TriggerEvent *)MEMALLOC(sizeof(TriggerEvent) + sizeof(Property)); + TRIGGER_CHECK(event != NULL, return, "Failed to alloc memory"); + event->type = type; + event->request.data = (char*)event + sizeof(uv_work_t); + memcpy_s(event + 1, sizeof(Property), content, sizeof(Property)); + break; + } + case EVENT_ACTION: { + break; + } + case EVENT_BOOT: { + TRIGGER_CHECK(content != NULL, return, "Must add param"); + event = (TriggerEvent *)MEMALLOC(sizeof(TriggerExecute) + strlen((char*)content) + 1); + TRIGGER_CHECK(event != NULL, return, "Failed to alloc memory"); + event->type = type; + event->request.data = (char*)event + sizeof(uv_work_t); + memcpy_s(event + 1, strlen((char*)content) + 1, content, strlen((char*)content) + 1); + break; + } + case EVENT_EXECUTE: + event = (TriggerEvent *)MEMALLOC(sizeof(TriggerExecute)); + TRIGGER_CHECK(event != NULL, return, "Failed to alloc memory"); + event->type = type; + event->request.data = (char*)event + sizeof(uv_work_t); + ((TriggerExecute*)event)->trigger = (TriggerNode *)content; + break; + default: + return; + } + uv_queue_work(uv_default_loop(), &event->request, EventProcess, AfterEventProcess); + TRIGGER_LOGI("PostTrigger %d success.\n", event->type); +} + +void ProcessIdle(uv_idle_t* handle) +{ + TRIGGER_LOGI("ProcessIdle "); +} + +void StartTriggerService() +{ + TRIGGER_LOGI("StartTriggerService "); + /* + uv_idle_t idler; + uv_idle_init(uv_default_loop(), &idler); + uv_idle_start(&idler, ProcessIdle); + */ +} diff --git a/services/trigger/trigger_processor.h b/services/trigger/trigger_processor.h new file mode 100755 index 0000000000000000000000000000000000000000..5075f59d99cff8f4a5f664b628794ff04382eba1 --- /dev/null +++ b/services/trigger/trigger_processor.h @@ -0,0 +1,56 @@ +/* + * 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 TriggerExecute { + TriggerEvent event; + TriggerNode *trigger; +} TriggerExecute; + +int DoCmdExecute(TriggerNode *trigger, const char *cmdName, const char *command); +int DoTiggerExecute(TriggerNode *trigger); + +void DoPropertyTigger(const Property *prop); +void DoActionTigger(TriggerEvent *event); +void EventProcess(uv_work_t *req); +void AfterEventProcess(uv_work_t *req, int status); +void *RunTriggerProcessor(void *args); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif +#endif \ No newline at end of file