/* * 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.h" #include #include #include #include #include #include #include #include "init_adapter.h" #include "init_perms.h" #define CAP_NUM 2 // 240 seconds, 4 minutes 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; static int SetAllAmbientCapability() { for (int i = 0; i <= CAP_LAST_CAP; ++i) { if (SetAmbientCapability(i) != 0) { return SERVICE_FAILURE; } } return SERVICE_SUCCESS; } static int SetPerms(const Service *service) { if (KeepCapability() != 0) { return SERVICE_FAILURE; } if (setgid(service->servPerm.gID) != 0) { return SERVICE_FAILURE; } if (setuid(service->servPerm.uID) != 0) { return SERVICE_FAILURE; } // umask call always succeeds and return the previous mask value which is not needed here (void)umask(DEFAULT_UMASK_INIT); struct __user_cap_header_struct capHeader; capHeader.version = _LINUX_CAPABILITY_VERSION_3; capHeader.pid = 0; struct __user_cap_data_struct capData[CAP_NUM] = {0}; for (unsigned int i = 0; i < service->servPerm.capsCnt; ++i) { if (service->servPerm.caps[i] == FULL_CAP) { for (int i = 0; i < CAP_NUM; ++i) { capData[i].effective = FULL_CAP; capData[i].permitted = FULL_CAP; capData[i].inheritable = FULL_CAP; } break; } capData[CAP_TO_INDEX(service->servPerm.caps[i])].effective |= CAP_TO_MASK(service->servPerm.caps[i]); capData[CAP_TO_INDEX(service->servPerm.caps[i])].permitted |= CAP_TO_MASK(service->servPerm.caps[i]); capData[CAP_TO_INDEX(service->servPerm.caps[i])].inheritable |= CAP_TO_MASK(service->servPerm.caps[i]); } if (capset(&capHeader, capData) != 0) { return SERVICE_FAILURE; } for (unsigned int i = 0; i < service->servPerm.capsCnt; ++i) { if (service->servPerm.caps[i] == FULL_CAP) { return SetAllAmbientCapability(); } if (SetAmbientCapability(service->servPerm.caps[i]) != 0) { return SERVICE_FAILURE; } } return SERVICE_SUCCESS; } int ServiceStart(Service *service) { if (service == NULL) { printf("[Init] start service failed! null ptr.\n"); return SERVICE_FAILURE; } if (service->attribute & SERVICE_ATTR_INVALID) { printf("[Init] start service %s invalid.\n", service->name); return SERVICE_FAILURE; } struct stat pathStat = {0}; service->attribute &= (~(SERVICE_ATTR_NEED_RESTART | SERVICE_ATTR_NEED_STOP)); if (stat(service->pathArgs[0], &pathStat) != 0) { service->attribute |= SERVICE_ATTR_INVALID; printf("[Init] start service %s invalid, please check %s.\n",\ service->name, service->pathArgs[0]); return SERVICE_FAILURE; } int pid = fork(); if (pid == 0) { // permissions if (SetPerms(service) != SERVICE_SUCCESS) { printf("[Init] service %s exit! set perms failed! err %d.\n", service->name, errno); _exit(0x7f); // 0x7f: user specified } 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); } _exit(0x7f); // 0x7f: user specified } else if (pid < 0) { printf("[Init] start service %s fork failed!\n", service->name); return SERVICE_FAILURE; } service->pid = pid; printf("[Init] start service %s succeed, pid %d.\n", service->name, service->pid); return SERVICE_SUCCESS; } int ServiceStop(Service *service) { if (service == NULL) { printf("[Init] stop service failed! null ptr.\n"); return SERVICE_FAILURE; } service->attribute &= ~SERVICE_ATTR_NEED_RESTART; service->attribute |= SERVICE_ATTR_NEED_STOP; if (service->pid <= 0) { return SERVICE_SUCCESS; } if (kill(service->pid, SIGKILL) != 0) { printf("[Init] stop service %s pid %d failed! err %d.\n", service->name, service->pid, errno); return SERVICE_FAILURE; } printf("[Init] stop service %s, pid %d.\n", service->name, service->pid); return SERVICE_SUCCESS; } void ServiceReap(Service *service) { if (service == NULL) { printf("[Init] reap service failed! null ptr.\n"); return; } 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); service->crashCnt = 0; return; } // for one-shot service if (service->attribute & SERVICE_ATTR_ONCE) { // no need to restart if (!(service->attribute & SERVICE_ATTR_NEED_RESTART)) { service->attribute &= (~SERVICE_ATTR_NEED_STOP); return; } // the service could be restart even if it is one-shot service } // the service that does not need to be restarted restarts, indicating that it has crashed if (!(service->attribute & SERVICE_ATTR_NEED_RESTART)) { // crash time and count check time_t curTime = time(NULL); if (service->crashCnt == 0) { service->firstCrashTime = curTime; ++service->crashCnt; } else if (difftime(curTime, service->firstCrashTime) > CRASH_TIME_LIMIT) { service->firstCrashTime = curTime; service->crashCnt = 1; } else { ++service->crashCnt; if (service->crashCnt > CRASH_COUNT_LIMIT) { printf("[Init] reap service %s, crash too many times!\n", service->name); return; } } } int ret = ServiceStart(service); if (ret != SERVICE_SUCCESS) { printf("[Init] reap service %s start failed!\n", service->name); } service->attribute &= (~SERVICE_ATTR_NEED_RESTART); }