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 #if defined(SURPPORT_SELINUX) && defined(UPDATER_MODE)
19 #include "selinux/selinux.h"
20 #endif
21 #endif
22
23 namespace Hdc {
24 // Do not add thread-specific init op in the following methods as it's running in child thread.
AsyncCmd()25 AsyncCmd::AsyncCmd()
26 {
27 }
28
~AsyncCmd()29 AsyncCmd::~AsyncCmd()
30 {
31 if (childShell != nullptr) {
32 delete childShell;
33 childShell = nullptr;
34 }
35 WRITE_LOG(LOG_DEBUG, "~AsyncCmd");
36 };
37
ReadyForRelease()38 bool AsyncCmd::ReadyForRelease()
39 {
40 if (childShell != nullptr && !childShell->ReadyForRelease()) {
41 return false;
42 }
43 if (refCount != 0) {
44 return false;
45 }
46 if (childShell != nullptr) {
47 delete childShell;
48 childShell = nullptr;
49 }
50 Base::CloseFd(fd);
51 return true;
52 }
53
DoRelease()54 void AsyncCmd::DoRelease()
55 {
56 WRITE_LOG(LOG_DEBUG, "AsyncCmd::DoRelease finish");
57 if (childShell != nullptr) {
58 childShell->StopWorkOnThread(false, nullptr);
59 }
60 if (pid > 0) {
61 uv_kill(pid, SIGTERM);
62 }
63 }
64
Initial(uv_loop_t * loopIn,const CmdResultCallback callback,uint32_t optionsIn)65 bool AsyncCmd::Initial(uv_loop_t *loopIn, const CmdResultCallback callback, uint32_t optionsIn)
66 {
67 #if defined _WIN32 || defined HDC_HOST
68 WRITE_LOG(LOG_FATAL, "Not support for win32 or host side");
69 return false;
70 #endif
71 loop = loopIn;
72 resultCallback = callback;
73 options = optionsIn;
74 return true;
75 }
76
FinishShellProc(const void * context,const bool result,const string exitMsg)77 bool AsyncCmd::FinishShellProc(const void *context, const bool result, const string exitMsg)
78 {
79 AsyncCmd *thisClass = static_cast<AsyncCmd *>(const_cast<void *>(context));
80 WRITE_LOG(LOG_DEBUG, "FinishShellProc finish pipeRead fd:%d pid:%d", thisClass->fd, thisClass->pid);
81 thisClass->resultCallback(true, result, thisClass->cmdResult + exitMsg);
82 --thisClass->refCount;
83 return true;
84 };
85
ChildReadCallback(const void * context,uint8_t * buf,const int size)86 bool AsyncCmd::ChildReadCallback(const void *context, uint8_t *buf, const int size)
87 {
88 AsyncCmd *thisClass = static_cast<AsyncCmd *>(const_cast<void *>(context));
89 if (thisClass->options & OPTION_COMMAND_ONETIME) {
90 string s(reinterpret_cast<char *>(buf), size);
91 thisClass->cmdResult += s;
92 return true;
93 }
94 string s(reinterpret_cast<char *>(buf), size);
95 return thisClass->resultCallback(false, 0, s);
96 };
97
98 #if !defined(_WIN32) && !defined(HDC_HOST)
SetSelinuxLabel()99 static void SetSelinuxLabel()
100 {
101 #if defined(SURPPORT_SELINUX) && defined(UPDATER_MODE)
102 char *con = nullptr;
103 if (getcon(&con) != 0) {
104 return;
105 }
106 if ((strcmp(con, "u:r:hdcd:s0") != 0) && (strcmp(con, "u:r:updater:s0") != 0)) {
107 freecon(con);
108 return;
109 }
110 setcon("u:r:sh:s0");
111 freecon(con);
112 #endif
113 }
114 #endif
115
ThreadFork(const string & command,bool readWrite,int & cpid)116 int AsyncCmd::ThreadFork(const string &command, bool readWrite, int &cpid)
117 {
118 AsyncParams params = AsyncParams(command, readWrite, cpid);
119 pthread_t threadId;
120 void *popenRes;
121 int ret = pthread_create(&threadId, nullptr, reinterpret_cast<void *(*)(void *)>(Popen), ¶ms);
122 if (ret != 0) {
123 constexpr int bufSize = 1024;
124 char buf[bufSize] = { 0 };
125 #ifdef _WIN32
126 strerror_s(buf, bufSize, errno);
127 #else
128 strerror_r(errno, buf, bufSize);
129 #endif
130 WRITE_LOG(LOG_DEBUG, "fork Thread create failed:%s", buf);
131 return ERR_GENERIC;
132 }
133 pthread_join(threadId, &popenRes);
134 return static_cast<int>(reinterpret_cast<size_t>(popenRes));
135 }
136
Popen(void * arg)137 void *AsyncCmd::Popen(void *arg)
138 {
139 #ifdef _WIN32
140 return reinterpret_cast<void *>(ERR_NO_SUPPORT);
141 #else
142 AsyncParams params = *reinterpret_cast<AsyncParams *>(arg);
143 string command = params.commandParam;
144 bool readWrite = params.readWriteParam;
145 int &cpid = params.cpidParam;
146 constexpr uint8_t pipeRead = 0;
147 constexpr uint8_t pipeWrite = 1;
148 pid_t childPid;
149 int fds[2];
150 pipe(fds);
151 WRITE_LOG(LOG_DEBUG, "Popen pipe fds[pipeRead]:%d fds[pipeWrite]:%d",
152 fds[pipeRead], fds[pipeWrite]);
153
154 if ((childPid = fork()) == -1) {
155 return reinterpret_cast<void *>(ERR_GENERIC);
156 }
157 if (childPid == 0) {
158 WRITE_LOG(LOG_DEBUG, "Popen close pipe fds[pipeRead]:%d fds[pipeWrite]:%d",
159 fds[pipeRead], fds[pipeWrite]);
160 Base::DeInitProcess();
161 // avoid cpu 100% when watch -n 2 ls command
162 dup2(fds[pipeRead], STDIN_FILENO);
163 if (readWrite) {
164 dup2(fds[pipeWrite], STDOUT_FILENO);
165 dup2(fds[pipeWrite], STDERR_FILENO);
166 }
167 close(fds[pipeRead]);
168 close(fds[pipeWrite]);
169
170 setsid();
171 setpgid(childPid, childPid);
172 #if !defined(HDC_HOST)
173 SetSelinuxLabel();
174 #endif
175 string shellPath = Base::GetShellPath();
176 execl(shellPath.c_str(), shellPath.c_str(), "-c", command.c_str(), NULL);
177 } else {
178 if (readWrite) {
179 Base::CloseFd(fds[pipeWrite]);
180 fcntl(fds[pipeRead], F_SETFD, FD_CLOEXEC);
181 } else {
182 Base::CloseFd(fds[pipeRead]);
183 fcntl(fds[pipeWrite], F_SETFD, FD_CLOEXEC);
184 }
185 }
186 cpid = childPid;
187 if (readWrite) {
188 return reinterpret_cast<void *>(fds[pipeRead]);
189 } else {
190 return reinterpret_cast<void *>(fds[pipeWrite]);
191 }
192 #endif
193 }
194
ExecuteCommand(const string & command)195 bool AsyncCmd::ExecuteCommand(const string &command)
196 {
197 string cmd = command;
198 Base::Trim(cmd, "\"");
199 if ((fd = ThreadFork(cmd, true, pid)) < 0) {
200 return false;
201 }
202 WRITE_LOG(LOG_DEBUG, "ExecuteCommand cmd:%s fd:%d pid:%d", cmd.c_str(), fd, pid);
203 childShell = new(std::nothrow) HdcFileDescriptor(loop, fd, this, ChildReadCallback, FinishShellProc);
204 if (childShell == nullptr) {
205 WRITE_LOG(LOG_FATAL, "ExecuteCommand new childShell failed");
206 return false;
207 }
208 if (!childShell->StartWorkOnThread()) {
209 return false;
210 }
211 ++refCount;
212 return true;
213 }
214 } // namespace Hdc
215