]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - hurd/hurd/signal.h
Update copyright dates with scripts/update-copyrights
[thirdparty/glibc.git] / hurd / hurd / signal.h
index d28d1c6c11eb3481f47af6671e596e9c4719af53..37b7dec5a0f9c7022c19043ac1de42bcdf5252db 100644 (file)
@@ -1,31 +1,27 @@
 /* Implementing POSIX.1 signals under the Hurd.
-Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.
-This file is part of the GNU C Library.
+   Copyright (C) 1993-2021 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
+   <https://www.gnu.org/licenses/>.  */
 
 #ifndef        _HURD_SIGNAL_H
 
 #define        _HURD_SIGNAL_H  1
 #include <features.h>
-/* Make sure <signal.h> is going to define NSIG.  */
-#ifndef __USE_GNU
-#error "Must have `_GNU_SOURCE' feature test macro to use this file"
-#endif
 
+#define __need_size_t
 #define __need_NULL
 #include <stddef.h>
 
@@ -35,35 +31,66 @@ Cambridge, MA 02139, USA.  */
 #include <hurd/hurd_types.h>
 #include <signal.h>
 #include <errno.h>
+#include <bits/types/error_t.h>
+#include <bits/types/stack_t.h>
+#include <bits/types/sigset_t.h>
+#include <bits/sigaction.h>
 #include <hurd/msg.h>
 
-#include <cthreads.h>          /* For `struct mutex'.  */
+#include <setjmp.h>            /* For `jmp_buf'.  */
 #include <spin-lock.h>
-#include <hurd/threadvar.h>    /* We cache sigstate in a threadvar.  */
+struct hurd_signal_preemptor;  /* <hurd/sigpreempt.h> */
+#if defined __USE_EXTERN_INLINES && defined _LIBC
+# if IS_IN (libc) || IS_IN (libpthread)
+#  include <sigsetops.h>
+# endif
+#endif
+
+
+/* Full details of a signal.  */
+struct hurd_signal_detail
+  {
+    /* Codes from origination Mach exception_raise message.  */
+    integer_t exc, exc_code, exc_subcode;
+    /* Sigcode as passed or computed from exception codes.  */
+    integer_t code;
+    /* Error code as passed or extracted from exception codes.  */
+    error_t error;
+  };
 
 
 /* Per-thread signal state.  */
 
 struct hurd_sigstate
   {
-    spin_lock_t lock;          /* Locks most of the rest of the structure.  */
+    spin_lock_t critical_section_lock; /* Held if in critical section.  */
 
-    int critical_section;      /* Nonzero if in critical section.  */
+    spin_lock_t lock;          /* Locks most of the rest of the structure.  */
 
+    /* The signal state holds a reference on the thread port.  */
     thread_t thread;
+
     struct hurd_sigstate *next; /* Linked-list of thread sigstates.  */
 
     sigset_t blocked;          /* What signals are blocked.  */
     sigset_t pending;          /* Pending signals, possibly blocked.  */
-    struct sigaction actions[NSIG];
-    struct sigaltstack sigaltstack;
-    struct
-      {
-       /* For each signal that may be pending, the
-          sigcode and error code to deliver it with.  */
-       long int code;
-       error_t error;
-      } pending_data[NSIG];
+
+    /* Signal handlers.  ACTIONS[0] is used to mark the threads with POSIX
+       semantics: if sa_handler is SIG_IGN instead of SIG_DFL, this thread
+       will receive global signals and use the process-wide action vector
+       instead of this one.  */
+    struct sigaction actions[_NSIG];
+
+    stack_t sigaltstack;
+
+    /* Chain of thread-local signal preemptors; see <hurd/sigpreempt.h>.
+       Each element of this chain is in local stack storage, and the chain
+       parallels the stack: the head of this chain is in the innermost
+       stack frame, and each next element in an outermore frame.  */
+    struct hurd_signal_preemptor *preemptors;
+
+    /* For each signal that may be pending, the details to deliver it with.  */
+    struct hurd_signal_detail pending_data[_NSIG];
 
     /* If `suspended' is set when this thread gets a signal,
        the signal thread sends an empty message to it.  */
@@ -84,15 +111,19 @@ struct hurd_sigstate
        <hurd/userlink.h> for details.  This member is only used by the
        thread itself, and always inside a critical section.  */
     struct hurd_userlink *active_resources;
+
+    /* These are locked normally.  */
+    int cancel;                        /* Flag set by hurd_thread_cancel.  */
+    void (*cancel_hook) (void);        /* Called on cancellation.  */
   };
 
 /* Linked list of states of all threads whose state has been asked for.  */
 
 extern struct hurd_sigstate *_hurd_sigstates;
 
