Commands
Interface for asyncronously operating on commands. You can write to STDIN, read from STDOUT and STDERR.
Note: currently the implementation is done in a separate thread for each new command,
as there is no possibility to read or write asynchronously from file descriptors.
File information
Functions
Functions to initialize
class ICommandProvider * CreateCommandProvider(class IIoMux * iomux);
Overview
This function will create an ICommandProvider instance. This instance can be used to create ICommand objects.
CreateCommandProvider
-
Parameters
class IIoMux * iomux | The iomux instance which is to be used. |
Return value
The ICommandProvider instance. That instance can be freed as soon as it no longer is used by calling the C++ delete operator.
Classes
ICommandProvider
class ICommandProvider {
public:
virtual class ICommand * CreateCommand(class IInstanceLog * const log, class UTask * user, const char * command, int expectedExitCode, size_t initialReadLength = 0) = 0;
};
Public functions
virtual class ICommand * CreateCommand
Creates an ICommand instance.
Parameters
class IInstanceLog * const log | The IInstanceLog instance which is to be used. |
class UTask * user | The UTask instance which will receive TaskProgress/TaskComplete/TaskFailed callbacks. |
const char * command | The actual command which will be executed. |
int expectedExitCode | The expected exit code. Use a negative value to ignore the exit code. |
size_t initialReadLength | You can specify a number to directly start reading the command output. |
Return value
The ICommand instance. That instance can be freed as soon as it called TaskFailed/TaskComplete, not before!
Classes
ICommand
ICommand is derived from ITask, so the ITask/UTask mechanisms are used for progress and completion.
The ITask::Start function is called inside CreateCommand, so it is not needed to call this function on your own.
The buffer of the read data is maintained within this class.
STDERR is automatically redirected and can be fetched afterwards with Error(). Note that max 8192 bytes are fetched from STDERR, the remaining data is discarded.
General handling
The TaskProgress progress parameter is used differently, depending on a previous Read or Write. Valid values are:
- COMMAND_PROGRESS_READ
- COMMAND_PROGRESS_WRITE
- The TaskComplete callback is triggered when the process has terminated and all data has been read or after ICommand::Close() has been called.
- The TaskFailed callback is triggered when something failed. In this case the command has already terminated.
- The TaskProgress(this, COMMAND_PROGRESS_READ) callback is triggered after a previous Read(...) call when the data is available. You can retrieve the data with Get() afterwards.
- The TaskProgress(this, COMMAND_PROGRESS_WRITE) callback is triggered after a previous Write(...) call when the data has been written.
class ICommand : public ITask {
public:
virtual void Write(byte * data, size_t length, bool last = false) = 0;
virtual void Read(size_t length) = 0;
virtual void Get(const byte * & data, size_t & length) = 0;
virtual void Close() = 0;
virtual const char * Error() = 0;
virtual int GetExitCode() = 0;
};
Public functions
void Write
Initiates writing of data to STDIN. May be called after TaskProgress from the previous block.
You mustn't call several writes without waiting for the TaskProgress callback which indicates that the data has been written.
Parameters
byte * data | A pointer to a byte array. |
size_t length | The length of the data to write. |
bool last | Set true if this is the last write call, which closes STDIN. |
void Read
Initiates reading of the next block. May be called after TaskProgress from the previous block.
You mustn't call several reads without waiting for the TaskProgress callback which indicates that the data has been read.
Parameters
size_t length | The length of the data to read. |
void Get
Gets a pointer to the read block of data. May be called after the TaskProgress for the read operation.
The pointer is valid until the next call to Read. I the length is not equal to the length of a previous Read call, the end of the stream is reached and you'll get a TaskComplete/TaskFailed afterwards.
Parameters
const byte * & data | Pointer reference to the data buffer. |
size_t & length | The available size in the data buffer. |
void Close
Terminates the process if it is still running. You do not need to call Close() if you do not want to kill the process before it ends on its own.
Parameters
const char * Error
Returns the output of STDERR or NULL if nothing has been written to STDERR.
Parameters
Return value
The const char * error string.
int GetExitCode()
Parameters
Return value
The int exit code of the command.
Examples
Command
// in main.cpp
class ICommandProvider * provider = CreateCommandProvider(iomux);
// another cpp file
class Test : public UTask {
class ICommand * command;
public:
Test(class ICommandProvider * provider, class IInstanceLog * log) {
this->command = provider::CreateCommand(log, this, "/usr/bin/...", 0);
byte * data = ...;
this->command->Write(data, dataLen, true); // we write one data buffer and wait for TaskProgress
}
void TaskProgress(class ITask * task, dword progress = 0) {
if(progress == COMMAND_PROGRESS_WRITE) { // just one write op, so read now
this->command->Read(1000); // read 1000 bytes
}
else if(progress == COMMAND_PROGRESS_READ) {
size_t length = 0;
const byte * data = NULL;
this->command->Get(data, length);
// do something with the data
if(length == 1000) { // more data available
this->command->Read(1000);
}
// else wait for TaskComplete/Failed
}
}
void TaskComplete(class ITask * task) {
delete this->command; // this->command == task
this->command = nullptr;
}
void TaskFailed(class ITask * task) {
debug->printf("%i %s", this->command->GetExitCode(), this->command->Error());
delete this->command; // this->command == task
this->command = nullptr;
}
}