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"
41 #include "blocxx/ExceptionIds.hpp"
42 #include "blocxx/TimeoutTimer.hpp"
44 #include "blocxx/GlobalPtr.hpp"
46 
47 #if !defined(BLOCXX_WIN32)
49 #include "blocxx/PosixExec.hpp"
50 #else
51 #include "blocxx/UnnamedPipe.hpp"
52 #include "blocxx/WinExec.hpp"
53 #endif
54 
55 extern "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 
80 namespace BLOCXX_NAMESPACE
81 {
85 
86 
88 namespace Exec
89 {
91 
94 system(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 
111 int
112 safeSystem(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 
161 namespace 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 
182 namespace
183 {
184 
185 #ifndef BLOCXX_MIN
186 #define BLOCXX_MIN(x, y) (x) < (y) ? (x) : (y)
187 #endif
188 
190 class StringOutputGatherer : public OutputCallback
191 {
192 public:
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  }
205 private:
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 
231 class SingleStringInputCallback : public InputCallback
232 {
233 public:
234  SingleStringInputCallback(const String& s)
235  : m_s(s)
236  {
237  }
238 private:
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 
294 BLOCXX_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 
349 void
350 gatherOutput(String& output, const ProcessRef& proc, int timeoutSecs, int outputLimit)
351 {
352  gatherOutput(output, proc, Timeout::relativeWithReset(timeoutSecs), outputLimit);
353 }
355 void
356 gatherOutput(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 
367 {
368 
369 }
370 
372 void
373 OutputCallback::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 
380 {
381 }
382 
384 void
385 InputCallback::getData(Array<char>& inputBuffer, const ProcessRef& theProc, size_t streamIndex)
386 {
387  doGetData(inputBuffer, theProc, streamIndex);
388 }
389 
390 namespace
391 {
392  struct ProcessOutputState
393  {
394  bool inIsOpen;
395  bool outIsOpen;
396  bool errIsOpen;
398 
399  ProcessOutputState()
400  : inIsOpen(true)
401  , outIsOpen(true)
402  , errIsOpen(true)
403  , availableDataLen(0)
404  {
405  }
406  };
407 
408 }
409 
411 void
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  {
443  Select::SelectObjectArray selObjs;
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 
608 void 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