]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/atf/atf-run/io.cpp
Copy head (r256279) to stable/10 as part of the 10.0-RELEASE cycle.
[FreeBSD/stable/10.git] / contrib / atf / atf-run / io.cpp
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/exceptions.hpp"
45 #include "../atf-c++/detail/sanity.hpp"
46 #include "../atf-c++/utils.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
57 impl::file_handle::file_handle(void) :
58     m_handle(invalid_value())
59 {
60 }
61
62 impl::file_handle::file_handle(handle_type h) :
63     m_handle(h)
64 {
65     PRE(m_handle != invalid_value());
66 }
67
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
74 impl::file_handle::~file_handle(void)
75 {
76     if (is_valid())
77         close();
78 }
79
80 impl::file_handle&
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
90 impl::file_handle::is_valid(void)
91     const
92 {
93     return m_handle != invalid_value();
94 }
95
96 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
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
117 impl::file_handle::get(void)
118     const
119 {
120     PRE(is_valid());
121
122     return m_handle;
123 }
124
125 void
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
147 impl::file_handle::invalid_value(void)
148 {
149     return -1;
150 }
151
152 // ------------------------------------------------------------------------
153 // The "systembuf" class.
154 // ------------------------------------------------------------------------
155
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
179 impl::systembuf::~systembuf(void)
180 {
181     delete [] m_read_buf;
182     delete [] m_write_buf;
183 }
184
185 impl::systembuf::int_type
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
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
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
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
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
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
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
285 impl::muxer::~muxer(void)
286 {
287 }
288
289 size_t
290 impl::muxer::read_one(const size_t index, const int fd, std::string& accum,
291                       const bool report_errors)
292 {
293     atf::utils::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
320 impl::muxer::mux(volatile const bool& terminate)
321 {
322     atf::utils::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
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 }