-extern struct mutex _hurd_siglock; /* Locks _hurd_sigstates.  */
-
-/* Get the sigstate of a given thread, taking its lock.  */
+/* Get the sigstate of a given thread.  If there was no sigstate for
+   the thread, one is created, and the thread gains a reference.  If
+   the given thread is MACH_PORT_NULL, return the global sigstate.  */
 
 extern struct hurd_sigstate *_hurd_thread_sigstate (thread_t);
 
@@ -105,15 +136,45 @@ extern struct hurd_sigstate *_hurd_self_sigstate (void)
        by different threads.  */
      __attribute__ ((__const__));
 
-_EXTERN_INLINE struct hurd_sigstate *
+/* Process-wide signal state.  */
+
+extern struct hurd_sigstate *_hurd_global_sigstate;
+
+/* Mark the given thread as a process-wide signal receiver.  */
+
+extern void _hurd_sigstate_set_global_rcv (struct hurd_sigstate *ss);
+
+/* A thread can either use its own action vector and pending signal set
+   or use the global ones, depending on wether it has been marked as a
+   global receiver. The accessors below take that into account.  */
+
+extern void _hurd_sigstate_lock (struct hurd_sigstate *ss);
+extern struct sigaction *_hurd_sigstate_actions (struct hurd_sigstate *ss);
+extern sigset_t _hurd_sigstate_pending (const struct hurd_sigstate *ss);
+extern void _hurd_sigstate_unlock (struct hurd_sigstate *ss);
+
+/* Used by libpthread to remove stale sigstate structures.  */
+extern void _hurd_sigstate_delete (thread_t thread);
+
+#ifndef _HURD_SIGNAL_H_EXTERN_INLINE
+#define _HURD_SIGNAL_H_EXTERN_INLINE __extern_inline
+#endif
+
+#if defined __USE_EXTERN_INLINES && defined _LIBC
+# if IS_IN (libc)
+_HURD_SIGNAL_H_EXTERN_INLINE struct hurd_sigstate *
 _hurd_self_sigstate (void)
 {
-  struct hurd_sigstate **location =
-    (void *) __hurd_threadvar_location (_HURD_THREADVAR_SIGSTATE);
-  if (*location == NULL)
-    *location = _hurd_thread_sigstate (__mach_thread_self ());
-  return *location;
+  if (THREAD_GETMEM (THREAD_SELF, _hurd_sigstate) == NULL)
+    {
+      thread_t self = __mach_thread_self ();
+      THREAD_SETMEM (THREAD_SELF, _hurd_sigstate, _hurd_thread_sigstate (self));
+      __mach_port_deallocate (__mach_task_self (), self);
+    }
+  return THREAD_GETMEM (THREAD_SELF, _hurd_sigstate);
 }
+# endif
+#endif
 \f
 /* Thread listening on our message port; also called the "signal thread".  */
 
@@ -124,12 +185,6 @@ extern thread_t _hurd_msgport_thread;
 
 extern mach_port_t _hurd_msgport;
 
-
-/* Thread to receive process-global signals.  */
-
-extern thread_t _hurd_sigthread;
-
-
 /* Resource limit on core file size.  Enforced by hurdsig.c.  */
 extern int _hurd_core_limit;
 \f
@@ -138,40 +193,59 @@ extern int _hurd_core_limit;
    A critical section is a section of code which cannot safely be interrupted
    to run a signal handler; for example, code that holds any lock cannot be
    interrupted lest the signal handler try to take the same lock and
-   deadlock result.  */
+   deadlock result.
+
+   As a consequence, a critical section will see its RPCs return EINTR, even if
+   SA_RESTART is set!  In that case, the critical section should be left, so
+   that the handler can run, and the whole critical section be tried again, to
+   avoid unexpectingly exposing EINTR to the application.  */
 
