// // Automated Testing Framework (atf) // // Copyright (c) 2007 The NetBSD Foundation, Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #if !defined(_ATF_RUN_IO_HPP_) #define _ATF_RUN_IO_HPP_ #include #include #include #include "fs.hpp" #include "../atf-c++/utils.hpp" namespace atf { namespace atf_run { // ------------------------------------------------------------------------ // The "file_handle" class. // ------------------------------------------------------------------------ //! //! \brief Simple RAII model for system file handles. //! //! The \a file_handle class is a simple RAII model for native system file //! handles. This class wraps one of such handles grabbing its ownership, //! and automaticaly closes it upon destruction. It is basically used //! inside the library to avoid leaking open file handles, shall an //! unexpected execution trace occur. //! //! A \a file_handle object can be copied but doing so invalidates the //! source object. There can only be a single valid \a file_handle object //! for a given system file handle. This is similar to std::auto_ptr\<\>'s //! semantics. //! //! This class also provides some convenience methods to issue special file //! operations under their respective platforms. //! class file_handle { public: //! //! \brief Opaque name for the native handle type. //! //! Each operating system identifies file handles using a specific type. //! The \a handle_type type is used to transparently refer to file //! handles regarless of the operating system in which this class is //! used. //! //! If this class is used in a POSIX system, \a NativeSystemHandle is //! an integer type while it is a \a HANDLE in a Win32 system. //! typedef int handle_type; //! //! \brief Constructs an invalid file handle. //! //! This constructor creates a new \a file_handle object that represents //! an invalid file handle. An invalid file handle can be copied but //! cannot be manipulated in any way (except checking for its validity). //! //! \see is_valid() //! file_handle(void); //! //! \brief Constructs a new file handle from a native file handle. //! //! This constructor creates a new \a file_handle object that takes //! ownership of the given \a h native file handle. The user must not //! close \a h on his own during the lifetime of the new object. //! Ownership can be reclaimed using disown(). //! //! \pre The native file handle must be valid; a close operation must //! succeed on it. //! //! \see disown() //! file_handle(handle_type h); //! //! \brief Copy constructor; invalidates the source handle. //! //! This copy constructor creates a new file handle from a given one. //! Ownership of the native file handle is transferred to the new //! object, effectively invalidating the source file handle. This //! avoids having two live \a file_handle objects referring to the //! same native file handle. The source file handle need not be //! valid in the name of simplicity. //! //! \post The source file handle is invalid. //! \post The new file handle owns the source's native file handle. //! file_handle(const file_handle& fh); //! //! \brief Releases resources if the handle is valid. //! //! If the file handle is valid, the destructor closes it. //! //! \see is_valid() //! ~file_handle(void); //! //! \brief Assignment operator; invalidates the source handle. //! //! This assignment operator transfers ownership of the RHS file //! handle to the LHS one, effectively invalidating the source file //! handle. This avoids having two live \a file_handle objects //! referring to the same native file handle. The source file //! handle need not be valid in the name of simplicity. //! //! \post The RHS file handle is invalid. //! \post The LHS file handle owns RHS' native file handle. //! \return A reference to the LHS file handle. //! file_handle& operator=(const file_handle& fh); //! //! \brief Checks whether the file handle is valid or not. //! //! Returns a boolean indicating whether the file handle is valid or //! not. If the file handle is invalid, no other applications can be //! executed other than the destructor. //! //! \return True if the file handle is valid; false otherwise. //! bool is_valid(void) const; //! //! \brief Closes the file handle. //! //! Explicitly closes the file handle, which must be valid. Upon //! exit, the handle is not valid any more. //! //! \pre The file handle is valid. //! \post The file handle is invalid. //! \post The native file handle is closed. //! void close(void); //! //! \brief Reclaims ownership of the native file handle. //! //! Explicitly reclaims ownership of the native file handle contained //! in the \a file_handle object, returning the native file handle. //! The caller is responsible of closing it later on. //! //! \pre The file handle is valid. //! \post The file handle is invalid. //! \return The native file handle. //! handle_type disown(void); //! //! \brief Gets the native file handle. //! //! Returns the native file handle for the \a file_handle object. //! The caller can issue any operation on it except closing it. //! If closing is required, disown() shall be used. //! //! \pre The file handle is valid. //! \return The native file handle. //! handle_type get(void) const; //! //! \brief Changes the native file handle to the given one. //! //! Given a new native file handle \a h, this operation assigns this //! handle to the current object, closing its old native file handle. //! In other words, it first calls dup2() to remap the old handle to //! the new one and then closes the old handle. //! //! If \a h matches the current value of the handle, this is a no-op. //! This is done for simplicity, to avoid the caller having to check //! this condition on its own. //! //! If \a h is open, it is automatically closed by dup2(). //! //! This operation is only available in POSIX systems. //! //! \pre The file handle is valid. //! \pre The native file handle \a h is valid; i.e., it must be //! closeable. //! \post The file handle's native file handle is \a h. //! \throw system_error If the internal remapping operation fails. //! void posix_remap(handle_type h); private: //! //! \brief Internal handle value. //! //! This variable holds the native handle value for the file handle //! hold by this object. It is interesting to note that this needs //! to be mutable because the copy constructor and the assignment //! operator invalidate the source object. //! mutable handle_type m_handle; //! //! \brief Constant function representing an invalid handle value. //! //! Returns the platform-specific handle value that represents an //! invalid handle. This is a constant function rather than a regular //! constant because, in the latter case, we cannot define it under //! Win32 due to the value being of a complex type. //! static handle_type invalid_value(void); }; // ------------------------------------------------------------------------ // The "systembuf" class. // ------------------------------------------------------------------------ //! //! \brief std::streambuf implementation for system file handles. //! //! systembuf provides a std::streambuf implementation for system file //! handles. Contrarywise to file_handle, this class does \b not take //! ownership of the native file handle; this should be taken care of //! somewhere else. //! //! This class follows the expected semantics of a std::streambuf object. //! However, it is not copyable to avoid introducing inconsistences with //! the on-disk file and the in-memory buffers. //! class systembuf : public std::streambuf, atf::utils::noncopyable { public: typedef int handle_type; //! //! \brief Constructs a new systembuf for the given file handle. //! //! This constructor creates a new systembuf object that reads or //! writes data from/to the \a h native file handle. This handle //! is \b not owned by the created systembuf object; the code //! should take care of it externally. //! //! This class buffers input and output; the buffer size may be //! tuned through the \a bufsize parameter, which defaults to 8192 //! bytes. //! //! \see pistream. //! explicit systembuf(handle_type h, std::size_t bufsize = 8192); ~systembuf(void); private: //! //! \brief Native file handle used by the systembuf object. //! handle_type m_handle; //! //! \brief Internal buffer size used during read and write operations. //! std::size_t m_bufsize; //! //! \brief Internal buffer used during read operations. //! char* m_read_buf; //! //! \brief Internal buffer used during write operations. //! char* m_write_buf; protected: //! //! \brief Reads new data from the native file handle. //! //! This operation is called by input methods when there are no more //! data in the input buffer. The function fills the buffer with new //! data, if available. //! //! \pre All input positions are exhausted (gptr() >= egptr()). //! \post The input buffer has new data, if available. //! \returns traits_type::eof() if a read error occurrs or there are //! no more data to be read. Otherwise returns //! traits_type::to_int_type(*gptr()). //! virtual int_type underflow(void); //! //! \brief Makes room in the write buffer for additional data. //! //! This operation is called by output methods when there is no more //! space in the output buffer to hold a new element. The function //! first flushes the buffer's contents to disk and then clears it to //! leave room for more characters. The given \a c character is //! stored at the beginning of the new space. //! //! \pre All output positions are exhausted (pptr() >= epptr()). //! \post The output buffer has more space if no errors occurred //! during the write to disk. //! \post *(pptr() - 1) is \a c. //! \returns traits_type::eof() if a write error occurrs. Otherwise //! returns traits_type::not_eof(c). //! virtual int_type overflow(int c); //! //! \brief Flushes the output buffer to disk. //! //! Synchronizes the systembuf buffers with the contents of the file //! associated to this object through the native file handle. The //! output buffer is flushed to disk and cleared to leave new room //! for more data. //! //! \returns 0 on success, -1 if an error occurred. //! virtual int sync(void); }; // ------------------------------------------------------------------------ // The "pistream" class. // ------------------------------------------------------------------------ //! //! \brief Child process' output stream. //! //! The pistream class represents an output communication channel with the //! child process. The child process writes data to this stream and the //! parent process can read it through the pistream object. In other //! words, from the child's point of view, the communication channel is an //! output one, but from the parent's point of view it is an input one; //! hence the confusing pistream name. //! //! pistream objects cannot be copied because they own the file handle //! they use to communicate with the child and because they buffer data //! that flows through the communication channel. //! //! A pistream object behaves as a std::istream stream in all senses. //! The class is only provided because it must provide a method to let //! the caller explicitly close the communication channel. //! //! \remark Blocking remarks: Functions that read data from this //! stream can block if the associated file handle blocks during the read. //! As this class is used to communicate with child processes through //! anonymous pipes, the most typical blocking condition happens when the //! child has no more data to send to the pipe's system buffer. When //! this happens, the buffer eventually empties and the system blocks //! until the writer generates some data. //! class pistream : public std::istream, utils::noncopyable { //! //! \brief The systembuf object used to manage this stream's data. //! systembuf m_systembuf; public: //! //! \brief Creates a new process' output stream. //! //! Given a file handle, this constructor creates a new pistream //! object that owns the given file handle \a fh. Ownership of //! \a fh is transferred to the created pistream object. //! //! \pre \a fh is valid. //! \post \a fh is invalid. //! \post The new pistream object owns \a fh. //! explicit pistream(const int); }; // ------------------------------------------------------------------------ // The "muxer" class. // ------------------------------------------------------------------------ class muxer : utils::noncopyable { const int* m_fds; const size_t m_nfds; const size_t m_bufsize; atf::utils::auto_array< std::string > m_buffers; protected: virtual void line_callback(const size_t, const std::string&) = 0; size_t read_one(const size_t, const int, std::string&, const bool); public: muxer(const int*, const size_t, const size_t bufsize = 1024); virtual ~muxer(void); void mux(volatile const bool&); void flush(void); }; } // namespace atf_run } // namespace atf #endif // !defined(_ATF_RUN_IO_HPP_)