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