[pe.cust.sources] UNIX Clinic by Axel Schreiner - 4/84

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