From fbcbfbf7e5eb0556556140755ef981f9b7d7bc0c Mon Sep 17 00:00:00 2001 From: Jean-Francois Dockes Date: Thu, 31 May 2018 16:44:50 +0200 Subject: [PATCH] shared code --- src/utils/execmd.cpp | 394 ++----------------------------------------- src/utils/execmd.h | 13 +- src/utils/log.h | 15 ++ src/utils/netcon.cpp | 3 +- 4 files changed, 45 insertions(+), 380 deletions(-) diff --git a/src/utils/execmd.cpp b/src/utils/execmd.cpp index 7204f5a6..3c4c63bb 100644 --- a/src/utils/execmd.cpp +++ b/src/utils/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 @@ public: 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 @@ void ExecCmd::setTimeout(int mS) 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 @@ public: ", 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 @@ void ReExec::reexec() argv[i] = 0; execvp(m_argv[0].c_str(), (char *const*)argv); } - - -//////////////////////////////////////////////////////////////////// -#else // TEST - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#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& files) -{ - ExecCmd cmd; - - vector myparams; - - if (cmd.startExec(cmdstr, myparams, 1, 1) < 0) { - cerr << "startExec " << cmdstr << " failed. Missing command?\n"; - return false; - } - - for (vector::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 tokens; - stringToTokens(ibuf, tokens); - if (tokens.size() != 2) { - cerr << "bad line in filter output: [" << ibuf << "]\n"; - return false; - } - vector::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 [file ...]: test execm:\n" - " should be the path to an execm filter\n" - " 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 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 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 - diff --git a/src/utils/execmd.h b/src/utils/execmd.h index 7da15d92..852c9a19 100644 --- a/src/utils/execmd.h +++ b/src/utils/execmd.h @@ -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 @@ -117,6 +117,17 @@ public: */ void setStderr(const std::string& stderrFile); + /** + * Set kill wait timeout. This is the maximum time we'll wait for + * the command after sending a SIGTERM, before sending a SIGKILL. + + * @param mS the maximum number of mS to wait. Note that values + * below 1000 mS make no sense as the program will sleep for + * longer time before retrying the waitpid(). Use -1 for + * forever (bad idea), 0 for absolutely no pity. + */ + void setKillTimeout(int mS); + /** * Execute command. * diff --git a/src/utils/log.h b/src/utils/log.h index 1060ab4a..1c3549f8 100644 --- a/src/utils/log.h +++ b/src/utils/log.h @@ -34,6 +34,8 @@ #ifndef _LOG_H_X_INCLUDED_ #define _LOG_H_X_INCLUDED_ +#include + #include #include #include @@ -191,4 +193,17 @@ private: #endif #define LOGFATAL LOGFAT +#if (_POSIX_C_SOURCE >= 200112L) && ! _GNU_SOURCE +#define LOGSYSERR(who, what, arg) { \ + char buf[200]; buf[0] = 0; strerror_r(errno, buf, 200); \ + LOGERR(who << ": " << what << "(" << arg << "): errno " << errno << \ + ": " << buf << std::endl); \ + } +#else +#define LOGSYSERR(who, what, arg) { \ + char buf[200]; buf[0] = 0; \ + LOGERR(who << ": " << what << "(" << arg << "): errno " << errno << \ + ": " << strerror_r(errno, buf, 200) << std::endl); \ + } +#endif #endif /* _LOG_H_X_INCLUDED_ */ diff --git a/src/utils/netcon.cpp b/src/utils/netcon.cpp index cd3840ed..02492e91 100644 --- a/src/utils/netcon.cpp +++ b/src/utils/netcon.cpp @@ -72,10 +72,11 @@ using namespace std; static const int one = 1; static const int zero = 0; +#ifndef LOGSYSERR #define LOGSYSERR(who, call, spar) \ LOGERR(who << ": " << call << "(" << spar << ") errno " << \ errno << " (" << strerror(errno) << ")\n") - +#endif #ifndef MIN #define MIN(a,b) ((a)<(b)?(a):(b)) #endif