int pthread_sigmask (int how, const sigset_t *newmask, sigset_t *oldmask) { sigset_t local_newmask; /* The only thing we have to make sure here is that SIGCANCEL and SIGSETXID is not blocked. */ if (newmask != NULL && (__builtin_expect (__sigismember (newmask, SIGCANCEL), 0) || __builtin_expect (__sigismember (newmask, SIGSETXID), 0))) { local_newmask = *newmask; __sigdelset (&local_newmask, SIGCANCEL); __sigdelset (&local_newmask, SIGSETXID); newmask = &local_newmask; } #ifdef INTERNAL_SYSCALL /* We know that realtime signals are available if NPTL is used. */ INTERNAL_SYSCALL_DECL (err); int result = INTERNAL_SYSCALL (rt_sigprocmask, err, 4, how, newmask, oldmask, _NSIG / 8); return (INTERNAL_SYSCALL_ERROR_P (result, err) ? INTERNAL_SYSCALL_ERRNO (result, err) : 0); #else return sigprocmask (how, newmask, oldmask) == -1 ? errno : 0; #endif }
int sigprocmask(int how, const sigset_t * set, sigset_t * oldset) { #ifdef SIGCANCEL sigset_t local_newmask; /* * The only thing we have to make sure here is that SIGCANCEL and * SIGSETXID are not blocked. */ if (set != NULL && (__builtin_expect (__sigismember (set, SIGCANCEL), 0) # ifdef SIGSETXID || __builtin_expect (__sigismember (set, SIGSETXID), 0) # endif )) { local_newmask = *set; __sigdelset (&local_newmask, SIGCANCEL); # ifdef SIGSETXID __sigdelset (&local_newmask, SIGSETXID); # endif set = &local_newmask; } #endif return (__syscall_sigprocmask(how, set, oldset)); }
/* Return any pending signal or wait for one for the given time. */ static int do_sigwait (const sigset_t *set, int *sig) { int ret; #ifdef SIGCANCEL sigset_t tmpset; if (set != NULL && (__builtin_expect (__sigismember (set, SIGCANCEL), 0) # ifdef SIGSETXID || __builtin_expect (__sigismember (set, SIGSETXID), 0) # endif )) { /* Create a temporary mask without the bit for SIGCANCEL set. */ // We are not copying more than we have to. memcpy (&tmpset, set, _NSIG / 8); __sigdelset (&tmpset, SIGCANCEL); # ifdef SIGSETXID __sigdelset (&tmpset, SIGSETXID); # endif set = &tmpset; } #endif /* XXX The size argument hopefully will have to be changed to the real size of the user-level sigset_t. */ #ifdef INTERNAL_SYSCALL INTERNAL_SYSCALL_DECL (err); do ret = INTERNAL_SYSCALL (rt_sigtimedwait, err, 4, set, NULL, NULL, _NSIG / 8); while (INTERNAL_SYSCALL_ERROR_P (ret, err) && INTERNAL_SYSCALL_ERRNO (ret, err) == EINTR); if (! INTERNAL_SYSCALL_ERROR_P (ret, err)) { *sig = ret; ret = 0; } else ret = INTERNAL_SYSCALL_ERRNO (ret, err); #else do ret = INLINE_SYSCALL (rt_sigtimedwait, 4, set, NULL, NULL, _NSIG / 8); while (ret == -1 && errno == EINTR); if (ret != -1) { *sig = ret; ret = 0; } else ret = errno; #endif return ret; }
/* Clear a pending signal and return the associated detailed signal information. SS must be locked, and must have signal SIGNO pending, either directly or through the global sigstate. */ static struct hurd_signal_detail sigstate_clear_pending (struct hurd_sigstate *ss, int signo) { if (sigstate_is_global_rcv (ss) && __sigismember (&_hurd_global_sigstate->pending, signo)) { __sigdelset (&_hurd_global_sigstate->pending, signo); return _hurd_global_sigstate->pending_data[signo]; } assert (__sigismember (&ss->pending, signo)); __sigdelset (&ss->pending, signo); return ss->pending_data[signo]; }
static int __NC(sigwait)(const sigset_t *set, int *sig) { sigset_t tmp_mask; struct sigaction saved[NSIG]; struct sigaction action; int save_errno; int this; /* Prepare set. */ __sigfillset (&tmp_mask); /* Unblock all signals in the SET and register our nice handler. */ action.sa_handler = ignore_signal; action.sa_flags = 0; __sigfillset (&action.sa_mask); /* Block all signals for handler. */ /* Make sure we recognize error conditions by setting WAS_SIG to a value which does not describe a legal signal number. */ was_sig = -1; for (this = 1; this < NSIG; ++this) if (__sigismember (set, this)) { /* Unblock this signal. */ __sigdelset (&tmp_mask, this); /* Register temporary action handler. */ /* In Linux (as of 2.6.25), fails only if sig is SIGKILL or SIGSTOP */ /* (so, will it work correctly if set has, say, SIGSTOP?) */ if (sigaction (this, &action, &saved[this]) != 0) goto restore_handler; } /* Now we can wait for signals. */ __NC(sigsuspend)(&tmp_mask); restore_handler: save_errno = errno; while (--this >= 1) if (__sigismember (set, this)) /* We ignore errors here since we must restore all handlers. */ sigaction (this, &saved[this], NULL); __set_errno (save_errno); /* Store the result and return. */ *sig = was_sig; return was_sig == -1 ? -1 : 0; }
void __handle_signal (shim_tcb_t * tcb, int sig, ucontext_t * uc) { struct shim_thread * thread = (struct shim_thread *) tcb->tp; int begin_sig = 1, end_sig = NUM_KNOWN_SIGS; if (sig) end_sig = (begin_sig = sig) + 1; sig = begin_sig; if (!thread->has_signal.counter) return; while (atomic_read(&thread->has_signal)) { struct shim_signal * signal = NULL; for ( ; sig < end_sig ; sig++) if (!__sigismember(&thread->signal_mask, sig) && (signal = fetch_signal_log(tcb, thread, sig))) break; if (!signal) break; if (!signal->context_stored) __store_context(tcb, NULL, signal); __handle_one_signal(tcb, sig, signal); free(signal); DkThreadYieldExecution(); } tcb->context.preempt &= ~SIGNAL_DELAYED; }
error_t _hurd_ctty_output (io_t port, io_t ctty, error_t (*rpc) (io_t)) { if (ctty == MACH_PORT_NULL) return (*rpc) (port); else { struct hurd_sigstate *ss = _hurd_self_sigstate (); error_t err; do { /* Don't use the ctty io port if we are blocking or ignoring SIGTTOU. We redo this check at the top of the loop in case the signal handler changed the state. */ __spin_lock (&ss->lock); if (__sigismember (&ss->blocked, SIGTTOU) || ss->actions[SIGTTOU].sa_handler == SIG_IGN) err = EIO; else err = 0; __spin_unlock (&ss->lock); if (err) return (*rpc) (port); err = (*rpc) (ctty); if (err == EBACKGROUND) { if (_hurd_orphaned) /* Our process group is orphaned, so we never generate a signal; we just fail. */ err = EIO; else { /* Send a SIGTTOU signal to our process group. We must remember here not to clobber ERR, since the loop condition below uses it to recall that we should retry after a stop. */ __USEPORT (CTTYID, _hurd_sig_post (0, SIGTTOU, port)); /* XXX what to do if error here? */ /* At this point we should have just run the handler for SIGTTOU or resumed after being stopped. Now this is still a "system call", so check to see if we should restart it. */ __spin_lock (&ss->lock); if (!(ss->actions[SIGTTOU].sa_flags & SA_RESTART)) err = EINTR; __spin_unlock (&ss->lock); } } /* If the last RPC generated a SIGTTOU, loop to try it again. */ } while (err == EBACKGROUND); return err; } }
int sigismember(__const sigset_t *__set, int __signo) { if (__signo == 0 || __signo >= _NSIG) { errno = EINVAL; return -1; } __sigismember(__set, __signo); return 0; }
/* Return 1 if SIGNO is in SET, 0 if not. */ int sigismember (const sigset_t *set, int signo) { if (set == NULL || signo <= 0 || signo >= NSIG) { __set_errno (EINVAL); return -1; } return __sigismember (set, signo); }
error_t _hurd_ctty_input (io_t port, io_t ctty, error_t (*rpc) (io_t)) { error_t err; if (ctty == MACH_PORT_NULL) return (*rpc) (port); do { err = (*rpc) (ctty); if (err == EBACKGROUND) { /* We are a background job and tried to read from the tty. We should probably get a SIGTTIN signal. */ if (_hurd_orphaned) /* Our process group is orphaned. Don't stop; just fail. */ err = EIO; else { struct hurd_sigstate *ss = _hurd_self_sigstate (); __spin_lock (&ss->lock); if (__sigismember (&ss->blocked, SIGTTIN) || ss->actions[SIGTTIN].sa_handler == SIG_IGN) /* We are blocking or ignoring SIGTTIN. Just fail. */ err = EIO; __spin_unlock (&ss->lock); if (err == EBACKGROUND) { /* Send a SIGTTIN signal to our process group. We must remember here not to clobber ERR, since the loop condition below uses it to recall that we should retry after a stop. */ __USEPORT (CTTYID, _hurd_sig_post (0, SIGTTIN, port)); /* XXX what to do if error here? */ /* At this point we should have just run the handler for SIGTTIN or resumed after being stopped. Now this is still a "system call", so check to see if we should restart it. */ __spin_lock (&ss->lock); if (!(ss->actions[SIGTTIN].sa_flags & SA_RESTART)) err = EINTR; __spin_unlock (&ss->lock); } } } /* If the last RPC generated a SIGTTIN, loop to try it again. */ } while (err == EBACKGROUND); return err; }
/* Run through all pending sighandlers and trigger them with a NULL info field. * These handlers are triggered as the result of a pthread_kill(), and thus * don't require individual 'info' structs. */ static void __run_pending_sighandlers() { struct pthread_tcb *me = pthread_self(); sigset_t andset = me->sigpending & (~me->sigmask); for (int i = 1; i < _NSIG; i++) { if (__sigismember(&andset, i)) { __sigdelset(&me->sigpending, i); trigger_posix_signal(i, NULL, &me->sigdata->u_ctx); } } uthread_yield(FALSE, __exit_sighandler_cb, 0); }
static int do_sigtimedwait (const sigset_t *set, siginfo_t *info, const struct timespec *timeout) { #ifdef SIGCANCEL sigset_t tmpset; if (set != NULL && (__builtin_expect (__sigismember (set, SIGCANCEL), 0) # ifdef SIGSETXID || __builtin_expect (__sigismember (set, SIGSETXID), 0) # endif )) { /* Create a temporary mask without the bit for SIGCANCEL set. */ // We are not copying more than we have to. memcpy (&tmpset, set, _NSIG / 8); __sigdelset (&tmpset, SIGCANCEL); # ifdef SIGSETXID __sigdelset (&tmpset, SIGSETXID); # endif set = &tmpset; } #endif /* XXX The size argument hopefully will have to be changed to the real size of the user-level sigset_t. */ int result = INLINE_SYSCALL (rt_sigtimedwait, 4, set, info, timeout, _NSIG / 8); /* The kernel generates a SI_TKILL code in si_code in case tkill is used. tkill is transparently used in raise(). Since having SI_TKILL as a code is useful in general we fold the results here. */ if (result != -1 && info != NULL && info->si_code == SI_TKILL) info->si_code = SI_USER; return result; }
static kern_return_t set_int (int which, int value) { switch (which) { case INIT_UMASK: _hurd_umask = value; return 0; /* These are pretty odd things to do. But you asked for it. */ case INIT_SIGMASK: { struct hurd_sigstate *ss = _hurd_thread_sigstate (_hurd_sigthread); __spin_lock (&ss->lock); ss->blocked = value; __spin_unlock (&ss->lock); return 0; } case INIT_SIGPENDING: { struct hurd_sigstate *ss = _hurd_thread_sigstate (_hurd_sigthread); __spin_lock (&ss->lock); ss->pending = value; __spin_unlock (&ss->lock); return 0; } case INIT_SIGIGN: { struct hurd_sigstate *ss = _hurd_thread_sigstate (_hurd_sigthread); int sig; const sigset_t ign = value; __spin_lock (&ss->lock); for (sig = 1; sig < NSIG; ++sig) { if (__sigismember (&ign, sig)) ss->actions[sig].sa_handler = SIG_IGN; else if (ss->actions[sig].sa_handler == SIG_IGN) ss->actions[sig].sa_handler = SIG_DFL; } __spin_unlock (&ss->lock); return 0; case INIT_TRACEMASK: _hurdsig_traced = value; return 0; } default: return EINVAL; } }
/* If the given signal is unmasked, prep the pthread to run it's signal * handler, but don't run it yet. In either case, make the pthread runnable * again. Once the signal handler is complete, the original context will be * restored and restarted. */ static void __pthread_signal_and_restart(struct pthread_tcb *pthread, int signo, int code, void *addr) { if (!__sigismember(&pthread->sigmask, signo)) { if (pthread->sigdata) { printf("Pthread sighandler faulted, signal: %d\n", signo); /* uthread.c already copied out the faulting ctx into the uth */ print_user_context(&pthread->uthread.u_ctx); exit(-1); } struct siginfo info = {0}; info.si_signo = signo; info.si_code = code; info.si_addr = addr; __pthread_prep_sighandler(pthread, __run_sighandler, &info); } pth_thread_runnable(&pthread->uthread); }
void deliver_signal (siginfo_t * info, PAL_CONTEXT * context) { shim_tcb_t * tcb = SHIM_GET_TLS(); struct shim_thread * cur_thread = (struct shim_thread *) tcb->tp; int sig = info->si_signo; __disable_preempt(tcb); struct shim_signal * signal = __alloca(sizeof(struct shim_signal)); /* save in signal */ memset(signal, 0, sizeof(struct shim_signal)); __store_info(info, signal); __store_context(tcb, context, signal); if ((tcb->context.preempt & ~SIGNAL_DELAYED) > 1) goto delay; if (__sigismember(&cur_thread->signal_mask, sig)) goto delay; __handle_signal(tcb, sig, &signal->context); __handle_one_signal(tcb, sig, signal); goto out; delay: { if (!(signal = remalloc(signal,sizeof(struct shim_signal)))) goto out; struct shim_signal ** signal_log = allocate_signal_log(cur_thread, sig); if (!signal_log) { sys_printf("signal queue is full (TID = %u, SIG = %d)\n", tcb->tid, sig); free(signal); goto out; } *signal_log = signal; } out: __enable_preempt(tcb); }
/* Select any of pending signals from SET or wait for any to arrive. */ int __sigwait (const sigset_t *set, int *sig) { struct hurd_sigstate *ss; sigset_t mask, ready; int signo = 0; struct hurd_signal_preemptor preemptor; jmp_buf buf; mach_port_t wait; mach_msg_header_t msg; sighandler_t preempt_fun (struct hurd_signal_preemptor *pe, struct hurd_sigstate *ss, int *sigp, struct hurd_signal_detail *detail) { if (signo) /* We've already been run; don't interfere. */ return SIG_ERR; signo = *sigp; /* Make sure this is all kosher */ assert (__sigismember (&mask, signo)); /* Make sure this signal is unblocked */ __sigdelset (&ss->blocked, signo); return pe->handler; } void handler (int sig) { assert (sig == signo); longjmp (buf, 1); }
/* This is a quick and dirty, but not 100% compliant with * the stupid SysV SIGCHLD vs. SIG_IGN behaviour. It is * fine unless you are messing with SIGCHLD... */ unsigned int sleep (unsigned int sec) { unsigned int res; struct timespec ts = { .tv_sec = (long int) seconds, .tv_nsec = 0 }; res = nanosleep(&ts, &ts); if (res) res = (unsigned int) ts.tv_sec + (ts.tv_nsec >= 500000000L); return res; } # else /* We are going to use the `nanosleep' syscall of the kernel. But the kernel does not implement the sstupid SysV SIGCHLD vs. SIG_IGN behaviour for this syscall. Therefore we have to emulate it here. */ unsigned int sleep (unsigned int seconds) { struct timespec ts = { .tv_sec = (long int) seconds, .tv_nsec = 0 }; sigset_t set; struct sigaction oact; unsigned int result; /* This is not necessary but some buggy programs depend on this. */ if (seconds == 0) { # ifdef CANCELLATION_P int cancelhandling; CANCELLATION_P (THREAD_SELF); # endif return 0; } /* Linux will wake up the system call, nanosleep, when SIGCHLD arrives even if SIGCHLD is ignored. We have to deal with it in libc. */ __sigemptyset (&set); __sigaddset (&set, SIGCHLD); /* Is SIGCHLD set to SIG_IGN? */ sigaction (SIGCHLD, NULL, &oact); /* never fails */ if (oact.sa_handler == SIG_IGN) { /* Yes. Block SIGCHLD, save old mask. */ sigprocmask (SIG_BLOCK, &set, &set); /* never fails */ } /* Run nanosleep, with SIGCHLD blocked if SIGCHLD is SIG_IGNed. */ result = nanosleep (&ts, &ts); if (result != 0) { /* Got EINTR. Return remaining time. */ result = (unsigned int) ts.tv_sec + (ts.tv_nsec >= 500000000L); } if (!__sigismember (&set, SIGCHLD)) { /* We did block SIGCHLD, and old mask had no SIGCHLD bit. IOW: we need to unblock SIGCHLD now. Do it. */ /* this sigprocmask call never fails, thus never updates errno, and therefore we don't need to save/restore it. */ sigprocmask (SIG_SETMASK, &set, NULL); /* never fails */ } return result; } # endif #else /* __UCLIBC_HAS_REALTIME__ */ /* no nanosleep, use signals and alarm() */ static void sleep_alarm_handler(int attribute_unused sig) { } unsigned int sleep (unsigned int seconds) { struct sigaction act, oact; sigset_t set, oset; unsigned int result, remaining; time_t before, after; int old_errno = errno; /* This is not necessary but some buggy programs depend on this. */ if (seconds == 0) return 0; /* block SIGALRM */ __sigemptyset (&set); __sigaddset (&set, SIGALRM); sigprocmask (SIG_BLOCK, &set, &oset); /* can't fail */ act.sa_handler = sleep_alarm_handler; act.sa_flags = 0; act.sa_mask = oset; sigaction(SIGALRM, &act, &oact); /* never fails */ before = time(NULL); remaining = alarm(seconds); if (remaining && remaining > seconds) { /* restore user's alarm */ sigaction(SIGALRM, &oact, NULL); alarm(remaining); /* restore old alarm */ sigsuspend(&oset); after = time(NULL); } else { sigsuspend (&oset); after = time(NULL); sigaction (SIGALRM, &oact, NULL); } result = after - before; alarm(remaining > result ? remaining - result : 0); sigprocmask (SIG_SETMASK, &oset, NULL); __set_errno(old_errno); return result > seconds ? 0 : seconds - result; } #endif /* __UCLIBC_HAS_REALTIME__ */ libc_hidden_def(sleep)
/* Set the disposition for SIG. */ __sighandler_t sigset (int sig, __sighandler_t disp) { struct sigaction act; struct sigaction oact; sigset_t set; sigset_t oset; #ifdef SIG_HOLD /* Handle SIG_HOLD first. */ if (disp == SIG_HOLD) { /* Create an empty signal set. */ if (__sigemptyset (&set) < 0) return SIG_ERR; /* Add the specified signal. */ if (__sigaddset (&set, sig) < 0) return SIG_ERR; /* Add the signal set to the current signal mask. */ if (__sigprocmask (SIG_BLOCK, &set, &oset) < 0) return SIG_ERR; /* If the signal was already blocked signal this to the caller. */ if (__sigismember (&oset, sig)) return SIG_HOLD; /* We need to determine whether a specific handler is installed. */ if (__sigaction (sig, NULL, &oact) < 0) return SIG_ERR; return oact.sa_handler; } #endif /* SIG_HOLD */ /* Check signal extents to protect __sigismember. */ if (disp == SIG_ERR || sig < 1 || sig >= NSIG) { __set_errno (EINVAL); return SIG_ERR; } act.sa_handler = disp; if (__sigemptyset (&act.sa_mask) < 0) return SIG_ERR; act.sa_flags = 0; if (__sigaction (sig, &act, &oact) < 0) return SIG_ERR; /* Create an empty signal set. */ if (__sigemptyset (&set) < 0) return SIG_ERR; /* Add the specified signal. */ if (__sigaddset (&set, sig) < 0) return SIG_ERR; /* Remove the signal set from the current signal mask. */ if (__sigprocmask (SIG_UNBLOCK, &set, &oset) < 0) return SIG_ERR; /* If the signal was already blocked return SIG_HOLD. */ return __sigismember (&oset, sig) ? SIG_HOLD : oact.sa_handler; }
/* Spawn a new process executing PATH with the attributes describes in *ATTRP. Before running the process perform the actions described in FILE-ACTIONS. */ int __spawni (pid_t *pid, const char *file, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], int xflags) { pid_t new_pid; char *path, *p, *name; size_t len; size_t pathlen; /* Do this once. */ short int flags = attrp == NULL ? 0 : attrp->__flags; /* Generate the new process. */ if ((flags & POSIX_SPAWN_USEVFORK) != 0 /* If no major work is done, allow using vfork. Note that we might perform the path searching. But this would be done by a call to execvp(), too, and such a call must be OK according to POSIX. */ || ((flags & (POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_RESETIDS)) == 0 && file_actions == NULL)) new_pid = __vfork (); else new_pid = __fork (); if (new_pid != 0) { if (new_pid < 0) return errno; /* The call was successful. Store the PID if necessary. */ if (pid != NULL) *pid = new_pid; return 0; } /* Set signal mask. */ if ((flags & POSIX_SPAWN_SETSIGMASK) != 0 && __sigprocmask (SIG_SETMASK, &attrp->__ss, NULL) != 0) _exit (SPAWN_ERROR); /* Set signal default action. */ if ((flags & POSIX_SPAWN_SETSIGDEF) != 0) { /* We have to iterate over all signals. This could possibly be done better but it requires system specific solutions since the sigset_t data type can be very different on different architectures. */ int sig; struct sigaction sa; memset (&sa, '\0', sizeof (sa)); sa.sa_handler = SIG_DFL; for (sig = 1; sig <= _NSIG; ++sig) if (__sigismember (&attrp->__sd, sig) != 0 && __sigaction (sig, &sa, NULL) != 0) _exit (SPAWN_ERROR); } #ifdef _POSIX_PRIORITY_SCHEDULING /* Set the scheduling algorithm and parameters. */ if ((flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER)) == POSIX_SPAWN_SETSCHEDPARAM) { if (__sched_setparam (0, &attrp->__sp) == -1) _exit (SPAWN_ERROR); } else if ((flags & POSIX_SPAWN_SETSCHEDULER) != 0) { if (__sched_setscheduler (0, attrp->__policy, &attrp->__sp) == -1) _exit (SPAWN_ERROR); } #endif /* Set the process group ID. */ if ((flags & POSIX_SPAWN_SETPGROUP) != 0 && __setpgid (0, attrp->__pgrp) != 0) _exit (SPAWN_ERROR); /* Set the effective user and group IDs. */ if ((flags & POSIX_SPAWN_RESETIDS) != 0 && (local_seteuid (__getuid ()) != 0 || local_setegid (__getgid ()) != 0)) _exit (SPAWN_ERROR); /* Execute the file actions. */ if (file_actions != NULL) { int cnt; struct rlimit64 fdlimit; bool have_fdlimit = false; for (cnt = 0; cnt < file_actions->__used; ++cnt) { struct __spawn_action *action = &file_actions->__actions[cnt]; switch (action->tag) { case spawn_do_close: if (close_not_cancel (action->action.close_action.fd) != 0) { if (! have_fdlimit) { getrlimit64 (RLIMIT_NOFILE, &fdlimit); have_fdlimit = true; } /* Only signal errors for file descriptors out of range. */ if (action->action.close_action.fd < 0 || action->action.close_action.fd >= fdlimit.rlim_cur) /* Signal the error. */ _exit (SPAWN_ERROR); } break; case spawn_do_open: { int new_fd = open_not_cancel (action->action.open_action.path, action->action.open_action.oflag | O_LARGEFILE, action->action.open_action.mode); if (new_fd == -1) /* The `open' call failed. */ _exit (SPAWN_ERROR); /* Make sure the desired file descriptor is used. */ if (new_fd != action->action.open_action.fd) { if (__dup2 (new_fd, action->action.open_action.fd) != action->action.open_action.fd) /* The `dup2' call failed. */ _exit (SPAWN_ERROR); if (close_not_cancel (new_fd) != 0) /* The `close' call failed. */ _exit (SPAWN_ERROR); } } break; case spawn_do_dup2: if (__dup2 (action->action.dup2_action.fd, action->action.dup2_action.newfd) != action->action.dup2_action.newfd) /* The `dup2' call failed. */ _exit (SPAWN_ERROR); break; } } } if ((xflags & SPAWN_XFLAGS_USE_PATH) == 0 || strchr (file, '/') != NULL) { /* The FILE parameter is actually a path. */ __execve (file, argv, envp); maybe_script_execute (file, argv, envp, xflags); /* Oh, oh. `execve' returns. This is bad. */ _exit (SPAWN_ERROR); } /* We have to search for FILE on the path. */ path = getenv ("PATH"); if (path == NULL) { /* There is no `PATH' in the environment. The default search path is the current directory followed by the path `confstr' returns for `_CS_PATH'. */ len = confstr (_CS_PATH, (char *) NULL, 0); path = (char *) __alloca (1 + len); path[0] = ':'; (void) confstr (_CS_PATH, path + 1, len); } len = strlen (file) + 1; pathlen = strlen (path); name = __alloca (pathlen + len + 1); /* Copy the file name at the top. */ name = (char *) memcpy (name + pathlen + 1, file, len); /* And add the slash. */ *--name = '/'; p = path; do { char *startp; path = p; p = __strchrnul (path, ':'); if (p == path) /* Two adjacent colons, or a colon at the beginning or the end of `PATH' means to search the current directory. */ startp = name + 1; else startp = (char *) memcpy (name - (p - path), path, p - path); /* Try to execute this name. If it works, execv will not return. */ __execve (startp, argv, envp); maybe_script_execute (startp, argv, envp, xflags); switch (errno) { case EACCES: case ENOENT: case ESTALE: case ENOTDIR: /* Those errors indicate the file is missing or not executable by us, in which case we want to just try the next path directory. */ break; default: /* Some other error means we found an executable file, but something went wrong executing it; return the error to our caller. */ _exit (SPAWN_ERROR); } } while (*p++ != '\0'); /* Return with an error. */ _exit (SPAWN_ERROR); }
/* We are going to use the `nanosleep' syscall of the kernel. But the kernel does not implement the stupid SysV SIGCHLD vs. SIG_IGN behaviour for this syscall. Therefore we have to emulate it here. */ unsigned int __sleep (unsigned int seconds) { const unsigned int max = (unsigned int) (((unsigned long int) (~((time_t) 0))) >> 1); struct timespec ts; sigset_t set, oset; unsigned int result; /* This is not necessary but some buggy programs depend on this. */ if (__glibc_unlikely (seconds == 0)) { #ifdef CANCELLATION_P CANCELLATION_P (THREAD_SELF); #endif return 0; } ts.tv_sec = 0; ts.tv_nsec = 0; again: if (sizeof (ts.tv_sec) <= sizeof (seconds)) { /* Since SECONDS is unsigned assigning the value to .tv_sec can overflow it. In this case we have to wait in steps. */ ts.tv_sec += MIN (seconds, max); seconds -= (unsigned int) ts.tv_sec; } else { ts.tv_sec = (time_t) seconds; seconds = 0; } /* Linux will wake up the system call, nanosleep, when SIGCHLD arrives even if SIGCHLD is ignored. We have to deal with it in libc. We block SIGCHLD first. */ __sigemptyset (&set); __sigaddset (&set, SIGCHLD); if (__sigprocmask (SIG_BLOCK, &set, &oset)) return -1; /* If SIGCHLD is already blocked, we don't have to do anything. */ if (!__sigismember (&oset, SIGCHLD)) { int saved_errno; struct sigaction oact; __sigemptyset (&set); __sigaddset (&set, SIGCHLD); /* We get the signal handler for SIGCHLD. */ if (__sigaction (SIGCHLD, (struct sigaction *) NULL, &oact) < 0) { saved_errno = errno; /* Restore the original signal mask. */ (void) __sigprocmask (SIG_SETMASK, &oset, (sigset_t *) NULL); __set_errno (saved_errno); return -1; } /* Note the sleep() is a cancellation point. But since we call nanosleep() which itself is a cancellation point we do not have to do anything here. */ if (oact.sa_handler == SIG_IGN) { //__libc_cleanup_push (cl, &oset); /* We should leave SIGCHLD blocked. */ while (1) { result = __nanosleep (&ts, &ts); if (result != 0 || seconds == 0) break; if (sizeof (ts.tv_sec) <= sizeof (seconds)) { ts.tv_sec = MIN (seconds, max); seconds -= (unsigned int) ts.tv_nsec; } } //__libc_cleanup_pop (0); saved_errno = errno; /* Restore the original signal mask. */ (void) __sigprocmask (SIG_SETMASK, &oset, (sigset_t *) NULL); __set_errno (saved_errno); goto out; } /* We should unblock SIGCHLD. Restore the original signal mask. */ (void) __sigprocmask (SIG_SETMASK, &oset, (sigset_t *) NULL); } result = __nanosleep (&ts, &ts); if (result == 0 && seconds != 0) goto again; out: if (result != 0) /* Round remaining time. */ result = seconds + (unsigned int) ts.tv_sec + (ts.tv_nsec >= 500000000L); return result; }
/* We are going to use the `nanosleep' syscall of the kernel. But the kernel does not implement the sstupid SysV SIGCHLD vs. SIG_IGN behaviour for this syscall. Therefore we have to emulate it here. */ unsigned int sleep (unsigned int seconds) { struct timespec ts = { tv_sec: (long int) seconds, tv_nsec: 0 }; sigset_t set, oset; unsigned int result; /* This is not necessary but some buggy programs depend on this. */ if (seconds == 0) return 0; /* Linux will wake up the system call, nanosleep, when SIGCHLD arrives even if SIGCHLD is ignored. We have to deal with it in libc. We block SIGCHLD first. */ if (__sigemptyset (&set) < 0 || __sigaddset (&set, SIGCHLD) < 0 || sigprocmask (SIG_BLOCK, &set, &oset)) return -1; /* If SIGCHLD is already blocked, we don't have to do anything. */ if (!__sigismember (&oset, SIGCHLD)) { int saved_errno; struct sigaction oact; if (__sigemptyset (&set) < 0 || __sigaddset (&set, SIGCHLD) < 0) return -1; /* We get the signal handler for SIGCHLD. */ if (sigaction (SIGCHLD, (struct sigaction *) NULL, &oact) < 0) { saved_errno = errno; /* Restore the original signal mask. */ (void) sigprocmask (SIG_SETMASK, &oset, (sigset_t *) NULL); __set_errno (saved_errno); return -1; } if (oact.sa_handler == SIG_IGN) { /* We should leave SIGCHLD blocked. */ result = nanosleep (&ts, &ts); saved_errno = errno; /* Restore the original signal mask. */ (void) sigprocmask (SIG_SETMASK, &oset, (sigset_t *) NULL); __set_errno (saved_errno); } else { /* We should unblock SIGCHLD. Restore the original signal mask. */ (void) sigprocmask (SIG_SETMASK, &oset, (sigset_t *) NULL); result = nanosleep (&ts, &ts); } } else result = nanosleep (&ts, &ts); if (result != 0) /* Round remaining time. */ result = (unsigned int) ts.tv_sec + (ts.tv_nsec >= 500000000L); return result; }