/* 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>
#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. */
<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);
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". */
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
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)
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. */
#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);
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. */
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 */