home *** CD-ROM | disk | FTP | other *** search
- /*******************************************************************************
- * The BYTE UNIX Benchmarks - Release 2
- * Module: dbmserv.c SID: 2.4 4/17/90 16:45:37
- *
- *******************************************************************************
- * 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:
- * change output to stdout 4/13/89 ben
- * errors to stderr 5/24/89 ben
- * added activity counters and ifdef time output
- * 7/6/89 - Removed global semaphore use. Callers pid now goes
- * into type field of message. Semaphore now only controls
- * append operation and indicates presence of server. RG
- * 7/11/89 - Semaphores are back. One controls extending the file,
- * the other controls the number of people simultaneously allowed
- * on the request queue. This latter semaphore must be tuned for
- * a particular system to keep it from deadlocking.
- ******************************************************************************/
- /*
- * Multi-user DBMS simulation.
- * Server
- * 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.)
- */
- char id[] = "@(#) @(#)dbmserv.c:1.5 -- 7/10/89 18:54:58";
-
- #include <stdio.h>
- #include <setjmp.h>
- #include <ctype.h>
- #include <errno.h>
- #include <sys/types.h>
- #include <sys/signal.h>
- #include <sys/ipc.h>
- #include <sys/msg.h>
- #include <sys/param.h>
- #include <sys/sem.h>
-
- #ifdef VERBOSE
- #define DEBUG /* remove after debugging */
- #endif
-
- #define ERROR (-1)
- #define QERR 1
-
- #define SEEK_SET 0
- #define SEEK_END 2
-
- #define RMODE 0644
- #define WMODE 0666
-
- /*
- ** 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. Queues are neamed from client's
- ** point of view
- */
- #define RQUEUE "WRIT" /* Read queue */
- #define WQUEUE "READ" /* Write queue */
- #define SEMA "SEMA" /* Semaphore */
- /*
- ** 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:
- * nnnnnnnnnnmmmmmmmmmm
- * n = total time
- * m = number of errors
- */
- #define DIEREQ 99 /* Orders server to terminate. */
-
- /*
- ** Return codes.
- */
- #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 */
- /*
- ** Structures.
- */
-
- typedef struct {
- int request; /* Request type and response code */
- char recdat[RECLEN]; /* DBMS record data */
- } msgdata;
-
- typedef struct {
- long type; /* Holds caller's pid */
- msgdata data; /* Pointer to request and data */
- } amess;
-
-
- struct ticker { unsigned long real,
- system,
- cpu; };
- /*
- ** Structure instances.
- */
- amess hisreq;
- amess myresp;
-
- /* Semaphore operations for initially unlocking the queue and
- ** append semaphores. */
-
- struct sembuf unlock0;
- struct sembuf unlock1 = {1 , 1, SEM_UNDO};
-
- FILE *dbfp; /* Pointer for database file */
- int readq; /* ID of read queue */
- int writeq; /* ID of write queue */
- int qsize; /* Size of read/write queues */
- int qsema; /* ID of queue semaphore */
- jmp_buf myjump; /* Jump buffer for signals */
- #ifdef VERBOSE
- struct ticker total_time = {0L, 0L, 0L}; /* Total time */
- #endif
- unsigned long rd_cnt, /* read request counter */
- wr_cnt, /* write request counter */
- ad_cnt, /* add request counter */
- gt_cnt, /* get request counter */
- rs_cnt; /* result request counter */
- unsigned long errcnt; /* Total error count */
- unsigned long total_tasks; /* Total number of tasks logged in */
-
- extern int errno;
-
- /*
- ** Function defs.
- */
- int capt_sig();
- key_t makey();
-
-
- /******************************************************************
- # # ## # # #
- ## ## # # # ## #
- # ## # # # # # # #
- # # ###### # # # #
- # # # # # # ##
- # # # # # # #
- ********************************************************************/
-
- main(argc,argv,envp)
- int argc;
- char *argv[];
- char **envp;
-
- {
-
- /*
- ** User must specify database name and queue size.
- */
- if(argc<3)
- {
- fprintf(stderr,"usage: %s dbasefile queuesize\n",argv[0]);
- exit(1);
- }
-
- /*
- ** The file must already exist.
- */
- if((dbfp=fopen(argv[1],"r+"))==(FILE *)NULL)
- {
- fprintf(stderr,"**Open error on: %s\n",argv[1]);
- exit(1);
- }
-
- /*
- ** Get queuesize value.
- */
- if((qsize=atoi(argv[2]))<=0)
- {
- fprintf(stderr,"**Illegal queue size\n");
- exit(1);
- }
- unlock0.sem_num = 0; /* Build unlock structure */
- unlock0.sem_op = (short)qsize;
- unlock0.sem_flg = (short)SEM_UNDO;
-
- /*
- ** Set up the message queues.
- */
- if((readq=msgget(makey(RQUEUE),IPC_CREAT|IPC_EXCL|RMODE))==
- ERROR)
- {
- qerror("**Cannot create read queue");
- cleanup();
- exit(1);
- }
-
- if((writeq=msgget(makey(WQUEUE),IPC_CREAT|IPC_EXCL|WMODE))==
- ERROR)
- {
- qerror("**Cannot create write queue");
- cleanup();
- exit(1);
- }
-
- /*
- ** Initialize stuff.
- */
- errcnt=0L;
- total_tasks=0L;
- rd_cnt=0L;
- wr_cnt=0L;
- ad_cnt=0L;
- gt_cnt=0L;
- rs_cnt=0L;
-
- /*
- ** Set up the semaphores and unlock them (2 semaphores in the set).
- */
- if((qsema=semget(makey(SEMA),2,IPC_CREAT|IPC_EXCL|WMODE))==
- ERROR)
- {
- serror("**Cannot create semaphore");
- cleanup();
- exit(1);
- }
- if((semop(qsema,&unlock1,1)==ERROR) ||
- (semop(qsema,&unlock0,1)==ERROR))
- {
- serror("Unlocking semaphore");
- cleanup();
- exit(1);
- }
-
- /*
- ** Service requests from the outside world.
- */
- if(servicer())
- {
- fprintf(stderr,"**Server error**\n");
- fprintf(stderr,"**Errcode: %d\n",errno);
- fprintf(stderr,"**REQ: %ld\n",hisreq.type);
- }
-
- /*
- ** Output results.
- */
- {
- #ifdef VERBOSE
- fprintf(stdout,"Time: cpu %ld system %ld real %ld\n",
- total_time.cpu, total_time.system, total_time.real);
- fprintf(stdout,"Error : %ld Tasks logged: %ld\n",
- errcnt, total_tasks);
- fprintf(stdout,
- " %ld read; %ld write; %ld add; %ld get-last; %ld result: %ld errors\n",
- rd_cnt, wr_cnt, ad_cnt, gt_cnt, rs_cnt, errcnt);
- #endif
- }
-
- /*
- ** Close everything down.
- */
- cleanup();
- exit(0); /* Good night, ladies. */
- }
-
- /**************************** servicer *********************
- ** servicer()
- ** This routine handles all the requests coming in on the
- ** read message queue. Responses are sent along the write
- ** message queque.
- *************************************************************/
- servicer()
- {
- #ifdef VERBOSE
- unsigned long cpu_time,system_time,real_time;
- #endif
- unsigned long aderr;
- char idnum[IDLEN+1];
- char buff[RECLEN+1];
- int rcod;
-
- /*
- ** First set all the signals to capture.
- */
- setsignals();
-
- /*
- ** Build a longjump.
- */
- if(setjmp(myjump)!=0)
- return(0);
-
- /*
- ** Now loop and process messages.
- */
- while(1)
- {
- if(msgrcv(readq,&hisreq,RECLEN,0,MSG_NOERROR)<0)
- return(QERR); /* Error return */
- #ifdef DEBUG
- printf("receiving %d requwest\n",hisreq.data.request);
- #endif
-
-
- switch(hisreq.data.request)
- {
-
- /*
- ** Read operation.
- */
- case READREQ:
- ++rd_cnt;
- strncpy(idnum,hisreq.data.recdat,4);
- rcod=doread(idnum,buff);
- if(rcod==AOK)
- strncpy(myresp.data.recdat,buff,RECLEN);
- myresp.data.request=rcod;
- myresp.type=hisreq.type;
- if(msgsnd(writeq,&myresp,RECLEN,0)<0)
- return(QERR);
- #ifdef DEBUG
- printf("returning response\n");
- #endif
-
- break;
-
- /*
- ** Write operation.
- */
- case WRITEREQ:
- ++wr_cnt;
- myresp.data.request=(long)dowrite(hisreq.data.recdat);
- myresp.type=hisreq.type;
- if(msgsnd(writeq,&myresp,RECLEN,0)<0)
- return(QERR);
- break;
-
- /*
- ** Add operation.
- */
- case ADDREQ:
- ++ad_cnt;
- myresp.data.request=(long)doadd(hisreq.data.recdat);
- myresp.type=hisreq.type;
- if(msgsnd(writeq,&myresp,RECLEN,0)<0)
- return(QERR);
- break;
-
- /*
- ** Get-last-record-in-file operation.
- */
- case GETLREQ:
- ++gt_cnt;
- myresp.data.request=(long)dotell(myresp.data.recdat);
- myresp.type=hisreq.type;
- if(msgsnd(writeq,&myresp,RECLEN,0)<0)
- return(QERR);
- break;
-
- /*
- ** Record task's results operation.
- ** Note that this operation requires no
- ** response.
- */
- case RESULTREQ:
- ++rs_cnt;
- #ifdef VERBOSE
- sscanf(hisreq.data.recdat,"%ld %ld %ld %d",
- &cpu_time,&system_time,&real_time,&aderr);
- total_time.cpu+=cpu_time;
- total_time.system+=system_time;
- total_time.real+=real_time;
- #else
- sscanf(hisreq.data.recdat,"%d", &aderr);
- #endif
- errcnt+=aderr;
- total_tasks++;
- break;
-
- /*
- ** We have been asked to leave.
- */
- case DIEREQ:
- return(0);
-
- /*
- ** Eh?
- */
- default:
- myresp.data.request=DERR_UNK;
- myresp.type=hisreq.type;
- if(msgsnd(writeq,&myresp,RECLEN,0)<0)
- return(QERR);
- break;
- }
- }
- }
-
- /**************************** doread *********************
- ** Perform a read request.
- *************************************************************/
- doread(idnum,buff)
- char idnum[IDLEN+1];
- char buff[RECLEN];
- {
- long offset;
-
- /*
- ** Calculate offset.
- */
- idnum[IDLEN]='\0';
- offset=(atol(idnum)-1)*(long)RECLEN;
- if(offset<0L) return(DERR_RNF); /* Illegal offset */
-
- if( (fseek(dbfp,offset,SEEK_SET)!=0) ||
- (fread(buff,RECLEN,1,dbfp)!=1) )
- return(DERR_RNF); /* Seek or read failed */
-
- return(AOK); /* No problems */
- }
-
- /**************************** dowrite *********************
- ** Perform a write request.
- *************************************************************/
- dowrite(buff)
- char buff[RECLEN];
- {
- char idnum[IDLEN+1];
- long offset;
-
- strncpy(idnum,buff,4); /* Get id number */
-
- /*
- ** Calculate offset.
- */
- idnum[IDLEN]='\0';
- offset=(atol(idnum)-1)*(long)RECLEN;
- if(offset<0L) return(DERR_RNF); /* Illegal offset */
-
- if((fseek(dbfp,offset,SEEK_SET)!=0) ||
- (fwrite(buff,RECLEN,1,dbfp)!=1))
- return(DERR_RNF);
-
- return(AOK);
- }
-
- /**************************** doadd *********************
- ** Perform an add request.
- *************************************************************/
- doadd(buff)
- char buff[RECLEN];
- {
-
- long offset;
-
- /* Seek to the end of the file. */
- if(fseek(dbfp,0L,SEEK_END)!=0)
- return(DERR_WRD); /* Huh? */
-
- /* Get offset -- we presume caller has proper id set */
- offset=ftell(dbfp);
- if (fwrite(buff,RECLEN,1,dbfp)!=1)
- return(DERR_RNF); /* Failed write */
-
- return(AOK);
- }
-
- /**************************** dotell *********************
- ** Perform a "tell" request.
- ** Returns highest current id number in file.
- *************************************************************/
- dotell(buff)
- char buff[RECLEN];
- {
-
- long offset;
-
- /* Seek to the end of the file. */
- if(fseek(dbfp,0L,SEEK_END)!=0)
- return(DERR_WRD); /* Huh? */
-
- /* Get offset and calculate new id number */
- offset=ftell(dbfp);
- sprintf(buff,"%#04ld",(offset/(long)RECLEN));
-
- return(AOK);
- }
-
- /**************************** setsignals *********************
- ** Set up all the signals we want to capture or ignore.
- *************************************************************/
- setsignals()
- {
- static int diehard();
-
- /*
- ** Ignore hangup and interrupt.
- */
- signal(SIGHUP, diehard);
- signal(SIGINT, diehard);
-
- /*
- ** Capture the rest.
- */
- signal(SIGQUIT, capt_sig);
- signal(SIGILL, capt_sig);
- signal(SIGTRAP, capt_sig);
- signal(SIGIOT, capt_sig);
- signal(SIGEMT, capt_sig);
- signal(SIGFPE, capt_sig);
- signal(SIGBUS, capt_sig);
- signal(SIGSEGV, capt_sig);
- signal(SIGSYS, capt_sig);
- signal(SIGPIPE, capt_sig);
- signal(SIGALRM, capt_sig);
- signal(SIGTERM, capt_sig);
- signal(SIGUSR1, capt_sig);
- signal(SIGUSR2, capt_sig);
-
- return(0);
- }
-
- /********************** capt_sig ****************************
- ** Capture signals.
- ** This just performs the long jump and blasts us out.
- *************************************************************/
- static int capt_sig(sign)
- int sign;
- {
- longjmp(myjump,sign);
- }
-
- /*************************** diehard ************************
- for kills and other such interrupts: cleanup and exit
- *************************************************************/
- static int diehard()
- {
- cleanup();
- exit(1);
- }
-
- /*************************** cleanup *************************
- ** Clean up after yourself.
- ** Close open files, release queues and semaphore.
- **************************************************************/
- cleanup()
- {
- fclose(dbfp); /* Close the database file. */
- msgctl(readq,IPC_RMID); /* Close read queue. */
- msgctl(writeq,IPC_RMID); /* Close write queue. */
- semctl(qsema,0,IPC_RMID); /* Release semaphore. */
- return(0);
- }
-
- /******************* 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);
- }
-
- /******************************** 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;
- }
- }
-
-
-