--- a/src/execmd.cpp
+++ b/src/execmd.cpp
@@ -1,4 +1,4 @@
-/* Copyright (C) 2004 J.F.Dockes
+/* Copyright (C) 2004-2018 J.F.Dockes
* 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
@@ -14,7 +14,6 @@
* Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#ifndef TEST_EXECMD
#ifdef BUILDING_RECOLL
#include "autoconfig.h"
#else
@@ -74,6 +73,7 @@
ExecCmdProvide *m_provide{0};
bool m_killRequest{false};
int m_timeoutMs{1000};
+ int m_killTimeoutMs{2000};
int m_rlimit_as_mbytes{0};
string m_stderrFile;
// Pipe for data going to the command
@@ -122,6 +122,10 @@
m->m_timeoutMs = mS;
}
}
+void ExecCmd::setKillTimeout(int mS)
+{
+ m->m_killTimeoutMs = mS;
+}
void ExecCmd::setStderr(const std::string& stderrFile)
{
m->m_stderrFile = stderrFile;
@@ -276,17 +280,23 @@
", SIGTERM)\n");
int ret = killpg(grp, SIGTERM);
if (ret == 0) {
- for (int i = 0; i < 3; i++) {
- msleep(i == 0 ? 5 : (i == 1 ? 100 : 2000));
+ int ms_slept{0};
+ for (int i = 0; ; i++) {
+ int tosleep = i == 0 ? 5 : (i == 1 ? 100 : 1000);
+ msleep(tosleep);
+ ms_slept += tosleep;
int status;
(void)waitpid(m_parent->m_pid, &status, WNOHANG);
if (kill(m_parent->m_pid, 0) != 0) {
break;
}
- if (i == 2) {
- LOGDEB("ExecCmd: killpg(" << (grp) << ", SIGKILL)\n");
+ // killtimeout == -1 -> never KILL
+ if (m_parent->m_killTimeoutMs >= 0 &&
+ ms_slept >= m_parent->m_killTimeoutMs) {
+ LOGDEB("ExecCmd: killpg(" << grp << ", SIGKILL)\n");
killpg(grp, SIGKILL);
(void)waitpid(m_parent->m_pid, &status, WNOHANG);
+ break;
}
}
} else {
@@ -1144,375 +1154,3 @@
argv[i] = 0;
execvp(m_argv[0].c_str(), (char *const*)argv);
}
-
-
-////////////////////////////////////////////////////////////////////
-#else // TEST
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <signal.h>
-
-#include <string>
-#include <iostream>
-#include <sstream>
-#include <vector>
-
-#include "log.h"
-
-#include "execmd.h"
-#ifdef BUILDING_RECOLL
-#include "smallut.h"
-#include "cancelcheck.h"
-#endif
-
-using namespace std;
-
-#ifdef BUILDING_RECOLL
-// Testing the rclexecm protocol outside of recoll. Here we use the
-// rcldoc.py filter, you can try with rclaudio too, adjust the file arg
-// accordingly
-bool exercise_mhexecm(const string& cmdstr, const string& mimetype,
- vector<string>& files)
-{
- ExecCmd cmd;
-
- vector<string> myparams;
-
- if (cmd.startExec(cmdstr, myparams, 1, 1) < 0) {
- cerr << "startExec " << cmdstr << " failed. Missing command?\n";
- return false;
- }
-
- for (vector<string>::const_iterator it = files.begin();
- it != files.end(); it++) {
- // Build request message
- ostringstream obuf;
- obuf << "Filename: " << (*it).length() << "\n" << (*it);
- obuf << "Mimetype: " << mimetype.length() << "\n" << mimetype;
- // Bogus parameter should be skipped by filter
- obuf << "BogusParam: " << string("bogus").length() << "\n" << "bogus";
- obuf << "\n";
- cerr << "SENDING: [" << obuf.str() << "]\n";
- // Send it
- if (cmd.send(obuf.str()) < 0) {
- // The real code calls zapchild here, but we don't need it as
- // this will be handled by ~ExecCmd
- //cmd.zapChild();
- cerr << "send error\n";
- return false;
- }
-
- // Read answer
- for (int loop = 0;; loop++) {
- string name, data;
-
- // Code from mh_execm.cpp: readDataElement
- string ibuf;
- // Read name and length
- if (cmd.getline(ibuf) <= 0) {
- cerr << "getline error\n";
- return false;
- }
- // Empty line (end of message)
- if (!ibuf.compare("\n")) {
- cerr << "Got empty line\n";
- name.clear();
- break;
- }
-
- // Filters will sometimes abort before entering the real
- // protocol, ie if a module can't be loaded. Check the
- // special filter error first word:
- if (ibuf.find("RECFILTERROR ") == 0) {
- cerr << "Got RECFILTERROR\n";
- return false;
- }
-
- // We're expecting something like Name: len\n
- vector<string> tokens;
- stringToTokens(ibuf, tokens);
- if (tokens.size() != 2) {
- cerr << "bad line in filter output: [" << ibuf << "]\n";
- return false;
- }
- vector<string>::iterator it = tokens.begin();
- name = *it++;
- string& slen = *it;
- int len;
- if (sscanf(slen.c_str(), "%d", &len) != 1) {
- cerr << "bad line in filter output (no len): [" <<
- ibuf << "]\n";
- return false;
- }
- // Read element data
- data.erase();
- if (len > 0 && cmd.receive(data, len) != len) {
- cerr << "MHExecMultiple: expected " << len <<
- " bytes of data, got " << data.length() << endl;
- return false;
- }
-
- // Empty element: end of message
- if (name.empty()) {
- break;
- }
- cerr << "Got name: [" << name << "] data [" << data << "]\n";
- }
- }
- return true;
-}
-#endif
-
-static char *thisprog;
-static char usage [] =
- "trexecmd [-c -r -i -o] cmd [arg1 arg2 ...]\n"
- " -c : test cancellation (ie: trexecmd -c sleep 1000)\n"
- " -r : run reexec. Must be separate option.\n"
- " -i : command takes input\n"
- " -o : command produces output\n"
- " If -i is set, we send /etc/group contents to whatever command is run\n"
- " If -o is set, we print whatever comes out\n"
- "trexecmd -m <filter> <mimetype> <file> [file ...]: test execm:\n"
- " <filter> should be the path to an execm filter\n"
- " <mimetype> the type of the file parameters\n"
- "trexecmd -w cmd : do the 'which' thing\n"
- "trexecmd -l cmd test getline\n"
- ;
-
-static void Usage(void)
-{
- fprintf(stderr, "%s: usage:\n%s", thisprog, usage);
- exit(1);
-}
-
-static int op_flags;
-#define OPT_MOINS 0x1
-#define OPT_i 0x4
-#define OPT_w 0x8
-#define OPT_c 0x10
-#define OPT_r 0x20
-#define OPT_m 0x40
-#define OPT_o 0x80
-#define OPT_l 0x100
-
-// Data sink for data coming out of the command. We also use it to set
-// a cancellation after a moment.
-class MEAdv : public ExecCmdAdvise {
-public:
- void newData(int cnt) {
- if (op_flags & OPT_c) {
-#ifdef BUILDING_RECOLL
- static int callcnt;
- if (callcnt++ == 10) {
- // Just sets the cancellation flag
- CancelCheck::instance().setCancel();
- // Would be called from somewhere else and throws an
- // exception. We call it here for simplicity
- CancelCheck::instance().checkCancel();
- }
-#endif
- }
- cerr << "newData(" << cnt << ")" << endl;
- }
-};
-
-// Data provider, used if the -i flag is set
-class MEPv : public ExecCmdProvide {
-public:
- FILE *m_fp;
- string *m_input;
- MEPv(string *i)
- : m_input(i) {
- m_fp = fopen("/etc/group", "r");
- }
- ~MEPv() {
- if (m_fp) {
- fclose(m_fp);
- }
- }
- void newData() {
- char line[1024];
- if (m_fp && fgets(line, 1024, m_fp)) {
- m_input->assign((const char *)line);
- } else {
- m_input->erase();
- }
- }
-};
-
-
-
-ReExec reexec;
-int main(int argc, char *argv[])
-{
- reexec.init(argc, argv);
-
- if (0) {
- // Disabled: For testing reexec arg handling
- vector<string> newargs;
- newargs.push_back("newarg");
- newargs.push_back("newarg1");
- newargs.push_back("newarg2");
- newargs.push_back("newarg3");
- newargs.push_back("newarg4");
- reexec.insertArgs(newargs, 2);
- }
-
- thisprog = argv[0];
- argc--;
- argv++;
-
- while (argc > 0 && **argv == '-') {
- (*argv)++;
- if (!(**argv))
- /* Cas du "adb - core" */
- {
- Usage();
- }
- while (**argv)
- switch (*(*argv)++) {
- case 'c':
- op_flags |= OPT_c;
- break;
- case 'r':
- op_flags |= OPT_r;
- break;
- case 'w':
- op_flags |= OPT_w;
- break;
-#ifdef BUILDING_RECOLL
- case 'm':
- op_flags |= OPT_m;
- break;
-#endif
- case 'i':
- op_flags |= OPT_i;
- break;
- case 'l':
- op_flags |= OPT_l;
- break;
- case 'o':
- op_flags |= OPT_o;
- break;
- default:
- Usage();
- break;
- }
- argc--;
- argv++;
- }
-
- if (argc < 1) {
- Usage();
- }
-
- string arg1 = *argv++;
- argc--;
- vector<string> l;
- while (argc > 0) {
- l.push_back(*argv++);
- argc--;
- }
-
-#ifdef BUILDING_RECOLL
- DebugLog::getdbl()->setloglevel(DEBDEB1);
- DebugLog::setfilename("stderr");
-#endif
- signal(SIGPIPE, SIG_IGN);
-
- if (op_flags & OPT_r) {
- // Test reexec. Normally only once, next time we fall through
- // because we remove the -r option (only works if it was isolated, not like -rc
- chdir("/");
- argv[0] = strdup("");
- sleep(1);
- cerr << "Calling reexec\n";
- // We remove the -r arg from list, otherwise we are going to
- // loop (which you can try by commenting out the following
- // line)
- reexec.removeArg("-r");
- reexec.reexec();
- }
-
- if (op_flags & OPT_w) {
- // Test "which" method
- string path;
- if (ExecCmd::which(arg1, path)) {
- cout << path << endl;
- return 0;
- }
- return 1;
-#ifdef BUILDING_RECOLL
- } else if (op_flags & OPT_m) {
- if (l.size() < 2) {
- Usage();
- }
- string mimetype = l[0];
- l.erase(l.begin());
- return exercise_mhexecm(arg1, mimetype, l) ? 0 : 1;
-#endif
- } else if (op_flags & OPT_l) {
- ExecCmd mexec;
-
- if (mexec.startExec(arg1, l, false, true) < 0) {
- cerr << "Startexec failed\n";
- exit(1);
- }
- string output;
- int ret = mexec.getline(output, 2);
- cerr << "Got ret " << ret << " output " << output << endl;
- cerr << "Waiting\n";
- int status = mexec.wait();
- cerr << "Got status " << status << endl;
- exit(status);
- } else {
- // Default: execute command line arguments
- ExecCmd mexec;
-
- // Set callback to be called whenever there is new data
- // available and at a periodic interval, to check for
- // cancellation
- MEAdv adv;
- mexec.setAdvise(&adv);
- mexec.setTimeout(5);
-
- // Stderr output goes there
- mexec.setStderr("/tmp/trexecStderr");
-
- // A few environment variables. Check with trexecmd env
- mexec.putenv("TESTVARIABLE1=TESTVALUE1");
- mexec.putenv("TESTVARIABLE2=TESTVALUE2");
- mexec.putenv("TESTVARIABLE3=TESTVALUE3");
-
- string input, output;
- MEPv pv(&input);
-
- string *ip = 0;
- if (op_flags & OPT_i) {
- ip = &input;
- mexec.setProvide(&pv);
- }
- string *op = 0;
- if (op_flags & OPT_o) {
- op = &output;
- }
-
- int status = -1;
- try {
- status = mexec.doexec(arg1, l, ip, op);
- } catch (...) {
- cerr << "CANCELLED" << endl;
- }
-
- fprintf(stderr, "Status: 0x%x\n", status);
- if (op_flags & OPT_o) {
- cout << output;
- }
- exit(status >> 8);
- }
-}
-#endif // TEST
-