#include <unistd.h>
#include <sys/wait.h>
#include <msp/fs/dir.h>
+#include <msp/io/console.h>
+#include <msp/io/file.h>
#include <msp/io/print.h>
+#include <msp/time/units.h>
#include "externaltask.h"
using namespace std;
argv(a),
work_dir(wd),
pid(-1),
- exit_code(-1)
+ exit_code(-1),
+ stdout_dest(PASSTHROUGH),
+ stderr_dest(PASSTHROUGH),
+ capture_pipe(0)
{ }
+ExternalTask::~ExternalTask()
+{
+ delete capture_pipe;
+}
+
string ExternalTask::get_command() const
{
string cmd;
void ExternalTask::start()
{
+ if(stdout_dest==CAPTURE || stderr_dest==CAPTURE)
+ capture_pipe = new IO::Pipe;
+
if((pid = fork()))
{
if(pid==-1)
cargv[i] = argv[i].c_str();
cargv.back() = 0;
+ if(stdout_dest==IGNORE || stderr_dest==IGNORE)
+ {
+ IO::File devnull("/dev/null", IO::M_WRITE);
+ if(stdout_dest==IGNORE)
+ IO::cout.redirect(devnull);
+ if(stderr_dest==IGNORE)
+ IO::cerr.redirect(devnull);
+ }
+
+ if(capture_pipe)
+ {
+ if(stdout_dest==CAPTURE)
+ IO::cout.redirect(*capture_pipe);
+ if(stderr_dest==CAPTURE)
+ IO::cerr.redirect(*capture_pipe);
+ delete capture_pipe;
+ }
+
if(!work_dir.empty())
FS::chdir(work_dir);
execvp(cargv.front(), const_cast<char *const *>(&cargv.front()));
{
if(pid>0)
{
+ // XXX This is sub-optimal, should have support for a blocking wait
int status;
if(waitpid(pid, &status, WNOHANG)==pid)
{
else
exit_code = 1025;
pid = 0;
+ }
- signal_finished.emit(!exit_code);
+ // Do this after waiting to avoid a race condition
+ if(capture_pipe)
+ {
+ while(IO::poll(*capture_pipe, IO::P_INPUT, Time::zero))
+ {
+ char buf[1024];
+ unsigned len = capture_pipe->read(buf, sizeof(buf));
+ output.append(buf, len);
+ if(len<sizeof(buf))
+ break;
+ }
}
- else
+
+ if(pid>0)
return RUNNING;
+ else
+ signal_finished.emit(!exit_code);
}
return exit_code ? ERROR : SUCCESS;
}
+
+void ExternalTask::set_stdout(Destination d)
+{
+ stdout_dest = d;
+}
+
+void ExternalTask::set_stderr(Destination d)
+{
+ stderr_dest = d;
+}
#include <string>
#include <vector>
#include <msp/fs/path.h>
+#include <msp/io/pipe.h>
#include "task.h"
class ExternalTask: public Task
{
+public:
+ enum Destination
+ {
+ PASSTHROUGH,
+ CAPTURE,
+ IGNORE
+ };
+
private:
std::vector<std::string> argv;
Msp::FS::Path work_dir;
int pid;
int exit_code;
+ Destination stdout_dest;
+ Destination stderr_dest;
+ Msp::IO::Pipe *capture_pipe;
+ std::string output;
public:
ExternalTask(const std::vector<std::string> &, const Msp::FS::Path &);
+ virtual ~ExternalTask();
virtual std::string get_command() const;
virtual void start();
virtual Status check();
+
+ void set_stdout(Destination);
+ void set_stderr(Destination);
+ const std::string &get_output() const { return output; }
};
#endif