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), ¶ms); 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