carl@nrcaero.UUCP (Carl P. Swail) (03/21/85)
: '!/bin/sh' : 'This is a shell archive, meaning:' : '1. Remove everything above the #!/bin/sh line.' : '2. Save the resulting text in a file.' : '3. Execute the file with /bin/sh (not csh) to create the files:' : ' 4.84' : 'This archive created: Wed Mar 20 14:39:54 1985' export PATH; PATH=/bin:$PATH echo shar: extracting "'4.84'" '(28731 characters)' if test -f '4.84' then echo shar: over-writing existing file "'4.84'" fi sed 's/^X//' << \SHAR_EOF > '4.84' X.ds Sp \fIUNIX clinic\fR X.TL X\*(Sp 4/84 X.ND "\*(Sp 4/84" X.PP XThis column first appears in the German quarterly X.I unix/mail X(Hanser Verlag, Munich, Germany). XIt is copyrighted: \(co 1984 by Axel T. Schreiner, Ulm, West Germany. XIt may be reproduced Xas long as the copyright notice is included Xand reference is made to the original publication. X.PP XThe column attempts to discuss typical approaches Xto problem solving using the X.UX Xsystem. XIt emphasizes what the author considers to be Xgood programming pratices and appropriate choice of tools. X.SH X/lib/libc.a(signal.o) X.PP X.B signal (2) X.FS XFollowing the usual UNIX convention X.B signal (2) Xdenotes the function X.B signal() , Xwhich is explained in chapter 2 in the X.I "Unix Programmer's Manual" . X.FE Xdoes not X.I generate Xa signal: X.B signal (2) Xis the meager protection offered against the cruelty of X.B kill (2). XThis issue of \*(Sp demonstrates Xhow to deal with signals. XSince this topic has a lot of prerequisites Xwe start for once with a theoretical overview. X.SH XTheory X.PP X.I Signal Xis the term for a number of events Xwhich a process may encounter in a more or less unforeseen fashion: X.RS X.IP \fBSIGHUP\fR X.br Xmay happen when the controlling terminal is switched off, Xi.e. once the interface looses the carrier. X.IP \fBSIGINT\fR X.br Xcan happen if the X.I interrupt Xkey is pressed at the controlling terminal, Xusually X.I break Xor X.I del . X.IP \fBSIGQUIT\fR X.br Xcan happen if the X.I quit Xkey is pressed at the controlling terminal, Xusually X.I control -\e. X.IP \fBSIGKILL\fR X.br Xis signal 9, which can be sent explicitly with the X.I kill Xcommand or the X.B kill (2) Xsystem call. X.IP \fBSIGSYS\fR X.br Xresults from a system call with bad parameters, Xthat is primarily if programs from a foreign operating system are run Xunder UNIX. X.IP \fBSIGPIPE\fR X.br Xis sent to the writer of a X.I pipe , Xif the corresponding reader exits. X.IP \fBSIGALRM\fR X.br Xis received by a process Xonce a time interval set up by X.B alarm (2) Xhas expired. X.IP \fBSIGTERM\fR X.br Xis signal 15 which is sent by X.I kill Xas a default. X.RE X.PP XAll in all there are 16 signals Xranging from X.B 1 X(\fBSIGHUP\fR) Xto X.B 16 . XThey result from events at the controlling terminal, Xerrors in the process itself, Xor through actions of other processes. XSignal 16 has no predefined meaning. XThe list above does not contain the names of the signals Xwhich are provoked by arithmetic errors X(e.g., division by zero), Xunknown machine instructions, Xor by access to non-existing memory regions. XThese situations are somewhat machine dependent. X.PP XThe system call X\fBkill(\fIpid\fB, \fIsig\fB)\fR Xsends signal X.I sig Xto the process X.I pid . X.B kill (2) Xis a function: the result is X.B 0 Xon success and X.B \(mi1 Xif, for example, a signal is to be sent to a non-existing process. XThe super-user may send signals to arbitrary recipients; Xeverybody else can only send signals to processes with the same user number. XThere is a process X.B 0 , Xbut if a signal is sent to X.I pid X.B 0 , Xit is in reality sent to all processes in one's own process group. XThe super-user can send a signal to X.I pid X.B \(mi1 ; Xthis will reach all processes in the entire system Xwith the exception of processes X.B 0 Xand X.B 1 . XThis call is really only for the benefit of X.I /etc/init , Xi.e. for the process controlling the timesharing operation. X.PP XIf a process does not take defensive measures, Xreceiving a signal is deadly. XFor some signals in this case a memory dump is produced as a file X.I core X(assuming the process has appropriate privileges Xwith respect to the working directory, the terminated program text, etc.). X.B SIGINT Xand X.B SIGQUIT Xdiffer precisely in that only X.B SIGQUIT Xproduces a memory dump. XAs a rule one only terminates a process using the X.I interrupt Xkey, and if the system is short of disk space, the super-user might Xoccasionally execute X.ne 2 X.DS Xfind / -name core -a -exec rm {} \e; X.DE X.LP Xto locate and remove all memory dumps X.I core . X.PP XA process can defend itself against all signals with the exception of X.B SIGKILL : X.ne 5 X.DS X#include <signal.h> X X ... X signal(sig, SIG_IGN); X.DE X.LP X.B SIG_IGN Xrequests that the signal X.B sig Xis not received at all. X.ne 5 X.DS Xint f(); X X ... X signal(sig, f); X.DE X.LP XNow upon receipt of the signal X.B sig Xthe function X.B f Xis called, which will receive the signal number as an argument. X.ne 5 X.DS X#include <signal.h> X X ... X signal(sig, SIG_DFL); X.DE X.LP Xfinally restores the default setting, Xi.e. that a signal is deadly and might produce a memory dump. X.PP XSignal states (i.e. X.B SIG_DFL , X.B SIG_IGN , Xor acceptance by a function) Xare preserved across X.B fork (2). X.B SIG_IGN Xor X.B SIG_DFL Xalso remain in effect across X.B exec (2). XA catch function set up with X.B signal (2) Xof course cannot be retained across X.B exec (2) X(since this system call eliminates program text and data Xin the running process). XSignals connected to a function are therefore implicitly Xreturned to X.B SIG_DFL Xduring X.B exec (2). X.PP XOnce a catch function has been set up using X.B signal (2), Xit is only invoked X.I once Xfor most signals. XAs soon as the signal has been received by the process, Xi.e. even X.I before Xthe catch function is actually called, Xthe signal setting is returned to X.B SIG_DFL Xfor the next occurrence of the signal. XThat results in a race condition: Xif a signal is received twice in rapid succession, Xit is highly likely to be deadly. X.PP XA catch function usually has the following form: X.ne 10 X.DS X#include <signal.h> X Xstatic trap(sig) X register int sig; X{ X signal(sig, SIG_IGN); X ... X signal(sig, trap); X} X.DE X.ne 7 X.DS Xmain() X{ X ... X signal(SIGINT, trap); X ... X} X.DE X.LP XAt the beginning of the function the signal state is set to X.B SIG_IGN . XAt the end the signal is reconnected to the same function. XInbetween there can essentially be arbitrary program text; Xhowever, on the PDP-11, for example, at this point the floating Xpoint registers have not been saved. X.PP XA catch function will typically set a global variable Xwhich is inspected in the main program at a suitable point. XAs an alternative we can jump back into the main program: X.ne 11 X.DS X#include <setjmp.h> X#include <signal.h> X Xstatic jmp_buf reset; X Xstatic trap() X{ X ... X longjmp(reset, 1); X} X.DE X.ne 8 X.DS Xmain() X{ X ... X setjmp(reset); X signal(SIGINT, trap); X ... X} X.DE X.LP X.B setjmp (3) Xarranges that a subsequent call of X.B longjmp() Xhas a target point: Xthe program then will return a second time Xfrom the call to X.B setjmp() . XThe value returned is zero the first time Xand the value of the second argument to X.B longjmp() Xthereafter. XOnce returned to the main routine, Xe.g., a major command loop can be restarted, etc. XThis is the customary method for error and interrupt handling Xin programs such as X.I ed . X.PP XA process can only die or enter a signal catching function Xif it is ready for execution. XIf a process is swapped, Xit must be brought back into main memory before it can die! XEspecially while executing code in a device driver, Xa process can be blocked by the driver in a fashion Xso that it does not become ready to execute even when receiving a signal X\(em such processes can usually only be eliminated by a system restart. XIf you write device drivers Xyou should really arrange for suitable mechanisms X(\fBioctl\fR(2)) Xif a magnetic tape transport or a floppy disk drive goes on the blink! X.PP XThe definitions in this section must be taken with a grain of salt: XBerkeley UNIX has more signals than, e.g., UNIX version 7, Xand there is even an additional signal state X(\fBSIG_HOLD\fR) Xwhich eliminates the race condition in UNIX version 7. XOur discussion is limited to UNIX version 7 Xin an attempt to be generally applicable. X.SH XProcess group and controlling terminal X.PP XStarting UNIX primarily means getting X.I /etc/init Xto execute as process 1. XWhen timesharing service is started, Xthis process reads the file X.I /etc/ttys Xand generates one process for each terminal Xwhich is marked appropriately in this file. XEach such new process is the first one in a new X.I "process group" , Xsince it is the first in a chain of descendants Xto open a connection to a terminal device. XThe terminal itself is the X.I "controlling terminal" Xof the process group. X.PP XThe controlling terminal is inherited during X.B fork (2) Xand X.B exec (2) Xeven if there is no more file connection to it! XThe only way to obtain a process without a controlling terminal Xis to have it generated by process 1 X(in the file X.I /etc/rc , Xfrom which the timesharing service is started) Xand to never connect such a process to a terminal. X.SH X/bin/sh(wait) X.PP XSignals caused at a terminal, i.e., X.B SIGHUP , X.B SIGINT , Xand X.B SIGQUIT , Xare always sent to X.I all Xprocesses in a process group. XAn X.I interactive Xshell, Xi.e., one which is connected to a terminal for standard input Xand output, Xhas to defend itself against X.B SIGINT Xand X.B SIGQUIT , Xotherwise a terminal session would be quickly over. X.PP XEvery shell ignores X.B SIGQUIT . XAn interactive shell catches X.B SIGINT Xbut the catch function does nothing. XAs a consequence the shell command X.I wait Xcan be terminated using the X.I interrupt Xkey, since X.B wait (2) Xis one of the system calls which are terminated Xearly by a signal. X.PP XSince for a background process X.B SIGINT Xand X.B SIGQUIT Xare ignored by default, Xone can use X.ne 2 X.DS Xwait $! X.DE X.LP Xrather than the much less efficient X.I ps Xto discover if the last background process X.B $! Xis already done: Xeither X.I wait Xdoes not happen, i.e., the command returns immediately, Xor one interrupts the wait state using X.I interrupt Xand knows that the background process is still active. X.FS XDepending on the Bourne Shell implementation, X.I wait Xdoes not necessarily accept a parameter. XWithout a parameter the command applies to all Xbackground processes together. X.FE X.PP X.B SIGTERM Xis also ignored by an interactive shell. XThis implies that X.ne 2 X.DS Xkill 0 X.DE X.LP Xcan be used in a dire emergency to Xeliminate all of one's own processes, Xsince the shell itself and thus the terminal session Xis not eliminated by X.B SIGTERM . X.SH X/bin/sh(logout) X.PP XThe C shell has a special X.I logout Xcommand. XFor the Bourne shell Xone can be constructed by recording the process number of the login shell Xusing the following commands in X.I $HOME/.profile : X.ne 2 X.DS XSHELLID=$$; export SHELLID X.DE X.LP X.I logout Xthen is the following shell script, Xe.g., in a file X.I /usr/bin/logout : X.ne 2 X.DS Xkill -16 $SHELLID X.DE X.LP XWith X.ne 2 X.DS Xtrap "echo logout" 0 X.DE X.LP Xin X.I $HOME/.profile Xwe can additionally report Xthat a terminal session is about to be concluded as a response to X.I control-D . X.SH X/bin/nohup X.PP XA background process will ignore X.B SIGINT Xand X.B SIGQUIT Xbut not X.B SIGHUP . XThis can cause background processes to die Xas soon as the terminal is turned off following the end of a terminal session. XIf your terminal interface and driver operate in this fashion, Xyou should issue such background commands with the prefix X.I nohup X Xwhich will insulate a command against a X.B SIGHUP Xsignal. XAlso, Xyour successor at the terminal will be happier, Xsince standard and diagnostic output of your command will be redirected Xto a file X.I nohup.out Xin your directory and do not clutter up his screen! X.SH X/bin/kill X.SH X"kill" Berkeley style X.PP XThe X.I kill Xcommand is used to send a signal to one or more processes. XUsually, X.B SIGTERM Xis sent, Xbut a signal number may be specified explicitly. XAs an example for the X.B kill (2) Xsystem call we show a version of X.I kill Xwhere the signal number may be specified mnemonically. XThe following Bourne shell script could be used: X.ne 10 X.DS Xno= Xcase $1 in X-[Hh][Uu][Pp]) X no=-1 shift;; X ... X-*) X no=$1 shift;; Xesac Xkill $no $* X.DE X.PP XIf we move X.I /bin/kill Xfor example to X.I /usr/bin/kill Xand install this script as X.I /bin/kill , Xso... XWell, in that case your system will recursively get a severe headache Xas soon as the next X.I kill Xis issued: Xyour command search path X.B PATH Xwhich can be displayed using X.ne 2 X.DS Xecho $PATH X.DE X.LP Xnormally consists of the active directory, Xi.e., of nothing, Xfollowed by X.B /bin Xand then X.B /usr/bin . X.B kill Xin the last line of the shell script presumably will be found in X.I /bin , Xi.e., will be the shell script just issuing the command! XTo avoid recursion Xthe last line therefore must reference the moved, original X.I kill Xcommand. X.PP XBy the way: Xa shell script always should fully qualify system commands Xor it should set X.B PATH Xso that private commands cannot influence public shell scripts. X.PP XThe C shell executes X.I kill Xitself. XThe Bourne shell, however, does not; Xin an emergency a user may have too many processes Xand at the same time no way Xto kill them from within the Bourne shell. XThe following C program has the same capabilities as the Xshell script above. XHowever, if the program is called as X.ne 2 X.DS Xexec ekill ... X.DE X.LP Xit does not need a separate process table entry Xand it will return with a new copy of the Bourne shell. XCalled without an argument Xthe program will display a list of signal names: X.ne 13 X.DS X#include <stdio.h> X#include <signal.h> X#include <ctype.h> X X#define eq(a,b) (strcmp((a), (b)) == 0) X Xstatic char * signals[] = { X "HUP", "INT", "QUIT", "ILL", "TRAP", X "IOT", "EMT", "FPE", "KILL", "BUS", X "SEGV", "SYS", "PIPE", "ALRM", "TERM", X 0 }; Xstatic char eflag; /* argv[0][0] */ X.DE X.ne 10 X.DS Xexit(code) /* terminate or new shell */ X int code; X{ X if (eflag == 'e' X && isatty(fileno(stdin)) X && isatty(fileno(stdout))) X execl("/bin/sh", "sh", "-i", 0); X _exit(code); X} X.DE X.ne 13 X.DS Xstatic int signum(name) /* number of a signal */ X char * name; X{ register char ** sig = signals, X * ucase = stoupper(strsave(name)); X X while (*sig) X if (eq(ucase, *sig++)) X { cfree(ucase); X return sig - signals; X } X return -1; X} X.DE X.ne 31 X.DS Xmain(argc, argv) X int argc; X register char ** argv; X{ register int sig = SIGTERM; X X eflag = **argv; X if (*++argv && **argv == '-') X { if (isdigit(*++*argv)) X sig = atoi(*argv); X else X sig = signum(*argv); X if (sig <= 0 || sig >= NSIG) X { fputs(*argv, stderr); X fputs(": is not a signal\en", stderr); X exit(1); X } X ++argv; X } X if (*argv) X do X if (isdigit(**argv) X && kill(atoi(*argv), sig) == -1) X perror(*argv); X while (*++argv); X else X for (argv = signals; *argv; ++ argv) X fprintf(stderr, "%d\et%s\en", X argv - signals + 1, *argv); X exit(0); X} X.DE X.LP XObserve that X.B exit() Xmust be explicitly called at the end of X.B main() : Xin this program we define X.B exit() Xso that either the current process calls up a Bourne shell using X.B exec (2), Xor that the process itself is terminated using X.B _exit() . X.B exit() , Xby the way, Xcannot be defined local to the program, Xi.e., using X.B static , Xotherwise library functions called by our program could still Xdrag in the official version of X.B exit() . X.PP XWe have used two functions Xwhich may be useful in other applications: X.ne 11 X.DS X#include <stdio.h> X Xchar * strsave(s) /* save string dynamically */ X register char * s; X{ register char * save = calloc(strlen(s), sizeof(char)); X X if (save) X return strcpy(save, s); X perror("strsave"), exit(1); X} X.DE X.LP X.B strsave() Xis mentioned in the C book by Kernighan and Ritchie, Xbut it has unfortunately not made its way into the C library. XThis version assumes Xthat X.B strcpy() Xdelivers its first argument as a result X\(em which is claimed by string(3) Xbut unfortunately not by the library Xwhich X.I lint Xon our system uses for checking library calls... X.PP XThe following function converts its string(!) argument to upper case: X.ne 12 X.DS X#include <ctype.h> X Xchar * stoupper(s) /* string to UPPER CASE */ X register char * s; X{ register char * cp; X X for (cp = s; *cp; ++cp) X if (islower(*cp)) X *cp = toupper(*cp); X return s; X} X.DE X.LP XNote that (at least in some implementations) X.B toupper() Xwill even deliver "upper case" Xif the argument is not a lower case letter! XActually, Xthis is quite reasonable: Xif we already know the character to be a lower case letter, Xwe need not call X.B islower() ; Xthis will only save a few microseconds, Xif the call is not made implicitly by X.B toupper() . X.SH X/etc/init X.SH XControlling multi-user service X.PP X.I init , Xprocess number 1, Xin some implementations of UNIX version 7 Xreacts to a signal and reads the file X.I /etc/ttys Xagain. XA terminal can participate in multi-user operations, Xif it is mentioned in this file on a line starting with X.B 1 . XIf the line starts with X.B 0 , Xthe terminal is excluded from multi-user service. X.PP XIf X.I init Xaccepts a suitable signal, Xwe can therefore control multi-user service dynamically Xby changing X.I /etc/ttys Xappropriately and then issuing the signal. XThis happens in the following program Xwhich is installed as X.I attach Xand X.I detach Xin Edition VII: X.FS XEdition VII is a trademark of The Perkin-Elmer Corporation. X.FE X.I attach Xarranges for the terminals mentioned as arguments Xto participate in multi-user operations, X.I detach Xdrops the terminals Xand eliminates the associated processes. X.PP XThese commands should be used with some caution; Xif signals are sent to X.I init Xin rapid succession, Xprocess 1 might be eliminated Xand with it essentially the entire system! XThe program therefore must first modify the file for X.I all Xarguments Xand then send a single signal. XReading the file is the more expensive operation. XWe therefore compare each line of the file with all arguments to the command. X.PP XThe terminal name appears at the end of a line in the file Xand we are willing to select a terminal based on Xthe last few letters of its name. XOf course, Xthe argument X.B e Xwill then cause X.B console X.I "as well as" X.B ttye Xto be attached or detached... X.PP XThe program itself is quite simple: Xwe copy X.I /etc/ttys Xinto a temporary file and change the relevant lines. XFor efficiency reasons we compare each line Xwith all arguments before writing it to the output file. X.I attach Xis less destructive than X.I detach , Xconsequently the program really must be called as X.I detach Xfor a terminal to be disconnected. X.ne 9 X.DS X#include <stdio.h> X X#define LEN 20 /* max. line length */ X#define TTYS "/etc/ttys" /* terminal table */ X#define TMP "/etc/ttys.tmpXXXXXX" /* temporary copy */ X#define TELL kill(1, 2) == -1 && (perror("init"), exit(1)) X X#define eq(a,b) (strcmp((a), (b)) == 0) X.DE X.ne 14 X.DS Xmain(argc, argv) X int argc; X char ** argv; X{ register char ** argp; X char buf[LEN], * last, X * tmp = mktemp(TMP), X set = **argv == 'd'? '0': '1'; X int change = 0; X X if (! freopen(TTYS, "r", stdin)) X perror(TTYS), exit(1); X if (! freopen(tmp, "w", stdout)) X perror(tmp), exit(1); X.DE X.ne 19 X.DS X while (fgets(buf, sizeof buf, stdin)) X { last = buf + strlen(buf) - 1; X if (*last != '\en') X { fputs(buf, stderr); X fputs(": too long\en", stderr); X exit(1); X } X *last = '\e0'; X for (argp = argv+1; *argp; ++ argp) X if (**argp X && eq(last - strlen(*argp), *argp)) X break; X if (*argp && buf[0] != set) X { ++ change; X buf[0] = set; X } X puts(buf); X } X.DE X.ne 9 X.DS X if (change) X { fflush(stdout); X rename(TTYS, tmp); X TELL; X } X else X unlink(tmp); X} X.DE X.PP XIf at least one line was changed, Xwe replace X.I /etc/ttys Xby the copy and inform X.I init . XNormally only the super user can change X.I /etc X(!), Xtherefore only the super user can create the copy; Xthus, Xrenaming should be possible: X.ne 15 X.DS X#include <stdio.h> X Xrename(new, old) /* rename a file */ X char * new, * old; X{ X unlink(new); X if (link(old, new) == -1) X { fputs(old, stderr); X fputs(", ", stderr); X perror(new), exit(1); X } X if (unlink(old) == -1) X perror(old), exit(1); X} X.DE X.SH XBlocking signals X.PP XWhile X.I /etc/ttys Xis being replaced, Xwe would prefer not to be interrupted Xsince we might otherwise find our system Xwithout a list of user terminals during the next bootstrap! XNot even the super user can prevent X.B SIGKILL , Xbut all other signals can be blocked with the following X.B disable() Xfunction. XA subsequent call to X.B enable() Xwill later restore the original situation: X.ne 12 X.DS X#include <signal.h> X Xstatic int (* sigs[NSIG-1])(); X Xdisable() X{ register int i; X X for (i = 1; i < NSIG; ++ i) X if (i != SIGKILL) X sigs[i-1] = signal(i, SIG_IGN); X} X.DE X.ne 8 X.DS Xenable() X{ register int i; X X for (i = 1; i < NSIG; ++ i) X if (i != SIGKILL) X signal(i, sigs[i-1]); X} X.DE X.SH X/lib/libc(abort.o) X.SH XWith extreme prejudice X.PP X.B abort() , Xat least according to chapter 3 of the manual, Xexecutes the PDP-11 X.B iot Xinstruction and thus normally will terminate the process Xwith a core dump. XI wonder what our Perkin-Elmer system does? X.PP XBeginning with UNIX version 7 one can send signals to oneself, Xand a portable way to terminate a process with a core dump Xis the following: X.ne 8 X.DS X#include <signal.h> X Xabort() X{ X signal(SIGQUIT, SIG_DFL); X kill(getpid(), SIGQUIT); X} X.DE X.SH X/bin/stty X.SH XStacked signals X.PP XDepending on context, Xvarious cleanup tasks may need to be performed Xat various points in a program: Xsomewhere there exists a temporary file, Xelsewhere a terminal echo may have been turned off, Xelsewhere yet a file may have the wrong protection keys, Xand in well founded cases a second or third process Xmight additionally be on the prowl. XIt is relatively easy to deal with one such case at a time: X.ne 14 X.DS X#include <signal.h> X#include <sgtty.h> X#include <stdio.h> X Xstatic struct sgttyb sgttyb; X Xecho(i) X register int i; X{ X stty(fileno(stdout), & sgttyb); X if (i) X kill(getpid(), i); X} X.DE X.ne 16 X.DS Xmain() X{ int flags; X X gtty(fileno(stdout), & sgttyb); X flags = sgttyb.sg_flags, sgttyb.sg_flags &= ~ECHO; X stty(fileno(stdout), & sgttyb); X sgttyb.sg_flags = flags; X X ... X signal(SIGINT, echo); X ... X echo(0); X signal(SIGINT, SIG_DFL); X ... X} X.DE X.LP X.B echo() Xhere takes care of the necessary cleanup operations. XIf the routine was called through a signal, Xthe same signal is subsequently sent to the process itself again. X.PP XIn the general case, Xwe should be able to stack the repairing functions. XThis can be done using the following functions Xwhich mostly combine the ideas of X.B disable() Xand X.B abort() : X.RS X.IP "\fBint catchall(f) int (* f)();\fR" X.br Xdeclares, similar to X.B signal (2), X.B f Xas a reaction to all signals except X.B SIGKILL . X.IP "\fBint catch(f) int (* f)();\fR" X.br Xdoes the same, Xbut only for those signals which are not ignored at present. X.IP "\fBint recatch(i) int i;\fR" X.br XThe aforementioned function X.B f() Xis called with the number of the signal effecting the call as an argument. XAt the beginning of this function Xthe signal itself has been set to X.B SIG_DFL . X.B recatch(i) Xarranges for the signal X.B i Xto be set just as it was set X.I with Xthe last call of X.B catchall() Xor X.B catch() . X.IP "\fBint uncatch()\fR" X.br Xarranges for all signals the setting Xwhich was in effect X.I before Xthe last call to X.B catchall() Xor X.B catch() . X.RE X.LP XThe functions return 0 on success and -1 on failure. X.PP XThese functions can be used to stack signal reactions: Xif we only want to execute the latest reaction, Xwe conclude a trap function using X.B recatch() . XIf all reactions to the signal should be executed, Xwe specify X.B uncatch() Xat the end of each trap function Xand then send ourselves the signal again. XAt the beginning of a trap function Xthe signal should be set to X.B SIG_IGN Xin either case. X.PP XThe functions themselves are not very complicated. XEssentially they dynamically manage a stack Xon which the old settings of the signals are stored, Xwhich can be obtained from X.B signal (2): X.ne 16 X.DS X#include <signal.h> X Xstatic struct signals { X int (* s_sigs[NSIG-1])(); X struct signals * s_prev; X } * top; X Xstatic struct signals * push() X{ register struct signals * p = (struct signals *) X calloc(1, sizeof(struct signals)); X X if (p) X p->s_prev = top, top = p; X return p; X} X.DE X.ne 14 X.DS Xint catchall(f) X register int (* f)(); X{ register int i; X register struct signals * p = push(); X X if (p) X { for (i = 1; i < NSIG; ++ i) X if (i != SIGKILL) X p->s_sigs[i-1] = signal(i, f); X return 0; X } X return -1; X} X.DE X.ne 16 X.DS Xint catch(f) X register int (* f)(); X{ register int i; X register struct signals * p = push(); X X if (p) X { for (i = 1; i < NSIG; ++ i) X if (i != SIGKILL X && (p->s_sigs[i-1] = signal(i, SIG_IGN)) X != SIG_IGN) X signal(i, f); X return 0; X } X return -1; X} X.DE X.ne 15 X.DS Xint uncatch() X{ register int i; X register struct signals * p = top; X X if (p) X { for (i = 1; i < NSIG; ++ i) X if (i != SIGKILL) X signal(i, p->s_sigs[i-1]); X top = top->s_prev; X cfree(p); X return 0; X } X return -1; X} X.DE X.ne 10 X.DS Xint recatch(i) X register int i; X{ X if (top) X { signal(i, top->s_sigs[i-1]); X return 0; X } X return -1; X} X.DE X.PP X.B catch() Xcontains the typical statements Xwhich should always really be used Xif X.B SIGINT Xor X.B SIGQUIT Xshould be caught. XThe first call to X.B signal() Xreports whether the signal is currently being ignored: Xin this case the setting of the signal has not yet been changed. XOnly if the signal is not being ignored Xis it connected to the desired new reaction. XThese statements prevent a background process X(for which these signals are ignored by default) Xfrom being subject to these signals again. X.SH X/usr/games X.SH XSignal puzzle X.PP XWhat does the following program do: X.ne 30 X.DS X#include <signal.h> X Xcatch() {} X Xmain() X{ int vater, ich, sohn; X X signal(16, catch); X for (vater = 0; ich = getpid(); vater = ich) X switch (sohn = fork()) { X default: X pause(); X case -1: X signal(SIGALRM, catch); X alarm(2); X getchar(); X alarm(0); X if (vater) X { signal(16, catch); X kill(vater, 16); X pause(); X } X kill(sohn, 16); X wait(0); X exit(0); X case 0: X ; X } X} X.DE X.LP XDue to a complete absense of X.B goto Xstatements Xthis must be a so-called X.I structured Xprogram. XAccording to an often published opinion, Xhowever, Xsuch programs are always essentially immediately intelligible. X.QP X.I Hint: XEven if you do everything else as a super user: Xdon't, at least this once! XOtherwise your system should self-destruct Xand this columnist shall disavow any knowledge of this program. X SHAR_EOF echo shar: a missing newline was added to "'4.84'" if test 28731 -ne "`wc -c '4.84'`" then echo shar: error transmitting "'4.84'" '(should have been 28731 characters)' fi : ' End of shell archive' exit 0 -- Carl Swail Mail: National Research Council of Canada Building U-66, Montreal Road Ottawa, Ontario, Canada K1A 0R6 Phone: (613) 998-3408 USENET: {pesnta,lsuc}!nrcaero!carl {cornell,uw-beaver}!utcsrgv!dciem!nrcaero!carl {allegra,decvax,duke,floyd,ihnp4,linus}!utzoo!dciem!nrcaero!carl