| // 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. | |
| #include <errno.h> | |
| #include <fcntl.h> | |
| #include <limits.h> | |
| #include <signal.h> | |
| #include <stdio.h> | |
| #include <sys/types.h> | |
| #include <unistd.h> | |
| #include "src/main/cpp/blaze_util.h" | |
| #include "src/main/cpp/blaze_util_platform.h" | |
| #include "src/main/cpp/util/errors.h" | |
| #include "src/main/cpp/util/exit_code.h" | |
| #include "src/main/cpp/util/file.h" | |
| namespace blaze { | |
| using blaze_util::die; | |
| using blaze_util::pdie; | |
| using std::string; | |
| using std::vector; | |
| void ExecuteProgram(const string &exe, const vector<string> &args_vector) { | |
| if (VerboseLogging()) { | |
| string dbg; | |
| for (const auto &s : args_vector) { | |
| dbg.append(s); | |
| dbg.append(" "); | |
| } | |
| char cwd[PATH_MAX] = {}; | |
| if (getcwd(cwd, sizeof(cwd)) == NULL) { | |
| pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR, "getcwd() failed"); | |
| } | |
| fprintf(stderr, "Invoking binary %s in %s:\n %s\n", exe.c_str(), cwd, | |
| dbg.c_str()); | |
| } | |
| // Copy to a char* array for execv: | |
| int n = args_vector.size(); | |
| const char **argv = new const char *[n + 1]; | |
| for (int i = 0; i < n; ++i) { | |
| argv[i] = args_vector[i].c_str(); | |
| } | |
| argv[n] = NULL; | |
| execv(exe.c_str(), const_cast<char **>(argv)); | |
| } | |
| std::string ConvertPath(const std::string &path) { return path; } | |
| std::string ConvertPathList(const std::string& path_list) { return path_list; } | |
| std::string ListSeparator() { return ":"; } | |
| 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 string& daemon_output) { | |
| // Don't call 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: | |
| if (open(daemon_output.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666) == -1) { | |
| // In a daemon, no-one can hear you scream. | |
| open("/dev/null", O_WRONLY); | |
| } | |
| (void) dup(STDOUT_FILENO); // stderr (2>&1) | |
| } | |
| class PipeBlazeServerStartup : public BlazeServerStartup { | |
| public: | |
| PipeBlazeServerStartup(int pipe_fd); | |
| virtual ~PipeBlazeServerStartup(); | |
| virtual bool IsStillAlive(); | |
| private: | |
| int pipe_fd; | |
| }; | |
| PipeBlazeServerStartup::PipeBlazeServerStartup(int pipe_fd) { | |
| this->pipe_fd = pipe_fd; | |
| if (fcntl(pipe_fd, F_SETFL, O_NONBLOCK | fcntl(pipe_fd, F_GETFL))) { | |
| pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR, | |
| "Failed: fcntl to enable O_NONBLOCK on pipe"); | |
| } | |
| } | |
| PipeBlazeServerStartup::~PipeBlazeServerStartup() { | |
| close(pipe_fd); | |
| } | |
| bool PipeBlazeServerStartup::IsStillAlive() { | |
| char c; | |
| return read(this->pipe_fd, &c, 1) == -1 && errno == EAGAIN; | |
| } | |
| void WriteSystemSpecificProcessIdentifier(const string& server_dir); | |
| void ExecuteDaemon(const string& exe, | |
| const std::vector<string>& args_vector, | |
| const string& daemon_output, const string& server_dir, | |
| BlazeServerStartup** server_startup) { | |
| int fds[2]; | |
| if (pipe(fds)) { | |
| pdie(blaze_exit_code::INTERNAL_ERROR, "pipe creation failed"); | |
| } | |
| int child = fork(); | |
| if (child == -1) { | |
| pdie(blaze_exit_code::INTERNAL_ERROR, "fork() failed"); | |
| } else if (child > 0) { // we're the parent | |
| close(fds[1]); // parent keeps only the reading side | |
| *server_startup = new PipeBlazeServerStartup(fds[0]); | |
| return; | |
| } else { | |
| close(fds[0]); // child keeps only the writing side | |
| } | |
| Daemonize(daemon_output); | |
| string pid_string = ToString(getpid()); | |
| string pid_file = blaze_util::JoinPath(server_dir, kServerPidFile); | |
| string pid_symlink_file = blaze_util::JoinPath(server_dir, kServerPidSymlink); | |
| if (!WriteFile(pid_string, pid_file)) { | |
| // The exit code does not matter because we are already in the daemonized | |
| // server. The output of this operation will end up in jvm.out . | |
| pdie(0, "Cannot write PID file"); | |
| } | |
| UnlinkPath(pid_symlink_file.c_str()); | |
| if (symlink(pid_string.c_str(), pid_symlink_file.c_str()) < 0) { | |
| pdie(0, "Cannot write PID symlink"); | |
| } | |
| WriteSystemSpecificProcessIdentifier(server_dir); | |
| ExecuteProgram(exe, args_vector); | |
| pdie(0, "Cannot execute %s", exe.c_str()); | |
| } | |
| string RunProgram(const string& exe, const std::vector<string>& args_vector) { | |
| int fds[2]; | |
| if (pipe(fds)) { | |
| pdie(blaze_exit_code::INTERNAL_ERROR, "pipe creation failed"); | |
| } | |
| int child = fork(); | |
| if (child == -1) { | |
| pdie(blaze_exit_code::INTERNAL_ERROR, "fork() failed"); | |
| } else if (child > 0) { // we're the parent | |
| close(fds[1]); // parent keeps only the reading side | |
| string result; | |
| if (!ReadFileDescriptor(fds[0], &result)) { | |
| pdie(blaze_exit_code::INTERNAL_ERROR, "Cannot read subprocess output"); | |
| } | |
| return result; | |
| } else { // We're the child | |
| close(fds[0]); // child keeps only the writing side | |
| // Redirect output to the writing side of the dup. | |
| dup2(fds[1], STDOUT_FILENO); | |
| dup2(fds[1], STDERR_FILENO); | |
| // Execute the binary | |
| ExecuteProgram(exe, args_vector); | |
| pdie(blaze_exit_code::INTERNAL_ERROR, "Failed to run %s", exe.c_str()); | |
| } | |
| return string(""); // We cannot reach here, just placate the compiler. | |
| } | |
| bool ReadDirectorySymlink(const string &name, string* result) { | |
| char buf[PATH_MAX + 1]; | |
| int len = readlink(name.c_str(), buf, PATH_MAX); | |
| if (len < 0) { | |
| return false; | |
| } | |
| buf[len] = 0; | |
| *result = buf; | |
| return true; | |
| } | |
| bool CompareAbsolutePaths(const string& a, const string& b) { | |
| return a == b; | |
| } | |
| } // namespace blaze. |