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