| // 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. | 
 |  | 
 | #ifndef _GNU_SOURCE | 
 | #define _GNU_SOURCE 1  // for F_OFD_SETLK on Linux | 
 | #endif | 
 |  | 
 | #define _WITH_DPRINTF | 
 |  | 
 | #include <dirent.h> | 
 | #include <errno.h> | 
 | #include <fcntl.h> | 
 | #include <limits.h> | 
 | #include <poll.h> | 
 | #include <pwd.h> | 
 | #include <signal.h> | 
 | #include <spawn.h> | 
 | #include <stdarg.h> | 
 | #include <stdint.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 <algorithm> | 
 | #include <cassert> | 
 | #include <cstdlib> | 
 | #include <fstream> | 
 | #include <iterator> | 
 | #include <map> | 
 | #include <set> | 
 | #include <string> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "src/main/cpp/blaze_util.h" | 
 | #include "src/main/cpp/blaze_util_platform.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_platform.h" | 
 | #include "src/main/cpp/util/logging.h" | 
 | #include "src/main/cpp/util/md5.h" | 
 | #include "src/main/cpp/util/numbers.h" | 
 | #include "src/main/cpp/util/path.h" | 
 | #include "src/main/cpp/util/path_platform.h" | 
 | #include "src/main/cpp/util/strings.h" | 
 |  | 
 | namespace blaze { | 
 |  | 
 | using blaze_util::GetLastErrorString; | 
 |  | 
 | using std::set; | 
 | using std::string; | 
 | using std::vector; | 
 |  | 
 | namespace embedded_binaries { | 
 |  | 
 | class PosixDumper : public Dumper { | 
 |  public: | 
 |   static PosixDumper* Create(string* error); | 
 |   ~PosixDumper() { Finish(nullptr); } | 
 |   void Dump(const void* data, size_t size, | 
 |             const blaze_util::Path& path) override; | 
 |   bool Finish(string* error) override; | 
 |  | 
 |  private: | 
 |   PosixDumper() : was_error_(false) {} | 
 |  | 
 |   set<blaze_util::Path> dir_cache_; | 
 |   string error_msg_; | 
 |   bool was_error_; | 
 | }; | 
 |  | 
 | Dumper* Create(string* error) { return PosixDumper::Create(error); } | 
 |  | 
 | PosixDumper* PosixDumper::Create(string* error) { return new PosixDumper(); } | 
 |  | 
 | void PosixDumper::Dump(const void* data, const size_t size, | 
 |                        const blaze_util::Path& path) { | 
 |   if (was_error_) { | 
 |     return; | 
 |   } | 
 |  | 
 |   blaze_util::Path parent = path.GetParent(); | 
 |   // Performance optimization: memoize the paths we already created a | 
 |   // directory for, to spare a stat in attempting to recreate an already | 
 |   // existing directory. | 
 |   if (dir_cache_.insert(parent).second) { | 
 |     if (!blaze_util::MakeDirectories(parent, 0777)) { | 
 |       was_error_ = true; | 
 |       string msg = GetLastErrorString(); | 
 |       error_msg_ = | 
 |           string("couldn't create '") + path.AsPrintablePath() + "': " + msg; | 
 |     } | 
 |   } | 
 |  | 
 |   if (was_error_) { | 
 |     return; | 
 |   } | 
 |  | 
 |   if (!blaze_util::WriteFile(data, size, path, 0755)) { | 
 |     was_error_ = true; | 
 |     string msg = GetLastErrorString(); | 
 |     error_msg_ = string("Failed to write zipped file '") + | 
 |                  path.AsPrintablePath() + "': " + msg; | 
 |   } | 
 | } | 
 |  | 
 | bool PosixDumper::Finish(string* error) { | 
 |   if (was_error_ && error) { | 
 |     *error = error_msg_; | 
 |   } | 
 |   return !was_error_; | 
 | } | 
 |  | 
 | }  // namespace embedded_binaries | 
 |  | 
 | 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; server killed. (This may be " | 
 |             "expensive, see https://bazel.build/advanced/performance/" | 
 |             "iteration-speed#avoid-ctrl-c.)\n\n", | 
 |             SignalHandler::Get().GetProductName().c_str()); | 
 |         if (SignalHandler::Get().GetServerProcessInfo()->server_pid_ != -1) { | 
 |           KillServerProcess( | 
 |               SignalHandler::Get().GetServerProcessInfo()->server_pid_, | 
 |               SignalHandler::Get().GetOutputBase()); | 
 |         } | 
 |         _exit(1); | 
 |       } | 
 |       SigPrintf( | 
 |           "\n%s caught interrupt signal; cancelling pending invocation.\n\n", | 
 |           SignalHandler::Get().GetProductName().c_str()); | 
 |       SignalHandler::Get().CancelServer(); | 
 |       break; | 
 |     case SIGTERM: | 
 |       SigPrintf( | 
 |           "\n%s caught terminate signal; cancelling pending invocation.\n\n", | 
 |           SignalHandler::Get().GetProductName().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().GetServerProcessInfo()->server_pid_, | 
 |                 SignalHandler::Get() | 
 |                     .GetServerProcessInfo() | 
 |                     ->jvm_log_file_.AsNativePath() | 
 |                     .c_str()); | 
 |       kill(SignalHandler::Get().GetServerProcessInfo()->server_pid_, SIGQUIT); | 
 |       break; | 
 |   } | 
 |  | 
 |   errno = saved_errno; | 
 | } | 
 |  | 
 | void SignalHandler::Install(const string& product_name, | 
 |                             const blaze_util::Path& output_base, | 
 |                             const ServerProcessInfo* server_process_info, | 
 |                             SignalHandler::Callback cancel_server) { | 
 |   product_name_ = product_name; | 
 |   output_base_ = output_base; | 
 |   server_process_info_ = server_process_info; | 
 |   cancel_server_ = cancel_server; | 
 |  | 
 |   // Unblock all signals. | 
 |   sigset_t sigset; | 
 |   sigemptyset(&sigset); | 
 |   sigprocmask(SIG_SETMASK, &sigset, nullptr); | 
 |  | 
 |   // SIGWINCH is reserved for Bazel server internal use and cannot be passed to | 
 |   // it. The JVM is not attached to a terminal, making a signal insufficient to | 
 |   // react to window size change event anyway. | 
 |   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 blaze_util::ToString(getpid()); } | 
 |  | 
 | string GetHomeDir() { return GetPathEnv("HOME"); } | 
 |  | 
 | string GetJavaBinaryUnderJavabase() { return "bin/java"; } | 
 |  | 
 | string Which(const string& executable) { | 
 |   const string path = GetPathEnv("PATH"); | 
 |   if (path.empty()) { | 
 |     return ""; | 
 |   } | 
 |  | 
 |   const vector<string> pieces = blaze_util::Split(path, ':'); | 
 |   for (string piece : pieces) { | 
 |     if (piece.empty()) { | 
 |       piece = "."; | 
 |     } | 
 |  | 
 |     struct stat file_stat; | 
 |     const string candidate = blaze_util::JoinPath(piece, executable); | 
 |     if (access(candidate.c_str(), X_OK) == 0 && | 
 |         stat(candidate.c_str(), &file_stat) == 0 && | 
 |         S_ISREG(file_stat.st_mode)) { | 
 |       return candidate; | 
 |     } | 
 |   } | 
 |   return ""; | 
 | } | 
 |  | 
 | // Converter of C++ data structures to a C-style array of strings. | 
 | // | 
 | // The primary consumer of this class is the execv family of functions | 
 | // (including posix_spawn) which require mutable arrays as their inputs even | 
 | // if they do not modify them. | 
 | class CharPP { | 
 |  public: | 
 |   // Constructs a new CharPP from a list of arguments. | 
 |   explicit CharPP(const vector<string>& args) { | 
 |     charpp_ = static_cast<char**>(malloc(sizeof(char*) * (args.size() + 1))); | 
 |     size_t i = 0; | 
 |     for (; i < args.size(); i++) { | 
 |       charpp_[i] = strdup(args[i].c_str()); | 
 |     } | 
 |     charpp_[i] = nullptr; | 
 |   } | 
 |  | 
 |   // Constructs a new CharPP from a list of environment variables. | 
 |   explicit CharPP(const std::map<string, EnvVarValue>& env) { | 
 |     charpp_ = static_cast<char**>(malloc(sizeof(char*) * (env.size() + 1))); | 
 |     size_t i = 0; | 
 |     for (const auto& iter : env) { | 
 |       const string& var = iter.first; | 
 |       const EnvVarValue& value = iter.second; | 
 |  | 
 |       switch (value.action) { | 
 |         case SET: | 
 |           charpp_[i] = strdup((var + "=" + value.value).c_str()); | 
 |           i++; | 
 |           break; | 
 |  | 
 |         case UNSET: | 
 |           break; | 
 |  | 
 |         default: | 
 |           assert(false); | 
 |       } | 
 |     } | 
 |     charpp_[i] = nullptr; | 
 |   } | 
 |  | 
 |   // Deletes all memory held by the CharPP. | 
 |   ~CharPP() { | 
 |     for (char** ptr = charpp_; *ptr != nullptr; ptr++) { | 
 |       free(*ptr); | 
 |     } | 
 |     free(charpp_); | 
 |   } | 
 |  | 
 |   // Obtains the raw pointer to the array of strings. | 
 |   char** get() { return charpp_; } | 
 |  | 
 |   // Prevent copies as we manually manage memory. | 
 |   CharPP(const CharPP&) = delete; | 
 |   CharPP& operator=(const CharPP&) = delete; | 
 |  | 
 |  private: | 
 |   char** charpp_; | 
 | }; | 
 |  | 
 | ATTRIBUTE_NORETURN static void ExecuteProgram(const blaze_util::Path& exe, | 
 |                                               const vector<string>& args_vector, | 
 |                                               const bool run_in_user_cgroup) { | 
 |   BAZEL_LOG(INFO) << "Invoking binary " << exe.AsPrintablePath() << " in " | 
 |                   << blaze_util::GetCwd(); | 
 |  | 
 |   // TODO(jmmv): This execution does not respect any settings we might apply | 
 |   // to the server process with ConfigureDaemonProcess when executed in the | 
 |   // background as a daemon.  Because we use that function to lower the | 
 |   // priority of Bazel on macOS from a QoS perspective, this could have | 
 |   // adverse scheduling effects on any tools invoked via ExecuteProgram. | 
 |   CharPP argv(args_vector); | 
 |   execv(exe.AsNativePath().c_str(), argv.get()); | 
 |   string err = GetLastErrorString(); | 
 |   BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR) | 
 |       << "execv of '" << exe.AsPrintablePath() << "' failed: " << err; | 
 |   // TODO(jmmv): Mark BAZEL_DIE as ATTRIBUTE_NORETURN so that the following | 
 |   // code is not necessary. | 
 |   std::abort();  // Not reachable. | 
 | } | 
 |  | 
 | void ExecuteServerJvm(const blaze_util::Path& exe, | 
 |                       const std::vector<string>& server_jvm_args, | 
 |                       const bool run_in_user_cgroup) { | 
 |   ExecuteProgram(exe, server_jvm_args, run_in_user_cgroup); | 
 | } | 
 |  | 
 | void ExecuteRunRequest(const blaze_util::Path& exe, | 
 |                        const std::vector<string>& run_request_args) { | 
 |   ExecuteProgram(exe, run_request_args, | 
 |                  /* run_in_user_cgroup= */ false); | 
 | } | 
 |  | 
 | const char kListSeparator = ':'; | 
 |  | 
 | bool SymlinkDirectories(const blaze_util::Path& target, | 
 |                         const blaze_util::Path& link) { | 
 |   return symlink(target.AsNativePath().c_str(), link.AsNativePath().c_str()) == | 
 |          0; | 
 | } | 
 |  | 
 | // 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; | 
 |   } | 
 | } | 
 |  | 
 | // Sets platform-specific attributes for the creation of the daemon process. | 
 | // | 
 | // Returns zero on success or -1 on error, in which case errno is set to the | 
 | // corresponding error details. | 
 | int ConfigureDaemonProcess(posix_spawnattr_t* attrp, | 
 |                            const StartupOptions& options); | 
 |  | 
 | void WriteSystemSpecificProcessIdentifier(const blaze_util::Path& server_dir, | 
 |                                           pid_t server_pid); | 
 |  | 
 | int ExecuteDaemon( | 
 |     const blaze_util::Path& exe, const std::vector<string>& args_vector, | 
 |     const std::map<string, EnvVarValue>& env, | 
 |     const blaze_util::Path& daemon_output, const bool daemon_output_append, | 
 |     const blaze_util::Path& binaries_dir, const blaze_util::Path& server_dir, | 
 |     const StartupOptions& options, BlazeServerStartup** server_startup) { | 
 |   const blaze_util::Path pid_file = server_dir.GetRelative(kServerPidFile); | 
 |   const string daemonize = binaries_dir.GetRelative("daemonize").AsNativePath(); | 
 |  | 
 |   std::vector<string> daemonize_args = {"daemonize", "-l", | 
 |                                         daemon_output.AsNativePath(), "-p", | 
 |                                         pid_file.AsNativePath()}; | 
 |   if (daemon_output_append) { | 
 |     daemonize_args.push_back("-a"); | 
 |   } | 
 | #ifdef __linux__ | 
 |   if (!options.cgroup_parent.empty()) { | 
 |     daemonize_args.push_back("-c"); | 
 |     daemonize_args.push_back(options.cgroup_parent); | 
 |   } | 
 |   if (options.run_in_user_cgroup) { | 
 |     daemonize_args.push_back("-s"); | 
 |     daemonize_args.push_back( | 
 |         server_dir.GetRelative("systemd-wrapper.sh").AsNativePath()); | 
 |   } | 
 | #endif | 
 |   daemonize_args.push_back("--"); | 
 |   daemonize_args.push_back(exe.AsNativePath()); | 
 |   std::copy(args_vector.begin(), args_vector.end(), | 
 |             std::back_inserter(daemonize_args)); | 
 |  | 
 |   int fds[2]; | 
 |  | 
 |   if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) { | 
 |     BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR) | 
 |         << "socket creation failed: " << GetLastErrorString(); | 
 |   } | 
 |  | 
 |   posix_spawn_file_actions_t file_actions; | 
 |   if (posix_spawn_file_actions_init(&file_actions) != 0) { | 
 |     BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR) | 
 |         << "Failed to create posix_spawn_file_actions: " | 
 |         << GetLastErrorString(); | 
 |   } | 
 |   if (posix_spawn_file_actions_addclose(&file_actions, fds[0]) != 0) { | 
 |     BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR) | 
 |         << "Failed to modify posix_spawn_file_actions: " | 
 |         << GetLastErrorString(); | 
 |   } | 
 |  | 
 |   posix_spawnattr_t attrp; | 
 |   if (posix_spawnattr_init(&attrp) != 0) { | 
 |     BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR) | 
 |         << "Failed to create posix_spawnattr: " << GetLastErrorString(); | 
 |   } | 
 |   if (ConfigureDaemonProcess(&attrp, options) == -1) { | 
 |     BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR) | 
 |         << "Failed to modify posix_spawnattr: " << GetLastErrorString(); | 
 |   } | 
 |  | 
 |   pid_t transient_pid; | 
 |   if (posix_spawn(&transient_pid, daemonize.c_str(), &file_actions, &attrp, | 
 |                   CharPP(daemonize_args).get(), CharPP(env).get()) != 0) { | 
 |     BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR) | 
 |         << "Failed to execute JVM via " << daemonize << ": " | 
 |         << GetLastErrorString(); | 
 |   } | 
 |   close(fds[1]); | 
 |  | 
 |   posix_spawnattr_destroy(&attrp); | 
 |   posix_spawn_file_actions_destroy(&file_actions); | 
 |  | 
 |   // Wait for daemonize to exit. This guarantees that the pid file exists. | 
 |   int status; | 
 |   if (waitpid(transient_pid, &status, 0) == -1) { | 
 |     BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR) | 
 |         << "waitpid failed: " << GetLastErrorString(); | 
 |   } | 
 |   if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS) { | 
 |     BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR) | 
 |         << "daemonize didn't exit cleanly: " << GetLastErrorString(); | 
 |   } | 
 |  | 
 |   std::ifstream pid_reader(pid_file.AsNativePath()); | 
 |   if (!pid_reader) { | 
 |     string err = GetLastErrorString(); | 
 |     BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR) | 
 |         << "Failed to open " << pid_file.AsPrintablePath() << ": " << err; | 
 |   } | 
 |   pid_t server_pid; | 
 |   pid_reader >> server_pid; | 
 |   if (pid_reader.fail()) { | 
 |     BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR) | 
 |         << "Failed to read pid from: " << pid_file.AsPrintablePath(); | 
 |   } | 
 |  | 
 |   WriteSystemSpecificProcessIdentifier(server_dir, server_pid); | 
 |  | 
 |   *server_startup = new SocketBlazeServerStartup(fds[0]); | 
 |   return server_pid; | 
 | } | 
 |  | 
 | blaze_util::Path GetHashedBaseDir(const blaze_util::Path& 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 root.GetRelative(digest.String()); | 
 | } | 
 |  | 
 | void CreateSecureDirectory(const blaze_util::Path& path) { | 
 |   struct stat fileinfo = {}; | 
 |  | 
 |   if (!blaze_util::MakeDirectories(path, 0755)) { | 
 |     string err = GetLastErrorString(); | 
 |     BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) | 
 |         << "mkdir('" << path.AsPrintablePath() << "'): " << err; | 
 |   } | 
 |  | 
 |   // The path already exists. | 
 |   // Check ownership and mode, and verify that it is a directory. | 
 |  | 
 |   if (lstat(path.AsNativePath().c_str(), &fileinfo) < 0) { | 
 |     string err = GetLastErrorString(); | 
 |     BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) | 
 |         << "lstat('" << path.AsPrintablePath() << "'): " << err; | 
 |   } | 
 |  | 
 |   if (fileinfo.st_uid != geteuid()) { | 
 |     BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) | 
 |         << "'" << path.AsPrintablePath() << "' is not owned by me"; | 
 |   } | 
 |  | 
 |   if ((fileinfo.st_mode & 022) != 0) { | 
 |     int new_mode = fileinfo.st_mode & (~022); | 
 |     if (chmod(path.AsNativePath().c_str(), new_mode) < 0) { | 
 |       BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) | 
 |           << "'" << path.AsPrintablePath() << "' has mode " | 
 |           << (fileinfo.st_mode & 07777) << ", chmod to " << new_mode | 
 |           << " failed"; | 
 |     } | 
 |   } | 
 |  | 
 |   if (stat(path.AsNativePath().c_str(), &fileinfo) < 0) { | 
 |     string err = GetLastErrorString(); | 
 |     BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) | 
 |         << "stat('" << path.AsPrintablePath() << "'): " << err; | 
 |   } | 
 |  | 
 |   if (!S_ISDIR(fileinfo.st_mode)) { | 
 |     BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) | 
 |         << "'" << path.AsPrintablePath() << "' is not a directory"; | 
 |   } | 
 |  | 
 |   ExcludePathFromBackup(path); | 
 | } | 
 |  | 
 | string GetEnv(const string& name) { | 
 |   char* result = getenv(name.c_str()); | 
 |   return result != nullptr ? string(result) : ""; | 
 | } | 
 |  | 
 | string GetPathEnv(const string& name) { return GetEnv(name); } | 
 |  | 
 | bool ExistsEnv(const string& name) { return getenv(name.c_str()) != nullptr; } | 
 |  | 
 | 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, nullptr, _IONBF, 0); | 
 |   setvbuf(stderr, nullptr, _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 bool TryLock(int fd, LockMode mode, const string& name) { | 
 |   struct flock lock = {}; | 
 |   lock.l_type = static_cast<short>(  // NOLINT (short is the right type) | 
 |       mode == LockMode::kShared ? F_RDLCK : F_WRLCK); | 
 |   // Locking is advisory, so any range works as long as it overlaps with the | 
 |   // ones requested by other processes. | 
 |   lock.l_whence = SEEK_SET; | 
 |   lock.l_start = 0; | 
 |   lock.l_len = 1; | 
 |   // Prefer OFD locks when 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. | 
 | #ifdef F_OFD_SETLK | 
 |   if (fcntl(fd, F_OFD_SETLK, &lock) == 0) { | 
 |     return true; | 
 |   } | 
 |   if (errno != EINVAL) { | 
 |     if (errno != EACCES && errno != EAGAIN) { | 
 |       BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) | 
 |           << "fcntl failed for " << name << " lock: " << GetLastErrorString(); | 
 |     } | 
 |     return false; | 
 |   } | 
 |   // Fall back to POSIX locks on EINVAL. | 
 | #endif | 
 |   if (fcntl(fd, F_SETLK, &lock) == 0) { | 
 |     return true; | 
 |   } | 
 |   if (errno != EACCES && errno != EAGAIN) { | 
 |     BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) | 
 |         << "fctnl failed for " << name << " lock: " << GetLastErrorString(); | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | static bool StillExists(int fd, const string& name) { | 
 |   struct stat st; | 
 |   if (fstat(fd, &st) < 0) { | 
 |     BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) | 
 |         << "fstat failed for " << name << " lock: " << GetLastErrorString(); | 
 |   } | 
 |   return st.st_nlink > 0; | 
 | } | 
 |  | 
 | static string ReadOwnerInformation(int fd, const string& name) { | 
 |   // Assume that 4KB are sufficient to fit the owner information. | 
 |   string buffer(4096, 0); | 
 |   ssize_t r = pread(fd, &buffer[0], buffer.size(), 0); | 
 |   if (r < 0) { | 
 |     BAZEL_LOG(WARNING) << "pread failed for " << name | 
 |                        << " lock: " << GetLastErrorString(); | 
 |     r = 0; | 
 |   } | 
 |   buffer.resize(r); | 
 |   return buffer; | 
 | } | 
 |  | 
 | static void WriteOwnerInformation(int fd) { | 
 |   (void)ftruncate(fd, 0); | 
 |   lseek(fd, 0, SEEK_SET); | 
 |   // Locking is advisory, so it doesn't matter that this may overflow the | 
 |   // locked range. | 
 |   dprintf(fd, "pid=%d\nowner=client\n", getpid()); | 
 |   string cwd = blaze_util::GetCwd(); | 
 |   dprintf(fd, "cwd=%s\n", cwd.c_str()); | 
 |   const char* tty = ttyname(STDIN_FILENO);  // NOLINT (single-threaded) | 
 |   if (tty != nullptr) { | 
 |     dprintf(fd, "tty=%s\n", tty); | 
 |   } | 
 | } | 
 |  | 
 | std::pair<LockHandle, DurationMillis> AcquireLock(const std::string& name, | 
 |                                                   const blaze_util::Path& path, | 
 |                                                   LockMode mode, | 
 |                                                   bool batch_mode, bool block) { | 
 |   const uint64_t start_time = GetMillisecondsMonotonic(); | 
 |   bool multiple_attempts = false; | 
 |   string owner; | 
 |  | 
 |   while (true) { | 
 |     int flags = O_CREAT | (mode == LockMode::kShared ? O_RDONLY : O_RDWR); | 
 |     // Keep server from inheriting a useless fd if we are not in batch mode. | 
 |     if (!batch_mode) { | 
 |       flags |= O_CLOEXEC; | 
 |     } | 
 |  | 
 |     int fd = open(path.AsNativePath().c_str(), flags, 0644); | 
 |     if (fd < 0) { | 
 |       BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) | 
 |           << "open failed for " << name << " lock: " << GetLastErrorString(); | 
 |     } | 
 |  | 
 |     // Attempt to take the lock. | 
 |     if (TryLock(fd, mode, name)) { | 
 |       // Check that the lock file was not concurrently deleted. | 
 |       if (StillExists(fd, name)) { | 
 |         // If taking an exclusive lock, identify ourselves in the lock file. | 
 |         // The contents are printed for human consumption when another client | 
 |         // fails to take the lock, but not parsed otherwise. | 
 |         if (mode == LockMode::kExclusive) { | 
 |           WriteOwnerInformation(fd); | 
 |         } | 
 |         // If we succeeded on the first try, report zero wait time to avoid | 
 |         // unnecessary noise in the logs. We are interested in how long it took | 
 |         // for other commands to complete, not how fast acquiring a lock is. | 
 |         const uint64_t end_time = GetMillisecondsMonotonic(); | 
 |         const auto wait_time = multiple_attempts | 
 |                                    ? DurationMillis(start_time, end_time) | 
 |                                    : DurationMillis(); | 
 |         return std::make_pair(static_cast<LockHandle>(fd), wait_time); | 
 |       } | 
 |     } | 
 |  | 
 |     // Someone else holds the lock. Obtain the identity of the current lock | 
 |     // owner and print it out. | 
 |     string new_owner = ReadOwnerInformation(fd, name); | 
 |     if (new_owner != owner) { | 
 |       owner = new_owner; | 
 |       BAZEL_LOG(USER) << "Another command holds the " << name << " lock: \n" | 
 |                       << owner; | 
 |       if (block) { | 
 |         BAZEL_LOG(USER) << "Waiting for it to complete..."; | 
 |         fflush(stderr); | 
 |       } | 
 |     } | 
 |  | 
 |     if (!block) { | 
 |       BAZEL_DIE(blaze_exit_code::LOCK_HELD_NOBLOCK_FOR_LOCK) | 
 |           << "Exiting because the " << name | 
 |           << " lock is held and --noblock_for_lock was given."; | 
 |     } | 
 |  | 
 |     multiple_attempts = true; | 
 |  | 
 |     close(fd); | 
 |     TrySleep(500); | 
 |   } | 
 | } | 
 |  | 
 | void ReleaseLock(LockHandle lock_handle) { | 
 |   close(static_cast<int>(lock_handle)); | 
 | } | 
 |  | 
 | bool KillServerProcess(int pid, const blaze_util::Path& output_base) { | 
 |   // Kill the process and make sure it's dead before proceeding. | 
 |   errno = 0; | 
 |   if (killpg(pid, SIGKILL) == -1) { | 
 |     BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) | 
 |         << "Attempted to kill stale server process (pid=" << pid | 
 |         << ") using SIGKILL: " << GetLastErrorString(); | 
 |   } | 
 |   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, nullptr); | 
 | } | 
 |  | 
 | string GetUserName() { | 
 |   string user = GetEnv("USER"); | 
 |   if (!user.empty()) { | 
 |     return user; | 
 |   } | 
 |   errno = 0; | 
 |   passwd* pwent = getpwuid(getuid());  // NOLINT (single-threaded) | 
 |   if (pwent == nullptr || pwent->pw_name == nullptr) { | 
 |     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"); | 
 |   // 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" || ExistsEnv("INSIDE_EMACS"); | 
 | } | 
 |  | 
 | bool IsStandardTerminal() { | 
 |   string term = GetEnv("TERM"); | 
 |   bool isEmacs = IsEmacsTerminal(); | 
 |  | 
 |   // Emacs 22+ terminal emulation uses 'eterm-color' as its terminfo name and, | 
 |   // more importantly, supports color in terminals. | 
 |   // see https://github.com/emacs-mirror/emacs/blob/master/etc/NEWS.22#L331-L333 | 
 |   if (isEmacs && term == "eterm-color") { | 
 |     return true; | 
 |   } | 
 |   if (term.empty() || term == "dumb" || term == "emacs" || | 
 |       term == "xterm-mono" || term == "symbolics" || term == "9term" || | 
 |       isEmacs) { | 
 |     return false; | 
 |   } | 
 |   return isatty(STDOUT_FILENO) && isatty(STDERR_FILENO); | 
 | } | 
 |  | 
 | 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 and | 
 | // allow_infinity is false, uses the kernel-level limit 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, const bool allow_infinity) { | 
 |   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; | 
 |   } | 
 |  | 
 |   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. | 
 |     rl.rlim_cur = rl.rlim_max; | 
 |   } else { | 
 |     if ((rl.rlim_max == RLIM_INFINITY && !allow_infinity) || | 
 |         rl.rlim_max > explicit_limit) { | 
 |       rl.rlim_cur = explicit_limit; | 
 |     } else { | 
 |       rl.rlim_cur = rl.rlim_max; | 
 |     } | 
 |   } | 
 |  | 
 |   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, false); | 
 |   success &= UnlimitResource(RLIMIT_NPROC, false); | 
 |   return success; | 
 | } | 
 |  | 
 | bool UnlimitCoredumps() { return UnlimitResource(RLIMIT_CORE, true); } | 
 |  | 
 | void EnsurePythonPathOption(vector<string>* options) { | 
 |   // do nothing. | 
 | } | 
 |  | 
 | }  // namespace blaze |