xref: /netbsd-src/external/bsd/atf/dist/tools/io.cpp (revision d780102efefa02003390cc43ea410dbd0ebb4a85)
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 
file_handle(void)52 impl::file_handle::file_handle(void) :
53     m_handle(invalid_value())
54 {
55 }
56 
file_handle(handle_type h)57 impl::file_handle::file_handle(handle_type h) :
58     m_handle(h)
59 {
60     assert(m_handle != invalid_value());
61 }
62 
file_handle(const file_handle & fh)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 
~file_handle(void)69 impl::file_handle::~file_handle(void)
70 {
71     if (is_valid())
72         close();
73 }
74 
75 impl::file_handle&
operator =(const file_handle & fh)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
is_valid(void) const85 impl::file_handle::is_valid(void)
86     const
87 {
88     return m_handle != invalid_value();
89 }
90 
91 void
close(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
disown(void)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
get(void) const112 impl::file_handle::get(void)
113     const
114 {
115     assert(is_valid());
116 
117     return m_handle;
118 }
119 
120 void
posix_remap(handle_type h)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
invalid_value(void)142 impl::file_handle::invalid_value(void)
143 {
144     return -1;
145 }
146 
147 // ------------------------------------------------------------------------
148 // The "systembuf" class.
149 // ------------------------------------------------------------------------
150 
systembuf(handle_type h,std::size_t bufsize)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 
~systembuf(void)174 impl::systembuf::~systembuf(void)
175 {
176     delete [] m_read_buf;
177     delete [] m_write_buf;
178 }
179 
180 impl::systembuf::int_type
underflow(void)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
overflow(int c)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
sync(void)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 
pistream(const int fd)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
safe_poll(struct pollfd fds[],nfds_t nfds,int timeout)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
safe_read(const int fd,void * buffer,const size_t nbytes,const bool report_errors)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 
muxer(const int * fds,const size_t nfds,const size_t bufsize)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 
~muxer(void)280 impl::muxer::~muxer(void)
281 {
282 }
283 
284 size_t
read_one(const size_t index,const int fd,std::string & accum,const bool report_errors)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
mux(volatile const bool & terminate)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
flush(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