/*
* System.cpp
*
* Copyright (C) 2022 by Posit Software, PBC
*
* Unless you have received this program directly from Posit Software pursuant
* to the terms of a commercial license agreement with Posit Software, then
* this program is licensed to you under the terms of version 3 of the
* GNU Affero General Public License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
*
*/
#define RSTUDIO_DEBUG_MACROS_DISABLED
#include
#include
#include
#include
#include
#include
#include
#include
#ifndef _WIN32
#include
#endif
#include
#include
#include
#include
#include
namespace rstudio {
namespace core {
namespace system {
#ifdef _WIN32
#define kPathSeparator ";"
#else
#define kPathSeparator ":"
#endif
bool realPathsEqual(const FilePath& a, const FilePath& b)
{
FilePath aReal, bReal;
Error error = realPath(a, &aReal);
if (error)
{
LOG_ERROR(error);
return false;
}
error = realPath(b, &bReal);
if (error)
{
LOG_ERROR(error);
return false;
}
return aReal == bReal;
}
void addToSystemPath(const FilePath& path, bool prepend)
{
std::string systemPath = system::getenv("PATH");
if (prepend)
systemPath = path.getAbsolutePath() + kPathSeparator + systemPath;
else
systemPath = systemPath + kPathSeparator + path.getAbsolutePath();
system::setenv("PATH", systemPath);
}
int exitFailure(const Error& error, const ErrorLocation& loggedFromLocation)
{
if (error)
core::log::logError(error, loggedFromLocation);
return EXIT_FAILURE;
}
int exitFailure(const std::string& errMsg,
const ErrorLocation& loggedFromLocation)
{
core::log::logErrorMessage(errMsg, loggedFromLocation);
return EXIT_FAILURE;
}
int exitSuccess()
{
return EXIT_SUCCESS;
}
std::string generateShortenedUuid()
{
std::string uuid = core::system::generateUuid(false);
return core::hash::crc32HexHash(uuid);
}
// logger's program identity (this process's binary name)
std::string s_programIdentity;
// logging options representing the latest state of the logging configuration file
boost::shared_ptr s_logOptions;
// mutex for logging synchronization
boost::recursive_mutex s_loggingMutex;
namespace {
void initializeLogWriter()
{
using namespace log;
// requires prior synchronization
LoggerType loggerType = s_logOptions->loggerType();
LogMessageFormatType formatType = s_logOptions->logMessageFormatType();
// options currently only used for file logging
LoggerOptions options = s_logOptions->loggerOptions();
log::LogLevel logLevel = s_logOptions->logLevel();
switch (loggerType)
{
case LoggerType::kFile:
{
FileLogDestination* dst = new FileLogDestination(
generateShortenedUuid(),
logLevel,
formatType,
s_programIdentity,
boost::get(options),
true);
addLogDestination(
std::shared_ptr(dst));
ttyCheck(dst->path());
break;
}
case LoggerType::kStdErr:
{
addLogDestination(
std::shared_ptr(new StderrLogDestination(
generateShortenedUuid(),
logLevel,
formatType,
true)));
break;
}
case LoggerType::kSysLog:
default:
{
#ifndef _WIN32
addLogDestination(
std::shared_ptr(new SyslogDestination(
generateShortenedUuid(),
logLevel,
formatType,
s_programIdentity,
true)));
ttyCheck("syslog");
#endif
}
}
}
void initializeLogWriter(const std::string& logSection)
{
using namespace log;
// requires prior synchronization
LoggerType loggerType = s_logOptions->loggerType(logSection);
LogMessageFormatType formatType = s_logOptions->logMessageFormatType(logSection);
// options currently only used for file logging
LoggerOptions options = s_logOptions->loggerOptions(logSection);
log::LogLevel logLevel = s_logOptions->logLevel(logSection);
switch (loggerType)
{
case LoggerType::kFile:
{
addLogDestination(
std::shared_ptr(new FileLogDestination(
generateShortenedUuid(),
logLevel,
formatType,
s_programIdentity,
boost::get(options),
true)), logSection);
break;
}
case LoggerType::kStdErr:
{
addLogDestination(
std::shared_ptr(new StderrLogDestination(
generateShortenedUuid(),
logLevel,
formatType,
true)), logSection);
break;
}
case LoggerType::kSysLog:
default:
{
#ifndef _WIN32
addLogDestination(
std::shared_ptr(new SyslogDestination(
generateShortenedUuid(),
logLevel,
formatType,
s_programIdentity,
true)), logSection);
#endif
}
}
}
void initializeLogWriters()
{
// requires prior synchronization
log::setProgramId(s_programIdentity);
// Initialize primary log writer
initializeLogWriter();
// Initialize secondary log writers
std::vector logSections = s_logOptions->loggerOverrides();
for (const std::string& loggerName : logSections)
initializeLogWriter(loggerName);
}
} // anonymous namespace
void ttyCheck(const std::string& destination)
{
// When running in a TTY, log some information about why we're logging to the TTY
// in addition to file/syslog.
if (stderrIsTerminal())
std::cerr data = boost::shared_ptr(new ProcessInfo(process));
(*pOutTree)[process.pid] = nodePtr;
}
}
// second pass, link the nodes together
for (ProcessTreeT::value_type& element : *pOutTree)
{
PidType parent = element.second->data->ppid;
ProcessTreeT::iterator iter = pOutTree->find(parent);
// if we cannot find the parent in the tree, move on
if (iter == pOutTree->end())
continue;
// add this node to its parent's children
iter->second->children.push_back(element.second);
}
}
void getChildren(const boost::shared_ptr& node,
std::vector* pOutChildren,
int depth)
{
std::unordered_set visited;
for (const boost::shared_ptr& child : node->children)
{
// cycle protection - only visit the node and its children if it hasn't been visited already
// depth protection - do not infinitely recurse for process creation bombs
if (visited.count(child->data->pid) == 0 && depth < 100)
{
visited.insert(child->data->pid);
pOutChildren->push_back(*child->data.get());
getChildren(child, pOutChildren, depth + 1);
}
}
}
} // namespace system
} // namespace core
} // namespace rstudio