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

  1. /**************************************************************************
  2.  * Source Id :
  3.  *
  4.  * $Id: dclient.cc,v 1.20 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.  *  Client code for multi-db
  24.  *
  25.  *  Original Author : Darren Platt
  26.  *
  27.  *-------------------------------------------------------------------------
  28.  * Revision History:
  29.  *
  30.  * $Log: dclient.cc,v $
  31. // Revision 1.20  1993/11/05  13:46:57    kevinl
  32. // Protocol and fixes
  33. //
  34. // Revision 1.19  1993/11/05  12:45:05    kevinl
  35. // Fixed problems that CC picked up (and some multi bugs)
  36. //
  37. // Revision 1.18  1993/11/05  11:45:04    darrenp
  38. // Fix for linux - not accounting for the # bytes sent in a message
  39. // correctly, and linux doesn't use -1 for an error condition.
  40. // You added len+sizeof(long) and the return checks for len bytes sent.
  41. //
  42. // Revision 1.17  1993/11/05  07:23:02    kevinl
  43. // Made all clients use same channel and moved pid into message
  44. // gib now removes the message queue nicely
  45. // Handles missing clients that have been cleaned up and have messages outstanding
  46. //
  47. // Revision 1.16  1993/11/04  15:07:01    kevinl
  48. // We now let the server remove the shared memory segment
  49. //
  50. // Revision 1.15  1993/11/04  13:46:44    darrenp
  51. // Client formally detaches from its given shared memory segment
  52. // after notifying server of its intent to die.
  53. //
  54. // Revision 1.14  1993/11/04  13:14:02    darrenp
  55. // Handed over responsibility for creating the rendezvous point
  56. // and the ahred memory segment to the server
  57. //
  58. // Revision 1.13  1993/11/04  11:30:38    darrenp
  59. // Removed unlink code.
  60. //
  61. // Revision 1.12  1993/11/03  14:00:24    darrenp
  62. // Cleans up diamond.pid files properly now.
  63. //
  64. // Revision 1.11  1993/11/03  11:29:01    davison
  65. // Ultrix fix
  66. //
  67. // Revision 1.10  1993/10/31  16:25:15    darrenp
  68. // added ipcproto include
  69. //
  70. // Revision 1.9  1993/10/29  15:54:37  darrenp
  71. // bug fixes in memorary attachment.
  72. //
  73. // Revision 1.8  1993/10/24  14:37:00  daz
  74. // SunOS fixes, diagnostics commented out, cmall bug fixes for
  75. // big endian machines.
  76. //
  77. // Revision 1.7  1993/10/13  10:26:28  daz
  78. // *** empty log message ***
  79. //
  80. // Revision 1.6  1993/07/21  14:25:42  daz
  81. // *** empty log message ***
  82. //
  83. // Revision 1.5  1993/07/21  10:45:44  daz
  84. // *** empty log message ***
  85. //
  86. // Revision 1.4  1993/07/04  16:08:03  daz
  87. // *** empty log message ***
  88. //
  89. // Revision 1.3  1993/07/04  15:00:49  daz
  90. // *** empty log message ***
  91. //
  92. // Revision 1.2  1993/07/04  13:42:19  daz
  93. // *** empty log message ***
  94. //
  95. // Revision 1.1  1993/07/04  02:21:13  daz
  96. // Initial revision
  97. //
  98.  **************************************************************************/
  99.  
  100. // Implement the necessary IPC calls to link this clients
  101. // diamondBase object into the central server.
  102.  
  103. #include <iostream.h>
  104. #ifdef __EMX__
  105. #include <strstrea.h>
  106. #else
  107. #include <strstream.h>
  108. #endif
  109. #include <stdlib.h>
  110. #include <unistd.h>
  111. #include <assert.h>
  112. #include <sys/types.h>
  113. extern "C"
  114. {
  115. #include <sys/ipc.h>
  116. #include <sys/shm.h>
  117. #include <sys/msg.h>
  118. }
  119. #include "ipcproto.h"
  120. #include <fcntl.h>
  121. #include <errno.h>
  122.  
  123. #include "dclient.h"
  124. #include "object.h"
  125.  
  126. // Used to keep a linked list of object pointers at this
  127. // end to handle any requests.
  128. struct objNode {
  129.     object    *obj;
  130.     long    refId;
  131.     objNode *next;
  132.     objNode *prev;
  133. };
  134.  
  135. objNode *head = 0;
  136.  
  137. //
  138. // Code for handling the object list at this end.
  139. // We need to keep track of all the objects and their
  140. // corresponding reference ids so that when a request
  141. // comes from the server, the appropriate object can be called.
  142. //
  143.  
  144. void
  145. insertObj(objNode *f)
  146. {
  147.     // Insert at head of list - be lazy.
  148.     f->prev = 0;
  149.     f->next = head;
  150.     head = f;
  151. }
  152.  
  153. objNode *
  154. findObj(long refId)
  155. {
  156.     // Find obj node in list.
  157.     objNode *f;
  158.     for(f=head;f;f=f->next) {
  159.         if (f->refId == refId) break;
  160.     }
  161.     return f;
  162. }
  163.  
  164. void
  165. delObj(objNode *f)
  166. {
  167.     // Remove object from list.
  168.     if (f->prev) f->prev->next = f->next;
  169.     if (f->next) f->next->prev = f->prev;
  170.     if (!f->prev) head = 0;
  171. }
  172. char* diamondBase::verStr(void)
  173. {
  174.     return "$Id: dclient.cc,v 1.20 1993/11/05 13:46:57 kevinl Exp $";
  175. }
  176.  
  177. char* diamondBase::version(bool full)
  178. {
  179.     // Call across to server and ask for version string from its
  180.     // diamondBase
  181.     return "Yeah sure";
  182. }
  183.  
  184. diamondBase::diamondBase(char* name)
  185. {
  186.     // Change the semantics of the provided name here.
  187.     // Rather than sepcifying a relation pathname file,
  188.     // specify the name of the server object we wish to join to.
  189.     // In fact - that isn't even implemented - we just ignore it
  190.     // at the moment.
  191.  
  192.     // Do an initial connection with the server and get an id for
  193.     // this connection.
  194.     // cout << "Multi diamondbase constructor" << endl;
  195.  
  196.     key_t key = ftok("./gibraltar",'S');
  197.  
  198.     msqId = msgget(key,0);
  199.  
  200.     // Did we attach to the message queue successfully
  201.     if (msqId == -1) {
  202.         cerr << "Server not found" << endl;
  203.         perror("This was it");
  204.         exit(1);
  205.     }
  206.  
  207.     // We now put all requests on the same channel so they are treated equally.
  208.     // sendChannel = BASE_REQ + getpid();
  209.  
  210.     sendChannel = BASE_REQ;
  211.     recChannel = BASE_RESP + getpid();
  212.  
  213.     long    mess[2];
  214.  
  215.     // Send a message off to the server informing it of
  216.     // our process id, and therefore our send and request
  217.     // channels.
  218.  
  219.     // 0 message gets pid on it anyway
  220.     if (!sendMess(&mess,0,ATTACH_CHANNEL)) {
  221.         cerr << "Error connecting to server (send)" << endl;
  222.         exit(1);
  223.     }
  224.  
  225.     // Now wait for the reply which should consist of an error code.
  226.     // There are potential problems if the server dies at this point -
  227.     // it would be preferable if the client didn't hang.
  228.  
  229.     if (!recMess(&mess,sizeof(long))) {
  230.         cerr << "Error connecting to server (receive)" << endl;
  231.         exit(1);
  232.     }
  233.     // See if there was anything wrong with the connection.
  234.     if (mess[1]) {
  235.         // Server reported an error
  236.         dbErr((dbError)mess[1],"Connecting to server");
  237.         return;
  238.     }
  239.  
  240.     // Attach to a shared memory area specifically for our id,
  241.     // which the server will have created and attach to in order to effect
  242.     // memory transfers.
  243.     key_t        key2;
  244.     char        buffer[100];
  245.     ostrstream    rendezvous(buffer,sizeof buffer);
  246.  
  247.     // The shared memory segment will have been made to
  248.     // rendezvous with /tmp/diamond.ourPid
  249.     rendezvous << "/tmp/diamond." << getpid() << '\000';
  250.  
  251.     // Get the shared memory segment.
  252.     key2 = ftok(buffer,'C');
  253.     shmId = shmget(key2,MAX_TRANS_AREA,0);
  254.  
  255.     if (shmId == -1) {
  256.         dbErr(db_err,"DClient: Couldn't establish server transfer area");
  257.         return;
  258.     }
  259.  
  260.     // Now try to attach to the shared memory segment
  261.     // which is used for data transfer. Allocate a piece of
  262.     // memory for us, and don't round down our supplied address (no address)
  263.     transArea = (char*)shmat(shmId,0,0);
  264.  
  265.     if (!transArea) {
  266.         dbErr(db_err,"Couldn't attach to server transfer area");
  267.         return;
  268.     }
  269.  
  270.     // Diagnostics
  271.     // cout << "msqId = " << msqId << endl;
  272.     // cout << "shmId = " << shmId << endl;
  273.  
  274.     // cout << "Connected ok" << endl;
  275.     // Everything is all hooked up.
  276.     dbErr(db_ok,"Everything is hunky dorey");
  277. }
  278.  
  279. dbError diamondBase::detach(long refId)
  280. {
  281.     // Notify server of intent to disconnect
  282.     if (msqId != -1) {
  283.         // Transmit detach code 'D' followed by refId
  284.         // to server and fetch return code.
  285.         long buffer[4];
  286.         ((char *)buffer)[2*sizeof(long)] = 'D';
  287.         buffer[3] = refId;
  288.         if (!sendMess(buffer,sizeof(long) * 2)) return dbErr(db_comm);
  289.  
  290.         // Receive error code
  291.         if (!recMess(buffer,sizeof(long))) return dbErr(db_comm);
  292.  
  293.         // Should remove the object's bookmark in the
  294.         // linked list.
  295.         objNode *o = findObj(refId);
  296.         if (o) { // If it is in there.
  297.             delObj(o); // Remove from list
  298.             delete o; // Remove associated memory.
  299.         }
  300.         return dbErr((dbError)(buffer[1]));
  301.     }
  302.     return dbErr(db_ok);
  303. }
  304.  
  305. dbError diamondBase::attach(
  306.     const char* name,
  307.     object* resBuffer,
  308.     long& refId,
  309.     dbObjData *&)
  310. {
  311.     // Attach to server object, assigning refId value in the process,
  312.     // keep the object pointer somewhere - pass the name to the server
  313.     // - will need to register the object pointers available.
  314.  
  315.     // Check if the relation is too big for our transfer area.
  316.     // This doesn't work, apparently b/c the actual object hasn't even
  317.     // been constructed yet - we are just cosntructing the base diaRel.
  318.     // if (resBuffer->getDataLength() > MAX_TRANS_AREA) {
  319.     //        return dbErr(db_toomany);
  320.     // }
  321.  
  322.     // Send attach message, plus relation name
  323.     long len = strlen(name) + 2 + sizeof(long);
  324.     char *tmp = new char[len+sizeof(long)];
  325.  
  326.     // Memory allocation succeeded ?
  327.     if (!tmp) return dbErr(db_nomem);
  328.  
  329.     // Leave first sizeof(long) bytes free for message id
  330.     // next byte is the transaction id 'A', then rest is null
  331.     // terminated relation name.
  332.     tmp[2*sizeof(long)] = 'A';
  333.     strcpy(tmp+2*sizeof(long)+1,name);
  334.  
  335.     // Transmit to server
  336.     if (!sendMess(tmp,len)) return dbErr(db_comm);
  337.     delete tmp;
  338.  
  339.     // Receive error code + refId if successful.
  340.     long response[2];
  341.     if (!recMess(response,sizeof(long))) return dbErr(db_comm);
  342.  
  343.     if (response[1]>=0) { // +ve value is refId on success
  344.         refId = response[1];
  345.  
  346.         // We need to stash the object pointer resBuffer here
  347.         // for future transactions that the server might require
  348.         // us to perform.
  349.         objNode *o = new objNode;
  350.         if (!o) {
  351.             detach(refId); // Say bye bye.
  352.             return dbErr(db_nomem);
  353.         }
  354.         // Set up the linked list to hold it.
  355.         o->obj = resBuffer;
  356.         o->refId = refId;
  357.         insertObj(o);
  358.         return dbErr(db_ok);
  359.     }
  360.     // -ve value indicates error !
  361.     refId = -1; // Flag the refId as being invalid.
  362.     return dbErr((dbError)(response[1]));
  363. }
  364.  
  365. void diamondBase::sendLong(long l)
  366. {
  367.     long    tmp[3];
  368.     tmp[2] = l;
  369.     sendMess(tmp,sizeof(long));
  370. }
  371.  
  372. dbError diamondBase::trans(long refId,transId id,long idx)
  373. {
  374.     // Execute a transaction. This will involve stating the
  375.     // transId and refId to the server, then waiting for any
  376.     // messages regarding the object, and finally receiving an
  377.     // error status message.
  378.  
  379.     if (refId == -1) {
  380.         // They are quoting an invalid reference id - report back
  381.         // immediately that they are in error.
  382.         return db_refid;
  383.     }
  384.  
  385.     long    tmp[10];
  386.  
  387.     // Three long words in this messge - the
  388.     // reference id, the transaction we want performed and the
  389.     // index (if necessary - send it anyway. The server will
  390.     // know from the refId which object is involved and hence
  391.     // which client.
  392.  
  393.     ((char *)tmp)[2*sizeof(long)] = 'T';
  394.     tmp[3] = refId;
  395.     tmp[4] = id;
  396.     tmp[5] = idx;
  397.  
  398.     //
  399.     // New plan - every time we go to carry out a transaction,
  400.     // we copy our current version of the data into the
  401.     // shared memory area. This keeps the decision of whether
  402.     // or not the server needs to look at the latest data, very
  403.     // simple. It avoids the need for any messages coming back too.
  404.  
  405.     // Go through the routine of finding the object associated
  406.     // with the refId - just so we can get its data length,
  407.     // quite silly really - should be an easier way - server should
  408.     // cite it at connect time, and we should store it.
  409.  
  410.     objNode *o = findObj(refId);
  411.  
  412.     assert(o);
  413.     object *obj = o->obj;
  414.  
  415.     // Couldn't test this at attach time for some reason, so
  416.     // do it here.
  417.  
  418.     assert(obj->getDataLength()<=MAX_TRANS_AREA);
  419.  
  420.     // cout << "Sending '" << (char *)(void *)(*obj)+8 << "'" << endl;
  421.     memcpy(transArea,(void *)(*obj),obj->getDataLength());
  422.     // cout << ((long *)transArea)[0] << " " << ((long *)transArea)[1] << endl;
  423.  
  424.     if (!sendMess(tmp,sizeof(long)*4)) return dbErr(db_comm);
  425.  
  426.     // Fetch message from the server.
  427.     // Under this scheme, the only message which can come back
  428.     // is the 'q' - quit, I'm done message. Nice and simple.
  429.  
  430.     if (!recMess(tmp,sizeof(long)*9)) {
  431.             return dbErr(db_comm);
  432.     }
  433.  
  434.     // Take a copy of the final data that resulted.
  435.     memcpy((void *)(*obj),transArea,obj->getDataLength());
  436.     // cout << "Receiving '" << (char *)(void *)(*obj)+8 << "'" << endl;
  437.     // cout << ((long *)transArea)[0] << " " << ((long *)transArea)[1] << endl;
  438.  
  439.     return dbErr((dbError)(tmp[2])); // Pick up error code from message
  440. }
  441.  
  442. diamondBase::~diamondBase()
  443. {
  444.     // Notify server of intent to disconnect.
  445.  
  446.     long tmp[3];
  447.  
  448.     ((char *)tmp)[2*sizeof(long)] = 'Q'; // Quit message.
  449.     // Transmit to server
  450.     sendMess(tmp,sizeof(long));
  451.     shmdt(transArea);
  452. }
  453.  
  454. bool diamondBase::sendMess(void *data,long len,long chan)
  455. {
  456.     long actualChan = chan;
  457.     // If they didn't specify a channel, then assume the
  458.     // standard channel for communicating requests to the server.
  459.     if (actualChan == -1) actualChan = sendChannel;
  460.  
  461.     // The start of the pointed to memory must have the
  462.     // message type in it, so we stick it in - the caller *must*
  463.     // leave room for it.
  464.  
  465.     ((long *)data)[0] = actualChan;
  466.     ((long *)data)[1] = serverProtocol << 24 | getpid();
  467.     // Send the data off to the server.
  468.     long retCode = msgsnd(msqId,(msgbuf *)data,len+sizeof(long),0);
  469. #ifdef linux
  470.     if (retCode!=len+sizeof(long)) {
  471. #else
  472.     // Sun case
  473.     if (retCode!=0) {
  474. #endif
  475.         perror("Transmitting message");
  476.         return false;
  477.     }
  478.     // cout << "DIAG: sendmess " << ((long *)data)[0] << endl;
  479.     return true;
  480. }
  481.  
  482. bool diamondBase::recMess(void *data,long len)
  483. {
  484.     // Receive data from the server.
  485.     long retCode = msgrcv(msqId,(msgbuf *)data,len,recChannel,0);
  486.     if (retCode==-1) {
  487.         perror("Receiving message");
  488.         return false;
  489.     }
  490.  
  491.     // The first bit of the received data should be the
  492.     // channel number - and since we said what we wanted,
  493.     // we should have got it - let's be paranoid.
  494.     if (((long *)data)[0] != recChannel) {
  495.         cerr << "Yikes - got wrong channel number!" << endl;
  496.         return false;
  497.     }
  498. //    cout << "DIAG: recmess " << ((long *)data)[0] << endl;
  499.     return true;
  500. }
  501.  
  502.