示例#1
0
int main(int argc, char * const argv[]) 
{
   try
   {
      // initialize log
      initializeSystemLog("rpostback", core::system::kLogLevelWarning);

      // ignore SIGPIPE
      Error error = core::system::ignoreSignal(core::system::SigPipe);
      if (error)
         LOG_ERROR(error);

      // read program options 
      Options& options = session::postback::options();
      ProgramStatus status = options.read(argc, argv); 
      if ( status.exit() )
         return status.exitCode() ;
      
       // determine postback uri
      std::string uri = std::string(kLocalUriLocationPrefix kPostbackUriScope) + 
                        options.command();
      
      // build postback request
      http::Request request;
      request.setMethod("POST");
      request.setUri(uri);
      request.setHeader("Accept", "*/*");
      request.setHeader("Connection", "close");
      request.setBody(options.argument());

      // send it
      http::Response response;
      error = sendRequest(&request, &response);
      if (error)
         return exitFailure(error);

      std::string exitCode = response.headerValue(kPostbackExitCodeHeader);
      std::cout << response.body();
      return safe_convert::stringTo<int>(exitCode, EXIT_FAILURE);
   }
   CATCH_UNEXPECTED_EXCEPTION
   
   // if we got this far we had an unexpected exception
   return EXIT_FAILURE ;
}
示例#2
0
int main(int argc, char * const argv[]) 
{
   try
   { 
      // initialize log
      initializeSystemLog("rpostback", core::system::kLogLevelWarning);

      // read program options 
      Options& options = session::postback::options();
      ProgramStatus status = options.read(argc, argv); 
      if ( status.exit() )
         return status.exitCode() ;
      
       // determine postback uri
      std::string uri = std::string(kLocalUriLocationPrefix kPostbackUriScope) + 
                        options.command();
      
      // determine stream path
      std::string userIdentity = core::system::getenv(kRStudioUserIdentity);
      FilePath streamPath = session::local_streams::streamPath(userIdentity);

      // build postback request
      http::Request request;
      request.setMethod("POST");
      request.setUri(uri);
      request.setHeader("Accept", "*/*");
      request.setHeader("Connection", "close");
      request.setBody(options.argument());

      // send it
      http::Response response;
      Error error = http::sendRequest(streamPath, request,  &response);
      if (error)
         return exitFailure(error);
      else
         return EXIT_SUCCESS;
   }
   CATCH_UNEXPECTED_EXCEPTION
   
   // if we got this far we had an unexpected exception
   return EXIT_FAILURE ;
}
示例#3
0
core::ProgramStatus Options::read(int argc, char * const argv[])
{
   using namespace boost::program_options ;
   
   // compute the resource path
   FilePath resourcePath;
   Error error = core::system::installPath("..", argc, argv, &resourcePath);
   if (error)
   {
      LOG_ERROR_MESSAGE("Unable to determine install path: "+error.summary());
      return ProgramStatus::exitFailure();
   }

   // detect running in OSX bundle and tweak resource path
#ifdef __APPLE__
   if (resourcePath.complete("Info.plist").exists())
      resourcePath = resourcePath.complete("Resources");
#endif

   // detect running in x64 directory and tweak resource path
#ifdef _WIN32
   if (resourcePath.complete("x64").exists())
      resourcePath = resourcePath.parent();
#endif

   // program - name and execution
   options_description program("program");
   program.add_options()
      (kProgramModeSessionOption,
         value<std::string>(&programMode_)->default_value("server"),
         "program mode (desktop or server");
   
   // agreement
   options_description agreement("agreement");
   agreement.add_options()
      ("agreement-file",
      value<std::string>(&agreementFilePath_)->default_value(""),
      "agreement file");

   // docs url
   options_description docs("docs");
   docs.add_options()
      ("docs-url",
       value<std::string>(&docsURL_)->default_value(""),
       "custom docs url");

   // www options
   options_description www("www") ;
   www.add_options()
      ("www-local-path",
         value<std::string>(&wwwLocalPath_)->default_value("www"),
         "www local path")
      ("www-port",
         value<std::string>(&wwwPort_)->default_value("8787"),
         "port to listen on");

   // session options
   options_description session("session") ;
   session.add_options()
      ("session-timeout-minutes",
         value<int>(&timeoutMinutes_)->default_value(120),
         "session timeout (minutes)" )
      ("session-preflight-script",
         value<std::string>(&preflightScript_)->default_value(""),
         "session preflight script")
      ("session-create-public-folder",
         value<bool>(&createPublicFolder_)->default_value(false),
         "automatically create public folder");

   // r options
   options_description r("r") ;
   r.add_options()
      ("r-core-source",
         value<std::string>(&coreRSourcePath_)->default_value("R"),
         "Core R source path")
      ("r-modules-source", 
         value<std::string>(&modulesRSourcePath_)->default_value("R/modules"),
         "Modules R source path")
      ("r-session-packages",
         value<std::string>(&sessionPackagesPath_)->default_value("R/library"),
         "R packages path")
      ("r-libs-user",
         value<std::string>(&rLibsUser_)->default_value("~/R/library"),
         "R user library path")
      ("r-cran-repos",
         value<std::string>(&rCRANRepos_)->default_value(""),
         "Default CRAN repository")
      ("r-auto-reload-source",
         value<bool>(&autoReloadSource_)->default_value(false),
         "Reload R source if it changes during the session")
      ("r-compatible-graphics-engine-version",
         value<int>(&rCompatibleGraphicsEngineVersion_)->default_value(8),
         "Maximum graphics engine version we are compatible with")
      ("r-css-file",
         value<std::string>(&rHelpCssFilePath_)->default_value("resources/R.css"),
         "Custom R.css file")
      ("r-shell-escape",
         value<bool>(&rShellEscape_)->default_value(false),
         "Support shell escape");

   // limits options
   options_description limits("limits");
   limits.add_options()
      ("limit-file-upload-size-mb",
       value<int>(&limitFileUploadSizeMb_)->default_value(0),
       "limit of file upload size")
      ("limit-cpu-time-minutes",
       value<int>(&limitCpuTimeMinutes_)->default_value(0),
       "limit on time of top level computations")
      ("limit-xfs-disk-quota",
       value<bool>(&limitXfsDiskQuota_)->default_value(false),
       "limit xfs disk quota");
   
   // external options
   options_description external("external");
   external.add_options()
      ("external-rpostback-path", 
       value<std::string>(&rpostbackPath_)->default_value("bin/rpostback"),
       "Path to rpostback executable");
   
   // user options (default user identity to current username)
   std::string currentUsername = core::system::username();
   options_description user("user") ;
   user.add_options()
      (kUserIdentitySessionOption "," kUserIdentitySessionOptionShort,
       value<std::string>(&userIdentity_)->default_value(currentUsername),
       "user identity" );
   
   // define program options
   FilePath defaultConfigPath("/etc/rstudio/rsession.conf");
   std::string configFile = defaultConfigPath.exists() ?
                                 defaultConfigPath.absolutePath() : "";
   core::program_options::OptionsDescription optionsDesc("rsession",
                                                         configFile);

   optionsDesc.commandLine.add(program);
   optionsDesc.commandLine.add(agreement);
   optionsDesc.commandLine.add(docs);
   optionsDesc.commandLine.add(www);
   optionsDesc.commandLine.add(session);
   optionsDesc.commandLine.add(r);
   optionsDesc.commandLine.add(limits);
   optionsDesc.commandLine.add(external);
   optionsDesc.commandLine.add(user);
   
   // define groups included in config-file processing
   optionsDesc.configFile.add(program);
   optionsDesc.configFile.add(agreement);
   optionsDesc.configFile.add(docs);
   optionsDesc.configFile.add(www);
   optionsDesc.configFile.add(session);
   optionsDesc.configFile.add(r);
   optionsDesc.configFile.add(limits);
   optionsDesc.configFile.add(external);
   optionsDesc.configFile.add(user);

   // read configuration
   ProgramStatus status = core::program_options::read(optionsDesc, argc,argv);
   if (status.exit())
      return status;
   
   // make sure the program mode is valid
   if (programMode_ != kSessionProgramModeDesktop &&
       programMode_ != kSessionProgramModeServer)
   {
      LOG_ERROR_MESSAGE("invalid program mode: " + programMode_);
      return ProgramStatus::exitFailure();
   }

   // compute program identity
   programIdentity_ = "rsession-" + userIdentity_;

   // compute user home path
   FilePath userHomePath = core::system::userHomePath("R_USER|HOME");
   userHomePath_ = userHomePath.absolutePath();

   // compute user scratch path
   std::string scratchPathName;
   if (programMode_ == kSessionProgramModeDesktop)
      scratchPathName = "RStudio-Desktop";
   else
      scratchPathName = "RStudio";
   userScratchPath_ = core::system::userSettingsPath(
                                       userHomePath,
                                       scratchPathName).absolutePath();

   // session timeout seconds is always -1 in desktop mode
   if (programMode_ == kSessionProgramModeDesktop)
      timeoutMinutes_ = 0;

   // if we are in desktop mode and no agreement file path was
   // specified then default to gpl-standalone
   if ( (programMode_ == kSessionProgramModeDesktop) &&
        agreementFilePath_.empty())
   {
      agreementFilePath_ = "resources/agpl-3.0-standalone.html";
   }

   // convert relative paths by completing from the app resource path
   resolvePath(resourcePath, &rHelpCssFilePath_);
   resolvePath(resourcePath, &agreementFilePath_);
   resolvePath(resourcePath, &wwwLocalPath_);
   resolvePath(resourcePath, &coreRSourcePath_);
   resolvePath(resourcePath, &modulesRSourcePath_);
   resolvePath(resourcePath, &sessionPackagesPath_);
   resolvePath(resourcePath, &rpostbackPath_);

   // shared secret with parent
   secret_ = core::system::getenv("RS_SHARED_SECRET");
   core::system::unsetenv("RS_SHARED_SECRET");

   // initial working dir override
   initialWorkingDirOverride_ = core::system::getenv("RS_INITIAL_WD");
   core::system::unsetenv("RS_INITIAL_WD");

   // initial environment file override
   initialEnvironmentFileOverride_ = core::system::getenv("RS_INITIAL_ENV");
   core::system::unsetenv("RS_INITIAL_ENV");

   // limit rpc client uid
   limitRpcClientUid_ = -1;
   std::string limitUid = core::system::getenv(kRStudioLimitRpcClientUid);
   if (!limitUid.empty())
   {
      limitRpcClientUid_ = core::safe_convert::stringTo<int>(limitUid, -1);
      core::system::unsetenv(kRStudioLimitRpcClientUid);
   }

   // return status
   return status;
}
core::ProgramStatus Options::read(int argc, char * const argv[])
{
   using namespace boost::program_options ;
   
   // get the shared secret
   monitorSharedSecret_ = core::system::getenv(kMonitorSharedSecretEnvVar);
   core::system::unsetenv(kMonitorSharedSecretEnvVar);

   // compute the resource path
   FilePath resourcePath;
   Error error = core::system::installPath("..", argv[0], &resourcePath);
   if (error)
   {
      LOG_ERROR_MESSAGE("Unable to determine install path: "+error.summary());
      return ProgramStatus::exitFailure();
   }

   // detect running in OSX bundle and tweak resource path
#ifdef __APPLE__
   if (resourcePath.complete("Info.plist").exists())
      resourcePath = resourcePath.complete("Resources");
#endif

   // detect running in x64 directory and tweak resource path
#ifdef _WIN32
   if (resourcePath.complete("x64").exists())
      resourcePath = resourcePath.parent();
#endif

   // verify installation flag
   options_description verify("verify");
   verify.add_options()
     (kVerifyInstallationSessionOption,
     value<bool>(&verifyInstallation_)->default_value(false),
     "verify the current installation");

   // program - name and execution
   options_description program("program");
   program.add_options()
      (kProgramModeSessionOption,
         value<std::string>(&programMode_)->default_value("server"),
         "program mode (desktop or server");
   
   // log -- logging options
   options_description log("log");
   log.add_options()
      ("log-stderr",
      value<bool>(&logStderr_)->default_value(true),
      "write log entries to stderr");

   // agreement
   options_description agreement("agreement");
   agreement.add_options()
      ("agreement-file",
      value<std::string>(&agreementFilePath_)->default_value(""),
      "agreement file");

   // docs url
   options_description docs("docs");
   docs.add_options()
      ("docs-url",
       value<std::string>(&docsURL_)->default_value(""),
       "custom docs url");

   // www options
   options_description www("www") ;
   www.add_options()
      ("www-local-path",
         value<std::string>(&wwwLocalPath_)->default_value("www"),
         "www local path")
      ("www-symbol-maps-path",
         value<std::string>(&wwwSymbolMapsPath_)->default_value(
                                                         "www-symbolmaps"),
         "www symbol maps path")
      ("www-port",
         value<std::string>(&wwwPort_)->default_value("8787"),
         "port to listen on")
      ("www-address",
         value<std::string>(&wwwAddress_)->default_value("127.0.0.1"),
         "port to listen on")
      ("standalone",
         value<bool>(&standalone_)->default_value(false),
         "run standalone");

   // session options
   std::string saveActionDefault;
   options_description session("session") ;
   session.add_options()
      (kTimeoutSessionOption,
         value<int>(&timeoutMinutes_)->default_value(120),
         "session timeout (minutes)" )
      (kDisconnectedTimeoutSessionOption,
         value<int>(&disconnectedTimeoutMinutes_)->default_value(0),
         "session disconnected timeout (minutes)" )
      ("session-preflight-script",
         value<std::string>(&preflightScript_)->default_value(""),
         "session preflight script")
      ("session-create-public-folder",
         value<bool>(&createPublicFolder_)->default_value(false),
         "automatically create public folder")
      ("session-create-profile",
         value<bool>(&createProfile_)->default_value(false),
         "automatically create .Rprofile")
      ("session-rprofile-on-resume-default",
          value<bool>(&rProfileOnResumeDefault_)->default_value(false),
          "default user setting for running Rprofile on resume")
      ("session-save-action-default",
       value<std::string>(&saveActionDefault)->default_value(""),
          "default save action (yes, no, or ask)");

   // allow options
   options_description allow("allow");
   allow.add_options()
      ("allow-vcs-executable-edit",
         value<bool>(&allowVcsExecutableEdit_)->default_value(true),
         "allow editing of vcs executables")
      ("allow-r-cran-repos-edit",
         value<bool>(&allowCRANReposEdit_)->default_value(true),
         "Allow editing of CRAN repository")
      ("allow-vcs",
         value<bool>(&allowVcs_)->default_value(true),
         "allow use of version control features")
      ("allow-package-installation",
         value<bool>(&allowPackageInstallation_)->default_value(true),
         "allow installation of packages from the packages pane")
      ("allow-shell",
         value<bool>(&allowShell_)->default_value(true),
         "allow access to shell dialog")
      ("allow-file-downloads",
         value<bool>(&allowFileDownloads_)->default_value(true),
         "allow file downloads from the files pane")
      ("allow-remove-public-folder",
         value<bool>(&allowRemovePublicFolder_)->default_value(true),
         "allow removal of the user public folder")
      ("allow-rpubs-publish",
         value<bool>(&allowRpubsPublish_)->default_value(true),
        "allow publishing to rpubs");

   // r options
   bool rShellEscape; // no longer works but don't want to break any
                      // config files which formerly used it
                      // TODO: eliminate this option entirely
   options_description r("r") ;
   r.add_options()
      ("r-core-source",
         value<std::string>(&coreRSourcePath_)->default_value("R"),
         "Core R source path")
      ("r-modules-source", 
         value<std::string>(&modulesRSourcePath_)->default_value("R/modules"),
         "Modules R source path")
      ("r-session-library",
         value<std::string>(&sessionLibraryPath_)->default_value("R/library"),
         "R library path")
      ("r-session-packages",
         value<std::string>(&sessionPackagesPath_)->default_value("R/packages"),
         "R packages path")
      ("r-session-package-archives",
          value<std::string>(&sessionPackageArchivesPath_)->default_value("R/packages"),
         "R package archives path")
      ("r-libs-user",
         value<std::string>(&rLibsUser_)->default_value(""),
         "R user library path")
      ("r-cran-repos",
         value<std::string>(&rCRANRepos_)->default_value(""),
         "Default CRAN repository")
      ("r-auto-reload-source",
         value<bool>(&autoReloadSource_)->default_value(false),
         "Reload R source if it changes during the session")
      ("r-compatible-graphics-engine-version",
         value<int>(&rCompatibleGraphicsEngineVersion_)->default_value(10),
         "Maximum graphics engine version we are compatible with")
      ("r-resources-path",
         value<std::string>(&rResourcesPath_)->default_value("resources"),
         "Directory containing external resources")
      ("r-shell-escape",
         value<bool>(&rShellEscape)->default_value(false),
         "Support shell escape (deprecated, no longer works)")
      ("r-home-dir-override",
         value<std::string>(&rHomeDirOverride_)->default_value(""),
         "Override for R_HOME (used for debug configurations)")
      ("r-doc-dir-override",
         value<std::string>(&rDocDirOverride_)->default_value(""),
         "Override for R_DOC_DIR (used for debug configurations)");

   // limits options
   options_description limits("limits");
   limits.add_options()
      ("limit-file-upload-size-mb",
       value<int>(&limitFileUploadSizeMb_)->default_value(0),
       "limit of file upload size")
      ("limit-cpu-time-minutes",
       value<int>(&limitCpuTimeMinutes_)->default_value(0),
       "limit on time of top level computations")
      ("limit-xfs-disk-quota",
       value<bool>(&limitXfsDiskQuota_)->default_value(false),
       "limit xfs disk quota");
   
   // external options
   options_description external("external");
   external.add_options()
      ("external-rpostback-path", 
       value<std::string>(&rpostbackPath_)->default_value(kDefaultPostbackPath),
       "Path to rpostback executable")
      ("external-consoleio-path",
       value<std::string>(&consoleIoPath_)->default_value("bin/consoleio.exe"),
       "Path to consoleio executable")
      ("external-gnudiff-path",
       value<std::string>(&gnudiffPath_)->default_value("bin/gnudiff"),
       "Path to gnudiff utilities (windows-only)")
      ("external-gnugrep-path",
       value<std::string>(&gnugrepPath_)->default_value("bin/gnugrep"),
       "Path to gnugrep utilities (windows-only)")
      ("external-msysssh-path",
       value<std::string>(&msysSshPath_)->default_value("bin/msys_ssh"),
       "Path to msys_ssh utilities (windows-only)")
      ("external-sumatra-path",
       value<std::string>(&sumatraPath_)->default_value("bin/sumatra"),
       "Path to SumatraPDF (windows-only)")
      ("external-hunspell-dictionaries-path",
       value<std::string>(&hunspellDictionariesPath_)->default_value("resources/dictionaries"),
       "Path to hunspell dictionaries")
      ("external-mathjax-path",
        value<std::string>(&mathjaxPath_)->default_value("resources/mathjax-23"),
        "Path to mathjax library")
      ("external-pandoc-path",
        value<std::string>(&pandocPath_)->default_value(kDefaultPandocPath),
        "Path to pandoc binaries");

   // user options (default user identity to current username)
   std::string currentUsername = core::system::username();
   options_description user("user") ;
   user.add_options()
      (kUserIdentitySessionOption "," kUserIdentitySessionOptionShort,
       value<std::string>(&userIdentity_)->default_value(currentUsername),
       "user identity" )
      (kShowUserIdentitySessionOption,
       value<bool>(&showUserIdentity_)->default_value(true),
       "show the user identity");

   // overlay options
   options_description overlay("overlay");
   addOverlayOptions(&overlay);

   // define program options
   FilePath defaultConfigPath("/etc/rstudio/rsession.conf");
   std::string configFile = defaultConfigPath.exists() ?
                                 defaultConfigPath.absolutePath() : "";
   core::program_options::OptionsDescription optionsDesc("rsession",
                                                         configFile);

   optionsDesc.commandLine.add(verify);
   optionsDesc.commandLine.add(program);
   optionsDesc.commandLine.add(log);
   optionsDesc.commandLine.add(agreement);
   optionsDesc.commandLine.add(docs);
   optionsDesc.commandLine.add(www);
   optionsDesc.commandLine.add(session);
   optionsDesc.commandLine.add(allow);
   optionsDesc.commandLine.add(r);
   optionsDesc.commandLine.add(limits);
   optionsDesc.commandLine.add(external);
   optionsDesc.commandLine.add(user);

   // define groups included in config-file processing
   optionsDesc.configFile.add(program);
   optionsDesc.configFile.add(log);
   optionsDesc.configFile.add(agreement);
   optionsDesc.configFile.add(docs);
   optionsDesc.configFile.add(www);
   optionsDesc.configFile.add(session);
   optionsDesc.configFile.add(allow);
   optionsDesc.configFile.add(r);
   optionsDesc.configFile.add(limits);
   optionsDesc.configFile.add(external);
   optionsDesc.configFile.add(user);
   optionsDesc.configFile.add(overlay);

   // read configuration
   ProgramStatus status = core::program_options::read(optionsDesc, argc,argv);
   if (status.exit())
      return status;
   
   // make sure the program mode is valid
   if (programMode_ != kSessionProgramModeDesktop &&
       programMode_ != kSessionProgramModeServer)
   {
      LOG_ERROR_MESSAGE("invalid program mode: " + programMode_);
      return ProgramStatus::exitFailure();
   }

   // call overlay hooks
   resolveOverlayOptions();
   std::string errMsg;
   if (!validateOverlayOptions(&errMsg))
   {
      program_options::reportError(errMsg, ERROR_LOCATION);
      return ProgramStatus::exitFailure();
   }

   // compute program identity
   programIdentity_ = "rsession-" + userIdentity_;

   // provide special home path in temp directory if we are verifying
   if (verifyInstallation_)
   {
      // we create a special home directory in server mode (since the
      // user we are running under might not have a home directory)
      if (programMode_ == kSessionProgramModeServer)
      {
         verifyInstallationHomeDir_ = "/tmp/rstudio-verify-installation";
         Error error = FilePath(verifyInstallationHomeDir_).ensureDirectory();
         if (error)
         {
            LOG_ERROR(error);
            return ProgramStatus::exitFailure();
         }
         core::system::setenv("R_USER", verifyInstallationHomeDir_);
      }
   }

   // compute user paths
   r_util::SessionType sessionType =
      (programMode_ == kSessionProgramModeDesktop) ?
                                    r_util::SessionTypeDesktop :
                                    r_util::SessionTypeServer;

   r_util::UserDirectories userDirs = r_util::userDirectories(sessionType);
   userHomePath_ = userDirs.homePath;
   userScratchPath_ = userDirs.scratchPath;

   // set HOME if we are in standalone mode (this enables us to reflect
   // R_USER back into HOME on Linux)
   if (standalone())
      core::system::setenv("HOME", userHomePath_);

   // session timeout seconds is always -1 in desktop mode
   if (programMode_ == kSessionProgramModeDesktop)
      timeoutMinutes_ = 0;

   // convert string save action default to intenger
   if (saveActionDefault == "yes")
      saveActionDefault_ = r::session::kSaveActionSave;
   else if (saveActionDefault == "no")
      saveActionDefault_ = r::session::kSaveActionNoSave;
   else if (saveActionDefault == "ask" || saveActionDefault.empty())
      saveActionDefault_ = r::session::kSaveActionAsk;
   else
   {
      program_options::reportWarnings(
         "Invalid value '" + saveActionDefault + "' for "
         "session-save-action-default. Valid values are yes, no, and ask.",
         ERROR_LOCATION);
      saveActionDefault_ = r::session::kSaveActionAsk;
   }

   // convert relative paths by completing from the app resource path
   resolvePath(resourcePath, &rResourcesPath_);
   resolvePath(resourcePath, &agreementFilePath_);
   resolvePath(resourcePath, &wwwLocalPath_);
   resolvePath(resourcePath, &wwwSymbolMapsPath_);
   resolvePath(resourcePath, &coreRSourcePath_);
   resolvePath(resourcePath, &modulesRSourcePath_);
   resolvePath(resourcePath, &sessionLibraryPath_);
   resolvePath(resourcePath, &sessionPackagesPath_);
   resolvePath(resourcePath, &sessionPackageArchivesPath_);
   resolvePostbackPath(resourcePath, &rpostbackPath_);
#ifdef _WIN32
   resolvePath(resourcePath, &consoleIoPath_);
   resolvePath(resourcePath, &gnudiffPath_);
   resolvePath(resourcePath, &gnugrepPath_);
   resolvePath(resourcePath, &msysSshPath_);
   resolvePath(resourcePath, &sumatraPath_);
#endif
   resolvePath(resourcePath, &hunspellDictionariesPath_);
   resolvePath(resourcePath, &mathjaxPath_);
   resolvePandocPath(resourcePath, &pandocPath_);

   // shared secret with parent
   secret_ = core::system::getenv("RS_SHARED_SECRET");
   /* SECURITY: Need RS_SHARED_SECRET to be available to
      rpostback. However, we really ought to communicate
      it in a more secure manner than this, at least on
      Windows where even within the same user session some
      processes can have different priviliges (integrity
      levels) than others. For example, using a named pipe
      with proper SACL to retrieve the shared secret, where
      the name of the pipe is in an environment variable. */
   //core::system::unsetenv("RS_SHARED_SECRET");

   // initial working dir override
   initialWorkingDirOverride_ = core::system::getenv(kRStudioInitialWorkingDir);
   core::system::unsetenv(kRStudioInitialWorkingDir);

   // initial environment file override
   initialEnvironmentFileOverride_ = core::system::getenv(kRStudioInitialEnvironment);
   core::system::unsetenv(kRStudioInitialEnvironment);

   // initial project
   initialProjectPath_ = core::system::getenv(kRStudioInitialProject);
   core::system::unsetenv(kRStudioInitialProject);

   // limit rpc client uid
   limitRpcClientUid_ = -1;
   std::string limitUid = core::system::getenv(kRStudioLimitRpcClientUid);
   if (!limitUid.empty())
   {
      limitRpcClientUid_ = core::safe_convert::stringTo<int>(limitUid, -1);
      core::system::unsetenv(kRStudioLimitRpcClientUid);
   }

   // return status
   return status;
}
示例#5
0
ProgramStatus Options::read(int argc, char * const argv[])
{
   using namespace boost::program_options ;

   // compute install path
   FilePath installPath;
   Error error = core::system::installPath("..", argc, argv, &installPath);
   if (error)
   {
      LOG_ERROR_MESSAGE("Unable to determine install path: "+error.summary());
      return ProgramStatus::exitFailure();
   }

   // verify installation flag
   options_description verify("verify");
   verify.add_options()
     ("verify-installation",
     value<bool>(&verifyInstallation_)->default_value(false),
     "verify the current installation");

   // special program offline option (based on file existence at 
   // startup for easy bash script enable/disable of offline state)
   serverOffline_ = FilePath("/etc/rstudio/offline").exists();

   // program - name and execution
   options_description server("server");
   server.add_options()
      ("server-working-dir",
         value<std::string>(&serverWorkingDir_)->default_value("/"),
         "program working directory")
      ("server-user",
         value<std::string>(&serverUser_)->default_value(kDefaultProgramUser),
         "program user")
      ("server-daemonize",
         value<bool>(&serverDaemonize_)->default_value(1),
         "run program as daemon")
      ("server-app-armor-enabled",
         value<bool>(&serverAppArmorEnabled_)->default_value(1),
         "is app armor enabled for this session");

   // www - web server options
   options_description www("www") ;
   www.add_options()
      ("www-address",
         value<std::string>(&wwwAddress_)->default_value("0.0.0.0"),
         "server address")
      ("www-port",
         value<std::string>(&wwwPort_)->default_value("8787"),
         "port to listen on")
      ("www-local-path",
         value<std::string>(&wwwLocalPath_)->default_value("www"),
         "www files path")
      ("www-thread-pool-size",
         value<int>(&wwwThreadPoolSize_)->default_value(2),
         "thread pool size");

   // rsession
   options_description rsession("rsession");
   rsession.add_options()
      ("rsession-which-r",
         value<std::string>(&rsessionWhichR_)->default_value(""),
         "path to main R program (e.g. /usr/bin/R)")
      ("rsession-path", 
         value<std::string>(&rsessionPath_)->default_value("bin/rsession"),
         "path to rsession executable")
      ("rldpath-path",
         value<std::string>(&rldpathPath_)->default_value("bin/r-ldpath"),
         "path to r-ldpath script")
      ("rsession-ld-library-path",
         value<std::string>(&rsessionLdLibraryPath_)->default_value(""),
         "default LD_LIBRARY_PATH for rsession")
      ("rsession-config-file",
         value<std::string>(&rsessionConfigFile_)->default_value(""),
         "path to rsession config file")
      ("rsession-memory-limit-mb",
         value<int>(&rsessionMemoryLimitMb_)->default_value(0),
         "rsession memory limit (mb)")
      ("rsession-stack-limit-mb",
         value<int>(&rsessionStackLimitMb_)->default_value(0),
         "rsession stack limit (mb)")
      ("rsession-process-limit",
         value<int>(&rsessionUserProcessLimit_)->default_value(0),
         "rsession user process limit");
   
   // still read depracated options (so we don't break config files)
   bool deprecatedAuthPamRequiresPriv;
   options_description auth("auth");
   auth.add_options()
      ("auth-validate-users",
        value<bool>(&authValidateUsers_)->default_value(true),
        "validate that authenticated users exist on the target system")
      ("auth-required-user-group",
        value<std::string>(&authRequiredUserGroup_)->default_value(""),
        "limit to users belonging to the specified group")
      ("auth-pam-helper-path",
        value<std::string>(&authPamHelperPath_)->default_value("bin/rserver-pam"),
       "path to PAM helper binary")
      ("auth-pam-requires-priv",
        value<bool>(&deprecatedAuthPamRequiresPriv)->default_value(true),
        "deprecated: will always be true");

   // define program options
   FilePath defaultConfigPath("/etc/rstudio/rserver.conf");
   std::string configFile = defaultConfigPath.exists() ?
                                 defaultConfigPath.absolutePath() : "";
   program_options::OptionsDescription optionsDesc("rserver", configFile);
   optionsDesc.commandLine.add(verify).add(server).add(www).add(rsession).add(auth);
   optionsDesc.configFile.add(server).add(www).add(rsession).add(auth);
 
   // read options
   ProgramStatus status = core::program_options::read(optionsDesc, argc, argv);
   if (status.exit())
      return status;
    
   // if specified, confirm that the program user exists. however, if the
   // program user is the default and it doesn't exist then allow that to pass,
   // this just means that the user did a simple make install and hasn't setup
   // an rserver user yet. in this case the program will run as root
   if (!serverUser_.empty())
   {
      // if we aren't running as root then forget the programUser
      if (!util::system::realUserIsRoot())
      {
         serverUser_ = "";
      }
      // if there is a program user specified and it doesn't exist....
      else if (!util::system::user::exists(serverUser_))
      {
         if (serverUser_ == kDefaultProgramUser)
         {
            // administrator hasn't created an rserver system account yet
            // so run as root
            serverUser_ = "";
         }
         else
         {
            LOG_ERROR_MESSAGE("Server user "+ serverUser_ +" does not exist");
            return ProgramStatus::exitFailure();
         }
      }
   }

   // if app armor is enabled do a further check to see whether
   // the profile exists. if it doesn't then disable it
   if (serverAppArmorEnabled_)
   {
      if (!FilePath("/etc/apparmor.d/rstudio-server").exists())
         serverAppArmorEnabled_ = false;
   }

   // convert relative paths by completing from the system installation
   // path (this allows us to be relocatable)
   resolvePath(installPath, &wwwLocalPath_);
   resolvePath(installPath, &authPamHelperPath_);
   resolvePath(installPath, &rsessionPath_);
   resolvePath(installPath, &rldpathPath_);
   resolvePath(installPath, &rsessionConfigFile_);

   // return status
   return status;
}
示例#6
0
int main(int argc, char * const argv[]) 
{
   try
   {
      // initialize log
      initializeSystemLog(kProgramIdentity, core::system::kLogLevelWarning);

      // ignore SIGPIPE
      Error error = core::system::ignoreSignal(core::system::SigPipe);
      if (error)
         LOG_ERROR(error);

      // read program options 
      Options& options = server::options();
      ProgramStatus status = options.read(argc, argv); 
      if ( status.exit() )
         return status.exitCode() ;
      
      // daemonize if requested
      if (options.serverDaemonize())
      {
         Error error = core::system::daemonize();
         if (error)
            return core::system::exitFailure(error, ERROR_LOCATION);

         error = core::system::ignoreTerminalSignals();
         if (error)
            return core::system::exitFailure(error, ERROR_LOCATION);

         // set file creation mask to 022 (might have inherted 0 from init)
         setUMask(core::system::OthersNoWriteMask);
      }

      // detect R environment variables (calls R (and this forks) so must
      // happen after daemonize so that upstart script can correctly track us
      std::string errMsg;
      bool detected = r_environment::initialize(&errMsg);
      if (!detected)
      {
         program_options::reportError(errMsg, ERROR_LOCATION);
         return EXIT_FAILURE;
      }

      // increase the number of open files allowed (need more files
      // so we can supports lots of concurrent connectins)
      if (core::system::realUserIsRoot())
      {
         Error error = setResourceLimit(core::system::FilesLimit, 4096);
         if (error)
            return core::system::exitFailure(error, ERROR_LOCATION);
      }

      // set working directory
      error = FilePath(options.serverWorkingDir()).makeCurrentPath();
      if (error)
         return core::system::exitFailure(error, ERROR_LOCATION);

      // initialize crypto utils
      core::system::crypto::initialize();

      // initialize secure cookie module
      error = auth::secure_cookie::initialize();
      if (error)
         return core::system::exitFailure(error, ERROR_LOCATION);

      // initialize the session proxy
      error = session_proxy::initialize();
      if (error)
         return core::system::exitFailure(error, ERROR_LOCATION);

      // initialize http server
      error = httpServerInit();
      if (error)
         return core::system::exitFailure(error, ERROR_LOCATION);

      // register the monitor log writer (needs to happen after
      // http server init so the io service is available)
      core::system::addLogWriter(monitorAsyncLogWriter());

      // call overlay initialize
      error = overlay::initialize();
      if (error)
         return core::system::exitFailure(error, ERROR_LOCATION);

      // add handlers and initiliaze addins (offline has distinct behavior)
      if (server::options().serverOffline())
      {
         offline::httpServerAddHandlers();
      }
      else
      {
         // add handlers
         httpServerAddHandlers();

         // initialize addins
         error = addins::initialize();
         if (error)
            return core::system::exitFailure(error, ERROR_LOCATION);

         // initialize pam auth if we don't already have an auth handler
         if (!auth::handler::isRegistered())
         {
            error = pam_auth::initialize();
            if (error)
               return core::system::exitFailure(error, ERROR_LOCATION);
         }
      }

      // enforce restricted mode if we are running under app armor
      // note that failure to do this (for whatever unanticipated reason)
      // is not considered fatal however it is logged as an error
      // so the sys-admin is informed
      if (options.serverAppArmorEnabled())
      {
         error = app_armor::enforceRestricted();
         if (error)
            LOG_ERROR(error);
      }

      // give up root privilige if requested
      std::string runAsUser = options.serverUser();
      if (!runAsUser.empty())
      {
         // drop root priv
         Error error = core::system::temporarilyDropPriv(runAsUser);
         if (error)
            return core::system::exitFailure(error, ERROR_LOCATION);
      }

      // run special verify installation mode if requested
      if (options.verifyInstallation())
      {
         Error error = session_proxy::runVerifyInstallationSession();
         if (error)
            return core::system::exitFailure(error, ERROR_LOCATION);

         return EXIT_SUCCESS;
      }

      // call overlay startup
      error = overlay::startup();
      if (error)
         return core::system::exitFailure(error, ERROR_LOCATION);

      // run http server
      error = s_pHttpServer->run(options.wwwThreadPoolSize());
      if (error)
         return core::system::exitFailure(error, ERROR_LOCATION);

      // wait for signals
      error = waitForSignals();
      if (error)
         return core::system::exitFailure(error, ERROR_LOCATION);

      // NOTE: we never get here because waitForSignals waits forever
      return EXIT_SUCCESS;
   }
   CATCH_UNEXPECTED_EXCEPTION
   
   // if we got this far we had an unexpected exception
   return EXIT_FAILURE ;
}
示例#7
0
int main(int argc, char** argv)
{
   try
   {
      // initialize log
      initializeSystemLog("rsinverse", core::system::kLogLevelWarning);

      // ignore SIGPIPE
      Error error = core::system::ignoreSignal(core::system::SigPipe);
      if (error)
         LOG_ERROR(error);

      // read options
      using namespace boost::program_options ;
      options_description rsinverseOptions("rsinverse");
      unsigned int windowHandle;
      std::string port, sharedSecret, sourceFile;
      int line;
      rsinverseOptions.add_options()
         ("hwnd",
            value<unsigned int>(&windowHandle),
            "hwnd of rstudio instance")
         ("port",
            value<std::string>(&port),
            "port of rstudio instance")
         ("secret",
            value<std::string>(&sharedSecret),
            "rstudio shared secret")
         ("source-file",
            value<std::string>(&sourceFile),
            "source file to navigate to")
         ("line",
            value<int>(&line),
            "line of code to navigate to");

      // define program options (allow positional specification)
      core::program_options::OptionsDescription optDesc("rsinverse");
      optDesc.commandLine.add(rsinverseOptions);
      optDesc.positionalOptions.add("hwnd", 1);
      optDesc.positionalOptions.add("port", 1);
      optDesc.positionalOptions.add("secret", 1);
      optDesc.positionalOptions.add("source-file", 1);
      optDesc.positionalOptions.add("line", 1);

      // read options
      ProgramStatus status = core::program_options::read(optDesc, argc, argv);
      if (status.exit())
         return status.exitCode();

      // activate the window
      HWND hRStudioWnd = reinterpret_cast<HWND>(windowHandle);
      if (::IsWindow(hRStudioWnd))
      {
         HWND hwndPopup = ::GetLastActivePopup(hRStudioWnd);
         if (::IsWindow(hwndPopup))
            hRStudioWnd = hwndPopup;
         ::SetForegroundWindow(hRStudioWnd);
         if (::IsIconic(hRStudioWnd))
            ::ShowWindow(hRStudioWnd, SW_RESTORE);
      }

      // we presume that the path is passed to us in the system encoding
      sourceFile = string_utils::systemToUtf8(sourceFile);

      // enocde the source file and line as a query string
      std::string requestBody;
      core::http::Fields args;
      args.push_back(std::make_pair("source-file", sourceFile));
      args.push_back(std::make_pair("line",
                                     safe_convert::numberToString(line)));
      http::util::buildQueryString(args, &requestBody);


      // determine postback uri
      std::string uri = std::string(kLocalUriLocationPrefix kPostbackUriScope) +
                       "rsinverse";

      // build postback request
      http::Request request;
      request.setMethod("POST");
      request.setUri(uri);
      request.setHeader("Accept", "*/*");
      request.setHeader("X-Shared-Secret", sharedSecret);
      request.setHeader("Connection", "close");
      request.setBody(requestBody);

      // send it
      http::Response response;
      std::string pipeName = core::system::getenv("RS_LOCAL_PEER");
      error = http::sendRequest(pipeName,
                                request,
                                http::ConnectionRetryProfile(
                                     boost::posix_time::seconds(10),
                                     boost::posix_time::milliseconds(50)),
                                &response);

      std::string exitCode = response.headerValue(kPostbackExitCodeHeader);
      return safe_convert::stringTo<int>(exitCode, EXIT_FAILURE);
   }
   CATCH_UNEXPECTED_EXCEPTION

   // if we got this far we had an unexpected exception
   return EXIT_FAILURE ;
}
示例#8
0
int main(int argc, char * const argv[]) 
{
   try
   {
      // initialize log
      const char * const kProgramIdentity = "rserver";
      initializeSystemLog(kProgramIdentity, core::system::kLogLevelWarning);

      // ignore SIGPIPE (don't log error because we should never call
      // syslog prior to daemonizing)
      core::system::ignoreSignal(core::system::SigPipe);

      // read program options 
      std::ostringstream osWarnings;
      Options& options = server::options();
      ProgramStatus status = options.read(argc, argv, osWarnings);
      std::string optionsWarnings = osWarnings.str();
      if ( status.exit() )
      {
         if (!optionsWarnings.empty())
            program_options::reportWarnings(optionsWarnings, ERROR_LOCATION);

         return status.exitCode() ;
      }
      
      // daemonize if requested
      if (options.serverDaemonize())
      {
         Error error = core::system::daemonize();
         if (error)
            return core::system::exitFailure(error, ERROR_LOCATION);

         error = core::system::ignoreTerminalSignals();
         if (error)
            return core::system::exitFailure(error, ERROR_LOCATION);

         // set file creation mask to 022 (might have inherted 0 from init)
         if (options.serverSetUmask())
            setUMask(core::system::OthersNoWriteMask);
      }

      // wait until now to output options warnings (we need to wait for our
      // first call to logging functions until after daemonization)
      if (!optionsWarnings.empty())
         program_options::reportWarnings(optionsWarnings, ERROR_LOCATION);

      // increase the number of open files allowed (need more files
      // so we can supports lots of concurrent connectins)
      if (core::system::realUserIsRoot())
      {
         Error error = setResourceLimit(core::system::FilesLimit, 4096);
         if (error)
            return core::system::exitFailure(error, ERROR_LOCATION);
      }

      // set working directory
      Error error = FilePath(options.serverWorkingDir()).makeCurrentPath();
      if (error)
         return core::system::exitFailure(error, ERROR_LOCATION);

      // initialize crypto utils
      core::system::crypto::initialize();

      // initialize secure cookie module
      error = auth::secure_cookie::initialize();
      if (error)
         return core::system::exitFailure(error, ERROR_LOCATION);

      // initialize the session proxy
      error = session_proxy::initialize();
      if (error)
         return core::system::exitFailure(error, ERROR_LOCATION);

      // initialize http server
      error = httpServerInit();
      if (error)
         return core::system::exitFailure(error, ERROR_LOCATION);

      // initialize the process supervisor (needs to happen post http server
      // init for access to the scheduled command list)
      error = process_supervisor::initialize();
      if (error)
         return core::system::exitFailure(error, ERROR_LOCATION);

      // initialize monitor (needs to happen post http server init for access
      // to the server's io service)
      monitor::initializeMonitorClient(kMonitorSocketPath,
                                       server::options().monitorSharedSecret(),
                                       s_pHttpServer->ioService());

      // add a monitor log writer
      core::system::addLogWriter(
                monitor::client().createLogWriter(kProgramIdentity));

      // call overlay initialize
      error = overlay::initialize();
      if (error)
         return core::system::exitFailure(error, ERROR_LOCATION);

      // detect R environment variables (calls R (and this forks) so must
      // happen after daemonize so that upstart script can correctly track us
      std::string errMsg;
      bool detected = r_environment::initialize(&errMsg);
      if (!detected)
      {
         program_options::reportError(errMsg, ERROR_LOCATION);
         return EXIT_FAILURE;
      }

      // add handlers and initiliaze addins (offline has distinct behavior)
      if (server::options().serverOffline())
      {
         offline::httpServerAddHandlers();
      }
      else
      {
         // add handlers
         httpServerAddHandlers();

         // initialize addins
         error = addins::initialize();
         if (error)
            return core::system::exitFailure(error, ERROR_LOCATION);

         // initialize pam auth if we don't already have an auth handler
         if (!auth::handler::isRegistered())
         {
            error = pam_auth::initialize();
            if (error)
               return core::system::exitFailure(error, ERROR_LOCATION);
         }
      }

      // enforce restricted mode if we are running under app armor
      // note that failure to do this (for whatever unanticipated reason)
      // is not considered fatal however it is logged as an error
      // so the sys-admin is informed
      if (options.serverAppArmorEnabled())
      {
         error = app_armor::enforceRestricted();

         // log error unless it's path not found (which indicates that
         // libapparmor isn't installed on this system)
         if (error && !isPathNotFoundError(error))
            LOG_ERROR(error);
      }

      // give up root privilige if requested
      std::string runAsUser = options.serverUser();
      if (!runAsUser.empty())
      {
         // drop root priv
         Error error = core::system::temporarilyDropPriv(runAsUser);
         if (error)
            return core::system::exitFailure(error, ERROR_LOCATION);
      }

      // run special verify installation mode if requested
      if (options.verifyInstallation())
      {
         Error error = session_proxy::runVerifyInstallationSession();
         if (error)
            return core::system::exitFailure(error, ERROR_LOCATION);

         return EXIT_SUCCESS;
      }

      // call overlay startup
      error = overlay::startup();
      if (error)
         return core::system::exitFailure(error, ERROR_LOCATION);

      // run http server
      error = s_pHttpServer->run(options.wwwThreadPoolSize());
      if (error)
         return core::system::exitFailure(error, ERROR_LOCATION);

      // wait for signals
      error = waitForSignals();
      if (error)
         return core::system::exitFailure(error, ERROR_LOCATION);

      // NOTE: we never get here because waitForSignals waits forever
      return EXIT_SUCCESS;
   }
   CATCH_UNEXPECTED_EXCEPTION
   
   // if we got this far we had an unexpected exception
   return EXIT_FAILURE ;
}
示例#9
0
ProgramStatus Options::read(int argc,
                            char * const argv[],
                            std::ostream& osWarnings)
{
   using namespace boost::program_options ;

   // compute install path
   Error error = core::system::installPath("..", argv[0], &installPath_);
   if (error)
   {
      LOG_ERROR_MESSAGE("Unable to determine install path: "+error.summary());
      return ProgramStatus::exitFailure();
   }

   // compute the resource and binary paths
   FilePath resourcePath = installPath_;
   FilePath binaryPath = installPath_.childPath("bin");

   // detect running in OSX bundle and tweak paths
#ifdef __APPLE__
   if (installPath_.complete("Info.plist").exists())
   {
      resourcePath = installPath_.complete("Resources");
      binaryPath = installPath_.complete("MacOS");
   }
#endif

   // verify installation flag
   options_description verify("verify");
   verify.add_options()
     ("verify-installation",
     value<bool>(&verifyInstallation_)->default_value(false),
     "verify the current installation");

   // special program offline option (based on file existence at 
   // startup for easy bash script enable/disable of offline state)
   serverOffline_ = FilePath("/var/lib/rstudio-server/offline").exists();

   // generate monitor shared secret
   monitorSharedSecret_ = core::system::generateUuid();

   // program - name and execution
   options_description server("server");
   server.add_options()
      ("server-working-dir",
         value<std::string>(&serverWorkingDir_)->default_value("/"),
         "program working directory")
      ("server-user",
         value<std::string>(&serverUser_)->default_value(kDefaultProgramUser),
         "program user")
      ("server-daemonize",
         value<bool>(&serverDaemonize_)->default_value(
                                      core::system::effectiveUserIsRoot()),
         "run program as daemon")
      ("server-app-armor-enabled",
         value<bool>(&serverAppArmorEnabled_)->default_value(1),
         "is app armor enabled for this session");

   // www - web server options
   options_description www("www") ;
   www.add_options()
      ("www-address",
         value<std::string>(&wwwAddress_)->default_value("0.0.0.0"),
         "server address")
      ("www-port",
         value<std::string>(&wwwPort_)->default_value(""),
         "port to listen on")
      ("www-local-path",
         value<std::string>(&wwwLocalPath_)->default_value("www"),
         "www files path")
      ("www-symbol-maps-path",
         value<std::string>(&wwwSymbolMapsPath_)->default_value(
                                                      "www-symbolmaps"),
        "www symbol maps path")
      ("www-use-emulated-stack",
       value<bool>(&wwwUseEmulatedStack_)->default_value(false),
       "use gwt emulated stack")
      ("www-thread-pool-size",
         value<int>(&wwwThreadPoolSize_)->default_value(2),
         "thread pool size")
      ("www-proxy-localhost",
         value<bool>(&wwwProxyLocalhost_)->default_value(true),
         "proxy requests to localhost ports over main server port");

   // rsession
   Deprecated dep;
   options_description rsession("rsession");
   rsession.add_options()
      ("rsession-which-r",
         value<std::string>(&rsessionWhichR_)->default_value(""),
         "path to main R program (e.g. /usr/bin/R)")
      ("rsession-path", 
         value<std::string>(&rsessionPath_)->default_value("rsession"),
         "path to rsession executable")
      ("rldpath-path",
         value<std::string>(&rldpathPath_)->default_value("r-ldpath"),
         "path to r-ldpath script")
      ("rsession-ld-library-path",
         value<std::string>(&rsessionLdLibraryPath_)->default_value(""),
         "default LD_LIBRARY_PATH for rsession")
      ("rsession-config-file",
         value<std::string>(&rsessionConfigFile_)->default_value(""),
         "path to rsession config file")
      ("rsession-memory-limit-mb",
         value<int>(&dep.memoryLimitMb)->default_value(dep.memoryLimitMb),
         "rsession memory limit (mb) - DEPRECATED")
      ("rsession-stack-limit-mb",
         value<int>(&dep.stackLimitMb)->default_value(dep.stackLimitMb),
         "rsession stack limit (mb) - DEPRECATED")
      ("rsession-process-limit",
         value<int>(&dep.userProcessLimit)->default_value(dep.userProcessLimit),
         "rsession user process limit - DEPRECATED");
   
   // still read depracated options (so we don't break config files)
   options_description auth("auth");
   auth.add_options()
      ("auth-none",
        value<bool>(&authNone_)->default_value(
                                 !core::system::effectiveUserIsRoot()),
        "don't do any authentication")
      ("auth-validate-users",
        value<bool>(&authValidateUsers_)->default_value(
                                 core::system::effectiveUserIsRoot()),
        "validate that authenticated users exist on the target system")
      ("auth-required-user-group",
        value<std::string>(&authRequiredUserGroup_)->default_value(""),
        "limit to users belonging to the specified group")
      ("auth-pam-helper-path",
        value<std::string>(&authPamHelperPath_)->default_value("rserver-pam"),
       "path to PAM helper binary")
      ("auth-pam-requires-priv",
        value<bool>(&dep.authPamRequiresPriv)->default_value(
                                                   dep.authPamRequiresPriv),
        "deprecated: will always be true");

   options_description monitor("monitor");
   monitor.add_options()
      (kMonitorIntervalSeconds,
       value<int>(&monitorIntervalSeconds_)->default_value(300),
       "monitoring interval");

   // define program options
   FilePath defaultConfigPath("/etc/rstudio/rserver.conf");
   std::string configFile = defaultConfigPath.exists() ?
                                 defaultConfigPath.absolutePath() : "";
   program_options::OptionsDescription optionsDesc("rserver", configFile);

   // overlay hook
   addOverlayOptions(&server, &www, &rsession, &auth, &monitor);

   optionsDesc.commandLine.add(verify).add(server).add(www).add(rsession).add(auth).add(monitor);
   optionsDesc.configFile.add(server).add(www).add(rsession).add(auth).add(monitor);
 
   // read options
   bool help = false;
   ProgramStatus status = core::program_options::read(optionsDesc,
                                                      argc,
                                                      argv,
                                                      &help);

   // terminate if this was a help request
   if (help)
      return ProgramStatus::exitSuccess();

   // report deprecation warnings
   reportDeprecationWarnings(dep, osWarnings);

   // call overlay hooks
   resolveOverlayOptions();
   std::string errMsg;
   if (!validateOverlayOptions(&errMsg, osWarnings))
   {
      program_options::reportError(errMsg, ERROR_LOCATION);
      return ProgramStatus::exitFailure();
   }

   // exit if the call to read indicated we should -- note we don't do this
   // immediately so that we can allow overlay validation to occur (otherwise
   // a --test-config wouldn't test overlay options)
   if (status.exit())
      return status;

   // rationalize auth settings
   if (authNone_)
      authValidateUsers_ = false;

   // if specified, confirm that the program user exists. however, if the
   // program user is the default and it doesn't exist then allow that to pass,
   // this just means that the user did a simple make install and hasn't setup
   // an rserver user yet. in this case the program will run as root
   if (!serverUser_.empty())
   {
      // if we aren't running as root then forget the programUser
      if (!core::system::realUserIsRoot())
      {
         serverUser_ = "";
      }
      // if there is a program user specified and it doesn't exist....
      else if (!core::system::user::exists(serverUser_))
      {
         if (serverUser_ == kDefaultProgramUser)
         {
            // administrator hasn't created an rserver system account yet
            // so we'll end up running as root
            serverUser_ = "";
         }
         else
         {
            LOG_ERROR_MESSAGE("Server user "+ serverUser_ +" does not exist");
            return ProgramStatus::exitFailure();
         }
      }
   }

   // if app armor is enabled do a further check to see whether
   // the profile exists. if it doesn't then disable it
   if (serverAppArmorEnabled_)
   {
      if (!FilePath("/etc/apparmor.d/rstudio-server").exists())
         serverAppArmorEnabled_ = false;
   }

   // convert relative paths by completing from the system installation
   // path (this allows us to be relocatable)
   resolvePath(resourcePath, &wwwLocalPath_);
   resolvePath(resourcePath, &wwwSymbolMapsPath_);
   resolvePath(binaryPath, &authPamHelperPath_);
   resolvePath(binaryPath, &rsessionPath_);
   resolvePath(binaryPath, &rldpathPath_);
   resolvePath(resourcePath, &rsessionConfigFile_);

   // return status
   return status;
}