init_common_service.c 12.5 KB
Newer Older
W
wenjun 已提交
1
/*
Z
zhong_ning 已提交
2
 * Copyright (c) 2021 Huawei Device Co., Ltd.
W
wenjun 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15
 * 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"
16

W
wenjun 已提交
17
#include <errno.h>
Z
zhong_ning 已提交
18
#include <fcntl.h>
W
wenjun 已提交
19
#include <signal.h>
Z
zhong_ning 已提交
20
#include <stdlib.h>
M
mamingshuai 已提交
21
#include <string.h>
Z
zhong_ning 已提交
22 23 24
#ifdef __MUSL__
#include <stropts.h>
#endif
25
#include <sys/capability.h>
Z
zhong_ning 已提交
26
#include <sys/param.h>
W
wenjun 已提交
27 28 29
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
30

31
#include "init.h"
W
wenjun 已提交
32
#include "init_adapter.h"
Z
zhong_ning 已提交
33 34 35
#include "init_cmds.h"
#include "init_log.h"
#include "init_service_socket.h"
Z
zhong_ning 已提交
36
#include "init_utils.h"
Z
zhong_ning 已提交
37
#include "securec.h"
W
wenjun 已提交
38

Q
Qin Fandong 已提交
39
#ifdef WITH_SELINUX
X
xionglei6 已提交
40 41
#include "init_selinux_param.h"
#include <selinux/selinux.h>
Q
Qin Fandong 已提交
42 43
#endif // WITH_SELINUX

Z
zhong_ning 已提交
44 45 46
#ifndef TIOCSCTTY
#define TIOCSCTTY 0x540E
#endif
W
wenjun 已提交
47
// 240 seconds, 4 minutes
48
static const int CRASH_TIME_LIMIT = 240;
W
wenjun 已提交
49 50 51
// maximum number of crashes within time CRASH_TIME_LIMIT for one service
static const int CRASH_COUNT_LIMIT = 4;

Z
zhong_ning 已提交
52
// 240 seconds, 4 minutes
53
static const int CRITICAL_CRASH_TIME_LIMIT = 240;
Z
zhong_ning 已提交
54 55 56
// maximum number of crashes within time CRITICAL_CRASH_TIME_LIMIT for one service
static const int CRITICAL_CRASH_COUNT_LIMIT = 4;

S
sun_fan 已提交
57
static int SetAllAmbientCapability(void)
M
mamingshuai 已提交
58 59 60 61 62 63 64 65 66
{
    for (int i = 0; i <= CAP_LAST_CAP; ++i) {
        if (SetAmbientCapability(i) != 0) {
            return SERVICE_FAILURE;
        }
    }
    return SERVICE_SUCCESS;
}

W
wenjun 已提交
67 68
static int SetPerms(const Service *service)
{
Z
zhong_ning 已提交
69
    INIT_CHECK_RETURN_VALUE(KeepCapability() == 0, SERVICE_FAILURE);
Z
zhong_ning 已提交
70
    if (service->servPerm.gIDCnt > 0) {
Z
zhong_ning 已提交
71 72
        INIT_ERROR_CHECK(setgid(service->servPerm.gIDArray[0]) == 0, return SERVICE_FAILURE,
            "SetPerms, setgid for %s failed. %d", service->name, errno);
Z
zhong_ning 已提交
73 74 75 76
    }
    if (service->servPerm.gIDCnt > 1) {
        INIT_ERROR_CHECK(setgroups(service->servPerm.gIDCnt - 1, &service->servPerm.gIDArray[1]) == 0,
             return SERVICE_FAILURE,
Z
zhong_ning 已提交
77
            "SetPerms, setgroups failed. errno = %d, gIDCnt=%d", errno, service->servPerm.gIDCnt);
W
wenjun 已提交
78
    }
Z
zhong_ning 已提交
79 80
    if (service->servPerm.uID != 0) {
        if (setuid(service->servPerm.uID) != 0) {
Z
zhong_ning 已提交
81
            INIT_LOGE("setuid of service: %s failed, uid = %d", service->name, service->servPerm.uID);
Z
zhong_ning 已提交
82 83
            return SERVICE_FAILURE;
        }
W
wenjun 已提交
84 85 86 87 88 89 90 91
    }

    // 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;
Z
zhong_ning 已提交
92
    struct __user_cap_data_struct capData[CAP_NUM] = {};
W
wenjun 已提交
93 94
    for (unsigned int i = 0; i < service->servPerm.capsCnt; ++i) {
        if (service->servPerm.caps[i] == FULL_CAP) {
95 96 97 98
            for (int j = 0; j < CAP_NUM; ++j) {
                capData[j].effective = FULL_CAP;
                capData[j].permitted = FULL_CAP;
                capData[j].inheritable = FULL_CAP;
W
wenjun 已提交
99 100 101 102 103 104 105 106 107
            }
            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) {
Z
zhong_ning 已提交
108
        INIT_LOGE("capset faild for service: %s, error: %d", service->name, errno);
W
wenjun 已提交
109 110
        return SERVICE_FAILURE;
    }
M
mamingshuai 已提交
111 112 113 114 115
    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) {
Z
zhong_ning 已提交
116
            INIT_LOGE("SetAmbientCapability faild for service: %s", service->name);
M
mamingshuai 已提交
117 118 119
            return SERVICE_FAILURE;
        }
    }
W
wenjun 已提交
120 121 122
    return SERVICE_SUCCESS;
}

S
sun_fan 已提交
123
static void OpenConsole(void)
Z
zhong_ning 已提交
124
{
125
    const int stdError = 2;
Z
zhong_ning 已提交
126 127 128 129 130 131 132
    setsid();
    WaitForFile("/dev/console", WAIT_MAX_COUNT);
    int fd = open("/dev/console", O_RDWR);
    if (fd >= 0) {
        ioctl(fd, TIOCSCTTY, 0);
        dup2(fd, 0);
        dup2(fd, 1);
133
        dup2(fd, stdError); // Redirect fd to 0, 1, 2
Z
zhong_ning 已提交
134 135
        close(fd);
    } else {
Z
zhong_ning 已提交
136
        INIT_LOGE("Open /dev/console failed. err = %d", errno);
Z
zhong_ning 已提交
137 138 139 140
    }
    return;
}

141
static int WritePid(const Service *service)
S
sun_fan 已提交
142
{
143 144 145 146
    const int maxPidStrLen = 50;
    char pidString[maxPidStrLen];
    pid_t childPid = getpid();
    int len = snprintf_s(pidString, maxPidStrLen, maxPidStrLen - 1, "%d", childPid);
X
add ut  
xionglei6 已提交
147
    INIT_ERROR_CHECK(len > 0, return SERVICE_FAILURE, "Failed to format pid for service %s", service->name);
148 149
    for (int i = 0; i < service->writePidArgs.count; i++) {
        if (service->writePidArgs.argv[i] == NULL) {
S
sun_fan 已提交
150
            continue;
S
sun_fan 已提交
151
        }
152 153 154 155 156 157 158 159
        FILE *fd = NULL;
        char *realPath = GetRealPath(service->writePidArgs.argv[i]);
        if (realPath != NULL) {
            fd = fopen(realPath, "wb");
        } else {
            fd = fopen(service->writePidArgs.argv[i], "wb");
        }
        if (fd != NULL) {
X
add ut  
xionglei6 已提交
160 161
            INIT_CHECK_ONLY_ELOG((int)fwrite(pidString, 1, len, fd) == len,
                "Failed to write %s pid:%s", service->writePidArgs.argv[i], pidString);
162 163 164 165 166 167 168 169
            (void)fclose(fd);
        } else {
            INIT_LOGE("Failed to open %s.", service->writePidArgs.argv[i]);
        }
        if (realPath != NULL) {
            free(realPath);
        }
        INIT_LOGD("ServiceStart writepid filename=%s, childPid=%s, ok", service->writePidArgs.argv[i], pidString);
S
sun_fan 已提交
170
    }
171
    return SERVICE_SUCCESS;
S
sun_fan 已提交
172 173
}

X
xionglei6 已提交
174
void SetSecon(Service *service)
Q
Qin Fandong 已提交
175 176 177 178 179 180 181 182 183 184 185 186
{
#ifdef WITH_SELINUX
    if (*(service->secon)) {
        if (setexeccon(service->secon) < 0) {
            INIT_LOGE("failed to set service %s's secon (%s).", service->name, service->secon);
        } else {
            INIT_LOGI("service %s secon set to %s.", service->name, service->secon);
        }
    }
#endif // WITH_SELINUX
}

W
wenjun 已提交
187 188
int ServiceStart(Service *service)
{
S
sun_fan 已提交
189
    INIT_ERROR_CHECK(service != NULL, return SERVICE_FAILURE, "start service failed! null ptr.");
190 191 192
    INIT_ERROR_CHECK(service->pid <= 0, return SERVICE_SUCCESS, "service : %s had started already.", service->name);
    INIT_ERROR_CHECK(service->pathArgs.count > 0,
        return SERVICE_FAILURE, "start service %s pathArgs is NULL.", service->name);
W
wenjun 已提交
193
    if (service->attribute & SERVICE_ATTR_INVALID) {
Z
zhong_ning 已提交
194
        INIT_LOGE("start service %s invalid.", service->name);
W
wenjun 已提交
195 196
        return SERVICE_FAILURE;
    }
197
    struct stat pathStat = { 0 };
W
wenjun 已提交
198
    service->attribute &= (~(SERVICE_ATTR_NEED_RESTART | SERVICE_ATTR_NEED_STOP));
199 200 201 202 203 204
    if (stat(service->pathArgs.argv[0], &pathStat) != 0) {
        service->attribute |= SERVICE_ATTR_INVALID;
        INIT_LOGE("start service %s invalid, please check %s.", service->name, service->pathArgs.argv[0]);
        return SERVICE_FAILURE;
    }
    int pid = fork();
W
wenjun 已提交
205
    if (pid == 0) {
206
        int ret = CreateServiceSocket(service->socketCfg);
X
add ut  
xionglei6 已提交
207
        INIT_ERROR_CHECK(ret >= 0, _exit(PROCESS_EXIT_CODE), "service %s exit! create socket failed!", service->name);
X
xionglei6 已提交
208
        CreateServiceFile(service->fileCfg);
Z
zhong_ning 已提交
209 210 211
        if (service->attribute & SERVICE_ATTR_CONSOLE) {
            OpenConsole();
        }
212
        // permissions
X
add ut  
xionglei6 已提交
213 214
        INIT_ERROR_CHECK(SetPerms(service) == SERVICE_SUCCESS, _exit(PROCESS_EXIT_CODE),
            "service %s exit! set perms failed! err %d.", service->name, errno);
215
        // write pid
X
add ut  
xionglei6 已提交
216 217
        INIT_ERROR_CHECK(WritePid(service) == SERVICE_SUCCESS, _exit(PROCESS_EXIT_CODE),
            "service %s exit! write pid failed!", service->name);
Q
Qin Fandong 已提交
218
        SetSecon(service);
219 220
        ServiceExec(service);
        _exit(PROCESS_EXIT_CODE);
W
wenjun 已提交
221
    } else if (pid < 0) {
Z
zhong_ning 已提交
222
        INIT_LOGE("start service %s fork failed!", service->name);
W
wenjun 已提交
223 224
        return SERVICE_FAILURE;
    }
225
    INIT_LOGI("service %s starting pid %d", service->name, pid);
W
wenjun 已提交
226
    service->pid = pid;
227
    NotifyServiceChange(service->name, "running");
W
wenjun 已提交
228 229 230 231 232
    return SERVICE_SUCCESS;
}

int ServiceStop(Service *service)
{
233
    INIT_ERROR_CHECK(service != NULL, return SERVICE_FAILURE, "stop service failed! null ptr.");
W
wenjun 已提交
234 235 236 237 238
    service->attribute &= ~SERVICE_ATTR_NEED_RESTART;
    service->attribute |= SERVICE_ATTR_NEED_STOP;
    if (service->pid <= 0) {
        return SERVICE_SUCCESS;
    }
239
    CloseServiceSocket(service->socketCfg);
X
xionglei6 已提交
240
    CloseServiceFile(service->fileCfg);
X
add ut  
xionglei6 已提交
241 242
    INIT_ERROR_CHECK(kill(service->pid, SIGKILL) == 0, return SERVICE_FAILURE,
        "stop service %s pid %d failed! err %d.", service->name, service->pid, errno);
243
    NotifyServiceChange(service->name, "stopping");
Z
zhong_ning 已提交
244
    INIT_LOGI("stop service %s, pid %d.", service->name, service->pid);
W
wenjun 已提交
245 246 247
    return SERVICE_SUCCESS;
}

S
sun_fan 已提交
248
static bool CalculateCrashTime(Service *service, int crashTimeLimit, int crashCountLimit)
Z
zhong_ning 已提交
249
{
S
sun_fan 已提交
250 251 252 253 254 255 256 257 258 259 260 261 262
    INIT_ERROR_CHECK(service != NULL && crashTimeLimit > 0 && crashCountLimit > 0, return 0,
        "Service name=%s, input params error.", service->name);
    time_t curTime = time(NULL);
    if (service->crashCnt == 0) {
        service->firstCrashTime = curTime;
        ++service->crashCnt;
    } else if (difftime(curTime, service->firstCrashTime) > crashTimeLimit) {
        service->firstCrashTime = curTime;
        service->crashCnt = 1;
    } else {
        ++service->crashCnt;
        if (service->crashCnt > crashCountLimit) {
            return false;
Z
zhong_ning 已提交
263 264
        }
    }
S
sun_fan 已提交
265
    return true;
Z
zhong_ning 已提交
266 267 268 269
}

static int ExecRestartCmd(const Service *service)
{
270 271
    INIT_ERROR_CHECK(service != NULL, return SERVICE_FAILURE, "Exec service failed! null ptr.");
    INIT_ERROR_CHECK(service->restartArg != NULL, return SERVICE_FAILURE, "restartArg is null");
Z
zhong_ning 已提交
272

273 274 275
    for (int i = 0; i < service->restartArg->cmdNum; i++) {
        INIT_LOGI("ExecRestartCmd cmdLine->cmdContent %s ", service->restartArg->cmds[i].cmdContent);
        DoCmdByIndex(service->restartArg->cmds[i].cmdIndex, service->restartArg->cmds[i].cmdContent);
Z
zhong_ning 已提交
276
    }
277
    free(service->restartArg);
Z
zhong_ning 已提交
278 279 280
    return SERVICE_SUCCESS;
}

W
wenjun 已提交
281 282
void ServiceReap(Service *service)
{
283 284
    INIT_CHECK(service != NULL, return);
    INIT_LOGI("Reap service %s, pid %d.", service->name, service->pid);
Z
zhong_ning 已提交
285
    service->pid = -1;
286 287
    NotifyServiceChange(service->name, "stopped");

Z
zhong_ning 已提交
288
    if (service->attribute & SERVICE_ATTR_INVALID) {
289
        INIT_LOGE("Reap service %s invalid.", service->name);
M
mamingshuai 已提交
290 291
        return;
    }
292 293

    CloseServiceSocket(service->socketCfg);
X
xionglei6 已提交
294
    CloseServiceFile(service->fileCfg);
W
wenjun 已提交
295 296 297 298 299 300
    // 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;
    }
301

W
wenjun 已提交
302 303 304 305 306 307 308
    // 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;
        }
309
        // the service could be restart even if it is one-shot service
W
wenjun 已提交
310
    }
311 312

    if (service->attribute & SERVICE_ATTR_CRITICAL) { // critical
S
sun_fan 已提交
313 314 315
        if (CalculateCrashTime(service, CRITICAL_CRASH_TIME_LIMIT, CRITICAL_CRASH_COUNT_LIMIT) == false) {
            INIT_LOGE("Critical service \" %s \" crashed %d times, rebooting system",
                service->name, CRITICAL_CRASH_COUNT_LIMIT);
316
            ExecReboot("reboot");
S
sun_fan 已提交
317 318 319 320 321
        }
    } else if (!(service->attribute & SERVICE_ATTR_NEED_RESTART)) {
        if (CalculateCrashTime(service, CRASH_TIME_LIMIT, CRASH_COUNT_LIMIT) == false) {
            INIT_LOGE("Service name=%s, crash %d times, no more start.", service->name, CRASH_COUNT_LIMIT);
            return;
W
wenjun 已提交
322 323
        }
    }
324 325 326 327

    int ret = 0;
    if (service->restartArg != NULL) {
        ret = ExecRestartCmd(service);
X
add ut  
xionglei6 已提交
328
        INIT_CHECK_ONLY_ELOG(ret == SERVICE_SUCCESS, "Failed to exec restartArg for %s", service->name);
329 330 331 332
    }
    ret = ServiceStart(service);
    if (ret != SERVICE_SUCCESS) {
        INIT_LOGE("reap service %s start failed!", service->name);
Z
zhong_ning 已提交
333
    }
W
wenjun 已提交
334 335
    service->attribute &= (~SERVICE_ATTR_NEED_RESTART);
}