• 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 #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), &params);
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