diff --git a/src/utils/execmd.cpp b/src/utils/execmd.cpp index 70dfa694..ee0bd93e 100644 --- a/src/utils/execmd.cpp +++ b/src/utils/execmd.cpp @@ -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 #include #include +#include #ifdef PUTENV_ARG_NOT_CONST #include #endif + #include #include #include @@ -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& 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& 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& 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& 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& 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& 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& 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& 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; diff --git a/src/utils/execmd.h b/src/utils/execmd.h index 82099035..62a7f8cf 100644 --- a/src/utils/execmd.h +++ b/src/utils/execmd.h @@ -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 #include +/** 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 env; + std::list m_env; + ExecCmdAdvise *m_advise; + bool m_cancelRequest; };