home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume22
/
byte-benchmarks
/
part03
/
dbmserv.c
< prev
Wrap
C/C++ Source or Header
|
1990-06-07
|
15KB
|
666 lines
/*******************************************************************************
* 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;
}
}