/* 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 approbiate ** 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 "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 timecount just cleans up. */ __stkargs unsigned int alarm (unsigned int seconds) { static struct timeval tv; static first = 1; if (!treq) { printf("No handler installed !\n"); 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 = 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 = 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 = 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; } /*************************************************************************/