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