[comp.sources.unix] v22i030: Byte Unix benchmarks, Part03/05

rsalz@uunet.uu.net (Rich Salz) (05/08/90)

Submitted-by: "Ben Smith @ BYTE" <ben@bytepb.byte.com>
Posting-number: Volume 22, Issue 30
Archive-name: byte-benchmarks/part03

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 3 (of 5)."
# Contents:  bench.doc big.c dbmserv.c
# Wrapped by rsalz@papaya.bbn.com on Tue May  8 08:55:32 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'bench.doc' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'bench.doc'\"
else
echo shar: Extracting \"'bench.doc'\" \(14682 characters\)
sed "s/^X//" >'bench.doc' <<'END_OF_FILE'
X@BT
X
X[TITLE]BYTE's UNIX Benchmarks
X[DEK]Separating fact from fiction in the exploding UNIX empire
X[TOC]Before you jump into the UNIX pool, see how your favorite system stacks
Xup against the rest of the pack.
X
XBen Smith
X
XIn making purchase decisions, it's difficult to know whom to believe. Each
Xvendor claims, predictably, that their products are better than the
Xcompetition's, but how does one prove, or debunk, these claims?
X  Cost and performance typically top the list of considerations for those
Xseeking to purchase equipment, and while cost can be easily compared,
Xperformance cannot, and comparing costs without analyzing each system's
Xrelative value is a worthless exercise.
X  When DOS became popular, it allowed for the development of performance
Xmeasurement programs, benchmarks, that would run on any system that ran DOS.
XBYTE's lab technicians set about creating their own, and the BYTE DOS
Xbenchmarks were born. Dozens of systems have been clocked using these
Xfacilities, and each review of a new DOS-based system includes the results of
Xthese benchmarks.
X  This is all very well, but while DOS is installed on a great many systems,
Xit is no longer the !ITAL!only!ENDITAL! popular multi-platform operating
Xsystem. User demands for greater expandability, better performance and
Xmulti-tasking have turned UNIX systems into one of the fastest growing
Xsegments of the market. When UNIX stepped from minicomputers to workstations,
Xit established itself as the defacto OS for an exciting new breed of machine.
XNow, with solid implementations for affordable Intel and Motorola-based
Xplatforms, UNIX is making a name for itself in the PC realm. As UNIX finds
Xits way into the mainstream, it is necessary to have the tools to objectively
Xmeasure not only the performance of various hardware platforms, but of
Xdifferent versions of UNIX as well.
X
X!SUBHED!Unix is Not MS-DOS!ENDSUBHED!
XConceptually, BYTE's UNIX benchmarks are the same as BYTE's MS-DOS
Xbenchmarks: We have combined evaluation of both low-level operations and
Xhigh-level applications type programs to highlight the performance of the
Xentire system.
X  But UNIX is considerably different from MS-DOS. In the first place, it is a
X!ITAL!multi!ENDITAL!-tasking, !ITAL!multi!ENDITAL!-user operating system. It
Xis also portable, able to run on many different kinds of computers. MS-DOS is
Xa !ITAL!single!ENDITAL!-tasking, !ITAL!single!ENDITAL!-user operating system,
Xand it is intended to run on essentially one kind of computer, an IBM-PC or
XPC ``clone,'' utilizing a specific class of processor from Intel. As a
Xresult, the UNIX benchmarks differ from their MS-DOS counterparts. Even
Xthough there are some equivalent low-level tests, you will find that even
Xthese run differently; the popular Dhrystone benchmark commonly gives
Xdifferent results, on the same hardware, when run under both DOS and UNIX.
XThe reason? Different compilers are being used, and the underlying operating
Xsystems and services are wildly different.
X  Another important difference is that Microsoft is the only real source of
XDOS; other suppliers simply repackage Microsoft's basic operating system
Xunder other names. In contrast, there are many different kinds of UNIX, and
Xwhile similarities exist (the core UNIX from Dell, Everex and Interactive
XSystems are virtually the same), there are UNIX and UNIX-like operating
Xsystems that differ greatly from one another. Conclusion: The UNIX benchmarks
Xare evaluating the implementation of UNIX and the resident compiler as well
Xas the hardware on which it is running (the MS-DOS and Apple Macintosh
Xbenchmarks use a common compiler, the public-domain Small C).
X  With so many variables, what is constant? Well, we have established a
Xbaseline, SCO Xenix 386 version 2.3.1. running on the Everex 386/33 with 4
XMbytes of RAM and an 80387 math coprocessor. While it isn't UNIX per se
X(because AT&T decides which implementations may be called ``UNIX''), it is
Xmore popular than any other PC UNIX implementation. It is specifically
Xdesigned for 80386-based computers with full 32 bit memory access. The Everex
X386/33 was chosen because it is one of today's highest performance 386
Xcomputers properly configured to run the full 32 bit operating system. (Some
X386 computers cannot access memory through single 32 bit operations; small
Xmatter if you are just running MS-DOS, an 8 bit operating system, but serious
Xif you want to run UNIX.) This combination of system and OS is timely, but
Xwe'll continue to adjust the baseline as needed to reflect the installed PC
Xand workstation UNIX base.
X
X!SUBHED!The Low Level Bench Programs!ENDSUBHED!
XThe BYTE UNIX benchmarks consist of eight groups of programs: arithmetic,
Xsystem calls, memory operations, disk operations, dhrystone, database
Xoperations, system loading, and miscellaneous. These can be roughly divided
Xinto the low-level tests (arithmetic, system calls, memory, disk, and
Xdhrystone) and high-level tests (database operations, system loading, and the
XC-compiler test that is part of the miscellaneous set).
X  The Dhrystone test is known more formally as ``Dhrystone 2''. It performs
Xno floating-point operations, but it does involve arrays, character strings,
Xindirect addressing, and most of the non-floating point instructions that
Xmight be found in an application program. It also includes conditional
Xoperations and other common program flow controls. The output of the test is
Xthe number of dhrystone loops per second. It is used in the BYTE benchmarks
Xbecause of its wide selection of operations and because it is one of the most
Xwidely run benchmark programs.
X  A future version of the BYTE UNIX benchmarks will include the Whetstone
Xbenchmark test program, as well. The Whetstone benchmark is conceptually
Xsimilar to the Dhrystone, but with an emphasis on math; it is a mix of
Xfloating point and integer arithmetic, function calls, array operations,
Xconditionals, and transcendental function calls.
X  All the arithmetic tests have the same source code with different data
Xtypes substituted for the operations: register, short, int, long, float,
Xdouble, and an empty loop for calculating the overhead required by the
Xprogram. The actual test involves assignment, addition, subtraction,
Xmultiplication, and division. Very simple. But don't bother running the float
Xand double precision test unless you have a math co-processor; what takes a
Xmath co-processor system 15 seconds, may take an unaided processor 30 minutes
Xor more!
X  The system call tests are: system call overhead, pipe throughput, pipe
Xcontext switching, spawning of child processes, execl (replacement of the
Xcurrent process by a new process), and file read, write, and copy. The system
Xcall overhead test evaluates the time required to do iterations of
X!MONO!dup()!ENDMONO!, !MONO!close()!ENDMONO!, !MONO!getpid()!ENDMONO!,
X!MONO!getuid()!ENDMONO!, and !MONO!umask()!ENDMONO! calls.
X  The pipe throughput test has no real counterpart in real-world programming;
Xin it, a single process opens a pipe (an inter-process communications channel
Xthat works rather like its plumbing namesake) to itself and spins a megabyte
Xaround this short loop. You might call this the pipe overhead test. The
Xcontext switching pipe test is more like a real-world application; the test
Xprogram spawns a child process with which it carries on a bi-directional pipe
Xconversation.
X  The spawn test creates a child process which immediately dies after its own
X!MONO!fork()!ENDMONO!. The process is repeated over and over. Similarly, the
Xexec test is a process that repeatedly changes to a new incarnation. One of
Xthe arguments passed to the new incarnation is the number of remaining
Xiterations (there has to be some control, after all).
X The file read, write, and copy tests capture the number of characters that
Xcan be written, read, and copied in a specified time (default is 10 seconds).
XIf you run this test with the minimum element (1 second), you should see a
Xsignificantly higher value for all operations if your system implements disk
Xcacheing. Be sure you have plenty of disk space before you run this test.
X
X!SUBHED!The High-Level Bench Programs!ENDSUBHED!
XTo qualify as a high-level test, the test must involve operations that a
Xreal-world application program might employ, including heavy use of the CPU
Xand disk. At the time of writing this article, we have currently implemented
Xonly the system loading and database tests, but we will be adding several new
Xtests in the months ahead.
X  The system loading test is a shell script that is run by 1, 2, 4, and 8
Xconcurrent processes. The script consists of an alphabetic sort one file to
Xanother; taking the octal dump of the result and doing a numeric sort to a
Xthird file; running grep on the result of the alphabetic sort file;
X!MONO!tee!ENDMONO!ing the result to a file and to !MONO!wc!ENDMONO! (word
Xcount); writing the final result to a file; and removing all of the resulting
Xfiles. This script was used in the original BYTE UNIX benchmarks (1983), but
Xthe source file is several magnitudes larger than the original.
X  The C compile and link is nothing more than that.
X  The database operations consist of random read, write, and add operations
Xon a database file. The operations are handled by a server process; the
Xrequests come from client processes. The test is run with 1, 2, 4, and 8
Xclient processes. The test employs semaphores and message queues. Semaphores
Xare being used less and less these days. BSD systems use sockets instead in
Xplace of both of these System V.3 IPC utilities. System V.4 offers both. This
Xtest is being rewritten using sockets, but since Xenix doesn't implement
Xsockets, our baseline configuration becomes instantly obsolete when we
Xreplace the database test. Just another one of those little problems in
Xtrying to create journalistic computer benchmarks: any program that has been
Xfully debugged is probably obsolete [ Murphy, et al ].
X
X!SUBHED!Miscellany!ENDSUBHED!
XThe remaining tests are in the miscellaneous group: Tower of Hanoi (a test of
Xrecursive operations) and a test of the UNIX arbitrary precision calculator
Xcalculating the square root of two to 99 decimal places.
X  No doubt, we will be adding tests to this suite as we see the need to test
Xand evaluate from different perspectives.
X
X!SUBHED!Problems in the Modern World!ENDSUBHED!
XThe major problem we have had with developing the UNIX benchmark programs is
Xdesigning them so that they fairly reflect the strengths and weaknesses of
Xall the systems on which we anticipate using them. For example, the
Xoperations should allow RISC machines to give appropriately high performance
Xfor the sorts of operations that RISC is good for, and should also illustrate
Ximprovements provided by faster bus speeds, better math coprocessors and the
Xlike. In the case of RISC, the efficiency of the compiler is of utmost
Ximportance; RISC compilers must rearrange instructions to take advantage of
Xinstruction pipelining (for an overview of RISC, see BYTE, May 1988).
X  The majority of the UNIX systems that we look at employ disk caching. This
Xis especially important because modern UNIX includes swapping and paging out
Xto disk when there is insufficient memory for a task or the number of tasks.
XIt is an interesting exercise to run the disk file operations test with
Xincreasingly large files and note the point at which performance drops.
X
X!SUBHED!How They Work!ENDSUBHED!
XA 400 line Bourne shell script (!MONO!Run!ENDMONO!) administers the
Xbenchmarking system. After the evaluation of the command line options, the
Xbenchmarking operation for each test has three stages: parameter setup,
Xtiming the execution of the test, and calculation/formatting operations (see
XFigure 1). After !MONO!Run!ENDMONO! determines the parameters for the test,
Xit sends a formatted description to the output file. !MONO!Run!ENDMONO! then
Xinvokes the specific test by means of the UNIX command !MONO!time!ENDMONO!.
XThe output of !MONO!time!ENDMONO! and any output from the test itself end up
Xin a raw data file. Most tests are run six times so that any variance can be
Xaveraged. On completion of a set of tests, !MONO!Run!ENDMONO! invokes a
Xcleanup script, which does the statistical calculations on the raw data using
Xthe !MONO!awk!ENDMONO! formatting language.
X  The greater part of the benchmark programs are written in C and are
Xcompiled on the test machine prior to running the tests. 
X
X!SUBHED!Using the Results!ENDSUBHED!
XIf all you need is a raw measure of performance, then feel free to use the
XDhrystone and Whetstone tests as indices of just that. But if you want to use
Xthe benchmarks to evaluate a machine's ability to serve some real need, then
Xyou should do the following:
X1. Analyze your requirements as far as the type of computing, amount and type
Xof communications I/O, and amount and type of disk I/O.
X2. Score the subject machines using weighting factors that reflect your
Xrequirements.
X3. Generate a price vs. performance plot.
X4. Use the price vs. performance, along with information about the
Xreliability servicability of the hardware.
X  Step 4 is really more of an art than anything else. It is extremely
Ximportant, however, to not rely on price vs. performance alone.
X We use our UNIX Benchmarks for doing a rough analysis and comparison of
Xdivergent machines. (See figure 2, ``UNIX Machines Tested.'') As you can see,
Xwe even go so far as to generate a single index number, a sort of reduction
Xof all of the benchmark tests to a single value. This index is generated by
Xsumming the the individual indices of the dhrystone test, the floating point
Xtest, the shell test with eight concurrent processes, the C compiler time,
Xthe !MONO!dc!ENDMONO! routine, and the tower of hanoi time. By definition,
Xthe combined index for the baseline machine is six. Indicess above six imply
Xa better overall performance than the baseline machine; indices less than
Xsix, worse performance. Always keep in mind that having a single index rating
Xfor a machine may make good cocktail conversation, but it is incredibly
Xsimplistic. It is like reducing a complex sculptural shape to a single point;
Xyou no longer can tell what you are looking at. This number doesn't reflect
Xany real-world use of a UNIX system. However, the index is devised so that it
Xgives an overall indication of different kinds of system operations and so is
Xvaluable to our reviews.
X  BYTE's UNIX benchmarking suite is small enough to port easily to any UNIX
Xsystem, yet diverse and flexible enough to be useful for a wide spectrum of
Xbenchmarking requirements. Besides, they're in the public domain, so they can
Xbe obtained for little, if any, cost. What better reason do you need to use
Xthem?
END_OF_FILE
if test 14682 -ne `wc -c <'bench.doc'`; then
    echo shar: \"'bench.doc'\" unpacked with wrong size!
