|
a/src/execmd.cpp |
|
b/src/execmd.cpp |
1 |
/* Copyright (C) 2004 J.F.Dockes
|
1 |
/* Copyright (C) 2004-2018 J.F.Dockes
|
2 |
* This program is free software; you can redistribute it and/or modify
|
2 |
* This program is free software; you can redistribute it and/or modify
|
3 |
* it under the terms of the GNU General Public License as published by
|
3 |
* it under the terms of the GNU General Public License as published by
|
4 |
* the Free Software Foundation; either version 2 of the License, or
|
4 |
* the Free Software Foundation; either version 2 of the License, or
|
5 |
* (at your option) any later version.
|
5 |
* (at your option) any later version.
|
6 |
*
|
6 |
*
|
|
... |
|
... |
12 |
* You should have received a copy of the GNU General Public License
|
12 |
* You should have received a copy of the GNU General Public License
|
13 |
* along with this program; if not, write to the
|
13 |
* along with this program; if not, write to the
|
14 |
* Free Software Foundation, Inc.,
|
14 |
* Free Software Foundation, Inc.,
|
15 |
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
15 |
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
16 |
*/
|
16 |
*/
|
17 |
#ifndef TEST_EXECMD
|
|
|
18 |
#ifdef BUILDING_RECOLL
|
17 |
#ifdef BUILDING_RECOLL
|
19 |
#include "autoconfig.h"
|
18 |
#include "autoconfig.h"
|
20 |
#else
|
19 |
#else
|
21 |
#include "config.h"
|
20 |
#include "config.h"
|
22 |
#endif
|
21 |
#endif
|
|
... |
|
... |
72 |
vector<string> m_env;
|
71 |
vector<string> m_env;
|
73 |
ExecCmdAdvise *m_advise{0};
|
72 |
ExecCmdAdvise *m_advise{0};
|
74 |
ExecCmdProvide *m_provide{0};
|
73 |
ExecCmdProvide *m_provide{0};
|
75 |
bool m_killRequest{false};
|
74 |
bool m_killRequest{false};
|
76 |
int m_timeoutMs{1000};
|
75 |
int m_timeoutMs{1000};
|
|
|
76 |
int m_killTimeoutMs{2000};
|
77 |
int m_rlimit_as_mbytes{0};
|
77 |
int m_rlimit_as_mbytes{0};
|
78 |
string m_stderrFile;
|
78 |
string m_stderrFile;
|
79 |
// Pipe for data going to the command
|
79 |
// Pipe for data going to the command
|
80 |
int m_pipein[2]{-1,-1};
|
80 |
int m_pipein[2]{-1,-1};
|
81 |
std::shared_ptr<NetconCli> m_tocmd;
|
81 |
std::shared_ptr<NetconCli> m_tocmd;
|
|
... |
|
... |
120 |
{
|
120 |
{
|
121 |
if (mS > 30) {
|
121 |
if (mS > 30) {
|
122 |
m->m_timeoutMs = mS;
|
122 |
m->m_timeoutMs = mS;
|
123 |
}
|
123 |
}
|
124 |
}
|
124 |
}
|
|
|
125 |
void ExecCmd::setKillTimeout(int mS)
|
|
|
126 |
{
|
|
|
127 |
m->m_killTimeoutMs = mS;
|
|
|
128 |
}
|
125 |
void ExecCmd::setStderr(const std::string& stderrFile)
|
129 |
void ExecCmd::setStderr(const std::string& stderrFile)
|
126 |
{
|
130 |
{
|
127 |
m->m_stderrFile = stderrFile;
|
131 |
m->m_stderrFile = stderrFile;
|
128 |
}
|
132 |
}
|
129 |
pid_t ExecCmd::getChildPid()
|
133 |
pid_t ExecCmd::getChildPid()
|
|
... |
|
... |
274 |
if (m_parent->m_pid > 0 && (grp = getpgid(m_parent->m_pid)) > 0) {
|
278 |
if (m_parent->m_pid > 0 && (grp = getpgid(m_parent->m_pid)) > 0) {
|
275 |
LOGDEB("ExecCmd: pid " << m_parent->m_pid << " killpg(" << grp <<
|
279 |
LOGDEB("ExecCmd: pid " << m_parent->m_pid << " killpg(" << grp <<
|
276 |
", SIGTERM)\n");
|
280 |
", SIGTERM)\n");
|
277 |
int ret = killpg(grp, SIGTERM);
|
281 |
int ret = killpg(grp, SIGTERM);
|
278 |
if (ret == 0) {
|
282 |
if (ret == 0) {
|
|
|
283 |
int ms_slept{0};
|
279 |
for (int i = 0; i < 3; i++) {
|
284 |
for (int i = 0; ; i++) {
|
280 |
msleep(i == 0 ? 5 : (i == 1 ? 100 : 2000));
|
285 |
int tosleep = i == 0 ? 5 : (i == 1 ? 100 : 1000);
|
|
|
286 |
msleep(tosleep);
|
|
|
287 |
ms_slept += tosleep;
|
281 |
int status;
|
288 |
int status;
|
282 |
(void)waitpid(m_parent->m_pid, &status, WNOHANG);
|
289 |
(void)waitpid(m_parent->m_pid, &status, WNOHANG);
|
283 |
if (kill(m_parent->m_pid, 0) != 0) {
|
290 |
if (kill(m_parent->m_pid, 0) != 0) {
|
284 |
break;
|
291 |
break;
|
285 |
}
|
292 |
}
|
286 |
if (i == 2) {
|
293 |
// killtimeout == -1 -> never KILL
|
|
|
294 |
if (m_parent->m_killTimeoutMs >= 0 &&
|
|
|
295 |
ms_slept >= m_parent->m_killTimeoutMs) {
|
287 |
LOGDEB("ExecCmd: killpg(" << (grp) << ", SIGKILL)\n");
|
296 |
LOGDEB("ExecCmd: killpg(" << grp << ", SIGKILL)\n");
|
288 |
killpg(grp, SIGKILL);
|
297 |
killpg(grp, SIGKILL);
|
289 |
(void)waitpid(m_parent->m_pid, &status, WNOHANG);
|
298 |
(void)waitpid(m_parent->m_pid, &status, WNOHANG);
|
|
|
299 |
break;
|
290 |
}
|
300 |
}
|
291 |
}
|
301 |
}
|
292 |
} else {
|
302 |
} else {
|
293 |
LOGERR("ExecCmd: error killing process group " << (grp) <<
|
303 |
LOGERR("ExecCmd: error killing process group " << (grp) <<
|
294 |
": " << errno << "\n");
|
304 |
": " << errno << "\n");
|
|
... |
|
... |
1142 |
argv[i++] = it->c_str();
|
1152 |
argv[i++] = it->c_str();
|
1143 |
}
|
1153 |
}
|
1144 |
argv[i] = 0;
|
1154 |
argv[i] = 0;
|
1145 |
execvp(m_argv[0].c_str(), (char *const*)argv);
|
1155 |
execvp(m_argv[0].c_str(), (char *const*)argv);
|
1146 |
}
|
1156 |
}
|
1147 |
|
|
|
1148 |
|
|
|
1149 |
////////////////////////////////////////////////////////////////////
|
|
|
1150 |
#else // TEST
|
|
|
1151 |
|
|
|
1152 |
#include <stdio.h>
|
|
|
1153 |
#include <stdlib.h>
|
|
|
1154 |
#include <unistd.h>
|
|
|
1155 |
#include <string.h>
|
|
|
1156 |
#include <signal.h>
|
|
|
1157 |
|
|
|
1158 |
#include <string>
|
|
|
1159 |
#include <iostream>
|
|
|
1160 |
#include <sstream>
|
|
|
1161 |
#include <vector>
|
|
|
1162 |
|
|
|
1163 |
#include "log.h"
|
|
|
1164 |
|
|
|
1165 |
#include "execmd.h"
|
|
|
1166 |
#ifdef BUILDING_RECOLL
|
|
|
1167 |
#include "smallut.h"
|
|
|
1168 |
#include "cancelcheck.h"
|
|
|
1169 |
#endif
|
|
|
1170 |
|
|
|
1171 |
using namespace std;
|
|
|
1172 |
|
|
|
1173 |
#ifdef BUILDING_RECOLL
|
|
|
1174 |
// Testing the rclexecm protocol outside of recoll. Here we use the
|
|
|
1175 |
// rcldoc.py filter, you can try with rclaudio too, adjust the file arg
|
|
|
1176 |
// accordingly
|
|
|
1177 |
bool exercise_mhexecm(const string& cmdstr, const string& mimetype,
|
|
|
1178 |
vector<string>& files)
|
|
|
1179 |
{
|
|
|
1180 |
ExecCmd cmd;
|
|
|
1181 |
|
|
|
1182 |
vector<string> myparams;
|
|
|
1183 |
|
|
|
1184 |
if (cmd.startExec(cmdstr, myparams, 1, 1) < 0) {
|
|
|
1185 |
cerr << "startExec " << cmdstr << " failed. Missing command?\n";
|
|
|
1186 |
return false;
|
|
|
1187 |
}
|
|
|
1188 |
|
|
|
1189 |
for (vector<string>::const_iterator it = files.begin();
|
|
|
1190 |
it != files.end(); it++) {
|
|
|
1191 |
// Build request message
|
|
|
1192 |
ostringstream obuf;
|
|
|
1193 |
obuf << "Filename: " << (*it).length() << "\n" << (*it);
|
|
|
1194 |
obuf << "Mimetype: " << mimetype.length() << "\n" << mimetype;
|
|
|
1195 |
// Bogus parameter should be skipped by filter
|
|
|
1196 |
obuf << "BogusParam: " << string("bogus").length() << "\n" << "bogus";
|
|
|
1197 |
obuf << "\n";
|
|
|
1198 |
cerr << "SENDING: [" << obuf.str() << "]\n";
|
|
|
1199 |
// Send it
|
|
|
1200 |
if (cmd.send(obuf.str()) < 0) {
|
|
|
1201 |
// The real code calls zapchild here, but we don't need it as
|
|
|
1202 |
// this will be handled by ~ExecCmd
|
|
|
1203 |
//cmd.zapChild();
|
|
|
1204 |
cerr << "send error\n";
|
|
|
1205 |
return false;
|
|
|
1206 |
}
|
|
|
1207 |
|
|
|
1208 |
// Read answer
|
|
|
1209 |
for (int loop = 0;; loop++) {
|
|
|
1210 |
string name, data;
|
|
|
1211 |
|
|
|
1212 |
// Code from mh_execm.cpp: readDataElement
|
|
|
1213 |
string ibuf;
|
|
|
1214 |
// Read name and length
|
|
|
1215 |
if (cmd.getline(ibuf) <= 0) {
|
|
|
1216 |
cerr << "getline error\n";
|
|
|
1217 |
return false;
|
|
|
1218 |
}
|
|
|
1219 |
// Empty line (end of message)
|
|
|
1220 |
if (!ibuf.compare("\n")) {
|
|
|
1221 |
cerr << "Got empty line\n";
|
|
|
1222 |
name.clear();
|
|
|
1223 |
break;
|
|
|
1224 |
}
|
|
|
1225 |
|
|
|
1226 |
// Filters will sometimes abort before entering the real
|
|
|
1227 |
// protocol, ie if a module can't be loaded. Check the
|
|
|
1228 |
// special filter error first word:
|
|
|
1229 |
if (ibuf.find("RECFILTERROR ") == 0) {
|
|
|
1230 |
cerr << "Got RECFILTERROR\n";
|
|
|
1231 |
return false;
|
|
|
1232 |
}
|
|
|
1233 |
|
|
|
1234 |
// We're expecting something like Name: len\n
|
|
|
1235 |
vector<string> tokens;
|
|
|
1236 |
stringToTokens(ibuf, tokens);
|
|
|
1237 |
if (tokens.size() != 2) {
|
|
|
1238 |
cerr << "bad line in filter output: [" << ibuf << "]\n";
|
|
|
1239 |
return false;
|
|
|
1240 |
}
|
|
|
1241 |
vector<string>::iterator it = tokens.begin();
|
|
|
1242 |
name = *it++;
|
|
|
1243 |
string& slen = *it;
|
|
|
1244 |
int len;
|
|
|
1245 |
if (sscanf(slen.c_str(), "%d", &len) != 1) {
|
|
|
1246 |
cerr << "bad line in filter output (no len): [" <<
|
|
|
1247 |
ibuf << "]\n";
|
|
|
1248 |
return false;
|
|
|
1249 |
}
|
|
|
1250 |
// Read element data
|
|
|
1251 |
data.erase();
|
|
|
1252 |
if (len > 0 && cmd.receive(data, len) != len) {
|
|
|
1253 |
cerr << "MHExecMultiple: expected " << len <<
|
|
|
1254 |
" bytes of data, got " << data.length() << endl;
|
|
|
1255 |
return false;
|
|
|
1256 |
}
|
|
|
1257 |
|
|
|
1258 |
// Empty element: end of message
|
|
|
1259 |
if (name.empty()) {
|
|
|
1260 |
break;
|
|
|
1261 |
}
|
|
|
1262 |
cerr << "Got name: [" << name << "] data [" << data << "]\n";
|
|
|
1263 |
}
|
|
|
1264 |
}
|
|
|
1265 |
return true;
|
|
|
1266 |
}
|
|
|
1267 |
#endif
|
|
|
1268 |
|
|
|
1269 |
static char *thisprog;
|
|
|
1270 |
static char usage [] =
|
|
|
1271 |
"trexecmd [-c -r -i -o] cmd [arg1 arg2 ...]\n"
|
|
|
1272 |
" -c : test cancellation (ie: trexecmd -c sleep 1000)\n"
|
|
|
1273 |
" -r : run reexec. Must be separate option.\n"
|
|
|
1274 |
" -i : command takes input\n"
|
|
|
1275 |
" -o : command produces output\n"
|
|
|
1276 |
" If -i is set, we send /etc/group contents to whatever command is run\n"
|
|
|
1277 |
" If -o is set, we print whatever comes out\n"
|
|
|
1278 |
"trexecmd -m <filter> <mimetype> <file> [file ...]: test execm:\n"
|
|
|
1279 |
" <filter> should be the path to an execm filter\n"
|
|
|
1280 |
" <mimetype> the type of the file parameters\n"
|
|
|
1281 |
"trexecmd -w cmd : do the 'which' thing\n"
|
|
|
1282 |
"trexecmd -l cmd test getline\n"
|
|
|
1283 |
;
|
|
|
1284 |
|
|
|
1285 |
static void Usage(void)
|
|
|
1286 |
{
|
|
|
1287 |
fprintf(stderr, "%s: usage:\n%s", thisprog, usage);
|
|
|
1288 |
exit(1);
|
|
|
1289 |
}
|
|
|
1290 |
|
|
|
1291 |
static int op_flags;
|
|
|
1292 |
#define OPT_MOINS 0x1
|
|
|
1293 |
#define OPT_i 0x4
|
|
|
1294 |
#define OPT_w 0x8
|
|
|
1295 |
#define OPT_c 0x10
|
|
|
1296 |
#define OPT_r 0x20
|
|
|
1297 |
#define OPT_m 0x40
|
|
|
1298 |
#define OPT_o 0x80
|
|
|
1299 |
#define OPT_l 0x100
|
|
|
1300 |
|
|
|
1301 |
// Data sink for data coming out of the command. We also use it to set
|
|
|
1302 |
// a cancellation after a moment.
|
|
|
1303 |
class MEAdv : public ExecCmdAdvise {
|
|
|
1304 |
public:
|
|
|
1305 |
void newData(int cnt) {
|
|
|
1306 |
if (op_flags & OPT_c) {
|
|
|
1307 |
#ifdef BUILDING_RECOLL
|
|
|
1308 |
static int callcnt;
|
|
|
1309 |
if (callcnt++ == 10) {
|
|
|
1310 |
// Just sets the cancellation flag
|
|
|
1311 |
CancelCheck::instance().setCancel();
|
|
|
1312 |
// Would be called from somewhere else and throws an
|
|
|
1313 |
// exception. We call it here for simplicity
|
|
|
1314 |
CancelCheck::instance().checkCancel();
|
|
|
1315 |
}
|
|
|
1316 |
#endif
|
|
|
1317 |
}
|
|
|
1318 |
cerr << "newData(" << cnt << ")" << endl;
|
|
|
1319 |
}
|
|
|
1320 |
};
|
|
|
1321 |
|
|
|
1322 |
// Data provider, used if the -i flag is set
|
|
|
1323 |
class MEPv : public ExecCmdProvide {
|
|
|
1324 |
public:
|
|
|
1325 |
FILE *m_fp;
|
|
|
1326 |
string *m_input;
|
|
|
1327 |
MEPv(string *i)
|
|
|
1328 |
: m_input(i) {
|
|
|
1329 |
m_fp = fopen("/etc/group", "r");
|
|
|
1330 |
}
|
|
|
1331 |
~MEPv() {
|
|
|
1332 |
if (m_fp) {
|
|
|
1333 |
fclose(m_fp);
|
|
|
1334 |
}
|
|
|
1335 |
}
|
|
|
1336 |
void newData() {
|
|
|
1337 |
char line[1024];
|
|
|
1338 |
if (m_fp && fgets(line, 1024, m_fp)) {
|
|
|
1339 |
m_input->assign((const char *)line);
|
|
|
1340 |
} else {
|
|
|
1341 |
m_input->erase();
|
|
|
1342 |
}
|
|
|
1343 |
}
|
|
|
1344 |
};
|
|
|
1345 |
|
|
|
1346 |
|
|
|
1347 |
|
|
|
1348 |
ReExec reexec;
|
|
|
1349 |
int main(int argc, char *argv[])
|
|
|
1350 |
{
|
|
|
1351 |
reexec.init(argc, argv);
|
|
|
1352 |
|
|
|
1353 |
if (0) {
|
|
|
1354 |
// Disabled: For testing reexec arg handling
|
|
|
1355 |
vector<string> newargs;
|
|
|
1356 |
newargs.push_back("newarg");
|
|
|
1357 |
newargs.push_back("newarg1");
|
|
|
1358 |
newargs.push_back("newarg2");
|
|
|
1359 |
newargs.push_back("newarg3");
|
|
|
1360 |
newargs.push_back("newarg4");
|
|
|
1361 |
reexec.insertArgs(newargs, 2);
|
|
|
1362 |
}
|
|
|
1363 |
|
|
|
1364 |
thisprog = argv[0];
|
|
|
1365 |
argc--;
|
|
|
1366 |
argv++;
|
|
|
1367 |
|
|
|
1368 |
while (argc > 0 && **argv == '-') {
|
|
|
1369 |
(*argv)++;
|
|
|
1370 |
if (!(**argv))
|
|
|
1371 |
/* Cas du "adb - core" */
|
|
|
1372 |
{
|
|
|
1373 |
Usage();
|
|
|
1374 |
}
|
|
|
1375 |
while (**argv)
|
|
|
1376 |
switch (*(*argv)++) {
|
|
|
1377 |
case 'c':
|
|
|
1378 |
op_flags |= OPT_c;
|
|
|
1379 |
break;
|
|
|
1380 |
case 'r':
|
|
|
1381 |
op_flags |= OPT_r;
|
|
|
1382 |
break;
|
|
|
1383 |
case 'w':
|
|
|
1384 |
op_flags |= OPT_w;
|
|
|
1385 |
break;
|
|
|
1386 |
#ifdef BUILDING_RECOLL
|
|
|
1387 |
case 'm':
|
|
|
1388 |
op_flags |= OPT_m;
|
|
|
1389 |
break;
|
|
|
1390 |
#endif
|
|
|
1391 |
case 'i':
|
|
|
1392 |
op_flags |= OPT_i;
|
|
|
1393 |
break;
|
|
|
1394 |
case 'l':
|
|
|
1395 |
op_flags |= OPT_l;
|
|
|
1396 |
break;
|
|
|
1397 |
case 'o':
|
|
|
1398 |
op_flags |= OPT_o;
|
|
|
1399 |
break;
|
|
|
1400 |
default:
|
|
|
1401 |
Usage();
|
|
|
1402 |
break;
|
|
|
1403 |
}
|
|
|
1404 |
argc--;
|
|
|
1405 |
argv++;
|
|
|
1406 |
}
|
|
|
1407 |
|
|
|
1408 |
if (argc < 1) {
|
|
|
1409 |
Usage();
|
|
|
1410 |
}
|
|
|
1411 |
|
|
|
1412 |
string arg1 = *argv++;
|
|
|
1413 |
argc--;
|
|
|
1414 |
vector<string> l;
|
|
|
1415 |
while (argc > 0) {
|
|
|
1416 |
l.push_back(*argv++);
|
|
|
1417 |
argc--;
|
|
|
1418 |
}
|
|
|
1419 |
|
|
|
1420 |
#ifdef BUILDING_RECOLL
|
|
|
1421 |
DebugLog::getdbl()->setloglevel(DEBDEB1);
|
|
|
1422 |
DebugLog::setfilename("stderr");
|
|
|
1423 |
#endif
|
|
|
1424 |
signal(SIGPIPE, SIG_IGN);
|
|
|
1425 |
|
|
|
1426 |
if (op_flags & OPT_r) {
|
|
|
1427 |
// Test reexec. Normally only once, next time we fall through
|
|
|
1428 |
// because we remove the -r option (only works if it was isolated, not like -rc
|
|
|
1429 |
chdir("/");
|
|
|
1430 |
argv[0] = strdup("");
|
|
|
1431 |
sleep(1);
|
|
|
1432 |
cerr << "Calling reexec\n";
|
|
|
1433 |
// We remove the -r arg from list, otherwise we are going to
|
|
|
1434 |
// loop (which you can try by commenting out the following
|
|
|
1435 |
// line)
|
|
|
1436 |
reexec.removeArg("-r");
|
|
|
1437 |
reexec.reexec();
|
|
|
1438 |
}
|
|
|
1439 |
|
|
|
1440 |
if (op_flags & OPT_w) {
|
|
|
1441 |
// Test "which" method
|
|
|
1442 |
string path;
|
|
|
1443 |
if (ExecCmd::which(arg1, path)) {
|
|
|
1444 |
cout << path << endl;
|
|
|
1445 |
return 0;
|
|
|
1446 |
}
|
|
|
1447 |
return 1;
|
|
|
1448 |
#ifdef BUILDING_RECOLL
|
|
|
1449 |
} else if (op_flags & OPT_m) {
|
|
|
1450 |
if (l.size() < 2) {
|
|
|
1451 |
Usage();
|
|
|
1452 |
}
|
|
|
1453 |
string mimetype = l[0];
|
|
|
1454 |
l.erase(l.begin());
|
|
|
1455 |
return exercise_mhexecm(arg1, mimetype, l) ? 0 : 1;
|
|
|
1456 |
#endif
|
|
|
1457 |
} else if (op_flags & OPT_l) {
|
|
|
1458 |
ExecCmd mexec;
|
|
|
1459 |
|
|
|
1460 |
if (mexec.startExec(arg1, l, false, true) < 0) {
|
|
|
1461 |
cerr << "Startexec failed\n";
|
|
|
1462 |
exit(1);
|
|
|
1463 |
}
|
|
|
1464 |
string output;
|
|
|
1465 |
int ret = mexec.getline(output, 2);
|
|
|
1466 |
cerr << "Got ret " << ret << " output " << output << endl;
|
|
|
1467 |
cerr << "Waiting\n";
|
|
|
1468 |
int status = mexec.wait();
|
|
|
1469 |
cerr << "Got status " << status << endl;
|
|
|
1470 |
exit(status);
|
|
|
1471 |
} else {
|
|
|
1472 |
// Default: execute command line arguments
|
|
|
1473 |
ExecCmd mexec;
|
|
|
1474 |
|
|
|
1475 |
// Set callback to be called whenever there is new data
|
|
|
1476 |
// available and at a periodic interval, to check for
|
|
|
1477 |
// cancellation
|
|
|
1478 |
MEAdv adv;
|
|
|
1479 |
mexec.setAdvise(&adv);
|
|
|
1480 |
mexec.setTimeout(5);
|
|
|
1481 |
|
|
|
1482 |
// Stderr output goes there
|
|
|
1483 |
mexec.setStderr("/tmp/trexecStderr");
|
|
|
1484 |
|
|
|
1485 |
// A few environment variables. Check with trexecmd env
|
|
|
1486 |
mexec.putenv("TESTVARIABLE1=TESTVALUE1");
|
|
|
1487 |
mexec.putenv("TESTVARIABLE2=TESTVALUE2");
|
|
|
1488 |
mexec.putenv("TESTVARIABLE3=TESTVALUE3");
|
|
|
1489 |
|
|
|
1490 |
string input, output;
|
|
|
1491 |
MEPv pv(&input);
|
|
|
1492 |
|
|
|
1493 |
string *ip = 0;
|
|
|
1494 |
if (op_flags & OPT_i) {
|
|
|
1495 |
ip = &input;
|
|
|
1496 |
mexec.setProvide(&pv);
|
|
|
1497 |
}
|
|
|
1498 |
string *op = 0;
|
|
|
1499 |
if (op_flags & OPT_o) {
|
|
|
1500 |
op = &output;
|
|
|
1501 |
}
|
|
|
1502 |
|
|
|
1503 |
int status = -1;
|
|
|
1504 |
try {
|
|
|
1505 |
status = mexec.doexec(arg1, l, ip, op);
|
|
|
1506 |
} catch (...) {
|
|
|
1507 |
cerr << "CANCELLED" << endl;
|
|
|
1508 |
}
|
|
|
1509 |
|
|
|
1510 |
fprintf(stderr, "Status: 0x%x\n", status);
|
|
|
1511 |
if (op_flags & OPT_o) {
|
|
|
1512 |
cout << output;
|
|
|
1513 |
}
|
|
|
1514 |
exit(status >> 8);
|
|
|
1515 |
}
|
|
|
1516 |
}
|
|
|
1517 |
#endif // TEST
|
|
|
1518 |
|
|
|