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