fi
chmod +x 'bench.doc'
# end of 'bench.doc'
fi
if test -f 'big.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'big.c'\"
else
echo shar: Extracting \"'big.c'\" \(13750 characters\)
sed "s/^X//" >'big.c' <<'END_OF_FILE'
X/*******************************************************************************
X *  The BYTE UNIX Benchmarks - Release 2
X *          Module: big.c   SID: 2.4 4/17/90 16:45:31
X *          
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Ben Smith or Rick Grehan at BYTE Magazine
X *	bensmith@bixpb.UUCP    rick_g@bixpb.UUCP
X *
X *******************************************************************************
X *  Modification Log:
X *
X ******************************************************************************/
Xchar SCCSid[] = "@(#) @(#)big.c:2.4 -- 4/17/90 16:45:31";
X/*
X *  dummy code for execl test [ old version of makework.c ]
X *
X *  makework [ -r rate ] [ -c copyfile ] nusers
X *
X *  job streams are specified on standard input with lines of the form
X *  full_path_name_for_command [ options ] [ <standard_input_file ]
X *
X *  "standard input" is send to all nuser instances of the commands in the
X *  job streams at a rate not in excess of "rate" characters per second
X *  per command
X *
X */
X
X#include <stdio.h>
X#include <signal.h>
X
X#define DEF_RATE	5.0
X#define GRANULE		5
X#define CHUNK		60
X#define MAXCHILD	12
X#define MAXWORK		10
X
Xfloat	thres;
Xfloat	est_rate = DEF_RATE;
Xint	nusers;		/* number of concurrent users to be simulated by
X			 * this process */
Xint	firstuser;	/* ordinal identification of first user for this
X			 * process */
Xint	nwork = 0;	/* number of job streams */
Xint	exit_status = 0;	/* returned to parent */
Xint	sigpipe;	/* pipe write error flag */
X
Xstruct st_work {
X	char	*cmd;		/* name of command to run */
X	char	**av;		/* arguments to command */
X	char	*input;		/* standard input buffer */
X	int	inpsize;	/* size of standard input buffer */
X	char	*outf;		/* standard output (filename) */
X} work[MAXWORK];
X
Xstruct {
X	int	xmit;	/* # characters sent */
X	char	*bp;	/* std input buffer pointer */
X	int	blen;	/* std input buffer length */
X	int	fd;	/* stdin to command */
X	int	pid;	/* child PID */
X	char	*line;	/* start of input line */ 
X	int	firstjob;	/* inital piece of work */
X	int	thisjob;	/* current piece of work */
X} child[MAXCHILD], *cp;
X
Xmain(argc, argv)
Xint	argc;
Xchar	*argv[];
X{
X    int		i;
X    int		l;
X    int		fcopy = 0;	/* fd for copy output */
X    int		master = 1;	/* the REAL master, == 0 for clones */
X    int		nchild;		/* no. of children for a clone to run */
X    int		done;		/* count of children finished */
X    int		output;		/* aggregate output char count for all
X				   children */
X    int		c;
X    int		thiswork = 0;	/* next job stream to allocate */
X    int		nch;		/* # characters to write */
X    int		written;	/* # characters actully written */
X    char	logname[15];	/* name of the log file(s) */
X    int		onalarm();
X    int		pipeerr();
X    int		wrapup();
X    int		grunt();
X    int		pvec[2];	/* for pipes */
X    char	*p;
X    char	*prog;		/* my name */
X
X#if ! debug
X    freopen("masterlog.00", "a", stderr);
X#endif
X    fprintf(stderr, "*** New Run ***  ");
X    prog = argv[0];
X    while (argc > 1 && argv[1][0] == '-')  {
X	p = &argv[1][1];
X	argc--;
X	argv++;
X	while (*p) {
X	    switch (*p) {
X	    case 'r':
X			est_rate = atoi(argv[1]);
X			sscanf(argv[1], "%f", &est_rate);
X			if (est_rate <= 0) {
X			    fprintf(stderr, "%s: bad rate, reset to %.2f chars/sec\n", prog, DEF_RATE);
X			    est_rate = DEF_RATE;
X			}
X			argc--;
X			argv++;
X			break;
X
X	    case 'c':
X			fcopy = open(argv[1], 1);
X			if (fcopy < 0)
X				fcopy = creat(argv[1], 0600);
X			if (fcopy < 0) {
X			    fprintf(stderr, "%s: cannot open copy file '%s'\n",
X				prog, argv[1]);
X			    exit(2);
X			}
X			lseek(fcopy, 0L, 2);	/* append at end of file */
X			argc--;
X			argv++;
X			break;
X
X	    default:
X		fprintf(stderr, "%s: bad flag '%c'\n", prog, *p);
X			exit(4);
X	    }
X	    p++;
X	}
X    }
X    
X    if (argc < 2) {
X	fprintf(stderr, "%s: missing nusers\n", prog);
X	exit(4);
X    }
X
X    nusers = atoi(argv[1]);
X    if (nusers < 1) {
X	fprintf(stderr, "%s: impossible nusers (%d<-%s)\n", prog, nusers, argv[1]);
X	exit(4);
X    }
X    fprintf(stderr, "%d Users\n", nusers);
X    argc--;
X    argv++;
X
X    /* build job streams */
X    getwork();
X#if debug
X    dumpwork();
X#endif
X
X    /* clone copies of myself to run up to MAXCHILD jobs each */
X    firstuser = MAXCHILD;
X    fprintf(stderr, "master pid %d\n", getpid());
X    fflush(stderr);
X    while (nusers > MAXCHILD) {
X	fflush(stderr);
X	if (nusers >= 2*MAXCHILD)
X	    /* the next clone must run MAXCHILD jobs */
X	    nchild = MAXCHILD;
X	else
X	    /* the next clone must run the leftover jobs */
X	    nchild = nusers - MAXCHILD;
X	if ((l = fork()) == -1) {
X	    /* fork failed */
X	    fatal("** clone fork failed **\n");
X	    goto bepatient;
X	} else if (l > 0) {
X	    fprintf(stderr, "master clone pid %d\n", l);
X	    /* I am the master with nchild fewer jobs to run */
X	    nusers -= nchild;
X	    firstuser += MAXCHILD;
X	    continue;
X	} else {
X	    /* I am a clone, run MAXCHILD jobs */
X#if ! debug
X	    sprintf(logname, "masterlog.%02d", firstuser/MAXCHILD);
X	    freopen(logname, "w", stderr);
X#endif
X	    master = 0;
X	    nusers = nchild;
X	    break;
X	}
X    }
X    if (master)
X	firstuser = 0;
X
X    close(0);
X    for (i = 0; i < nusers; i++ ) {
X	fprintf(stderr, "user %d job %d ", firstuser+i, thiswork);
X	if (pipe(pvec) == -1) {
X	    /* this is fatal */
X	    fatal("** pipe failed **\n");
X	    goto bepatient;
X	}
X	fflush(stderr);
X	if ((child[i].pid = fork()) == 0) {
X	    int	fd;
X	    /* the command */
X	    if (pvec[0] != 0) {
X		close(0);
X		dup(pvec[0]);
X	    }
X#if ! debug
X	    sprintf(logname, "userlog.%02d", firstuser+i);
X	    freopen(logname, "w", stderr);
X#endif
X	    for (fd = 3; fd < 24; fd++)
X		close(fd);
X	    if (work[thiswork].outf[0] != '\0') {
X		/* redirect std output */
X		char	*q;
X		for (q = work[thiswork].outf; *q != '\n'; q++) ;
X		*q = '\0';
X		if (freopen(work[thiswork].outf, "w", stdout) == NULL) {
X		    fprintf(stderr, "makework: cannot open %s for std output\n",
X			work[thiswork].outf);
X		    fflush(stderr);
X		}
X		*q = '\n';
X	    }
X	    execv(work[thiswork].cmd, work[thiswork].av);
X	    /* don't expect to get here! */
X	    fatal("** exec failed **\n");
X	    goto bepatient;
X	}
X	else if (child[i].pid == -1) {
X	    fatal("** fork failed **\n");
X	    goto bepatient;
X	}
X	else {
X	    close(pvec[0]);
X	    child[i].fd = pvec[1];
X	    child[i].line = child[i].bp = work[thiswork].input;
X	    child[i].blen = work[thiswork].inpsize;
X	    child[i].thisjob = thiswork;
X	    child[i].firstjob = thiswork;
X	    fprintf(stderr, "pid %d pipe fd %d", child[i].pid, child[i].fd);
X	    if (work[thiswork].outf[0] != '\0') {
X		char *q;
X		fprintf(stderr, " > ");
X		for (q=work[thiswork].outf; *q != '\n'; q++)
X		    fputc(*q, stderr);
X	    }
X	    fputc('\n', stderr);
X	    thiswork++;
X	    if (thiswork >= nwork)
X		thiswork = 0;
X	}
X    }
X    fflush(stderr);
X
X    srand(time(0));
X    thres = 0;
X    done = output = 0;
X    for (i = 0; i < nusers; i++) {
X	if (child[i].blen == 0)
X	    done++;
X	else
X	    thres += est_rate * GRANULE;
X    }
X    est_rate = thres;
X
X    signal(SIGALRM, onalarm);
X    signal(SIGPIPE, pipeerr);
X    alarm(GRANULE);
X    while (done < nusers) {
X	for (i = 0; i < nusers; i++) {
X	    cp = &child[i];
X	    if (cp->xmit >= cp->blen) continue;
X	    l = rand() % CHUNK + 1;	/* 1-CHUNK chars */
X	    if (l == 0) continue;
X	    if (cp->xmit + l > cp->blen)
X		l = cp->blen - cp->xmit;
X	    p = cp->bp;
X	    cp->bp += l;
X	    cp->xmit += l;
X#if debug
X	    fprintf(stderr, "child %d, %d processed, %d to go\n", i, cp->xmit, cp->blen - cp->xmit);
X#endif
X	    while (p < cp->bp) {
X		if (*p == '\n' || (p == &cp->bp[-1] && cp->xmit >= cp->blen)) {
X		    /* write it out */
X		    nch = p - cp->line + 1;
X		    if ((written = write(cp->fd, cp->line, nch)) != nch) {
X			/* argh! */
X			cp->line[nch] = '\0';
X			fprintf(stderr, "user %d job %d cmd %s ",
X				firstuser+i, cp->thisjob, cp->line);
X 			fprintf(stderr, "write(,,%d) returns %d\n", nch, written);
X			if (sigpipe)
X			    fatal("** SIGPIPE error **\n");
X			else
X			    fatal("** write error **\n");
X			goto bepatient;
X
X		    }
X		    if (fcopy)
X			write(fcopy, cp->line, p - cp->line + 1);
X#if debug
X		    fprintf(stderr, "child %d gets \"", i);
X		    {
X			char *q = cp->line;
X			while (q <= p) {
X				if (*q >= ' ' && *q <= '~')
X					fputc(*q, stderr);
X				else
X					fprintf(stderr, "\\%03o", *q);
X				q++;
X			}
X		    }
X		    fputc('"', stderr);
X#endif
X		    cp->line = &p[1];
X		}
X		p++;
X	    }
X	    if (cp->xmit >= cp->blen) {
X		done++;
X		close(cp->fd);
X#if debug
X	fprintf(stderr, "child %d, close std input\n", i);
X#endif
X	    }
X	    output += l;
X	}
X	while (output > thres) {
X	    pause();
X#if debug
X	    fprintf(stderr, "after pause: output, thres, done %d %.2f %d\n", output, thres, done);
X#endif
X	}
X    }
X
Xbepatient:
X    alarm(0);
X/****
X *  If everything is going OK, we should simply be able to keep
X *  looping unitil 'wait' fails, however some descendent process may
X *  be in a state from which it can never exit, and so a timeout
X *  is used.
X *  5 minutes should be ample, since the time to run all jobs is of
X *  the order of 5-10 minutes, however some machines are painfully slow,
X *  so the timeout has been set at 20 minutes (1200 seconds).
X ****/
X    signal(SIGALRM, grunt);
X    alarm(1200);
X    while ((c = wait(&l)) != -1) {
X        for (i = 0; i < nusers; i++) {
X	    if (c == child[i].pid) {
X		fprintf(stderr, "user %d job %d pid %d done", firstuser+i, child[i].thisjob, c);
X		if (l != 0) {
X		    if (l & 0x7f)
X			fprintf(stderr, " status %d", l & 0x7f);
X		    if (l & 0xff00)
X			fprintf(stderr, " exit code %d", (l>>8) & 0xff);
X		    exit_status = 4;
X		}
X		fputc('\n', stderr);
X		c = child[i].pid = -1;
X		break;
X	    }
X	}
X	if (c != -1) {
X	    fprintf(stderr, "master clone done, pid %d ", c);
X	    if (l != 0) {
X		if (l & 0x7f)
X		    fprintf(stderr, " status %d", l & 0x7f);
X		if (l & 0xff00)
X		    fprintf(stderr, " exit code %d", (l>>8) & 0xff);
X		exit_status = 4;
X	    }
X	    fputc('\n', stderr);
X	}
X    }
X    alarm(0);
X    wrapup("Finished waiting ...");
X
X
X}
X
Xonalarm()
X{
X    thres += est_rate;
X    signal(SIGALRM, onalarm);
X    alarm(GRANULE);
X}
X
Xgrunt()
X{
X    /* timeout after label "bepatient" in main */
X    exit_status = 4;
X    wrapup("Timed out waiting for jobs to finish ...");
X}
X
Xpipeerr()
X{
X	sigpipe++;
X}
X
Xwrapup(reason)
Xchar	*reason;
X{
X    int i;
X    int killed = 0;
X    fflush(stderr);
X    for (i = 0; i < nusers; i++) {
X	if (child[i].pid > 0 && kill(child[i].pid, SIGKILL) != -1) {
X	    if (!killed) {
X		killed++;
X		fprintf(stderr, "%s\n", reason);
X		fflush(stderr);
X	    }
X	    fprintf(stderr, "user %d job %d pid %d killed off\n", firstuser+i, child[i].thisjob, child[i].pid);
X	fflush(stderr);
X	}
X    }
X    exit(exit_status);
X}
X
Xgetwork()
X{
X    int			i;
X    int			f;
X    int			ac;
X    char		*lp;
X    char		*q;
X    struct st_work	*w;
X    char		line[512];
X    char		c;
X    char		*malloc(), *realloc();
X
X    while (gets(line) != NULL) {
X	if (nwork >= MAXWORK) {
X	    fprintf(stderr, stderr, "Too many jobs specified, .. increase MAXWORK\n");
X	    exit(4);
X	}
X	w = &work[nwork];
X	lp = line;
X	i = 1;
X	while (*lp && *lp != ' ') {
X	    i++;
X	    lp++;
X	}
X	w->cmd = (char *)malloc(i);
X	strncpy(w->cmd, line, i-1);
X	w->cmd[i-1] = '\0';
X	w->inpsize = 0;
X	w->input = "";
X	/* start to build arg list */
X	ac = 2;
X	w->av = (char **)malloc(2*sizeof(char *));
X	q = w->cmd;
X	while (*q) q++;
X	q--;
X	while (q >= w->cmd) {
X	    if (*q == '/') {
X		q++;
X		break;
X	    }
X	    q--;
X	}
X	w->av[0] = q;
X	while (*lp) {
X	    if (*lp == ' ') {
X		/* space */
X		lp++;
X		continue;
X	    }
X	    else if (*lp == '<') {
X		/* standard input for this job */
X		q = ++lp;
X		while (*lp && *lp != ' ') lp++;
X		c = *lp;
X		*lp = '\0';
X		if ((f = open(q, 0)) == -1) {
X		    fprintf(stderr, "cannot open input file (%s) for job %d\n",
X				q, nwork);
X		    exit(4);
X		}
X		/* gobble input */
X		w->input = (char *)malloc(512);
X		while ((i = read(f, &w->input[w->inpsize], 512)) > 0) {
X		    w->inpsize += i;
X		    w->input = (char *)realloc(w->input, w->inpsize+512);
X		}
X		w->input = (char *)realloc(w->input, w->inpsize);
X		close(f);
X		/* extract stdout file name from line beginning "C=" */
X		w->outf = "";
X		for (q = w->input; q < &w->input[w->inpsize-10]; q++) {
X		    if (*q == '\n' && strncmp(&q[1], "C=", 2) == 0) {
X			w->outf = &q[3];
X			break;
X		    }
X		}
X#if debug
X		if (*w->outf) {
X		    fprintf(stderr, "stdout->");
X		    for (q=w->outf; *q != '\n'; q++)
X			fputc(*q, stderr);
X		    fputc('\n', stderr);
X		}
X#endif
X	    }
X	    else {
X		/* a command option */
X		ac++;
X		w->av = (char **)realloc(w->av, ac*sizeof(char *));
X		q = lp;
X		i = 1;
X		while (*lp && *lp != ' ') {
X		    lp++;
X		    i++;
X		}
X		w->av[ac-2] = (char *)malloc(i);
X		strncpy(w->av[ac-2], q, i-1);
X		w->av[ac-2][i-1] = '\0';
X	    }
X	}
X	w->av[ac-1] = (char *)0;
X	nwork++;
X    }
X}
X
X#if debug
Xdumpwork()
X{
X    int		i;
X    int		j;
X
X    for (i = 0; i < nwork; i++) {
X	fprintf(stderr, "job %d: cmd: %s\n", i, work[i].cmd);
X	j = 0;
X	while (work[i].av[j]) {
X		fprintf(stderr, "argv[%d]: %s\n", j, work[i].av[j]);
X		j++;
X	}
X	fprintf(stderr, "input: %d chars text: ", work[i].inpsize);
X	if (work[i].input == (char *)0)
X		fprintf(stderr, "<NULL>\n");
X	else {
X	        register char	*pend;
X	        char		*p;
X		char		c;
X		p = work[i].input;
X		while (*p) {
X			pend = p;
X			while (*pend && *pend != '\n')
X				pend++;
X			c = *pend;
X			*pend = '\0';
X			fprintf(stderr, "%s\n", p);
X			*pend = c;
X			p = &pend[1];
X		}
X	}
X    }
X}
X#endif
X
Xfatal(s)
Xchar *s;
X{
X    int	i;
X    fprintf(stderr, s);
X    fflush(stderr);
X    perror("Reason?");
X    fflush(stderr);
X    for (i = 0; i < nusers; i++) {
X	if (child[i].pid > 0 && kill(child[i].pid, SIGKILL) != -1) {
X	    fprintf(stderr, "pid %d killed off\n", child[i].pid);
X	    fflush(stderr);
X	}
X    }
X    exit_status = 4;
X    return;
X}
END_OF_FILE
if test 13750 -ne `wc -c <'big.c'`; then
    echo shar: \"'big.c'\" unpacked with wrong size!
