1 /*
2  * SessionConsoleProcessInfo.hpp
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 #ifndef SESSION_CONSOLE_PROCESS_INFO_HPP
16 #define SESSION_CONSOLE_PROCESS_INFO_HPP
17 
18 #include <boost/circular_buffer.hpp>
19 #include <boost/enable_shared_from_this.hpp>
20 
21 #include <shared_core/FilePath.hpp>
22 #include <core/json/JsonRpc.hpp>
23 #include <core/system/Process.hpp>
24 #include <core/system/Types.hpp>
25 
26 #include <session/SessionTerminalShell.hpp>
27 
28 namespace rstudio {
29 namespace core {
30    class Error;
31 }
32 }
33 
34 namespace rstudio {
35 namespace session {
36 namespace console_process {
37 
38 enum InteractionMode
39 {
40    InteractionNever = 0,
41    InteractionPossible = 1,
42    InteractionAlways = 2
43 };
44 
45 enum ChannelMode
46 {
47    Rpc = 0,
48    Websocket = 1,
49 };
50 
51 enum AutoCloseMode
52 {
53    DefaultAutoClose = 0, // obey user preference
54    AlwaysAutoClose = 1, // always auto-close
55    NeverAutoClose = 2, // never auto-close
56    CleanExitAutoClose = 3, // auto-close only if shell returned zero exit code
57 };
58 
59 enum SerializationMode
60 {
61    ClientSerialization = 0, // serialize for front-end client
62    PersistentSerialization = 1  // serialize for persistent storage
63 };
64 
65 extern const int kDefaultMaxOutputLines;
66 extern const int kDefaultTerminalMaxOutputLines;
67 extern const int kNoTerminal;
68 extern const int kNewTerminal;
69 extern const size_t kOutputBufferSize;
70 
71 // ConsoleProcess metadata that is persisted, and sent to the client on
72 // create/reconnect
73 class ConsoleProcessInfo : boost::noncopyable,
74                            public boost::enable_shared_from_this<ConsoleProcessInfo>
75 {
76 private:
77    // This constructor is only for resurrecting orphaned processes (i.e. for
78    // suspend/resume scenarios)
79    ConsoleProcessInfo();
80 
81 public:
82    // constructor for interactive terminals
83    ConsoleProcessInfo(
84          const std::string& caption,
85          const std::string& title,
86          const std::string& handle,
87          int terminalSequence,
88          TerminalShell::ShellType shellType,
89          bool altBufferActive,
90          const core::FilePath& cwd,
91          int cols, int rows, bool zombie, bool trackEnv);
92 
93    // constructor for non-terminals
94    ConsoleProcessInfo(
95          const std::string& caption,
96          InteractionMode mode,
97          int maxOutputLines = kDefaultMaxOutputLines);
98 
99    virtual ~ConsoleProcessInfo() = default;
100 
101    // Caption is shown on terminal tabs, e.g. Terminal 1
setCaption(std::string & caption)102    void setCaption(std::string& caption) { caption_ = caption; }
getCaption() const103    std::string getCaption() const { return caption_; }
104 
105    // Title is set by terminal escape sequence, typically to show current dir
setTitle(std::string & title)106    void setTitle(std::string& title) { title_ = title; }
getTitle() const107    std::string getTitle() const { return title_; }
108 
109    // Handle client uses to refer to this process
110    void ensureHandle();
getHandle() const111    std::string getHandle() const { return handle_; }
112 
113    // Sequence number of the associated terminal; used to control display
114    // order of terminal tabs; constant 'kNoTerminal' indicates a non-terminal
setTerminalSequence(int sequence)115    void setTerminalSequence(int sequence) { terminalSequence_ = sequence; }
getTerminalSequence() const116    int getTerminalSequence() const { return terminalSequence_; }
117 
118    // Whether a ConsoleProcess object should start a new process on resume after
119    // its process has been killed by a suspend.
setAllowRestart(bool allowRestart)120    void setAllowRestart(bool allowRestart) { allowRestart_ = allowRestart; }
getAllowRestart() const121    bool getAllowRestart() const { return allowRestart_; }
122 
setInteractionMode(InteractionMode mode)123    void setInteractionMode(InteractionMode mode) { interactionMode_ = mode; }
getInteractionMode() const124    InteractionMode getInteractionMode() const { return interactionMode_; }
125 
setMaxOutputLines(int maxOutputLines)126    void setMaxOutputLines(int maxOutputLines) { maxOutputLines_ = maxOutputLines; }
getMaxOutputLines() const127    int getMaxOutputLines() const { return maxOutputLines_; }
128 
setShowOnOutput(bool showOnOutput)129    void setShowOnOutput(bool showOnOutput) { showOnOutput_ = showOnOutput; }
getShowOnOutput() const130    bool getShowOnOutput() const { return showOnOutput_; }
131 
132    // Buffer output in case client disconnects/reconnects and needs
133    // to recover some history.
134    void appendToOutputBuffer(const std::string &str);
135    void appendToOutputBuffer(char ch);
136    std::string bufferedOutput() const;
137    std::string getSavedBufferChunk(int chunk, bool* pMoreAvailable) const;
138    std::string getFullSavedBuffer() const;
139    int getBufferLineCount() const;
140    void deleteLogFile(bool lastLineOnly = false) const;
141    void deleteEnvFile() const;
142    void saveConsoleEnvironment(const core::system::Options& environment);
143 
144    // Has the process exited, and what was the exit code?
145    void setExitCode(int exitCode);
getExitCode() const146    boost::optional<int> getExitCode() const { return exitCode_; }
147    void resetExitCode();
148 
149    // Does this process have child processes?
setHasChildProcs(bool hasChildProcs)150    void setHasChildProcs(bool hasChildProcs) { childProcs_ = hasChildProcs; }
getHasChildProcs() const151    bool getHasChildProcs() const { return childProcs_; }
152 
153    // What type of shell is this child process running in?
getShellType() const154    TerminalShell::ShellType getShellType() const { return shellType_; }
setShellType(TerminalShell::ShellType type)155    void setShellType(TerminalShell::ShellType type) { shellType_ = type; }
156 
157    // Type of channel for communicating input/output with client
getChannelMode() const158    ChannelMode getChannelMode() const { return channelMode_; }
159 
160    // Mode-dependent identifier for channel
getChannelId() const161    std::string getChannelId() const { return channelId_; }
162 
setChannelMode(ChannelMode mode,const std::string & channelId)163    void setChannelMode(ChannelMode mode, const std::string& channelId)
164    {
165       channelMode_ = mode;
166       channelId_ = channelId;
167    }
168 
169    // Is terminal showing alt-buffer (a full-screen ncurses program)?
setAltBufferActive(bool altBufferActive)170    void setAltBufferActive(bool altBufferActive) { altBufferActive_ = altBufferActive; }
getAltBufferActive() const171    bool getAltBufferActive() const { return altBufferActive_; }
172 
173    // Last-known current working directory
setCwd(const core::FilePath & cwd)174    void setCwd(const core::FilePath& cwd) { cwd_ = cwd; }
getCwd() const175    core::FilePath getCwd() const { return cwd_; }
176 
177    // Last-known terminal dimensions
setCols(int cols)178    void setCols(int cols) { cols_ = cols; }
setRows(int rows)179    void setRows(int rows) { rows_ = rows; }
getCols() const180    int getCols() const { return cols_; }
getRows() const181    int getRows() const { return rows_; }
182 
183    // Was terminal session restarted?
setRestarted(bool restarted)184    void setRestarted(bool restarted) { restarted_ = restarted; }
getRestarted() const185    bool getRestarted() const { return restarted_; }
186 
187    // Close terminal session after process exits?
setAutoClose(AutoCloseMode autoClose)188    void setAutoClose(AutoCloseMode autoClose) { autoClose_ = autoClose; }
getAutoClose() const189    AutoCloseMode getAutoClose() const { return autoClose_; }
190 
191    // Is terminal session a zombie (process exited but keeping buffer)
setZombie(bool zombie)192    void setZombie(bool zombie) { zombie_ = zombie; }
getZombie() const193    bool getZombie() const { return zombie_; }
194 
195    // Track terminal session's environment?
setTrackEnv(bool trackEnv)196    void setTrackEnv(bool trackEnv) { trackEnv_ = trackEnv; }
getTrackEnv() const197    bool getTrackEnv() const { return trackEnv_; }
198 
199    core::json::Object toJson(SerializationMode serialMode) const;
200    static boost::shared_ptr<ConsoleProcessInfo> fromJson(const core::json::Object& obj);
201 
202    static std::string loadConsoleProcessMetadata();
203    static void deleteOrphanedLogs(bool (*validHandle)(const std::string&));
204    static void saveConsoleProcesses(const std::string& metadata);
205    static void loadConsoleEnvironment(const std::string& handle, core::system::Options* pEnv);
206 
207    static AutoCloseMode closeModeFromPref(std::string prefValue);
208 
209 private:
210    std::string caption_;
211    std::string title_;
212    std::string handle_;
213    int terminalSequence_ = kNoTerminal;
214    bool allowRestart_ = false;
215    InteractionMode interactionMode_ = InteractionNever;
216    int maxOutputLines_ = kDefaultMaxOutputLines;
217    bool showOnOutput_ = false;
218    boost::circular_buffer<char> outputBuffer_ {kOutputBufferSize};
219    boost::optional<int> exitCode_;
220 #ifdef _WIN32
221    bool childProcs_ = false; // child process detection not supported on Windows
222 #else
223    bool childProcs_ = true;
224 #endif
225    bool altBufferActive_ = false;
226    TerminalShell::ShellType shellType_ = TerminalShell::ShellType::Default;
227    ChannelMode channelMode_ = Rpc;
228    std::string channelId_;
229    core::FilePath cwd_;
230    int cols_ = core::system::kDefaultCols;
231    int rows_ = core::system::kDefaultRows;
232    bool restarted_ = false;
233    AutoCloseMode autoClose_ = DefaultAutoClose;
234    bool zombie_ = false;
235    bool trackEnv_ = false;
236 };
237 
238 } // namespace console_process
239 } // namespace session
240 } // namespace rstudio
241 
242 #endif // SESSION_CONSOLE_PROCESS_INFO_HPP
243