home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / pc / source / byteunix.lzh / byte.3 / dbmserv.c < prev   
C/C++ Source or Header  |  1990-05-11  |  15KB  |  666 lines

  1. /*******************************************************************************
  2.  *  The BYTE UNIX Benchmarks - Release 2
  3.  *          Module: dbmserv.c   SID: 2.4 4/17/90 16:45:37
  4.  *          
  5.  *******************************************************************************
  6.  * Bug reports, patches, comments, suggestions should be sent to:
  7.  *
  8.  *    Ben Smith or Rick Grehan at BYTE Magazine
  9.  *    bensmith@bixpb.UUCP    rick_g@bixpb.UUCP
  10.  *
  11.  *******************************************************************************
  12.  *  Modification Log:
  13.  *  change output to stdout 4/13/89 ben
  14.  *  errors to stderr 5/24/89 ben
  15.  *  added activity counters and ifdef time output
  16.  *  7/6/89 - Removed global semaphore use.  Callers pid now goes
  17.  *   into type field of message.  Semaphore now only controls
  18.  *   append operation and indicates presence of server.  RG
  19.  * 7/11/89 - Semaphores are back.  One controls extending the file,
  20.  *   the other controls the number of people simultaneously allowed
  21.  *   on the request queue.  This latter semaphore must be tuned for
  22.  *   a particular system to keep it from deadlocking.
  23. ******************************************************************************/
  24. /*
  25.  * Multi-user DBMS simulation.
  26.  * Server
  27.  * Database has the form:
  28.  * IIIINNNNNNNNNNNNNNNNNNNNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPPPPPPPPPP
  29.  * Where IIII is the 4-digit id number (0-padded)
  30.  *       NN... is the 20-character name
  31.  *       AA... is the 40-character address
  32.  *       PP... is the 10-character phone number
  33.  * Records are accessed by ID number (1 is the 0th record, 2 is 1st..etc.)
  34.  */
  35. char id[] = "@(#) @(#)dbmserv.c:1.5 -- 7/10/89 18:54:58";
  36.  
  37. #include <stdio.h>
  38. #include <setjmp.h>
  39. #include <ctype.h>
  40. #include <errno.h>
  41. #include <sys/types.h>
  42. #include <sys/signal.h>
  43. #include <sys/ipc.h>
  44. #include <sys/msg.h>
  45. #include <sys/param.h>
  46. #include <sys/sem.h>
  47.  
  48. #ifdef VERBOSE
  49. #define DEBUG  /* remove after debugging */
  50. #endif
  51.  
  52. #define ERROR (-1)
  53. #define QERR 1
  54.  
  55. #define SEEK_SET 0
  56. #define SEEK_END 2
  57.  
  58. #define RMODE 0644
  59. #define WMODE 0666
  60.  
  61. /*
  62. ** Record definitions.
  63. */
  64.  
  65. #define IDLEN 4
  66. #define NAMELEN 20
  67. #define ADDRLEN 40
  68. #define PHONLEN 10
  69. #define RECLEN  74    /* Sum of the above. */
  70.  
  71. /*
  72. ** Queue and semaphore names. Queues are neamed from client's
  73. **  point of view
  74. */
  75. #define    RQUEUE    "WRIT"        /* Read queue */
  76. #define WQUEUE    "READ"        /* Write queue */
  77. #define SEMA    "SEMA"        /* Semaphore */
  78. /*
  79. ** Message types.
  80. */
  81. #define READREQ    1        /* Read a record */
  82. #define WRITEREQ 2        /* Write a record */
  83. #define ADDREQ 3        /* Add a new record */
  84. #define GETLREQ 4        /* Get largest record number */
  85. #define RESULTREQ 10        /* Record contains results figures */
  86.                 /* Results are stored as:
  87.                 *  nnnnnnnnnnmmmmmmmmmm
  88.                 *   n = total time
  89.                 *   m = number of errors
  90.                 */
  91. #define DIEREQ 99        /* Orders server to terminate. */
  92.  
  93. /*
  94. ** Return codes.
  95. */
  96. #define AOK 1            /* Request met ok */
  97. #define DERR_RNF 2        /* Record not found */
  98. #define DERR_RAE 3        /* Record already exists */
  99. #define DERR_WRD 4        /* Unexplainable error */
  100. #define DERR_UNK 9        /* Unknown request type */
  101. /*
  102. ** Structures.
  103. */
  104.  
  105. typedef struct {
  106.     int request;        /* Request type and response code */
  107.     char recdat[RECLEN];    /* DBMS record data */
  108. } msgdata;
  109.  
  110. typedef struct {
  111.     long type;        /* Holds caller's pid */
  112.     msgdata data;        /* Pointer to request and data */
  113. } amess;
  114.  
  115.  
  116. struct ticker { unsigned long real,
  117.                system,
  118.                cpu; };
  119. /*
  120. ** Structure instances.
  121. */
  122. amess hisreq;
  123. amess myresp;
  124.  
  125. /* Semaphore operations for initially unlocking the queue and
  126. ** append semaphores.  */
  127.  
  128. struct sembuf unlock0;
  129. struct sembuf unlock1 = {1 , 1, SEM_UNDO};
  130.  
  131. FILE *dbfp;            /* Pointer for database file */
  132. int readq;            /* ID of read queue */
  133. int writeq;            /* ID of write queue */
  134. int qsize;            /* Size of read/write queues */
  135. int qsema;            /* ID of queue semaphore */
  136. jmp_buf myjump;            /* Jump buffer for signals */
  137. #ifdef VERBOSE
  138. struct ticker total_time = {0L, 0L, 0L}; /* Total time */
  139. #endif
  140. unsigned long rd_cnt,         /* read request counter */
  141.           wr_cnt,        /* write request counter */
  142.           ad_cnt,        /* add request counter */
  143.           gt_cnt,        /* get request counter */
  144.           rs_cnt;        /* result request counter */
  145. unsigned long errcnt;        /* Total error count */
  146. unsigned long total_tasks;    /* Total number of tasks logged in */
  147.  
  148. extern int errno;
  149.  
  150. /*
  151. ** Function defs.
  152. */
  153. int capt_sig();
  154. key_t makey();
  155.  
  156.  
  157. /******************************************************************
  158.          #    #    ##       #    #    #
  159.          ##  ##   #  #      #    ##   #
  160.          # ## #  #    #     #    # #  #
  161.          #    #  ######     #    #  # #
  162.          #    #  #    #     #    #   ##
  163.          #    #  #    #     #    #    #
  164. ********************************************************************/
  165.  
  166. main(argc,argv,envp)
  167. int argc;
  168. char *argv[];
  169. char **envp;
  170.  
  171. {
  172.  
  173.     /*
  174.     ** User must specify database name and queue size.
  175.     */
  176.     if(argc<3)
  177.     {
  178.         fprintf(stderr,"usage: %s dbasefile queuesize\n",argv[0]);
  179.         exit(1);
  180.     }
  181.  
  182.     /*
  183.     ** The file must already exist.
  184.     */
  185.     if((dbfp=fopen(argv[1],"r+"))==(FILE *)NULL)
  186.     {
  187.         fprintf(stderr,"**Open error on: %s\n",argv[1]);
  188.         exit(1);
  189.     }
  190.  
  191.     /*
  192.     ** Get queuesize value.
  193.     */
  194.     if((qsize=atoi(argv[2]))<=0)
  195.     {
  196.         fprintf(stderr,"**Illegal queue size\n");
  197.         exit(1);
  198.     }
  199.     unlock0.sem_num = 0;        /* Build unlock structure */
  200.     unlock0.sem_op = (short)qsize;
  201.     unlock0.sem_flg = (short)SEM_UNDO;
  202.  
  203.     /*
  204.     ** Set up the message queues.
  205.     */
  206.     if((readq=msgget(makey(RQUEUE),IPC_CREAT|IPC_EXCL|RMODE))==
  207.           ERROR)
  208.     {
  209.         qerror("**Cannot create read queue");
  210.         cleanup();
  211.         exit(1);
  212.     }
  213.  
  214.     if((writeq=msgget(makey(WQUEUE),IPC_CREAT|IPC_EXCL|WMODE))==
  215.            ERROR)
  216.     {
  217.         qerror("**Cannot create write queue");
  218.         cleanup();
  219.         exit(1);
  220.     }
  221.  
  222.     /*
  223.     ** Initialize stuff.
  224.     */
  225.     errcnt=0L;
  226.     total_tasks=0L;
  227.     rd_cnt=0L;
  228.     wr_cnt=0L;
  229.     ad_cnt=0L;
  230.     gt_cnt=0L;
  231.     rs_cnt=0L;
  232.  
  233.     /*
  234.     ** Set up the semaphores and unlock them (2 semaphores in the set).
  235.     */
  236.     if((qsema=semget(makey(SEMA),2,IPC_CREAT|IPC_EXCL|WMODE))==
  237.         ERROR)
  238.     {
  239.         serror("**Cannot create semaphore");
  240.         cleanup();
  241.         exit(1);
  242.     }
  243.     if((semop(qsema,&unlock1,1)==ERROR) ||
  244.         (semop(qsema,&unlock0,1)==ERROR))
  245.     {
  246.         serror("Unlocking semaphore");
  247.         cleanup();
  248.         exit(1);
  249.     }
  250.  
  251.     /*
  252.     ** Service requests from the outside world.
  253.     */
  254.     if(servicer())
  255.     {
  256.         fprintf(stderr,"**Server error**\n");
  257.         fprintf(stderr,"**Errcode: %d\n",errno);
  258.         fprintf(stderr,"**REQ: %ld\n",hisreq.type);
  259.     }
  260.  
  261.     /*
  262.     ** Output results.
  263.     */
  264.        {
  265. #ifdef VERBOSE
  266.        fprintf(stdout,"Time: cpu %ld   system %ld  real %ld\n",
  267.         total_time.cpu, total_time.system, total_time.real);
  268.        fprintf(stdout,"Error : %ld  Tasks logged: %ld\n",
  269.           errcnt, total_tasks);
  270.        fprintf(stdout,
  271. " %ld read; %ld write; %ld add; %ld get-last; %ld result: %ld errors\n",
  272.         rd_cnt, wr_cnt, ad_cnt, gt_cnt, rs_cnt, errcnt);
  273. #endif
  274.        }
  275.  
  276.     /*
  277.     ** Close everything down.
  278.     */
  279.     cleanup();
  280.     exit(0);            /* Good night, ladies. */
  281. }
  282.  
  283. /**************************** servicer *********************
  284. ** servicer()
  285. ** This routine handles all the requests coming in on the
  286. ** read message queue.  Responses are sent along the write
  287. ** message queque.
  288. *************************************************************/
  289. servicer()
  290. {
  291. #ifdef VERBOSE
  292.     unsigned long cpu_time,system_time,real_time;
  293. #endif
  294.     unsigned long aderr;
  295.     char idnum[IDLEN+1];
  296.     char buff[RECLEN+1];
  297.     int rcod;
  298.  
  299.     /*
  300.     ** First set all the signals to capture.
  301.     */
  302.     setsignals();
  303.  
  304.     /*
  305.     ** Build a longjump.
  306.     */
  307.     if(setjmp(myjump)!=0)
  308.         return(0);
  309.  
  310.     /*
  311.     ** Now loop and process messages.
  312.     */
  313.     while(1)
  314.     {
  315.         if(msgrcv(readq,&hisreq,RECLEN,0,MSG_NOERROR)<0)
  316.             return(QERR);    /* Error return */
  317. #ifdef DEBUG
  318. printf("receiving %d requwest\n",hisreq.data.request);
  319. #endif
  320.  
  321.  
  322.         switch(hisreq.data.request)
  323.         {
  324.  
  325.         /*
  326.         ** Read operation.
  327.         */
  328.         case READREQ:
  329.             ++rd_cnt;
  330.             strncpy(idnum,hisreq.data.recdat,4);
  331.             rcod=doread(idnum,buff);
  332.             if(rcod==AOK)
  333.               strncpy(myresp.data.recdat,buff,RECLEN);
  334.             myresp.data.request=rcod;
  335.             myresp.type=hisreq.type;
  336.             if(msgsnd(writeq,&myresp,RECLEN,0)<0)
  337.                 return(QERR);
  338. #ifdef DEBUG
  339. printf("returning response\n");
  340. #endif
  341.  
  342.             break;
  343.  
  344.         /*
  345.         ** Write operation.
  346.         */
  347.         case WRITEREQ:
  348.             ++wr_cnt;
  349.             myresp.data.request=(long)dowrite(hisreq.data.recdat);
  350.             myresp.type=hisreq.type;
  351.             if(msgsnd(writeq,&myresp,RECLEN,0)<0)
  352.                 return(QERR);
  353.             break;
  354.  
  355.         /*
  356.         ** Add operation.
  357.         */
  358.         case ADDREQ:
  359.             ++ad_cnt;
  360.             myresp.data.request=(long)doadd(hisreq.data.recdat);
  361.             myresp.type=hisreq.type;
  362.             if(msgsnd(writeq,&myresp,RECLEN,0)<0)
  363.                 return(QERR);
  364.             break;
  365.  
  366.         /*
  367.         ** Get-last-record-in-file operation.
  368.         */
  369.         case GETLREQ:
  370.             ++gt_cnt;
  371.             myresp.data.request=(long)dotell(myresp.data.recdat);
  372.             myresp.type=hisreq.type;
  373.             if(msgsnd(writeq,&myresp,RECLEN,0)<0)
  374.                 return(QERR);
  375.             break;
  376.  
  377.         /*
  378.         ** Record task's results operation.
  379.         ** Note that this operation requires no
  380.         ** response.
  381.         */
  382.         case RESULTREQ:
  383.             ++rs_cnt;
  384. #ifdef VERBOSE
  385.             sscanf(hisreq.data.recdat,"%ld %ld %ld %d",
  386.                 &cpu_time,&system_time,&real_time,&aderr);
  387.             total_time.cpu+=cpu_time;
  388.             total_time.system+=system_time;
  389.             total_time.real+=real_time;
  390. #else
  391.             sscanf(hisreq.data.recdat,"%d", &aderr);
  392. #endif
  393.             errcnt+=aderr;
  394.             total_tasks++;
  395.             break;
  396.  
  397.         /*
  398.         ** We have been asked to leave.
  399.         */
  400.         case DIEREQ:
  401.             return(0);
  402.  
  403.         /*
  404.         ** Eh?
  405.         */
  406.         default:
  407.             myresp.data.request=DERR_UNK;
  408.             myresp.type=hisreq.type;
  409.             if(msgsnd(writeq,&myresp,RECLEN,0)<0)
  410.                 return(QERR);
  411.             break;
  412.         }
  413.     }
  414. }
  415.  
  416. /**************************** doread *********************
  417. ** Perform a read request.
  418. *************************************************************/
  419. doread(idnum,buff)
  420. char idnum[IDLEN+1];
  421. char buff[RECLEN];
  422. {
  423.     long offset;
  424.  
  425.     /*
  426.     ** Calculate offset.
  427.     */
  428.     idnum[IDLEN]='\0';
  429.     offset=(atol(idnum)-1)*(long)RECLEN;
  430.     if(offset<0L) return(DERR_RNF);        /* Illegal offset */
  431.  
  432.     if( (fseek(dbfp,offset,SEEK_SET)!=0) ||
  433.         (fread(buff,RECLEN,1,dbfp)!=1) )
  434.     return(DERR_RNF);            /* Seek or read failed */
  435.  
  436.     return(AOK);                /* No problems */
  437. }
  438.  
  439. /**************************** dowrite *********************
  440. ** Perform a write request.
  441. *************************************************************/
  442. dowrite(buff)
  443. char buff[RECLEN];
  444. {
  445.     char idnum[IDLEN+1];
  446.     long offset;
  447.  
  448.     strncpy(idnum,buff,4);            /* Get id number */
  449.  
  450.     /*
  451.     ** Calculate offset.
  452.     */
  453.     idnum[IDLEN]='\0';
  454.     offset=(atol(idnum)-1)*(long)RECLEN;
  455.     if(offset<0L) return(DERR_RNF);        /* Illegal offset */
  456.  
  457.     if((fseek(dbfp,offset,SEEK_SET)!=0) ||
  458.         (fwrite(buff,RECLEN,1,dbfp)!=1))
  459.        return(DERR_RNF);
  460.  
  461.     return(AOK);
  462. }
  463.  
  464. /**************************** doadd *********************
  465. ** Perform an add request.
  466. *************************************************************/
  467. doadd(buff)
  468. char buff[RECLEN];
  469. {
  470.  
  471.     long offset;
  472.     
  473.     /* Seek to the end of the file. */
  474.     if(fseek(dbfp,0L,SEEK_END)!=0)
  475.         return(DERR_WRD);        /* Huh? */
  476.  
  477.     /* Get offset -- we presume caller has proper id set */
  478.     offset=ftell(dbfp);
  479.     if (fwrite(buff,RECLEN,1,dbfp)!=1)
  480.         return(DERR_RNF);        /* Failed write */
  481.     
  482.     return(AOK);
  483. }
  484.  
  485. /**************************** dotell *********************
  486. ** Perform a "tell" request.
  487. ** Returns highest current id number in file.
  488. *************************************************************/
  489. dotell(buff)
  490. char buff[RECLEN];
  491. {
  492.     
  493.     long offset;
  494.     
  495.     /* Seek to the end of the file. */
  496.     if(fseek(dbfp,0L,SEEK_END)!=0)
  497.         return(DERR_WRD);        /* Huh? */
  498.  
  499.     /* Get offset and calculate new id number */
  500.     offset=ftell(dbfp);
  501.     sprintf(buff,"%#04ld",(offset/(long)RECLEN));
  502.     
  503.     return(AOK);
  504. }
  505.  
  506. /**************************** setsignals *********************
  507. ** Set up all the signals we want to capture or ignore.
  508. *************************************************************/
  509. setsignals()
  510. {
  511. static int diehard();
  512.  
  513.     /*
  514.     ** Ignore hangup and interrupt.
  515.     */
  516.     signal(SIGHUP, diehard);
  517.     signal(SIGINT, diehard);
  518.  
  519.     /*
  520.     ** Capture the rest.
  521.     */
  522.     signal(SIGQUIT, capt_sig);
  523.     signal(SIGILL, capt_sig);
  524.     signal(SIGTRAP, capt_sig);
  525.     signal(SIGIOT, capt_sig);
  526.     signal(SIGEMT, capt_sig);
  527.     signal(SIGFPE, capt_sig);
  528.     signal(SIGBUS, capt_sig);
  529.     signal(SIGSEGV, capt_sig);
  530.     signal(SIGSYS, capt_sig);
  531.     signal(SIGPIPE, capt_sig);
  532.     signal(SIGALRM, capt_sig);
  533.     signal(SIGTERM, capt_sig);
  534.     signal(SIGUSR1, capt_sig);
  535.     signal(SIGUSR2, capt_sig);
  536.  
  537.     return(0);
  538. }
  539.  
  540. /********************** capt_sig ****************************
  541. ** Capture signals.
  542. ** This just performs the long jump and blasts us out.
  543. *************************************************************/
  544. static int capt_sig(sign)
  545. int sign;
  546. {
  547.     longjmp(myjump,sign);
  548. }
  549.  
  550. /*************************** diehard ************************
  551. for kills and other such interrupts: cleanup and exit
  552. *************************************************************/
  553. static int diehard()
  554. {
  555. cleanup();
  556. exit(1);
  557. }
  558.  
  559. /*************************** cleanup *************************
  560. ** Clean up after yourself.
  561. ** Close open files, release queues and semaphore.
  562. **************************************************************/
  563. cleanup()
  564. {
  565.     fclose(dbfp);            /* Close the database file. */
  566.     msgctl(readq,IPC_RMID);        /* Close read queue. */
  567.     msgctl(writeq,IPC_RMID);    /* Close write queue. */
  568.     semctl(qsema,0,IPC_RMID);    /* Release semaphore. */
  569.     return(0);
  570. }
  571.  
  572. /******************* makey **************************************
  573. ** Following routine converts an ASCII string to a key_t value.
  574. ** This routine originally appeared in ADVANCED PROGRAMMERS GUIDE
  575. ** TO UNIX SYSTEM V by R. Thomas, PHD; L. R. Rogers, and J. L. Yates.
  576. ** Osborne McGraw Hill.
  577. ******************************************************************/
  578. key_t makey(p)
  579. char *p;
  580. {
  581.     key_t keyv;
  582.     int i;
  583.  
  584.     if(isnumber(p))
  585.         keyv = (key_t)atol(p);
  586.     else
  587.     {
  588.         keyv=(key_t)0;
  589.         for(i=0;i<sizeof(key_t) && p[i];i++)
  590.             keyv=(keyv << 8) | p[i];
  591.     }
  592.     return(keyv);
  593. }
  594.  
  595. /********************** isnumber ***************************/
  596. int isnumber(p)
  597. char *p;
  598. {
  599.     for( ; *p && isdigit(*p); p++) ;
  600.     return(*p ? 0 : 1);
  601. }
  602.  
  603. /******************************** qerror **********************
  604.  ** prints out the errormessage associate with a queue
  605.  ***************************************************************/
  606. qerror(s)
  607. char *s; /* message prefix string */
  608. {
  609. extern int errno;
  610.  
  611. fprintf(stderr,"QUEUE ERROR: %s:\n     ",s);
  612. switch(errno)
  613.    {
  614.    case EACCES: fprintf(stderr,
  615.        "message queue exists, but locked out (EACCES)\n");
  616.        break;
  617.    case ENOENT: fprintf(stderr,
  618.        "message queue does not exist (ENOENT)\n");
  619.        break;
  620.    case ENOSPC: fprintf(stderr,
  621.        "too manny message queus (ENOSPC)\n");
  622.        break;
  623.    case EEXIST: fprintf(stderr,
  624.        "message queue exists, but locked out (EEXIST)\n");
  625.        break;
  626.    default: fprintf(stderr,
  627.        "unknown error (%n)",errno);
  628.        break;
  629.    }
  630. return(0);
  631. }
  632.  
  633. /************ serror **********************
  634.  ** prints out the errormessage associate with a semaphore
  635.  ***************************************************************/
  636. serror(s)
  637. char *s; /* message prefix string */
  638. {
  639. extern int errno;
  640.  
  641. fprintf(stderr,"SEMAPHORE ERROR: %s:\n     ",s);
  642. switch(errno)
  643.    {
  644.    case EINVAL: fprintf(stderr,
  645.        "invalid number of semaphore sets (EINVAL)\n");
  646.        break;
  647.    case EACCES: fprintf(stderr,
  648.        "semaphore exists, but invalid operation (EACCES)\n");
  649.        break;
  650.    case ENOENT: fprintf(stderr,
  651.        "semaphore does not exist (ENOENT)\n");
  652.        break;
  653.    case ENOSPC: fprintf(stderr,
  654.        "too many semaphores (ENOSPC)\n");
  655.        break;
  656.    case EEXIST: fprintf(stderr,
  657.        "semaphore exists, but locked out (EEXIST)\n");
  658.        break;
  659.    default: fprintf(stderr,
  660.        "unknown error (%n)",errno);
  661.        break;
  662.    }
  663. }
  664.  
  665.  
  666.