|
a/src/utils/execmd.cpp |
|
b/src/utils/execmd.cpp |
1 |
#ifndef lint
|
1 |
#ifndef lint
|
2 |
static char rcsid[] = "@(#$Id: execmd.cpp,v 1.13 2006-01-24 12:22:20 dockes Exp $ (C) 2004 J.F.Dockes";
|
2 |
static char rcsid[] = "@(#$Id: execmd.cpp,v 1.14 2006-01-26 17:44:51 dockes Exp $ (C) 2004 J.F.Dockes";
|
3 |
#endif
|
3 |
#endif
|
4 |
/*
|
4 |
/*
|
5 |
* This program is free software; you can redistribute it and/or modify
|
5 |
* This program is free software; you can redistribute it and/or modify
|
6 |
* it under the terms of the GNU General Public License as published by
|
6 |
* it under the terms of the GNU General Public License as published by
|
7 |
* the Free Software Foundation; either version 2 of the License, or
|
7 |
* the Free Software Foundation; either version 2 of the License, or
|
|
... |
|
... |
47 |
void ExecCmd::putenv(const string &ea)
|
47 |
void ExecCmd::putenv(const string &ea)
|
48 |
{
|
48 |
{
|
49 |
m_env.push_back(ea);
|
49 |
m_env.push_back(ea);
|
50 |
}
|
50 |
}
|
51 |
|
51 |
|
|
|
52 |
/** A resource manager to ensure that execcmd cleans up if an exception is
|
|
|
53 |
* raised in the callback */
|
|
|
54 |
class ExecCmdRsrc {
|
|
|
55 |
public:
|
|
|
56 |
int pipein[2];
|
|
|
57 |
int pipeout[2];
|
|
|
58 |
pid_t pid;
|
|
|
59 |
ExecCmdRsrc() {
|
|
|
60 |
reset();
|
|
|
61 |
}
|
|
|
62 |
void reset() {
|
|
|
63 |
pipein[0] = pipein[1] = pipeout[0] = pipeout[1] = -1;
|
|
|
64 |
pid = -1;
|
|
|
65 |
}
|
|
|
66 |
~ExecCmdRsrc() {
|
|
|
67 |
int status;
|
|
|
68 |
if (pid > 0) {
|
|
|
69 |
LOGDEB(("ExecCmd: killing cmd\n"));
|
|
|
70 |
if (kill(pid, SIGTERM) == 0) {
|
|
|
71 |
for (int i = 0; i < 3; i++) {
|
|
|
72 |
(void)waitpid(pid, &status, WNOHANG);
|
|
|
73 |
if (kill(pid, 0) != 0)
|
|
|
74 |
break;
|
|
|
75 |
sleep(1);
|
|
|
76 |
if (i == 2) {
|
|
|
77 |
LOGDEB(("ExecCmd: killing (KILL) cmd\n"));
|
|
|
78 |
kill(pid, SIGKILL);
|
|
|
79 |
}
|
|
|
80 |
}
|
|
|
81 |
}
|
|
|
82 |
}
|
|
|
83 |
if (pipein[0] >= 0)
|
|
|
84 |
close(pipein[0]);
|
|
|
85 |
if (pipein[1] >= 0)
|
|
|
86 |
close(pipein[1]);
|
|
|
87 |
if (pipeout[0] >= 0)
|
|
|
88 |
close(pipeout[0]);
|
|
|
89 |
if (pipeout[1] >= 0)
|
|
|
90 |
close(pipeout[1]);
|
|
|
91 |
}
|
|
|
92 |
};
|
|
|
93 |
|
|
|
94 |
|
52 |
int ExecCmd::doexec(const string &cmd, const list<string>& args,
|
95 |
int ExecCmd::doexec(const string &cmd, const list<string>& args,
|
53 |
const string *inputstring, string *output)
|
96 |
const string *inputstring, string *output)
|
54 |
{
|
97 |
{
|
55 |
{ // Debug and logging
|
98 |
{ // Debug and logging
|
56 |
string command = cmd + " ";
|
99 |
string command = cmd + " ";
|
|
... |
|
... |
61 |
LOGDEB(("ExecCmd::doexec: %s\n", command.c_str()));
|
104 |
LOGDEB(("ExecCmd::doexec: %s\n", command.c_str()));
|
62 |
}
|
105 |
}
|
63 |
const char *input = inputstring ? inputstring->data() : 0;
|
106 |
const char *input = inputstring ? inputstring->data() : 0;
|
64 |
unsigned int inputlen = inputstring ? inputstring->length() : 0;
|
107 |
unsigned int inputlen = inputstring ? inputstring->length() : 0;
|
65 |
|
108 |
|
66 |
int pipein[2]; // subproc input
|
109 |
ExecCmdRsrc e;
|
67 |
int pipeout[2]; // subproc output
|
|
|
68 |
pipein[0] = pipein[1] = pipeout[0] = pipeout[1] = -1;
|
|
|
69 |
|
110 |
|
70 |
if (input && pipe(pipein) < 0) {
|
111 |
if (input && pipe(e.pipein) < 0) {
|
71 |
LOGERR(("ExecCmd::doexec: pipe(2) failed. errno %d\n", errno));
|
112 |
LOGERR(("ExecCmd::doexec: pipe(2) failed. errno %d\n", errno));
|
72 |
return -1;
|
113 |
return -1;
|
73 |
}
|
114 |
}
|
74 |
if (output && pipe(pipeout) < 0) {
|
115 |
if (output && pipe(e.pipeout) < 0) {
|
75 |
LOGERR(("ExecCmd::doexec: pipe(2) failed. errno %d\n", errno));
|
116 |
LOGERR(("ExecCmd::doexec: pipe(2) failed. errno %d\n", errno));
|
76 |
if (pipein[0] >= 0) close(pipein[0]);
|
|
|
77 |
if (pipein[1] >= 0) close(pipein[1]);
|
|
|
78 |
return -1;
|
117 |
return -1;
|
79 |
}
|
118 |
}
|
80 |
|
119 |
|
81 |
pid_t pid = fork();
|
120 |
e.pid = fork();
|
82 |
if (pid < 0) {
|
121 |
if (e.pid < 0) {
|
83 |
LOGERR(("ExecCmd::doexec: fork(2) failed. errno %d\n", errno));
|
122 |
LOGERR(("ExecCmd::doexec: fork(2) failed. errno %d\n", errno));
|
84 |
return -1;
|
123 |
return -1;
|
85 |
}
|
124 |
}
|
86 |
|
125 |
|
87 |
if (pid) {
|
126 |
if (e.pid) {
|
88 |
if (input) {
|
127 |
if (input) {
|
89 |
close(pipein[0]);
|
128 |
close(e.pipein[0]);
|
90 |
pipein[0] = -1;
|
129 |
e.pipein[0] = -1;
|
91 |
}
|
130 |
}
|
92 |
if (output) {
|
131 |
if (output) {
|
93 |
close(pipeout[1]);
|
132 |
close(e.pipeout[1]);
|
94 |
pipeout[1] = -1;
|
133 |
e.pipeout[1] = -1;
|
95 |
}
|
134 |
}
|
96 |
fd_set readfds, writefds;
|
135 |
fd_set readfds, writefds;
|
97 |
struct timeval tv;
|
136 |
struct timeval tv;
|
98 |
tv.tv_sec = 1;
|
137 |
tv.tv_sec = 1;
|
99 |
tv.tv_usec = 0;
|
138 |
tv.tv_usec = 0;
|
100 |
|
139 |
|
101 |
if (input || output) {
|
140 |
if (input || output) {
|
102 |
if (input)
|
141 |
if (input)
|
103 |
fcntl(pipein[1], F_SETFL, O_NONBLOCK);
|
142 |
fcntl(e.pipein[1], F_SETFL, O_NONBLOCK);
|
104 |
if (output)
|
143 |
if (output)
|
105 |
fcntl(pipeout[0], F_SETFL, O_NONBLOCK);
|
144 |
fcntl(e.pipeout[0], F_SETFL, O_NONBLOCK);
|
106 |
unsigned int nwritten = 0;
|
145 |
unsigned int nwritten = 0;
|
107 |
int nfds = MAX(pipein[1], pipeout[0]) + 1;
|
146 |
int nfds = MAX(e.pipein[1], e.pipeout[0]) + 1;
|
108 |
for(; nfds > 0;) {
|
147 |
for(; nfds > 0;) {
|
109 |
if (m_cancelRequest)
|
148 |
if (m_cancelRequest)
|
110 |
break;
|
149 |
break;
|
111 |
|
150 |
|
112 |
FD_ZERO(&writefds);
|
151 |
FD_ZERO(&writefds);
|
113 |
FD_ZERO(&readfds);
|
152 |
FD_ZERO(&readfds);
|
114 |
if (pipein[1] >= 0)
|
153 |
if (e.pipein[1] >= 0)
|
115 |
FD_SET(pipein[1], &writefds);
|
154 |
FD_SET(e.pipein[1], &writefds);
|
116 |
if (pipeout[0] >= 0)
|
155 |
if (e.pipeout[0] >= 0)
|
117 |
FD_SET(pipeout[0], &readfds);
|
156 |
FD_SET(e.pipeout[0], &readfds);
|
118 |
nfds = MAX(pipein[1], pipeout[0]) + 1;
|
157 |
nfds = MAX(e.pipein[1], e.pipeout[0]) + 1;
|
119 |
//struct timeval to; to.tv_sec = 1;to.tv_usec=0;
|
158 |
//struct timeval to; to.tv_sec = 1;to.tv_usec=0;
|
120 |
//cerr << "pipein[1] "<< pipein[1] << " pipeout[0] " <<
|
159 |
//cerr << "e.pipein[1] "<< e.pipein[1] << " e.pipeout[0] " <<
|
121 |
//pipeout[0] << " nfds " << nfds << endl;
|
160 |
//e.pipeout[0] << " nfds " << nfds << endl;
|
122 |
int ss;
|
161 |
int ss;
|
123 |
if ((ss = select(nfds, &readfds, &writefds, 0, &tv)) <= 0) {
|
162 |
if ((ss = select(nfds, &readfds, &writefds, 0, &tv)) <= 0) {
|
124 |
if (ss == 0) {
|
163 |
if (ss == 0) {
|
125 |
// Timeout, is ok.
|
164 |
// Timeout, is ok.
|
126 |
continue;
|
165 |
continue;
|
127 |
}
|
166 |
}
|
128 |
LOGERR(("ExecCmd::doexec: select(2) failed. errno %d\n",
|
167 |
LOGERR(("ExecCmd::doexec: select(2) failed. errno %d\n",
|
129 |
errno));
|
168 |
errno));
|
130 |
break;
|
169 |
break;
|
131 |
}
|
170 |
}
|
132 |
if (pipein[1] >= 0 && FD_ISSET(pipein[1], &writefds)) {
|
171 |
if (e.pipein[1] >= 0 && FD_ISSET(e.pipein[1], &writefds)) {
|
133 |
int n = write(pipein[1], input + nwritten,
|
172 |
int n = write(e.pipein[1], input + nwritten,
|
134 |
inputlen - nwritten);
|
173 |
inputlen - nwritten);
|
135 |
if (n < 0) {
|
174 |
if (n < 0) {
|
136 |
LOGERR(("ExecCmd::doexec: write(2) failed. errno %d\n",
|
175 |
LOGERR(("ExecCmd::doexec: write(2) failed. errno %d\n",
|
137 |
errno));
|
176 |
errno));
|
138 |
goto out;
|
177 |
goto out;
|
139 |
}
|
178 |
}
|
140 |
nwritten += n;
|
179 |
nwritten += n;
|
141 |
if (nwritten == inputlen) {
|
180 |
if (nwritten == inputlen) {
|
142 |
// cerr << "Closing output" << endl;
|
181 |
// cerr << "Closing output" << endl;
|
143 |
close(pipein[1]);
|
182 |
close(e.pipein[1]);
|
144 |
pipein[1] = -1;
|
183 |
e.pipein[1] = -1;
|
145 |
}
|
184 |
}
|
146 |
}
|
185 |
}
|
147 |
if (pipeout[0] > 0 && FD_ISSET(pipeout[0], &readfds)) {
|
186 |
if (e.pipeout[0] > 0 && FD_ISSET(e.pipeout[0], &readfds)) {
|
148 |
char buf[8192];
|
187 |
char buf[8192];
|
149 |
int n = read(pipeout[0], buf, 8192);
|
188 |
int n = read(e.pipeout[0], buf, 8192);
|
150 |
if (n == 0) {
|
189 |
if (n == 0) {
|
151 |
goto out;
|
190 |
goto out;
|
152 |
} else if (n < 0) {
|
191 |
} else if (n < 0) {
|
153 |
LOGERR(("ExecCmd::doexec: read(2) failed. errno %d\n",
|
192 |
LOGERR(("ExecCmd::doexec: read(2) failed. errno %d\n",
|
154 |
errno));
|
193 |
errno));
|
|
... |
|
... |
163 |
}
|
202 |
}
|
164 |
}
|
203 |
}
|
165 |
|
204 |
|
166 |
out:
|
205 |
out:
|
167 |
int status = -1;
|
206 |
int status = -1;
|
168 |
|
|
|
169 |
if (m_cancelRequest) {
|
207 |
if (!m_cancelRequest) {
|
170 |
// If we were canceled, need to cleanup
|
208 |
LOGDEB(("Execmd: canceled\n"));
|
171 |
LOGDEB1(("Killing cmd\n"));
|
|
|
172 |
kill(pid, SIGTERM);
|
|
|
173 |
for (int i = 0; i < 3; i++) {
|
|
|
174 |
if (kill(pid, 0) != 0)
|
|
|
175 |
break;
|
|
|
176 |
sleep(1);
|
|
|
177 |
if (i == 2) {
|
|
|
178 |
LOGDEB1(("Killing (KILL) cmd\n"));
|
|
|
179 |
kill(pid, SIGKILL);
|
|
|
180 |
}
|
|
|
181 |
}
|
|
|
182 |
}
|
|
|
183 |
(void)waitpid(pid, &status, 0);
|
209 |
(void)waitpid(e.pid, &status, 0);
|
184 |
if (pipein[0] >= 0)
|
210 |
e.pid = -1;
|
185 |
close(pipein[0]);
|
211 |
}
|
186 |
if (pipein[1] >= 0)
|
|
|
187 |
close(pipein[1]);
|
|
|
188 |
if (pipeout[0] >= 0)
|
|
|
189 |
close(pipeout[0]);
|
|
|
190 |
if (pipeout[1] >= 0)
|
|
|
191 |
close(pipeout[1]);
|
|
|
192 |
LOGDEB1(("ExecCmd::doexec: father got status 0x%x\n", status));
|
212 |
LOGDEB1(("ExecCmd::doexec: father got status 0x%x\n", status));
|
193 |
return status;
|
213 |
return status;
|
194 |
|
214 |
|
195 |
} else {
|
215 |
} else {
|
196 |
// In child process. Set up pipes, environment, and exec command
|
216 |
// In child process. Set up pipes, environment, and exec command
|
197 |
|
217 |
|
198 |
if (input) {
|
218 |
if (input) {
|
199 |
close(pipein[1]);
|
219 |
close(e.pipein[1]);
|
200 |
pipein[1] = -1;
|
220 |
e.pipein[1] = -1;
|
201 |
if (pipein[0] != 0) {
|
221 |
if (e.pipein[0] != 0) {
|
202 |
dup2(pipein[0], 0);
|
222 |
dup2(e.pipein[0], 0);
|
203 |
close(pipein[0]);
|
223 |
close(e.pipein[0]);
|
204 |
pipein[0] = -1;
|
224 |
e.pipein[0] = -1;
|
205 |
}
|
225 |
}
|
206 |
}
|
226 |
}
|
207 |
if (output) {
|
227 |
if (output) {
|
208 |
close(pipeout[0]);
|
228 |
close(e.pipeout[0]);
|
209 |
pipeout[0] = -1;
|
229 |
e.pipeout[0] = -1;
|
210 |
if (pipeout[1] != 1) {
|
230 |
if (e.pipeout[1] != 1) {
|
211 |
if (dup2(pipeout[1], 1) < 0) {
|
231 |
if (dup2(e.pipeout[1], 1) < 0) {
|
212 |
LOGERR(("ExecCmd::doexec: dup2(2) failed. errno %d\n",
|
232 |
LOGERR(("ExecCmd::doexec: dup2(2) failed. errno %d\n",
|
213 |
errno));
|
233 |
errno));
|
214 |
}
|
234 |
}
|
215 |
if (close(pipeout[1]) < 0) {
|
235 |
if (close(e.pipeout[1]) < 0) {
|
216 |
LOGERR(("ExecCmd::doexec: close(2) failed. errno %d\n",
|
236 |
LOGERR(("ExecCmd::doexec: close(2) failed. errno %d\n",
|
217 |
errno));
|
237 |
errno));
|
218 |
}
|
238 |
}
|
219 |
pipeout[1] = -1;
|
239 |
e.pipeout[1] = -1;
|
220 |
}
|
240 |
}
|
221 |
}
|
241 |
}
|
222 |
|
242 |
|
223 |
// Count args
|
243 |
e.reset();
|
224 |
list<string>::const_iterator it;
|
244 |
|
225 |
int i = 0;
|
|
|
226 |
for (it = args.begin(); it != args.end(); it++) i++;
|
|
|
227 |
// Allocate arg vector (2 more for arg0 + final 0)
|
245 |
// Allocate arg vector (2 more for arg0 + final 0)
|
228 |
typedef const char *Ccharp;
|
246 |
typedef const char *Ccharp;
|
229 |
Ccharp *argv;
|
247 |
Ccharp *argv;
|
230 |
argv = (Ccharp *)malloc((i+2) * sizeof(char *));
|
248 |
argv = (Ccharp *)malloc((args.size()+2) * sizeof(char *));
|
231 |
if (argv == 0) {
|
249 |
if (argv == 0) {
|
232 |
LOGERR(("ExecCmd::doexec: malloc() failed. errno %d\n",
|
250 |
LOGERR(("ExecCmd::doexec: malloc() failed. errno %d\n",
|
233 |
errno));
|
251 |
errno));
|
234 |
exit(1);
|
252 |
exit(1);
|
235 |
}
|
253 |
}
|
236 |
|
254 |
|
237 |
// Fill up argv
|
255 |
// Fill up argv
|
238 |
argv[0] = path_getsimple(cmd).c_str();
|
256 |
argv[0] = path_getsimple(cmd).c_str();
|
239 |
i = 1;
|
257 |
int i = 1;
|
|
|
258 |
list<string>::const_iterator it;
|
240 |
for (it = args.begin(); it != args.end(); it++) {
|
259 |
for (it = args.begin(); it != args.end(); it++) {
|
241 |
argv[i++] = it->c_str();
|
260 |
argv[i++] = it->c_str();
|
242 |
}
|
261 |
}
|
243 |
argv[i] = 0;
|
262 |
argv[i] = 0;
|
244 |
|
263 |
|
|
... |
|
... |
252 |
::putenv(strdup(it->c_str()));
|
271 |
::putenv(strdup(it->c_str()));
|
253 |
#else
|
272 |
#else
|
254 |
::putenv(it->c_str());
|
273 |
::putenv(it->c_str());
|
255 |
#endif
|
274 |
#endif
|
256 |
}
|
275 |
}
|
257 |
|
|
|
258 |
execvp(cmd.c_str(), (char *const*)argv);
|
276 |
execvp(cmd.c_str(), (char *const*)argv);
|
259 |
// Hu ho
|
277 |
// Hu ho
|
260 |
LOGERR(("ExecCmd::doexec: execvp(%s) failed. errno %d\n", cmd.c_str(),
|
278 |
LOGERR(("ExecCmd::doexec: execvp(%s) failed. errno %d\n", cmd.c_str(),
|
261 |
errno));
|
279 |
errno));
|
262 |
_exit(128);
|
280 |
_exit(128);
|
|
... |
|
... |
267 |
#include <stdio.h>
|
285 |
#include <stdio.h>
|
268 |
#include <string>
|
286 |
#include <string>
|
269 |
#include <iostream>
|
287 |
#include <iostream>
|
270 |
#include <list>
|
288 |
#include <list>
|
271 |
#include "debuglog.h"
|
289 |
#include "debuglog.h"
|
|
|
290 |
#include "cancelcheck.h"
|
|
|
291 |
|
272 |
using namespace std;
|
292 |
using namespace std;
|
273 |
|
293 |
|
274 |
#include "execmd.h"
|
294 |
#include "execmd.h"
|
275 |
|
295 |
|
276 |
const char *data = "Une ligne de donnees\n";
|
296 |
const char *data = "Une ligne de donnees\n";
|
277 |
class MEAdv : public ExecCmdAdvise {
|
297 |
class MEAdv : public ExecCmdAdvise {
|
278 |
public:
|
298 |
public:
|
279 |
ExecCmd *cmd;
|
299 |
ExecCmd *cmd;
|
280 |
void newData() {cerr << "New Data!" << endl;cmd->setCancel();}
|
300 |
void newData() {
|
|
|
301 |
cerr << "New Data!" << endl;
|
|
|
302 |
CancelCheck::instance().setCancel();
|
|
|
303 |
CancelCheck::instance().checkCancel();
|
|
|
304 |
// cmd->setCancel();
|
|
|
305 |
}
|
281 |
};
|
306 |
};
|
|
|
307 |
|
282 |
int main(int argc, const char **argv)
|
308 |
int main(int argc, const char **argv)
|
283 |
{
|
309 |
{
|
284 |
DebugLog::getdbl()->setloglevel(DEBDEB1);
|
310 |
DebugLog::getdbl()->setloglevel(DEBDEB1);
|
285 |
DebugLog::setfilename("stderr");
|
311 |
DebugLog::setfilename("stderr");
|
286 |
if (argc < 2) {
|
312 |
if (argc < 2) {
|
|
... |
|
... |
302 |
//ip = &input;
|
328 |
//ip = &input;
|
303 |
mexec.putenv("TESTVARIABLE1=TESTVALUE1");
|
329 |
mexec.putenv("TESTVARIABLE1=TESTVALUE1");
|
304 |
mexec.putenv("TESTVARIABLE2=TESTVALUE2");
|
330 |
mexec.putenv("TESTVARIABLE2=TESTVALUE2");
|
305 |
mexec.putenv("TESTVARIABLE3=TESTVALUE3");
|
331 |
mexec.putenv("TESTVARIABLE3=TESTVALUE3");
|
306 |
|
332 |
|
|
|
333 |
int status = -1;
|
|
|
334 |
try {
|
307 |
int status = mexec.doexec(cmd, l, ip, &output);
|
335 |
status = mexec.doexec(cmd, l, ip, &output);
|
|
|
336 |
} catch (CancelExcept) {
|
|
|
337 |
cerr << "CANCELED" << endl;
|
|
|
338 |
}
|
308 |
|
339 |
|
309 |
fprintf(stderr, "Status: 0x%x\n", status);
|
340 |
fprintf(stderr, "Status: 0x%x\n", status);
|
310 |
cout << "Output:" << output << endl;
|
341 |
cout << "Output:" << output << endl;
|
311 |
exit (status >> 8);
|
342 |
exit (status >> 8);
|
312 |
}
|
343 |
}
|