blob: cd99fb1b30d2ab8a4f02f71ba57c4ecf6a8952ed [file] [log] [blame]
// Copyright 2020 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "util/process_wrapper/system.h"
#include "util/process_wrapper/utils.h"
// posix headers
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <iostream>
#include <vector>
namespace process_wrapper {
namespace {
class OutputPipe {
public:
static constexpr size_t kReadEndDesc = 0;
static constexpr size_t kWriteEndDesc = 1;
~OutputPipe() {
CloseReadEnd();
CloseWriteEnd();
}
int CreateEnds() {
if (pipe(output_pipe_desc_) != 0) {
std::cerr << "process wrapper error: failed to open the stdout pipes.\n";
return false;
}
return true;
}
void DupWriteEnd(int newfd) {
dup2(output_pipe_desc_[kWriteEndDesc], newfd);
CloseReadEnd();
CloseWriteEnd();
}
void CloseReadEnd() { Close(kReadEndDesc); }
void CloseWriteEnd() { Close(kWriteEndDesc); }
int ReadEndDesc() const { return output_pipe_desc_[kReadEndDesc]; }
int WriteEndDesc() const { return output_pipe_desc_[kWriteEndDesc]; }
bool WriteToFile(const System::StrType &stdout_file) {
CloseWriteEnd();
constexpr size_t kBufferSize = 4096;
char buffer[kBufferSize];
int output_file_desc =
open(stdout_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (output_file_desc == -1) {
std::cerr << "process wrapper error: failed to open redirection file: "
<< std::strerror(errno) << ".\n";
return false;
}
while (1) {
ssize_t read_bytes = read(ReadEndDesc(), buffer, kBufferSize);
if (read_bytes < 0) {
std::cerr
<< "process wrapper error: failed to read child process output: "
<< std::strerror(errno) << ".\n";
return false;
} else if (read_bytes == 0) {
break;
}
ssize_t written_bytes = write(output_file_desc, buffer, read_bytes);
if (written_bytes < 0 || written_bytes != read_bytes) {
std::cerr << "process wrapper error: failed to write to ouput file: "
<< std::strerror(errno) << ".\n";
return false;
}
}
CloseReadEnd();
close(output_file_desc);
return true;
}
private:
void Close(size_t idx) {
if (output_pipe_desc_[idx] > 0) {
close(output_pipe_desc_[idx]);
}
output_pipe_desc_[idx] = -1;
}
int output_pipe_desc_[2] = {-1};
};
} // namespace
System::StrType System::GetWorkingDirectory() {
const size_t kMaxBufferLength = 4096;
char cwd[kMaxBufferLength];
if (getcwd(cwd, sizeof(cwd)) == NULL) {
return System::StrType{};
}
return System::StrType{cwd};
}
int System::Exec(const System::StrType &executable,
const System::Arguments &arguments,
const System::EnvironmentBlock &environment_block,
const StrType &stdout_file, const StrType &stderr_file) {
OutputPipe stdout_pipe;
if (!stdout_file.empty() && !stdout_pipe.CreateEnds()) {
return -1;
}
OutputPipe stderr_pipe;
if (!stderr_file.empty() && !stderr_pipe.CreateEnds()) {
return -1;
}
pid_t child_pid = fork();
if (child_pid < 0) {
std::cerr << "process wrapper error: failed to fork the current process: "
<< std::strerror(errno) << ".\n";
return -1;
} else if (child_pid == 0) {
if (!stdout_file.empty()) {
stdout_pipe.DupWriteEnd(STDOUT_FILENO);
}
if (!stderr_file.empty()) {
stderr_pipe.DupWriteEnd(STDERR_FILENO);
}
std::vector<char *> argv;
argv.push_back(const_cast<char *>(executable.c_str()));
for (const StrType &argument : arguments) {
argv.push_back(const_cast<char *>(argument.c_str()));
}
argv.push_back(nullptr);
std::vector<char *> envp;
for (const StrType &ev : environment_block) {
envp.push_back(const_cast<char *>(ev.c_str()));
}
envp.push_back(nullptr);
umask(022);
execve(executable.c_str(), argv.data(), envp.data());
std::cerr << "process wrapper error: failed to exec the new process: "
<< std::strerror(errno) << ".\n";
return -1;
}
if (!stdout_file.empty()) {
if (!stdout_pipe.WriteToFile(stdout_file)) {
return -1;
}
}
if (!stderr_file.empty()) {
if (!stderr_pipe.WriteToFile(stderr_file)) {
return -1;
}
}
int err, exit_status = -1;
do {
err = waitpid(child_pid, &exit_status, 0);
} while (err == -1 && errno == EINTR);
return exit_status;
}
} // namespace process_wrapper