[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/andschar/rstudio/master/src/cpp/core/system/Win32System.cpp [Back]  [Original]

/*
 * 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


Web Proxy Viewer  |  New URL  |  Original Page