• 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 "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