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 
39 static SubInitInfo g_subInitInfo[INIT_CONTEXT_MAIN] = {};
40 static const char *g_subContext[INIT_CONTEXT_MAIN] = {
41     "u:r:chipset_init:s0"
42 };
43 
44 static void SubInitMain(InitContextType type, int readFd, int writeFd);
45 static void HandleRecvMessage(SubInitInfo *subInfo, char *buffer, uint32_t size);
46 static int CreateSocketPair(int socket[2]);
47 static int SubInitSetSelinuxContext(InitContextType type);
48 
SubInitRun(const SubInitForkArg * arg)49 static 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
SubInitFork(int (* childFunc)(const SubInitForkArg * arg),const SubInitForkArg * args)66 pid_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 
SubInitStart(InitContextType type)76 static 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 
SubInitStop(pid_t pid)106 static 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 
SubInitExecuteCmd(InitContextType type,const char * name,const char * cmdContent)117 static 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 
CreateSocketPair(int socket[2])156 static 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 
CheckSocketPermission(const SubInitInfo * subInfo)178 static 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 
HandleRecvMessage_(SubInitInfo * subInfo,char * buffer,uint32_t size)191 static 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 
HandleRecvMessage(SubInitInfo * subInfo,char * buffer,uint32_t size)211 static 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 
SubInitMain(InitContextType type,int readFd,int writeFd)221 static 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 
SubInitSetSelinuxContext(InitContextType type)254 static 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
GetSubInitInfo(InitContextType type)266 SubInitInfo *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 
MODULE_CONSTRUCTOR(void)273 MODULE_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