Files
2024-02-19 00:24:47 -05:00

487 lines
13 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Copyright (C) 1991, 1992 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 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.
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. */
#include <hurd.h>
#include <gnu-stabs.h>
struct mutex _hurd_siglock;
int _hurd_stopped;
/* Port that receives signals and other miscellaneous messages. */
mach_port_t _hurd_sigport;
/* Thread which receives task-global signals. */
thread_t _hurd_sigthread;
/* Linked-list of per-thread signal state. */
struct _hurd_sigstate *_hurd_sigstates;
struct _hurd_sigstate *
_hurd_thread_sigstate (thread_t thread)
{
struct _hurd_sigstate *ss;
__mutex_lock (&_hurd_siglock);
for (ss = _hurd_sigstates; ss != NULL; ss = ss->next)
if (ss->thread == thread)
break;
if (ss == NULL)
{
ss = calloc (1, sizeof (*ss)); /* Zero-initialized. */
if (ss == NULL)
__libc_fatal ("hurd: Can't allocate thread sigstate\n");
ss->thread = thread;
__mutex_init (&ss->lock);
ss->next = _hurd_sigstates;
_hurd_sigstates = ss;
}
__mutex_lock (&ss->lock);
__mutex_unlock (&_hurd_siglock);
return ss;
}
#include <hurd/core.h>
/* Limit on size of core files. */
int _hurd_core_limit;
/* Call the core server to mummify us before we die.
Returns nonzero if a core file was written. */
static inline int
write_corefile (int signo, int sigcode)
{
error_t err;
mach_port_t coreserver;
int dealloc_crdir, dealloc_ccdir;
file_t crdir, ccdir;
if (_hurd_core_limit == 0)
/* User doesn't want a core. */
return 0;
coreserver = __path_lookup (_SERVERS_CORE, 0, 0);
if (coreserver == MACH_PORT_NULL)
return 0;
ccdir = _hurd_port_get (&_hurd_ccdir, &dealloc_ccdir);
crdir = _hurd_port_get (&_hurd_crdir, &dealloc_crdir);
err = __hurd_path_lookup (crdir, ccdir, "core"
FS_LOOKUP_WRITE|FS_LOOKUP_CREATE,
0666 & ~_hurd_umask,
&file);
_hurd_port_free (crdir, &dealloc_crdir);
if (!err)
{
err = __core_dump_task (coreserver,
__mach_task_self (),
file,
signo, sigcode,
getenv ("GNUTARGET"));
__mach_port_deallocate (__mach_task_self (), coreserver);
if (!err && _hurd_core_limit != RLIM_INFINITY)
{
io_statbuf_t stb;
err = __io_stat (file, &stb);
if (!err && stb.stb_size > _hurd_core_limit)
err = EFBIG;
}
__mach_port_deallocate (__mach_task_self (), file);
if (err)
(void) __dir_unlink (ccdir, "core");
}
_hurd_port_free (ccdir, &dealloc_ccdir);
return !err;
}
extern const size_t _hurd_thread_state_count;
/* How long to give servers to respond to
interrupt_operation before giving up on them. */
mach_msg_timeout_t _hurd_interrupt_timeout = 1000; /* One second. */
/* SS->thread is suspended. Fills STATE in with its registers.
SS->lock is held and kept. */
static inline void
abort_rpcs (struct _hurd_sigstate *ss, int signo, void *state)
{
if (ss->intr_port != MACH_PORT_NULL)
{
/* This is the address the PC will be at if the thread
is waiting for a mach_msg syscall to return. */
extern const int __mach_msg_trap_syscall_pc;
extern error_t _hurd_thread_state (thread_t, void *state);
extern int *_hurd_thread_pc (void *state);
/* Abort whatever the thread is doing.
If it is in the mach_msg syscall doing the send,
the syscall will return MACH_SEND_INTERRUPTED. */
__thread_abort (ss->thread);
_hurd_thread_state (ss->thread, state);
if (_hurd_thread_pc (state) == &__mach_msg_trap_syscall_pc)
{
/* The thread was waiting for the RPC to return.
Abort the operation. The RPC will return EINTR. */
struct
{
mach_msg_header_t header;
mach_msg_type_t type;
kern_return_t retcode;
} msg;
kern_return_t err;
msg.header.msgh_request_port = ss->intr_port;
msg.header.msgh_reply_port = __mach_reply_port ();
msg.header.msgh_seqno = 0;
msg.header.msgh_id = 33000; /* interrupt_operation XXX */
err = __mach_msg (&msg.header,
MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_TIMEOUT,
sizeof (msg.header), sizeof (msg),
msg.header.msgh_reply_port,
_hurd_interrupt_timeout,
MACH_PORT_NULL);
if (err != MACH_MSG_SUCCESS)
/* The interrupt didn't work.
Destroy the receive right the thread is blocked on. */
__mach_port_destroy (__mach_task_self (),
/* XXX */
_hurd_thread_reply_port (ss->thread));
else
/* In case the server returned something screwy. */
__mach_msg_destroy (&msg.header);
/* Tell the thread whether it should restart the
operation or return EINTR when it wakes up. */
ss->intr_restart = ss->actions[signo].sa_flags & SA_RESTART;
}
/* If the thread is anywhere before the system call trap,
it will start the operation after the signal is handled.
If the thread is after the system call trap, but before it has
cleared SS->intr_port, the operation is already finished. */
}
}
/* Abort the RPCs being run by all threads but this one;
all other threads should be suspended. */
static inline void
abort_all_rpcs (int signo, void *state)
{
thread_t me = __mach_thread_self ();
thread_t *threads;
size_t nthreads, i;
__task_threads (__mach_task_self (), &threads, &nthreads);
for (i = 0; i < nthreads; ++i)
{
if (threads[i] != me)
{
struct _hurd_sigstate *ss = _hurd_thread_sigstate (*nthreads);
abort_rpcs (ss, signo, state);
__mutex_unlock (&ss->lock);
}
__mach_port_deallocate (__mach_task_self (), threads[i]);
}
}
/* Deliver a signal.
SS->lock is held on entry and released before return. */
error_t
_hurd_internal_post_signal (reply_port_t reply,
struct _hurd_sigstate *ss,
int signo,
int sigcode,
sigset_t *restore_blocked)
{
char thread_state[_hurd_thread_state_count];
enum { stop, ignore, core, term } act;
if (ss->actions[signo].sa_handler == SIG_DFL)
switch (signo)
{
case 0:
/* A sig_post msg with SIGNO==0 is sent to
tell us to check for pending signals. */
act = ignore;
break;
case SIGTTIN:
case SIGTTOU:
case SIGSTOP:
case SIGTSTP:
ss->pending &= ~sigmask (SIGCONT);
act = stop;
break;
case SIGCONT:
ss->pending &= ~(sigmask (SIGSTOP) | sigmask (SIGTSTP) |
sigmask (SIGTTIN) | sigmask (SIGTTOU));
/* Fall through. */
case SIGIO:
case SIGURG:
case SIGCHLD:
case SIGWINCH:
act = ignore;
break;
case SIGQUIT:
case SIGILL:
case SIGTRAP:
case SIGIOT:
case SIGEMT:
case SIGFPE:
case SIGBUS:
case SIGSEGV:
case SIGSYS:
act = core;
break;
default:
act = term;
break;
}
else if (ss->actions[signo].sa_handler == SIG_IGN)
act = ignore;
else
act = handle;
if (_hurd_orphaned && (signo == SIGTTIN || signo == SIGTTOU) && act == stop)
{
sigcode = signo;
signo = SIGKILL;
act = term;
}
/* Handle receipt of a blocked signal. */
if ((__sigismember (signo, &ss->blocked) && act != ignore) ||
(signo != SIGKILL && _hurd_stopped))
{
__sigaddmember (signo, &ss->pending);
/* Save the code to be given to the handler when SIGNO is unblocked. */
ss->sigcodes[signo] = sigcode;
act = ignore;
}
if (restore_blocked != NULL)
ss->blocked = *restore_blocked;
switch (act)
{
case stop:
if (reply != MACH_PORT_NULL)
__sig_post_reply (reply, POSIX_SUCCESS);
_HURD_PORT_USE
(&_hurd_proc,
({
/* Hold the siglock while stopping other threads to be
sure it is not held by another thread afterwards. */
__mutex_unlock (&ss->lock);
__mutex_lock (&_hurd_siglock);
__proc_dostop (port, __mach_thread_self ());
__mutex_unlock (&_hurd_siglock);
abort_all_rpcs (signo, thread_state);
__proc_markstop (port, signo);
}));
_hurd_stopped = 1;
__mutex_lock (&ss->lock);
if (ss->suspended)
/* There is a sigsuspend waiting. Tell it to wake up. */
__condition_signal (&ss->arrived);
else
__mutex_unlock (&ss->lock);
return MIG_NO_REPLY; /* Already replied. */
case ignore:
if (reply != MACH_PORT_NULL)
__sig_post_reply (reply, POSIX_SUCCESS);
break;
case core:
case term:
if (reply != MACH_PORT_NULL)
__sig_post_reply (reply, POSIX_SUCCESS);
_HURD_PORT_USE
(&_hurd_proc,
({
__proc_dostop (port, __mach_thread_self ());
abort_all_rpcs (signo, thread_state);
__proc_exit (port,
(W_EXITCODE (0, signo) |
(act == core && write_corefile (signo, sigcode) ?
WCOREDUMP : 0)))
}));
__task_terminate (__mach_task_self ());
return MIG_NO_REPLY; /* Yeah, right. */
case handle:
if (reply != MACH_PORT_NULL)
__sig_post_reply (reply, POSIX_SUCCESS);
__thread_suspend (ss->thread);
abort_rpcs (ss, signo, thread_state);
{
const sigset_t blocked = ss->blocked;
ss->blocked |= __sigmask (signo) | ss->actions[signo].sa_mask;
_hurd_run_sighandler (ss->thread, signo, sigcode,
ss->actions[signo].sa_handler,
ss->actions[signo].sa_flags,
blocked,
&ss->sigstack,
thread_state);
}
__thread_resume (ss->thread);
}
/* We get here only if we are handling or ignoring the signal;
otherwise we are stopped or dead by now. We still hold SS->lock.
Check for pending signals, and loop to post them. */
for (signo = 1; signo < NSIG; ++signo)
if (__sigismember (signo, &ss->pending))
{
__sigdelmember (signo, &ss->pending);
return _hurd_internal_post_signal (MACH_PORT_NULL, ss,
signo, ss->sigcodes[signo],
NULL);
}
if (ss->suspended)
/* There is a sigsuspend waiting. Tell it to wake up. */
__condition_signal (&ss->arrived);
else
__mutex_unlock (&ss->lock);
return MIG_NO_REPLY;
}
/* Called by the proc server to send a signal. */
error_t
__sig_post (sigthread_t me,
mig_reply_port_t reply,
int signo,
mach_port_t refport)
{
struct _hurd_sigstate *ss;
if (signo < 0 || signo >= NSIG)
return EINVAL;
if (refport == __mach_task_self ())
/* Can send any signal. */
goto win;
else if (refport == _hurd_cttyport)
switch (signo)
{
case SIGINT:
case SIGQUIT:
case SIGTSTP:
case SIGHUP:
goto win;
}
else
{
static mach_port_t sessport = MACH_PORT_NULL;
if (sessport == MACH_PORT_NULL)
_HURD_PORT_USE (&_hurd_proc,
__proc_getsidport (port, &sessport));
if (sessport != MACH_PORT_NULL &&
refport == sessport && signo == SIGCONT)
goto win;
}
/* XXX async io? */
return EPERM;
win:
ss = _hurd_thread_sigstate (_hurd_sigthread);
return _hurd_internal_post_signal (reply, ss, signo, 0, NULL);
}
/* Called by the exception handler to take a signal. */
void
_hurd_exc_post_signal (thread_t thread, int signo, int sigcode)
{
(void) _hurd_internal_post_signal (MACH_PORT_NULL,
_hurd_thread_sigstate (thread),
signo, sigcode, NULL);
}
void
_hurdsig_init (void)
{
thread_t sigthread;
__mutex_init (&_hurd_siglock);
if (_hurd_sigport == MACH_PORT_NULL)
if (err = __mach_port_allocate (__mach_task_self (),
MACH_PORT_RIGHT_RECEIVE,
&_hurd_sigport))
__libc_fatal ("hurd: Can't create signal port receive right\n");
if (err = __thread_create (__mach_task_self (), &sigthread))
__libc_fatal ("hurd: Can't create signal thread\n");
if (err = _hurd_start_sigthread (sigthread, _hurd_sigport_receive))
__libc_fatal ("hurd: Can't start signal thread\n");
_hurd_sigport_thread = sigthread;
/* Make a send right to the signal port. */
if (err = __mach_port_insert_right (__mach_task_self (),
_hurd_sigport,
MACH_PORT_RIGHT_MAKE_SEND))
__libc_fatal ("hurd: Can't create send right to signal port\n");
/* Receive exceptions on the signal port. */
__task_set_special_port (__mach_task_self (),
TASK_EXCEPTION,
_hurd_sigport);
}
/* Make PROCSERVER be our proc server port.
Tell the proc server that we exist. */
void
_hurd_proc_init (process_t procserver, char **argv)
{
mach_port_t oldsig, oldtask;
_hurd_port_init (&_hurd_proc, procserver);
/* Tell the proc server where our args and environment are. */
__proc_setprocargs (procserver, argv, __environ);
/* Initialize the signal code; Mach exceptions will become signals.
This sets _hurd_sigport; it must be run before _hurd_proc_init. */
_hurdsig_init ();
/* Give the proc server our task and signal ports. */
__proc_setports (procserver,
_hurd_sigport, __mach_task_self (),
&oldsig, &oldtask);
if (oldsig != MACH_PORT_NULL)
__mach_port_deallocate (__mach_task_self (), oldsig);
if (oldtask != MACH_PORT_NULL)
__mach_port_deallocate (__mach_task_self (), oldtask);
}