<commit_before>/*
 * FileLock.cpp
 *
 * Copyright (C) 2009-18 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 <core/FileLock.hpp>

// #define RSTUDIO_ENABLE_DEBUG_MACROS
#include <core/Macros.hpp>

#include <core/Settings.hpp>
#include <shared_core/Error.hpp>
#include <core/Log.hpp>
#include <core/FileSerializer.hpp>
#include <core/http/SocketUtils.hpp>
#include <core/system/Environment.hpp>

#include <boost/algorithm/string.hpp>

// borrowed from SessionConstants.hpp
#define kRStudioSessionRoute "RSTUDIO_SESSION_ROUTE"

namespace rstudio {
namespace core {

namespace {

const char * const kLockTypeAdvisory  = "advisory";
const char * const kLockTypeLinkBased = "linkbased";

// use advisory locks on Windows by default; link-based elsewhere
#ifdef _WIN32
# define kLockTypeDefault      kLockTypeAdvisory
# define kLockTypeDefaultEnum  (FileLock::LOCKTYPE_ADVISORY)
#else
# define kLockTypeDefault      kLockTypeLinkBased
# define kLockTypeDefaultEnum  (FileLock::LOCKTYPE_LINKBASED)
#endif 

const char * const kLocksConfPath    = "/etc/rstudio/file-locks";
const double kDefaultRefreshRate     = 20.0;
const double kDefaultTimeoutInterval = 30.0;

std::string lockTypeToString(FileLock::LockType type)
{
   switch (type)
   {
   case FileLock::LOCKTYPE_ADVISORY:  return kLockTypeAdvisory;
   case FileLock::LOCKTYPE_LINKBASED: return kLockTypeLinkBased;
   }
   
   // not reached
   return std::string();
}

FileLock::LockType stringToLockType(const std::string& lockType,
                                    FileLock::LockType defaultLockType)
{
   using namespace boost::algorithm;
   
   if (boost::iequals(lockType, kLockTypeAdvisory))
      return FileLock::LOCKTYPE_ADVISORY;
   else if (boost::iequals(lockType, kLockTypeLinkBased))
      return FileLock::LOCKTYPE_LINKBASED;
   
   LOG_WARNING_MESSAGE("unrecognized lock type '" + lockType + "'");
   return defaultLockType;
}

double getFieldPositive(const Settings& settings,
                        const std::string& name,
                        double defaultValue)
{
   double value = settings.getDouble(name, defaultValue);
   if (value < 0)
   {
      LOG_WARNING_MESSAGE("invalid field '" + name + "': must be positive");
      return defaultValue;
   }
   
   return value;
}

bool s_isInitialized = false;

} // end anonymous namespace

bool FileLock::verifyInitialized()
{
   if (!s_isInitialized)
   {
      static bool s_warned = false;
      if (s_warned)
         return s_isInitialized;
      
      s_warned = true;
      LOG_WARNING_MESSAGE(
               "FileLock classes not yet initialized; please call "
               "'FileLock::initialize()' and 'FileLock::cleanUp()' as appropriate");
   }
   
   return s_isInitialized;
}

void FileLock::initialize(FileLock::LockType fallbackLockType)
{
   // read settings
   FilePath locksConfPath(kLocksConfPath);

   Settings settings;
   if (locksConfPath.exists())
   {
      Error error = settings.initialize(locksConfPath);
      if (error)
         LOG_ERROR(error);
   }
   
#ifdef _WIN32
   // TODO: link-based locks are not yet implemented on Windows
   defaultLockType = LOCKTYPE_ADVISORY;
#endif
   
   // default lock type
   std::string lockTypePref = settings.get("lock-type");
   FileLock::s_defaultType = lockTypePref.empty()
         ? fallbackLockType
         : stringToLockType(lockTypePref, fallbackLockType);

   // timeout interval
   double timeoutInterval = getFieldPositive(settings, "timeout-interval", kDefaultTimeoutInterval);
   FileLock::s_timeoutInterval = boost::posix_time::seconds(static_cast<long>(timeoutInterval));
   
   // refresh rate
   double refreshRate = getFieldPositive(settings, "refresh-rate", kDefaultRefreshRate);
   FileLock::s_refreshRate = boost::posix_time::seconds(static_cast<long>(refreshRate));
   
   // logging
   bool loggingEnabled = settings.getBool("enable-logging", false);
   FileLock::s_loggingEnabled = loggingEnabled;
   
   // logfile
   std::string logFile = settings.get("log-file");
   FileLock::s_logFile = FilePath(logFile);
   
   std::stringstream ss;
   ss << "(PID " << ::getpid() << "): Initialized file locks ("
      << "lock-type=" << lockTypeToString(FileLock::s_defaultType) << ", "
      << "timeout-interval=" << FileLock::s_timeoutInterval.total_seconds() << "s, "
      << "refresh-rate=" << FileLock::s_refreshRate.total_seconds() << "s, "
      << "log-file=" << logFile << ")"
      << std::endl;
   FileLock::log(ss.str());

   std::string distributedLockingOption = core::system::getenv(kRStudioDistributedLockingEnabled);<commit_msg>enforce advisory locks on Windows<commit_after>/*
 * FileLock.cpp
 *
 * Copyright (C) 2009-18 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 <core/FileLock.hpp>

// #define RSTUDIO_ENABLE_DEBUG_MACROS
#include <core/Macros.hpp>

#include <core/Settings.hpp>
#include <shared_core/Error.hpp>
#include <core/Log.hpp>
#include <core/FileSerializer.hpp>
#include <core/http/SocketUtils.hpp>
#include <core/system/Environment.hpp>

#include <boost/algorithm/string.hpp>

// borrowed from SessionConstants.hpp
#define kRStudioSessionRoute "RSTUDIO_SESSION_ROUTE"

namespace rstudio {
namespace core {

namespace {

const char * const kLockTypeAdvisory  = "advisory";
const char * const kLockTypeLinkBased = "linkbased";

// use advisory locks on Windows by default; link-based elsewhere
#ifdef _WIN32
# define kLockTypeDefault      kLockTypeAdvisory
# define kLockTypeDefaultEnum  (FileLock::LOCKTYPE_ADVISORY)
#else
# define kLockTypeDefault      kLockTypeLinkBased
# define kLockTypeDefaultEnum  (FileLock::LOCKTYPE_LINKBASED)
#endif 

const char * const kLocksConfPath    = "/etc/rstudio/file-locks";
const double kDefaultRefreshRate     = 20.0;
const double kDefaultTimeoutInterval = 30.0;

std::string lockTypeToString(FileLock::LockType type)
{
   switch (type)
   {
   case FileLock::LOCKTYPE_ADVISORY:  return kLockTypeAdvisory;
   case FileLock::LOCKTYPE_LINKBASED: return kLockTypeLinkBased;
   }
   
   // not reached
   return std::string();
}

FileLock::LockType stringToLockType(const std::string& lockType,
                                    FileLock::LockType defaultLockType)
{
   using namespace boost::algorithm;
   
   if (boost::iequals(lockType, kLockTypeAdvisory))
      return FileLock::LOCKTYPE_ADVISORY;
   else if (boost::iequals(lockType, kLockTypeLinkBased))
      return FileLock::LOCKTYPE_LINKBASED;
   
   LOG_WARNING_MESSAGE("unrecognized lock type '" + lockType + "'");
   return defaultLockType;
}

double getFieldPositive(const Settings& settings,
                        const std::string& name,
                        double defaultValue)
{
   double value = settings.getDouble(name, defaultValue);
   if (value < 0)
   {
      LOG_WARNING_MESSAGE("invalid field '" + name + "': must be positive");
      return defaultValue;
   }
   
   return value;
}

bool s_isInitialized = false;

} // end anonymous namespace

bool FileLock::verifyInitialized()
{
   if (!s_isInitialized)
   {
      static bool s_warned = false;
      if (s_warned)
         return s_isInitialized;
      
      s_warned = true;
      LOG_WARNING_MESSAGE(
               "FileLock classes not yet initialized; please call "
               "'FileLock::initialize()' and 'FileLock::cleanUp()' as appropriate");
   }
   
   return s_isInitialized;
}

void FileLock::initialize(FileLock::LockType fallbackLockType)
{
   // read settings
   FilePath locksConfPath(kLocksConfPath);

   Settings settings;
   if (locksConfPath.exists())
   {
      Error error = settings.initialize(locksConfPath);
      if (error)
         LOG_ERROR(error);
   }
   
#ifdef _WIN32
   // TODO: link-based locks are not yet implemented on Windows
   FileLock::s_defaultType = LOCKTYPE_ADVISORY;
#else
   // default lock type
   std::string lockTypePref = settings.get("lock-type");
   FileLock::s_defaultType = lockTypePref.empty()
         ? fallbackLockType
         : stringToLockType(lockTypePref, fallbackLockType);
#endif

   // timeout interval
   double timeoutInterval = getFieldPositive(settings, "timeout-interval", kDefaultTimeoutInterval);
   FileLock::s_timeoutInterval = boost::posix_time::seconds(static_cast<long>(timeoutInterval));
   
   // refresh rate
   double refreshRate = getFieldPositive(settings, "refresh-rate", kDefaultRefreshRate);
   FileLock::s_refreshRate = boost::posix_time::seconds(static_cast<long>(refreshRate));
   
   // logging
   bool loggingEnabled = settings.getBool("enable-logging", false);
   FileLock::s_loggingEnabled = loggingEnabled;
   
   // logfile
   std::string logFile = settings.get("log-file");
   FileLock::s_logFile = FilePath(logFile);
   
   std::stringstream ss;
   ss << "(PID " << ::getpid() << "): Initialized file locks ("
      << "lock-type=" << lockTypeToString(FileLock::s_defaultType) << ", "
      << "timeout-interval=" << FileLock::s_timeoutInterval.total_seconds() << "s, "
      << "refresh-rate=" << FileLock::s_refreshRate.total_seconds() << "s, "
      << "log-file=" << logFile << ")"
      << std::endl;
   FileLock::log(ss.str());

   std::string distributedLockingOption = core::system::getenv(kRStudioDistributedLockingEnabled);
{'fraction_non_alphanumeric': 0.10985221674876847, 'fraction_numerical': 0.003940886699507389, 'mean_word_length': 3.838417540514776, 'pattern_counts': {'":': 0, '<': 91, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3293550', 'n_tokens_mistral': 3086, 'n_tokens_neox': 3008, 'n_words': 718}