Merge glibc-ports into ports/ directory.
[platform/upstream/glibc.git] / hurd / hurdsig.c
index 7affb90..e1e0919 100644 (file)
@@ -1,29 +1,36 @@
-/* Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
-This file is part of the GNU C Library.
+/* Copyright (C) 1991,92,93,94,95,96,97,98,99,2000,2001,2002,2005,2008
+       Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
 
-The GNU C Library is free software; you can redistribute it and/or
-modify it under the terms of the GNU 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 <mach.h>
+#include <mach/thread_switch.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"
 
 const char *_hurdsig_getenv (const char *);
 
@@ -39,6 +46,11 @@ 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;
+unsigned long int *__hurd_sigthread_variables;
+
 /* Linked-list of per-thread signal state.  */
 struct hurd_sigstate *_hurd_sigstates;
 
@@ -74,11 +86,12 @@ _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->preemptors = NULL;
+      ss->suspended = MACH_PORT_NULL;
       ss->intr_port = MACH_PORT_NULL;
       ss->context = NULL;
 
@@ -113,28 +126,38 @@ _hurd_thread_sigstate (thread_t thread)
 
 #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 +174,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 +210,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 +224,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);
 }
@@ -235,12 +241,9 @@ interrupted_reply_port_location (struct machine_thread_all_state *thread_state,
   mach_port_t *portloc = (mach_port_t *) __hurd_threadvar_location_from_sp
     (_HURD_THREADVAR_MIG_REPLY, (void *) thread_state->basic.SP);
 
-  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 stack.  */
+    return NULL;
 
   /* Fault now if this pointer is bogus.  */
   *(volatile mach_port_t *) portloc = *portloc;
@@ -251,7 +254,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.
 
@@ -265,15 +272,16 @@ interrupted_reply_port_location (struct machine_thread_all_state *thread_state,
    incoming signal, returns the reply port to be received on.  Otherwise
    returns MACH_PORT_NULL.
 
+   SIGNO is used to find the applicable SA_RESTART bit.  If SIGNO is zero,
+   the RPC fails with EINTR instead of restarting (thread_cancel).
+
    *STATE_CHANGE is set nonzero if STATE->basic was modified and should
    be applied back to the thread if it might ever run again, else zero.  */
 
 mach_port_t
-_hurdsig_abort_rpcs (struct hurd_sigstate *ss, int signo, int sigthread, 
+_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_in_trap;
   mach_port_t rcv_port = MACH_PORT_NULL;
@@ -288,7 +296,7 @@ _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)
     {
@@ -318,7 +326,7 @@ _hurdsig_abort_rpcs (struct hurd_sigstate *ss, int signo, int sigthread,
 
        mach_port_t *reply = interrupted_reply_port_location (state,
                                                              sigthread);
-       error_t err = __interrupt_operation (intr_port);
+       error_t err = __interrupt_operation (intr_port, _hurdsig_interrupt_timeout);
 
        if (err)
          {
@@ -380,9 +388,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;
@@ -391,15 +399,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.  */
@@ -423,9 +442,11 @@ 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) | \
@@ -434,7 +455,7 @@ struct mutex _hurd_signal_preempt_lock;
 /* 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)
@@ -443,25 +464,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.  */
@@ -476,47 +502,113 @@ _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;
     }
+  /* Resume the process after a suspension.  */
+  void resume (void)
+    {
+      /* Resume the process from being stopped.  */
+      thread_t *threads;
+      mach_msg_type_number_t nthreads, i;
+      error_t err;
+
+      if (! _hurd_stopped)
+       return;
+
+      /* Tell the proc server we are continuing.  */
+      __USEPORT (PROC, __proc_mark_cont (port));
+      /* Fetch ports to all our threads and resume them.  */
+      err = __task_threads (__mach_task_self (), &threads, &nthreads);
+      assert_perror (err);
+      for (i = 0; i < nthreads; ++i)
+       {
+         if (threads[i] != _hurd_msgport_thread &&
+             (act != handle || threads[i] != ss->thread))
+           {
+             err = __thread_resume (threads[i]);
+             assert_perror (err);
+           }
+         err = __mach_port_deallocate (__mach_task_self (),
+                                       threads[i]);
+         assert_perror (err);
+       }
+      __vm_deallocate (__mach_task_self (),
+                      (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;
+    }
+
+  if (signo == 0)
+    {
+      if (untraced)
+       /* This is PTRACE_CONTINUE.  */
+       resume ();
+
+      /* This call is just to check for pending signals.  */
+      __spin_lock (&ss->lock);
+      goto check_pending_signals;
+    }
 
  post_signal:
 
   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);
-
-      handler = ss->actions[signo].sa_handler;
-
-      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)
@@ -530,6 +622,8 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
          return;
        }
 
+      handler = ss->actions[signo].sa_handler;
+
       if (handler == SIG_DFL)
        /* Figure out the default action for this signal.  */
        switch (signo)
@@ -595,7 +689,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)
@@ -604,35 +698,7 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
            ss->pending &= ~STOPSIGS;
 
          if (_hurd_stopped && act != stop && (untraced || signo == SIGCONT))
-           {
-             /* Resume the process from being stopped.  */
-             thread_t *threads;
-             mach_msg_type_number_t nthreads, i;
-             error_t err;
-             /* Tell the proc server we are continuing.  */
-             __USEPORT (PROC, __proc_mark_cont (port));
-             /* Fetch ports to all our threads and resume them.  */
-             err = __task_threads (__mach_task_self (), &threads, &nthreads);
-             assert_perror (err);
-             for (i = 0; i < nthreads; ++i)
-               {
-                 if (threads[i] != _hurd_msgport_thread &&
-                     (act != handle || threads[i] != ss->thread))
-                   {
-                     err = __thread_resume (threads[i]);
-                     assert_perror (err);
-                   }
-                 err = __mach_port_deallocate (__mach_task_self (),
-                                               threads[i]);
-                 assert_perror (err);
-               }
-             __vm_deallocate (__mach_task_self (),
-                              (vm_address_t) threads,
-                              nthreads * sizeof *threads);
-             _hurd_stopped = 0;
-             /* The thread that will run the handler is already suspended.  */
-             ss_suspended = 1;
-           }
+           resume ();
        }
     }
 
