home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / db02_src.zip / gib.cc < prev    next >
C/C++ Source or Header  |  1994-02-22  |  23KB  |  907 lines

  1. /*-------------------------------------------------------------------------
  2.  * Source Id :
  3.  *
  4.  * $Id: gib.cc,v 1.22 1993/11/05 13:46:57 kevinl Exp $
  5.  *-------------------------------------------------------------------------
  6.  * Project Notes :
  7.  *
  8.  *  Diamond Base
  9.  *  ============
  10.  *    A solid database implementation, spurred on by the continuing
  11.  *  Metal (Lead) Base saga.
  12.  *
  13.  *  Project Team :
  14.  *      A. Davison
  15.  *      K. Lentin
  16.  *      D. Platt
  17.  *
  18.  *    Project Commenced : 05-02-1993
  19.  *
  20.  *-------------------------------------------------------------------------
  21.  *  Module Notes :
  22.  *
  23.  *  multi-server module
  24.  *
  25.  *  Original Author : Darren Platt
  26.  *
  27.  *-------------------------------------------------------------------------
  28.  * Revision History:
  29.  *
  30.  * $Log: gib.cc,v $
  31. // Revision 1.22  1993/11/05  13:46:57    kevinl
  32. // Protocol and fixes
  33. //
  34. // Revision 1.21  1993/11/05  13:06:52    darrenp
  35. // Changed signal handler prototypes to differentiate linux from CC
  36. //
  37. // Revision 1.20  1993/11/05  12:45:05    kevinl
  38. // Fixed problems that CC picked up (and some multi bugs)
  39. //
  40. // Revision 1.19  1993/11/05  07:23:02    kevinl
  41. // Made all clients use same channel and moved pid into message
  42. // gib now removes the message queue nicely
  43. // Handles missing clients that have been cleaned up and have messages
  44. // outstanding
  45. //
  46. // Revision 1.18  1993/11/04  23:08:37    darrenp
  47. // Made signal callbacks typesafe (to keep linux happy)
  48. // added time.h include (same thing - for interval timers)
  49. //
  50. // Revision 1.17  1993/11/04  15:10:53    kevinl
  51. // recMess now has 3 state return
  52. // Alarm set to clean up dead clients
  53. // creat replaces open
  54. // More signal handling
  55. // We now do the shared memory creation/removal
  56. // Removed fork
  57. //
  58. // Revision 1.16  1993/11/03  14:00:24    darrenp
  59. // some prototypical logging - not being used.
  60. //
  61. // Revision 1.15  1993/11/03  12:41:54    darrenp
  62. // Added sigterm shutdown facilitites.
  63. //
  64. // Revision 1.14  1993/11/03  11:29:11    davison
  65. // Ultrix fix.
  66. //
  67. // Revision 1.13  1993/10/31  17:22:11    darrenp
  68. // Fix to returnErr to shut up compiler.
  69. //
  70. // Revision 1.12  1993/10/31  16:25:15    darrenp
  71. // added ipcproto include,
  72. // numerous linked list fixes.
  73. //
  74. // Revision 1.13  1993/10/31  13:24:12    daz
  75. // Fixed a generic bug in the delXXX functions where the head
  76. // of the list was being deleted.
  77. //
  78. // Revision 1.12  1993/10/31  12:22:51    daz
  79. // Not passing shmId correctly to shmctl for delete - fixed.
  80. //
  81. // Revision 1.11  1993/10/29  15:54:37    darrenp
  82. // bug fixes in memorary attachment.
  83. //
  84. // Revision 1.10  1993/10/24  14:35:55    daz
  85. // Changes to permit compilation under SunOS - debugging info
  86. // removed, big endian bug fix.
  87. //
  88. // Revision 1.9  1993/10/13  10:26:28  daz
  89. // *** empty log message ***
  90. //
  91. // Revision 1.8  1993/10/04  10:53:53  daz
  92. // *** empty log message ***
  93. //
  94. // Revision 1.7  1993/07/21  14:25:42  daz
  95. // *** empty log message ***
  96. //
  97. // Revision 1.6  1993/07/21  10:45:44  daz
  98. // *** empty log message ***
  99. //
  100. // Revision 1.5  1993/07/04  16:08:03  daz
  101. // *** empty log message ***
  102. //
  103. // Revision 1.4  1993/07/04  15:00:44  daz
  104. // *** empty log message ***
  105. //
  106. // Revision 1.2  1993/07/04  13:42:19  daz
  107. // *** empty log message ***
  108. //
  109. // Revision 1.1  1993/07/04  02:21:13  daz
  110. // Initial revision
  111. //
  112.  **************************************************************************/
  113.  
  114. // This program is the front-end to the multi-user version of the database
  115.  
  116.  
  117. #include "gib.h"
  118. #include "dcdefs.h"
  119.  
  120. // Need these for inter process communication.
  121. #include <sys/types.h>
  122. extern "C"
  123. {
  124. #include <sys/ipc.h>
  125. #include <sys/msg.h>
  126. #include <sys/shm.h>
  127. }
  128.  
  129. #ifndef linux
  130. #include "ipcproto.h"
  131. #endif
  132.  
  133. #ifdef __EMX__
  134. #include <strstrea.h>
  135. #else
  136. #include <strstream.h>
  137. #endif
  138. #include <errno.h>
  139. #include <signal.h>
  140. #include <stdlib.h>
  141. #include <stdio.h>
  142. #include <sys/time.h>
  143.  
  144. //
  145. // Globals
  146. //
  147. diamondBase theDiamondBase("server.db");
  148. long        msqId;                // Message queue id for the server
  149. long        shmId;                // Shared memory id for the server
  150. long        serverRefId = 0;    // ids we hand out to clients.
  151. bool        shutdown = false;    // Flag to indicate shutdown request.
  152.  
  153. //
  154. // Prototypes - local
  155. //
  156. void handleAttach(long m[]);    // Handle attachment.
  157. void handleTrans(long m[]);        // Handle transaction.
  158. #ifdef __CC
  159. void termHandler(int,...);        // Handles SIGTERM requests
  160. void alarmHandler(int,...);        // Handles ALARM signals
  161. #else
  162. void termHandler(int);            // Handles SIGTERM requests
  163. void alarmHandler(int);         // Handles ALARM signals
  164. #endif
  165. void performShutdown(void);        // Shutdown server.
  166. void goIntoBackground(void);    // Become a proper server.
  167.  
  168. // This maintains a list of all the nodes that we are faking.
  169. fakeNode    *firstFO = 0;
  170. clientInfo    *firstCI = 0;
  171. long        clientCount = 0;
  172.  
  173. //
  174. // Setup the alarm timer
  175. //
  176. void
  177. setAlarm(void)
  178. {
  179.     signal(SIGALRM, alarmHandler);
  180.     struct itimerval v;
  181.     v.it_interval.tv_sec = v.it_value.tv_sec = 60;
  182.     v.it_interval.tv_usec = v.it_value.tv_usec = 0;
  183.     setitimer(ITIMER_REAL, &v, 0);
  184. }
  185.  
  186. //
  187. // Remove the message queue and exit
  188. //
  189. void
  190. gibExit(int ret)
  191. {
  192.     msqid_ds    dummy;
  193.     msgctl(msqId,IPC_RMID,&dummy);
  194.     exit(ret);
  195. }
  196.  
  197. //
  198. // Life as a server is pretty tough - set up shop and
  199. // wait for something interesting to do.
  200. //
  201. main(void)
  202. {
  203.     // Well, all we really need to do now is set up the inter process
  204.     // communication and wait for database requests.
  205.  
  206.     // Create a message queue identifier
  207.     // Use the character identifier 'S' and the rendezvous
  208.     // point 'gibraltar' - seems a fairly solid place to setup a
  209.     // database.
  210.  
  211.     cout << "Diamondbase Jeweler Server: Version " << serverVersion << endl;
  212.  
  213.     // Create a file which will be used as a rendezvous point for clients
  214.     // wishing to connect to us.
  215.  
  216.     long gibraltarFD = creat("gibraltar", 0600);
  217.     if (gibraltarFD==-1) {
  218.         cerr << "Couldn't establish rendezvous point gibraltar" << endl;
  219.         exit(1);
  220.     }
  221.     close(gibraltarFD);
  222.  
  223.     // Create a key from the file system rendezvous point.
  224.     key_t key = ftok("gibraltar",'S');
  225.  
  226.     // Create the actual message queue
  227.     // Must nuke existing ones which might have pending messages.
  228.     //------------------------------------------------------------
  229.     //
  230.     // See if there is an existing one.
  231.  
  232.     msqId = msgget(key,0600);
  233.  
  234.     if (msqId != -1) {
  235.         // Should have failed.
  236.         // There is an existing queue - destroy it.
  237.         msqid_ds    dummy;
  238.         msgctl(msqId,IPC_RMID,&dummy);
  239.     }
  240.  
  241.     // Now create it.
  242.     msqId = msgget(key,IPC_CREAT | 0600);
  243.  
  244.     // Did an error occur ?
  245.     if (msqId == -1) {
  246.         cerr << "Couldn't establish server message queue" << endl;
  247.         perror("Reason:");
  248.         exit(1);
  249.     }
  250.  
  251.     // Diagnostics
  252.     // ---------------------------------
  253.     // cout << "MsqId = " << msqId << endl;
  254.     // cout << "ShmId = " << shmId << endl;
  255.  
  256.     // Insert wait loop here for incoming requests.
  257.     // Now an incoming attach request might be one long,
  258.     // so allocate that much space for the moment.
  259.     // Plus space for the message type.
  260.  
  261.     long    mess[100]; // Maximum message length.
  262.  
  263.     // Set up a signal handler to permit incoming term signals
  264.     // indicating we should shutdown.
  265.     signal(SIGTERM,termHandler);
  266.     signal(SIGHUP,termHandler);
  267.     signal(SIGINT,termHandler);
  268.  
  269.     setAlarm();
  270.  
  271.     // Background this process.
  272.     // A few things we want to do here - we set up our own
  273.     // session id, so we aren't attached to the process group which
  274.     // created us. We will also open a log file to record any output.
  275.     goIntoBackground();
  276.  
  277.     // Loop around, expecting a lot of these sorts of
  278.     // requests.
  279.     while(1) {
  280.         // Get the message with least number less than
  281.         // abs(BASE_RESP - 1). That way we hear about requests
  282.         // and attach messages, but ignore outgoing responses which
  283.         // we have generated and which are meant for other clients.
  284.         // see diagram below.
  285.  
  286.         // cout << "DIAG: Waiting for message with # <" << BASE_RESP -1 << endl;
  287.  
  288.         // Check for posted shutdown request.
  289.         if (shutdown) performShutdown();
  290.  
  291.         // Get a message in our request range ( new attachments, or
  292.         // client requests.
  293.         switch (recMess(mess,399,-(BASE_RESP-1))) {
  294.  
  295.         case rcvERR:
  296.             cerr << "Error reading messages" << endl;
  297.             perror("");
  298.             gibExit(1);
  299.  
  300.         case rcvINTR:
  301.             continue;
  302.  
  303.         case rcvOK:
  304.             // Nothing
  305.             break;
  306.         }
  307.  
  308.         // Check for posted shutdown request.
  309.         if (shutdown) performShutdown();
  310.  
  311.         // Decide what sort of message it is.
  312.         // Incoming messages for attaches always occur on the
  313.         // one message channel - other transaction requests
  314.         // come in on a channel specific to the client's pid.
  315.         //
  316.         // The message space is partitioned as follows:
  317.         // 0         <- lowest
  318.         //
  319.         // ATTACH_CHANNEL <- Attach requests on this channel.
  320.         //
  321.         // BASE_RESP  <- responses go out at here + pid
  322.         //
  323.         //
  324.         // BASE_REQ <- requests come in at here + pid.
  325.         //
  326.         // <--------- Highest
  327.  
  328.         if (mess[0] == ATTACH_CHANNEL) {
  329.             // It's a new request to attach.
  330.             handleAttach(mess);
  331.  
  332.         } else if (mess[0] >=BASE_REQ && mess[0] <BASE_RESP) {
  333.             // It's a transaction request message.
  334.             handleTrans(mess);
  335.         } else {
  336.             // Hmmm - shouldn't get other kinds of messages !
  337.             cerr << "Unexpected message type received # " << mess[0] << endl;
  338.         }
  339.     }
  340. }
  341.  
  342. // Client list handling routines.
  343. // Just a dumb list for the moment
  344. // - should be implememented as a binary search tree
  345. // or a hash table.
  346.  
  347. void
  348. insertCI(clientInfo *f)
  349. {
  350.     // Insert at head of list - be lazy.
  351.     f->prev = 0;
  352.     f->next = firstCI;
  353.     if (f->next) f->next->prev = f;
  354.     firstCI = f;
  355. }
  356.  
  357. clientInfo *
  358. findCI(long pid)
  359. {
  360.     // Find clientInfo in list.
  361.     clientInfo *f;
  362.     for(f=firstCI;f;f=f->next) {
  363.         if (f->pid == pid) break;
  364.     }
  365.     return f;
  366. }
  367.  
  368. void
  369. delCI(clientInfo *f)
  370. {
  371.     // Remove fake object from list.
  372.     if (f->prev) f->prev->next = f->next;
  373.     if (f->next) f->next->prev = f->prev;
  374.     if (!f->prev) firstCI = f->next;
  375. }
  376.  
  377. // Fake node list handling routines.
  378. // Just a dumb list for the moment - should probably move to
  379. // fake.cc - could be implememented as a binary search tree
  380. // or a hash table.
  381.  
  382. void
  383. insertFO(fakeNode *f)
  384. {
  385.     // Insert at head of list - be lazy.
  386.     f->prev = 0;
  387.     f->next = firstFO;
  388.     if (f->next) f->next->prev = f;
  389.     firstFO = f;
  390. }
  391.  
  392. fakeNode *
  393. findFO(long client,long refId)
  394. {
  395.     // Find fake node in list.
  396.     fakeNode *f;
  397.     for(f=firstFO;f;f=f->next) {
  398.         if (f->client == client && f->refId == refId) break;
  399.     }
  400.     return f;
  401. }
  402.  
  403. void
  404. delFO(fakeNode *f)
  405. {
  406.     // Remove fake object from list.
  407.     if (f->prev) f->prev->next = f->next;
  408.     if (f->next) f->next->prev = f->prev;
  409.     if (!f->prev) firstFO = f->next;
  410. }
  411.  
  412. fakeObject::fakeObject(const char *relName) : diaGRel(relName)
  413. {}
  414.  
  415. // Code for processing transactions
  416.  
  417. //
  418. // The initial attach request which comes in on
  419. // the attach channel is handled here.
  420. // The initial attach doesn't involve much - when the
  421. // client is ready to start some serious dialogue with the
  422. // server, it must send a proper attach transaction. This is just to
  423. // reserve the channel, and assign id numbers etc.
  424. // ---------------------------------------------
  425.  
  426. void handleAttach(long m[])
  427. {
  428.     // Ok - the client should have sent their process id
  429.     // as the second long of the message. The request and
  430.     // repsonse message ids can be calculated from their pid.
  431.  
  432.     // Now attach to the shared memory transfer area for use
  433.     // in data transfer operations between server and client.
  434.     // Note that it has a maximum size - that's a bit of a bummer
  435.     // really, since the relation could theoreticallly be larger.
  436.     // They have created a shared memory segment under the name
  437.     // /tmp/diamond.pid type 'C' - which we should be able to
  438.     // attach to.
  439.  
  440.     char buffer[100];
  441.     ostrstream    rendezvous(buffer,sizeof buffer);
  442.  
  443.     long protocol = (m[1] & (255 << 24)) >> 24;
  444.     m[1] -= protocol;
  445.  
  446.     if (protocol <= serverProtocol) {
  447.         cerr << "Attach request from pid " << m[1] << " with protocol " << protocol << " - too old." << endl;
  448.         returnErr(-db_protocol, m[1]);
  449.         return;
  450.     }
  451.  
  452.     rendezvous << "/tmp/diamond." << m[1] << '\000';
  453.     int fd;
  454.     if ((fd=creat(buffer,0600)) == -1) {
  455.         perror("Rendevous creation");
  456.         returnErr(-db_err, m[1]);
  457.         return;
  458.     }
  459.  
  460.     close(fd);
  461.     // cout << "Shared buffer '" << buffer << "'" << endl;
  462.  
  463.     key_t key = ftok(buffer,'C');
  464.  
  465. #if DEBUG
  466.     cerr << "ftok(" << buffer << ") returned " << key << endl;
  467. #endif
  468.  
  469.     shmId = shmget(key, MAX_TRANS_AREA, 0600 | IPC_CREAT);
  470.  
  471. #if DEBUG
  472.     cerr << "shmget returned " << shmId << endl;
  473. #endif
  474.  
  475.     if (shmId == -1) {
  476.         cerr << "GIB: Couldn't establish server transfer area for pid"
  477.             << m[1] << endl;
  478.         perror("Reason:");
  479.         returnErr(-db_nomem,m[1]);
  480.         return;
  481.     }
  482.  
  483.     // Now try to attach to the shared memory segment
  484.     // which is used for data transfer. Allocate a piece of
  485.     // memory for us, and don't round down our supplied address (no address)
  486.     char *transArea = (char*)shmat(shmId,0,0);
  487.  
  488.     if ((long)transArea==-1) {
  489.         cerr << "GIB: Couldn't attach to server transfer area " << shmId << endl;
  490.         perror("Reason:");
  491.         returnErr(-db_nomem,m[1]);
  492.         return;
  493.     }
  494. #if DEBUG
  495.     else
  496.         cerr << "GIB: SUCCESSFULLY attached to server transfer area " << shmId << endl;
  497. #endif
  498.  
  499.     // Insert details of this new client onto a list,
  500.     // the list remembers their shared memory id, their transfer
  501.     // area and their pid, since later connections will have to
  502.     // remember the transfer area being used for that particular
  503.     // client.
  504.     clientInfo    *ci = new clientInfo;
  505.     ci->pid = m[1];
  506.     ci->transArea = transArea;
  507.     ci->shmId = shmId;
  508.     insertCI(ci);
  509.     clientCount++;
  510.  
  511.     // Diagnostics:
  512.     // ----------------------
  513.     cout << "+pid " << m[1] << endl;
  514.  
  515.     // Return an ok message, cite their pid to the response routine
  516.     // so that it knows on which channel to respond.
  517.     returnErr(-db_ok,m[1]);
  518. }
  519.  
  520. //
  521. // Generic reply routine.
  522. // Caller cites the client id number (their pid)
  523. // and a return code (one long).
  524.  
  525. void returnErr(long e,long client)
  526. {
  527.     // Generic routine for passing back a single long
  528.     // value which is an error code.
  529.     long    buffer[2];
  530.     buffer[1] = e;
  531.  
  532.     if (!sendMess(buffer,sizeof(long),client+BASE_RESP)) {
  533.         cerr << "Failed to send error code to client" << endl;
  534.     }
  535. }
  536.  
  537. // Handle the attachment of a new class from a client.
  538. // Involves creating a new diagrel to handle the database
  539. // requests at this end.
  540.  
  541. void relAttach(long client,const char *relName)
  542. {
  543.     // Diagnostics:
  544.     // Note the request to attach.
  545.     // -----------------------------
  546.     // cout << "request to attach '" << relName << "' for " << client << endl;
  547.  
  548.     // We need to register a new fake object * with the
  549.     // database engine. Build one first
  550.  
  551.     fakeObject *f;
  552.     fakeNode *fn;
  553.     f = new fakeObject(relName);
  554.     fn = new fakeNode;
  555.  
  556.     if (!f || !fn) {
  557.         // Ran out of memory in server - bad things will ensue.
  558.         returnErr(-db_nomem,client);
  559.         return;
  560.     }
  561.  
  562.     // Check to see if registration with the dbEngine went
  563.     // ok ?
  564.     long    refId;
  565.     if (!f->ok()) {
  566.         // Hmm - it didn't !
  567.         returnErr(-(f->status),client);
  568.         delete f;
  569.         delete fn;
  570.     } else {
  571.         // Purrr - we just need to tell them what
  572.         // our choice of refId was. Ask diaGRel.
  573.  
  574.         refId = fn->refId = f->getRefId();
  575.  
  576.         //
  577.         // Also, set up the copy of the transArea which is common to that
  578.         // process. Get this from the client info structure.
  579.         //
  580.         clientInfo *ci = findCI(client);
  581.  
  582.         if (!ci) {
  583.             delete f;
  584.             delete fn;
  585.             return;
  586.         }
  587.  
  588.         f->transArea = ci->transArea;
  589.  
  590.         returnErr(refId,client);
  591.  
  592.         // We should also place this fake object onto our list.
  593.  
  594.         f->client = fn->client = client; // redundant ?
  595.         fn->obj = f; // Might change in a non linked list imp.
  596.         insertFO(fn);
  597.     }
  598. }
  599.  
  600. // Remove an existing object.
  601. // They have requested a detach.
  602.  
  603. void relDetach(long client,long refId)
  604. {
  605.     // Find the client they want.
  606.     fakeNode *f = findFO(client,refId);
  607.     if (!f) {
  608.         // Whinge if we couldn't find it.
  609.         returnErr(-db_nfound,client);
  610.         return;
  611.     }
  612.  
  613.     // Delete the fakeObject itself. - this will cause the
  614.     // diaGRel to detach from the engine, as required.
  615.     delete f->obj;
  616.  
  617.     delFO(f); // Remove node from list
  618.     delete f; // Remove node from memory
  619.  
  620.     returnErr(db_ok,client);
  621. }
  622.  
  623. void clientDetach(long client)
  624. {
  625.     // A process is quitting at the other end,
  626.     // we should go through and clean up any of its
  627.     // remaining fakeObjects.
  628.  
  629.     // This operation could also occur on the initiative of the server
  630.     // if it suspects a client process has died.
  631.     fakeNode *f,*g;
  632.     for(f=firstFO;f;f=g) {
  633.         g=f->next;
  634.         if (f->client == client) {
  635.             // Free it from memory and the list.
  636.             delete f->obj;
  637.             delFO(f);
  638.             delete f;
  639.         }
  640.     }
  641.     // And remove its client object.
  642.     // First nuke its shared memory segment.
  643.     clientInfo *ci = findCI(client);
  644.     if (!ci)
  645.         return;
  646.     shmid_ds dummy;
  647.     if (shmdt(ci->transArea) == -1)
  648.         perror("Detaching from transfer area");
  649.     if (shmctl(ci->shmId,IPC_RMID,&dummy) == -1)
  650.         perror("Removing shared memory segment");
  651.  
  652.     // Suck up all the client's messages
  653.  
  654.     // Diagnostics:
  655.     // ----------------------
  656.     clientCount--;
  657.     char buffer[100];
  658.     ostrstream    rendezvous(buffer,sizeof buffer);
  659.  
  660.     rendezvous << "/tmp/diamond." << client << '\000';
  661.     unlink(rendezvous.str());
  662.     cout << "-pid " << client << endl;
  663.  
  664.     // Now get rid of it.
  665.     delCI(ci);
  666. }
  667.  
  668. void processUnknown(long client, char type)
  669. {
  670.     // cout << "Process with id #" << client << " issued unknown "
  671.     //    "transaction type '" << type << "'" << endl;
  672.     returnErr(-db_err,client);
  673. }
  674.  
  675. void processObjTrans(long client,long refId,long trans,long idx)
  676. {
  677.     // Carry out a specific operation on a specific object
  678.     // using the database engine.
  679.     // Make the db engine do the actual work - kick up our heels
  680.     // and take life easy for a while till the database starts demanding
  681.     // work from our fakedObject's diaGRel base class.
  682.  
  683.     fakeObject *f = findFO(client,refId)->obj;
  684.     if (!f) return; // Not found.
  685.  
  686.     memcpy(f->theData,f->transArea,f->getDataLength());
  687.     long d1,d2;
  688.  
  689.     d1 = ((long *)f->theData)[0];
  690.     d2 = ((long *)f->theData)[1];
  691.     // cout << "Incoming '" << (char *)(f->theData)+8 << "' " <<
  692.     //    d1 << " " << d2 << endl;
  693.     // cout << "Execute trans type " << trans << endl;
  694.  
  695.     dbError e = theDiamondBase.trans(
  696.                     refId,
  697.                     (diamondBase::transId)trans,
  698.                     idx);
  699.  
  700.     memcpy(f->transArea,f->theData,f->getDataLength());
  701.     d1 = ((long *)f->theData)[0];
  702.     d2 = ((long *)f->theData)[1];
  703.     // cout << "Outgoing '" << (char *)(f->theData)+8 << "' " <<
  704.     //    d1 << " " << d2 << endl;
  705.  
  706.     // Send back a message of type 'q' with data = retCode
  707.  
  708.     long    tmp[3];     // temporary place to construct message.
  709.     tmp[1] = (long)'q'; // operation.
  710.     tmp[2] = e;            // error code.
  711.  
  712.     // Send it back on the clients channel.
  713.     sendMess(tmp,sizeof(long)*2,client + BASE_RESP);
  714. }
  715.  
  716. //
  717. // This routine dispatches the incoming transactions
  718. // from the clients. They fall into several categories.
  719. // Nothing more than a glorified case statement which works
  720. // out which client we are talking to.
  721.  
  722. void handleTrans(long m[])
  723. {
  724.     // We now get the pid as number 1
  725.     //long    client = m[0] - BASE_REQ;
  726.     long protocol = (m[1] & (255 << 24)) >> 24;
  727.     m[1] -= protocol;
  728.  
  729.     long    client = m[1];
  730.  
  731.     // This is a bit ugly - the transaction code always
  732.     // gets put just after the first long word.
  733.     char    type = ((char *)m)[2*sizeof(long)];
  734.  
  735.     // Dispatch different transaction types.
  736.     switch(type) {
  737.         case 'A': // Attach
  738.             relAttach(client,((char *)m)+2*sizeof(long)+1);
  739.             break;
  740.         case 'D': // Detach
  741.             relDetach(client,m[3]);
  742.             break;
  743.         case 'T': // General transaction on an object
  744.             processObjTrans(client,m[3],m[4],m[5]);
  745.             break;
  746.         case 'Q': // Quit
  747.             clientDetach(client);
  748.             break;
  749.         default:
  750.             processUnknown(client,type);
  751.     }
  752. }
  753.  
  754. void
  755. checkTimer()
  756. {
  757.     struct itimerval v;
  758.     getitimer(ITIMER_REAL, &v);
  759.     cerr << "TIMER: " << v.it_interval.tv_sec << "," << v.it_value.tv_sec << endl;
  760. }
  761.  
  762. // Some IPC wrappers.
  763. // Have fun reverse engineering them under a non-IPC
  764. // environment.
  765. // ---------------------------
  766.  
  767. bool sendMess(void *data,long len,long chan)
  768. {
  769.     // The start of the pointed to memory must have the
  770.     // message type in it, so we stick it in - the caller *must*
  771.     // leave room for it.
  772.  
  773.     // NB This is slightly different from the dclient.cc version which
  774.     // also attaches the pid.
  775.  
  776.     ((long *)data)[0] = chan;
  777.  
  778.     // Send the data off to the server.
  779.     long retCode = msgsnd(msqId,(msgbuf *)data,len,0);
  780. #ifdef linux
  781.     if (retCode!=len) {
  782. #else
  783.     // Sun case
  784.     if (retCode!=0) {
  785. #endif
  786.         perror("Transmitting message");
  787.         return false;
  788.     }
  789.     // cout << "DIAG: sendmess " << ((long *)data)[0] << endl;
  790.     return true;
  791. }
  792.  
  793. rcvRes recMess(void *data,long len,long chan)
  794. {
  795.     // Grab the allotted data from the server
  796.  
  797.     //checkTimer();
  798.     long retCode = msgrcv(msqId,(msgbuf *)data,len,chan,0);
  799.     if (retCode == -1) {
  800.         if (errno == EINTR) {
  801.             cerr << "EINTR" << endl;
  802.             return rcvINTR;
  803.         } else {
  804.             perror("Receiving message");
  805.             return rcvERR;
  806.         }
  807.     }
  808.     // cout << "DIAG: recmess " << ((long *)data)[0] << endl;
  809.     return rcvOK;
  810. }
  811.  
  812. //
  813. // Handle signal terminations by setting a flag
  814. // which indicates we should shutdown as soon as we complete
  815. // the current transaction.
  816. //
  817. void
  818. #ifdef __CC
  819. termHandler(int,...)
  820. #else
  821. termHandler(int)
  822. #endif
  823. {
  824.     shutdown = true;
  825.     cout << "Server shutdown requested" << endl;
  826.  
  827.     cout << clientCount << " clients remaining" << endl;
  828. }
  829.  
  830. //
  831. // Handle the periodic alarm to check for shared mem attachments
  832. //
  833.  
  834. void
  835. #ifdef __CC
  836. alarmHandler(int,...)
  837. #else
  838. alarmHandler(int)
  839. #endif
  840. {
  841.     clientInfo *c = firstCI, *n;
  842.  
  843.     struct shmid_ds buf;
  844.  
  845.     cerr << "ALARM HANDLER" << endl;
  846.  
  847.     while (c) {
  848.         shmctl(c->shmId, IPC_STAT, &buf);
  849.         cerr << "ALARM: shmId: " << c->shmId << "     attachments " << buf.shm_nattch << endl;
  850.  
  851.         // Store the next pointer in case clientDetach affects the current client
  852.         n = c->next;
  853.  
  854.         // If there is 1 or fewer attachments, the client is gone.
  855.  
  856.         if (buf.shm_nattch <= 1) {
  857.             clientDetach(c->pid);
  858.             cerr << "     : Process Detached" << endl;
  859.         }
  860.  
  861.         c = n;
  862.     }
  863.     signal(SIGALRM, alarmHandler);
  864. }
  865.  
  866. void
  867. performShutdown()
  868. {
  869.     // There is a bit of a dilemna over what to do here when
  870.     // a shutdown is requested. Be nice and wait for all the
  871.     // little client thingies to go home.
  872.  
  873.     if (clientCount) return;
  874.     cout << "shutdown done." << endl;
  875.     cout.flush();
  876.     cerr.flush();
  877.     gibExit(0);
  878. }
  879.  
  880. void
  881. goIntoBackground(void)
  882. {
  883. //    if (fork()) exit(0);
  884.  
  885.     // Although taken from another working program, this
  886.     // logging code results inthe output being lost, so we
  887.     // shall ignore it for the moment.
  888. #if 0
  889.     setsid();
  890.     freopen("/dev/null","r",stdin);
  891.  
  892.     // Make sure our logfile is opened with the correct flags
  893.     long fd = open("jeweler.log", O_APPEND | O_WRONLY | O_CREAT,0600);
  894.  
  895.     fclose(stdout);
  896.     fclose(stderr);
  897.  
  898.     if (fd != 1) {
  899.         if (dup2(fd,1)==-1) perror("dup2 problem");
  900.     }
  901.     if (fd != 2) {
  902.         if (dup2(fd,2)==-1) perror("dup2 problem");
  903.     }
  904.     if (fd != 1 && fd != 2) close(fd);
  905. #endif
  906. }
  907.