home *** CD-ROM | disk | FTP | other *** search
-
- /*******************************************************************************
- * The BYTE UNIX Benchmarks - Release 2
- * Module: dbmscli.c SID: 2.4 4/17/90 16:45:36
- *
- *******************************************************************************
- * Bug reports, patches, comments, suggestions should be sent to:
- *
- * Ben Smith or Rick Grehan at BYTE Magazine
- * bensmith@bixpb.UUCP rick_g@bixpb.UUCP
- *
- *****************************************************************************
- * Modification Log:
- *
- * 7/6/89 - Reworked messaging system so the semaphores were no
- * longer used in all cases. Semaphores now only control the append
- * operation. Messages contain caller's pid in type field.
- * The semaphore also serves to indicate the presence of the server. RG
- *
- * 7/6/89 - added some debugging output to VERBOSE
- * 7/9/89 - ifdef code that starts server from client - problems. BSS
- * 7/11/89 - Added semaphore to set. One semaphore controls the queue,
- * the other controls the append operation. RG
- * New command line format: dbmserv <filename> <queue size>
- *****************************************************************************/
- /*
- * Multi-user DBMS simulation.
- * Clients
- * Database has the form:
- * IIIINNNNNNNNNNNNNNNNNNNNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPPPPPPPPPP
- * Where IIII is the 4-digit id number (0-padded)
- * NN... is the 20-character name
- * AA... is the 40-character address
- * PP... is the 10-character phone number
- * Records are accessed by ID number (1 is the 0th record, 2 is 1st..etc.)
- *
- * This is the client system, which you execute with:
- * dbmscli n t i
- * where:
- * n = the number of tasks to fork
- * t = is the sleep time in seconds each task waits between requests
- * i = is the number of test iterations
- *
- * NOTE: This version assumes Unix V compatibility.
- */
- char id[] = "@(#) @(#)dbmscli.c:1.5 -- 7/10/89 18:54:57";
-
- #include <stdio.h>
- #include <ctype.h>
- #include <errno.h>
- #include <sys/types.h>
- #include <sys/signal.h>
- #include <sys/ipc.h>
- #include <sys/sem.h>
- #include <sys/msg.h>
- #include <sys/param.h>
-
- #ifdef VERBOSE
- #include <sys/times.h>
- #define DEBUG /* remove this after debugging */
- #endif
-
- #define ERROR (-1)
- /*
- ** Record definitions.
- */
-
- #define IDLEN 4
- #define NAMELEN 20
- #define ADDRLEN 40
- #define PHONLEN 10
- #define RECLEN 74 /* Sum of the above. */
-
- /*
- ** Queue and semaphore names.
- */
- #define RQUEUE "READ" /* Read queue */
- #define WQUEUE "WRIT" /* Write queue */
- #define SEMA "SEMA" /* Semaphore name */
-
- /*
- ** Message types.
- */
- #define READREQ 1 /* Read a record */
- #define WRITEREQ 2 /* Write a record */
- #define ADDREQ 3 /* Add a new record */
- #define GETLREQ 4 /* Get largest record number */
- #define RESULTREQ 10 /* Record contains results figures */
- /* Results are stored as:
- * nnnnnnnnnnbmmmmmmmmmmb
- * n = total time
- * m = number of errors
- * b = blank
- */
- #define DIEREQ 99 /* Orders server to terminate. */
-
-
- /*
- ** Return codes from the server.
- */
- #define AOK 1 /* Request met ok */
- #define DERR_RNF 2 /* Record not found */
- #define DERR_RAE 3 /* Record already exists */
- #define DERR_WRD 4 /* Unexplainable error */
- #define DERR_UNK 9 /* Unknown request type */
-
- /*
- ** Error codes.
- */
- #define QERR 1 /* Queue error */
- #define SEMERR 2 /* Semaphore error */
-
- /*
- ** Structures.
- */
-
- typedef struct {
- char id[IDLEN];
- char name[NAMELEN];
- char address[ADDRLEN];
- char phone[PHONLEN];
- } dbrec;
-
- typedef struct {
- int request; /* Request type and response code */
- char recdat[RECLEN]; /* DBMS record data */
- } msgdata;
-
- typedef struct {
- long type; /* Hold's caller's pid */
- msgdata data; /* Pointer to request and data */
- } amess;
-
- struct ticker { unsigned long real,
- system,
- cpu; };
-
- /******************************************************************
- #### # #### ##### ## # ####
- # # # # # # # # # # #
- # # # # ##### # # # ####
- # ### # # # # # ###### # #
- # # # # # # # # # # # #
- #### ###### #### ##### # # ###### ####
- *******************************************************************/
- /*
- ** Structure instances.
- */
- dbrec myrec; /* Data record */
- amess myreq; /* Client request */
- amess hisresp; /* Response from server */
-
- struct sembuf unlockq = { 0 , 1 , SEM_UNDO };
- struct sembuf lockq = { 0 , -1 , SEM_UNDO };
- struct sembuf unlocka = { 0, 1, SEM_UNDO };
- struct sembuf locka = { 0, -1, SEM_UNDO };
-
- #ifdef VERBOSE
- struct tms tbuff; /* For times system call */
- struct ticker stopwatch = {0L, 0L, 0L};
- struct ticker tottime = {0L, 0L, 0L};
- #endif
-
- int ntasks = 1; /* Number of tasks */
- int sleeptime = 0; /* Time to sleep */
- int iters = 1000; /* iterations */
- int readq; /* ID of read queue */
- int writeq; /* ID of write queue */
- int qsema; /* ID of semaphore for append */
- unsigned long errcnt; /* Error count */
- int waitval; /* Status return location for wait() */
- int rid; /* ID chosen at random */
- int need_server; /* flag for server */
- key_t sema_key;
-
- /*
- ** Externs and function defs.
- */
- long randnum(), randwc();
- key_t makey();
-
- /**************************************************************************
- # # ## # # #
- ## ## # # # ## #
- # ## # # # # # # #
- # # ###### # # # #
- # # # # # # ##
- # # # # # # #
- ****************************************************************************/
- main(argc,argv,envp)
- int argc;
- char *argv[];
- char **envp;
- {
-
- int i,j,cerr; /* Loop variables */
-
- /*
- * Make sure we have proper input.
- */
- if(argc<2)
- {
- fprintf(stderr,
- "usage: %s datafile [ntasks] [sleeptime] [iter]\n",argv[0]);
- exit(1);
- }
-
- /************* process command line parameters ************/
- /*
- * Get number of tasks and sleeptime.
- */
- if(argc==5)
- {
- if((iters=atoi(argv[4]))<=0)
-
- {
- fprintf(stderr,"**Illegal iter\n");
- exit(1);
- }
- }
-
- if(argc >=4)
- {
- if((sleeptime=atoi(argv[3]))<0)
- {
- fprintf(stderr,"**Illegal sleep time\n");
- exit(1);
- }
- }
-
- if(argc >=3)
- {
- if((ntasks=atoi(argv[2]))<=0)
- {
- fprintf(stderr,"**Illegal ntasks\n");
- exit(1);
- }
- }
-
- /* argv[1] is the data file name */
-
- /*
- * Make sure the server is active.
- */
-
- sema_key=makey(SEMA);
- /* test to see if the server is already running */
- if((qsema=semget(sema_key,2,0))==ERROR)
- {
- int bad_news();
-
- /* fork off for an exec to the server */
- if(!(j=fork())) /* this is the child aspiring to be */
- { /* a server */
- #ifdef DEBUG
- printf("I am the child (%d) aspiring to be a server\nn",
- getpid());
- #endif
- argv[0]="dbmserv";
- execvp(argv[0],argv);
- /* if it gets this far, exec must have failed */
- perror("exec of dbmserv failed");
- exit(1);
- }
- /* better not do anything until the server
- * is running. Just wait for the server,
- * in this case a child process, gives us
- * signal to proceed.
- */
- signal(SIGALRM,bad_news);
- alarm(30); /* give the server 30 seconds to get it going */
- while ((qsema=semget(sema_key,1,0))==ERROR)
- {
- #ifdef DEBUG
- printf("waiting for server\n");
- #endif
- sleep(1);
- }
-
- }
-
- /* get the message queue ids */
- if((readq=msgget(makey(RQUEUE),0))==ERROR)
- {
- qerror("Client getting read queue id");
- exit(1);
- }
- #ifdef DEBUG
- else
- printf("read queue id is %d\n",readq);
- #endif
- if((writeq=msgget(makey(WQUEUE),0))==ERROR)
- {
- qerror("Client getting write queue id");
- exit(1);
- }
- #ifdef DEBUG
- else
- printf("write queue id is %d\n",readq);
- #endif
- /*
- * Now fork off a bunch of processes, giving each
- * one a different starting seed.
- * (if fork() returns zero, this is already the child.
- */
- i=ntasks;
- do {
- j=fork();
- } while((j!=0)&&(--i!=0));
-
- /*
- * Am I a child or a father?
- */
- if( j==0)
- {
- #ifdef DEBUG
- printf("I am child client %d\n",getpid());
- #endif
- /* ***Child process*** */
- if((cerr=dotests(i,iters))!=0)
- {
- fprintf(stderr,"**Child error:\n");
- switch(cerr)
- {
- case(QERR):
- qerror(" Error is");
- break;
- case(SEMERR):
- serror(" Error is");
- break;
- default:
- perror(" Error is");
- break;
- }
- exit(1); /* If any error - just die. */
- }
- else
- {
- clearrec(myreq.data.recdat);
- #ifdef VERBOSE
- /*
- ** Log net elapsed time.
- */
- sprintf(myreq.data.recdat,"%#010ld %#010ld %#010ld %#010d ",
- tottime.cpu,tottime.system,tottime.real,errcnt);
- #else
- sprintf(myreq.data.recdat,"%#010d ", errcnt);
- #endif
- myreq.type=RESULTREQ;
- if(semop(qsema,&lockq,1) == ERROR) exit(0);
- if(msgsnd(writeq,&myreq,RECLEN,0)<0)
- exit(0);
- if(semop(qsema,&unlockq,1) == ERROR) exit(0);
-
- exit(0);
- }
- }
- else
- {
- #ifdef DEBUG
- printf("I am parent client %d\n",getpid());
- #endif
- /* ***Father process*** */
- /*
- ** Wait for the children to complete.
- */
- i=ntasks;
- while(i--) wait(&waitval);
-
- /*
- ** Now tell the server to go away.
- ** Note that if we have trouble (for some reason)
- ** getting the semaphore...we just barrel on through.
- */
- myreq.data.request=DIEREQ;
- myreq.type=1L; /* Pid not needed */
- if(msgsnd(writeq,&myreq,RECLEN,0)<0)
- qerror("**Message queue error during termination\n");
- exit(0);
- }
- }
-
- /********************************* dotests *************************
- ** Execute tests.
- ** Input number acts as seed for random number generator.
- ** Returns -1 if failure.
- ********************************************************************/
- int
- dotests(sseed,iter)
- int sseed;
- int iter; /* Number of iterations */
- {
-
- int i,j; /* Loop variables */
- int maxid; /* Max ID currently in database */
- long mypid; /* Get my process id */
-
- /*
- ** Initialize the random number seed.
- */
- randnum((unsigned long)sseed);
-
- /*
- ** Initialize error count and timer stuff.
- */
- errcnt=0;
-
- /*
- ** I need to know my process id.
- */
- mypid = (long)getpid();
-
- /*
- ** Find out what the maximum id in the database
- ** is.
- */
- myreq.data.request=GETLREQ;
- myreq.type=mypid;
- #ifdef DEBUG
- printf("About to send 1st transmission\n");
- #endif
- if(semop(qsema,&lockq,1)==ERROR) return(SEMERR);
- if(msgsnd(writeq,&myreq,RECLEN,0)<0) return(QERR);
- if(msgrcv(readq,&hisresp,RECLEN,mypid,MSG_NOERROR)<0) return(QERR);
- if(semop(qsema,&unlockq,1)==ERROR) return(SEMERR);
- #ifdef DEBUG
- printf("maxid string = %s\n",hisresp.data.recdat);
- #endif
- maxid=atoi(hisresp.data.recdat);
- #ifdef DEBUG
- printf("maxid = %d\n",maxid);
- #endif
-
- /*
- ** Now loop through the tests iter times.
- */
- for(i=0;i<iter;i++)
- {
- #ifdef DEBUG
- printf("In outer loop of client\n");
- #endif
- /* Do 4 reads */
- for(j=0;j<4;j++)
- {
- #ifdef DEBUG
- printf("In inner loop of client\n");
- #endif
- rid=(int)randwc(maxid)+1;
- clearrec(myreq.data.recdat);
- sprintf(myreq.data.recdat,"%#04d",rid);
- myreq.data.request=READREQ;
-
- #ifdef VERBOSE
- /* Turn on timer */
- stopwatch.real = times(&tbuff);
- stopwatch.system = tbuff.tms_stime;
- stopwatch.cpu = tbuff.tms_utime;
- #endif
- #ifdef DEBUG
- printf("About to read\n");
- #endif
-
- if(semop(qsema,&lockq,1)==ERROR) return(SEMERR);
- if(msgsnd(writeq,&myreq,RECLEN,0)<0)
- return(QERR);
- if(msgrcv(readq,&hisresp,RECLEN,mypid,MSG_NOERROR)<0)
- return(QERR);
- if(semop(qsema,&unlockq,1)==ERROR) return(SEMERR);
-
-
- #ifdef VERBOSE
- /* Turn off timer */
- tottime.real += times(&tbuff)-stopwatch.real;
- tottime.system += tbuff.tms_stime-stopwatch.system;
- tottime.cpu += tbuff.tms_utime-stopwatch.cpu;
-
- #endif
- /* Did we get what we should? */
- errcnt+=verify();
- }
-
- /* Do 1 write */
- rid=(int)randwc(maxid)+1;
- clearrec(myreq.data.recdat);
- sprintf(myreq.data.recdat,"%#04d",rid);
- loadrec((rid-1)%10);
- strncpy(myreq.data.recdat+4,myrec.name,RECLEN-4);
- myreq.data.request=WRITEREQ;
-
- #ifdef VERBOSE
- /* Turn on timer */
- stopwatch.real = times(&tbuff);
- stopwatch.system = tbuff.tms_stime;
- stopwatch.cpu = tbuff.tms_utime;
- #endif
-
- if(semop(qsema,&lockq,1)==ERROR) return(SEMERR);
- if(msgsnd(writeq,&myreq,RECLEN,0)<0)
- return(QERR);
- if(msgrcv(readq,&hisresp,RECLEN,mypid,MSG_NOERROR)<0)
- return(QERR);
- if(semop(qsema,&unlockq,1)==ERROR) return(SEMERR);
- #ifdef DEBUG
- printf("Message recieved\n");
- #endif
-
- #ifdef VERBOSE
- /* Turn off timer */
- tottime.real += times(&tbuff)-stopwatch.real;
- tottime.system += tbuff.tms_stime-stopwatch.system;
- tottime.cpu += tbuff.tms_utime-stopwatch.cpu;
- #endif
-
- if(hisresp.data.request!=(long)AOK) errcnt++;
- /* Sleep a little */
- if(sleeptime) sleep(sleeptime);
-
- /* Do an append every 10 times through the loop */
- if((i%10)==0)
- {
- myreq.data.request=GETLREQ;
-
- #ifdef VERBOSE
- /* Turn on timer */
- stopwatch.real = times(&tbuff);
- stopwatch.system = tbuff.tms_stime;
- stopwatch.cpu = tbuff.tms_utime;
- #endif
- if(semop(qsema,&locka,1)==ERROR) return(SEMERR);
- if(semop(qsema,&lockq,1)==ERROR) return(SEMERR);
- if(msgsnd(writeq,&myreq,RECLEN,0)<0) return(QERR);
- if(msgrcv(readq,&hisresp,RECLEN,mypid,MSG_NOERROR)<0)
- return(QERR);
- if(semop(qsema,&unlockq,1)==ERROR) return(SEMERR);
-
- #ifdef VERBOSE
- /* Turn off timer */
- tottime.real += times(&tbuff)-stopwatch.real;
- tottime.system += tbuff.tms_stime-stopwatch.system;
- tottime.cpu += tbuff.tms_utime-stopwatch.cpu;
- #endif
-
- maxid=atoi(hisresp.data.recdat);
- rid=(maxid+=1);
- clearrec(myreq.data.recdat);
- sprintf(myreq.data.recdat,"%#04d",rid);
- loadrec((rid-1)%10);
- strncpy(myreq.data.recdat+4,myrec.name,RECLEN-4);
- myreq.data.request=ADDREQ;
-
- #ifdef VERBOSE
- /* Turn on timer */
- stopwatch.real = times(&tbuff);
- stopwatch.system = tbuff.tms_stime;
- stopwatch.cpu = tbuff.tms_utime;
- #endif
-
- if(semop(qsema,&lockq,1)==ERROR) return(SEMERR);
- if(msgsnd(writeq,&myreq,RECLEN,0)<0) return(QERR);
- if(msgrcv(readq,&hisresp,RECLEN,mypid,MSG_NOERROR)<0)
- return(QERR);
- if(semop(qsema,&unlockq,1)==ERROR) return(SEMERR);
- if(semop(qsema,&unlocka,1)==ERROR) return(SEMERR);
-
- #ifdef VERBOSE
- /* Turn off timer */
- tottime.real += times(&tbuff)-stopwatch.real;
- tottime.system += tbuff.tms_stime-stopwatch.system;
- tottime.cpu += tbuff.tms_utime-stopwatch.cpu;
- #endif
-
- if(hisresp.data.request!=(long)AOK) errcnt++;
-
- /* Sleep a little */
- if(sleeptime) sleep(sleeptime);
-
- }
- }
-
- /*
- ** All's well that ends well.
- */
- return(0);
- }
-
- /*
- ** verify
- */
- int verify()
- {
- char buffr[80];
-
- /* Is there response an error response? */
- if(hisresp.data.request!=(long)AOK)
- return(1);
-
- /* Was it the number we were looking for? */
- strncpy(buffr,hisresp.data.recdat,4);
- buffr[4]='\0';
- if(atoi(buffr)!=rid)
- return(1);
-
- /* Check the record number with its contents */
- loadrec((rid-1)%10);
-
- if(strncmp(hisresp.data.recdat+4,myrec.name,RECLEN-4)!=0)
- return(1);
-
- /* Looks good */
- return(0);
- }
-
-
- /*
- ** Clear a record
- */
- clearrec(rptr)
- char *rptr;
- {
- int i;
-
- for(i=0;i<RECLEN;++i)
- *rptr++='\0';
-
- return;
- }
-
- /*
- ** Load the record up with random data.
- */
- loadrec(sel)
- int sel; /* Select which fake record */
-
- {
-
- char *nname;
- char *naddr;
- char *nphon;
-
- switch(sel)
- {
- case 0: nname="Tom Thompson ";
- naddr="9401 Billy Willy Road ";
- nphon="3334442222";
- break;
- case 1: nname="Steve Apiki ";
- naddr="50 Hawaii Way c/o Jack Lord ";
- nphon="1234443333";
- break;
- case 2: nname="Stan Diehl ";
- naddr="27 Hoptoad Hollow Way ";
- nphon="3332221111";
- break;
- case 3: nname="Judy Grehan ";
- naddr="Suite 3, WallState Building ";
- nphon="9995556666";
- break;
- case 4: nname="Aaron Richards ";
- naddr="Highway 40 OverPass, Second Pylon ";
- nphon="8883339999";
- break;
- case 5: nname="Benjamin Davidson ";
- naddr="Under The Bridge, HeavyWater City ";
- nphon="7773229988";
- break;
- case 6: nname="Dayle Woolston ";
- naddr="4040 Pleasant Way, WICAT Central ";
- nphon="2228332299";
- break;
- case 7: nname="Jim Carls ";
- naddr="Big Oak Tree Behind Barsodie's ";
- nphon="32244566657";
- break;
- case 8: nname="Steve Smith ";
- naddr="7000 Aloth Cove ";
- nphon="2118332929";
- break;
- case 9:
- default: nname="Blind Willy Chitlins";
- naddr="Unavailable Until Further Notice ";
- nphon="3456789012";
- break;
- }
-
- /*
- ** Fill the structure with fake data.
- */
- strncpy(myrec.name,nname,NAMELEN);
- strncpy(myrec.address,naddr,ADDRLEN);
- strncpy(myrec.phone,nphon,PHONLEN);
-
- return;
- }
-
- /*
- ** randwc(num)
- ** Returns random modulo num.
- */
- long randwc(num)
- long num;
- {
- return(randnum(0L)%num);
- }
-
- /*
- ** randnum(val)
- ** Second order linear congruential generator.
- ** Constants suggested by J. G. Skellam.
- ** If val==0, returns next member of sequence.
- ** val!=0, "seeds" generator with val.
- */
- long randnum(lngval)
- unsigned long lngval;
- {
- register unsigned long interm;
- static unsigned long randw[2] = { 13 , 117 };
-
- if (lngval!=0L) randw[1]=lngval;
-
- interm=(randw[0]*254754L+randw[1]*529562L)%999563L;
- randw[1]=randw[0];
- randw[0]=interm;
- return(interm);
- }
-
- /************************** makey ******************************
- ** Following routine converts an ASCII string to a key_t value.
- ** This routine originally appeared in ADVANCED PROGRAMMERS GUIDE
- ** TO UNIX SYSTEM V by R. Thomas, PHD; L. R. Rogers, and J. L. Yates.
- ** Osborne McGraw Hill.
- *******************************************************************/
- key_t
- makey(p)
- char *p;
- {
- key_t keyv;
- int i;
-
- if(isnumber(p))
- keyv = (key_t)atol(p);
- else
- {
- keyv=(key_t)0;
- for(i=0;i<sizeof(key_t) && p[i];i++)
- keyv=(keyv << 8) | p[i];
- }
- return(keyv);
- }
-
- /***************************** isnumber *************************/
- int isnumber(p)
- char *p;
- {
- for( ; *p && isdigit(*p); p++) ;
- return(*p ? 0 : 1);
- }
-
- /**************************** badnews **************************/
- int bad_news() /* things are screwed up */
- {
- fprintf(stderr,"TIMED OUT\n");
- exit(1);
- }
-
- /******************************** qerror **********************
- ** prints out the errormessage associate with a queue
- ***************************************************************/
- qerror(s)
- char *s; /* message prefix string */
- {
- extern int errno;
-
- fprintf(stderr,"QUEUE ERROR: %s:\n ",s);
- switch(errno)
- {
- case EACCES: fprintf(stderr,
- "message queue exists, but locked out (EACCES)\n");
- break;
- case ENOENT: fprintf(stderr,
- "message queue does not exist (ENOENT)\n");
- break;
- case ENOSPC: fprintf(stderr,
- "too manny message queus (ENOSPC)\n");
- break;
- case EEXIST: fprintf(stderr,
- "message queue exists, but locked out (EEXIST)\n");
- break;
- default: fprintf(stderr,
- "unknown error (%n)",errno);
- break;
- }
- return(0);
- }
-
- /******************************** serror **********************
- ** prints out the errormessage associate with a semaphore
- ***************************************************************/
- serror(s)
- char *s; /* message prefix string */
- {
- extern int errno;
-
- fprintf(stderr,"SEMAPHORE ERROR: %s:\n ",s);
- switch(errno)
- {
- case EINVAL: fprintf(stderr,
- "invalid number of semaphore sets (EINVAL)\n");
- break;
- case EACCES: fprintf(stderr,
- "semaphore exists, but invalid operation (EACCES)\n");
- break;
- case ENOENT: fprintf(stderr,
- "semaphore does not exist (ENOENT)\n");
- break;
- case ENOSPC: fprintf(stderr,
- "too many semaphores (ENOSPC)\n");
- break;
- case EEXIST: fprintf(stderr,
- "semaphore exists, but locked out (EEXIST)\n");
- break;
- default: fprintf(stderr,
- "unknown error (%n)",errno);
- break;
- }
- return(0);
- }
-