add feedback and possible cancellation
This commit is contained in:
parent
8154aac7d8
commit
7ded975cf3
@ -1,5 +1,5 @@
|
||||
#ifndef lint
|
||||
static char rcsid[] = "@(#$Id: execmd.cpp,v 1.12 2006-01-23 13:32:28 dockes Exp $ (C) 2004 J.F.Dockes";
|
||||
static char rcsid[] = "@(#$Id: execmd.cpp,v 1.13 2006-01-24 12:22:20 dockes Exp $ (C) 2004 J.F.Dockes";
|
||||
#endif
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@ -24,9 +24,11 @@ static char rcsid[] = "@(#$Id: execmd.cpp,v 1.12 2006-01-23 13:32:28 dockes Exp
|
||||
#include <sys/select.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#ifdef PUTENV_ARG_NOT_CONST
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
@ -39,15 +41,16 @@ static char rcsid[] = "@(#$Id: execmd.cpp,v 1.12 2006-01-23 13:32:28 dockes Exp
|
||||
#ifndef NO_NAMESPACES
|
||||
using namespace std;
|
||||
#endif /* NO_NAMESPACES */
|
||||
#define MAX(A,B) (A>B?A:B)
|
||||
|
||||
#define MAX(A,B) ((A) > (B) ? (A) : (B))
|
||||
|
||||
void ExecCmd::putenv(const string &ea)
|
||||
{
|
||||
env.push_back(ea);
|
||||
m_env.push_back(ea);
|
||||
}
|
||||
|
||||
int ExecCmd::doexec(const string &cmd, const list<string>& args,
|
||||
const string *input, string *output)
|
||||
const string *inputstring, string *output)
|
||||
{
|
||||
{ // Debug and logging
|
||||
string command = cmd + " ";
|
||||
@ -57,6 +60,8 @@ int ExecCmd::doexec(const string &cmd, const list<string>& args,
|
||||
}
|
||||
LOGDEB(("ExecCmd::doexec: %s\n", command.c_str()));
|
||||
}
|
||||
const char *input = inputstring ? inputstring->data() : 0;
|
||||
unsigned int inputlen = inputstring ? inputstring->length() : 0;
|
||||
|
||||
int pipein[2]; // subproc input
|
||||
int pipeout[2]; // subproc output
|
||||
@ -68,8 +73,8 @@ int ExecCmd::doexec(const string &cmd, const list<string>& args,
|
||||
}
|
||||
if (output && pipe(pipeout) < 0) {
|
||||
LOGERR(("ExecCmd::doexec: pipe(2) failed. errno %d\n", errno));
|
||||
close(pipein[0]);
|
||||
close(pipein[1]);
|
||||
if (pipein[0] >= 0) close(pipein[0]);
|
||||
if (pipein[1] >= 0) close(pipein[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -89,14 +94,21 @@ int ExecCmd::doexec(const string &cmd, const list<string>& args,
|
||||
pipeout[1] = -1;
|
||||
}
|
||||
fd_set readfds, writefds;
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 1;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
if (input || output) {
|
||||
if (input)
|
||||
fcntl(pipein[1], F_SETFL, O_NONBLOCK);
|
||||
if (output)
|
||||
fcntl(pipeout[0], F_SETFL, O_NONBLOCK);
|
||||
int nwritten = 0;
|
||||
unsigned int nwritten = 0;
|
||||
int nfds = MAX(pipein[1], pipeout[0]) + 1;
|
||||
for(;nfds > 0;) {
|
||||
for(; nfds > 0;) {
|
||||
if (m_cancelRequest)
|
||||
break;
|
||||
|
||||
FD_ZERO(&writefds);
|
||||
FD_ZERO(&readfds);
|
||||
if (pipein[1] >= 0)
|
||||
@ -107,29 +119,34 @@ int ExecCmd::doexec(const string &cmd, const list<string>& args,
|
||||
//struct timeval to; to.tv_sec = 1;to.tv_usec=0;
|
||||
//cerr << "pipein[1] "<< pipein[1] << " pipeout[0] " <<
|
||||
//pipeout[0] << " nfds " << nfds << endl;
|
||||
if (select(nfds, &readfds, &writefds, 0, 0) <= 0) {
|
||||
int ss;
|
||||
if ((ss = select(nfds, &readfds, &writefds, 0, &tv)) <= 0) {
|
||||
if (ss == 0) {
|
||||
// Timeout, is ok.
|
||||
continue;
|
||||
}
|
||||
LOGERR(("ExecCmd::doexec: select(2) failed. errno %d\n",
|
||||
errno));
|
||||
break;
|
||||
}
|
||||
if (pipein[1] >= 0 && FD_ISSET(pipein[1], &writefds)) {
|
||||
int n = write(pipein[1], input->c_str()+nwritten,
|
||||
input->length() - nwritten);
|
||||
int n = write(pipein[1], input + nwritten,
|
||||
inputlen - nwritten);
|
||||
if (n < 0) {
|
||||
LOGERR(("ExecCmd::doexec: write(2) failed. errno %d\n",
|
||||
errno));
|
||||
goto out;
|
||||
}
|
||||
nwritten += n;
|
||||
if (nwritten == (int)input->length()) {
|
||||
if (nwritten == inputlen) {
|
||||
// cerr << "Closing output" << endl;
|
||||
close(pipein[1]);
|
||||
pipein[1] = -1;
|
||||
}
|
||||
}
|
||||
if (pipeout[0] > 0 && FD_ISSET(pipeout[0], &readfds)) {
|
||||
char buf[1024];
|
||||
int n = read(pipeout[0], buf, 1024);
|
||||
char buf[8192];
|
||||
int n = read(pipeout[0], buf, 8192);
|
||||
if (n == 0) {
|
||||
goto out;
|
||||
} else if (n < 0) {
|
||||
@ -139,14 +156,31 @@ int ExecCmd::doexec(const string &cmd, const list<string>& args,
|
||||
} else if (n > 0) {
|
||||
// cerr << "READ: " << n << endl;
|
||||
output->append(buf, n);
|
||||
if (m_advise)
|
||||
m_advise->newData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
out:
|
||||
int status;
|
||||
pid = waitpid(pid, &status, 0);
|
||||
int status = -1;
|
||||
|
||||
if (m_cancelRequest) {
|
||||
// If we were canceled, need to cleanup
|
||||
LOGDEB1(("Killing cmd\n"));
|
||||
kill(pid, SIGTERM);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (kill(pid, 0) != 0)
|
||||
break;
|
||||
sleep(1);
|
||||
if (i == 2) {
|
||||
LOGDEB1(("Killing (KILL) cmd\n"));
|
||||
kill(pid, SIGKILL);
|
||||
}
|
||||
}
|
||||
}
|
||||
(void)waitpid(pid, &status, 0);
|
||||
if (pipein[0] >= 0)
|
||||
close(pipein[0]);
|
||||
if (pipein[1] >= 0)
|
||||
@ -157,7 +191,10 @@ int ExecCmd::doexec(const string &cmd, const list<string>& args,
|
||||
close(pipeout[1]);
|
||||
LOGDEB1(("ExecCmd::doexec: father got status 0x%x\n", status));
|
||||
return status;
|
||||
|
||||
} else {
|
||||
// In child process. Set up pipes, environment, and exec command
|
||||
|
||||
if (input) {
|
||||
close(pipein[1]);
|
||||
pipein[1] = -1;
|
||||
@ -210,7 +247,7 @@ int ExecCmd::doexec(const string &cmd, const list<string>& args,
|
||||
while (argv[i]) cerr << argv[i++] << endl;}
|
||||
#endif
|
||||
|
||||
for (it = env.begin(); it != env.end(); it++) {
|
||||
for (it = m_env.begin(); it != m_env.end(); it++) {
|
||||
#ifdef PUTENV_ARG_NOT_CONST
|
||||
::putenv(strdup(it->c_str()));
|
||||
#else
|
||||
@ -237,7 +274,11 @@ using namespace std;
|
||||
#include "execmd.h"
|
||||
|
||||
const char *data = "Une ligne de donnees\n";
|
||||
|
||||
class MEAdv : public ExecCmdAdvise {
|
||||
public:
|
||||
ExecCmd *cmd;
|
||||
void newData() {cerr << "New Data!" << endl;cmd->setCancel();}
|
||||
};
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
DebugLog::getdbl()->setloglevel(DEBDEB1);
|
||||
@ -252,6 +293,9 @@ int main(int argc, const char **argv)
|
||||
l.push_back(argv[i]);
|
||||
}
|
||||
ExecCmd mexec;
|
||||
MEAdv adv;
|
||||
adv.cmd = &mexec;
|
||||
mexec.setAdvise(&adv);
|
||||
string input, output;
|
||||
input = data;
|
||||
string *ip = 0;
|
||||
|
||||
@ -1,14 +1,21 @@
|
||||
#ifndef _EXECMD_H_INCLUDED_
|
||||
#define _EXECMD_H_INCLUDED_
|
||||
/* @(#$Id: execmd.h,v 1.4 2005-11-18 15:19:14 dockes Exp $ (C) 2004 J.F.Dockes */
|
||||
/* @(#$Id: execmd.h,v 1.5 2006-01-24 12:22:20 dockes Exp $ (C) 2004 J.F.Dockes */
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
/** Callback function object to advise of new data arrival */
|
||||
class ExecCmdAdvise {
|
||||
public:
|
||||
virtual ~ExecCmdAdvise() {}
|
||||
virtual void newData() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
Execute command possibly taking both input and output (will do
|
||||
asynchronous io as appropriate for things to work).
|
||||
*/
|
||||
* Execute command possibly taking both input and output (will do
|
||||
* asynchronous io as appropriate for things to work).
|
||||
*/
|
||||
class ExecCmd {
|
||||
public:
|
||||
/**
|
||||
@ -34,8 +41,18 @@ class ExecCmd {
|
||||
*/
|
||||
void putenv(const std::string &envassign);
|
||||
|
||||
/** Set function object to call whenever new data is available */
|
||||
void setAdvise(ExecCmdAdvise *adv) {m_advise = adv;}
|
||||
|
||||
/** Cancel exec. This can be called from another thread or from newData */
|
||||
void setCancel() {m_cancelRequest = true;}
|
||||
|
||||
ExecCmd() : m_advise(0), m_cancelRequest(false) {}
|
||||
|
||||
private:
|
||||
std::list<std::string> env;
|
||||
std::list<std::string> m_env;
|
||||
ExecCmdAdvise *m_advise;
|
||||
bool m_cancelRequest;
|
||||
};
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user