-_EXTERN_INLINE void *
+extern void *_hurd_critical_section_lock (void);
+
+#if defined __USE_EXTERN_INLINES && defined _LIBC
+# if IS_IN (libc)
+_HURD_SIGNAL_H_EXTERN_INLINE void *
 _hurd_critical_section_lock (void)
 {
-  struct hurd_sigstate **location =
-    (void *) __hurd_threadvar_location (_HURD_THREADVAR_SIGSTATE);
-  struct hurd_sigstate *ss = *location;
-  if (ss == NULL)
-    /* The thread variable is unset; this must be the first time we've
-       asked for it.  In this case, the critical section flag cannot
-       possible already be set.  Look up our sigstate structure the slow
-       way; this locks the sigstate lock.  */
-    ss = *location = _hurd_thread_sigstate (__mach_thread_self ());
-  else
-    __spin_lock (&ss->lock);
+  struct hurd_sigstate *ss;
 
-  if (ss->critical_section)
+#ifdef __LIBC_NO_TLS
+  if (__LIBC_NO_TLS ())
+    /* TLS is currently initializing, no need to enter critical section.  */
+    return NULL;
+#endif
+
+  ss = THREAD_GETMEM (THREAD_SELF, _hurd_sigstate);
+  if (ss == NULL)
     {
-      /* We are already in a critical section, so do nothing.  */
-      __spin_unlock (&ss->lock);
-      return NULL;
+      thread_t self = __mach_thread_self ();
+
+      /* The thread variable is unset; this must be the first time we've
+        asked for it.  In this case, the critical section flag cannot
+        possible already be set.  Look up our sigstate structure the slow
+        way.  */
+      ss = _hurd_thread_sigstate (self);
+      THREAD_SETMEM (THREAD_SELF, _hurd_sigstate, ss);
+      __mach_port_deallocate (__mach_task_self (), self);
     }
 
-  /* Set the critical section flag so no signal handler will run.  */
-  ss->critical_section = 1;
-  __spin_unlock (&ss->lock);
+  if (! __spin_try_lock (&ss->critical_section_lock))
+    /* We are already in a critical section, so do nothing.  */
+    return NULL;
 
-  /* Return our sigstate pointer; this will be passed to
-     _hurd_critical_section_unlock to clear the critical section flag. */
+  /* With the critical section lock held no signal handler will run.
+     Return our sigstate pointer; this will be passed to
+     _hurd_critical_section_unlock to unlock it.  */
   return ss;
 }
+# endif
+#endif
 
-_EXTERN_INLINE void
+extern void _hurd_critical_section_unlock (void *our_lock);
+
+#if defined __USE_EXTERN_INLINES && defined _LIBC
+# if IS_IN (libc)
+_HURD_SIGNAL_H_EXTERN_INLINE void
 _hurd_critical_section_unlock (void *our_lock)
 {
   if (our_lock == NULL)
@@ -179,21 +253,22 @@ _hurd_critical_section_unlock (void *our_lock)
     return;
   else
     {
-      /* It was us who acquired the critical section lock.  Clear the
-        critical section flag.  */
-      struct hurd_sigstate *ss = our_lock;
+      /* It was us who acquired the critical section lock.  Unlock it.  */
+      struct hurd_sigstate *ss = (struct hurd_sigstate *) our_lock;
       sigset_t pending;
-      __spin_lock (&ss->lock);
-      ss->critical_section = 0;
-      pending = ss->pending & ~ss->blocked;
-      __spin_unlock (&ss->lock);
-      if (pending)
+      _hurd_sigstate_lock (ss);
+      __spin_unlock (&ss->critical_section_lock);
+      pending = _hurd_sigstate_pending(ss) & ~ss->blocked;
+      _hurd_sigstate_unlock (ss);
+      if (! __sigisemptyset (&pending))
        /* There are unblocked signals pending, which weren't
           delivered because we were in the critical section.
           Tell the signal thread to deliver them now.  */
-       __msg_sig_post (_hurd_msgport, 0, __mach_task_self ());
+       __msg_sig_post (_hurd_msgport, 0, 0, __mach_task_self ());
     }
 }
+# endif
+#endif
 
 /* Convenient macros for simple uses of critical sections.
    These two must be used as a pair at the same C scoping level.  */
@@ -203,35 +278,42 @@ _hurd_critical_section_unlock (void *our_lock)
 #define HURD_CRITICAL_END \
       _hurd_critical_section_unlock (__hurd_critical__); } while (0)
 \f
-/* Initialize the signal code, and start the signal thread.  */
+/* Initialize the signal code, and start the signal thread.
+   Arguments give the "init ints" from exec_startup.  */
 
-extern void _hurdsig_init (void);
+extern void _hurdsig_init (const int *intarray, size_t intarraysize);
 
 /* Initialize proc server-assisted fault recovery for the signal thread.  */
 
 extern void _hurdsig_fault_init (void);
 
-/* Raise a signal as described by SIGNO, SIGCODE and SIGERROR, on the
-   thread whose sigstate SS points to.  If SS is a null pointer, this
-   instead affects the calling thread.  */
+/* Raise a signal as described by SIGNO an DETAIL, on the thread whose
+   sigstate SS points to.  If SS is a null pointer, this instead affects
+   the calling thread.  */
 
