2005-03-29 02:00:36 +04:00
|
|
|
/*
|
|
|
|
* Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de.
|
2004-11-12 20:21:14 +03:00
|
|
|
* Copyright 2002, Angelo Mottola, a.mottola@libero.it.
|
|
|
|
*
|
|
|
|
* Distributed under the terms of the MIT License.
|
|
|
|
*/
|
2002-10-23 21:31:10 +04:00
|
|
|
|
2004-10-13 18:54:51 +04:00
|
|
|
/* POSIX signals handling routines */
|
2004-09-01 16:05:09 +04:00
|
|
|
|
2002-10-26 02:36:08 +04:00
|
|
|
#include <OS.h>
|
2002-10-26 20:13:36 +04:00
|
|
|
#include <KernelExport.h>
|
2002-10-23 21:31:10 +04:00
|
|
|
#include <debug.h>
|
2005-02-10 05:53:48 +03:00
|
|
|
#include <debugger.h>
|
2002-10-23 21:31:10 +04:00
|
|
|
#include <thread.h>
|
2004-03-16 05:53:41 +03:00
|
|
|
#include <team.h>
|
2002-10-23 21:31:10 +04:00
|
|
|
#include <int.h>
|
|
|
|
#include <sem.h>
|
2003-01-27 06:11:45 +03:00
|
|
|
#include <ksignal.h>
|
2002-10-23 21:31:10 +04:00
|
|
|
#include <syscalls.h>
|
2005-02-10 05:53:48 +03:00
|
|
|
#include <user_debugger.h>
|
2002-10-23 21:31:10 +04:00
|
|
|
|
2004-01-26 05:01:46 +03:00
|
|
|
#include <stddef.h>
|
|
|
|
#include <string.h>
|
2002-10-23 21:31:10 +04:00
|
|
|
|
2004-11-12 20:21:14 +03:00
|
|
|
//#define TRACE_SIGNAL
|
|
|
|
#ifdef TRACE_SIGNAL
|
|
|
|
# define TRACE(x) dprintf x
|
|
|
|
#else
|
|
|
|
# define TRACE(x) ;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2003-01-27 16:55:57 +03:00
|
|
|
#define SIGNAL_TO_MASK(signal) (1LL << (signal - 1))
|
|
|
|
|
2002-11-17 07:49:14 +03:00
|
|
|
|
|
|
|
const char * const sigstr[NSIG] = {
|
2002-10-23 21:31:10 +04:00
|
|
|
"NONE", "HUP", "INT", "QUIT", "ILL", "CHLD", "ABRT", "PIPE",
|
|
|
|
"FPE", "KILL", "STOP", "SEGV", "CONT", "TSTP", "ALRM", "TERM",
|
|
|
|
"TTIN", "TTOU", "USR1", "USR2", "WINCH", "KILLTHR", "TRAP"
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2005-02-24 19:28:45 +03:00
|
|
|
static bool
|
2005-03-13 00:45:49 +03:00
|
|
|
notify_debugger(struct thread *thread, int signal, struct sigaction *handler,
|
|
|
|
bool deadly, cpu_status *state)
|
2005-02-24 19:28:45 +03:00
|
|
|
{
|
|
|
|
bool result;
|
2005-03-13 00:45:49 +03:00
|
|
|
uint64 signalMask = SIGNAL_TO_MASK(signal);
|
|
|
|
|
|
|
|
// first check the ignore signal masks the debugger specified for the thread
|
|
|
|
|
|
|
|
if (thread->debug_info.ignore_signals_once & signalMask) {
|
|
|
|
thread->debug_info.ignore_signals_once &= ~signalMask;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (thread->debug_info.ignore_signals & signalMask)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// deliver the event
|
2005-02-24 19:28:45 +03:00
|
|
|
|
|
|
|
RELEASE_THREAD_LOCK();
|
|
|
|
restore_interrupts(*state);
|
|
|
|
|
|
|
|
result = user_debug_handle_signal(signal, handler, deadly);
|
|
|
|
|
|
|
|
*state = disable_interrupts();
|
|
|
|
GRAB_THREAD_LOCK();
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-02-10 05:53:48 +03:00
|
|
|
/**
|
|
|
|
* Expects interrupts off and thread lock held.
|
|
|
|
* The function may release the lock and enable interrupts temporarily, so the
|
|
|
|
* caller must be aware that operations before calling this function and after
|
|
|
|
* its return might not belong to the same atomic section.
|
|
|
|
*/
|
2005-03-29 02:00:36 +04:00
|
|
|
|
2002-10-28 23:38:16 +03:00
|
|
|
int
|
2005-02-10 05:53:48 +03:00
|
|
|
handle_signals(struct thread *thread, cpu_status *state)
|
2002-10-23 21:31:10 +04:00
|
|
|
{
|
2003-01-27 16:55:57 +03:00
|
|
|
uint32 signalMask = thread->sig_pending & (~thread->sig_block_mask);
|
2005-03-29 02:00:36 +04:00
|
|
|
int i, signal, global_resched = 0;
|
2002-10-23 21:31:10 +04:00
|
|
|
struct sigaction *handler;
|
2003-01-27 16:55:57 +03:00
|
|
|
|
2005-02-25 17:20:47 +03:00
|
|
|
// If SIGKILL[THR] are pending, we ignore other signals.
|
|
|
|
// Otherwise check, if the thread shall stop for debugging.
|
|
|
|
if (signalMask & KILL_SIGNALS) {
|
|
|
|
signalMask &= KILL_SIGNALS;
|
|
|
|
} else if (thread->debug_info.flags & B_THREAD_DEBUG_STOP) {
|
2005-02-24 19:28:45 +03:00
|
|
|
RELEASE_THREAD_LOCK();
|
|
|
|
restore_interrupts(*state);
|
2005-02-10 05:53:48 +03:00
|
|
|
|
2005-02-24 19:28:45 +03:00
|
|
|
user_debug_stop_thread();
|
2005-02-10 05:53:48 +03:00
|
|
|
|
2005-02-24 19:28:45 +03:00
|
|
|
*state = disable_interrupts();
|
|
|
|
GRAB_THREAD_LOCK();
|
2005-02-10 05:53:48 +03:00
|
|
|
|
2005-02-24 19:28:45 +03:00
|
|
|
signalMask = thread->sig_pending & (~thread->sig_block_mask);
|
|
|
|
}
|
2005-02-10 05:53:48 +03:00
|
|
|
|
2005-02-24 19:28:45 +03:00
|
|
|
if (signalMask == 0)
|
2003-01-27 16:55:57 +03:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = 0; i < NSIG; i++) {
|
|
|
|
if (signalMask & 0x1) {
|
2005-02-24 19:28:45 +03:00
|
|
|
bool debugSignal = !(~atomic_get(&thread->team->debug_info.flags)
|
|
|
|
& (B_TEAM_DEBUG_SIGNALS | B_TEAM_DEBUG_DEBUGGER_INSTALLED));
|
2005-02-10 05:53:48 +03:00
|
|
|
|
2005-03-29 02:00:36 +04:00
|
|
|
signal = i + 1;
|
2003-01-27 16:55:57 +03:00
|
|
|
handler = &thread->sig_action[i];
|
|
|
|
signalMask >>= 1;
|
2005-03-29 02:00:36 +04:00
|
|
|
thread->sig_pending &= ~SIGNAL_TO_MASK(signal);
|
2003-01-27 16:55:57 +03:00
|
|
|
|
2005-03-29 02:00:36 +04:00
|
|
|
TRACE(("Thread 0x%lx received signal %s\n", thread->id, sigstr[signal]));
|
2003-01-27 16:55:57 +03:00
|
|
|
|
|
|
|
if (handler->sa_handler == SIG_IGN) {
|
|
|
|
// signal is to be ignored
|
2004-05-24 13:06:20 +04:00
|
|
|
// ToDo: apply zombie cleaning on SIGCHLD
|
2005-02-24 19:28:45 +03:00
|
|
|
|
|
|
|
// notify the debugger
|
|
|
|
if (debugSignal)
|
2005-03-29 02:00:36 +04:00
|
|
|
notify_debugger(thread, signal, handler, false, state);
|
2003-01-27 16:55:57 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (handler->sa_handler == SIG_DFL) {
|
|
|
|
// default signal behaviour
|
2005-03-29 02:00:36 +04:00
|
|
|
switch (signal) {
|
2003-01-27 16:55:57 +03:00
|
|
|
case SIGCHLD:
|
|
|
|
case SIGWINCH:
|
|
|
|
case SIGTSTP:
|
|
|
|
case SIGTTIN:
|
|
|
|
case SIGTTOU:
|
|
|
|
case SIGCONT:
|
2005-02-24 19:28:45 +03:00
|
|
|
// notify the debugger
|
2005-03-13 00:45:49 +03:00
|
|
|
if (debugSignal) {
|
2005-03-29 02:00:36 +04:00
|
|
|
notify_debugger(thread, signal, handler, false, state);
|
2005-03-13 00:45:49 +03:00
|
|
|
}
|
2003-01-27 16:55:57 +03:00
|
|
|
continue;
|
|
|
|
|
|
|
|
case SIGSTOP:
|
2005-02-24 19:28:45 +03:00
|
|
|
// notify the debugger
|
|
|
|
if (debugSignal) {
|
2005-03-29 02:00:36 +04:00
|
|
|
if (!notify_debugger(thread, signal, handler, false,
|
2005-03-13 00:45:49 +03:00
|
|
|
state)) {
|
2005-02-24 19:28:45 +03:00
|
|
|
continue;
|
2005-03-13 00:45:49 +03:00
|
|
|
}
|
2005-02-24 19:28:45 +03:00
|
|
|
}
|
|
|
|
|
2003-01-27 16:55:57 +03:00
|
|
|
thread->next_state = B_THREAD_SUSPENDED;
|
|
|
|
global_resched = 1;
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case SIGQUIT:
|
|
|
|
case SIGILL:
|
|
|
|
case SIGTRAP:
|
|
|
|
case SIGABRT:
|
|
|
|
case SIGFPE:
|
|
|
|
case SIGSEGV:
|
2005-03-29 02:00:36 +04:00
|
|
|
TRACE(("Shutting down thread 0x%lx due to signal #%d\n", thread->id, signal));
|
2003-01-27 16:55:57 +03:00
|
|
|
case SIGKILL:
|
|
|
|
case SIGKILLTHR:
|
|
|
|
default:
|
2004-10-13 18:54:51 +04:00
|
|
|
if (thread->exit.reason != THREAD_RETURN_EXIT)
|
|
|
|
thread->exit.reason = THREAD_RETURN_INTERRUPTED;
|
|
|
|
|
2005-02-24 19:28:45 +03:00
|
|
|
// notify the debugger
|
2005-03-29 02:00:36 +04:00
|
|
|
if (debugSignal && signal != SIGKILL
|
|
|
|
&& signal != SIGKILLTHR) {
|
|
|
|
if (!notify_debugger(thread, signal, handler, true,
|
2005-03-13 00:45:49 +03:00
|
|
|
state)) {
|
2005-02-24 19:28:45 +03:00
|
|
|
continue;
|
2005-03-13 00:45:49 +03:00
|
|
|
}
|
2005-02-24 19:28:45 +03:00
|
|
|
}
|
|
|
|
|
2003-01-27 16:55:57 +03:00
|
|
|
RELEASE_THREAD_LOCK();
|
2005-02-10 05:53:48 +03:00
|
|
|
restore_interrupts(*state);
|
2004-10-13 18:54:51 +04:00
|
|
|
|
2004-05-24 13:06:20 +04:00
|
|
|
// ToDo: when we have more than a thread per process,
|
|
|
|
// it can likely happen (for any thread other than the first)
|
|
|
|
// that here, interrupts are still disabled.
|
|
|
|
// Changing the above line with "enable_interrupts()" fixes
|
|
|
|
// the problem, though we should find its cause.
|
|
|
|
// We absolutely need interrupts enabled when we enter
|
|
|
|
// thread_exit().
|
|
|
|
|
2003-01-27 16:55:57 +03:00
|
|
|
thread_exit();
|
2002-10-23 21:31:10 +04:00
|
|
|
}
|
2003-01-27 16:55:57 +03:00
|
|
|
}
|
|
|
|
|
2005-02-24 19:28:45 +03:00
|
|
|
// notify the debugger
|
2005-02-10 05:53:48 +03:00
|
|
|
if (debugSignal) {
|
2005-03-29 02:00:36 +04:00
|
|
|
if (!notify_debugger(thread, signal, handler, false, state))
|
2005-02-10 05:53:48 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2004-03-16 05:23:32 +03:00
|
|
|
// ToDo: it's not safe to call arch_setup_signal_frame with
|
|
|
|
// interrupts disabled since it writes to the user stack
|
|
|
|
// and may page fault.
|
|
|
|
|
2003-01-27 16:55:57 +03:00
|
|
|
// User defined signal handler
|
2004-11-12 20:21:14 +03:00
|
|
|
TRACE(("### Setting up custom signal handler frame...\n"));
|
2005-03-29 02:00:36 +04:00
|
|
|
arch_setup_signal_frame(thread, handler, signal, thread->sig_block_mask);
|
2003-01-27 16:55:57 +03:00
|
|
|
|
|
|
|
if (handler->sa_flags & SA_ONESHOT)
|
|
|
|
handler->sa_handler = SIG_DFL;
|
|
|
|
if (!(handler->sa_flags & SA_NOMASK))
|
2005-03-29 02:00:36 +04:00
|
|
|
thread->sig_block_mask |= (handler->sa_mask | SIGNAL_TO_MASK(signal)) & BLOCKABLE_SIGS;
|
2003-01-27 16:55:57 +03:00
|
|
|
|
|
|
|
return global_resched;
|
|
|
|
} else
|
|
|
|
signalMask >>= 1;
|
2002-10-23 21:31:10 +04:00
|
|
|
}
|
2003-01-27 16:55:57 +03:00
|
|
|
arch_check_syscall_restart(thread);
|
|
|
|
|
2002-10-28 23:38:16 +03:00
|
|
|
return global_resched;
|
2002-10-23 21:31:10 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-02-25 17:20:47 +03:00
|
|
|
bool
|
|
|
|
is_kill_signal_pending()
|
|
|
|
{
|
|
|
|
bool result;
|
|
|
|
struct thread *thread = thread_get_current_thread();
|
|
|
|
|
|
|
|
cpu_status state = disable_interrupts();
|
|
|
|
GRAB_THREAD_LOCK();
|
|
|
|
|
|
|
|
result = (thread->sig_pending & KILL_SIGNALS);
|
|
|
|
|
|
|
|
RELEASE_THREAD_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-14 18:48:40 +04:00
|
|
|
static status_t
|
|
|
|
deliver_signal(struct thread *thread, uint signal, uint32 flags)
|
|
|
|
{
|
|
|
|
if (flags & B_CHECK_PERMISSION) {
|
|
|
|
// ToDo: introduce euid & uid fields to the team and check permission
|
|
|
|
}
|
|
|
|
|
|
|
|
if (thread->team == team_get_kernel_team()) {
|
|
|
|
// Signals to kernel threads will only wake them up
|
|
|
|
if (thread->state == B_THREAD_SUSPENDED) {
|
|
|
|
thread->state = thread->next_state = B_THREAD_READY;
|
|
|
|
scheduler_enqueue_in_run_queue(thread);
|
|
|
|
}
|
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
thread->sig_pending |= SIGNAL_TO_MASK(signal);
|
|
|
|
|
|
|
|
switch (signal) {
|
|
|
|
case SIGKILL:
|
|
|
|
{
|
|
|
|
struct thread *mainThread = thread->team->main_thread;
|
|
|
|
// Forward KILLTHR to the main thread of the team
|
|
|
|
|
|
|
|
mainThread->sig_pending |= SIGNAL_TO_MASK(SIGKILLTHR);
|
|
|
|
// Wake up main thread
|
|
|
|
if (mainThread->state == B_THREAD_SUSPENDED) {
|
|
|
|
mainThread->state = mainThread->next_state = B_THREAD_READY;
|
|
|
|
scheduler_enqueue_in_run_queue(mainThread);
|
|
|
|
} else if (mainThread->state == B_THREAD_WAITING)
|
|
|
|
sem_interrupt_thread(mainThread);
|
|
|
|
|
|
|
|
// Supposed to fall through
|
|
|
|
}
|
|
|
|
case SIGKILLTHR:
|
|
|
|
// Wake up suspended threads and interrupt waiting ones
|
|
|
|
if (thread->state == B_THREAD_SUSPENDED) {
|
|
|
|
thread->state = thread->next_state = B_THREAD_READY;
|
|
|
|
scheduler_enqueue_in_run_queue(thread);
|
|
|
|
} else if (thread->state == B_THREAD_WAITING)
|
|
|
|
sem_interrupt_thread(thread);
|
|
|
|
break;
|
|
|
|
case SIGCONT:
|
|
|
|
// Wake up thread if it was suspended
|
|
|
|
if (thread->state == B_THREAD_SUSPENDED) {
|
|
|
|
thread->state = thread->next_state = B_THREAD_READY;
|
|
|
|
scheduler_enqueue_in_run_queue(thread);
|
|
|
|
}
|
|
|
|
break;
|
2005-03-29 02:00:36 +04:00
|
|
|
|
2004-10-14 18:48:40 +04:00
|
|
|
default:
|
|
|
|
if (thread->sig_pending & (~thread->sig_block_mask | SIGNAL_TO_MASK(SIGCHLD))) {
|
|
|
|
// Interrupt thread if it was waiting
|
|
|
|
if (thread->state == B_THREAD_WAITING)
|
|
|
|
sem_interrupt_thread(thread);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-10-23 21:31:10 +04:00
|
|
|
int
|
2004-10-14 18:48:40 +04:00
|
|
|
send_signal_etc(pid_t id, uint signal, uint32 flags)
|
2002-10-23 21:31:10 +04:00
|
|
|
{
|
2004-10-14 18:48:40 +04:00
|
|
|
status_t status = B_BAD_THREAD_ID;
|
2003-01-27 06:11:45 +03:00
|
|
|
struct thread *thread;
|
|
|
|
cpu_status state;
|
|
|
|
|
2003-01-27 16:55:57 +03:00
|
|
|
if (signal < 1 || signal > MAX_SIGNO)
|
2002-10-23 21:31:10 +04:00
|
|
|
return B_BAD_VALUE;
|
2003-01-27 06:11:45 +03:00
|
|
|
|
2004-10-14 18:48:40 +04:00
|
|
|
if (id == 0) {
|
|
|
|
// send a signal to the current team
|
|
|
|
id = thread_get_current_thread()->team->main_thread->id;
|
|
|
|
}
|
|
|
|
|
2002-10-23 21:31:10 +04:00
|
|
|
state = disable_interrupts();
|
2003-01-27 06:11:45 +03:00
|
|
|
|
2004-10-14 18:48:40 +04:00
|
|
|
if (id > 0) {
|
|
|
|
// send a signal to the specified thread
|
2003-01-27 06:11:45 +03:00
|
|
|
|
2004-10-14 18:48:40 +04:00
|
|
|
GRAB_THREAD_LOCK();
|
|
|
|
|
|
|
|
thread = thread_get_thread_struct_locked(id);
|
|
|
|
if (thread != NULL)
|
|
|
|
status = deliver_signal(thread, signal, flags);
|
2003-01-27 16:55:57 +03:00
|
|
|
} else {
|
2004-10-14 18:48:40 +04:00
|
|
|
// send a signal to the specified process group
|
|
|
|
// (the absolute value of the id)
|
|
|
|
|
|
|
|
GRAB_THREAD_LOCK();
|
|
|
|
|
2005-03-29 02:00:36 +04:00
|
|
|
thread = thread_get_thread_struct_locked(-id);
|
2004-10-14 18:48:40 +04:00
|
|
|
if (thread != NULL) {
|
|
|
|
struct process_group *group;
|
|
|
|
struct team *team, *next;
|
|
|
|
|
|
|
|
// we need a safe way to get from the thread to the process group
|
|
|
|
id = thread->team->id;
|
|
|
|
|
|
|
|
RELEASE_THREAD_LOCK();
|
|
|
|
GRAB_TEAM_LOCK();
|
|
|
|
|
|
|
|
// get a pointer to the process group
|
|
|
|
team = team_get_team_struct_locked(id);
|
|
|
|
group = team->group;
|
|
|
|
|
|
|
|
for (team = group->teams; team != NULL; team = next) {
|
|
|
|
// ToDo: there is a *big* race condition here on SMP machines;
|
|
|
|
// the team pointer will probably have gone bad in the mean time
|
|
|
|
next = team->group_next;
|
|
|
|
id = team->main_thread->id;
|
|
|
|
|
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
GRAB_THREAD_LOCK();
|
|
|
|
|
|
|
|
thread = thread_get_thread_struct_locked(id);
|
|
|
|
if (thread != NULL) {
|
|
|
|
// we don't stop because of an error sending the signal; we
|
|
|
|
// rather want to send as much signals as possible
|
|
|
|
status = deliver_signal(thread, signal, flags);
|
2002-10-23 21:31:10 +04:00
|
|
|
}
|
2004-10-14 18:48:40 +04:00
|
|
|
|
|
|
|
RELEASE_THREAD_LOCK();
|
|
|
|
GRAB_TEAM_LOCK();
|
|
|
|
}
|
|
|
|
|
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
GRAB_THREAD_LOCK();
|
2002-10-23 21:31:10 +04:00
|
|
|
}
|
2004-10-14 18:48:40 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// ToDo: maybe the scheduler should only be invoked is there is reason to do it?
|
|
|
|
// (ie. deliver_signal() moved some threads in the running queue?)
|
|
|
|
if ((flags & B_DO_NOT_RESCHEDULE) == 0)
|
2003-01-27 06:11:45 +03:00
|
|
|
scheduler_reschedule();
|
|
|
|
|
2002-10-23 21:31:10 +04:00
|
|
|
RELEASE_THREAD_LOCK();
|
|
|
|
restore_interrupts(state);
|
2003-01-27 16:55:57 +03:00
|
|
|
|
2004-10-14 18:48:40 +04:00
|
|
|
return status;
|
2002-10-23 21:31:10 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-11-18 04:58:00 +03:00
|
|
|
int
|
2003-01-27 16:55:57 +03:00
|
|
|
send_signal(pid_t threadID, uint signal)
|
2002-11-18 04:58:00 +03:00
|
|
|
{
|
2004-03-16 05:23:32 +03:00
|
|
|
// The BeBook states that this function wouldn't be exported
|
|
|
|
// for drivers, but, of course, it's wrong.
|
2003-01-27 16:55:57 +03:00
|
|
|
return send_signal_etc(threadID, signal, 0);
|
2002-11-18 04:58:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-10-23 21:31:10 +04:00
|
|
|
int
|
2003-01-27 06:11:45 +03:00
|
|
|
has_signals_pending(void *_thread)
|
2002-10-23 21:31:10 +04:00
|
|
|
{
|
2003-01-27 06:11:45 +03:00
|
|
|
struct thread *thread = (struct thread *)_thread;
|
|
|
|
if (thread == NULL)
|
|
|
|
thread = thread_get_current_thread();
|
2002-10-23 21:31:10 +04:00
|
|
|
|
2003-01-27 06:11:45 +03:00
|
|
|
return thread->sig_pending & ~thread->sig_block_mask;
|
2002-10-23 21:31:10 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-09-01 16:05:09 +04:00
|
|
|
int
|
|
|
|
sigprocmask(int how, const sigset_t *set, sigset_t *oldSet)
|
|
|
|
{
|
|
|
|
struct thread *thread = thread_get_current_thread();
|
|
|
|
sigset_t oldMask = thread->sig_block_mask;
|
|
|
|
|
|
|
|
// ToDo: "sig_block_mask" is probably not the right thing to change?
|
|
|
|
// At least it's often changed at other places...
|
|
|
|
|
|
|
|
switch (how) {
|
|
|
|
case SIG_BLOCK:
|
|
|
|
atomic_or(&thread->sig_block_mask, *set);
|
|
|
|
break;
|
|
|
|
case SIG_UNBLOCK:
|
|
|
|
atomic_and(&thread->sig_block_mask, ~*set);
|
|
|
|
break;
|
|
|
|
case SIG_SETMASK:
|
|
|
|
atomic_set(&thread->sig_block_mask, *set);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (oldSet != NULL)
|
|
|
|
*oldSet = oldMask;
|
|
|
|
|
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-03-13 00:45:49 +03:00
|
|
|
/** \brief Similar to sigaction(), just for a specified thread.
|
|
|
|
*
|
|
|
|
* A \a threadID is < 0 specifies the current thread.
|
|
|
|
*
|
|
|
|
*/
|
2005-03-29 02:00:36 +04:00
|
|
|
|
2002-10-23 21:31:10 +04:00
|
|
|
int
|
2005-03-13 00:45:49 +03:00
|
|
|
sigaction_etc(thread_id threadID, int signal, const struct sigaction *act,
|
|
|
|
struct sigaction *oact)
|
2002-10-23 21:31:10 +04:00
|
|
|
{
|
2003-01-27 16:55:57 +03:00
|
|
|
struct thread *thread;
|
|
|
|
cpu_status state;
|
2005-03-13 00:45:49 +03:00
|
|
|
status_t error = B_OK;
|
2003-01-27 06:11:45 +03:00
|
|
|
|
2003-01-27 16:55:57 +03:00
|
|
|
if (signal < 1 || signal > MAX_SIGNO
|
|
|
|
|| signal == SIGKILL || signal == SIGKILLTHR || signal == SIGSTOP)
|
2002-10-23 21:31:10 +04:00
|
|
|
return EINVAL;
|
2003-01-27 06:11:45 +03:00
|
|
|
|
2002-10-23 21:31:10 +04:00
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_THREAD_LOCK();
|
2003-01-27 06:11:45 +03:00
|
|
|
|
2005-03-13 00:45:49 +03:00
|
|
|
thread = (threadID < 0
|
|
|
|
? thread_get_current_thread()
|
|
|
|
: thread_get_thread_struct_locked(threadID));
|
2003-01-27 06:11:45 +03:00
|
|
|
|
2005-03-13 00:45:49 +03:00
|
|
|
if (thread) {
|
|
|
|
if (oact) {
|
|
|
|
memcpy(oact, &thread->sig_action[signal - 1],
|
|
|
|
sizeof(struct sigaction));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (act) {
|
|
|
|
memcpy(&thread->sig_action[signal - 1], act,
|
|
|
|
sizeof(struct sigaction));
|
|
|
|
}
|
2003-01-27 06:11:45 +03:00
|
|
|
|
2005-03-13 00:45:49 +03:00
|
|
|
if (act && act->sa_handler == SIG_IGN)
|
2003-01-27 16:55:57 +03:00
|
|
|
thread->sig_pending &= ~SIGNAL_TO_MASK(signal);
|
2005-03-13 00:45:49 +03:00
|
|
|
else if (act && act->sa_handler == SIG_DFL) {
|
|
|
|
if (signal == SIGCONT || signal == SIGCHLD
|
|
|
|
|| signal == SIGWINCH) {
|
|
|
|
thread->sig_pending &= ~SIGNAL_TO_MASK(signal);
|
|
|
|
}
|
|
|
|
} /*else
|
|
|
|
dprintf("### custom signal handler set\n");*/
|
|
|
|
} else
|
|
|
|
error = B_BAD_THREAD_ID;
|
2003-01-27 06:11:45 +03:00
|
|
|
|
2002-10-23 21:31:10 +04:00
|
|
|
RELEASE_THREAD_LOCK();
|
|
|
|
restore_interrupts(state);
|
2003-01-27 06:11:45 +03:00
|
|
|
|
2005-03-13 00:45:49 +03:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
sigaction(int signal, const struct sigaction *act, struct sigaction *oact)
|
|
|
|
{
|
|
|
|
return sigaction_etc(-1, signal, act, oact);
|
2002-10-23 21:31:10 +04:00
|
|
|
}
|
|
|
|
|
2002-10-26 02:36:08 +04:00
|
|
|
|
2005-03-29 02:00:36 +04:00
|
|
|
/** Triggers a SIGALRM to the thread that issued the timer and reschedules */
|
2003-01-27 06:11:45 +03:00
|
|
|
|
2002-10-26 02:36:08 +04:00
|
|
|
static int32
|
|
|
|
alarm_event(timer *t)
|
|
|
|
{
|
2004-01-26 05:01:46 +03:00
|
|
|
// The hook can be called from any context, but we have to
|
|
|
|
// deliver the signal to the thread that originally called
|
|
|
|
// set_alarm().
|
|
|
|
// Since thread->alarm is this timer structure, we can just
|
|
|
|
// cast it back - ugly but it works for now
|
|
|
|
struct thread *thread = (struct thread *)((uint8 *)t - offsetof(struct thread, alarm));
|
|
|
|
// ToDo: investigate adding one user parameter to the timer structure to fix this hack
|
|
|
|
|
2004-11-12 20:21:14 +03:00
|
|
|
TRACE(("alarm_event: thread = %p\n", thread));
|
2004-01-26 05:01:46 +03:00
|
|
|
send_signal_etc(thread->id, SIGALRM, B_DO_NOT_RESCHEDULE);
|
2003-01-27 06:11:45 +03:00
|
|
|
|
2002-10-26 02:36:08 +04:00
|
|
|
return B_INVOKE_SCHEDULER;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bigtime_t
|
2003-01-27 06:11:45 +03:00
|
|
|
set_alarm(bigtime_t time, uint32 mode)
|
2002-10-26 02:36:08 +04:00
|
|
|
{
|
2003-01-27 16:55:57 +03:00
|
|
|
struct thread *thread = thread_get_current_thread();
|
2002-10-26 02:36:08 +04:00
|
|
|
bigtime_t rv = 0;
|
2003-01-27 06:11:45 +03:00
|
|
|
|
2003-01-27 16:55:57 +03:00
|
|
|
ASSERT(B_ONE_SHOT_RELATIVE_ALARM == B_ONE_SHOT_RELATIVE_TIMER);
|
|
|
|
// just to be sure no one changes the headers some day
|
2002-11-17 03:52:43 +03:00
|
|
|
|
2004-11-12 20:21:14 +03:00
|
|
|
TRACE(("set_alarm: thread = %p\n", thread));
|
2004-01-26 05:01:46 +03:00
|
|
|
|
2003-01-27 16:55:57 +03:00
|
|
|
if (thread->alarm.period)
|
|
|
|
rv = (bigtime_t)thread->alarm.entry.key - system_time();
|
2003-01-27 06:11:45 +03:00
|
|
|
|
2003-01-27 16:55:57 +03:00
|
|
|
cancel_timer(&thread->alarm);
|
|
|
|
|
|
|
|
if (time != B_INFINITE_TIMEOUT)
|
|
|
|
add_timer(&thread->alarm, &alarm_event, time, mode);
|
2003-01-27 06:11:45 +03:00
|
|
|
|
2002-10-26 02:36:08 +04:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2003-01-27 06:11:45 +03:00
|
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
|
|
|
|
|
|
bigtime_t
|
2004-08-29 00:51:47 +04:00
|
|
|
_user_set_alarm(bigtime_t time, uint32 mode)
|
2003-01-27 06:11:45 +03:00
|
|
|
{
|
|
|
|
return set_alarm(time, mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
2004-08-29 00:51:47 +04:00
|
|
|
_user_send_signal(pid_t team, uint signal)
|
2003-01-27 06:11:45 +03:00
|
|
|
{
|
2004-10-14 18:48:40 +04:00
|
|
|
return send_signal_etc(team, signal, B_CHECK_PERMISSION);
|
2003-01-27 06:11:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-09-01 16:05:09 +04:00
|
|
|
int
|
|
|
|
_user_sigprocmask(int how, const sigset_t *userSet, sigset_t *userOldSet)
|
|
|
|
{
|
|
|
|
sigset_t set, oldSet;
|
|
|
|
status_t status;
|
|
|
|
|
|
|
|
if (userSet == NULL)
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
|
|
|
if (user_memcpy(&set, userSet, sizeof(sigset_t)) < B_OK
|
|
|
|
|| (userOldSet != NULL && user_memcpy(&oldSet, userOldSet, sizeof(sigset_t)) < B_OK))
|
|
|
|
return B_BAD_ADDRESS;
|
|
|
|
|
|
|
|
status = sigprocmask(how, &set, userOldSet ? &oldSet : NULL);
|
|
|
|
|
|
|
|
// copy old set if asked for
|
|
|
|
if (status >= B_OK && userOldSet != NULL && user_memcpy(userOldSet, &oldSet, sizeof(sigset_t)) < B_OK)
|
|
|
|
return B_BAD_ADDRESS;
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-01-27 06:11:45 +03:00
|
|
|
int
|
2005-03-29 02:00:36 +04:00
|
|
|
_user_sigaction(int signal, const struct sigaction *userAction, struct sigaction *userOldAction)
|
2003-01-27 06:11:45 +03:00
|
|
|
{
|
|
|
|
struct sigaction act, oact;
|
2004-09-01 16:05:09 +04:00
|
|
|
status_t status;
|
2003-01-27 06:11:45 +03:00
|
|
|
|
|
|
|
if ((userAction != NULL && user_memcpy(&act, userAction, sizeof(struct sigaction)) < B_OK)
|
|
|
|
|| (userOldAction != NULL && user_memcpy(&oact, userOldAction, sizeof(struct sigaction)) < B_OK))
|
|
|
|
return B_BAD_ADDRESS;
|
|
|
|
|
2005-03-29 02:00:36 +04:00
|
|
|
status = sigaction(signal, userAction ? &act : NULL, userOldAction ? &oact : NULL);
|
2003-01-27 06:11:45 +03:00
|
|
|
|
|
|
|
// only copy the old action if a pointer has been given
|
2004-09-01 16:05:09 +04:00
|
|
|
if (status >= B_OK && userOldAction != NULL
|
2003-01-27 06:11:45 +03:00
|
|
|
&& user_memcpy(userOldAction, &oact, sizeof(struct sigaction)) < B_OK)
|
|
|
|
return B_BAD_ADDRESS;
|
|
|
|
|
2004-09-01 16:05:09 +04:00
|
|
|
return status;
|
2003-01-27 06:11:45 +03:00
|
|
|
}
|
|
|
|
|
2005-09-08 16:26:16 +04:00
|
|
|
|
|
|
|
int
|
2005-09-13 20:12:20 +04:00
|
|
|
_user_sigsuspend(const sigset_t *mask)
|
|
|
|
{
|
2005-09-08 16:26:16 +04:00
|
|
|
sigset_t set;
|
|
|
|
if (mask == NULL)
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
|
|
|
if (user_memcpy(&set, mask, sizeof(sigset_t)) < B_OK)
|
|
|
|
return B_BAD_ADDRESS;
|
|
|
|
|
|
|
|
// Todo : implement
|
|
|
|
return EINVAL;
|
|
|
|
}
|
2005-09-13 20:12:20 +04:00
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
_user_sigpending(sigset_t *set) {
|
|
|
|
if (set == NULL)
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
|
|
|
// Todo : implement
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|