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