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