Belle II Software development
CaptureStream Class Reference

Class to capture anything written to stream into a string. More...

#include <IOIntercept.h>

Inheritance diagram for CaptureStream:
StreamInterceptor

Public Member Functions

 CaptureStream (std::ostream &stream, FILE *fileObject)
 Create a StreamInterceptor which writes into a pipe.
 
 ~CaptureStream ()
 Close file descriptors.
 
const std::string & getOutput () const
 Get the output, only set after finish()
 
bool start ()
 Start intercepting the output.
 
bool finish ()
 Restore the stream and get the output from the pipe.
 

Protected Member Functions

void setReplacementFD (int fd)
 set the replacement file descriptor, should be called in the constructor of derived classes
 
bool replaceFD (int fileDescriptor)
 Replace the file descriptor of m_fileObject with the one passed.
 

Static Protected Member Functions

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.
 

Protected Attributes

std::ostream & m_stream
 C++ stream object, only needed to flush before replacement.
 
FILE * m_fileObject
 File object of the file we want to replace, needed to obtain file descriptor and to flush.
 
int m_savedFD { -1}
 Saved file descriptor: a duplicate of the file descriptor of m_fileObject.
 
int m_replacementFD { -1}
 Replacement file descriptor to be used while capturing.
 
bool m_capturing {false}
 Check whether we are already capturing.
 

Private Attributes

int m_pipeReadFD { -1}
 file descriptor of the read end of the pipe
 
std::string m_outputStr
 string with the output, only filled after finish()
 

Friends

class CaptureStreamAbortHandler
 allow handling of SIGABRT
 

Detailed Description

Class to capture anything written to stream into a string.

Underlying this uses a pipe to buffer the output.

Warning
the default capacity for capturing output is 64 kB but might be smaller on certain systems. If there is more output than this while capturing it the captured output will be truncated and processes who check if output was successful might fail.

Definition at line 103 of file IOIntercept.h.

Constructor & Destructor Documentation

◆ CaptureStream()

CaptureStream ( std::ostream &  stream,
FILE *  fileObject 
)

Create a StreamInterceptor which writes into a pipe.

Definition at line 156 of file IOIntercept.cc.

156 : StreamInterceptor(stream, fileObject)
157 {
158 int pipeFD[2] {0};
159 if (pipe2(pipeFD, O_NONBLOCK) < 0) {
160 B2ERROR("Error creating pipe: " << std::strerror(errno));
161 return;
162 }
163 m_pipeReadFD = pipeFD[0];
164 setReplacementFD(pipeFD[1]);
165 }
int m_pipeReadFD
file descriptor of the read end of the pipe
Definition: IOIntercept.h:117
void setReplacementFD(int fd)
set the replacement file descriptor, should be called in the constructor of derived classes
Definition: IOIntercept.h:54
StreamInterceptor(std::ostream &stream, FILE *fileObject)
Construct keeping a reference to the std::ostream and the file descriptor which are associated with t...
Definition: IOIntercept.cc:88

◆ ~CaptureStream()

Close file descriptors.

Definition at line 167 of file IOIntercept.cc.

168 {
169 // the base destructor is called after this one so we need to make sure
170 // everything is finished before we close the pipe.
171 finish();
172 // no need to close the write part, done by base class
173 if (m_pipeReadFD >= 0) close(m_pipeReadFD);
174 }
bool finish()
Restore the stream and get the output from the pipe.
Definition: IOIntercept.cc:185

Member Function Documentation

◆ finish()

bool finish ( )

Restore the stream and get the output from the pipe.

Definition at line 185 of file IOIntercept.cc.

186 {
190 return true;
191 }
192 return false;
193 }
void removeObject(CaptureStream *obj)
Remove a CaptureStream object to no longer guard against SIGABRT.
Definition: IOIntercept.cc:51
static CaptureStreamAbortHandler & getInstance()
Return the singleton instance.
Definition: IOIntercept.cc:43
std::string m_outputStr
string with the output, only filled after finish()
Definition: IOIntercept.h:119
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.
Definition: IOIntercept.cc:102
bool finish()
stop intercepting the stream.
Definition: IOIntercept.h:46

◆ getOutput()

const std::string & getOutput ( ) const
inline

Get the output, only set after finish()

Definition at line 110 of file IOIntercept.h.

110{ return m_outputStr; }

◆ readFD()

void readFD ( int  fd,
std::string &  out 
)
staticprotectedinherited

