]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - hurd/hurdsig.c
Break lines before not after operators, batch 4.
[thirdparty/glibc.git] / hurd / hurdsig.c
index 13ffb71822e5103c35dab93c119610b7e6d6a7cc..1b511d1e8f9932322862c573b3a4c903c22c5f9e 100644 (file)
@@ -1,29 +1,39 @@
-/* Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
-This file is part of the GNU C Library.
+/* Copyright (C) 1991-2019 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 Library General Public License as
-published by the Free Software Foundation; either version 2 of the
-License, or (at your option) any later version.
+   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
-Library General Public License for more details.
+   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 Library General Public
-License along with the GNU C Library; see the file COPYING.LIB.  If
-not, write to the Free Software Foundation, Inc., 675 Mass Ave,
-Cambridge, MA 02139, USA.  */
+   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 <stdlib.h>
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <cthreads.h>          /* For `struct mutex'.  */
+#include <pthreadP.h>
+#include <mach.h>
+#include <mach/thread_switch.h>
+#include <mach/mig_support.h>
+
 #include <hurd.h>
+#include <hurd/id.h>
 #include <hurd/signal.h>
-#include <cthreads.h>          /* For `struct mutex'.  */
-#include <string.h>
+
 #include "hurdfault.h"
 #include "hurdmalloc.h"                /* XXX */
+#include "../locale/localeinfo.h"
+
+#include <libc-diag.h>
 
 const char *_hurdsig_getenv (const char *);
 
@@ -39,11 +49,15 @@ 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;
+
 /* Linked-list of per-thread signal state.  */
 struct hurd_sigstate *_hurd_sigstates;
 
 /* Timeout for RPC's after interrupt_operation. */
-mach_msg_timeout_t _hurd_interrupted_rpc_timeout = 3000;
+mach_msg_timeout_t _hurd_interrupted_rpc_timeout = 60000;
 \f
 static void
 default_sigaction (struct sigaction actions[NSIG])
@@ -74,11 +88,13 @@ _hurd_thread_sigstate (thread_t thread)
       ss->thread = thread;
       __spin_lock_init (&ss->lock);
 
-      /* Initialze default state.  */
+      /* Initialize default state.  */
       __sigemptyset (&ss->blocked);
       __sigemptyset (&ss->pending);
       memset (&ss->sigaltstack, 0, sizeof (ss->sigaltstack));
-      ss->suspended = 0;
+      ss->sigaltstack.ss_flags |= SS_DISABLE;
+      ss->preemptors = NULL;
+      ss->suspended = MACH_PORT_NULL;
       ss->intr_port = MACH_PORT_NULL;
       ss->context = NULL;
 
@@ -108,33 +124,44 @@ _hurd_thread_sigstate (thread_t thread)
   __mutex_unlock (&_hurd_siglock);
   return ss;
 }
+libc_hidden_def (_hurd_thread_sigstate)
 \f
 /* Signal delivery itself is on this page.  */
 
 #include <hurd/fd.h>
 #include <hurd/crash.h>
+#include <hurd/resource.h>
 #include <hurd/paths.h>
 #include <setjmp.h>
 #include <fcntl.h>
 #include <sys/wait.h>
-#include "thread_state.h"
+#include <thread_state.h>
 #include <hurd/msg_server.h>
 #include <hurd/msg_reply.h>    /* For __msg_sig_post_reply.  */
-#include <assert.h>
 #include <hurd/interrupt.h>
+#include <assert.h>
+#include <unistd.h>
 
-int _hurd_core_limit;  /* XXX */
 
 /* Call the crash dump server to mummify us before we die.
    Returns nonzero if a core file was written.  */
 static int
-write_corefile (int signo, long int sigcode, int sigerror)
+write_corefile (int signo, const struct hurd_signal_detail *detail)
 {
   error_t err;
   mach_port_t coreserver;
   file_t file, coredir;
   const char *name;
 
+  /* Don't bother locking since we just read the one word.  */
+  rlim_t corelimit = _hurd_rlimits[RLIMIT_CORE].rlim_cur;
+
+  if (corelimit == 0)
+    /* No core dumping, thank you very much.  Note that this makes
+       `ulimit -c 0' prevent crash-suspension too, which is probably
+       what the user wanted.  */
+    return 0;
+
   /* XXX RLIMIT_CORE:
      When we have a protocol to make the server return an error
      for RLIMIT_FSIZE, then tell the corefile fs server the RLIMIT_CORE
@@ -151,48 +178,34 @@ write_corefile (int signo, long int sigcode, int sigerror)
     return 0;
 
   /* Get a port to the directory where the new core file will reside.  */
+  file = MACH_PORT_NULL;
   name = _hurdsig_getenv ("COREFILE");
   if (name == NULL)
     name = "core";
   coredir = __file_name_split (name, (char **) &name);
-  if (coredir == MACH_PORT_NULL)
-    return 0;
-  /* Create the new file, but don't link it into the directory yet.  */
-  if (err = __dir_mkfile (coredir, O_WRONLY|O_CREAT,
-                         0600 & ~_hurd_umask, /* XXX ? */
-                         &file))
-    return 0;
+  if (coredir != MACH_PORT_NULL)
+    /* Create the new file, but don't link it into the directory yet.  */
+    __dir_mkfile (coredir, O_WRONLY|O_CREAT,
+                 0600 & ~_hurd_umask, /* XXX ? */
+                 &file);
 
   /* Call the core dumping server to write the core file.  */
   err = __crash_dump_task (coreserver,
                           __mach_task_self (),
-                          file, _hurdsig_getenv ("GNUTARGET"),
-                          signo, sigcode, sigerror);
+                          file,
+                          signo, detail->code, detail->error,
+                          detail->exc, detail->exc_code, detail->exc_subcode,
+                          _hurd_ports[INIT_PORT_CTTYID].port,
+                          MACH_MSG_TYPE_COPY_SEND);
   __mach_port_deallocate (__mach_task_self (), coreserver);
