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/auto_array.hpp" 45 #include "../atf-c++/detail/exceptions.hpp" 46 #include "../atf-c++/detail/sanity.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::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::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 } 362