Read the contents of a file descriptor until there is no more input and place them in out.

Parameters
fdfile descriptor to read, should be opened in non blocking mode O_NOBLOCK
outstring to be replaced with all the bytes read from fd

Definition at line 102 of file IOIntercept.cc.

103 {
104 out.clear();
105 if (fd <= 0) return;
106 // need a buffer to read
107 static std::unique_ptr<char[]> buffer(new char[1024]);
108 // so then, read everything
109 while (true) {
110 ssize_t size = read(fd, buffer.get(), 1024);
111 if (size <= 0) {
112 // in case we get interrupted by signal, try again
113 if (size < 0 && errno == EINTR) continue;
114 break;
115 }
116 out.append(buffer.get(), static_cast<size_t>(size));
117 }
118 }

◆ replaceFD()

bool replaceFD ( int  fileDescriptor)
protectedinherited

Replace the file descriptor of m_fileObject with the one passed.

Parameters
fileDescriptorfile descriptor to be set for m_fileObject using dup2()

Definition at line 120 of file IOIntercept.cc.

121 {
122 // obviously we don't want to replace invalid descriptors
123 if (fileDescriptor < 0) return false;
124 // flush existing stream
125 m_stream << std::flush;
126 std::fflush(m_fileObject);
127 // and clear the bad bits we might have gotten when pipe capacity is reached
128 m_stream.clear();
129 // and then replace the file descriptor
130 const int currentFD = fileno(m_fileObject);
131 if (currentFD < 0) {
132 B2ERROR("Error obtaining file descriptor: " << std::strerror(errno));
133 return false;
134 }
135 while (dup2(fileDescriptor, currentFD) < 0) {
136 if (errno != EINTR && errno != EBUSY) {
137 B2ERROR("Error in dup2(), cannot replace file descriptor: " << std::strerror(errno));
138 return false;
139 }
140 // interrupted or busy, let's try again
141 }
142 return true;
143 }
std::ostream & m_stream
C++ stream object, only needed to flush before replacement.
Definition: IOIntercept.h:65
FILE * m_fileObject
File object of the file we want to replace, needed to obtain file descriptor and to flush.
Definition: IOIntercept.h:67

◆ setReplacementFD()

void setReplacementFD ( int  fd)
inlineprotectedinherited

set the replacement file descriptor, should be called in the constructor of derived classes

Definition at line 54 of file IOIntercept.h.

54{ m_replacementFD = fd; }
int m_replacementFD
Replacement file descriptor to be used while capturing.
Definition: IOIntercept.h:71

◆ start()

bool start ( )

Start intercepting the output.

Definition at line 176 of file IOIntercept.cc.

177 {
180 return true;
181 }
182 return false;
183 }
void addObject(CaptureStream *obj)
Add a CaptureStream object to guard against SIGABRT.
Definition: IOIntercept.cc:49
bool start()
start intercepting the stream.
Definition: IOIntercept.h:38

Friends And Related Function Documentation

◆ CaptureStreamAbortHandler

friend class CaptureStreamAbortHandler
friend

allow handling of SIGABRT

Definition at line 121 of file IOIntercept.h.

Member Data Documentation

◆ m_capturing

bool m_capturing {false}
protectedinherited

Check whether we are already capturing.

Definition at line 73 of file IOIntercept.h.

◆ m_fileObject

FILE* m_fileObject
protectedinherited

File object of the file we want to replace, needed to obtain file descriptor and to flush.

Definition at line 67 of file IOIntercept.h.

◆ m_outputStr

std::string m_outputStr
private

string with the output, only filled after finish()

Definition at line 119 of file IOIntercept.h.

◆ m_pipeReadFD

int m_pipeReadFD { -1}
private

file descriptor of the read end of the pipe

Definition at line 117 of file IOIntercept.h.

◆ m_replacementFD

int m_replacementFD { -1}
protectedinherited

Replacement file descriptor to be used while capturing.

Definition at line 71 of file IOIntercept.h.

◆ m_savedFD

int m_savedFD { -1}
protectedinherited

Saved file descriptor: a duplicate of the file descriptor of m_fileObject.

Definition at line 69 of file IOIntercept.h.

◆ m_stream

std::ostream& m_stream
protectedinherited

C++ stream object, only needed to flush before replacement.

Definition at line 65 of file IOIntercept.h.


The documentation for this class was generated from the following files: