1 /*
2  * Copyright (c) 2022 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 
16 #include <fcntl.h>
17 #include <unistd.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 
21 #include "beget_ext.h"
22 #include "control_fd.h"
23 #include "init_utils.h"
24 #include "securec.h"
25 
26 static CmdService g_cmdService;
27 static LoopHandle g_controlFdLoop = NULL;
28 
29 CallbackControlFdProcess g_controlFdFunc = NULL;
30 
OnClose(const TaskHandle task)31 static void OnClose(const TaskHandle task)
32 {
33     CmdTask *agent = (CmdTask *)LE_GetUserData(task);
34     BEGET_ERROR_CHECK(agent != NULL, return, "[control_fd] Can not get agent");
35     OH_ListRemove(&agent->item);
36     OH_ListInit(&agent->item);
37 }
38 
CheckSocketPermission(const TaskHandle task)39 CONTROL_FD_STATIC int CheckSocketPermission(const TaskHandle task)
40 {
41     struct ucred uc = {-1, -1, -1};
42     socklen_t len = sizeof(uc);
43     if (getsockopt(LE_GetSocketFd(task), SOL_SOCKET, SO_PEERCRED, &uc, &len) < 0) {
44         BEGET_LOGE("Failed to get socket option. err = %d", errno);
45         return -1;
46     }
47     // Only root is permitted to use control fd of init.
48     if (uc.uid != 0) { // non-root user
49         errno = EPERM;
50         return -1;
51     }
52     return 0;
53 }
54 
CmdOnRecvMessage(const TaskHandle task,const uint8_t * buffer,uint32_t buffLen)55 CONTROL_FD_STATIC void CmdOnRecvMessage(const TaskHandle task, const uint8_t *buffer, uint32_t buffLen)
56 {
57     if (buffer == NULL) {
58         return;
59     }
60     CmdTask *agent = (CmdTask *)LE_GetUserData(task);
61     BEGET_ERROR_CHECK(agent != NULL, return, "[control_fd] Can not get agent");
62 
63     // parse msg to exec
64     CmdMessage *msg = (CmdMessage *)buffer;
65     if ((msg->type >= ACTION_MAX) || (msg->cmd[0] == '\0') || (msg->ptyName[0] == '\0')) {
66         BEGET_LOGE("[control_fd] Received msg is invalid");
67         return;
68     }
69 
70     BEGET_ERROR_CHECK(CheckSocketPermission(task) >= 0, return, "Check socket permission failed, err = %d", errno);
71 
72 #ifndef STARTUP_INIT_TEST
73     agent->pid = fork();
74     if (agent->pid == 0) {
75         OpenConsole();
76         char *realPath = GetRealPath(msg->ptyName);
77         BEGET_ERROR_CHECK(realPath != NULL, _exit(1), "Failed get realpath, err=%d", errno);
78         int n = strncmp(realPath, "/dev/pts/", strlen("/dev/pts/"));
79         BEGET_ERROR_CHECK(n == 0, free(realPath); _exit(1), "pts path %s is invaild", realPath);
80         int fd = open(realPath, O_RDWR);
81         free(realPath);
82         BEGET_ERROR_CHECK(fd >= 0, _exit(1), "Failed open %s, err=%d", msg->ptyName, errno);
83         (void)dup2(fd, STDIN_FILENO);
84         (void)dup2(fd, STDOUT_FILENO);
85         (void)dup2(fd, STDERR_FILENO); // Redirect fd to 0, 1, 2
86         (void)close(fd);
87         if (g_controlFdFunc != NULL) {
88             g_controlFdFunc(msg->type, msg->cmd, NULL);
89         }
90         _exit(0);
91     } else if (agent->pid < 0) {
92         BEGET_LOGE("[control_fd] Failed to fork child process, err = %d", errno);
93     }
94 #endif
95     return;
96 }
97 
SendMessage(LoopHandle loop,TaskHandle task,const char * message)98 CONTROL_FD_STATIC int SendMessage(LoopHandle loop, TaskHandle task, const char *message)
99 {
100     if (message == NULL) {
101         BEGET_LOGE("[control_fd] Invalid parameter");
102         return -1;
103     }
104     BufferHandle handle = NULL;
105     uint32_t bufferSize = strlen(message) + 1;
106     handle = LE_CreateBuffer(loop, bufferSize);
107     char *buff = (char *)LE_GetBufferInfo(handle, NULL, &bufferSize);
108     BEGET_ERROR_CHECK(buff != NULL, return -1, "[control_fd] Failed get buffer info");
109     int ret = memcpy_s(buff, bufferSize, message, strlen(message) + 1);
110     BEGET_ERROR_CHECK(ret == 0, LE_FreeBuffer(g_controlFdLoop, task, handle);
111         return -1, "[control_fd] Failed memcpy_s err=%d", errno);
112     LE_STATUS status = LE_Send(loop, task, handle, strlen(message) + 1);
113     BEGET_ERROR_CHECK(status == LE_SUCCESS, return -1, "[control_fd] Failed le send msg");
114     return 0;
115 }
116 
CmdOnIncommingConnect(const LoopHandle loop,const TaskHandle server)117 CONTROL_FD_STATIC int CmdOnIncommingConnect(const LoopHandle loop, const TaskHandle server)
118 {
119     TaskHandle client = NULL;
120     LE_StreamInfo info = {};
121 #ifndef STARTUP_INIT_TEST
122     info.baseInfo.flags = TASK_STREAM | TASK_PIPE | TASK_CONNECT;
123 #else
124     info.baseInfo.flags = TASK_STREAM | TASK_PIPE | TASK_CONNECT | TASK_TEST;
125 #endif
126     info.baseInfo.close = OnClose;
127     info.baseInfo.userDataSize = sizeof(CmdTask);
128     info.disConnectComplete = NULL;
129     info.sendMessageComplete = NULL;
130     info.recvMessage = CmdOnRecvMessage;
131     int ret = LE_AcceptStreamClient(g_controlFdLoop, server, &client, &info);
132     BEGET_ERROR_CHECK(ret == 0, return -1, "[control_fd] Failed accept stream")
133     CmdTask *agent = (CmdTask *)LE_GetUserData(client);
134     BEGET_ERROR_CHECK(agent != NULL, return -1, "[control_fd] Invalid agent");
135     agent->task = client;
136     OH_ListInit(&agent->item);
137     ret = SendMessage(g_controlFdLoop, agent->task, "connect success.");
138     BEGET_ERROR_CHECK(ret == 0, return -1, "[control_fd] Failed send msg");
139     OH_ListAddTail(&g_cmdService.head, &agent->item);
140     return 0;
141 }
142 
CmdServiceInit(const char * socketPath,CallbackControlFdProcess func,LoopHandle loop)143 void CmdServiceInit(const char *socketPath, CallbackControlFdProcess func, LoopHandle loop)
144 {
145     if ((socketPath == NULL) || (func == NULL) || (loop == NULL)) {
146         BEGET_LOGE("[control_fd] Invalid parameter");
147         return;
148     }
149     OH_ListInit(&g_cmdService.head);
150     LE_StreamServerInfo info = {};
151     info.baseInfo.flags = TASK_STREAM | TASK_SERVER | TASK_PIPE;
152     info.server = (char *)socketPath;
153     info.socketId = -1;
154     info.baseInfo.close = NULL;
155     info.disConnectComplete = NULL;
156     info.incommingConnect = CmdOnIncommingConnect;
157     info.sendMessageComplete = NULL;
158     info.recvMessage = NULL;
159     g_controlFdFunc = func;
160     if (g_controlFdLoop == NULL) {
161         g_controlFdLoop = loop;
162     }
163     (void)LE_CreateStreamServer(g_controlFdLoop, &g_cmdService.serverTask, &info);
164 }
165 
ClientTraversalProc(ListNode * node,void * data)166 static int ClientTraversalProc(ListNode *node, void *data)
167 {
168     CmdTask *info = ListEntry(node, CmdTask, item);
169     int pid = *(int *)data;
170     return pid - info->pid;
171 }
172 
CmdServiceProcessDelClient(pid_t pid)173 void CmdServiceProcessDelClient(pid_t pid)
174 {
175     ListNode *node = OH_ListFind(&g_cmdService.head, (void *)&pid, ClientTraversalProc);
176     if (node != NULL) {
177         CmdTask *agent = ListEntry(node, CmdTask, item);
178         OH_ListRemove(&agent->item);
179         OH_ListInit(&agent->item);
180         LE_CloseTask(g_controlFdLoop, agent->task);
181     }
182 }
183 
CmdServiceDestroyProc(ListNode * node)184 static void CmdServiceDestroyProc(ListNode *node)
185 {
186     if (node == NULL) {
187         return;
188     }
189     CmdTask *agent = ListEntry(node, CmdTask, item);
190     OH_ListRemove(&agent->item);
191     OH_ListInit(&agent->item);
192     LE_CloseTask(g_controlFdLoop, agent->task);
193 }
194 
CmdServiceProcessDestroyClient(void)195 void CmdServiceProcessDestroyClient(void)
196 {
197     OH_ListRemoveAll(&g_cmdService.head, CmdServiceDestroyProc);
198     LE_StopLoop(g_controlFdLoop);
199 }
200