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