-  if (! err)
+
+  if (! err && file != MACH_PORT_NULL)
     /* The core dump into FILE succeeded, so now link it into the
        directory.  */
-    err = __dir_link (file, coredir, name);
+    err = __dir_link (coredir, file, name, 1);
   __mach_port_deallocate (__mach_task_self (), file);
   __mach_port_deallocate (__mach_task_self (), coredir);
-  return !err;
-}
-
-
-/* Send a sig_post reply message if it hasn't already been sent.  */
-static inline void
-post_reply (mach_port_t *reply_port, mach_msg_type_name_t reply_port_type,
-           int untraced,
-           error_t result)
-{
-  error_t err;
-  if (reply_port == NULL || *reply_port == MACH_PORT_NULL)
-    return;
-  err = (untraced ? __msg_sig_post_untraced_reply : __msg_sig_post_reply)
-    (*reply_port, reply_port_type, result);
-  *reply_port = MACH_PORT_NULL;
-  if (err != MACH_SEND_INVALID_DEST) /* Ignore dead reply port.  */
-    assert_perror (err);
+  return !err && file != MACH_PORT_NULL;
 }
 
 
@@ -201,13 +214,10 @@ post_reply (mach_port_t *reply_port, mach_msg_type_name_t reply_port_type,
    record whether we have done thread_abort.  */
 #define THREAD_ABORTED 1
 
-/* SS->thread is suspended.  Abort the thread and get its basic state.  If
-   REPLY_PORT is not NULL, send a reply on *REPLY_PORT after aborting the
-   thread.  */
+/* SS->thread is suspended.  Abort the thread and get its basic state.  */
 static void
 abort_thread (struct hurd_sigstate *ss, struct machine_thread_all_state *state,
-             mach_port_t *reply_port, mach_msg_type_name_t reply_port_type,
-             int untraced)
+             void (*reply) (void))
 {
   if (!(state->set & THREAD_ABORTED))
     {
@@ -218,8 +228,8 @@ abort_thread (struct hurd_sigstate *ss, struct machine_thread_all_state *state,
       state->set = THREAD_ABORTED;
     }
 
-  if (reply_port)
-    post_reply (reply_port, reply_port_type, untraced, 0);
+  if (reply)
+    (*reply) ();
 
   machine_get_basic_state (ss->thread, state);
 }
@@ -229,21 +239,24 @@ abort_thread (struct hurd_sigstate *ss, struct machine_thread_all_state *state,
    that this location can be set without faulting, or else return NULL.  */
 
 static mach_port_t *
-interrupted_reply_port_location (struct machine_thread_all_state *thread_state,
+interrupted_reply_port_location (thread_t thread,
+                                struct machine_thread_all_state *thread_state,
                                 int sigthread)
 {
-  mach_port_t *portloc = (mach_port_t *) __hurd_threadvar_location_from_sp
-    (_HURD_THREADVAR_MIG_REPLY, (void *) thread_state->basic.SP);
+  mach_port_t *portloc = &THREAD_TCB(thread, thread_state)->reply_port;
 
-  if (sigthread && _hurdsig_catch_fault (SIGSEGV))
-    {
-      assert (_hurdsig_fault_sigcode == (long int) portloc);
-      /* Faulted trying to read the stack.  */
-      return NULL;
-    }
+  if (sigthread && _hurdsig_catch_memory_fault (portloc))
+    /* Faulted trying to read the TCB.  */
+    return NULL;
 
+  DIAG_PUSH_NEEDS_COMMENT;
+  /* GCC 6 and before seem to be confused by the setjmp call inside
+     _hurdsig_catch_memory_fault and think that we may be returning a second
+     time to here with portloc uninitialized (but we never do). */
+  DIAG_IGNORE_NEEDS_COMMENT (6, "-Wmaybe-uninitialized");
   /* Fault now if this pointer is bogus.  */
   *(volatile mach_port_t *) portloc = *portloc;
+  DIAG_POP_NEEDS_COMMENT;
 
   if (sigthread)
     _hurdsig_end_catch_fault ();
@@ -251,7 +264,11 @@ interrupted_reply_port_location (struct machine_thread_all_state *thread_state,
   return portloc;
 }
 \f
-#include "intr-msg.h"
+#include <hurd/sigpreempt.h>
+#include <intr-msg.h>
+
+/* Timeout on interrupt_operation calls.  */
+mach_msg_timeout_t _hurdsig_interrupt_timeout = 1000;
 
 /* SS->thread is suspended.
 
@@ -274,10 +291,9 @@ interrupted_reply_port_location (struct machine_thread_all_state *thread_state,
 mach_port_t
 _hurdsig_abort_rpcs (struct hurd_sigstate *ss, int signo, int sigthread,
                     struct machine_thread_all_state *state, int *state_change,
-                    mach_port_t *reply_port,
-                    mach_msg_type_name_t reply_port_type,
-                    int untraced)
+                    void (*reply) (void))
 {
+  extern const void _hurd_intr_rpc_msg_about_to;
   extern const void _hurd_intr_rpc_msg_in_trap;
   mach_port_t rcv_port = MACH_PORT_NULL;
   mach_port_t intr_port;
@@ -291,9 +307,10 @@ _hurdsig_abort_rpcs (struct hurd_sigstate *ss, int signo, int sigthread,
 
   /* Abort the thread's kernel context, so any pending message send or
      receive completes immediately or aborts.  */
-  abort_thread (ss, state, reply_port, reply_port_type, untraced);
+  abort_thread (ss, state, reply);
 
-  if (state->basic.PC < (natural_t) &_hurd_intr_rpc_msg_in_trap)
+  if (state->basic.PC >= (natural_t) &_hurd_intr_rpc_msg_about_to
+      && state->basic.PC < (natural_t) &_hurd_intr_rpc_msg_in_trap)
     {
       /* The thread is about to do the RPC, but hasn't yet entered
         mach_msg.  Mutate the thread's state so it knows not to try
@@ -304,11 +321,11 @@ _hurdsig_abort_rpcs (struct hurd_sigstate *ss, int signo, int sigthread,
       state->basic.SYSRETURN = MACH_SEND_INTERRUPTED;
       *state_change = 1;
     }
-  else if (state->basic.PC == (natural_t) &_hurd_intr_rpc_msg_in_trap &&
+  else if (state->basic.PC == (natural_t) &_hurd_intr_rpc_msg_in_trap
           /* The thread was blocked in the system call.  After thread_abort,
              the return value register indicates what state the RPC was in
              when interrupted.  */
-          state->basic.SYSRETURN == MACH_RCV_INTERRUPTED)
+          && state->basic.SYSRETURN == MACH_RCV_INTERRUPTED)
       {
        /* The RPC request message was sent and the thread was waiting for
           the reply message; now the message receive has been aborted, so
@@ -319,9 +336,10 @@ _hurdsig_abort_rpcs (struct hurd_sigstate *ss, int signo, int sigthread,
           our nonzero return tells the trampoline code to finish the message
           receive operation before running the handler.  */
 
-       mach_port_t *reply = interrupted_reply_port_location (state,
+       mach_port_t *reply = interrupted_reply_port_location (ss->thread,
+                                                             state,
                                                              sigthread);
-       error_t err = __interrupt_operation (intr_port);
+       error_t err = __interrupt_operation (intr_port, _hurdsig_interrupt_timeout);
 
        if (err)
          {
@@ -383,9 +401,9 @@ abort_all_rpcs (int signo, struct machine_thread_all_state *state, int live)
   reply_ports = alloca (nthreads * sizeof *reply_ports);
 
   nthreads = 0;
-  for (ss = _hurd_sigstates; ss != NULL; ss = ss->next)
+  for (ss = _hurd_sigstates; ss != NULL; ss = ss->next, ++nthreads)
     if (ss->thread == _hurd_msgport_thread)
-      reply_ports[nthreads++] = MACH_PORT_NULL;
+      reply_ports[nthreads] = MACH_PORT_NULL;
     else
       {
        int state_changed;
@@ -394,15 +412,26 @@ abort_all_rpcs (int signo, struct machine_thread_all_state *state, int live)
        /* Abort any operation in progress with interrupt_operation.
           Record the reply port the thread is waiting on.
           We will wait for all the replies below.  */
-       reply_ports[nthreads++] = _hurdsig_abort_rpcs (ss, signo, 1,
-                                                      state, &state_changed,
-                                                      NULL, 0, 0);
-       if (state_changed && live)
-         /* Aborting the RPC needed to change this thread's state,
-            and it might ever run again.  So write back its state.  */
-         __thread_set_state (ss->thread, MACHINE_THREAD_STATE_FLAVOR,
-                             (natural_t *) &state->basic,
-                             MACHINE_THREAD_STATE_COUNT);
+       reply_ports[nthreads] = _hurdsig_abort_rpcs (ss, signo, 1,
+                                                    state, &state_changed,
+                                                    NULL);
+       if (live)
+         {
+           if (reply_ports[nthreads] != MACH_PORT_NULL)
+             {
+               /* We will wait for the reply to this RPC below, so the
+                  thread must issue a new RPC rather than waiting for the
+                  reply to the one it sent.  */
+               state->basic.SYSRETURN = EINTR;
+               state_changed = 1;
+             }
+           if (state_changed)
+             /* Aborting the RPC needed to change this thread's state,
+                and it might ever run again.  So write back its state.  */
+             __thread_set_state (ss->thread, MACHINE_THREAD_STATE_FLAVOR,
+                                 (natural_t *) &state->basic,
+                                 MACHINE_THREAD_STATE_COUNT);
+         }
       }
 
   /* Wait for replies from all the successfully interrupted RPCs.  */
@@ -426,18 +455,20 @@ abort_all_rpcs (int signo, struct machine_thread_all_state *state, int live)
       }
 }
 
+struct hurd_signal_preemptor *_hurdsig_preemptors = 0;
+sigset_t _hurdsig_preempted_set;
 
-struct hurd_signal_preempt *_hurd_signal_preempt[NSIG];
-struct mutex _hurd_signal_preempt_lock;
+/* XXX temporary to deal with spelling fix */
+weak_alias (_hurdsig_preemptors, _hurdsig_preempters)
 
 /* Mask of stop signals.  */
-#define STOPSIGS (sigmask (SIGTTIN) | sigmask (SIGTTOU) \
-                 sigmask (SIGSTOP) | sigmask (SIGTSTP))
+#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, long int sigcode, int sigerror,
+                           int signo, struct hurd_signal_detail *detail,
                            mach_port_t reply_port,
                            mach_msg_type_name_t reply_port_type,
                            int untraced)
@@ -446,25 +477,30 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
   struct machine_thread_all_state thread_state;
   enum { stop, ignore, core, term, handle } act;
   sighandler_t handler;
-  struct hurd_signal_preempt *pe;
-  sighandler_t (*preempt) (thread_t, int, long int, int) = NULL;
   sigset_t pending;
   int ss_suspended;
 
   /* Reply to this sig_post message.  */
-  inline void reply (void)
+  __typeof (__msg_sig_post_reply) *reply_rpc
+    = (untraced ? __msg_sig_post_untraced_reply : __msg_sig_post_reply);
+  void reply (void)
     {
-      post_reply (&reply_port, reply_port_type, untraced, 0);
+      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)
     {
       __sigaddset (&ss->pending, signo);
-      /* Save the code to be given to the handler when SIGNO is
+      /* Save the details to be given to the handler when SIGNO is
         unblocked.  */
-      ss->pending_data[signo].code = sigcode;
-      ss->pending_data[signo].error = sigerror;
+      ss->pending_data[signo] = *detail;
     }
 
   /* Suspend the process with SIGNO.  */
@@ -479,7 +515,8 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
                   __proc_dostop (port, _hurd_msgport_thread);
                   __mutex_unlock (&_hurd_siglock);
                   abort_all_rpcs (signo, &thread_state, 1);
-                  __proc_mark_stop (port, signo);
+                  reply ();
+                  __proc_mark_stop (port, signo, detail->code);
                 }));
       _hurd_stopped = 1;
     }
