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
37
38#include "blocxx/BLOCXX_config.h"
39
40#if !defined(BLOCXX_WIN32)
41
42#include "blocxx/PosixExec.hpp"
43#include "blocxx/Format.hpp"
45#include "blocxx/Assertion.hpp"
47#include "blocxx/Paths.hpp"
49#include "blocxx/Select.hpp"
50
51extern "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
88namespace BLOCXX_NAMESPACE
89{
90
91namespace // 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
233namespace Exec
234{
235
236using 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
288void 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
337PreExec::PreExec(bool precompute_max_descriptors)
338 : m_max_descriptors(precompute_max_descriptors ? getMaxOpenFiles() : 0)
339{
340}
341
345
349
350} // end Exec namespace
351
352namespace PosixExec
353{
354//----------- Standard PreExec -------------
355//------------------------------------------
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//------------------------------------------
379
380bool 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
393ProcessRef 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
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
#define BLOCXX_ASSERT(CON)
BLOCXX_ASSERT works similar to the assert() macro, but instead of calling abort(),...
Definition Assertion.hpp:57
#define BLOCXX_THROW(exType, msg)
Throw an exception using FILE and LINE.
#define BLOCXX_THROW_ERRNO_MSG1(exType, msg, errnum)
Throw an exception using FILE, LINE, errnum and strerror(errnum)
#define _PATH_DEVNULL
Definition Paths.hpp:71
#define NSIG
Definition PosixExec.cpp:72
#define ETIMEDOUT
This class is used to specify what spawn() should do between fork and exec.
Definition Exec.hpp:106
::BLOCXX_NAMESPACE::UnnamedPipe * pipe_pointer_t
Definition Exec.hpp:108
static void setupStandardDescriptors(pipe_pointer_t const pparr[])
For calling from PreExec::call.
static void resetSignals()
For calling from PreExec::call.
void closeDescriptorsOnExec(std::vector< bool > const &keep)
For calling from PreExec::call.
PreExec(bool precompute_max_descriptors=false)
static void setNewProcessGroup()
For calling from PreExec::call().
static void closePipesOnExec(pipe_pointer_t const pparr[])
For calling from PreExec::call.
virtual bool keepStd(int d) const =0
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.
virtual void call(pipe_pointer_t const pparr[])
Resets all signals to their default actions and sets to close-on-exec all descriptors except the stan...
virtual void call(pipe_pointer_t const pparr[])
Resets all signals to their default actions and sets to close-on-exec all descriptors except the stan...
Class for communicating with and managing a child process.
Definition Process.hpp:62
A timeout can be absolute, which means that it will happen at the specified DateTime.
Definition Timeout.hpp:56
static Timeout relative(float seconds)
Definition Timeout.cpp:58
Abstract interface for an UnnamedPipe.
virtual Descriptor getInputDescriptor() const =0
Get the underlying input descriptor.
void setReadTimeout(const Timeout &timeout)
Sets the read timeout value.
static UnnamedPipeRef createUnnamedPipe(EOpen doOpen=E_OPEN)
Create an instance of the concrete class that implements the UnnamedPipe interface.
virtual Descriptor getOutputDescriptor() const =0
Get the underlying output descriptor.
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
ProcessRef spawnImpl(char const *exec_path, char const *const argv[], char const *const envp[], Exec::PreExec &pre_exec)
char * strcpy_trunc(char *dst, std::size_t dstsize, char const *src)
PROMISE: copies the first n = min(strlen(src), dstsize - 1) characters of C-string src to dst,...
Taken from RFC 1321.
IntrusiveReference< UnnamedPipe > UnnamedPipeRef
IntrusiveReference< Process > ProcessRef