1 /*
2  * SessionMain.cpp
3  *
4  * Copyright (C) 2021 by RStudio, PBC
5  *
6  * Unless you have received this program directly from RStudio pursuant
7  * to the terms of a commercial license agreement with RStudio, then
8  * this program is licensed to you under the terms of version 3 of the
9  * GNU Affero General Public License. This program is distributed WITHOUT
10  * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
11  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
12  * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
13  *
14  */
15 
16 #include <session/SessionMain.hpp>
17 
18 // required to avoid Win64 winsock order of include
19 // compilation problem
20 #include <boost/asio/io_service.hpp>
21 #include <boost/scope_exit.hpp>
22 
23 #ifndef _WIN32
24 #include <sys/types.h>
25 #include <unistd.h>
26 #endif
27 
28 #include <string>
29 #include <vector>
30 #include <queue>
31 #include <map>
32 #include <algorithm>
33 #include <cstdlib>
34 #include <csignal>
35 #include <limits>
36 
37 #include <boost/shared_ptr.hpp>
38 #include <boost/function.hpp>
39 #include <boost/lexical_cast.hpp>
40 #include <boost/format.hpp>
41 
42 #include <boost/date_time/posix_time/posix_time.hpp>
43 #include <boost/algorithm/string/predicate.hpp>
44 #include <boost/algorithm/string/join.hpp>
45 
46 #include <core/CrashHandler.hpp>
47 #include <core/BoostSignals.hpp>
48 #include <core/BoostThread.hpp>
49 #include <core/ConfigUtils.hpp>
50 #include <core/FileLock.hpp>
51 #include <core/Exec.hpp>
52 #include <core/Scope.hpp>
53 #include <core/Settings.hpp>
54 #include <core/Thread.hpp>
55 #include <core/Log.hpp>
56 #include <core/system/System.hpp>
57 #include <core/ProgramStatus.hpp>
58 #include <core/FileSerializer.hpp>
59 #include <core/http/URL.hpp>
60 #include <core/http/Request.hpp>
61 #include <core/http/Response.hpp>
62 #include <core/http/UriHandler.hpp>
63 #include <core/json/JsonRpc.hpp>
64 #include <core/system/Crypto.hpp>
65 #include <core/system/Process.hpp>
66 #include <core/system/Environment.hpp>
67 #include <core/system/ParentProcessMonitor.hpp>
68 #include <core/system/Xdg.hpp>
69 
70 #include <core/system/FileMonitor.hpp>
71 #include <core/text/TemplateFilter.hpp>
72 #include <core/r_util/RSessionContext.hpp>
73 #include <core/r_util/REnvironment.hpp>
74 #include <core/WaitUtils.hpp>
75 
76 #include <r/RJsonRpc.hpp>
77 #include <r/RExec.hpp>
78 #include <r/ROptions.hpp>
79 #include <r/RFunctionHook.hpp>
80 #include <r/RInterface.hpp>
81 #include <r/session/RSession.hpp>
82 #include <r/session/RSessionState.hpp>
83 #include <r/session/RClientState.hpp>
84 #include <r/session/RConsoleActions.hpp>
85 #include <r/session/RConsoleHistory.hpp>
86 #include <r/session/RGraphics.hpp>
87 #include <r/session/REventLoop.hpp>
88 #include <r/RUtil.hpp>
89 
90 #include <monitor/MonitorClient.hpp>
91 
92 #include <session/SessionConstants.hpp>
93 #include <session/SessionOptions.hpp>
94 #include <session/SessionSourceDatabase.hpp>
95 #include <session/SessionPersistentState.hpp>
96 #include <session/SessionContentUrls.hpp>
97 #include <session/SessionScopes.hpp>
98 #include <session/SessionClientEventService.hpp>
99 #include <session/SessionUrlPorts.hpp>
100 #include <session/RVersionSettings.hpp>
101 
102 #include <shared_core/Error.hpp>
103 #include <shared_core/FilePath.hpp>
104 #include <shared_core/StderrLogDestination.hpp>
105 
106 #include "SessionAddins.hpp"
107 
108 #include "SessionModuleContextInternal.hpp"
109 
110 #include "SessionClientEventQueue.hpp"
111 #include "SessionClientInit.hpp"
112 #include "SessionConsoleInput.hpp"
113 #include "SessionDirs.hpp"
114 #include "SessionHttpMethods.hpp"
115 #include "SessionInit.hpp"
116 #include "SessionMainProcess.hpp"
117 #include "SessionRpc.hpp"
118 #include "SessionSuspend.hpp"
119 #include "SessionOfflineService.hpp"
120 
121 #include <session/SessionRUtil.hpp>
122 #include <session/SessionPackageProvidedExtension.hpp>
123 
124 #include "modules/RStudioAPI.hpp"
125 #include "modules/SessionAbout.hpp"
126 #include "modules/SessionAskPass.hpp"
127 #include "modules/SessionAskSecret.hpp"
128 #include "modules/SessionAuthoring.hpp"
129 #include "modules/SessionBreakpoints.hpp"
130 #include "modules/SessionHTMLPreview.hpp"
131 #include "modules/SessionCodeSearch.hpp"
132 #include "modules/SessionConfigFile.hpp"
133 #include "modules/SessionConsole.hpp"
134 #include "modules/SessionCRANMirrors.hpp"
135 #include "modules/SessionCrypto.hpp"
136 #include "modules/SessionErrors.hpp"
137 #include "modules/SessionFiles.hpp"
138 #include "modules/SessionFind.hpp"
139 #include "modules/SessionGraphics.hpp"
140 #include "modules/SessionDependencies.hpp"
141 #include "modules/SessionDependencyList.hpp"
142 #include "modules/SessionDirty.hpp"
143 #include "modules/SessionWorkbench.hpp"
144 #include "modules/SessionHelp.hpp"
145 #include "modules/SessionPlots.hpp"
146 #include "modules/SessionPath.hpp"
147 #include "modules/SessionPackages.hpp"
148 #include "modules/SessionPackrat.hpp"
149 #include "modules/SessionPlumberViewer.hpp"
150 #include "modules/SessionProfiler.hpp"
151 #include "modules/SessionRAddins.hpp"
152 #include "modules/SessionRCompletions.hpp"
153 #include "modules/SessionRenv.hpp"
154 #include "modules/SessionRPubs.hpp"
155 #include "modules/SessionRHooks.hpp"
156 #include "modules/SessionRSConnect.hpp"
157 #include "modules/SessionShinyViewer.hpp"
158 #include "modules/SessionSpelling.hpp"
159 #include "modules/SessionSource.hpp"
160 #include "modules/SessionTests.hpp"
161 #include "modules/SessionThemes.hpp"
162 #include "modules/SessionTutorial.hpp"
163 #include "modules/SessionUpdates.hpp"
164 #include "modules/SessionVCS.hpp"
165 #include "modules/SessionHistory.hpp"
166 #include "modules/SessionLimits.hpp"
167 #include "modules/SessionLists.hpp"
168 #include "modules/SessionUserPrefs.hpp"
169 #include "modules/build/SessionBuild.hpp"
170 #include "modules/clang/SessionClang.hpp"
171 #include "modules/connections/SessionConnections.hpp"
172 #include "modules/customsource/SessionCustomSource.hpp"
173 #include "modules/data/SessionData.hpp"
174 #include "modules/environment/SessionEnvironment.hpp"
175 #include "modules/jobs/SessionJobs.hpp"
176 #include "modules/overlay/SessionOverlay.hpp"
177 #include "modules/plumber/SessionPlumber.hpp"
178 #include "modules/presentation/SessionPresentation.hpp"
179 #include "modules/preview/SessionPreview.hpp"
180 #include "modules/rmarkdown/RMarkdownTemplates.hpp"
181 #include "modules/rmarkdown/SessionRMarkdown.hpp"
182 #include "modules/rmarkdown/SessionRmdNotebook.hpp"
183 #include "modules/rmarkdown/SessionBookdown.hpp"
184 #include "modules/quarto/SessionQuarto.hpp"
185 #include "modules/shiny/SessionShiny.hpp"
186 #include "modules/sql/SessionSql.hpp"
187 #include "modules/stan/SessionStan.hpp"
188 #include "modules/viewer/SessionViewer.hpp"
189 #include "modules/SessionDiagnostics.hpp"
190 #include "modules/SessionMarkers.hpp"
191 #include "modules/SessionSnippets.hpp"
192 #include "modules/SessionUserCommands.hpp"
193 #include "modules/SessionRAddins.hpp"
194 #include "modules/mathjax/SessionMathJax.hpp"
195 #include "modules/panmirror/SessionPanmirror.hpp"
196 #include "modules/zotero/SessionZotero.hpp"
197 #include "modules/SessionLibPathsIndexer.hpp"
198 #include "modules/SessionObjectExplorer.hpp"
199 #include "modules/SessionReticulate.hpp"
200 #include "modules/SessionPythonEnvironments.hpp"
201 #include "modules/SessionCrashHandler.hpp"
202 #include "modules/SessionRVersions.hpp"
203 #include "modules/SessionTerminal.hpp"
204 #include "modules/SessionFonts.hpp"
205 #include "modules/SessionSystemResources.hpp"
206 
207 #include <session/SessionProjectTemplate.hpp>
208 
209 #include "modules/SessionGit.hpp"
210 #include "modules/SessionSVN.hpp"
211 
212 #include <session/SessionConsoleProcess.hpp>
213 
214 #include <session/projects/ProjectsSettings.hpp>
215 #include <session/projects/SessionProjects.hpp>
216 #include "projects/SessionProjectsInternal.hpp"
217 
218 #include <session/prefs/UserPrefs.hpp>
219 #include <session/prefs/UserState.hpp>
220 
221 #include "workers/SessionWebRequestWorker.hpp"
222 
223 #include <session/SessionHttpConnectionListener.hpp>
224 
225 #include "session-config.h"
226 
227 #include <tests/TestRunner.hpp>
228 
229 using namespace rstudio;
230 using namespace rstudio::core;
231 
232 // Use rsession alias to avoid collision with 'session'
233 // object brought in by Catch
234 namespace rsession = rstudio::session;
235 using namespace rsession;
236 using namespace rsession::client_events;
237 
238 // forward-declare overlay methods
239 namespace rstudio {
240 namespace session {
241 
242 namespace {
243 
244 std::string s_fallbackLibraryPath;
245 
246 } // end anonymous namespace
247 
disableExecuteRprofile()248 bool disableExecuteRprofile()
249 {
250    // check for session-specific override
251    if (options().rRunRprofile() == kRunRprofileNo)
252       return true;
253    if (options().rRunRprofile() == kRunRprofileYes)
254       return false;
255 
256    bool disableExecuteRprofile = false;
257 
258    const projects::ProjectContext& projContext = projects::projectContext();
259    if (projContext.hasProject())
260    {
261       disableExecuteRprofile = projContext.config().disableExecuteRprofile;
262    }
263 
264    return disableExecuteRprofile;
265 }
266 
quitChildProcesses()267 bool quitChildProcesses()
268 {
269    // allow project override
270    const projects::ProjectContext& projContext = projects::projectContext();
271    if (projContext.hasProject())
272    {
273       switch(projContext.config().quitChildProcessesOnExit)
274       {
275       case r_util::YesValue:
276          return true;
277       case r_util::NoValue:
278          return false;
279       default:
280          // fall through
281          break;
282       }
283    }
284 
285    // no project override
286    return rsession::options().quitChildProcessesOnExit();
287 }
288 
289 // terminates all child processes, including those unknown to us
290 // no waiting is done to ensure the children shutdown
291 // this is merely a best effort to stop children
terminateAllChildProcesses()292 void terminateAllChildProcesses()
293 {
294    if (!quitChildProcesses())
295       return;
296 
297    Error error = system::terminateChildProcesses();
298    if (error)
299       LOG_ERROR(error);
300 }
301 
302 namespace overlay {
303 Error initialize();
304 Error initializeSessionProxy();
305 } // namespace overlay
306 } // namespace session
307 } // namespace rstudio
308 
309 namespace {
310 
311 // R browseUrl handlers
312 std::vector<module_context::RBrowseUrlHandler> s_rBrowseUrlHandlers;
313 
314 // R browseFile handlers
315 std::vector<module_context::RBrowseFileHandler> s_rBrowseFileHandlers;
316 
317 // indicates whether we should destroy the session at cleanup time
318 // (true if the user does a full quit)
319 bool s_destroySession = false;
320 
321 // did we fail to coerce the charset to UTF-8
322 bool s_printCharsetWarning = false;
323 
handleINT(int)324 void handleINT(int)
325 {
326    rstudio::r::exec::setInterruptsPending(true);
327 }
328 
detectChanges(module_context::ChangeSource source)329 void detectChanges(module_context::ChangeSource source)
330 {
331    module_context::events().onDetectChanges(source);
332 }
333 
334 // allow console_input requests to come in when we aren't explicitly waiting
335 // on them (i.e. waitForMethod("console_input")). place them into into a buffer
336 // which is then checked by rConsoleRead prior to it calling waitForMethod
bufferConsoleInput(const core::json::JsonRpcRequest & request,json::JsonRpcResponse * pResponse)337 Error bufferConsoleInput(const core::json::JsonRpcRequest& request,
338                          json::JsonRpcResponse* pResponse)
339 {
340    // extract the input
341    return console_input::extractConsoleInput(request);
342 }
343 
doSuspendForRestart(const rstudio::r::session::RSuspendOptions & options)344 void doSuspendForRestart(const rstudio::r::session::RSuspendOptions& options)
345 {
346    module_context::consoleWriteOutput("\nRestarting R session...\n\n");
347 
348    rstudio::r::session::suspendForRestart(options);
349 }
350 
suspendForRestart(const core::json::JsonRpcRequest & request,json::JsonRpcResponse * pResponse)351 Error suspendForRestart(const core::json::JsonRpcRequest& request,
352                         json::JsonRpcResponse* pResponse)
353 {
354    // when launcher sessions restart, they need to set a special exit code
355    // to ensure that the rsession-run script restarts the rsession process
356    // instead of having to submit an entirely new launcher session
357    int exitStatus = options().getBoolOverlayOption(kLauncherSessionOption) ?
358             EX_SUSPEND_RESTART_LAUNCHER_SESSION :
359             EX_CONTINUE;
360 
361    rstudio::r::session::RSuspendOptions options(exitStatus);
362    Error error = json::readObjectParam(
363                                request.params, 0,
364                                "save_minimal", &(options.saveMinimal),
365                                "save_workspace", &(options.saveWorkspace),
366                                "exclude_packages", &(options.excludePackages));
367    if (error)
368       return error;
369 
370    pResponse->setAfterResponse(boost::bind(doSuspendForRestart, options));
371    return Success();
372 }
373 
374 
ping(const core::json::JsonRpcRequest & request,json::JsonRpcResponse * pResponse)375 Error ping(const core::json::JsonRpcRequest& request,
376            json::JsonRpcResponse* pResponse)
377 {
378    return Success();
379 }
380 
startClientEventService()381 Error startClientEventService()
382 {
383    return clientEventService().start(rsession::persistentState().activeClientId());
384 }
385 
startOfflineService()386 Error startOfflineService()
387 {
388    return offlineService().start();
389 }
390 
registerSignalHandlers()391 Error registerSignalHandlers()
392 {
393    using boost::bind;
394    using namespace rstudio::core::system;
395 
396    ExecBlock registerBlock;
397 
398    module_context::initializeConsoleCtrlHandler();
399 
400    // SIGINT: set interrupt flag on R session
401    registerBlock.addFunctions()
402          (bind(handleSignal, SigInt, handleINT));
403 
404    // USR1 and USR2: perform suspend in server mode
405    if (rsession::options().programMode() == kSessionProgramModeServer)
406    {
407       registerBlock.addFunctions()
408          (bind(handleSignal, SigUsr1, suspend::handleUSR1))
409          (bind(handleSignal, SigUsr2, suspend::handleUSR2));
410    }
411    // USR1 and USR2: ignore in desktop mode
412    else
413    {
414       registerBlock.addFunctions()
415          (bind(ignoreSignal, SigUsr1))
416          (bind(ignoreSignal, SigUsr2));
417    }
418 
419    return registerBlock.execute();
420 }
421 
422 
runPreflightScript()423 Error runPreflightScript()
424 {
425    // alias options
426    Options& options = rsession::options();
427 
428    // run the preflight script (if specified)
429    if (rsession::options().programMode() == kSessionProgramModeServer)
430    {
431       FilePath preflightScriptPath = options.preflightScriptPath();
432       if (!preflightScriptPath.isEmpty())
433       {
434          if (preflightScriptPath.exists())
435          {
436             // run the script (ignore errors and continue no matter what
437             // the outcome of the script is)
438             std::string script = preflightScriptPath.getAbsolutePath();
439             core::system::ProcessResult result;
440             Error error = runCommand(script,
441                                      core::system::ProcessOptions(),
442                                      &result);
443             if (error)
444             {
445                error.addProperty("preflight-script", script);
446                LOG_ERROR(error);
447             }
448          }
449          else
450          {
451             LOG_WARNING_MESSAGE("preflight script does not exist: " +
452                                    preflightScriptPath.getAbsolutePath());
453          }
454       }
455    }
456 
457    // always return success
458    return Success();
459 }
460 
461 // implemented below
462 void stopMonitorWorkerThread();
463 
exitEarly(int status)464 void exitEarly(int status)
465 {
466    stopMonitorWorkerThread();
467    FileLock::cleanUp();
468    FilePath(s_fallbackLibraryPath).removeIfExists();
469    ::exit(status);
470 }
471 
rInit(const rstudio::r::session::RInitInfo & rInitInfo)472 Error rInit(const rstudio::r::session::RInitInfo& rInitInfo)
473 {
474    // save state we need to reference later
475    suspend::setSessionResumed(rInitInfo.resumed);
476 
477    // record built-in waitForMethod handlers
478    module_context::registerWaitForMethod(kLocatorCompleted);
479    module_context::registerWaitForMethod(kEditCompleted);
480    module_context::registerWaitForMethod(kChooseFileCompleted);
481    module_context::registerWaitForMethod(kUserPromptCompleted);
482    module_context::registerWaitForMethod(kHandleUnsavedChangesCompleted);
483    module_context::registerWaitForMethod(kRStudioAPIShowDialogMethod);
484 
485    // execute core initialization functions
486    using boost::bind;
487    using namespace rstudio::core::system;
488    using namespace rsession::module_context;
489    ExecBlock initialize;
490    initialize.addFunctions()
491 
492       // client event service
493       (startClientEventService)
494 
495       // rpc methods
496       (rpc::initialize)
497 
498       // json-rpc listeners
499       (bind(registerRpcMethod, kConsoleInput, bufferConsoleInput))
500       (bind(registerRpcMethod, "suspend_for_restart", suspendForRestart))
501       (bind(registerRpcMethod, "ping", ping))
502 
503       // signal handlers
504       (registerSignalHandlers)
505 
506       // main module context
507       (module_context::initialize)
508 
509       // prefs (early init required -- many modules including projects below require
510       // preference access)
511       (modules::prefs::initialize)
512 
513       // projects (early project init required -- module inits below
514       // can then depend on e.g. computed defaultEncoding)
515       (projects::initialize)
516 
517       // source database
518       (source_database::initialize)
519 
520       // content urls
521       (content_urls::initialize)
522 
523       // URL port transformations
524       (url_ports::initialize)
525 
526       // overlay R
527       (bind(sourceModuleRFile, "SessionOverlay.R"))
528 
529       // addins
530       (addins::initialize)
531 
532       // console processes
533       (console_process::initialize)
534 
535       (http_methods::initialize)
536 
537       // r utils
538       (r_utils::initialize)
539 
540       // modules with c++ implementations
541       (modules::spelling::initialize)
542       (modules::lists::initialize)
543       (modules::path::initialize)
544       (modules::limits::initialize)
545       (modules::ppe::initialize)
546       (modules::ask_pass::initialize)
547       (modules::console::initialize)
548 #ifdef RSTUDIO_SERVER
549       (modules::crypto::initialize)
550 #endif
551       (modules::code_search::initialize)
552       (modules::clang::initialize)
553       (modules::connections::initialize)
554       (modules::files::initialize)
555       (modules::find::initialize)
556       (modules::environment::initialize)
557       (modules::dependencies::initialize)
558       (modules::dependency_list::initialize)
559       (modules::dirty::initialize)
560       (modules::workbench::initialize)
561       (modules::data::initialize)
562       (modules::help::initialize)
563       (modules::presentation::initialize)
564       (modules::preview::initialize)
565       (modules::plots::initialize)
566       (modules::packages::initialize)
567       (modules::cran_mirrors::initialize)
568       (modules::profiler::initialize)
569       (modules::viewer::initialize)
570       (modules::quarto::initialize)
571       (modules::rmarkdown::initialize)
572       (modules::rmarkdown::notebook::initialize)
573       (modules::rmarkdown::templates::initialize)
574       (modules::rmarkdown::bookdown::initialize)
575       (modules::rpubs::initialize)
576       (modules::shiny::initialize)
577       (modules::sql::initialize)
578       (modules::stan::initialize)
579       (modules::plumber::initialize)
580       (modules::source::initialize)
581       (modules::source_control::initialize)
582       (modules::authoring::initialize)
583       (modules::html_preview::initialize)
584       (modules::history::initialize)
585       (modules::build::initialize)
586       (modules::overlay::initialize)
587       (modules::breakpoints::initialize)
588       (modules::errors::initialize)
589       (modules::updates::initialize)
590       (modules::about::initialize)
591       (modules::shiny_viewer::initialize)
592       (modules::plumber_viewer::initialize)
593       (modules::rsconnect::initialize)
594       (modules::packrat::initialize)
595       (modules::renv::initialize)
596       (modules::rhooks::initialize)
597       (modules::r_packages::initialize)
598       (modules::diagnostics::initialize)
599       (modules::markers::initialize)
600       (modules::snippets::initialize)
601       (modules::user_commands::initialize)
602       (modules::r_addins::initialize)
603       (modules::projects::templates::initialize)
604       (modules::mathjax::initialize)
605       (modules::panmirror::initialize)
606       (modules::zotero::initialize)
607       (modules::rstudioapi::initialize)
608       (modules::libpaths::initialize)
609       (modules::explorer::initialize)
610       (modules::ask_secret::initialize)
611       (modules::reticulate::initialize)
612       (modules::python_environments::initialize)
613       (modules::tests::initialize)
614       (modules::jobs::initialize)
615       (modules::themes::initialize)
616       (modules::customsource::initialize)
617       (modules::crash_handler::initialize)
618       (modules::r_versions::initialize)
619       (modules::terminal::initialize)
620       (modules::config_file::initialize)
621       (modules::tutorial::initialize)
622       (modules::graphics::initialize)
623       (modules::fonts::initialize)
624       (modules::system_resources::initialize)
625 
626       // workers
627       (workers::web_request::initialize)
628 
629       // R code
630       (bind(sourceModuleRFile, "SessionCodeTools.R"))
631       (bind(sourceModuleRFile, "SessionPatches.R"))
632 
633       (startOfflineService)
634 
635       // unsupported functions
636       (bind(rstudio::r::function_hook::registerUnsupported, "bug.report", "utils"))
637       (bind(rstudio::r::function_hook::registerUnsupported, "help.request", "utils"))
638    ;
639 
640    Error error = initialize.execute();
641    if (error)
642       return error;
643 
644    // if we are in verify installation mode then we should exit (successfully) now
645    if (rsession::options().verifyInstallation())
646    {
647       // in desktop mode we write a success message and execute diagnostics
648       if (rsession::options().programMode() == kSessionProgramModeDesktop)
649       {
650          std::cout << "Successfully initialized R session."
651                    << std::endl << std::endl;
652          FilePath diagFile = module_context::sourceDiagnostics();
653          if (!diagFile.isEmpty())
654          {
655             std::cout << "Diagnostics report written to: "
656                       << diagFile << std::endl
657                       << "Please audit the report and remove any sensitive information "
658                       << "before submitting." << std::endl << std::endl;
659 
660             Error error = rstudio::r::exec::RFunction(".rs.showDiagnostics").call();
661             if (error)
662                LOG_ERROR(error);
663          }
664       }
665       rsession::options().verifyInstallationHomeDir().removeIfExists();
666 
667       int exitCode = modules::overlay::verifyInstallation();
668       exitEarly(exitCode);
669    }
670 
671    // register all of the json rpc methods implemented in R
672    json::JsonRpcMethods rMethods;
673    error = rstudio::r::json::getRpcMethods(&rMethods);
674    if (error)
675       return error;
676 
677    for (json::JsonRpcMethod method : rMethods)
678    {
679       registerRpcMethod(json::adaptMethodToAsync(method));
680    }
681 
682    // add gwt handlers if we are running desktop mode
683    if ((rsession::options().programMode() == kSessionProgramModeDesktop) ||
684        rsession::options().standalone())
685    {
686       http_methods::registerGwtHandlers();
687    }
688 
689    // enque abend warning event if necessary (but not in standalone
690    // mode since those processes are often aborted unceremoniously)
691    using namespace rsession::client_events;
692    if (rsession::persistentState().hadAbend() && !options().standalone())
693    {
694       LOG_ERROR_MESSAGE("The previous R session terminated abnormally");
695       ClientEvent abendWarningEvent(kAbendWarning);
696       rsession::clientEventQueue().add(abendWarningEvent);
697    }
698 
699    if (s_printCharsetWarning)
700       rstudio::r::exec::warning("Character set is not UTF-8; please change your locale");
701 
702    // propagate console history options
703    rstudio::r::session::consoleHistory().setRemoveDuplicates(
704                                  prefs::userPrefs().removeHistoryDuplicates());
705 
706 
707    // register function editor on windows
708 #ifdef _WIN32
709    error = rstudio::r::exec::RFunction(".rs.registerFunctionEditor").call();
710    if (error)
711       LOG_ERROR(error);
712 #endif
713 
714    // clear out stale uploaded tmp files
715    if (module_context::userUploadedFilesScratchPath().exists())
716    {
717       std::vector<FilePath> childPaths;
718       error = module_context::userUploadedFilesScratchPath().getChildren(childPaths);
719       if (error)
720          LOG_ERROR(error);
721 
722       constexpr double secondsPerDay = 60*60*24;
723       for (const FilePath& childPath : childPaths)
724       {
725          double diffTime = std::difftime(std::time(nullptr),
726                                          childPath.getLastWriteTime());
727          if (diffTime > secondsPerDay)
728          {
729             Error error = childPath.remove();
730             if (error)
731                LOG_ERROR(error);
732          }
733       }
734    }
735 
736    // set flag indicating we had an abnormal end (if this doesn't get
737    // unset by the time we launch again then we didn't terminate normally
738    // i.e. either the process dying unexpectedly or a call to R_Suicide)
739    if (!rsession::options().runTests())
740    {
741       rsession::persistentState().setAbend(true);
742    }
743 
744    // begin session
745    using namespace module_context;
746    activeSession().beginSession(rVersion(), rHomeDir(), rVersionLabel());
747 
748    // setup fork handlers
749    main_process::setupForkHandlers();
750 
751    // success!
752    return Success();
753 }
754 
rInitComplete()755 void rInitComplete()
756 {
757    module_context::events().onInitComplete();
758 }
759 
notifyIfRVersionChanged()760 void notifyIfRVersionChanged()
761 {
762    using namespace rstudio::r::session::state;
763 
764    SessionStateInfo info = getSessionStateInfo();
765 
766    if (info.activeRVersion != info.suspendedRVersion)
767    {
768       const char* fmt =
769             "R version change [%1% -> %2%] detected when restoring session; "
770             "search path not restored";
771 
772       boost::format formatter(fmt);
773       formatter
774             % std::string(info.suspendedRVersion)
775             % std::string(info.activeRVersion);
776 
777       std::string msg = formatter.str();
778       ::REprintf("%s\n", msg.c_str());
779    }
780 }
781 
rSessionInitHook(bool newSession)782 void rSessionInitHook(bool newSession)
783 {
784    // allow any packages listening to complete initialization
785    modules::rhooks::invokeHook(kSessionInitHook, newSession);
786 
787    // finish off initialization
788    module_context::events().afterSessionInitHook(newSession);
789 
790    // notify the user if the R version has changed
791    notifyIfRVersionChanged();
792 
793    // fire an event to the client
794    ClientEvent event(client_events::kDeferredInitCompleted);
795    module_context::enqueClientEvent(event);
796 }
797 
rDeferredInit(bool newSession)798 void rDeferredInit(bool newSession)
799 {
800    module_context::events().onDeferredInit(newSession);
801 
802    // schedule execution of the session init hook
803    module_context::scheduleDelayedWork(
804                         boost::posix_time::seconds(1),
805                         boost::bind(rSessionInitHook, newSession));
806 }
807 
rEditFile(const std::string & file)808 int rEditFile(const std::string& file)
809 {
810    // read file contents
811    FilePath filePath(file);
812    std::string fileContents;
813    if (filePath.exists())
814    {
815       Error readError = core::readStringFromFile(filePath, &fileContents);
816       if (readError)
817       {
818          LOG_ERROR(readError);
819          return 1; // r will raise/report an error indicating edit failed
820       }
821    }
822 
823    // fire edit event
824    ClientEvent editEvent = rsession::showEditorEvent(fileContents, true, false);
825    rsession::clientEventQueue().add(editEvent);
826 
827    // wait for edit_completed
828    json::JsonRpcRequest request;
829    bool succeeded = http_methods::waitForMethod(kEditCompleted,
830                                         editEvent,
831                                         suspend::disallowSuspend,
832                                         &request);
833 
834    if (!succeeded)
835       return false;
836 
837    // user cancelled edit
838    if (request.params[0].isNull())
839    {
840       return 0; // no-op, object will be re-parsed from original content
841    }
842 
843    // user confirmed edit
844    else
845    {
846       // extract the content
847       std::string editedFileContents;
848       Error error = json::readParam(request.params, 0, &editedFileContents);
849       if (error)
850       {
851          LOG_ERROR(error);
852          return 1; // error (r will notify user via the console)
853       }
854 
855       // write the content back to the file (append newline expected by R)
856       editedFileContents += "\n";
857       Error writeError = core::writeStringToFile(filePath, editedFileContents);
858       if (writeError)
859       {
860          LOG_ERROR(writeError);
861          return 1 ; // error (r will notify user via the console)
862       }
863 
864       // success!
865       return 0;
866    }
867 }
868 
869 
rChooseFile(bool newFile)870 FilePath rChooseFile(bool newFile)
871 {
872    // fire choose file event
873    ClientEvent chooseFileEvent(kChooseFile, newFile);
874    rsession::clientEventQueue().add(chooseFileEvent);
875 
876    // wait for choose_file_completed
877    json::JsonRpcRequest request;
878    bool succeeded = http_methods::waitForMethod(kChooseFileCompleted,
879                                         chooseFileEvent,
880                                         suspend::disallowSuspend,
881                                         &request);
882 
883    if (!succeeded)
884       return FilePath();
885 
886    // extract the file name
887    std::string fileName;
888    if (!request.params[0].isNull())
889    {
890       Error error = json::readParam(request.params, 0, &fileName);
891       if (error)
892          LOG_ERROR(error);
893 
894       // resolve aliases and return it
895       return module_context::resolveAliasedPath(fileName);
896    }
897    else
898    {
899       return FilePath();
900    }
901 }
902 
903 
rBusy(bool busy)904 void rBusy(bool busy)
905 {
906    if (main_process::wasForked())
907       return;
908 
909    // screen out busy = true events that occur when R isn't busy
910    if (busy && !console_input::executing())
911       return;
912 
913    ClientEvent busyEvent(kBusy, busy);
914    rsession::clientEventQueue().add(busyEvent);
915 }
916 
rConsoleWrite(const std::string & output,int otype)917 void rConsoleWrite(const std::string& output, int otype)
918 {
919    if (main_process::wasForked())
920       return;
921 
922    int event = otype == 1 ? kConsoleWriteError : kConsoleWriteOutput;
923    ClientEvent writeEvent(event, output);
924    rsession::clientEventQueue().add(writeEvent);
925 
926    // fire event
927    module_context::events().onConsoleOutput(
928                   otype == 1 ? module_context::ConsoleOutputError :
929                                module_context::ConsoleOutputNormal,
930                   output);
931 
932 }
933 
rConsoleHistoryReset()934 void rConsoleHistoryReset()
935 {
936    json::Array historyJson;
937    rstudio::r::session::consoleHistory().asJson(&historyJson);
938    json::Object resetJson;
939    resetJson["history"] = historyJson;
940    resetJson["preserve_ui_context"] = false;
941    ClientEvent event(kConsoleResetHistory, resetJson);
942    rsession::clientEventQueue().add(event);
943 }
944 
rLocator(double * x,double * y)945 bool rLocator(double* x, double* y)
946 {
947    // since locator can be called in a loop we need to checkForChanges
948    // here (because we'll never get back to the REPL). this enables
949    // identify() to correctly update the plot after each click
950    detectChanges(module_context::ChangeSourceREPL);
951 
952    // fire locator event
953    ClientEvent locatorEvent(kLocator);
954    rsession::clientEventQueue().add(locatorEvent);
955 
956    // wait for locator_completed
957    json::JsonRpcRequest request;
958    bool succeeded = http_methods::waitForMethod(kLocatorCompleted,
959                                         locatorEvent,
960                                         suspend::disallowSuspend,
961                                         &request);
962 
963    if (!succeeded)
964       return false;
965 
966    // see if we got a point
967    if ((request.params.getSize() > 0) && !request.params[0].isNull())
968    {
969       // read the x and y
970       Error error = json::readObjectParam(request.params, 0,
971                                           "x", x,
972                                           "y", y);
973       if (error)
974       {
975          LOG_ERROR(error);
976          return false;
977       }
978 
979       // return true
980       return true;
981    }
982    else
983    {
984       return false;
985    }
986 }
987 
rShowFile(const std::string & title,const FilePath & filePath,bool del)988 void rShowFile(const std::string& title, const FilePath& filePath, bool del)
989 {
990    if (rsession::options().programMode() == kSessionProgramModeServer)
991    {
992       // for files in the temporary directory, show as content
993       //
994       // (perform this check first to handle case where
995       // tempdir lives within the user's home directory)
996       if (filePath.isWithin(module_context::tempDir()))
997       {
998          module_context::showContent(title, filePath);
999       }
1000 
1001       // for files in the user's home directory and pdfs use an external browser
1002       else if (module_context::isVisibleUserFile(filePath) ||
1003           (filePath.getExtensionLowerCase() == ".pdf"))
1004       {
1005          module_context::showFile(filePath);
1006       }
1007 
1008       // otherwise, show as content
1009       else
1010       {
1011          module_context::showContent(title, filePath);
1012       }
1013    }
1014    else // (rsession::options().programMode() == kSessionProgramModeDesktop
1015    {
1016 #ifdef _WIN32
1017     if (!filePath.getExtension().empty())
1018     {
1019        module_context::showFile(filePath);
1020        del = false;
1021     }
1022     else
1023     {
1024        module_context::showContent(title, filePath);
1025     }
1026 #else
1027     module_context::showContent(title, filePath);
1028 #endif
1029    }
1030 
1031    if (del)
1032    {
1033       Error error = filePath.removeIfExists();
1034       if (error)
1035          LOG_ERROR(error);
1036    }
1037 }
1038 
rBrowseURL(const std::string & url)1039 void rBrowseURL(const std::string& url)
1040 {
1041    // first see if any of our handlers want to take it
1042    for (std::vector<module_context::RBrowseUrlHandler>::const_iterator
1043             it = s_rBrowseUrlHandlers.begin();
1044             it != s_rBrowseUrlHandlers.end();
1045             ++it)
1046    {
1047       if ((*it)(url))
1048          return;
1049    }
1050 
1051    // raise event to client
1052    rsession::clientEventQueue().add(browseUrlEvent(url));
1053 }
1054 
rBrowseFile(const core::FilePath & filePath)1055 void rBrowseFile(const core::FilePath& filePath)
1056 {
1057    // see if any of our handlers want to take it
1058    for (std::vector<module_context::RBrowseFileHandler>::const_iterator
1059             it = s_rBrowseFileHandlers.begin();
1060             it != s_rBrowseFileHandlers.end();
1061             ++it)
1062    {
1063       if ((*it)(filePath))
1064          return;
1065    }
1066 
1067    // see if this is an html file in the session temporary directory (in which
1068    // case we can serve it over http)
1069    if ((filePath.getMimeContentType() == "text/html") &&
1070        filePath.isWithin(module_context::tempDir()) &&
1071        rstudio::r::util::hasRequiredVersion("2.14"))
1072    {
1073       std::string path = filePath.getRelativePath(module_context::tempDir());
1074       std::string url = module_context::sessionTempDirUrl(path);
1075       rsession::clientEventQueue().add(browseUrlEvent(url));
1076    }
1077    // otherwise just show the file
1078    else
1079    {
1080       module_context::showFile(filePath);
1081    }
1082 }
1083 
rShowHelp(const std::string & helpURL)1084 void rShowHelp(const std::string& helpURL)
1085 {
1086    ClientEvent showHelpEvent(kShowHelp, helpURL);
1087    rsession::clientEventQueue().add(showHelpEvent);
1088 }
1089 
rShowMessage(const std::string & message)1090 void rShowMessage(const std::string& message)
1091 {
1092    ClientEvent event = showErrorMessageEvent("R Error", message);
1093    rsession::clientEventQueue().add(event);
1094 }
1095 
logExitEvent(const monitor::Event & precipitatingEvent)1096 void logExitEvent(const monitor::Event& precipitatingEvent)
1097 {
1098    using namespace monitor;
1099    client().logEvent(precipitatingEvent);
1100    client().logEvent(Event(kSessionScope, kSessionExitEvent));
1101 }
1102 
rSuspended(const rstudio::r::session::RSuspendOptions & options)1103 void rSuspended(const rstudio::r::session::RSuspendOptions& options)
1104 {
1105    // log to monitor
1106    using namespace monitor;
1107    std::string data;
1108    if (suspend::suspendedFromTimeout())
1109       data = safe_convert::numberToString(rsession::options().timeoutMinutes());
1110    logExitEvent(Event(kSessionScope, kSessionSuspendEvent, data));
1111 
1112    // fire event
1113    module_context::onSuspended(options, &(persistentState().settings()));
1114 }
1115 
rResumed()1116 void rResumed()
1117 {
1118    module_context::onResumed(persistentState().settings());
1119 }
1120 
rHandleUnsavedChanges()1121 bool rHandleUnsavedChanges()
1122 {
1123    // enque the event
1124    ClientEvent event(client_events::kHandleUnsavedChanges);
1125    module_context::enqueClientEvent(event);
1126 
1127    // wait for method
1128    json::JsonRpcRequest request;
1129    bool succeeded = http_methods::waitForMethod(
1130                         kHandleUnsavedChangesCompleted,
1131                         boost::bind(http_methods::waitForMethodInitFunction,
1132                                     event),
1133                         suspend::disallowSuspend,
1134                         &request);
1135 
1136    if (!succeeded)
1137       return false;
1138 
1139    // read response and return it
1140    bool handled = false;
1141    Error error = json::readParam(request.params, 0, &handled);
1142    if (error)
1143       LOG_ERROR(error);
1144    return handled;
1145 }
1146 
rQuit()1147 void rQuit()
1148 {
1149    if (main_process::wasForked())
1150       return;
1151 
1152    // log to monitor
1153    using namespace monitor;
1154    logExitEvent(Event(kSessionScope, kSessionQuitEvent));
1155 
1156    // notify modules
1157    module_context::events().onQuit();
1158 
1159    // enque a quit event
1160    bool switchProjects;
1161    if (options().switchProjectsWithUrl())
1162    {
1163       switchProjects = !http_methods::nextSessionUrl().empty();
1164    }
1165    else
1166    {
1167       switchProjects = !projects::ProjectsSettings(options().userScratchPath())
1168                               .switchToProjectPath().empty();
1169    }
1170 
1171    // if we aren't switching projects then destroy the active session at cleanup
1172    s_destroySession = !switchProjects;
1173 
1174    json::Object jsonData;
1175    jsonData["switch_projects"] = switchProjects;
1176    jsonData["next_session_url"] = http_methods::nextSessionUrl();
1177    ClientEvent quitEvent(kQuit, jsonData);
1178    rsession::clientEventQueue().add(quitEvent);
1179 }
1180 
1181 // NOTE: this event is never received on windows (because we can't
1182 // override suicide on windows)
rSuicide(const std::string & message)1183 void rSuicide(const std::string& message)
1184 {
1185    if (main_process::wasForked())
1186       return;
1187 
1188    // log to monitor
1189    using namespace monitor;
1190    logExitEvent(Event(kSessionScope, kSessionSuicideEvent));
1191 
1192    // log the error if it was unexpected
1193    if (!message.empty())
1194       LOG_ERROR_MESSAGE("R SUICIDE: " + message);
1195 
1196    // enque suicide event so the client knows
1197    ClientEvent suicideEvent(kSuicide, message);
1198    rsession::clientEventQueue().add(suicideEvent);
1199 }
1200 
1201 // terminate all children of the provided process supervisor
1202 // and then wait a brief period to attempt to reap the child
terminateAllChildren(core::system::ProcessSupervisor * pSupervisor,const ErrorLocation & location)1203 void terminateAllChildren(core::system::ProcessSupervisor* pSupervisor,
1204                           const ErrorLocation& location)
1205 {
1206    // send kill signal
1207    pSupervisor->terminateAll();
1208 
1209    // wait and reap children (but for no longer than 1 second)
1210    if (!pSupervisor->wait(boost::posix_time::milliseconds(10),
1211                           boost::posix_time::milliseconds(1000)))
1212    {
1213       core::log::logWarningMessage(
1214             "Process supervisor did not terminate within 1 second",
1215             location);
1216    }
1217 }
1218 
rCleanup(bool terminatedNormally)1219 void rCleanup(bool terminatedNormally)
1220 {
1221    try
1222    {
1223       // bail completely if we were forked
1224       if (main_process::wasForked())
1225          return;
1226 
1227       // note that we didn't abend
1228       if (terminatedNormally)
1229          rsession::persistentState().setAbend(false);
1230 
1231       // set active session flag indicating we are no longer running
1232       module_context::activeSession().endSession();
1233 
1234       // fire shutdown event to modules
1235       module_context::events().onShutdown(terminatedNormally);
1236 
1237       // destroy session if requested
1238       if (s_destroySession)
1239       {
1240          Error error = module_context::activeSession().destroy();
1241          if (error)
1242             LOG_ERROR(error);
1243 
1244          // fire destroy event to modules
1245          module_context::events().onDestroyed();
1246       }
1247 
1248       // clean up locks
1249       FileLock::cleanUp();
1250 
1251       // stop file monitor (need to do this explicitly as otherwise we can
1252       // run into issues during close where the runtime attempts to clean
1253       // up its data structures at same time that monitor wants to exit)
1254       //
1255       // https://github.com/rstudio/rstudio/issues/5222
1256       system::file_monitor::stop();
1257 
1258       // stop the monitor thread
1259       stopMonitorWorkerThread();
1260 
1261       // cause graceful exit of clientEventService (ensures delivery
1262       // of any pending events prior to process termination). wait a
1263       // very brief interval first to allow the quit or other termination
1264       // related events to get into the queue
1265       boost::this_thread::sleep(boost::posix_time::milliseconds(100));
1266 
1267       // only stop the http services if we are in server mode. in desktop
1268       // mode we had issues with both OSX crashing and with Windows taking
1269       // the full 3 seconds to terminate. the cleanup is kind of a nice
1270       // to have and most important on the server where we delete the
1271       // unix domain socket file so it is no big deal to bypass it
1272       if (rsession::options().programMode() == kSessionProgramModeServer)
1273       {
1274          clientEventService().stop();
1275          httpConnectionListener().stop();
1276       }
1277 
1278       // terminate known child processes
1279       terminateAllChildren(&module_context::processSupervisor(),
1280                            ERROR_LOCATION);
1281 
1282       // terminate unknown child processes
1283       // processes launched by means we do not control
1284       terminateAllChildProcesses();
1285    }
1286    CATCH_UNEXPECTED_EXCEPTION
1287 
1288 }
1289 
rSerialization(int action,const FilePath & targetPath)1290 void rSerialization(int action, const FilePath& targetPath)
1291 {
1292    json::Object serializationActionObject;
1293    serializationActionObject["type"] = action;
1294    if (!targetPath.isEmpty())
1295    {
1296       serializationActionObject["targetPath"] =
1297                            module_context::createAliasedPath(targetPath);
1298    }
1299 
1300    ClientEvent event(kSessionSerialization, serializationActionObject);
1301    rsession::clientEventQueue().add(event);
1302 }
1303 
rRunTests()1304 void rRunTests()
1305 {
1306    // run tests
1307    int status = tests::run();
1308 
1309    // try to clean up session
1310    rCleanup(true);
1311 
1312    // exit if we haven't already
1313    exitEarly(status);
1314 }
1315 
ensureRProfile()1316 void ensureRProfile()
1317 {
1318    // check if we need to create the profile (bail if we don't)
1319    Options& options = rsession::options();
1320    if (!options.createProfile())
1321       return;
1322 
1323    FilePath rProfilePath = options.userHomePath().completePath(".Rprofile");
1324    if (!rProfilePath.exists() && !prefs::userState().autoCreatedProfile())
1325    {
1326       prefs::userState().setAutoCreatedProfile(true);
1327 
1328       std::string p;
1329       p = "# .Rprofile -- commands to execute at the beginning of each R session\n"
1330           "#\n"
1331           "# You can use this file to load packages, set options, etc.\n"
1332           "#\n"
1333           "# NOTE: changes in this file won't be reflected until after you quit\n"
1334           "# and start a new session\n"
1335           "#\n\n";
1336 
1337       Error error = writeStringToFile(rProfilePath, p);
1338       if (error)
1339          LOG_ERROR(error);
1340    }
1341 }
1342 
ensurePublicFolder()1343 void ensurePublicFolder()
1344 {
1345    // check if we need to create the public folder (bail if we don't)
1346    Options& options = rsession::options();
1347    if (!options.createPublicFolder())
1348       return;
1349 
1350    FilePath publicPath = options.userHomePath().completePath("Public");
1351    if (!publicPath.exists())
1352    {
1353       // create directory
1354       Error error = publicPath.ensureDirectory();
1355       if (error)
1356       {
1357          LOG_ERROR(error);
1358          return;
1359       }
1360 
1361       // write notice
1362       boost::format fmt(
1363        "\n"
1364        "Files within your public folder are readable (but not writeable)\n"
1365        "by others. The path for other users to access your public folder is:\n"
1366        "\n"
1367        "  /shared/%1%/\n"
1368        "\n"
1369        "For example, to source a file named \"Utils.R\" from your public\n"
1370        "folder another user would enter the command:\n"
1371        "\n"
1372        "  source(\"/shared/%1%/Utils.R\")\n"
1373        "\n"
1374        "To load a dataset named \"Data.csv\" they would enter the command:\n"
1375        "\n"
1376        "  read.csv(\"/shared/%1%/Data.csv\")\n"
1377        "\n"
1378        "Other users can also browse and open the files available in your\n"
1379        "public folder by:\n"
1380        "\n"
1381        "  1) Selecting the Open File... menu item\n"
1382        "  2) Entering /shared/%1%/ as the file name\n"
1383        "  3) Clicking the Open button (or pressing the Enter key)\n"
1384        "\n"
1385       );
1386       std::string notice = boost::str(fmt % options.userIdentity());
1387 
1388       FilePath noticePath = publicPath.completePath("AboutPublic.txt");
1389       error = writeStringToFile(noticePath, notice);
1390       if (error)
1391          LOG_ERROR(error);
1392    }
1393 }
1394 
ensureRLibsUser(const core::FilePath & userHomePath,const std::string & rLibsUser)1395 void ensureRLibsUser(const core::FilePath& userHomePath,
1396                      const std::string& rLibsUser)
1397 {
1398    FilePath rLibsUserPath = FilePath::resolveAliasedPath(rLibsUser,
1399                                                          userHomePath);
1400    Error error = rLibsUserPath.ensureDirectory();
1401    if (error)
1402       LOG_ERROR(error);
1403 }
1404 
1405 #ifdef __APPLE__
1406 // we now launch our child processes from the desktop using our standard
1407 // process management code which closes all file descriptors thereby
1408 // breaking parent_process_monitor. So on the Mac we use the more simplistic
1409 // approach of polling for ppid == 1. This is fine because we expect that
1410 // the Desktop will _always_ outlive us (it waits for us to exit before
1411 // closing) so anytime it exits before we do it must be a crash). we don't
1412 // call abort() however because we don't want a crash report to occur
detectParentTermination()1413 void detectParentTermination()
1414 {
1415    while(true)
1416    {
1417       boost::this_thread::sleep(boost::posix_time::milliseconds(500));
1418       if (::getppid() == 1)
1419       {
1420          exitEarly(EXIT_FAILURE);
1421       }
1422    }
1423 }
1424 #else
detectParentTermination()1425 void detectParentTermination()
1426 {
1427    using namespace parent_process_monitor;
1428    ParentTermination result = waitForParentTermination();
1429    if (result == ParentTerminationAbnormal)
1430    {
1431       LOG_ERROR_MESSAGE("Parent terminated");
1432 
1433       // we no longer exit with ::abort because it generated unwanted exceptions
1434       // ::_Exit should perform the same functionality (not running destructors and exiting process)
1435       // without generating an exception
1436       std::_Exit(EXIT_FAILURE);
1437    }
1438    else if (result == ParentTerminationNormal)
1439    {
1440       //LOG_ERROR_MESSAGE("Normal terminate");
1441    }
1442    else if (result == ParentTerminationWaitFailure)
1443    {
1444       LOG_ERROR_MESSAGE("waitForParentTermination failed");
1445    }
1446 }
1447 #endif
1448 
saveWorkspaceOption()1449 SA_TYPE saveWorkspaceOption()
1450 {
1451    // convert from internal type to R type
1452    int saveAction = module_context::saveWorkspaceAction();
1453    if (saveAction == rstudio::r::session::kSaveActionSave)
1454       return SA_SAVE;
1455    else if (saveAction == rstudio::r::session::kSaveActionNoSave)
1456       return SA_NOSAVE;
1457    else
1458       return SA_SAVEASK;
1459 }
1460 
restoreWorkspaceOption()1461 bool restoreWorkspaceOption()
1462 {
1463    // check options for session-specific override
1464    if (options().rRestoreWorkspace() == kRestoreWorkspaceNo)
1465       return false;
1466    else if (options().rRestoreWorkspace() == kRestoreWorkspaceYes)
1467       return true;
1468 
1469    // allow project override
1470    const projects::ProjectContext& projContext = projects::projectContext();
1471    if (projContext.hasProject())
1472    {
1473       switch(projContext.config().restoreWorkspace)
1474       {
1475       case r_util::YesValue:
1476          return true;
1477       case r_util::NoValue:
1478          return false;
1479       default:
1480          // fall through
1481          break;
1482       }
1483    }
1484 
1485    // no project override
1486    return prefs::userPrefs().loadWorkspace() ||
1487           !rsession::options().initialEnvironmentFileOverride().isEmpty();
1488 }
1489 
alwaysSaveHistoryOption()1490 bool alwaysSaveHistoryOption()
1491 {
1492    return prefs::userPrefs().alwaysSaveHistory();
1493 }
1494 
getStartupEnvironmentFilePath()1495 FilePath getStartupEnvironmentFilePath()
1496 {
1497    FilePath envFile = rsession::options().initialEnvironmentFileOverride();
1498    if (!envFile.isEmpty())
1499       return envFile;
1500    else
1501       return dirs::rEnvironmentDir().completePath(".RData");
1502 }
1503 
loadCranRepos(const std::string & repos,rstudio::r::session::ROptions * pROptions)1504 void loadCranRepos(const std::string& repos,
1505                    rstudio::r::session::ROptions* pROptions)
1506 {
1507    std::vector<std::string> parts;
1508    boost::split(parts, repos, boost::is_any_of("|"));
1509    pROptions->rCRANSecondary = "";
1510 
1511    std::vector<std::string> secondary;
1512    for (size_t idxParts = 0; idxParts < parts.size() - 1; idxParts += 2)
1513    {
1514       if (string_utils::toLower(parts[idxParts]) == "cran")
1515          pROptions->rCRANUrl = parts[idxParts + 1];
1516       else
1517          secondary.push_back(std::string(parts[idxParts]) + "|" + parts[idxParts + 1]);
1518    }
1519    pROptions->rCRANSecondary = algorithm::join(secondary, "|");
1520 }
1521 
1522 } // anonymous namespace
1523 
1524 
1525 // provide definition methods for rsession::module_context
1526 namespace rstudio {
1527 namespace session {
1528 namespace module_context {
1529 
registerRBrowseUrlHandler(const RBrowseUrlHandler & handler)1530 Error registerRBrowseUrlHandler(const RBrowseUrlHandler& handler)
1531 {
1532    s_rBrowseUrlHandlers.push_back(handler);
1533    return Success();
1534 }
1535 
registerRBrowseFileHandler(const RBrowseFileHandler & handler)1536 Error registerRBrowseFileHandler(const RBrowseFileHandler& handler)
1537 {
1538    s_rBrowseFileHandlers.push_back(handler);
1539    return Success();
1540 }
1541 
showUserPrompt(const UserPrompt & userPrompt)1542 UserPrompt::Response showUserPrompt(const UserPrompt& userPrompt)
1543 {
1544    // enque user prompt event
1545    json::Object obj;
1546    obj["type"] = static_cast<int>(userPrompt.type);
1547    obj["caption"] = userPrompt.caption;
1548    obj["message"] = userPrompt.message;
1549    obj["yesLabel"] = userPrompt.yesLabel;
1550    obj["noLabel"] = userPrompt.noLabel;
1551    obj["yesIsDefault"] = userPrompt.yesIsDefault;
1552    obj["includeCancel"] = userPrompt.includeCancel;
1553    ClientEvent userPromptEvent(client_events::kUserPrompt, obj);
1554    rsession::clientEventQueue().add(userPromptEvent);
1555 
1556    // wait for user_prompt_completed
1557    json::JsonRpcRequest request;
1558    http_methods::waitForMethod(kUserPromptCompleted,
1559                        userPromptEvent,
1560                        suspend::disallowSuspend,
1561                        &request);
1562 
1563    // read the response param
1564    int response;
1565    Error error = json::readParam(request.params, 0, &response);
1566    if (error)
1567    {
1568       LOG_ERROR(error);
1569       return UserPrompt::ResponseCancel;
1570    }
1571 
1572    // return response (don't cast so that we can make sure the integer
1573    // returned matches one of enumerated type values and warn otherwise)
1574    switch (response)
1575    {
1576       case UserPrompt::ResponseYes:
1577          return UserPrompt::ResponseYes;
1578 
1579       case UserPrompt::ResponseNo:
1580          return UserPrompt::ResponseNo;
1581 
1582       case UserPrompt::ResponseCancel:
1583          return UserPrompt::ResponseCancel;
1584 
1585       default:
1586          LOG_WARNING_MESSAGE("Unexpected user prompt response: " +
1587                              boost::lexical_cast<std::string>(response));
1588 
1589          return UserPrompt::ResponseCancel;
1590    };
1591 }
1592 
saveWorkspaceAction()1593 int saveWorkspaceAction()
1594 {
1595    // allow project override
1596    const projects::ProjectContext& projContext = projects::projectContext();
1597    if (projContext.hasProject())
1598    {
1599       switch(projContext.config().saveWorkspace)
1600       {
1601       case r_util::YesValue:
1602          return rstudio::r::session::kSaveActionSave;
1603       case r_util::NoValue:
1604          return rstudio::r::session::kSaveActionNoSave;
1605       case r_util::AskValue:
1606          return rstudio::r::session::kSaveActionAsk;
1607       default:
1608          // fall through
1609          break;
1610       }
1611    }
1612 
1613    // no project override, read from settings
1614    std::string action = prefs::userPrefs().saveWorkspace();
1615    if (action == kSaveWorkspaceAlways)
1616       return rstudio::r::session::kSaveActionSave;
1617    else if (action == kSaveWorkspaceNever)
1618       return rstudio::r::session::kSaveActionNoSave;
1619    else if (action == kSaveWorkspaceAsk)
1620       return rstudio::r::session::kSaveActionAsk;
1621 
1622    return rstudio::r::session::kSaveActionAsk;
1623 }
1624 
syncRSaveAction()1625 void syncRSaveAction()
1626 {
1627    rstudio::r::session::setSaveAction(saveWorkspaceOption());
1628 }
1629 
1630 } // namespace module_context
1631 } // namespace session
1632 } // namespace rstudio
1633 
1634 namespace {
1635 
sessionExitFailure(const core::Error & error,const core::ErrorLocation & location)1636 int sessionExitFailure(const core::Error& error,
1637                        const core::ErrorLocation& location)
1638 {
1639    if (error)
1640       core::log::logError(error, location);
1641    return EXIT_FAILURE;
1642 }
1643 
1644 
ctypeEnvName()1645 std::string ctypeEnvName()
1646 {
1647    if (!core::system::getenv("LC_ALL").empty())
1648       return "LC_ALL";
1649    if (!core::system::getenv("LC_CTYPE").empty())
1650       return "LC_CTYPE";
1651    if (!core::system::getenv("LANG").empty())
1652       return "LANG";
1653    return "LC_CTYPE";
1654 }
1655 
1656 /*
1657 If possible, we want to coerce the character set to UTF-8.
1658 We can't do this by directly calling setlocale because R
1659 will override those settings when it starts up. Instead we
1660 set the corresponding environment variables, which R will
1661 use.
1662 
1663 The exception is Win32, which doesn't allow UTF-8 to be used
1664 as an ANSI codepage.
1665 
1666 Returns false if we tried and failed to set the charset to
1667 UTF-8, either because we didn't recognize the locale string
1668 format or because the system didn't accept our new locale
1669 string.
1670 */
ensureUtf8Charset()1671 bool ensureUtf8Charset()
1672 {
1673 #if _WIN32
1674    return true;
1675 #else
1676    std::string name = ctypeEnvName();
1677    std::string ctype = core::system::getenv(name);
1678 
1679    if (regex_utils::search(ctype, boost::regex("UTF-8$")))
1680       return true;
1681 
1682 #if __APPLE__
1683    // For Mac, we attempt to do the fixup in DesktopMain.cpp. If it didn't
1684    // work, let's not do anything rash here--just let the UTF-8 warning show.
1685    return false;
1686 #else
1687 
1688    std::string newCType;
1689    if (ctype.empty() || ctype == "C" || ctype == "POSIX")
1690    {
1691       newCType = "en_US.UTF-8";
1692    }
1693    else
1694    {
1695       using namespace boost;
1696 
1697       smatch match;
1698       if (regex_utils::match(ctype, match, regex("(\\w+_\\w+)(\\.[^@]+)?(@.+)?")))
1699       {
1700          // Try to replace the charset while keeping everything else the same.
1701          newCType = match[1] + ".UTF-8" + match[3];
1702       }
1703    }
1704 
1705    if (!newCType.empty())
1706    {
1707       if (setlocale(LC_CTYPE, newCType.c_str()))
1708       {
1709          core::system::setenv(name, newCType);
1710          setlocale(LC_CTYPE, "");
1711          return true;
1712       }
1713    }
1714 
1715    return false;
1716 #endif
1717 #endif
1718 }
1719 
1720 // io_service for performing monitor work on the thread
1721 boost::asio::io_service s_monitorIoService;
1722 
monitorWorkerThreadFunc()1723 void monitorWorkerThreadFunc()
1724 {
1725    boost::asio::io_service::work work(s_monitorIoService);
1726    s_monitorIoService.run();
1727 }
1728 
stopMonitorWorkerThread()1729 void stopMonitorWorkerThread()
1730 {
1731    s_monitorIoService.stop();
1732 }
1733 
initMonitorClient()1734 void initMonitorClient()
1735 {
1736    if (!options().getBoolOverlayOption(kLauncherSessionOption))
1737    {
1738       monitor::initializeMonitorClient(core::system::getenv(kMonitorSocketPathEnvVar),
1739                                        options().monitorSharedSecret(),
1740                                        s_monitorIoService);
1741    }
1742    else
1743    {
1744       modules::overlay::initMonitorClient(s_monitorIoService);
1745    }
1746 
1747    // start the monitor work thread
1748    // we handle monitor calls in a separate thread to ensure that calls
1749    // to the monitor (which are likely across machines and thus very expensive)
1750    // do not hamper the liveliness of the session as a whole
1751    core::thread::safeLaunchThread(monitorWorkerThreadFunc);
1752 }
1753 
1754 } // anonymous namespace
1755 
1756 // run session
main(int argc,char * const argv[])1757 int main(int argc, char * const argv[])
1758 {
1759    try
1760    {
1761       // save fallback library path
1762       s_fallbackLibraryPath = core::system::getenv("RSTUDIO_FALLBACK_LIBRARY_PATH");
1763 
1764       // sleep on startup if requested (mainly for debugging)
1765       std::string sleepOnStartup = core::system::getenv("RSTUDIO_SESSION_SLEEP_ON_STARTUP");
1766       if (!sleepOnStartup.empty())
1767       {
1768          int sleepDuration = core::safe_convert::stringTo<int>(sleepOnStartup, 0);
1769          if (sleepDuration > 0)
1770          {
1771             boost::this_thread::sleep(boost::posix_time::seconds(sleepDuration));
1772          }
1773       }
1774 
1775       // terminate immediately with given exit code (for testing/debugging)
1776       std::string exitOnStartup = core::system::getenv("RSTUDIO_SESSION_EXIT_ON_STARTUP");
1777       if (!exitOnStartup.empty())
1778       {
1779          int exitCode = core::safe_convert::stringTo<int>(exitOnStartup, EXIT_FAILURE);
1780 
1781          std::cerr << "RSession terminating with exit code " << exitCode << " as requested.\n";
1782          std::cout << "RSession will now exit.\n";
1783          return core::safe_convert::stringTo<int>(exitOnStartup, EXIT_FAILURE);
1784       }
1785 
1786       // initialize log so we capture all errors including ones which occur
1787       // reading the config file (if we are in desktop mode then the log
1788       // will get re-initialized below)
1789       std::string programId = "rsession-" + core::system::username();
1790       core::log::setProgramId(programId);
1791       core::system::initializeLog(programId, core::log::LogLevel::WARN);
1792 
1793       // ignore SIGPIPE
1794       Error error = core::system::ignoreSignal(core::system::SigPipe);
1795       if (error)
1796          LOG_ERROR(error);
1797 
1798       // move to own process group
1799 #ifndef _WIN32
1800 #if defined(__FreeBSD__)
1801       ::setpgrp(0, 0);
1802 #else
1803       ::setpgrp();
1804 #endif
1805 #endif
1806 
1807       // get main thread id (used to distinguish forks which occur
1808       // from the main thread vs. child threads)
1809       main_process::initThreadId();
1810 
1811       // ensure LANG and UTF-8 character set
1812 #ifndef _WIN32
1813       r_util::ensureLang();
1814 #endif
1815       s_printCharsetWarning = !ensureUtf8Charset();
1816 
1817       // remove DYLD_INSERT_LIBRARIES variable (injected on macOS Desktop
1818       // to support restrictions with hardened runtime)
1819 #ifdef __APPLE__
1820       core::system::unsetenv("DYLD_INSERT_LIBRARIES");
1821 #endif
1822 
1823       // fix up HOME / R_USER environment variables
1824       // (some users on Windows report these having trailing
1825       // slashes, which confuses a number of RStudio routines)
1826       boost::regex reTrailing("[/\\\\]+$");
1827       for (const std::string& envvar : {"HOME", "R_USER"})
1828       {
1829          std::string oldVal = core::system::getenv(envvar);
1830          if (!oldVal.empty())
1831          {
1832             std::string newVal = boost::regex_replace(oldVal, reTrailing, "");
1833             if (oldVal != newVal)
1834                core::system::setenv(envvar, newVal);
1835          }
1836       }
1837 
1838       // read program options
1839       std::ostringstream osWarnings;
1840       Options& options = rsession::options();
1841       ProgramStatus status = options.read(argc, argv, osWarnings);
1842       std::string optionsWarnings = osWarnings.str();
1843 
1844       if (!optionsWarnings.empty())
1845          program_options::reportWarnings(optionsWarnings, ERROR_LOCATION);
1846 
1847       if (status.exit())
1848          return status.exitCode();
1849 
1850       // print version if requested
1851       if (options.version())
1852       {
1853          std::string gitCommit(RSTUDIO_GIT_COMMIT);
1854          std::cout << RSTUDIO_VERSION ", \"" RSTUDIO_RELEASE_NAME "\" "
1855                       "(" << gitCommit.substr(0, 8) << ", " RSTUDIO_BUILD_DATE ") "
1856                       "for " RSTUDIO_PACKAGE_OS << std::endl;
1857          return 0;
1858       }
1859 
1860       // convenience flags for server and desktop mode
1861       bool desktopMode = options.programMode() == kSessionProgramModeDesktop;
1862       bool serverMode = options.programMode() == kSessionProgramModeServer;
1863 
1864       // catch unhandled exceptions
1865       core::crash_handler::ProgramMode mode = serverMode ?
1866                core::crash_handler::ProgramMode::Server :
1867                core::crash_handler::ProgramMode::Desktop;
1868       error = core::crash_handler::initialize(mode);
1869       if (error)
1870          LOG_ERROR(error);
1871 
1872       // set program mode environment variable so any child processes
1873       // (like rpostback) can determine what the program mode is
1874       core::system::setenv(kRStudioProgramMode, options.programMode());
1875 
1876       // reflect stderr logging
1877       if (options.logStderr())
1878          log::addLogDestination(
1879             std::shared_ptr<log::ILogDestination>(new log::StderrLogDestination(
1880                                                      core::system::generateShortenedUuid(),
1881                                                      log::LogLevel::WARN,
1882                                                      log::LogMessageFormatType::PRETTY)));
1883 
1884       // initialize monitor but stop its thread on exit
1885       initMonitorClient();
1886       BOOST_SCOPE_EXIT(void)
1887       {
1888          stopMonitorWorkerThread();
1889       }
1890       BOOST_SCOPE_EXIT_END
1891 
1892       // register monitor log writer (but not in standalone or verify installation mode)
1893       if (!options.standalone() && !options.verifyInstallation())
1894       {
1895          core::log::addLogDestination(
1896             monitor::client().createLogDestination(core::system::generateShortenedUuid(), log::LogLevel::WARN, options.programIdentity()));
1897       }
1898 
1899       // initialize file lock config
1900       FileLock::initialize(desktopMode ? FileLock::LOCKTYPE_ADVISORY : FileLock::LOCKTYPE_LINKBASED);
1901 
1902       // re-initialize log for desktop mode
1903       if (desktopMode)
1904       {
1905          if (options.verifyInstallation())
1906          {
1907             core::system::initializeStderrLog(options.programIdentity(),
1908                                 core::log::LogLevel::WARN);
1909          }
1910          else
1911          {
1912             core::system::initializeLog(options.programIdentity(),
1913                                         core::log::LogLevel::WARN,
1914                                         options.userLogPath());
1915          }
1916       }
1917 
1918       // initialize overlay
1919       error = rsession::overlay::initialize();
1920       if (error)
1921          return sessionExitFailure(error, ERROR_LOCATION);
1922 
1923       // set the rstudio environment variable so code can check for
1924       // whether rstudio is running
1925       core::system::setenv("RSTUDIO", "1");
1926 
1927       // Mirror the R getOptions("width") value in an environment variable
1928       core::system::setenv("RSTUDIO_CONSOLE_WIDTH",
1929                safe_convert::numberToString(rstudio::r::options::kDefaultWidth));
1930 
1931       // set the rstudio user identity environment variable (can differ from
1932       // username in debug configurations). this is provided so that
1933       // rpostback knows what local stream to connect back to
1934       core::system::setenv(kRStudioUserIdentity, options.userIdentity());
1935       if (desktopMode)
1936       {
1937          // do the same for port number, for rpostback in rdesktop configs
1938          core::system::setenv(kRSessionPortNumber, options.wwwPort());
1939       }
1940 
1941       // provide session stream for postback in server mode
1942       if (serverMode)
1943       {
1944          r_util::SessionContext context = options.sessionContext();
1945          std::string stream = r_util::sessionContextFile(context);
1946          core::system::setenv(kRStudioSessionStream, stream);
1947       }
1948 
1949       // set the standalone port if we are running in standalone mode
1950       if (options.standalone())
1951       {
1952          core::system::setenv(kRSessionStandalonePortNumber, options.wwwPort());
1953       }
1954 
1955       // ensure we aren't being started as a low (priviliged) account
1956       if (serverMode &&
1957           !options.verifyInstallation() &&
1958           core::system::currentUserIsPrivilleged(options.authMinimumUserId()))
1959       {
1960          Error error = systemError(boost::system::errc::permission_denied,
1961                                    ERROR_LOCATION);
1962          boost::format fmt("User '%1%' has id %2%, which is lower than the "
1963                            "minimum user id of %3% (this is controlled by "
1964                            "the the auth-minimum-user-id rserver option)");
1965          std::string msg = boost::str(fmt % core::system::username()
1966                                           % core::system::effectiveUserId()
1967                                           % options.authMinimumUserId());
1968          error.addProperty("message", msg);
1969          return sessionExitFailure(error, ERROR_LOCATION);
1970       }
1971 
1972 #ifdef RSTUDIO_SERVER
1973       if (serverMode)
1974       {
1975          Error error = core::system::crypto::rsaInit();
1976          if (error)
1977             LOG_ERROR(error);
1978       }
1979 #endif
1980 
1981       // start the file monitor
1982       core::system::file_monitor::initialize();
1983 
1984       // initialize client event queue. this must be done very early
1985       // in main so that any other code which needs to enque an event
1986       // has access to the queue
1987       rsession::initializeClientEventQueue();
1988 
1989       // detect parent termination
1990       if (desktopMode)
1991          core::thread::safeLaunchThread(detectParentTermination);
1992 
1993       // set the rpostback absolute path
1994       FilePath rpostback = options.rpostbackPath()
1995                                   .getParent().getParent()
1996                                   .completeChildPath("rpostback");
1997       core::system::setenv(
1998             "RS_RPOSTBACK_PATH",
1999             string_utils::utf8ToSystem(rpostback.getAbsolutePath()));
2000 
2001       std::string firstProjectPath = "";
2002       if (!options.verifyInstallation())
2003       {
2004          // Validate the config and data directories.
2005          core::system::xdg::verifyUserDirs();
2006 
2007          // determine if this is a new user and get the first project path if so
2008          bool newUser = false;
2009 
2010          FilePath userScratchPath = options.userScratchPath();
2011          if (userScratchPath.exists())
2012          {
2013             // if the lists directory has not yet been created,
2014             // this is a new user
2015             FilePath listsPath = userScratchPath.completeChildPath("monitored/lists");
2016             if (!listsPath.exists())
2017                newUser = true;
2018          }
2019          else
2020          {
2021             // create the scratch path
2022             error = userScratchPath.ensureDirectory();
2023             if (error)
2024                return sessionExitFailure(error, ERROR_LOCATION);
2025 
2026             newUser = true;
2027          }
2028 
2029          if (newUser)
2030          {
2031             // this is a brand new user
2032             // check to see if there is a first project template
2033             if (!options.firstProjectTemplatePath().empty())
2034             {
2035                // copy the project template to the user's home dir
2036                FilePath templatePath = FilePath(options.firstProjectTemplatePath());
2037                if (templatePath.exists())
2038                {
2039                   error = templatePath.copyDirectoryRecursive(
2040                      options.userHomePath().completeChildPath(
2041                         templatePath.getFilename()));
2042                   if (error)
2043                      LOG_ERROR(error);
2044                   else
2045                   {
2046                      FilePath firstProjPath = options.userHomePath().completeChildPath(templatePath.getFilename())
2047                                                      .completeChildPath(templatePath.getFilename() + ".Rproj");
2048                      if (firstProjPath.exists())
2049                         firstProjectPath = firstProjPath.getAbsolutePath();
2050                      else
2051                         LOG_WARNING_MESSAGE("Could not find first project path " + firstProjPath.getAbsolutePath() +
2052                                             ". Please ensure the template contains an Rproj file.");
2053                   }
2054                }
2055             }
2056          }
2057       }
2058 
2059       // initialize user preferences and state
2060       error = prefs::initializePrefs();
2061       if (error)
2062          return sessionExitFailure(error, ERROR_LOCATION);
2063       error = prefs::initializeState();
2064       if (error)
2065          return sessionExitFailure(error, ERROR_LOCATION);
2066 
2067       // startup projects -- must be after userSettings is initialized
2068       // but before persistentState and setting working directory
2069       projects::startup(firstProjectPath);
2070 
2071       // initialize persistent state
2072       error = rsession::persistentState().initialize();
2073       if (error)
2074          return sessionExitFailure(error, ERROR_LOCATION);
2075 
2076       // set working directory
2077       FilePath workingDir = dirs::getInitialWorkingDirectory();
2078       error = workingDir.makeCurrentPath();
2079       if (error)
2080          return sessionExitFailure(error, ERROR_LOCATION);
2081 
2082       // override the active session's working directory
2083       // it is created with the default value of ~, so if our session options
2084       // have specified that a different directory should be used, we should
2085       // persist the value to the session state as soon as possible
2086       module_context::activeSession().setWorkingDir(workingDir.getAbsolutePath());
2087 
2088       // start http connection listener
2089       error = waitWithTimeout(
2090             http_methods::startHttpConnectionListenerWithTimeout, 0, 100, 1);
2091       if (error)
2092          return sessionExitFailure(error, ERROR_LOCATION);
2093 
2094       // start session proxy to route traffic to localhost-listening applications (like Shiny)
2095       // this has to come after regular overlay initialization as it depends on persistent state
2096       error = overlay::initializeSessionProxy();
2097       if (error)
2098          return sessionExitFailure(error, ERROR_LOCATION);
2099 
2100       // run optional preflight script -- needs to be after the http listeners
2101       // so the proxy server sees that we have startup up
2102       error = runPreflightScript();
2103       if (error)
2104          return sessionExitFailure(error, ERROR_LOCATION);
2105 
2106       // server-only user file/directory initialization
2107       if (serverMode)
2108       {
2109          // r profile file
2110          ensureRProfile();
2111 
2112          // public folder
2113          ensurePublicFolder();
2114 
2115          // ensure the user has an R library directory
2116          if (!options.rLibsUser().empty())
2117             ensureRLibsUser(options.userHomePath(), options.rLibsUser());
2118       }
2119 
2120       // we've gotten through startup so let's log a start event
2121       using namespace monitor;
2122       client().logEvent(Event(kSessionScope, kSessionStartEvent));
2123 
2124       // install home and doc dir overrides if requested (for debugger mode)
2125       if (!options.rHomeDirOverride().empty())
2126          core::system::setenv("R_HOME", options.rHomeDirOverride());
2127       if (!options.rDocDirOverride().empty())
2128          core::system::setenv("R_DOC_DIR", options.rDocDirOverride());
2129 
2130       // set ANSI support environment variable before running code from .Rprofile
2131       modules::console::syncConsoleColorEnv();
2132 
2133       // r options
2134       rstudio::r::session::ROptions rOptions;
2135       rOptions.userHomePath = options.userHomePath();
2136       rOptions.userScratchPath = options.userScratchPath();
2137       rOptions.scopedScratchPath = module_context::scopedScratchPath();
2138       rOptions.sessionScratchPath = module_context::sessionScratchPath();
2139       rOptions.logPath = options.userLogPath();
2140       rOptions.sessionPort = options.wwwPort();
2141       rOptions.startupEnvironmentFilePath = getStartupEnvironmentFilePath();
2142       rOptions.rEnvironmentDir = boost::bind(dirs::rEnvironmentDir);
2143       rOptions.rHistoryDir = boost::bind(dirs::rHistoryDir);
2144       rOptions.alwaysSaveHistory = boost::bind(alwaysSaveHistoryOption);
2145       rOptions.rSourcePath = options.coreRSourcePath();
2146       if (!desktopMode) // ignore r-libs-user in desktop mode
2147          rOptions.rLibsUser = options.rLibsUser();
2148 
2149       // name of the source of the CRAN repo value (for logging)
2150       std::string source;
2151 
2152       // CRAN repos configuration follows; order of precedence is:
2153       //
2154       // 1. The user's personal preferences file (rstudio-prefs.json), if CRAN repo editing is
2155       //    permitted by policy
2156       // 2. The system-wide preferences file (rstudio-prefs.json)
2157       // 3. The repo settings specified in the loaded version of R
2158       // 4. The session's repo settings (in rsession.conf/repos.conf)
2159       // 5. The server's repo settings
2160       // 6. The default repo settings from the preferences schema (user-prefs-schema.json)
2161       // 7. If all else fails, cran.rstudio.com
2162       std::string layerName;
2163       auto val = prefs::userPrefs().readValue(kCranMirror, &layerName);
2164       if (val && ((options.allowCRANReposEdit() && layerName == kUserPrefsUserLayer) ||
2165                   layerName == kUserPrefsSystemLayer))
2166       {
2167          // If we got here, either (a) there is a user value and the user is allowed to set their
2168          // own CRAN repos, or (b) there's a system value, which we always respect
2169          rOptions.rCRANUrl = prefs::userPrefs().getCRANMirror().url;
2170          rOptions.rCRANSecondary = prefs::userPrefs().getCRANMirror().secondary;
2171          source = layerName + "-level preference file";
2172       }
2173       else if (!core::system::getenv("RSTUDIO_R_REPO").empty())
2174       {
2175          // repo was specified in the r version
2176          std::string repo = core::system::getenv("RSTUDIO_R_REPO");
2177 
2178          // the repo can either be a repos.conf-style file, or a URL
2179          FilePath reposFile(repo);
2180          if (reposFile.exists())
2181          {
2182             std::string reposConfig = Options::parseReposConfig(reposFile);
2183             loadCranRepos(reposConfig, &rOptions);
2184             source = reposFile.getAbsolutePath() + " via RSTUDIO_R_REPO environment variable";
2185          }
2186          else
2187          {
2188             rOptions.rCRANUrl = repo;
2189             source = "RSTUDIO_R_REPO environment variable";
2190          }
2191       }
2192       else if (!options.rCRANMultipleRepos().empty())
2193       {
2194          // repo was specified in a repos file
2195          loadCranRepos(options.rCRANMultipleRepos(), &rOptions);
2196          source = "repos file";
2197       }
2198       else if (!options.rCRANUrl().empty())
2199       {
2200          // Global server option
2201          rOptions.rCRANUrl = options.rCRANUrl();
2202          source = "global session option";
2203       }
2204       else if (val && layerName == kUserPrefsDefaultLayer)
2205       {
2206          // If we found defaults in the prefs schema, use them.
2207          rOptions.rCRANUrl = prefs::userPrefs().getCRANMirror().url;
2208          rOptions.rCRANSecondary = prefs::userPrefs().getCRANMirror().secondary;
2209          source = "preference defaults";
2210       }
2211       else
2212       {
2213          // Hard-coded repo of last resort so we don't wind up without a repo setting (users will
2214          // not be able to install packages without one)
2215          rOptions.rCRANUrl = "https://cran.rstudio.com/";
2216          source = "hard-coded default";
2217       }
2218 
2219       LOG_INFO_MESSAGE("Set CRAN URL for session to '" + rOptions.rCRANUrl + "' (source: " +
2220             source + ")");
2221 
2222       rOptions.useInternet2 = prefs::userPrefs().useInternet2();
2223       rOptions.rCompatibleGraphicsEngineVersion =
2224                               options.rCompatibleGraphicsEngineVersion();
2225       rOptions.serverMode = serverMode;
2226       rOptions.autoReloadSource = options.autoReloadSource();
2227       rOptions.restoreWorkspace = restoreWorkspaceOption();
2228       rOptions.saveWorkspace = saveWorkspaceOption();
2229       if (options.rRestoreWorkspace() != kRestoreWorkspaceDefault)
2230       {
2231          // if workspace restore is set to a non-default option, apply it to
2232          // environment restoration as well (the intent of the option is usually
2233          // to recover a session with an overhelming or problematic environment)
2234          rOptions.restoreEnvironmentOnResume =
2235             options.rRestoreWorkspace() == kRestoreWorkspaceYes;
2236       }
2237       rOptions.disableRProfileOnStart = disableExecuteRprofile();
2238       rOptions.rProfileOnResume = serverMode &&
2239                                   prefs::userPrefs().runRprofileOnResume();
2240       rOptions.packratEnabled = persistentState().settings().getBool("packratEnabled");
2241       rOptions.sessionScope = options.sessionScope();
2242       rOptions.runScript = options.runScript();
2243       rOptions.suspendOnIncompleteStatement = options.suspendOnIncompleteStatement();
2244 
2245       // r callbacks
2246       rstudio::r::session::RCallbacks rCallbacks;
2247       rCallbacks.init = rInit;
2248       rCallbacks.initComplete = rInitComplete;
2249       rCallbacks.consoleRead = console_input::rConsoleRead;
2250       rCallbacks.editFile = rEditFile;
2251       rCallbacks.showFile = rShowFile;
2252       rCallbacks.chooseFile = rChooseFile;
2253       rCallbacks.busy = rBusy;
2254       rCallbacks.consoleWrite = rConsoleWrite;
2255       rCallbacks.consoleHistoryReset = rConsoleHistoryReset;
2256       rCallbacks.locator = rLocator;
2257       rCallbacks.deferredInit = rDeferredInit;
2258       rCallbacks.suspended = rSuspended;
2259       rCallbacks.resumed = rResumed;
2260       rCallbacks.handleUnsavedChanges = rHandleUnsavedChanges;
2261       rCallbacks.quit = rQuit;
2262       rCallbacks.suicide = rSuicide;
2263       rCallbacks.cleanup = rCleanup;
2264       rCallbacks.browseURL = rBrowseURL;
2265       rCallbacks.browseFile = rBrowseFile;
2266       rCallbacks.showHelp = rShowHelp;
2267       rCallbacks.showMessage = rShowMessage;
2268       rCallbacks.serialization = rSerialization;
2269 
2270       // set test callback if enabled
2271       if (options.runTests())
2272          rCallbacks.runTests = rRunTests;
2273 
2274       // run r (does not return, terminates process using exit)
2275       error = rstudio::r::session::run(rOptions, rCallbacks);
2276       if (error)
2277       {
2278           // this is logically equivilant to R_Suicide
2279           logExitEvent(Event(kSessionScope, kSessionSuicideEvent));
2280 
2281           // return failure
2282           return sessionExitFailure(error, ERROR_LOCATION);
2283       }
2284 
2285       // return success for good form
2286       return EXIT_SUCCESS;
2287    }
2288    CATCH_UNEXPECTED_EXCEPTION
2289 
2290    // if we got this far we had an unexpected exception
2291    return EXIT_FAILURE;
2292 }
2293