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 "async_cmd.h" 16#include <pthread.h> 17#if !defined(_WIN32) && !defined(HDC_HOST) 18#include "parameter.h" 19#include "base.h" 20#if defined(SURPPORT_SELINUX) 21#include "selinux/selinux.h" 22#endif 23#endif 24 25namespace Hdc { 26// Do not add thread-specific init op in the following methods as it's running in child thread. 27AsyncCmd::AsyncCmd() 28{ 29} 30 31AsyncCmd::~AsyncCmd() 32{ 33 if (Base::CloseFd(fd) != 0) { 34 WRITE_LOG(LOG_INFO, "AsyncCmd Release, close fd:%d", fd); 35 } 36 if (childShell != nullptr) { 37 delete childShell; 38 childShell = nullptr; 39 } 40}; 41 42bool AsyncCmd::ReadyForRelease() 43{ 44 if (childShell != nullptr && !childShell->ReadyForRelease()) { 45 WRITE_LOG(LOG_WARN, "childShell not ready for release pid:%d", pid); 46 return false; 47 } 48 if (refCount != 0) { 49 WRITE_LOG(LOG_WARN, "refCount:%u not ready for release", refCount); 50 return false; 51 } 52 if (childShell != nullptr) { 53 delete childShell; 54 childShell = nullptr; 55 } 56 return true; 57} 58 59void AsyncCmd::DoRelease() 60{ 61 if (childShell != nullptr) { 62 childShell->StopWorkOnThread(false, nullptr); 63 } 64 if (pid > 0) { 65 uv_kill(pid, SIGTERM); 66 } 67 WRITE_LOG(LOG_INFO, "DoRelease pid:%d", pid); 68} 69 70bool AsyncCmd::Initial(uv_loop_t *loopIn, const CmdResultCallback callback, uint32_t optionsIn) 71{ 72#if defined _WIN32 || defined HDC_HOST 73 WRITE_LOG(LOG_FATAL, "Not support for win32 or host side"); 74 return false; 75#endif 76 loop = loopIn; 77 resultCallback = callback; 78 options = optionsIn; 79 return true; 80} 81 82bool AsyncCmd::FinishShellProc(const void *context, const bool result, const string exitMsg) 83{ 84 AsyncCmd *thisClass = static_cast<AsyncCmd *>(const_cast<void *>(context)); 85 WRITE_LOG(LOG_DEBUG, "FinishShellProc finish pipeRead fd:%d pid:%d", thisClass->fd, thisClass->pid); 86 thisClass->resultCallback(true, result, thisClass->cmdResult + exitMsg); 87 --thisClass->refCount; 88 return true; 89}; 90 91bool AsyncCmd::ChildReadCallback(const void *context, uint8_t *buf, const int size) 92{ 93 AsyncCmd *thisClass = static_cast<AsyncCmd *>(const_cast<void *>(context)); 94 if (thisClass->options & OPTION_COMMAND_ONETIME) { 95 string s(reinterpret_cast<char *>(buf), size); 96 thisClass->cmdResult += s; 97 return true; 98 } 99 string s(reinterpret_cast<char *>(buf), size); 100 return thisClass->resultCallback(false, 0, s); 101}; 102 103#if !defined(_WIN32) && !defined(HDC_HOST) 104bool AsyncCmd::GetDevItem(const char *key, string &out) 105{ 106 bool ret = true; 107 char tmpStringBuf[BUF_SIZE_MEDIUM] = ""; 108#ifdef HARMONY_PROJECT 109 auto res = GetParameter(key, nullptr, tmpStringBuf, BUF_SIZE_MEDIUM); 110 if (res <= 0) { 111 WRITE_LOG(LOG_WARN, "GetDevItem false key:%s", key); 112 return false; 113 } 114#else 115 string sFailString = Base::StringFormat("Get parameter \"%s\" fail", key); 116 string stringBuf = "param get " + string(key); 117 Base::RunPipeComand(stringBuf.c_str(), tmpStringBuf, BUF_SIZE_MEDIUM - 1, true); 118 if (!strcmp(sFailString.c_str(), tmpStringBuf)) { 119 WRITE_LOG(LOG_WARN, "GetDevItem false tmpStringBuf:%s", tmpStringBuf); 120 ret = false; 121 Base::ZeroArray(tmpStringBuf); 122 } 123#endif 124 out = tmpStringBuf; 125 return ret; 126} 127 128static void SetSelinuxLabel(bool isRoot) 129{ 130#if defined(SURPPORT_SELINUX) 131 char *con = nullptr; 132 if (getcon(&con) != 0) { 133 WRITE_LOG(LOG_WARN, "SetSelinuxLabel isRoot:%d", isRoot); 134 return; 135 } 136 if ((strcmp(con, "u:r:hdcd:s0") != 0) && (strcmp(con, "u:r:updater:s0") != 0)) { 137 WRITE_LOG(LOG_WARN, "SetSelinuxLabel con:%s isRoot:%d", con, isRoot); 138 freecon(con); 139 return; 140 } 141 if (isRoot) { 142 setcon("u:r:su:s0"); 143 } else { 144 setcon("u:r:sh:s0"); 145 } 146 freecon(con); 147#endif 148} 149#endif 150 151int AsyncCmd::ThreadFork(const string &command, bool readWrite, int &cpid) 152{ 153 string debugMode = ""; 154 string rootMode = ""; 155 bool isRoot = false; 156#if !defined(_WIN32) && !defined(HDC_HOST) 157 GetDevItem("const.debuggable", debugMode); 158 GetDevItem("persist.hdc.root", rootMode); 159#endif 160 if (debugMode == "1" && rootMode == "1") { 161 isRoot = true; 162 } 163 AsyncParams params = AsyncParams(command, readWrite, cpid, isRoot); 164 pthread_t threadId; 165 void *popenRes; 166 int ret = pthread_create(&threadId, nullptr, reinterpret_cast<void *(*)(void *)>(Popen), ¶ms); 167 if (ret != 0) { 168 constexpr int bufSize = 1024; 169 char buf[bufSize] = { 0 }; 170#ifdef _WIN32 171 strerror_s(buf, bufSize, errno); 172#else 173 strerror_r(errno, buf, bufSize); 174#endif 175 WRITE_LOG(LOG_DEBUG, "fork Thread create failed:%s", buf); 176 return ERR_GENERIC; 177 } 178 pthread_join(threadId, &popenRes); 179 return static_cast<int>(reinterpret_cast<size_t>(popenRes)); 180} 181 182void *AsyncCmd::Popen(void *arg) 183{ 184#ifdef _WIN32 185 return reinterpret_cast<void *>(ERR_NO_SUPPORT); 186#else 187#ifndef HOST_MAC 188 int ret = pthread_setname_np(pthread_self(), "hdcd_popen"); 189 if (ret != 0) { 190 WRITE_LOG(LOG_DEBUG, "set Thread name failed."); 191 } 192#else 193 int ret = pthread_setname_np("hdcd_popen"); 194 if (ret != 0) { 195 WRITE_LOG(LOG_DEBUG, "set Thread name failed."); 196 } 197#endif 198 auto param = reinterpret_cast<AsyncParams *>(arg); 199 if (param == nullptr) { 200 WRITE_LOG(LOG_FATAL, "get param is nullptr."); 201 return reinterpret_cast<void *>(ERR_PARAM_NULLPTR); 202 } 203 AsyncParams params = *param; 204 string command = params.commandParam; 205 bool readWrite = params.readWriteParam; 206 int &cpid = params.cpidParam; 207 bool isRoot = params.isRoot; 208 constexpr uint8_t pipeRead = 0; 209 constexpr uint8_t pipeWrite = 1; 210 pid_t childPid; 211 int fds[2]; 212 pipe(fds); 213 WRITE_LOG(LOG_DEBUG, "Popen pipe fds[pipeRead]:%d fds[pipeWrite]:%d, mode %d", 214 fds[pipeRead], fds[pipeWrite], isRoot); 215 216 if ((childPid = fork()) == -1) { 217 WRITE_LOG(LOG_FATAL, "Popen fork failed errno:%d", errno); 218 return reinterpret_cast<void *>(ERR_GENERIC); 219 } 220 if (childPid == 0) { 221 Base::DeInitProcess(); 222 // avoid cpu 100% when watch -n 2 ls command 223 dup2(fds[pipeRead], STDIN_FILENO); 224 if (readWrite) { 225 dup2(fds[pipeWrite], STDOUT_FILENO); 226 dup2(fds[pipeWrite], STDERR_FILENO); 227 } 228 close(fds[pipeRead]); 229 close(fds[pipeWrite]); 230 231 setsid(); 232 setpgid(childPid, childPid); 233#if !defined(_WIN32) && !defined(HDC_HOST) 234 SetSelinuxLabel(isRoot); 235#endif 236 string shellPath = Base::GetShellPath(); 237 execl(shellPath.c_str(), shellPath.c_str(), "-c", command.c_str(), NULL); 238 } else { 239 if (readWrite) { 240 Base::CloseFd(fds[pipeWrite]); 241 fcntl(fds[pipeRead], F_SETFD, FD_CLOEXEC); 242 } else { 243 Base::CloseFd(fds[pipeRead]); 244 fcntl(fds[pipeWrite], F_SETFD, FD_CLOEXEC); 245 } 246 } 247 cpid = childPid; 248 if (readWrite) { 249 return reinterpret_cast<void *>(fds[pipeRead]); 250 } else { 251 return reinterpret_cast<void *>(fds[pipeWrite]); 252 } 253#endif 254} 255 256bool AsyncCmd::ExecuteCommand(const string &command) 257{ 258 string cmd = command; 259 cmd = Base::ShellCmdTrim(cmd); 260 if ((fd = ThreadFork(cmd, true, pid)) < 0) { 261 WRITE_LOG(LOG_FATAL, "ExecuteCommand failed cmd:%s fd:%d", cmd.c_str(), fd); 262 return false; 263 } 264 WRITE_LOG(LOG_DEBUG, "ExecuteCommand cmd:%s fd:%d pid:%d", cmd.c_str(), fd, pid); 265 childShell = new(std::nothrow) HdcFileDescriptor(loop, fd, this, ChildReadCallback, FinishShellProc, false); 266 if (childShell == nullptr) { 267 WRITE_LOG(LOG_FATAL, "ExecuteCommand new childShell failed"); 268 return false; 269 } 270 if (!childShell->StartWorkOnThread()) { 271 WRITE_LOG(LOG_FATAL, "ExecuteCommand StartWorkOnThread failed"); 272 return false; 273 } 274 ++refCount; 275 return true; 276} 277} // namespace Hdc 278