1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15#include "init_context.h"
16
17#include <poll.h>
18#ifdef WITH_SELINUX
19#include <policycoreutils.h>
20#include <selinux/selinux.h>
21#endif
22#include <sys/prctl.h>
23#include <sys/types.h>
24#include <sys/socket.h>
25#include <unistd.h>
26
27#include "init_module_engine.h"
28#include "plugin_adapter.h"
29#include "init_cmds.h"
30#include "init_utils.h"
31#include "securec.h"
32
33#ifdef STARTUP_INIT_TEST
34#define TIMEOUT_DEF  2
35#else
36#define TIMEOUT_DEF  5
37#endif
38
39static SubInitInfo g_subInitInfo[INIT_CONTEXT_MAIN] = {};
40static const char *g_subContext[INIT_CONTEXT_MAIN] = {
41    "u:r:chipset_init:s0"
42};
43
44static void SubInitMain(InitContextType type, int readFd, int writeFd);
45static void HandleRecvMessage(SubInitInfo *subInfo, char *buffer, uint32_t size);
46static int CreateSocketPair(int socket[2]);
47static int SubInitSetSelinuxContext(InitContextType type);
48
49static int SubInitRun(const SubInitForkArg *arg)
50{
51    PLUGIN_LOGW("SubInitRun %d ", arg->type);
52    SubInitSetSelinuxContext(arg->type);
53#ifndef STARTUP_INIT_TEST
54    close(arg->socket[0]);
55#endif
56    SubInitMain(arg->type, arg->socket[1], arg->socket[1]);
57    close(arg->socket[1]);
58#ifndef STARTUP_INIT_TEST
59    _exit(PROCESS_EXIT_CODE);
60#else
61    return 0;
62#endif
63}
64
65#ifndef STARTUP_INIT_TEST
66pid_t SubInitFork(int (*childFunc)(const SubInitForkArg *arg), const SubInitForkArg *args)
67{
68    pid_t pid = fork();
69    if (pid == 0) {
70        childFunc(args);
71    }
72    return pid;
73}
74#endif
75
76static int SubInitStart(InitContextType type)
77{
78    PLUGIN_CHECK(type < INIT_CONTEXT_MAIN, return -1, "Invalid type %d", type);
79    SubInitInfo *subInfo = &g_subInitInfo[type];
80    if (subInfo->state != SUB_INIT_STATE_IDLE) {
81        return 0;
82    }
83    SubInitForkArg arg = { 0 };
84    arg.type = type;
85    int ret = CreateSocketPair(arg.socket);
86    PLUGIN_CHECK(ret == 0, return -1, "Failed to create socket for %d", type);
87
88    subInfo->state = SUB_INIT_STATE_STARTING;
89    pid_t pid = SubInitFork(SubInitRun, &arg);
90    if (pid < 0) {
91        close(arg.socket[0]);
92        close(arg.socket[1]);
93        subInfo->state = SUB_INIT_STATE_IDLE;
94        return -1;
95    }
96#ifndef STARTUP_INIT_TEST
97    close(arg.socket[1]);
98#endif
99    subInfo->sendFd = arg.socket[0];
100    subInfo->recvFd = arg.socket[0];
101    subInfo->state = SUB_INIT_STATE_RUNNING;
102    subInfo->subPid = pid;
103    return 0;
104}
105
106static void SubInitStop(pid_t pid)
107{
108    for (size_t i = 0; i < ARRAY_LENGTH(g_subInitInfo); i++) {
109        if (g_subInitInfo[i].subPid == pid) {
110            close(g_subInitInfo[i].sendFd);
111            g_subInitInfo[i].subPid = 0;
112            g_subInitInfo[i].state = SUB_INIT_STATE_IDLE;
113        }
114    }
115}
116
117static int SubInitExecuteCmd(InitContextType type, const char *name, const char *cmdContent)
118{
119    static char buffer[MAX_CMD_LEN] = {0};
120    PLUGIN_CHECK(type < INIT_CONTEXT_MAIN, return -1, "Invalid type %d", type);
121    PLUGIN_CHECK(name != NULL, return -1, "Invalid cmd name");
122    SubInitInfo *subInfo = &g_subInitInfo[type];
123    PLUGIN_CHECK(subInfo->state == SUB_INIT_STATE_RUNNING, return -1, "Sub init %d is not running ", type);
124
125    int len = 0;
126    if (cmdContent != NULL) {
127        len = snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%s %s", name, cmdContent);
128    } else {
129        len = snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%s ", name);
130    }
131    PLUGIN_CHECK(len > 0, return -1, "Failed to format cmd %s", name);
132    buffer[len] = '\0';
133    PLUGIN_LOGV("send cmd '%s'", buffer);
134    int ret = send(subInfo->sendFd, buffer, len, 0);
135    if (ret < 0 && errno == EPIPE) {
136        PLUGIN_LOGI("Failed to send cmd %s to %d, need fork new chip init process", name, subInfo->type);
137        SubInitStop(subInfo->subPid);
138        SubInitStart(type);
139        ret = send(subInfo->sendFd, buffer, len, 0);
140    }
141    PLUGIN_CHECK(ret > 0, return errno, "Failed to send cmd %s to %d errno %d", name, subInfo->type, errno);
142
143    // block and wait result
144    ssize_t rLen = TEMP_FAILURE_RETRY(read(subInfo->recvFd, buffer, sizeof(buffer)));
145    while ((rLen < 0) && (errno == EAGAIN)) {
146        rLen = TEMP_FAILURE_RETRY(read(subInfo->recvFd, buffer, sizeof(buffer)));
147    }
148    PLUGIN_CHECK(rLen >= 0 && (size_t)rLen < sizeof(buffer), return errno,
149        "Failed to read result from %d for cmd %s errno %d", subInfo->type, name, errno);
150    // change to result
151    buffer[rLen] = '\0';
152    PLUGIN_LOGV("recv cmd result %s", buffer);
153    return atoi(buffer);
154}
155
156static int CreateSocketPair(int socket[2])
157{
158    int ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, socket);
159    PLUGIN_CHECK(ret == 0, return -1, "Create socket fail errno %d", errno);
160
161    int opt = 1;
162    struct timeval timeout = {TIMEOUT_DEF, 0};
163    do {
164        ret = setsockopt(socket[0], SOL_SOCKET, SO_PASSCRED, &opt, sizeof(opt));
165        PLUGIN_CHECK(ret == 0, break, "Failed to set opt for %d errno %d", socket[0], errno);
166        ret = setsockopt(socket[1], SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
167        PLUGIN_CHECK(ret == 0, break, "Failed to set opt for %d errno %d", socket[1], errno);
168        ret = setsockopt(socket[0], SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
169        PLUGIN_CHECK(ret == 0, break, "Failed to set opt for %d errno %d", socket[0], errno);
170    } while (0);
171    if (ret != 0) {
172        close(socket[0]);
173        close(socket[1]);
174    }
175    return ret;
176}
177
178static int CheckSocketPermission(const SubInitInfo *subInfo)
179{
180    struct ucred uc = {-1, -1, -1};
181    socklen_t len = sizeof(uc);
182    // Only root is permitted to use control fd of init.
183    if (getsockopt(subInfo->recvFd, SOL_SOCKET, SO_PEERCRED, &uc, &len) < 0 || uc.uid != 0) {
184        INIT_LOGE("Failed to get socket option. err = %d", errno);
185        errno = EPERM;
186        return -1;
187    }
188    return 0;
189}
190
191static int HandleRecvMessage_(SubInitInfo *subInfo, char *buffer, uint32_t size)
192{
193    if (CheckSocketPermission(subInfo) != 0) {
194        return -1;
195    }
196    ssize_t rLen = TEMP_FAILURE_RETRY(read(subInfo->recvFd, buffer, size));
197    while ((rLen < 0) && (errno == EAGAIN)) {
198        rLen = TEMP_FAILURE_RETRY(read(subInfo->recvFd, buffer, size));
199    }
200    PLUGIN_CHECK(rLen >= 0 && rLen < size, return errno,
201        "Read message for %d fail errno %d rLen %d", subInfo->type, errno, rLen);
202    buffer[rLen] = '\0';
203    PLUGIN_LOGI("Exec cmd '%s' in sub init %s", buffer, g_subContext[subInfo->type]);
204    int index = 0;
205    const char *cmd = GetMatchCmd(buffer, &index);
206    PLUGIN_CHECK(cmd != NULL, return -1, "Can not find cmd %s", buffer);
207    DoCmdByIndex(index, buffer + strlen(cmd) + 1, NULL);
208    return 0;
209}
210
211static void HandleRecvMessage(SubInitInfo *subInfo, char *buffer, uint32_t size)
212{
213    int ret = HandleRecvMessage_(subInfo, buffer, size);
214    int len = snprintf_s(buffer, size, size - 1, "%d", ret);
215    PLUGIN_CHECK(len > 0, return, "Failed to format result %d", ret);
216    buffer[len] = '\0';
217    ret = send(subInfo->sendFd, buffer, len, 0);
218    PLUGIN_CHECK(ret > 0, return, "Failed to send result to %d errno %d", subInfo->type, errno);
219}
220
221static void SubInitMain(InitContextType type, int readFd, int writeFd)
222{
223    PLUGIN_LOGI("SubInitMain, sub init %s[%d] enter", g_subContext[type], getpid());
224#ifndef STARTUP_INIT_TEST
225    (void)prctl(PR_SET_NAME, "chipset_init");
226    const int timeout = 30000; // 30000 30s
227#else
228    const int timeout = 1000; // 1000 1s
229#endif
230    char buffer[MAX_CMD_LEN] = {0};
231    struct pollfd pfd = {};
232    pfd.events = POLLIN;
233    pfd.fd = readFd;
234    SubInitInfo subInfo = {};
235    subInfo.type = type;
236    subInfo.recvFd = readFd;
237    subInfo.sendFd = writeFd;
238    while (1) {
239        pfd.revents = 0;
240        int ret = poll(&pfd, 1, timeout);
241        if (ret == 0) {
242            PLUGIN_LOGI("Poll sub init timeout, sub init %d exit", type);
243            return;
244        } else if (ret < 0) {
245            PLUGIN_LOGE("Failed to poll sub init socket!");
246            return;
247        }
248        if ((unsigned int)pfd.revents & POLLIN) {
249            HandleRecvMessage(&subInfo, buffer, sizeof(buffer));
250        }
251    }
252}
253
254static int SubInitSetSelinuxContext(InitContextType type)
255{
256    PLUGIN_CHECK(type < INIT_CONTEXT_MAIN, return -1, "Invalid type %d", type);
257#ifdef INIT_SUPPORT_CHIPSET_INIT
258#ifdef WITH_SELINUX
259    setcon(g_subContext[type]);
260#endif
261#endif
262    return 0;
263}
264
265#ifdef STARTUP_INIT_TEST
266SubInitInfo *GetSubInitInfo(InitContextType type)
267{
268    PLUGIN_CHECK(type < INIT_CONTEXT_MAIN, return NULL, "Invalid type %d", type);
269    return &g_subInitInfo[type];
270}
271#endif
272
273MODULE_CONSTRUCTOR(void)
274{
275    for (size_t i = 0; i < ARRAY_LENGTH(g_subContext); i++) {
276        SubInitContext context = {
277            (InitContextType)i,
278            SubInitStart,
279            SubInitStop,
280            SubInitExecuteCmd,
281            SubInitSetSelinuxContext
282        };
283        InitSubInitContext((InitContextType)i, &context);
284    }
285}
286