9#include <framework/utilities/IOIntercept.h>
10#include <framework/logging/Logger.h>
17#include <boost/algorithm/string.hpp>
57 if (result == SIG_ERR) B2FATAL(
"Cannot register abort handler");
60 static void handle(
int signal);
69 std::set<CaptureStream*> objects;
75 close(stream->m_replacementFD);
77 if (stream->finish()) {
78 const std::string& out = stream->getOutput();
80 if (out.size()) write(stream->m_savedFD, out.c_str(), out.size());
84 write(STDERR_FILENO,
"abort() called, exiting\n", 24);
85 std::_Exit(EXIT_FAILURE);
89 m_stream(stream), m_fileObject(fileObject), m_savedFD(dup(fileno(m_fileObject)))
92 B2ERROR(
"Error duplicating file descriptor: " << std::strerror(errno));
107 static std::unique_ptr<char[]> buffer(
new char[1024]);
110 ssize_t size = read(fd, buffer.get(), 1024);
113 if (size < 0 && errno == EINTR)
continue;
116 out.append(buffer.get(),
static_cast<size_t>(size));
123 if (fileDescriptor < 0)
return false;
132 B2ERROR(
"Error obtaining file descriptor: " << std::strerror(errno));
135 while (dup2(fileDescriptor, currentFD) < 0) {
136 if (errno != EINTR && errno != EBUSY) {
137 B2ERROR(
"Error in dup2(), cannot replace file descriptor: " << std::strerror(errno));
147 int devNull = open(
"/dev/null", O_WRONLY);
150 B2ERROR(
"Cannot open /dev/null: " << std::strerror(errno));
159 if (pipe2(pipeFD, O_NONBLOCK) < 0) {
160 B2ERROR(
"Error creating pipe: " << std::strerror(errno));
208 void sendLogMessage(
LogConfig::ELogLevel logLevel,
int debugLevel,
const std::string& name,
const std::string& indent,
214 boost::algorithm::trim_right(message);
216 while (!message.empty()) {
217 std::string new_message = boost::algorithm::trim_left_copy_if(message, boost::algorithm::is_any_of(
" \t\r"));
218 if (new_message.empty()) {
219 message = new_message;
220 }
else if (new_message[0] ==
'\n') {
221 message = new_message.substr(1, std::string::npos);
228 if (message.empty())
return;
230 boost::algorithm::replace_all(message,
"\n",
"\n" + indent);
232 B2LOG(logLevel, debugLevel,
"Output from " << name <<
":\n" << indent << message);
Small class to handle std::abort() calls by external libraries.
CaptureStreamAbortHandler()
Register handler.
void removeObject(CaptureStream *obj)
Remove a CaptureStream object to no longer guard against SIGABRT.
void addObject(CaptureStream *obj)
Add a CaptureStream object to guard against SIGABRT.
static CaptureStreamAbortHandler & getInstance()
Return the singleton instance.
CaptureStreamAbortHandler(CaptureStreamAbortHandler &&)=delete
Singleton, no move construction.
static void handle(int signal)
signal handler: print all pending redirection buffers and exit
std::set< CaptureStream * > m_objects
list of all active stream redirections
CaptureStreamAbortHandler & operator=(const CaptureStreamAbortHandler &)=delete
Singleton, no assignment.
CaptureStreamAbortHandler(const CaptureStreamAbortHandler &)=delete
Singleton, no copy construction.
Class to capture anything written to stream into a string.
CaptureStream(std::ostream &stream, FILE *fileObject)
Create a StreamInterceptor which writes into a pipe.
std::string m_outputStr
string with the output, only filled after finish()
int m_pipeReadFD
file descriptor of the read end of the pipe
bool start()
Start intercepting the output.
~CaptureStream()
Close file descriptors.
bool finish()
Restore the stream and get the output from the pipe.
DiscardStream(std::ostream &stream, FILE *fileObject)
Create StreamInterceptor which will redirect to /dev/null.
const std::string & getStdOut() const
Return the captured stdout output if any.
bool finish()
Finish intercepting the output.
const std::string & getStdErr() const
Return the captured stderr output if any.
LogConfig::ELogLevel m_stderrLevel
severity of the log message to be emitted for output on stderr
const std::string m_name
Name of the output producing tool/library.
int m_stderrDebugLevel
debug level for the log message to be emitted for output on stderr if m_stderrLevel is c_Debug
LogConfig::ELogLevel m_stdoutLevel
severity of the log message to be emitted for output on stdout
bool finish()
Finish the capture and emit the message if output has appeard on stdout or stderr.
int m_stdoutDebugLevel
debug level for the log message to be emitted for output on stdout if m_stdoutLevel is c_Debug
std::string m_indent
Identation to add to the beginning of each line of output.
Base class with all necessary features to intercept output to a file descriptor.
static void readFD(int fd, std::string &out)
Read the contents of a file descriptor until there is no more input and place them in out.
std::ostream & m_stream
C++ stream object, only needed to flush before replacement.
bool replaceFD(int fileDescriptor)
Replace the file descriptor of m_fileObject with the one passed.
~StreamInterceptor()
close file descriptors
FILE * m_fileObject
File object of the file we want to replace, needed to obtain file descriptor and to flush.
void setReplacementFD(int fd)
set the replacement file descriptor, should be called in the constructor of derived classes
bool start()
start intercepting the stream.
StreamInterceptor(std::ostream &stream, FILE *fileObject)
Construct keeping a reference to the std::ostream and the file descriptor which are associated with t...
int m_replacementFD
Replacement file descriptor to be used while capturing.
int m_savedFD
Saved file descriptor: a duplicate of the file descriptor of m_fileObject.
bool finish()
stop intercepting the stream.
ELogLevel
Definition of the supported log levels.
static LogSystem & Instance()
Static method to get a reference to the LogSystem instance.
Encapsulate all classes needed to intercept stdout and stderr.