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