#ifndef STDIO_REDIRECTER_CLASS #define STDIO_REDIRECTER_CLASS /*****************************************************************************\ * * * Name : stdio_redirecter * * Author : Chris Koeritz * * * ******************************************************************************* * Copyright (c) 2005-$now By Author. 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 is online at: * * http://www.fsf.org/copyleft/gpl.html * * Please send any updates to: fred@gruntose.com * \*****************************************************************************/ #include #include #include namespace application { //! Redirects I/O for a newly launched application. class stdio_redirecter : public virtual basis::root_object { public: stdio_redirecter(const basis::astring &command, const basis::astring ¶meters); //!< controls the I/O for a program started with "command" and "parameters". /*!< this creates an io redirecter that will trap the inputs and outputs of a program called "command" that is launched with the "parameters". the program can be communicated with via the read and write methods below. */ stdio_redirecter(); //!< creates a blank redirecter which should be started up with reset(). ~stdio_redirecter(); //!< shuts down the program that was launched, if it's still running. basis::outcome reset(const basis::astring &command, const basis::astring ¶meters); //!< shuts down the active program and starts with new parameters. DEFINE_CLASS_NAME("stdio_redirecter"); enum outcomes { OKAY = basis::common::OKAY, NOT_FOUND = basis::common::NOT_FOUND, //!< the file to launch was not found. NONE_READY = basis::common::NONE_READY, //!< there was no data available. PARTIAL = basis::common::PARTIAL, //!< only some of the bytes could be stored. ACCESS_DENIED = basis::common::ACCESS_DENIED //!< the OS told us we could not. }; basis::outcome health() const { return _persistent_result; } //!< if the object constructed successfully, this returns OKAY. /*!< otherwise an error occurred during the pipe creation or process fork. */ bool running(); //!< returns true if the application is still running. /*!< if it exited on its own, then this will return false. */ int exit_value() const { return _exit_value; } //!< returns the exit value from the app after it was launched. /*!< this is only valid if the launch succeeded, the app ran, and now the running() method is returning false because the application exited. */ int process_id() const { return _process_id; } //!< the process id of the launched application. /*!< this is only valid if the app is still running. */ void zap_program(); //!< attempts to force a shutdown of the launched application. /*!< this also closes out all of our pipes and handles. */ basis::outcome read(basis::byte_array &received); //!< attempts to read bytes from the program's standard output. /*!< if any bytes were found, OKAY is returned and the bytes are stored in "received". */ basis::outcome write(const basis::byte_array &to_write, int &written); //!< writes the bytes in "to_write" into the program's standard input. /*!< OKAY is returned if all were successfully written. the number of bytes that were actually sent to the program is put in "written". if only a portion of the bytes could be written, then PARTIAL is returned. */ basis::outcome write(const basis::astring &to_write, int &written); //!< sends a string "to_write" to the launched program's standard input. basis::outcome read_stderr(basis::byte_array &received); //!< reads from the program's standard error stream similarly to read(). void close_input(); //!< shuts down the standard input to the program. /*!< this simulates when the program receives an end of file on its main input. */ // internal use only... void std_thread_action(bool is_stdout); //!< invoked by our threads when data becomes available. /*!< if "is_stdout" is true, then that's the pipe we get data from. otherwise we will try to get data from stderr. */ private: #ifdef __UNIX__ int _output_fds[2]; //!< file descriptors for parent's output. int _input_fds[2]; //!< file descriptors for parent's input. int _stderr_fds[2]; //!< file descriptors for standard error from child. #endif #ifdef __WIN32__ void *_child_in, *_child_out, *_child_err; //!< child process pipes replace io. void *_parent_in, *_parent_out, *_parent_err; //!< our access to the pipes. void *_app_handle; //!< refers to the launched application. #endif basis::astring *_command; //!< the application that we'll start and switch I/O on. basis::astring *_parms; //!< the parameters for the app we will launch. basis::outcome _persistent_result; //!< this records failures during construction. processes::ethread *_stdout_reader; //!< gets data from process's stdout. processes::ethread *_stderr_reader; //!< gets data from process's stderr. basis::byte_array *_stdout_queue; //!< holds data acquired from stdout. basis::byte_array *_stderr_queue; //!< holds data acquired from stderr. basis::mutex *_lock; //!< protects our queues. int _process_id; //!< pid for the launched program. int _exit_value; //!< stored once when we notice app is gone. basis::outcome create_pipes(); //!< creates the three pipes that the child will be given as stdin, stdout and stderr. basis::outcome launch_program(int &new_process_id); //!< launches the application using the previously established pipes. }; } //namespace. #endif