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
17 namespace Hdc {
18 // Do not add thread-specific init op in the following methods as it's running in child thread.
AsyncCmd()19 AsyncCmd::AsyncCmd()
20 {
21 }
22
~AsyncCmd()23 AsyncCmd::~AsyncCmd()
24 {
25 if (childShell != nullptr) {
26 delete childShell;
27 childShell = nullptr;
28 }
29 WRITE_LOG(LOG_DEBUG, "~AsyncCmd");
30 };
31
ReadyForRelease()32 bool AsyncCmd::ReadyForRelease()
33 {
34 if (childShell != nullptr && !childShell->ReadyForRelease()) {
35 return false;
36 }
37 if (refCount != 0) {
38 return false;
39 }
40 if (childShell != nullptr) {
41 delete childShell;
42 childShell = nullptr;
43 }
44 Base::CloseFd(fd);
45 return true;
46 }
47
DoRelease()48 void AsyncCmd::DoRelease()
49 {
50 WRITE_LOG(LOG_DEBUG, "AsyncCmd::DoRelease finish");
51 if (childShell != nullptr) {
52 childShell->StopWork(false, nullptr);
53 }
54 if (pid > 0) {
55 uv_kill(pid, SIGTERM);
56 }
57 }
58
Initial(uv_loop_t * loopIn,const CmdResultCallback callback,uint32_t optionsIn)59 bool AsyncCmd::Initial(uv_loop_t *loopIn, const CmdResultCallback callback, uint32_t optionsIn)
60 {
61 #if defined _WIN32 || defined HDC_HOST
62 WRITE_LOG(LOG_FATAL, "Not support for win32 or host side");
63 return false;
64 #endif
65 loop = loopIn;
66 resultCallback = callback;
67 options = optionsIn;
68 return true;
69 }
70
FinishShellProc(const void * context,const bool result,const string exitMsg)71 bool AsyncCmd::FinishShellProc(const void *context, const bool result, const string exitMsg)
72 {
73 WRITE_LOG(LOG_DEBUG, "FinishShellProc finish");
74 AsyncCmd *thisClass = static_cast<AsyncCmd *>(const_cast<void *>(context));
75 thisClass->resultCallback(true, result, thisClass->cmdResult + exitMsg);
76 --thisClass->refCount;
77 return true;
78 };
79
ChildReadCallback(const void * context,uint8_t * buf,const int size)80 bool AsyncCmd::ChildReadCallback(const void *context, uint8_t *buf, const int size)
81 {
82 AsyncCmd *thisClass = static_cast<AsyncCmd *>(const_cast<void *>(context));
83 if (thisClass->options & OPTION_COMMAND_ONETIME) {
84 string s(reinterpret_cast<char *>(buf), size);
85 thisClass->cmdResult += s;
86 return true;
87 }
88 string s(reinterpret_cast<char *>(buf), size);
89 return thisClass->resultCallback(false, 0, s);
90 };
91
Popen(string command,bool readWrite,int & cpid)92 int AsyncCmd::Popen(string command, bool readWrite, int &cpid)
93 {
94 #ifdef _WIN32
95 return ERR_NO_SUPPORT;
96 #else
97 constexpr uint8_t pipeRead = 0;
98 constexpr uint8_t pipeWrite = 1;
99 pid_t childPid;
100 int fds[2];
101 pipe(fds);
102
103 if ((childPid = fork()) == -1) {
104 return ERR_GENERIC;
105 }
106 if (childPid == 0) {
107 Base::DeInitProcess();
108 if (readWrite) {
109 dup2(fds[pipeWrite], STDOUT_FILENO);
110 dup2(fds[pipeWrite], STDERR_FILENO);
111 } else {
112 dup2(fds[pipeRead], STDIN_FILENO);
113 }
114 Base::CloseFd(fds[pipeRead]);
115 Base::CloseFd(fds[pipeWrite]);
116
117 setsid();
118 setpgid(childPid, childPid);
119 string shellPath = Base::GetShellPath();
120 execl(shellPath.c_str(), shellPath.c_str(), "-c", command.c_str(), NULL);
121 exit(0);
122 } else {
123 if (readWrite) {
124 Base::CloseFd(fds[pipeWrite]);
125 fcntl(fds[pipeRead], F_SETFD, FD_CLOEXEC);
126 } else {
127 Base::CloseFd(fds[pipeRead]);
128 fcntl(fds[pipeWrite], F_SETFD, FD_CLOEXEC);
129 }
130 }
131 cpid = childPid;
132 if (readWrite) {
133 return fds[pipeRead];
134 } else {
135 return fds[pipeWrite];
136 }
137 #endif
138 }
139
ExecuteCommand(const string & command)140 bool AsyncCmd::ExecuteCommand(const string &command)
141 {
142 string cmd = command;
143 Base::Trim(cmd, "\"");
144 if ((fd = Popen(cmd, true, pid)) < 0) {
145 return false;
146 }
147 childShell = new(std::nothrow) HdcFileDescriptor(loop, fd, this, ChildReadCallback, FinishShellProc);
148 if (childShell == nullptr) {
149 WRITE_LOG(LOG_FATAL, "ExecuteCommand new childShell failed");
150 return false;
151 }
152 if (!childShell->StartWork()) {
153 return false;
154 }
155 ++refCount;
156 return true;
157 }
158 } // namespace Hdc
159