/// This finalizer is called automatically as the auditor library /// is being unmapped, but only if interception is on (aka active) /// and only on "normal" exit as described in Interposer/exit.h. /*static*/ void interposed_finalize(void) { if (_auditor_isActiveByRequest()) { pid_t pid; // This is a really cute trick I got from a guy named Nate Eldredge. // The exit status is not given to the library finalizer but we // need it, so what can we do? This is the answer: do a meaningless // fork. The child just returns so that the parent can wait for // it and gets its exit status. There's a slight chance that // the extra fork will cause non-beneficial side effects but // that should be quite rare. // IDEA Would it be better to use vfork() here? A big IDE // like Eclipse might have a large memory footprint and forking // it only to exit might be costly and/or have an OOM failure. // Answer, no, vfork is not allowed to return. There could be // an answer within posix_spawn but I haven't looked into it. if ((pid = fork_real()) == -1) { putil_syserr(0, "fork"); } else if (pid == 0) { // The child just returns so the parent can wait for it. } else { int wstat, status = 7; if (waitpid(pid, &wstat, 0) == pid) { if (WIFEXITED(wstat)) { status = WEXITSTATUS(wstat); } } else { putil_syserr(0, "waitpid"); } _audit_end("exit", EXITING, status); // Do an explicit _exit( to abort any remaining library // finalizers since they will have run in the child. I.e. // any *previous* finalizers will have run in the // original (now parent) copy and any *subsequent* // ones will have run in the child, so continuing with // exit processing here would run the latter set twice. _exit_real(status); } } else if (_auditor_isActiveByDefault()) { _audit_end("exit", EXITING, 0); } /* * The cleanups below would be done by exit anyway but we try to * free things explicitly so that leak detectors such as valgrind * can more easily pinpoint real leaks. Perhaps we could have an * optimized "production mode" which skips the explicit cleanup. */ code_fini(); prop_fini(); vb_fini(); }
/// Interposes over the vfork() function. /// It's nearly impossible to wrap vfork() since the docs say: /// "The procedure that called vfork() should not /// return while running in the child's context ...". Any /// wrapper function would have to return to reach the exec(). /// So we convert vfork to fork. There may be a few corner /// cases where this affects behavior but generally use of /// vfork is an anachronism, and SUS does contain verbiage to /// the effect that vfork may be equivalent to fork. /// Better ideas solicited. In particular it appears that /// we could hack in some assembler code to play with the /// stack. See a response by Jonathan Adams in comp.unix.solaris /// from 2004-09-17, subject 'how to "wrap" vfork?' /// @return same as the wrapped function /*static*/ pid_t vfork(void) { const char *call = "vfork"; interposer_late_init(call); WRAPPER_DEBUG("ENTERING vfork_wrapper() => %p\n", fork_real); if (interposer_get_active()) { return fork_wrapper(call, fork_real); } else { return fork_real(); } }
/* * hildon-desktop is typically running with higher priority and * runs protected against out-of-memory killing from the kernel. * Spawned children however should not be given these priviledges. */ pid_t fork (void) { static pid_t (*fork_real) () = NULL; pid_t pid; if (fork_real == NULL) fork_real = dlsym (RTLD_NEXT, "fork"); pid = fork_real (); if (pid == 0) { pid_t child_pid; int priority; int fd; errno = 0; child_pid = getpid (); /* If the child process inherited desktop's high priority, * give child default priority */ priority = getpriority (PRIO_PROCESS, child_pid); if (!errno && priority < 0) { setpriority (PRIO_PROCESS, child_pid, 0); } /* Unprotect from OOM */ fd = g_open ("/proc/self/oom_adj", O_WRONLY, 0); if (fd >= 0) { write (fd, "0", sizeof (char)); close (fd); } } return pid; }
pid_t fork(void) { static int loaded=0; static pid_t (*fork_real)(void); pid_t rv; if(!loaded) { fork_real=0; fork_real = (pid_t (*)(void)) dlsym(RTLD_NEXT, "fork"); if(!dlerror()) loaded=1; else { IPMERR("Intercepting fork(): load failed\n"); } } #if defined(HAVE_POSIXIO_TRACE) || defined(HAVE_MPI_TRACE) if( task.tracefile ) { fprintf(task.tracefile, "Before fork(), pid=%d\n", getpid()); } #endif rv = fork_real(); if(!rv) { ipm_init(0); #if defined(HAVE_POSIXIO_TRACE) || defined(HAVE_MPI_TRACE) task.tracefile=stderr; #endif } #if defined(HAVE_POSIXIO_TRACE) || defined(HAVE_MPI_TRACE) if( task.tracefile ) { fprintf(task.tracefile, "After fork(), pid=%d\n", getpid()); } #endif return rv; }