1//
2// Automated Testing Framework (atf)
3//
4// Copyright (c) 2007, 2008, 2009, 2010 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
30extern "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
40extern "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
50namespace impl = atf::atf_run;
51#define IMPL_NAME "atf::atf_run"
52
53// ------------------------------------------------------------------------
54// The "file_handle" class.
55// ------------------------------------------------------------------------
56
57impl::file_handle::file_handle(void) :
58    m_handle(invalid_value())
59{
60}
61
62impl::file_handle::file_handle(handle_type h) :
63    m_handle(h)
64{
65    PRE(m_handle != invalid_value());
66}
67
68impl::file_handle::file_handle(const file_handle& fh) :
69    m_handle(fh.m_handle)
70{
71    fh.m_handle = invalid_value();
72}
73
74impl::file_handle::~file_handle(void)
75{
76    if (is_valid())
77        close();
78}
79
80impl::file_handle&
81impl::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
89bool
90impl::file_handle::is_valid(void)
91    const
92{
93    return m_handle != invalid_value();
94}
95
96void
97impl::file_handle::close(void)
98{
99    PRE(is_valid());
100
101    ::close(m_handle);
102
103    m_handle = invalid_value();
104}
105
106impl::file_handle::handle_type
107impl::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
116impl::file_handle::handle_type
117impl::file_handle::get(void)
118    const
119{
120    PRE(is_valid());
121
122    return m_handle;
123}
124
125void
126impl::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
146impl::file_handle::handle_type
147impl::file_handle::invalid_value(void)
148{
149    return -1;
150}
151
152// ------------------------------------------------------------------------
153// The "systembuf" class.
154// ------------------------------------------------------------------------
155
156impl::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
179impl::systembuf::~systembuf(void)
180{
181    delete [] m_read_buf;
182    delete [] m_write_buf;
183}
184
185impl::systembuf::int_type
186impl::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
202impl::systembuf::int_type
203impl::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
215int
216impl::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
232impl::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
243static int
244safe_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
258static size_t
259safe_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
277impl::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
285impl::muxer::~muxer(void)
286{
287}
288
289size_t
290impl::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
319void
320impl::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
352void
353impl::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