modify for l2 init

This commit is contained in:
zhong_ning 2021-06-29 00:04:23 +08:00
parent 2a43725c10
commit 2b19a1cf0b
55 changed files with 6427 additions and 580 deletions

View File

@ -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

View File

@ -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 = [
]
}

View File

@ -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 <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <sys/un.h>
#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;
}

10
ohos.build Executable file
View File

@ -0,0 +1,10 @@
{
"subsystem": "startup",
"parts": {
"init": {
"module_list": [
"//base/startup/init_lite/services:startup_init"
]
}
}
}

View File

@ -25,12 +25,17 @@ if (defined(ohos_lite)) {
"src/init_read_cfg.c",
"src/init_service.c",
"src/init_service_manager.c",
"src/init_import.c",
"src/init_signal_handler.c",
"src/init_utils.c",
"src/init_service_socket.c",
"src/main.c",
"src/init_capability.c",
]
include_dirs = [
"include",
"//base/startup/init_lite/services/include",
"//base/startup/init_lite/services/property/include",
"//third_party/cJSON",
"//third_party/bounds_checking_function/include",
"//base/startup/syspara_lite/interfaces/kits",
@ -74,7 +79,6 @@ if (defined(ohos_lite)) {
}
} else {
import("//build/ohos.gni")
ohos_executable("updaterueventd") {
sources = [
"src/list.c",
@ -86,37 +90,66 @@ if (defined(ohos_lite)) {
]
deps = [ "//third_party/bounds_checking_function:libsec_static" ]
install_enable = true
part_name = "updater"
part_name = "init"
}
ohos_executable("updaterinit") {
ohos_executable("init") {
sources = [
"src/device.c",
"src/init_adapter.c",
"src/init_cmds.c",
"src/init_jobs.c",
"src/init_log.c",
"src/init_read_cfg.c",
"src/init_service.c",
"src/init_service_manager.c",
"src/init_import.c",
"src/init_signal_handler.c",
"src/init_utils.c",
"src/init_service_socket.c",
"src/main.c",
"src/init_capability.c",
]
include_dirs = [
"include",
"//base/startup/init_lite/services/include/property",
"//base/startup/init_lite/services/include/trigger",
"//base/startup/init_lite/services/include",
"//base/startup/init_lite/services/property/include",
"//third_party/cJSON",
"//third_party/bounds_checking_function/include",
]
deps = [
"//base/startup/init_lite/services/property:propertyserver",
"//base/startup/init_lite/services/property:propertyclient",
"//base/startup/init_lite/services/trigger:triggerservice",
"//third_party/bounds_checking_function:libsec_static",
"//third_party/cJSON:cjson_static",
]
if (use_musl) {
deps += [
"//third_party/mksh:sh",
"//third_party/toybox:toybox"
]
}
install_enable = true
part_name = "updater"
part_name = "init"
}
ohos_prebuilt_etc("init.cfg") {
source = "//device/hisilicon/hi3516dv300/updater/init.cfg"
relative_install_dir = "init"
subsystem_name = "updater"
part_name = "init"
}
group("startup_init") {
deps = [
":init.cfg",
":init",
":updaterueventd",
"//base/startup/init_lite/services/trigger:triggerservice",
"//base/startup/init_lite/services/property:propertyserver",
"//base/startup/init_lite/services/property:propertyclient",
"//base/startup/init_lite/services/property:setparam",
"//base/startup/init_lite/services/property:getparam"
]
}
}

View File

@ -21,9 +21,11 @@
extern "C" {
#endif
#endif
#include <sys/types.h>
void MountBasicFs();
void CreateDeviceNode();
int MakeSocketDir(const char *path, mode_t mode);
#ifdef __cplusplus
#if __cplusplus

View File

@ -16,10 +16,10 @@
#ifndef BASE_STARTUP_INITLITE_ADAPTER_H
#define BASE_STARTUP_INITLITE_ADAPTER_H
#ifdef __LINUX__
#include <linux/capability.h>
#else
#ifdef OHOS_LITE
#include <sys/capability.h>
#else
#include <linux/capability.h>
#endif
#ifdef __cplusplus
@ -28,7 +28,7 @@ extern "C" {
#endif
#endif
#ifdef __LINUX__
#ifndef OHOS_LITE
/* Control the ambient capability set */
#ifndef PR_CAP_AMBIENT
#define PR_CAP_AMBIENT 47

View File

@ -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

View File

@ -23,9 +23,10 @@ extern "C" {
#endif
#define MAX_CMD_NAME_LEN 10
#define MAX_CMD_CONTENT_LEN 128
#define MAX_CMD_CNT_IN_ONE_JOB 30
#define MAX_CMD_CONTENT_LEN 256
#define MAX_CMD_CNT_IN_ONE_JOB 200
#define MAX_COPY_BUF_SIZE 256
#define DEFAULT_COPY_ARGS_CNT 2
// one cmd line
typedef struct {
char name[MAX_CMD_NAME_LEN + 1];
@ -35,6 +36,8 @@ typedef struct {
void ParseCmdLine(const char* cmdStr, CmdLine* resCmd);
void DoCmd(const CmdLine* curCmd);
void DoCmdByName(const char *name, const char *cmdContent);
const char *GetMatchCmd(const char *cmdStr);
#ifdef __cplusplus
#if __cplusplus
}

19
services/include/init_import.h Executable file
View File

@ -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

View File

@ -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
}

91
services/include/init_log.h Executable file
View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif
typedef enum StatupLogLevel {
STARTUP_DEBUG = 0,
STARTUP_INFO,
STARTUP_WARN,
STARTUP_ERROR,
STARTUP_FATAL
} StatupLogLevel;
#define __FILE_NAME__ (strrchr((__FILE__), '/') ? strrchr((__FILE__), '/') + 1 : (__FILE__))
#ifdef OHOS_LITE
#define INIT_LOGE(format, ...) printf("%s %d: "format, __FILE_NAME__, __LINE__, ##__VA_ARGS__)
#define INIT_LOGW(format, ...) printf("%s %d: "format, __FILE_NAME__, __LINE__, ##__VA_ARGS__)
#define INIT_LOGI(format, ...) printf("%s %d: "format, __FILE_NAME__, __LINE__, ##__VA_ARGS__)
#define INIT_LOGD(format, ...) printf("%s %d: "format, __FILE_NAME__, __LINE__, ##__VA_ARGS__)
#else
#define INIT_LOGE(format, ...) printf("%s %d: "format, __FILE_NAME__, __LINE__, ##__VA_ARGS__)
#define INIT_LOGW(format, ...) printf("%s %d: "format, __FILE_NAME__, __LINE__, ##__VA_ARGS__)
#define INIT_LOGI(format, ...) printf("%s %d: "format, __FILE_NAME__, __LINE__, ##__VA_ARGS__)
#define INIT_LOGD(format, ...) printf("%s %d: "format, __FILE_NAME__, __LINE__, ##__VA_ARGS__)
void InitLog(int logLevel, const char *fileName, int line, const char *fmt, ...);
void Logger(StatupLogLevel level, const char *format, ...);
#endif
#define INIT_ERROR_CHECK(ret, statement, format, ...) \
if (!(ret)) { \
INIT_LOGE(format, ##__VA_ARGS__); \
statement; \
}
#define INIT_CHECK_ONLY_RETURN(ret, statement) \
if (!(ret)) { \
statement; \
}
#ifdef SUPPORT_HILOG
#include "hilog/log.h"
static constexpr OHOS::HiviewDFX::HiLogLabel STARTUP_LABEL = {LOG_CORE, 0, "STARTUP"};
StatupLogLevel level_;
int JudgeLevel(const StatupLogLevel level) { return return; }
#define STARTUP_LOG(LEVEL, LABEL, Level, fmt, ...) \
Logger(__FILE_NAME__, (__LINE__), fmt, ##__VA_ARGS__); \
if (JudgeLevel(StatupLogLevel::LEVEL)) \
OHOS::HiviewDFX::HiLog::Level(STARTUP_LABEL, "[%{public}s(%{public}d)] " fmt, \
__FILE_NAME__, __LINE__, ##__VA_ARGS__)
#else
#define STARTUP_LOG(LEVEL, LABEL, Level, fmt, ...) \
printf("[%s][%s:%d] " fmt "\n", LABEL, __FILE_NAME__, __LINE__, ##__VA_ARGS__);
#endif
#define STARTUP_LOGI(LABEL, fmt, ...) STARTUP_LOG(STARTUP_INFO, LABEL, Info, fmt, ##__VA_ARGS__)
#define STARTUP_LOGE(LABEL, fmt, ...) STARTUP_LOG(STARTUP_ERROR, LABEL, Error, fmt, ##__VA_ARGS__)
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif // INIT_LOG_H

View File

@ -22,9 +22,12 @@ extern "C" {
#endif
#endif
#define INIT_CONFIGURATION_FILE "/etc/init.cfg"
#define INIT_CONFIGURATION_FILE "/init.cfg"
#define MAX_PATH_ARGS_CNT 20
#define MAX_ONE_ARG_LEN 200 // max length of one param/path
void InitReadCfg();
void ParseInitCfg(const char *configFile);
#ifdef __cplusplus
#if __cplusplus

View File

@ -17,6 +17,8 @@
#define BASE_STARTUP_INITLITE_SERVICE_H
#include <sys/types.h>
#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);

View File

@ -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

View File

@ -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 <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
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

41
services/include/init_utils.h Executable file
View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef INIT_UTILS_H
#define INIT_UTILS_H
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif
struct CmdArgs {
int argc;
char **argv;
};
struct CmdArgs* GetCmd(const char *cmdContent, const char *delim);
void FreeCmd(struct CmdArgs **cmd);
int DecodeUid(const char *name);
void CheckAndCreateDir(const char *fileName);
char* ReadFileToBuf(const char *configFile);
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif // INIT_UTILS_H

View File

@ -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);

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2020 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef BASE_STARTUP_PROPERTY_H
#define BASE_STARTUP_PROPERTY_H
#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif
#define PROPERTY_VALUE_LEN_MAX 96
typedef u_int32_t PropertyHandle;
typedef struct {
u_int32_t serial;
PropertyHandle handle;
char value[PROPERTY_VALUE_LEN_MAX];
} PropertyCacheNode;
typedef const char *(*PropertyEvaluatePtr)(u_int32_t cacheCount, PropertyCacheNode *node);
typedef struct {
pthread_mutex_t lock;
u_int32_t serial;
u_int32_t cacheCount;
PropertyEvaluatePtr evaluate;
PropertyCacheNode *cacheNode;
} PropertyCache;
/**
*
* 使
*
*/
int SystemSetParameter(const char *name, const char *value);
/**
*
* 使
* value == nullvalue的长度
* value的大小认为是len
*
*/
int SystemGetParameter(const char *name, char *value, unsigned int *len);
/**
*
* 使
* value == nullvalue的长度
* value的大小认为是len
*
*/
int SystemGetParameterName(PropertyHandle handle, char *name, unsigned int len);
/**
*
*
*
*/
int SystemTraversalParameter(void (*traversalParameter)(PropertyHandle handle, void* cookie), void* cookie);
/**
*
*
*
*/
int SystemGetParameterValue(PropertyHandle handle, char *value, unsigned int *len);
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2020 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef BASE_STARTUP_TRIGER_H
#define BASE_STARTUP_TRIGER_H
#include <stdio.h>
#include "cJSON.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif
typedef enum {
EVENT_PROPERTY, // 属性修改事件
EVENT_BOOT
} EventType;
void PostTrigger(EventType type, void *content, u_int32_t contentLen);
void PostPropertyTrigger(const char *name, const char *value);
void StartTriggerService();
int ParseTriggerConfig(cJSON *fileRoot);
void DoTriggerExec(const char *content);
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif

106
services/property/BUILD.gn Executable file
View File

@ -0,0 +1,106 @@
# Copyright (c) 2020 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import("//build/ohos.gni")
ohos_static_library("propertyserver") {
sources = [
"manager/property_manager.c",
"manager/property_trie.c",
"manager/property_cache.c",
"service/property_service.c",
"service/property_persist.c",
"//base/startup/init_lite/services/src/init_utils.c",
]
include_dirs = [
"include",
"//base/startup/init_lite/services/include/property",
"//base/startup/init_lite/services/include/trigger",
"//base/startup/init_lite/services/include",
"//third_party/libuv/include",
"//third_party/cJSON",
]
deps = [
"//third_party/libuv:uv_static",
"//base/startup/init_lite/services/trigger:triggerservice",
"//third_party/bounds_checking_function:libsec_static",
]
part_name = "init"
subsystem_name = "startup"
}
ohos_static_library("propertyclient") {
sources = [
"manager/property_manager.c",
"client/property_request.c",
"manager/property_trie.c",
"manager/property_cache.c",
"//base/startup/init_lite/services/src/init_utils.c",
]
include_dirs = [
"include",
"//base/startup/init_lite/services/include/property",
"//base/startup/init_lite/services/include/trigger",
"//base/startup/init_lite/services/include",
"//third_party/libuv/include",
"//third_party/cJSON",
]
deps = [
"//third_party/libuv:uv_static",
"//third_party/bounds_checking_function:libsec_static",
]
part_name = "init"
subsystem_name = "startup"
}
ohos_executable("getparam") {
sources = [
"cmd/property_get.c",
]
include_dirs = [
"include",
"//base/startup/init_lite/services/include/property",
"//base/startup/init_lite/services/include/trigger",
"//base/startup/init_lite/services/include",
]
deps = [
"//base/startup/init_lite/services/property:propertyclient",
"//third_party/bounds_checking_function:libsec_static",
"//third_party/cJSON:cjson_static",
]
install_enable = true
part_name = "init"
}
ohos_executable("setparam") {
sources = [
"cmd/property_set.c",
]
include_dirs = [
"include",
"//base/startup/init_lite/services/include/property",
"//base/startup/init_lite/services/include/trigger",
"//base/startup/init_lite/services/include",
]
deps = [
"//base/startup/init_lite/services/property:propertyclient",
"//third_party/bounds_checking_function:libsec_static",
"//third_party/cJSON:cjson_static",
]
install_enable = true
part_name = "init"
}

View File

@ -0,0 +1,156 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "property_request.h"
#include <string.h>
#include <unistd.h>
#include <time.h>
#include "property_manager.h"
#include "uv.h"
#define LABEL "Client"
#define BUFFER_SIZE 200
#define PropertyEntry(ptr, type, member) (type *)((char *)(ptr) - offsetof(type, member))
static PropertyWorkSpace g_propertyWorkSpaceReadOnly = {ATOMIC_VAR_INIT(0), {}, {}, {}};
static void OnWrite(uv_write_t* req, int status)
{
PROPERTY_LOGI("OnWrite status %d", status);
}
static void OnReceiveAlloc(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buf)
{
// 这里需要按实际回复大小申请内存,不需要大内存
buf->base = (char *)malloc(sizeof(ResponseMsg));
buf->len = suggestedSize;
PROPERTY_LOGI("OnReceiveAlloc handle %p %zu", handle, suggestedSize);
}
static void OnReceiveResponse(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf)
{
RequestNode *req = PropertyEntry(handle, RequestNode, handle);
PROPERTY_LOGI("OnReceiveResponse %p", handle);
if (nread <= 0 || buf == NULL || buf->base == NULL) {
free(buf->base);
uv_close((uv_handle_t*)handle, NULL);
uv_stop(req->loop);
return;
}
ResponseMsg *response = (ResponseMsg *)(buf->base);
PROPERTY_CHECK(response != NULL, return, "The response is null");
PROPERTY_LOGI("OnReceiveResponse %p cmd %d result: %d", handle, response->type, response->result);
switch (response->type) {
case SET_PROPERTY:
req->result = response->result;
break;
default:
PROPERTY_LOGE("not supported the command: %d", response->type);
break;
}
PROPERTY_LOGE("Close handle %p", handle);
free(buf->base);
uv_close((uv_handle_t*)handle, NULL);
uv_stop(req->loop);
}
static void OnConnection(uv_connect_t *connect, int status)
{
PROPERTY_CHECK(status >= 0, return, "Failed to conntect status %s", uv_strerror(status));
uv_write_t wr;
RequestNode *request = PropertyEntry(connect, RequestNode, connect);
PROPERTY_LOGI("Connect to server handle %p", &(request->handle));
uv_buf_t buf = uv_buf_init((char*)&request->msg, request->msg.contentSize + sizeof(request->msg));
int ret = uv_write2(&wr, (uv_stream_t*)&(request->handle), &buf, 1, (uv_stream_t*)&(request->handle), OnWrite);
PROPERTY_CHECK(ret >= 0, return, "Failed to uv_write2 porperty");
// read result
ret = uv_read_start((uv_stream_t*)&(request->handle), OnReceiveAlloc, OnReceiveResponse);
PROPERTY_CHECK(ret >= 0, return, "Failed to uv_read_start response");
}
static int StartRequest(int cmd, RequestNode *request)
{
PROPERTY_CHECK(request != NULL, return -1, "Invalid request");
request->result = -1;
request->msg.type = cmd;
request->loop = uv_loop_new();
uv_pipe_init(request->loop, &request->handle, 1);
uv_pipe_connect(&request->connect, &request->handle, PIPE_NAME, OnConnection);
uv_run(request->loop, UV_RUN_DEFAULT);
int result = request->result;
free(request);
return result;
}
int SystemSetParameter(const char *name, const char *value)
{
PROPERTY_CHECK(name != NULL && value != NULL, return -1, "Invalid param");
int ret = CheckPropertyName(name, 0);
PROPERTY_CHECK(ret == 0, return ret, "Illegal property name");
PROPERTY_LOGI("StartRequest %s", name);
u_int32_t msgSize = sizeof(RequestMsg) + strlen(name) + strlen(value) + 2;
RequestNode *request = (RequestNode *)malloc(sizeof(RequestNode) + msgSize);
PROPERTY_CHECK(request != NULL, return -1, "Failed to malloc for connect");
// 带字符串结束符
int contentSize = BuildPropertyContent(request->msg.content, msgSize - sizeof(RequestMsg), name, value);
PROPERTY_CHECK(contentSize > 0, return -1, "Failed to copy porperty");
request->msg.contentSize = contentSize;
return StartRequest(SET_PROPERTY, request);
}
int SystemGetParameter(const char *name, char *value, unsigned int *len)
{
PROPERTY_CHECK(name != NULL && len != NULL, return -1, "The name or value is null");
InitPropertyWorkSpace(&g_propertyWorkSpaceReadOnly, 1, NULL);
PropertyHandle handle = 0;
int ret = ReadPropertyWithCheck(&g_propertyWorkSpaceReadOnly, name, &handle);
PROPERTY_CHECK(ret == 0, return ret, "Can not get param for %s", name);
return ReadPropertyValue(&g_propertyWorkSpaceReadOnly, handle, value, len);
}
int SystemGetParameterName(PropertyHandle handle, char *name, unsigned int len)
{
PROPERTY_CHECK(name != NULL && handle != 0, return -1, "The name is null");
InitPropertyWorkSpace(&g_propertyWorkSpaceReadOnly, 1, NULL);
return ReadPropertyName(&g_propertyWorkSpaceReadOnly, handle, name, len);
}
int SystemGetParameterValue(PropertyHandle handle, char *value, unsigned int *len)
{
PROPERTY_CHECK(len != NULL && handle != 0, return -1, "The value is null");
InitPropertyWorkSpace(&g_propertyWorkSpaceReadOnly, 1, NULL);
return ReadPropertyValue(&g_propertyWorkSpaceReadOnly, handle, value, len);
}
int SystemTraversalParameter(void (*traversalParameter)(PropertyHandle handle, void* cookie), void* cookie)
{
PROPERTY_CHECK(traversalParameter != NULL, return -1, "The param is null");
InitPropertyWorkSpace(&g_propertyWorkSpaceReadOnly, 1, NULL);
return TraversalProperty(&g_propertyWorkSpaceReadOnly, traversalParameter, cookie);
}
const char *SystemDetectPropertyChange(PropertyCache *cache,
PropertyEvaluatePtr evaluate, u_int32_t count, const char *properties[][2])
{
PROPERTY_CHECK(cache != NULL && evaluate != NULL && properties != NULL, return NULL, "The param is null");
return DetectPropertyChange(&g_propertyWorkSpaceReadOnly, cache, evaluate, count, properties);
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string.h>
#include <stdio.h>
#include "property.h"
#define HELP_PARAM "--help"
#define BUFFER_SIZE 256
static void ProcessParam(PropertyHandle handle, void* cookie)
{
SystemGetParameterName(handle, (char*)cookie, BUFFER_SIZE);
u_int32_t size = BUFFER_SIZE;
SystemGetParameterValue(handle, ((char*)cookie) + BUFFER_SIZE, &size);
printf("\t%s=%s \n", (char*)cookie, ((char*)cookie) + BUFFER_SIZE);
}
int main(int argc, char* argv[])
{
if (argc == 1) { // 显示所有的记录
char value[BUFFER_SIZE + BUFFER_SIZE] = {0};
SystemTraversalParameter(ProcessParam, (void*)value);
return 0;
}
if (argc == 2 && strncmp(argv[1], HELP_PARAM, strlen(HELP_PARAM)) == 0) { // 显示帮助
printf("usage: getprop NAME VALUE\n");
return 0;
}
if (argc != 2) {
printf("usage: getprop NAME VALUE\n");
return 0;
}
char value[BUFFER_SIZE] = {0};
u_int32_t size = BUFFER_SIZE;
int ret = SystemGetParameter(argv[1], value, &size);
if (ret == 0) {
printf("getparm %s %s \n", argv[1], value);
} else {
printf("getparm %s %s fail\n", argv[1], value);
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string.h>
#include <stdio.h>
#include "property.h"
#define HELP_PARAM "--help"
int main(int argc, char* argv[])
{
if (argc == 1 || argc > 3) {
printf("setparm: Need 2 arguments (see \"setparm --help\")\n");
return 0;
}
if (argc == 2 && strncmp(argv[1], HELP_PARAM, strlen(HELP_PARAM)) == 0) {
printf("usage: setprop NAME VALUE\n");
return 0;
}
int ret = SystemSetParameter(argv[1], argv[2]);
if (ret == 0) {
printf("setparm %s %s success\n", argv[1], argv[2]);
} else {
printf("setparm %s %s fail\n", argv[1], argv[2]);
}
}

View File

@ -0,0 +1,165 @@
/*
* Copyright (c) 2020 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef BASE_STARTUP_PROPERTY_MANAGER_H
#define BASE_STARTUP_PROPERTY_MANAGER_H
#include <stdio.h>
#include <string.h>
#include "init_log.h"
#include "property.h"
#include "property_trie.h"
#include "securec.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif
typedef enum {
PROPERTY_CODE_INVALID_PARAM = 100,
PROPERTY_CODE_INVALID_NAME,
PROPERTY_CODE_INVALID_VALUE,
PROPERTY_CODE_REACHED_MAX,
PROPERTY_CODE_PERMISSION_DENIED,
PROPERTY_CODE_READ_ONLY_PROPERTY,
PROPERTY_CODE_NOT_SUPPORT,
PROPERTY_CODE_ERROR_MAP_FILE,
PROPERTY_CODE_NOT_FOUND_PROP,
PROPERTY_CODE_NOT_INIT
} PROPERTY_CODE;
#define IS_READY_ONLY(name) strncmp((name), "ro.", strlen("ro.")) == 0
#define LABEL_STRING_LEN 128
#ifdef STARTUP_LOCAL
#define PIPE_NAME "/tmp/propertyservice.sock"
#define PROPERTY_STORAGE_PATH "/media/sf_ubuntu/test/__properties__/property_storage"
#define PROPERTY_PERSIST_PATH "/media/sf_ubuntu/test/property/persist_properties.property"
#define PROPERTY_INFO_PATH "/media/sf_ubuntu/test/__properties__/property_info"
#else
#define PIPE_NAME "/dev/unix/socket/PropertyService"
#define PROPERTY_STORAGE_PATH "/dev/__properties__/property_storage"
#define PROPERTY_PERSIST_PATH "/data/property/persist_properties.property"
#define PROPERTY_INFO_PATH "/dev/__properties__/property_info"
#endif
#define SUBSTR_INFO_NAME 0
#define SUBSTR_INFO_LABEL 1
#define SUBSTR_INFO_TYPE 2
#define SUBSTR_INFO_MAX 4
#define WORKSPACE_FLAGS_INIT 0x01
#define WORKSPACE_FLAGS_LOADED 0x02
#define PROPERTY_LOGI(fmt, ...) STARTUP_LOGI(LABEL, fmt, ##__VA_ARGS__)
#define PROPERTY_LOGE(fmt, ...) STARTUP_LOGE(LABEL, fmt, ##__VA_ARGS__)
#define PROPERTY_CHECK(retCode, exper, ...) \
if (!(retCode)) { \
PROPERTY_LOGE(__VA_ARGS__); \
exper; \
}
#define futex(addr1, op, val, rel, addr2, val3) \
syscall(SYS_futex, addr1, op, val, rel, addr2, val3)
#define futex_wait_always(addr1) \
syscall(SYS_futex, addr1, FUTEX_WAIT, *(int*)(addr1), 0, 0, 0)
#define futex_wake_single(addr1) \
syscall(SYS_futex, addr1, FUTEX_WAKE, 1, 0, 0, 0)
typedef struct UserCred {
pid_t pid;
uid_t uid;
gid_t gid;
} UserCred;
typedef struct {
char label[LABEL_STRING_LEN];
UserCred cred;
} PropertySecurityLabel;
typedef struct PropertyAuditData {
const UserCred *cr;
const char *name;
} PropertyAuditData;
typedef struct {
atomic_uint_least32_t flags;
WorkSpace propertyLabelSpace;
WorkSpace propertySpace;
PropertySecurityLabel label;
} PropertyWorkSpace;
typedef struct {
atomic_uint_least32_t flags;
WorkSpace persistWorkSpace;
} PropertyPersistWorkSpace;
typedef struct {
char value[128];
} SubStringInfo;
int InitPropertyWorkSpace(PropertyWorkSpace *workSpace, int onlyRead, const char *context);
void ClosePropertyWorkSpace(PropertyWorkSpace *workSpace);
int ReadPropertyWithCheck(PropertyWorkSpace *workSpace, const char *name, PropertyHandle *handle);
int ReadPropertyValue(PropertyWorkSpace *workSpace, PropertyHandle handle, char *value, u_int32_t *len);
int ReadPropertyName(PropertyWorkSpace *workSpace, PropertyHandle handle, char *name, u_int32_t len);
u_int32_t ReadPropertySerial(PropertyWorkSpace *workSpace, PropertyHandle handle);
int AddProperty(WorkSpace *workSpace, const char *name, const char *value);
int WriteProperty(WorkSpace *workSpace, const char *name, const char *value);
int WritePropertyWithCheck(PropertyWorkSpace *workSpace,
const PropertySecurityLabel *srcLabel, const char *name, const char *value);
int WritePropertyInfo(PropertyWorkSpace *workSpace, SubStringInfo *info, int subStrNumber);
int CheckPropertyValue(WorkSpace *workSpace, const TrieDataNode *node, const char *name, const char *value);
int CheckPropertyName(const char *name, int propInfo);
int CanReadProperty(PropertyWorkSpace *workSpace, u_int32_t labelIndex, const char *name);
int CanWriteProperty(PropertyWorkSpace *workSpace,
const PropertySecurityLabel *srcLabel, const TrieDataNode *node, const char *name, const char *value);
int CheckMacPerms(PropertyWorkSpace *workSpace,
const PropertySecurityLabel *srcLabel, const char *name, u_int32_t labelIndex);
int CheckControlPropertyPerms(PropertyWorkSpace *workSpace,
const PropertySecurityLabel *srcLabel, const char *name, const char *value);
int GetSubStringInfo(const char *buff, u_int32_t buffLen, char delimiter, SubStringInfo *info, int subStrNumber);
int BuildPropertyContent(char *content, u_int32_t contentSize, const char *name, const char *value);
PropertyWorkSpace *GetPropertyWorkSpace();
typedef void (*TraversalParamPtr)(PropertyHandle handle, void* context);
typedef struct {
TraversalParamPtr traversalParamPtr;
void *context;
} PropertyTraversalContext;
int TraversalProperty(PropertyWorkSpace *workSpace, TraversalParamPtr walkFunc, void *cookie);
int InitPersistPropertyWorkSpace(const char *context);
int RefreshPersistProperties(PropertyWorkSpace *workSpace, const char *context);
void ClosePersistPropertyWorkSpace();
int WritePersistProperty(const char *name, const char *value);
const char *DetectPropertyChange(PropertyWorkSpace *workSpace, PropertyCache *cache,
PropertyEvaluatePtr evaluate, u_int32_t count, const char *properties[][2]);
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2020 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef BASE_STARTUP_PROPERTY_REQUEST_H
#define BASE_STARTUP_PROPERTY_REQUEST_H
#include <stdio.h>
#include "property.h"
#include "property_manager.h"
#include "uv.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif
typedef enum RequestType {
SET_PROPERTY,
GET_PROPERTY,
} RequestType;
typedef struct {
PropertySecurityLabel securitylabel;
RequestType type;
int contentSize;
char content[0];
} RequestMsg;
typedef struct {
RequestType type;
int result;
int contentSize;
char content[0];
} ResponseMsg;
typedef struct {
uv_loop_t *loop;
uv_connect_t connect;
uv_pipe_t handle;
int result;
RequestMsg msg;
} RequestNode;
typedef struct {
uv_write_t writer;
ResponseMsg msg;
} ResponseNode;
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2020 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef BASE_STARTUP_PROPERTY_SERVICE_H
#define BASE_STARTUP_PROPERTY_SERVICE_H
#include <stdio.h>
#include "property.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif
/**
* Init
*
*
*/
void InitPropertyService();
/**
* Init
*
*
*/
int LoadDefaultProperty(const char *fileName);
/**
* Init
* 使selinux label
*
*/
int LoadPropertyInfo(const char *fileName);
/**
* Init
* main启动的最后调用线
*
*/
int StartPropertyService();
/**
* Init
* 使
*
*/
int SystemWriteParameter(const char *name, const char *value);
/**
* Init
*
*
*/
int SystemReadParameter(const char *name, char *value, unsigned int *len);
/**
* Init
*
*
*/
int SystemTraversalParameters(void (*traversalParameter)(PropertyHandle handle, void* cookie), void* cookie);
/**
* Init
*
*
*/
int LoadPersistProperties();
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif

View File

@ -0,0 +1,139 @@
/*
* Copyright (c) 2020 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef BASE_STARTUP_PROPERTY_TRIE_H
#define BASE_STARTUP_PROPERTY_TRIE_H
#include <linux/futex.h>
#include <stdatomic.h>
#include <stdio.h>
#include <string.h>
#include <sys/syscall.h>
#include "init_log.h"
#include "property.h"
#include "securec.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif
#define PROPERTY_WORKSPACE_MAX 64*1024
#define TRIE_SERIAL_DIRTY_OFFSET 1
#define TRIE_SERIAL_KEY_LEN_OFFSET 24
#define TRIE_SERIAL_DATA_LEN_OFFSET 16
#define FILENAME_LEN_MAX 255
#define TRIE_DATA_LEN_MAX 128
#define NODE_INDEX unsigned int
#define TRIE_NODE_HEADER \
atomic_uint_least32_t serial; \
NODE_INDEX left; \
NODE_INDEX right;
#define DATA_ENTRY_KEY_LEN(entry) (entry)->dataLength >> TRIE_SERIAL_KEY_LEN_OFFSET
#define DATA_ENTRY_DATA_LEN(entry) (((entry)->dataLength >> TRIE_SERIAL_DATA_LEN_OFFSET) & 0x00FF)
#define DATA_ENTRY_DIRTY(serial) ((serial) & 1)
#define FUTEX_WAIT 0
#define FUTEX_WAKE 1
#define __futex(ftx, op, value, timeout, bitset) \
syscall(__NR_futex, ftx, op, value, timeout, NULL, bitset)
#define futex_wake(ftx, count) __futex(ftx, FUTEX_WAKE, count, NULL, 0)
#define futex_wait(ftx, value, timeout) __futex(ftx, FUTEX_WAIT, value, timeout, 0)
typedef struct {
TRIE_NODE_HEADER;
char key[0];
} TrieNode;
typedef struct {
TRIE_NODE_HEADER;
NODE_INDEX child;
NODE_INDEX labelIndex;
NODE_INDEX dataIndex;
char key[0];
} TrieDataNode;
typedef struct {
atomic_uint_least32_t serial;
atomic_uint_least32_t dataLength;
char data[0];
} DataEntry;
typedef struct {
atomic_uint_least32_t serial;
u_int32_t currOffset;
u_int32_t firstNode;
u_int32_t dataSize;
u_int32_t reserved_[28];
char data[0];
} WorkArea;
struct WorkSpace_;
typedef u_int32_t (*AllocTrieNodePtr)(struct WorkSpace_ *workSpace, const char *key, u_int32_t keyLen);
typedef int (*CompareTrieNodePtr)(TrieNode *node, const char *key2, u_int32_t key2Len);
typedef struct WorkSpace_ {
char fileName[FILENAME_LEN_MAX + 1];
WorkArea *area;
TrieNode *rootNode;
AllocTrieNodePtr allocTrieNode;
CompareTrieNodePtr compareTrieNode;
} WorkSpace;
u_int32_t GetWorkSpaceSerial(WorkSpace *workSpace);
int CompareTrieNode(TrieNode *node, const char *key, u_int32_t keyLen);
u_int32_t AllocateTrieNode(WorkSpace *workSpace, const char *key, u_int32_t keyLen);
int CompareTrieDataNode(TrieNode *node, const char *key, u_int32_t keyLen);
u_int32_t AllocateTrieDataNode(WorkSpace *workSpace, const char *key, u_int32_t keyLen);
u_int32_t GetTrieNodeOffset(WorkSpace *workSpace, const TrieNode *current);
TrieNode *GetTrieNode(WorkSpace *workSpace, NODE_INDEX *index);
u_int32_t GetTrieKeyLen(TrieNode *current);
void SaveIndex(NODE_INDEX *index, u_int32_t offset);
TrieDataNode *AddTrieDataNode(WorkSpace *workSpace, const char *key, u_int32_t keyLen);
TrieDataNode *AddToSubTrie(WorkSpace *workSpace, TrieDataNode *dataNode, const char *key, u_int32_t keyLen);
TrieDataNode *FindSubTrie(WorkSpace *workSpace, TrieDataNode *dataNode, const char *key, u_int32_t keyLen);
TrieDataNode *FindTrieDataNode(WorkSpace *workSpace, const char *key, u_int32_t keyLen, int matchPrefix);
TrieNode *AddTrieNode(WorkSpace *workSpace, TrieNode *root, const char *key, u_int32_t keyLen);
TrieNode *FindTrieNode(WorkSpace *workSpace, TrieNode *tree, const char *key, u_int32_t keyLen);
int InitWorkSpace_(WorkSpace *workSpace, int mode, int prot, u_int32_t spaceSize, int readOnly);
int InitPersistWorkSpace(const char *fileName, WorkSpace *workSpace);
int InitWorkSpace(const char *fileName, WorkSpace *workSpace, int onlyRead);
void CloseWorkSpace(WorkSpace *workSpace);
typedef int (*TraversalTrieNodePtr)(WorkSpace *workSpace, TrieNode *node, void *cookie);
int TraversalTrieNode(WorkSpace *workSpace, TrieNode *root, TraversalTrieNodePtr walkFunc, void *cookie);
int TraversalTrieDataNode(WorkSpace *workSpace, TrieDataNode *current, TraversalTrieNodePtr walkFunc, void *cookie);
u_int32_t AddData(WorkSpace *workSpace, const char *key, u_int32_t keyLen, const char *value, u_int32_t valueLen);
int UpdateDataValue(DataEntry *entry, const char *value);
int GetDataName(const DataEntry *entry, char *name, u_int32_t len);
int GetDataValue(const DataEntry *entry, char *value, u_int32_t len);
u_int32_t GetDataSerial(const DataEntry *entry);
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif // BASE_STARTUP_PROPERTY_TRIE_H

View File

@ -0,0 +1,148 @@
/*
* Copyright (c) 2020 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "property.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include "property_manager.h"
#define LABEL "Manager"
#define MAX_PROPERT_IN_WATCH 5
#define NORMAL_MEMORY_FOR_PROPERTY_CACHE 4 * 1024
static WorkSpace g_workSpace;
static pthread_mutex_t cacheLock = PTHREAD_MUTEX_INITIALIZER;
static int InitNormalMemory(WorkSpace *workSpace, u_int32_t spaceSize)
{
PROPERTY_CHECK(workSpace != NULL, return -1, "Invalid param");
if (workSpace->area != NULL) {
return 0;
}
void *areaAddr = (void *)mmap(NULL, spaceSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_POPULATE | MAP_ANON, -1, 0);
PROPERTY_CHECK(areaAddr != MAP_FAILED, return -1, "Failed to map memory error %s", strerror(errno));
workSpace->area = (WorkArea*)areaAddr;
atomic_init(&workSpace->area->serial, 0);
workSpace->area->dataSize = spaceSize;
workSpace->area->currOffset = sizeof(WorkArea);
PROPERTY_LOGI("InitNormalMemory success, currOffset %u firstNode %u dataSize %u",
workSpace->area->currOffset, workSpace->area->firstNode, workSpace->area->dataSize);
return 0;
}
static PropertyCacheNode *AllocPropertyCacheNode(WorkSpace *workSpace, u_int32_t size)
{
PROPERTY_CHECK(workSpace != NULL, return 0, "Invalid param");
PROPERTY_CHECK((workSpace->area->currOffset + size) < workSpace->area->dataSize, return 0,
"Failed to allocate currOffset %d, dataSize %d", workSpace->area->currOffset, workSpace->area->dataSize);
PropertyCacheNode *cache = (PropertyCacheNode *)(workSpace->area->data + workSpace->area->currOffset);
workSpace->area->currOffset += size;
return cache;
}
static int CreatePropertyCache(PropertyCache *cache, PropertyWorkSpace *workSpace, PropertyEvaluatePtr evaluate)
{
PROPERTY_CHECK(cache != NULL && evaluate != NULL, return -1, "Invalid param");
if (cache->cacheNode != NULL) {
return 0;
}
int ret = InitNormalMemory(&g_workSpace, NORMAL_MEMORY_FOR_PROPERTY_CACHE);
PROPERTY_CHECK(ret == 0, return -1, "Failed to init normal memory");
pthread_mutex_init(&cache->lock, NULL);
cache->serial = GetWorkSpaceSerial(&workSpace->propertySpace);
cache->cacheCount = 0;
cache->evaluate = evaluate;
cache->cacheNode = (PropertyCacheNode *)AllocPropertyCacheNode(&g_workSpace,
sizeof(PropertyCache) * MAX_PROPERT_IN_WATCH);
PROPERTY_CHECK(cache->cacheNode != NULL, return -1, "Failed to malloc memory");
return 0;
}
static int AddPropertyNode(PropertyCache *cache, PropertyWorkSpace *workSpace, const char *name, const char *defValue)
{
PROPERTY_CHECK(cache != NULL && name != NULL, return -1, "Invalid param");
PROPERTY_CHECK(cache->cacheCount < MAX_PROPERT_IN_WATCH, return -1, "Full property in cache");
PropertyCacheNode *cacheNode = &cache->cacheNode[cache->cacheCount++];
int ret = memcpy_s(cacheNode->value, sizeof(cacheNode->value), defValue, strlen(defValue));
PROPERTY_CHECK(ret == 0, return -1, "Failed to copy default value");
ret = ReadPropertyWithCheck(workSpace, name, &cacheNode->handle);
PROPERTY_CHECK(ret == 0, return -1, "Failed to read property");
cacheNode->serial = ReadPropertySerial(workSpace, cacheNode->handle);
return ret;
}
static int CheckCacheNode(PropertyWorkSpace *workSpace, PropertyCacheNode *cacheNode)
{
return cacheNode && ReadPropertySerial(workSpace, cacheNode->handle) != cacheNode->serial;
}
static void RefreshCacheNode(PropertyWorkSpace *workSpace, PropertyCacheNode *cacheNode)
{
cacheNode->serial = ReadPropertySerial(workSpace, cacheNode->handle);
u_int32_t len = sizeof(cacheNode->value);
ReadPropertyValue(workSpace, cacheNode->handle, cacheNode->value, &len);
}
static const char *TestPropertyCache(PropertyCache *cache, PropertyWorkSpace *workSpace)
{
int changeDetected;
if (pthread_mutex_trylock(&cache->lock)) {
return cache->evaluate(cache->cacheCount, cache->cacheNode);
}
if (GetWorkSpaceSerial(&workSpace->propertySpace) != cache->serial) {
changeDetected = 1;
}
for (u_int32_t i = 0; (i < cache->cacheCount) && changeDetected == 0; i++) {
changeDetected = CheckCacheNode(workSpace, &cache->cacheNode[i]);
}
if (changeDetected) {
for (u_int32_t i = 0; i < cache->cacheCount; i++) {
RefreshCacheNode(workSpace, &cache->cacheNode[i]);
}
cache->serial = GetWorkSpaceSerial(&workSpace->propertySpace);
}
pthread_mutex_unlock(&cache->lock);
return cache->evaluate(cache->cacheCount, cache->cacheNode);
}
const char *DetectPropertyChange(PropertyWorkSpace *workSpace, PropertyCache *cache,
PropertyEvaluatePtr evaluate, u_int32_t count, const char *properties[][2])
{
pthread_mutex_lock(&cacheLock);
while (cache->cacheCount == 0) {
int ret = CreatePropertyCache(cache, workSpace, evaluate);
PROPERTY_CHECK(ret == 0, break, "Failed to create cache");
for (u_int32_t i = 0; i < count; i++) {
ret = AddPropertyNode(cache, workSpace, properties[i][0], properties[i][1]);
PROPERTY_CHECK(ret == 0, break, "Failed to add property cache");
}
PROPERTY_CHECK(ret == 0, break, "Failed to add property cache");
}
pthread_mutex_unlock(&cacheLock);
return TestPropertyCache(cache, workSpace);
}

View File

@ -0,0 +1,532 @@
/*
* Copyright (c) 2020 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "property_manager.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define LABEL "Manager"
#ifdef PROPERTY_SUPPORT_SELINUX
static int SelinuxAuditCallback(void *data,
__attribute__((unused))security_class_t class, char *msgBuf, size_t msgSize)
{
PropertyAuditData *auditData = (PropertyAuditData*)(data);
PROPERTY_CHECK(auditData != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
PROPERTY_CHECK(auditData->name != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
PROPERTY_CHECK(auditData->cr != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
snprintf(msgBuf, msgSize, "property=%s pid=%d uid=%d gid=%d",
auditData->name, auditData->cr->pid, auditData->cr->uid, auditData->cr->gid);
return 0;
}
#endif
int InitPropertyWorkSpace(PropertyWorkSpace *workSpace, int onlyRead, const char *context)
{
u_int32_t flags = atomic_load_explicit(&workSpace->flags, memory_order_relaxed);
PROPERTY_LOGI("InitPropertyWorkSpace flags %x", flags);
if ((flags & WORKSPACE_FLAGS_INIT) == WORKSPACE_FLAGS_INIT) {
return 0;
}
#ifdef PROPERTY_SUPPORT_SELINUX
union selinux_callback cb;
cb.func_audit = SelinuxAuditCallback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
#endif
#ifdef PROPERTY_SUPPORT_SELINUX
if (context && fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0) != 0) {
PROPERTY_LOGI("fsetxattr context %s fail", context);
}
#endif
PROPERTY_CHECK(workSpace != NULL, return PROPERTY_CODE_INVALID_NAME, "Invalid param");
workSpace->propertySpace.compareTrieNode = CompareTrieDataNode;
workSpace->propertySpace.allocTrieNode = AllocateTrieDataNode;
int ret = InitWorkSpace(PROPERTY_STORAGE_PATH, &workSpace->propertySpace, onlyRead);
// 保存selinux 使用的属性标签值
workSpace->propertyLabelSpace.compareTrieNode = CompareTrieNode; // 必须先设置
workSpace->propertyLabelSpace.allocTrieNode = AllocateTrieNode;
ret |= InitWorkSpace(PROPERTY_INFO_PATH, &workSpace->propertyLabelSpace, onlyRead);
atomic_store_explicit(&workSpace->flags, WORKSPACE_FLAGS_INIT, memory_order_release);
return ret;
}
void ClosePropertyWorkSpace(PropertyWorkSpace *workSpace)
{
CloseWorkSpace(&workSpace->propertySpace);
CloseWorkSpace(&workSpace->propertyLabelSpace);
atomic_store_explicit(&workSpace->flags, 0, memory_order_release);
}
int WritePropertyInfo(PropertyWorkSpace *workSpace, SubStringInfo *info, int subStrNumber)
{
PROPERTY_CHECK(workSpace != NULL && info != NULL && subStrNumber > SUBSTR_INFO_NAME,
return PROPERTY_CODE_INVALID_PARAM, "Failed to check param");
const char *name = info[SUBSTR_INFO_NAME].value;
char *label = NULL;
char *type = NULL;
if (subStrNumber >= SUBSTR_INFO_LABEL) {
label = info[SUBSTR_INFO_LABEL].value;
} else {
label = "u:object_r:default_prop:s0";
}
if (subStrNumber >= SUBSTR_INFO_TYPE) {
type = info[SUBSTR_INFO_TYPE].value;
} else {
type = "string";
}
int ret = CheckPropertyName(name, 1);
PROPERTY_CHECK(ret == 0, return ret, "Illegal property name %s", name);
// 先保存标签值
TrieNode *node = AddTrieNode(&workSpace->propertyLabelSpace,
workSpace->propertyLabelSpace.rootNode, label, strlen(label));
PROPERTY_CHECK(node != NULL, return PROPERTY_CODE_REACHED_MAX, "Failed to add label");
u_int32_t offset = GetTrieNodeOffset(&workSpace->propertyLabelSpace, node);
TrieDataNode *dataNode = AddTrieDataNode(&workSpace->propertySpace, name, strlen(name));
PROPERTY_CHECK(dataNode != NULL, return PROPERTY_CODE_REACHED_MAX, "Failed to add node %s", name);
TrieNode *entry = (TrieNode *)GetTrieNode(&workSpace->propertyLabelSpace, &dataNode->labelIndex);
if (entry != 0) { // 已经存在label
PROPERTY_LOGE("Has been set label %s old label %s new label: %s", name, entry->key, label);
}
SaveIndex(&dataNode->labelIndex, offset);
return 0;
}
int AddProperty(WorkSpace *workSpace, const char *name, const char *value)
{
PROPERTY_CHECK(workSpace != NULL && name != NULL && value != NULL,
return PROPERTY_CODE_INVALID_PARAM, "Failed to check param");
TrieDataNode *node = AddTrieDataNode(workSpace, name, strlen(name));
PROPERTY_CHECK(node != NULL, return PROPERTY_CODE_REACHED_MAX, "Failed to add node");
DataEntry *entry = (DataEntry *)GetTrieNode(workSpace, &node->dataIndex);
if (entry == NULL) {
u_int32_t offset = AddData(workSpace, name, strlen(name), value, strlen(value));
PROPERTY_CHECK(offset != 0, return PROPERTY_CODE_REACHED_MAX, "Failed to allocate name %s", name);
SaveIndex(&node->dataIndex, offset);
}
PROPERTY_LOGI("AddProperty trie %p dataIndex %u name %s", node, node->dataIndex, name);
atomic_store_explicit(&workSpace->area->serial,
atomic_load_explicit(&workSpace->area->serial, memory_order_relaxed) + 1,
memory_order_release);
futex_wake(&workSpace->area->serial, INT_MAX);
return 0;
}
int UpdateProperty(WorkSpace *workSpace, u_int32_t *dataIndex, const char *name, const char *value)
{
PROPERTY_CHECK(workSpace != NULL && name != NULL && value != NULL,
return PROPERTY_CODE_INVALID_PARAM, "Failed to check param");
DataEntry *entry = (DataEntry *)GetTrieNode(workSpace, dataIndex);
if (entry == NULL) {
PROPERTY_LOGE("Failed to update property value %s %u", name, *dataIndex);
return -1;
}
u_int32_t keyLen = DATA_ENTRY_KEY_LEN(entry);
PROPERTY_CHECK(keyLen == strlen(name), return PROPERTY_CODE_INVALID_NAME, "Failed to check name len %s", name);
u_int32_t serial = atomic_load_explicit(&entry->serial, memory_order_relaxed);
serial |= 1;
atomic_store_explicit(&entry->serial, serial | 1, memory_order_release);
atomic_thread_fence(memory_order_release);
int ret = UpdateDataValue(entry, value);
if (ret != 0) {
PROPERTY_LOGE("Failed to update property value %s %s", name, value);
}
atomic_store_explicit(&entry->serial, serial + 1, memory_order_release);
futex_wake(&entry->serial, INT_MAX);
atomic_store_explicit(&workSpace->area->serial,
atomic_load_explicit(&workSpace->area->serial, memory_order_relaxed) + 1, memory_order_release);
futex_wake(&workSpace->area->serial, INT_MAX);
return ret;
}
DataEntry *FindProperty(WorkSpace *workSpace, const char *name)
{
PROPERTY_CHECK(workSpace != NULL, return NULL, "Failed to check param");
PROPERTY_CHECK(name != NULL, return NULL, "Invalid param size");
TrieDataNode *node = FindTrieDataNode(workSpace, name, strlen(name), 0);
if (node != NULL) {
return (DataEntry *)GetTrieNode(workSpace, &node->dataIndex);
}
return NULL;
}
int WritePropertyWithCheck(PropertyWorkSpace *workSpace,
const PropertySecurityLabel *srcLabel, const char *name, const char *value)
{
PROPERTY_CHECK(workSpace != NULL && srcLabel != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
PROPERTY_CHECK(value != NULL && name != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
u_int32_t flags = atomic_load_explicit(&workSpace->flags, memory_order_relaxed);
if ((flags & WORKSPACE_FLAGS_INIT) != WORKSPACE_FLAGS_INIT) {
return PROPERTY_CODE_NOT_INIT;
}
int ret = CheckPropertyName(name, 0);
PROPERTY_CHECK(ret == 0, return ret, "Illegal property name %s", name);
// 取最长匹配的属性的label
TrieDataNode *propertInfo = FindTrieDataNode(&workSpace->propertySpace, name, strlen(name), 1);
ret = CanWriteProperty(workSpace, srcLabel, propertInfo, name, value);
PROPERTY_CHECK(ret == 0, return ret, "Permission to write property %s", name);
return WriteProperty(&workSpace->propertySpace, name, value);
}
int WriteProperty(WorkSpace *workSpace, const char *name, const char *value)
{
PROPERTY_CHECK(workSpace != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
PROPERTY_CHECK(value != NULL && name != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
TrieDataNode *node = FindTrieDataNode(workSpace, name, strlen(name), 0);
int ret = CheckPropertyValue(workSpace, node, name, value);
PROPERTY_CHECK(ret == 0, return ret, "Invalid value %s %s", name, value);
if (node != NULL) {
return UpdateProperty(workSpace, &node->dataIndex, name, value);
}
return AddProperty(workSpace, name, value);
}
int ReadPropertyWithCheck(PropertyWorkSpace *workSpace, const char *name, PropertyHandle *handle)
{
PROPERTY_CHECK(handle != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
PROPERTY_CHECK(workSpace != NULL && name != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
u_int32_t flags = atomic_load_explicit(&workSpace->flags, memory_order_relaxed);
if ((flags & WORKSPACE_FLAGS_INIT) != WORKSPACE_FLAGS_INIT) {
return PROPERTY_CODE_NOT_INIT;
}
*handle = 0;
// 取最长匹配
TrieDataNode *propertyInfo = FindTrieDataNode(&workSpace->propertySpace, name, strlen(name), 1);
int ret = CanReadProperty(workSpace, propertyInfo == NULL ? 0 : propertyInfo->labelIndex, name);
PROPERTY_CHECK(ret == 0, return ret, "Permission to read property %s", name);
// 查找结点
TrieDataNode *node = FindTrieDataNode(&workSpace->propertySpace, name, strlen(name), 0);
if (node != NULL && node->dataIndex != 0) {
PROPERTY_LOGI("ReadPropertyWithCheck trie %p dataIndex %u name %s", node, node->dataIndex, name);
*handle = node->dataIndex;
return 0;
}
return PROPERTY_CODE_NOT_FOUND_PROP;
}
int ReadPropertyValue(PropertyWorkSpace *workSpace, PropertyHandle handle, char *value, u_int32_t *len)
{
PROPERTY_CHECK(workSpace != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
u_int32_t flags = atomic_load_explicit(&workSpace->flags, memory_order_relaxed);
if ((flags & WORKSPACE_FLAGS_INIT) != WORKSPACE_FLAGS_INIT) {
return PROPERTY_CODE_NOT_INIT;
}
DataEntry *entry = (DataEntry *)GetTrieNode(&workSpace->propertySpace, &handle);
if (entry == NULL) {
return -1;
}
if (value == NULL) {
*len = DATA_ENTRY_DATA_LEN(entry);;
return 0;
}
while (1) {
u_int32_t serial = GetDataSerial(entry);
int ret = GetDataValue(entry, value, *len);
PROPERTY_CHECK(ret == 0, return ret, "Failed to get value");
atomic_thread_fence(memory_order_acquire);
if (serial == atomic_load_explicit(&(entry->serial), memory_order_relaxed)) {
return 0;
}
}
return 0;
}
int ReadPropertyName(PropertyWorkSpace *workSpace, PropertyHandle handle, char *name, u_int32_t len)
{
PROPERTY_CHECK(workSpace != NULL && name != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
DataEntry *entry = (DataEntry *)GetTrieNode(&workSpace->propertySpace, &handle);
if (entry == NULL) {
return -1;
}
return GetDataValue(entry, name, len);
}
u_int32_t ReadPropertySerial(PropertyWorkSpace *workSpace, PropertyHandle handle)
{
PROPERTY_CHECK(workSpace != NULL, return 0, "Invalid param");
DataEntry *entry = (DataEntry *)GetTrieNode(&workSpace->propertySpace, &handle);
if (entry == NULL) {
return 0;
}
return GetDataSerial(entry);
}
int CheckControlPropertyPerms(PropertyWorkSpace *workSpace,
const PropertySecurityLabel *srcLabel, const char *name, const char *value)
{
PROPERTY_CHECK(srcLabel != NULL && name != NULL && value != NULL,
return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
char * ctrlName[] = {
"ctl.start", "ctl.stop", "ctl.restart"
};
size_t size1 = strlen("ctl.") + strlen(value);
size_t size2 = strlen(name) + strlen(value) + 1;
size_t size = ((size1 > size2) ? size1 : size2) + 1;
char *legacyName = (char*)malloc(size);
PROPERTY_CHECK(legacyName != NULL, return PROPERTY_CODE_INVALID_PARAM, "Failed to alloc memory");
// We check the legacy method first but these properties are dontaudit, so we only log an audit
// if the newer method fails as well. We only do this with the legacy ctl. properties.
for (size_t i = 0; i < sizeof(ctrlName) / sizeof(char*); i++) {
if (strcmp(name, ctrlName[i]) == 0) {
// The legacy permissions model is that ctl. properties have their name ctl.<action> 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.<service> to
// check permissions.
int n = snprintf_s(legacyName, size, size, "ctl.%s", value);
PROPERTY_CHECK(n > 0, free(legacyName); return PROPERTY_CODE_INVALID_PARAM, "Failed to snprintf value");
legacyName[n] = '\0';
TrieDataNode *node = FindTrieDataNode(&workSpace->propertySpace, legacyName, strlen(legacyName), 1);
int ret = CheckMacPerms(workSpace, srcLabel, legacyName, node == NULL ? 0 : node->labelIndex);
if (ret == 0) {
free(legacyName);
return 0;
}
break;
}
}
int n = snprintf_s(legacyName, size, size, "%s$%s", name, value);
PROPERTY_CHECK(n > 0, free(legacyName); return PROPERTY_CODE_INVALID_PARAM, "Failed to snprintf value");
TrieDataNode *node = FindTrieDataNode(&workSpace->propertySpace, name, strlen(name), 1);
int ret = CheckMacPerms(workSpace, srcLabel, name, node == NULL ? 0 : node->labelIndex);
free(legacyName);
return ret;
}
int CheckPropertyName(const char *name, int propInfo)
{
size_t nameLen = strlen(name);
if (nameLen >= PROPERTY_VALUE_LEN_MAX) {
return PROPERTY_CODE_INVALID_NAME;
}
if (nameLen < 1 || name[0] == '.' || (!propInfo && name[nameLen - 1] == '.')) {
PROPERTY_LOGE("CheckPropertyName %s %d", name, propInfo);
return PROPERTY_CODE_INVALID_NAME;
}
/* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
/* Don't allow ".." to appear in a property name */
for (size_t i = 0; i < nameLen; i++) {
if (name[i] == '.') {
if (name[i - 1] == '.') {
return PROPERTY_CODE_INVALID_NAME;
}
continue;
}
if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') {
continue;
}
if (isalnum(name[i])) {
continue;
}
return PROPERTY_CODE_INVALID_NAME;
}
return 0;
}
int CheckPropertyValue(WorkSpace *workSpace, const TrieDataNode *node, const char *name, const char *value)
{
if (IS_READY_ONLY(name)) {
if (node != NULL && node->dataIndex != 0) {
PROPERTY_LOGE("Read-only property was already set %s", name);
return PROPERTY_CODE_READ_ONLY_PROPERTY;
}
} else {
// 限制非read only的属性防止属性值修改后原空间不能保存
PROPERTY_CHECK(strlen(value) < PROPERTY_VALUE_LEN_MAX,
return PROPERTY_CODE_INVALID_VALUE, "Illegal property value");
}
return 0;
}
int CheckMacPerms(PropertyWorkSpace *workSpace,
const PropertySecurityLabel *srcLabel, const char *name, u_int32_t labelIndex)
{
#ifdef PROPERTY_SUPPORT_SELINUX
PropertyAuditData auditData;
auditData.name = name;
auditData.cr = &srcLabel->cred;
int ret = 0;
TrieNode *node = (TrieNode *)GetTrieNode(&workSpace->propertyLabelSpace, &labelIndex);
if (node != 0) { // 已经存在label
ret = selinux_check_access(srcLabel, node->key, "property_service", "set", &auditData);
} else {
ret = selinux_check_access(srcLabel, "u:object_r:default_prop:s0", "property_service", "set", &auditData);
}
return ret == 0 ? 0 : PROPERTY_CODE_PERMISSION_DENIED;
#else
return 0;
#endif
}
int CanWriteProperty(PropertyWorkSpace *workSpace,
const PropertySecurityLabel *srcLabel, const TrieDataNode *node, const char *name, const char *value)
{
PROPERTY_CHECK(workSpace != NULL && name != NULL && value != NULL && srcLabel != NULL,
return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
if (strncmp(name, "ctl.", strlen("ctl.")) == 0) { // 处理ctrl TODO
return CheckControlPropertyPerms(workSpace, srcLabel, name, value);
}
int ret = CheckMacPerms(workSpace, srcLabel, name, node == NULL ? 0 : node->labelIndex);
PROPERTY_CHECK(ret == 0, return ret, "SELinux permission check failed");
return 0;
}
int CanReadProperty(PropertyWorkSpace *workSpace, u_int32_t labelIndex, const char *name)
{
PROPERTY_CHECK(workSpace != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
PROPERTY_CHECK(name != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
#ifdef PROPERTY_SUPPORT_SELINUX
PropertyAuditData auditData;
auditData.name = name;
UserCred cr = {.pid = 0, .uid = 0, .gid = 0};
auditData.cr = &cr;
int ret = 0;
TrieNode *node = (TrieNode *)GetTrieNode(&workSpace->propertyLabelSpace, &labelIndex);
if (node != 0) { // 已经存在label
ret = selinux_check_access(&workSpace->context, node->key, "property_service", "read", &auditData);
} else {
ret = selinux_check_access(&workSpace->context, "selinux_check_access", "file", "read", &auditData);
}
return ret == 0 ? 0 : PROPERTY_CODE_PERMISSION_DENIED;
#else
return 0;
#endif
}
int GetSubStringInfo(const char *buff, u_int32_t buffLen, char delimiter, SubStringInfo *info, int subStrNumber)
{
size_t i = 0;
// 去掉开始的空格
for (; i < strlen(buff); i++) {
if (!isspace(buff[i])) {
break;
}
}
// 过滤掉注释
if (buff[i] == '#') {
return -1;
}
// 分割字符串
int curr = 0;
int valueCurr = 0;
for (; i < buffLen; i++) {
if (buff[i] == '\n' || buff[i] == '\r') {
break;
}
if (buff[i] == delimiter && valueCurr != 0) {
info[curr].value[valueCurr] = '\0';
valueCurr = 0;
curr++;
} else if (isspace(buff[i])) { // 无效字符,进行过滤
continue;
} else {
if ((valueCurr + 1) >= (int)sizeof(info[curr].value)) {
continue;
}
info[curr].value[valueCurr++] = buff[i];
}
if (curr >= subStrNumber) {
break;
}
}
if (valueCurr > 0) {
info[curr].value[valueCurr] = '\0';
valueCurr = 0;
curr++;
}
return curr;
}
int BuildPropertyContent(char *content, u_int32_t contentSize, const char *name, const char *value)
{
PROPERTY_CHECK(name != NULL && value != NULL && content != NULL, return -1, "Invalid param");
u_int32_t nameLen = (u_int32_t)strlen(name);
u_int32_t valueLen = (u_int32_t)strlen(value);
PROPERTY_CHECK(contentSize >= (nameLen + valueLen + 2), return -1, "Invalid content size %u", contentSize);
int offset = 0;
int ret = memcpy_s(content + offset, contentSize - offset, name, nameLen);
offset += nameLen;
ret |= memcpy_s(content + offset, contentSize - offset, "=", 1);
offset += 1;
ret |= memcpy_s(content + offset, contentSize - offset, value, valueLen);
offset += valueLen;
content[offset] = '\0';
PROPERTY_CHECK(ret == 0, return -1, "Failed to copy porperty");
offset += 1;
return offset;
}
int ProcessPropertTraversal(WorkSpace *workSpace, TrieNode *node, void *cookie)
{
PropertyTraversalContext *context = (PropertyTraversalContext *)cookie;
TrieDataNode *current = (TrieDataNode *)node;
if (current == NULL) {
return 0;
}
if (current->dataIndex == 0) {
return 0;
}
context->traversalParamPtr(current->dataIndex, context->context);
return 0;
}
int TraversalProperty(PropertyWorkSpace *workSpace, TraversalParamPtr walkFunc, void *cookie)
{
PropertyTraversalContext context = {
walkFunc, cookie
};
return TraversalTrieDataNode(&workSpace->propertySpace,
(TrieDataNode *)workSpace->propertySpace.rootNode, ProcessPropertTraversal, &context);
}

View File

@ -0,0 +1,536 @@
/*
* Copyright (c) 2020 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "property_trie.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include "init_utils.h"
#include "property.h"
#include "property_manager.h"
#define LABEL "Manager"
int InitWorkSpace(const char *fileName, WorkSpace *workSpace, int onlyRead)
{
PROPERTY_CHECK(fileName != NULL, return PROPERTY_CODE_INVALID_NAME, "Invalid param");
PROPERTY_CHECK(workSpace != NULL, return PROPERTY_CODE_INVALID_NAME, "Invalid param");
if (workSpace->area != NULL) {
return 0;
}
int ret = memcpy_s(workSpace->fileName, FILENAME_LEN_MAX, fileName, strlen(fileName));
PROPERTY_CHECK(ret == 0, return PROPERTY_CODE_INVALID_NAME, "Copy file %s fail ", fileName);
int openMode = 0;
int prot = PROT_READ;
if (onlyRead) {
openMode = O_RDONLY;
} else {
openMode = O_CREAT | O_RDWR | O_TRUNC;
prot = PROT_READ | PROT_WRITE;
}
ret = InitWorkSpace_(workSpace, openMode, prot, PROPERTY_WORKSPACE_MAX, onlyRead);
PROPERTY_CHECK(ret == 0, return ret, "Failed to init workspace %s", workSpace->fileName);
return ret;
}
int InitPersistWorkSpace(const char *fileName, WorkSpace *workSpace)
{
PROPERTY_CHECK(fileName != NULL, return PROPERTY_CODE_INVALID_NAME, "Invalid param");
PROPERTY_CHECK(workSpace != NULL, return PROPERTY_CODE_INVALID_NAME, "Invalid param");
if (workSpace->area != NULL) {
return 0;
}
int ret = memcpy_s(workSpace->fileName, FILENAME_LEN_MAX, fileName, strlen(fileName));
PROPERTY_CHECK(ret == 0, return PROPERTY_CODE_INVALID_NAME, "Copy file %s fail ", fileName);
int flag = (access(fileName, F_OK) == 0) ? 1 : 0;
int openMode = (flag == 0) ? (O_CREAT | O_RDWR | O_TRUNC) : O_RDWR;
int prot = PROT_READ | PROT_WRITE;
ret = InitWorkSpace_(workSpace, openMode, prot, PROPERTY_WORKSPACE_MAX, flag);
PROPERTY_CHECK(ret == 0, return ret, "Failed to init workspace %s", workSpace->fileName);
return ret;
}
int InitWorkSpace_(WorkSpace *workSpace, int mode, int prot, u_int32_t spaceSize, int readOnly)
{
PROPERTY_CHECK(workSpace != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid fileName");
PROPERTY_CHECK(workSpace->allocTrieNode != NULL,
return PROPERTY_CODE_INVALID_PARAM, "Invalid param %s", workSpace->fileName);
PROPERTY_CHECK(workSpace->compareTrieNode != NULL,
return PROPERTY_CODE_INVALID_PARAM, "Invalid param %s", workSpace->fileName);
PROPERTY_LOGI("InitWorkSpace %s ", workSpace->fileName);
CheckAndCreateDir(workSpace->fileName);
int fd = open(workSpace->fileName, mode, 00777); //0444);
PROPERTY_CHECK(fd >= 0, return PROPERTY_CODE_INVALID_NAME,
"Open file %s fail error %s", workSpace->fileName, strerror(errno));
if (!readOnly) {
lseek(fd, spaceSize, SEEK_SET);
write(fd, "", 1);
}
void *areaAddr = (void *)mmap(NULL, spaceSize, prot, MAP_SHARED, fd, 0);
PROPERTY_CHECK(areaAddr != MAP_FAILED, close(fd); return PROPERTY_CODE_ERROR_MAP_FILE,
"Failed to map memory error %s", strerror(errno));
close(fd);
if (!readOnly) {
workSpace->area = (WorkArea*)areaAddr;
atomic_init(&workSpace->area->serial, 0);
workSpace->area->dataSize = spaceSize;
workSpace->area->currOffset = sizeof(WorkArea);
// 创建一个key为#的节点
u_int32_t offset = workSpace->allocTrieNode(workSpace, "#", 1);
workSpace->area->firstNode = offset;
workSpace->rootNode = GetTrieNode(workSpace, &offset);
} else {
workSpace->area = (WorkArea*)areaAddr;
workSpace->rootNode = GetTrieNode(workSpace, &workSpace->area->firstNode);
}
PROPERTY_LOGI("InitWorkSpace success, currOffset %u firstNode %u dataSize %u",
workSpace->area->currOffset, workSpace->area->firstNode, workSpace->area->dataSize);
return 0;
}
void CloseWorkSpace(WorkSpace *workSpace)
{
PROPERTY_CHECK(workSpace != NULL && workSpace->area != NULL, return, "The workspace is null");
munmap((char *)workSpace->area, workSpace->area->dataSize);
workSpace->area = NULL;
}
u_int32_t GetWorkSpaceSerial(WorkSpace *workSpace)
{
PROPERTY_CHECK(workSpace != NULL && workSpace->area != NULL, return 0, "The workspace is null");
return (u_int32_t)workSpace->area->serial;
}
u_int32_t AllocateTrieNode(WorkSpace *workSpace, const char *key, u_int32_t keyLen)
{
u_int32_t len = keyLen + sizeof(TrieNode) + 1;
PROPERTY_CHECK((workSpace->area->currOffset + len) < workSpace->area->dataSize, return 0,
"Failed to allocate currOffset %d, dataSize %d", workSpace->area->currOffset, workSpace->area->dataSize);
TrieNode *node = (TrieNode*)(workSpace->area->data + workSpace->area->currOffset + len);
atomic_init(&node->serial, ATOMIC_VAR_INIT(keyLen << TRIE_SERIAL_KEY_LEN_OFFSET));
int ret = memcpy_s(node->key, keyLen, key, keyLen);
PROPERTY_CHECK(ret == 0, return 0, "Failed to copy key");
node->key[keyLen] = '\0';
node->left = 0;
node->right = 0;
u_int32_t offset = workSpace->area->currOffset;
workSpace->area->currOffset += len;
return offset;
}
u_int32_t AllocateTrieDataNode(WorkSpace *workSpace, const char *key, u_int32_t keyLen)
{
u_int32_t len = keyLen + sizeof(TrieDataNode) + 1;
PROPERTY_CHECK((workSpace->area->currOffset + len) < workSpace->area->dataSize, return 0,
"Failed to allocate currOffset %d, dataSize %d", workSpace->area->currOffset, workSpace->area->dataSize);
TrieDataNode *node = (TrieDataNode*)(workSpace->area->data + workSpace->area->currOffset);
atomic_init(&node->serial, ATOMIC_VAR_INIT(keyLen << TRIE_SERIAL_KEY_LEN_OFFSET));
int ret = memcpy_s(node->key, keyLen, key, keyLen);
PROPERTY_CHECK(ret == 0, return 0, "Failed to copy key");
node->key[keyLen] = '\0';
node->left = 0;
node->right = 0;
node->child = 0;
node->dataIndex = 0;
node->labelIndex = 0;
u_int32_t offset = workSpace->area->currOffset;
workSpace->area->currOffset += len;
return offset;
}
TrieNode *GetTrieNode(WorkSpace *workSpace, NODE_INDEX *index)
{
if (index == NULL) {
return NULL;
}
u_int32_t offset = *index; // atomic_load_explicit(&current->children, memory_order_relaxed);
if (offset == 0 || offset > workSpace->area->dataSize) {
return NULL;
}
return (TrieNode*)(workSpace->area->data + offset);
}
u_int32_t GetTrieKeyLen(TrieNode *current)
{
return (current)->serial >> TRIE_SERIAL_KEY_LEN_OFFSET;
}
u_int32_t GetTrieNodeOffset(WorkSpace *workSpace, const TrieNode *current)
{
return (((char *)current) - workSpace->area->data);
}
void SaveIndex(NODE_INDEX *index, u_int32_t offset)
{
// atomic_store_explicit(&current->children, new_offset, memory_order_release);
*index = offset;
}
int CompareTrieDataNode(TrieNode *node, const char *key, u_int32_t keyLen)
{
TrieDataNode *data = (TrieDataNode *)node;
u_int32_t len = GetTrieKeyLen((TrieNode *)data);
if (len > keyLen) {
return -1;
} else if (len < keyLen) {
return 1;
}
return strncmp(data->key, key, keyLen);
}
int CompareTrieNode(TrieNode *node, const char *key, u_int32_t keyLen)
{
u_int32_t len = GetTrieKeyLen(node);
if (len > keyLen) {
return -1;
} else if (len < keyLen) {
return 1;
}
return strncmp(node->key, key, keyLen);
}
static void GetNextKey(const char **remainingKey, int *hasDot, char **subKey, u_int32_t *subKeyLen)
{
*subKey = strchr(*remainingKey, '.');
if (*subKey != NULL) {
if ((*subKey)[0] == '.') {
*hasDot = 1;
}
*subKeyLen = *subKey - *remainingKey;
} else {
*subKeyLen = strlen(*remainingKey);
}
}
TrieDataNode *AddTrieDataNode(WorkSpace *workSpace, const char *key, u_int32_t keyLen)
{
PROPERTY_CHECK(workSpace->allocTrieNode != NULL, return NULL, "Invalid param %s", key);
PROPERTY_CHECK(workSpace->compareTrieNode != NULL, return NULL, "Invalid param %s", key);
const char *remainingKey = key;
TrieDataNode *current = (TrieDataNode *)workSpace->rootNode;
while (1) {
int hasDot = 0;
u_int32_t subKeyLen = 0;
char *subKey = NULL;
GetNextKey(&remainingKey, &hasDot, &subKey, &subKeyLen);
if (!subKeyLen) {
return NULL;
}
u_int32_t offset = subKey == NULL ? strlen(key) : subKey - key;
if (current->child != 0) { // 如果child存在则检查是否匹配
TrieDataNode *next = (TrieDataNode*)GetTrieNode(workSpace, &current->child);
if (next != NULL && workSpace->compareTrieNode((TrieNode*)next, remainingKey, subKeyLen) == 0) {
current = next;
} else { // 不匹配,需要建立子树
current = (TrieDataNode*)AddToSubTrie(workSpace, current, key, offset);
}
} else if (hasDot) {
u_int32_t offset = workSpace->allocTrieNode(workSpace, remainingKey, subKeyLen);
PROPERTY_CHECK(offset != 0, return NULL, "Failed to allocate key %s", key);
SaveIndex(&current->child, offset);
current = (TrieDataNode*)GetTrieNode(workSpace, &current->child);
} else {
current = (TrieDataNode*)AddToSubTrie(workSpace, current, key, offset);
}
if (current == NULL) {
return NULL;
}
if (subKey == NULL || strcmp(subKey, ".") == 0) {
break;
}
remainingKey = subKey + 1;
}
return current;
}
TrieDataNode *AddToSubTrie(WorkSpace *workSpace, TrieDataNode *dataNode, const char *key, u_int32_t keyLen)
{
TrieDataNode *root = NULL;
int ret = workSpace->compareTrieNode((TrieNode *)dataNode, key, keyLen);
if (ret <= 0) {
root = (TrieDataNode *)GetTrieNode(workSpace, &dataNode->left);
if (root == NULL) {
u_int32_t offset = workSpace->allocTrieNode(workSpace, key, keyLen);
PROPERTY_CHECK(offset != 0, return NULL, "Failed to allocate key %s", key);
SaveIndex(&dataNode->left, offset);
return (TrieDataNode *)GetTrieNode(workSpace, &dataNode->left);
}
} else {
root = (TrieDataNode *)GetTrieNode(workSpace, &dataNode->right);
if (root == NULL) {
u_int32_t offset = workSpace->allocTrieNode(workSpace, key, keyLen);
PROPERTY_CHECK(offset != 0, return NULL, "Failed to allocate key %s", key);
SaveIndex(&dataNode->right, offset);
return (TrieDataNode *)GetTrieNode(workSpace, &dataNode->right);
}
}
return (TrieDataNode*)AddTrieNode(workSpace, (TrieNode*)root, key, keyLen);
}
TrieNode *AddTrieNode(WorkSpace *workSpace, TrieNode *root, const char *key, u_int32_t keyLen)
{
PROPERTY_CHECK(root != NULL, return NULL, "Invalid param %s", key);
TrieNode *current = root;
TrieNode *next = NULL;
while (1) {
if (current == NULL) {
return NULL;
}
int ret = workSpace->compareTrieNode(current, key, keyLen);
if (ret == 0) {
return current;
}
if (ret < 0) {
next = GetTrieNode(workSpace, &current->left);
if (next == NULL) {
u_int32_t offset = workSpace->allocTrieNode(workSpace, key, keyLen);
PROPERTY_CHECK(offset != 0, return NULL, "Failed to allocate key %s", key);
SaveIndex(&current->left, offset);
return GetTrieNode(workSpace, &current->left);
}
} else {
next = GetTrieNode(workSpace, &current->right);
if (next == NULL) {
u_int32_t offset = workSpace->allocTrieNode(workSpace, key, keyLen);
PROPERTY_CHECK(offset != 0, return NULL, "Failed to allocate key %s", key);
SaveIndex(&current->right, offset);
return GetTrieNode(workSpace, &current->right);
}
}
current = next;
}
return current;
}
TrieDataNode *FindTrieDataNode(WorkSpace *workSpace, const char *key, u_int32_t keyLen, int matchPrefix)
{
PROPERTY_CHECK(workSpace->allocTrieNode != NULL, return NULL, "Invalid param %s", key);
PROPERTY_CHECK(workSpace->compareTrieNode != NULL, return NULL, "Invalid param %s", key);
const char *remainingKey = key;
TrieDataNode *matchNode = (TrieDataNode *)workSpace->rootNode;
TrieDataNode *current = (TrieDataNode *)workSpace->rootNode;
while (1) {
int hasDot = 0;
u_int32_t subKeyLen = 0;
char *subKey = NULL;
GetNextKey(&remainingKey, &hasDot, &subKey, &subKeyLen);
if (!subKeyLen) {
return matchPrefix ? matchNode : NULL;
}
u_int32_t offset = subKey == NULL ? strlen(key) : subKey - key;
if (current->child != 0) { // 如果child存在则检查是否匹配
TrieDataNode *next = (TrieDataNode*)GetTrieNode(workSpace, &current->child);
if (next != NULL && workSpace->compareTrieNode((TrieNode*)next, remainingKey, subKeyLen) == 0) {
current = next;
} else { // 不匹配,搜索子树
current = (TrieDataNode*)FindSubTrie(workSpace, current, key, offset);
}
} else {
current = (TrieDataNode*)FindSubTrie(workSpace, current, key, offset);
}
if (current == NULL) {
return matchPrefix ? matchNode : NULL;
}
matchNode = current;
if (subKey == NULL || strcmp(subKey, ".") == 0) {
break;
}
remainingKey = subKey + 1;
}
return matchPrefix ? matchNode : current;
}
TrieDataNode *FindSubTrie(WorkSpace *workSpace, TrieDataNode *dataNode, const char *key, u_int32_t keyLen)
{
TrieDataNode *root = NULL;
int ret = workSpace->compareTrieNode((TrieNode*)dataNode, key, keyLen);
if (ret <= 0) {
root = (TrieDataNode *)GetTrieNode(workSpace, &dataNode->left);
if (root == NULL) {
return NULL;
}
} else {
root = (TrieDataNode *)GetTrieNode(workSpace, &dataNode->right);
if (root == NULL) {
return NULL;
}
}
return (TrieDataNode*)FindTrieNode(workSpace, (TrieNode*)root, key, keyLen);
}
TrieNode *FindTrieNode(WorkSpace *workSpace, TrieNode *root, const char *key, u_int32_t keyLen)
{
PROPERTY_CHECK(root != NULL, return NULL, "Invalid param %s", key);
TrieNode *current = root;
TrieNode *next = NULL;
while (1) {
if (current == NULL) {
return NULL;
}
int ret = workSpace->compareTrieNode(current, key, keyLen);
if (ret == 0) {
return current;
}
if (ret < 0) {
next = GetTrieNode(workSpace, &current->left);
} else {
next = GetTrieNode(workSpace, &current->right);
}
if (next == NULL) {
return NULL;
}
current = next;
}
return current;
}
int TraversalTrieDataNode(WorkSpace *workSpace, TrieDataNode *current, TraversalTrieNodePtr walkFunc, void* cookie)
{
PROPERTY_CHECK(walkFunc != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
PROPERTY_CHECK(workSpace != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
if (current == NULL) {
return 0;
}
while (1) {
TrieDataNode *child = NULL;
// 显示子树
TraversalTrieDataNode(workSpace, (TrieDataNode*)GetTrieNode(workSpace, &current->left), walkFunc, cookie);
TraversalTrieDataNode(workSpace, (TrieDataNode*)GetTrieNode(workSpace, &current->right), walkFunc, cookie);
walkFunc(workSpace, (TrieNode *)current, cookie);
if (current->child != 0) { // 如果child存在则检查是否匹配
child = (TrieDataNode*)GetTrieNode(workSpace, &current->child);
}
if (child == NULL) {
return 0;
}
current = child;
}
return 0;
}
int TraversalTrieNode(WorkSpace *workSpace, TrieNode *root, TraversalTrieNodePtr walkFunc, void* cookie)
{
PROPERTY_CHECK(workSpace != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
PROPERTY_CHECK(walkFunc != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
if (root == NULL) {
return 0;
}
TraversalTrieNode(workSpace, GetTrieNode(workSpace, &root->left), walkFunc, cookie);
TraversalTrieNode(workSpace, GetTrieNode(workSpace, &root->right), walkFunc, cookie);
walkFunc(workSpace, (TrieNode *)root, cookie);
return 0;
}
u_int32_t AddData(WorkSpace *workSpace, const char *key, u_int32_t keyLen, const char *value, u_int32_t valueLen)
{
PROPERTY_CHECK(workSpace != NULL, return 0, "Invalid param");
PROPERTY_CHECK(key != NULL && value != NULL, return 0, "Invalid param");
u_int32_t realLen = sizeof(DataEntry) + 1 + 1;
if (valueLen > PROPERTY_VALUE_LEN_MAX) { // value超过最大时只能是只读属性保存最大字符串
realLen = keyLen + valueLen;
} else {
realLen = keyLen + PROPERTY_VALUE_LEN_MAX; // 属性保存,预留最大属性值长度
}
PROPERTY_CHECK((workSpace->area->currOffset + realLen) < workSpace->area->dataSize, return 0,
"Failed to allocate currOffset %d, dataSize %d", workSpace->area->currOffset, workSpace->area->dataSize);
DataEntry *node = (DataEntry*)(workSpace->area->data + workSpace->area->currOffset);
u_int32_t dataLength = keyLen << TRIE_SERIAL_KEY_LEN_OFFSET | valueLen << TRIE_SERIAL_DATA_LEN_OFFSET;
atomic_init(&node->serial, ATOMIC_VAR_INIT(0));
atomic_init(&node->dataLength, ATOMIC_VAR_INIT(dataLength));
int ret = memcpy_s(node->data, keyLen, key, keyLen);
ret |= memcpy_s(node->data + keyLen + 1, valueLen, value, valueLen);
PROPERTY_CHECK(ret == 0, return 0, "Failed to copy key");
node->data[keyLen] = '=';
node->data[keyLen + 1 + valueLen] = '\0';
u_int32_t offset = workSpace->area->currOffset;
workSpace->area->currOffset += realLen;
return offset;
}
int UpdateDataValue(DataEntry *entry, const char *value)
{
PROPERTY_CHECK(entry != NULL && value != NULL, return PROPERTY_CODE_INVALID_PARAM, "Failed to check param");
int ret = PROPERTY_CODE_INVALID_VALUE;
u_int32_t keyLen = DATA_ENTRY_KEY_LEN(entry);
u_int32_t valueLen = strlen(value);
u_int32_t oldLen = DATA_ENTRY_DATA_LEN(entry);
if (oldLen < PROPERTY_VALUE_LEN_MAX && valueLen < PROPERTY_VALUE_LEN_MAX) {
PROPERTY_LOGE("Old value %s new value %s", entry->data + keyLen + 1, value);
ret = memcpy_s(entry->data + keyLen + 1, PROPERTY_VALUE_LEN_MAX, value, valueLen + 1);
PROPERTY_CHECK(ret == 0, return PROPERTY_CODE_INVALID_VALUE, "Failed to copy value");
u_int32_t dataLength = keyLen << TRIE_SERIAL_KEY_LEN_OFFSET | valueLen << TRIE_SERIAL_DATA_LEN_OFFSET;
atomic_store_explicit(&entry->dataLength, dataLength, memory_order_release);
}
return ret;
}
u_int32_t GetDataSerial(const DataEntry *entry)
{
u_int32_t serial = atomic_load_explicit(&entry->serial, memory_order_acquire);
while (DATA_ENTRY_DIRTY(serial)) {
futex_wait(&entry->serial, serial, NULL);
serial = atomic_load_explicit(&entry->serial, memory_order_acquire);
}
return serial;
}
int GetDataName(const DataEntry *entry, char *name, u_int32_t len)
{
PROPERTY_CHECK(entry != NULL && name != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
u_int32_t keyLen = DATA_ENTRY_KEY_LEN(entry);
PROPERTY_CHECK(len > keyLen, return -1, "Invalid param size");
int ret = memcpy_s(name, len, entry->data, keyLen);
PROPERTY_CHECK(ret == 0, return PROPERTY_CODE_INVALID_PARAM, "Failed to copy name");
name[keyLen] = '\0';
return ret;
}
int GetDataValue(const DataEntry *entry, char *value, u_int32_t len)
{
PROPERTY_CHECK(entry != NULL && value != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
u_int32_t keyLen = DATA_ENTRY_KEY_LEN(entry);
u_int32_t valueLen = DATA_ENTRY_DATA_LEN(entry);
PROPERTY_CHECK(len > valueLen, return PROPERTY_CODE_INVALID_PARAM, "Invalid value len %u %u", len, valueLen);
int ret = memcpy_s(value, len, entry->data + keyLen + 1, valueLen);
PROPERTY_CHECK(ret == 0, return PROPERTY_CODE_INVALID_PARAM, "Failed to copy value");
value[valueLen] = '\0';
return ret;
}

View File

@ -0,0 +1,156 @@
/*
* Copyright (c) 2020 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "property.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include "property_manager.h"
#include "property_trie.h"
#define LABEL "Manager"
#define MAX_BUFF 256
typedef struct {
WorkSpace *workSpace;
WorkSpace *persistWorkSpace;
char *buffer;
} PersistContext;
static PropertyPersistWorkSpace g_persistWorkSpace = {ATOMIC_VAR_INIT(0), };
static int ProcessPropertTraversal(WorkSpace *workSpace, TrieNode *node, void *cookie)
{
PROPERTY_CHECK(workSpace != 0 && node != NULL && cookie != NULL, return -1, "Invalid param");
TrieDataNode *current = (TrieDataNode *)node;
if (current == NULL || current->dataIndex == 0) {
return 0;
}
DataEntry *entry = (DataEntry *)GetTrieNode(workSpace, &current->dataIndex);
if (entry == NULL) {
return -1;
}
PersistContext *persistContext = (PersistContext *)cookie;
int ret = GetDataName(entry, persistContext->buffer, MAX_BUFF);
if (strncmp(persistContext->buffer, "persist.", strlen("persist.")) != 0) {
return 0;
}
ret |= GetDataValue(entry, persistContext->buffer + MAX_BUFF, MAX_BUFF);
if (ret == 0) { // 只支持新建
PROPERTY_LOGI("Insert new persist property from normal property %s", persistContext->buffer);
ret = AddProperty(persistContext->persistWorkSpace, persistContext->buffer, persistContext->buffer + MAX_BUFF);
}
PROPERTY_CHECK(ret == 0, return ret, "Failed to add persist property");
return ret;
}
static int ProcessPersistPropertTraversal(WorkSpace *workSpace, TrieNode *node, void *cookie)
{
TrieDataNode *current = (TrieDataNode *)node;
if (current == NULL || current->dataIndex == 0) {
return 0;
}
DataEntry *entry = (DataEntry *)GetTrieNode(workSpace, &current->dataIndex);
if (entry == NULL) {
return -1;
}
PersistContext *persistContext = (PersistContext *)cookie;
int ret = GetDataName(entry, persistContext->buffer, MAX_BUFF);
ret |= GetDataValue(entry, persistContext->buffer + MAX_BUFF, MAX_BUFF);
if (ret == 0) {
PROPERTY_LOGI("update normal property %s %s from persist property ",
persistContext->buffer, persistContext->buffer + MAX_BUFF);
ret = WriteProperty(persistContext->workSpace, persistContext->buffer, persistContext->buffer + MAX_BUFF);
}
PROPERTY_CHECK(ret == 0, return ret, "Failed to add persist property");
return ret;
}
int InitPersistPropertyWorkSpace(const char *context)
{
u_int32_t flags = atomic_load_explicit(&g_persistWorkSpace.flags, memory_order_relaxed);
PROPERTY_LOGI("InitPersistPropertyWorkSpace flags %x", flags);
if ((flags & WORKSPACE_FLAGS_INIT) == WORKSPACE_FLAGS_INIT) {
return 0;
}
g_persistWorkSpace.persistWorkSpace.compareTrieNode = CompareTrieDataNode;
g_persistWorkSpace.persistWorkSpace.allocTrieNode = AllocateTrieDataNode;
int ret = InitPersistWorkSpace(PROPERTY_PERSIST_PATH, &g_persistWorkSpace.persistWorkSpace);
atomic_store_explicit(&g_persistWorkSpace.flags, WORKSPACE_FLAGS_INIT, memory_order_release);
return ret;
}
int RefreshPersistProperties(PropertyWorkSpace *workSpace, const char *context)
{
u_int32_t flags = atomic_load_explicit(&g_persistWorkSpace.flags, memory_order_relaxed);
if ((flags & WORKSPACE_FLAGS_INIT) != WORKSPACE_FLAGS_INIT) {
int ret = InitPersistPropertyWorkSpace(context);
PROPERTY_CHECK(ret == 0, return ret, "Failed to init persist property");
flags = atomic_load_explicit(&g_persistWorkSpace.flags, memory_order_relaxed);
}
if ((flags & WORKSPACE_FLAGS_LOADED) == WORKSPACE_FLAGS_LOADED) {
return 0;
}
// 申请临时的缓存,用于数据读取
char *buffer = (char *)malloc(MAX_BUFF + MAX_BUFF);
PROPERTY_CHECK(buffer != NULL, return -1, "Failed to malloc memory for property");
PersistContext persistContext = {
&workSpace->propertySpace, &g_persistWorkSpace.persistWorkSpace, buffer
};
// 遍历当前的属性并把persist的写入
int ret = TraversalTrieDataNode(&workSpace->propertySpace,
(TrieDataNode *)workSpace->propertySpace.rootNode, ProcessPropertTraversal, &persistContext);
// 修改默认属性值
ret = TraversalTrieDataNode(&g_persistWorkSpace.persistWorkSpace,
(TrieDataNode *)g_persistWorkSpace.persistWorkSpace.rootNode, ProcessPersistPropertTraversal, &persistContext);
atomic_store_explicit(&g_persistWorkSpace.flags, flags | WORKSPACE_FLAGS_LOADED, memory_order_release);
free(buffer);
return ret;
}
void ClosePersistPropertyWorkSpace()
{
CloseWorkSpace(&g_persistWorkSpace.persistWorkSpace);
atomic_store_explicit(&g_persistWorkSpace.flags, 0, memory_order_release);
}
int WritePersistProperty(const char *name, const char *value)
{
PROPERTY_CHECK(value != NULL && name != NULL, return PROPERTY_CODE_INVALID_PARAM, "Invalid param");
u_int32_t flags = atomic_load_explicit(&g_persistWorkSpace.flags, memory_order_relaxed);
if ((flags & WORKSPACE_FLAGS_LOADED) != WORKSPACE_FLAGS_LOADED) {
return 0;
}
if (strncmp(name, "persist.", strlen("persist.")) != 0) {
return 0;
}
return WriteProperty(&g_persistWorkSpace.persistWorkSpace, name, value);
}

View File

@ -0,0 +1,282 @@
/*
* Copyright (c) 2020 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "property_service.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "property.h"
#include "property_manager.h"
#include "property_request.h"
#include "trigger.h"
#include "uv.h"
#define BUFFER_SIZE 256
#define LABEL "Server"
static char *g_initContext = "";
static PropertyWorkSpace g_propertyWorkSpace = {ATOMIC_VAR_INIT(0), {}, {}, {}};
void InitPropertyService()
{
int ret = InitPropertyWorkSpace(&g_propertyWorkSpace, 0, g_initContext);
PROPERTY_CHECK(ret == 0, return, "Init propert workspace fail");
}
int LoadDefaultProperty(const char *fileName)
{
u_int32_t flags = atomic_load_explicit(&g_propertyWorkSpace.flags, memory_order_relaxed);
if ((flags & WORKSPACE_FLAGS_INIT) != WORKSPACE_FLAGS_INIT) {
return PROPERTY_CODE_NOT_INIT;
}
FILE *fp = fopen(fileName, "r");
PROPERTY_CHECK(fp != NULL, return -1, "Open file %s fail", fileName);
char buff[BUFFER_SIZE];
SubStringInfo *info = malloc(sizeof(SubStringInfo) * (SUBSTR_INFO_LABEL + 1));
while(fgets(buff, BUFFER_SIZE, fp) != NULL) {
int subStrNumber = GetSubStringInfo(buff, strlen(buff), '\n', info, SUBSTR_INFO_LABEL + 1);
if (subStrNumber <= SUBSTR_INFO_LABEL) {
continue;
}
if (strncmp(info[0].value, "ctl.", strlen("ctl.")) == 0) {
PROPERTY_LOGE("Do not set ctl. properties from init %s", info[0].value);
continue;
}
if (strcmp(info[0].value, "selinux.restorecon_recursive") == 0) {
PROPERTY_LOGE("Do not set selinux.restorecon_recursive from init %s", info[0].value);
continue;
}
int ret = CheckPropertyName(info[0].value, 0);
PROPERTY_CHECK(ret == 0, continue, "Illegal property name %s", info[0].value);
ret = WriteProperty(&g_propertyWorkSpace.propertySpace, info[0].value, info[1].value);
PROPERTY_CHECK(ret == 0, continue, "Failed to set property %d %s", ret, buff);
ret = WritePersistProperty(info[0].value, info[1].value);
PROPERTY_CHECK(ret == 0, continue, "Failed to set persist property %d %s", ret, buff);
}
fclose(fp);
free(info);
PROPERTY_LOGI("LoadDefaultProperty proterty success %s", fileName);
return 0;
}
int LoadPropertyInfo(const char *fileName)
{
u_int32_t flags = atomic_load_explicit(&g_propertyWorkSpace.flags, memory_order_relaxed);
if ((flags & WORKSPACE_FLAGS_INIT) != WORKSPACE_FLAGS_INIT) {
return PROPERTY_CODE_NOT_INIT;
}
FILE *fp = fopen(fileName, "r");
PROPERTY_CHECK(fp != NULL, return -1, "Open file %s fail", fileName);
SubStringInfo *info = malloc(sizeof(SubStringInfo) * SUBSTR_INFO_MAX);
char buff[BUFFER_SIZE];
int propInfoCount = 0;
while(fgets(buff, BUFFER_SIZE, fp) != NULL) {
int subStrNumber = GetSubStringInfo(buff, strlen(buff), ' ', info, SUBSTR_INFO_MAX);
if (subStrNumber <= 0) {
continue;
}
int ret = WritePropertyInfo(&g_propertyWorkSpace, info, subStrNumber);
PROPERTY_CHECK(ret == 0, continue, "Failed to write property info %d %s", ret, buff);
propInfoCount++;
}
fclose(fp);
free(info);
PROPERTY_LOGI("Load proterty info %d success %s", propInfoCount, fileName);
return 0;
}
static int ProcessPropertySet(RequestMsg *msg)
{
PROPERTY_CHECK(msg != NULL, return PROPERTY_CODE_INVALID_PARAM, "Failed to check param");
SubStringInfo info[3];
int ret = GetSubStringInfo(msg->content, msg->contentSize, '=', info, sizeof(info)/sizeof(info[0]));
PROPERTY_CHECK(ret >= 2, return ret, "Failed to get name from content %s", msg->content);
PROPERTY_LOGI("ProcessPropertySet name %s value: %s", info[0].value, info[1].value);
ret = WritePropertyWithCheck(&g_propertyWorkSpace, &msg->securitylabel, info[0].value, info[1].value);
PROPERTY_CHECK(ret == 0, return ret, "Failed to set property %d name %s %s", ret, info[0].value, info[1].value);
ret = WritePersistProperty(info[0].value, info[1].value);
PROPERTY_CHECK(ret == 0, return ret, "Failed to set property");
// notify event to process trigger
PostTrigger(EVENT_PROPERTY, msg->content, msg->contentSize);
return 0;
}
static void OnClose(uv_handle_t *handle)
{
free(handle);
}
static void OnReceiveAlloc(uv_handle_t *handle, size_t suggestedSize, uv_buf_t* buf)
{
// 这里需要按实际消息的大小申请内存,取最大消息的长度
buf->base = (char *)malloc(sizeof(RequestMsg) + BUFFER_SIZE * 2);
buf->len = suggestedSize;
}
static void OnWriteResponse(uv_write_t *req, int status)
{
// 发送成功,释放请求内存
PROPERTY_LOGI("OnWriteResponse status %d", status);
ResponseNode *node = (ResponseNode*)req;
free(node);
}
static void SendResponse(uv_stream_t *handle, RequestType type, int result, void *content, int size)
{
int ret = 0;
// 申请整块内存,用于回复数据和写请求
ResponseNode *response = (ResponseNode *)malloc(sizeof(ResponseNode) + size);
PROPERTY_CHECK(response != NULL, return, "Failed to alloc memory for response");
response->msg.type = type;
response->msg.contentSize = size;
response->msg.result = result;
if (content != NULL && size != 0) {
ret = memcpy_s(response->msg.content, size, content, size);
PROPERTY_CHECK(ret == 0, return, "Failed to copy content");
}
uv_buf_t buf = uv_buf_init((char *)&response->msg, sizeof(response->msg) + size);
ret = uv_write2(&response->writer, handle, &buf, 1, handle, OnWriteResponse);
PROPERTY_CHECK(ret >= 0, return, "Failed to uv_write2 ret %s", uv_strerror(ret));
}
static void OnReceiveRequest(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf)
{
if (nread <= 0 || buf == NULL || buf->base == NULL) {
uv_close((uv_handle_t*)handle, OnClose);
free(buf->base);
return;
}
int freeHandle = 1;
RequestMsg *msg = (RequestMsg *)buf->base;
switch (msg->type) {
case SET_PROPERTY: {
freeHandle = 0;
int ret = ProcessPropertySet(msg);
SendResponse(handle, SET_PROPERTY, ret, NULL, 0);
break;
}
default:
PROPERTY_LOGE("not supported the command: %d", msg->type);
break;
}
free(buf->base);
uv_close((uv_handle_t*)handle, OnClose);
}
static void RemoveSocket(int sig)
{
uv_fs_t req;
uv_fs_unlink(uv_default_loop(), &req, PIPE_NAME, NULL);
ClosePropertyWorkSpace(&g_propertyWorkSpace);
ClosePersistPropertyWorkSpace();
uv_stop(uv_default_loop());
exit(0);
}
static void OnConnection(uv_stream_t *server, int status)
{
PROPERTY_CHECK(status >= 0, return, "Error status %d", status);
PROPERTY_CHECK(server != NULL, return, "Error server");
uv_pipe_t *stream = (uv_pipe_t*)malloc(sizeof(uv_pipe_t));
PROPERTY_CHECK(stream != NULL, return, "Failed to alloc stream");
int ret = uv_pipe_init(uv_default_loop(), (uv_pipe_t*)stream, 1);
PROPERTY_CHECK(ret == 0, free(stream); return, "Failed to uv_pipe_init %d", ret);
stream->data = server;
ret = uv_accept(server, (uv_stream_t *)stream);
PROPERTY_CHECK(ret == 0, uv_close((uv_handle_t*)stream, NULL); free(stream);
return, "Failed to uv_accept %d", ret);
ret = uv_read_start((uv_stream_t *)stream, OnReceiveAlloc, OnReceiveRequest);
PROPERTY_CHECK(ret == 0, uv_close((uv_handle_t*)stream, NULL); free(stream);
return, "Failed to uv_read_start %d", ret);
}
int StartPropertyService()
{
PROPERTY_LOGI("StartPropertyService.");
uv_fs_t req;
uv_fs_unlink(uv_default_loop(), &req, PIPE_NAME, NULL);
signal(SIGINT, RemoveSocket);
uv_pipe_t pipeServer;
int ret = uv_pipe_init(uv_default_loop(), &pipeServer, 0);
PROPERTY_CHECK(ret == 0, return ret, "Failed to uv_pipe_init %d", ret);
ret = uv_pipe_bind(&pipeServer, PIPE_NAME);
PROPERTY_CHECK(ret == 0, return ret, "Failed to uv_pipe_bind %d %s", ret, uv_err_name(ret));
ret = uv_listen((uv_stream_t*)&pipeServer, SOMAXCONN, OnConnection);
PROPERTY_CHECK(ret == 0, return ret, "Failed to uv_listen %d %s", ret, uv_err_name(ret));
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
PROPERTY_LOGI("Start service exit.");
return 0;
}
int SystemWriteParameter(const char *name, const char *value)
{
PROPERTY_CHECK(name != NULL && value != NULL, return -1, "The name is null");
PROPERTY_LOGI("SystemWriteParameter name %s value: %s", name, value);
int ret = WritePropertyWithCheck(&g_propertyWorkSpace, &g_propertyWorkSpace.label, name, value);
if (ret == 0) {
ret = WritePersistProperty(name, value);
PROPERTY_CHECK(ret == 0, return ret, "Failed to set property");
} else {
PROPERTY_LOGE("Failed to set property %d name %s %s", ret, name, value);
}
// notify event to process trigger
PostPropertyTrigger(name, value);
return 0;
}
int SystemReadParameter(const char *name, char *value, unsigned int *len)
{
PROPERTY_CHECK(name != NULL && len != NULL, return -1, "The name is null");
PropertyHandle handle = 0;
int ret = ReadPropertyWithCheck(&g_propertyWorkSpace, name, &handle);
if (ret == 0) {
ret = ReadPropertyValue(&g_propertyWorkSpace, handle, value, len);
}
return ret;
}
int SystemTraversalParameters(void (*traversalParameter)(PropertyHandle handle, void* cookie), void* cookie)
{
PROPERTY_CHECK(traversalParameter != NULL, return -1, "The param is null");
return TraversalProperty(&g_propertyWorkSpace, traversalParameter, cookie);
}
PropertyWorkSpace *GetPropertyWorkSpace()
{
return &g_propertyWorkSpace;
}
int LoadPersistProperties()
{
return RefreshPersistProperties(&g_propertyWorkSpace, g_initContext);
}

View File

@ -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;
}

View File

@ -20,8 +20,10 @@
#include <unistd.h>
#include <sys/prctl.h>
#include <sys/reboot.h>
#ifdef __LINUX__
#if ((defined __LINUX__) || (!defined OHOS_LITE))
#include <linux/securebits.h>
#endif
#ifdef __LINUX__
#include "init_signal_handler.h"
#endif
@ -35,9 +37,9 @@ void RebootSystem()
int KeepCapability()
{
#ifdef __LINUX__
#if ((defined __LINUX__) || (!defined OHOS_LITE))
if (prctl(PR_SET_SECUREBITS, SECBIT_NO_SETUID_FIXUP | SECBIT_NO_SETUID_FIXUP_LOCKED)) {
printf("[Init] prctl failed\n");
printf("[Init] prctl PR_SET_SECUREBITS failed: %d\n", errno);
return -1;
}
#endif
@ -46,9 +48,9 @@ int KeepCapability()
int SetAmbientCapability(int cap)
{
#ifdef __LINUX__
#if ((defined __LINUX__) || (!defined OHOS_LITE))
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0)) {
printf("[Init] prctl PR_CAP_AMBIENT failed\n");
printf("[Init] prctl PR_CAP_AMBIENT failed: %d\n", errno);
return -1;
}
#endif

165
services/src/init_capability.c Executable file
View File

@ -0,0 +1,165 @@
/*
* Copyright (c) 2020 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "init_capability.h"
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef OHOS_LITE
#include <sys/capability.h>
#else
#include <linux/capability.h>
#endif
#include <sys/types.h>
#include <unistd.h>
#include "init_log.h"
#include "init_perms.h"
#define MAX_CAPS_CNT_FOR_ONE_SERVICE 100
struct CapStrCapNum {
char *capStr;
int CapNum;
};
static struct CapStrCapNum g_capStrCapNum[] = {
{"CHOWN", CAP_CHOWN},
{"DAC_OVERRIDE", CAP_DAC_OVERRIDE},
{"DAC_READ_SEARCH", CAP_DAC_READ_SEARCH},
{"FOWNER", CAP_FOWNER},
{"FSETID", CAP_FSETID},
{"KILL", CAP_KILL},
{"SETGID", CAP_SETGID},
{"SETUID", CAP_SETUID},
{"SETPCAP", CAP_SETPCAP},
{"LINUX_IMMUTABLE", CAP_LINUX_IMMUTABLE},
{"NET_BIND_SERVICE", CAP_NET_BIND_SERVICE},
{"NET_BROADCAST", CAP_NET_BROADCAST},
{"NET_ADMIN", CAP_NET_ADMIN},
{"NET_RAW", CAP_NET_RAW},
{"IPC_LOCK", CAP_IPC_LOCK},
{"IPC_OWNER", CAP_IPC_OWNER},
{"SYS_MODULE", CAP_SYS_MODULE},
{"SYS_RAWIO", CAP_SYS_RAWIO},
{"SYS_CHROOT", CAP_SYS_CHROOT},
{"SYS_PTRACE", CAP_SYS_PTRACE},
{"SYS_PACCT", CAP_SYS_PACCT},
{"SYS_ADMIN", CAP_SYS_ADMIN},
{"SYS_BOOT", CAP_SYS_BOOT},
{"SYS_NICE", CAP_SYS_NICE},
{"SYS_RESOURCE", CAP_SYS_RESOURCE},
{"SYS_TIME", CAP_SYS_TIME},
{"SYS_TTY_CONFIG", CAP_SYS_TTY_CONFIG},
{"MKNOD", CAP_MKNOD},
{"LEASE", CAP_LEASE},
{"AUDIT_WRITE", CAP_AUDIT_WRITE},
{"AUDIT_CONTROL", CAP_AUDIT_CONTROL},
{"SETFCAP", CAP_SETFCAP},
{"MAC_OVERRIDE", CAP_MAC_OVERRIDE},
{"MAC_ADMIN", CAP_MAC_ADMIN},
{"SYSLOG", CAP_SYSLOG},
{"WAKE_ALARM", CAP_WAKE_ALARM},
{"BLOCK_SUSPEND", CAP_BLOCK_SUSPEND},
{"AUDIT_READ", CAP_AUDIT_READ},
};
static int GetServiceStringCaps(const cJSON* filedJ, Service* curServ) // string form
{
unsigned int i = 0;
for (; i < curServ->servPerm.capsCnt; ++i) {
if (cJSON_GetArrayItem(filedJ, i) == NULL || !cJSON_GetStringValue(cJSON_GetArrayItem(filedJ, i))
|| strlen(cJSON_GetStringValue(cJSON_GetArrayItem(filedJ, i))) <= 0) { // check all errors
INIT_LOGE("[Init] GetServiceStringCaps, parse item[%d] as string, error.\n", i);
break;
}
char* fieldStr = cJSON_GetStringValue(cJSON_GetArrayItem(filedJ, i));
int mapSize = sizeof(g_capStrCapNum) / sizeof(struct CapStrCapNum); // search
int j = 0;
for (; j < mapSize; j++) {
if (!strcmp(fieldStr, g_capStrCapNum[j].capStr)) {
break;
}
}
if (j < mapSize) {
curServ->servPerm.caps[i] = g_capStrCapNum[j].CapNum;
} else {
INIT_LOGE("[Init] GetServiceStringCaps, fieldStr = %s, error.\n", fieldStr);
break;
}
if (curServ->servPerm.caps[i] > CAP_LAST_CAP && curServ->servPerm.caps[i] != FULL_CAP) {
// resources will be released by function: ReleaseServiceMem
INIT_LOGE("[Init] GetServiceStringCaps, cap = %d, error.\n", curServ->servPerm.caps[i]);
return SERVICE_FAILURE;
}
}
int ret = i == curServ->servPerm.capsCnt ? SERVICE_SUCCESS : SERVICE_FAILURE;
return ret;
}
int GetServiceCaps(const cJSON* curArrItem, Service* curServ)
{
curServ->servPerm.capsCnt = 0;
curServ->servPerm.caps = NULL;
cJSON* filedJ = cJSON_GetObjectItem(curArrItem, "caps");
if (filedJ == NULL) {
INIT_LOGE("[Init] GetServiceCaps, caps is not found. but maybe ok.\n");
return SERVICE_SUCCESS;
}
if (!cJSON_IsArray(filedJ)) {
INIT_LOGE("[Init] GetServiceCaps, caps is not a list, error.\n");
return SERVICE_FAILURE;
}
// caps array does not exist, means do not need any capability
int capsCnt = cJSON_GetArraySize(filedJ);
if (capsCnt <= 0) {
return SERVICE_SUCCESS;
}
if (capsCnt > MAX_CAPS_CNT_FOR_ONE_SERVICE) {
INIT_LOGE("[Init] GetServiceCaps, too many caps[cnt %d] for one service, should not exceed %d.\n",
capsCnt, MAX_CAPS_CNT_FOR_ONE_SERVICE);
return SERVICE_FAILURE;
}
curServ->servPerm.caps = (unsigned int*)malloc(sizeof(unsigned int) * capsCnt);
if (curServ->servPerm.caps == NULL) {
INIT_LOGE("[Init] GetServiceCaps, malloc error.\n");
return SERVICE_FAILURE;
}
curServ->servPerm.capsCnt = capsCnt;
int i = 0;
for (; i < capsCnt; ++i) { // number form
cJSON* capJ = cJSON_GetArrayItem(filedJ, i);
if (!cJSON_IsNumber(capJ) || cJSON_GetNumberValue(capJ) < 0) {
// resources will be released by function: ReleaseServiceMem
INIT_LOGE("[Init] GetServiceCaps, capJ is not a number or capJ < 0, error.\n");
break;
}
curServ->servPerm.caps[i] = (unsigned int)cJSON_GetNumberValue(capJ);
if (curServ->servPerm.caps[i] > CAP_LAST_CAP && curServ->servPerm.caps[i] != FULL_CAP) { // CAP_LAST_CAP = 37
// resources will be released by function: ReleaseServiceMem
INIT_LOGE("[Init] GetServiceCaps, caps = %d, error.\n", curServ->servPerm.caps[i]);
return SERVICE_FAILURE;
}
}
if (i == capsCnt) {
return SERVICE_SUCCESS;
}
int ret = GetServiceStringCaps(filedJ, curServ);
return ret;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020 Huawei Device Co., Ltd.
* Copyright (c) 2020-2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@ -23,17 +23,24 @@
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/wait.h>
#include <unistd.h>
#ifndef OHOS_LITE
#include <fcntl.h>
#ifndef OHOS_LITE
#include <linux/module.h>
#endif
#include <sys/syscall.h>
#include "init_jobs.h"
#include "init_log.h"
#include "init_service_manager.h"
#include "init_utils.h"
#include "securec.h"
#ifndef OHOS_LITE
#include "property_service.h"
#include "trigger.h"
#endif
#include "init_service_manager.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 +50,9 @@
#define LOADCFG_MAX_LOOP 20 // loadcfg, to prevent to be trapped in infite loop
#define OCTAL_TYPE 8 // 8 means octal to decimal
#define MAX_BUFFER 256
#define AUTHORITY_MAX_SIZE 128
#define CONVERT_MICROSEC_TO_SEC(x) ((x) / 1000 / 1000)
static const char *g_supportCfg[] = {
"/etc/patch.cfg",
"/patch/fstab.cfg",
@ -54,8 +64,21 @@ static const char* g_supportedCmds[] = {
"chmod ",
"chown ",
"mount ",
"export ",
"loadcfg ",
"insmod ",
"rm ",
"rmdir ",
"write ",
"exec ",
"mknode ",
"makedev ",
"symlink ",
"stop ",
"trigger ",
"reset ",
"copy ",
"load_persist_props "
};
void ParseCmdLine(const char* cmdStr, CmdLine* resCmd)
@ -88,93 +111,165 @@ void ParseCmdLine(const char* cmdStr, CmdLine* resCmd)
}
if (!foundAndSucceed) {
printf("[Init][Debug], Cannot parse command: %s\n", cmdStr);
(void)memset_s(resCmd, sizeof(*resCmd), 0, sizeof(*resCmd));
}
}
static void DoStart(const char* cmdContent)
{
printf("[init][Debug] DoStart %s \n", cmdContent);
StartServiceByName(cmdContent);
}
static void DoMkDir(const char* cmdContent)
static void DoStop(const char* cmdContent)
{
mode_t mode = DEFAULT_DIR_MODE;
if (mkdir(cmdContent, mode) != 0) {
if (errno != EEXIST) {
printf("[Init] DoMkDir, failed for %s, err %d.\n", cmdContent, errno);
}
}
StopServiceByName(cmdContent);
}
static void DoChmod(const char* cmdContent)
static void DoReset(const char* cmdContent)
{
// format: 0xxx /xxx/xxx/xxx
if (cmdContent[0] != '0' || cmdContent[MODE_LEN] != ' ' || strlen(cmdContent) <= MODE_LEN + 1) {
printf("[Init] DoChmod, bad format for %s.\n", cmdContent);
return;
}
INIT_LOGE("[init][Debug] DoReset %s \n", cmdContent);
DoStop(cmdContent);
DoStart(cmdContent);
}
for (size_t i = 1; i < MODE_LEN; ++i) {
if (cmdContent[i] > '7' || cmdContent[i] < '0') {
printf("[Init] DoChmod, bad mode format for %s.\n", cmdContent);
return;
}
static void DoCopy(const char* cmdContent)
{
int srcFd = -1;
int dstFd = -1;
int rdLen = 0;
int rtLen = 0;
char buf[MAX_COPY_BUF_SIZE] = {0};
mode_t mode = 0;
struct stat fileStat = {0};
struct CmdArgs *ctx = GetCmd(cmdContent, " ");
if (ctx == NULL || ctx->argv == NULL || ctx->argc != DEFAULT_COPY_ARGS_CNT) {
INIT_LOGE("[Init] DoCopy failed.\n");
goto out;
}
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 (chmod(pathBeginStr, mode) != 0) {
printf("[Init] DoChmod, failed for %s, err %d.\n", cmdContent, errno);
srcFd = open(ctx->argv[0], O_RDONLY);
INIT_ERROR_CHECK(srcFd >= 0, goto out, "[Init] copy open %s fail %d! \n", ctx->argv[0], errno);
INIT_ERROR_CHECK(stat(ctx->argv[0], &fileStat) == 0, goto out, "[Init] stat fail \n");
mode = fileStat.st_mode;
dstFd = open(ctx->argv[1], O_WRONLY | O_TRUNC | O_CREAT, mode);
INIT_ERROR_CHECK(dstFd >= 0, goto out, "[Init] copy open %s fail %d! \n", ctx->argv[1], errno);
while ((rdLen = read(srcFd, buf, sizeof(buf) - 1)) > 0) {
rtLen = write(dstFd, buf, rdLen);
INIT_ERROR_CHECK(rtLen == rdLen, goto out, "[Init] write %s file fail %d! \n", ctx->argv[1], errno);
}
fsync(dstFd);
out:
FreeCmd(&ctx);
ctx = NULL;
close(srcFd);
srcFd = -1;
close(dstFd);
dstFd = -1;
return;
}
static void DoChown(const char* cmdContent)
{
// format: 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: 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);
}
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);
}
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 DoMkDir(const char* cmdContent)
{
// 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 (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;
}
}
if (secondSpace <= firstSpace || firstSpace + 1 == secondSpace || secondSpace == strLen - 1) {
printf("[Init] DoChown, bad format for %s.\n", cmdContent);
return;
}
// only numbers valid
for (size_t i = 0; i < secondSpace; ++i) {
if (i != firstSpace && !isdigit(cmdContent[i])) {
printf("[Init] DoChown, bad format for %s.\n", cmdContent);
return;
if (ctx->argc > 1) {
mode = strtoul(ctx->argv[1], NULL, OCTAL_TYPE);
if (chmod(ctx->argv[0], mode) != 0) {
printf("[Init] DoMkDir failed for %s, err %d.\n", cmdContent, errno);
}
int ownerPos = 2;
int groupPos = 3;
char chownCmdContent[AUTHORITY_MAX_SIZE] = { 0 };
if (snprintf_s(chownCmdContent, AUTHORITY_MAX_SIZE, AUTHORITY_MAX_SIZE - 1, "%s %s %s",
ctx->argv[ownerPos], ctx->argv[groupPos], ctx->argv[0]) == -1) {
INIT_LOGE("[Init] DoMkDir snprintf failed.\n");
goto out;
}
DoChown(chownCmdContent);
}
out:
FreeCmd(&ctx);
return;
}
static void DoChmod(const char* cmdContent)
{
// format: chmod xxxx /xxx/xxx/xxx
struct CmdArgs *ctx = GetCmd(cmdContent, " ");
if (ctx == NULL || ctx->argv == NULL || ctx->argc != 2) {
INIT_LOGE("[Init] DoChmod failed.\n");
goto out;
}
uid_t owner = strtoul(cmdContent, NULL, 0);
const char* groupBegin = cmdContent + firstSpace + 1;
gid_t group = strtoul(groupBegin, NULL, 0);
const char *path = cmdContent + secondSpace + 1;
if (chown(path, owner, group) != 0) {
printf("[Init] DoChown, failed for %s, err %d.\n", cmdContent, errno);
mode_t mode = strtoul(ctx->argv[0], NULL, OCTAL_TYPE);
if (mode == 0) {
INIT_LOGE("[Init] DoChmod, strtoul failed for %s, er %d.\n", cmdContent, errno);
goto out;
}
if (chmod(ctx->argv[1], mode) != 0) {
printf("[Init] DoChmod, failed for %s, err %d.\n", cmdContent, errno);
}
out:
FreeCmd(&ctx);
return;
}
static char* CopySubStr(const char* srcStr, size_t startPos, size_t endPos)
@ -206,7 +301,23 @@ static char* CopySubStr(const char* srcStr, size_t startPos, size_t endPos)
return retStr;
}
static int GetMountFlag(unsigned long* mountflags, const char* targetStr)
static void WaitForFile(const char *source)
{
struct stat sourceInfo;
unsigned int waitTime = 500000;
int maxCount = 10; // 10 means that sleep 10 times, 500ms at a time
int count = 0;
do {
usleep(waitTime);
count++;
} while ((stat(source, &sourceInfo) < 0) && (errno == ENOENT) && (count < maxCount));
if (count == maxCount) {
INIT_LOGE("[Init] wait for file:%s failed after %d.\n", source, maxCount * CONVERT_MICROSEC_TO_SEC(waitTime));
}
return;
}
static int GetMountFlag(unsigned long* mountflags, const char* targetStr, const char *source)
{
if (targetStr == NULL) {
return 0;
@ -220,6 +331,10 @@ static int GetMountFlag(unsigned long* mountflags, const char* targetStr)
(*mountflags) |= MS_NOSUID;
} else if (strncmp(targetStr, "rdonly", strlen("rdonly")) == 0) {
(*mountflags) |= MS_RDONLY;
} else if (strncmp(targetStr, "noatime", strlen("noatime")) == 0) {
(*mountflags) |= MS_NOATIME;
} else if (strncmp(targetStr, "wait", strlen("wait")) == 0) {
WaitForFile(source);
} else {
return 0;
}
@ -296,7 +411,7 @@ static void DoMount(const char* cmdContent)
while (indexOffset < spaceCnt) {
size_t tmpStrEndPos = (indexOffset == spaceCnt - 1) ? strLen : spacePosArr[indexOffset + 1];
char* tmpStr = CopySubStr(cmdContent, spacePosArr[indexOffset] + 1, tmpStrEndPos);
int ret = GetMountFlag(&mountflags, tmpStr);
int ret = GetMountFlag(&mountflags, tmpStr, source);
free(tmpStr);
tmpStr = NULL;
@ -487,30 +602,291 @@ static void DoLoadCfg(const char *path)
fclose(fp);
}
void DoCmd(const CmdLine* curCmd)
static void DoWrite(const char *cmdContent)
{
if (curCmd == NULL) {
return;
// 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;
}
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);
#ifndef OHOS_LITE
} else if (strncmp(curCmd->name, "insmod ", strlen("insmod ")) == 0) {
DoInsmod(curCmd->cmdContent);
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 {
printf("[Init] DoCmd, unknown cmd name %s.\n", curCmd->name);
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)
{
// 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);
}
void DoCmdByName(const char *name, const char *cmdContent)
{
if (name == NULL || cmdContent == NULL) {
return;
}
if (strncmp(name, "start ", strlen("start ")) == 0) {
DoStart(cmdContent);
} else if (strncmp(name, "mkdir ", strlen("mkdir ")) == 0) {
DoMkDir(cmdContent);
} else if (strncmp(name, "stop ", strlen("stop ")) == 0) {
DoStop(cmdContent);
} else if (strncmp(name, "reset ", strlen("reset ")) == 0) {
DoReset(cmdContent);
} else if (strncmp(name, "copy ", strlen("copy ")) == 0) {
DoCopy(cmdContent);
} else if (strncmp(name, "chmod ", strlen("chmod ")) == 0) {
DoChmod(cmdContent);
} else if (strncmp(name, "chown ", strlen("chown ")) == 0) {
DoChown(cmdContent);
} else if (strncmp(name, "mount ", strlen("mount ")) == 0) {
DoMount(cmdContent);
} else if (strncmp(name, "write ", strlen("write ")) == 0) {
DoWrite(cmdContent);
} else if (strncmp(name, "rmdir ", strlen("rmdir ")) == 0) {
DoRmdir(cmdContent);
} else if (strncmp(name, "rm ", strlen("rm ")) == 0) {
DoRm(cmdContent);
} else if (strncmp(name, "export ", strlen("export ")) == 0) {
DoExport(cmdContent);
} else if (strncmp(name, "exec ", strlen("exec ")) == 0) {
DoExec(cmdContent);
#ifndef __LITEOS__
} else if (strncmp(name, "symlink ", strlen("symlink ")) == 0) {
DoSymlink(cmdContent);
} else if (strncmp(name, "makedev ", strlen("makedev ")) == 0) {
DoMakeDevice(cmdContent);
} else if (strncmp(name, "mknode ", strlen("mknode ")) == 0) {
DoMakeNode(cmdContent);
#endif
} else if (strncmp(name, "loadcfg ", strlen("loadcfg ")) == 0) {
DoLoadCfg(cmdContent);
#ifndef OHOS_LITE
} else if (strncmp(name, "insmod ", strlen("insmod ")) == 0) {
DoInsmod(cmdContent);
} else if (strncmp(name, "trigger ", strlen("trigger ")) == 0) {
printf("[Init][Debug], ready to trigger job: %s\n", name);
DoTriggerExec(cmdContent);
} else if (strncmp(name, "load_persist_props ", strlen("load_persist_props ")) == 0) {
LoadPersistProperties();
#endif
} else {
printf("[Init] DoCmd, unknown cmd name %s.\n", 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;
}

49
services/src/init_import.c Executable file
View File

@ -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 <stdio.h>
#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;
}

View File

@ -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;
}
}
}

61
services/src/init_log.c Executable file
View File

@ -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 <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#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);
}

View File

@ -16,6 +16,7 @@
#include "init_read_cfg.h"
#include <errno.h>
#include <dirent.h>
#include <linux/capability.h>
#include <stdio.h>
#include <stdlib.h>
@ -23,9 +24,16 @@
#include <sys/stat.h>
#include <unistd.h>
#include "init_import.h"
#include "init_jobs.h"
#include "init_log.h"
#include "init_perms.h"
#include "init_service_manager.h"
#include "init_utils.h"
#ifndef OHOS_LITE
#include "trigger.h"
#endif
#include "securec.h"
#ifndef __LINUX__
#ifdef OHOS_LITE
@ -33,368 +41,91 @@
#endif
#endif
static const long MAX_JSON_FILE_LEN = 102400; // max init.cfg size 100KB
static const int MAX_PATH_ARGS_CNT = 20; // max path and args count
static const int MAX_ONE_ARG_LEN = 64; // max length of one param/path
#define MAX_SERVICES_CNT_IN_FILE 100
#define MAX_CAPS_CNT_FOR_ONE_SERVICE 100
#define UID_STR_IN_CFG "uid"
#define GID_STR_IN_CFG "gid"
#define ONCE_STR_IN_CFG "once"
#define IMPORTANT_STR_IN_CFG "importance"
#define BIN_SH_NOT_ALLOWED "/bin/sh"
static char* ReadFileToBuf()
#define FILE_NAME_MAX_SIZE 100
static void ParseInitCfgContents(cJSON *root)
{
char* buffer = NULL;
FILE* fd = NULL;
struct stat fileStat = {0};
do {
if (stat(INIT_CONFIGURATION_FILE, &fileStat) != 0 ||
fileStat.st_size <= 0 || fileStat.st_size > MAX_JSON_FILE_LEN) {
break;
}
// parse services
ParseAllServices(root);
fd = fopen(INIT_CONFIGURATION_FILE, "r");
if (fd == NULL) {
break;
}
// parse jobs
ParseAllJobs(root);
buffer = (char*)malloc(fileStat.st_size + 1);
if (buffer == NULL) {
break;
}
#ifndef OHOS_LITE
ParseTriggerConfig(root);
#endif
if (fread(buffer, fileStat.st_size, 1, fd) != 1) {
free(buffer);
buffer = NULL;
break;
}
buffer[fileStat.st_size] = '\0';
} while (0);
if (fd != NULL) {
fclose(fd);
fd = NULL;
}
return buffer;
// parse imports
ParseAllImports(root);
}
static cJSON* GetArrItem(const cJSON* fileRoot, int* arrSize, const char* arrName)
void ParseInitCfg(const char *configFile)
{
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)
{
curServ->servPerm.capsCnt = 0;
curServ->servPerm.caps = NULL;
cJSON* filedJ = cJSON_GetObjectItem(curArrItem, "caps");
if (!cJSON_IsArray(filedJ)) {
return SERVICE_FAILURE;
}
// caps array does not exist, means do not need any capability
int capsCnt = cJSON_GetArraySize(filedJ);
if (capsCnt <= 0) {
return SERVICE_SUCCESS;
}
if (capsCnt > MAX_CAPS_CNT_FOR_ONE_SERVICE) {
printf("[Init] GetServiceCaps, too many caps[cnt %d] for one service, should not exceed %d.\n",
capsCnt, MAX_CAPS_CNT_FOR_ONE_SERVICE);
return SERVICE_FAILURE;
}
curServ->servPerm.caps = (unsigned int*)malloc(sizeof(unsigned int) * capsCnt);
if (curServ->servPerm.caps == NULL) {
return SERVICE_FAILURE;
}
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;
}
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;
}
static void ParseAllServices(const cJSON* fileRoot)
{
int servArrSize = 0;
cJSON* serviceArr = GetArrItem(fileRoot, &servArrSize, SERVICES_ARR_NAME_IN_JSON);
if (serviceArr == NULL) {
printf("[Init] InitReadCfg, get array %s failed.\n", SERVICES_ARR_NAME_IN_JSON);
if (configFile == NULL || *configFile == '\0') {
printf("[Init] Invalid config file\n");
return;
}
if (servArrSize > MAX_SERVICES_CNT_IN_FILE) {
printf("[Init] InitReadCfg, too many services[cnt %d] detected, should not exceed %d.\n",
servArrSize, MAX_SERVICES_CNT_IN_FILE);
return;
}
Service* retServices = (Service*)malloc(sizeof(Service) * servArrSize);
if (retServices == NULL) {
printf("[Init] InitReadCfg, malloc for %s arr failed! %d.\n", SERVICES_ARR_NAME_IN_JSON, servArrSize);
return;
}
if (memset_s(retServices, sizeof(Service) * servArrSize, 0, sizeof(Service) * servArrSize) != EOK) {
free(retServices);
retServices = NULL;
return;
}
for (int i = 0; i < servArrSize; ++i) {
cJSON* curItem = cJSON_GetArrayItem(serviceArr, i);
if (GetServiceName(curItem, &retServices[i]) != SERVICE_SUCCESS ||
GetServicePathAndArgs(curItem, &retServices[i]) != SERVICE_SUCCESS ||
GetServiceNumber(curItem, &retServices[i], UID_STR_IN_CFG) != SERVICE_SUCCESS ||
GetServiceGids(curItem, &retServices[i]) != SERVICE_SUCCESS ||
GetServiceNumber(curItem, &retServices[i], ONCE_STR_IN_CFG) != SERVICE_SUCCESS ||
GetServiceNumber(curItem, &retServices[i], IMPORTANT_STR_IN_CFG) != SERVICE_SUCCESS ||
GetServiceCaps(curItem, &retServices[i]) != SERVICE_SUCCESS) {
// release resources if it fails
ReleaseServiceMem(&retServices[i]);
retServices[i].attribute |= SERVICE_ATTR_INVALID;
printf("[Init] InitReadCfg, parse information for service %d failed.\n", i);
continue;
}
}
RegisterServices(retServices, servArrSize);
}
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;
}
static void ReadCfgs(const char *dirPath)
{
DIR *pDir = opendir(dirPath);
if (pDir == NULL) {
INIT_LOGE("[Init], ParseCfgs open cfg dir :%s failed.%d\n", dirPath, errno);
return;
}
struct dirent *dp;
while ((dp = readdir(pDir)) != NULL) {
char fileName[FILE_NAME_MAX_SIZE];
if (snprintf_s(fileName, FILE_NAME_MAX_SIZE, FILE_NAME_MAX_SIZE - 1, "%s/%s", dirPath, dp->d_name) == -1) {
INIT_LOGE("[Init], ParseCfgs snprintf_s failed.\n");
closedir(pDir);
return;
}
struct stat st;
if (stat(fileName, &st) == 0) {
if (strstr(dp->d_name, ".cfg") == NULL) {
continue;
}
INIT_LOGE("[Init], fileName :%s.\n", fileName);
ParseInitCfg(fileName);
}
}
closedir(pDir);
return;
}
static void ParseOtherCfgs()
{
ReadCfgs("/system/etc/init");
return;
}
void InitReadCfg()
{
ParseInitCfg(INIT_CONFIGURATION_FILE);
ParseOtherCfgs();
printf("[init], Parse init config file done.\n");
DumpAllServices();
// DumpAllJobs();
// do jobs
DoJob("pre-init");
#ifndef __LINUX__

View File

@ -19,12 +19,16 @@
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include "init_adapter.h"
#include "init_cmds.h"
#include "init_log.h"
#include "init_perms.h"
#include "init_service_socket.h"
#define CAP_NUM 2
@ -33,6 +37,13 @@ static const int CRASH_TIME_LIMIT = 240;
// maximum number of crashes within time CRASH_TIME_LIMIT for one service
static const int CRASH_COUNT_LIMIT = 4;
// 240 seconds, 4 minutes
static const int CRITICAL_CRASH_TIME_LIMIT = 240;
// maximum number of crashes within time CRITICAL_CRASH_TIME_LIMIT for one service
static const int CRITICAL_CRASH_COUNT_LIMIT = 4;
static const int MAX_PID_STRING_LENGTH = 50;
static int SetAllAmbientCapability()
{
for (int i = 0; i <= CAP_LAST_CAP; ++i) {
@ -48,12 +59,17 @@ static int SetPerms(const Service *service)
if (KeepCapability() != 0) {
return SERVICE_FAILURE;
}
if (setgroups(service->servPerm.gidsCnt, service->servPerm.gIDs) != 0) {
if (setgroups(service->servPerm.gIDCnt, service->servPerm.gIDArray) != 0) {
INIT_LOGE("[Init] SetPerms, setgroups failed. errno = %d, gIDCnt=%d\n", errno, service->servPerm.gIDCnt);
return SERVICE_FAILURE;
}
if (setuid(service->servPerm.uID) != 0) {
return SERVICE_FAILURE;
if (service->servPerm.uID != 0) {
if (setuid(service->servPerm.uID) != 0) {
printf("[Init] setuid of service: %s failed, uid = %d\n", service->name, service->servPerm.uID);
return SERVICE_FAILURE;
}
}
// umask call always succeeds and return the previous mask value which is not needed here
@ -79,6 +95,7 @@ static int SetPerms(const Service *service)
}
if (capset(&capHeader, capData) != 0) {
printf("[Init][Debug], capset faild for service: %s, error: %d\n", service->name, errno);
return SERVICE_FAILURE;
}
for (unsigned int i = 0; i < service->servPerm.capsCnt; ++i) {
@ -86,6 +103,7 @@ static int SetPerms(const Service *service)
return SetAllAmbientCapability();
}
if (SetAmbientCapability(service->servPerm.caps[i]) != 0) {
printf("[Init][Debug], SetAmbientCapability faild for service: %s\n", service->name);
return SERVICE_FAILURE;
}
}
@ -112,19 +130,56 @@ int ServiceStart(Service *service)
service->name, service->pathArgs[0]);
return SERVICE_FAILURE;
}
int ret = 0;
int pid = fork();
if (pid == 0) {
if (service->socketCfg != NULL) { // start socket service
printf("[init] Create socket \n");
ret = DoCreateSocket(service->socketCfg);
if (ret < 0) {
return SERVICE_FAILURE;
}
}
// permissions
if (SetPerms(service) != SERVICE_SUCCESS) {
printf("[Init] service %s exit! set perms failed! err %d.\n", service->name, errno);
INIT_LOGE("[Init] service %s exit! set perms failed! err %d.\n", service->name, errno);
_exit(0x7f); // 0x7f: user specified
}
char pidString[MAX_PID_STRING_LENGTH]; // writepid
pid_t childPid = getpid();
if (snprintf(pidString, MAX_PID_STRING_LENGTH, "%d", childPid) <= 0) {
INIT_LOGE("[Init] start service writepid sprintf failed.\n");
return SERVICE_FAILURE;
}
for (int i = 0; i < MAX_WRITEPID_FILES; i++) {
if (service->writepidFiles[i] == NULL) {
continue;
}
FILE *fd = fopen(service->writepidFiles[i], "wb");
if (fd == NULL) {
INIT_LOGE("[Init] start service writepidFiles %s invalid.\n", service->writepidFiles[i]);
continue;
}
if (fwrite(pidString, 1, strlen(pidString), fd) != strlen(pidString)) {
INIT_LOGE("[Init] start service writepid error.file:%s pid:%s\n", service->writepidFiles[i], pidString);
}
fclose(fd);
printf("[Init] ServiceStart writepid filename=%s, childPid=%s, ok\n", service->writepidFiles[i], pidString);
}
printf("[init] service->name is %s \n", service->name);
#ifndef OHOS_LITE
// L2 Can not be reset env
if (execv(service->pathArgs[0], service->pathArgs) != 0) {
printf("[Init] service %s execve failed! err %d.\n", service->name, errno);
}
#else
char* env[] = {"LD_LIBRARY_PATH=/storage/app/libs", NULL};
if (execve(service->pathArgs[0], service->pathArgs, env) != 0) {
printf("[Init] service %s execve failed! err %d.\n", service->name, errno);
}
#endif
_exit(0x7f); // 0x7f: user specified
} else if (pid < 0) {
printf("[Init] start service %s fork failed!\n", service->name);
@ -158,6 +213,45 @@ int ServiceStop(Service *service)
return SERVICE_SUCCESS;
}
// the service need to be restarted, if it crashed more than 4 times in 4 minutes
void CheckCritical(Service *service)
{
if (service->attribute & SERVICE_ATTR_CRITICAL) { // critical
// crash time and count check
time_t curTime = time(NULL);
if (service->criticalCrashCnt == 0) {
service->firstCriticalCrashTime = curTime;
++service->criticalCrashCnt;
} else if (difftime(curTime, service->firstCriticalCrashTime) > CRITICAL_CRASH_TIME_LIMIT) {
service->firstCriticalCrashTime = curTime;
service->criticalCrashCnt = 1;
} else {
++service->criticalCrashCnt;
if (service->criticalCrashCnt > CRITICAL_CRASH_COUNT_LIMIT) {
INIT_LOGE("[Init] reap critical service %s, crash too many times! Need reboot!\n", service->name);
RebootSystem();
}
}
}
}
static int ExecRestartCmd(const Service *service)
{
printf("[init] ExecRestartCmd \n");
if ((service == NULL) || (service->onRestart == NULL) || (service->onRestart->cmdLine == NULL)) {
return SERVICE_FAILURE;
}
for (int i = 0; i < service->onRestart->cmdNum; i++) {
printf("[init] SetOnRestart cmdLine->name %s cmdLine->cmdContent %s \n", service->onRestart->cmdLine[i].name,
service->onRestart->cmdLine[i].cmdContent);
DoCmd(&service->onRestart->cmdLine[i]);
}
free(service->onRestart->cmdLine);
free(service->onRestart);
return SERVICE_SUCCESS;
}
void ServiceReap(Service *service)
{
if (service == NULL) {
@ -166,7 +260,6 @@ void ServiceReap(Service *service)
}
service->pid = -1;
// stopped by system-init itself, no need to restart even if it is not one-shot service
if (service->attribute & SERVICE_ATTR_NEED_STOP) {
service->attribute &= (~SERVICE_ATTR_NEED_STOP);
@ -203,9 +296,17 @@ void ServiceReap(Service *service)
}
}
int ret = ServiceStart(service);
CheckCritical(service);
int ret = 0;
if (service->onRestart != NULL) {
ret = ExecRestartCmd(service);
if (ret != SERVICE_SUCCESS) {
printf("[Init] SetOnRestart fail \n");
}
}
ret = ServiceStart(service);
if (ret != SERVICE_SUCCESS) {
printf("[Init] reap service %s start failed!\n", service->name);
INIT_LOGE("[Init] reap service %s start failed!\n", service->name);
}
service->attribute &= (~SERVICE_ATTR_NEED_RESTART);

View File

@ -1,91 +1,785 @@
/*
* Copyright (c) 2020 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "init_service_manager.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "init_adapter.h"
#include "init_jobs.h"
// All serivce processes that init will fork+exec.
static Service* g_services = NULL;
static int g_servicesCnt = 0;
void RegisterServices(Service* services, int servicesCnt)
{
g_services = services;
g_servicesCnt = servicesCnt;
}
static int FindServiceByName(const char* servName)
{
if (servName == NULL) {
return -1;
}
for (int i = 0; i < g_servicesCnt; ++i) {
if (strlen(g_services[i].name) == strlen(servName) &&
strncmp(g_services[i].name, servName, strlen(g_services[i].name)) == 0) {
return i;
}
}
return -1;
}
void StartServiceByName(const char* servName)
{
// find service by name
int servIdx = FindServiceByName(servName);
if (servIdx < 0) {
printf("[Init] StartServiceByName, cannot find service %s.\n", servName);
return;
}
if (ServiceStart(&g_services[servIdx]) != SERVICE_SUCCESS) {
printf("[Init] StartServiceByName, service %s start failed!\n", g_services[servIdx].name);
}
return;
}
void StopAllServices()
{
for (int i = 0; i < g_servicesCnt; i++) {
if (ServiceStop(&g_services[i]) != SERVICE_SUCCESS) {
printf("[Init] StopAllServices, service %s stop failed!\n", g_services[i].name);
}
}
}
void ReapServiceByPID(int pid)
{
for (int i = 0; i < g_servicesCnt; i++) {
if (g_services[i].pid == pid) {
if (g_services[i].attribute & SERVICE_ATTR_IMPORTANT) {
// important process exit, need to reboot system
g_services[i].pid = -1;
StopAllServices();
RebootSystem();
}
ServiceReap(&g_services[i]);
break;
}
}
}
/*
* Copyright (c) 2020 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "init_service_manager.h"
#include <ctype.h>
#include <limits.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include "cJSON.h"
#include "init_adapter.h"
#include "init_jobs.h"
#include "init_log.h"
#include "init_perms.h"
#include "init_read_cfg.h"
#include "init_capability.h"
#include "init_service_socket.h"
#include "init_utils.h"
#include "securec.h"
// All serivce processes that init will fork+exec.
static Service* g_services = NULL;
static int g_servicesCnt = 0;
void DumpAllServices()
{
printf("[Init] Ready to dump all services:\n");
printf("[Init] total service number: %d\n", g_servicesCnt);
for (int i = 0; i < g_servicesCnt; i++) {
printf("\tservice name: [%s]\n", g_services[i].name);
printf("\tpath :");
for (int j = 0; j < g_services[i].pathArgsCnt; j++) {
printf(" %s", g_services[i].pathArgs[j]);
}
printf("\n");
}
printf("[Init] Dump all services finished\n");
}
void RegisterServices(Service* services, int servicesCnt)
{
g_services = services;
g_servicesCnt += servicesCnt;
}
static void ReleaseServiceMem(Service* curServ)
{
if (curServ->pathArgs != NULL) {
for (int i = 0; i < curServ->pathArgsCnt; ++i) {
if (curServ->pathArgs[i] != NULL) {
free(curServ->pathArgs[i]);
curServ->pathArgs[i] = NULL;
}
}
free(curServ->pathArgs);
curServ->pathArgs = NULL;
}
curServ->pathArgsCnt = 0;
if (curServ->servPerm.caps != NULL) {
free(curServ->servPerm.caps);
curServ->servPerm.caps = NULL;
}
curServ->servPerm.capsCnt = 0;
for (int i = 0; i < MAX_WRITEPID_FILES; i++) {
if (curServ->writepidFiles[i] != NULL) {
free(curServ->writepidFiles[i]);
curServ->writepidFiles[i] = NULL;
}
}
if (curServ->servPerm.gIDArray != NULL) {
free(curServ->servPerm.gIDArray);
curServ->servPerm.gIDArray = NULL;
}
curServ->servPerm.gIDCnt = 0;
}
static int GetServiceName(const cJSON* curArrItem, Service* curServ)
{
char* fieldStr = cJSON_GetStringValue(cJSON_GetObjectItem(curArrItem, "name"));
if (fieldStr == NULL) {
INIT_LOGE("[init] GetServiceName cJSON_GetStringValue error\n");
return SERVICE_FAILURE;
}
size_t strLen = strlen(fieldStr);
if (strLen == 0 || strLen > MAX_SERVICE_NAME) {
INIT_LOGE("[init] GetServiceName strLen = %d, error\n", strLen);
return SERVICE_FAILURE;
}
if (memcpy_s(curServ->name, MAX_SERVICE_NAME, fieldStr, strLen) != EOK) {
INIT_LOGE("[init] GetServiceName memcpy_s error\n");
return SERVICE_FAILURE;
}
curServ->name[strLen] = '\0';
return SERVICE_SUCCESS;
}
static int IsForbidden(const char* fieldStr)
{
size_t fieldLen = strlen(fieldStr);
size_t forbidStrLen = strlen(BIN_SH_NOT_ALLOWED);
if (fieldLen == forbidStrLen) {
if (strncmp(fieldStr, BIN_SH_NOT_ALLOWED, fieldLen) == 0) {
return 1;
}
return 0;
} else if (fieldLen > forbidStrLen) {
// "/bin/shxxxx" is valid but "/bin/sh xxxx" is invalid
if (strncmp(fieldStr, BIN_SH_NOT_ALLOWED, forbidStrLen) == 0) {
if (fieldStr[forbidStrLen] == ' ') {
return 1;
}
}
return 0;
} else {
return 0;
}
}
// TODO: move this function to common files
static cJSON* GetArrItem(const cJSON* fileRoot, int* arrSize, const char* arrName)
{
cJSON* arrItem = cJSON_GetObjectItemCaseSensitive(fileRoot, arrName);
if (!cJSON_IsArray(arrItem)) {
printf("[Init] GetArrItem, item %s is not an array!\n", arrName);
return NULL;
}
*arrSize = cJSON_GetArraySize(arrItem);
if (*arrSize <= 0) {
return NULL;
}
return arrItem;
}
static int GetWritepidStrings(const cJSON *curArrItem, Service *curServ) // writepid
{
int writepidCnt = 0;
cJSON* filedJ = GetArrItem(curArrItem, &writepidCnt, "writepid");
if (writepidCnt <= 0) { // not item is ok.
return SERVICE_SUCCESS;
}
if (writepidCnt > MAX_WRITEPID_FILES) {
INIT_LOGE("[Init] GetWritepidStrings, too many writepid[cnt %d] for one service, should not exceed %d.\n",
writepidCnt, MAX_WRITEPID_FILES);
return SERVICE_FAILURE;
}
for (int i = 0; i < writepidCnt; ++i) {
if (!cJSON_GetArrayItem(filedJ, i) || !cJSON_GetStringValue(cJSON_GetArrayItem(filedJ, i))
|| strlen(cJSON_GetStringValue(cJSON_GetArrayItem(filedJ, i))) <= 0) { // check all errors
INIT_LOGE("[Init] GetWritepidStrings, parse item[%d] error.\n", i);
return SERVICE_FAILURE;
}
char *fieldStr = cJSON_GetStringValue(cJSON_GetArrayItem(filedJ, i));
size_t strLen = strlen(fieldStr);
curServ->writepidFiles[i] = (char *)malloc(sizeof(char) * strLen + 1);
if (curServ->writepidFiles[i] == NULL) {
INIT_LOGE("[Init] GetWritepidStrings, malloc item[%d] error.\n", i);
return SERVICE_FAILURE;
}
if (memcpy_s(curServ->writepidFiles[i], strLen + 1, fieldStr, strLen) != EOK) {
INIT_LOGE("[Init] GetWritepidStrings, memcpy_s error.\n");
return SERVICE_FAILURE;
}
curServ->writepidFiles[i][strLen] = '\0';
}
return SERVICE_SUCCESS;
}
static int GetGidOneItem(const cJSON *curArrItem, Service *curServ) // gid one item
{
cJSON* filedJ = cJSON_GetObjectItem(curArrItem, GID_STR_IN_CFG);
if (filedJ == NULL) {
INIT_LOGE("[Init] GetGidOneItem, gid is not an item.\n");
return SERVICE_FAILURE; // not found
}
curServ->servPerm.gIDCnt = 1;
curServ->servPerm.gIDArray = (gid_t *)malloc(sizeof(gid_t));
if (curServ->servPerm.gIDArray == NULL) {
INIT_LOGE("[Init] GetGidOneItem, can't malloc, error.\n");
return SERVICE_FAILURE;
}
if (cJSON_IsString(filedJ)) {
char* fieldStr = cJSON_GetStringValue(filedJ);
gid_t gID = DecodeUid(fieldStr);
if (gID == (gid_t)(-1)) {
INIT_LOGE("[Init] GetGidOneItem, DecodeUid %s error.\n", fieldStr);
return SERVICE_FAILURE;
}
curServ->servPerm.gIDArray[0] = gID;
return SERVICE_SUCCESS;
}
if (cJSON_IsNumber(filedJ)) {
gid_t gID = (int)cJSON_GetNumberValue(filedJ);
if (gID < 0) {
INIT_LOGE("[Init] GetGidOneItem, gID = %d error.\n", gID);
return SERVICE_FAILURE;
}
curServ->servPerm.gIDArray[0] = gID;
return SERVICE_SUCCESS;
}
INIT_LOGE("[Init] GetGidOneItem, this gid is neither a string nor a number, error.\n");
return SERVICE_FAILURE;
}
static int GetGidArray(const cJSON *curArrItem, Service *curServ) // gid array
{
int gIDCnt = 0;
cJSON* filedJ = GetArrItem(curArrItem, &gIDCnt, GID_STR_IN_CFG); // "gid" must have 1 item.
if (gIDCnt <= 0) { // not a array, but maybe a item?
INIT_LOGE("[Init] GetGidArray, gid is not a list.\n");
return GetGidOneItem(curArrItem, curServ);
}
if (gIDCnt > NGROUPS_MAX + 1) {
INIT_LOGE("[Init] GetGidArray, too many gids[cnt %d] for one service, should not exceed %d.\n",
gIDCnt, NGROUPS_MAX + 1);
return SERVICE_FAILURE;
}
curServ->servPerm.gIDArray = (gid_t *)malloc(sizeof(gid_t) * gIDCnt);
if (curServ->servPerm.gIDArray == NULL) {
INIT_LOGE("[init] GetGidArray malloc error\n");
return SERVICE_FAILURE;
}
curServ->servPerm.gIDCnt = gIDCnt;
int i = 0;
for (; i < gIDCnt; ++i) {
if (cJSON_GetArrayItem(filedJ, i) == NULL || !cJSON_GetStringValue(cJSON_GetArrayItem(filedJ, i))
|| strlen(cJSON_GetStringValue(cJSON_GetArrayItem(filedJ, i))) <= 0) { // check all errors
INIT_LOGE("[Init] GetGidArray, parse item[%d] as string, error.\n", i);
break;
}
char* fieldStr = cJSON_GetStringValue(cJSON_GetArrayItem(filedJ, i));
gid_t gID = DecodeUid(fieldStr);
if ((gID) == (gid_t)(-1)) {
INIT_LOGE("[Init] GetGidArray, DecodeUid item[%d] error.\n", i);
return SERVICE_FAILURE;
}
curServ->servPerm.gIDArray[i] = gID;
}
if (i == gIDCnt) {
return SERVICE_SUCCESS;
}
for (i = 0; i < gIDCnt; ++i) {
if (cJSON_GetArrayItem(filedJ, i) == NULL || !cJSON_IsNumber(cJSON_GetArrayItem(filedJ, i))) {
INIT_LOGE("[Init] GetGidArray, parse item[%d] as number, error.\n", i);
break;
}
gid_t gID = (int)cJSON_GetNumberValue(cJSON_GetArrayItem(filedJ, i));
if (gID < 0) {
INIT_LOGE("[init] GetGidArray gID = %d, error\n", gID);
break;
}
curServ->servPerm.gIDArray[i] = gID;
}
int ret = i == gIDCnt ? SERVICE_SUCCESS : SERVICE_FAILURE;
return ret;
}
static int GetServicePathAndArgs(const cJSON* curArrItem, Service* curServ)
{
cJSON* pathItem = cJSON_GetObjectItem(curArrItem, "path");
if (!cJSON_IsArray(pathItem)) {
INIT_LOGE("[init] GetServicePathAndArgs path item not found or not a array\n");
return SERVICE_FAILURE;
}
int arrSize = cJSON_GetArraySize(pathItem);
if (arrSize <= 0 || arrSize > MAX_PATH_ARGS_CNT) { // array size invalid
INIT_LOGE("[init] GetServicePathAndArgs arrSize = %d, error\n", arrSize);
return SERVICE_FAILURE;
}
curServ->pathArgs = (char**)malloc((arrSize + 1) * sizeof(char*));
if (curServ->pathArgs == NULL) {
INIT_LOGE("[init] GetServicePathAndArgs malloc 1 error\n");
return SERVICE_FAILURE;
}
for (int i = 0; i < arrSize + 1; ++i) {
curServ->pathArgs[i] = NULL;
}
curServ->pathArgsCnt = arrSize + 1;
for (int i = 0; i < arrSize; ++i) {
char* curParam = cJSON_GetStringValue(cJSON_GetArrayItem(pathItem, i));
if (curParam == NULL || strlen(curParam) > MAX_ONE_ARG_LEN) {
// resources will be released by function: ReleaseServiceMem
if (curParam == NULL) {
INIT_LOGE("[init] GetServicePathAndArgs curParam == NULL, error\n");
} else {
INIT_LOGE("[init] GetServicePathAndArgs strlen = %d, error\n", strlen(curParam));
}
return SERVICE_FAILURE;
}
if (i == 0 && IsForbidden(curParam)) {
// resources will be released by function: ReleaseServiceMem
INIT_LOGE("[init] GetServicePathAndArgs i == 0 && IsForbidden, error\n");
return SERVICE_FAILURE;
}
size_t paramLen = strlen(curParam);
curServ->pathArgs[i] = (char*)malloc(paramLen + 1);
if (curServ->pathArgs[i] == NULL) {
// resources will be released by function: ReleaseServiceMem
INIT_LOGE("[init] GetServicePathAndArgs i == 0 && IsForbidden, error\n");
return SERVICE_FAILURE;
}
if (memcpy_s(curServ->pathArgs[i], paramLen + 1, curParam, paramLen) != EOK) {
// resources will be released by function: ReleaseServiceMem
INIT_LOGE("[init] GetServicePathAndArgs malloc 2 error.\n");
return SERVICE_FAILURE;
}
curServ->pathArgs[i][paramLen] = '\0';
}
return SERVICE_SUCCESS;
}
static int GetServiceNumber(const cJSON* curArrItem, Service* curServ, const char* targetField)
{
cJSON* filedJ = cJSON_GetObjectItem(curArrItem, targetField);
if (filedJ == NULL && (strncmp(targetField, CRITICAL_STR_IN_CFG, strlen(CRITICAL_STR_IN_CFG)) == 0
|| strncmp(targetField, DISABLED_STR_IN_CFG, strlen(DISABLED_STR_IN_CFG)) == 0
|| strncmp(targetField, ONCE_STR_IN_CFG, strlen(ONCE_STR_IN_CFG)) == 0
|| strncmp(targetField, IMPORTANT_STR_IN_CFG, strlen(IMPORTANT_STR_IN_CFG)) == 0)) {
return SERVICE_SUCCESS; // not found "critical","disabled","once","importance" item is ok
}
if (!cJSON_IsNumber(filedJ)) {
INIT_LOGE("[Init] GetServiceNumber, %s is null or is not a number, error.\n", targetField);
return SERVICE_FAILURE;
}
int value = (int)cJSON_GetNumberValue(filedJ);
if (value < 0) {
INIT_LOGE("[Init] GetServiceNumber, value = %d, error.\n", value);
return SERVICE_FAILURE;
}
if (strncmp(targetField, ONCE_STR_IN_CFG, strlen(ONCE_STR_IN_CFG)) == 0) {
if (value != 0) {
curServ->attribute |= SERVICE_ATTR_ONCE;
}
} else if (strncmp(targetField, IMPORTANT_STR_IN_CFG, strlen(IMPORTANT_STR_IN_CFG)) == 0) {
if (value != 0) {
curServ->attribute |= SERVICE_ATTR_IMPORTANT;
}
} else if (strncmp(targetField, CRITICAL_STR_IN_CFG, strlen(CRITICAL_STR_IN_CFG)) == 0) { // set critical
curServ->attribute &= ~SERVICE_ATTR_CRITICAL;
if (value == 1) {
curServ->attribute |= SERVICE_ATTR_CRITICAL;
}
} else if (strncmp(targetField, DISABLED_STR_IN_CFG, strlen(DISABLED_STR_IN_CFG)) == 0) { // set disabled
curServ->attribute &= ~SERVICE_ATTR_DISABLED;
if (value == 1) {
curServ->attribute |= SERVICE_ATTR_DISABLED;
}
} else {
INIT_LOGE("[Init] GetServiceNumber, item = %s, not expected, error.\n", targetField);
return SERVICE_FAILURE;
}
return SERVICE_SUCCESS;
}
static int GetUidStringNumber(const cJSON *curArrItem, Service *curServ)
{
cJSON* filedJ = cJSON_GetObjectItem(curArrItem, UID_STR_IN_CFG);
if (filedJ == NULL) {
INIT_LOGE("[Init] GetUidStringNumber, %s not found, error.\n", UID_STR_IN_CFG);
return SERVICE_FAILURE; // not found
}
if (cJSON_IsString(filedJ)) {
char* fieldStr = cJSON_GetStringValue(filedJ);
int uID = DecodeUid(fieldStr);
if (uID < 0) {
INIT_LOGE("[Init] GetUidStringNumber, DecodeUid %s error.\n", fieldStr);
return SERVICE_FAILURE;
}
curServ->servPerm.uID = uID;
return SERVICE_SUCCESS;
}
if (cJSON_IsNumber(filedJ)) {
int uID = (int)cJSON_GetNumberValue(filedJ);
if (uID < 0) {
INIT_LOGE("[Init] GetUidStringNumber, uID = %d error.\n", uID);
return SERVICE_FAILURE;
}
curServ->servPerm.uID = uID;
return SERVICE_SUCCESS;
}
INIT_LOGE("[Init] GetUidStringNumber, this uid is neither a string nor a number, error.\n");
return SERVICE_FAILURE;
}
static int SplitString(char *srcPtr, char **dstPtr)
{
if (!srcPtr) {
return -1;
}
char *buf = NULL;
dstPtr[0] = strtok_r(srcPtr, " ", &buf);
int i = 0;
while (dstPtr[i])
{
i++;
dstPtr[i] = strtok_r(NULL, " ", &buf);
}
dstPtr[i] = "\0";
int num = i;
for (int j = 0; j < num; j++)
{
printf("dstPtr[%d] is %s \n", j, dstPtr[j]);
}
return num;
}
#define MAX_SOCK_NAME_LEN 16
#define SOCK_OPT_NUMS 6
enum SockOptionTab
{
SERVICE_SOCK_NAME = 0,
SERVICE_SOCK_TYPE,
SERVICE_SOCK_PERM,
SERVICE_SOCK_UID,
SERVICE_SOCK_GID,
SERVICE_SOCK_SETOPT
};
static int ParseServiceSocket(char **opt, const int optNum, struct ServiceSocket *sockopt)
{
printf("[init] ParseServiceSocket\n");
if (optNum != SOCK_OPT_NUMS) {
return -1;
}
sockopt->name = (char *)calloc(MAX_SOCK_NAME_LEN, sizeof(char));
if (sockopt->name == NULL) {
return -1;
}
if (opt[SERVICE_SOCK_NAME] == NULL) {
return -1;
}
printf("[init] ParseServiceSocket SERVICE_SOCK_NAME is %s \n", opt[SERVICE_SOCK_NAME]);
int ret = memcpy_s(sockopt->name, MAX_SOCK_NAME_LEN, opt[SERVICE_SOCK_NAME], MAX_SOCK_NAME_LEN - 1);
if (ret != 0) {
return -1;
}
if (opt[SERVICE_SOCK_TYPE] == NULL) {
return -1;
}
printf("[init] ParseServiceSocket SERVICE_SOCK_TYPE is %s \n", opt[SERVICE_SOCK_TYPE]);
sockopt->type =
strncmp(opt[SERVICE_SOCK_TYPE], "stream", strlen(opt[SERVICE_SOCK_TYPE])) == 0 ? SOCK_STREAM :
(strncmp(opt[SERVICE_SOCK_TYPE], "dgram", strlen(opt[SERVICE_SOCK_TYPE])) == 0 ? SOCK_DGRAM : SOCK_SEQPACKET);
if (opt[SERVICE_SOCK_PERM] == NULL) {
return -1;
}
printf("[init] ParseServiceSocket SERVICE_SOCK_PERM is %s \n", opt[SERVICE_SOCK_PERM]);
sockopt->perm = strtoul(opt[SERVICE_SOCK_PERM], 0, 8); //¡Áa?¡¥?a8????
if (opt[SERVICE_SOCK_UID] == NULL) {
return -1;
}
printf("[init] ParseServiceSocket SERVICE_SOCK_UID is %s \n", opt[SERVICE_SOCK_UID]);
int uuid = DecodeUid(opt[SERVICE_SOCK_UID]);
if (uuid < 0) {
return -1;
}
sockopt->uid = uuid;
printf("[init] ParseServiceSocket uuid is %d \n", uuid);
if (opt[SERVICE_SOCK_GID] == NULL) {
return -1;
}
printf("[init] ParseServiceSocket SERVICE_SOCK_GID is %s \n", opt[SERVICE_SOCK_GID]);
int ggid = DecodeUid(opt[SERVICE_SOCK_GID]);
if (ggid < 0) {
return -1;
}
sockopt->gid = ggid;
printf("[init] ParseServiceSocket ggid is %d \n", ggid);
if (opt[SERVICE_SOCK_SETOPT] == NULL) {
return -1;
}
printf("[init] ParseServiceSocket SERVICE_SOCK_SETOPT is %s \n", opt[SERVICE_SOCK_SETOPT]);
sockopt->passcred = strncmp(opt[SERVICE_SOCK_SETOPT], "passcred", strlen(opt[SERVICE_SOCK_SETOPT])) == 0 ? true : false;
sockopt->next = NULL;
return 0;
}
static int GetServiceSocket(const cJSON* curArrItem, Service* curServ)
{
printf("[init] GetServiceSocket \n");
cJSON* filedJ = cJSON_GetObjectItem(curArrItem, "socket");
if (!cJSON_IsArray(filedJ)) {
return SERVICE_FAILURE;
}
int sockCnt = cJSON_GetArraySize(filedJ);
if (sockCnt <= 0) {
return SERVICE_FAILURE;
}
printf("[init] GetServiceSocket sockCnt is %d \n", sockCnt);
curServ->socketCfg = NULL;
for (int i = 0; i < sockCnt; ++i) {
cJSON* sockJ = cJSON_GetArrayItem(filedJ, i);
if (!cJSON_IsString(sockJ) || !cJSON_GetStringValue(sockJ)) {
return SERVICE_FAILURE;
}
char *sockStr = cJSON_GetStringValue(sockJ);
char *tmpStr[SOCK_OPT_NUMS] = {NULL,};
int num = SplitString(sockStr, tmpStr);
if (num != SOCK_OPT_NUMS) {
return SERVICE_FAILURE;
}
printf("[init] GetServiceSocket num is %d \n", num);
struct ServiceSocket *socktmp = (struct ServiceSocket *)calloc(1, sizeof(struct ServiceSocket));
if (!socktmp) {
return SERVICE_FAILURE;
}
printf("[init] ParseServiceSocket\n");
int ret = ParseServiceSocket(tmpStr, SOCK_OPT_NUMS, socktmp);
if (ret < 0) {
return SERVICE_FAILURE;
}
if (curServ->socketCfg == NULL) {
curServ->socketCfg = socktmp;
} else {
socktmp->next = curServ->socketCfg->next;
curServ->socketCfg->next = socktmp;
}
}
return SERVICE_SUCCESS;
}
static int GetServiceOnRestart(const cJSON* curArrItem, Service* curServ)
{
printf("[init] GetServiceOnRestart \n");
cJSON* filedJ = cJSON_GetObjectItem(curArrItem, "onrestart");
if (!cJSON_IsArray(filedJ)) {
return SERVICE_FAILURE;
}
int cmdCnt = cJSON_GetArraySize(filedJ);
if (cmdCnt <= 0) {
return SERVICE_FAILURE;
}
curServ->onRestart = (struct OnRestartCmd *)calloc(1, sizeof(struct OnRestartCmd));
if (curServ->onRestart == NULL) {
return SERVICE_FAILURE;
}
curServ->onRestart->cmdLine = (CmdLine *)calloc(cmdCnt, sizeof(CmdLine));
if (curServ->onRestart->cmdLine == NULL) {
return SERVICE_FAILURE;
}
curServ->onRestart->cmdNum = cmdCnt;
for (int i = 0; i < cmdCnt; ++i) {
cJSON* cmdJ = cJSON_GetArrayItem(filedJ, i);
if (!cJSON_IsString(cmdJ) || !cJSON_GetStringValue(cmdJ)) {
return SERVICE_FAILURE;
}
char *cmdStr = cJSON_GetStringValue(cmdJ);
ParseCmdLine(cmdStr, &curServ->onRestart->cmdLine[i]);
printf("[init] SetOnRestart cmdLine->name %s cmdLine->cmdContent %s \n", curServ->onRestart->cmdLine[i].name,
curServ->onRestart->cmdLine[i].cmdContent);
}
return SERVICE_SUCCESS;
}
static int CheckServiceKeyName(const cJSON* curService)
{
char *cfgServiceKeyList[] = {"name", "path", "uid", "gid", "once",
"importance", "caps", "disabled", "writepid", "critical", "socket",
};
if (curService == NULL) {
return SERVICE_FAILURE;
}
cJSON *child = curService->child;
if (child == NULL) {
return SERVICE_FAILURE;
}
while (child) {
int i = 0;
int keyListSize = sizeof(cfgServiceKeyList) / sizeof(char *);
for (; i < keyListSize; i++) {
if (!strcmp(child->string, cfgServiceKeyList[i])) {
break;
}
}
if(i < keyListSize) {
child = child->next;
} else {
INIT_LOGE("[Init] CheckServiceKeyName, key name %s is not found. error.\n", child->string);
return SERVICE_FAILURE;
}
}
return SERVICE_SUCCESS;
}
void ParseAllServices(const cJSON* fileRoot)
{
int servArrSize = 0;
cJSON* serviceArr = GetArrItem(fileRoot, &servArrSize, SERVICES_ARR_NAME_IN_JSON);
if (serviceArr == NULL) {
printf("[Init] InitReadCfg, get array %s failed.\n", SERVICES_ARR_NAME_IN_JSON);
return;
}
printf("[Init] servArrSize is %d \n", servArrSize);
if (servArrSize > MAX_SERVICES_CNT_IN_FILE) {
printf("[Init] InitReadCfg, too many services[cnt %d] detected, should not exceed %d.\n",
servArrSize, MAX_SERVICES_CNT_IN_FILE);
return;
}
Service* retServices = (Service*)realloc(g_services, sizeof(Service) * (g_servicesCnt + servArrSize));
if (retServices == NULL) {
printf("[Init] InitReadCfg, realloc for %s arr failed! %d.\n", SERVICES_ARR_NAME_IN_JSON, servArrSize);
return;
}
// Skip already saved services,
Service* tmp = retServices + g_servicesCnt;
if (memset_s(tmp, sizeof(Service) * servArrSize, 0, sizeof(Service) * servArrSize) != EOK) {
free(retServices);
retServices = NULL;
return;
}
for (int i = 0; i < servArrSize; ++i) {
cJSON* curItem = cJSON_GetArrayItem(serviceArr, i);
if (CheckServiceKeyName(curItem) != SERVICE_SUCCESS) {
ReleaseServiceMem(&tmp[i]);
tmp[i].attribute |= SERVICE_ATTR_INVALID;
continue;
}
tmp[i].servPerm.uID = (uid_t)(-1); // set 0xffffffff as init value
int ret1 = GetServiceName(curItem, &tmp[i]);
int ret2 = GetServicePathAndArgs(curItem, &tmp[i]);
int ret3 = GetUidStringNumber(curItem, &tmp[i]); // uid in string or number form
int ret4 = GetGidArray(curItem, &tmp[i]); // gid array
int ret5 = GetServiceNumber(curItem, &tmp[i], ONCE_STR_IN_CFG);
int ret6 = GetServiceNumber(curItem, &tmp[i], IMPORTANT_STR_IN_CFG);
int ret7 = GetServiceNumber(curItem, &tmp[i], CRITICAL_STR_IN_CFG); // critical
int ret8 = GetServiceNumber(curItem, &tmp[i], DISABLED_STR_IN_CFG); // disabled
int ret9 = GetWritepidStrings(curItem, &tmp[i]); // writepid
int reta = GetServiceCaps(curItem, &tmp[i]);
int retAll = ret1 | ret2 | ret3 | ret4 | ret5 | ret6 | ret7 | ret8 | ret9 | reta;
if (retAll != SERVICE_SUCCESS) {
// release resources if it fails
ReleaseServiceMem(&tmp[i]);
tmp[i].attribute |= SERVICE_ATTR_INVALID;
printf("[Init] InitReadCfg, parse information for service %d failed. ", i);
printf("service name = [%s]\n", tmp[i].name);
printf("ret1=%d,ret2=%d,ret3=%d,ret4=%d,ret5=%d,ret6=%d,ret7=%d,ret8=%d,ret9=%d,reta=%d,",
ret1, ret2, ret3, ret4, ret5, ret6, ret7, ret8, ret9, reta);
continue;
} else {
printf("[init] ParseAllServices ParseAllServices Service[%d] name=%s, uid=%d, critical=%d, disabled=%d\n",
i, tmp[i].name, tmp[i].servPerm.uID, tmp[i].attribute & SERVICE_ATTR_CRITICAL ? 1 : 0,
tmp[i].attribute & SERVICE_ATTR_DISABLED ? 1 : 0);
for(int j = 0; j < tmp[i].servPerm.gIDCnt; j++) {
printf("\t\tgIDArray[%d] = %d\n", j, tmp[i].servPerm.gIDArray[j]);
}
for(int j = 0; j < MAX_WRITEPID_FILES; j++) {
if(tmp[i].writepidFiles[j])
printf("\t\twritepidFiles[%d] = %s\n", j, tmp[i].writepidFiles[j]);
}
}
if (GetServiceSocket(curItem, &tmp[i]) != SERVICE_SUCCESS) {
// free list
}
if (GetServiceOnRestart(curItem, &tmp[i]) != SERVICE_SUCCESS) {
// free
}
}
// Increase service counter.
RegisterServices(retServices, servArrSize);
}
static int FindServiceByName(const char* servName)
{
if (servName == NULL) {
return -1;
}
for (int i = 0; i < g_servicesCnt; ++i) {
if (strlen(g_services[i].name) == strlen(servName) &&
strncmp(g_services[i].name, servName, strlen(g_services[i].name)) == 0) {
return i;
}
}
return -1;
}
void StartServiceByName(const char* servName)
{
// find service by name
int servIdx = FindServiceByName(servName);
if (servIdx < 0) {
printf("[Init] StartServiceByName, cannot find service %s.\n", servName);
return;
}
if (ServiceStart(&g_services[servIdx]) != SERVICE_SUCCESS) {
printf("[Init] StartServiceByName, service %s start failed!\n", g_services[servIdx].name);
}
return;
}
void StopServiceByName(const char* servName)
{
// find service by name
int servIdx = FindServiceByName(servName);
if (servIdx < 0) {
INIT_LOGE("[Init] StopServiceByName, cannot find service %s.\n", servName);
return;
}
if (ServiceStop(&g_services[servIdx]) != SERVICE_SUCCESS) {
INIT_LOGE("[Init] StopServiceByName, service %s start failed!\n", g_services[servIdx].name);
}
return;
}
void StopAllServices()
{
for (int i = 0; i < g_servicesCnt; i++) {
if (ServiceStop(&g_services[i]) != SERVICE_SUCCESS) {
printf("[Init] StopAllServices, service %s stop failed!\n", g_services[i].name);
}
}
}
void ReapServiceByPID(int pid)
{
for (int i = 0; i < g_servicesCnt; i++) {
if (g_services[i].pid == pid) {
if (g_services[i].attribute & SERVICE_ATTR_IMPORTANT) {
// important process exit, need to reboot system
g_services[i].pid = -1;
StopAllServices();
RebootSystem();
}
ServiceReap(&g_services[i]);
break;
}
}
}

View File

@ -0,0 +1,172 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "init_service_socket.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <sys/un.h>
#define HOS_SOCKET_DIR "/dev/unix/socket"
#define HOS_SOCKET_ENV_PREFIX "OHOS_SOCKET_"
static int CreateSocket(struct ServiceSocket *sockopt)
{
if (!sockopt || !sockopt->name) {
return -1;
}
printf("[init] CreateSocket\n");
int sockFd = socket(PF_UNIX, sockopt->type, 0);
if (sockFd < 0) {
printf("[init] socket fail %d \n", errno);
return -1;
}
struct sockaddr_un addr;
bzero(&addr,sizeof(addr));
addr.sun_family = AF_UNIX;
snprintf(addr.sun_path, sizeof(addr.sun_path), HOS_SOCKET_DIR"/%s",
sockopt->name);
printf("[init] addr.sun_path is %s \n", addr.sun_path);
if (access(addr.sun_path, F_OK)) {
printf("[init] %s already exist, remove it\n", addr.sun_path);
unlink(addr.sun_path);
}
if (sockopt->passcred) {
int on = 1;
if (setsockopt(sockFd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
unlink(addr.sun_path);
close(sockFd);
return -1;
}
}
if (bind(sockFd, (struct sockaddr *)&addr, sizeof(addr))) {
printf("[Init] Create socket for service %s failed: %d\n", sockopt->name, errno);
unlink(addr.sun_path);
close(sockFd);
return -1;
}
printf("[init] bind socket success\n");
if (lchown(addr.sun_path, sockopt->uid, sockopt->gid)) {
unlink(addr.sun_path);
close(sockFd);
printf("[init] lchown fail %d \n", errno);
return -1;
}
if (fchmodat(AT_FDCWD, addr.sun_path, sockopt->perm, AT_SYMLINK_NOFOLLOW)) {
unlink(addr.sun_path);
close(sockFd);
printf("[Init] fchmodat fail %d \n", errno);
return -1;
}
printf("[Init] CreateSocket success \n");
return sockFd;
}
static int SetSocketEnv(int fd, char *name)
{
printf("[init] SetSocketEnv\n");
char pubName[64] = {0};
char val[16] = {0};
snprintf(pubName, sizeof(pubName), HOS_SOCKET_ENV_PREFIX"%s", name);
printf("[init] pubName is %s \n", pubName);
snprintf(val, sizeof(val), "%d", fd);
printf("[init] val is %s \n", val);
int ret = setenv(pubName, val, 1);
if (ret < 0) {
printf("[init] setenv fail %d \n", errno);
return -1;
}
fcntl(fd, F_SETFD, 0);
return 0;
}
int DoCreateSocket(struct ServiceSocket *sockopt)
{
if (!sockopt) {
return -1;
}
printf("[init] DoCreateSocket \n");
struct ServiceSocket *tmpSock = sockopt;
while (tmpSock) {
printf("[init] tmpSock %p \n", tmpSock);
int fd = CreateSocket(tmpSock);
if (fd < 0) {
return -1;
}
int ret = SetSocketEnv(fd, tmpSock->name);
if (ret < 0) {
return -1;
}
tmpSock = tmpSock->next;
}
return 0;
}
int GetControlFromEnv(char *path)
{
if (path == NULL) {
return -1;
}
char *cp = path;
while (*cp) {
if (!isalnum(*cp)) *cp = '_';
++cp;
}
const char *val = getenv(path);
if (val == NULL) {
return -1;
}
errno = 0;
int fd = strtol(val, NULL, 10);
if (errno) {
return -1;
}
if (fcntl(fd, F_GETFD) < 0) {
return -1;
}
return fd;
}
int GetControlSocket(const char *name)
{
if (name == NULL) {
return -1;
}
char path[128] = {0};
snprintf(path, sizeof(path), HOS_SOCKET_ENV_PREFIX"%s", name);
int fd = GetControlFromEnv(path);
struct sockaddr_un addr;
socklen_t addrlen = sizeof(addr);
int ret = getsockname(fd, (struct sockaddr*)&addr, &addrlen);
if (ret < 0) {
return -1;
}
char sockDir[128] = {0};
snprintf(sockDir, sizeof(sockDir), HOS_SOCKET_DIR"/%s", name);
if (strncmp(sockDir, addr.sun_path, strlen(sockDir)) == 0) {
return fd;
}
return -1;
}

180
services/src/init_utils.c Executable file
View File

@ -0,0 +1,180 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "init_utils.h"
#include <ctype.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "init_cmds.h"
#include "init_log.h"
#include "init_utils.h"
#include "securec.h"
#define MAX_BUF_SIZE 1024
#ifdef STARTUP_UT
#define LOG_FILE_NAME "/media/sf_ubuntu/test/log.txt"
#else
#define LOG_FILE_NAME "/data/startup_log.txt"
#endif
#define MAX_BUFFER 256
#define MAX_EACH_CMD_LENGTH 30
#define MAX_JSON_FILE_LEN 102400 // max init.cfg size 100KB
struct CmdArgs* GetCmd(const char *cmdContent, const char *delim)
{
struct CmdArgs *ctx = (struct CmdArgs *)malloc(sizeof(struct CmdArgs));
INIT_CHECK_ONLY_RETURN(ctx != NULL, return NULL);
ctx->argv = (char**)malloc(sizeof(char*) * MAX_CMD_NAME_LEN);
INIT_CHECK_ONLY_RETURN(ctx->argv != NULL, FreeCmd(&ctx); return NULL);
char tmpCmd[MAX_BUFFER];
INIT_CHECK_ONLY_RETURN(strncpy_s(tmpCmd, strlen(cmdContent) + 1, cmdContent, strlen(cmdContent)) == EOK,
FreeCmd(&ctx);
return NULL);
tmpCmd[strlen(cmdContent)] = '\0';
char *buffer = NULL;
char *token = strtok_r(tmpCmd, delim, &buffer);
ctx->argc = 0;
while (token != NULL) {
ctx->argv[ctx->argc] = calloc(sizeof(char *), MAX_EACH_CMD_LENGTH);
INIT_CHECK_ONLY_RETURN(ctx->argv[ctx->argc] != NULL, FreeCmd(&ctx); return NULL);
INIT_CHECK_ONLY_RETURN(strncpy_s(ctx->argv[ctx->argc], strlen(cmdContent) + 1, token, strlen(token)) == EOK,
FreeCmd(&ctx);
return NULL);
if (ctx->argc > MAX_CMD_NAME_LEN - 1) {
INIT_LOGE("[Init] GetCmd failed, max cmd number is 10.\n");
FreeCmd(&ctx);
return NULL;
}
token = strtok_r(NULL, delim, &buffer);
ctx->argc += 1;
}
ctx->argv[ctx->argc] = NULL;
return ctx;
}
void FreeCmd(struct CmdArgs **cmd)
{
struct CmdArgs *tmpCmd = *cmd;
INIT_CHECK_ONLY_RETURN(tmpCmd != NULL, return);
for (int i = 0; i < tmpCmd->argc; ++i) {
INIT_CHECK_ONLY_RETURN(tmpCmd->argv[i] == NULL, free(tmpCmd->argv[i]));
}
INIT_CHECK_ONLY_RETURN(tmpCmd->argv == NULL, free(tmpCmd->argv));
free(tmpCmd);
return;
}
void Logger(StatupLogLevel level, const char *format, ...)
{
FILE* pFile = fopen(LOG_FILE_NAME, "a");
static char *logLeveInfo[] = { "VERBOSE", "INFO", "WARN", "ERROR", "FATAL" };
if (level >= sizeof(logLeveInfo) / sizeof(char*) || pFile == NULL) {
return;
}
time_t t;
struct tm *localTimer;
time(&t);
localTimer = localtime (&t);
fprintf(pFile, "[%d/%d/%d %d:%d:%d][%s]", localTimer->tm_year + 1900, localTimer->tm_mon, localTimer->tm_mday,
localTimer->tm_hour, localTimer->tm_min, localTimer->tm_sec, logLeveInfo[level]);
va_list list;
va_start(list, format);
vfprintf(pFile, format, list);
va_end(list);
fprintf(pFile, "%s", " \n");
fflush(pFile);
fclose(pFile);
}
int DecodeUid(const char *name)
{
if (isalpha(name[0])) {
struct passwd *pwd = getpwnam(name);
if (!pwd) {
return -1;
}
return pwd->pw_uid;
} else if (isdigit(name[0])) {
uid_t result = strtoul(name, 0, 10);
return result;
} else {
return -1;
}
}
void CheckAndCreateDir(const char *fileName)
{
char *path = strndup(fileName, strrchr(fileName, '/') - fileName);
if (path != NULL && access(path, F_OK) != 0) {
mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
}
free(path);
}
char* ReadFileToBuf(const char *configFile)
{
char* buffer = NULL;
FILE* fd = NULL;
struct stat fileStat = {0};
if (configFile == NULL || *configFile == '\0') {
return NULL;
}
do {
if (stat(configFile, &fileStat) != 0 ||
fileStat.st_size <= 0 || fileStat.st_size > MAX_JSON_FILE_LEN) {
break;
}
fd = fopen(configFile, "r");
if (fd == NULL) {
break;
}
buffer = (char*)malloc(fileStat.st_size + 1);
if (buffer == NULL) {
break;
}
if (fread(buffer, fileStat.st_size, 1, fd) != 1) {
free(buffer);
buffer = NULL;
break;
}
buffer[fileStat.st_size] = '\0';
} while (0);
if (fd != NULL) {
fclose(fd);
fd = NULL;
}
return buffer;
}

View File

@ -33,6 +33,8 @@
#ifndef OHOS_LITE
#include "device.h"
#include "property.h"
#include "property_service.h"
#endif
static const pid_t INIT_PROCESS_PID = 1;
@ -82,6 +84,9 @@ int main(int argc, char * const argv[])
// 2. Mount basic filesystem and create common device node.
MountBasicFs();
CreateDeviceNode();
MakeSocketDir("/dev/unix/socket/", 0755);
InitPropertyService();
#endif
// 3. signal register
@ -107,6 +112,12 @@ int main(int argc, char * const argv[])
// 5. read configuration file and do jobs
InitReadCfg();
LoadDefaultProperty("/system/build.prop");
LoadDefaultProperty("/system/buildz.prop");
LoadDefaultProperty("/vendor/build.prop");
LoadDefaultProperty("/vendor/default.prop");
LoadDefaultProperty("/vendor/odm/etc/build.prop");
LoadDefaultProperty("/system/etc/prop.default");
#ifdef OHOS_DEBUG
struct timespec tmCfg;
if (clock_gettime(CLOCK_REALTIME, &tmCfg) != 0) {
@ -121,6 +132,9 @@ int main(int argc, char * const argv[])
#endif
printf("[Init] main, entering wait.\n");
#ifndef OHOS_LITE
StartPropertyService();
#endif
while (1) {
// pause only returns when a signal was caught and the signal-catching function returned.
// pause only returns -1, no need to process the return value.

View File

@ -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",

View File

@ -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);

40
services/trigger/BUILD.gn Executable file
View File

@ -0,0 +1,40 @@
# Copyright (c) 2020 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import("//build/ohos.gni")
ohos_static_library("triggerservice") {
sources = [
"trigger_manager.c",
"trigger_processor.c",
"trigger_checker.c",
"//base/startup/init_lite/services/src/init_utils.c",
]
include_dirs = [
".",
"//base/startup/init_lite/services/include/property",
"//base/startup/init_lite/services/include/trigger",
"//base/startup/init_lite/services/include",
"//base/startup/init_lite/services/property/include",
"//third_party/libuv/include",
"//third_party/cJSON",
]
deps = [
"//third_party/libuv:uv_static",
"//third_party/bounds_checking_function:libsec_static",
]
part_name = "startup"
subsystem_name = "startup"
}

View File

@ -0,0 +1,298 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "trigger_checker.h"
#include <ctype.h>
#include "trigger_manager.h"
#include "property_service.h"
// 申请整块能存作为计算的节点
int CalculatorInit(LogicCalculator *calculator, int dataNumber, int dataUnit, int needCondition)
{
TRIGGER_CHECK(calculator != NULL, return -1, "Invalid param");
int dataSize = dataUnit * dataNumber;
if (needCondition) {
dataSize += 5 * SUPPORT_DATA_BUFFER_MAX;
}
calculator->data = (char *)malloc(dataSize);
TRIGGER_CHECK(calculator->data != NULL, return -1, "Failed to malloc for calculator");
calculator->dataNumber = dataNumber;
calculator->endIndex = 0;
calculator->dataUnit = dataUnit;
dataSize = dataUnit * dataNumber;
calculator->conditionName = calculator->data + dataSize;
dataSize += SUPPORT_DATA_BUFFER_MAX;
calculator->conditionContent = calculator->data + dataSize;
dataSize += SUPPORT_DATA_BUFFER_MAX;
calculator->inputName = calculator->data + dataSize;
dataSize += SUPPORT_DATA_BUFFER_MAX;
calculator->inputContent = calculator->data + dataSize;
dataSize += SUPPORT_DATA_BUFFER_MAX;
calculator->readContent = calculator->data + dataSize;
return 0;
}
void CalculatorFree(LogicCalculator *calculator)
{
TRIGGER_CHECK(calculator != NULL, return, "Invalid param");
free(calculator->data);
calculator->data = NULL;
}
static void CalculatorClear(LogicCalculator *calculator)
{
TRIGGER_CHECK(calculator != NULL, return, "Invalid param");
calculator->endIndex = 0;
}
static int CalculatorPushChar(LogicCalculator *calculator, char data)
{
TRIGGER_CHECK(calculator != NULL, return -1, "Invalid param");
TRIGGER_CHECK(calculator->endIndex < calculator->dataNumber, return -1, "More data for calculator support");
TRIGGER_CHECK(sizeof(char) == calculator->dataUnit, return -1, "More data for calculator support");
calculator->data[calculator->endIndex++] = data;
return 0;
}
static int CalculatorPopChar(LogicCalculator *calculator, char *data)
{
TRIGGER_CHECK(calculator != NULL, return -1, "Invalid param");
TRIGGER_CHECK(calculator->endIndex < calculator->dataNumber, return -1, "More data for calculator support");
if (calculator->endIndex == 0) {
return -1;
}
*data = calculator->data[--calculator->endIndex];
return 0;
}
static int CalculatorPush(LogicCalculator *calculator, void *data)
{
TRIGGER_CHECK(calculator != NULL, return -1, "Invalid param");
TRIGGER_CHECK(calculator->endIndex < calculator->dataNumber, return -1, "More data for calculator support");
char *tmpData = (calculator->data + calculator->dataUnit * calculator->endIndex);
int ret = memcpy_s(tmpData, calculator->dataUnit, data, calculator->dataUnit);
TRIGGER_CHECK(ret == 0, return -1, "Failed to copy logic data");
calculator->endIndex++;
return 0;
}
static int CalculatorPop(LogicCalculator *calculator, void *data)
{
TRIGGER_CHECK(calculator != NULL || data == NULL, return -1, "Invalid param");
TRIGGER_CHECK(calculator->endIndex < calculator->dataNumber, return -1, "More data for calculator support");
if (calculator->endIndex == 0) {
return -1;
}
char *tmpData = calculator->data + calculator->dataUnit * (calculator->endIndex - 1);
int ret = memcpy_s(data, calculator->dataUnit, tmpData, calculator->dataUnit);
TRIGGER_CHECK(ret == 0, return -1, "Failed to copy logic data");
calculator->endIndex--;
return 0;
}
static int CalculatorLength(const LogicCalculator *calculator)
{
TRIGGER_CHECK(calculator != NULL, return 0, "Invalid param");
return calculator->endIndex;
}
static int PrefixAdd(char *prefix, u_int32_t *prefixIndex, u_int32_t prefixLen, char op)
{
if ((*prefixIndex + 3) >= prefixLen) {
return -1;
}
prefix[(*prefixIndex)++] = ' ';
prefix[(*prefixIndex)++] = op;
prefix[(*prefixIndex)++] = ' ';
return 0;
}
static int HandleOperationOr(LogicCalculator *calculator, char *prefix, u_int32_t *prefixIndex, u_int32_t prefixLen)
{
int ret = 0;
char e;
prefix[(*prefixIndex)++] = ' ';
if(CalculatorLength(calculator) == 0) {
CalculatorPushChar(calculator, '|');
} else {
do {
CalculatorPopChar(calculator, &e);
if (e == '(') {
CalculatorPushChar(calculator, e);
} else {
ret = PrefixAdd(prefix, prefixIndex, prefixLen, e);
TRIGGER_CHECK(ret == 0, return -1, "Invalid prefix");
}
} while (CalculatorLength(calculator) > 0 && e != '(');
CalculatorPushChar(calculator, '|');
}
return 0;
}
static int ComputeSubCondition(LogicCalculator *calculator, LogicData *data, const char *condition)
{
if (!LOGIC_DATA_TEST_FLAG(data, LOGIC_DATA_FLAGS_ORIGINAL)) {
return LOGIC_DATA_TEST_FLAG(data, LOGIC_DATA_FLAGS_TRUE);
}
// 解析条件
int ret = GetValueFromContent(condition + data->startIndex,
data->endIndex - data->startIndex, 0, calculator->conditionName, SUPPORT_DATA_BUFFER_MAX);
TRIGGER_CHECK(ret == 0, return -1, "Failed parse content name");
ret = GetValueFromContent(condition + data->startIndex, data->endIndex - data->startIndex,
strlen(calculator->conditionName) + 1, calculator->conditionContent, SUPPORT_DATA_BUFFER_MAX);
TRIGGER_CHECK(ret == 0, return -1, "Failed parse content value");
TRIGGER_LOGI("ComputeSubCondition subcondition \'%s\' \'%s\'",
calculator->conditionName, calculator->conditionContent);
// check name
if (strcmp(calculator->conditionName, calculator->inputName) == 0) {
if (strcmp(calculator->conditionContent, "*") == 0) {
return 1;
}
if (strcmp(calculator->conditionContent, calculator->inputContent) == 0) {
return 1;
}
} else {
u_int32_t len = SUPPORT_DATA_BUFFER_MAX;
ret = SystemReadParameter(calculator->conditionName, calculator->readContent, &len);
if (ret == 0 && strcmp(calculator->conditionContent, calculator->readContent) == 0) {
return 1;
}
}
return 0;
}
int GetValueFromContent(const char *content, u_int32_t contentSize, u_int32_t start, char *value, u_int32_t valueSize)
{
u_int32_t contentIndex = start;
u_int32_t currIndex = 0;
while (contentIndex < contentSize && currIndex < valueSize) {
if (isspace(content[contentIndex])) {
contentIndex++;
continue;
}
if (content[contentIndex] == '=') {
value[currIndex++] = '\0';
return 0;
}
value[currIndex++] = content[contentIndex++];
}
if (currIndex < valueSize) {
value[currIndex] = '\0';
return 0;
}
return -1;
}
int ComputeCondition(LogicCalculator *calculator, const char *condition)
{
u_int32_t currIndex = 0;
u_int32_t start = 0;
int noneOper = 1;
CalculatorClear(calculator);
LogicData data1 = {};
LogicData data2 = {};
while (currIndex < strlen(condition)) {
if (condition[currIndex] == '|' || condition[currIndex] == '&') {
noneOper = 0;
int ret = CalculatorPop(calculator, (void*)&data2);
ret |= CalculatorPop(calculator, (void*)&data1);
TRIGGER_CHECK(ret == 0, return -1, "Failed to pop data");
ret = ComputeSubCondition(calculator, &data1, condition);
data1.flags = 0;
if (condition[currIndex] == '|' && ret == 1) {
LOGIC_DATA_SET_FLAG(&data1, LOGIC_DATA_FLAGS_TRUE);
} else if (condition[currIndex] == '|' || ret == 1) {
if (ComputeSubCondition(calculator, &data2, condition) == 1) {
LOGIC_DATA_SET_FLAG(&data1, LOGIC_DATA_FLAGS_TRUE);
}
}
ret = CalculatorPush(calculator, (void*)&data1);
TRIGGER_CHECK(ret == 0, return -1, "Failed to push data");
start = currIndex + 1; // 跳过符号
} else if (isspace(condition[currIndex])) {
if (start == currIndex) {
start = ++currIndex;
continue;
}
data1.flags = LOGIC_DATA_FLAGS_ORIGINAL;
data1.startIndex = start;
data1.endIndex = currIndex;
int ret = CalculatorPush(calculator, (void*)&data1);
TRIGGER_CHECK(ret == 0, return -1, "Failed to push data");
start = currIndex + 1;
}
currIndex++;
}
if (noneOper) {
data1.flags = LOGIC_DATA_FLAGS_ORIGINAL;
data1.startIndex = start;
data1.endIndex = strlen(condition);
} else {
int ret = CalculatorPop(calculator, &data1);
TRIGGER_CHECK(ret == 0, return -1, "Invalid calculator");
}
return ComputeSubCondition(calculator, &data1, condition);
}
int ConvertInfixToPrefix(const char *condition, char *prefix, u_int32_t prefixLen)
{
char e;
int ret = 0;
u_int32_t curr = 0;
u_int32_t prefixIndex = 0;
LogicCalculator calculator;
CalculatorInit(&calculator, 100, 1, 0);
while (curr < strlen(condition)) {
if (condition[curr] == ')') {
CalculatorPopChar(&calculator, &e);
while (e != '(') {
ret = PrefixAdd(prefix, &prefixIndex, prefixLen, e);
TRIGGER_CHECK(ret == 0, CalculatorFree(&calculator); return -1, "Invalid prefix");
ret = CalculatorPopChar(&calculator, &e);
TRIGGER_CHECK(ret == 0, CalculatorFree(&calculator); return -1, "Invalid calculator");
}
} else if (condition[curr] == '|') {
TRIGGER_CHECK(condition[curr + 1] == '|', CalculatorFree(&calculator); return -1, "Invalid condition");
ret = HandleOperationOr(&calculator, prefix, &prefixIndex, prefixLen);
TRIGGER_CHECK(ret == 0, CalculatorFree(&calculator); return -1, "Invalid prefix");
curr++;
} else if (condition[curr] == '&') {
TRIGGER_CHECK(condition[curr + 1] == '&', CalculatorFree(&calculator); return -1, "Invalid condition");
prefix[prefixIndex++] = ' ';
CalculatorPushChar(&calculator, condition[curr]);
curr++;
} else if (condition[curr] == '(') {
CalculatorPushChar(&calculator, condition[curr]);
} else {
prefix[prefixIndex++] = condition[curr];
}
curr++;
TRIGGER_CHECK(prefixIndex < prefixLen, CalculatorFree(&calculator); return -1, "Invalid prefixIndex");
}
while (CalculatorLength(&calculator) > 0) {
CalculatorPopChar(&calculator, &e);
ret = PrefixAdd(prefix, &prefixIndex, prefixLen, e);
TRIGGER_CHECK(ret == 0, CalculatorFree(&calculator);
return -1, "Invalid prefix %u %u", prefixIndex, prefixLen);
}
prefix[prefixIndex] = '\0';
CalculatorFree(&calculator);
return 0;
}

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef STARTUP_TRIGER_CHECKER_H
#define STARTUP_TRIGER_CHECKER_H
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif
#define SUPPORT_DATA_BUFFER_MAX 128
#define CONDITION_EXTEND_LEN 32
#define LOGIC_DATA_FLAGS_ORIGINAL 0x1
#define LOGIC_DATA_FLAGS_TRUE 0x08
#define LOGIC_DATA_TEST_FLAG(data, flag) (((data)->flags & flag) == (flag))
#define LOGIC_DATA_SET_FLAG(data, flag) (data)->flags |= (flag)
#define LOGIC_DATA_CLEAR_FLAG(data, flag) (data)->flags &= ~(flag)
typedef struct {
u_int32_t flags;
u_int32_t startIndex;
u_int32_t endIndex;
} LogicData;
typedef struct {
int dataNumber;
int endIndex;
int dataUnit;
char *conditionName;
char *conditionContent;
char *inputName;
char *inputContent;
char *readContent;
char *data;
} LogicCalculator;
int CalculatorInit(LogicCalculator *calculator, int dataNumber, int dataUnit, int needCondition);
void CalculatorFree(LogicCalculator *calculator);
int ConvertInfixToPrefix(const char *condition, char *prefix, u_int32_t prefixLen);
int ComputeCondition(LogicCalculator *calculator, const char *condition);
int GetValueFromContent(const char *content, u_int32_t contentSize, u_int32_t start, char *value, u_int32_t valueSize);
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif // STARTUP_TRIGER_CHECKER_H

View File

@ -0,0 +1,381 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "trigger_manager.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "init_cmds.h"
#include "init_utils.h"
#include "trigger_checker.h"
#define TRIGGER_AREA_SPACE 1024*64
#define TRIGGER_EXECUTE_QUEUE 64
#define BUFFER_SIZE 256
#define CHECK_INDEX_VALID(workSpace, index) \
(u_int32_t)(index) < sizeof((workSpace)->header) / sizeof((workSpace)->header[0])
#ifdef STARTUP_LOCAL
#define TRIGGER_PATH "/media/sf_ubuntu/test/__trigger__/trigger"
#else
#define TRIGGER_PATH "/dev/__trigger__/trigger"
#endif
int InitTriggerWorkSpace(TriggerWorkSpace *workSpace)
{
TRIGGER_CHECK(workSpace != NULL, return -1, "Invalid parm");
if (workSpace->area != NULL) {
return 0;
}
CheckAndCreateDir(TRIGGER_PATH);
int fd = open(TRIGGER_PATH, O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0444);
TRIGGER_CHECK(fd >= 0, return -1, "Open file fail error %s", strerror(errno));
lseek(fd, TRIGGER_AREA_SPACE, SEEK_SET);
write(fd, "", 1);
void *areaAddr = (void *)mmap(NULL, TRIGGER_AREA_SPACE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
TRIGGER_CHECK(areaAddr != MAP_FAILED, close(fd); return -1,
"Failed to map memory error %s", strerror(errno));
close(fd);
// 第一部分做执行队列
workSpace->executeQueue.executeQueue = (u_int32_t *)areaAddr;
workSpace->executeQueue.queueCount = TRIGGER_EXECUTE_QUEUE;
workSpace->executeQueue.startIndex = 0;
workSpace->executeQueue.endIndex = 0;
pthread_mutex_init(&workSpace->executeQueue.mutex, NULL);
// 动态数据保存
workSpace->area = (TriggerArea *)(areaAddr + TRIGGER_EXECUTE_QUEUE * sizeof(u_int32_t));
atomic_init(&workSpace->area->serial, ATOMIC_VAR_INIT(0));
workSpace->area->dataSize = TRIGGER_AREA_SPACE - sizeof(TriggerArea) - TRIGGER_EXECUTE_QUEUE * sizeof(u_int32_t);
workSpace->area->currOffset = sizeof(TriggerArea) + TRIGGER_EXECUTE_QUEUE * sizeof(u_int32_t);
for (size_t i = 0; i < sizeof(workSpace->header) / sizeof(workSpace->header[0]); i++) {
atomic_init(&workSpace->header[i].firstTrigger, ATOMIC_VAR_INIT(0));
atomic_init(&workSpace->header[i].lastTrigger, ATOMIC_VAR_INIT(0));
}
return 0;
}
static CommandNode *GetCmdByIndex(TriggerWorkSpace *workSpace, TriggerNode *trigger, u_int32_t index)
{
if (index == 0 || index == (u_int32_t)-1) {
return NULL;
}
u_int32_t size = sizeof(CommandNode) + 2;
TRIGGER_CHECK((index + size) < workSpace->area->dataSize,
return NULL, "Invalid index for cmd %u", index);
return (CommandNode *)(workSpace->area->data + index);
}
static u_int32_t AddCommand(TriggerWorkSpace *workSpace, TriggerNode *trigger, const char *cmdName, const char *content)
{
TRIGGER_CHECK(workSpace != NULL && trigger != NULL, return 0, "list is null");
TRIGGER_CHECK(content != NULL, return 0, "command is null");
u_int32_t size = sizeof(CommandNode) + strlen(cmdName) + 1;
size += (content == NULL) ? 1 : strlen(content) + 1;
TRIGGER_CHECK((workSpace->area->currOffset + size) < workSpace->area->dataSize,
return 0, "Not enough memory for cmd %u %u", size, workSpace->area->currOffset);
CommandNode *node = (CommandNode *)(workSpace->area->data + workSpace->area->currOffset);
TRIGGER_CHECK(node != NULL, return 0, "Failed to alloc memory for command");
int ret = memcpy_s(node->name, sizeof(node->name) - 1, cmdName, strlen(cmdName));
TRIGGER_CHECK(ret == 0, return 0, "Failed to copy command");
node->name[strlen(cmdName)] = '\0';
if (content != NULL) {
ret = memcpy_s(node->content, size, content, strlen(content));
node->content[strlen(content)] = '\0';
TRIGGER_CHECK(ret == 0, return 0, "Failed to copy command");
} else {
node->content[0] = '\0';
}
u_int32_t offset = workSpace->area->currOffset;
atomic_init(&node->next, ATOMIC_VAR_INIT(0));
// 插入队列
if (trigger->firstCmd == 0) {
atomic_store_explicit(&trigger->firstCmd, offset, memory_order_release);
atomic_store_explicit(&trigger->lastCmd, offset, memory_order_release);
} else {
CommandNode *lastNode = GetCmdByIndex(workSpace, trigger, trigger->lastCmd);
if (lastNode != NULL) {
atomic_store_explicit(&lastNode->next, offset, memory_order_release);
}
atomic_store_explicit(&trigger->lastCmd, offset, memory_order_release);
}
workSpace->area->currOffset += size;
return offset;
}
static TriggerNode *GetTriggerByIndex(TriggerWorkSpace *workSpace, u_int32_t index)
{
if (index == 0 || index == (u_int32_t)-1) {
return NULL;
}
u_int32_t size = sizeof(TriggerNode) + 1;
TRIGGER_CHECK((index + size) < workSpace->area->dataSize,
return NULL, "Invalid index for trigger %u", index);
return (TriggerNode *)(workSpace->area->data + index);
}
static u_int32_t AddTrigger(TriggerWorkSpace *workSpace, int type, const char *name, const char *condition)
{
TRIGGER_CHECK(workSpace != NULL && name != NULL, return 0, "list is null");
const char *tmpCond = condition;
if (type == TRIGGER_BOOT && condition == NULL) {
tmpCond = name;
}
u_int32_t conditionSize = (tmpCond == NULL) ? 1 : strlen(tmpCond) + 1 + CONDITION_EXTEND_LEN;
TRIGGER_CHECK((workSpace->area->currOffset + sizeof(TriggerNode) + conditionSize) < workSpace->area->dataSize,
return -1, "Not enough memory for cmd");
TriggerNode *node = (TriggerNode *)(workSpace->area->data + workSpace->area->currOffset);
TRIGGER_CHECK(node != NULL, return 0, "Failed to alloc memory for trigger");
node->type = type;
int ret = memcpy_s(node->name, sizeof(node->name) - 1, name, strlen(name));
TRIGGER_CHECK(ret == 0, return 0, "Failed to memcpy_s for trigger");
node->name[strlen(name)] = '\0';
if (tmpCond != NULL) {
if (type == TRIGGER_PROPERTY) {
ret = ConvertInfixToPrefix(tmpCond, node->condition, conditionSize);
TRIGGER_CHECK(ret == 0, return 0, "Failed to memcpy_s for trigger");
} else {
ret = memcpy_s(node->condition, strlen(tmpCond) + 1, tmpCond, strlen(tmpCond));
TRIGGER_CHECK(ret == 0, return 0, "Failed to memcpy_s for trigger");
node->condition[strlen(tmpCond)] = '\0';
}
} else {
node->condition[0] = '\0';
}
u_int32_t offset = workSpace->area->currOffset;
atomic_init(&node->serial, ATOMIC_VAR_INIT(0));
atomic_init(&node->next, ATOMIC_VAR_INIT(0));
atomic_init(&node->firstCmd, ATOMIC_VAR_INIT(0));
atomic_init(&node->lastCmd, ATOMIC_VAR_INIT(0));
// 插入到trigger队列中
if (workSpace->header[type].firstTrigger == 0) {
atomic_store_explicit(&workSpace->header[type].firstTrigger, offset, memory_order_release);
atomic_store_explicit(&workSpace->header[type].lastTrigger, offset, memory_order_release);
} else {
TriggerNode *lastNode = GetTriggerByIndex(workSpace, workSpace->header[type].lastTrigger);
if (lastNode != NULL) {
atomic_store_explicit(&lastNode->next, offset, memory_order_release);
}
atomic_store_explicit(&workSpace->header[type].lastTrigger, offset, memory_order_release);
}
workSpace->area->currOffset += conditionSize + sizeof(TriggerNode);
return offset;
}
static int GetTriggerIndex(const char *type)
{
if (strncmp("property", type, strlen("property")) == 0) {
return TRIGGER_PROPERTY;
}
static const char *triggerType[] = {
"boot", "early-init", "init", "early-init", "late-init",
"early-fs", "post-fs", "late-fs", "post-fs-data",
"nonencrypted",
"firmware_mounts_complete",
"load_persist_props_action"
};
for (size_t i = 0; i < sizeof(triggerType) / sizeof(char*); i++) {
if (strncmp(triggerType[i], type, strlen(triggerType[i])) == 0) {
return TRIGGER_BOOT;
}
}
return TRIGGER_UNKNOW;
}
static int CheckBootTriggerMatch(TriggerNode *trigger, void *content, u_int32_t contentSize)
{
if (strncmp(trigger->name, (char *)content, contentSize) == 0) {
return 1;
}
return 0;
}
int ParseTrigger(TriggerWorkSpace *workSpace, cJSON *triggerItem)
{
TRIGGER_CHECK(triggerItem != NULL, return -1, "Invalid file");
TRIGGER_CHECK(workSpace != NULL, return -1, "Failed to create trigger list");
char *name = cJSON_GetStringValue(cJSON_GetObjectItem(triggerItem, "name"));
TRIGGER_CHECK(name != NULL, return -1, "Can not get name from cfg");
char *condition = cJSON_GetStringValue(cJSON_GetObjectItem(triggerItem, "condition"));
int index = GetTriggerIndex(name);
TRIGGER_CHECK(CHECK_INDEX_VALID(workSpace, index), return -1, "Failed to get trigger index");
u_int32_t offset = AddTrigger(workSpace, index, name, condition);
//TRIGGER_LOGE("AddTrigger %u %s %u", offset, name, workSpace->area->currOffset);
TRIGGER_CHECK(offset > 0, return -1, "Failed to create trigger %s", name);
TriggerNode *trigger = GetTriggerByIndex(workSpace, offset);
// 添加命令行
cJSON* cmdItems = cJSON_GetObjectItem(triggerItem, CMDS_ARR_NAME_IN_JSON);
TRIGGER_CHECK(cJSON_IsArray(cmdItems), return -1, "Command item must be array");
int cmdLinesCnt = cJSON_GetArraySize(cmdItems);
TRIGGER_CHECK(cmdLinesCnt > 0, return -1, "Command array size must positive");
for (int i = 0; i < cmdLinesCnt; ++i) {
char *cmdLineStr = cJSON_GetStringValue(cJSON_GetArrayItem(cmdItems, i));
TRIGGER_CHECK(cmdLinesCnt > 0, continue, "Command is null");
size_t cmdLineLen = strlen(cmdLineStr);
const char *matchCmd = GetMatchCmd(cmdLineStr);
if (matchCmd == NULL && strncmp(cmdLineStr, TRIGGER_CMD, strlen(TRIGGER_CMD)) == 0) {
matchCmd = TRIGGER_CMD;
}
TRIGGER_CHECK(matchCmd != NULL, continue, "Command not support %s", cmdLineStr);
size_t matchLen = strlen(matchCmd);
if (matchLen == cmdLineLen) {
offset = AddCommand(workSpace, trigger, matchCmd, NULL);
} else {
offset = AddCommand(workSpace, trigger, matchCmd, cmdLineStr + matchLen);
}
//TRIGGER_LOGE("AddCommand %u %s %u", offset, cmdLineStr, workSpace->area->currOffset);
TRIGGER_CHECK(offset > 0, continue, "Failed to add command %s", cmdLineStr);
}
return 0;
}
int ExecuteTrigger(TriggerWorkSpace *workSpace, TriggerNode *trigger, CMD_EXECUTE cmdExecuter)
{
TRIGGER_CHECK(workSpace != NULL && trigger != NULL && cmdExecuter != NULL, return -1, "Invalid param");
TRIGGER_LOGI("ExecuteCmds trigger %s", trigger->name);
CommandNode *cmd = GetCmdByIndex(workSpace, trigger, trigger->firstCmd);
while (cmd != NULL) {
cmdExecuter(trigger, cmd->name, cmd->content);
cmd = GetCmdByIndex(workSpace, trigger, cmd->next);
}
return 0;
}
int ExecuteQueuePush(TriggerWorkSpace *workSpace, TriggerNode *trigger, u_int32_t triggerIndex)
{
TRIGGER_CHECK(workSpace != NULL, return -1, "Invalid area");
pthread_mutex_lock(&workSpace->executeQueue.mutex);
u_int32_t index = workSpace->executeQueue.endIndex++ % workSpace->executeQueue.queueCount;
workSpace->executeQueue.executeQueue[index] = triggerIndex;
pthread_mutex_unlock(&workSpace->executeQueue.mutex);
return 0;
}
TriggerNode *ExecuteQueuePop(TriggerWorkSpace *workSpace)
{
if (workSpace->executeQueue.endIndex <= workSpace->executeQueue.startIndex) {
return NULL;
}
pthread_mutex_lock(&workSpace->executeQueue.mutex);
u_int32_t currIndex = workSpace->executeQueue.startIndex % workSpace->executeQueue.queueCount;
u_int32_t triggerIndex = workSpace->executeQueue.executeQueue[currIndex];
workSpace->executeQueue.executeQueue[currIndex] = 0;
workSpace->executeQueue.startIndex++;
pthread_mutex_unlock(&workSpace->executeQueue.mutex);
return GetTriggerByIndex(workSpace, triggerIndex);
}
int ExecuteQueueSize(TriggerWorkSpace *workSpace)
{
TRIGGER_CHECK(workSpace != NULL, return 0, "Invalid param");
return workSpace->executeQueue.endIndex - workSpace->executeQueue.startIndex;
}
int CheckTrigger(TriggerWorkSpace *workSpace,
int type, void *content, u_int32_t contentSize, TRIGGER_CHECK_DONE triggerExecuter)
{
static TRIGGER_MATCH triggerCheckMatch[TRIGGER_MAX] = {
CheckBootTriggerMatch, NULL, NULL
};
TRIGGER_LOGI("ExecuteTrigger check content %s", (char*)content);
TRIGGER_CHECK(workSpace != NULL, return -1, "Trigger not init");
TRIGGER_CHECK(CHECK_INDEX_VALID(workSpace, type), return -1, "Invalid type %d", type);
TRIGGER_CHECK((u_int32_t)type < sizeof(triggerCheckMatch) / sizeof(triggerCheckMatch[0]),
return -1, "Failed to get check function");
TRIGGER_CHECK(triggerCheckMatch[type] != NULL, return 0, "Failed to get check function");
u_int32_t index = workSpace->header[type].firstTrigger;
TriggerNode *trigger = GetTriggerByIndex(workSpace, workSpace->header[type].firstTrigger);
while (trigger != NULL) {
if (triggerCheckMatch[type](trigger, content, contentSize) == 1) { // 等于1 则认为匹配
triggerExecuter(trigger, index);
}
index = trigger->next;
trigger = GetTriggerByIndex(workSpace, trigger->next);
}
return 0;
}
int CheckPropertyTrigger(TriggerWorkSpace *workSpace,
const char *content, u_int32_t contentSize, TRIGGER_CHECK_DONE triggerExecuter)
{
TRIGGER_CHECK(workSpace != NULL && content != NULL && triggerExecuter != NULL,
return -1, "Failed arg for property trigger");
LogicCalculator calculator;
CalculatorInit(&calculator, 100, sizeof(LogicData), 1);
// 先解析content
int ret = GetValueFromContent(content, contentSize, 0, calculator.inputName, SUPPORT_DATA_BUFFER_MAX);
TRIGGER_CHECK(ret == 0, CalculatorFree(&calculator); return -1, "Failed parse content name");
ret = GetValueFromContent(content, contentSize,
strlen(calculator.inputName) + 1, calculator.inputContent, SUPPORT_DATA_BUFFER_MAX);
TRIGGER_CHECK(ret == 0, CalculatorFree(&calculator); return -1, "Failed parse content value");
TRIGGER_LOGI("CheckPropertyTrigger content %s ", content);
u_int32_t index = workSpace->header[TRIGGER_PROPERTY].firstTrigger;
TriggerNode *trigger = GetTriggerByIndex(workSpace, workSpace->header[TRIGGER_PROPERTY].firstTrigger);
while (trigger != NULL) {
TRIGGER_LOGI("CheckPropertyTrigger name:%s condition \'%s\' ", trigger->name, trigger->condition);
ret = ComputeCondition(&calculator, trigger->condition);
if (ret == 1) {
triggerExecuter(trigger, index);
}
index = trigger->next;
trigger = GetTriggerByIndex(workSpace, trigger->next);
}
CalculatorFree(&calculator);
return 0;
}
TriggerNode *GetTriggerByName(TriggerWorkSpace *workSpace, const char *triggerName, u_int32_t *triggerIndex)
{
TRIGGER_CHECK(workSpace != NULL && triggerName != NULL, return NULL, "Invalid param");
for (size_t i = 0; i < sizeof(workSpace->header) / sizeof(workSpace->header[0]); i++) {
u_int32_t index = workSpace->header[i].firstTrigger;
TriggerNode *trigger = GetTriggerByIndex(workSpace, workSpace->header[i].firstTrigger);
while (trigger != NULL) {
if (strcmp(triggerName, trigger->name) == 0) {
*triggerIndex = index;
return trigger;
}
index = trigger->next;
trigger = GetTriggerByIndex(workSpace, trigger->next);
}
}
return NULL;
}

View File

@ -0,0 +1,147 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef STARTUP_TRIGER_MANAGER_H
#define STARTUP_TRIGER_MANAGER_H
#include <pthread.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include "cJSON.h"
#include "init_log.h"
#include "list.h"
#include "property_service.h"
#include "securec.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif
#define TRIGGER_LOGI(fmt, ...) STARTUP_LOGI("Trigger", fmt, ##__VA_ARGS__)
#define TRIGGER_LOGE(fmt, ...) STARTUP_LOGE("Trigger", fmt, ##__VA_ARGS__)
#define TRIGGER_CHECK(retCode, exper, ...) \
if (!(retCode)) { \
TRIGGER_LOGE(__VA_ARGS__); \
exper; \
}
#define COND_STACK_SIZE 20
#define TRIGGER_CMD "trigger "
#define TRIGGER_ARR_NAME_IN_JSON "jobs"
#define CMDS_ARR_NAME_IN_JSON "cmds"
typedef struct {
char data[COND_STACK_SIZE];
int top;
} CONDITION_STACK;
/*
on boot
exec /system/bin/sleep 4
start telephony_sa
*/
typedef enum {
TRIGGER_BOOT = 0,
TRIGGER_PROPERTY,
TRIGGER_UNKNOW,
TRIGGER_MAX
}TriggerType;
#define MAX_TRIGGER_CMD_NAME_LEN 16
#define MAX_TRIGGER_NAME_LEN 64
#define MAX_TRIGGER_TYPE_LEN 16
// Command对象列表主要存储每个triger需要执行那些Command操作。
typedef struct CommandNode {
atomic_uint_least32_t next;
char name[MAX_TRIGGER_CMD_NAME_LEN];
char content[0];
} CommandNode;
typedef struct {
atomic_uint_least32_t serial;
atomic_uint_least32_t next;
atomic_uint_least32_t firstCmd;
atomic_uint_least32_t lastCmd;
int type;
char name[MAX_TRIGGER_NAME_LEN];
char condition[0];
} TriggerNode;
typedef struct {
atomic_uint_least32_t serial;
u_int32_t dataSize;
u_int32_t startSize;
u_int32_t currOffset;
char data[0];
} TriggerArea;
typedef struct {
atomic_uint_least32_t firstTrigger;
atomic_uint_least32_t lastTrigger;
} TriggerHeader;
typedef struct {
u_int32_t *executeQueue;
u_int32_t queueCount;
u_int32_t startIndex;
u_int32_t endIndex;
pthread_mutex_t mutex;
} TriggerExecuteQueue;
typedef struct TriggerWorkSpace {
TriggerExecuteQueue executeQueue;
TriggerHeader header[TRIGGER_MAX];
TriggerArea *area;
} TriggerWorkSpace;
int InitTriggerWorkSpace(TriggerWorkSpace *workSpace);
int ParseTrigger(TriggerWorkSpace *workSpace, cJSON *triggerItem);
typedef int (*TRIGGER_MATCH)(TriggerNode *trigger, void *content, u_int32_t contentSize);
typedef int (*TRIGGER_CHECK_DONE) (TriggerNode *trigger, u_int32_t index);
typedef int (*CMD_EXECUTE) (TriggerNode *trigger, const char *cmdName, const char *command);
TriggerNode *GetTriggerByName(TriggerWorkSpace *workSpace, const char *triggerName, u_int32_t *triggerIndex);
int ExecuteTrigger(TriggerWorkSpace *workSpace, TriggerNode *trigger, CMD_EXECUTE cmdExecuter);
int CheckTrigger(TriggerWorkSpace *workSpace,
int type, void *content, u_int32_t contentSize, TRIGGER_CHECK_DONE triggerExecuter);
int CheckPropertyTrigger(TriggerWorkSpace *workSpace,
const char *content, u_int32_t contentSize, TRIGGER_CHECK_DONE triggerExecuter);
TriggerNode *ExecuteQueuePop(TriggerWorkSpace *workSpace);
int ExecuteQueuePush(TriggerWorkSpace *workSpace, TriggerNode *trigger, u_int32_t index);
int ExecuteQueueSize(TriggerWorkSpace *workSpace);
#define TRIGGER_NODE_IN_QUEUE(trigger) \
(atomic_load_explicit(&(trigger)->serial, memory_order_relaxed) & 0x01)
#define TRIGGER_NODE_SET_QUEUE_FLAG(trigger) \
atomic_store_explicit(&(trigger)->serial, (trigger)->serial | 0x01, memory_order_relaxed)
#define TRIGGER_NODE_CLEAR_QUEUE_FLAG(trigger) \
atomic_store_explicit(&(trigger)->serial, (trigger)->serial & ~0x01, memory_order_relaxed)
TriggerWorkSpace *GetTriggerWorkSpace();
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif // STARTUP_TRIGER_MANAGER_H

View File

@ -0,0 +1,225 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "trigger_processor.h"
#include <pthread.h>
#include <unistd.h>
#include "init_cmds.h"
#include "property_manager.h"
#include "trigger_checker.h"
#include "uv.h"
#define SYS_POWER_CTRL "sys.powerctrl."
static int g_triggerServiceStart = 0;
static TriggerWorkSpace g_triggerWorkSpace = {};
static int DoCmdExecute(TriggerNode *trigger, const char *cmdName, const char *command)
{
TRIGGER_CHECK(trigger != NULL && cmdName != NULL && command != NULL, return -1, "Invalid param");
if (strncmp(cmdName, TRIGGER_CMD, strlen(TRIGGER_CMD)) == 0) {
u_int32_t triggerIndex = 0;
TriggerNode *node = GetTriggerByName(&g_triggerWorkSpace, command, &triggerIndex);
if (node != NULL && !TRIGGER_NODE_IN_QUEUE(node)) { // 不在队列中
TRIGGER_LOGI("DoCmdExecute trigger %s", node->name);
TRIGGER_NODE_SET_QUEUE_FLAG(node);
ExecuteQueuePush(&g_triggerWorkSpace, node, triggerIndex);
}
return 0;
}
TRIGGER_LOGI("DoCmdExecute trigger %s cmd %s", trigger->name, cmdName);
DoCmdByName(cmdName, command);
return 0;
}
static int DoTiggerCheckResult(TriggerNode *trigger, u_int32_t triggerIndex)
{
// 直接执行,不需要添加队列
if (!g_triggerServiceStart) {
TRIGGER_LOGI("DoTiggerExecute trigger %s", trigger->name);
ExecuteTrigger(&g_triggerWorkSpace, trigger, DoCmdExecute);
TRIGGER_NODE_CLEAR_QUEUE_FLAG(trigger);
}
// 已经在队列中了,则不执行 TODO
if (TRIGGER_NODE_IN_QUEUE(trigger)) {
TRIGGER_LOGI("DoTiggerExecute trigger %s has been waiting execute", trigger->name);
return 0;
}
if (trigger->type == TRIGGER_BOOT) { // 立刻执行
TRIGGER_LOGI("DoTiggerExecute trigger %s", trigger->name);
ExecuteTrigger(&g_triggerWorkSpace, trigger, DoCmdExecute);
TRIGGER_NODE_CLEAR_QUEUE_FLAG(trigger);
} else {
TRIGGER_NODE_SET_QUEUE_FLAG(trigger);
TRIGGER_LOGI("DoTiggerExecute trigger %s", trigger->name);
ExecuteQueuePush(&g_triggerWorkSpace, trigger, triggerIndex);
}
return 0;
}
void ExecuteQueueWork()
{
int executeCount = 0;
TriggerNode *trigger = ExecuteQueuePop(&g_triggerWorkSpace);
while (trigger != NULL) {
ExecuteTrigger(&g_triggerWorkSpace, trigger, DoCmdExecute);
TRIGGER_NODE_CLEAR_QUEUE_FLAG(trigger);
executeCount++;
if (executeCount > 5) {
break;
}
trigger = ExecuteQueuePop(&g_triggerWorkSpace);
}
}
static void CheckTriggers(int type, void *content, u_int32_t contentLen)
{
switch (type) {
case EVENT_PROPERTY: {
CheckPropertyTrigger(&g_triggerWorkSpace, content, contentLen, DoTiggerCheckResult);
break;
}
case EVENT_BOOT: {
CheckTrigger(&g_triggerWorkSpace, TRIGGER_BOOT, content, contentLen, DoTiggerCheckResult);
break;
}
default:
TRIGGER_LOGI("CheckTriggers: %d", type);
break;
}
}
static void ProcessAfterEvent(uv_work_t *req, int status)
{
free(req);
ExecuteQueueWork();
}
static void ProcessEvent(uv_work_t *req)
{
TriggerDataEvent *event = (TriggerDataEvent *)req;
CheckTriggers(event->type, event->content, event->contentSize);
}
static const char *GetCmdInfo(const char *content, u_int32_t contentSize, char **cmdParam)
{
char cmd[MAX_CMD_NAME_LEN + 1] = { 0 };
int ret = GetValueFromContent(content, contentSize, 0, cmd, sizeof(cmd));
TRIGGER_CHECK(ret == 0, return NULL, "Failed parse cmd");
u_int32_t cmdLen = strlen(cmd);
TRIGGER_CHECK(cmdLen < MAX_CMD_NAME_LEN, return NULL, "Failed parse cmd");
cmd[cmdLen] = ' ';
cmd[cmdLen + 1] = '\0';
*cmdParam = content + cmdLen + 1;
return GetMatchCmd(cmd);
}
static void SendTriggerEvent(TriggerDataEvent *event)
{
int ctrlSize = strlen(SYS_POWER_CTRL);
if (strncmp(event->content, SYS_POWER_CTRL, ctrlSize) == 0) {
char *cmdParam = NULL;
const char *matchCmd = GetCmdInfo(event->content + ctrlSize, event->contentSize - ctrlSize, &cmdParam);
if (matchCmd != NULL) {
DoCmdByName(matchCmd, cmdParam);
} else {
TRIGGER_LOGE("SendTriggerEvent cmd %s not found", event->content);
}
free(event);
return;
}
if (event->type == EVENT_BOOT || g_triggerServiceStart == 0) {
CheckTriggers(EVENT_PROPERTY, event->content, event->contentSize);
free(event);
} else {
uv_queue_work(uv_default_loop(), &event->request, ProcessEvent, ProcessAfterEvent);
}
}
void PostPropertyTrigger(const char *name, const char *value)
{
TRIGGER_CHECK(name != NULL && value != NULL, return, "Invalid param");
TRIGGER_LOGI("PostPropertyTrigger %s ", name);
int contentLen = strlen(name) + strlen(value) + 2;
TriggerDataEvent *event = (TriggerDataEvent *)malloc(sizeof(TriggerDataEvent) + contentLen);
TRIGGER_CHECK(event != NULL, return, "Failed to alloc memory");
event->type = EVENT_PROPERTY;
event->request.data = (char*)event + sizeof(uv_work_t);
event->contentSize = BuildPropertyContent(event->content, contentLen, name, value);
TRIGGER_CHECK(event->contentSize > 0, return, "Failed to copy porperty");
SendTriggerEvent(event);
TRIGGER_LOGI("PostPropertyTrigger %s success", name);
}
void PostTrigger(EventType type, void *content, u_int32_t contentLen)
{
TRIGGER_LOGI("PostTrigger %d", type);
TRIGGER_CHECK(content != NULL && contentLen > 0, return, "Invalid param");
if (type == EVENT_BOOT || g_triggerServiceStart == 0) {
CheckTriggers(type, content, contentLen);
} else {
TriggerDataEvent *event = (TriggerDataEvent *)malloc(sizeof(TriggerDataEvent) + contentLen + 1);
TRIGGER_CHECK(event != NULL, return, "Failed to alloc memory");
event->type = type;
event->request.data = (char*)event + sizeof(uv_work_t);
event->contentSize = contentLen;
memcpy_s(event->content, contentLen, content, contentLen);
event->content[contentLen] = '\0';
SendTriggerEvent(event);
}
TRIGGER_LOGI("PostTrigger %d success", type);
}
void StartTriggerService()
{
TRIGGER_LOGI("StartTriggerService ");
g_triggerServiceStart = 1;
}
int ParseTriggerConfig(cJSON *fileRoot)
{
TRIGGER_CHECK(fileRoot != NULL, return -1, "Invalid file");
int ret = InitTriggerWorkSpace(&g_triggerWorkSpace);
TRIGGER_CHECK(ret == 0, return -1, "Failed to init trigger");
cJSON *triggers = cJSON_GetObjectItemCaseSensitive(fileRoot, TRIGGER_ARR_NAME_IN_JSON);
TRIGGER_CHECK(cJSON_IsArray(triggers), return -1, "Trigger item must array");
int size = cJSON_GetArraySize(triggers);
TRIGGER_CHECK(size > 0, return -1, "Trigger array size must positive");
for (int i = 0; i < size; ++i) {
cJSON *item = cJSON_GetArrayItem(triggers, i);
ParseTrigger(&g_triggerWorkSpace, item);
}
return 0;
}
void DoTriggerExec(const char *content)
{
TRIGGER_CHECK(content != NULL, return, "Invalid trigger content");
u_int32_t triggerIndex = 0;
TriggerNode *trigger = GetTriggerByName(&g_triggerWorkSpace, content, &triggerIndex);
if (trigger != NULL) {
ExecuteTrigger(&g_triggerWorkSpace, trigger, DoCmdExecute);
}
}
TriggerWorkSpace *GetTriggerWorkSpace()
{
return &g_triggerWorkSpace;
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef BASE_STARTUP_EVENT_MANAGER_H
#define BASE_STARTUP_EVENT_MANAGER_H
#include <stdio.h>
#include "property.h"
#include "trigger.h"
#include "trigger_manager.h"
#include "uv.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif
typedef struct TriggerEvent {
uv_work_t request;
EventType type;
} TriggerEvent;
typedef struct {
uv_work_t request;
EventType type;
u_int32_t contentSize;
char content[0];
} TriggerDataEvent;
typedef struct TriggerExecute {
TriggerEvent event;
TriggerNode *trigger;
} TriggerExecute;
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif