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 ; }
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 ; }
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; }
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; }
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 ; }
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 ; }
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 ; }
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; }