• 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 
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