* 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 <errno.h>
#include <limits.h>
#include <sched.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <getopt.h>
#include "securec.h"
#include "parameter.h"
#include "malloc.h"
#include "devhost_dump.h"
#include "devhost_service.h"
#include "devhost_service_full.h"
#include "hdf_base.h"
#include "hdf_core_log.h"
#include "hdf_power_manager.h"
#define HDF_LOG_TAG hdf_device_host
#define DEVHOST_MIN_INPUT_PARAM_NUM 5
#define DEVHOST_INPUT_PARAM_HOSTID_POS 1
#define DEVHOST_HOST_NAME_POS 2
#define DEVHOST_PROCESS_PRI_POS 3
#define DEVHOST_THREAD_PRI_POS 4
#define PARAM_BUF_LEN 128
#define MALLOPT_PARA_CNT 2
#define INVALID_PRIORITY "0"
#define DEVHOST_ARGUMENT 2
typedef struct {
int hostId;
const char *hostName;
int schedPriority;
int processPriority;
int malloptKey;
int malloptValue;
} HostConfig;
bool HdfStringToInt(const char *str, int *value)
{
if (str == NULL || value == NULL) {
return false;
}
char *end = NULL;
errno = 0;
const int base = 10;
long result = strtol(str, &end, base);
if (end == str || end[0] != '\0' || errno == ERANGE || result > INT_MAX || result < INT_MIN) {
return false;
}
*value = (int)result;
return true;
}
static void SetMallopt(int32_t malloptKey, int32_t malloptValue)
{
int ret = mallopt(malloptKey, malloptValue);
if (ret != 1) {
HDF_LOGE("mallopt failed, malloptKey:%{public}d, malloptValue:%{public}d, ret:%{public}d",
malloptKey, malloptValue, ret);
return;
}
HDF_LOGI("host set mallopt succeed, mallopt:%{public}d %{public}d", malloptKey, malloptValue);
}
typedef int32_t (*CommandFunc)(const char *key, const char *value, HostConfig *config);
typedef struct CommandToFunc {
const char *opt;
CommandFunc func;
} CommandToFunc;
static int32_t CommandIFunc(const char *key, const char *value, HostConfig *config)
{
(void)key;
if (!HdfStringToInt(value, &config->hostId)) {
HDF_LOGE("Invalid process ID: %{public}s", value);
return HDF_ERR_INVALID_PARAM;
}
return HDF_SUCCESS;
}
static int32_t CommandNFunc(const char *key, const char *value, HostConfig *config)
{
(void)key;
config->hostName = value;
return HDF_SUCCESS;
}
static int32_t CommandPFunc(const char *key, const char *value, HostConfig *config)
{
(void)key;
if (!HdfStringToInt(value, &config->processPriority)) {
HDF_LOGE("Invalid process ID: %{public}s", value);
}
return HDF_SUCCESS;
}
static int32_t CommandSFunc(const char *key, const char *value, HostConfig *config)
{
(void)key;
if (!HdfStringToInt(value, &config->schedPriority)) {
HDF_LOGE("Invalid process ID: %{public}s", value);
}
return HDF_SUCCESS;
}
static int32_t CommandMFunc(const char *key, const char *value, HostConfig *config)
{
(void)key;
if (!HdfStringToInt(value, &config->malloptKey)) {
HDF_LOGE("Invalid process ID: %{public}s", value);
}
return HDF_SUCCESS;
}
static int32_t CommandVFunc(const char *key, const char *value, HostConfig *config)
{
(void)key;
if (!HdfStringToInt(value, &config->malloptValue)) {
HDF_LOGE("Invalid process ID: %{public}s", value);
}
return HDF_SUCCESS;
}
static int32_t CommandNumFunc(const char *key, const char *value, HostConfig *config)
{
int32_t malloptKey = 0;
int32_t malloptValue = 0;
if (!HdfStringToInt(value, &malloptValue)) {
HDF_LOGE("Invalid value: %{public}s", value);
return HDF_FAILURE;
}
if (!HdfStringToInt(key, &malloptKey)) {
HDF_LOGE("Invalid key: %{public}s", key);
return HDF_FAILURE;
}
SetMallopt(malloptKey, malloptValue);
return HDF_SUCCESS;
}
static struct CommandToFunc g_commandFuncs[] = {
{"-i", CommandIFunc},
{"-n", CommandNFunc},
{"-p", CommandPFunc},
{"-s", CommandSFunc},
{"-m", CommandMFunc},
{"-v", CommandVFunc},
{"-1005", CommandNumFunc},
{"-1006", CommandNumFunc},
{"-1007", CommandNumFunc},
{"-1008", CommandNumFunc},
{"-1009", CommandNumFunc},
{"-1010", CommandNumFunc},
{"-1011", CommandNumFunc},
{"-1012", CommandNumFunc},
{"-1013", CommandNumFunc},
{"-1014", CommandNumFunc},
};
static void StartMemoryHook(const char* processName)
{
const char defaultValue[PARAM_BUF_LEN] = { 0 };
const char targetPrefix[] = "startup:";
const int targetPrefixLen = strlen(targetPrefix);
char paramValue[PARAM_BUF_LEN] = { 0 };
int retParam = GetParameter("libc.hook_mode", defaultValue, paramValue, sizeof(paramValue));
if (retParam <= 0 || strncmp(paramValue, targetPrefix, targetPrefixLen) != 0) {
return;
}
if (strstr(paramValue + targetPrefixLen, processName) != NULL) {
const int hookSignal = 36;
HDF_LOGE("raise hook signal %{public}d to %{public}s", hookSignal, processName);
raise(hookSignal);
}
}
static void SetProcTitle(char **argv, const char *newTitle)
{
if (newTitle == NULL) {
HDF_LOGE("failed because newTitle is NULL");
return;
}
size_t len = strlen(argv[0]);
if (strlen(newTitle) > len) {
HDF_LOGE("failed to set new process title because the '%{public}s' is too long", newTitle);
return;
}
(void)memset_s(argv[0], len, 0, len);
if (strcpy_s(argv[0], len + 1, newTitle) != EOK) {
HDF_LOGE("failed to set new process title");
return;
}
prctl(PR_SET_NAME, newTitle);
}
static void HdfSetProcPriority(int32_t procPriority, int32_t schedPriority)
{
if (setpriority(PRIO_PROCESS, 0, procPriority) != 0) {
HDF_LOGE("host setpriority failed: %{public}d", errno);
}
struct sched_param param = {0};
param.sched_priority = schedPriority;
if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) {
HDF_LOGE("host sched_setscheduler failed: %{public}d", errno);
} else {
HDF_LOGI("host sched_setscheduler succeed:%{public}d %{public}d", procPriority, schedPriority);
}
}
static int FindFunc(const char* arg, const char* value, HostConfig *config)
{
for (size_t i = 0; i < sizeof(g_commandFuncs) / sizeof(g_commandFuncs[0]); ++i) {
if (strcmp(g_commandFuncs[i].opt, arg) == 0) {
return g_commandFuncs[i].func(arg, value, config);
}
}
HDF_LOGE("FindFunc failed: %{public}s", arg);
return HDF_FAILURE;
}
static int ParseCommandLineArgs(int argc, char **argv, HostConfig *config)
{
for (int i = 1; i < argc; i += DEVHOST_ARGUMENT) {
const char* arg = argv[i];
if (arg == NULL) {
HDF_LOGE("NULL argument:arg");
continue;
}
int valueIndex = i + 1;
if (valueIndex >= argc) {
HDF_LOGE("Missing argument for %{public}s", arg);
continue;
}
const char* value = argv[valueIndex];
if (value == NULL) {
HDF_LOGE("NULL argument: value");
continue;
}
if (FindFunc(arg, value, config) == HDF_ERR_INVALID_PARAM) {
return HDF_ERR_INVALID_PARAM;
}
}
return HDF_SUCCESS;
}
static int InitializeHost(const HostConfig *config, char **argv)
{
prctl(PR_SET_PDEATHSIG, SIGKILL);
HDF_LOGI("hdf device host %{public}s %{public}d start", config->hostName, config->hostId);
SetProcTitle(argv, config->hostName);
StartMemoryHook(config->hostName);
if ((config->processPriority != 0) && (config->schedPriority != 0)) {
HdfSetProcPriority(config->processPriority, config->schedPriority);
}
if ((config->malloptKey != 0) && (config->malloptValue != 0)) {
SetMallopt(config->malloptKey, config->malloptValue);
}
return HDF_SUCCESS;
}
static int StartHostService(const HostConfig *config)
{
struct IDevHostService *instance = DevHostServiceNewInstance(config->hostId, config->hostName);
if (instance == NULL || instance->StartService == NULL) {
HDF_LOGE("DevHostServiceGetInstance fail");
return HDF_ERR_INVALID_OBJECT;
}
HDF_LOGD("create IDevHostService of %{public}s success", config->hostName);
DevHostDumpInit();
HDF_LOGD("%{public}s start device service begin", config->hostName);
int status = instance->StartService(instance);
if (status != HDF_SUCCESS) {
HDF_LOGE("Devhost StartService fail, return: %{public}d", status);
DevHostServiceFreeInstance(instance);
DevHostDumpDeInit();
return status;
}
HdfPowerManagerInit();
struct DevHostServiceFull *fullService = (struct DevHostServiceFull *)instance;
struct HdfMessageLooper *looper = &fullService->looper;
if ((looper != NULL) && (looper->Start != NULL)) {
HDF_LOGI("%{public}s start loop", config->hostName);
looper->Start(looper);
}
DevHostServiceFreeInstance(instance);
HdfPowerManagerExit();
DevHostDumpDeInit();
return HDF_SUCCESS;
}
int main(int argc, char **argv)
{
if (argc < DEVHOST_MIN_INPUT_PARAM_NUM) {
HDF_LOGE("Devhost main parameter error, argc: %{public}d", argc);
return HDF_ERR_INVALID_PARAM;
}
HostConfig config = {0};
if (ParseCommandLineArgs(argc, argv, &config) != HDF_SUCCESS) {
HDF_LOGE("ParseCommandLineArgs(argc, argv, &config) != HDF_SUCCESS");
return HDF_ERR_INVALID_PARAM;
}
if (InitializeHost(&config, argv) != HDF_SUCCESS) {
return HDF_ERR_INVALID_PARAM;
}
int status = StartHostService(&config);
HDF_LOGI("hdf device host %{public}s %{public}d exit", config.hostName, config.hostId);
return status;
}