/* hosts/amiga/signal.c
**
** Adapt the default-signal handling for inclusion of timer-
** and user-generated signals. In detail this means setting the
** task_exception_code hook on a stub which 'raise()s' the appropriate
** signals.
** This is closely coupled with the timer functions so they are also
** implemented here.
**
** LPMud uses the signals SIGHUP (driver process abortion by the user),
** SIGALRM (alarm timed out) and an interrupt signal for select().
** Amylaar additionally uses SIGUSR1 (update of the master by the user).
** These three signals are not caused by other program parts (as the standard
** implementation assumes), but instead are caused by timer time-outs or
** keypresses. Therefore these are implemented explicitely by using
** the Amiga's internal task signal system, which allows the setting of
** an exception handler which is called whenever a task signal is raised.
** The other signals are passed through to the standard clib signal().
**
** For easier compilation, the catch_exception() which needs registerized
** args is put in a separate file signal_rr.c .
**
** This code is based on the UnixLib by Erik van Roode.
**
** 18-Oct-92 [lars] Done for DICE 2.06.40
** 24-Feb-93 [lars] Small fix to support compilation for OS 1.3
** 28-Feb-93 [lars] Moved to DICE 2.07.53
** 09-Feb-93 [lars] Added check_signals() and default break handling.
*/
#include <sys/types.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/tasks.h>
#include <exec/interrupts.h>
#include <devices/timer.h>
#ifdef INCLUDE_VERSION
#include <dos/dos.h>
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#else
#include <libraries/dos.h>
#endif
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include "nsignal.h"
#undef signal /* This macro is implemented here! */
/*-----------------------------------------------------------------------*/
extern __regargs __geta4 ULONG catch_exception(__D0 ULONG);
ULONG sys_signal_alarm = 0, /* The system-signal-masks */
sys_signal_hup = 0, sys_signal_usr = 0;
void (*handler_hup) (void), (*handler_alarm) (void), (*handler_usr) (void);
static struct timerequest *treq = NULL; /* the alarm()-timer */
/*-----------------------------------------------------------------------
** int start_timer (struct timeval *tv, struct timerequest *tr)
**
** Start a timer <tr> with given timeval <tv>.
*/
__stkargs int start_timer(struct timeval * tv, struct timerequest * tr)
{
if (!tr) {
printf("no request structure\n");
return 0;
}
if (tv->tv_secs == 0L && tv->tv_micro < 2L)
tv->tv_micro = 2L; /* minimal delay */
tr->tr_time = *tv;
tr->tr_node.io_Command = TR_ADDREQUEST;
SendIO((struct IORequest *) tr);
return 1;
}
/*-----------------------------------------------------------------------
** int setup_timer (LONG unit, struct timerequest **tr)
**
** Setup a timer counting in <unit>, and store it in <tr>.
*/
__stkargs int setup_timer(LONG unit, struct timerequest ** tr)
{
struct MsgPort *timerport;
struct timerequest *req;
if (*tr)
return 1;
if (!(timerport = (struct MsgPort *) CreatePort(0L, 0L))) {
*tr = NULL;
printf("setup_timer: could not create port\n");
return 0;
}
if (!(req = (struct timerequest *) CreateExtIO(timerport
,sizeof(struct timerequest))
)) {
DeletePort(timerport);
*tr = NULL;
printf("setup_timer: could not get request\n");
return 0;
}
if (OpenDevice(TIMERNAME, unit, (struct IORequest *) req, 0L)) {
CloseDevice((struct IORequest *) req);
DeleteExtIO((struct IORequest *) req);
DeletePort(timerport);
printf("setup_timer: could not open timer\n");
*tr = NULL;
return 0;
}
*tr = req;
return 1;
}
/*-----------------------------------------------------------------------
** void cleanup_timer (struct timerequest **tr)
**
** Cleanup given timer <tr>.
*/
__stkargs void cleanup_timer(struct timerequest ** tr)
{
struct MsgPort *tp;
struct timerequest *tmp;
UBYTE pFlags;
if (*tr) {
tmp = *tr;
tp = tmp->tr_node.io_Message.mn_ReplyPort;
if (tp) {
/* abort the current request */
pFlags = tp->mp_Flags; /* still needed for DeletePort */
tp->mp_Flags = PA_IGNORE;
AbortIO((struct IORequest *) tmp);
WaitIO((struct IORequest *) tmp);
while (GetMsg(tp));
Forbid();
tp->mp_Flags = pFlags;
DeletePort(tp);
Permit();
}
CloseDevice((struct IORequest *) tmp);
DeleteExtIO((struct IORequest *) tmp);
}
*tr = NULL;
}
/*-----------------------------------------------------------------------
** __stkargs int alarm (int seconds)
**
** Start a timer which raises SIGALRM after <seconds>.
** Also cleans up the mess made by a previous alarm.
** Specifying a zero time count just cleans up.
*/
__stkargs unsigned int alarm(unsigned int seconds)
{
static struct timeval tv;
static first = 1;
if (!treq) {
printf("No handler installed !\n");
if (seconds > 0)
return 0; /* Heartbeat won't work :+( */
}
tv.tv_secs = seconds;
tv.tv_micro = 0;
if (seconds > 0) {
/* first call of alarm() : WaitIO on unsent request ..... */
if (!first) {
treq->tr_node.io_Message.mn_ReplyPort->mp_Flags = PA_IGNORE;
AbortIO((struct IORequest *) treq);
WaitIO((struct IORequest *) treq);
treq->tr_node.io_Message.mn_ReplyPort->mp_Flags = PA_SIGNAL;
}
first = 0;
start_timer(&tv, treq);
} else {
/*
* if I don't use this code, AbortIO will generate a signal, which
* will trigger catch_alarm. catch_alarm will then generate CTRL-E.
* This can be resolved by preventing the signal to occur :+)
*/
treq->tr_node.io_Message.mn_ReplyPort->mp_Flags = PA_IGNORE;
AbortIO((struct IORequest *) treq);
WaitIO((struct IORequest *) treq);
cleanup_timer(&treq);
first = 1;
}
return 0;
}
/*-----------------------------------------------------------------------
** __sigfunc new_signal (int signo, __sigfunc handler)
**
** Set the <handler> for <signo>.
** The signals SIGHUP, SIGUSR1 and SIGALRM are treated manually to
** allow the system-signals call the handlers via external exceptions.
*/
__stkargs __sigfunc new_signal(int signo, __sigfunc handler)
{
register struct Task *this_task;
this_task = (struct Task *) FindTask(NULL);
switch (signo) {
case SIGALRM:{
ULONG sigalrm;
sigalrm = sys_signal_alarm;
if ((__sigfunc) handler == SIG_IGN) { /* remove SIGALRM
* handler */
SetExcept(0L, sigalrm); /* Only sigalrm !! */
sys_signal_alarm = 0;
handler_alarm = NULL;
cleanup_timer(&treq);
} else { /* install handler */
if (!setup_timer(UNIT_VBLANK, &treq)) {
printf("Could not setup_timer\n");
break; /* What else ?? */
}
sigalrm = 1L << (treq->tr_node.io_Message.mn_ReplyPort->mp_SigBit);
this_task->tc_ExceptCode = (APTR) catch_exception;
sys_signal_alarm = sigalrm;
handler_alarm = (void (*) ()) handler;
SetExcept(sigalrm, sigalrm);
/* If we start treq, handler will be called */
}
break;
}
case SIGHUP:{
ULONG sighup;
sighup = (((__sigfunc) handler == SIG_IGN) || ((__sigfunc) handler == SIG_DFL))
? 0 : EXT_SIGHUP;
this_task->tc_ExceptCode = (APTR) catch_exception;
sys_signal_hup = sighup;
handler_hup = (void (*) ()) handler;
SetExcept(sighup, EXT_SIGHUP);
break;
}
case SIGUSR1:{
ULONG sigusr;
sigusr = (((__sigfunc) handler == SIG_IGN) || ((__sigfunc) handler == SIG_DFL))
? 0 : EXT_SIGUSR;
this_task->tc_ExceptCode = (APTR) catch_exception;
sys_signal_usr = sigusr;
handler_usr = (void (*) ()) handler;
SetExcept(sigusr, EXT_SIGUSR);
break;
}
default:
signal(signo, handler);
break;
}
return handler;
}
/*-----------------------------------------------------------------------
** ULONG check_signals (void)
**
** Check the tasks external signals and call the associated handler
** (if any).
** Result is the signal mask.
*/
static int _ChkSignalLockout = 0; /* simple semaphore for
* check_signals() */
ULONG check_signals(void)
{
ULONG mask;
if (_ChkSignalLockout)
return 0L;
++_ChkSignalLockout;
mask = ((struct Task *) FindTask(NULL))->tc_SigRecvd;
/* Default Ctrl-C handling */
if (!sys_signal_hup && (mask & SIGBREAKF_CTRL_C)) {
SetSignal(0L, SIGBREAKF_CTRL_C);
write(2, "*** Break.\n", 11);
exit(EXIT_FAILURE);
}
/* Handle our special exceptions */
if (mask & sys_signal_alarm) {
(*handler_alarm) ();
SetSignal(0L, sys_signal_alarm);
}
if (mask & sys_signal_hup) {
(*handler_hup) ();
SetSignal(0L, sys_signal_hup);
}
if (mask & sys_signal_usr) {
(*handler_usr) ();
SetSignal(0L, sys_signal_usr);
}
--_ChkSignalLockout;
return mask;
}
/*************************************************************************/