// 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 <errno.h>
|
#include <fcntl.h>
|
#include <sys/stat.h>
|
#include <sys/types.h>
|
#include <sys/wait.h>
|
#include <unistd.h>
|
|
#include <string>
|
|
#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<char>* 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<string>& command, std::vector<char>* output) {
|
std::vector<char*> 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<char*>(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
|