/************************************************************************ * This program is Copyright (C) 1986 by Jonathan Payne. JOVE is * * provided to you without charge, and with no warranty. You may give * * away copies of JOVE, including sources, provided that this notice is * * included in all the files. * ************************************************************************/ /* This is a keyboard server for JOVE. It wraps the keyboard input in packets so that JOVE can identify its source. By the time we get here, our standard output goes to JOVE's process input. It is suspended when no interactive subprocesses processes are active. JOVE communicates with this server through signals: - KBD_TOGGLE_SUSPEND_SIG (JOVE -> kbd) to suspend/wakeup the server; - KBD_TIMEOUT_SIG (JOVE -> kbd) requests a timed-out read; - KBD_FATAL_ERROR_SIG (kbd -> JOVE) to report an unrecoverable error. These signals are defined in "process.h". Packet layout: - header.pid - process id. of sender (the kbd process) - header.nbytes - if >= 0, the number of data bytes that follow; if < 0, reports a read error, its value is the negated `errno' at the time the read error occurred. EINTR errors are filtered out, unless a timeout was requested. A special case of an "error" is the value KBD_SUSPENDED which is actually the acknowledge of a suspend request. - data[header.nbytes] - the characters read by the server from stdin. Each packet is sent in a single atomic write, so other processes writing on the same pipe cannot interfere. */ #define NOT_JOVE #include "tune.h" #ifdef PIPEPROCS RCS("$Id: kbd.c,v 14.31 1993/02/15 02:01:48 tom Exp tom $") #include "jove.h" #include "io.h" #include "process.h" /* {{There must be better places to handle this...}} */ # ifndef _NSIG # ifdef NSIGS # define _NSIG NSIGS # endif # endif # ifndef _NSIG # define _NSIG "(warning) _NSIG is undefined for this system." # define _NSIG 32 /* should be enough for most systems. */ # endif #ifdef BSD_SIGS # define pause() sigpause(0L) #endif #ifdef DEBUG void DIAGNOSE(str) char *str; { write(2, str, strlen(str)); write(2, "\r\n", 2); } #else # define DIAGNOSE(str) #endif /* The keyboard server sends KBD_FATAL_ERROR_SIG to JOVE whenever it runs into an unrecoverable error, and then exits. It is up to JOVE to do something sensible with it. */ #define FATAL_ERROR() kill(getppid(), KBD_FATAL_ERROR_SIG) /* JOVE sends KBD_TOGGLE_SUSPEND_SIG whenever it wants the kbd process (this program) to stop competing for input from the keyboard. JOVE does this when it realizes that there are no more interactive processes running. The reason we go through all this trouble is that JOVE slows down a lot when it's getting its keyboard input via a pipe. */ private sig_tp suspend_or_wakeup __(( int _(sig) )), timeout __(( int _(sig) )), shoot __(( int _(sig) )), spurious __(( int _(sig) )); private int suspended ZERO; private sig_tp suspend_or_wakeup(sig) { suspended ^= 1; signal(sig, suspend_or_wakeup); DIAGNOSE(suspended ? "{suspend}" : "{wakeup}"); } /* JOVE sends KBD_TIMEOUT_SIG if it wants the kbd process to setup a timed-out read. This is done because it is not reliable to have JOVE setup a timeout itself when I-processes are active. (since the insertion of I-process output can take a considerable amount of time.) */ private int alarmed ZERO; private sig_tp shoot(sig) { alarmed = 1; /* this is a one-shot */ DIAGNOSE("{shoot}"); } private sig_tp timeout(sig) { alarmed = -1; /* < 0 means timeout request. */ signal(sig, timeout); DIAGNOSE("{timeout}"); } /* This catches spurious interrupts. */ private sig_tp spurious(sig) { DIAGNOSE("{spurious interrupt}"); FATAL_ERROR(); /* signal JOVE that we're rotten */ kill(getpid(), sig); /* ...and die as we would have. */ } #endif /* PIPEPROCS */ void main() { #ifdef PIPEPROCS struct { struct header header; char buf[10]; } packet; register int sig; register int n = 0; for (sig = 1; sig < _NSIG; sig++) { /* setup signal handlers */ switch (sig) { case KBD_TOGGLE_SUSPEND_SIG: signal(sig, suspend_or_wakeup); break; case KBD_TIMEOUT_SIG: signal(sig, timeout); break; default: signal(sig, spurious); } } packet.header.pid = getpid(); DIAGNOSE("{kbd}"); for (;;) { /* Wait here while we're suspended. Both the read (from a tty) and the write (to a pipe) are interruptable so we'll always end up here when a suspend signal comes in. The original kbd process paused inside a signal handler, which seemed to cause problems in Minix. Also it didn't cope with mixed timeout and suspend signals the way it was. This should be safer. */ if (suspended) { /* send an acknowledgment so JOVE knows when to stop draining the process input pipe; retry on timeout interrupts. */ DIAGNOSE("{acknowledge}"); packet.header.nbytes = KBD_SUSPENDED; while (write(1, &packet, sizeof packet.header) < 0 && alarmed) { alarm(0); alarmed = 0; } DIAGNOSE("{suspended}"); while (suspended) pause(); alarm(0); /* start with clean slate */ alarmed = 0; } if (alarmed < 0) { /* setup timeout */ DIAGNOSE("{alarm}"); signal(SIGALRM, shoot); alarm(ALARM_1_SEC); } if ((packet.header.nbytes = n = read(0, packet.buf, sizeof packet.buf)) <= 0) { if (n == 0) continue; if (errno != EINTR) { /* now bail out on read error */ DIAGNOSE("{read failed}"); FATAL_ERROR(); _exit(-errno); } if (alarmed <= 0) { DIAGNOSE("{interrupted}"); continue; } DIAGNOSE("{alarmed}"); n = 0; packet.header.nbytes = -errno; /* report error */ } if (alarmed < 0) /* timeout set but not yet expired; */ alarm(0); /* so turn it off now. */ alarmed = 0; /* Make sure the packet is sent; retry interrupted writes. */ while (write(1, &packet, sizeof packet.header + n) < 0) { if (errno == EINTR) continue; DIAGNOSE("{write failed}"); FATAL_ERROR(); _exit(-errno); } } DIAGNOSE("{?kbd past loop?}"); FATAL_ERROR(); #endif /* PIPEPROCS */ _exit(EXIT_SUCCESS); } /*====================================================================== * $Log: kbd.c,v $ * Revision 14.31 1993/02/15 02:01:48 tom * remove (void) casts. * * Revision 14.30 1993/01/26 18:43:17 tom * cleanup whitespace; some random optimizations. * * Revision 14.26 1992/08/26 23:57:05 tom * add RCS directives. * */