GitHub Viewer
/*
* Win32System.cpp
*
* Copyright (C) 2009-17 by RStudio, Inc.
*
* Unless you have received this program directly from RStudio pursuant
* to the terms of a commercial license agreement with RStudio, then
* this program is licensed to you under the terms of version 3 of the
* GNU Affero General Public License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifndef JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
#define JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE 0x2000
#endif
#ifndef JOB_OBJECT_LIMIT_BREAKAWAY_OK
#define JOB_OBJECT_LIMIT_BREAKAWAY_OK 0x00000800
#endif
namespace rstudio {
namespace core {
namespace system {
namespace {
// main log writer
LogWriter* s_pLogWriter = NULL;
// additional log writers
std::vector s_logWriters;
Error initJobObject(bool* detachFromJob)
{
/*
* Create a Job object and assign this process to it. This will
* cause all child processes to be assigned to the same job.
* With JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE set, all the child
* processes will be killed when this process terminates (since
* it is the only one holding a handle to the job). With
* JOB_OBJECT_LIMIT_BREAKAWAY_OK set it is possible to pass
* CREATE_BREAKAWAY_FROM_JOB to CreateProcess (this is required
* by Chrome for creating its sub-processes)
*/
// If detachFromJob is true, it means we need to relaunch this
// executable with CREATE_BREAKAWAY_FROM_JOB
*detachFromJob = false;
HANDLE hJob = ::CreateJobObject(NULL, NULL);
if (!hJob)
return systemError(::GetLastError(), ERROR_LOCATION);
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };
jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE |
JOB_OBJECT_LIMIT_BREAKAWAY_OK;
::SetInformationJobObject(hJob,
JobObjectExtendedLimitInformation,
&jeli,
sizeof(jeli));
if (::AssignProcessToJobObject(hJob, ::GetCurrentProcess()))
{
DWORD error = ::GetLastError();
if (error == ERROR_ACCESS_DENIED)
{
// Use an environment variable to prevent us from somehow
// getting into an infinite loop of detaching (which would
// otherwise occur if ERROR_ACCESS_DENIED is being returned
// for some reason other than an existing job object being
// attached). This works because environment variables are
// inherited by our job-detached child process.
if (getenv("_RSTUDIO_LEVEL").empty())
{
setenv("_RSTUDIO_LEVEL", "1");
*detachFromJob = true;
}
}
return systemError(error, ERROR_LOCATION);
}
return Success();
}
bool isHiddenFile(const std::string& path)
{
DWORD attribs = ::GetFileAttributesA(path.c_str());
if (attribs == INVALID_FILE_ATTRIBUTES)
return false;
else if (attribs & FILE_ATTRIBUTE_HIDDEN)
return true;
else
return false;
}
} // anonymous namespace
void initHook()
{
// Logging will NOT work in this function!!
bool detachFromJob;
Error error = initJobObject(&detachFromJob);
if (!detachFromJob)
return;
TCHAR path[MAX_PATH];
if (!::GetModuleFileName(NULL, path, MAX_PATH))
return; // Couldn't get the path of the current .exe
STARTUPINFO startupInfo;
memset(&startupInfo, 0, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
PROCESS_INFORMATION procInfo;
memset(&procInfo, 0, sizeof(procInfo));
if (!::CreateProcess(NULL,
::GetCommandLine(),
NULL,
NULL,
TRUE,
CREATE_BREAKAWAY_FROM_JOB | ::GetPriorityClass(::GetCurrentProcess()),
NULL,
NULL,
&startupInfo,
&procInfo))
{
return; // Couldn't execute
}
::AllowSetForegroundWindow(procInfo.dwProcessId);
::WaitForSingleObject(procInfo.hProcess, INFINITE);
DWORD exitCode;
if (!::GetExitCodeProcess(procInfo.hProcess, &exitCode))
exitCode = ::GetLastError();
::CloseHandle(procInfo.hProcess);
::CloseHandle(procInfo.hThread);
::ExitProcess(exitCode);
}
void initializeSystemLog(const std::string& programIdentity, int logLevel)
{
}
void initializeStderrLog(const std::string& programIdentity, int logLevel)
{
if (s_pLogWriter)
delete s_pLogWriter;
s_pLogWriter = new StderrLogWriter(programIdentity, logLevel);
}
void initializeLog(const std::string& programIdentity, int logLevel, const FilePath& settingsDir)
{
if (s_pLogWriter)
delete s_pLogWriter;
s_pLogWriter = new FileLogWriter(programIdentity, logLevel, settingsDir);
}
void setLogToStderr(bool logToStderr)
{
if (s_pLogWriter)
s_pLogWriter->setLogToStderr(logToStderr);
}
void addLogWriter(boost::shared_ptr pLogWriter)
{
s_logWriters.push_back(pLogWriter);
}
void log(LogLevel logLevel, const std::string& message)
{
if (s_pLogWriter)
s_pLogWriter->log(logLevel, message);
std::for_each(s_logWriters.begin(),
s_logWriters.end(),
boost::bind(&LogWriter::log, _1, logLevel, message));
}
bool isWin64()
{
return !getenv("PROCESSOR_ARCHITEW6432").empty()
|| getenv("PROCESSOR_ARCHITECTURE") == "AMD64";
}
bool isCurrentProcessWin64()
{
return getenv("PROCESSOR_ARCHITECTURE") == "AMD64";
}
bool isVistaOrLater()
{
OSVERSIONINFOA osVersion;
ZeroMemory(&osVersion, sizeof(OSVERSIONINFOA));
osVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
if (::GetVersionExA(&osVersion))
{
return osVersion.dwMajorVersion >= 6;
}
else
{
LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));
return false;
}
}
bool isWin7OrLater()
{
OSVERSIONINFOA osVersion;
ZeroMemory(&osVersion, sizeof(OSVERSIONINFOA));
osVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
if (::GetVersionExA(&osVersion))
{
// 6.0 Vista, 6.1 Win7, 6.2 Win8, 6.3 Win8.1, >6 is Win10+
return osVersion.dwMajorVersion > 6 ||
osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion > 0;
}
else
{
LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));
return false;
}
}
std::string username()
{
return system::getenv("USERNAME");
}
unsigned int effectiveUserId()
{
return 0; // no concept of this on Win32
}
// home path strategies
namespace {
FilePath environmentHomePath(std::string envVariables)
{
using namespace boost::algorithm;
// use environment override if specified
if (!envVariables.empty())
{
for (split_iterator it =
make_split_iterator(envVariables, first_finder("|", is_iequal()));
it != split_iterator();
++it)
{
std::string envHomePath =
system::getenv(boost::copy_range(*it));
if (!envHomePath.empty())
{
FilePath userHomePath(envHomePath);
if (userHomePath.exists())
return userHomePath;
}
}
}
// no override
return FilePath();
}
FilePath currentCSIDLPersonalHomePath()
{
// query for My Documents directory
const DWORD SHGFP_TYPE_CURRENT = 0;
wchar_t homePath[MAX_PATH];
HRESULT hr = ::SHGetFolderPathW(NULL,
CSIDL_PERSONAL,
NULL,
SHGFP_TYPE_CURRENT,
homePath);
if (SUCCEEDED(hr))
{
return FilePath(homePath);
}
else
{
LOG_WARNING_MESSAGE("Unable to retreive user home path. HRESULT: " +
safe_convert::numberToString(hr));
return FilePath();
}
}
FilePath defaultCSIDLPersonalHomePath()
{
// query for default and force creation (works around situations
// where redirected path is not available)
const DWORD SHGFP_TYPE_DEFAULT = 1;
wchar_t homePath[MAX_PATH];
HRESULT hr = ::SHGetFolderPathW(NULL,
CSIDL_PERSONAL|CSIDL_FLAG_CREATE,
NULL,
SHGFP_TYPE_DEFAULT,
homePath);
if (SUCCEEDED(hr))
{
return FilePath(homePath);
}
else
{
LOG_WARNING_MESSAGE("Unable to retreive user home path. HRESULT: " +
safe_convert::numberToString(hr));
return FilePath();
}
}
FilePath homepathHomePath()
{
std::string homeDrive = core::system::getenv("HOMEDRIVE");
std::string homePath = core::system::getenv("HOMEPATH");
if (!homeDrive.empty() && !homePath.empty())
return FilePath(homeDrive + homePath);
else
return FilePath();
}
FilePath homedriveHomePath()
{
std::string homeDrive = core::system::getenv("HOMEDRIVE");
if (homeDrive.empty())
homeDrive = "C:";
return FilePath(homeDrive);
}
typedef std::pair HomePathSource;
} // anonymous namespace
FilePath userHomePath(std::string envOverride)
{
using boost::bind;
std::vector sources;
sources.push_back(std::make_pair("R_USER|HOME",
bind(environmentHomePath, envOverride)));
sources.push_back(std::make_pair("SHGFP_TYPE_CURRENT",
currentCSIDLPersonalHomePath));
sources.push_back(std::make_pair("SHGFP_TYPE_DEFAULT",
defaultCSIDLPersonalHomePath));
std::string envFallback = "USERPROFILE";
sources.push_back(std::make_pair(envFallback,
bind(environmentHomePath, envFallback)));
sources.push_back(std::make_pair("HOMEPATH",
homepathHomePath));
sources.push_back(std::make_pair("HOMEDRIVE",
homedriveHomePath));
BOOST_FOREACH(const HomePathSource& source, sources)
{
FilePath homePath = source.second();
if (!homePath.empty())
{
// return if we found one that exists
if (homePath.exists())
{
std::string path = homePath.absolutePath();
// standardize drive letter capitalization if in X:/y/z format
if (path.length() > 1 && path[1] == ':')
{
path[0] = toupper(path[0]);
homePath = FilePath(path);
}
return homePath;
}
// otherwise warn that we got a value that didn't exist
LOG_WARNING_MESSAGE("Home path returned by " + source.first + " (" +
homePath.absolutePath() + ") does not exist.");
}
}
// no luck!
LOG_ERROR_MESSAGE("No valid home path found for user");
return FilePath();
}
FilePath userSettingsPath(const FilePath& userHomeDirectory,
const std::string& appName)
{
wchar_t path[MAX_PATH + 1];
std::wstring appNameWide(appName.begin(), appName.end());
HRESULT hr = ::SHGetFolderPathAndSubDirW(
NULL,
CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE,
NULL,
SHGFP_TYPE_CURRENT,
appNameWide.c_str(),
path);
if (hr != S_OK)
{
LOG_ERROR_MESSAGE("Unable to retreive user home path. HRESULT: " +
safe_convert::numberToString(hr));
return FilePath();
}
return FilePath(std::wstring(path));
}
bool currentUserIsPrivilleged(unsigned int minimumUserId)
{
return false;
}
Error captureCommand(const std::string& command, std::string* pOutput)
{
// WIN32 popen docs:
// http://msdn.microsoft.com/en-us/library/96ayss4b(VS.80).aspx
// NOTE: note that popen only works from win32 console applications!
// start process
FILE* fp = ::_popen(command.c_str(), "r");
if (fp == NULL)
return systemError(errno, ERROR_LOCATION);
// collect output
const int kBuffSize = 1024;
char buffer[kBuffSize];
while (::fgets(buffer, kBuffSize, fp) != NULL)
*pOutput += buffer;
// check if an error terminated our output
Error error ;
if (::ferror(fp))
error = systemError(boost::system::errc::io_error, ERROR_LOCATION);
// close file
if (::_pclose(fp) == -1)
{
// log existing error before overwriting it
if (error)
LOG_ERROR(error);
error = systemError(errno, ERROR_LOCATION);
}
// return status
return error;
}
Error realPath(const FilePath& filePath, FilePath* pRealPath)
{
std::wstring wPath = filePath.absolutePathW();
std::vector buffer(512);
DWORD res = ::GetFullPathNameW(wPath.c_str(),
buffer.size(),
&(buffer[0]),
NULL);
if (res == 0)
{
Error error = systemError(::GetLastError(), ERROR_LOCATION);
error.addProperty("path", filePath);
return error;
}
else if (res > buffer.size())
{
buffer.resize(res);
res = ::GetFullPathNameW(wPath.c_str(),
buffer.size(),
&(buffer[0]),
NULL);
if (res == 0)
return systemError(::GetLastError(), ERROR_LOCATION);
else if (res > buffer.size())
return systemError(boost::system::windows_error::bad_length,
ERROR_LOCATION);
}
wPath = std::wstring(&(buffer[0]), res);
*pRealPath = FilePath(wPath);
return Success();
}
Error realPath(const std::string& path, FilePath* pRealPath)
{
return realPath(FilePath(path), pRealPath);
}
bool isHiddenFile(const FilePath& filePath)
{
return isHiddenFile(filePath.absolutePath());
}
bool isHiddenFile(const FileInfo& fileInfo)
{
return isHiddenFile(fileInfo.absolutePath());
}
bool isReadOnly(const FilePath& filePath)
{
// TODO: readonly detection for windows
return false;
}
Error makeFileHidden(const FilePath& path)
{
std::wstring filePath = path.absolutePathW();
LPCWSTR lpszPath = filePath.c_str();
DWORD attribs = ::GetFileAttributesW(lpszPath);
if (attribs == INVALID_FILE_ATTRIBUTES)
return systemError(GetLastError(), ERROR_LOCATION);
if (!::SetFileAttributesW(lpszPath, attribs | FILE_ATTRIBUTE_HIDDEN))
return systemError(GetLastError(), ERROR_LOCATION);
return Success();
}
bool stderrIsTerminal()
{
return _isatty(_fileno(stderr));
}
bool stdoutIsTerminal()
{
return _isatty(_fileno(stdout));
}
// uuid
std::string generateUuid(bool includeDashes)
{
// create the uuid
UUID uuid = {0};
::UuidCreate(&uuid);
PUCHAR pChar = NULL;
::UuidToStringA(&uuid, &pChar);
std::string uuidStr((char*)pChar);
::RpcStringFreeA(&pChar);
// remove dashes if requested
if (!includeDashes)
boost::algorithm::replace_all(uuidStr, "-", "");
// return
return uuidStr;
}
PidType currentProcessId()
{
return ::GetCurrentProcessId();
}
Error executablePath(const char * argv0,
FilePath* pExecutablePath)
{
*pExecutablePath = FilePath(_pgmptr);
return Success();
}
// installation path
Error installPath(const std::string& relativeToExecutable,
const char * argv0,
FilePath* pInstallationPath)
{
// get full executable path
FilePath exePath;
Error error = executablePath(argv0, &exePath);
if (error)
return error;
// resolve to install path using given relative path
if (relativeToExecutable == "..") // common case
*pInstallationPath = exePath.parent().parent();
else
*pInstallationPath = exePath.parent().complete(relativeToExecutable);
return Success();
}
void fixupExecutablePath(FilePath* pExePath)
{
if (pExePath->extension().empty())
*pExePath = pExePath->parent().complete(pExePath->filename() + ".exe");
}
void abort()
{
::exit(1);
}
////////////////////////////////////////////////////////////////////////////
//
// No signals on Win32 so all of these are no-ops
//
//
Error ignoreTerminalSignals()
{
return Success();
}
Error ignoreChildExits()
{
return Success();
}
Error reapChildren()
{
return Success();
}
struct SignalBlocker::Impl
{
};
SignalBlocker::SignalBlocker()
: pImpl_(new Impl())
{
}
Error SignalBlocker::block(SignalType signal)
{
return Success();
}
Error SignalBlocker::blockAll()
{
return Success();
}
SignalBlocker::~SignalBlocker()
{
try
{
}
catch(...)
{
}
}
Error clearSignalMask()
{
return Success();
}
Error handleSignal(SignalType signal, void (*handler)(int))
{
return Success();
}
core::Error ignoreSignal(SignalType signal)
{
return Success();
}
Error useDefaultSignalHandler(SignalType signal)
{
return Success();
}
void sendSignalToSelf(SignalType signal)
{
}
class ClipboardScope : boost::noncopyable
{
public:
ClipboardScope() : opened_(false) {}
Error open()
{
if (!::OpenClipboard(NULL))
{
return systemError(::GetLastError(), ERROR_LOCATION);
}
else
{
opened_ = true;
return Success();
}
}
~ClipboardScope()
{
try
{
if (opened_)
{
if (!::CloseClipboard())
LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));
}
}
catch(...)
{
}
}
private:
bool opened_;
};
class EnhMetaFile : boost::noncopyable
{
public:
EnhMetaFile() : hMF_(NULL) {}
Error open(const FilePath& path)
{
hMF_ = ::GetEnhMetaFileW(path.absolutePathW().c_str());
if (hMF_ == NULL)
return systemError(::GetLastError(), ERROR_LOCATION);
else
return Success();
}
~EnhMetaFile()
{
try
{
if (hMF_ != NULL)
{
if (!::DeleteEnhMetaFile(hMF_))
LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));
}
}
catch(...)
{
}
}
HENHMETAFILE handle() const { return hMF_; }
void release()
{
hMF_ = NULL;
}
private:
HENHMETAFILE hMF_;
};
Error copyMetafileToClipboard(const FilePath& path)
{
// open metafile
EnhMetaFile enhMetaFile;
Error error = enhMetaFile.open(path);
if (error)
return error;
// open the clipboard
ClipboardScope clipboardScope;
error = clipboardScope.open();
if (error)
return error;
// emtpy the clipboard
if (!::EmptyClipboard())
return systemError(::GetLastError(), ERROR_LOCATION);
// set the clipboard data
if (!::SetClipboardData(CF_ENHMETAFILE, enhMetaFile.handle()))
return systemError(::GetLastError(), ERROR_LOCATION);
// release the handle (because the clipboard now owns it)
enhMetaFile.release();
// return success
return Success();
}
void ensureLongPath(FilePath* pFilePath)
{
const std::size_t kBuffSize = (MAX_PATH*2) + 1;
char buffer[kBuffSize];
std::string path = string_utils::utf8ToSystem(pFilePath->absolutePath());
if (::GetLongPathName(path.c_str(),
buffer,
kBuffSize) > 0)
{
*pFilePath = FilePath(string_utils::systemToUtf8(buffer));
}
}
Error expandEnvironmentVariables(std::string value, std::string* pResult)
{
if (value.empty())
{
*pResult = value;
return Success();
}
DWORD sizeRequired = ::ExpandEnvironmentStrings(value.c_str(), NULL, 0);
if (!sizeRequired)
return systemError(::GetLastError(), ERROR_LOCATION);
std::vector buffer;
buffer.reserve(sizeRequired);
int result = ::ExpandEnvironmentStrings(value.c_str(),
&buffer[0],
buffer.capacity());
if (!result)
return systemError(GetLastError(), ERROR_LOCATION);
else if (result > buffer.capacity())
return systemError(ERROR_MORE_DATA, ERROR_LOCATION); // not expected
*pResult = std::string(&buffer[0]);
return Success();
}
FilePath expandComSpec()
{
std::string result;
Error err = expandEnvironmentVariables("%COMSPEC%", &result);
if (err)
return FilePath();
return FilePath(result);
}
Error terminateProcess(PidType pid)
{
HANDLE hProc = ::OpenProcess(PROCESS_TERMINATE, false, pid);
if (!hProc)
return systemError(::GetLastError(), ERROR_LOCATION);
if (!::TerminateProcess(hProc, 1))
return systemError(::GetLastError(), ERROR_LOCATION);
return Success();
}
std::vector getSubprocesses(PidType pid)
{
std::vector subprocs;
HANDLE hSnapShot;
CloseHandleOnExitScope closeSnapShot(&hSnapShot, ERROR_LOCATION);
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapShot == INVALID_HANDLE_VALUE)
{
// err on the side of assuming child processes, so we don't kill
// a job unintentionally
LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));
return subprocs;
}
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(pe32);
if (!Process32First(hSnapShot, &pe32))
{
LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));
return subprocs;
}
do
{
if (pe32.th32ParentProcessID == pid)
{
// Found a child process
SubprocInfo info;
info.pid = pe32.th32ProcessID;
info.exe = pe32.szExeFile;
subprocs.push_back(info);
}
} while (Process32Next(hSnapShot, &pe32));
return subprocs;
}
FilePath currentWorkingDir(PidType pid)
{
// NYI for Win32; commonly accepted technique for this is to use
// CreateRemoteThread to inject code to run GetCurrentDirectory in the
// context of the target program. That is ugly and we aren't
// likely to ever do it.
return FilePath();
}
Error closeHandle(HANDLE* pHandle, const ErrorLocation& location)
{
if (*pHandle != NULL)
{
BOOL result = ::CloseHandle(*pHandle);
*pHandle = NULL;
if (!result)
return systemError(::GetLastError(), location);
else
return Success();
}
else
{
return Success();
}
}
CloseHandleOnExitScope::~CloseHandleOnExitScope()
{
try
{
// A "null" handle can contain INVALID_HANDLE or NULL, depending
// on the context. This is a painful inconsistency in Windows, see:
// https://blogs.msdn.microsoft.com/oldnewthing/20040302-00/?p=40443
if (!pHandle_ || *pHandle_ == INVALID_HANDLE_VALUE || *pHandle_ == NULL)
return;
Error error = closeHandle(pHandle_, location_);
if (error)
LOG_ERROR(error);
}
catch(...)
{
}
}
struct ProcessInfo
{
DWORD processId;
DWORD parentProcessId;
};
// simple cass to encapsulate parent-child
// relationship of processes
struct ProcessTreeNode
{
boost::shared_ptr data;
std::vector children;
};
// process tree, indexed by pid
typedef std::map ProcessTreeT;
void createProcessTree(const std::vector& processes,
ProcessTreeT *pOutTree)
{
// first pass, create the nodes in the tree
BOOST_FOREACH(const ProcessInfo& process, processes)
{
ProcessTreeT::iterator iter = pOutTree->find(process.processId);
if (iter == pOutTree->end())
{
// process not found, so create a new entry for it
boost::shared_ptr nodePtr = boost::shared_ptr(
new ProcessTreeNode());
nodePtr->data = boost::shared_ptr(new ProcessInfo(process));
(*pOutTree)[process.processId] = nodePtr;
}
}
// second pass, link the nodes together
BOOST_FOREACH(ProcessTreeT::value_type& element, *pOutTree)
{
DWORD parent = element.second->data->parentProcessId;
ProcessTreeT::iterator iter = pOutTree->find(parent);
// if we cannot find the parent in the tree, move on
if (iter == pOutTree->end())
continue;
// add this node to its parent's children
iter->second->children.push_back(element.second);
}
}
void getChildren(const boost::shared_ptr& node,
std::vector *pOutChildren)
{
BOOST_FOREACH(const boost::shared_ptr& child, node->children)
{
pOutChildren->push_back(*child->data.get());
getChildren(child, pOutChildren);
}
}
Error getProcesses(std::vector *pOutProcesses)
{
PROCESSENTRY32 processEntry;
memset(&processEntry, 0, sizeof(PROCESSENTRY32));
processEntry.dwSize = sizeof(PROCESSENTRY32);
HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnap == INVALID_HANDLE_VALUE)
{
return systemError(::GetLastError(), ERROR_LOCATION);
}
if (Process32First(hSnap, &processEntry))
{
BOOL moreProcesses = TRUE;
while (moreProcesses)
{
ProcessInfo process;
process.processId = processEntry.th32ProcessID;
process.parentProcessId = processEntry.th32ParentProcessID;
pOutProcesses->push_back(process);
moreProcesses = ::Process32Next(hSnap, &processEntry);
}
}
return Success();
}
Error getChildProcesses(std::vector *pOutProcesses)
{
if (!pOutProcesses)
return systemError(EINVAL, ERROR_LOCATION);
// get all processes
std::vector processes;
Error error = getProcesses(&processes);
if (error) return error;
// build a process tree of the processes
ProcessTreeT tree;
createProcessTree(processes, &tree);
// return just the children of this process
ProcessTreeT::const_iterator iter = tree.find(::GetCurrentProcessId());
if (iter == tree.end())
return Success();
const boost::shared_ptr& rootNode = iter->second;
getChildren(rootNode, pOutProcesses);
return Success();
}
Error terminateChildProcesses()
{
std::vector childProcesses;
Error error = getChildProcesses(&childProcesses);
if (error)
return error;
BOOST_FOREACH(const ProcessInfo& process, childProcesses)
{
HANDLE hChildProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, process.processId);
if (hChildProc)
{
if (!::TerminateProcess(hChildProc, 1))
{
LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));
}
if (!::CloseHandle(hChildProc))
{
LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));
}
}
else
{
LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));
}
}
// the actual kill is best effort
// so return success regardless
return Success();
}
void setHomeToUserProfile(core::system::Options* pChildEnv)
{
std::string userProfile = core::system::getenv(*pChildEnv, "USERPROFILE");
core::system::setenv(pChildEnv, "HOME", userProfile);
}
} // namespace system
} // namespace core
} // namespace rstudio