• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 "shell.h"
16 #include <sys/wait.h>
17 #include <cerrno>
18 #include <csignal>
19 #include <cstdlib>
20 #include <string>
21 #include "fcntl.h"
22 #include "functional"
23 #include "new"
24 #include <pthread.h>
25 #include "unistd.h"
26 #include "base.h"
27 #include "file_descriptor.h"
28 #include "system_depend.h"
29 #if defined(SURPPORT_SELINUX)
30 #include "selinux/selinux.h"
31 #endif
32 
33 namespace Hdc {
34 std::mutex HdcShell::mutexPty;
35 
HdcShell(HTaskInfo hTaskInfo)36 HdcShell::HdcShell(HTaskInfo hTaskInfo)
37     : HdcTaskBase(hTaskInfo)
38 {
39     childShell = nullptr;
40     fdPTY = 0;
41 }
42 
~HdcShell()43 HdcShell::~HdcShell()
44 {
45     WRITE_LOG(LOG_DEBUG, "~HdcShell channelId:%u", taskInfo->channelId);
46 };
47 
ReadyForRelease()48 bool HdcShell::ReadyForRelease()
49 {
50     if (!HdcTaskBase::ReadyForRelease()) {
51         WRITE_LOG(LOG_WARN, "not ready for release channelId:%u", taskInfo->channelId);
52         return false;
53     }
54     if (!childReady) {
55         WRITE_LOG(LOG_WARN, "childReady false channelId:%u", taskInfo->channelId);
56         return true;
57     }
58     if (!childShell->ReadyForRelease()) {
59         WRITE_LOG(LOG_WARN, "childShell not ready for release channelId:%u", taskInfo->channelId);
60         return false;
61     }
62     delete childShell;
63     childShell = nullptr;
64     WRITE_LOG(LOG_DEBUG, "ReadyForRelease close fdPTY:%d", fdPTY);
65     Base::CloseFd(fdPTY);
66     return true;
67 }
68 
StopTask()69 void HdcShell::StopTask()
70 {
71     singalStop = true;
72     WRITE_LOG(LOG_DEBUG, "StopTask pidShell:%d childReady:%d", pidShell, childReady);
73     if (!childReady) {
74         return;
75     }
76     if (childShell) {
77         childShell->StopWorkOnThread(false, nullptr);
78     }
79 
80     if (pidShell > 1) {
81         kill(pidShell, SIGKILL);
82         int status;
83         waitpid(pidShell, &status, 0);
84         WRITE_LOG(LOG_DEBUG, "StopTask, kill pidshell:%d", pidShell);
85     }
86 };
87 
SpecialSignal(uint8_t ch)88 bool HdcShell::SpecialSignal(uint8_t ch)
89 {
90     const uint8_t TXT_SIGNAL_ETX = 0x3;
91     bool ret = true;
92     switch (ch) {
93         case TXT_SIGNAL_ETX: {  // Ctrl+C
94             if (fdPTY <= 0) {
95                 break;
96             }
97             pid_t tpgid = tcgetpgrp(fdPTY);
98             if (tpgid > 1) {
99                 kill(tpgid, SIGINT);
100             }
101             break;
102         }
103         default:
104             ret = false;
105             break;
106     }
107     return ret;
108 }
109 
CommandDispatch(const uint16_t command,uint8_t * payload,const int payloadSize)110 bool HdcShell::CommandDispatch(const uint16_t command, uint8_t *payload, const int payloadSize)
111 {
112     switch (command) {
113         case CMD_SHELL_INIT: {  // initial
114             if (StartShell()) {
115                 LogMsg(MSG_FAIL, "Shell initialize failed");
116             }
117             break;
118         }
119         case CMD_SHELL_DATA:
120             if (!childReady) {
121                 WRITE_LOG(LOG_DEBUG, "Shell not running");
122                 return false;
123             }
124             if (payloadSize == 1 && SpecialSignal(payload[0])) {
125             } else {
126                 int ret = childShell->Write(payload, payloadSize);
127                 if (ret < 0) {
128                     return false;
129                 }
130             }
131             break;
132         default:
133             break;
134     }
135     return true;
136 }
137 
ChildForkDo(int pts,const char * cmd,const char * arg0,const char * arg1)138 int HdcShell::ChildForkDo(int pts, const char *cmd, const char *arg0, const char *arg1)
139 {
140     dup2(pts, STDIN_FILENO);
141     dup2(pts, STDOUT_FILENO);
142     dup2(pts, STDERR_FILENO);
143     close(pts);
144     string text = "/proc/self/oom_score_adj";
145     int fd = 0;
146     if ((fd = open(text.c_str(), O_WRONLY)) >= 0) {
147         write(fd, "0", 1);
148         close(fd);
149     }
150     char *env = nullptr;
151     if (((env = getenv("HOME")) && chdir(env) < 0) || chdir("/")) {
152     }
153     int ret = execl(cmd, cmd, arg0, arg1, nullptr);
154     if (ret < 0) {
155         WRITE_LOG(LOG_DEBUG, "execl failed, %s", strerror(errno));
156         return -1;
157     }
158     return 0;
159 }
160 
SetSelinuxLabel()161 static void SetSelinuxLabel()
162 {
163 #if defined(SURPPORT_SELINUX)
164     char *con = nullptr;
165     if (getcon(&con) != 0) {
166         return;
167     }
168     if ((con != nullptr) && (strcmp(con, "u:r:hdcd:s0") != 0) && (strcmp(con, "u:r:updater:s0") != 0)) {
169         freecon(con);
170         return;
171     }
172     string debugMode = "";
173     SystemDepend::GetDevItem("const.debuggable", debugMode);
174     if (debugMode != "1") {
175         setcon("u:r:sh:s0");
176     } else {
177         string rootMode = "";
178         string flashdMode = "";
179         SystemDepend::GetDevItem("persist.hdc.root", rootMode);
180         SystemDepend::GetDevItem("updater.flashd.configfs", flashdMode);
181         if ((debugMode == "1" && rootMode == "1") || (debugMode == "1" && flashdMode == "1")) {
182             setcon("u:r:su:s0");
183         } else {
184             setcon("u:r:sh:s0");
185         }
186     }
187     freecon(con);
188 #endif
189 }
190 
ThreadFork(const char * cmd,const char * arg0,const char * arg1)191 int HdcShell::ThreadFork(const char *cmd, const char *arg0, const char *arg1)
192 {
193     auto params = new ShellParams(cmd, arg0, arg1, ptm, devname);
194     if (!params) {
195         return -1;
196     }
197     pthread_t threadId;
198     void *shellRes;
199     int ret = pthread_create(&threadId, nullptr, reinterpret_cast<void *(*)(void *)>(ShellFork), params);
200     if (ret != 0) {
201         constexpr int bufSize = 1024;
202         char buf[bufSize] = { 0 };
203         strerror_r(errno, buf, bufSize);
204         WRITE_LOG(LOG_DEBUG, "fork Thread create failed:%s", buf);
205         delete params;
206         return ERR_GENERIC;
207     }
208     pthread_join(threadId, &shellRes);
209     delete params;
210     return static_cast<int>(reinterpret_cast<size_t>(shellRes));
211 }
212 
ShellFork(void * arg)213 void *HdcShell::ShellFork(void *arg)
214 {
215     int ret = pthread_setname_np(pthread_self(), "hdcd_shellfork");
216     if (ret != 0) {
217         WRITE_LOG(LOG_DEBUG, "set Thread name failed.");
218     }
219     ShellParams params = *reinterpret_cast<ShellParams *>(arg);
220     const char *cmd = params.cmdParam;
221     const char *arg0 = params.arg0Param;
222     const char *arg1 = params.arg1Param;
223     int ptmParam = params.ptmParam;
224     char *devParam = params.devParam;
225     pid_t pid = 0;
226     pid = fork();
227     if (pid < 0) {
228         constexpr int bufSize = 1024;
229         char buf[bufSize] = { 0 };
230         strerror_r(errno, buf, bufSize);
231         WRITE_LOG(LOG_DEBUG, "Fork shell failed:%s", buf);
232         return reinterpret_cast<void *>(ERR_GENERIC);
233     }
234     if (pid == 0) {
235         WRITE_LOG(LOG_DEBUG, "ShellFork close ptmParam:%d", ptmParam);
236         Base::DeInitProcess();
237         HdcShell::mutexPty.unlock();
238         setsid();
239         SetSelinuxLabel();
240         close(ptmParam);
241         int pts = 0;
242         if ((pts = open(devParam, O_RDWR | O_CLOEXEC)) < 0) {
243             return reinterpret_cast<void *>(-1);
244         }
245         ChildForkDo(pts, cmd, arg0, arg1);
246         // proc finish
247     } else {
248         return reinterpret_cast<void *>(pid);
249     }
250     return reinterpret_cast<void *>(0);
251 }
252 
CreateSubProcessPTY(const char * cmd,const char * arg0,const char * arg1,pid_t * pid)253 int HdcShell::CreateSubProcessPTY(const char *cmd, const char *arg0, const char *arg1, pid_t *pid)
254 {
255     ptm = open(devPTMX.c_str(), O_RDWR | O_CLOEXEC);
256     if (ptm < 0) {
257         constexpr int bufSize = 1024;
258         char buf[bufSize] = { 0 };
259         strerror_r(errno, buf, bufSize);
260         WRITE_LOG(LOG_DEBUG, "Cannot open ptmx, error:%s", buf);
261         return ERR_FILE_OPEN;
262     }
263     if (grantpt(ptm) || unlockpt(ptm)) {
264         constexpr int bufSize = 1024;
265         char buf[bufSize] = { 0 };
266         strerror_r(errno, buf, bufSize);
267         WRITE_LOG(LOG_DEBUG, "Cannot open2 ptmx, error:%s", buf);
268         Base::CloseFd(ptm);
269         return ERR_API_FAIL;
270     }
271     if (ptsname_r(ptm, devname, sizeof(devname)) != 0) {
272         constexpr int bufSize = 1024;
273         char buf[bufSize] = { 0 };
274         strerror_r(errno, buf, bufSize);
275         WRITE_LOG(LOG_DEBUG, "Trouble with  ptmx, error:%s", buf);
276         Base::CloseFd(ptm);
277         return ERR_API_FAIL;
278     }
279     *pid = ThreadFork(cmd, arg0, arg1);
280     return ptm;
281 }
282 
FinishShellProc(const void * context,const bool result,const string exitMsg)283 bool HdcShell::FinishShellProc(const void *context, const bool result, const string exitMsg)
284 {
285     WRITE_LOG(LOG_DEBUG, "FinishShellProc finish");
286     HdcShell *thisClass = reinterpret_cast<HdcShell *>(const_cast<void *>(context));
287     thisClass->TaskFinish();
288     --thisClass->refCount;
289     return true;
290 };
291 
ChildReadCallback(const void * context,uint8_t * buf,const int size)292 bool HdcShell::ChildReadCallback(const void *context, uint8_t *buf, const int size)
293 {
294     HdcShell *thisClass = reinterpret_cast<HdcShell *>(const_cast<void *>(context));
295     return thisClass->SendToAnother(CMD_KERNEL_ECHO_RAW, reinterpret_cast<uint8_t *>(buf), size);
296 };
297 
StartShell()298 int HdcShell::StartShell()
299 {
300     int ret = 0;
301     HdcShell::mutexPty.lock();
302     do {
303         if ((fdPTY = CreateSubProcessPTY(Base::GetShellPath().c_str(), "-", 0, &pidShell)) < 0) {
304             ret = ERR_PROCESS_SUB_FAIL;
305             break;
306         }
307         childShell = new(std::nothrow) HdcFileDescriptor(loopTask, fdPTY, this, ChildReadCallback,
308                                                          FinishShellProc, true);
309         if (childShell == nullptr) {
310             WRITE_LOG(LOG_FATAL, "StartShell new childShell failed");
311             ret = ERR_GENERIC;
312             break;
313         }
314         if (!childShell->StartWorkOnThread()) {
315             WRITE_LOG(LOG_FATAL, "StartShell childShell->StartWorkOnThread false");
316             ret = ERR_API_FAIL;
317             break;
318         }
319         childReady = true;
320         ++refCount;
321     } while (false);
322     WRITE_LOG(LOG_DEBUG, "StartShell pid:%d channelId:%u ret:%d", pidShell, taskInfo->channelId, ret);
323     if (ret != RET_SUCCESS) {
324         if (pidShell > 0) {
325             kill(pidShell, SIGKILL);
326         }
327         // fdPTY close by ~clase
328     }
329     HdcShell::mutexPty.unlock();
330     return ret;
331 }
332 }  // namespace Hdc
333