fi
chmod +x 'big.c'
# end of 'big.c'
fi
if test -f 'dbmserv.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'dbmserv.c'\"
else
echo shar: Extracting \"'dbmserv.c'\" \(15460 characters\)
sed "s/^X//" >'dbmserv.c' <<'END_OF_FILE'
X/*******************************************************************************
X *  The BYTE UNIX Benchmarks - Release 2
X *          Module: dbmserv.c   SID: 2.4 4/17/90 16:45:37
X *          
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Ben Smith or Rick Grehan at BYTE Magazine
X *	bensmith@bixpb.UUCP    rick_g@bixpb.UUCP
X *
X *******************************************************************************
X *  Modification Log:
X *  change output to stdout 4/13/89 ben
X *  errors to stderr 5/24/89 ben
X *  added activity counters and ifdef time output
X *  7/6/89 - Removed global semaphore use.  Callers pid now goes
X *   into type field of message.  Semaphore now only controls
X *   append operation and indicates presence of server.  RG
X * 7/11/89 - Semaphores are back.  One controls extending the file,
X *   the other controls the number of people simultaneously allowed
X *   on the request queue.  This latter semaphore must be tuned for
X *   a particular system to keep it from deadlocking.
X******************************************************************************/
X/*
X * Multi-user DBMS simulation.
X * Server
X * Database has the form:
X * IIIINNNNNNNNNNNNNNNNNNNNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPPPPPPPPPP
X * Where IIII is the 4-digit id number (0-padded)
X *       NN... is the 20-character name
X *       AA... is the 40-character address
X *       PP... is the 10-character phone number
X * Records are accessed by ID number (1 is the 0th record, 2 is 1st..etc.)
X */
Xchar id[] = "@(#) @(#)dbmserv.c:1.5 -- 7/10/89 18:54:58";
X
X#include <stdio.h>
X#include <setjmp.h>
X#include <ctype.h>
X#include <errno.h>
X#include <sys/types.h>
X#include <sys/signal.h>
X#include <sys/ipc.h>
X#include <sys/msg.h>
X#include <sys/param.h>
X#include <sys/sem.h>
X
X#ifdef VERBOSE
X#define DEBUG  /* remove after debugging */
X#endif
X
X#define ERROR (-1)
X#define QERR 1
X
X#define SEEK_SET 0
X#define SEEK_END 2
X
X#define RMODE 0644
X#define WMODE 0666
X
X/*
X** Record definitions.
X*/
X
X#define IDLEN 4
X#define NAMELEN 20
X#define ADDRLEN 40
X#define PHONLEN 10
X#define RECLEN  74	/* Sum of the above. */
X
X/*
X** Queue and semaphore names. Queues are neamed from client's
X**  point of view
X*/
X#define	RQUEUE	"WRIT"		/* Read queue */
X#define WQUEUE	"READ"		/* Write queue */
X#define SEMA	"SEMA"		/* Semaphore */
X/*
X** Message types.
X*/
X#define READREQ	1		/* Read a record */
X#define WRITEREQ 2		/* Write a record */
X#define ADDREQ 3		/* Add a new record */
X#define GETLREQ 4		/* Get largest record number */
X#define RESULTREQ 10		/* Record contains results figures */
X				/* Results are stored as:
X				*  nnnnnnnnnnmmmmmmmmmm
X				*   n = total time
X				*   m = number of errors
X				*/
X#define DIEREQ 99		/* Orders server to terminate. */
X
X/*
X** Return codes.
X*/
X#define AOK 1			/* Request met ok */
X#define DERR_RNF 2		/* Record not found */
X#define DERR_RAE 3		/* Record already exists */
X#define DERR_WRD 4		/* Unexplainable error */
X#define DERR_UNK 9		/* Unknown request type */
X/*
X** Structures.
X*/
X
Xtypedef struct {
X	int request;		/* Request type and response code */
X	char recdat[RECLEN];	/* DBMS record data */
X} msgdata;
X
Xtypedef struct {
X	long type;		/* Holds caller's pid */
X	msgdata data;		/* Pointer to request and data */
X} amess;
X
X
Xstruct ticker { unsigned long real,
X		       system,
X		       cpu; };
X/*
X** Structure instances.
X*/
Xamess hisreq;
Xamess myresp;
X
X/* Semaphore operations for initially unlocking the queue and
X** append semaphores.  */
X
Xstruct sembuf unlock0;
Xstruct sembuf unlock1 = {1 , 1, SEM_UNDO};
X
XFILE *dbfp;			/* Pointer for database file */
Xint readq;			/* ID of read queue */
Xint writeq;			/* ID of write queue */
Xint qsize;			/* Size of read/write queues */
Xint qsema;			/* ID of queue semaphore */
Xjmp_buf myjump;			/* Jump buffer for signals */
X#ifdef VERBOSE
Xstruct ticker total_time = {0L, 0L, 0L}; /* Total time */
X#endif
Xunsigned long rd_cnt, 		/* read request counter */
X	      wr_cnt,		/* write request counter */
X	      ad_cnt,		/* add request counter */
X	      gt_cnt,		/* get request counter */
X	      rs_cnt;		/* result request counter */
Xunsigned long errcnt;		/* Total error count */
Xunsigned long total_tasks;	/* Total number of tasks logged in */
X
Xextern int errno;
X
X/*
X** Function defs.
X*/
Xint capt_sig();
Xkey_t makey();
X
X
X/******************************************************************
X		 #    #    ##       #    #    #
X		 ##  ##   #  #      #    ##   #
X		 # ## #  #    #     #    # #  #
X		 #    #  ######     #    #  # #
X		 #    #  #    #     #    #   ##
X		 #    #  #    #     #    #    #
X********************************************************************/
X
Xmain(argc,argv,envp)
Xint argc;
Xchar *argv[];
Xchar **envp;
X
X{
X
X	/*
X	** User must specify database name and queue size.
X	*/
X	if(argc<3)
X	{
X		fprintf(stderr,"usage: %s dbasefile queuesize\n",argv[0]);
X		exit(1);
X	}
X
X	/*
X	** The file must already exist.
X	*/
X	if((dbfp=fopen(argv[1],"r+"))==(FILE *)NULL)
X	{
X		fprintf(stderr,"**Open error on: %s\n",argv[1]);
X		exit(1);
X	}
X
X	/*
X	** Get queuesize value.
X	*/
X	if((qsize=atoi(argv[2]))<=0)
X	{
X		fprintf(stderr,"**Illegal queue size\n");
X		exit(1);
X	}
X	unlock0.sem_num = 0;		/* Build unlock structure */
X	unlock0.sem_op = (short)qsize;
X	unlock0.sem_flg = (short)SEM_UNDO;
X
X	/*
X	** Set up the message queues.
X	*/
X	if((readq=msgget(makey(RQUEUE),IPC_CREAT|IPC_EXCL|RMODE))==
X		  ERROR)
X	{
X		qerror("**Cannot create read queue");
X		cleanup();
X		exit(1);
X	}
X
X	if((writeq=msgget(makey(WQUEUE),IPC_CREAT|IPC_EXCL|WMODE))==
X		   ERROR)
X	{
X		qerror("**Cannot create write queue");
X		cleanup();
X		exit(1);
X	}
X
X	/*
X	** Initialize stuff.
X	*/
X	errcnt=0L;
X	total_tasks=0L;
X	rd_cnt=0L;
X	wr_cnt=0L;
X	ad_cnt=0L;
X	gt_cnt=0L;
X	rs_cnt=0L;
X
X	/*
X	** Set up the semaphores and unlock them (2 semaphores in the set).
X	*/
X	if((qsema=semget(makey(SEMA),2,IPC_CREAT|IPC_EXCL|WMODE))==
X		ERROR)
X	{
X		serror("**Cannot create semaphore");
X		cleanup();
X		exit(1);
X	}
X	if((semop(qsema,&unlock1,1)==ERROR) ||
X		(semop(qsema,&unlock0,1)==ERROR))
X	{
X		serror("Unlocking semaphore");
X		cleanup();
X		exit(1);
X	}
X
X	/*
X	** Service requests from the outside world.
X	*/
X	if(servicer())
X	{
X		fprintf(stderr,"**Server error**\n");
X		fprintf(stderr,"**Errcode: %d\n",errno);
X		fprintf(stderr,"**REQ: %ld\n",hisreq.type);
X	}
X
X	/*
X	** Output results.
X	*/
X	   {
X#ifdef VERBOSE
X	   fprintf(stdout,"Time: cpu %ld   system %ld  real %ld\n",
X		total_time.cpu, total_time.system, total_time.real);
X	   fprintf(stdout,"Error : %ld  Tasks logged: %ld\n",
X	      errcnt, total_tasks);
X	   fprintf(stdout,
X" %ld read; %ld write; %ld add; %ld get-last; %ld result: %ld errors\n",
X		rd_cnt, wr_cnt, ad_cnt, gt_cnt, rs_cnt, errcnt);
X#endif
X	   }
X
X	/*
X	** Close everything down.
X	*/
X	cleanup();
X	exit(0);			/* Good night, ladies. */
X}
X
X/**************************** servicer *********************
X** servicer()
X** This routine handles all the requests coming in on the
X** read message queue.  Responses are sent along the write
X** message queque.
X*************************************************************/
Xservicer()
X{
X#ifdef VERBOSE
X	unsigned long cpu_time,system_time,real_time;
X#endif
X	unsigned long aderr;
X	char idnum[IDLEN+1];
X	char buff[RECLEN+1];
X	int rcod;
X
X	/*
X	** First set all the signals to capture.
X	*/
X	setsignals();
X
X	/*
X	** Build a longjump.
X	*/
X	if(setjmp(myjump)!=0)
X		return(0);
X
X	/*
X	** Now loop and process messages.
X	*/
X	while(1)
X	{
X		if(msgrcv(readq,&hisreq,RECLEN,0,MSG_NOERROR)<0)
X			return(QERR);	/* Error return */
X#ifdef DEBUG
Xprintf("receiving %d requwest\n",hisreq.data.request);
X#endif
X
X
X		switch(hisreq.data.request)
X		{
X
X		/*
X		** Read operation.
X		*/
X		case READREQ:
X			++rd_cnt;
X			strncpy(idnum,hisreq.data.recdat,4);
X			rcod=doread(idnum,buff);
X			if(rcod==AOK)
X			  strncpy(myresp.data.recdat,buff,RECLEN);
X			myresp.data.request=rcod;
X			myresp.type=hisreq.type;
X			if(msgsnd(writeq,&myresp,RECLEN,0)<0)
X				return(QERR);
X#ifdef DEBUG
Xprintf("returning response\n");
X#endif
X
X			break;
X
X		/*
X		** Write operation.
X		*/
X		case WRITEREQ:
X			++wr_cnt;
X			myresp.data.request=(long)dowrite(hisreq.data.recdat);
X			myresp.type=hisreq.type;
X			if(msgsnd(writeq,&myresp,RECLEN,0)<0)
X				return(QERR);
X			break;
X
X		/*
X		** Add operation.
X		*/
X		case ADDREQ:
X			++ad_cnt;
X			myresp.data.request=(long)doadd(hisreq.data.recdat);
X			myresp.type=hisreq.type;
X			if(msgsnd(writeq,&myresp,RECLEN,0)<0)
X				return(QERR);
X			break;
X
X		/*
X		** Get-last-record-in-file operation.
X		*/
X		case GETLREQ:
X			++gt_cnt;
X			myresp.data.request=(long)dotell(myresp.data.recdat);
X			myresp.type=hisreq.type;
X			if(msgsnd(writeq,&myresp,RECLEN,0)<0)
X				return(QERR);
X			break;
X
X		/*
X		** Record task's results operation.
X		** Note that this operation requires no
X		** response.
X		*/
X		case RESULTREQ:
X			++rs_cnt;
X#ifdef VERBOSE
X			sscanf(hisreq.data.recdat,"%ld %ld %ld %d",
X				&cpu_time,&system_time,&real_time,&aderr);
X			total_time.cpu+=cpu_time;
X			total_time.system+=system_time;
X			total_time.real+=real_time;
X#else
X			sscanf(hisreq.data.recdat,"%d", &aderr);
X#endif
X			errcnt+=aderr;
X			total_tasks++;
X			break;
X
X		/*
X		** We have been asked to leave.
X		*/
X		case DIEREQ:
X			return(0);
X
X		/*
X		** Eh?
X		*/
X		default:
X			myresp.data.request=DERR_UNK;
X			myresp.type=hisreq.type;
X			if(msgsnd(writeq,&myresp,RECLEN,0)<0)
X				return(QERR);
X			break;
X		}
X	}
X}
X
X/**************************** doread *********************
X** Perform a read request.
X*************************************************************/
Xdoread(idnum,buff)
Xchar idnum[IDLEN+1];
Xchar buff[RECLEN];
X{
X	long offset;
X
X	/*
X	** Calculate offset.
X	*/
X	idnum[IDLEN]='\0';
X	offset=(atol(idnum)-1)*(long)RECLEN;
X	if(offset<0L) return(DERR_RNF);		/* Illegal offset */
X
X	if( (fseek(dbfp,offset,SEEK_SET)!=0) ||
X	    (fread(buff,RECLEN,1,dbfp)!=1) )
X	return(DERR_RNF);			/* Seek or read failed */
X
X	return(AOK);				/* No problems */
X}
X
X/**************************** dowrite *********************
X** Perform a write request.
X*************************************************************/
Xdowrite(buff)
Xchar buff[RECLEN];
X{
X	char idnum[IDLEN+1];
X	long offset;
X
X	strncpy(idnum,buff,4);			/* Get id number */
X
X	/*
X	** Calculate offset.
X	*/
X	idnum[IDLEN]='\0';
X	offset=(atol(idnum)-1)*(long)RECLEN;
X	if(offset<0L) return(DERR_RNF);		/* Illegal offset */
X
X	if((fseek(dbfp,offset,SEEK_SET)!=0) ||
X		(fwrite(buff,RECLEN,1,dbfp)!=1))
X   	return(DERR_RNF);
X
X	return(AOK);
X}
X
X/**************************** doadd *********************
X** Perform an add request.
X*************************************************************/
Xdoadd(buff)
Xchar buff[RECLEN];
X{
X
X	long offset;
X	
X	/* Seek to the end of the file. */
X	if(fseek(dbfp,0L,SEEK_END)!=0)
X		return(DERR_WRD);		/* Huh? */
X
X	/* Get offset -- we presume caller has proper id set */
X	offset=ftell(dbfp);
X	if (fwrite(buff,RECLEN,1,dbfp)!=1)
X		return(DERR_RNF);		/* Failed write */
X	
X	return(AOK);
X}
X
X/**************************** dotell *********************
X** Perform a "tell" request.
X** Returns highest current id number in file.
X*************************************************************/
Xdotell(buff)
Xchar buff[RECLEN];
X{
X	
X	long offset;
X	
X	/* Seek to the end of the file. */
X	if(fseek(dbfp,0L,SEEK_END)!=0)
X		return(DERR_WRD);		/* Huh? */
X
X	/* Get offset and calculate new id number */
X	offset=ftell(dbfp);
X	sprintf(buff,"%#04ld",(offset/(long)RECLEN));
X	
X	return(AOK);
X}
X
X/**************************** setsignals *********************
X** Set up all the signals we want to capture or ignore.
X*************************************************************/
Xsetsignals()
X{
Xstatic int diehard();
X
X	/*
X	** Ignore hangup and interrupt.
X	*/
X	signal(SIGHUP, diehard);
X	signal(SIGINT, diehard);
X
X	/*
X	** Capture the rest.
X	*/
X	signal(SIGQUIT, capt_sig);
X	signal(SIGILL, capt_sig);
X	signal(SIGTRAP, capt_sig);
X	signal(SIGIOT, capt_sig);
X	signal(SIGEMT, capt_sig);
X	signal(SIGFPE, capt_sig);
X	signal(SIGBUS, capt_sig);
X	signal(SIGSEGV, capt_sig);
X	signal(SIGSYS, capt_sig);
X	signal(SIGPIPE, capt_sig);
X	signal(SIGALRM, capt_sig);
X	signal(SIGTERM, capt_sig);
X	signal(SIGUSR1, capt_sig);
X	signal(SIGUSR2, capt_sig);
X
X	return(0);
X}
X
X/********************** capt_sig ****************************
X** Capture signals.
X** This just performs the long jump and blasts us out.
X*************************************************************/
Xstatic int capt_sig(sign)
Xint sign;
X{
X	longjmp(myjump,sign);
X}
X
X/*************************** diehard ************************
Xfor kills and other such interrupts: cleanup and exit
X*************************************************************/
Xstatic int diehard()
X{
Xcleanup();
Xexit(1);
X}
X
X/*************************** cleanup *************************
X** Clean up after yourself.
X** Close open files, release queues and semaphore.
X**************************************************************/
Xcleanup()
X{
X	fclose(dbfp);			/* Close the database file. */
X	msgctl(readq,IPC_RMID);		/* Close read queue. */
X	msgctl(writeq,IPC_RMID);	/* Close write queue. */
X	semctl(qsema,0,IPC_RMID);	/* Release semaphore. */
X	return(0);
X}
X
X/******************* makey **************************************
X** Following routine converts an ASCII string to a key_t value.
X** This routine originally appeared in ADVANCED PROGRAMMERS GUIDE
X** TO UNIX SYSTEM V by R. Thomas, PHD; L. R. Rogers, and J. L. Yates.
X** Osborne McGraw Hill.
X******************************************************************/
Xkey_t makey(p)
Xchar *p;
X{
X	key_t keyv;
X	int i;
X
X	if(isnumber(p))
X		keyv = (key_t)atol(p);
X	else
X	{
X		keyv=(key_t)0;
X		for(i=0;i<sizeof(key_t) && p[i];i++)
X			keyv=(keyv << 8) | p[i];
X	}
X	return(keyv);
X}
X
X/********************** isnumber ***************************/
Xint isnumber(p)
Xchar *p;
X{
X	for( ; *p && isdigit(*p); p++) ;
X	return(*p ? 0 : 1);
X}
X
X/******************************** qerror **********************
X ** prints out the errormessage associate with a queue
X ***************************************************************/
Xqerror(s)
Xchar *s; /* message prefix string */
X{
Xextern int errno;
X
Xfprintf(stderr,"QUEUE ERROR: %s:\n     ",s);
Xswitch(errno)
X   {
X   case EACCES: fprintf(stderr,
X       "message queue exists, but locked out (EACCES)\n");
X       break;
X   case ENOENT: fprintf(stderr,
X       "message queue does not exist (ENOENT)\n");
X       break;
X   case ENOSPC: fprintf(stderr,
X       "too manny message queus (ENOSPC)\n");
X       break;
X   case EEXIST: fprintf(stderr,
X       "message queue exists, but locked out (EEXIST)\n");
X       break;
X   default: fprintf(stderr,
X       "unknown error (%n)",errno);
X       break;
X   }
Xreturn(0);
X}
X
X/************ serror **********************
X ** prints out the errormessage associate with a semaphore
X ***************************************************************/
Xserror(s)
Xchar *s; /* message prefix string */
X{
Xextern int errno;
X
Xfprintf(stderr,"SEMAPHORE ERROR: %s:\n     ",s);
Xswitch(errno)
X   {
X   case EINVAL: fprintf(stderr,
X       "invalid number of semaphore sets (EINVAL)\n");
X       break;
X   case EACCES: fprintf(stderr,
X       "semaphore exists, but invalid operation (EACCES)\n");
X       break;
X   case ENOENT: fprintf(stderr,
X       "semaphore does not exist (ENOENT)\n");
X       break;
X   case ENOSPC: fprintf(stderr,
X       "too many semaphores (ENOSPC)\n");
X       break;
X   case EEXIST: fprintf(stderr,
X       "semaphore exists, but locked out (EEXIST)\n");
X       break;
X   default: fprintf(stderr,
X       "unknown error (%n)",errno);
X       break;
X   }
X}
X
X
END_OF_FILE
if test 15460 -ne `wc -c <'dbmserv.c'`; then
    echo shar: \"'dbmserv.c'\" unpacked with wrong size!
fi
chmod +x 'dbmserv.c'
# end of 'dbmserv.c'
fi
echo shar: End of archive 3 \(of 5\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 5 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 5 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
exit 0 # Just in case...
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.