1 /*
2  * System.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 #define RSTUDIO_DEBUG_MACROS_DISABLED
17 
18 #include <atomic>
19 #include <unordered_set>
20 
21 #include <boost/variant.hpp>
22 
23 #include <shared_core/Hash.hpp>
24 #include <shared_core/FileLogDestination.hpp>
25 #include <shared_core/FilePath.hpp>
26 #include <shared_core/SafeConvert.hpp>
27 #include <shared_core/StderrLogDestination.hpp>
28 
29 #ifndef _WIN32
30 #include <shared_core/system/SyslogDestination.hpp>
31 #endif
32 
33 #include <core/Algorithm.hpp>
34 #include <core/Log.hpp>
35 #include <core/LogOptions.hpp>
36 #include <core/system/System.hpp>
37 #include <core/system/Environment.hpp>
38 
39 namespace rstudio {
40 namespace core {
41 namespace system {
42 
43 #ifdef _WIN32
44 #define kPathSeparator ";"
45 #else
46 #define kPathSeparator ":"
47 #endif
48 
49 
realPathsEqual(const FilePath & a,const FilePath & b)50 bool realPathsEqual(const FilePath& a, const FilePath& b)
51 {
52    FilePath aReal, bReal;
53 
54    Error error = realPath(a, &aReal);
55    if (error)
56    {
57       LOG_ERROR(error);
58       return false;
59    }
60 
61    error = realPath(b, &bReal);
62    if (error)
63    {
64       LOG_ERROR(error);
65       return false;
66    }
67 
68    return aReal == bReal;
69 }
70 
addToSystemPath(const FilePath & path,bool prepend)71 void addToSystemPath(const FilePath& path, bool prepend)
72 {
73    std::string systemPath = system::getenv("PATH");
74    if (prepend)
75       systemPath = path.getAbsolutePath() + kPathSeparator + systemPath;
76    else
77       systemPath = systemPath + kPathSeparator + path.getAbsolutePath();
78    system::setenv("PATH", systemPath);
79 }
80 
exitFailure(const Error & error,const ErrorLocation & loggedFromLocation)81 int exitFailure(const Error& error, const ErrorLocation& loggedFromLocation)
82 {
83    if (error)
84       core::log::logError(error, loggedFromLocation);
85    return EXIT_FAILURE;
86 }
87 
exitFailure(const std::string & errMsg,const ErrorLocation & loggedFromLocation)88 int exitFailure(const std::string& errMsg,
89                 const ErrorLocation& loggedFromLocation)
90 {
91    core::log::logErrorMessage(errMsg, loggedFromLocation);
92    return EXIT_FAILURE;
93 }
94 
exitSuccess()95 int exitSuccess()
96 {
97    return EXIT_SUCCESS;
98 }
99 
generateShortenedUuid()100 std::string generateShortenedUuid()
101 {
102    std::string uuid = core::system::generateUuid(false);
103    return core::hash::crc32HexHash(uuid);
104 }
105 
106 // logger's program identity (this process's binary name)
107 std::string s_programIdentity;
108 
109 // logging options representing the latest state of the logging configuration file
110 boost::shared_ptr<log::LogOptions> s_logOptions;
111 
112 // mutex for logging synchronization
113 boost::recursive_mutex s_loggingMutex;
114 
115 namespace {
116 
initializeLogWriter()117 void initializeLogWriter()
118 {
119    using namespace log;
120 
121    // requires prior synchronization
122    LoggerType loggerType = s_logOptions->loggerType();
123    LogMessageFormatType formatType = s_logOptions->logMessageFormatType();
124 
125    // options currently only used for file logging
126    LoggerOptions options = s_logOptions->loggerOptions();
127 
128    log::LogLevel logLevel = s_logOptions->logLevel();
129 
130    switch (loggerType)
131    {
132       case LoggerType::kFile:
133       {
134          addLogDestination(
135             std::shared_ptr<ILogDestination>(new FileLogDestination(
136                generateShortenedUuid(),
137                logLevel,
138                formatType,
139                s_programIdentity,
140                boost::get<FileLogOptions>(options),
141                true)));
142          break;
143       }
144       case LoggerType::kStdErr:
145       {
146          addLogDestination(
147             std::shared_ptr<ILogDestination>(new StderrLogDestination(
148                generateShortenedUuid(),
149                logLevel,
150                formatType,
151                true)));
152          break;
153       }
154       case LoggerType::kSysLog:
155       default:
156       {
157 #ifndef _WIN32
158          addLogDestination(
159             std::shared_ptr<ILogDestination>(new SyslogDestination(
160                generateShortenedUuid(),
161                logLevel,
162                formatType,
163                s_programIdentity,
164                true)));
165 #endif
166       }
167    }
168 }
169 
initializeLogWriter(const std::string & logSection)170 void initializeLogWriter(const std::string& logSection)
171 {
172    using namespace log;
173 
174    // requires prior synchronization
175    LoggerType loggerType = s_logOptions->loggerType(logSection);
176    LogMessageFormatType formatType = s_logOptions->logMessageFormatType(logSection);
177 
178    // options currently only used for file logging
179    LoggerOptions options = s_logOptions->loggerOptions(logSection);
180 
181    log::LogLevel logLevel = s_logOptions->logLevel(logSection);
182 
183    switch (loggerType)
184    {
185       case LoggerType::kFile:
186       {
187          addLogDestination(
188             std::shared_ptr<ILogDestination>(new FileLogDestination(
189                generateShortenedUuid(),
190                logLevel,
191                formatType,
192                s_programIdentity,
193                boost::get<FileLogOptions>(options),
194                true)), logSection);
195          break;
196       }
197       case LoggerType::kStdErr:
198       {
199          addLogDestination(
200             std::shared_ptr<ILogDestination>(new StderrLogDestination(
201                generateShortenedUuid(),
202                logLevel,
203                formatType,
204                true)), logSection);
205          break;
206       }
207       case LoggerType::kSysLog:
208       default:
209       {
210 #ifndef _WIN32
211          addLogDestination(
212             std::shared_ptr<ILogDestination>(new SyslogDestination(
213                generateShortenedUuid(),
214                logLevel,
215                formatType,
216                s_programIdentity,
217                true)), logSection);
218 #endif
219       }
220    }
221 }
222 
initializeLogWriters()223 void initializeLogWriters()
224 {
225    // requires prior synchronization
226    log::setProgramId(s_programIdentity);
227 
228    // Initialize primary log writer
229    initializeLogWriter();
230 
231    // Initialize secondary log writers
232    std::vector<std::string> logSections = s_logOptions->loggerOverrides();
233    for (const std::string& loggerName : logSections)
234       initializeLogWriter(loggerName);
235 }
236 
237 } // anonymous namespace
238 
loggerType(const std::string & in_sectionName)239 log::LoggerType loggerType(const std::string& in_sectionName)
240 {
241    RECURSIVE_LOCK_MUTEX(s_loggingMutex)
242    {
243       if (s_logOptions)
244 
245          return s_logOptions->loggerType(in_sectionName);
246    }
247    END_LOCK_MUTEX
248 
249    // default return - only occurs if we fail to lock mutex
250    return log::LoggerType::kSysLog;
251 }
252 
lowestLogLevel()253 log::LogLevel lowestLogLevel()
254 {
255    RECURSIVE_LOCK_MUTEX(s_loggingMutex)
256    {
257       if (!s_logOptions)
258          return log::LogLevel::WARN;
259 
260       return s_logOptions->lowestLogLevel();
261    }
262    END_LOCK_MUTEX
263 
264    // default return - only occurs if we fail to lock mutex
265    return log::LogLevel::WARN;
266 }
267 
initLog()268 Error initLog()
269 {
270    // requires prior synchronization
271 
272    Error error = s_logOptions->read();
273    if (error)
274       return error;
275 
276    initializeLogWriters();
277 
278    return Success();
279 }
280 
reinitLog()281 Error reinitLog()
282 {
283    RECURSIVE_LOCK_MUTEX(s_loggingMutex)
284    {
285       log::removeReloadableLogDestinations();
286       return initLog();
287    }
288    END_LOCK_MUTEX
289 
290    return Success();
291 }
292 
initializeStderrLog(const std::string & programIdentity,log::LogLevel logLevel,bool enableConfigReload)293 Error initializeStderrLog(const std::string& programIdentity,
294                           log::LogLevel logLevel,
295                           bool enableConfigReload)
296 {
297    RECURSIVE_LOCK_MUTEX(s_loggingMutex)
298    {
299       // create default stderr logger options
300       log::StdErrLogOptions options;
301       s_logOptions.reset(new log::LogOptions(programIdentity, logLevel, log::LoggerType::kStdErr, log::LogMessageFormatType::PRETTY, options));
302       s_programIdentity = programIdentity;
303 
304       Error error = initLog();
305       if (error)
306          return error;
307    }
308    END_LOCK_MUTEX
309 
310    if (enableConfigReload)
311       initializeLogConfigReload();
312 
313    return Success();
314 }
315 
initializeLog(const std::string & programIdentity,log::LogLevel logLevel,const FilePath & logDir,bool enableConfigReload)316 Error initializeLog(const std::string& programIdentity,
317                     log::LogLevel logLevel,
318                     const FilePath& logDir,
319                     bool enableConfigReload)
320 {
321    RECURSIVE_LOCK_MUTEX(s_loggingMutex)
322    {
323       // create default file logger options
324       log::FileLogOptions options(logDir);
325       s_logOptions.reset(new log::LogOptions(programIdentity, logLevel, log::LoggerType::kFile, log::LogMessageFormatType::PRETTY, options));
326       s_programIdentity = programIdentity;
327 
328       Error error = initLog();
329       if (error)
330          return error;
331    }
332    END_LOCK_MUTEX
333 
334    if (enableConfigReload)
335       initializeLogConfigReload();
336 
337    return Success();
338 }
339 
initializeLog(const std::string & programIdentity,log::LogLevel logLevel,bool enableConfigReload)340 Error initializeLog(const std::string& programIdentity,
341                     log::LogLevel logLevel,
342                     bool enableConfigReload)
343 {
344    return initializeLog(programIdentity, logLevel, log::LogOptions::defaultLogDirectory(), enableConfigReload);
345 }
346 
initFileLogDestination(log::LogLevel level,FilePath defaultLogDir)347 void initFileLogDestination(log::LogLevel level, FilePath defaultLogDir)
348 {
349    if (!log::hasFileLogDestination())
350    {
351       log::FileLogOptions defaultOptions(defaultLogDir);
352       log::addLogDestination(std::shared_ptr<core::log::ILogDestination>(
353               new log::FileLogDestination(generateShortenedUuid(),
354                                           level,
355                                           log::LogMessageFormatType::PRETTY,
356                                           s_programIdentity,
357                                           defaultOptions)));
358    }
359 }
360 
log(log::LogLevel logLevel,const char * message,const std::string & logSection)361 void log(log::LogLevel logLevel,
362          const char* message,
363          const std::string& logSection)
364 {
365    log(logLevel, std::string(message), logSection);
366 }
367 
log(log::LogLevel logLevel,const std::string & message,const std::string & logSection)368 void log(log::LogLevel logLevel,
369          const std::string& message,
370          const std::string& logSection)
371 {
372    switch (logLevel)
373    {
374       case log::LogLevel::ERR:
375       {
376          log::logErrorMessage(message, logSection);
377          break;
378       }
379       case log::LogLevel::WARN:
380       {
381          log::logWarningMessage(message, logSection);
382          break;
383       }
384       case log::LogLevel::DEBUG:
385       {
386          log::logDebugMessage(message, logSection);
387          break;
388       }
389       case log::LogLevel::INFO:
390       {
391          log::logInfoMessage(message, logSection);
392          break;
393       }
394       case log::LogLevel::OFF:
395       default:
396       {
397          return;
398       }
399    }
400 }
401 
logLevelToStr(log::LogLevel level)402 const char* logLevelToStr(log::LogLevel level)
403 {
404    switch(level)
405    {
406       case log::LogLevel::ERR:
407          return "ERROR";
408       case log::LogLevel::WARN:
409          return "WARNING";
410       case log::LogLevel::INFO:
411          return "INFO";
412       case log::LogLevel::DEBUG:
413          return "DEBUG";
414       case log::LogLevel::OFF:
415          return "OFF";
416       default:
417          LOG_WARNING_MESSAGE("Unexpected log level: " +
418                              safe_convert::numberToString(static_cast<int>(level)));
419          return "ERROR";
420    }
421 }
422 
currentProcessPidStr()423 std::string currentProcessPidStr()
424 {
425    return safe_convert::numberToString(currentProcessId());
426 }
427 
createProcessTree(const std::vector<ProcessInfo> & processes,ProcessTreeT * pOutTree)428 void createProcessTree(const std::vector<ProcessInfo>& processes,
429                        ProcessTreeT *pOutTree)
430 {
431    // first pass, create the nodes in the tree
432    for (const ProcessInfo& process : processes)
433    {
434       ProcessTreeT::iterator iter = pOutTree->find(process.pid);
435       if (iter == pOutTree->end())
436       {
437          // process not found, so create a new entry for it
438          boost::shared_ptr<ProcessTreeNode> nodePtr = boost::shared_ptr<ProcessTreeNode>(
439                                                          new ProcessTreeNode());
440 
441          nodePtr->data = boost::shared_ptr<ProcessInfo>(new ProcessInfo(process));
442 
443          (*pOutTree)[process.pid] = nodePtr;
444       }
445    }
446 
447    // second pass, link the nodes together
448    for (ProcessTreeT::value_type& element : *pOutTree)
449    {
450       PidType parent = element.second->data->ppid;
451       ProcessTreeT::iterator iter = pOutTree->find(parent);
452 
453       // if we cannot find the parent in the tree, move on
454       if (iter == pOutTree->end())
455          continue;
456 
457       // add this node to its parent's children
458       iter->second->children.push_back(element.second);
459    }
460 }
461 
getChildren(const boost::shared_ptr<ProcessTreeNode> & node,std::vector<ProcessInfo> * pOutChildren,int depth)462 void getChildren(const boost::shared_ptr<ProcessTreeNode>& node,
463                  std::vector<ProcessInfo>* pOutChildren,
464                  int depth)
465 {
466    std::unordered_set<PidType> visited;
467    for (const boost::shared_ptr<ProcessTreeNode>& child : node->children)
468    {
469       // cycle protection - only visit the node and its children if it hasn't been visited already
470       // depth protection - do not infinitely recurse for process creation bombs
471       if (visited.count(child->data->pid) == 0 && depth < 100)
472       {
473          visited.insert(child->data->pid);
474          pOutChildren->push_back(*child->data.get());
475          getChildren(child, pOutChildren, depth + 1);
476       }
477    }
478 }
479 
480 } // namespace system
481 } // namespace core
482 } // namespace rstudio
483 
484