xref: /developtools/hdc/src/common/async_cmd.cpp (revision cc290419)
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
25namespace Hdc {
26// Do not add thread-specific init op in the following methods as it's running in child thread.
27AsyncCmd::AsyncCmd()
28{
29}
30
31AsyncCmd::~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
42bool 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
59void 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
70bool 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
82bool 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
91bool 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)
104bool 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
128static 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 ((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
151int AsyncCmd::ThreadFork(const string &command, 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    AsyncParams params = AsyncParams(command, readWrite, cpid, isRoot);
164    pthread_t threadId;
165    void *popenRes;
166    int ret = pthread_create(&threadId, nullptr, reinterpret_cast<void *(*)(void *)>(Popen), &params);
167    if (ret != 0) {
168        constexpr int bufSize = 1024;
169        char buf[bufSize] = { 0 };
170#ifdef _WIN32
171        strerror_s(buf, bufSize, errno);
172#else
173        strerror_r(errno, buf, bufSize);
174#endif
175        WRITE_LOG(LOG_DEBUG, "fork Thread create failed:%s", buf);
176        return ERR_GENERIC;
177    }
178    pthread_join(threadId, &popenRes);
179    return static_cast<int>(reinterpret_cast<size_t>(popenRes));
180}
181
182void *AsyncCmd::Popen(void *arg)
183{
184#ifdef _WIN32
185    return reinterpret_cast<void *>(ERR_NO_SUPPORT);
186#else
187#ifndef HOST_MAC
188    int ret = pthread_setname_np(pthread_self(), "hdcd_popen");
189    if (ret != 0) {
190        WRITE_LOG(LOG_DEBUG, "set Thread name failed.");
191    }
192#else
193    int ret = pthread_setname_np("hdcd_popen");
194    if (ret != 0) {
195        WRITE_LOG(LOG_DEBUG, "set Thread name failed.");
196    }
197#endif
198    auto param = reinterpret_cast<AsyncParams *>(arg);
199    if (param == nullptr) {
200        WRITE_LOG(LOG_FATAL, "get param is nullptr.");
201        return reinterpret_cast<void *>(ERR_PARAM_NULLPTR);
202    }
203    AsyncParams params = *param;
204    string command = params.commandParam;
205    bool readWrite = params.readWriteParam;
206    int &cpid = params.cpidParam;
207    bool isRoot = params.isRoot;
208    constexpr uint8_t pipeRead = 0;
209    constexpr uint8_t pipeWrite = 1;
210    pid_t childPid;
211    int fds[2];
212    pipe(fds);
213    WRITE_LOG(LOG_DEBUG, "Popen pipe fds[pipeRead]:%d fds[pipeWrite]:%d, mode %d",
214        fds[pipeRead], fds[pipeWrite], isRoot);
215
216    if ((childPid = fork()) == -1) {
217        WRITE_LOG(LOG_FATAL, "Popen fork failed errno:%d", errno);
218        return reinterpret_cast<void *>(ERR_GENERIC);
219    }
220    if (childPid == 0) {
221        Base::DeInitProcess();
222        // avoid cpu 100% when watch -n 2 ls command
223        dup2(fds[pipeRead], STDIN_FILENO);
224        if (readWrite) {
225            dup2(fds[pipeWrite], STDOUT_FILENO);
226            dup2(fds[pipeWrite], STDERR_FILENO);
227        }
228        close(fds[pipeRead]);
229        close(fds[pipeWrite]);
230
231        setsid();
232        setpgid(childPid, childPid);
233#if !defined(_WIN32) && !defined(HDC_HOST)
234        SetSelinuxLabel(isRoot);
235#endif
236        string shellPath = Base::GetShellPath();
237        execl(shellPath.c_str(), shellPath.c_str(), "-c", command.c_str(), NULL);
238    } else {
239        if (readWrite) {
240            Base::CloseFd(fds[pipeWrite]);
241            fcntl(fds[pipeRead], F_SETFD, FD_CLOEXEC);
242        } else {
243            Base::CloseFd(fds[pipeRead]);
244            fcntl(fds[pipeWrite], F_SETFD, FD_CLOEXEC);
245        }
246    }
247    cpid = childPid;
248    if (readWrite) {
249        return reinterpret_cast<void *>(fds[pipeRead]);
250    } else {
251        return reinterpret_cast<void *>(fds[pipeWrite]);
252    }
253#endif
254}
255
256bool AsyncCmd::ExecuteCommand(const string &command)
257{
258    string cmd = command;
259    cmd = Base::ShellCmdTrim(cmd);
260    if ((fd = ThreadFork(cmd, true, pid)) < 0) {
261        WRITE_LOG(LOG_FATAL, "ExecuteCommand failed cmd:%s fd:%d", cmd.c_str(), fd);
262        return false;
263    }
264    WRITE_LOG(LOG_DEBUG, "ExecuteCommand cmd:%s fd:%d pid:%d", cmd.c_str(), fd, pid);
265    childShell = new(std::nothrow) HdcFileDescriptor(loop, fd, this, ChildReadCallback, FinishShellProc, false);
266    if (childShell == nullptr) {
267        WRITE_LOG(LOG_FATAL, "ExecuteCommand new childShell failed");
268        return false;
269    }
270    if (!childShell->StartWorkOnThread()) {
271        WRITE_LOG(LOG_FATAL, "ExecuteCommand StartWorkOnThread failed");
272        return false;
273    }
274    ++refCount;
275    return true;
276}
277}  // namespace Hdc
278