Home | History | Annotate | Line # | Download | only in tools
      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 <cassert>
     38 #include <cerrno>
     39 #include <cstring>
     40 
     41 #include "auto_array.hpp"
     42 #include "exceptions.hpp"
     43 #include "io.hpp"
     44 
     45 namespace impl = tools::io;
     46 #define IMPL_NAME "tools::io"
     47 
     48 // ------------------------------------------------------------------------
     49 // The "file_handle" class.
     50 // ------------------------------------------------------------------------
     51 
     52 impl::file_handle::file_handle(void) :
     53     m_handle(invalid_value())
     54 {
     55 }
     56 
     57 impl::file_handle::file_handle(handle_type h) :
     58     m_handle(h)
     59 {
     60     assert(m_handle != invalid_value());
     61 }
     62 
     63 impl::file_handle::file_handle(const file_handle& fh) :
     64     m_handle(fh.m_handle)
     65 {
     66     fh.m_handle = invalid_value();
     67 }
     68 
     69 impl::file_handle::~file_handle(void)
     70 {
     71     if (is_valid())
     72         close();
     73 }
     74 
     75 impl::file_handle&
     76 impl::file_handle::operator=(const file_handle& fh)
     77 {
     78     m_handle = fh.m_handle;
     79     fh.m_handle = invalid_value();
     80 
     81     return *this;
     82 }
     83 
     84 bool
     85 impl::file_handle::is_valid(void)
     86     const
     87 {
     88     return m_handle != invalid_value();
     89 }
     90 
     91 void
     92 impl::file_handle::close(void)
     93 {
     94     assert(is_valid());
     95 
     96     ::close(m_handle);
     97 
     98     m_handle = invalid_value();
     99 }
    100 
    101 impl::file_handle::handle_type
    102 impl::file_handle::disown(void)
    103 {
    104     assert(is_valid());
    105 
    106     handle_type h = m_handle;
    107     m_handle = invalid_value();
    108     return h;
    109 }
    110 
    111 impl::file_handle::handle_type
    112 impl::file_handle::get(void)
    113     const
    114 {
    115     assert(is_valid());
    116 
    117     return m_handle;
    118 }
    119 
    120 void
    121 impl::file_handle::posix_remap(handle_type h)
    122 {
    123     assert(is_valid());
    124 
    125     if (m_handle == h)
    126         return;
    127 
    128     if (::dup2(m_handle, h) == -1)
    129         throw tools::system_error(IMPL_NAME "::file_handle::posix_remap",
    130                                 "dup2(2) failed", errno);
    131 
    132     if (::close(m_handle) == -1) {
    133         ::close(h);
    134         throw tools::system_error(IMPL_NAME "::file_handle::posix_remap",
    135                                 "close(2) failed", errno);
    136     }
    137 
    138     m_handle = h;
    139 }
    140 
    141 impl::file_handle::handle_type
    142 impl::file_handle::invalid_value(void)
    143 {
    144     return -1;
    145 }
    146 
    147 // ------------------------------------------------------------------------
    148 // The "systembuf" class.
    149 // ------------------------------------------------------------------------
    150 
    151 impl::systembuf::systembuf(handle_type h, std::size_t bufsize) :
    152     m_handle(h),
    153     m_bufsize(bufsize),
    154     m_read_buf(NULL),
    155     m_write_buf(NULL)
    156 {
    157     assert(m_handle >= 0);
    158     assert(m_bufsize > 0);
    159 
    160     try {
    161         m_read_buf = new char[bufsize];
    162         m_write_buf = new char[bufsize];
    163     } catch (...) {
    164         if (m_read_buf != NULL)
    165             delete [] m_read_buf;
    166         if (m_write_buf != NULL)
    167             delete [] m_write_buf;
    168         throw;
    169     }
    170 
    171     setp(m_write_buf, m_write_buf + m_bufsize);
    172 }
    173 
    174 impl::systembuf::~systembuf(void)
    175 {
    176     delete [] m_read_buf;
    177     delete [] m_write_buf;
    178 }
    179 
    180 impl::systembuf::int_type
    181 impl::systembuf::underflow(void)
    182 {
    183     assert(gptr() >= egptr());
    184 
    185     bool ok;
    186     ssize_t cnt = ::read(m_handle, m_read_buf, m_bufsize);
    187     ok = (cnt != -1 && cnt != 0);
    188 
    189     if (!ok)
    190         return traits_type::eof();
    191     else {
    192         setg(m_read_buf, m_read_buf, m_read_buf + cnt);
    193         return traits_type::to_int_type(*gptr());
    194     }
    195 }
    196 
    197 impl::systembuf::int_type
    198 impl::systembuf::overflow(int c)
    199 {
    200     assert(pptr() >= epptr());
    201     if (sync() == -1)
    202         return traits_type::eof();
    203     if (!traits_type::eq_int_type(c, traits_type::eof())) {
    204         traits_type::assign(*pptr(), c);
    205         pbump(1);
    206     }
    207     return traits_type::not_eof(c);
    208 }
    209 
    210 int
    211 impl::systembuf::sync(void)
    212 {
    213     ssize_t cnt = pptr() - pbase();
    214 
    215     bool ok;
    216     ok = ::write(m_handle, pbase(), cnt) == cnt;
    217 
    218     if (ok)
    219         pbump(-cnt);
    220     return ok ? 0 : -1;
    221 }
    222 
    223 // ------------------------------------------------------------------------
    224 // The "pistream" class.
    225 // ------------------------------------------------------------------------
    226 
    227 impl::pistream::pistream(const int fd) :
    228     std::istream(NULL),
    229     m_systembuf(fd)
    230 {
    231     rdbuf(&m_systembuf);
    232 }
    233 
    234 // ------------------------------------------------------------------------
    235 // The "muxer" class.
    236 // ------------------------------------------------------------------------
    237 
    238 static int
    239 safe_poll(struct pollfd fds[], nfds_t nfds, int timeout)
    240 {
    241     int ret = ::poll(fds, nfds, timeout);
    242     if (ret == -1) {
    243         if (errno == EINTR)
    244             ret = 0;
    245         else
    246             throw tools::system_error(IMPL_NAME "::safe_poll", "poll(2) failed",
    247                                     errno);
    248     }
    249     assert(ret >= 0);
    250     return ret;
    251 }
    252 
    253 static size_t
    254 safe_read(const int fd, void* buffer, const size_t nbytes,
    255           const bool report_errors)
    256 {
    257     int ret;
    258     while ((ret = ::read(fd, buffer, nbytes)) == -1 && errno == EINTR) {}
    259     if (ret == -1) {
    260         assert(errno != EINTR);
    261 
    262         if (report_errors)
    263             throw tools::system_error(IMPL_NAME "::safe_read", "read(2) failed",
    264                                     errno);
    265         else
    266             ret = 0;
    267     }
    268     assert(ret >= 0);
    269     return static_cast< size_t >(ret);
    270 }
    271 
    272 impl::muxer::muxer(const int* fds, const size_t nfds, const size_t bufsize) :
    273     m_fds(fds),
    274     m_nfds(nfds),
    275     m_bufsize(bufsize),
    276     m_buffers(new std::string[nfds])
    277 {
    278 }
    279 
    280 impl::muxer::~muxer(void)
    281 {
    282 }
    283 
    284 size_t
    285 impl::muxer::read_one(const size_t index, const int fd, std::string& accum,
    286                       const bool report_errors)
    287 {
    288     tools::auto_array< char > buffer(new char[m_bufsize]);
    289     const size_t nbytes = safe_read(fd, buffer.get(), m_bufsize - 1,
    290                                     report_errors);
    291     assert(nbytes < m_bufsize);
    292     buffer[nbytes] = '\0';
    293 
    294     std::string line(accum);
    295 
    296     size_t line_start = 0;
    297     for (size_t i = 0; i < nbytes; i++) {
    298         if (buffer[i] == '\n') {
    299             line_callback(index, line);
    300             line.clear();
    301             accum.clear();
    302             line_start = i + 1;
    303         } else if (buffer[i] == '\r') {
    304             // Do nothing.
    305         } else {
    306             line.append(1, buffer[i]);
    307         }
    308     }
    309     accum.append(&buffer[line_start]);
    310 
    311     return nbytes;
    312 }
    313 
    314 void
    315 impl::muxer::mux(volatile const bool& terminate)
    316 {
    317     tools::auto_array< struct pollfd > poll_fds(new struct pollfd[m_nfds]);
    318     for (size_t i = 0; i < m_nfds; i++) {
    319         poll_fds[i].fd = m_fds[i];
    320         poll_fds[i].events = POLLIN;
    321     }
    322 
    323     size_t nactive = m_nfds;
    324     while (nactive > 0 && !terminate) {
    325         int ret;
    326         while (!terminate && (ret = safe_poll(poll_fds.get(), 2, 250)) == 0) {}
    327 
    328         for (size_t i = 0; !terminate && i < m_nfds; i++) {
    329             if (poll_fds[i].events == 0)
    330                 continue;
    331 
    332             if (poll_fds[i].revents & POLLHUP) {
    333                 // Any data still available at this point will be processed by
    334                 // a call to the flush method.
    335                 poll_fds[i].events = 0;
    336 
    337                 assert(nactive >= 1);
    338                 nactive--;
    339             } else if (poll_fds[i].revents & (POLLIN | POLLRDNORM | POLLRDBAND |
    340                                        POLLPRI)) {
    341                 (void)read_one(i, poll_fds[i].fd, m_buffers[i], true);
    342             }
    343         }
    344     }
    345 }
    346 
    347 void
    348 impl::muxer::flush(void)
    349 {
    350     for (size_t i = 0; i < m_nfds; i++) {
    351         while (read_one(i, m_fds[i], m_buffers[i], false) > 0) {}
    352 
    353         if (!m_buffers[i].empty())
    354             line_callback(i, m_buffers[i]);
    355     }
    356 }
    357