blob: 4f8d9c766c5ea8497dd786c84795200524a568ab [file] [log] [blame]
// Copyright 2015 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.
#define _WITH_DPRINTF
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h> // PATH_MAX
#include <poll.h>
#include <pwd.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <cassert>
#include <cinttypes>
#include "src/main/cpp/blaze_util.h"
#include "src/main/cpp/blaze_util_platform.h"
#include "src/main/cpp/global_variables.h"
#include "src/main/cpp/startup_options.h"
#include "src/main/cpp/util/errors.h"
#include "src/main/cpp/util/exit_code.h"
#include "src/main/cpp/util/file.h"
#include "src/main/cpp/util/logging.h"
#include "src/main/cpp/util/md5.h"
#include "src/main/cpp/util/numbers.h"
namespace blaze {
using blaze_exit_code::INTERNAL_ERROR;
using blaze_util::GetLastErrorString;
using std::string;
using std::vector;
SignalHandler SignalHandler::INSTANCE;
// The number of the last received signal that should cause the client
// to shutdown. This is saved so that the client's WTERMSIG can be set
// correctly. (Currently only SIGPIPE uses this mechanism.)
static volatile sig_atomic_t signal_handler_received_signal = 0;
// Signal handler.
static void handler(int signum) {
int saved_errno = errno;
static volatile sig_atomic_t sigint_count = 0;
switch (signum) {
case SIGINT:
if (++sigint_count >= 3) {
SigPrintf(
"\n%s caught third interrupt signal; killed.\n\n",
SignalHandler::Get().GetGlobals()->options->product_name.c_str());
if (SignalHandler::Get().GetGlobals()->server_pid != -1) {
KillServerProcess(
SignalHandler::Get().GetGlobals()->server_pid,
SignalHandler::Get().GetGlobals()->options->output_base);
}
_exit(1);
}
SigPrintf(
"\n%s caught interrupt signal; shutting down.\n\n",
SignalHandler::Get().GetGlobals()->options->product_name.c_str());
SignalHandler::Get().CancelServer();
break;
case SIGTERM:
SigPrintf(
"\n%s caught terminate signal; shutting down.\n\n",
SignalHandler::Get().GetGlobals()->options->product_name.c_str());
SignalHandler::Get().CancelServer();
break;
case SIGPIPE:
signal_handler_received_signal = SIGPIPE;
break;
case SIGQUIT:
SigPrintf("\nSending SIGQUIT to JVM process %d (see %s).\n\n",
SignalHandler::Get().GetGlobals()->server_pid,
SignalHandler::Get().GetGlobals()->jvm_log_file.c_str());
kill(SignalHandler::Get().GetGlobals()->server_pid, SIGQUIT);
break;
}
errno = saved_errno;
}
void SignalHandler::Install(GlobalVariables* globals,
SignalHandler::Callback cancel_server) {
_globals = globals;
_cancel_server = cancel_server;
// Unblock all signals.
sigset_t sigset;
sigemptyset(&sigset);
sigprocmask(SIG_SETMASK, &sigset, NULL);
signal(SIGINT, handler);
signal(SIGTERM, handler);
signal(SIGPIPE, handler);
signal(SIGQUIT, handler);
}
ATTRIBUTE_NORETURN void SignalHandler::PropagateSignalOrExit(int exit_code) {
if (signal_handler_received_signal) {
// Kill ourselves with the same signal, so that callers see the
// right WTERMSIG value.
signal(signal_handler_received_signal, SIG_DFL);
raise(signal_handler_received_signal);
exit(1); // (in case raise didn't kill us for some reason)
} else {
exit(exit_code);
}
}
string GetProcessIdAsString() {
return ToString(getpid());
}
string GetHomeDir() { return GetEnv("HOME"); }
string FindSystemWideBlazerc() {
string path = "/etc/bazel.bazelrc";
if (blaze_util::CanReadFile(path)) {
return path;
}
return "";
}
string GetJavaBinaryUnderJavabase() { return "bin/java"; }
// NB: execve() requires pointers to non-const char arrays but .c_str() returns
// a pointer to const char arrays. We could do the const_cast in this function,
// but it's better to violate const correctness as late as possible. No one
// cares about what happens just before execve() because we'll soon become a new
// binary anyway.
const char** ConvertStringVectorToArgv(const vector<string>& args) {
const char** argv = new const char*[args.size() + 1];
for (size_t i = 0; i < args.size(); i++) {
argv[i] = args[i].c_str();
}
argv[args.size()] = NULL;
return argv;
}
void ExecuteProgram(const string& exe, const vector<string>& args_vector) {
BAZEL_LOG(INFO) << "Invoking binary " << exe << " in "
<< blaze_util::GetCwd();
const char** argv = ConvertStringVectorToArgv(args_vector);
execv(exe.c_str(), const_cast<char**>(argv));
}
std::string ConvertPath(const std::string &path) { return path; }
std::string PathAsJvmFlag(const std::string& path) { return path; }
const char kListSeparator = ':';
bool SymlinkDirectories(const string &target, const string &link) {
return symlink(target.c_str(), link.c_str()) == 0;
}
// Causes the current process to become a daemon (i.e. a child of
// init, detached from the terminal, in its own session.) We don't
// change cwd, though.
static void Daemonize(const char* daemon_output,
const bool daemon_output_append) {
// Don't call BAZEL_DIE or exit() in this function; we're already in a child
// process so it won't work as expected. Just don't do anything that can
// possibly fail. :)
signal(SIGHUP, SIG_IGN);
if (fork() > 0) {
// This second fork is required iff there's any chance cmd will
// open an specific tty explicitly, e.g., open("/dev/tty23"). If
// not, this fork can be removed.
_exit(blaze_exit_code::SUCCESS);
}
setsid();
close(0);
close(1);
close(2);
open("/dev/null", O_RDONLY); // stdin
// stdout:
int out_flags =
O_WRONLY | O_CREAT | (daemon_output_append ? O_APPEND : O_TRUNC);
if (open(daemon_output, out_flags, 0666) == -1) {
// In a daemon, no-one can hear you scream.
open("/dev/null", O_WRONLY);
}
(void) dup(STDOUT_FILENO); // stderr (2>&1)
}
// Notifies the client about the death of the server process by keeping a socket
// open in the server. If the server dies for any reason, the socket will be
// closed, which can be detected by the client.
class SocketBlazeServerStartup : public BlazeServerStartup {
public:
SocketBlazeServerStartup(int pipe_fd);
virtual ~SocketBlazeServerStartup();
virtual bool IsStillAlive();
private:
int fd;
};
SocketBlazeServerStartup::SocketBlazeServerStartup(int fd)
: fd(fd) {
}
SocketBlazeServerStartup::~SocketBlazeServerStartup() {
close(fd);
}
bool SocketBlazeServerStartup::IsStillAlive() {
struct pollfd pfd;
pfd.fd = fd;
pfd.events = POLLIN;
int result;
do {
result = poll(&pfd, 1, 0);
} while (result < 0 && errno == EINTR);
if (result == 0) {
// Timeout, server is still alive
return true;
} else {
// Whether it's an error or pfd.revents & POLLHUP > 0, we assume child is
// dead.
return false;
}
}
// NB: There should only be system calls in this function. See the comment
// before ExecuteDaemon() to understand why. strerror() and strlen() are
// hopefully okay.
static void DieAfterFork(const char* message) {
char* error_string = strerror(errno); // strerror is hopefully okay
write(STDERR_FILENO, message, strlen(message)); // strlen should be OK
write(STDERR_FILENO, ": ", 2);
write(STDERR_FILENO, error_string, strlen(error_string));
write(STDERR_FILENO, "\n", 1);
_exit(blaze_exit_code::INTERNAL_ERROR);
}
// NB: There should only be system calls in this function. See the comment
// before ExecuteDaemon() to understand why.
static void ReadFromFdWithRetryEintr(
int fd, void *buf, size_t count, const char* error_message) {
ssize_t result;
do {
result = read(fd, buf, count);
} while (result < 0 && errno == EINTR);
if (result < 0 || static_cast<size_t>(result) != count) {
DieAfterFork(error_message);
}
}
// NB: There should only be system calls in this function. See the comment
// before ExecuteDaemon() to understand why.
static void WriteToFdWithRetryEintr(
int fd, void *buf, size_t count, const char* error_message) {
ssize_t result;
do {
// Ideally, we'd use send(..., MSG_NOSIGNAL), but that's not available on
// Darwin.
result = write(fd, buf, count);
} while (result < 0 && errno == EINTR);
if (result < 0 || static_cast<size_t>(result) != count) {
DieAfterFork(error_message);
}
}
void WriteSystemSpecificProcessIdentifier(
const string& server_dir, pid_t server_pid);
// We do a lot of seemingly-needless complications to avoid doing anything
// complex after a fork(). The reason is that forking in multi-threaded
// programs is fraught with peril.
//
// One root cause is that fork() only forks the thread it was called from. If
// another thread holds a lock, it will never be relinquished in the child
// process, and malloc() sometimes does lock. Thus, we need to avoid allocating
// any dynamic memory in the child process, which is hard if any C++ feature is
// used.
//
// We also don't know what libc does behind the scenes, so it's advisable to
// avoid anything that's not a system call. read(), write(), fork() and execv()
// aren't guaranteed to be pure system calls, either, but we can't get any
// closer to this ideal without writing logic specific to each POSIX system we
// run on.
//
// Another way to tackle this issue would be to pre-fork a child process before
// spawning any threads which is then used to fork all the other necessary
// child processes. However, then we'd need to invent a protocol that can handle
// sending stdout/stderr back and forking multiple times, which isn't trivial,
// either.
//
// Yet another fix would be not to use multiple threads before forking. However,
// we need to use gRPC to figure out if a server process is already present and
// gRPC currently cannot be convinced not to spawn any threads.
//
// Another way would be to use posix_spawn(). However, since we need to create a
// daemon process, we'd need to posix_spawn() a little child process that
// daemonizes then exec()s the actual JVM, which is also non-trivial. So I hope
// this will be good enough because for all its flaws, this solution is at least
// localized here.
int ExecuteDaemon(const string& exe,
const std::vector<string>& args_vector,
const std::map<string, EnvVarValue>& env,
const string& daemon_output,
const bool daemon_output_append,
const string& server_dir,
BlazeServerStartup** server_startup) {
int fds[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) {
BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR)
<< "socket creation failed: " << GetLastErrorString();
}
const char* daemon_output_chars = daemon_output.c_str();
const char** argv = ConvertStringVectorToArgv(args_vector);
const char* exe_chars = exe.c_str();
int child = fork();
if (child == -1) {
BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR)
<< "fork() failed: " << GetLastErrorString();
return -1;
} else if (child > 0) {
// Parent process (i.e. the client)
close(fds[1]); // parent keeps one side...
int unused_status;
waitpid(child, &unused_status, 0); // child double-forks
pid_t server_pid = 0;
ReadFromFdWithRetryEintr(fds[0], &server_pid, sizeof server_pid,
"cannot read server PID from server");
string pid_file = blaze_util::JoinPath(server_dir, kServerPidFile);
if (!blaze_util::WriteFile(ToString(server_pid), pid_file)) {
BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR)
<< "cannot write PID file: " << GetLastErrorString();
return -1;
}
WriteSystemSpecificProcessIdentifier(server_dir, server_pid);
char dummy = 'a';
WriteToFdWithRetryEintr(fds[0], &dummy, 1,
"cannot notify server about having written PID file");
*server_startup = new SocketBlazeServerStartup(fds[0]);
return server_pid;
} else {
// Child process (i.e. the server)
// NB: There should only be system calls in this branch. See the comment
// before ExecuteDaemon() to understand why.
close(fds[0]); // ...child keeps the other.
{
WithEnvVars env_obj(env);
Daemonize(daemon_output_chars, daemon_output_append);
pid_t server_pid = getpid();
WriteToFdWithRetryEintr(fds[1], &server_pid, sizeof server_pid,
"cannot communicate server PID to client");
// We wait until the client writes the PID file so that there is no race
// condition; the server expects the PID file to already be there so that
// it can read it and know its own PID (see the ctor GrpcServerImpl) and
// so that it can kill itself if the PID file is deleted (see
// GrpcServerImpl.PidFileWatcherThread)
char dummy;
ReadFromFdWithRetryEintr(
fds[1], &dummy, 1,
"cannot get PID file write acknowledgement from client");
execv(exe_chars, const_cast<char**>(argv));
DieAfterFork("Cannot execute daemon");
return -1;
}
}
}
bool CompareAbsolutePaths(const string& a, const string& b) {
return a == b;
}
string GetHashedBaseDir(const string& root, const string& hashable) {
unsigned char buf[blaze_util::Md5Digest::kDigestLength];
blaze_util::Md5Digest digest;
digest.Update(hashable.data(), hashable.size());
digest.Finish(buf);
return blaze_util::JoinPath(root, digest.String());
}
void CreateSecureOutputRoot(const string& path) {
const char* root = path.c_str();
struct stat fileinfo = {};
if (!blaze_util::MakeDirectories(root, 0755)) {
BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
<< "mkdir('" << root << "'): " << GetLastErrorString();
}
// The path already exists.
// Check ownership and mode, and verify that it is a directory.
if (lstat(root, &fileinfo) < 0) {
BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
<< "lstat('" << root << "'): " << GetLastErrorString();
}
if (fileinfo.st_uid != geteuid()) {
BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
<< "'" << root << "' is not owned by me";
}
if ((fileinfo.st_mode & 022) != 0) {
int new_mode = fileinfo.st_mode & (~022);
if (chmod(root, new_mode) < 0) {
BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
<< "'" << root << "' has mode " << (fileinfo.st_mode & 07777)
<< ", chmod to " << new_mode << " failed";
}
}
if (stat(root, &fileinfo) < 0) {
BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
<< "stat('" << root << "'): " << GetLastErrorString();
}
if (!S_ISDIR(fileinfo.st_mode)) {
BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
<< "'" << root << "' is not a directory";
}
ExcludePathFromBackup(root);
}
string GetEnv(const string& name) {
char* result = getenv(name.c_str());
return result != NULL ? string(result) : "";
}
bool ExistsEnv(const string& name) {
return getenv(name.c_str()) != NULL;
}
void SetEnv(const string& name, const string& value) {
setenv(name.c_str(), value.c_str(), 1);
}
void UnsetEnv(const string& name) {
unsetenv(name.c_str());
}
bool WarnIfStartedFromDesktop() { return false; }
void SetupStdStreams() {
// Set non-buffered output mode for stderr/stdout. The server already
// line-buffers messages where it makes sense, so there's no need to do set
// line-buffering here. On the other hand the server sometimes sends binary
// output (when for example a query returns results as proto), in which case
// we must not perform line buffering on the client side. So turn off
// buffering here completely.
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
// Ensure we have three open fds. Otherwise we can end up with
// bizarre things like stdout going to the lock file, etc.
if (fcntl(STDIN_FILENO, F_GETFL) == -1) open("/dev/null", O_RDONLY);
if (fcntl(STDOUT_FILENO, F_GETFL) == -1) open("/dev/null", O_WRONLY);
if (fcntl(STDERR_FILENO, F_GETFL) == -1) open("/dev/null", O_WRONLY);
}
// A signal-safe version of fprintf(stderr, ...).
//
// WARNING: any output from the blaze client may be interleaved
// with output from the blaze server. In --curses mode,
// the Blaze server often erases the previous line of output.
// So, be sure to end each such message with TWO newlines,
// otherwise it may be erased by the next message from the
// Blaze server.
// Also, it's a good idea to start each message with a newline,
// in case the Blaze server has written a partial line.
void SigPrintf(const char *format, ...) {
char buf[1024];
va_list ap;
va_start(ap, format);
int r = vsnprintf(buf, sizeof buf, format, ap);
va_end(ap);
if (write(STDERR_FILENO, buf, r) <= 0) {
// We don't care, just placate the compiler.
}
}
static int setlk(int fd, struct flock *lock) {
#ifdef __linux__
// If we're building with glibc <2.20, or another libc which predates
// OFD locks, define the constant ourselves. This assumes that the libc
// and kernel definitions for struct flock are identical.
#ifndef F_OFD_SETLK
#define F_OFD_SETLK 37
#endif
#endif
#ifdef F_OFD_SETLK
// Prefer OFD locks if available. POSIX locks can be lost "accidentally"
// due to any close() on the lock file, and are not reliably preserved
// across execve() on Linux, which we need for --batch mode.
if (fcntl(fd, F_OFD_SETLK, lock) == 0) return 0;
if (errno != EINVAL) {
if (errno != EACCES && errno != EAGAIN) {
BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
<< "unexpected result from F_OFD_SETLK: " << GetLastErrorString();
}
return -1;
}
// F_OFD_SETLK was added in Linux 3.15. Older kernels return EINVAL.
// Fall back to F_SETLK in that case.
#endif
if (fcntl(fd, F_SETLK, lock) == 0) return 0;
if (errno != EACCES && errno != EAGAIN) {
BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
<< "unexpected result from F_SETLK: " << GetLastErrorString();
}
return -1;
}
uint64_t AcquireLock(const string& output_base, bool batch_mode, bool block,
BlazeLock* blaze_lock) {
string lockfile = blaze_util::JoinPath(output_base, "lock");
int lockfd = open(lockfile.c_str(), O_CREAT|O_RDWR, 0644);
if (lockfd < 0) {
BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
<< "cannot open lockfile '" << lockfile
<< "' for writing: " << GetLastErrorString();
}
// Keep server from inheriting a useless fd if we are not in batch mode
if (!batch_mode) {
if (fcntl(lockfd, F_SETFD, FD_CLOEXEC) == -1) {
BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
<< "fcntl(F_SETFD) failed for lockfile: " << GetLastErrorString();
}
}
struct flock lock = {};
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
// This doesn't really matter now, but allows us to subdivide the lock
// later if that becomes meaningful. (Ranges beyond EOF can be locked.)
lock.l_len = 4096;
// Take the exclusive server lock. If we fail, we busy-wait until the lock
// becomes available.
//
// We used to rely on fcntl(F_SETLKW) to lazy-wait for the lock to become
// available, which is theoretically fine, but doing so prevents us from
// determining if the PID of the server holding the lock has changed under the
// hood. There have been multiple bug reports where users (especially macOS
// ones) mention that the Blaze invocation hangs on a non-existent PID. This
// should help troubleshoot those scenarios in case there really is a bug
// somewhere.
bool multiple_attempts = false;
string owner;
const uint64_t start_time = GetMillisecondsMonotonic();
while (setlk(lockfd, &lock) == -1) {
string buffer(4096, 0);
ssize_t r = pread(lockfd, &buffer[0], buffer.size(), 0);
if (r < 0) {
BAZEL_LOG(WARNING) << "pread() lock file: " << strerror(errno);
r = 0;
}
buffer.resize(r);
if (owner != buffer) {
// Each time we learn a new lock owner, print it out.
owner = buffer;
BAZEL_LOG(USER) << "Another command holds the client lock: \n" << owner;
if (block) {
BAZEL_LOG(USER) << "Waiting for it to complete...";
fflush(stderr);
}
}
if (!block) {
BAZEL_DIE(blaze_exit_code::BAD_ARGV)
<< "Exiting because the lock is held and --noblock_for_lock was "
"given.";
}
TrySleep(500);
multiple_attempts = true;
}
const uint64_t end_time = GetMillisecondsMonotonic();
// If we took the lock on the first try, force the reported wait time to 0 to
// avoid unnecessary noise in the logs. In this metric, we are only
// interested in knowing how long it took for other commands to complete, not
// how fast acquiring a lock is.
const uint64_t wait_time = !multiple_attempts ? 0 : end_time - start_time;
// Identify ourselves in the lockfile.
// The contents are printed for human consumption when another client
// fails to take the lock, but not parsed otherwise.
(void) ftruncate(lockfd, 0);
lseek(lockfd, 0, SEEK_SET);
// Arguably we should ensure this fits in the 4KB we lock. In practice no one
// will have a cwd long enough to overflow that, and nothing currently uses
// the rest of the lock file anyway.
dprintf(lockfd, "pid=%d\nowner=client\n", getpid());
string cwd = blaze_util::GetCwd();
dprintf(lockfd, "cwd=%s\n", cwd.c_str());
if (const char *tty = ttyname(STDIN_FILENO)) { // NOLINT (single-threaded)
dprintf(lockfd, "tty=%s\n", tty);
}
blaze_lock->lockfd = lockfd;
return wait_time;
}
void ReleaseLock(BlazeLock* blaze_lock) {
close(blaze_lock->lockfd);
}
bool KillServerProcess(int pid, const string& output_base) {
// Kill the process and make sure it's dead before proceeding.
killpg(pid, SIGKILL);
if (!AwaitServerProcessTermination(pid, output_base,
kPostKillGracePeriodSeconds)) {
BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
<< "Attempted to kill stale server process (pid=" << pid
<< ") using SIGKILL, but it did not die in a timely fashion.";
}
return true;
}
void TrySleep(unsigned int milliseconds) {
time_t seconds_part = (time_t)(milliseconds / 1000);
long nanoseconds_part = ((long)(milliseconds % 1000)) * 1000 * 1000;
struct timespec sleeptime = {seconds_part, nanoseconds_part};
nanosleep(&sleeptime, NULL);
}
string GetUserName() {
string user = GetEnv("USER");
if (!user.empty()) {
return user;
}
errno = 0;
passwd *pwent = getpwuid(getuid()); // NOLINT (single-threaded)
if (pwent == NULL || pwent->pw_name == NULL) {
BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
<< "$USER is not set, and unable to look up name of current user: "
<< GetLastErrorString();
}
return pwent->pw_name;
}
bool IsEmacsTerminal() {
string emacs = GetEnv("EMACS");
string inside_emacs = GetEnv("INSIDE_EMACS");
// GNU Emacs <25.1 (and ~all non-GNU emacsen) set EMACS=t, but >=25.1 doesn't
// do that and instead sets INSIDE_EMACS=<stuff> (where <stuff> can look like
// e.g. "25.1.1,comint"). So we check both variables for maximum
// compatibility.
return emacs == "t" || !inside_emacs.empty();
}
// Returns true iff both stdout and stderr are connected to a
// terminal, and it can support color and cursor movement
// (this is computed heuristically based on the values of
// environment variables).
bool IsStandardTerminal() {
string term = GetEnv("TERM");
if (term.empty() || term == "dumb" || term == "emacs" ||
term == "xterm-mono" || term == "symbolics" || term == "9term" ||
IsEmacsTerminal()) {
return false;
}
return isatty(STDOUT_FILENO) && isatty(STDERR_FILENO);
}
// Returns the number of columns of the terminal to which stdout is
// connected, or $COLUMNS (default 80) if there is no such terminal.
int GetTerminalColumns() {
struct winsize ws;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) {
return ws.ws_col;
}
string columns_env = GetEnv("COLUMNS");
if (!columns_env.empty()) {
char* endptr;
int columns = blaze_util::strto32(columns_env.c_str(), &endptr, 10);
if (*endptr == '\0') { // $COLUMNS is a valid number
return columns;
}
}
return 80; // default if not a terminal.
}
// Raises a resource limit to the maximum allowed value.
//
// This function raises the limit of the resource given in "resource" from its
// soft limit to its hard limit. If the hard limit is unlimited, uses the
// kernel-level limit fetched from the sysctl property given in "sysctl_name"
// because setting the soft limit to unlimited may not work.
//
// Note that this is a best-effort operation. Any failure during this process
// will result in a warning but execution will continue.
static bool UnlimitResource(const int resource) {
struct rlimit rl;
if (getrlimit(resource, &rl) == -1) {
BAZEL_LOG(WARNING) << "failed to get resource limit " << resource << ": "
<< strerror(errno);
return false;
}
if (rl.rlim_cur == rl.rlim_max) {
// Nothing to do. Return early to prevent triggering any warnings caused by
// the code below. This way, we will only show warnings the first time the
// Blaze server is started and not on each command invocation.
return true;
}
rl.rlim_cur = rl.rlim_max;
if (rl.rlim_cur == RLIM_INFINITY) {
const rlim_t explicit_limit = GetExplicitSystemLimit(resource);
if (explicit_limit <= 0) {
// If not implemented (-1) or on an error (0), do nothing and try to
// increase the soft limit to the hard one. This might fail, but it's good
// to try anyway.
assert(rl.rlim_cur == rl.rlim_max);
} else {
rl.rlim_cur = explicit_limit;
}
}
if (setrlimit(resource, &rl) == -1) {
BAZEL_LOG(WARNING) << "failed to raise resource limit " << resource
<< " to " << static_cast<intmax_t>(rl.rlim_cur) << ": "
<< strerror(errno);
return false;
}
return true;
}
bool UnlimitResources() {
bool success = true;
success &= UnlimitResource(RLIMIT_NOFILE);
success &= UnlimitResource(RLIMIT_NPROC);
return success;
}
void DetectBashOrDie() {
// do nothing.
}
void EnsurePythonPathOption(vector<string>* options) {
// do nothing.
}
} // namespace blaze.