/* XXX should be __sigpending ? */ int sigpending (sigset_t *set) { struct hurd_sigstate *ss; sigset_t pending; if (set == NULL) { errno = EINVAL; return -1; } ss = _hurd_self_sigstate (); __spin_lock (&ss->lock); pending = ss->pending; __spin_unlock (&ss->lock); *set = pending; return 0; }
/* This function must be very careful not to depend on Hurd threadvar variables. We arrange that by using special stubs arranged for at the end of this file. */ static void profile_waiter (void) { mach_msg_header_t msg; mach_port_t timeout_reply_port; profil_reply_port = __mach_reply_port (); timeout_reply_port = __mach_reply_port (); while (1) { __spin_lock (&lock); fetch_samples (); __spin_unlock (&lock); __mach_msg (&msg, MACH_RCV_MSG|MACH_RCV_TIMEOUT, 0, sizeof msg, timeout_reply_port, collector_timeout, MACH_PORT_NULL); } }
/* Run signals handlers on the stack specified by SS (if not NULL). If OSS is not NULL, it is filled in with the old signal stack status. */ int __sigaltstack (const stack_t *argss, stack_t *oss) { struct hurd_sigstate *s; stack_t ss, old; /* Fault before taking any locks. */ if (argss != NULL) ss = *argss; if (oss != NULL) *(volatile stack_t *) oss = *oss; s = _hurd_self_sigstate (); __spin_lock (&s->lock); if (argss != NULL && (ss.ss_flags & SS_DISABLE) && (s->sigaltstack.ss_flags & SS_ONSTACK)) { /* Can't disable a stack that is in use. */ __spin_unlock (&s->lock); errno = EINVAL; return -1; } old = s->sigaltstack; if (argss != NULL) s->sigaltstack = ss; __spin_unlock (&s->lock); if (oss != NULL) *oss = old; return 0; }
/* Open a directory stream on NAME. */ DIR * __opendir (const char *name) { DIR *dirp; int fd; struct hurd_fd *d; fd = __open (name, O_RDONLY); if (fd < 0) return NULL; dirp = (DIR *) malloc (sizeof (DIR)); if (dirp == NULL) { __close (fd); return NULL; } /* Extract the pointer to the descriptor structure. */ __mutex_lock (&_hurd_dtable_lock); d = dirp->__fd = _hurd_dtable[fd]; __mutex_unlock (&_hurd_dtable_lock); /* Set the descriptor to close on exec. */ __spin_lock (&d->port.lock); d->flags |= FD_CLOEXEC; __spin_unlock (&d->port.lock); dirp->__data = dirp->__ptr = NULL; dirp->__entry_data = dirp->__entry_ptr = 0; dirp->__allocation = 0; dirp->__size = 0; __libc_lock_init (dirp->__lock); return dirp; }
void _hurd_raise_signal (struct hurd_sigstate *ss, int signo, const struct hurd_signal_detail *detail) { if (ss == NULL) { ss = _hurd_self_sigstate (); __spin_lock (&ss->lock); } /* Mark SIGNO as pending to be delivered. */ __sigaddset (&ss->pending, signo); ss->pending_data[signo] = *detail; __spin_unlock (&ss->lock); /* Send a message to the signal thread so it will wake up and check for pending signals. This is a generic "poll request" message (SIGNO==0) rather than delivering this signal and its detail, because we have already marked the signal as pending for the particular thread we want. Generating the signal with an RPC might deliver it to some other thread. */ __msg_sig_post (_hurd_msgport, 0, 0, __mach_task_self ()); }
/* Duplicate FD to FD2, closing the old FD2 and making FD2 be open on the same file as FD is, and setting FD2's flags according to FLAGS. Return FD2 or -1. */ int __dup3 (int fd, int fd2, int flags) { struct hurd_fd *d; /* Both passing flags different from O_CLOEXEC and FD2 being the same as FD are invalid. */ if ((flags & ~O_CLOEXEC || fd2 == fd) /* ... with the exception in case that dup2 behavior is requested: if FD is valid and FD2 is already the same then just return it. */ && ! (flags == -1 && fd2 == fd)) return __hurd_fail (EINVAL); /* Extract the ports and flags from FD. */ d = _hurd_fd_get (fd); if (d == NULL) return __hurd_fail (EBADF); HURD_CRITICAL_BEGIN; __spin_lock (&d->port.lock); if (d->port.port == MACH_PORT_NULL) { __spin_unlock (&d->port.lock); fd2 = __hurd_fail (EBADF); } else if (fd2 == fd) __spin_unlock (&d->port.lock); else { struct hurd_userlink ulink, ctty_ulink; int d_flags = d->flags; io_t ctty = _hurd_port_get (&d->ctty, &ctty_ulink); io_t port = _hurd_port_locked_get (&d->port, &ulink); /* Unlocks D. */ if (fd2 < 0) fd2 = __hurd_fail (EBADF); else { /* Get a hold of the destination descriptor. */ struct hurd_fd *d2; __mutex_lock (&_hurd_dtable_lock); if (fd2 >= _hurd_dtablesize) { /* The table is not large enough to hold the destination descriptor. Enlarge it as necessary to allocate this descriptor. */ __mutex_unlock (&_hurd_dtable_lock); d2 = _hurd_alloc_fd (NULL, fd2); if (d2) __spin_unlock (&d2->port.lock); __mutex_lock (&_hurd_dtable_lock); } else { d2 = _hurd_dtable[fd2]; if (d2 == NULL) { /* Must allocate a new one. We don't initialize the port cells with this call so that if it fails (out of memory), we will not have already added user references for the ports, which we would then have to deallocate. */ d2 = _hurd_dtable[fd2] = _hurd_new_fd (MACH_PORT_NULL, MACH_PORT_NULL); } } __mutex_unlock (&_hurd_dtable_lock); if (d2 == NULL) { fd2 = -1; if (errno == EINVAL) errno = EBADF; /* POSIX.1-1990 6.2.1.2 ll 54-55. */ } else { /* Give the ports each a user ref for the new descriptor. */ __mach_port_mod_refs (__mach_task_self (), port, MACH_PORT_RIGHT_SEND, 1); if (ctty != MACH_PORT_NULL) __mach_port_mod_refs (__mach_task_self (), ctty, MACH_PORT_RIGHT_SEND, 1); /* Install the ports and flags in the new descriptor slot. */ __spin_lock (&d2->port.lock); if (flags & O_CLOEXEC) d2->flags = d_flags | FD_CLOEXEC; else /* dup clears FD_CLOEXEC. */ d2->flags = d_flags & ~FD_CLOEXEC; _hurd_port_set (&d2->ctty, ctty); _hurd_port_locked_set (&d2->port, port); /* Unlocks D2. */ } } _hurd_port_free (&d->port, &ulink, port); if (ctty != MACH_PORT_NULL) _hurd_port_free (&d->ctty, &ctty_ulink, port); } HURD_CRITICAL_END; return fd2; }
/* Before fork, lock the interlock so that we are in a clean state. */ static void fork_profil_prepare (void) { __spin_lock (&lock); }
/* Open a directory stream on NAME. */ DIR * __opendir (const char *name) { DIR *dirp; int fd; struct hurd_fd *d; if (name[0] == '\0') { /* POSIX.1-1990 says an empty name gets ENOENT; but `open' might like it fine. */ __set_errno (ENOENT); return NULL; } { /* Append trailing slash to directory name to force ENOTDIR if it's not a directory. We open using the O_NONBLOCK flag so that a nondirectory with blocking behavior (FIFO or device) gets ENOTDIR immediately rather than waiting for the special file's open wakeup predicate. */ size_t len = strlen (name); if (name[len - 1] == '/') fd = __open (name, O_RDONLY | O_NONBLOCK); else { char n[len + 2]; memcpy (n, name, len); n[len] = '/'; n[len + 1] = '\0'; fd = __open (n, O_RDONLY | O_NONBLOCK); } } if (fd < 0) return NULL; dirp = (DIR *) malloc (sizeof (DIR)); if (dirp == NULL) { __close (fd); return NULL; } /* Extract the pointer to the descriptor structure. */ __mutex_lock (&_hurd_dtable_lock); d = dirp->__fd = _hurd_dtable[fd]; __mutex_unlock (&_hurd_dtable_lock); /* Set the descriptor to close on exec. */ __spin_lock (&d->port.lock); d->flags |= FD_CLOEXEC; __spin_unlock (&d->port.lock); dirp->__data = dirp->__ptr = NULL; dirp->__entry_data = dirp->__entry_ptr = 0; dirp->__allocation = 0; dirp->__size = 0; __libc_lock_init (dirp->__lock); return dirp; }
/* Overlay TASK, executing FILE with arguments ARGV and environment ENVP. If TASK == mach_task_self (), some ports are dealloc'd by the exec server. ARGV and ENVP are terminated by NULL pointers. */ error_t _hurd_exec (task_t task, file_t file, char *const argv[], char *const envp[]) { error_t err; char *args, *env; size_t argslen, envlen; int ints[INIT_INT_MAX]; mach_port_t ports[_hurd_nports]; struct hurd_userlink ulink_ports[_hurd_nports]; file_t *dtable; unsigned int dtablesize, i; struct hurd_port **dtable_cells; struct hurd_userlink *ulink_dtable; struct hurd_sigstate *ss; mach_port_t *please_dealloc, *pdp; /* XXX needs to be hurdmalloc XXX */ if (err = __argz_create (argv, &args, &argslen)) return err; if (err = __argz_create (envp, &env, &envlen)) goto outargs; /* Load up the ports to give to the new program. */ for (i = 0; i < _hurd_nports; ++i) if (i == INIT_PORT_PROC && task != __mach_task_self ()) { /* This is another task, so we need to ask the proc server for the right proc server port for it. */ if (err = __USEPORT (PROC, __proc_task2proc (port, task, &ports[i]))) { while (--i > 0) _hurd_port_free (&_hurd_ports[i], &ulink_ports[i], ports[i]); goto outenv; } } else ports[i] = _hurd_port_get (&_hurd_ports[i], &ulink_ports[i]); /* Load up the ints to give the new program. */ for (i = 0; i < INIT_INT_MAX; ++i) switch (i) { case INIT_UMASK: ints[i] = _hurd_umask; break; case INIT_SIGMASK: case INIT_SIGIGN: case INIT_SIGPENDING: /* We will set these all below. */ break; case INIT_TRACEMASK: ints[i] = _hurdsig_traced; break; default: ints[i] = 0; } ss = _hurd_self_sigstate (); assert (! __spin_lock_locked (&ss->critical_section_lock)); __spin_lock (&ss->critical_section_lock); __spin_lock (&ss->lock); ints[INIT_SIGMASK] = ss->blocked; ints[INIT_SIGPENDING] = ss->pending; ints[INIT_SIGIGN] = 0; for (i = 1; i < NSIG; ++i) if (ss->actions[i].sa_handler == SIG_IGN) ints[INIT_SIGIGN] |= __sigmask (i); /* We hold the sigstate lock until the exec has failed so that no signal can arrive between when we pack the blocked and ignored signals, and when the exec actually happens. A signal handler could change what signals are blocked and ignored. Either the change will be reflected in the exec, or the signal will never be delivered. Setting the critical section flag avoids anything we call trying to acquire the sigstate lock. */ __spin_unlock (&ss->lock); /* Pack up the descriptor table to give the new program. */ __mutex_lock (&_hurd_dtable_lock); dtablesize = _hurd_dtable ? _hurd_dtablesize : _hurd_init_dtablesize; if (task == __mach_task_self ()) /* Request the exec server to deallocate some ports from us if the exec succeeds. The init ports and descriptor ports will arrive in the new program's exec_startup message. If we failed to deallocate them, the new program would have duplicate user references for them. But we cannot deallocate them ourselves, because we must still have them after a failed exec call. */ please_dealloc = __alloca ((_hurd_nports + (2 * dtablesize)) * sizeof (mach_port_t)); else please_dealloc = NULL; pdp = please_dealloc; if (_hurd_dtable != NULL) { dtable = __alloca (dtablesize * sizeof (dtable[0])); ulink_dtable = __alloca (dtablesize * sizeof (ulink_dtable[0])); dtable_cells = __alloca (dtablesize * sizeof (dtable_cells[0])); for (i = 0; i < dtablesize; ++i) { struct hurd_fd *const d = _hurd_dtable[i]; if (d == NULL) { dtable[i] = MACH_PORT_NULL; continue; } __spin_lock (&d->port.lock); if (d->flags & FD_CLOEXEC) { /* This descriptor is marked to be closed on exec. So don't pass it to the new program. */ dtable[i] = MACH_PORT_NULL; if (pdp && d->port.port != MACH_PORT_NULL) { /* We still need to deallocate the ports. */ *pdp++ = d->port.port; if (d->ctty.port != MACH_PORT_NULL) *pdp++ = d->ctty.port; } __spin_unlock (&d->port.lock); } else { if (pdp && d->ctty.port != MACH_PORT_NULL) /* All the elements of DTABLE are added to PLEASE_DEALLOC below, so we needn't add the port itself. But we must deallocate the ctty port as well as the normal port that got installed in DTABLE[I]. */ *pdp++ = d->ctty.port; dtable[i] = _hurd_port_locked_get (&d->port, &ulink_dtable[i]); dtable_cells[i] = &d->port; } } } else { dtable = _hurd_init_dtable; ulink_dtable = NULL; dtable_cells = NULL; } /* The information is all set up now. Try to exec the file. */ { if (pdp) { /* Request the exec server to deallocate some ports from us if the exec succeeds. The init ports and descriptor ports will arrive in the new program's exec_startup message. If we failed to deallocate them, the new program would have duplicate user references for them. But we cannot deallocate them ourselves, because we must still have them after a failed exec call. */ for (i = 0; i < _hurd_nports; ++i) *pdp++ = ports[i]; for (i = 0; i < dtablesize; ++i) *pdp++ = dtable[i]; } err = __file_exec (file, task, 0, args, argslen, env, envlen, dtable, MACH_MSG_TYPE_COPY_SEND, dtablesize, ports, MACH_MSG_TYPE_COPY_SEND, _hurd_nports, ints, INIT_INT_MAX, please_dealloc, pdp - please_dealloc, &_hurd_msgport, task == __mach_task_self () ? 1 : 0); } /* Release references to the standard ports. */ for (i = 0; i < _hurd_nports; ++i) if (i == INIT_PORT_PROC && task != __mach_task_self ()) __mach_port_deallocate (__mach_task_self (), ports[i]); else _hurd_port_free (&_hurd_ports[i], &ulink_ports[i], ports[i]); if (ulink_dtable != NULL) /* Release references to the file descriptor ports. */ for (i = 0; i < dtablesize; ++i) if (dtable[i] != MACH_PORT_NULL) _hurd_port_free (dtable_cells[i], &ulink_dtable[i], dtable[i]); /* Release lock on the file descriptor table. */ __mutex_unlock (&_hurd_dtable_lock); /* Safe to let signals happen now. */ _hurd_critical_section_unlock (ss); outargs: free (args); outenv: free (env); return err; }