blob: 53de75000cc1404831e35a9153fcb13de75b4062 [file] [log] [blame]
// 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 "chromiumos-wide-profiling/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 "chromiumos-wide-profiling/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