#ifndef lint static char rcsid[] = "@(#$Id: execmd.cpp,v 1.16 2006-01-30 09:28:36 dockes Exp $ (C) 2004 J.F.Dockes"; #endif /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef TEST_EXECMD #include #include #include #include #include #include #include #ifdef PUTENV_ARG_NOT_CONST #include #endif #include #include #include #include #include "execmd.h" #include "pathut.h" #include "debuglog.h" #ifndef NO_NAMESPACES using namespace std; #endif /* NO_NAMESPACES */ #define MAX(A,B) ((A) > (B) ? (A) : (B)) void ExecCmd::putenv(const string &ea) { m_env.push_back(ea); } /** A resource manager to ensure that execcmd cleans up if an exception is * raised in the callback */ class ExecCmdRsrc { public: int pipein[2]; int pipeout[2]; pid_t pid; ExecCmdRsrc() { reset(); } void reset() { pipein[0] = pipein[1] = pipeout[0] = pipeout[1] = -1; pid = -1; } ~ExecCmdRsrc() { int status; if (pid > 0) { LOGDEB(("ExecCmd: killing cmd\n")); if (kill(pid, SIGTERM) == 0) { for (int i = 0; i < 3; i++) { (void)waitpid(pid, &status, WNOHANG); if (kill(pid, 0) != 0) break; sleep(1); if (i == 2) { LOGDEB(("ExecCmd: killing (KILL) cmd\n")); kill(pid, SIGKILL); } } } } if (pipein[0] >= 0) close(pipein[0]); if (pipein[1] >= 0) close(pipein[1]); if (pipeout[0] >= 0) close(pipeout[0]); if (pipeout[1] >= 0) close(pipeout[1]); } }; int ExecCmd::doexec(const string &cmd, const list& args, const string *inputstring, string *output) { { // Debug and logging string command = cmd + " "; for (list::const_iterator it = args.begin();it != args.end(); it++) { command += "{" + *it + "} "; } LOGDEB(("ExecCmd::doexec: %s\n", command.c_str())); } const char *input = inputstring ? inputstring->data() : 0; unsigned int inputlen = inputstring ? inputstring->length() : 0; ExecCmdRsrc e; if (input && pipe(e.pipein) < 0) { LOGERR(("ExecCmd::doexec: pipe(2) failed. errno %d\n", errno)); return -1; } if (output && pipe(e.pipeout) < 0) { LOGERR(("ExecCmd::doexec: pipe(2) failed. errno %d\n", errno)); return -1; } e.pid = fork(); if (e.pid < 0) { LOGERR(("ExecCmd::doexec: fork(2) failed. errno %d\n", errno)); return -1; } if (e.pid) { if (input) { close(e.pipein[0]); e.pipein[0] = -1; } if (output) { close(e.pipeout[1]); e.pipeout[1] = -1; } fd_set readfds, writefds; struct timeval tv; tv.tv_sec = 1; tv.tv_usec = 0; if (input || output) { if (input) fcntl(e.pipein[1], F_SETFL, O_NONBLOCK); if (output) fcntl(e.pipeout[0], F_SETFL, O_NONBLOCK); unsigned int nwritten = 0; int nfds = MAX(e.pipein[1], e.pipeout[0]) + 1; for(; nfds > 0;) { if (m_cancelRequest) break; FD_ZERO(&writefds); FD_ZERO(&readfds); if (e.pipein[1] >= 0) FD_SET(e.pipein[1], &writefds); if (e.pipeout[0] >= 0) FD_SET(e.pipeout[0], &readfds); nfds = MAX(e.pipein[1], e.pipeout[0]) + 1; //struct timeval to; to.tv_sec = 1;to.tv_usec=0; //cerr << "e.pipein[1] "<< e.pipein[1] << " e.pipeout[0] " << //e.pipeout[0] << " nfds " << nfds << endl; int ss; if ((ss = select(nfds, &readfds, &writefds, 0, &tv)) <= 0) { if (ss == 0) { // Timeout, is ok. if (m_advise) m_advise->newData(0); continue; } LOGERR(("ExecCmd::doexec: select(2) failed. errno %d\n", errno)); break; } if (e.pipein[1] >= 0 && FD_ISSET(e.pipein[1], &writefds)) { int n = write(e.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 == inputlen) { // cerr << "Closing output" << endl; close(e.pipein[1]); e.pipein[1] = -1; } } if (e.pipeout[0] > 0 && FD_ISSET(e.pipeout[0], &readfds)) { char buf[8192]; int n = read(e.pipeout[0], buf, 8192); if (n == 0) { goto out; } else if (n < 0) { LOGERR(("ExecCmd::doexec: read(2) failed. errno %d\n", errno)); goto out; } else if (n > 0) { // cerr << "READ: " << n << endl; output->append(buf, n); if (m_advise) m_advise->newData(n); } } } } out: int status = -1; if (!m_cancelRequest) { (void)waitpid(e.pid, &status, 0); e.pid = -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(e.pipein[1]); e.pipein[1] = -1; if (e.pipein[0] != 0) { dup2(e.pipein[0], 0); close(e.pipein[0]); e.pipein[0] = -1; } } if (output) { close(e.pipeout[0]); e.pipeout[0] = -1; if (e.pipeout[1] != 1) { if (dup2(e.pipeout[1], 1) < 0) { LOGERR(("ExecCmd::doexec: dup2(2) failed. errno %d\n", errno)); } if (close(e.pipeout[1]) < 0) { LOGERR(("ExecCmd::doexec: close(2) failed. errno %d\n", errno)); } e.pipeout[1] = -1; } } e.reset(); // Allocate arg vector (2 more for arg0 + final 0) typedef const char *Ccharp; Ccharp *argv; argv = (Ccharp *)malloc((args.size()+2) * sizeof(char *)); if (argv == 0) { LOGERR(("ExecCmd::doexec: malloc() failed. errno %d\n", errno)); exit(1); } // Fill up argv argv[0] = path_getsimple(cmd).c_str(); int i = 1; list::const_iterator it; for (it = args.begin(); it != args.end(); it++) { argv[i++] = it->c_str(); } argv[i] = 0; #if 0 {int i = 0;cerr << "cmd: " << cmd << endl << "ARGS: " << endl; while (argv[i]) cerr << argv[i++] << endl;} #endif for (it = m_env.begin(); it != m_env.end(); it++) { #ifdef PUTENV_ARG_NOT_CONST ::putenv(strdup(it->c_str())); #else ::putenv(it->c_str()); #endif } execvp(cmd.c_str(), (char *const*)argv); // Hu ho LOGERR(("ExecCmd::doexec: execvp(%s) failed. errno %d\n", cmd.c_str(), errno)); _exit(128); } } #else // TEST #include #include #include #include #include "debuglog.h" #include "cancelcheck.h" using namespace std; #include "execmd.h" const char *data = "Une ligne de donnees\n"; class MEAdv : public ExecCmdAdvise { public: ExecCmd *cmd; void newData(int) { cerr << "New Data!" << endl; CancelCheck::instance().setCancel(); CancelCheck::instance().checkCancel(); // cmd->setCancel(); } }; int main(int argc, const char **argv) { DebugLog::getdbl()->setloglevel(DEBDEB1); DebugLog::setfilename("stderr"); if (argc < 2) { cerr << "Usage: execmd cmd arg1 arg2 ..." << endl; exit(1); } const string cmd = argv[1]; list l; for (int i = 2; i < argc; i++) { l.push_back(argv[i]); } ExecCmd mexec; MEAdv adv; adv.cmd = &mexec; mexec.setAdvise(&adv); string input, output; input = data; string *ip = 0; //ip = &input; mexec.putenv("TESTVARIABLE1=TESTVALUE1"); mexec.putenv("TESTVARIABLE2=TESTVALUE2"); mexec.putenv("TESTVARIABLE3=TESTVALUE3"); int status = -1; try { status = mexec.doexec(cmd, l, ip, &output); } catch (CancelExcept) { cerr << "CANCELED" << endl; } fprintf(stderr, "Status: 0x%x\n", status); cout << "Output:" << output << endl; exit (status >> 8); } #endif // TEST