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