summaryrefslogtreecommitdiff
path: root/hurd
diff options
context:
space:
mode:
Diffstat (limited to 'hurd')
-rw-r--r--hurd/Makefile5
-rw-r--r--hurd/Versions8
-rw-r--r--hurd/ctty-input.c16
-rw-r--r--hurd/ctty-output.c16
-rw-r--r--hurd/dtable.c2
-rw-r--r--hurd/exc2signal.c33
-rw-r--r--hurd/hurd/signal.h69
-rw-r--r--hurd/hurd/sigpreempt.h4
-rw-r--r--hurd/hurd/threadvar.h15
-rw-r--r--hurd/hurdauth.c1
-rw-r--r--hurd/hurdexec.c11
-rw-r--r--hurd/hurdfault.c2
-rw-r--r--hurd/hurdfchdir.c1
-rw-r--r--hurd/hurdinit.c2
-rw-r--r--hurd/hurdioctl.c1
-rw-r--r--hurd/hurdmsg.c24
-rw-r--r--hurd/hurdselect.c262
-rw-r--r--hurd/hurdsig.c477
-rw-r--r--hurd/hurdsock.c5
-rw-r--r--hurd/intern-fd.c1
-rw-r--r--hurd/lookup-retry.c78
-rw-r--r--hurd/setauth.c1
-rw-r--r--hurd/seteuids.c1
-rw-r--r--hurd/thread-cancel.c3
24 files changed, 753 insertions, 285 deletions
diff --git a/hurd/Makefile b/hurd/Makefile
index 1bd6b85573..0063ed4eb3 100644
--- a/hurd/Makefile
+++ b/hurd/Makefile
@@ -29,11 +29,12 @@ inline-headers = hurd.h $(addprefix hurd/,fd.h signal.h \
# The RPC interfaces go in a separate library.
interface-library := libhurduser
user-interfaces := $(addprefix hurd/,\
- auth startup \
+ auth auth_request auth_reply startup \
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
diff --git a/hurd/Versions b/hurd/Versions
index af6a0e45de..b8d52b3d54 100644
--- a/hurd/Versions
+++ b/hurd/Versions
@@ -123,6 +123,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;
+ }
GLIBC_2.26 {
# "quasi-internal" functions
_hurd_exec_paths;
diff --git a/hurd/ctty-input.c b/hurd/ctty-input.c
index ec4092b855..de4cb950e8 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 12fdf78159..dc9bba8af1 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/dtable.c b/hurd/dtable.c
index 5c204676f8..c36d60fc86 100644
--- a/hurd/dtable.c
+++ b/hurd/dtable.c
@@ -235,6 +235,7 @@ ctty_new_pgrp (void)
__mutex_unlock (&_hurd_dtable_lock);
HURD_CRITICAL_END;
+ /* TODO: handle EINTR! */
(void) &ctty_new_pgrp; /* Avoid "defined but not used" warning. */
}
@@ -297,6 +298,7 @@ reauth_dtable (void)
__mutex_unlock (&_hurd_dtable_lock);
HURD_CRITICAL_END;
+ /* TODO: handle EINTR! */
(void) &reauth_dtable; /* Avoid "defined but not used" warning. */
}
diff --git a/hurd/exc2signal.c b/hurd/exc2signal.c
index 10aa1ca4cf..0ecc5eb178 100644
--- a/hurd/exc2signal.c
+++ b/hurd/exc2signal.c
@@ -25,8 +25,8 @@
/* Translate the Mach exception codes, as received in an `exception_raise' RPC,
into a signal number and signal subcode. */
-void
-_hurd_exception2signal (struct hurd_signal_detail *detail, int *signo)
+static void
+exception2signal (struct hurd_signal_detail *detail, int *signo, int posix)
{
detail->error = 0;
@@ -38,11 +38,18 @@ _hurd_exception2signal (struct hurd_signal_detail *detail, int *signo)
break;
case EXC_BAD_ACCESS:
- if (detail->exc_code == KERN_PROTECTION_FAILURE)
- *signo = SIGSEGV;
- else
- *signo = SIGBUS;
- detail->code = detail->exc_subcode;
+ switch (detail->exc_code)
+ {
+ case KERN_PROTECTION_FAILURE:
+ *signo = SIGSEGV;
+ detail->code = posix ? SEGV_ACCERR : detail->exc_subcode;
+ break;
+
+ default:
+ *signo = SIGBUS;
+ detail->code = 0;
+ break;
+ }
detail->error = detail->exc_code;
break;
@@ -68,4 +75,16 @@ _hurd_exception2signal (struct hurd_signal_detail *detail, int *signo)
break;
}
}
+
+void
+_hurd_exception2signal (struct hurd_signal_detail *detail, int *signo)
+{
+ exception2signal (detail, signo, 1);
+}
libc_hidden_def (_hurd_exception2signal)
+
+void
+_hurd_exception2signal_legacy (struct hurd_signal_detail *detail, int *signo)
+{
+ exception2signal (detail, signo, 0);
+}
diff --git a/hurd/hurd/signal.h b/hurd/hurd/signal.h
index f84c4ef485..3bb4c2e886 100644
--- a/hurd/hurd/signal.h
+++ b/hurd/hurd/signal.h
@@ -68,12 +68,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];
+
stack_t sigaltstack;
/* Chain of thread-local signal preemptors; see <hurd/sigpreempt.h>.
@@ -116,7 +124,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);
@@ -129,6 +139,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
@@ -138,9 +168,14 @@ extern struct hurd_sigstate *_hurd_self_sigstate (void)
_HURD_SIGNAL_H_EXTERN_INLINE struct hurd_sigstate *
_hurd_self_sigstate (void)
{
- if (THREAD_SELF->_hurd_sigstate == NULL)
- THREAD_SELF->_hurd_sigstate = _hurd_thread_sigstate (__mach_thread_self ());
- return THREAD_SELF->_hurd_sigstate;
+ struct hurd_sigstate **location = &THREAD_SELF->_hurd_sigstate;
+ if (*location == NULL)
+ {
+ thread_t self = __mach_thread_self ();
+ *location = _hurd_thread_sigstate (self);
+ __mach_port_deallocate (__mach_task_self (), self);
+ }
+ return *location;
}
# endif
#endif
@@ -154,12 +189,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;
@@ -177,6 +206,7 @@ extern void *_hurd_critical_section_lock (void);
_HURD_SIGNAL_H_EXTERN_INLINE void *
_hurd_critical_section_lock (void)
{
+ struct hurd_sigstate **location;
struct hurd_sigstate *ss;
#ifdef __LIBC_NO_TLS
@@ -185,14 +215,18 @@ _hurd_critical_section_lock (void)
return NULL;
#endif
- ss = THREAD_SELF->_hurd_sigstate;
+ location = &THREAD_SELF->_hurd_sigstate;
+ 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
way. */
- ss = THREAD_SELF->_hurd_sigstate = _hurd_thread_sigstate (__mach_thread_self ());
+ ss = *location = _hurd_thread_sigstate (self);
+ __mach_port_deallocate (__mach_task_self (), self);
}
if (! __spin_try_lock (&ss->critical_section_lock))
@@ -222,10 +256,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.
@@ -265,6 +299,11 @@ extern int _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/hurd/sigpreempt.h b/hurd/hurd/sigpreempt.h
index a1df82e2c6..51b422c512 100644
--- a/hurd/hurd/sigpreempt.h
+++ b/hurd/hurd/sigpreempt.h
@@ -50,9 +50,9 @@ struct hurd_signal_preemptor
struct hurd_signal_preemptor *next; /* List structure. */
};
-#define HURD_PREEMPT_SIGNAL_P(preemptor, signo, sigcode) \
+#define HURD_PREEMPT_SIGNAL_P(preemptor, signo, address) \
(((preemptor)->signals & sigmask (signo)) && \
- (sigcode) >= (preemptor)->first && (sigcode) <= (preemptor)->last)
+ (address) >= (preemptor)->first && (address) <= (preemptor)->last)
/* Signal preemptors applying to all threads; locked by _hurd_siglock. */
diff --git a/hurd/hurd/threadvar.h b/hurd/hurd/threadvar.h
index 414e452db5..c61e3bb9d9 100644
--- a/hurd/hurd/threadvar.h
+++ b/hurd/hurd/threadvar.h
@@ -22,21 +22,6 @@
#include <features.h>
#include <tls.h>
-/* The per-thread variables are found by ANDing this mask
- with the value of the stack pointer and then adding this offset.
-
- In the multi-threaded case, cthreads initialization sets
- __hurd_threadvar_stack_mask to ~(cthread_stack_size - 1), a mask which
- finds the base of the fixed-size cthreads stack; and
- __hurd_threadvar_stack_offset to a small offset that skips the data
- cthreads itself maintains at the base of each thread's stack.
-
- In the single-threaded or libpthread case, __hurd_threadvar_stack_mask is
- zero, so the stack pointer is ignored. */
-
-extern unsigned long int __hurd_threadvar_stack_mask;
-extern unsigned long int __hurd_threadvar_stack_offset;
-
/* The variables __hurd_sigthread_stack_base and
__hurd_sigthread_stack_end define the bounds of the stack used by the
signal thread, so that thread can always be specifically identified. */
diff --git a/hurd/hurdauth.c b/hurd/hurdauth.c
index 1a7e67ea0e..c61af64a53 100644
--- a/hurd/hurdauth.c
+++ b/hurd/hurdauth.c
@@ -223,6 +223,7 @@ _S_msg_del_auth (mach_port_t me,
}
__mutex_unlock (&_hurd_id.lock);
HURD_CRITICAL_END;
+ /* TODO: handle EINTR */
if (err)
return err;
diff --git a/hurd/hurdexec.c b/hurd/hurdexec.c
index 732c9ec34b..99f0488e0c 100644
--- a/hurd/hurdexec.c
+++ b/hurd/hurdexec.c
@@ -123,15 +123,15 @@ _hurd_exec_paths (task_t task, file_t file,
ss = _hurd_self_sigstate ();
- 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
@@ -142,7 +142,7 @@ _hurd_exec_paths (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);
@@ -428,6 +428,7 @@ _hurd_exec_paths (task_t task, file_t file,
/* Safe to let signals happen now. */
_hurd_critical_section_unlock (ss);
+ /* FIXME: handle EINTR */
outargs:
free (args);
diff --git a/hurd/hurdfault.c b/hurd/hurdfault.c
index 39a4522811..e9a9a4f9cc 100644
--- a/hurd/hurdfault.c
+++ b/hurd/hurdfault.c
@@ -70,7 +70,7 @@ _hurdsig_fault_catch_exception_raise (mach_port_t port,
codes into a signal number and subcode. */
_hurd_exception2signal (&d, &signo);
- return HURD_PREEMPT_SIGNAL_P (&_hurdsig_fault_preemptor, signo, d.code)
+ return HURD_PREEMPT_SIGNAL_P (&_hurdsig_fault_preemptor, signo, d.exc_subcode)
? 0 : EGREGIOUS;
}
diff --git a/hurd/hurdfchdir.c b/hurd/hurdfchdir.c
index cde753cc68..3759e3c734 100644
--- a/hurd/hurdfchdir.c
+++ b/hurd/hurdfchdir.c
@@ -53,6 +53,7 @@ _hurd_change_directory_port_from_fd (struct hurd_port *portcell, int fd)
}));
HURD_CRITICAL_END;
+ /* TODO:handle EINTR */
return ret;
}
diff --git a/hurd/hurdinit.c b/hurd/hurdinit.c
index 6af1ddf610..9d654f0d1e 100644
--- a/hurd/hurdinit.c
+++ b/hurd/hurdinit.c
@@ -175,7 +175,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/hurdioctl.c b/hurd/hurdioctl.c
index 5b05b3bfff..c40fa9ea2b 100644
--- a/hurd/hurdioctl.c
+++ b/hurd/hurdioctl.c
@@ -211,6 +211,7 @@ install_ctty (mach_port_t cttyid)
__mutex_lock (&_hurd_dtable_lock);
_hurd_locked_install_cttyid (cttyid);
HURD_CRITICAL_END;
+ /* TODO: handle EINTR! */
}
diff --git a/hurd/hurdmsg.c b/hurd/hurdmsg.c
index 63e5e81b87..0a73b54574 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 74f176d79d..15cc8fee21 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;
@@ -73,16 +80,39 @@ _hurd_select (int nfds,
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))
@@ -90,6 +120,7 @@ _hurd_select (int nfds,
if (pollfds)
{
+ int error = 0;
/* 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,
@@ -123,28 +154,47 @@ _hurd_select (int nfds,
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 (d[i].cell != NULL)
+ {
+ 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;
+ /* Bogus descriptor, make it EBADF already. */
+ d[i].error = EBADF;
+ d[i].type = SELECT_ERROR;
+ error = 1;
}
__mutex_unlock (&_hurd_dtable_lock);
HURD_CRITICAL_END;
- if (i < nfds)
+ if (error)
{
- if (sigmask)
- __sigprocmask (SIG_SETMASK, &oset, NULL);
- errno = EBADF;
- return -1;
+ /* Set timeout to 0. */
+ struct timeval now;
+ err = __gettimeofday(&now, NULL);
+ if (err)
+ {
+ /* Really bad luck. */
+ err = errno;
+ HURD_CRITICAL_BEGIN;
+ __mutex_lock (&_hurd_dtable_lock);
+ while (i-- > 0)
+ if (d[i].type & ~SELECT_ERROR != 0)
+ _hurd_port_free (&d[i].cell->port, &d[i].ulink,
+ d[i].io_port);
+ __mutex_unlock (&_hurd_dtable_lock);
+ HURD_CRITICAL_END;
+ errno = err;
+ return -1;
+ }
+ ts.tv_sec = now.tv_sec;
+ ts.tv_nsec = now.tv_usec * 1000;
+ reply_msgid = IO_SELECT_TIMEOUT_REPLY_MSGID;
}
lastfd = i - 1;
@@ -171,9 +221,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 +235,15 @@ _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];
+ if (d[i].cell != NULL)
+ d[i].io_port = _hurd_port_get (&d[i].cell->port,
+ &d[i].ulink);
+ }
+ if (i >= _hurd_dtablesize || d[i].cell == NULL ||
+ d[i].io_port == MACH_PORT_NULL)
{
/* If one descriptor is bogus, we fail completely. */
while (i-- > 0)
@@ -215,6 +268,9 @@ _hurd_select (int nfds,
errno = EBADF;
return -1;
}
+
+ if (nfds > _hurd_dtablesize)
+ nfds = _hurd_dtablesize;
}
@@ -232,19 +288,19 @@ _hurd_select (int nfds,
portset = MACH_PORT_NULL;
for (i = firstfd; i <= lastfd; ++i)
- if (d[i].type)
+ if (!(d[i].type & ~SELECT_ERROR))
+ d[i].reply_port = MACH_PORT_NULL;
+ else
{
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 +321,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 +379,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 | 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 +425,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 +448,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;
}
@@ -411,7 +485,7 @@ _hurd_select (int nfds,
/* 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;
@@ -419,7 +493,7 @@ _hurd_select (int nfds,
if (firstfd != -1)
for (i = firstfd; i <= lastfd; ++i)
- if (d[i].type)
+ if (d[i].reply_port != MACH_PORT_NULL)
__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
@@ -441,23 +515,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. */
@@ -469,16 +557,30 @@ _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 = 0;
+ if (readfds != NULL && FD_ISSET (i, readfds))
+ type |= SELECT_READ;
+ if (writefds != NULL && FD_ISSET (i, writefds))
+ type |= SELECT_WRITE;
+ if (exceptfds != NULL && FD_ISSET (i, exceptfds))
+ type |= SELECT_URG;
+ }
+
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);
}
@@ -487,5 +589,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 aa82f63413..2f16ad5482 100644
--- a/hurd/hurdsig.c
+++ b/hurd/hurdsig.c
@@ -46,9 +46,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;
@@ -56,6 +53,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;
@@ -84,7 +84,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);
@@ -97,16 +97,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);
@@ -115,15 +120,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;
}
libc_hidden_def (_hurd_thread_sigstate)
+
+/* 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. */
@@ -218,6 +326,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);
@@ -364,7 +474,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;
}
@@ -452,6 +562,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;
@@ -462,35 +596,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)
{
@@ -535,8 +654,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);
@@ -549,27 +672,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. */
@@ -578,7 +732,7 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
{ /* PE cannot be null. */
do
{
- if (HURD_PREEMPT_SIGNAL_P (pe, signo, detail->code))
+ if (HURD_PREEMPT_SIGNAL_P (pe, signo, detail->exc_subcode))
{
if (pe->preemptor)
{
@@ -627,12 +781,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. */
@@ -725,9 +879,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;
@@ -762,6 +914,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;
@@ -806,6 +959,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)
{
@@ -873,7 +1028,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)
@@ -942,23 +1097,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). */
@@ -973,42 +1133,110 @@ _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;
+}
- /* 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 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;
+
+ 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;
+}
+
+/* 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 = 0;
+
+ 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;
/* No pending signals left undelivered for this thread.
If we were sent signal 0, we need to check for pending
signals for all threads. */
@@ -1059,9 +1287,29 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
}
}
- /* All pending signals delivered to all threads.
- Now we can send the reply message even for signal 0. */
- reply ();
+ /* 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.
@@ -1180,9 +1428,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. */
@@ -1210,7 +1459,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. */
@@ -1221,8 +1470,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)
@@ -1245,27 +1494,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
@@ -1366,6 +1622,7 @@ reauth_proc (mach_port_t new)
if (! HURD_PORT_USE (&_hurd_ports[INIT_PORT_PROC],
__proc_reauthenticate (port, ref,
MACH_MSG_TYPE_MAKE_SEND) ||
+ /* FIXME: handle EINTR */
__auth_user_authenticate (new, ref,
MACH_MSG_TYPE_MAKE_SEND,
&ignore))
diff --git a/hurd/hurdsock.c b/hurd/hurdsock.c
index bb98a6b745..e9334ba78d 100644
--- a/hurd/hurdsock.c
+++ b/hurd/hurdsock.c
@@ -52,6 +52,7 @@ _hurd_socket_server (int domain, int dead)
return MACH_PORT_NULL;
}
+retry:
HURD_CRITICAL_BEGIN;
__mutex_lock (&lock);
@@ -102,6 +103,10 @@ _hurd_socket_server (int domain, int dead)
__mutex_unlock (&lock);
HURD_CRITICAL_END;
+ if (!server && errno == EINTR)
+ /* Got a signal while inside an RPC of the critical section, retry again */
+ goto retry;
+
return server;
}
diff --git a/hurd/intern-fd.c b/hurd/intern-fd.c
index 3b00824cc1..085ae2bd10 100644
--- a/hurd/intern-fd.c
+++ b/hurd/intern-fd.c
@@ -37,6 +37,7 @@ _hurd_intern_fd (io_t port, int flags, int dealloc)
__spin_unlock (&d->port.lock);
}
HURD_CRITICAL_END;
+ /* FIXME: handle EINTR! */
if (d == NULL)
{
diff --git a/hurd/lookup-retry.c b/hurd/lookup-retry.c
index b596848624..53cacdab15 100644
--- a/hurd/lookup-retry.c
+++ b/hurd/lookup-retry.c
@@ -25,6 +25,7 @@
#include <string.h>
#include <_itoa.h>
#include <eloop-threshold.h>
+#include <unistd.h>
/* Translate the error from dir_lookup into the error the user sees. */
static inline error_t
@@ -59,6 +60,7 @@ __hurd_file_name_lookup_retry (error_t (*use_init_port)
error_t err;
char *file_name;
int nloops;
+ file_t lastdir = MACH_PORT_NULL;
error_t lookup_op (file_t startdir)
{
@@ -107,14 +109,15 @@ __hurd_file_name_lookup_retry (error_t (*use_init_port)
{
case FS_RETRY_REAUTH:
if (err = reauthenticate (*result))
- return err;
+ goto out;
/* Fall through. */
case FS_RETRY_NORMAL:
if (nloops++ >= __eloop_threshold ())
{
__mach_port_deallocate (__mach_task_self (), *result);
- return ELOOP;
+ err = ELOOP;
+ goto out;
}
/* An empty RETRYNAME indicates we have the final port. */
@@ -180,7 +183,7 @@ __hurd_file_name_lookup_retry (error_t (*use_init_port)
if (err)
__mach_port_deallocate (__mach_task_self (), *result);
- return err;
+ goto out;
}
startdir = *result;
@@ -195,7 +198,10 @@ __hurd_file_name_lookup_retry (error_t (*use_init_port)
if (*result != MACH_PORT_NULL)
__mach_port_deallocate (__mach_task_self (), *result);
if (nloops++ >= __eloop_threshold ())
- return ELOOP;
+ {
+ err = ELOOP;
+ goto out;
+ }
file_name = &retryname[1];
break;
@@ -214,7 +220,8 @@ __hurd_file_name_lookup_retry (error_t (*use_init_port)
(*end != '/' && *end != '\0'))
{
errno = save;
- return ENOENT;
+ err = ENOENT;
+ goto out;
}
if (! get_dtable_port)
err = EGRATUITOUS;
@@ -232,9 +239,12 @@ __hurd_file_name_lookup_retry (error_t (*use_init_port)
}
errno = save;
if (err)
- return err;
+ goto out;
if (*end == '\0')
- return 0;
+ {
+ err = 0;
+ goto out;
+ }
else
{
/* Do a normal retry on the remaining components. */
@@ -261,9 +271,12 @@ __hurd_file_name_lookup_retry (error_t (*use_init_port)
if (err = __host_info (__mach_host_self (), HOST_BASIC_INFO,
(integer_t *) &hostinfo,
&hostinfocnt))
- return err;
+ goto out;
if (hostinfocnt != HOST_BASIC_INFO_COUNT)
- return EGRATUITOUS;
+ {
+ err = EGRATUITOUS;
+ goto out;
+ }
p = _itoa (hostinfo.cpu_subtype, &retryname[8], 10, 0);
*--p = '/';
p = _itoa (hostinfo.cpu_type, &retryname[8], 10, 0);
@@ -299,10 +312,11 @@ __hurd_file_name_lookup_retry (error_t (*use_init_port)
}
case '\0':
- return opentty (result);
+ err = opentty (result);
+ goto out;
case '/':
if (err = opentty (&startdir))
- return err;
+ goto out;
strcpy (retryname, &retryname[4]);
break;
default:
@@ -312,14 +326,48 @@ __hurd_file_name_lookup_retry (error_t (*use_init_port)
goto bad_magic;
break;
+ case 'p':
+ if (retryname[1] == 'i' && retryname[2] == 'd' &&
+ (retryname[3] == '/' || retryname[3] == 0))
+ {
+ char *p, buf[1024]; /* XXX */
+ size_t len;
+ p = _itoa (__getpid (), &buf[sizeof buf], 10, 0);
+ len = &buf[sizeof buf] - p;
+ memcpy (buf, p, len);
+ strcpy (buf + len, &retryname[3]);
+ strcpy (retryname, buf);
+
+ /* Do a normal retry on the remaining components. */
+ __mach_port_mod_refs (__mach_task_self (), lastdir,
+ MACH_PORT_RIGHT_SEND, 1);
+ startdir = lastdir;
+ file_name = retryname;
+ }
+ else
+ goto bad_magic;
+ break;
+
default:
bad_magic:
- return EGRATUITOUS;
+ err = EGRATUITOUS;
+ goto out;
}
break;
default:
- return EGRATUITOUS;
+ err = EGRATUITOUS;
+ goto out;
+ }
+
+ if (MACH_PORT_VALID (*result) && *result != lastdir)
+ {
+ if (MACH_PORT_VALID (lastdir))
+ __mach_port_deallocate (__mach_task_self (), lastdir);
+
+ lastdir = *result;
+ __mach_port_mod_refs (__mach_task_self (), lastdir,
+ MACH_PORT_RIGHT_SEND, 1);
}
if (startdir != MACH_PORT_NULL)
@@ -332,6 +380,10 @@ __hurd_file_name_lookup_retry (error_t (*use_init_port)
err = (*use_init_port) (dirport, &lookup_op);
} while (! err);
+out:
+ if (MACH_PORT_VALID (lastdir))
+ __mach_port_deallocate (__mach_task_self (), lastdir);
+
return err;
}
weak_alias (__hurd_file_name_lookup_retry, hurd_file_name_lookup_retry)
diff --git a/hurd/setauth.c b/hurd/setauth.c
index 025be580e1..a8af439c97 100644
--- a/hurd/setauth.c
+++ b/hurd/setauth.c
@@ -107,6 +107,7 @@ _hurd_setauth (auth_t new)
__mutex_unlock (&reauth_lock);
HURD_CRITICAL_END;
+ /* FIXME: handle EINTR! */
return 0;
}
diff --git a/hurd/seteuids.c b/hurd/seteuids.c
index edfa4c2195..0dbe44d78b 100644
--- a/hurd/seteuids.c
+++ b/hurd/seteuids.c
@@ -47,6 +47,7 @@ seteuids (int n, const uid_t *uids)
}
__mutex_unlock (&_hurd_id.lock);
HURD_CRITICAL_END;
+ /* FIXME: handle EINTR! */
if (err)
return __hurd_fail (err);
diff --git a/hurd/thread-cancel.c b/hurd/thread-cancel.c
index c70b814dda..cd06597f2e 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);
@@ -80,6 +79,7 @@ hurd_thread_cancel (thread_t thread)
}
_hurd_critical_section_unlock (ss);
+ /* FIXME: handle EINTR */
return err;
}
@@ -91,7 +91,6 @@ hurd_check_cancel (void)
int cancel;
__spin_lock (&ss->lock);
- assert (! __spin_lock_locked (&ss->critical_section_lock));
cancel = ss->cancel;
ss->cancel = 0;
__spin_unlock (&ss->lock);