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

/*
 * CrashHandler.cpp
 *
 * Copyright (C) 2019 by RStudio, PBC
 *
 * 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 

#ifndef _WIN32
#include 
#endif

#include "config.h"

#ifndef RSTUDIO_CRASHPAD_ENABLED
namespace rstudio {
namespace core {
namespace crash_handler {

Error initialize(ProgramMode programMode)
{
   return Success();
}

ConfigSource configSource()
{
   return ConfigSource::Default;
}

bool isHandlerEnabled()
{
   return false;
}

Error setUserHandlerEnabled(bool)
{
   return Success();
}

bool hasUserBeenPromptedForPermission()
{
   return true;
}

Error setUserHasBeenPromptedForPermission()
{
   return Success();
}

} // namespace crash_handler
} // namespace core
} // namespace rstudio
#else

#include 
#include 
#include 

#define kCrashHandlingEnabled         "crash-handling-enabled"
#define kCrashHandlingEnabledDefault  false
#define kCrashDatabasePath            "crash-db-path"
#define kCrashDatabasePathDefault     ""
#define kUploadUrl                    "upload-url"
#define kUploadUrlDefault             "https://sentry.io/api/1379214/minidump/?sentry_key=4e76b8d2cffb49419fec1b431e09247c"
#define kUploadDumps                  "uploads-enabled"
#define kUploadDumpsDefault           true
#define kUploadProxy                  "upload-proxy"
#define kUploadProxyDefault           ""

namespace rstudio {
namespace core {
namespace crash_handler {

namespace {

boost::shared_ptr s_crashpadClient;
boost::shared_ptr s_settings;
ProgramMode s_programMode;

FilePath adminConfFile()
{
#ifndef _WIN32
   return FilePath("/etc/rstudio/crash-handler.conf");
#else
   return core::system::systemSettingsPath("RStudio", false).completePath("crash-handler.conf");
#endif
}

FilePath userConfFile()
{
   return core::system::userSettingsPath(
      core::system::userHomePath(),
      "R",
      true).completePath("crash-handler.conf");
}

void readOptions()
{
   s_settings.reset(new Settings());

   FilePath optionsFile = adminConfFile();

   if (s_programMode == ProgramMode::Desktop && !optionsFile.exists())
   {
      // admin-level file does not exist - check for existence of user file
      // this is only done for desktop mode, as only the admin file should be
      // respected in server mode
      optionsFile = userConfFile();

      // if the user options file does not explicitly exist, we will forcefully create it
      // this is to ensure that there is an actual file backing for the settings
      // as multiple user clients can update it simultaneously, so we need to have it
      // backed to file as opposed to just hanging around in memory so that all clients
      // can properly see the state at all times
      if (!optionsFile.exists())
      {
         Error error = optionsFile.getParent().ensureDirectory();
         if (!error)
            error = optionsFile.ensureFile();
         if (error)
            LOG_ERROR(error);
      }
   }

   if (optionsFile.exists())
   {
      Error error = s_settings->initialize(optionsFile);
      if (error)
         LOG_ERROR(error);
   }
}

base::FilePath googleFilePath(const std::string& str)
{
#ifdef _WIN32
   std::string utf8Str = core::string_utils::systemToUtf8(str);
   std::wstring wideStr = core::string_utils::utf8ToWide(utf8Str);
   return base::FilePath(wideStr);
#else
   return base::FilePath(str);
#endif
}

void logClientCreation(const base::FilePath& handlerPath,
                       const base::FilePath& databasePath,
                       const std::string& uploadUrl)
{
#ifdef _WIN32
   std::wstringstream msg;
   std::wstring uploadUrlStr = core::string_utils::utf8ToWide(uploadUrl);
#else
   std::stringstream msg;
   const std::string& uploadUrlStr = uploadUrl;
#endif

    msg SetUploadsEnabled(uploadDumps);
   else
      return systemError(boost::system::errc::no_such_file_or_directory, ERROR_LOCATION);

   logClientCreation(handlerPath, databasePath, uploadUrl);

   // initialize and start crashpad client
   s_crashpadClient.reset(new crashpad::CrashpadClient());
   std::map annotations;
   annotations["sentry[release]"] = RSTUDIO_VERSION;
   std::vector args {"--no-rate-limit"};

#ifdef __linux__
   // export proxy environment variable if set
   // if not set, the default (system-wide setting) or any existing proxy env var is used instead
   // see https://curl.haxx.se/libcurl/c/CURLOPT_PROXY.html for more info
   std::string proxy = s_settings->get(kUploadProxy, kUploadProxyDefault);
   if (!proxy.empty())
      core::system::setenv("ALL_PROXY", proxy);

   bool success = s_crashpadClient->StartHandlerAtCrash(handlerPath,
                                                        databasePath,
                                                        base::FilePath(),
                                                        uploadUrl,
                                                        annotations,
                                                        args);
#else
   bool success = s_crashpadClient->StartHandler(handlerPath,
                                                 databasePath,
                                                 base::FilePath(),
                                                 uploadUrl,
                                                 annotations,
                                                 args,
                                                 true,
                                                 false);

#endif

   return success ? Success() : systemError(boost::system::errc::invalid_argument, ERROR_LOCATION);
}

ConfigSource configSource()
{
   FilePath settingsPath = s_settings->filePath();
   if (settingsPath.isEmpty())
      return ConfigSource::Default;

   if (settingsPath == adminConfFile())
      return ConfigSource::Admin;
   else if (settingsPath == userConfFile())
      return ConfigSource::User;
   else
      return ConfigSource::Default;
}

bool isHandlerEnabled()
{
   return s_settings->getBool(kCrashHandlingEnabled, kCrashHandlingEnabledDefault);
}

Error setUserHandlerEnabled(bool handlerEnabled)
{
   ConfigSource source = configSource();

   if (source == ConfigSource::Admin)
   {
      // the admin setting is in effect so there's nothing to change
      return Success();
   }

   if (source == ConfigSource::Default)
   {
      FilePath userFile = userConfFile();
      Error error = userFile.ensureFile();
      if (error)
         return error;

      Settings settings;
      error = settings.initialize(userFile);
      if (error)
         return error;

      settings.set(kCrashHandlingEnabled, handlerEnabled);
   }
   else
   {
      // we already have the user file open - simply update the settings directly
      s_settings->set(kCrashHandlingEnabled, handlerEnabled);
   }

   return Success();
}

bool hasUserBeenPromptedForPermission()
{
   if (!permissionFile().exists())
   {
      // if for some reason the parent directory is not writeable
      // we will just treat the user as if they have been prompted
      // to prevent indefinite repeated promptings
      if (!file_utils::isDirectoryWriteable(permissionFile().getParent()))
         return true;
      else
         return false;
   }
   else
      return true;
}

Error setUserHasBeenPromptedForPermission()
{
   return permissionFile().ensureFile();
}

} // namespace crash_handler
} // namespace core
} // namespace rstudio

#endif

Web Proxy Viewer  |  New URL  |  Original Page