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
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{
115 return ps.getPOSIXwaitpidStatus();
116}
117
120 char const * exec_path,
121 char const * const argv[], char const * const envp[],
123)
124{
125#ifdef BLOCXX_WIN32
127#else
129#endif
130}
131
134 char const * exec_path,
135 char const * const argv[], char const * const envp[],
137)
138{
140 {
142 }
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)
197 {
198 }
199 StringOutputGatherer(String& stdoutput, int outputLimit)
203 {
204 }
205private:
206 virtual void doHandleData(const char* data, size_t dataLen,
209 {
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
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 }
271}
272
275 char const * const command[],
276 String& output,
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,
287 }
288
291}
292
312
315 ProcessRef const & proc,
316 String & output,
317 Timeout const & timeout,
318 int outputLimit,
319 String const & input)
320{
322 SingleStringInputCallback singleStringInputCallback(input);
323
324 StringOutputGatherer gatherer(output, outputLimit);
326 proc->waitCloseTerm();
327 return proc->processStatus();
328}
329
332 ProcessRef const & proc,
333 String & output,
335 Timeout const & timeout,
336 int outputLimit,
337 String const & input)
338{
340 SingleStringInputCallback singleStringInputCallback(input);
341
342 StringOutputGatherer gatherer(output, erroutput, outputLimit);
344 proc->waitCloseTerm();
345 return proc->processStatus();
346}
347
349void
355void
357{
359
360 StringOutputGatherer gatherer(output, outputLimit);
361 SingleStringInputCallback singleStringInputCallback = SingleStringInputCallback(String());
363}
364
370
372void
377
382
384void
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{
415
417 int numOpenPipes(procs.size() * 2); // count of stdout & stderr. Ignore stdin for purposes of algorithm termination.
418
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 {
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 }
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 }
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 {
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 {
505 }
509 {
510 if (procs[streamIndex]->out()->getReadSelectObj() == selObjs[i].s)
511 {
512 readstream = procs[streamIndex]->out();
513 }
514 }
515
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,
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 {
571 }
575 {
577 }
578
579 if (!writestream)
580 {
581 continue; // for loop
582 }
583
584 size_t offset = inputs[streamIndex].size() - 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);
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{
612 procs.push_back(process);
613
614 StringOutputGatherer gatherer(output, outputLimit);
615 SingleStringInputCallback singleStringInputCallback = SingleStringInputCallback(input);
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 Array.hpp:66
void push_back(const T &x)
Append an element to the end of the Array.
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
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.
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 operator==(const Array< T > &x, const Array< T > &y)