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 if (pid > 0) {
65 uv_kill(pid, SIGTERM);
66 }
67 WRITE_LOG(LOG_INFO, "DoRelease pid:%d", pid);
68 }
69
Initial(uv_loop_t * loopIn,const CmdResultCallback callback,uint32_t optionsIn)70 bool 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
FinishShellProc(const void * context,const bool result,const string exitMsg)82 bool 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
ChildReadCallback(const void * context,uint8_t * buf,const int size)91 bool 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)
GetDevItem(const char * key,string & out)104 bool 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
SetSelinuxLabel(bool isRoot)128 static 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 ((con != nullptr) && (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
ThreadFork(const string & command,const string & optionPath,bool readWrite,int & cpid)151 int AsyncCmd::ThreadFork(const string &command, const string &optionPath, 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 auto params = new AsyncParams(command, readWrite, cpid, isRoot, optionPath);
164 if (!params) {
165 return -1;
166 }
167 pthread_t threadId;
168 void *popenRes;
169 int ret = pthread_create(&threadId, nullptr, reinterpret_cast<void *(*)(void *)>(Popen), params);
170 if (ret != 0) {
171 constexpr int bufSize = 1024;
172 char buf[bufSize] = { 0 };
173 #ifdef _WIN32
174 strerror_s(buf, bufSize, errno);
175 #else
176 strerror_r(errno, buf, bufSize);
177 #endif
178 WRITE_LOG(LOG_DEBUG, "fork Thread create failed:%s", buf);
179 delete params;
180 return ERR_GENERIC;
181 }
182 pthread_join(threadId, &popenRes);
183 delete params;
184 return static_cast<int>(reinterpret_cast<size_t>(popenRes));
185 }
186
Popen(void * arg)187 void *AsyncCmd::Popen(void *arg)
188 {
189 #ifdef _WIN32
190 return reinterpret_cast<void *>(ERR_NO_SUPPORT);
191 #else
192 #ifndef HOST_MAC
193 int ret = pthread_setname_np(pthread_self(), "hdcd_popen");
194 if (ret != 0) {
195 WRITE_LOG(LOG_DEBUG, "set Thread name failed.");
196 }
197 #else
198 int ret = pthread_setname_np("hdcd_popen");
199 if (ret != 0) {
200 WRITE_LOG(LOG_DEBUG, "set Thread name failed.");
201 }
202 #endif
203 auto param = reinterpret_cast<AsyncParams *>(arg);
204 if (param == nullptr) {
205 WRITE_LOG(LOG_FATAL, "get param is nullptr.");
206 return reinterpret_cast<void *>(ERR_PARAM_NULLPTR);
207 }
208 AsyncParams params = *param;
209 string command = params.commandParam;
210 bool readWrite = params.readWriteParam;
211 int &cpid = params.cpidParam;
212 bool isRoot = params.isRoot;
213 constexpr uint8_t pipeRead = 0;
214 constexpr uint8_t pipeWrite = 1;
215 pid_t childPid;
216 int fds[2];
217 pipe(fds);
218 WRITE_LOG(LOG_DEBUG, "Popen pipe fds[pipeRead]:%d fds[pipeWrite]:%d, mode %d",
219 fds[pipeRead], fds[pipeWrite], isRoot);
220
221 if ((childPid = fork()) == -1) {
222 WRITE_LOG(LOG_FATAL, "Popen fork failed errno:%d", errno);
223 return reinterpret_cast<void *>(ERR_GENERIC);
224 }
225 if (childPid == 0) {
226 Base::DeInitProcess();
227 // avoid cpu 100% when watch -n 2 ls command
228 dup2(fds[pipeRead], STDIN_FILENO);
229 if (readWrite) {
230 dup2(fds[pipeWrite], STDOUT_FILENO);
231 dup2(fds[pipeWrite], STDERR_FILENO);
232 }
233 close(fds[pipeRead]);
234 close(fds[pipeWrite]);
235
236 setsid();
237 setpgid(childPid, childPid);
238 #if !defined(_WIN32) && !defined(HDC_HOST)
239 SetSelinuxLabel(isRoot);
240 #endif
241 string shellPath = Base::GetShellPath();
242 if (!params.optionPath.empty() && chdir(params.optionPath.c_str()) != 0) {
243 string cmdEcho = "echo \"[E003006] Internal error: AsyncCmd chdir failed:" + params.optionPath + "\"";
244 execl(shellPath.c_str(), shellPath.c_str(), "-c", cmdEcho.c_str(), NULL);
245 } else {
246 execl(shellPath.c_str(), shellPath.c_str(), "-c", command.c_str(), NULL);
247 }
248 } else {
249 if (readWrite) {
250 Base::CloseFd(fds[pipeWrite]);
251 fcntl(fds[pipeRead], F_SETFD, FD_CLOEXEC);
252 } else {
253 Base::CloseFd(fds[pipeRead]);
254 fcntl(fds[pipeWrite], F_SETFD, FD_CLOEXEC);
255 }
256 }
257 cpid = childPid;
258 if (readWrite) {
259 return reinterpret_cast<void *>(fds[pipeRead]);
260 } else {
261 return reinterpret_cast<void *>(fds[pipeWrite]);
262 }
263 #endif
264 }
265
ExecuteCommand(const string & command,string executePath)266 bool AsyncCmd::ExecuteCommand(const string &command, string executePath)
267 {
268 string cmd = command;
269 cmd = Base::ShellCmdTrim(cmd);
270 if ((fd = ThreadFork(cmd, executePath, true, pid)) < 0) {
271 WRITE_LOG(LOG_FATAL, "ExecuteCommand failed cmd:%s fd:%d", cmd.c_str(), fd);
272 return false;
273 }
274 WRITE_LOG(LOG_DEBUG, "ExecuteCommand cmd:%s fd:%d pid:%d", cmd.c_str(), fd, pid);
275 childShell = new(std::nothrow) HdcFileDescriptor(loop, fd, this, ChildReadCallback, FinishShellProc, false);
276 if (childShell == nullptr) {
277 WRITE_LOG(LOG_FATAL, "ExecuteCommand new childShell failed");
278 return false;
279 }
280 if (!childShell->StartWorkOnThread()) {
281 WRITE_LOG(LOG_FATAL, "ExecuteCommand StartWorkOnThread failed");
282 return false;
283 }
284 ++refCount;
285 return true;
286 }
287 } // namespace Hdc
288