/* 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.
** 05-Jun-94 [lars] cleanup_alarm() added.
** 11-Aug-95 [lars] __sigfunc replaced by SIGFUNC.
*/
#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 "nsignal.h"
#undef signal /* This macro is implemented here! */
/*-----------------------------------------------------------------------*/
extern __regargs GETA4 ASM ULONG catch_exception( REGD0 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;
}
/*-----------------------------------------------------------------------
** void cleanup_alarm ()
**
** Cleanup the alarm timer.
*/
STKARGS void cleanup_alarm () {
cleanup_timer(&treq);
}
/*-----------------------------------------------------------------------
** 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 timecount just cleans up.
*/
STKARGS unsigned int alarm (unsigned int seconds) {
static struct timeval tv;
static first = 1;
if (!treq) {
if (seconds > 0) /* alarm(0) is used to clean up the timer */
printf("No handler installed !\n"); /* Heartbeat won't work */
return 0;
}
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(*)(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(*)(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(*)(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;
}
/*************************************************************************/