blocxx
Exec.cpp
Go to the documentation of this file.
1/*******************************************************************************
2* Copyright (C) 2005, Quest Software, Inc. All rights reserved.
3* Copyright (C) 2006, Novell, Inc. All rights reserved.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7*
8* * Redistributions of source code must retain the above copyright notice,
9* this list of conditions and the following disclaimer.
10* * Redistributions in binary form must reproduce the above copyright
11* notice, this list of conditions and the following disclaimer in the
12* documentation and/or other materials provided with the distribution.
13* * Neither the name of
14* Quest Software, Inc.,
15* nor Novell, Inc.,
16* nor the names of its contributors or employees may be used to
17* endorse or promote products derived from this software without
18* specific prior written permission.
19*
20* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30* POSSIBILITY OF SUCH DAMAGE.
31*******************************************************************************/
32
33
38#include "blocxx/BLOCXX_config.h"
39#include "blocxx/Exec.hpp"
40#include "blocxx/Select.hpp"
44#include "blocxx/GlobalPtr.hpp"
46
47#if !defined(BLOCXX_WIN32)
49#include "blocxx/PosixExec.hpp"
50#else
52#include "blocxx/WinExec.hpp"
53#endif
54
55extern "C"
56{
57#ifdef BLOCXX_HAVE_SYS_RESOURCE_H
58#include <sys/resource.h>
59#endif
60#ifdef BLOCXX_HAVE_SYS_TYPES_H
61#include <sys/types.h>
62#endif
63#ifdef BLOCXX_HAVE_UNISTD_H
64#include <unistd.h>
65#endif
66#ifndef BLOCXX_WIN32
67#include <sys/wait.h>
68#include <fcntl.h>
69#endif
70}
71
72#ifdef BLOCXX_NCR
73#if defined(sigaction)
74#undef sigaction
75#endif
76#undef SIG_DFL
77#define SIG_DFL (void(*)())0
78#endif
79
80namespace BLOCXX_NAMESPACE
81{
85
86
88namespace Exec
89{
91
94system(const Array<String>& command, const char* const envp[], const Timeout& timeout)
95{
96
97#ifndef BLOCXX_WIN32
99#else
101#endif
102
103 ProcessRef proc = Exec::spawn(command[0], command, envp, spe);
104
105 proc->waitCloseTerm(Timeout::relative(0), timeout, Timeout::relative(0));
106 return proc->processStatus();
107}
108
109
111int
112safeSystem(const Array<String>& command, const char* const envp[])
113{
114 Process::Status ps = system(command, envp);
115 return ps.getPOSIXwaitpidStatus();
116}
117
120 char const * exec_path,
121 char const * const argv[], char const * const envp[],
122 PreExec & pre_exec
123)
124{
125#ifdef BLOCXX_WIN32
126 return WinExec::spawnImpl(exec_path, argv, envp, pre_exec);
127#else
128 return PosixExec::spawnImpl(exec_path, argv, envp, pre_exec);
129#endif
130}
131
134 char const * exec_path,
135 char const * const argv[], char const * const envp[],
136 PreExec & pre_exec
137)
138{
140 {
141 return WaitpidThreadFix::spawnProcess(exec_path, argv, envp, pre_exec);
142 }
143 return spawnImpl(exec_path, argv, envp, pre_exec);
144}
145
148 char const * const argv[], char const * const envp[]
149)
150{
151
152#ifdef BLOCXX_WIN32
154#else
156#endif
157
158 return spawn(argv[0], argv, envp, pre_exec);
159}
160
161namespace Impl
162{
164{
165 // prevent the parent from using the child's end of the pipes.
166 if (ppipe[BLOCXX_IN])
167 {
168 ppipe[BLOCXX_IN]->closeInputHandle();
169 }
170 if (ppipe[BLOCXX_OUT])
171 {
172 ppipe[BLOCXX_OUT]->closeOutputHandle();
173 }
174 if (ppipe[BLOCXX_SERR])
175 {
176 ppipe[BLOCXX_SERR]->closeOutputHandle();
177 }
178 ppipe[BLOCXX_EXEC_ERR]->closeOutputHandle();
179}
180} // end namespace Impl
181
182namespace
183{
184
185#ifndef BLOCXX_MIN
186#define BLOCXX_MIN(x, y) (x) < (y) ? (x) : (y)
187#endif
188
190class StringOutputGatherer : public OutputCallback
191{
192public:
193 StringOutputGatherer(String& stdoutput, String& erroutput, int outputLimit)
194 : m_output(stdoutput)
195 , m_erroutput(erroutput)
196 , m_outputLimit(outputLimit)
197 {
198 }
199 StringOutputGatherer(String& stdoutput, int outputLimit)
200 : m_output(stdoutput)
201 , m_erroutput(stdoutput)
202 , m_outputLimit(outputLimit)
203 {
204 }
205private:
206 virtual void doHandleData(const char* data, size_t dataLen,
207 EOutputSource outputSource, const ProcessRef& theProc,
208 size_t streamIndex, Array<char>& inputBuffer)
209 {
210 String& output = (outputSource == E_STDOUT) ? m_output : m_erroutput;
211 if (m_outputLimit >= 0 && output.length() + dataLen > static_cast<size_t>(m_outputLimit))
212 {
213 // the process output too much, so just copy what we can and return error
214 int lentocopy = BLOCXX_MIN(m_outputLimit - output.length(), dataLen);
215 if (lentocopy >= 0)
216 {
217 output += String(data, lentocopy);
218 }
219 BLOCXX_THROW(ExecBufferFullException,
220 "Exec::StringOutputGatherer::doHandleData(): buffer full");
221 }
222
223 output += String(data, dataLen);
224 }
225 String& m_output;
226 String& m_erroutput;
228};
229
231class SingleStringInputCallback : public InputCallback
232{
233public:
234 SingleStringInputCallback(const String& s)
235 : m_s(s)
236 {
237 }
238private:
239 virtual void doGetData(Array<char>& inputBuffer, const ProcessRef& theProc, size_t streamIndex)
240 {
241 if (m_s.length() > 0)
242 {
243 inputBuffer.insert(inputBuffer.end(), m_s.c_str(), m_s.c_str() + m_s.length());
244 m_s.erase();
245 }
246 else if (theProc->in()->isOpen())
247 {
248 theProc->in()->close();
249 }
250 }
251 String m_s;
252};
253
254}// end anonymous namespace
255
258 char const * const command[],
259 String& output,
260 char const * const envVars[],
261 const Timeout& timeout,
262 int outputLimit,
263 char const * input)
264{
265 if (g_execMockObject.get())
266 {
267 return g_execMockObject.get()->executeProcessAndGatherOutput(command, output, envVars, timeout, outputLimit, input);
268 }
269 return feedProcessAndGatherOutput(spawn(command, envVars),
270 output, timeout, outputLimit, input);
271}
272
275 char const * const command[],
276 String& output,
277 String& erroutput,
278 char const * const envVars[],
279 const Timeout& timeout,
280 int outputLimit,
281 char const * input)
282{
283 if (g_execMockObject.get())
284 {
285 return g_execMockObject.get()->executeProcessAndGatherOutput2(command, output,
286 erroutput, envVars, timeout, outputLimit, input);
287 }
288
289 return feedProcessAndGatherOutput(spawn(command, envVars),
290 output, erroutput, timeout, outputLimit, input);
291}
292
294BLOCXX_COMMON_API void executeProcessAndGatherOutput(
295 const Array<String>& command,
296 String& output, int& processstatus,
297 int timeoutsecs, int outputlimit,
298 const String& input)
299{
300 Timeout timeout = Timeout::infinite;
301 if (timeoutsecs != -1)
302 {
303 timeout = Timeout::relative(timeoutsecs);
304 }
306 output,
307 timeout,
308 outputlimit, input);
309
310 processstatus = ps.getPOSIXwaitpidStatus();
311}
312
315 ProcessRef const & proc,
316 String & output,
317 Timeout const & timeout,
318 int outputLimit,
319 String const & input)
320{
321 Array<ProcessRef> procarr(1, proc);
322 SingleStringInputCallback singleStringInputCallback(input);
323
324 StringOutputGatherer gatherer(output, outputLimit);
325 processInputOutput(gatherer, procarr, singleStringInputCallback, timeout);
326 proc->waitCloseTerm();
327 return proc->processStatus();
328}
329
332 ProcessRef const & proc,
333 String & output,
334 String & erroutput,
335 Timeout const & timeout,
336 int outputLimit,
337 String const & input)
338{
339 Array<ProcessRef> procarr(1, proc);
340 SingleStringInputCallback singleStringInputCallback(input);
341
342 StringOutputGatherer gatherer(output, erroutput, outputLimit);
343 processInputOutput(gatherer, procarr, singleStringInputCallback, timeout);
344 proc->waitCloseTerm();
345 return proc->processStatus();
346}
347
349void
350gatherOutput(String& output, const ProcessRef& proc, int timeoutSecs, int outputLimit)
351{
352 gatherOutput(output, proc, Timeout::relativeWithReset(timeoutSecs), outputLimit);
353}
355void
356gatherOutput(String& output, const ProcessRef& proc, const Timeout& timeout, int outputLimit)
357{
358 Array<ProcessRef> procs(1, proc);
359
360 StringOutputGatherer gatherer(output, outputLimit);
361 SingleStringInputCallback singleStringInputCallback = SingleStringInputCallback(String());
362 processInputOutput(gatherer, procs, singleStringInputCallback, timeout);
363}
364
370
372void
373OutputCallback::handleData(const char* data, size_t dataLen, EOutputSource outputSource, const ProcessRef& theProc, size_t streamIndex, Array<char>& inputBuffer)
374{
375 doHandleData(data, dataLen, outputSource, theProc, streamIndex, inputBuffer);
376}
377
382
384void
385InputCallback::getData(Array<char>& inputBuffer, const ProcessRef& theProc, size_t streamIndex)
386{
387 doGetData(inputBuffer, theProc, streamIndex);
388}
389
390namespace
391{
392 struct ProcessOutputState
393 {
398
399 ProcessOutputState()
400 : inIsOpen(true)
401 , outIsOpen(true)
402 , errIsOpen(true)
404 {
405 }
406 };
407
408}
409
411void
413{
414 TimeoutTimer timer(timeout);
415
416 Array<ProcessOutputState> processStates(procs.size());
417 int numOpenPipes(procs.size() * 2); // count of stdout & stderr. Ignore stdin for purposes of algorithm termination.
418
419 Array<Array<char> > inputs(processStates.size());
420 for (size_t i = 0; i < processStates.size(); ++i)
421 {
422 input.getData(inputs[i], procs[i], i);
423 processStates[i].availableDataLen = inputs[i].size();
424 if (!procs[i]->out()->isOpen())
425 {
426 processStates[i].outIsOpen = false;
427 }
428 if (!procs[i]->err()->isOpen())
429 {
430 processStates[i].errIsOpen = false;
431 }
432 if (!procs[i]->in()->isOpen())
433 {
434 processStates[i].inIsOpen = false;
435 }
436
437 }
438
439 timer.start();
440
441 while (numOpenPipes > 0)
442 {
444 std::map<int, int> inputIndexProcessIndex;
445 std::map<int, int> outputIndexProcessIndex;
446 for (size_t i = 0; i < procs.size(); ++i)
447 {
448 if (processStates[i].outIsOpen)
449 {
450 Select::SelectObject selObj(procs[i]->out()->getReadSelectObj());
451 selObj.waitForRead = true;
452 selObjs.push_back(selObj);
453 inputIndexProcessIndex[selObjs.size() - 1] = i;
454 }
455 if (processStates[i].errIsOpen)
456 {
457 Select::SelectObject selObj(procs[i]->err()->getReadSelectObj());
458 selObj.waitForRead = true;
459 selObjs.push_back(selObj);
460 inputIndexProcessIndex[selObjs.size() - 1] = i;
461 }
462 if (processStates[i].inIsOpen && processStates[i].availableDataLen > 0)
463 {
464 Select::SelectObject selObj(procs[i]->in()->getWriteSelectObj());
465 selObj.waitForWrite = true;
466 selObjs.push_back(selObj);
467 outputIndexProcessIndex[selObjs.size() - 1] = i;
468 }
469
470 }
471
472 int selectrval = Select::selectRW(selObjs, timer.asRelativeTimeout());
473 switch (selectrval)
474 {
476 {
477 BLOCXX_THROW_ERRNO_MSG(ExecErrorException, "Exec::gatherOutput: error selecting on stdout and stderr");
478 }
479 break;
481 {
482 timer.loop();
483 if (timer.expired())
484 {
485 BLOCXX_THROW(ExecTimeoutException, "Exec::gatherOutput: timedout");
486 }
487 }
488 break;
489 default:
490 {
491 int availableToFind = selectrval;
492
493 // reset the timeout counter
494 timer.resetOnLoop();
495
496 for (size_t i = 0; i < selObjs.size() && availableToFind > 0; ++i)
497 {
498 if (!selObjs[i].readAvailable)
499 {
500 continue;
501 }
502 else
503 {
504 --availableToFind;
505 }
506 int streamIndex = inputIndexProcessIndex[i];
507 UnnamedPipeRef readstream;
508 if (processStates[streamIndex].outIsOpen)
509 {
510 if (procs[streamIndex]->out()->getReadSelectObj() == selObjs[i].s)
511 {
512 readstream = procs[streamIndex]->out();
513 }
514 }
515
516 if (!readstream && processStates[streamIndex].errIsOpen)
517 {
518 if (procs[streamIndex]->err()->getReadSelectObj() == selObjs[i].s)
519 {
520 readstream = procs[streamIndex]->err();
521 }
522 }
523
524 if (!readstream)
525 {
526 continue; // for loop
527 }
528
529 char buff[1024];
530 int readrc = readstream->read(buff, sizeof(buff) - 1);
531 if (readrc == 0)
532 {
533 if (readstream == procs[streamIndex]->out())
534 {
535 processStates[streamIndex].outIsOpen = false;
536 procs[streamIndex]->out()->close();
537 }
538 else
539 {
540 processStates[streamIndex].errIsOpen = false;
541 procs[streamIndex]->err()->close();
542 }
543 --numOpenPipes;
544 }
545 else if (readrc == -1)
546 {
547 BLOCXX_THROW_ERRNO_MSG(ExecErrorException, "Exec::gatherOutput: read error");
548 }
549 else
550 {
551 buff[readrc] = '\0';
552 output.handleData(
553 buff,
554 readrc,
555 readstream == procs[streamIndex]->out() ? E_STDOUT : E_STDERR,
556 procs[streamIndex],
557 streamIndex, inputs[streamIndex]);
558 }
559 }
560
561 // handle stdin for all processes which have data to send to them.
562 for (size_t i = 0; i < selObjs.size() && availableToFind > 0; ++i)
563 {
564 if (!selObjs[i].writeAvailable)
565 {
566 continue;
567 }
568 else
569 {
570 --availableToFind;
571 }
572 int streamIndex = outputIndexProcessIndex[i];
573 UnnamedPipeRef writestream;
574 if (processStates[streamIndex].inIsOpen)
575 {
576 writestream = procs[streamIndex]->in();
577 }
578
579 if (!writestream)
580 {
581 continue; // for loop
582 }
583
584 size_t offset = inputs[streamIndex].size() - processStates[streamIndex].availableDataLen;
585 int writerc = writestream->write(&inputs[streamIndex][offset], processStates[streamIndex].availableDataLen);
586 if (writerc == -1 && errno == EPIPE)
587 {
588 processStates[streamIndex].inIsOpen = false;
589 procs[streamIndex]->in()->close();
590 }
591 else if (writerc == -1)
592 {
593 BLOCXX_THROW_ERRNO_MSG(ExecErrorException, "Exec::gatherOutput: write error");
594 }
595 else if (writerc != 0)
596 {
597 inputs[streamIndex].erase(inputs[streamIndex].begin(), inputs[streamIndex].begin() + writerc);
598 input.getData(inputs[streamIndex], procs[streamIndex], streamIndex);
599 processStates[streamIndex].availableDataLen = inputs[streamIndex].size();
600 }
601 }
602 }
603 break;
604 }
605 }
606}
607
608void processInputOutput(const String& input, String& output, const ProcessRef& process,
609 const Timeout& timeout, int outputLimit)
610{
611 Array<ProcessRef> procs;
612 procs.push_back(process);
613
614 StringOutputGatherer gatherer(output, outputLimit);
615 SingleStringInputCallback singleStringInputCallback = SingleStringInputCallback(input);
616 processInputOutput(gatherer, procs, singleStringInputCallback, timeout);
617}
618
619
620} // end namespace Exec
621
622} // end namespace BLOCXX_NAMESPACE
623
#define BLOCXX_DEFINE_EXCEPTION_WITH_ID(NAME)
Define a new exception class named <NAME>Exception that derives from Exception.
#define BLOCXX_THROW(exType, msg)
Throw an exception using FILE and LINE.
#define BLOCXX_DEFINE_EXCEPTION_WITH_BASE_AND_ID(NAME, BASE)
Define a new exception class named <NAME>Exception that derives from <BASE>.
#define BLOCXX_THROW_ERRNO_MSG(exType, msg)
Throw an exception using FILE, LINE, errno and strerror(errno)
bool errIsOpen
Definition Exec.cpp:396
String m_s
Definition Exec.cpp:251
String & m_erroutput
Definition Exec.cpp:226
bool inIsOpen
Definition Exec.cpp:394
int m_outputLimit
Definition Exec.cpp:227
#define BLOCXX_MIN(x, y)
Definition Exec.cpp:186
bool outIsOpen
Definition Exec.cpp:395
size_t availableDataLen
Definition Exec.cpp:397
String & m_output
Definition Exec.cpp:225
#define BLOCXX_GLOBAL_PTR_INIT
This macro is provided to abstract the details of GlobalPtr.
Array<> wraps std::vector<> in COWReference<> adding ref counting and copy on write capability.
Definition ArrayFwd.hpp:46
void push_back(const T &x)
Append an element to the end of the Array.
iterator erase(iterator position)
Remove an element of the Array specified with an iterator.
size_type size() const
virtual void doGetData(Array< char > &inputBuffer, const ProcessRef &theProc, size_t streamIndex)=0
void getData(Array< char > &inputBuffer, const ProcessRef &theProc, size_t streamIndex)
Definition Exec.cpp:385
virtual void doHandleData(const char *data, size_t dataLen, EOutputSource outputSource, const ProcessRef &theProc, size_t streamIndex, Array< char > &inputBuffer)=0
void handleData(const char *data, size_t dataLen, EOutputSource outputSource, const ProcessRef &theProc, size_t streamIndex, Array< char > &inputBuffer)
Definition Exec.cpp:373
This class is used to specify what spawn() should do between fork and exec.
Definition Exec.hpp:106
This class can be used to store a global pointer.
Definition GlobalPtr.hpp:84
virtual int write(const void *dataOut, int dataOutLen, ErrorAction errorAsException=E_RETURN_ON_ERROR)=0
Write a specified number of bytes to the device that is exposing the IOIFC interface.
virtual int read(void *dataIn, int dataInLen, ErrorAction errorAsException=E_RETURN_ON_ERROR)=0
Read a specified number of bytes from the device that is exposing the IOIFC interface.
Portable process status.
Definition Process.hpp:123
int getPOSIXwaitpidStatus() const
Get the result from waitpid()
Definition Process.cpp:191
This String class is an abstract data type that represents as NULL terminated string of characters.
Definition String.hpp:67
A timeout can be absolute, which means that it will happen at the specified DateTime.
Definition Timeout.hpp:56
static Timeout relativeWithReset(float seconds)
Definition Timeout.cpp:66
static Timeout relative(float seconds)
Definition Timeout.cpp:58
static Timeout infinite
Definition Timeout.hpp:62
A TimeoutTimer is used by an algorithm to determine when a timeout has expired.
bool expired() const
Indicates whether the last loop time has exceeded the timeout.
void start()
Meant to be called by timeout functions which loop.
void loop()
Meant to be called by timeout functions which loop, but don't want to reset the interval.
void resetOnLoop()
Meant to be called by timeout functions which loop, and that want to reset the interval.
unsigned const BLOCXX_OUT
Definition Exec.hpp:785
unsigned const BLOCXX_NPIPE
Definition Exec.hpp:788
unsigned const BLOCXX_IN
Definition Exec.hpp:784
void close_child_ends(UnnamedPipeRef ppipe[BLOCXX_NPIPE])
Definition Exec.cpp:163
unsigned const BLOCXX_EXEC_ERR
Definition Exec.hpp:787
unsigned const BLOCXX_SERR
Definition Exec.hpp:786
int safeSystem(const Array< String > &command, const char *const envp[])
This is deprecated.
Definition Exec.cpp:112
ProcessRef spawnImpl(char const *exec_path, char const *const argv[], char const *const envp[], PreExec &pre_exec)
Definition Exec.cpp:119
Process::Status executeProcessAndGatherOutput(char const *const command[], String &output, char const *const envVars[], const Timeout &timeout, int outputLimit, char const *input)
Execute a command and run feedProcessAndGatherOutput() on the process.
Definition Exec.cpp:257
ProcessRef spawn(char const *exec_path, char const *const argv[], char const *const envp[], PreExec &pre_exec)
Run the executable exec_path in a child process, with argv for the program arguments and envp for the...
Definition Exec.cpp:133
void processInputOutput(OutputCallback &output, Array< ProcessRef > &procs, InputCallback &input, const Timeout &timeout)
Send input and wait for output from child processes.
Definition Exec.cpp:412
Process::Status feedProcessAndGatherOutput(ProcessRef const &proc, String &output, Timeout const &timeout, int outputLimit, String const &input)
Send input to a process, collect the output, and wait for it to exit.
Definition Exec.cpp:314
::BLOCXX_NAMESPACE::GlobalPtr< ExecMockObject, Impl::NullFactory > g_execMockObject
Definition Exec.cpp:90
Process::Status system(const Array< String > &command, const char *const envp[], const Timeout &timeout)
Execute a command.
Definition Exec.cpp:94
void gatherOutput(String &output, const ProcessRef &proc, int timeoutSecs, int outputLimit)
Definition Exec.cpp:350
ProcessRef spawnImpl(char const *exec_path, char const *const argv[], char const *const envp[], Exec::PreExec &pre_exec)
const int SELECT_TIMEOUT
The value returned from select when the timeout value has expired.
Definition Select.hpp:59
int selectRW(SelectObjectArray &selarray, UInt32 ms)
Definition Select.cpp:92
const int SELECT_ERROR
The value returned from select when any error occurs other than timeout.
Definition Select.hpp:63
BLOCXX_COMMON_API ProcessRef spawnProcess(char const *exec_path, char const *const argv[], char const *const envp[], Exec::PreExec &pre_exec)
BLOCXX_COMMON_API bool shouldUseWaitpidThreadFix()
ProcessRef spawnImpl(char const *exec_path, char const *const argv[], char const *const envp[], Exec::PreExec &pre_exec)
Taken from RFC 1321.
IntrusiveReference< Process > ProcessRef
bool waitForRead
Input parameter. Set it to true to indicate that waiting for read availability on s is desired.
Definition Select.hpp:103
bool waitForWrite
Input parameter. Set it to true to indicate that waiting for write availability on s is desired.
Definition Select.hpp:105