• 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 #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), &params);
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