-extern void _hurd_raise_signal (struct hurd_sigstate *ss,
-                               int signo, long int sigcode, int sigerror);
+extern int _hurd_raise_signal (struct hurd_sigstate *ss, int signo,
+                              const struct hurd_signal_detail *detail);
 
 /* Translate a Mach exception into a signal (machine-dependent).  */
 
-extern void _hurd_exception2signal (int exception, int code, int subcode,
-                                   int *signo, long int *sigcode, int *error);
+extern void _hurd_exception2signal (struct hurd_signal_detail *detail,
+                                   int *signo);
+
+/* Translate a Mach exception into a signal with a legacy sigcode.  */
+
+extern void _hurd_exception2signal_legacy (struct hurd_signal_detail *detail,
+                                          int *signo);
 
 
 /* Make the thread described by SS take the signal described by SIGNO and
-   SIGCODE.  If the process is traced, this will in fact stop with a SIGNO
+   DETAIL.  If the process is traced, this will in fact stop with a SIGNO
    as the stop signal unless UNTRACED is nonzero.  When the signal can be
    considered delivered, sends a sig_post reply message on REPLY_PORT
    indicating success.  SS is not locked.  */
 
 extern void _hurd_internal_post_signal (struct hurd_sigstate *ss,
-                                       int signo, long int sigcode, int error,
+                                       int signo,
+                                       struct hurd_signal_detail *detail,
                                        mach_port_t reply_port,
                                        mach_msg_type_name_t reply_port_type,
                                        int untraced);
