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