1 //
2 // Automated Testing Framework (atf)
3 //
4 // Copyright (c) 2007 The NetBSD Foundation, Inc.
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 // 1. Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 // 2. Redistributions in binary form must reproduce the above copyright
13 // notice, this list of conditions and the following disclaimer in the
14 // documentation and/or other materials provided with the distribution.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17 // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 //
29
30 extern "C" {
31 #include <fcntl.h>
32 #include <poll.h>
33 #include <signal.h>
34 #include <unistd.h>
35 }
36
37 #include <cerrno>
38 #include <cstring>
39
40 extern "C" {
41 #include "../atf-c/error.h"
42 }
43
44 #include "../atf-c++/detail/auto_array.hpp"
45 #include "../atf-c++/detail/exceptions.hpp"
46 #include "../atf-c++/detail/sanity.hpp"
47
48 #include "io.hpp"
49
50 namespace impl = atf::atf_run;
51 #define IMPL_NAME "atf::atf_run"
52
53 // ------------------------------------------------------------------------
54 // The "file_handle" class.
55 // ------------------------------------------------------------------------
56
file_handle(void)57 impl::file_handle::file_handle(void) :
58 m_handle(invalid_value())
59 {
60 }
61
file_handle(handle_type h)62 impl::file_handle::file_handle(handle_type h) :
63 m_handle(h)
64 {
65 PRE(m_handle != invalid_value());
66 }
67
file_handle(const file_handle & fh)68 impl::file_handle::file_handle(const file_handle& fh) :
69 m_handle(fh.m_handle)
70 {
71 fh.m_handle = invalid_value();
72 }
73
~file_handle(void)74 impl::file_handle::~file_handle(void)
75 {
76 if (is_valid())
77 close();
78 }
79
80 impl::file_handle&
operator =(const file_handle & fh)81 impl::file_handle::operator=(const file_handle& fh)
82 {
83 m_handle = fh.m_handle;
84 fh.m_handle = invalid_value();
85
86 return *this;
87 }
88
89 bool
is_valid(void) const90 impl::file_handle::is_valid(void)
91 const
92 {
93 return m_handle != invalid_value();
94 }
95
96 void
close(void)97 impl::file_handle::close(void)
98 {
99 PRE(is_valid());
100
101 ::close(m_handle);
102
103 m_handle = invalid_value();
104 }
105
106 impl::file_handle::handle_type
disown(void)107 impl::file_handle::disown(void)
108 {
109 PRE(is_valid());
110
111 handle_type h = m_handle;
112 m_handle = invalid_value();
113 return h;
114 }
115
116 impl::file_handle::handle_type
get(void) const117 impl::file_handle::get(void)
118 const
119 {
120 PRE(is_valid());
121
122 return m_handle;
123 }
124
125 void
posix_remap(handle_type h)126 impl::file_handle::posix_remap(handle_type h)
127 {
128 PRE(is_valid());
129
130 if (m_handle == h)
131 return;
132
133 if (::dup2(m_handle, h) == -1)
134 throw system_error(IMPL_NAME "::file_handle::posix_remap",
135 "dup2(2) failed", errno);
136
137 if (::close(m_handle) == -1) {
138 ::close(h);
139 throw system_error(IMPL_NAME "::file_handle::posix_remap",
140 "close(2) failed", errno);
141 }
142
143 m_handle = h;
144 }
145
146 impl::file_handle::handle_type
invalid_value(void)147 impl::file_handle::invalid_value(void)
148 {
149 return -1;
150 }
151
152 // ------------------------------------------------------------------------
153 // The "systembuf" class.
154 // ------------------------------------------------------------------------
155
systembuf(handle_type h,std::size_t bufsize)156 impl::systembuf::systembuf(handle_type h, std::size_t bufsize) :
157 m_handle(h),
158 m_bufsize(bufsize),
159 m_read_buf(NULL),
160 m_write_buf(NULL)
161 {
162 PRE(m_handle >= 0);
163 PRE(m_bufsize > 0);
164
165 try {
166 m_read_buf = new char[bufsize];
167 m_write_buf = new char[bufsize];
168 } catch (...) {
169 if (m_read_buf != NULL)
170 delete [] m_read_buf;
171 if (m_write_buf != NULL)
172 delete [] m_write_buf;
173 throw;
174 }
175
176 setp(m_write_buf, m_write_buf + m_bufsize);
177 }
178
~systembuf(void)179 impl::systembuf::~systembuf(void)
180 {
181 delete [] m_read_buf;
182 delete [] m_write_buf;
183 }
184
185 impl::systembuf::int_type
underflow(void)186 impl::systembuf::underflow(void)
187 {
188 PRE(gptr() >= egptr());
189
190 bool ok;
191 ssize_t cnt = ::read(m_handle, m_read_buf, m_bufsize);
192 ok = (cnt != -1 && cnt != 0);
193
194 if (!ok)
195 return traits_type::eof();
196 else {
197 setg(m_read_buf, m_read_buf, m_read_buf + cnt);
198 return traits_type::to_int_type(*gptr());
199 }
200 }
201
202 impl::systembuf::int_type
overflow(int c)203 impl::systembuf::overflow(int c)
204 {
205 PRE(pptr() >= epptr());
206 if (sync() == -1)
207 return traits_type::eof();
208 if (!traits_type::eq_int_type(c, traits_type::eof())) {
209 traits_type::assign(*pptr(), c);
210 pbump(1);
211 }
212 return traits_type::not_eof(c);
213 }
214
215 int
sync(void)216 impl::systembuf::sync(void)
217 {
218 ssize_t cnt = pptr() - pbase();
219
220 bool ok;
221 ok = ::write(m_handle, pbase(), cnt) == cnt;
222
223 if (ok)
224 pbump(-cnt);
225 return ok ? 0 : -1;
226 }
227
228 // ------------------------------------------------------------------------
229 // The "pistream" class.
230 // ------------------------------------------------------------------------
231
pistream(const int fd)232 impl::pistream::pistream(const int fd) :
233 std::istream(NULL),
234 m_systembuf(fd)
235 {
236 rdbuf(&m_systembuf);
237 }
238
239 // ------------------------------------------------------------------------
240 // The "muxer" class.
241 // ------------------------------------------------------------------------
242
243 static int
safe_poll(struct pollfd fds[],nfds_t nfds,int timeout)244 safe_poll(struct pollfd fds[], nfds_t nfds, int timeout)
245 {
246 int ret = ::poll(fds, nfds, timeout);
247 if (ret == -1) {
248 if (errno == EINTR)
249 ret = 0;
250 else
251 throw atf::system_error(IMPL_NAME "::safe_poll", "poll(2) failed",
252 errno);
253 }
254 INV(ret >= 0);
255 return ret;
256 }
257
258 static size_t
safe_read(const int fd,void * buffer,const size_t nbytes,const bool report_errors)259 safe_read(const int fd, void* buffer, const size_t nbytes,
260 const bool report_errors)
261 {
262 int ret;
263 while ((ret = ::read(fd, buffer, nbytes)) == -1 && errno == EINTR) {}
264 if (ret == -1) {
265 INV(errno != EINTR);
266
267 if (report_errors)
268 throw atf::system_error(IMPL_NAME "::safe_read", "read(2) failed",
269 errno);
270 else
271 ret = 0;
272 }
273 INV(ret >= 0);
274 return static_cast< size_t >(ret);
275 }
276
muxer(const int * fds,const size_t nfds,const size_t bufsize)277 impl::muxer::muxer(const int* fds, const size_t nfds, const size_t bufsize) :
278 m_fds(fds),
279 m_nfds(nfds),
280 m_bufsize(bufsize),
281 m_buffers(new std::string[nfds])
282 {
283 }
284
~muxer(void)285 impl::muxer::~muxer(void)
286 {
287 }
288
289 size_t
read_one(const size_t index,const int fd,std::string & accum,const bool report_errors)290 impl::muxer::read_one(const size_t index, const int fd, std::string& accum,
291 const bool report_errors)
292 {
293 atf::auto_array< char > buffer(new char[m_bufsize]);
294 const size_t nbytes = safe_read(fd, buffer.get(), m_bufsize - 1,
295 report_errors);
296 INV(nbytes < m_bufsize);
297 buffer[nbytes] = '\0';
298
299 std::string line(accum);
300
301 size_t line_start = 0;
302 for (size_t i = 0; i < nbytes; i++) {
303 if (buffer[i] == '\n') {
304 line_callback(index, line);
305 line.clear();
306 accum.clear();
307 line_start = i + 1;
308 } else if (buffer[i] == '\r') {
309 // Do nothing.
310 } else {
311 line.append(1, buffer[i]);
312 }
313 }
314 accum.append(&buffer[line_start]);
315
316 return nbytes;
317 }
318
319 void
mux(volatile const bool & terminate)320 impl::muxer::mux(volatile const bool& terminate)
321 {
322 atf::auto_array< struct pollfd > poll_fds(new struct pollfd[m_nfds]);
323 for (size_t i = 0; i < m_nfds; i++) {
324 poll_fds[i].fd = m_fds[i];
325 poll_fds[i].events = POLLIN;
326 }
327
328 size_t nactive = m_nfds;
329 while (nactive > 0 && !terminate) {
330 int ret;
331 while (!terminate && (ret = safe_poll(poll_fds.get(), 2, 250)) == 0) {}
332
333 for (size_t i = 0; !terminate && i < m_nfds; i++) {
334 if (poll_fds[i].events == 0)
335 continue;
336
337 if (poll_fds[i].revents & POLLHUP) {
338 // Any data still available at this point will be processed by
339 // a call to the flush method.
340 poll_fds[i].events = 0;
341
342 INV(nactive >= 1);
343 nactive--;
344 } else if (poll_fds[i].revents & (POLLIN | POLLRDNORM | POLLRDBAND |
345 POLLPRI)) {
346 (void)read_one(i, poll_fds[i].fd, m_buffers[i], true);
347 }
348 }
349 }
350 }
351
352 void
flush(void)353 impl::muxer::flush(void)
354 {
355 for (size_t i = 0; i < m_nfds; i++) {
356 while (read_one(i, m_fds[i], m_buffers[i], false) > 0) {}
357
358 if (!m_buffers[i].empty())
359 line_callback(i, m_buffers[i]);
360 }
361 }
362