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