int raise(int sig) { int pid, tid, ret; sigset_t set; sigfillset(&set); __sigprocmask(SIG_BLOCK, &set, &set); tid = syscall(SYS_gettid); pid = syscall(SYS_getpid); ret = syscall(SYS_tgkill, pid, tid, sig); __sigprocmask(SIG_SETMASK, &set, 0); return ret; }
int sigrelse (int sig) { sigset_t set; /* Retrieve current signal set. */ if (__sigprocmask (SIG_SETMASK, NULL, &set) < 0) return -1; /* Remove the specified signal. */ if (sigdelset (&set, sig) < 0) return -1; /* Set the new mask. */ return __sigprocmask (SIG_SETMASK, &set, NULL); }
int __v1__sigjmp_save (__v1__sigjmp_buf env, int savemask) { env[0].__mask_was_saved = (savemask && __sigprocmask (SIG_BLOCK, (sigset_t *) NULL, &env[0].__saved_mask) == 0); return 0; }
int ppoll (struct pollfd *fds, nfds_t nfds, const struct timespec *timeout, const sigset_t *sigmask) { int tval = -1; /* poll uses a simple millisecond value. Convert it. */ if (timeout != NULL) { if (timeout->tv_sec < 0 || timeout->tv_nsec < 0 || timeout->tv_nsec >= 1000000000) { __set_errno (EINVAL); return -1; } if (timeout->tv_sec > INT_MAX / 1000 || (timeout->tv_sec == INT_MAX / 1000 && ((timeout->tv_nsec + 999999) / 1000000 > INT_MAX % 1000))) /* We cannot represent the timeout in an int value. Wait forever. */ tval = -1; else tval = (timeout->tv_sec * 1000 + (timeout->tv_nsec + 999999) / 1000000); } /* The setting and restoring of the signal mask and the select call should be an atomic operation. This can't be done without kernel help. */ sigset_t savemask; if (sigmask != NULL) __sigprocmask (SIG_SETMASK, sigmask, &savemask); /* Note the ppoll() is a cancellation point. But since we call poll() which itself is a cancellation point we do not have to do anything here. */ int retval = __poll (fds, nfds, tval); if (sigmask != NULL) __sigprocmask (SIG_SETMASK, &savemask, NULL); return retval; }
int pthread_sigmask (int how, const sigset_t *newmask, sigset_t *oldmask) { /* Here we assume that sigprocmask actually does everything right. The only difference is the return value protocol. */ int result = __sigprocmask (how, newmask, oldmask); if (result < 0) result = errno; return result; }
/* Check the first NFDS descriptors each in READFDS (if not NULL) for read readiness, in WRITEFDS (if not NULL) for write readiness, and in EXCEPTFDS (if not NULL) for exceptional conditions. If TIMEOUT is not NULL, time out after waiting the interval specified therein. Additionally set the sigmask SIGMASK for this call. Returns the number of ready descriptors, or -1 for errors. */ int __pselect (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask) { struct timeval tval; int retval; sigset_t savemask; /* Change nanosecond number to microseconds. This might mean losing precision and therefore the `pselect` should be available. But for now it is hardly found. */ if (timeout != NULL) { /* Catch bugs which would be hidden by the TIMESPEC_TO_TIMEVAL computations. The division by 1000 truncates values. */ if (__builtin_expect (timeout->tv_nsec < 0, 0)) { __set_errno (EINVAL); return -1; } TIMESPEC_TO_TIMEVAL (&tval, timeout); } /* The setting and restoring of the signal mask and the select call should be an atomic operation. This can't be done without kernel help. */ if (sigmask != NULL) __sigprocmask (SIG_SETMASK, sigmask, &savemask); /* Note the pselect() is a cancellation point. But since we call select() which itself is a cancellation point we do not have to do anything here. */ retval = __select (nfds, readfds, writefds, exceptfds, timeout != NULL ? &tval : NULL); if (sigmask != NULL) __sigprocmask (SIG_SETMASK, &savemask, NULL); return retval; }
/* Block signals in MASK, returning the old mask. */ int __sigblock (int mask) { sigset_t set, oset; if (sigset_set_old_mask (&set, mask) < 0) return -1; if (__sigprocmask (SIG_BLOCK, &set, &oset) < 0) return -1; return sigset_get_old_mask (&oset); }
/* Cause an abnormal program termination with core-dump. */ __NORETURN void DEFUN_VOID(abort) { sigset_t sigs; if (__sigemptyset(&sigs) == 0 && __sigaddset(&sigs, SIGABRT) == 0) (void) __sigprocmask(SIG_UNBLOCK, &sigs, (sigset_t *) NULL); while (1) if (raise (SIGABRT)) /* If we can't signal ourselves, exit. */ _exit (127); /* If we signal ourselves and are still alive, or can't exit, loop forever. */ }
int sigprocmask(int how, const sigset_t *set, sigset_t *oset) { int error; /* * Guard against children of vfork(). */ if (curthread->ul_vfork) return (__sigprocmask(how, set, oset)); if ((error = thr_sigsetmask(how, set, oset)) != 0) { errno = error; return (-1); } return (0); }
/* Set the mask of blocked signals to MASK, wait for a signal to arrive, and then restore the mask. */ static int do_sigpause (int sig_or_mask, int is_sig) { sigset_t set; if (is_sig != 0) { /* The modern X/Open implementation is requested. */ if (__sigprocmask (0, NULL, &set) < 0 || sigdelset (&set, sig_or_mask) < 0) return -1; } else if (sigset_set_old_mask (&set, sig_or_mask) < 0) return -1; /* Note the sigpause() is a cancellation point. But since we call sigsuspend() which itself is a cancellation point we do not have to do anything here. */ return __sigsuspend (&set); }
/* Spawn a new process executing PATH with the attributes describes in *ATTRP. Before running the process perform the actions described in FILE-ACTIONS. */ static int __spawnix (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, int (*exec) (const char *, char *const *, char *const *)) { pid_t new_pid; struct posix_spawn_args args; int ec; if (__pipe2 (args.pipe, O_CLOEXEC)) return errno; /* To avoid imposing hard limits on posix_spawn{p} the total number of arguments is first calculated to allocate a mmap to hold all possible values. */ ptrdiff_t argc = 0; /* Linux allows at most max (0x7FFFFFFF, 1/4 stack size) arguments to be used in a execve call. We limit to INT_MAX minus one due the compatiblity code that may execute a shell script (maybe_script_execute) where it will construct another argument list with an additional argument. */ ptrdiff_t limit = INT_MAX - 1; while (argv[argc++] != NULL) if (argc == limit) { errno = E2BIG; return errno; } int prot = (PROT_READ | PROT_WRITE | ((GL (dl_stack_flags) & PF_X) ? PROT_EXEC : 0)); /* Add a slack area for child's stack. */ size_t argv_size = (argc * sizeof (void *)) + 512; size_t stack_size = ALIGN_UP (argv_size, GLRO(dl_pagesize)); void *stack = __mmap (NULL, stack_size, prot, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); if (__glibc_unlikely (stack == MAP_FAILED)) { close_not_cancel (args.pipe[0]); close_not_cancel (args.pipe[1]); return errno; } /* Disable asynchronous cancellation. */ int cs = LIBC_CANCEL_ASYNC (); args.file = file; args.exec = exec; args.fa = file_actions; args.attr = attrp ? attrp : &(const posix_spawnattr_t) { 0 }; args.argv = argv; args.argc = argc; args.envp = envp; args.xflags = xflags; __sigprocmask (SIG_BLOCK, &SIGALL_SET, &args.oldmask); /* The clone flags used will create a new child that will run in the same memory space (CLONE_VM) and the execution of calling thread will be suspend until the child calls execve or _exit. These condition as signal below either by pipe write (_exit with SPAWN_ERROR) or a successful execve. Also since the calling thread execution will be suspend, there is not need for CLONE_SETTLS. Although parent and child share the same TLS namespace, there will be no concurrent access for TLS variables (errno for instance). */ new_pid = CLONE (__spawni_child, STACK (stack, stack_size), stack_size, CLONE_VM | CLONE_VFORK | SIGCHLD, &args); close_not_cancel (args.pipe[1]); if (new_pid > 0) { if (__read (args.pipe[0], &ec, sizeof ec) != sizeof ec) ec = 0; else __waitpid (new_pid, NULL, 0); } else ec = -new_pid; __munmap (stack, stack_size); close_not_cancel (args.pipe[0]); if (!ec && new_pid) *pid = new_pid; __sigprocmask (SIG_SETMASK, &args.oldmask, 0); LIBC_CANCEL_RESET (cs); return ec; } /* 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 * acts, const posix_spawnattr_t * attrp, char *const argv[], char *const envp[], int xflags) { return __spawnix (pid, file, acts, attrp, argv, envp, xflags, xflags & SPAWN_XFLAGS_USE_PATH ? __execvpe : __execve); }
/* Check the first NFDS descriptors either in POLLFDS (if nonnnull) or in each of READFDS, WRITEFDS, EXCEPTFDS that is nonnull. If TIMEOUT is not NULL, time out after waiting the interval specified therein. Returns the number of ready descriptors, or -1 for errors. */ int _hurd_select (int nfds, struct pollfd *pollfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask) { int i; mach_port_t portset; int got; error_t err; fd_set rfds, wfds, xfds; int firstfd, lastfd; mach_msg_timeout_t to = 0; struct { struct hurd_userlink ulink; struct hurd_fd *cell; mach_port_t io_port; int type; mach_port_t reply_port; } d[nfds]; sigset_t oset; union typeword /* Use this to avoid unkosher casts. */ { mach_msg_type_t type; uint32_t word; }; assert (sizeof (union typeword) == sizeof (mach_msg_type_t)); assert (sizeof (uint32_t) == sizeof (mach_msg_type_t)); if (nfds < 0 || (pollfds == NULL && nfds > FD_SETSIZE)) { errno = EINVAL; return -1; } if (timeout != NULL) { if (timeout->tv_sec < 0 || timeout->tv_nsec < 0) { errno = EINVAL; return -1; } to = (timeout->tv_sec * 1000 + (timeout->tv_nsec + 999999) / 1000000); } if (sigmask && __sigprocmask (SIG_SETMASK, sigmask, &oset)) return -1; if (pollfds) { /* Collect interesting descriptors from the user's `pollfd' array. We do a first pass that reads the user's array before taking any locks. The second pass then only touches our own stack, and gets the port references. */ for (i = 0; i < nfds; ++i) if (pollfds[i].fd >= 0) { int type = 0; if (pollfds[i].events & POLLIN) type |= SELECT_READ; if (pollfds[i].events & POLLOUT) type |= SELECT_WRITE; if (pollfds[i].events & POLLPRI) type |= SELECT_URG; d[i].io_port = pollfds[i].fd; d[i].type = type; } else d[i].type = 0; HURD_CRITICAL_BEGIN; __mutex_lock (&_hurd_dtable_lock); for (i = 0; i < nfds; ++i) if (d[i].type != 0) { const int fd = (int) d[i].io_port; if (fd < _hurd_dtablesize) { d[i].cell = _hurd_dtable[fd]; d[i].io_port = _hurd_port_get (&d[i].cell->port, &d[i].ulink); if (d[i].io_port != MACH_PORT_NULL) continue; } /* If one descriptor is bogus, we fail completely. */ while (i-- > 0) if (d[i].type != 0) _hurd_port_free (&d[i].cell->port, &d[i].ulink, d[i].io_port); break; } __mutex_unlock (&_hurd_dtable_lock); HURD_CRITICAL_END; if (i < nfds) { if (sigmask) __sigprocmask (SIG_SETMASK, &oset, NULL); errno = EBADF; return -1; } lastfd = i - 1; firstfd = i == 0 ? lastfd : 0; } else { /* Collect interested descriptors from the user's fd_set arguments. Use local copies so we can't crash from user bogosity. */ if (readfds == NULL) FD_ZERO (&rfds); else rfds = *readfds; if (writefds == NULL) FD_ZERO (&wfds); else wfds = *writefds; if (exceptfds == NULL) FD_ZERO (&xfds); else xfds = *exceptfds; HURD_CRITICAL_BEGIN; __mutex_lock (&_hurd_dtable_lock); if (nfds > _hurd_dtablesize) nfds = _hurd_dtablesize; /* Collect the ports for interesting FDs. */ firstfd = lastfd = -1; for (i = 0; i < nfds; ++i) { int type = 0; if (readfds != NULL && FD_ISSET (i, &rfds)) type |= SELECT_READ; if (writefds != NULL && FD_ISSET (i, &wfds)) type |= SELECT_WRITE; if (exceptfds != NULL && FD_ISSET (i, &xfds)) type |= SELECT_URG; d[i].type = type; if (type) { d[i].cell = _hurd_dtable[i]; d[i].io_port = _hurd_port_get (&d[i].cell->port, &d[i].ulink); if (d[i].io_port == MACH_PORT_NULL) { /* If one descriptor is bogus, we fail completely. */ while (i-- > 0) if (d[i].type != 0) _hurd_port_free (&d[i].cell->port, &d[i].ulink, d[i].io_port); break; } lastfd = i; if (firstfd == -1) firstfd = i; } } __mutex_unlock (&_hurd_dtable_lock); HURD_CRITICAL_END; if (i < nfds) { if (sigmask) __sigprocmask (SIG_SETMASK, &oset, NULL); errno = EBADF; return -1; } } err = 0; got = 0; /* Send them all io_select request messages. */ if (firstfd == -1) /* But not if there were no ports to deal with at all. We are just a pure timeout. */ portset = __mach_reply_port (); else { portset = MACH_PORT_NULL; for (i = firstfd; i <= lastfd; ++i) if (d[i].type) { int type = d[i].type; d[i].reply_port = __mach_reply_port (); err = __io_select (d[i].io_port, d[i].reply_port, /* Poll only if there's a single descriptor. */ (firstfd == lastfd) ? to : 0, &type); switch (err) { case MACH_RCV_TIMED_OUT: /* No immediate response. This is normal. */ err = 0; if (firstfd == lastfd) /* When there's a single descriptor, we don't need a portset, so just pretend we have one, but really use the single reply port. */ portset = d[i].reply_port; else if (got == 0) /* We've got multiple reply ports, so we need a port set to multiplex them. */ { /* We will wait again for a reply later. */ if (portset == MACH_PORT_NULL) /* Create the portset to receive all the replies on. */ err = __mach_port_allocate (__mach_task_self (), MACH_PORT_RIGHT_PORT_SET, &portset); if (! err) /* Put this reply port in the port set. */ __mach_port_move_member (__mach_task_self (), d[i].reply_port, portset); } break; default: /* No other error should happen. Callers of select don't expect to see errors, so we simulate readiness of the erring object and the next call hopefully will get the error again. */ type = SELECT_ALL; /* FALLTHROUGH */ case 0: /* We got an answer. */ if ((type & SELECT_ALL) == 0) /* Bogus answer; treat like an error, as a fake positive. */ type = SELECT_ALL; /* This port is already ready already. */ d[i].type &= type; d[i].type |= SELECT_RETURNED; ++got; break; } _hurd_port_free (&d[i].cell->port, &d[i].ulink, d[i].io_port); } } /* Now wait for reply messages. */ if (!err && got == 0) { /* Now wait for io_select_reply messages on PORT, timing out as appropriate. */ union { mach_msg_header_t head; #ifdef MACH_MSG_TRAILER_MINIMUM_SIZE struct { mach_msg_header_t head; NDR_record_t ndr; error_t err; } error; struct { mach_msg_header_t head; NDR_record_t ndr; error_t err; int result; mach_msg_trailer_t trailer; } success; #else struct { mach_msg_header_t head; union typeword err_type; error_t err; } error; struct { mach_msg_header_t head; union typeword err_type; error_t err; union typeword result_type; int result; } success; #endif } msg; mach_msg_option_t options = (timeout == NULL ? 0 : MACH_RCV_TIMEOUT); error_t msgerr; while ((msgerr = __mach_msg (&msg.head, MACH_RCV_MSG | MACH_RCV_INTERRUPT | options, 0, sizeof msg, portset, to, MACH_PORT_NULL)) == MACH_MSG_SUCCESS) { /* We got a message. Decode it. */ #define IO_SELECT_REPLY_MSGID (21012 + 100) /* XXX */ #ifdef MACH_MSG_TYPE_BIT const union typeword inttype = { type: { MACH_MSG_TYPE_INTEGER_T, sizeof (integer_t) * 8, 1, 1, 0, 0 } }; #endif if (msg.head.msgh_id == IO_SELECT_REPLY_MSGID && msg.head.msgh_size >= sizeof msg.error && !(msg.head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && #ifdef MACH_MSG_TYPE_BIT msg.error.err_type.word == inttype.word #endif ) { /* This is a properly formatted message so far. See if it is a success or a failure. */ if (msg.error.err == EINTR && msg.head.msgh_size == sizeof msg.error) { /* EINTR response; poll for further responses and then return quickly. */ err = EINTR; goto poll; } if (msg.error.err || msg.head.msgh_size != sizeof msg.success || #ifdef MACH_MSG_TYPE_BIT msg.success.result_type.word != inttype.word || #endif (msg.success.result & SELECT_ALL) == 0) { /* Error or bogus reply. Simulate readiness. */ __mach_msg_destroy (&msg.head); msg.success.result = SELECT_ALL; } /* Look up the respondent's reply port and record its readiness. */ { int had = got; if (firstfd != -1) for (i = firstfd; i <= lastfd; ++i) if (d[i].type && d[i].reply_port == msg.head.msgh_local_port) { d[i].type &= msg.success.result; d[i].type |= SELECT_RETURNED; ++got; } assert (got > had); } } if (msg.head.msgh_remote_port != MACH_PORT_NULL) __mach_port_deallocate (__mach_task_self (), msg.head.msgh_remote_port); if (got) poll: { /* Poll for another message. */ to = 0; options |= MACH_RCV_TIMEOUT; } } if (msgerr == MACH_RCV_INTERRUPTED) /* Interruption on our side (e.g. signal reception). */ err = EINTR; if (got) /* At least one descriptor is known to be ready now, so we will return success. */ err = 0; }
/* Execute LINE as a shell command, returning its status. */ static int do_system (const char *line) { int status, save; pid_t pid; struct sigaction sa; #ifndef _LIBC_REENTRANT struct sigaction intr, quit; #endif sigset_t omask; sa.sa_handler = SIG_IGN; sa.sa_flags = 0; __sigemptyset (&sa.sa_mask); DO_LOCK (); if (ADD_REF () == 0) { if (__sigaction (SIGINT, &sa, &intr) < 0) { (void) SUB_REF (); goto out; } if (__sigaction (SIGQUIT, &sa, &quit) < 0) { save = errno; (void) SUB_REF (); goto out_restore_sigint; } } DO_UNLOCK (); /* We reuse the bitmap in the 'sa' structure. */ __sigaddset (&sa.sa_mask, SIGCHLD); save = errno; if (__sigprocmask (SIG_BLOCK, &sa.sa_mask, &omask) < 0) { #ifndef _LIBC if (errno == ENOSYS) __set_errno (save); else #endif { DO_LOCK (); if (SUB_REF () == 0) { save = errno; (void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL); out_restore_sigint: (void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL); __set_errno (save); } out: DO_UNLOCK (); return -1; } } #ifdef CLEANUP_HANDLER CLEANUP_HANDLER; #endif #ifdef FORK pid = FORK (); #else pid = __fork (); #endif if (pid == (pid_t) 0) { /* Child side. */ const char *new_argv[4]; new_argv[0] = SHELL_NAME; new_argv[1] = "-c"; new_argv[2] = line; new_argv[3] = NULL; /* Restore the signals. */ (void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL); (void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL); (void) __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL); INIT_LOCK (); /* Exec the shell. */ (void) __execve (SHELL_PATH, (char *const *) new_argv, __environ); _exit (127); } else if (pid < (pid_t) 0) /* The fork failed. */ status = -1; else /* Parent side. */ { /* Note the system() is a cancellation point. But since we call waitpid() which itself is a cancellation point we do not have to do anything here. */ if (TEMP_FAILURE_RETRY (__waitpid (pid, &status, 0)) != pid) status = -1; } #ifdef CLEANUP_HANDLER CLEANUP_RESET; #endif save = errno; DO_LOCK (); if ((SUB_REF () == 0 && (__sigaction (SIGINT, &intr, (struct sigaction *) NULL) | __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL)) != 0) || __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL) != 0) { #ifndef _LIBC /* glibc cannot be used on systems without waitpid. */ if (errno == ENOSYS) __set_errno (save); else #endif status = -1; } DO_UNLOCK (); return status; }
/* 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; }
static void cl (void *arg) { (void) __sigprocmask (SIG_SETMASK, arg, (sigset_t *) NULL); }
/* Cause an abnormal program termination with core-dump. */ void abort (void) { struct sigaction act; sigset_t sigs; /* First acquire the lock. */ __libc_lock_lock_recursive (lock); /* Now it's for sure we are alone. But recursive calls are possible. */ /* Unlock SIGABRT. */ if (stage == 0) { ++stage; if (__sigemptyset (&sigs) == 0 && __sigaddset (&sigs, SIGABRT) == 0) __sigprocmask (SIG_UNBLOCK, &sigs, (sigset_t *) NULL); } /* Flush all streams. We cannot close them now because the user might have registered a handler for SIGABRT. */ if (stage == 1) { ++stage; fflush (NULL); } /* Send signal which possibly calls a user handler. */ if (stage == 2) { /* This stage is special: we must allow repeated calls of `abort' when a user defined handler for SIGABRT is installed. This is risky since the `raise' implementation might also fail but I don't see another possibility. */ int save_stage = stage; stage = 0; __libc_lock_unlock_recursive (lock); raise (SIGABRT); __libc_lock_lock_recursive (lock); stage = save_stage + 1; } /* There was a handler installed. Now remove it. */ if (stage == 3) { ++stage; memset (&act, '\0', sizeof (struct sigaction)); act.sa_handler = SIG_DFL; __sigfillset (&act.sa_mask); act.sa_flags = 0; __sigaction (SIGABRT, &act, NULL); } /* Now close the streams which also flushes the output the user defined handler might has produced. */ if (stage == 4) { ++stage; __fcloseall (); } /* Try again. */ if (stage == 5) { ++stage; raise (SIGABRT); } /* Now try to abort using the system specific command. */ if (stage == 6) { ++stage; ABORT_INSTRUCTION; } /* If we can't signal ourselves and the abort instruction failed, exit. */ if (stage == 7) { ++stage; _exit (127); } /* If even this fails try to use the provided instruction to crash or otherwise make sure we never return. */ while (1) /* Try for ever and ever. */ ABORT_INSTRUCTION; }
License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <ansidecl.h> #include <stddef.h> #include <setjmp.h> #include <signal.h> /* This function is called by the `sigsetjmp' macro before doing a `__setjmp' on ENV[0].__jmpbuf. Always return zero. */ int DEFUN(__sigjmp_save, (env, savemask), sigjmp_buf env AND int savemask) { env[0].__mask_was_saved = (savemask && __sigprocmask (SIG_BLOCK, (sigset_t *) NULL, &env[0].__saved_mask) == 0); return 0; }
/* 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; }
/* Function used in the clone call to setup the signals mask, posix_spawn attributes, and file actions. It run on its own stack (provided by the posix_spawn call). */ static int __spawni_child (void *arguments) { struct posix_spawn_args *args = arguments; const posix_spawnattr_t *restrict attr = args->attr; const posix_spawn_file_actions_t *file_actions = args->fa; int p = args->pipe[1]; int ret; close_not_cancel (args->pipe[0]); /* The child must ensure that no signal handler are enabled because it shared memory with parent, so the signal disposition must be either SIG_DFL or SIG_IGN. It does by iterating over all signals and although it could possibly be more optimized (by tracking which signal potentially have a signal handler), it might requires system specific solutions (since the sigset_t data type can be very different on different architectures). */ struct sigaction sa; memset (&sa, '\0', sizeof (sa)); sigset_t hset; __sigprocmask (SIG_BLOCK, 0, &hset); for (int sig = 1; sig < _NSIG; ++sig) { if ((attr->__flags & POSIX_SPAWN_SETSIGDEF) && sigismember (&attr->__sd, sig)) { sa.sa_handler = SIG_DFL; } else if (sigismember (&hset, sig)) { if (__nptl_is_internal_signal (sig)) sa.sa_handler = SIG_IGN; else { __libc_sigaction (sig, 0, &sa); if (sa.sa_handler == SIG_IGN) continue; sa.sa_handler = SIG_DFL; } } else continue; __libc_sigaction (sig, &sa, 0); } #ifdef _POSIX_PRIORITY_SCHEDULING /* Set the scheduling algorithm and parameters. */ if ((attr->__flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER)) == POSIX_SPAWN_SETSCHEDPARAM) { if ((ret = __sched_setparam (0, &attr->__sp)) == -1) goto fail; } else if ((attr->__flags & POSIX_SPAWN_SETSCHEDULER) != 0) { if ((ret = __sched_setscheduler (0, attr->__policy, &attr->__sp)) == -1) goto fail; } #endif /* Set the process group ID. */ if ((attr->__flags & POSIX_SPAWN_SETPGROUP) != 0 && (ret = __setpgid (0, attr->__pgrp)) != 0) goto fail; /* Set the effective user and group IDs. */ if ((attr->__flags & POSIX_SPAWN_RESETIDS) != 0 && ((ret = local_seteuid (__getuid ())) != 0 || (ret = local_setegid (__getgid ())) != 0)) goto fail; /* Execute the file actions. */ if (file_actions != 0) { 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]; /* Dup the pipe fd onto an unoccupied one to avoid any file operation to clobber it. */ if ((action->action.close_action.fd == p) || (action->action.open_action.fd == p) || (action->action.dup2_action.fd == p)) { if ((ret = __dup (p)) < 0) goto fail; p = ret; } switch (action->tag) { case spawn_do_close: if ((ret = close_not_cancel (action->action.close_action.fd)) != 0) { if (!have_fdlimit) { __getrlimit64 (RLIMIT_NOFILE, &fdlimit); have_fdlimit = true; } /* Signal errors only for file descriptors out of range. */ if (action->action.close_action.fd < 0 || action->action.close_action.fd >= fdlimit.rlim_cur) goto fail; } break; case spawn_do_open: { ret = open_not_cancel (action->action.open_action.path, action->action. open_action.oflag | O_LARGEFILE, action->action.open_action.mode); if (ret == -1) goto fail; int new_fd = ret; /* Make sure the desired file descriptor is used. */ if (ret != action->action.open_action.fd) { if ((ret = __dup2 (new_fd, action->action.open_action.fd)) != action->action.open_action.fd) goto fail; if ((ret = close_not_cancel (new_fd)) != 0) goto fail; } } break; case spawn_do_dup2: if ((ret = __dup2 (action->action.dup2_action.fd, action->action.dup2_action.newfd)) != action->action.dup2_action.newfd) goto fail; break; } } } /* Set the initial signal mask of the child if POSIX_SPAWN_SETSIGMASK is set, otherwise restore the previous one. */ __sigprocmask (SIG_SETMASK, (attr->__flags & POSIX_SPAWN_SETSIGMASK) ? &attr->__ss : &args->oldmask, 0); args->exec (args->file, args->argv, args->envp); /* This is compatibility function required to enable posix_spawn run script without shebang definition for older posix_spawn versions (2.15). */ maybe_script_execute (args); ret = -errno; fail: /* Since sizeof errno < PIPE_BUF, the write is atomic. */ ret = -ret; if (ret) while (write_not_cancel (p, &ret, sizeof ret) < 0) continue; exit (SPAWN_ERROR); }
static int internal_function key_call_keyenvoy (u_long proc, xdrproc_t xdr_arg, char *arg, xdrproc_t xdr_rslt, char *rslt) { XDR xdrargs; XDR xdrrslt; FILE *fargs; FILE *frslt; sigset_t oldmask, mask; union wait status; int pid; int success; uid_t ruid; uid_t euid; static const char MESSENGER[] = "/usr/etc/keyenvoy"; success = 1; sigemptyset (&mask); sigaddset (&mask, SIGCHLD); __sigprocmask (SIG_BLOCK, &mask, &oldmask); /* * We are going to exec a set-uid program which makes our effective uid * zero, and authenticates us with our real uid. We need to make the * effective uid be the real uid for the setuid program, and * the real uid be the effective uid so that we can change things back. */ euid = __geteuid (); ruid = __getuid (); __setreuid (euid, ruid); pid = _openchild (MESSENGER, &fargs, &frslt); __setreuid (ruid, euid); if (pid < 0) { debug ("open_streams"); __sigprocmask (SIG_SETMASK, &oldmask, NULL); return (0); } xdrstdio_create (&xdrargs, fargs, XDR_ENCODE); xdrstdio_create (&xdrrslt, frslt, XDR_DECODE); if (!INTUSE(xdr_u_long) (&xdrargs, &proc) || !(*xdr_arg) (&xdrargs, arg)) { debug ("xdr args"); success = 0; } fclose (fargs); if (success && !(*xdr_rslt) (&xdrrslt, rslt)) { debug ("xdr rslt"); success = 0; } fclose(frslt); wait_again: if (__wait4 (pid, &status, 0, NULL) < 0) { if (errno == EINTR) goto wait_again; debug ("wait4"); if (errno == ECHILD || errno == ESRCH) perror ("wait"); else success = 0; } else if (status.w_retcode) { debug ("wait4 1"); success = 0; } __sigprocmask (SIG_SETMASK, &oldmask, NULL); return success; }
int __lckpwdf (void) { int flags; sigset_t saved_set; /* Saved set of caught signals. */ struct sigaction saved_act; /* Saved signal action. */ sigset_t new_set; /* New set of caught signals. */ struct sigaction new_act; /* New signal action. */ struct flock fl; /* Information struct for locking. */ int result; if (lock_fd != -1) /* Still locked by own process. */ return -1; /* Prevent problems caused by multiple threads. */ __libc_lock_lock (lock); int oflags = O_WRONLY | O_CREAT; #ifdef O_CLOEXEC oflags |= O_CLOEXEC; #endif lock_fd = __open (PWD_LOCKFILE, oflags, 0600); if (lock_fd == -1) /* Cannot create lock file. */ RETURN_CLOSE_FD (-1); #ifndef __ASSUME_O_CLOEXEC # ifdef O_CLOEXEC if (__have_o_cloexec <= 0) # endif { /* Make sure file gets correctly closed when process finished. */ flags = __fcntl (lock_fd, F_GETFD, 0); if (flags == -1) /* Cannot get file flags. */ RETURN_CLOSE_FD (-1); # ifdef O_CLOEXEC if (__have_o_cloexec == 0) __have_o_cloexec = (flags & FD_CLOEXEC) == 0 ? -1 : 1; if (__have_o_cloexec < 0) # endif { flags |= FD_CLOEXEC; /* Close on exit. */ if (__fcntl (lock_fd, F_SETFD, flags) < 0) /* Cannot set new flags. */ RETURN_CLOSE_FD (-1); } } #endif /* Now we have to get exclusive write access. Since multiple process could try this we won't stop when it first fails. Instead we set a timeout for the system call. Once the timer expires it is likely that there are some problems which cannot be resolved by waiting. It is important that we don't change the signal state. We must restore the old signal behaviour. */ memset (&new_act, '\0', sizeof (struct sigaction)); new_act.sa_handler = noop_handler; __sigfillset (&new_act.sa_mask); new_act.sa_flags = 0ul; /* Install new action handler for alarm and save old. */ if (__sigaction (SIGALRM, &new_act, &saved_act) < 0) /* Cannot install signal handler. */ RETURN_CLOSE_FD (-1); /* Now make sure the alarm signal is not blocked. */ __sigemptyset (&new_set); __sigaddset (&new_set, SIGALRM); if (__sigprocmask (SIG_UNBLOCK, &new_set, &saved_set) < 0) RETURN_RESTORE_HANDLER (-1); /* Start timer. If we cannot get the lock in the specified time we get a signal. */ alarm (TIMEOUT); /* Try to get the lock. */ memset (&fl, '\0', sizeof (struct flock)); fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; result = __fcntl (lock_fd, F_SETLKW, &fl); RETURN_CLEAR_ALARM (result); }