blocxx
PosixExec.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 
40 #if !defined(BLOCXX_WIN32)
41 
42 #include "blocxx/PosixExec.hpp"
43 #include "blocxx/Format.hpp"
44 #include "blocxx/SafeCString.hpp"
45 #include "blocxx/Assertion.hpp"
47 #include "blocxx/Paths.hpp"
48 #include "blocxx/TimeoutTimer.hpp"
49 #include "blocxx/Select.hpp"
50 
51 extern "C"
52 {
53 #ifdef BLOCXX_HAVE_SYS_RESOURCE_H
54 #include <sys/resource.h>
55 #endif
56 #ifdef BLOCXX_HAVE_SYS_TYPES_H
57 #include <sys/types.h>
58 #endif
59 #ifdef BLOCXX_HAVE_UNISTD_H
60 #include <unistd.h>
61 #endif
62 
63 #include <sys/wait.h>
64 #include <fcntl.h>
65 #include <errno.h>
66 #include <stdio.h> // for perror
67 #include <signal.h>
68 }
69 
70 // NSIG may be defined by signal.h, otherwise 64 should be plenty.
71 #ifndef NSIG
72 #define NSIG 64
73 #endif
74 
75 #if defined(sigemptyset)
76 // We want to use the function instead of the macro (for scoping reasons).
77 #undef sigemptyset
78 #endif // sigemptyset
79 
80 #ifdef BLOCXX_NCR
81 #if defined(sigaction)
82 #undef sigaction
83 #endif
84 #undef SIG_DFL
85 #define SIG_DFL (void(*)())0
86 #endif
87 
88 namespace BLOCXX_NAMESPACE
89 {
90 
91 namespace // anonymous
92 {
93 
94  void throw_child_error(Exec::PreExec::Error const & err, const String& process_path)
95  {
96  Format msg("Exec::spawn(%1): child startup failed: %2", process_path, err.message);
97  if (err.error_num != 0)
98  {
100  ExecErrorException, msg.c_str(), err.error_num);
101  }
102  else
103  {
104  BLOCXX_THROW(ExecErrorException, msg.c_str());
105  }
106  }
107 
108  void check(bool b, char const * message, bool use_errno = true)
109  {
110  if (!b)
111  {
112  Exec::PreExec::Error x;
113  SafeCString::strcpy_trunc(x.message, message);
114  x.error_num = use_errno ? errno : 0;
115  throw x;
116  }
117  }
118 
119  void parent_check(bool b, char const * msg)
120  {
121  if (!b)
122  {
123  BLOCXX_THROW(ExecErrorException, msg);
124  }
125  }
126 
127  void close_on_exec(Descriptor descr, bool may_be_bad)
128  {
129  int e = ::fcntl(descr, F_SETFD, FD_CLOEXEC);
130  check(e == 0 || may_be_bad && errno == EBADF, "fcntl");
131  }
132 
133  void handle_child_error(int rc, Exec::PreExec::Error const & ce, Process & proc, const String& process_path)
134  {
135  if (rc < 0) // read of error status from child failed
136  {
137  int errnum = errno;
138  // For some reason child initialization failed; kill it.
139  proc.waitCloseTerm(Timeout::relative(0.0), Timeout::relative(0.0), Timeout::relative(0.0));
140  if (errnum == ETIMEDOUT)
141  {
142  BLOCXX_THROW(ExecErrorException,
143  Format("Exec::spawn(%1): timed out waiting for child to exec()",process_path).c_str());
144  }
145  BLOCXX_THROW_ERRNO_MSG1(ExecErrorException,
146  Format("Exec::spawn(%1): error reading init status from child",process_path).c_str(), errnum);
147  }
148  if (rc > 0) // child sent an initialization error message
149  {
150  throw_child_error(ce, process_path);
151  }
152  // If rc == 0, initialization succeeded
153  }
154 
155  long getMaxOpenFiles()
156  {
157  long sysconfValue = sysconf(_SC_OPEN_MAX);
158  long maxOpen = sysconfValue;
159  rlimit rl;
160  rl.rlim_cur = rlim_t(0);
161  if( getrlimit(RLIMIT_NOFILE, &rl) != -1 )
162  {
163  if( sysconfValue < 0 )
164  {
165  maxOpen = rl.rlim_cur;
166  }
167  else
168  {
169  maxOpen = std::min<rlim_t>(rl.rlim_cur, sysconfValue);
170  }
171  }
172  // Check for a value of maxOpen that really is reasonable.
173  // This checks the maximum value to make sure it will fit in an int
174  // (required for close).
175  BLOCXX_ASSERT( (maxOpen > 2) && (maxOpen <= long(std::numeric_limits<int>::max())) );
176  return maxOpen;
177  }
178 
179  void init_child(char const * exec_path,
180  char const * const argv[], char const * const envp[],
181  Exec::PreExec & pre_exec, UnnamedPipe* ppipe[Exec::Impl::BLOCXX_NPIPE])
182  {
183  // This code must be careful not to allocate memory, as this can
184  // cause a deadlock on some platforms when there are multiple
185  // threads running at the time of the fork().
186 
187  int exec_err_desc = -1;
188  Exec::PreExec::Error err;
189  err.error_num = 0; // should be unnecessary, but just in case...
190  err.message[0] = '\0'; // should be unnecessary, but just in case...
191  try
192  {
193  int rc;
194  exec_err_desc = ppipe[Exec::Impl::BLOCXX_EXEC_ERR]->getOutputDescriptor();
195  pre_exec.call(ppipe);
196 
197  int rval = 0;
198  char * const * cc_argv = const_cast<char * const *>(argv);
199  char * const * cc_envp = const_cast<char * const *>(envp);
200  if (envp)
201  {
202  check(::execve(exec_path, cc_argv, cc_envp) != -1, "execve");
203  }
204  else
205  {
206  check(::execv(exec_path, cc_argv) != -1, "execv");
207  }
208  }
209  catch (Exec::PreExec::Error & e)
210  {
211  err = e;
212  }
213  catch (std::exception & e)
214  {
215  SafeCString::strcpy_trunc(err.message, e.what());
216  err.error_num = 0;
217  }
218  catch (Exec::PreExec::DontCatch & e)
219  {
220  throw;
221  }
222  catch (...)
223  {
224  SafeCString::strcpy_trunc(err.message, "unknown exception");
225  err.error_num = 0;
226  }
227  ssize_t rv = ::write(exec_err_desc, &err, sizeof(err));
228  ::_exit(127);
229  }
230 
231 } // end anonymous namespace
232 
233 namespace Exec
234 {
235 
236 using namespace Impl;
237 
239 // PreExec methods
240 //
242 {
243  /*
244  according to susv3:
245 
246  This volume of IEEE Std 1003.1-2001 specifies that signals set to
247  SIG_IGN remain set to SIG_IGN, and that the process signal mask be
248  unchanged across an exec. This is consistent with historical implemen-
249  tations, and it permits some useful functionality, such as the nohup
250  command. However, it should be noted that many existing applications
251  wrongly assume that they start with certain signals set to the default
252  action and/or unblocked. In particular, applications written with a
253  simpler signal model that does not include blocking of signals, such as
254  the one in the ISO C standard, may not behave properly if invoked with
255  some signals blocked. Therefore, it is best not to block or ignore sig-
256  nals across execs without explicit reason to do so, and especially not
257  to block signals across execs of arbitrary (not closely co-operating)
258  programs.
259 
260  so we'll reset the signal mask and all signal handlers to SIG_DFL.
261  We set them all just in case the current handlers may misbehave now
262  that we've fork()ed.
263  */
264  int rc;
265  ::sigset_t emptymask;
266  check(::sigemptyset(&emptymask) == 0, "sigemptyset");
267  check(::sigprocmask(SIG_SETMASK, &emptymask, 0) == 0, "sigprocmask");
268 
269  for (std::size_t sig = 1; sig <= NSIG; ++sig)
270  {
271  if (sig == SIGKILL || sig == SIGSTOP)
272  {
273  continue;
274  }
275  struct sigaction temp;
276  int e = ::sigaction(sig, 0, &temp);
277  check(e == 0 || errno == EINVAL, "sigaction [1]");
278  if (e == 0 && temp.sa_handler != SIG_DFL) // valid signal
279  {
280  temp.sa_handler = SIG_DFL;
281  // note that we don't check the return value because there are signals
282  // (e.g. SIGGFAULT on HP-UX), which are gettable, but not settable.
283  ::sigaction(sig, &temp, 0);
284  }
285  }
286 }
287 
288 void PreExec::closeDescriptorsOnExec(std::vector<bool> const & keep)
289 {
290  long numd = m_max_descriptors ? m_max_descriptors : getMaxOpenFiles();
291  for (int d = 3; d < int(numd); ++d) // Don't close standard descriptors
292  {
293  if (size_t(d) >= keep.size() || !keep[d])
294  {
295  close_on_exec(d, true);
296  }
297  }
298 }
299 
301 {
302  int nulld = 0;
303  if (!(ppipe[0] && ppipe[1] && ppipe[2]))
304  {
305  nulld = ::open(_PATH_DEVNULL, O_RDWR);
306  check(nulld >= 0, "open");
307  close_on_exec(nulld, false);
308  }
309  for (unsigned d = 0; d < 3; ++d)
310  {
311  PosixUnnamedPipe * p = dynamic_cast<PosixUnnamedPipe*>(ppipe[d]);
312  int ddup =
313  !p ? nulld : d==BLOCXX_IN ? p->getInputHandle() : p->getOutputHandle();
314  check(::dup2(ddup, d) != -1, "dup2");
315  }
316 }
317 
319 {
320  for (unsigned d = 0; d < BLOCXX_NPIPE; ++d)
321  {
322  UnnamedPipe* p = ppipe[d];
323  if (p)
324  {
325  close_on_exec(p->getInputDescriptor(), false);
326  close_on_exec(p->getOutputDescriptor(), false);
327  }
328  }
329 }
330 
332 {
333  int pgidrv = setpgid(0, 0);
334  BLOCXX_ASSERT(pgidrv == 0);
335 }
336 
337 PreExec::PreExec(bool precompute_max_descriptors)
338  : m_max_descriptors(precompute_max_descriptors ? getMaxOpenFiles() : 0)
339 {
340 }
341 
343 {
344 }
345 
347 {
348 }
349 
350 } // end Exec namespace
351 
352 namespace PosixExec
353 {
354 //----------- Standard PreExec -------------
355 //------------------------------------------
356 StandardPreExec::StandardPreExec() : PreExec(true)
357 {
358 }
359 
361 {
362  return true;
363 }
364 
366 {
367  std::vector<bool> empty;
368  PreExec::resetSignals();
369  PreExec::setNewProcessGroup();
370  PreExec::setupStandardDescriptors(pparr);
371  PreExec::closeDescriptorsOnExec(empty);
372 }
373 
374 //----------- System PreExec ---------------
375 //------------------------------------------
377 {
378 }
379 
380 bool SystemPreExec::keepStd(int d) const
381 {
382  return true; // want them all unchanged
383 }
384 
386 {
387  std::vector<bool> empty;
388  PreExec::resetSignals();
389  PreExec::setNewProcessGroup();
390  PreExec::closeDescriptorsOnExec(empty);
391 }
392 
393 ProcessRef spawnImpl(char const * exec_path, char const * const argv[], char const * const envp[],
394  Exec::PreExec & pre_exec)
395 {
396  // It's important that this code be exception-safe (proper release
397  // of resources when exception thrown), as at least one caller
398  // (the monitor code) relies on being able to throw a DontCatch-derived
399  // exception from pre_exec.call() in the child process and have
400  // it propagate out of the spawn call.
401  //
402  parent_check(exec_path, "Exec::spawn: null exec_path");
403  char const * default_argv[2] = { exec_path, 0 };
404  if (!argv || !*argv)
405  {
406  argv = default_argv;
407  }
408 
409  // Check this here so that any exceptions or core files caused by it can
410  // be traced to a real problem instead of the child processes just
411  // failing for an unreportable reason.
412  getMaxOpenFiles();
413 
415  UnnamedPipe* ppipe[Exec::BLOCXX_NPIPE] = {0};
416 
417  for (unsigned i = 0; i < Exec::BLOCXX_NPIPE; ++i)
418  {
419  if (i == Exec::BLOCXX_EXEC_ERR || pre_exec.keepStd(i))
420  {
422  ppipe[i] = upipe[i].getPtr();
423  }
424  }
425 
426 
427  ::pid_t child_pid = ::fork();
428  if (child_pid == 0) // child process
429  {
430  init_child(exec_path, argv, envp, pre_exec, ppipe); // never returns
431  }
432 
433  parent_check(child_pid >= 0, Format("Exec::spawn(%1): fork() failed", exec_path).c_str());
434 
435  Exec::close_child_ends(upipe);
436 
437  // 10 seconds should be plenty for the child to go from fork() to execv()
438  const Timeout SECONDS_TO_WAIT_FOR_CHILD_TO_EXEC = Timeout::relative(10);
439  upipe[Exec::BLOCXX_EXEC_ERR]->setReadTimeout(SECONDS_TO_WAIT_FOR_CHILD_TO_EXEC);
440 
441  ProcessRef retval(new Process(upipe[0], upipe[1], upipe[2], child_pid));
442 
443 
444  Exec::PreExec::Error child_error;
445  int nread = upipe[Exec::BLOCXX_EXEC_ERR]->read(&child_error, sizeof(child_error));
446  handle_child_error(nread, child_error, *retval, exec_path);
447 
448  return retval;
449 }
450 
451 } // end PosixExec namespace
452 
453 } // end BLOCXX_NAMESPACE
454 
455 #endif