1 /*
2  * System.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 
16 #ifndef CORE_SYSTEM_SYSTEM_HPP
17 #define CORE_SYSTEM_SYSTEM_HPP
18 
19 
20 #if defined(_WIN32)
21 #include <windows.h>
22 typedef DWORD PidType;
23 #else  // UNIX
24 #include <sys/types.h>
25 #include <sys/resource.h>
26 
27 #include <shared_core/system/PosixSystem.hpp>
28 
29 typedef pid_t PidType;
30 typedef uid_t UidType;
31 #endif
32 
33 #include <string>
34 #include <vector>
35 #include <map>
36 #include <iosfwd>
37 
38 #include <boost/utility.hpp>
39 #include <boost/shared_ptr.hpp>
40 #include <boost/function.hpp>
41 #include <boost/date_time.hpp>
42 
43 #include <core/Log.hpp>
44 #include <shared_core/Error.hpp>
45 #include <shared_core/FilePath.hpp>
46 
47 #include <core/system/Types.hpp>
48 
49 namespace rstudio {
50 namespace core {
51 
52 class FileInfo;
53 class FilePath;
54 
55 namespace system {
56 
57 // portable realPath
58 Error realPath(const FilePath& filePath, FilePath* pRealPath);
59 Error realPath(const std::string& path, FilePath* pRealPath);
60 bool realPathsEqual(const FilePath& a, const FilePath& b);
61 
62 void addToSystemPath(const FilePath& path, bool prepend = false);
63 
64 Error findProgramOnPath(const std::string& program,
65                         core::FilePath* pProgramPath);
66 
67 #ifndef _WIN32
68 Error closeAllFileDescriptors();
69 Error closeNonStdFileDescriptors();
70 
71 // retrieves a list of file descriptors opened by the specified child process
72 // and sends each fd to the child via pipe so that the child can signal-safely
73 // close the fds
74 Error closeChildFileDescriptorsFrom(pid_t childPid, int pipeFd, uint32_t fdStart);
75 
76 // gets the list of open fds for the current process
77 Error getOpenFds(std::vector<uint32_t>* pFds);
78 
79 // gets the list of open fds for the specified process
80 Error getOpenFds(pid_t pid, std::vector<uint32_t>* pFds);
81 
82 namespace signal_safe {
83 
84 // thread and signal-safe version of closeNonStdFileDescriptors()
85 void closeNonStdFileDescriptors(rlim_t fdLimit);
86 
87 // close file descriptors given to us by our parent process
88 // must be paired with a call to closeChildFileDescriptorsFrom in the parent
89 void closeFileDescriptorsFromParent(int pipeFd, uint32_t fdStart, rlim_t fdLimit);
90 
91 int clearSignalMask();
92 
93 } // namespace signal_safe
94 
95 void closeStdFileDescriptors();
96 void attachStdFileDescriptorsToDevNull();
97 void setStandardStreamsToDevNull();
98 
99 
100 // Handles EINTR retrying and error logging. Only for use with functions
101 // that return -1 on error and set errno.
102 template <typename T>
safePosixCall(const boost::function<T ()> & func,const ErrorLocation & location)103 void safePosixCall(const boost::function<T()>& func,
104                           const ErrorLocation& location)
105 {
106    Error error = posix::posixCall<T>(func, location, nullptr);
107    if (error)
108       LOG_ERROR(error);
109 }
110 
111 #endif
112 
113 #ifdef _WIN32
114 
115 // Is 64-bit Windows?
116 bool isWin64();
117 
118 // Is calling process 64-bit?
119 bool isCurrentProcessWin64();
120 
121 bool isWin7OrLater();
122 Error makeFileHidden(const FilePath& path);
123 Error copyMetafileToClipboard(const FilePath& path);
124 void ensureLongPath(FilePath* pFilePath);
125 Error expandEnvironmentVariables(std::string value, std::string* pResult);
126 FilePath expandComSpec();
127 
128 // close a handle then set it to NULL (so we can call this function
129 // repeatedly without failure or other side effects)
130 Error closeHandle(HANDLE* pHandle, const ErrorLocation& location);
131 
132 class CloseHandleOnExitScope : boost::noncopyable
133 {
134 public:
CloseHandleOnExitScope(HANDLE * pHandle,const ErrorLocation & location)135    CloseHandleOnExitScope(HANDLE* pHandle, const ErrorLocation& location)
136       : pHandle_(pHandle), location_(location)
137    {
138    }
139 
140    virtual ~CloseHandleOnExitScope();
detach()141    void detach() { pHandle_ = nullptr; }
142 private:
143    HANDLE* pHandle_;
144    ErrorLocation location_;
145 };
146 
147 // set $HOME to $USERPROFILE
148 void setHomeToUserProfile(core::system::Options* pChildEnv);
149 
150 // Folder for per-machine configuration data
151 FilePath systemSettingsPath(const std::string& appName, bool create);
152 
153 #endif // WIN32
154 
155 void initHook();
156 
157 // initialization
158 Error initializeSystemLog(const std::string& programIdentity,
159                           log::LogLevel logLevel,
160                           bool enableConfigReload = true);
161 
162 Error initializeStderrLog(const std::string& programIdentity,
163                           log::LogLevel logLevel,
164                           bool enableConfigReload = true);
165 
166 Error initializeLog(const std::string& programIdentity,
167                     log::LogLevel logLevel,
168                     const FilePath& logDir,
169                     bool enableConfigReload = true);
170 
171 Error initializeLog(const std::string& programIdentity,
172                     log::LogLevel logLevel,
173                     bool enableConfigReload = true);
174 
175 void initializeLogConfigReload();
176 
177 // common initialization functions - do not invoke directly
178 Error initLog();
179 Error reinitLog();
180 
181 void initFileLogDestination(const log::LogLevel level, const FilePath defaultLogDir);
182 
183 // exit
184 int exitFailure(const Error& error, const ErrorLocation& loggedFromLocation);
185 int exitFailure(const std::string& errMsg,
186                 const ErrorLocation& loggedFromLocation);
187 
188 // signals
189 
190 // ignore selected signals
191 Error ignoreTerminalSignals();
192 Error ignoreChildExits();
193 
194 // reap children (better way to handle child exits than ignoreChildExits
195 // because it doesn't prevent system from determining exit codes)
196 Error reapChildren();
197 
198 
199 enum SignalType
200 {
201    SigInt,
202    SigHup,
203    SigAbrt,
204    SigSegv,
205    SigIll,
206    SigUsr1,
207    SigUsr2,
208    SigPipe,
209    SigChld,
210    SigTerm
211 };
212 
213 
214 
215 // block all signals for a given scope
216 class SignalBlocker : boost::noncopyable
217 {
218 public:
219    SignalBlocker();
220    virtual ~SignalBlocker();
221    // COPYING: boost::noncopyable
222 
223    Error block(SignalType signal);
224    Error blockAll();
225 
226 private:
227    struct Impl;
228    boost::shared_ptr<Impl> pImpl_;
229 };
230 
231 core::Error clearSignalMask();
232 
233 core::Error handleSignal(SignalType signal, void (*handler)(int));
234 core::Error ignoreSignal(SignalType signal);
235 core::Error useDefaultSignalHandler(SignalType signal);
236 
237 void sendSignalToSelf(SignalType signal);
238 
239 // user info
240 std::string username();
241 
242 FilePath userHomePath(std::string envOverride = std::string());
243 FilePath userSettingsPath(const FilePath& userHomeDirectory,
244                           const std::string& appName,
245                           bool ensureDirectory = true /* create directory */);
246 unsigned int effectiveUserId();
247 bool effectiveUserIsRoot();
248 bool currentUserIsPrivilleged(unsigned int minimumUserId);
249 
250 // log
251 void log(log::LogLevel level,
252          const char* message,
253          const std::string&logSection = std::string());
254 
255 void log(log::LogLevel level,
256          const std::string& message,
257          const std::string& logSection = std::string());
258 
259 void log(log::LogLevel level,
260          const boost::function<std::string()>& action,
261          const std::string& logSection = std::string());
262 
263 const char* logLevelToStr(log::LogLevel level);
264 
265 log::LoggerType loggerType(const std::string& logSection = "");
266 
267 log::LogLevel lowestLogLevel();
268 
269 // filesystem
270 bool isHiddenFile(const FilePath& filePath);
271 bool isHiddenFile(const FileInfo& fileInfo);
272 bool isReadOnly(const FilePath& filePath);
273 
274 // terminals
275 bool stderrIsTerminal();
276 bool stdoutIsTerminal();
277 
278 // uuid
279 std::string generateUuid(bool includeDashes = true);
280 std::string generateShortenedUuid();
281 
282 // process info
283 
284 PidType currentProcessId();
285 std::string currentProcessPidStr();
286 
287 Error executablePath(int argc, const char * argv[],
288                      FilePath* pExecutablePath);
289 
290 Error executablePath(const char * argv0,
291                      FilePath* pExecutablePath);
292 
293 
294 Error installPath(const std::string& relativeToExecutable,
295                   const char * argv0,
296                   FilePath* pInstallationPath);
297 
298 void fixupExecutablePath(FilePath* pExePath);
299 
300 void abort();
301 
302 Error terminateProcess(PidType pid);
303 
304 struct SubprocInfo
305 {
306    PidType pid;
307    std::string exe;
308 };
309 
310 // Return list of child processes, by executable filename and pid
311 std::vector<SubprocInfo> getSubprocesses(PidType pid);
312 
313 // Get current-working directory of a process; returns empty FilePath
314 // if unable to determine cwd
315 FilePath currentWorkingDir(PidType pid);
316 
317 struct ProcessInfo
318 {
ProcessInforstudio::core::system::ProcessInfo319    ProcessInfo() : pid(0), ppid(0), pgrp(0) {}
320    PidType pid;
321    PidType ppid;
322    PidType pgrp;
323    std::string username;
324    std::string exe;
325    std::string state;
326    std::vector<std::string> arguments;
327 
328 #if !defined _WIN32 && !defined __APPLE__
329    core::Error creationTime(boost::posix_time::ptime* pCreationTime) const;
330 #endif
331 };
332 
333 // simple encapsulation of parent-child relationship of processes
334 struct ProcessTreeNode
335 {
336    boost::shared_ptr<ProcessInfo> data;
337    std::vector<boost::shared_ptr<ProcessTreeNode> > children;
338 };
339 
340 // process tree, indexed by pid
341 typedef std::map<PidType, boost::shared_ptr<ProcessTreeNode> > ProcessTreeT;
342 
343 Error terminateChildProcesses();
344 
345 void createProcessTree(const std::vector<ProcessInfo>& processes,
346                        ProcessTreeT *pOutTree);
347 
348 void getChildren(const boost::shared_ptr<ProcessTreeNode>& node,
349                  std::vector<ProcessInfo>* pOutChildren,
350                  int depth = 0);
351 
352 } // namespace system
353 } // namespace core
354 } // namespace rstudio
355 
356 #endif // CORE_SYSTEM_SYSTEM_HPP
357 
358