@@ -501,8 +538,8 @@ _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 (threads[i] != _hurd_msgport_thread
+             && (act != handle || threads[i] != ss->thread))
            {
              err = __thread_resume (threads[i]);
              assert_perror (err);
@@ -515,8 +552,9 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
                       (vm_address_t) threads,
                       nthreads * sizeof *threads);
       _hurd_stopped = 0;
-      /* The thread that will run the handler is already suspended.  */
-      ss_suspended = 1;
+      if (act == handle)
+       /* The thread that will run the handler is already suspended.  */
+       ss_suspended = 1;
     }
 
   if (signo == 0)
@@ -534,36 +572,56 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
 
   thread_state.set = 0;                /* We know nothing.  */
 
-  /* Check for a preempted signal.  Preempted signals
-     can arrive during critical sections.  */
-  __mutex_lock (&_hurd_signal_preempt_lock);
-  for (pe = _hurd_signal_preempt[signo]; pe != NULL; pe = pe->next)
-    if (pe->handler && sigcode >= pe->first && sigcode <= pe->last)
-      {
-       preempt = pe->handler;
-       break;
+  __spin_lock (&ss->lock);
+
+  /* Check for a preempted signal.  Preempted signals can arrive during
+     critical sections.  */
+  {
+    inline sighandler_t try_preemptor (struct hurd_signal_preemptor *pe)
+      {                                /* PE cannot be null.  */
+       do
+         {
+           if (HURD_PREEMPT_SIGNAL_P (pe, signo, detail->code))
+             {
+               if (pe->preemptor)
+                 {
+                   sighandler_t handler = (*pe->preemptor) (pe, ss,
+                                                            &signo, detail);
+                   if (handler != SIG_ERR)
+                     return handler;
+                 }
+               else
+                 return pe->handler;
+             }
+           pe = pe->next;
+         } while (pe != 0);
+       return SIG_ERR;
       }
-  __mutex_unlock (&_hurd_signal_preempt_lock);
 
-  handler = SIG_DFL;
-  if (preempt)
-    /* Let the preempting handler examine the thread.
-       If it returns SIG_DFL, we run the normal handler;
-       otherwise we use the handler it returns.  */
-    handler = (*preempt) (ss->thread, signo, sigcode, sigerror);
+    handler = ss->preemptors ? try_preemptor (ss->preemptors) : SIG_ERR;
+
+    /* If no thread-specific preemptor, check for a global one.  */
+    if (handler == SIG_ERR && __sigismember (&_hurdsig_preempted_set, signo))
+      {
+       __mutex_lock (&_hurd_siglock);
+       handler = try_preemptor (_hurdsig_preemptors);
+       __mutex_unlock (&_hurd_siglock);
+      }
+  }
 
   ss_suspended = 0;
 
-  if (handler != SIG_DFL)
+  if (handler == SIG_IGN)
+    /* Ignore the signal altogether.  */
+    act = ignore;
+  else if (handler != SIG_ERR)
     /* Run the preemption-provided handler.  */
     act = handle;
   else
     {
       /* No preemption.  Do normal handling.  */
 
-      __spin_lock (&ss->lock);
-
-      if (!untraced && (_hurd_exec_flags & EXEC_TRACED))
+      if (!untraced && __sigismember (&_hurdsig_traced, signo))
        {
          /* We are being traced.  Stop to tell the debugger of the signal.  */
          if (_hurd_stopped)
@@ -644,7 +702,7 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
       if (__sigmask (signo) & STOPSIGS)
        /* Stop signals clear a pending SIGCONT even if they
           are handled or ignored (but not if preempted).  */
-       ss->pending &= ~sigmask (SIGCONT);
+       __sigdelset (&ss->pending, SIGCONT);
       else
        {
          if (signo == SIGCONT)
@@ -657,21 +715,22 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
        }
     }
 
-  if (_hurd_orphaned && act == stop &&
-      (__sigmask (signo) & (__sigmask (SIGTTIN) | __sigmask (SIGTTOU) |
-                           __sigmask (SIGTSTP))))
+  if (_hurd_orphaned && act == stop
+      && (__sigmask (signo) & (__sigmask (SIGTTIN) | __sigmask (SIGTTOU)
+                              | __sigmask (SIGTSTP))))
     {
       /* If we would ordinarily stop for a job control signal, but we are
         orphaned so noone would ever notice and continue us again, we just
         quietly die, alone and in the dark.  */
-      sigcode = signo;
+      detail->code = signo;
       signo = SIGKILL;
       act = term;
     }
 
   /* Handle receipt of a blocked signal, or any signal while stopped.  */
-  if (__sigismember (&ss->blocked, signo) ||
-      (signo != SIGKILL && _hurd_stopped))
+  if (act != ignore            /* Signals ignored now are forgotten now.  */
+      && __sigismember (&ss->blocked, signo)
+      || (signo != SIGKILL && _hurd_stopped))
     {
       mark_pending ();
       act = ignore;
@@ -686,7 +745,8 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
          /* We are already stopped, but receiving an untraced stop
             signal.  Instead of resuming and suspending again, just
             notify the proc server of the new stop signal.  */
-         error_t err = __USEPORT (PROC, __proc_mark_stop (port, signo));
+         error_t err = __USEPORT (PROC, __proc_mark_stop
+                                  (port, signo, detail->code));
          assert_perror (err);
        }
       else
@@ -695,14 +755,29 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
       break;
 
     case ignore:
-      /* Nobody cares about this signal.  */
+      if (detail->exc)
+       /* Blocking or ignoring a machine exception is fatal.
+          Otherwise we could just spin on the faulting instruction.  */
+       goto fatal;
+
+      /* Nobody cares about this signal.  If there was a call to resume
+        above in SIGCONT processing and we've left a thread suspended,
+        now's the time to set it going. */
+      if (ss_suspended)
+       {
+         err = __thread_resume (ss->thread);
+         assert_perror (err);
+         ss_suspended = 0;
+       }
       break;
 
     sigbomb:
       /* We got a fault setting up the stack frame for the handler.
         Nothing to do but die; BSD gets SIGILL in this case.  */
-      sigcode = signo; /* XXX ? */
+      detail->code = signo;    /* XXX ? */
       signo = SIGILL;
+
+    fatal:
       act = core;
       /* FALLTHROUGH */
 
@@ -721,7 +796,7 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
        int status = W_EXITCODE (0, signo);
        /* Do a core dump if desired.  Only set the wait status bit saying we
           in fact dumped core if the operation was actually successful.  */
-       if (act == core && write_corefile (signo, sigcode, sigerror))
+       if (act == core && write_corefile (signo, detail))
          status |= WCOREFLAG;
        /* Tell proc how we died and then stick the saber in the gut.  */
        _hurd_exit (status);
@@ -746,7 +821,7 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
           RPC is in progress, abort_rpcs will do this.  But we must always
           do it before fetching the thread's state, because
           thread_get_state is never kosher before thread_abort.  */
-       abort_thread (ss, &thread_state, NULL, 0, 0);
+       abort_thread (ss, &thread_state, NULL);
 
        if (ss->context)
          {
@@ -755,10 +830,8 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
 
            mach_port_t *loc;
 
-           if (_hurdsig_catch_fault (SIGSEGV))
+           if (_hurdsig_catch_memory_fault (ss->context))
              {
-               assert (_hurdsig_fault_sigcode >= (long int) ss->context &&
-                       _hurdsig_fault_sigcode < (long int) (ss->context + 1));
                /* We faulted reading the thread's stack.  Forget that
                   context and pretend it wasn't there.  It almost
                   certainly crash if this handler returns, but that's it's
@@ -776,7 +849,8 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
 
            if (! machine_get_basic_state (ss->thread, &thread_state))
              goto sigbomb;
-           loc = interrupted_reply_port_location (&thread_state, 1);
+           loc = interrupted_reply_port_location (ss->thread,
+                                                  &thread_state, 1);
            if (loc && *loc != MACH_PORT_NULL)
              /* This is the reply port for the context which called
                 sigreturn.  Since we are abandoning that context entirely
@@ -786,23 +860,45 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
            /* The thread was in sigreturn, not in any interruptible RPC.  */
            wait_for_reply = 0;
 
-           assert (! ss->critical_section);
+           assert (! __spin_lock_locked (&ss->critical_section_lock));
          }
        else
          {
+           int crit = __spin_lock_locked (&ss->critical_section_lock);
+
            wait_for_reply
-             = (_hurdsig_abort_rpcs (ss, signo, 1,
+             = (_hurdsig_abort_rpcs (ss,
+                                     /* In a critical section, any RPC
+                                        should be cancelled instead of
+                                        restarted, regardless of
+                                        SA_RESTART, so the entire
+                                        "atomic" operation can be aborted
+                                        as a unit.  */
+                                     crit ? 0 : signo, 1,
                                      &thread_state, &state_changed,
-                                     &reply_port, reply_port_type, untraced)
+                                     &reply)
                 != MACH_PORT_NULL);
 
-           if (ss->critical_section)
+           if (crit)
              {
                /* The thread is in a critical section.  Mark the signal as
                   pending.  When it finishes the critical section, it will
                   check for pending signals.  */
                mark_pending ();
-               assert (! state_changed);
+               if (state_changed)
+                 /* Some cases of interrupting an RPC must change the
+                    thread state to back out the call.  Normally this
+                    change is rolled into the warping to the handler and
+                    sigreturn, but we are not running the handler now
+                    because the thread is in a critical section.  Instead,
+                    mutate the thread right away for the RPC interruption
+                    and resume it; the RPC will return early so the
+                    critical section can end soon.  */
+                 __thread_set_state (ss->thread, MACHINE_THREAD_STATE_FLAVOR,
+                                     (natural_t *) &thread_state.basic,
+                                     MACHINE_THREAD_STATE_COUNT);
+               /* */
+               ss->intr_port = MACH_PORT_NULL;
                __thread_resume (ss->thread);
                break;
              }
@@ -810,8 +906,7 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
 
        /* Call the machine-dependent function to set the thread up
           to run the signal handler, and preserve its old context.  */
-       scp = _hurd_setup_sighandler (ss, handler,
-                                     signo, sigcode,
+       scp = _hurd_setup_sighandler (ss, handler, signo, detail,
                                      wait_for_reply, &thread_state);
        if (scp == NULL)
          goto sigbomb;
@@ -821,7 +916,8 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
        {
          /* Fetch the thread variable for the MiG reply port,
             and set it to MACH_PORT_NULL.  */
-         mach_port_t *loc = interrupted_reply_port_location (&thread_state,
+         mach_port_t *loc = interrupted_reply_port_location (ss->thread,
+                                                             &thread_state,
                                                              1);
          if (loc)
            {
@@ -850,11 +946,22 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
        }
 
        /* Backdoor extra argument to signal handler.  */
-       scp->sc_error = sigerror;
+       scp->sc_error = detail->error;
 
-       /* Block SIGNO and requested signals while running the handler.  */
+       /* Block requested signals while running the handler.  */
        scp->sc_mask = ss->blocked;
-       ss->blocked |= __sigmask (signo) | ss->actions[signo].sa_mask;
+       __sigorset (&ss->blocked, &ss->blocked, &ss->actions[signo].sa_mask);
+
+       /* Also block SIGNO unless we're asked not to.  */
+       if (! (ss->actions[signo].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
+           && signo != SIGILL && signo != SIGTRAP)
+         ss->actions[signo].sa_handler = SIG_DFL;
 
        /* Start the thread running the handler (or possibly waiting for an
           RPC reply before running the handler).  */
@@ -884,7 +991,7 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
        thread finishes its critical section.  */
     inline int signals_pending (void)
       {
-       if (_hurd_stopped || ss->critical_section)
+       if (_hurd_stopped || __spin_lock_locked (&ss->critical_section_lock))
          return 0;
        return pending = ss->pending & ~ss->blocked;
       }
@@ -894,13 +1001,12 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
 
     if (signals_pending ())
       {
-      pending:
        for (signo = 1; signo < NSIG; ++signo)
          if (__sigismember (&pending, signo))
            {
+           deliver_pending:
              __sigdelset (&ss->pending, signo);
-             sigcode = ss->pending_data[signo].code;
-             sigerror = ss->pending_data[signo].error;
+             *detail = ss->pending_data[signo];
              __spin_unlock (&ss->lock);
              goto post_signal;
            }
@@ -916,8 +1022,18 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
        for (ss = _hurd_sigstates; ss != NULL; ss = ss->next)
          {
            __spin_lock (&ss->lock);
-           if (signals_pending ())
-             goto pending;
+           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);
@@ -931,16 +1047,11 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
            /* There is a sigsuspend waiting.  Tell it to wake up.  */
            error_t err;
            mach_msg_header_t msg;
-           err = __mach_port_insert_right (__mach_task_self (),
-                                           ss->suspended, ss->suspended,
-                                           MACH_MSG_TYPE_MAKE_SEND);
-           assert_perror (err);
-           msg.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_MOVE_SEND, 0);
+           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.  */
-           msg.msgh_seqno = 17; /* Random.  */
            ss->suspended = MACH_PORT_NULL;
            err = __mach_msg (&msg, MACH_SEND_MSG, sizeof msg, 0,
                              MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
@@ -982,6 +1093,7 @@ signal_allowed (int signo, mach_port_t refport)
     case SIGINFO:
     case SIGTTIN:
     case SIGTTOU:
+    case SIGWINCH:
       /* Job control signals can be sent by the controlling terminal.  */
       if (__USEPORT (CTTYID, port == refport))
        goto win;
@@ -1017,8 +1129,9 @@ signal_allowed (int signo, mach_port_t refport)
           authorizing SIGIO and SIGURG signals properly.  */
 
        int d;
+       int lucky = 0;          /* True if we find a match for REFPORT.  */
        __mutex_lock (&_hurd_dtable_lock);
-       for (d = 0; (unsigned int) d < (unsigned int) _hurd_dtablesize; ++d)
+       for (d = 0; !lucky && (unsigned) d < (unsigned) _hurd_dtablesize; ++d)
          {
            struct hurd_userlink ulink;
            io_t port;
@@ -1030,13 +1143,14 @@ signal_allowed (int signo, mach_port_t refport)
              {
                if (refport == asyncid)
                  /* Break out of the loop on the next iteration.  */
-                 d = -1;
+                 lucky = 1;
                __mach_port_deallocate (__mach_task_self (), asyncid);
              }
            _hurd_port_free (&_hurd_dtable[d]->port, &ulink, port);
          }
+       __mutex_unlock (&_hurd_dtable_lock);
        /* If we found a lucky winner, we've set D to -1 in the loop.  */
-       if (d < 0)
+       if (lucky)
          goto win;
       }
     }
@@ -1057,18 +1171,22 @@ signal_allowed (int signo, mach_port_t refport)
 kern_return_t
 _S_msg_sig_post (mach_port_t me,
                 mach_port_t reply_port, mach_msg_type_name_t reply_port_type,
-                int signo,
+                int signo, natural_t sigcode,
                 mach_port_t refport)
 {
   error_t err;
+  struct hurd_signal_detail d;
 
   if (err = signal_allowed (signo, refport))
     return err;
 
+  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),
-                             signo, 0, 0, reply_port, reply_port_type,
+                             signo, &d, reply_port, reply_port_type,
                              0); /* Stop if traced.  */
 
   return MIG_NO_REPLY;         /* Already replied.  */
@@ -1081,18 +1199,22 @@ kern_return_t
 _S_msg_sig_post_untraced (mach_port_t me,
                          mach_port_t reply_port,
                          mach_msg_type_name_t reply_port_type,
-                         int signo,
+                         int signo, natural_t sigcode,
                          mach_port_t refport)
 {
   error_t err;
+  struct hurd_signal_detail d;
 
   if (err = signal_allowed (signo, refport))
     return err;
 
+  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),
-                             signo, 0, 0, reply_port, reply_port_type,
+                             signo, &d, reply_port, reply_port_type,
                              1); /* Untraced flag. */
 
   return MIG_NO_REPLY;         /* Already replied.  */
@@ -1106,10 +1228,11 @@ extern void __mig_init (void *);
    thread.  */
 
 void
-_hurdsig_init (void)
+_hurdsig_init (const int *intarray, size_t intarraysize)
 {
   error_t err;
   vm_size_t stacksize;
+  struct hurd_sigstate *ss;
 
   __mutex_init (&_hurd_siglock);
 
@@ -1125,38 +1248,114 @@ _hurdsig_init (void)
                                  MACH_MSG_TYPE_MAKE_SEND);
   assert_perror (err);
 
+  /* 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.  */
+  if (intarraysize > INIT_SIGMASK)
+    ss->blocked = intarray[INIT_SIGMASK];
+  if (intarraysize > INIT_SIGPENDING)
+    ss->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;
+    }
+
   /* Set the default thread to receive task-global signals
      to this one, the main (first) user thread.  */
-  _hurd_sigthread = __mach_thread_self ();
+  _hurd_sigthread = ss->thread;
 
   /* Start the signal thread listening on the message port.  */
 
-  err = __thread_create (__mach_task_self (), &_hurd_msgport_thread);
-  assert_perror (err);
+#pragma weak __cthread_fork
+  if (!__cthread_fork)
+    {
+      err = __thread_create (__mach_task_self (), &_hurd_msgport_thread);
+      assert_perror (err);
 
-  stacksize = __vm_page_size * 4; /* Small stack for signal thread.  */
-  err = __mach_setup_thread (__mach_task_self (), _hurd_msgport_thread,
-                            _hurd_msgport_receive,
-                            (vm_address_t *) &__hurd_sigthread_stack_base,
-                            &stacksize);
-  assert_perror (err);
+      stacksize = __vm_page_size * 8; /* Small stack for signal thread.  */
+      err = __mach_setup_thread (__mach_task_self (), _hurd_msgport_thread,
+                                _hurd_msgport_receive,
+                                (vm_address_t *) &__hurd_sigthread_stack_base,
+                                &stacksize);
+      assert_perror (err);
+      err = __mach_setup_tls (_hurd_msgport_thread);
+      assert_perror (err);
 
-  __hurd_sigthread_stack_end = __hurd_sigthread_stack_base + stacksize;
-  __hurd_sigthread_variables =
-    malloc (__hurd_threadvar_max * sizeof (unsigned long int));
-  if (__hurd_sigthread_variables == NULL)
-    __libc_fatal ("hurd: Can't allocate thread variables for signal thread\n");
+      __hurd_sigthread_stack_end = __hurd_sigthread_stack_base + stacksize;
 
-  /* Reinitialize the MiG support routines so they will use a per-thread
-     variable for the cached reply port.  */
-  __mig_init ((void *) __hurd_sigthread_stack_base);
+      /* Reinitialize the MiG support routines so they will use a per-thread
+        variable for the cached reply port.  */
+      __mig_init ((void *) __hurd_sigthread_stack_base);
 
-  err = __thread_resume (_hurd_msgport_thread);
-  assert_perror (err);
+      err = __thread_resume (_hurd_msgport_thread);
+      assert_perror (err);
+    }
+  else
+    {
+      /* When cthreads is being used, we need to make the signal thread a
+         proper cthread.  Otherwise it cannot use mutex_lock et al, which
+         will be the cthreads versions.  Various of the message port RPC
+         handlers need to take locks, so we need to be able to call into
+         cthreads code and meet its assumptions about how our thread and
+         its stack are arranged.  Since cthreads puts it there anyway,
+         we'll let the signal thread's per-thread variables be found as for
+         any normal cthread, and just leave the magic __hurd_sigthread_*
+         values all zero so they'll be ignored.  */
+#pragma weak __cthread_detach
+#pragma weak __pthread_getattr_np
+#pragma weak __pthread_attr_getstack
+      __cthread_t thread = __cthread_fork (
+                            (cthread_fn_t) &_hurd_msgport_receive, 0);
+      __cthread_detach (thread);
+
+      if (__pthread_getattr_np)
+       {
+         /* Record signal thread stack layout for fork() */
+         pthread_attr_t attr;
+         void *addr;
+         size_t size;
+
+         __pthread_getattr_np ((pthread_t) thread, &attr);
+         __pthread_attr_getstack (&attr, &addr, &size);
+         __hurd_sigthread_stack_base = (uintptr_t) addr;
+         __hurd_sigthread_stack_end = __hurd_sigthread_stack_base + size;
+       }
+
+      /* XXX We need the thread port for the signal thread further on
+         in this thread (see hurdfault.c:_hurdsigfault_init).
+         Therefore we block until _hurd_msgport_thread is initialized
+         by the newly created thread.  This really shouldn't be
+         necessary; we should be able to fetch the thread port for a
+         cthread from here.  */
+      while (_hurd_msgport_thread == 0)
+       __swtch_pri (0);
+    }
 
   /* Receive exceptions on the signal port.  */
+#ifdef TASK_EXCEPTION_PORT
   __task_set_special_port (__mach_task_self (),
                           TASK_EXCEPTION_PORT, _hurd_msgport);
+#elif defined (EXC_MASK_ALL)
+  __task_set_exception_ports (__mach_task_self (),
+                             EXC_MASK_ALL & ~(EXC_MASK_SYSCALL
+                                              | EXC_MASK_MACH_SYSCALL
+                                              | EXC_MASK_RPC_ALERT),
+                             _hurd_msgport,
+                             EXCEPTION_DEFAULT, MACHINE_THREAD_STATE);
+#else
+# error task_set_exception_port?
+#endif
+
+  /* Sanity check.  Any pending, unblocked signals should have been
+     taken by our predecessor incarnation (i.e. parent or pre-exec state)
+     before packing up our init ints.  This assert is last (not above)
+     so that signal handling is all set up to handle the abort.  */
+  assert ((ss->pending &~ ss->blocked) == 0);
 }
 \f                              /* XXXX */
 /* Reauthenticate with the proc server.  */
@@ -1169,14 +1368,24 @@ reauth_proc (mach_port_t new)
   ref = __mach_reply_port ();
   if (! HURD_PORT_USE (&_hurd_ports[INIT_PORT_PROC],
                       __proc_reauthenticate (port, ref,
-                                             MACH_MSG_TYPE_MAKE_SEND) ||
-                      __auth_user_authenticate (new, port, ref,
-                                                MACH_MSG_TYPE_MAKE_SEND,
-                                                &ignore))
+                                             MACH_MSG_TYPE_MAKE_SEND)
+                      || __auth_user_authenticate (new, ref,
+                                                   MACH_MSG_TYPE_MAKE_SEND,
+                                                   &ignore))
       && ignore != MACH_PORT_NULL)
     __mach_port_deallocate (__mach_task_self (), ignore);
   __mach_port_destroy (__mach_task_self (), ref);
 
+  /* Set the owner of the process here too. */
+  __mutex_lock (&_hurd_id.lock);
+  if (!_hurd_check_ids ())
+    HURD_PORT_USE (&_hurd_ports[INIT_PORT_PROC],
+                  __proc_setowner (port,
+                                   (_hurd_id.gen.nuids
+                                    ? _hurd_id.gen.uids[0] : 0),
+                                   !_hurd_id.gen.nuids));
+  __mutex_unlock (&_hurd_id.lock);
+
   (void) &reauth_proc;         /* Silence compiler warning.  */
 }
 text_set_element (_hurd_reauth_hook, reauth_proc);
@@ -1187,14 +1396,36 @@ text_set_element (_hurd_reauth_hook, reauth_proc);
 const char *
 _hurdsig_getenv (const char *variable)
 {
-  if (_hurdsig_catch_fault (SIGSEGV))
+  if (__libc_enable_secure)
+    return NULL;
+
+  if (_hurdsig_catch_memory_fault (__environ))
     /* We bombed in getenv.  */
     return NULL;
   else
     {
-      const char *value = getenv (variable);
-      /* Fault now if VALUE is a bogus string.  */
-      (void) strlen (value);
+      const size_t len = strlen (variable);
+      char *value = NULL;
+      char *volatile *ep = __environ;
+      while (*ep)
+       {
+         const char *p = *ep;
+         _hurdsig_fault_preemptor.first = (long int) p;
+         _hurdsig_fault_preemptor.last = VM_MAX_ADDRESS;
+         if (! strncmp (p, variable, len) && p[len] == '=')
+           {
+             size_t valuelen;
+             p += len + 1;
+             valuelen = strlen (p);
+             _hurdsig_fault_preemptor.last = (long int) (p + valuelen);
+             value = malloc (++valuelen);
+             if (value)
+               memcpy (value, p, valuelen);
+             break;
+           }
+         _hurdsig_fault_preemptor.first = (long int) ++ep;
+         _hurdsig_fault_preemptor.last = (long int) (ep + 1);
+       }
       _hurdsig_end_catch_fault ();
       return value;
     }