// \file child.hpp // // Copyright (C) 2014 MicroNeil Research Corporation. // // This program is part of the MicroNeil Research Open Library Project. For // more information go to http://www.microneil.com/OpenLibrary/index.html // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the // Free Software Foundation; either version 2 of the License, or (at your // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // // You should have received a copy of the GNU General Public License along with // this program; if not, write to the Free Software Foundation, Inc., 59 Temple // Place, Suite 330, Boston, MA 02111-1307 USA //============================================================================== /* \brief The child module provides classes to spawn and communicate with child processes. */ #ifndef CHILD_HPP #define CHILD_HPP #ifdef _WIN32 #include #endif #include #include #include #include #include #include namespace CodeDweller { /** \namespace CodeDweller The CodeDweller namespace contains components providing high-level functionality for applications. */ /** Class that abstracts the creation of child processes. This class provides functionality to create a child process, communicate with the child process via streams and signals, and obtain the exit code of the child process. */ class Child { private: /// Streambuf class for reading the standard output of the child /// process. class ReadStreambuf : public std::streambuf { friend class Child; public: /// Reader streambuf constructor. // // \param[in] bufferSize is the size in bytes of the input // buffer. // explicit ReadStreambuf(std::size_t bufferSize = 4096); #ifdef _WIN32 /// Set the handle to read the standard output of the child /// process. // // \param[in] inHandle is the input handle for the standard // output of the child process. // void setInputHandle(HANDLE inHandle); #else /// Set the file descriptor to read the standard output of the /// child process. // // \param[in] inFd is the input file descriptor for the standard // output of the child process. // void setInputFileDescriptor(int inFd); #endif private: /** Return the number of bytes that can be read without blocking. This method checks if any input is available from the pipe, and returns the number of bytes in the input buffer plus 1. Reading that number of bytes will not block. Reading a larger number of bytes might block. \returns minimum number of bytes that can be read without blocking. */ size_t numBytesAvailable() const; /// Override streambuf::underflow(). int_type underflow(); /// Copy constructor not implemented. ReadStreambuf(const ReadStreambuf &); /// Copy constructor not implemented. ReadStreambuf &operator=(const ReadStreambuf &); /// Input handle. #ifdef _WIN32 HANDLE inputHandle; #else int inputFileDescriptor; #endif /// Read buffer. std::vector buffer; }; /// Streambuf class for writing to the standard input of the child /// process. // // Note: If an error occurs when writing the output from the // parent process, the output buffer is cleared. // class WriteStreambuf : public std::streambuf { public: /// Writer streambuf constructor. // // \param[in] bufferSize is the size in bytes of the input // buffer. // explicit WriteStreambuf(std::size_t bufferSize = 4096); #ifdef _WIN32 /// Set the handle to write the standard input of the child /// process. // // \param[in] outHandle is the output handle for the standard // input of the child process. // void setOutputHandle(HANDLE outHandle); #else /// Set the file descriptor to write the standard input of the /// child process. // // \param[in] outFd is the output file descriptor for the // standard input of the child process. // void setOutputFileDescriptor(int outFd); #endif private: /// Flush the output buffer. void flushBuffer(); /// Override streambuf::overflow(). int_type overflow(int_type ch); /// Override streambuf::sync(). int sync(); /// Copy constructor not implemented. WriteStreambuf(const WriteStreambuf &); /// Copy constructor not implemented. WriteStreambuf &operator=(const WriteStreambuf &); /// Output handle. #ifdef _WIN32 HANDLE outputHandle; #else int outputFileDescriptor; #endif /// Write buffer. std::vector buffer; }; /// Stream buffer for reading from the stdout of the child process; ReadStreambuf readStreambuf; /// Stream buffer for writing to the stdin of the child process; WriteStreambuf writeStreambuf; public: /** Constructor for spawning with command-line parameters. The constructor configures the object, but doesn't spawn the child process. \param[in] args contains the child executable file name and command-line parameters. args[0] contains the full path of the executable, and args[1] thru args[n] are the command-line parameters. \param[in] bufSize is the buffer size of the reader and writer streams used to communicate with the child process. */ Child(std::vector args, size_t bufSize = 4096); /** Constructor for spawning without command-line parameters. The constructor configures the object, but doesn't spawn the child process. \param[in] childpath contains the child executable file name. \param[in] bufSize is the buffer size of the reader and writer streams used to communicate with the child process. */ Child(std::string childpath, size_t bufSize = 4096); /** Destructor terminates the child process. */ ~Child(); /// Input stream to read data from the child's standard output. std::istream reader; /** Get the number of bytes available for input. @returns number of bytes that can be read from reader without blocking. */ size_t numBytesAvailable() const; /// Output stream to write data to the child's standard input. std::ostream writer; /** Spawn the child process. \throws runtime_error if an error occurs. */ void run(); /** Terminite the child process. \throws runtime_error if an error occurs. \throws logic_error if the child process is not running. */ void terminate(); /** Check whether the child process has exited. \returns True if the child process has exited, false otherwise. \throws runtime_error if an error occurs. \throws logic_error if the child process is not running. */ bool isDone(); /** Get the exit value of the child process. \returns The exit value of the child process if the child process has exited. \throws runtime_error if an error occurs. \throws logic_error if the child process has not exited. \throws logic_error if the child process is not running. */ int32_t result(); private: /// Exit code to use when terminating the child process. static const uint32_t terminateExitCode = 0; /// True if the child process was successfully started. bool childStarted; /// True if the child process has exited. bool childExited; /// Initialize data members. void init(); /// Child executable path and command-line parameters. std::vector cmdArgs; /// Child executable path and command-line parameters. std::string cmdline; #ifdef _WIN32 /// Child's process handle. HANDLE childProcess; /// Child's thread handle. HANDLE childThread; #else /// Child process ID. pid_t childPid; #endif /// Exit value of the process. int32_t exitCode; /// True if the exit code has been obtained. bool exitCodeObtainedFlag; /// Return text for the most recent error. // // \returns Human-readable description of the most recent error. // static std::string getErrorText(); }; } #endif // CHILD_HPP