scc 2025.09
SystemC components library
io-redirector.cpp
1/*******************************************************************************
2 * Copyright 2019-2022 MINRES Technologies GmbH
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *******************************************************************************/
16
17#include "io-redirector.h"
18#include <array>
19#ifdef _MSC_VER
20#include <io.h>
21#define popen _popen
22#define pclose _pclose
23#define stat _stat
24#define dup _dup
25#define dup2 _dup2
26#define fileno _fileno
27#define close _close
28#define pipe _pipe
29#define read _read
30#define eof _eof
31#else
32#include <poll.h>
33#include <thread>
34#include <unistd.h>
35#endif
36namespace util {
37IoRedirector::IoRedirector()
38: m_oldStdOut(0)
39, m_oldStdErr(0)
40, m_capturing(false) {
41 // make stdout & stderr streams unbuffered
42 std::lock_guard<std::mutex> lock(m_mutex);
43 setvbuf(stdout, nullptr, _IONBF, 0);
44 setvbuf(stderr, nullptr, _IONBF, 0);
45}
46
47void IoRedirector::start() {
48 std::lock_guard<std::mutex> lock(m_mutex);
49 if(m_capturing)
50 return;
51
52 create_pipes();
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));
57 m_capturing = true;
58#ifndef _MSC_VER
59 close_fd(m_pipe[WRITE]);
60#endif
61}
62
63auto IoRedirector::is_active() -> bool {
64 std::lock_guard<std::mutex> lock(m_mutex);
65 return m_capturing;
66}
67
68void IoRedirector::stop() {
69 std::lock_guard<std::mutex> lock(m_mutex);
70 if(!m_capturing)
71 return;
72
73 m_captured.clear();
74 copy_fd_to(m_oldStdOut, fileno(stdout));
75 copy_fd_to(m_oldStdErr, fileno(stderr));
76
77 std::array<char, bufSize> buf;
78 int bytesRead = 0;
79 bool fd_blocked(false);
80 do {
81 fd_blocked = false;
82#ifdef _MSC_VER
83 if(!eof(m_pipe[READ]))
84 bytesRead = read(m_pipe[READ], buf.data(), bufSize - 1);
85#else
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);
89#endif
90 if(bytesRead > 0) {
91 buf[bytesRead] = 0;
92 m_captured += buf.data();
93 } else if(bytesRead < 0) {
94 fd_blocked = (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR);
95 if(fd_blocked)
96 std::this_thread::sleep_for(std::chrono::milliseconds(10));
97 }
98 } while(fd_blocked || bytesRead == (bufSize - 1));
99
100 close_fd(m_oldStdOut);
101 close_fd(m_oldStdErr);
102 close_fd(m_pipe[READ]);
103#ifdef _MSC_VER
104 close_fd(m_pipe[WRITE]);
105#endif
106 m_capturing = false;
107}
108
109auto IoRedirector::get_output(bool blocking) -> std::string {
110 std::lock_guard<std::mutex> lock(m_mutex);
111 if(m_capturing) {
112 std::string ret;
113 std::array<char, bufSize> buf;
114 int bytesRead = 0;
115 bool fd_blocked(false);
116 do {
117 fd_blocked = false;
118#ifdef _MSC_VER
119 if(!eof(m_pipe[READ]))
120 bytesRead = read(m_pipe[READ], buf.data(), bufSize - 1);
121#else
122 int flags = fcntl(m_pipe[READ], F_GETFL, 0);
123 if(blocking)
124 fcntl(m_pipe[READ], F_SETFL, flags & ~O_NONBLOCK);
125 else
126 fcntl(m_pipe[READ], F_SETFL, flags | O_NONBLOCK);
127 bytesRead = read(m_pipe[READ], buf.data(), bufSize - 1);
128#endif
129 if(bytesRead > 0) {
130 buf[bytesRead] = 0;
131 ret += buf.data();
132 } else if(bytesRead < 0) {
133 fd_blocked = errno == EINTR;
134 if(fd_blocked)
135 std::this_thread::sleep_for(std::chrono::milliseconds(10));
136 }
137 } while(fd_blocked || bytesRead == (bufSize - 1));
138 return ret;
139 } else {
140 return m_captured;
141 }
142}
143
144auto IoRedirector::copy_fd(int fd) -> int {
145 int ret = -1;
146 bool fd_blocked = false;
147 do {
148 ret = dup(fd);
149 if(ret < 0 && (errno == EINTR || errno == EBUSY))
150 std::this_thread::sleep_for(std::chrono::milliseconds(10));
151 } while(ret < 0);
152 return ret;
153}
154void IoRedirector::create_pipes() {
155 int ret = -1;
156 do {
157#ifdef _MSC_VER
158 ret = pipe(m_pipe, 65536, O_BINARY);
159#else
160 ret = pipe(m_pipe) == -1;
161#endif
162 if(ret < 0 && (errno == EINTR || errno == EBUSY))
163 std::this_thread::sleep_for(std::chrono::milliseconds(10));
164 } while(ret < 0);
165}
166void IoRedirector::copy_fd_to(int src_fd, int dest_fd) {
167 int ret = -1;
168 do {
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));
172 } while(ret < 0);
173}
174
175void IoRedirector::close_fd(int& fd) {
176 int ret = -1;
177 do {
178 ret = close(fd);
179 if(ret < 0 && errno == EINTR)
180 std::this_thread::sleep_for(std::chrono::milliseconds(10));
181 } while(ret < 0);
182 fd = -1;
183}
184} // namespace util
SCC common utilities.
Definition bit_field.h:30