// Copyright 2015 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "run_command.h" #include #include #include #include #include #include #include #include "base/logging.h" #include "compat/string.h" namespace quipper { namespace { bool CloseFdOnExec(int fd) { int fd_flags = fcntl(fd, F_GETFD); if (fd_flags == -1) { PLOG(ERROR) << "F_GETFD"; return false; } if (fcntl(fd, F_SETFD, fd_flags | FD_CLOEXEC)) { PLOG(ERROR) << "F_SETFD FD_CLOEXEC"; return false; } return true; } void ReadFromFd(int fd, std::vector* output) { static const int kReadSize = 4096; ssize_t read_sz; size_t read_off = output->size(); do { output->resize(read_off + kReadSize); do { read_sz = read(fd, output->data() + read_off, kReadSize); } while (read_sz < 0 && errno == EINTR); if (read_sz < 0) { PLOG(FATAL) << "read"; break; } read_off += read_sz; } while (read_sz > 0); output->resize(read_off); } } // namespace int RunCommand(const std::vector& command, std::vector* output) { std::vector c_str_cmd; c_str_cmd.reserve(command.size() + 1); for (const auto& c : command) { // This cast is safe: POSIX states that exec shall not modify argv nor the // strings pointed to by argv. c_str_cmd.push_back(const_cast(c.c_str())); } c_str_cmd.push_back(nullptr); // Create pipe for stdout: int output_pipefd[2]; if (output) { if (pipe(output_pipefd)) { PLOG(ERROR) << "pipe"; return -1; } } // Pipe for the child to return errno if exec fails: int errno_pipefd[2]; if (pipe(errno_pipefd)) { PLOG(ERROR) << "pipe for errno"; return -1; } if (!CloseFdOnExec(errno_pipefd[1])) return -1; const pid_t child = fork(); if (child == 0) { close(errno_pipefd[0]); if (output) { if (close(output_pipefd[0]) < 0) { PLOG(FATAL) << "close read end of pipe"; } } int devnull_fd = open("/dev/null", O_WRONLY); if (devnull_fd < 0) { PLOG(FATAL) << "open /dev/null"; } if (dup2(output ? output_pipefd[1] : devnull_fd, 1) < 0) { PLOG(FATAL) << "dup2 stdout"; } if (dup2(devnull_fd, 2) < 0) { PLOG(FATAL) << "dup2 stderr"; } if (close(devnull_fd) < 0) { PLOG(FATAL) << "close /dev/null"; } execvp(c_str_cmd[0], c_str_cmd.data()); int exec_errno = errno; // exec failed... Write errno to a pipe so parent can retrieve it. int ret; do { ret = write(errno_pipefd[1], &exec_errno, sizeof(exec_errno)); } while (ret < 0 && errno == EINTR); close(errno_pipefd[1]); std::_Exit(EXIT_FAILURE); } if (close(errno_pipefd[1])) { PLOG(FATAL) << "close write end of errno pipe"; } if (output) { if (close(output_pipefd[1]) < 0) { PLOG(FATAL) << "close write end of pipe"; } } // Check for errno: int child_exec_errno; int read_errno_res; do { read_errno_res = read(errno_pipefd[0], &child_exec_errno, sizeof(child_exec_errno)); } while (read_errno_res < 0 && errno == EINTR); if (read_errno_res < 0) { PLOG(FATAL) << "read errno"; } if (close(errno_pipefd[0])) { PLOG(FATAL) << "close errno"; } if (read_errno_res > 0) { // exec failed in the child. while (waitpid(child, nullptr, 0) < 0 && errno == EINTR) { } errno = child_exec_errno; return -1; } // Read stdout from pipe. if (output) { ReadFromFd(output_pipefd[0], output); if (close(output_pipefd[0])) { PLOG(FATAL) << "close output"; } } // Wait for child. int exit_status; while (waitpid(child, &exit_status, 0) < 0 && errno == EINTR) { } errno = 0; if (WIFEXITED(exit_status)) return WEXITSTATUS(exit_status); return -1; } } // namespace quipper