diff options
Diffstat (limited to 'hurd')
-rw-r--r-- | hurd/Makefile | 4 | ||||
-rw-r--r-- | hurd/Versions | 8 | ||||
-rw-r--r-- | hurd/ctty-input.c | 16 | ||||
-rw-r--r-- | hurd/ctty-output.c | 16 | ||||
-rw-r--r-- | hurd/fcntl-internal.h | 60 | ||||
-rw-r--r-- | hurd/hurd/resource.h | 4 | ||||
-rw-r--r-- | hurd/hurd/signal.h | 63 | ||||
-rw-r--r-- | hurd/hurdexec.c | 9 | ||||
-rw-r--r-- | hurd/hurdinit.c | 2 | ||||
-rw-r--r-- | hurd/hurdmsg.c | 24 | ||||
-rw-r--r-- | hurd/hurdselect.c | 251 | ||||
-rw-r--r-- | hurd/hurdsig.c | 524 | ||||
-rw-r--r-- | hurd/hurdsocket.h | 22 | ||||
-rw-r--r-- | hurd/sysvshm.c | 96 | ||||
-rw-r--r-- | hurd/sysvshm.h | 47 | ||||
-rw-r--r-- | hurd/thread-cancel.c | 1 |
16 files changed, 848 insertions, 299 deletions
diff --git a/hurd/Makefile b/hurd/Makefile index f7c7c69d2c..09bd00f991 100644 --- a/hurd/Makefile +++ b/hurd/Makefile @@ -37,7 +37,8 @@ user-interfaces := $(addprefix hurd/,\ process process_request \ msg msg_reply msg_request \ exec exec_startup crash interrupt \ - fs fsys io term tioctl socket ifsock \ + fs fsys io io_reply io_request \ + term tioctl socket ifsock \ login password pfinet \ ) server-interfaces := hurd/msg faultexc @@ -58,6 +59,7 @@ routines = hurdstartup hurdinit \ vpprintf \ ports-get ports-set hurdports hurdmsg \ errno-loc \ + sysvshm \ $(sig) $(dtable) $(inlines) port-cleanup report-wait xattr sig = hurdsig hurdfault siginfo hurd-raise preempt-sig \ trampoline longjmp-ts catch-exc exc2signal hurdkill sigunwind \ diff --git a/hurd/Versions b/hurd/Versions index 738281da15..74df5298a6 100644 --- a/hurd/Versions +++ b/hurd/Versions @@ -152,6 +152,14 @@ libc { # functions used in macros & inline functions __errno_location; } + GLIBC_2.15 { + # functions used by libpthread and <hurd/signal.h> + _hurd_sigstate_set_global_rcv; + _hurd_sigstate_lock; + _hurd_sigstate_pending; + _hurd_sigstate_unlock; + _hurd_sigstate_delete; + } %if !SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2) HURD_CTHREADS_0.3 { diff --git a/hurd/ctty-input.c b/hurd/ctty-input.c index 4c22987455..f7e9e08b7a 100644 --- a/hurd/ctty-input.c +++ b/hurd/ctty-input.c @@ -43,12 +43,15 @@ _hurd_ctty_input (io_t port, io_t ctty, error_t (*rpc) (io_t)) else { struct hurd_sigstate *ss = _hurd_self_sigstate (); - __spin_lock (&ss->lock); + struct sigaction *actions; + + _hurd_sigstate_lock (ss); + actions = _hurd_sigstate_actions (ss); if (__sigismember (&ss->blocked, SIGTTIN) || - ss->actions[SIGTTIN].sa_handler == SIG_IGN) + actions[SIGTTIN].sa_handler == SIG_IGN) /* We are blocking or ignoring SIGTTIN. Just fail. */ err = EIO; - __spin_unlock (&ss->lock); + _hurd_sigstate_unlock (ss); if (err == EBACKGROUND) { @@ -65,10 +68,11 @@ _hurd_ctty_input (io_t port, io_t ctty, error_t (*rpc) (io_t)) 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)) + _hurd_sigstate_lock (ss); + actions = _hurd_sigstate_actions (ss); + if (!(actions[SIGTTIN].sa_flags & SA_RESTART)) err = EINTR; - __spin_unlock (&ss->lock); + _hurd_sigstate_unlock (ss); } } } diff --git a/hurd/ctty-output.c b/hurd/ctty-output.c index 387befba6f..1adedf37a1 100644 --- a/hurd/ctty-output.c +++ b/hurd/ctty-output.c @@ -34,16 +34,19 @@ _hurd_ctty_output (io_t port, io_t ctty, error_t (*rpc) (io_t)) do { + struct sigaction *actions; + /* 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); + _hurd_sigstate_lock (ss); + actions = _hurd_sigstate_actions (ss); if (__sigismember (&ss->blocked, SIGTTOU) || - ss->actions[SIGTTOU].sa_handler == SIG_IGN) + actions[SIGTTOU].sa_handler == SIG_IGN) err = EIO; else err = 0; - __spin_unlock (&ss->lock); + _hurd_sigstate_unlock (ss); if (err) return (*rpc) (port); @@ -70,10 +73,11 @@ _hurd_ctty_output (io_t port, io_t ctty, error_t (*rpc) (io_t)) 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)) + _hurd_sigstate_lock (ss); + actions = _hurd_sigstate_actions (ss); + if (!(actions[SIGTTOU].sa_flags & SA_RESTART)) err = EINTR; - __spin_unlock (&ss->lock); + _hurd_sigstate_unlock (ss); } } /* If the last RPC generated a SIGTTOU, loop to try it again. */ diff --git a/hurd/fcntl-internal.h b/hurd/fcntl-internal.h new file mode 100644 index 0000000000..c816b37bdf --- /dev/null +++ b/hurd/fcntl-internal.h @@ -0,0 +1,60 @@ +/* Copyright (C) 2008 Free Software Foundation, Inc. + + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + + +#include <fcntl.h> +#include <sys/socket.h> +#include <verify.h> + +/* Do some compile-time checks for the SOCK_* constants, which we rely on. */ +verify (SOCK_CLOEXEC == O_CLOEXEC); +verify ((SOCK_MAX | SOCK_TYPE_MASK) == SOCK_TYPE_MASK); +verify ((SOCK_CLOEXEC & SOCK_TYPE_MASK) == 0); +verify ((SOCK_NONBLOCK & SOCK_TYPE_MASK) == 0); + + +/* Helper functions for translating between O_* and SOCK_* flags. */ + +__extern_always_inline +int +sock_to_o_flags (int in) +{ + int out = 0; + + if (in & SOCK_NONBLOCK) + out |= O_NONBLOCK; + /* Others are passed through unfiltered. */ + out |= in & ~(SOCK_NONBLOCK); + + return out; +} + +__extern_always_inline +int +o_to_sock_flags (int in) +{ + int out = 0; + + if (in & O_NONBLOCK) + out |= SOCK_NONBLOCK; + /* Others are passed through unfiltered. */ + out |= in & ~(O_NONBLOCK); + + return out; +} diff --git a/hurd/hurd/resource.h b/hurd/hurd/resource.h index af38ed3979..6eccf6329a 100644 --- a/hurd/hurd/resource.h +++ b/hurd/hurd/resource.h @@ -42,8 +42,8 @@ extern error_t _hurd_priority_which_map (enum __priority_which which, int who, /* Convert between Mach priority values and the priority values used by getpriority, setpriority, and nice. */ -#define MACH_PRIORITY_TO_NICE(prio) (2 * ((prio) - 12)) -#define NICE_TO_MACH_PRIORITY(nice) (12 + ((nice) / 2)) +#define MACH_PRIORITY_TO_NICE(prio) ((prio) - 25) +#define NICE_TO_MACH_PRIORITY(nice) ((nice) + 25) diff --git a/hurd/hurd/signal.h b/hurd/hurd/signal.h index 1e3e93be0c..8d63d2d0c0 100644 --- a/hurd/hurd/signal.h +++ b/hurd/hurd/signal.h @@ -63,12 +63,20 @@ struct hurd_sigstate spin_lock_t lock; /* Locks most of the rest of the structure. */ + /* The signal state holds a reference on the thread port. */ thread_t thread; + struct hurd_sigstate *next; /* Linked-list of thread sigstates. */ sigset_t blocked; /* What signals are blocked. */ sigset_t pending; /* Pending signals, possibly blocked. */ + + /* Signal handlers. ACTIONS[0] is used to mark the threads with POSIX + semantics: if sa_handler is SIG_IGN instead of SIG_DFL, this thread + will receive global signals and use the process-wide action vector + instead of this one. */ struct sigaction actions[NSIG]; + struct sigaltstack sigaltstack; /* Chain of thread-local signal preemptors; see <hurd/sigpreempt.h>. @@ -111,7 +119,9 @@ extern struct hurd_sigstate *_hurd_sigstates; extern struct mutex _hurd_siglock; /* Locks _hurd_sigstates. */ -/* Get the sigstate of a given thread, taking its lock. */ +/* Get the sigstate of a given thread. If there was no sigstate for + the thread, one is created, and the thread gains a reference. If + the given thread is MACH_PORT_NULL, return the global sigstate. */ extern struct hurd_sigstate *_hurd_thread_sigstate (thread_t); @@ -124,6 +134,26 @@ extern struct hurd_sigstate *_hurd_self_sigstate (void) by different threads. */ __attribute__ ((__const__)); +/* Process-wide signal state. */ + +extern struct hurd_sigstate *_hurd_global_sigstate; + +/* Mark the given thread as a process-wide signal receiver. */ + +extern void _hurd_sigstate_set_global_rcv (struct hurd_sigstate *ss); + +/* A thread can either use its own action vector and pending signal set + or use the global ones, depending on wether it has been marked as a + global receiver. The accessors below take that into account. */ + +extern void _hurd_sigstate_lock (struct hurd_sigstate *ss); +extern struct sigaction *_hurd_sigstate_actions (struct hurd_sigstate *ss); +extern sigset_t _hurd_sigstate_pending (const struct hurd_sigstate *ss); +extern void _hurd_sigstate_unlock (struct hurd_sigstate *ss); + +/* Used by libpthread to remove stale sigstate structures. */ +extern void _hurd_sigstate_delete (thread_t thread); + #ifndef _HURD_SIGNAL_H_EXTERN_INLINE #define _HURD_SIGNAL_H_EXTERN_INLINE __extern_inline #endif @@ -134,7 +164,11 @@ _hurd_self_sigstate (void) { struct hurd_sigstate **location = &THREAD_SELF->_hurd_sigstate; if (*location == NULL) - *location = _hurd_thread_sigstate (__mach_thread_self ()); + { + thread_t self = __mach_thread_self (); + *location = _hurd_thread_sigstate (self); + __mach_port_deallocate (__mach_task_self (), self); + } return *location; } #endif @@ -148,12 +182,6 @@ extern thread_t _hurd_msgport_thread; extern mach_port_t _hurd_msgport; - -/* Thread to receive process-global signals. */ - -extern thread_t _hurd_sigthread; - - /* Resource limit on core file size. Enforced by hurdsig.c. */ extern int _hurd_core_limit; @@ -183,12 +211,20 @@ _hurd_critical_section_lock (void) ss = *location; if (ss == NULL) { + thread_t self = __mach_thread_self (); + /* The thread variable is unset; this must be the first time we've asked for it. In this case, the critical section flag cannot possible already be set. Look up our sigstate structure the slow +<<<<<<< HEAD + way. */ + ss = THREAD_SELF->_hurd_sigstate = _hurd_thread_sigstate (self); + __mach_port_deallocate (__mach_task_self (), self); +======= way; this locks the sigstate lock. */ ss = *location = _hurd_thread_sigstate (__mach_thread_self ()); __spin_unlock (&ss->lock); +>>>>>>> t/tls-threadvar } if (! __spin_try_lock (&ss->critical_section_lock)) @@ -216,10 +252,10 @@ _hurd_critical_section_unlock (void *our_lock) /* It was us who acquired the critical section lock. Unlock it. */ struct hurd_sigstate *ss = (struct hurd_sigstate *) our_lock; sigset_t pending; - __spin_lock (&ss->lock); + _hurd_sigstate_lock (ss); __spin_unlock (&ss->critical_section_lock); - pending = ss->pending & ~ss->blocked; - __spin_unlock (&ss->lock); + pending = _hurd_sigstate_pending(ss) & ~ss->blocked; + _hurd_sigstate_unlock (ss); if (! __sigisemptyset (&pending)) /* There are unblocked signals pending, which weren't delivered because we were in the critical section. @@ -258,6 +294,11 @@ extern void _hurd_raise_signal (struct hurd_sigstate *ss, int signo, extern void _hurd_exception2signal (struct hurd_signal_detail *detail, int *signo); +/* Translate a Mach exception into a signal with a legacy sigcode. */ + +extern void _hurd_exception2signal_legacy (struct hurd_signal_detail *detail, + int *signo); + /* Make the thread described by SS take the signal described by SIGNO and DETAIL. If the process is traced, this will in fact stop with a SIGNO diff --git a/hurd/hurdexec.c b/hurd/hurdexec.c index 0ced7f32b4..77bf833742 100644 --- a/hurd/hurdexec.c +++ b/hurd/hurdexec.c @@ -107,12 +107,13 @@ _hurd_exec (task_t task, file_t file, assert (! __spin_lock_locked (&ss->critical_section_lock)); __spin_lock (&ss->critical_section_lock); - __spin_lock (&ss->lock); + _hurd_sigstate_lock (ss); + struct sigaction *actions = _hurd_sigstate_actions (ss); ints[INIT_SIGMASK] = ss->blocked; - ints[INIT_SIGPENDING] = ss->pending; + ints[INIT_SIGPENDING] = _hurd_sigstate_pending (ss); ints[INIT_SIGIGN] = 0; for (i = 1; i < NSIG; ++i) - if (ss->actions[i].sa_handler == SIG_IGN) + if (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 @@ -123,7 +124,7 @@ _hurd_exec (task_t task, file_t file, critical section flag avoids anything we call trying to acquire the sigstate lock. */ - __spin_unlock (&ss->lock); + _hurd_sigstate_unlock (ss); /* Pack up the descriptor table to give the new program. */ __mutex_lock (&_hurd_dtable_lock); diff --git a/hurd/hurdinit.c b/hurd/hurdinit.c index 3b3619e68c..57397c0913 100644 --- a/hurd/hurdinit.c +++ b/hurd/hurdinit.c @@ -174,7 +174,7 @@ _hurd_new_proc_init (char **argv, /* This process is "traced", meaning it should stop on signals or exec. We are all set up now to handle signals. Stop ourselves, to inform our parent (presumably a debugger) that the exec has completed. */ - __msg_sig_post (_hurd_msgport, SIGTRAP, 0, __mach_task_self ()); + __msg_sig_post (_hurd_msgport, SIGTRAP, TRAP_TRACE, __mach_task_self ()); } #include <shlib-compat.h> diff --git a/hurd/hurdmsg.c b/hurd/hurdmsg.c index b4b7161977..ec0b01df90 100644 --- a/hurd/hurdmsg.c +++ b/hurd/hurdmsg.c @@ -121,17 +121,9 @@ get_int (int which, int *value) case INIT_UMASK: *value = _hurd_umask; return 0; - case INIT_SIGMASK: - { - struct hurd_sigstate *ss = _hurd_thread_sigstate (_hurd_sigthread); - __spin_lock (&ss->lock); - *value = ss->blocked; - __spin_unlock (&ss->lock); - return 0; - } case INIT_SIGPENDING: { - struct hurd_sigstate *ss = _hurd_thread_sigstate (_hurd_sigthread); + struct hurd_sigstate *ss = _hurd_global_sigstate; __spin_lock (&ss->lock); *value = ss->pending; __spin_unlock (&ss->lock); @@ -139,7 +131,7 @@ get_int (int which, int *value) } case INIT_SIGIGN: { - struct hurd_sigstate *ss = _hurd_thread_sigstate (_hurd_sigthread); + struct hurd_sigstate *ss = _hurd_global_sigstate; sigset_t ign; int sig; __spin_lock (&ss->lock); @@ -207,17 +199,9 @@ set_int (int which, int 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); + struct hurd_sigstate *ss = _hurd_global_sigstate; __spin_lock (&ss->lock); ss->pending = value; __spin_unlock (&ss->lock); @@ -225,7 +209,7 @@ set_int (int which, int value) } case INIT_SIGIGN: { - struct hurd_sigstate *ss = _hurd_thread_sigstate (_hurd_sigthread); + struct hurd_sigstate *ss = _hurd_global_sigstate; int sig; const sigset_t ign = value; __spin_lock (&ss->lock); diff --git a/hurd/hurdselect.c b/hurd/hurdselect.c index 4544cb753b..b5ebf35a40 100644 --- a/hurd/hurdselect.c +++ b/hurd/hurdselect.c @@ -16,14 +16,17 @@ License along with the GNU C Library; if not, see <http://www.gnu.org/licenses/>. */ +#include <sys/time.h> #include <sys/types.h> #include <sys/poll.h> #include <hurd.h> #include <hurd/fd.h> +#include <hurd/io_request.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <stdint.h> +#include <limits.h> /* All user select types. */ #define SELECT_ALL (SELECT_READ | SELECT_WRITE | SELECT_URG) @@ -31,6 +34,7 @@ /* Used to record that a particular select rpc returned. Must be distinct from SELECT_ALL (which better not have the high bit set). */ #define SELECT_RETURNED ((SELECT_ALL << 1) & ~SELECT_ALL) +#define SELECT_ERROR (SELECT_RETURNED << 1) /* Check the first NFDS descriptors either in POLLFDS (if nonnnull) or in each of READFDS, WRITEFDS, EXCEPTFDS that is nonnull. If TIMEOUT is not @@ -44,11 +48,13 @@ _hurd_select (int nfds, { int i; mach_port_t portset; - int got; + int got, ready; error_t err; fd_set rfds, wfds, xfds; int firstfd, lastfd; - mach_msg_timeout_t to = 0; + mach_msg_id_t reply_msgid; + mach_msg_timeout_t to; + struct timespec ts; struct { struct hurd_userlink ulink; @@ -56,6 +62,7 @@ _hurd_select (int nfds, mach_port_t io_port; int type; mach_port_t reply_port; + int error; } d[nfds]; sigset_t oset; @@ -67,22 +74,45 @@ _hurd_select (int nfds, assert (sizeof (union typeword) == sizeof (mach_msg_type_t)); assert (sizeof (uint32_t) == sizeof (mach_msg_type_t)); - if (nfds < 0 || nfds > FD_SETSIZE) + if (nfds < 0 || (!pollfds && nfds > FD_SETSIZE)) { errno = EINVAL; return -1; } - if (timeout != NULL) +#define IO_SELECT_REPLY_MSGID (21012 + 100) /* XXX */ +#define IO_SELECT_TIMEOUT_REPLY_MSGID (21031 + 100) /* XXX */ + + if (timeout == NULL) + reply_msgid = IO_SELECT_REPLY_MSGID; + else { - if (timeout->tv_sec < 0 || timeout->tv_nsec < 0) + struct timeval now; + + if (timeout->tv_sec < 0 || timeout->tv_nsec < 0 || + timeout->tv_nsec >= 1000000000) { errno = EINVAL; return -1; } - to = (timeout->tv_sec * 1000 + - (timeout->tv_nsec + 999999) / 1000000); + err = __gettimeofday(&now, NULL); + if (err) + return -1; + + ts.tv_sec = now.tv_sec + timeout->tv_sec; + ts.tv_nsec = now.tv_usec * 1000 + timeout->tv_nsec; + + if (ts.tv_nsec >= 1000000000) + { + ts.tv_sec++; + ts.tv_nsec -= 1000000000; + } + + if (ts.tv_sec < 0) + ts.tv_sec = LONG_MAX; /* XXX */ + + reply_msgid = IO_SELECT_TIMEOUT_REPLY_MSGID; } if (sigmask && __sigprocmask (SIG_SETMASK, sigmask, &oset)) @@ -128,25 +158,33 @@ _hurd_select (int nfds, 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; + /* Bogus descriptor, make it EBADF already. */ + d[i].error = EBADF; + d[i].type = SELECT_ERROR; + + /* And set timeout to 0. */ + { + struct timeval now; + err = __gettimeofday(&now, NULL); + if (err) + { + err = errno; + while (i-- > 0) + if (d[i].type & ~SELECT_ERROR != 0) + _hurd_port_free (&d[i].cell->port, &d[i].ulink, + d[i].io_port); + errno = err; + return -1; + } + ts.tv_sec = now.tv_sec; + ts.tv_nsec = now.tv_usec * 1000; + reply_msgid = IO_SELECT_TIMEOUT_REPLY_MSGID; + } } __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; } @@ -171,9 +209,6 @@ _hurd_select (int nfds, 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) @@ -188,9 +223,12 @@ _hurd_select (int nfds, 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 (i < _hurd_dtablesize) + { + d[i].cell = _hurd_dtable[i]; + d[i].io_port = _hurd_port_get (&d[i].cell->port, &d[i].ulink); + } + if (i >= _hurd_dtablesize || d[i].io_port == MACH_PORT_NULL) { /* If one descriptor is bogus, we fail completely. */ while (i-- > 0) @@ -215,6 +253,9 @@ _hurd_select (int nfds, errno = EBADF; return -1; } + + if (nfds > _hurd_dtablesize) + nfds = _hurd_dtablesize; } @@ -232,19 +273,17 @@ _hurd_select (int nfds, portset = MACH_PORT_NULL; for (i = firstfd; i <= lastfd; ++i) - if (d[i].type) + if (d[i].type & ~SELECT_ERROR) { 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) + if (timeout == NULL) + err = __io_select_request (d[i].io_port, d[i].reply_port, type); + else + err = __io_select_timeout_request (d[i].io_port, d[i].reply_port, + ts, type); + if (!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 @@ -265,32 +304,23 @@ _hurd_select (int nfds, __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; + } + else + { + /* No error should happen, but record it for later + processing. */ + d[i].error = err; + d[i].type |= SELECT_ERROR; ++got; - break; } _hurd_port_free (&d[i].cell->port, &d[i].ulink, d[i].io_port); } } + /* GOT is the number of replies (or errors), while READY is the number of + replies with at least one type bit set. */ + ready = 0; + /* Now wait for reply messages. */ if (!err && got == 0) { @@ -332,22 +362,35 @@ _hurd_select (int nfds, } success; #endif } msg; - mach_msg_option_t options = (timeout == NULL ? 0 : MACH_RCV_TIMEOUT); + mach_msg_option_t options; error_t msgerr; + + /* We rely on servers to implement the timeout, but when there are none, + do it on the client side. */ + if (timeout != NULL && firstfd == -1) + { + options = MACH_RCV_TIMEOUT; + to = timeout->tv_sec * 1000 + (timeout->tv_nsec + 999999) / 1000000; + } + else + { + options = 0; + to = MACH_MSG_TIMEOUT_NONE; + } + while ((msgerr = __mach_msg (&msg.head, - MACH_RCV_MSG | options, + 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 && + if (msg.head.msgh_id == reply_msgid && msg.head.msgh_size >= sizeof msg.error && !(msg.head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && #ifdef MACH_MSG_TYPE_BIT @@ -365,16 +408,18 @@ _hurd_select (int nfds, err = EINTR; goto poll; } + /* Keep in mind msg.success.result can be 0 if a timeout + occurred. */ 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) + msg.head.msgh_size != sizeof msg.success) { - /* Error or bogus reply. Simulate readiness. */ + /* Error or bogus reply. */ + if (!msg.error.err) + msg.error.err = EIO; __mach_msg_destroy (&msg.head); - msg.success.result = SELECT_ALL; } /* Look up the respondent's reply port and record its @@ -386,7 +431,19 @@ _hurd_select (int nfds, if (d[i].type && d[i].reply_port == msg.head.msgh_local_port) { - d[i].type &= msg.success.result; + if (msg.error.err) + { + d[i].error = msg.error.err; + d[i].type = SELECT_ERROR; + ++ready; + } + else + { + d[i].type &= msg.success.result; + if (d[i].type) + ++ready; + } + d[i].type |= SELECT_RETURNED; ++got; } @@ -407,15 +464,11 @@ _hurd_select (int nfds, } } - if (err == MACH_RCV_TIMED_OUT) - /* This is the normal value for ERR. We might have timed out and - read no messages. Otherwise, after receiving the first message, - we poll for more messages. We receive with a timeout of 0 to - effect a poll, so ERR is MACH_RCV_TIMED_OUT when the poll finds no - message waiting. */ - err = 0; + if (msgerr == MACH_RCV_INTERRUPTED) + /* Interruption on our side (e.g. signal reception). */ + err = EINTR; - if (got) + if (ready) /* At least one descriptor is known to be ready now, so we will return success. */ err = 0; @@ -423,7 +476,7 @@ _hurd_select (int nfds, if (firstfd != -1) for (i = firstfd; i <= lastfd; ++i) - if (d[i].type) + if (d[i].type & ~SELECT_ERROR) __mach_port_destroy (__mach_task_self (), d[i].reply_port); if (firstfd == -1 || (firstfd != lastfd && portset != MACH_PORT_NULL)) /* Destroy PORTSET, but only if it's not actually the reply port for a @@ -445,23 +498,37 @@ _hurd_select (int nfds, int type = d[i].type; int_fast16_t revents = 0; - if (type & SELECT_RETURNED) - { - if (type & SELECT_READ) - revents |= POLLIN; - if (type & SELECT_WRITE) - revents |= POLLOUT; - if (type & SELECT_URG) - revents |= POLLPRI; - } + if (type & SELECT_ERROR) + switch (d[i].error) + { + case EPIPE: + revents = POLLHUP; + break; + case EBADF: + revents = POLLNVAL; + break; + default: + revents = POLLERR; + break; + } + else + if (type & SELECT_RETURNED) + { + if (type & SELECT_READ) + revents |= POLLIN; + if (type & SELECT_WRITE) + revents |= POLLOUT; + if (type & SELECT_URG) + revents |= POLLPRI; + } pollfds[i].revents = revents; } else { - /* Below we recalculate GOT to include an increment for each operation + /* Below we recalculate READY to include an increment for each operation allowed on each fd. */ - got = 0; + ready = 0; /* Set the user bitarrays. We only ever have to clear bits, as all desired ones are initially set. */ @@ -473,16 +540,22 @@ _hurd_select (int nfds, if ((type & SELECT_RETURNED) == 0) type = 0; + /* 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. */ + if (type & SELECT_ERROR) + type = SELECT_ALL; + if (type & SELECT_READ) - got++; + ready++; else if (readfds) FD_CLR (i, readfds); if (type & SELECT_WRITE) - got++; + ready++; else if (writefds) FD_CLR (i, writefds); if (type & SELECT_URG) - got++; + ready++; else if (exceptfds) FD_CLR (i, exceptfds); } @@ -491,5 +564,5 @@ _hurd_select (int nfds, if (sigmask && __sigprocmask (SIG_SETMASK, &oset, NULL)) return -1; - return got; + return ready; } diff --git a/hurd/hurdsig.c b/hurd/hurdsig.c index 80eac9f998..a69b87df0b 100644 --- a/hurd/hurdsig.c +++ b/hurd/hurdsig.c @@ -43,9 +43,6 @@ mach_port_t _hurd_msgport; /* Thread listening on it. */ thread_t _hurd_msgport_thread; -/* Thread which receives task-global signals. */ -thread_t _hurd_sigthread; - /* These are set up by _hurdsig_init. */ unsigned long int __hurd_sigthread_stack_base; unsigned long int __hurd_sigthread_stack_end; @@ -53,6 +50,9 @@ unsigned long int __hurd_sigthread_stack_end; /* Linked-list of per-thread signal state. */ struct hurd_sigstate *_hurd_sigstates; +/* Sigstate for the task-global signals. */ +struct hurd_sigstate *_hurd_global_sigstate; + /* Timeout for RPC's after interrupt_operation. */ mach_msg_timeout_t _hurd_interrupted_rpc_timeout = 3000; @@ -81,7 +81,7 @@ _hurd_thread_sigstate (thread_t thread) { ss = malloc (sizeof (*ss)); if (ss == NULL) - __libc_fatal ("hurd: Can't allocate thread sigstate\n"); + __libc_fatal ("hurd: Can't allocate sigstate\n"); ss->thread = thread; __spin_lock_init (&ss->lock); @@ -94,16 +94,21 @@ _hurd_thread_sigstate (thread_t thread) ss->intr_port = MACH_PORT_NULL; ss->context = NULL; - /* Initialize the sigaction vector from the default signal receiving - thread's state, and its from the system defaults. */ - if (thread == _hurd_sigthread) - default_sigaction (ss->actions); + if (thread == MACH_PORT_NULL) + { + /* Process-wide sigstate, use the system defaults. */ + default_sigaction (ss->actions); + + /* The global sigstate is not added to the _hurd_sigstates list. + It is created with _hurd_thread_sigstate (MACH_PORT_NULL) + but should be accessed through _hurd_global_sigstate. */ + } else { - struct hurd_sigstate *s; - for (s = _hurd_sigstates; s != NULL; s = s->next) - if (s->thread == _hurd_sigthread) - break; + error_t err; + + /* Use the global actions as a default for new threads. */ + struct hurd_sigstate *s = _hurd_global_sigstate; if (s) { __spin_lock (&s->lock); @@ -112,14 +117,118 @@ _hurd_thread_sigstate (thread_t thread) } else default_sigaction (ss->actions); - } - ss->next = _hurd_sigstates; - _hurd_sigstates = ss; + ss->next = _hurd_sigstates; + _hurd_sigstates = ss; + + err = __mach_port_mod_refs (__mach_task_self (), thread, + MACH_PORT_RIGHT_SEND, 1); + if (err) + __libc_fatal ("hurd: Can't add reference on Mach thread\n"); + } } __mutex_unlock (&_hurd_siglock); return ss; } + +/* Destroy a sigstate structure. Called by libpthread just before the + * corresponding thread is terminated. */ +void +_hurd_sigstate_delete (thread_t thread) +{ + struct hurd_sigstate **ssp, *ss; + + __mutex_lock (&_hurd_siglock); + for (ssp = &_hurd_sigstates; *ssp; ssp = &(*ssp)->next) + if ((*ssp)->thread == thread) + break; + + ss = *ssp; + if (ss) + *ssp = ss->next; + + __mutex_unlock (&_hurd_siglock); + if (ss) + { + if (ss->thread != MACH_PORT_NULL) + __mach_port_deallocate (__mach_task_self (), ss->thread); + + free (ss); + } +} + +/* Make SS a global receiver, with pthread signal semantics. */ +void +_hurd_sigstate_set_global_rcv (struct hurd_sigstate *ss) +{ + assert (ss->thread != MACH_PORT_NULL); + ss->actions[0].sa_handler = SIG_IGN; +} + +/* Check whether SS is a global receiver. */ +static int +sigstate_is_global_rcv (const struct hurd_sigstate *ss) +{ + return (_hurd_global_sigstate != NULL) + && (ss->actions[0].sa_handler == SIG_IGN); +} + +/* Lock/unlock a hurd_sigstate structure. If the accessors below require + it, the global sigstate will be locked as well. */ +void +_hurd_sigstate_lock (struct hurd_sigstate *ss) +{ + if (sigstate_is_global_rcv (ss)) + __spin_lock (&_hurd_global_sigstate->lock); + __spin_lock (&ss->lock); +} +void +_hurd_sigstate_unlock (struct hurd_sigstate *ss) +{ + __spin_unlock (&ss->lock); + if (sigstate_is_global_rcv (ss)) + __spin_unlock (&_hurd_global_sigstate->lock); +} + +/* Retreive a thread's full set of pending signals, including the global + ones if appropriate. SS must be locked. */ +sigset_t +_hurd_sigstate_pending (const struct hurd_sigstate *ss) +{ + sigset_t pending = ss->pending; + if (sigstate_is_global_rcv (ss)) + __sigorset (&pending, &pending, &_hurd_global_sigstate->pending); + return pending; +} + +/* 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]; +} + +/* Retreive a thread's action vector. SS must be locked. */ +struct sigaction * +_hurd_sigstate_actions (struct hurd_sigstate *ss) +{ + if (sigstate_is_global_rcv (ss)) + return _hurd_global_sigstate->actions; + else + return ss->actions; +} + /* Signal delivery itself is on this page. */ @@ -214,6 +323,8 @@ static void abort_thread (struct hurd_sigstate *ss, struct machine_thread_all_state *state, void (*reply) (void)) { + assert (ss->thread != MACH_PORT_NULL); + if (!(state->set & THREAD_ABORTED)) { error_t err = __thread_abort (ss->thread); @@ -354,7 +465,7 @@ _hurdsig_abort_rpcs (struct hurd_sigstate *ss, int signo, int sigthread, call above will retry their RPCs unless we clear SS->intr_port. So we clear it for the thread taking a signal when SA_RESTART is clear, so that its call returns EINTR. */ - if (! signo || !(ss->actions[signo].sa_flags & SA_RESTART)) + if (! signo || !(_hurd_sigstate_actions (ss) [signo].sa_flags & SA_RESTART)) ss->intr_port = MACH_PORT_NULL; } @@ -442,6 +553,30 @@ abort_all_rpcs (int signo, struct machine_thread_all_state *state, int live) } } +/* Wake up any sigsuspend call that is blocking SS->thread. SS must be + locked. */ +static void +wake_sigsuspend (struct hurd_sigstate *ss) +{ + error_t err; + mach_msg_header_t msg; + + if (ss->suspended == MACH_PORT_NULL) + return; + + /* There is a sigsuspend waiting. Tell it to wake up. */ + msg.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_MAKE_SEND, 0); + msg.msgh_remote_port = ss->suspended; + msg.msgh_local_port = MACH_PORT_NULL; + /* These values do not matter. */ + msg.msgh_id = 8675309; /* Jenny, Jenny. */ + ss->suspended = MACH_PORT_NULL; + err = __mach_msg (&msg, MACH_SEND_MSG, sizeof msg, 0, + MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + assert_perror (err); +} + struct hurd_signal_preemptor *_hurdsig_preemptors = 0; sigset_t _hurdsig_preempted_set; @@ -452,35 +587,20 @@ weak_alias (_hurdsig_preemptors, _hurdsig_preempters) #define STOPSIGS (sigmask (SIGTTIN) | sigmask (SIGTTOU) | \ sigmask (SIGSTOP) | sigmask (SIGTSTP)) -/* Deliver a signal. SS is not locked. */ -void -_hurd_internal_post_signal (struct hurd_sigstate *ss, - int signo, struct hurd_signal_detail *detail, - mach_port_t reply_port, - mach_msg_type_name_t reply_port_type, - int untraced) +/* Actual delivery of a single signal. Called with SS unlocked. When + the signal is delivered, return SS, locked (or, if SS was originally + _hurd_global_sigstate, the sigstate of the actual thread the signal + was delivered to). If the signal is being traced, return NULL with + SS unlocked. */ +static struct hurd_sigstate * +post_signal (struct hurd_sigstate *ss, + int signo, struct hurd_signal_detail *detail, + int untraced, void (*reply) (void)) { - error_t err; struct machine_thread_all_state thread_state; enum { stop, ignore, core, term, handle } act; - sighandler_t handler; - sigset_t pending; int ss_suspended; - /* Reply to this sig_post message. */ - __typeof (__msg_sig_post_reply) *reply_rpc - = (untraced ? __msg_sig_post_untraced_reply : __msg_sig_post_reply); - void reply (void) - { - error_t err; - if (reply_port == MACH_PORT_NULL) - return; - err = (*reply_rpc) (reply_port, reply_port_type, 0); - reply_port = MACH_PORT_NULL; - if (err != MACH_SEND_INVALID_DEST) /* Ignore dead reply port. */ - assert_perror (err); - } - /* Mark the signal as pending. */ void mark_pending (void) { @@ -525,8 +645,12 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss, assert_perror (err); for (i = 0; i < nthreads; ++i) { - if (threads[i] != _hurd_msgport_thread && - (act != handle || threads[i] != ss->thread)) + if (act == handle && threads[i] == ss->thread) + { + /* The thread that will run the handler is kept suspended. */ + ss_suspended = 1; + } + else if (threads[i] != _hurd_msgport_thread) { err = __thread_resume (threads[i]); assert_perror (err); @@ -539,27 +663,58 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss, (vm_address_t) threads, nthreads * sizeof *threads); _hurd_stopped = 0; - if (act == handle) - /* The thread that will run the handler is already suspended. */ - ss_suspended = 1; } + error_t err; + sighandler_t handler; + if (signo == 0) { if (untraced) - /* This is PTRACE_CONTINUE. */ - resume (); + { + /* This is PTRACE_CONTINUE. */ + act = ignore; + resume (); + } /* This call is just to check for pending signals. */ - __spin_lock (&ss->lock); - goto check_pending_signals; + _hurd_sigstate_lock (ss); + return ss; } - post_signal: - thread_state.set = 0; /* We know nothing. */ - __spin_lock (&ss->lock); + _hurd_sigstate_lock (ss); + + /* If this is a global signal, try to find a thread ready to accept + it right away. This is especially important for untraced signals, + since going through the global pending mask would de-untrace them. */ + if (ss->thread == MACH_PORT_NULL) + { + struct hurd_sigstate *rss; + + __mutex_lock (&_hurd_siglock); + for (rss = _hurd_sigstates; rss != NULL; rss = rss->next) + { + if (! sigstate_is_global_rcv (rss)) + continue; + + /* The global sigstate is already locked. */ + __spin_lock (&rss->lock); + if (! __sigismember (&rss->blocked, signo)) + { + ss = rss; + break; + } + __spin_unlock (&rss->lock); + } + __mutex_unlock (&_hurd_siglock); + } + + /* We want the preemptors to be able to update the blocking mask + without affecting the delivery of this signal, so we save the + current value to test against later. */ + sigset_t blocked = ss->blocked; /* Check for a preempted signal. Preempted signals can arrive during critical sections. */ @@ -617,12 +772,12 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss, mark_pending (); else suspend (); - __spin_unlock (&ss->lock); + _hurd_sigstate_unlock (ss); reply (); - return; + return NULL; } - handler = ss->actions[signo].sa_handler; + handler = _hurd_sigstate_actions (ss) [signo].sa_handler; if (handler == SIG_DFL) /* Figure out the default action for this signal. */ @@ -715,9 +870,7 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss, } /* Handle receipt of a blocked signal, or any signal while stopped. */ - if (act != ignore && /* Signals ignored now are forgotten now. */ - __sigismember (&ss->blocked, signo) || - (signo != SIGKILL && _hurd_stopped)) + if (__sigismember (&blocked, signo) || (signo != SIGKILL && _hurd_stopped)) { mark_pending (); act = ignore; @@ -752,6 +905,7 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss, now's the time to set it going. */ if (ss_suspended) { + assert (ss->thread != MACH_PORT_NULL); err = __thread_resume (ss->thread); assert_perror (err); ss_suspended = 0; @@ -796,6 +950,8 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss, struct sigcontext *scp, ocontext; int wait_for_reply, state_changed; + assert (ss->thread != MACH_PORT_NULL); + /* Stop the thread and abort its pending RPC operations. */ if (! ss_suspended) { @@ -863,7 +1019,7 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss, as a unit. */ crit ? 0 : signo, 1, &thread_state, &state_changed, - &reply) + reply) != MACH_PORT_NULL); if (crit) @@ -932,23 +1088,28 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss, } } + struct sigaction *action = & _hurd_sigstate_actions (ss) [signo]; + /* Backdoor extra argument to signal handler. */ scp->sc_error = detail->error; /* Block requested signals while running the handler. */ scp->sc_mask = ss->blocked; - __sigorset (&ss->blocked, &ss->blocked, &ss->actions[signo].sa_mask); + __sigorset (&ss->blocked, &ss->blocked, &action->sa_mask); /* Also block SIGNO unless we're asked not to. */ - if (! (ss->actions[signo].sa_flags & (SA_RESETHAND | SA_NODEFER))) + if (! (action->sa_flags & (SA_RESETHAND | SA_NODEFER))) __sigaddset (&ss->blocked, signo); /* Reset to SIG_DFL if requested. SIGILL and SIGTRAP cannot be automatically reset when delivered; the system silently enforces this restriction. */ - if (ss->actions[signo].sa_flags & SA_RESETHAND + if (action->sa_flags & SA_RESETHAND && signo != SIGILL && signo != SIGTRAP) - ss->actions[signo].sa_handler = SIG_DFL; + action->sa_handler = SIG_DFL; + + /* Any sigsuspend call must return after the handler does. */ + wake_sigsuspend (ss); /* Start the thread running the handler (or possibly waiting for an RPC reply before running the handler). */ @@ -963,95 +1124,134 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss, } } - /* The signal has either been ignored or is now being handled. We can - consider it delivered and reply to the killer. */ - reply (); + return ss; +} + +/* Return the set of pending signals in SS which should be delivered. */ +static sigset_t +pending_signals (struct hurd_sigstate *ss) +{ + /* We don't worry about any pending signals if we are stopped, nor if + SS is in a critical section. We are guaranteed to get a sig_post + message before any of them become deliverable: either the SIGCONT + signal, or a sig_post with SIGNO==0 as an explicit poll when the + thread finishes its critical section. */ + if (_hurd_stopped || __spin_lock_locked (&ss->critical_section_lock)) + return 0; - /* We get here unless the signal was fatal. We still hold SS->lock. - Check for pending signals, and loop to post them. */ - { - /* Return nonzero if SS has any signals pending we should worry about. - We don't worry about any pending signals if we are stopped, nor if - SS is in a critical section. We are guaranteed to get a sig_post - message before any of them become deliverable: either the SIGCONT - signal, or a sig_post with SIGNO==0 as an explicit poll when the - thread finishes its critical section. */ - inline int signals_pending (void) + return _hurd_sigstate_pending (ss) & ~ss->blocked; +} + +/* Post the specified pending signals in SS and return 1. If one of + them is traced, abort immediately and return 0. SS must be locked on + entry and will be unlocked in all cases. */ +static int +post_pending (struct hurd_sigstate *ss, sigset_t pending, void (*reply) (void)) +{ + int signo; + struct hurd_signal_detail detail; + + /* Make sure SS corresponds to an actual thread, since we assume it won't + change in post_signal. */ + assert (ss->thread != MACH_PORT_NULL); + + for (signo = 1; signo < NSIG; ++signo) + if (__sigismember (&pending, signo)) { - if (_hurd_stopped || __spin_lock_locked (&ss->critical_section_lock)) + detail = sigstate_clear_pending (ss, signo); + _hurd_sigstate_unlock (ss); + + /* Will reacquire the lock, except if the signal is traced. */ + if (! post_signal (ss, signo, &detail, 0, reply)) return 0; - return pending = ss->pending & ~ss->blocked; } - check_pending_signals: - untraced = 0; + /* No more signals pending; SS->lock is still locked. */ + _hurd_sigstate_unlock (ss); - if (signals_pending ()) - { - for (signo = 1; signo < NSIG; ++signo) - if (__sigismember (&pending, signo)) - { - deliver_pending: - __sigdelset (&ss->pending, signo); - *detail = ss->pending_data[signo]; - __spin_unlock (&ss->lock); - goto post_signal; - } - } + return 1; +} - /* No pending signals left undelivered for this thread. - If we were sent signal 0, we need to check for pending - signals for all threads. */ - if (signo == 0) - { - __spin_unlock (&ss->lock); - __mutex_lock (&_hurd_siglock); - for (ss = _hurd_sigstates; ss != NULL; ss = ss->next) - { - __spin_lock (&ss->lock); - for (signo = 1; signo < NSIG; ++signo) - if (__sigismember (&ss->pending, signo) - && (!__sigismember (&ss->blocked, signo) - /* We "deliver" immediately pending blocked signals whose - action might be to ignore, so that if ignored they are - dropped right away. */ - || ss->actions[signo].sa_handler == SIG_IGN - || ss->actions[signo].sa_handler == SIG_DFL)) - { - mutex_unlock (&_hurd_siglock); - goto deliver_pending; - } - __spin_unlock (&ss->lock); - } - __mutex_unlock (&_hurd_siglock); - } - else - { - /* No more signals pending; SS->lock is still locked. - Wake up any sigsuspend call that is blocking SS->thread. */ - if (ss->suspended != MACH_PORT_NULL) - { - /* There is a sigsuspend waiting. Tell it to wake up. */ - error_t err; - mach_msg_header_t msg; - msg.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_MAKE_SEND, 0); - msg.msgh_remote_port = ss->suspended; - msg.msgh_local_port = MACH_PORT_NULL; - /* These values do not matter. */ - msg.msgh_id = 8675309; /* Jenny, Jenny. */ - ss->suspended = MACH_PORT_NULL; - err = __mach_msg (&msg, MACH_SEND_MSG, sizeof msg, 0, - MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, - MACH_PORT_NULL); - assert_perror (err); - } - __spin_unlock (&ss->lock); - } - } +/* Post all the pending signals of all threads and return 1. If a traced + signal is encountered, abort immediately and return 0. */ +static int +post_all_pending_signals (void (*reply) (void)) +{ + struct hurd_sigstate *ss; + sigset_t pending; - /* All pending signals delivered to all threads. - Now we can send the reply message even for signal 0. */ - reply (); + for (;;) + { + __mutex_lock (&_hurd_siglock); + for (ss = _hurd_sigstates; ss != NULL; ss = ss->next) + { + _hurd_sigstate_lock (ss); + + pending = pending_signals (ss); + if (pending) + /* post_pending() below will unlock SS. */ + break; + + _hurd_sigstate_unlock (ss); + } + __mutex_unlock (&_hurd_siglock); + + if (! pending) + return 1; + if (! post_pending (ss, pending, reply)) + return 0; + } +} + +/* Deliver a signal. SS is not locked. */ +void +_hurd_internal_post_signal (struct hurd_sigstate *ss, + int signo, struct hurd_signal_detail *detail, + mach_port_t reply_port, + mach_msg_type_name_t reply_port_type, + int untraced) +{ + /* Reply to this sig_post message. */ + __typeof (__msg_sig_post_reply) *reply_rpc + = (untraced ? __msg_sig_post_untraced_reply : __msg_sig_post_reply); + void reply (void) + { + error_t err; + if (reply_port == MACH_PORT_NULL) + return; + err = (*reply_rpc) (reply_port, reply_port_type, 0); + reply_port = MACH_PORT_NULL; + if (err != MACH_SEND_INVALID_DEST) /* Ignore dead reply port. */ + assert_perror (err); + } + + ss = post_signal (ss, signo, detail, untraced, reply); + if (! ss) + return; + + /* The signal was neither fatal nor traced. We still hold SS->lock. */ + if (signo != 0 && ss->thread != MACH_PORT_NULL) + { + /* The signal has either been ignored or is now being handled. We can + consider it delivered and reply to the killer. */ + reply (); + + /* Post any pending signals for this thread. */ + if (! post_pending (ss, pending_signals (ss), reply)) + return; + } + else + { + /* If this was a process-wide signal or a poll request, we need + to check for pending signals for all threads. */ + _hurd_sigstate_unlock (ss); + if (! post_all_pending_signals (reply)) + return; + + /* All pending signals delivered to all threads. + Now we can send the reply message even for signal 0. */ + reply (); + } } /* Decide whether REFPORT enables the sender to send us a SIGNO signal. @@ -1170,9 +1370,10 @@ _S_msg_sig_post (mach_port_t me, d.code = sigcode; d.exc = 0; - /* Post the signal to the designated signal-receiving thread. This will - reply when the signal can be considered delivered. */ - _hurd_internal_post_signal (_hurd_thread_sigstate (_hurd_sigthread), + /* Post the signal to a global receiver thread (or mark it pending in + the global sigstate). This will reply when the signal can be + considered delivered. */ + _hurd_internal_post_signal (_hurd_global_sigstate, signo, &d, reply_port, reply_port_type, 0); /* Stop if traced. */ @@ -1200,7 +1401,7 @@ _S_msg_sig_post_untraced (mach_port_t me, /* Post the signal to the designated signal-receiving thread. This will reply when the signal can be considered delivered. */ - _hurd_internal_post_signal (_hurd_thread_sigstate (_hurd_sigthread), + _hurd_internal_post_signal (_hurd_global_sigstate, signo, &d, reply_port, reply_port_type, 1); /* Untraced flag. */ @@ -1211,8 +1412,8 @@ extern void __mig_init (void *); #include <mach/task_special_ports.h> -/* Initialize the message port and _hurd_sigthread and start the signal - thread. */ +/* Initialize the message port, _hurd_global_sigstate, and start the + signal thread. */ void _hurdsig_init (const int *intarray, size_t intarraysize) @@ -1235,27 +1436,34 @@ _hurdsig_init (const int *intarray, size_t intarraysize) MACH_MSG_TYPE_MAKE_SEND); assert_perror (err); + /* Initialize the global signal state. */ + _hurd_global_sigstate = _hurd_thread_sigstate (MACH_PORT_NULL); + + /* We block all signals, and let actual threads pull them from the + pending mask. */ + __sigfillset(& _hurd_global_sigstate->blocked); + /* Initialize the main thread's signal state. */ ss = _hurd_self_sigstate (); - /* Copy inherited values from our parent (or pre-exec process state) - into the signal settings of the main thread. */ + /* Mark it as a process-wide signal receiver. Threads in this set use + the common action vector in _hurd_global_sigstate. */ + _hurd_sigstate_set_global_rcv (ss); + + /* Copy inherited signal settings from our parent (or pre-exec process + state) */ if (intarraysize > INIT_SIGMASK) ss->blocked = intarray[INIT_SIGMASK]; if (intarraysize > INIT_SIGPENDING) - ss->pending = intarray[INIT_SIGPENDING]; + _hurd_global_sigstate->pending = intarray[INIT_SIGPENDING]; if (intarraysize > INIT_SIGIGN && intarray[INIT_SIGIGN] != 0) { int signo; for (signo = 1; signo < NSIG; ++signo) if (intarray[INIT_SIGIGN] & __sigmask(signo)) - ss->actions[signo].sa_handler = SIG_IGN; + _hurd_global_sigstate->actions[signo].sa_handler = SIG_IGN; } - /* Set the default thread to receive task-global signals - to this one, the main (first) user thread. */ - _hurd_sigthread = ss->thread; - /* Start the signal thread listening on the message port. */ #pragma weak cthread_fork diff --git a/hurd/hurdsocket.h b/hurd/hurdsocket.h new file mode 100644 index 0000000000..e12298e7e6 --- /dev/null +++ b/hurd/hurdsocket.h @@ -0,0 +1,22 @@ +/* Hurd-specific socket functions + Copyright (C) 2013 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <string.h> + +#define _hurd_sun_path_dupa(__addr, __len) \ + strndupa ((__addr)->sun_path, (__len) - offsetof (struct sockaddr_un, sun_path)) diff --git a/hurd/sysvshm.c b/hurd/sysvshm.c new file mode 100644 index 0000000000..292cc01db4 --- /dev/null +++ b/hurd/sysvshm.c @@ -0,0 +1,96 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <stddef.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <dirent.h> +#include <sys/stat.h> +#include <sys/shm.h> + + +/* Description of an shm attachment. */ +struct sysvshm_attach +{ + /* Linked list. */ + struct sysvshm_attach *next; + + /* Map address. */ + void *addr; + + /* Map size. */ + size_t size; +}; + +/* List of attachments. */ +static struct sysvshm_attach *attach_list; + +/* A lock to protect the linked list of shared memory attachments. */ +static struct mutex sysvshm_lock = MUTEX_INITIALIZER; + + +/* Adds a segment attachment. */ +error_t +__sysvshm_add (void *addr, size_t size) +{ + struct sysvshm_attach *shm; + + shm = malloc (sizeof (*shm)); + if (!shm) + return errno; + + __mutex_lock (&sysvshm_lock); + shm->addr = addr; + shm->size = size; + shm->next = attach_list; + attach_list = shm; + __mutex_unlock (&sysvshm_lock); + + return 0; +} + +/* Removes a segment attachment. Returns its size if found, or EINVAL + otherwise. */ +error_t +__sysvshm_remove (void *addr, size_t *size) +{ + struct sysvshm_attach *shm; + struct sysvshm_attach **pshm = &attach_list; + + __mutex_lock (&sysvshm_lock); + shm = attach_list; + while (shm) + { + shm = *pshm; + if (shm->addr == addr) + { + *pshm = shm->next; + *size = shm->size; + __mutex_unlock (&sysvshm_lock); + return 0; + } + pshm = &shm->next; + shm = shm->next; + } + __mutex_unlock (&sysvshm_lock); + return EINVAL; +} diff --git a/hurd/sysvshm.h b/hurd/sysvshm.h new file mode 100644 index 0000000000..0c561c8548 --- /dev/null +++ b/hurd/sysvshm.h @@ -0,0 +1,47 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <paths.h> +#include <hurd.h> + +/* The area (from top to bottom) that is used for private keys. These + are all keys that have the second highest bit set. */ +#define SHM_PRIV_KEY_START INT_MAX +#define SHM_PRIV_KEY_END ((INT_MAX / 2) + 1) + +#define SHM_PREFIX "shm-" +#define SHM_DIR _PATH_DEV "shm/" + +/* The maximum number of characters in a shared memory segment file name. + 32 is the max number of characters in a 128 bit number in hex. */ +#if __WORDSIZE > 128 +#error Need to increase SHM_NAMEMAX. +#else +#define SHM_NAMEMAX (sizeof (SHM_PREFIX) - 1 + 32 + 1) +#endif + +/* Use this with printf and its variants. */ +#define SHM_NAMEPRI SHM_PREFIX "%0x" + + +/* Adds a segment attachment. */ +error_t __sysvshm_add (void *addr, size_t size); + +/* Removes a segment attachment. Returns its size if found, or EINVAL + otherwise. */ +error_t __sysvshm_remove (void *addr, size_t *size); diff --git a/hurd/thread-cancel.c b/hurd/thread-cancel.c index c7f88eec9b..2e8d91e7ba 100644 --- a/hurd/thread-cancel.c +++ b/hurd/thread-cancel.c @@ -51,7 +51,6 @@ hurd_thread_cancel (thread_t thread) return 0; } - assert (! __spin_lock_locked (&ss->critical_section_lock)); __spin_lock (&ss->critical_section_lock); __spin_lock (&ss->lock); err = __thread_suspend (thread); |