/* 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 #include 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 /* 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); }