blob: 9136c61a5befb659b195c44490544d4ca11caee8 [file] [log] [blame]
// Copyright 2014 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> // errno, ENAMETOOLONG
#include <limits.h>
#include <string.h> // strerror
#include <sys/cygwin.h>
#include <sys/socket.h>
#include <sys/statfs.h>
#include <unistd.h>
#include <windows.h>
#include <cstdlib>
#include <cstdio>
#include <thread> // NOLINT (to slience Google-internal linter)
#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"
#include "src/main/cpp/util/strings.h"
namespace blaze {
using blaze_util::die;
using blaze_util::pdie;
using std::string;
using std::vector;
static void PrintError(const string& op) {
DWORD last_error = ::GetLastError();
if (last_error == 0) {
return;
}
char* message_buffer;
size_t size = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
last_error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR) &message_buffer,
0,
NULL);
fprintf(stderr, "ERROR: %s: %s (%d)\n",
op.c_str(), message_buffer, last_error);
LocalFree(message_buffer);
}
void WarnFilesystemType(const string& output_base) {
}
string GetSelfPath() {
char buffer[PATH_MAX] = {};
if (!GetModuleFileName(0, buffer, sizeof(buffer))) {
pdie(255, "Error %u getting executable file name\n", GetLastError());
}
return string(buffer);
}
string GetOutputRoot() {
char* tmpdir = getenv("TMPDIR");
if (tmpdir == 0 || strlen(tmpdir) == 0) {
return "/var/tmp";
} else {
return string(tmpdir);
}
}
uint64_t MonotonicClock() {
struct timespec ts = {};
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000000000LL + ts.tv_nsec;
}
uint64_t ProcessClock() {
struct timespec ts = {};
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
return ts.tv_sec * 1000000000LL + ts.tv_nsec;
}
void SetScheduling(bool batch_cpu_scheduling, int io_nice_level) {
// TODO(bazel-team): There should be a similar function on Windows.
}
string GetProcessCWD(int pid) {
char server_cwd[PATH_MAX] = {};
if (readlink(
("/proc/" + ToString(pid) + "/cwd").c_str(),
server_cwd, sizeof(server_cwd)) < 0) {
return "";
}
return string(server_cwd);
}
bool IsSharedLibrary(const string &filename) {
return blaze_util::ends_with(filename, ".dll");
}
string GetDefaultHostJavabase() {
const char *javahome = getenv("JAVA_HOME");
if (javahome == NULL) {
die(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR,
"Error: JAVA_HOME not set.");
}
return javahome;
}
namespace {
void ReplaceAll(
std::string* s, const std::string& pattern, const std::string with) {
size_t pos = 0;
while (true) {
size_t pos = s->find(pattern, pos);
if (pos == std::string::npos) return;
*s = s->replace(pos, pattern.length(), with);
pos += with.length();
}
}
// Max command line length is per CreateProcess documentation
// (https://msdn.microsoft.com/en-us/library/ms682425(VS.85).aspx)
//
// Quoting rules are described here:
// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
static const int MAX_CMDLINE_LENGTH = 32768;
struct CmdLine {
char cmdline[MAX_CMDLINE_LENGTH];
};
static void CreateCommandLine(CmdLine* result, const string& exe,
const vector<string>& args_vector) {
string cmdline;
bool first = true;
for (const auto& s : args_vector) {
if (first) {
first = false;
// Skip first argument, instead use quoted executable name with ".exe"
// suffix.
cmdline.append("\"");
cmdline.append(exe);
cmdline.append(".exe");
cmdline.append("\"");
continue;
} else {
cmdline.append(" ");
}
bool has_space = s.find(" ") != string::npos;
if (has_space) {
cmdline.append("\"");
}
std::string::const_iterator it = s.begin();
while (it != s.end()) {
char ch = *it++;
switch (ch) {
case '"':
// Escape double quotes
cmdline.append("\\\"");
break;
case '\\':
if (it == s.end()) {
// Backslashes at the end of the string are quoted if we add quotes
cmdline.append(has_space ? "\\\\" : "\\");
} else {
// Backslashes everywhere else are quoted if they are followed by a
// quote or a backslash
cmdline.append(*it == '"' || *it == '\\' ? "\\\\" : "\\");
}
break;
default:
cmdline.append(1, ch);
}
}
if (has_space) {
cmdline.append("\"");
}
}
if (cmdline.size() >= MAX_CMDLINE_LENGTH) {
pdie(blaze_exit_code::INTERNAL_ERROR,
"Command line too long: %s", cmdline.c_str());
}
// Copy command line into a mutable buffer.
// CreateProcess is allowed to mutate its command line argument.
strncpy(result->cmdline, cmdline.c_str(), MAX_CMDLINE_LENGTH - 1);
result->cmdline[MAX_CMDLINE_LENGTH - 1] = 0;
}
} // namespace
string RunProgram(
const string& exe, const vector<string>& args_vector) {
SECURITY_ATTRIBUTES sa = {0};
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
HANDLE pipe_read, pipe_write;
if (!CreatePipe(&pipe_read, &pipe_write, &sa, 0)) {
pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR, "CreatePipe");
}
if (!SetHandleInformation(pipe_read, HANDLE_FLAG_INHERIT, 0)) {
pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR, "SetHandleInformation");
}
PROCESS_INFORMATION processInfo = {0};
STARTUPINFO startupInfo = {0};
startupInfo.hStdError = pipe_write;
startupInfo.hStdOutput = pipe_write;
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
CmdLine cmdline;
CreateCommandLine(&cmdline, exe, args_vector);
bool ok = CreateProcess(
NULL, // _In_opt_ LPCTSTR lpApplicationName,
// _Inout_opt_ LPTSTR lpCommandLine,
cmdline.cmdline,
NULL, // _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
NULL, // _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
true, // _In_ BOOL bInheritHandles,
0, // _In_ DWORD dwCreationFlags,
NULL, // _In_opt_ LPVOID lpEnvironment,
NULL, // _In_opt_ LPCTSTR lpCurrentDirectory,
&startupInfo, // _In_ LPSTARTUPINFO lpStartupInfo,
&processInfo); // _Out_ LPPROCESS_INFORMATION lpProcessInformation
if (!ok) {
pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR,
"RunProgram/CreateProcess: Error %d while executing %s",
GetLastError(), cmdline.cmdline);
}
CloseHandle(pipe_write);
std::string result = "";
DWORD bytes_read;
CHAR buf[1024];
for (;;) {
ok = ::ReadFile(pipe_read, buf, 1023, &bytes_read, NULL);
if (!ok || bytes_read == 0) {
break;
}
buf[bytes_read] = 0;
result = result + buf;
}
CloseHandle(pipe_read);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
return result;
}
// If we pass DETACHED_PROCESS to CreateProcess(), cmd.exe appropriately
// returns the command prompt when the client terminates. msys2, however, in
// its infinite wisdom, waits until the *server* terminates and cannot be
// convinced otherwise.
//
// So, we first pretend to be a POSIX daemon so that msys2 knows about our
// intentions and *then* we call CreateProcess(). Life ain't easy.
static bool DaemonizeOnWindows() {
if (fork() > 0) {
// We are the original client process.
return true;
}
if (fork() > 0) {
// We are the child of the original client process. Terminate so that the
// actual server is not a child process of the client.
exit(0);
}
setsid();
// Contrary to the POSIX version, we are not closing the three standard file
// descriptors here. CreateProcess() will take care of that and it's useful
// to see the error messages in ExecuteDaemon() on the console of the client.
return false;
}
// Keeping an eye on the server process on Windows is not implemented yet.
// TODO(lberki): Implement this, because otherwise if we can't start up a server
// process, the client will hang until it times out.
class DummyBlazeServerStartup : public BlazeServerStartup {
public:
DummyBlazeServerStartup() {}
virtual ~DummyBlazeServerStartup() {}
virtual bool IsStillAlive() { return true; }
};
void ExecuteDaemon(const string& exe, const std::vector<string>& args_vector,
const string& daemon_output, const string& server_dir,
BlazeServerStartup** server_startup) {
if (DaemonizeOnWindows()) {
// We are the client process
*server_startup = new DummyBlazeServerStartup();
return;
}
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
// We redirect stdout and stderr by telling CreateProcess to use a file handle
// we open below and these handles must be inheriatable
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
HANDLE output_file = CreateFile(
ConvertPath(daemon_output).c_str(), // lpFileName
GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess
// So that the file can be read while the server is running
FILE_SHARE_READ, // dwShareMode
&sa, // lpSecurityAttributes
CREATE_ALWAYS, // dwCreationDisposition
FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes
NULL); // hTemplateFile
if (output_file == INVALID_HANDLE_VALUE) {
pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR, "CreateFile");
}
HANDLE pipe_read, pipe_write;
if (!CreatePipe(&pipe_read, &pipe_write, &sa, 0)) {
pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR, "CreatePipe");
}
if (!SetHandleInformation(pipe_write, HANDLE_FLAG_INHERIT, 0)) {
pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR, "SetHandleInformation");
}
PROCESS_INFORMATION processInfo = {0};
STARTUPINFO startupInfo = {0};
startupInfo.hStdInput = pipe_read;
startupInfo.hStdError = output_file;
startupInfo.hStdOutput = output_file;
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
CmdLine cmdline;
CreateCommandLine(&cmdline, exe, args_vector);
// Propagate BAZEL_SH environment variable to a sub-process.
// todo(dslomov): More principled approach to propagating
// environment variables.
SetEnvironmentVariable("BAZEL_SH", getenv("BAZEL_SH"));
bool ok = CreateProcess(
NULL, // _In_opt_ LPCTSTR lpApplicationName,
// _Inout_opt_ LPTSTR lpCommandLine,
cmdline.cmdline,
NULL, // _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
NULL, // _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
TRUE, // _In_ BOOL bInheritHandles,
// _In_ DWORD dwCreationFlags,
DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP,
NULL, // _In_opt_ LPVOID lpEnvironment,
NULL, // _In_opt_ LPCTSTR lpCurrentDirectory,
&startupInfo, // _In_ LPSTARTUPINFO lpStartupInfo,
&processInfo); // _Out_ LPPROCESS_INFORMATION lpProcessInformation
if (!ok) {
pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR,
"ExecuteDaemon/CreateProcess: error %u executing: %s\n",
GetLastError(), cmdline.cmdline);
}
CloseHandle(output_file);
CloseHandle(pipe_write);
CloseHandle(pipe_read);
string pid_string = ToString(processInfo.dwProcessId);
string pid_file = blaze_util::JoinPath(server_dir, kServerPidFile);
if (!WriteFile(pid_string, pid_file)) {
// Not a lot we can do if this fails
fprintf(stderr, "Cannot write PID file %s\n", pid_file.c_str());
}
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
exit(0);
}
void BatchWaiterThread(HANDLE java_handle) {
WaitForSingleObject(java_handle, INFINITE);
}
static void MingwSignalHandler(int signum) {
// Java process will be terminated because we set the job to terminate if its
// handle is closed.
//
// Note that this is different how interruption is handled on Unix, where the
// Java process sets up a signal handler for SIGINT itself. That cannot be
// done on Windows without using native code, and it's better to have as
// little JNI as possible. The most important part of the cleanup after
// termination (killing all child processes) happens automatically on Windows
// anyway, since we put the batch Java process in its own job which does not
// allow breakaway processes.
exit(blaze_exit_code::ExitCode::INTERRUPTED);
}
// Returns whether assigning the given process to a job failed because nested
// jobs are not available on the current system.
static bool IsFailureDueToNestedJobsNotSupported(HANDLE process) {
BOOL is_in_job;
if (!IsProcessInJob(process, NULL, &is_in_job)) {
PrintError("IsProcessInJob()");
return false;
}
if (!is_in_job) {
// Not in a job.
return false;
}
OSVERSIONINFOEX version_info;
version_info.dwOSVersionInfoSize = sizeof(version_info);
if (!GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info))) {
PrintError("GetVersionEx()");
return false;
}
return version_info.dwMajorVersion < 6
|| version_info.dwMajorVersion == 6 && version_info.dwMinorVersion <= 1;
}
// Run the given program in the current working directory,
// using the given argument vector.
void ExecuteProgram(
const string& exe, const vector<string>& args_vector) {
CmdLine cmdline;
CreateCommandLine(&cmdline, exe, args_vector);
STARTUPINFO startupInfo = {0};
PROCESS_INFORMATION processInfo = {0};
// Propagate BAZEL_SH environment variable to a sub-process.
// todo(dslomov): More principled approach to propagating
// environment variables.
SetEnvironmentVariable("BAZEL_SH", getenv("BAZEL_SH"));
HANDLE job = CreateJobObject(NULL, NULL);
if (job == NULL) {
pdie(255, "Error %u while creating job\n", GetLastError());
}
JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = { 0 };
job_info.BasicLimitInformation.LimitFlags =
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
if (!SetInformationJobObject(
job,
JobObjectExtendedLimitInformation,
&job_info,
sizeof(job_info))) {
pdie(255, "Error %u while setting up job\n", GetLastError());
}
bool success = CreateProcess(
NULL, // _In_opt_ LPCTSTR lpApplicationName,
// _Inout_opt_ LPTSTR lpCommandLine,
cmdline.cmdline,
NULL, // _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
NULL, // _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
true, // _In_ BOOL bInheritHandles,
// _In_ DWORD dwCreationFlags,
CREATE_NEW_PROCESS_GROUP // So that Ctrl-Break does not affect it
| CREATE_BREAKAWAY_FROM_JOB // We'll put it in a new job
| CREATE_SUSPENDED, // So that it doesn't start a new job itself
NULL, // _In_opt_ LPVOID lpEnvironment,
NULL, // _In_opt_ LPCTSTR lpCurrentDirectory,
&startupInfo, // _In_ LPSTARTUPINFO lpStartupInfo,
&processInfo); // _Out_ LPPROCESS_INFORMATION lpProcessInformation
if (!success) {
pdie(255, "ExecuteProgram/CreateProcess: error %u executing: %s\n",
GetLastError(), cmdline.cmdline);
}
if (!AssignProcessToJobObject(job, processInfo.hProcess)) {
if (!IsFailureDueToNestedJobsNotSupported(processInfo.hProcess)) {
pdie(255, "Error %u while assigning process to job\n", GetLastError());
}
// Otherwise, the OS doesn't support nested jobs so we'll just have to
// make do without.
}
// Now that we put the process in a new job object, we can start executing it
if (ResumeThread(processInfo.hThread) == -1) {
pdie(255, "Error %u while starting Java process\n", GetLastError());
}
// msys doesn't deliver signals while a Win32 call is pending so we need to
// do the blocking call in another thread
signal(SIGINT, MingwSignalHandler);
std::thread batch_waiter_thread([=]() {
BatchWaiterThread(processInfo.hProcess);
});
// The output base lock is held while waiting
batch_waiter_thread.join();
DWORD exit_code;
GetExitCodeProcess(processInfo.hProcess, &exit_code);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
exit(exit_code);
}
string ListSeparator() { return ";"; }
string ConvertPath(const string& path) {
// If the path looks like %USERPROFILE%/foo/bar, don't convert.
if (path.empty() || path[0] == '%') {
return path;
}
char* wpath = static_cast<char*>(cygwin_create_path(
CCP_POSIX_TO_WIN_A, static_cast<const void*>(path.c_str())));
string result(wpath);
free(wpath);
return result;
}
// Convert a Unix path list to Windows path list
string ConvertPathList(const string& path_list) {
string w_list = "";
int start = 0;
int pos;
while ((pos = path_list.find(":", start)) != string::npos) {
w_list += ConvertPath(path_list.substr(start, pos - start)) + ";";
start = pos + 1;
}
if (start < path_list.size()) {
w_list += ConvertPath(path_list.substr(start));
}
return w_list;
}
string ConvertPathToPosix(const string& win_path) {
char* posix_path = static_cast<char*>(cygwin_create_path(
CCP_WIN_A_TO_POSIX, static_cast<const void*>(win_path.c_str())));
string result(posix_path);
free(posix_path);
return result;
}
// Cribbed from ntifs.h, not present in windows.h
#define REPARSE_MOUNTPOINT_HEADER_SIZE 8
typedef struct {
DWORD ReparseTag;
WORD ReparseDataLength;
WORD Reserved;
WORD SubstituteNameOffset;
WORD SubstituteNameLength;
WORD PrintNameOffset;
WORD PrintNameLength;
WCHAR PathBuffer[ANYSIZE_ARRAY];
} REPARSE_MOUNTPOINT_DATA_BUFFER, *PREPARSE_MOUNTPOINT_DATA_BUFFER;
HANDLE OpenDirectory(const string& path, bool readWrite) {
HANDLE result = ::CreateFile(
path.c_str(),
readWrite ? (GENERIC_READ | GENERIC_WRITE) : GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (result == INVALID_HANDLE_VALUE) {
PrintError("CreateFile(" + path + ")");
}
return result;
}
bool SymlinkDirectories(const string &posix_target, const string &posix_name) {
string target = ConvertPath(posix_target);
string name = ConvertPath(posix_name);
// Junctions are directories, so create one
if (!::CreateDirectory(name.c_str(), NULL)) {
PrintError("CreateDirectory(" + name + ")");
return false;
}
HANDLE directory = OpenDirectory(name, true);
if (directory == INVALID_HANDLE_VALUE) {
return false;
}
char reparse_buffer_bytes[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
REPARSE_MOUNTPOINT_DATA_BUFFER* reparse_buffer =
reinterpret_cast<REPARSE_MOUNTPOINT_DATA_BUFFER *>(reparse_buffer_bytes);
memset(reparse_buffer_bytes, 0, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
// non-parsed path prefix. Required for junction targets.
string prefixed_target = "\\??\\" + target;
int prefixed_target_length = ::MultiByteToWideChar(
CP_ACP,
0,
prefixed_target.c_str(),
-1,
reparse_buffer->PathBuffer,
MAX_PATH);
if (prefixed_target_length == 0) {
PrintError("MultiByteToWideChar(" + prefixed_target + ")");
CloseHandle(directory);
return false;
}
// In addition to their target, junctions also have another string which
// tells which target to show to the user. mklink cuts of the \??\ part, so
// that's what we do, too.
int target_length = ::MultiByteToWideChar(
CP_UTF8,
0,
target.c_str(),
-1,
reparse_buffer->PathBuffer + prefixed_target_length,
MAX_PATH);
if (target_length == 0) {
PrintError("MultiByteToWideChar(" + target + ")");
CloseHandle(directory);
return false;
}
reparse_buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
reparse_buffer->PrintNameOffset = prefixed_target_length * sizeof(WCHAR);
reparse_buffer->PrintNameLength = (target_length - 1) * sizeof(WCHAR);
reparse_buffer->SubstituteNameLength =
(prefixed_target_length - 1) * sizeof(WCHAR);
reparse_buffer->SubstituteNameOffset = 0;
reparse_buffer->Reserved = 0;
reparse_buffer->ReparseDataLength =
reparse_buffer->SubstituteNameLength +
reparse_buffer->PrintNameLength + 12;
DWORD bytes_returned;
bool result = ::DeviceIoControl(
directory,
FSCTL_SET_REPARSE_POINT,
reparse_buffer,
reparse_buffer->ReparseDataLength + REPARSE_MOUNTPOINT_HEADER_SIZE,
NULL,
0,
&bytes_returned,
NULL);
if (!result) {
PrintError("DeviceIoControl(FSCTL_SET_REPARSE_POINT, " + name + ")");
}
CloseHandle(directory);
return result;
}
bool ReadDirectorySymlink(const string &posix_name, string* result) {
string name = ConvertPath(posix_name);
HANDLE directory = OpenDirectory(name, false);
if (directory == INVALID_HANDLE_VALUE) {
return false;
}
char reparse_buffer_bytes[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
REPARSE_MOUNTPOINT_DATA_BUFFER* reparse_buffer =
reinterpret_cast<REPARSE_MOUNTPOINT_DATA_BUFFER *>(reparse_buffer_bytes);
memset(reparse_buffer_bytes, 0, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
reparse_buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
DWORD bytes_returned;
bool ok = ::DeviceIoControl(
directory,
FSCTL_GET_REPARSE_POINT,
NULL,
0,
reparse_buffer,
MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
&bytes_returned,
NULL);
if (!ok) {
PrintError("DeviceIoControl(FSCTL_GET_REPARSE_POINT, " + name + ")");
}
CloseHandle(directory);
if (!ok) {
return false;
}
vector<char> print_name(reparse_buffer->PrintNameLength * sizeof(WCHAR) + 1);
int count = ::WideCharToMultiByte(
CP_UTF8,
0,
reparse_buffer->PathBuffer +
(reparse_buffer->PrintNameOffset / sizeof(WCHAR)),
reparse_buffer->PrintNameLength,
&print_name[0],
print_name.size(),
NULL,
NULL);
if (count == 0) {
PrintError("WideCharToMultiByte()");
*result = "";
return false;
} else {
*result = ConvertPathToPosix(&print_name[0]);
return true;
}
}
static bool IsAbsoluteWindowsPath(const string& p) {
if (p.size() < 3) {
return false;
}
if (p.substr(1, 2) == ":/") {
return true;
}
if (p.substr(1, 2) == ":\\") {
return true;
}
return false;
}
bool CompareAbsolutePaths(const string& a, const string& b) {
string a_real = IsAbsoluteWindowsPath(a) ? ConvertPathToPosix(a) : a;
string b_real = IsAbsoluteWindowsPath(b) ? ConvertPathToPosix(b) : b;
return a_real == b_real;
}
bool VerifyServerProcess(
int pid, const string& output_base, const string& install_base) {
// TODO(lberki): This might accidentally kill an unrelated process if the
// server died and the PID got reused.
return true;
}
bool KillServerProcess(int pid) {
HANDLE process = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
if (process == NULL) {
// Cannot find the server process. Can happen if the PID file is stale.
return false;
}
bool result = TerminateProcess(process, /*uExitCode*/0);
if (!result) {
fprintf(stderr, "Cannot terminate server process with PID %d\n", pid);
}
CloseHandle(process);
return result;
}
// Not supported.
void ExcludePathFromBackup(const string &path) {
}
} // namespace blaze