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