scc  2022.4.0
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
36 namespace util {
37 IoRedirector::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 
47 void 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 
63 auto IoRedirector::is_active() -> bool {
64  std::lock_guard<std::mutex> lock(m_mutex);
65  return m_capturing;
66 }
67 
68 void 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 
109 auto 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 
144 auto 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 }
154 void 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 }
166 void 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 
175 void 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