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