Files
eepp/include/eepp/system/process.hpp

251 lines
9.6 KiB
C++

#ifndef EE_SYSTEM_PROCESS_HPP
#define EE_SYSTEM_PROCESS_HPP
#include <eepp/config.hpp>
#include <eepp/system/mutex.hpp>
#include <eepp/system/time.hpp>
#include <functional>
#include <string>
#include <thread>
#include <unordered_map>
namespace EE { namespace System {
class EE_API Process {
public:
enum Options {
// stdout and stderr are the same FILE.
CombinedStdoutStderr = 0x1,
// The child process should inherit the environment variables of the parent.
InheritEnvironment = 0x2,
// Enable asynchronous reading of stdout/stderr before it has completed.
EnableAsync = 0x4,
// Enable the child process to be spawned with no window visible if supported
// by the platform.
NoWindow = 0x8,
// Search for program names in the PATH variable. Always enabled on Windows.
// Note: this will **not** search for paths in any provided custom environment
// and instead uses the PATH of the spawning process.
SearchUserPath = 0x10
};
static inline constexpr Uint32 getDefaultOptions() {
return Options::SearchUserPath | Options::InheritEnvironment | Options::NoWindow;
}
struct Config {
size_t bufferSize = 131072;
Uint32 options = getDefaultOptions();
};
typedef std::function<void( const char* bytes, size_t n )> ReadFn;
static std::vector<std::string> parseArgs( const std::string& str );
Process();
/** @brief Create a process.
** @param command Command line to execute for this process.
** @param options A bit field of Options's to pass. */
Process( const std::string& command, Uint32 options = getDefaultOptions(),
const std::unordered_map<std::string, std::string>& environment = {},
const std::string& workingDirectory = "", const size_t& bufferSize = 132072 );
/** @brief Create a process.
** @param command Command line to execute for this process.
** @param args Command line arguments
** @param options A bit field of Options's to pass. */
Process( const std::string& command, const std::vector<std::string>& args,
Uint32 options = getDefaultOptions(),
const std::unordered_map<std::string, std::string>& environment = {},
const std::string& workingDirectory = "", const size_t& bufferSize = 132072 );
~Process();
/** @brief Create a process.
** @param command Command line to execute for this process.
** @param options A bit field of Options's to pass.
** @return On success true is returned. */
bool create( const std::string& command, Uint32 options = getDefaultOptions(),
const std::unordered_map<std::string, std::string>& environment = {},
const std::string& workingDirectory = "" );
/** @brief Create a process.
** @param command Command line to execute for this process.
** @param args Command line arguments to execute for this process.
** @param options A bit field of Options's to pass.
** @return On success true is returned. */
bool create( const std::string& command, const std::string& args,
Uint32 options = getDefaultOptions(),
const std::unordered_map<std::string, std::string>& environment = {},
const std::string& workingDirectory = "" );
/** @brief Create a process.
** @param command Command line to execute for this process.
** @param args Command line arguments to execute for this process.
** @param options A bit field of Options's to pass.
** @return On success true is returned. */
bool create( const std::string& command, const std::vector<std::string>& args,
Uint32 options = getDefaultOptions(),
const std::unordered_map<std::string, std::string>& environment = {},
const std::string& workingDirectory = "" );
/** @brief Starts a new thread to receive all stdout and stderr data */
void startAsyncRead( ReadFn readStdOut = nullptr, ReadFn readStdErr = nullptr );
/** @brief Read all standard output from the child process.
** @param buffer The buffer to read into.
** @return The number of bytes actually read into buffer. Can only be 0 if the
** process has complete.
**
** The only safe way to read from the standard output of a process during it's
** execution is to use the `Option::EnableAsync` option in
** conjuction with this method. */
size_t readAllStdOut( std::string& buffer, Time timeout = Time::Zero );
/** @brief Read the standard output from the child process.
** @param buffer The buffer to read into.
** @return The number of bytes actually read into buffer. Can only be 0 if the
** process has complete.
**
** The only safe way to read from the standard output of a process during it's
** execution is to use the `Option::EnableAsync` option in
** conjuction with this method. */
size_t readStdOut( std::string& buffer );
/** @brief Read the standard output from the child process.
** @param buffer The buffer to read into.
** @param size The maximum number of bytes to read.
** @return The number of bytes actually read into buffer. Can only be 0 if the
** process has complete.
**
** The only safe way to read from the standard output of a process during it's
** execution is to use the `Option::EnableAsync` option in
** conjuction with this method. */
size_t readStdOut( char* const buffer, const size_t& size );
/** @brief Read all the standard error from the child process.
** @param buffer The buffer to read into.
** @return The number of bytes actually read into buffer. Can only be 0 if the
** process has complete.
**
** The only safe way to read from the standard error of a process during it's
** execution is to use the `Option::EnableAsync` option in
** conjuction with this method. */
size_t readAllStdErr( std::string& buffer, Time timeout = Time::Zero );
/** @brief Read the standard error from the child process.
** @param buffer The buffer to read into.
** @return The number of bytes actually read into buffer. Can only be 0 if the
** process has complete.
**
** The only safe way to read from the standard error of a process during it's
** execution is to use the `Option::EnableAsync` option in
** conjuction with this method. */
size_t readStdErr( std::string& buffer );
/** @brief Read the standard error from the child process.
** @param buffer The buffer to read into.
** @param size The maximum number of bytes to read.
** @return The number of bytes actually read into buffer. Can only be 0 if the
** process has complete.
**
** The only safe way to read from the standard error of a process during it's
** execution is to use the `Option::EnableAsync` option in
** conjuction with this method. */
size_t readStdErr( char* const buffer, const size_t& size );
/** @brief Write the standard output from the child process.
** @param buffer The buffer to write into.
** @param size The number of bytes to write.
** @return The number of bytes actually written into buffer. */
size_t write( const char* buffer, const size_t& size );
/** @brief Write the standard output from the child process.
** @param buffer The buffer to write into.
** @return The number of bytes actually written into buffer. */
size_t write( const std::string& buffer );
/** @brief Write the standard output from the child process.
** @param buffer The buffer to write into.
** @return The number of bytes actually written into buffer. */
size_t write( const std::string_view& buffer );
/** @brief Wait for a process to finish execution.
** @param returnCodeOut The return code of the returned process (can be nullptr).
** @return On success true is returned.
**
** Joining a process will close the stdin pipe to the process. */
bool join( int* const returnCodeOut );
/** @brief Terminate a previously created process.
** @return On success true is returned.
**
** If the process to be destroyed had not finished execution, it will be
** terminated (i.e killed). */
bool kill();
/** @brief Destroy a previously created process.
** @return On success true is returned.
**
** If the process to be destroyed had not finished execution, it may out live
** the parent process. */
bool destroy();
/** @brief Returns if the subprocess is currently still alive and executing.
** @return If the process is still alive true returned. */
bool isAlive();
/** @brief Get the standard input file for a process.
** @return The file for standard input of the process.
**
** The file returned can be written to by the parent process to feed data to
** the standard input of the process. */
FILE* getStdIn() const;
/** @brief Get the standard output file for a process.
** @return The file for standard output of the process.
**
** The file returned can be read from by the parent process to read data from
** the standard output of the child process. */
FILE* getStdOut() const;
/** @brief Get the standard error file for a process.
** @return The file for standard error of the process.
**
** The file returned can be read from by the parent process to read data from
** the standard error of the child process.
**
** If the process was created with the Option::CombinedStdoutStderr
** option bit set, this function will return NULL, and the getStdOut
** function should be used for both the standard output and error combined. */
FILE* getStdErr() const;
/** Indicates that the process must start its shutdown */
void startShutdown();
/** Indicates if the process started its shutdown */
bool isShuttingDown() const;
protected:
void* mProcess{ nullptr };
bool mShuttingDown{ false };
bool mIsAsync{ false };
size_t mBufferSize{ 131072 };
std::thread mStdOutThread;
std::thread mStdErrThread;
Mutex mStdInMutex;
ReadFn mReadStdOutFn;
ReadFn mReadStdErrFn;
size_t readAll( std::string& buffer, bool readErr, Time timeout = Time::Zero );
};
}} // namespace EE::System
#endif // EE_SYSTEM_PROCESS_HPP