• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
UnsetEnvironment(const std::unordered_set<std::string> & unenv)78 void UnsetEnvironment(const std::unordered_set<std::string>& unenv) {
79   for (auto it = unenv.cbegin(); it != unenv.cend(); ++it) {
80     unsetenv(it->c_str());
81   }
82 }
83 }  // namespace
84 
Subprocess(Subprocess && subprocess)85 Subprocess::Subprocess(Subprocess&& subprocess)
86     : pid_(subprocess.pid_),
87       started_(subprocess.started_),
88       stopper_(subprocess.stopper_) {
89   // Make sure the moved object no longer controls this subprocess
90   subprocess.pid_ = -1;
91   subprocess.started_ = false;
92 }
93 
operator =(Subprocess && other)94 Subprocess& Subprocess::operator=(Subprocess&& other) {
95   pid_ = other.pid_;
96   started_ = other.started_;
97   stopper_ = other.stopper_;
98 
99   other.pid_ = -1;
100   other.started_ = false;
101   return *this;
102 }
103 
Wait()104 int Subprocess::Wait() {
105   if (pid_ < 0) {
106     LOG(ERROR)
107         << "Attempt to wait on invalid pid(has it been waited on already?): "
108         << pid_;
109     return -1;
110   }
111   int wstatus = 0;
112   auto pid = pid_;  // Wait will set pid_ to -1 after waiting
113   auto wait_ret = Wait(&wstatus, 0);
114   if (wait_ret < 0) {
115     auto error = errno;
116     LOG(ERROR) << "Error on call to waitpid: " << strerror(error);
117     return wait_ret;
118   }
119   int retval = 0;
120   if (WIFEXITED(wstatus)) {
121     retval = WEXITSTATUS(wstatus);
122     if (retval) {
123       LOG(ERROR) << "Subprocess " << pid
124                  << " exited with error code: " << retval;
125     }
126   } else if (WIFSIGNALED(wstatus)) {
127     LOG(ERROR) << "Subprocess " << pid
128                << " was interrupted by a signal: " << WTERMSIG(wstatus);
129     retval = -1;
130   }
131   return retval;
132 }
Wait(int * wstatus,int options)133 pid_t Subprocess::Wait(int* wstatus, int options) {
134   if (pid_ < 0) {
135     LOG(ERROR)
136         << "Attempt to wait on invalid pid(has it been waited on already?): "
137         << pid_;
138     return -1;
139   }
140   auto retval = waitpid(pid_, wstatus, options);
141   // We don't want to wait twice for the same process
142   pid_ = -1;
143   return retval;
144 }
145 
KillSubprocess(Subprocess * subprocess)146 bool KillSubprocess(Subprocess* subprocess) {
147   auto pid = subprocess->pid();
148   if (pid > 0) {
149     auto pgid = getpgid(pid);
150     if (pgid < 0) {
151       auto error = errno;
152       LOG(WARNING) << "Error obtaining process group id of process with pid="
153                    << pid << ": " << strerror(error);
154       // Send the kill signal anyways, because pgid will be -1 it will be sent
155       // to the process and not a (non-existent) group
156     }
157     bool is_group_head = pid == pgid;
158     if (is_group_head) {
159       return killpg(pid, SIGKILL) == 0;
160     } else {
161       return kill(pid, SIGKILL) == 0;
162     }
163   }
164   return true;
165 }
166 
~Command()167 Command::~Command() {
168   // Close all inherited file descriptors
169   for (const auto& entry : inherited_fds_) {
170     close(entry.second);
171   }
172   // Close all redirected file descriptors
173   for (const auto& entry : redirects_) {
174     close(entry.second);
175   }
176 }
177 
BuildParameter(std::stringstream * stream,SharedFD shared_fd)178 bool Command::BuildParameter(std::stringstream* stream, SharedFD shared_fd) {
179   int fd;
180   if (inherited_fds_.count(shared_fd)) {
181     fd = inherited_fds_[shared_fd];
182   } else {
183     fd = shared_fd->Fcntl(F_DUPFD_CLOEXEC, 3);
184     if (fd < 0) {
185       LOG(ERROR) << "Could not acquire a new file descriptor: " << shared_fd->StrError();
186       return false;
187     }
188     inherited_fds_[shared_fd] = fd;
189   }
190   *stream << fd;
191   return true;
192 }
193 
RedirectStdIO(Subprocess::StdIOChannel channel,SharedFD shared_fd)194 bool Command::RedirectStdIO(Subprocess::StdIOChannel channel,
195                             SharedFD shared_fd) {
196   if (!shared_fd->IsOpen()) {
197     return false;
198   }
199   if (redirects_.count(channel)) {
200     LOG(ERROR) << "Attempted multiple redirections of fd: "
201                << static_cast<int>(channel);
202     return false;
203   }
204   auto dup_fd = shared_fd->Fcntl(F_DUPFD_CLOEXEC, 3);
205   if (dup_fd < 0) {
206     LOG(ERROR) << "Could not acquire a new file descriptor: " << shared_fd->StrError();
207     return false;
208   }
209   redirects_[channel] = dup_fd;
210   return true;
211 }
RedirectStdIO(Subprocess::StdIOChannel subprocess_channel,Subprocess::StdIOChannel parent_channel)212 bool Command::RedirectStdIO(Subprocess::StdIOChannel subprocess_channel,
213                             Subprocess::StdIOChannel parent_channel) {
214   return RedirectStdIO(subprocess_channel,
215                        SharedFD::Dup(static_cast<int>(parent_channel)));
216 }
217 
Start(SubprocessOptions options) const218 Subprocess Command::Start(SubprocessOptions options) const {
219   auto cmd = ToCharPointers(command_);
220 
221   if (!validate_redirects(redirects_, inherited_fds_)) {
222     return Subprocess(-1, {});
223   }
224 
225   pid_t pid = fork();
226   if (!pid) {
227     if (options.ExitWithParent()) {
228       prctl(PR_SET_PDEATHSIG, SIGHUP); // Die when parent dies
229     }
230 
231     do_redirects(redirects_);
232     if (options.InGroup()) {
233       // This call should never fail (see SETPGID(2))
234       if (setpgid(0, 0) != 0) {
235         auto error = errno;
236         LOG(ERROR) << "setpgid failed (" << strerror(error) << ")";
237       }
238     }
239     for (const auto& entry : inherited_fds_) {
240       if (fcntl(entry.second, F_SETFD, 0)) {
241         int error_num = errno;
242         LOG(ERROR) << "fcntl failed: " << strerror(error_num);
243       }
244     }
245     int rval;
246     // If use_parent_env_ is false, the current process's environment is used as
247     // the environment of the child process. To force an empty emvironment for
248     // the child process pass the address of a pointer to NULL
249     if (use_parent_env_) {
250       UnsetEnvironment(unenv_);
251       rval = execvp(cmd[0], const_cast<char* const*>(cmd.data()));
252     } else {
253       auto envp = ToCharPointers(env_);
254       rval = execvpe(cmd[0], const_cast<char* const*>(cmd.data()),
255                     const_cast<char* const*>(envp.data()));
256     }
257     // No need for an if: if exec worked it wouldn't have returned
258     LOG(ERROR) << "exec of " << cmd[0] << " failed (" << strerror(errno)
259                << ")";
260     exit(rval);
261   }
262   if (pid == -1) {
263     LOG(ERROR) << "fork failed (" << strerror(errno) << ")";
264   }
265   if (options.Verbose()) { // "more verbose", and LOG(DEBUG) > LOG(VERBOSE)
266     LOG(DEBUG) << "Started (pid: " << pid << "): " << cmd[0];
267     for (int i = 1; cmd[i]; i++) {
268       LOG(DEBUG) << cmd[i];
269     }
270   } else {
271     LOG(VERBOSE) << "Started (pid: " << pid << "): " << cmd[0];
272     for (int i = 1; cmd[i]; i++) {
273       LOG(VERBOSE) << cmd[i];
274     }
275   }
276   return Subprocess(pid, subprocess_stopper_);
277 }
278 
279 // A class that waits for threads to exit in its destructor.
280 class ThreadJoiner {
281 std::vector<std::thread*> threads_;
282 public:
ThreadJoiner(const std::vector<std::thread * > threads)283   ThreadJoiner(const std::vector<std::thread*> threads) : threads_(threads) {}
~ThreadJoiner()284   ~ThreadJoiner() {
285     for (auto& thread : threads_) {
286       if (thread->joinable()) {
287         thread->join();
288       }
289     }
290   }
291 };
292 
RunWithManagedStdio(Command && cmd_tmp,const std::string * stdin,std::string * stdout,std::string * stderr,SubprocessOptions options)293 int RunWithManagedStdio(Command&& cmd_tmp, const std::string* stdin,
294                         std::string* stdout, std::string* stderr,
295                         SubprocessOptions options) {
296   /*
297    * The order of these declarations is necessary for safety. If the function
298    * returns at any point, the Command will be destroyed first, closing all
299    * of its references to SharedFDs. This will cause the thread internals to fail
300    * their reads or writes. The ThreadJoiner then waits for the threads to
301    * complete, as running the destructor of an active std::thread crashes the
302    * program.
303    *
304    * C++ scoping rules dictate that objects are descoped in reverse order to
305    * construction, so this behavior is predictable.
306    */
307   std::thread stdin_thread, stdout_thread, stderr_thread;
308   ThreadJoiner thread_joiner({&stdin_thread, &stdout_thread, &stderr_thread});
309   Command cmd = std::move(cmd_tmp);
310   bool io_error = false;
311   if (stdin != nullptr) {
312     SharedFD pipe_read, pipe_write;
313     if (!SharedFD::Pipe(&pipe_read, &pipe_write)) {
314       LOG(ERROR) << "Could not create a pipe to write the stdin of \""
315                 << cmd.GetShortName() << "\"";
316       return -1;
317     }
318     if (!cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdIn, pipe_read)) {
319       LOG(ERROR) << "Could not set stdout of \"" << cmd.GetShortName()
320                 << "\", was already set.";
321       return -1;
322     }
323     stdin_thread = std::thread([pipe_write, stdin, &io_error]() {
324       int written = WriteAll(pipe_write, *stdin);
325       if (written < 0) {
326         io_error = true;
327         LOG(ERROR) << "Error in writing stdin to process";
328       }
329     });
330   }
331   if (stdout != nullptr) {
332     SharedFD pipe_read, pipe_write;
333     if (!SharedFD::Pipe(&pipe_read, &pipe_write)) {
334       LOG(ERROR) << "Could not create a pipe to read the stdout of \""
335                 << cmd.GetShortName() << "\"";
336       return -1;
337     }
338     if (!cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, pipe_write)) {
339       LOG(ERROR) << "Could not set stdout of \"" << cmd.GetShortName()
340                 << "\", was already set.";
341       return -1;
342     }
343     stdout_thread = std::thread([pipe_read, stdout, &io_error]() {
344       int read = ReadAll(pipe_read, stdout);
345       if (read < 0) {
346         io_error = true;
347         LOG(ERROR) << "Error in reading stdout from process";
348       }
349     });
350   }
351   if (stderr != nullptr) {
352     SharedFD pipe_read, pipe_write;
353     if (!SharedFD::Pipe(&pipe_read, &pipe_write)) {
354       LOG(ERROR) << "Could not create a pipe to read the stderr of \""
355                 << cmd.GetShortName() << "\"";
356       return -1;
357     }
358     if (!cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdErr, pipe_write)) {
359       LOG(ERROR) << "Could not set stderr of \"" << cmd.GetShortName()
360                 << "\", was already set.";
361       return -1;
362     }
363     stderr_thread = std::thread([pipe_read, stderr, &io_error]() {
364       int read = ReadAll(pipe_read, stderr);
365       if (read < 0) {
366         io_error = true;
367         LOG(ERROR) << "Error in reading stderr from process";
368       }
369     });
370   }
371 
372   auto subprocess = cmd.Start(options);
373   if (!subprocess.Started()) {
374     return -1;
375   }
376   auto cmd_short_name = cmd.GetShortName();
377   {
378     // Force the destructor to run by moving it into a smaller scope.
379     // This is necessary to close the write end of the pipe.
380     Command forceDelete = std::move(cmd);
381   }
382   int wstatus;
383   subprocess.Wait(&wstatus, 0);
384   if (WIFSIGNALED(wstatus)) {
385     LOG(ERROR) << "Command was interrupted by a signal: " << WTERMSIG(wstatus);
386     return -1;
387   }
388   {
389     auto join_threads = std::move(thread_joiner);
390   }
391   if (io_error) {
392     LOG(ERROR) << "IO error communicating with " << cmd_short_name;
393     return -1;
394   }
395   return WEXITSTATUS(wstatus);
396 }
397 
execute(const std::vector<std::string> & command,const std::vector<std::string> & env)398 int execute(const std::vector<std::string>& command,
399             const std::vector<std::string>& env) {
400   Command cmd(command[0]);
401   for (size_t i = 1; i < command.size(); ++i) {
402     cmd.AddParameter(command[i]);
403   }
404   cmd.SetEnvironment(env);
405   auto subprocess = cmd.Start();
406   if (!subprocess.Started()) {
407     return -1;
408   }
409   return subprocess.Wait();
410 }
execute(const std::vector<std::string> & command)411 int execute(const std::vector<std::string>& command) {
412   Command cmd(command[0]);
413   for (size_t i = 1; i < command.size(); ++i) {
414     cmd.AddParameter(command[i]);
415   }
416   auto subprocess = cmd.Start();
417   if (!subprocess.Started()) {
418     return -1;
419   }
420   return subprocess.Wait();
421 }
422 
423 }  // namespace cuttlefish
424