17 #include "io-redirector.h"
22 #define pclose _pclose
26 #define fileno _fileno
37 IoRedirector::IoRedirector()
40 , m_capturing(false) {
42 std::lock_guard<std::mutex> lock(m_mutex);
43 setvbuf(stdout,
nullptr, _IONBF, 0);
44 setvbuf(stderr,
nullptr, _IONBF, 0);
47 void IoRedirector::start() {
48 std::lock_guard<std::mutex> lock(m_mutex);
53 m_oldStdOut = copy_fd(fileno(stdout));
54 m_oldStdErr = copy_fd(fileno(stderr));
55 copy_fd_to(m_pipe[WRITE], fileno(stdout));
56 copy_fd_to(m_pipe[WRITE], fileno(stderr));
59 close_fd(m_pipe[WRITE]);
63 auto IoRedirector::is_active() ->
bool {
64 std::lock_guard<std::mutex> lock(m_mutex);
68 void IoRedirector::stop() {
69 std::lock_guard<std::mutex> lock(m_mutex);
74 copy_fd_to(m_oldStdOut, fileno(stdout));
75 copy_fd_to(m_oldStdErr, fileno(stderr));
77 std::array<char, bufSize> buf;
79 bool fd_blocked(
false);
83 if(!eof(m_pipe[READ]))
84 bytesRead = read(m_pipe[READ], buf.data(), bufSize - 1);
86 int flags = fcntl(m_pipe[READ], F_GETFL, 0);
87 fcntl(m_pipe[READ], F_SETFL, flags & ~O_NONBLOCK);
88 bytesRead = read(m_pipe[READ], buf.data(), bufSize - 1);
92 m_captured += buf.data();
93 }
else if(bytesRead < 0) {
94 fd_blocked = (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR);
96 std::this_thread::sleep_for(std::chrono::milliseconds(10));
98 }
while(fd_blocked || bytesRead == (bufSize - 1));
100 close_fd(m_oldStdOut);
101 close_fd(m_oldStdErr);
102 close_fd(m_pipe[READ]);
104 close_fd(m_pipe[WRITE]);
109 auto IoRedirector::get_output(
bool blocking) -> std::string {
110 std::lock_guard<std::mutex> lock(m_mutex);
113 std::array<char, bufSize> buf;
115 bool fd_blocked(
false);
119 if(!eof(m_pipe[READ]))
120 bytesRead = read(m_pipe[READ], buf.data(), bufSize - 1);
122 int flags = fcntl(m_pipe[READ], F_GETFL, 0);
124 fcntl(m_pipe[READ], F_SETFL, flags & ~O_NONBLOCK);
126 fcntl(m_pipe[READ], F_SETFL, flags | O_NONBLOCK);
127 bytesRead = read(m_pipe[READ], buf.data(), bufSize - 1);
132 }
else if(bytesRead < 0) {
133 fd_blocked = errno == EINTR;
135 std::this_thread::sleep_for(std::chrono::milliseconds(10));
137 }
while(fd_blocked || bytesRead == (bufSize - 1));
144 auto IoRedirector::copy_fd(
int fd) ->
int {
146 bool fd_blocked =
false;
149 if(ret < 0 && (errno == EINTR || errno == EBUSY))
150 std::this_thread::sleep_for(std::chrono::milliseconds(10));
154 void IoRedirector::create_pipes() {
158 ret = pipe(m_pipe, 65536, O_BINARY);
160 ret = pipe(m_pipe) == -1;
162 if(ret < 0 && (errno == EINTR || errno == EBUSY))
163 std::this_thread::sleep_for(std::chrono::milliseconds(10));
166 void IoRedirector::copy_fd_to(
int src_fd,
int dest_fd) {
169 ret = dup2(src_fd, dest_fd);
170 if(ret < 0 && (errno == EINTR || errno == EBUSY))
171 std::this_thread::sleep_for(std::chrono::milliseconds(10));
175 void IoRedirector::close_fd(
int& fd) {
179 if(ret < 0 && errno == EINTR)
180 std::this_thread::sleep_for(std::chrono::milliseconds(10));