/* * Signal processing. * * This is mostly concerned with SIGINT processing. Various places in * the code want to handle SIGINT; we support this by maintaining a * stack of JMPs, with SIGINT jumping to the one on the top of that * stack. There are calls to push and pop the stack. * (SIGINT-provoked jumping does not perform a stack pop.) * * When other code wants to block SIGINT, we don't actually disable the * signal in the OS sense. Instead, we set a variable here and just * keep track of whether we've gotten any, so when they are unblocked * we can take the signal, without needing to trap to the kernel. We * still have to trap to the kernel when pushing and popping the * stack, but we can avoid it when blocking or unblocking. */ #include #include #include "debug.h" #include "structs.h" #include "signals.h" typedef struct stk STK; /* * An element in the stack of longjmp-to locations, for ^C handling. */ struct stk { STK *link; JMP *j; } ; /* * A count of SIGINTs, so other code can tell whether one has occurred * during computation. */ static volatile int interrupt_count = 1; /* * The stack of JMPs, used for SIGINT handling. */ static STK * volatile stack; /* * A sigset_t containing just SIGINT, used for blocking SIGINT. */ static sigset_t mask_sigs; /* * True if SIGINT is currently blocked. */ static volatile int int_blocked; /* * Count of SIGINTs which occurred while they were blocked. */ static volatile int while_blocked; /* * The SIGINT handler in the OS sense. */ static void sigint(int sig __attribute__((__unused__))) { STK *s; if (int_blocked) { while_blocked = 1; return; } trace_text("--SIGINT--\n"); interrupt_count ++; s = stack; if (s && s->j) longjmp(s->j->b,1); } /* * Called during startup to initialize the signal handling code. */ void initsignals(void) { struct sigaction sa; int_blocked = 0; while_blocked = 0; sigemptyset(&mask_sigs); sigaddset(&mask_sigs,SIGINT); stack = 0; sa.sa_handler = &sigint; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(SIGINT,&sa,0); } /* * Return the current interrupt count. Calling this before and after * something allows code to detect whether a SIGINT was processed * during that thing. */ int interrupted(void) { return(interrupt_count); } /* * Push a JMP on the SIGINT jump-to stack. */ void push_sigint_throw(JMP *j) { STK *s; sigset_t m; sigemptyset(&m); sigprocmask(SIG_BLOCK,&mask_sigs,&m); s = malloc(sizeof(STK)); *(volatile STK *)s = (STK){ .j = j, .link = stack }; stack = s; sigprocmask(SIG_SETMASK,&m,0); } /* * Pop the top JMP off the SIGINT jump-to stack. */ void pop_sigint_throw(void) { STK *s; sigset_t m; sigemptyset(&m); sigprocmask(SIG_BLOCK,&mask_sigs,&m); s = stack; stack = s->link; free(s); sigprocmask(SIG_SETMASK,&m,0); } #include "pline.h" // panic() /* * Block SIGINT processing. */ void block_int(void) { if (int_blocked) panic("nested SIGINT blocking"); while_blocked = 0; int_blocked = 1; } /* * Unblock SIGINT processing. */ void unblock_int(void) { int_blocked = 0; if (while_blocked) sigint(SIGINT); }