diff --git a/src/lib/Makefile b/src/lib/Makefile index a1f71972..f8f05975 100644 --- a/src/lib/Makefile +++ b/src/lib/Makefile @@ -6,8 +6,8 @@ LIBS = librcl.a all: $(LIBS) -OBJS = rclaspell.o rclconfig.o rclinit.o textsplit.o unacpp.o csguess.o indexer.o mimetype.o htmlparse.o myhtmlparse.o mimehandler.o internfile.o mh_exec.o mh_html.o mh_mail.o mh_mbox.o mh_text.o docseq.o docseqdb.o docseqhist.o filtseq.o history.o plaintorich.o recollq.o reslistpager.o sortseq.o wasastringtoquery.o wasatorcl.o rcldb.o rcldoc.o rclquery.o searchdata.o stemdb.o stoplist.o base64.o conftree.o copyfile.o debuglog.o execmd.o fstreewalk.o idfile.o fileudi.o md5.o mimeparse.o pathut.o pxattr.o readfile.o smallut.o transcode.o wipedir.o x11mon.o mime-getpart.o mime-parsefull.o mime-parseonlyheader.o mime-printbody.o mime-printdoc.o mime-printheader.o mime.o convert.o iodevice.o iofactory.o -DEPS = rclaspell.dep.stamp rclconfig.dep.stamp rclinit.dep.stamp textsplit.dep.stamp unacpp.dep.stamp csguess.dep.stamp indexer.dep.stamp mimetype.dep.stamp htmlparse.dep.stamp myhtmlparse.dep.stamp mimehandler.dep.stamp internfile.dep.stamp mh_exec.dep.stamp mh_html.dep.stamp mh_mail.dep.stamp mh_mbox.dep.stamp mh_text.dep.stamp docseq.dep.stamp docseqdb.dep.stamp docseqhist.dep.stamp filtseq.dep.stamp history.dep.stamp plaintorich.dep.stamp recollq.dep.stamp reslistpager.dep.stamp sortseq.dep.stamp wasastringtoquery.dep.stamp wasatorcl.dep.stamp rcldb.dep.stamp rcldoc.dep.stamp rclquery.dep.stamp searchdata.dep.stamp stemdb.dep.stamp stoplist.dep.stamp base64.dep.stamp conftree.dep.stamp copyfile.dep.stamp debuglog.dep.stamp execmd.dep.stamp fstreewalk.dep.stamp idfile.dep.stamp fileudi.dep.stamp md5.dep.stamp mimeparse.dep.stamp pathut.dep.stamp pxattr.dep.stamp readfile.dep.stamp smallut.dep.stamp transcode.dep.stamp wipedir.dep.stamp x11mon.dep.stamp mime-getpart.dep.stamp mime-parsefull.dep.stamp mime-parseonlyheader.dep.stamp mime-printbody.dep.stamp mime-printdoc.dep.stamp mime-printheader.dep.stamp mime.dep.stamp convert.dep.stamp iodevice.dep.stamp iofactory.dep.stamp +OBJS = rclaspell.o rclconfig.o rclinit.o textsplit.o unacpp.o csguess.o indexer.o mimetype.o htmlparse.o myhtmlparse.o mimehandler.o internfile.o mh_exec.o mh_html.o mh_mail.o mh_mbox.o mh_text.o docseq.o docseqdb.o docseqhist.o filtseq.o history.o plaintorich.o recollq.o reslistpager.o sortseq.o wasastringtoquery.o wasatorcl.o rcldb.o rcldoc.o rclquery.o searchdata.o stemdb.o stoplist.o base64.o conftree.o copyfile.o debuglog.o execmd.o fstreewalk.o idfile.o fileudi.o md5.o mimeparse.o netcon.o pathut.o pxattr.o readfile.o smallut.o transcode.o wipedir.o x11mon.o mime-getpart.o mime-parsefull.o mime-parseonlyheader.o mime-printbody.o mime-printdoc.o mime-printheader.o mime.o convert.o iodevice.o iofactory.o +DEPS = rclaspell.dep.stamp rclconfig.dep.stamp rclinit.dep.stamp textsplit.dep.stamp unacpp.dep.stamp csguess.dep.stamp indexer.dep.stamp mimetype.dep.stamp htmlparse.dep.stamp myhtmlparse.dep.stamp mimehandler.dep.stamp internfile.dep.stamp mh_exec.dep.stamp mh_html.dep.stamp mh_mail.dep.stamp mh_mbox.dep.stamp mh_text.dep.stamp docseq.dep.stamp docseqdb.dep.stamp docseqhist.dep.stamp filtseq.dep.stamp history.dep.stamp plaintorich.dep.stamp recollq.dep.stamp reslistpager.dep.stamp sortseq.dep.stamp wasastringtoquery.dep.stamp wasatorcl.dep.stamp rcldb.dep.stamp rcldoc.dep.stamp rclquery.dep.stamp searchdata.dep.stamp stemdb.dep.stamp stoplist.dep.stamp base64.dep.stamp conftree.dep.stamp copyfile.dep.stamp debuglog.dep.stamp execmd.dep.stamp fstreewalk.dep.stamp idfile.dep.stamp fileudi.dep.stamp md5.dep.stamp mimeparse.dep.stamp netcon.dep.stamp pathut.dep.stamp pxattr.dep.stamp readfile.dep.stamp smallut.dep.stamp transcode.dep.stamp wipedir.dep.stamp x11mon.dep.stamp mime-getpart.dep.stamp mime-parsefull.dep.stamp mime-parseonlyheader.dep.stamp mime-printbody.dep.stamp mime-printdoc.dep.stamp mime-printheader.dep.stamp mime.dep.stamp convert.dep.stamp iodevice.dep.stamp iofactory.dep.stamp librcl.a : $(DEPS) $(OBJS) unac.o ar ru librcl.a $(OBJS) unac.o @@ -103,6 +103,8 @@ md5.o : ../utils/md5.cpp $(CXX) $(ALL_CXXFLAGS) -c ../utils/md5.cpp mimeparse.o : ../utils/mimeparse.cpp $(CXX) $(ALL_CXXFLAGS) -c ../utils/mimeparse.cpp +netcon.o : ../utils/netcon.cpp + $(CXX) $(ALL_CXXFLAGS) -c ../utils/netcon.cpp pathut.o : ../utils/pathut.cpp $(CXX) $(ALL_CXXFLAGS) -c ../utils/pathut.cpp pxattr.o : ../utils/pxattr.cpp @@ -275,6 +277,9 @@ md5.dep.stamp : ../utils/md5.cpp mimeparse.dep.stamp : ../utils/mimeparse.cpp $(CXX) -M $(ALL_CXXFLAGS) ../utils/mimeparse.cpp > mimeparse.dep touch mimeparse.dep.stamp +netcon.dep.stamp : ../utils/netcon.cpp + $(CXX) -M $(ALL_CXXFLAGS) ../utils/netcon.cpp > netcon.dep + touch netcon.dep.stamp pathut.dep.stamp : ../utils/pathut.cpp $(CXX) -M $(ALL_CXXFLAGS) ../utils/pathut.cpp > pathut.dep touch pathut.dep.stamp @@ -340,6 +345,7 @@ include idfile.dep include fileudi.dep include md5.dep include mimeparse.dep +include netcon.dep include pathut.dep include pxattr.dep include readfile.dep diff --git a/src/lib/mkMake b/src/lib/mkMake index 609c7249..14f3f1c2 100755 --- a/src/lib/mkMake +++ b/src/lib/mkMake @@ -48,6 +48,7 @@ ${depth}/utils/idfile.cpp \ ${depth}/utils/fileudi.cpp \ ${depth}/utils/md5.cpp \ ${depth}/utils/mimeparse.cpp \ +${depth}/utils/netcon.cpp \ ${depth}/utils/pathut.cpp \ ${depth}/utils/pxattr.cpp \ ${depth}/utils/readfile.cpp \ diff --git a/src/utils/execmd.cpp b/src/utils/execmd.cpp index c6cbbb13..b7ce98c5 100644 --- a/src/utils/execmd.cpp +++ b/src/utils/execmd.cpp @@ -45,6 +45,7 @@ static char rcsid[] = "@(#$Id: execmd.cpp,v 1.27 2008-10-06 06:22:47 dockes Exp #include "pathut.h" #include "debuglog.h" #include "smallut.h" +#include "netcon.h" #ifndef NO_NAMESPACES using namespace std; @@ -145,6 +146,8 @@ public: close(m_parent->m_pipeout[0]); if (m_parent->m_pipeout[1] >= 0) close(m_parent->m_pipeout[1]); + m_parent->m_tocmd.release(); + m_parent->m_fromcmd.release(); pthread_sigmask(SIG_UNBLOCK, &m_parent->m_blkcld, 0); m_parent->reset(); } @@ -152,11 +155,10 @@ private: ExecCmd *m_parent; bool m_active; }; + ExecCmd::~ExecCmd() { - { - ExecCmdRsrc(this); - } + ExecCmdRsrc(this); } int ExecCmd::startExec(const string &cmd, const list& args, @@ -171,6 +173,8 @@ int ExecCmd::startExec(const string &cmd, const list& args, LOGDEB(("ExecCmd::startExec: (%d|%d) %s\n", has_input, has_output, command.c_str())); } + + // The resource manager ensures resources are freed if we return early ExecCmdRsrc e(this); if (has_input && pipe(m_pipein) < 0) { @@ -202,128 +206,166 @@ int ExecCmd::startExec(const string &cmd, const list& args, if (has_input) { close(m_pipein[0]); m_pipein[0] = -1; - fcntl(m_pipein[1], F_SETFL, O_NONBLOCK); + NetconCli *iclicon = new NetconCli(); + iclicon->setconn(m_pipein[1]); + m_tocmd = NetconP(iclicon); + m_pipein[1] = 0; } if (has_output) { close(m_pipeout[1]); m_pipeout[1] = -1; - fcntl(m_pipeout[0], F_SETFL, O_NONBLOCK); + NetconCli *oclicon = new NetconCli(); + oclicon->setconn(m_pipeout[0]); + m_fromcmd = NetconP(oclicon); + m_pipeout[0] = -1; } + + /* Don't want to undo what we just did ! */ e.inactivate(); + return 0; } +// Netcon callback. Send data to the command's input +class ExecWriter : public NetconWorker { +public: + ExecWriter(const string *input, ExecCmdProvide *provide) + : m_input(input), m_cnt(0), m_provide(provide) + {} + virtual int data(NetconData *con, Netcon::Event reason) + { + if (!m_input) return -1; + LOGDEB(("ExecWriter: input m_cnt %d input length %d\n", m_cnt, + m_input->length())); + if (m_cnt >= m_input->length()) { + // Fd ready for more but we got none. + if (m_provide) { + m_provide->newData(); + if (m_input->empty()) { + return 0; + } else { + m_cnt = 0; + } + LOGDEB2(("ExecWriter: provide m_cnt %d input length %d\n", + m_cnt, m_input->length())); + } else { + return 0; + } + } + int ret = con->send(m_input->c_str() + m_cnt, + m_input->length() - m_cnt); + LOGDEB2(("ExecWriter: wrote %d to command\n", ret)); + if (ret <= 0) { + LOGERR(("ExecWriter: data: can't write\n")); + return -1; + } + m_cnt += ret; + return ret; + } +private: + const string *m_input; + unsigned int m_cnt; // Current offset inside m_input + ExecCmdProvide *m_provide; +}; + +// Netcon callback. Get data from the command output. +class ExecReader : public NetconWorker { +public: + ExecReader(string *output, ExecCmdAdvise *advise) + : m_output(output), m_advise(advise) + {} + virtual int data(NetconData *con, Netcon::Event reason) + { + char buf[8192]; + int n = con->receive(buf, 8192); + LOGDEB(("ExecReader: got %d from command\n", n)); + if (n < 0) { + LOGERR(("ExecCmd::doexec: receive failed. errno %d\n", errno)); + } else if (n > 0) { + m_output->append(buf, n); + if (m_advise) + m_advise->newData(n); + } // else n == 0, just return + return n; + } +private: + string *m_output; + ExecCmdAdvise *m_advise; +}; int ExecCmd::doexec(const string &cmd, const list& args, - const string *inputstring, string *output) + const string *input, string *output) { - if (startExec(cmd, args, inputstring != 0, output != 0) < 0) { + if (startExec(cmd, args, input != 0, output != 0) < 0) { return -1; } - // Need something to take note of my own errors (apart from the command's) - bool haderror = false; - const char *input = inputstring ? inputstring->data() : 0; - unsigned int inputlen = inputstring ? inputstring->length() : 0; + // Cleanup in case we return early ExecCmdRsrc e(this); + int ret = 0; if (input || output) { - unsigned int nwritten = 0; - int nfds = MAX(m_pipein[1], m_pipeout[0]) + 1; - fd_set readfds, writefds; - struct timeval tv; - tv.tv_sec = m_timeoutMs / 1000; - tv.tv_usec = 1000 * (m_timeoutMs % 1000); - for(; nfds > 0;) { - if (m_cancelRequest) - break; + // Setup output + if (output) { + NetconCli *oclicon = dynamic_cast(m_fromcmd.getptr()); + if (!oclicon) { + LOGERR(("ExecCmd::doexec: no connection from command\n")); + return -1; + } + oclicon->setcallback(RefCntr + (new ExecReader(output, m_advise))); + Netcon::addselcon(m_fromcmd, Netcon::NETCONPOLL_READ); + // Give up ownership + m_fromcmd.release(); + } + // Setup input + if (input) { + NetconCli *iclicon = dynamic_cast(m_tocmd.getptr()); + if (!iclicon) { + LOGERR(("ExecCmd::doexec: no connection from command\n")); + return -1; + } + iclicon->setcallback(RefCntr + (new ExecWriter(input, m_provide))); + Netcon::addselcon(m_tocmd, Netcon::NETCONPOLL_WRITE); + // Give up ownership + m_tocmd.release(); + } - FD_ZERO(&writefds); - FD_ZERO(&readfds); - if (m_pipein[1] >= 0) - FD_SET(m_pipein[1], &writefds); - if (m_pipeout[0] >= 0) - FD_SET(m_pipeout[0], &readfds); - nfds = MAX(m_pipein[1], m_pipeout[0]) + 1; - //struct timeval to; to.tv_sec = 1;to.tv_usec=0; - //cerr << "m_pipein[1] "<< m_pipein[1] << " m_pipeout[0] " << - //m_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)); - haderror = true; + // Do the actual reading/writing/waiting + Netcon::setperiodichandler(0, 0, m_timeoutMs); + while ((ret = Netcon::selectloop()) > 0) { + LOGDEB(("ExecCmd::doexec: selectloop returned %d\n", ret)); + if (m_advise) + m_advise->newData(0); + if (m_cancelRequest) { + LOGINFO(("ExecCmd::doexec: cancel request\n")); break; } - if (m_pipein[1] >= 0 && FD_ISSET(m_pipein[1], &writefds)) { - int n = write(m_pipein[1], input + nwritten, - inputlen - nwritten); - if (n < 0) { - LOGERR(("ExecCmd::doexec: write(2) failed. errno %d\n", - errno)); - haderror = true; - goto out; - } - nwritten += n; - if (nwritten == inputlen) { - if (m_provide) { - m_provide->newData(); - if (inputstring->empty()) { - close(m_pipein[1]); - m_pipein[1] = -1; - } else { - input = inputstring->data(); - inputlen = inputstring->length(); - nwritten = 0; - } - } else { - // cerr << "Closing output" << endl; - close(m_pipein[1]); - m_pipein[1] = -1; - } - } - } - if (m_pipeout[0] > 0 && FD_ISSET(m_pipeout[0], &readfds)) { - char buf[8192]; - int n = read(m_pipeout[0], buf, 8192); - if (n == 0) { - goto out; - } else if (n < 0) { - LOGERR(("ExecCmd::doexec: read(2) failed. errno %d\n", - errno)); - haderror = true; - goto out; - } else if (n > 0) { - // cerr << "READ: " << n << endl; - output->append(buf, n); - if (m_advise) - m_advise->newData(n); - } - } } + LOGDEB(("ExecCmd::doexec: selectloop returned %d\n", ret)); } - out: + // Normal return: deactivate cleaner, wait() will do the cleanup e.inactivate(); - return wait(haderror); + + return ExecCmd::wait(ret); } int ExecCmd::send(const string& data) { + NetconCli *con = dynamic_cast(m_tocmd.getptr()); + if (con == 0) { + LOGERR(("ExecCmd::send: outpipe is closed\n")); + return -1; + } unsigned int nwritten = 0; while (nwritten < data.length()) { if (m_cancelRequest) break; - int n = write(m_pipein[1], data.c_str() + nwritten, - data.length() - nwritten); + int n = con->send(data.c_str() + nwritten, data.length() - nwritten); if (n < 0) { - LOGERR(("ExecCmd::doexec: write(2) failed. errno %d\n", errno)); + LOGERR(("ExecCmd::doexec: send failed\n")); return -1; } nwritten += n; @@ -333,51 +375,29 @@ int ExecCmd::send(const string& data) int ExecCmd::receive(string& data) { - if (m_pipeout[0] < 0) { - LOGERR(("ExecCmd::receive: pipe is closed\n")); - return -1; - } - int nfds = m_pipeout[0] + 1; - fd_set readfds; - struct timeval tv; - tv.tv_sec = m_timeoutMs / 1000; - tv.tv_usec = 1000 * (m_timeoutMs % 1000); - FD_ZERO(&readfds); - FD_SET(m_pipeout[0], &readfds); - int ss; - if ((ss = select(nfds, &readfds, 0, 0, &tv)) <= 0) { - if (ss == 0) { - // timeout - return 0; - } - LOGERR(("ExecCmd::receive: select(2) failed. errno %d\n", errno)); - return -1; - } - - if (!FD_ISSET(m_pipeout[0], &readfds)) { - LOGERR(("ExecCmd::receive: fd not ready after select ??\n")); + NetconCli *con = dynamic_cast(m_fromcmd.getptr()); + if (con == 0) { + LOGERR(("ExecCmd::receive: outpipe is closed\n")); return -1; } char buf[8192]; - int n = read(m_pipeout[0], buf, 8192); - if (n == 0) { - return 0; - } else if (n < 0) { - LOGERR(("ExecCmd::doexec: read(2) failed. errno %d\n", errno)); - return -1; - } else { - // cerr << "READ: " << n << endl; - data.assign(buf, n); + int n = con->receive(buf, 8192); + if (n < 0) { + LOGERR(("ExecCmd::receive: error\n")); + } else if (n > 0) { + data.append(buf, n); } return n; } +// Wait for command status and clean up all resources. int ExecCmd::wait(bool haderror) { ExecCmdRsrc e(this); int status = -1; if (!m_cancelRequest) { - (void)waitpid(m_pid, &status, 0); + if (waitpid(m_pid, &status, 0) < 0) + status = -1; m_pid = -1; } LOGDEB(("ExecCmd::wait: got status 0x%x\n", status)); @@ -479,11 +499,23 @@ using namespace std; #include "execmd.h" +static int op_flags; +#define OPT_MOINS 0x1 +#define OPT_b 0x4 +#define OPT_w 0x8 +#define OPT_c 0x10 + const char *data = "Une ligne de donnees\n"; class MEAdv : public ExecCmdAdvise { public: ExecCmd *cmd; void newData(int cnt) { + if (op_flags & OPT_c) { + static int callcnt; + if (callcnt++ == 3) { + throw CancelExcept(); + } + } cerr << "newData(" << cnt << ")" << endl; // CancelCheck::instance().setCancel(); // CancelCheck::instance().checkCancel(); @@ -517,8 +549,9 @@ public: static char *thisprog; static char usage [] = -"execmd cmd [arg1 arg2 ...]\n" -" \n\n" +"trexecmd [-c] cmd [arg1 arg2 ...]\n" +" -c : test cancellation (ie: trexecmd -c sleep 1000)\n" +"trexecmd -w cmd : do the which thing\n" ; static void Usage(void) { @@ -526,14 +559,8 @@ static void Usage(void) exit(1); } -static int op_flags; -#define OPT_MOINS 0x1 -#define OPT_s 0x2 -#define OPT_b 0x4 -#define OPT_w 0x8 int main(int argc, char **argv) { - int count = 10; thisprog = argv[0]; argc--; argv++; @@ -544,13 +571,8 @@ int main(int argc, char **argv) Usage(); while (**argv) switch (*(*argv)++) { - case 'b': op_flags |= OPT_b; if (argc < 2) Usage(); - if ((sscanf(*(++argv), "%d", &count)) != 1) - Usage(); - argc--; - goto b1; - case 's': op_flags |= OPT_s; break; case 'w': op_flags |= OPT_w; break; + case 'c': op_flags |= OPT_c; break; default: Usage(); break; } b1: argc--; argv++; @@ -567,7 +589,7 @@ int main(int argc, char **argv) DebugLog::getdbl()->setloglevel(DEBDEB1); DebugLog::setfilename("stderr"); - + signal(SIGPIPE, SIG_IGN); if (op_flags & OPT_w) { string path; if (ExecCmd::which(cmd, path)) { @@ -580,7 +602,7 @@ int main(int argc, char **argv) MEAdv adv; adv.cmd = &mexec; mexec.setAdvise(&adv); - mexec.setTimeout(500); + mexec.setTimeout(5); mexec.setStderr("/tmp/trexecStderr"); mexec.putenv("TESTVARIABLE1=TESTVALUE1"); mexec.putenv("TESTVARIABLE2=TESTVALUE2"); @@ -598,11 +620,11 @@ int main(int argc, char **argv) try { status = mexec.doexec(cmd, l, ip, &output); } catch (CancelExcept) { - cerr << "CANCELED" << endl; + cerr << "CANCELLED" << endl; } fprintf(stderr, "Status: 0x%x\n", status); - cout << "Output:[" << output << "]" << endl; + cout << output; exit (status >> 8); } #endif // TEST diff --git a/src/utils/execmd.h b/src/utils/execmd.h index c324490e..4a1b7fd2 100644 --- a/src/utils/execmd.h +++ b/src/utils/execmd.h @@ -27,12 +27,15 @@ using std::string; using std::vector; #endif +#include "netcon.h" + /** * Callback function object to advise of new data arrival, or just periodic * heartbeat if cnt is 0. * - * The code using ExeCmd should raise an exception inside newData() - * (and catch it doexec's caller) to interrupt the command. + * To interrupt the command, the code using ExecCmd should either + * raise an exception inside newData() (and catch it in doexec's caller), or + * call ExecCmd::setCancel() * */ class ExecCmdAdvise { @@ -75,7 +78,7 @@ class ExecCmd { * Add/replace environment variable before executing command. This must * be called before doexec() to have an effect (possibly multiple * times for several variables). - * @param envassign an environment assignment string (name=value) + * @param envassign an environment assignment string ("name=value") */ void putenv(const string &envassign); @@ -124,6 +127,10 @@ class ExecCmd { const string *input = 0, string *output = 0); + /* + * The next four methods can be used when a Q/A dialog needs to be + * performed with the command + */ int startExec(const string &cmd, const list& args, bool has_input, bool has_output); int send(const string& data); @@ -152,8 +159,7 @@ class ExecCmd { * @param path exec seach path to use instead of getenv(PATH) * @return true if found */ - static bool which(const string& cmd, string& exepath, - const char* path = 0); + static bool which(const string& cmd, string& exe, const char* path = 0); friend class ExecCmdRsrc; private: @@ -165,14 +171,17 @@ class ExecCmd { string m_stderrFile; // Pipe for data going to the command int m_pipein[2]; + NetconP m_tocmd; // Pipe for data coming out int m_pipeout[2]; + NetconP m_fromcmd; // Subprocess id pid_t m_pid; // Saved sigmask sigset_t m_blkcld; - // Reset internal execution state + // Reset internal state indicators. Any resources should have been + // previously freed void reset() { m_cancelRequest = false; m_pipein[0] = m_pipein[1] = m_pipeout[0] = m_pipeout[1] = -1; diff --git a/src/utils/netcon.cpp b/src/utils/netcon.cpp new file mode 120000 index 00000000..b4848824 --- /dev/null +++ b/src/utils/netcon.cpp @@ -0,0 +1 @@ +../../../../docklib/netcon.cpp \ No newline at end of file diff --git a/src/utils/netcon.h b/src/utils/netcon.h new file mode 120000 index 00000000..452be73b --- /dev/null +++ b/src/utils/netcon.h @@ -0,0 +1 @@ +../../../../docklib/netcon.h \ No newline at end of file