@@ -245,79 +327,46 @@ extern void _hurd_internal_post_signal (struct hurd_sigstate *ss,
 
 struct machine_thread_all_state;
 extern struct sigcontext *
-_hurd_setup_sighandler (struct hurd_sigstate *ss, __sighandler_t handler,
-                       int signo, long int sigcode,
+_hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action,
+                       __sighandler_t handler,
+                       int signo, struct hurd_signal_detail *detail,
                        int rpc_wait, struct machine_thread_all_state *state);
 
 /* Function run by the signal thread to receive from the signal port.  */
 
-extern void _hurd_msgport_receive (void);
-
-/* STATE describes a thread that had intr_port set (meaning it was inside
-   HURD_EINTR_RPC), after it has been thread_abort'd.  It it looks to have
-   just completed a mach_msg_trap system call that returned
-   MACH_RCV_INTERRUPTED, return nonzero and set *PORT to the receive right
-   being waited on.  */
-
-extern int _hurdsig_rcv_interrupted_p (struct machine_thread_all_state *state,
-                                      mach_port_t *port);
+extern void *_hurd_msgport_receive (void *arg);
 
 /* Set up STATE with a thread state that, when resumed, is
    like `longjmp (_hurd_sigthread_fault_env, 1)'.  */
 
 extern void _hurd_initialize_fault_recovery_state (void *state);
 
+/* Set up STATE to do the equivalent of `longjmp (ENV, VAL);'.  */
+
+extern void _hurd_longjmp_thread_state (void *state, jmp_buf env, int value);
 
 /* Function run for SIGINFO when its action is SIG_DFL and the current
    process is the session leader.  */
 
 extern void _hurd_siginfo_handler (int);
 
+/* Replacement for mach_msg used in RPCs to provide Hurd interruption
+   semantics.  Args are all the same as for mach_msg.  intr-rpc.h arranges
+   for this version to be used automatically by the RPC stubs the library
+   builds in place of the normal mach_msg. */
+error_t _hurd_intr_rpc_mach_msg (mach_msg_header_t *msg,
+                                mach_msg_option_t option,
+                                mach_msg_size_t send_size,
+                                mach_msg_size_t rcv_size,
+                                mach_port_t rcv_name,
+                                mach_msg_timeout_t timeout,
+                                mach_port_t notify);
 
-/* Perform interruptible RPC CALL on PORT.
-   The call should use 
-   The args in CALL should be constant or local variable refs.
-   They may be evaluated many times, and must not change.
-   PORT must not be deallocated before this RPC is finished.  */
-#define        HURD_EINTR_RPC(port, call)                                            \
-  ({                                                                         \
-    __label__ __do_call;       /* Give this label block scope.  */           \
-    error_t __err;                                                           \
-    struct hurd_sigstate *__ss = _hurd_self_sigstate ();                     \
-    __do_call:                                                               \
-    /* Tell the signal thread that we are doing an interruptible RPC on              \
-       this port.  If we get a signal and should return EINTR, the signal     \
-       thread will set this variable to MACH_PORT_NULL.  The RPC might       \
-       return EINTR when some other thread gets a signal, in which case we    \
-       want to restart our call.  */                                         \
-    __ss->intr_port = (port);                                                \
-    /* A signal may arrive here, after intr_port is set, but before the              \
-       mach_msg system call.  The signal handler might do an interruptible    \
-       RPC, and clobber intr_port; then it would not be set properly when     \
-       we actually did send the RPC, and a later signal wouldn't interrupt    \
-       that RPC.  So, _hurd_setup_sighandler saves intr_port in the          \
-       sigcontext, and sigreturn restores it.  */                            \
-    switch (__err = (call))                                                  \
-      {                                                                              \
-      case EINTR:              /* RPC went out and was interrupted.  */      \
-      case MACH_SEND_INTERRUPTED: /* RPC didn't get out.  */                 \
-       if (__ss->intr_port != MACH_PORT_NULL)                                \
-         /* If this signal was for us and it should interrupt calls, the     \
-             signal thread will have cleared SS->intr_port.  Since it's not   \
-             cleared, the signal was for another thread, or SA_RESTART is     \
-             set.  Restart the interrupted call.  */                         \
-         goto __do_call;                                                     \
-       /* FALLTHROUGH */                                                     \
-      case MACH_RCV_PORT_DIED:                                               \
-       /* Server didn't respond to interrupt_operation,                      \
-          so the signal thread destroyed the reply port.  */                 \
-       __err = EINTR;                                                        \
-       break;                                                                \
-      default:                 /* Quiet -Wswitch-enum.  */                   \
-      }                                                                              \
-    __ss->intr_port = MACH_PORT_NULL;                                        \
-    __err;                                                                   \
-  })                                                                         \
+
+/* Milliseconds to wait for an interruptible RPC to return after
+   `interrupt_operation'.  */
+
+extern mach_msg_timeout_t _hurd_interrupted_rpc_timeout;
 
 
 /* Mask of signals that cannot be caught, blocked, or ignored.  */
@@ -355,49 +404,25 @@ extern void _hurd_siginfo_handler (int);
     do                                                                       \
       {                                                                              \
        /* Get the message port.  */                                          \
-       if (__err = (fetch_msgport_expr))                                     \
+       __err = (error_t) (fetch_msgport_expr);                               \
+       if (__err)                                                            \
          break;                                                              \
        /* Get the reference port.  */                                        \
-       if (__err = (fetch_refport_expr))                                     \
+       __err = (error_t) (fetch_refport_expr);                               \
+       if (__err)                                                            \
          {                                                                   \
            /* Couldn't get it; deallocate MSGPORT and fail.  */              \
            __mach_port_deallocate (__mach_task_self (), msgport);            \
            break;                                                            \
          }                                                                   \
-       __err = (rpc_expr);                                                   \
+       __err = (error_t) (rpc_expr);                                         \
        __mach_port_deallocate (__mach_task_self (), msgport);                \
        if ((dealloc_refport) && refport != MACH_PORT_NULL)                   \
          __mach_port_deallocate (__mach_task_self (), refport);              \
-      } while (__err == MACH_SEND_INVALID_DEST ||                            \
-              __err == MIG_SERVER_DIED);                                     \
+      } while (__err == MACH_SEND_INVALID_DEST                               \
+              || __err == MIG_SERVER_DIED);                                  \
     __err;                                                                   \
 })
-\f
-/* Some other parts of the library need to preempt signals, to detect
-   errors that should not result in a POSIX signal.  For example, when
-   some mapped region of memory is used, an extraneous SIGSEGV might be
-   generated when the mapping server returns an error for a page fault.  */
-
-struct hurd_signal_preempt
-  {
-    /* Function to examine a thread receiving a given signal.  The handler
-       is called even for blocked signals.  This function is run in the
-       signal thread, with THREAD's sigstate locked; it should be as simple
-       and robust as possible.  THREAD is the thread which is about to
-       receive the signal.  SIGNO and SIGCODE would be passed to the normal
-       handler.
-
-       If the return value is SIG_DFL, normal signal processing continues.
-       If it is SIG_IGN, the signal is ignored.
-       Any other value is used in place of the normal handler.  */
-    sighandler_t (*handler) (thread_t thread,
-                            int signo, long int sigcode, int sigerror);
-    long int first, last;      /* Range of sigcodes this handler wants.  */
-    struct hurd_signal_preempt *next; /* Next handler on the chain. */
-  };
-
-extern struct hurd_signal_preempt *_hurd_signal_preempt[NSIG];
-extern struct mutex _hurd_signal_preempt_lock;
 
 
 #endif /* hurd/signal.h */