1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "common/libs/utils/subprocess.h"
18
19 #include <errno.h>
20 #include <signal.h>
21 #include <stdlib.h>
22 #include <sys/prctl.h>
23 #include <sys/types.h>
24 #include <sys/wait.h>
25 #include <unistd.h>
26
27 #include <map>
28 #include <set>
29 #include <thread>
30
31 #include <android-base/logging.h>
32
33 #include "common/libs/fs/shared_buf.h"
34
35 namespace cuttlefish {
36 namespace {
37
38 // If a redirected-to file descriptor was already closed, it's possible that
39 // some inherited file descriptor duped to this file descriptor and the redirect
40 // would override that. This function makes sure that doesn't happen.
validate_redirects(const std::map<Subprocess::StdIOChannel,int> & redirects,const std::map<SharedFD,int> & inherited_fds)41 bool validate_redirects(
42 const std::map<Subprocess::StdIOChannel, int>& redirects,
43 const std::map<SharedFD, int>& inherited_fds) {
44 // Add the redirected IO channels to a set as integers. This allows converting
45 // the enum values into integers instead of the other way around.
46 std::set<int> int_redirects;
47 for (const auto& entry : redirects) {
48 int_redirects.insert(static_cast<int>(entry.first));
49 }
50 for (const auto& entry : inherited_fds) {
51 auto dupped_fd = entry.second;
52 if (int_redirects.count(dupped_fd)) {
53 LOG(ERROR) << "Requested redirect of fd(" << dupped_fd
54 << ") conflicts with inherited FD.";
55 return false;
56 }
57 }
58 return true;
59 }
60
do_redirects(const std::map<Subprocess::StdIOChannel,int> & redirects)61 void do_redirects(const std::map<Subprocess::StdIOChannel, int>& redirects) {
62 for (const auto& entry : redirects) {
63 auto std_channel = static_cast<int>(entry.first);
64 auto fd = entry.second;
65 TEMP_FAILURE_RETRY(dup2(fd, std_channel));
66 }
67 }
68
ToCharPointers(const std::vector<std::string> & vect)69 std::vector<const char*> ToCharPointers(const std::vector<std::string>& vect) {
70 std::vector<const char*> ret = {};
71 for (const auto& str : vect) {
72 ret.push_back(str.c_str());
73 }
74 ret.push_back(NULL);
75 return ret;
76 }
77 } // namespace
78
Subprocess(Subprocess && subprocess)79 Subprocess::Subprocess(Subprocess&& subprocess)
80 : pid_(subprocess.pid_),
81 started_(subprocess.started_),
82 control_socket_(subprocess.control_socket_),
83 stopper_(subprocess.stopper_) {
84 // Make sure the moved object no longer controls this subprocess
85 subprocess.pid_ = -1;
86 subprocess.started_ = false;
87 subprocess.control_socket_ = SharedFD();
88 }
89
operator =(Subprocess && other)90 Subprocess& Subprocess::operator=(Subprocess&& other) {
91 pid_ = other.pid_;
92 started_ = other.started_;
93 control_socket_ = other.control_socket_;
94 stopper_ = other.stopper_;
95
96 other.pid_ = -1;
97 other.started_ = false;
98 other.control_socket_ = SharedFD();
99 return *this;
100 }
101
Wait()102 int Subprocess::Wait() {
103 if (pid_ < 0) {
104 LOG(ERROR)
105 << "Attempt to wait on invalid pid(has it been waited on already?): "
106 << pid_;
107 return -1;
108 }
109 int wstatus = 0;
110 auto pid = pid_; // Wait will set pid_ to -1 after waiting
111 auto wait_ret = Wait(&wstatus, 0);
112 if (wait_ret < 0) {
113 auto error = errno;
114 LOG(ERROR) << "Error on call to waitpid: " << strerror(error);
115 return wait_ret;
116 }
117 int retval = 0;
118 if (WIFEXITED(wstatus)) {
119 retval = WEXITSTATUS(wstatus);
120 if (retval) {
121 LOG(ERROR) << "Subprocess " << pid
122 << " exited with error code: " << retval;
123 }
124 } else if (WIFSIGNALED(wstatus)) {
125 LOG(ERROR) << "Subprocess " << pid
126 << " was interrupted by a signal: " << WTERMSIG(wstatus);
127 retval = -1;
128 }
129 return retval;
130 }
Wait(int * wstatus,int options)131 pid_t Subprocess::Wait(int* wstatus, int options) {
132 if (pid_ < 0) {
133 LOG(ERROR)
134 << "Attempt to wait on invalid pid(has it been waited on already?): "
135 << pid_;
136 return -1;
137 }
138 auto retval = waitpid(pid_, wstatus, options);
139 // We don't want to wait twice for the same process
140 pid_ = -1;
141 return retval;
142 }
143
KillSubprocess(Subprocess * subprocess)144 bool KillSubprocess(Subprocess* subprocess) {
145 auto pid = subprocess->pid();
146 if (pid > 0) {
147 auto pgid = getpgid(pid);
148 if (pgid < 0) {
149 auto error = errno;
150 LOG(WARNING) << "Error obtaining process group id of process with pid="
151 << pid << ": " << strerror(error);
152 // Send the kill signal anyways, because pgid will be -1 it will be sent
153 // to the process and not a (non-existent) group
154 }
155 bool is_group_head = pid == pgid;
156 if (is_group_head) {
157 return killpg(pid, SIGKILL) == 0;
158 } else {
159 return kill(pid, SIGKILL) == 0;
160 }
161 }
162 return true;
163 }
~ParameterBuilder()164 Command::ParameterBuilder::~ParameterBuilder() { Build(); }
Build()165 void Command::ParameterBuilder::Build() {
166 auto param = stream_.str();
167 stream_ = std::stringstream();
168 if (param.size()) {
169 cmd_->AddParameter(param);
170 }
171 }
172
~Command()173 Command::~Command() {
174 // Close all inherited file descriptors
175 for (const auto& entry : inherited_fds_) {
176 close(entry.second);
177 }
178 // Close all redirected file descriptors
179 for (const auto& entry : redirects_) {
180 close(entry.second);
181 }
182 }
183
BuildParameter(std::stringstream * stream,SharedFD shared_fd)184 bool Command::BuildParameter(std::stringstream* stream, SharedFD shared_fd) {
185 int fd;
186 if (inherited_fds_.count(shared_fd)) {
187 fd = inherited_fds_[shared_fd];
188 } else {
189 fd = shared_fd->Fcntl(F_DUPFD_CLOEXEC, 3);
190 if (fd < 0) {
191 LOG(ERROR) << "Could not acquire a new file descriptor: " << shared_fd->StrError();
192 return false;
193 }
194 inherited_fds_[shared_fd] = fd;
195 }
196 *stream << fd;
197 return true;
198 }
199
RedirectStdIO(Subprocess::StdIOChannel channel,SharedFD shared_fd)200 bool Command::RedirectStdIO(Subprocess::StdIOChannel channel,
201 SharedFD shared_fd) {
202 if (!shared_fd->IsOpen()) {
203 return false;
204 }
205 if (redirects_.count(channel)) {
206 LOG(ERROR) << "Attempted multiple redirections of fd: "
207 << static_cast<int>(channel);
208 return false;
209 }
210 auto dup_fd = shared_fd->Fcntl(F_DUPFD_CLOEXEC, 3);
211 if (dup_fd < 0) {
212 LOG(ERROR) << "Could not acquire a new file descriptor: " << shared_fd->StrError();
213 return false;
214 }
215 redirects_[channel] = dup_fd;
216 return true;
217 }
RedirectStdIO(Subprocess::StdIOChannel subprocess_channel,Subprocess::StdIOChannel parent_channel)218 bool Command::RedirectStdIO(Subprocess::StdIOChannel subprocess_channel,
219 Subprocess::StdIOChannel parent_channel) {
220 return RedirectStdIO(subprocess_channel,
221 SharedFD::Dup(static_cast<int>(parent_channel)));
222 }
223
Start(SubprocessOptions options) const224 Subprocess Command::Start(SubprocessOptions options) const {
225 auto cmd = ToCharPointers(command_);
226 // The parent socket will get closed on the child on the call to exec, the
227 // child socket will be closed on the parent when this function returns and no
228 // references to the fd are left
229 SharedFD parent_socket, child_socket;
230 if (options.WithControlSocket()) {
231 if (!SharedFD::SocketPair(AF_LOCAL, SOCK_STREAM, 0, &parent_socket,
232 &child_socket)) {
233 LOG(ERROR) << "Unable to create control socket pair: " << strerror(errno);
234 return Subprocess(-1, {});
235 }
236 // Remove FD_CLOEXEC from the child socket, ensure the parent has it
237 child_socket->Fcntl(F_SETFD, 0);
238 parent_socket->Fcntl(F_SETFD, FD_CLOEXEC);
239 }
240
241 if (!validate_redirects(redirects_, inherited_fds_)) {
242 return Subprocess(-1, {});
243 }
244
245 pid_t pid = fork();
246 if (!pid) {
247 if (options.ExitWithParent()) {
248 prctl(PR_SET_PDEATHSIG, SIGHUP); // Die when parent dies
249 }
250
251 do_redirects(redirects_);
252 if (options.InGroup()) {
253 // This call should never fail (see SETPGID(2))
254 if (setpgid(0, 0) != 0) {
255 auto error = errno;
256 LOG(ERROR) << "setpgid failed (" << strerror(error) << ")";
257 }
258 }
259 for (const auto& entry : inherited_fds_) {
260 if (fcntl(entry.second, F_SETFD, 0)) {
261 int error_num = errno;
262 LOG(ERROR) << "fcntl failed: " << strerror(error_num);
263 }
264 }
265 int rval;
266 // If use_parent_env_ is false, the current process's environment is used as
267 // the environment of the child process. To force an empty emvironment for
268 // the child process pass the address of a pointer to NULL
269 if (use_parent_env_) {
270 rval = execv(cmd[0], const_cast<char* const*>(cmd.data()));
271 } else {
272 auto envp = ToCharPointers(env_);
273 rval = execve(cmd[0], const_cast<char* const*>(cmd.data()),
274 const_cast<char* const*>(envp.data()));
275 }
276 // No need for an if: if exec worked it wouldn't have returned
277 LOG(ERROR) << "exec of " << cmd[0] << " failed (" << strerror(errno)
278 << ")";
279 exit(rval);
280 }
281 if (pid == -1) {
282 LOG(ERROR) << "fork failed (" << strerror(errno) << ")";
283 }
284 if (options.Verbose()) { // "more verbose", and LOG(DEBUG) > LOG(VERBOSE)
285 LOG(DEBUG) << "Started (pid: " << pid << "): " << cmd[0];
286 for (int i = 1; cmd[i]; i++) {
287 LOG(DEBUG) << cmd[i];
288 }
289 } else {
290 LOG(VERBOSE) << "Started (pid: " << pid << "): " << cmd[0];
291 for (int i = 1; cmd[i]; i++) {
292 LOG(VERBOSE) << cmd[i];
293 }
294 }
295 return Subprocess(pid, parent_socket, subprocess_stopper_);
296 }
297
298 // A class that waits for threads to exit in its destructor.
299 class ThreadJoiner {
300 std::vector<std::thread*> threads_;
301 public:
ThreadJoiner(const std::vector<std::thread * > threads)302 ThreadJoiner(const std::vector<std::thread*> threads) : threads_(threads) {}
~ThreadJoiner()303 ~ThreadJoiner() {
304 for (auto& thread : threads_) {
305 if (thread->joinable()) {
306 thread->join();
307 }
308 }
309 }
310 };
311
RunWithManagedStdio(Command && cmd_tmp,const std::string * stdin,std::string * stdout,std::string * stderr,SubprocessOptions options)312 int RunWithManagedStdio(Command&& cmd_tmp, const std::string* stdin,
313 std::string* stdout, std::string* stderr,
314 SubprocessOptions options) {
315 /*
316 * The order of these declarations is necessary for safety. If the function
317 * returns at any point, the Command will be destroyed first, closing all
318 * of its references to SharedFDs. This will cause the thread internals to fail
319 * their reads or writes. The ThreadJoiner then waits for the threads to
320 * complete, as running the destructor of an active std::thread crashes the
321 * program.
322 *
323 * C++ scoping rules dictate that objects are descoped in reverse order to
324 * construction, so this behavior is predictable.
325 */
326 std::thread stdin_thread, stdout_thread, stderr_thread;
327 ThreadJoiner thread_joiner({&stdin_thread, &stdout_thread, &stderr_thread});
328 Command cmd = std::move(cmd_tmp);
329 bool io_error = false;
330 if (stdin != nullptr) {
331 SharedFD pipe_read, pipe_write;
332 if (!SharedFD::Pipe(&pipe_read, &pipe_write)) {
333 LOG(ERROR) << "Could not create a pipe to write the stdin of \""
334 << cmd.GetShortName() << "\"";
335 return -1;
336 }
337 if (!cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdIn, pipe_read)) {
338 LOG(ERROR) << "Could not set stdout of \"" << cmd.GetShortName()
339 << "\", was already set.";
340 return -1;
341 }
342 stdin_thread = std::thread([pipe_write, stdin, &io_error]() {
343 int written = WriteAll(pipe_write, *stdin);
344 if (written < 0) {
345 io_error = true;
346 LOG(ERROR) << "Error in writing stdin to process";
347 }
348 });
349 }
350 if (stdout != nullptr) {
351 SharedFD pipe_read, pipe_write;
352 if (!SharedFD::Pipe(&pipe_read, &pipe_write)) {
353 LOG(ERROR) << "Could not create a pipe to read the stdout of \""
354 << cmd.GetShortName() << "\"";
355 return -1;
356 }
357 if (!cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, pipe_write)) {
358 LOG(ERROR) << "Could not set stdout of \"" << cmd.GetShortName()
359 << "\", was already set.";
360 return -1;
361 }
362 stdout_thread = std::thread([pipe_read, stdout, &io_error]() {
363 int read = ReadAll(pipe_read, stdout);
364 if (read < 0) {
365 io_error = true;
366 LOG(ERROR) << "Error in reading stdout from process";
367 }
368 });
369 }
370 if (stderr != nullptr) {
371 SharedFD pipe_read, pipe_write;
372 if (!SharedFD::Pipe(&pipe_read, &pipe_write)) {
373 LOG(ERROR) << "Could not create a pipe to read the stderr of \""
374 << cmd.GetShortName() << "\"";
375 return -1;
376 }
377 if (!cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdErr, pipe_write)) {
378 LOG(ERROR) << "Could not set stderr of \"" << cmd.GetShortName()
379 << "\", was already set.";
380 return -1;
381 }
382 stderr_thread = std::thread([pipe_read, stderr, &io_error]() {
383 int read = ReadAll(pipe_read, stderr);
384 if (read < 0) {
385 io_error = true;
386 LOG(ERROR) << "Error in reading stderr from process";
387 }
388 });
389 }
390
391 auto subprocess = cmd.Start(options);
392 if (!subprocess.Started()) {
393 return -1;
394 }
395 auto cmd_short_name = cmd.GetShortName();
396 {
397 // Force the destructor to run by moving it into a smaller scope.
398 // This is necessary to close the write end of the pipe.
399 Command forceDelete = std::move(cmd);
400 }
401 int wstatus;
402 subprocess.Wait(&wstatus, 0);
403 if (WIFSIGNALED(wstatus)) {
404 LOG(ERROR) << "Command was interrupted by a signal: " << WTERMSIG(wstatus);
405 return -1;
406 }
407 {
408 auto join_threads = std::move(thread_joiner);
409 }
410 if (io_error) {
411 LOG(ERROR) << "IO error communicating with " << cmd_short_name;
412 return -1;
413 }
414 return WEXITSTATUS(wstatus);
415 }
416
execute(const std::vector<std::string> & command,const std::vector<std::string> & env)417 int execute(const std::vector<std::string>& command,
418 const std::vector<std::string>& env) {
419 Command cmd(command[0]);
420 for (size_t i = 1; i < command.size(); ++i) {
421 cmd.AddParameter(command[i]);
422 }
423 cmd.SetEnvironment(env);
424 auto subprocess = cmd.Start();
425 if (!subprocess.Started()) {
426 return -1;
427 }
428 return subprocess.Wait();
429 }
execute(const std::vector<std::string> & command)430 int execute(const std::vector<std::string>& command) {
431 Command cmd(command[0]);
432 for (size_t i = 1; i < command.size(); ++i) {
433 cmd.AddParameter(command[i]);
434 }
435 auto subprocess = cmd.Start();
436 if (!subprocess.Started()) {
437 return -1;
438 }
439 return subprocess.Wait();
440 }
441
442 } // namespace cuttlefish
443