@@ -643,15 +709,14 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
       /* 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.
-     It matters that we test ACT first here, because we must never pass
-     SIGNO==0 to __sigismember.  */
-  if ((act != ignore && __sigismember (&ss->blocked, signo)) ||
+  /* 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))
     {
       mark_pending ();
@@ -667,7 +732,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
@@ -676,14 +742,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 */
 
@@ -702,7 +783,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);
@@ -727,7 +808,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)
          {
@@ -736,10 +817,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
@@ -754,7 +833,7 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
                ss->context = &ocontext;
              }
            _hurdsig_end_catch_fault ();
-           
+
            if (! machine_get_basic_state (ss->thread, &thread_state))
              goto sigbomb;
            loc = interrupted_reply_port_location (&thread_state, 1);
@@ -767,23 +846,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;
              }
@@ -791,8 +892,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;
@@ -831,11 +931,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).  */
@@ -851,12 +962,8 @@ _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.  The exception is
-     signal 0, which can be sent by a user thread to make us check for
-     pending signals.  In that case we want to deliver the pending signals
-     before replying.  */
-  if (signo != 0)
-    reply ();
+     consider it delivered and reply to the killer.  */
+  reply ();
 
   /* We get here unless the signal was fatal.  We still hold SS->lock.
      Check for pending signals, and loop to post them.  */
@@ -869,20 +976,22 @@ _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;
       }
 
+  check_pending_signals:
+    untraced = 0;
+
     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;
            }
@@ -898,8 +1007,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);
@@ -913,16 +1032,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,
@@ -964,6 +1078,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;
@@ -974,7 +1089,7 @@ signal_allowed (int signo, mach_port_t refport)
        /* A continue signal can be sent by anyone in the session.  */
        mach_port_t sessport;
        if (! __USEPORT (PROC, __proc_getsidport (port, &sessport)))
-         { 
+         {
            __mach_port_deallocate (__mach_task_self (), sessport);
            if (refport == sessport)
              goto win;
@@ -999,8 +1114,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;
@@ -1012,13 +1128,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;
       }
     }
@@ -1039,18 +1156,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.  */
@@ -1063,18 +1184,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.  */
@@ -1088,59 +1213,123 @@ 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);
 
-  if (err = __mach_port_allocate (__mach_task_self (),
-                                 MACH_PORT_RIGHT_RECEIVE,
-                                 &_hurd_msgport))
-    __libc_fatal ("hurd: Can't create message port receive right\n");
-  
+  err = __mach_port_allocate (__mach_task_self (),
+                             MACH_PORT_RIGHT_RECEIVE,
+                             &_hurd_msgport);
+  assert_perror (err);
+
   /* Make a send right to the signal port.  */
-  if (err = __mach_port_insert_right (__mach_task_self (),
-                                     _hurd_msgport,
-                                     _hurd_msgport,
-                                     MACH_MSG_TYPE_MAKE_SEND))
-    __libc_fatal ("hurd: Can't create send right to message port\n");
+  err = __mach_port_insert_right (__mach_task_self (),
+                                 _hurd_msgport,
+                                 _hurd_msgport,
+                                 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.  */
 
-  if (err = __thread_create (__mach_task_self (), &_hurd_msgport_thread))
-    __libc_fatal ("hurd: Can't create signal thread\n");
+  if (__hurd_threadvar_stack_mask == 0)
+    {
+      err = __thread_create (__mach_task_self (), &_hurd_msgport_thread);
+      assert_perror (err);
 
-  stacksize = __vm_page_size * 4; /* Small stack for signal thread.  */
-  if (err = __mach_setup_thread (__mach_task_self (), _hurd_msgport_thread,
+      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))
-    __libc_fatal ("hurd: Can't setup signal thread\n");
-
-  __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");
-
-  /* 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);
-
-  if (err = __thread_resume (_hurd_msgport_thread))
-    __libc_fatal ("hurd: Can't resume signal thread\n");
-    
-#if 0                          /* Don't confuse poor gdb.  */
+                                &stacksize);
+      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 threadvars for signal thread\n");
+      memset (__hurd_sigthread_variables, 0,
+             __hurd_threadvar_max * sizeof (unsigned long int));
+      __hurd_sigthread_variables[_HURD_THREADVAR_LOCALE]
+       = (unsigned long int) &_nl_global_locale;
+
+      /* 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);
+    }
+  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_fork
+#pragma weak cthread_detach
+      cthread_detach (cthread_fork ((cthread_fn_t) &_hurd_msgport_receive, 0));
+
+      /* 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.  */
@@ -1154,13 +1343,23 @@ reauth_proc (mach_port_t new)
   if (! HURD_PORT_USE (&_hurd_ports[INIT_PORT_PROC],
                       __proc_reauthenticate (port, ref,
                                              MACH_MSG_TYPE_MAKE_SEND) ||
-                      __auth_user_authenticate (new, port, ref,
+                      __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);
@@ -1171,14 +1370,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;
     }