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 execl(cmd, cmd, arg0, arg1, nullptr);
154 return 0;
155 }
156
SetSelinuxLabel()157 static void SetSelinuxLabel()
158 {
159 #if defined(SURPPORT_SELINUX)
160 char *con = nullptr;
161 if (getcon(&con) != 0) {
162 return;
163 }
164 if ((con != nullptr) && (strcmp(con, "u:r:hdcd:s0") != 0) && (strcmp(con, "u:r:updater:s0") != 0)) {
165 freecon(con);
166 return;
167 }
168 string debugMode = "";
169 SystemDepend::GetDevItem("const.debuggable", debugMode);
170 if (debugMode != "1") {
171 setcon("u:r:sh:s0");
172 } else {
173 string rootMode = "";
174 string flashdMode = "";
175 SystemDepend::GetDevItem("persist.hdc.root", rootMode);
176 SystemDepend::GetDevItem("updater.flashd.configfs", flashdMode);
177 if ((debugMode == "1" && rootMode == "1") || (debugMode == "1" && flashdMode == "1")) {
178 setcon("u:r:su:s0");
179 } else {
180 setcon("u:r:sh:s0");
181 }
182 }
183 freecon(con);
184 #endif
185 }
186
ThreadFork(const char * cmd,const char * arg0,const char * arg1)187 int HdcShell::ThreadFork(const char *cmd, const char *arg0, const char *arg1)
188 {
189 auto params = new ShellParams(cmd, arg0, arg1, ptm, devname);
190 if (!params) {
191 return -1;
192 }
193 pthread_t threadId;
194 void *shellRes;
195 int ret = pthread_create(&threadId, nullptr, reinterpret_cast<void *(*)(void *)>(ShellFork), params);
196 if (ret != 0) {
197 constexpr int bufSize = 1024;
198 char buf[bufSize] = { 0 };
199 strerror_r(errno, buf, bufSize);
200 WRITE_LOG(LOG_DEBUG, "fork Thread create failed:%s", buf);
201 delete params;
202 return ERR_GENERIC;
203 }
204 pthread_join(threadId, &shellRes);
205 delete params;
206 return static_cast<int>(reinterpret_cast<size_t>(shellRes));
207 }
208
ShellFork(void * arg)209 void *HdcShell::ShellFork(void *arg)
210 {
211 int ret = pthread_setname_np(pthread_self(), "hdcd_shellfork");
212 if (ret != 0) {
213 WRITE_LOG(LOG_DEBUG, "set Thread name failed.");
214 }
215 ShellParams params = *reinterpret_cast<ShellParams *>(arg);
216 const char *cmd = params.cmdParam;
217 const char *arg0 = params.arg0Param;
218 const char *arg1 = params.arg1Param;
219 int ptmParam = params.ptmParam;
220 char *devParam = params.devParam;
221 pid_t pid = 0;
222 pid = fork();
223 if (pid < 0) {
224 constexpr int bufSize = 1024;
225 char buf[bufSize] = { 0 };
226 strerror_r(errno, buf, bufSize);
227 WRITE_LOG(LOG_DEBUG, "Fork shell failed:%s", buf);
228 return reinterpret_cast<void *>(ERR_GENERIC);
229 }
230 if (pid == 0) {
231 WRITE_LOG(LOG_DEBUG, "ShellFork close ptmParam:%d", ptmParam);
232 Base::DeInitProcess();
233 HdcShell::mutexPty.unlock();
234 setsid();
235 SetSelinuxLabel();
236 close(ptmParam);
237 int pts = 0;
238 if ((pts = open(devParam, O_RDWR | O_CLOEXEC)) < 0) {
239 return reinterpret_cast<void *>(-1);
240 }
241 ChildForkDo(pts, cmd, arg0, arg1);
242 // proc finish
243 } else {
244 return reinterpret_cast<void *>(pid);
245 }
246 return reinterpret_cast<void *>(0);
247 }
248
CreateSubProcessPTY(const char * cmd,const char * arg0,const char * arg1,pid_t * pid)249 int HdcShell::CreateSubProcessPTY(const char *cmd, const char *arg0, const char *arg1, pid_t *pid)
250 {
251 ptm = open(devPTMX.c_str(), O_RDWR | O_CLOEXEC);
252 if (ptm < 0) {
253 constexpr int bufSize = 1024;
254 char buf[bufSize] = { 0 };
255 strerror_r(errno, buf, bufSize);
256 WRITE_LOG(LOG_DEBUG, "Cannot open ptmx, error:%s", buf);
257 return ERR_FILE_OPEN;
258 }
259 if (grantpt(ptm) || unlockpt(ptm)) {
260 constexpr int bufSize = 1024;
261 char buf[bufSize] = { 0 };
262 strerror_r(errno, buf, bufSize);
263 WRITE_LOG(LOG_DEBUG, "Cannot open2 ptmx, error:%s", buf);
264 Base::CloseFd(ptm);
265 return ERR_API_FAIL;
266 }
267 if (ptsname_r(ptm, devname, sizeof(devname)) != 0) {
268 constexpr int bufSize = 1024;
269 char buf[bufSize] = { 0 };
270 strerror_r(errno, buf, bufSize);
271 WRITE_LOG(LOG_DEBUG, "Trouble with ptmx, error:%s", buf);
272 Base::CloseFd(ptm);
273 return ERR_API_FAIL;
274 }
275 *pid = ThreadFork(cmd, arg0, arg1);
276 return ptm;
277 }
278
FinishShellProc(const void * context,const bool result,const string exitMsg)279 bool HdcShell::FinishShellProc(const void *context, const bool result, const string exitMsg)
280 {
281 WRITE_LOG(LOG_DEBUG, "FinishShellProc finish");
282 HdcShell *thisClass = reinterpret_cast<HdcShell *>(const_cast<void *>(context));
283 thisClass->TaskFinish();
284 --thisClass->refCount;
285 return true;
286 };
287
ChildReadCallback(const void * context,uint8_t * buf,const int size)288 bool HdcShell::ChildReadCallback(const void *context, uint8_t *buf, const int size)
289 {
290 HdcShell *thisClass = reinterpret_cast<HdcShell *>(const_cast<void *>(context));
291 return thisClass->SendToAnother(CMD_KERNEL_ECHO_RAW, reinterpret_cast<uint8_t *>(buf), size);
292 };
293
StartShell()294 int HdcShell::StartShell()
295 {
296 int ret = 0;
297 HdcShell::mutexPty.lock();
298 do {
299 if ((fdPTY = CreateSubProcessPTY(Base::GetShellPath().c_str(), "-", 0, &pidShell)) < 0) {
300 ret = ERR_PROCESS_SUB_FAIL;
301 break;
302 }
303 childShell = new(std::nothrow) HdcFileDescriptor(loopTask, fdPTY, this, ChildReadCallback,
304 FinishShellProc, true);
305 if (childShell == nullptr) {
306 WRITE_LOG(LOG_FATAL, "StartShell new childShell failed");
307 ret = ERR_GENERIC;
308 break;
309 }
310 if (!childShell->StartWorkOnThread()) {
311 WRITE_LOG(LOG_FATAL, "StartShell childShell->StartWorkOnThread false");
312 ret = ERR_API_FAIL;
313 break;
314 }
315 childReady = true;
316 ++refCount;
317 } while (false);
318 WRITE_LOG(LOG_DEBUG, "StartShell pid:%d channelId:%u ret:%d", pidShell, taskInfo->channelId, ret);
319 if (ret != RET_SUCCESS) {
320 if (pidShell > 0) {
321 kill(pidShell, SIGKILL);
322 }
323 // fdPTY close by ~clase
324 }
325 HdcShell::mutexPty.unlock();
326 return ret;
327 }
328 } // namespace Hdc
329