home *** CD-ROM | disk | FTP | other *** search
/ The CDPD Public Domain Collection for CDTV 4 / CDPD_IV.bin / networking / uucp / amigauucpsrc / bms / get.c < prev    next >
C/C++ Source or Header  |  1994-06-29  |  22KB  |  866 lines

  1.  
  2. /*
  3.  *  GET.C   - Get files and/or directories
  4.  */
  5.  
  6. #include "defs.h"
  7.  
  8. Prototype void GetBMS(char *, char *);
  9. Prototype void InitGet(void);
  10. Prototype void SMGetNoChannel(DBHan *, record_t, RegNode *, long);
  11. Prototype void SMRxGet(DBHan *, record_t, RegNode *, CtlNode *);
  12. Prototype void SMTxGet(DBHan *, record_t, RegNode *, CtlNode *);
  13. Prototype void SMToGet(DBHan *, record_t, RegNode *, CtlNode *);
  14. Prototype bool_t CheckGetDone(RegNode *, CtlNode *);
  15.  
  16. Prototype void ConstructRetryPacket(DBHan *, record_t, RegNode *, CtlNode *);
  17. Prototype void ProcessRetryPacket(DBHan *, record_t, RegNode *, CtlNode *);
  18.  
  19.  
  20. void
  21. InitGet(void)
  22. {
  23.     SetStateMachine(SMACH_GET, SMRxGet, SMTxGet, SMToGet);
  24. }
  25.  
  26. /*
  27.  *  GET a file/directory from a destination.  We are the client in this
  28.  *  case.
  29.  *
  30.  *  - create a control node to handle communication
  31.  *
  32.  *  - create a FileNode to handle the GET operation.  The node will be
  33.  *    expanded on the ack.  Note that we can receive file data before
  34.  *    the ack.    We can, in fact, receive file fragments totally out of
  35.  *    order before the hierarchy is even created.
  36.  */
  37.  
  38. void
  39. GetBMS(remFileName, locFileName)
  40. char *remFileName;
  41. char *locFileName;
  42. {
  43.     RegNode *rn;
  44.     CtlNode *cn;
  45.     FileNode *fn;
  46.  
  47.     if ((rn = FindFirstMarked()) == NULL || rn->rn_Node.ln_Name == NULL) {
  48.     bmslog("Get", "No destination specified");
  49.     return;
  50.     }
  51.     if (locFileName == NULL) {
  52.     bmslog("Get", "Did not specify TO <localfile/dir>");
  53.     return;
  54.     }
  55.     if (rn->rn_Comm.com_DID == 0) {
  56.     bmslog("Get", "Registration for this destination is still in progress");
  57.     bmslog("Get", "Request will be initiated after registration is complete");
  58.     }
  59.  
  60.     /*
  61.      *    Allocate node and initiate communications.  Can setup FileNode
  62.      *    after communications has been initiated
  63.      */
  64.  
  65.     cn = AllocCtlNode(rn);
  66.     InitCommLocalSide(SMACH_GET, rn, &cn->cn_Comm);
  67.     {
  68.     char *buf = zalloc(strlen(remFileName) + strlen(locFileName) + 10);
  69.     sprintf(buf, "GET-REQ-SEND", locFileName, remFileName);
  70.     SetCommMessage(&cn->cn_Comm, buf);
  71.     }
  72.     cn->cn_Comm.com_State = 0;
  73.  
  74.     /*
  75.      *    Allocate and setup a FileNode (we can theoretically setup several
  76.      *    but currently the command only supports one
  77.      */
  78.  
  79.     fn = AllocFileNode(cn);
  80.     fn->fn_Flags |= FNF_MASTER;
  81.     if (remFileName)
  82.     fn->fn_RemFileName = zstrdup(remFileName);
  83.     if (locFileName)
  84.     fn->fn_LocFileName = zstrdup(locFileName);
  85. }
  86.  
  87. /************************************************************************
  88.  *                                    *
  89.  *                GET STATE MACHINE                *
  90.  *                                    *
  91.  ************************************************************************
  92.  *
  93.  *  state 0:    Initiate request        (client)
  94.  *  state 1:    Initiate/retry
  95.  *  state 2:    Receive data and/or ack (1 week to)
  96.  *  state 3:    Done, send ack (2 week hold)
  97.  *
  98.  *  state 8:    Initialize for request        (server)
  99.  *  state 9:    Send Acknowledge
  100.  *  state 10    Send data, recv retries (loop to state 9 if get another req)
  101.  *  state 11:    Send Done, recv retries
  102.  *
  103.  */
  104.  
  105. /*
  106.  *  [re]open request on channel, we received the request so we are the
  107.  *  server.
  108.  */
  109.  
  110. void
  111. SMGetNoChannel(db, rid, rn, did)
  112. DBHan *db;
  113. record_t rid;
  114. RegNode *rn;
  115. long did;
  116. {
  117.     long size;
  118.     long type;
  119.     CtlNode *cn;
  120.  
  121.     bmslog("Open-Get", "Received GET from %s:%d", rn->rn_Node.ln_Name, did);
  122.  
  123.     if ((cn = FindCtlNode(rn, 0, did)) == NULL) {
  124.     cn = AllocCtlNode(rn);
  125.     InitCommRemoteSide(SMACH_GET, rn, &cn->cn_Comm, did);
  126.     SetCommMessage(&cn->cn_Comm, "OUTGOING");
  127.     cn->cn_Comm.com_State = 8;
  128.  
  129.     for (rid = ScanSubRecord(db, rid, &size, &type); rid; rid = ScanNextRecord(db, rid, &size, &type)) {
  130.         switch(type) {
  131.         case DBTYPE_BMS_NODE:   /*    file or dir (local only)    */
  132.         {
  133.             FileNode *fn = AllocFileNode(cn);
  134.             fn->fn_LocFileName = LoadRecord(db, rid, NULL, size + 1);
  135.             GetFileInfo(rn, cn, fn, &cn->cn_FileList);
  136.         }
  137.         break;
  138.         }
  139.     }
  140.     AssignFileNumbers(&cn->cn_FileList);
  141.     } else {
  142.     switch(cn->cn_Comm.com_State) {
  143.     case 8:
  144.     case 9:
  145.         break;
  146.     case 10:
  147.         cn->cn_Comm.com_State = 9;
  148.         cn->cn_Flags |= GF_MODIFIED;
  149.         break;
  150.     default:
  151.         break;
  152.     }
  153.     }
  154. }
  155.  
  156. /*
  157.  *  Receive data on this channel, either data we requested or controls
  158.  *  for the channel
  159.  */
  160.  
  161. void
  162. SMRxGet(db, rid, rn, cn)
  163. DBHan *db;
  164. record_t rid;
  165. RegNode *rn;
  166. CtlNode *cn;
  167. {
  168.     long size;
  169.     long type;
  170.  
  171.     cn->cn_Flags |= GF_MODIFIED;
  172.     cn->cn_Comm.com_Flags |= GF_MODIFIED;
  173.  
  174.     switch(cn->cn_Comm.com_State) {
  175.  
  176.     /*
  177.      *    CLIENT - RECEIVE DATA
  178.      */
  179.  
  180.     case 0:    /*  shouldn't get receive data  */
  181.     break;
  182.     case 1:    /*  expect acknowledge or data    */
  183.     case 2:    /*  expect acknowledge or data    */
  184.     case 3:    /*  done (all data received), handle incomming as redundant */
  185.     for (rid = ScanSubRecord(db, rid, &size, &type); rid; rid = ScanNextRecord(db, rid, &size, &type)) {
  186.         char *ptr = LoadRecord(db, rid, NULL, size + 1);
  187.  
  188.         if (cn->cn_Comm.com_State == 1) {
  189.         cn->cn_Comm.com_State = 2;
  190.         cn->cn_Comm.com_Retries = 0;
  191.         SetCommMessage(&cn->cn_Comm, "RECEIVE");
  192.         }
  193.         SetCommTimeout(&cn->cn_Comm, ONEWEEK);
  194.  
  195.         switch(type) {
  196.         case DBTYPE_BMS_ACK_OPEN_GET:
  197.         /*
  198.          *  If got ack, move to next state
  199.          */
  200.  
  201.         break;
  202.         case DBTYPE_BMS_FILE:
  203.         {
  204.             record_t sid;
  205.             FileNode *fn = MakeFindRecFileNode(cn, ptr, 0);  /*  sets GF_MODIFIED    */
  206.             FileChunk fc;
  207.             char *file_data = NULL;
  208.             char *tmp;
  209.  
  210.             clrmem(&fc, sizeof(fc));
  211.             fn->fn_Flags |= GF_MODIFIED;
  212.  
  213.             for (sid = ScanSubRecord(db, rid, &size, &type); sid; sid = ScanNextRecord(db, sid, &size, &type)) {
  214.             char *data = LoadRecord(db, sid, NULL, size + 1);
  215.  
  216.             switch(type) {
  217.             case DBTYPE_BMS_FILENO:
  218.                 fn->fn_FileNo = strtol(data, NULL, 0);
  219.                 break;
  220.             case DBTYPE_BMS_TTLFILES:
  221.                 fn->fn_Files = strtol(data, NULL, 0);
  222.                 break;
  223.             case DBTYPE_BMS_NOFILE:
  224.                 fn->fn_Flags |= FNF_BAD | FNF_SIZE;
  225.                 puts("FIL BAD");
  226.                 break;
  227.             case DBTYPE_BMS_NOACCESS:
  228.                 fn->fn_Flags |= FNF_ILLEGAL | FNF_SIZE;
  229.                 puts("FIL NO ACCESS");
  230.                 break;
  231.             case DBTYPE_BMS_SIZE:
  232.                 fn->fn_Size = strtol(data, NULL, 0);
  233.                 fn->fn_Flags |= FNF_SIZE;
  234.                 break;
  235.             case DBTYPE_BMS_OFFSET:     /*  before DATA       */
  236.                 fc.fc_Offset = strtol(data, NULL, 0);
  237.                 break;
  238.             case DBTYPE_BMS_AMI_PROTECTION: /*  w/offset 0 data   */
  239.                 fn->fn_Protection = strtol(data, NULL, 0);
  240.                 break;
  241.             case DBTYPE_BMS_AMI_TIMESTAMP:    /*  w/offset 0 data   */
  242.                 fn->fn_Date[0] = strtol(data, &tmp, 0);
  243.                 fn->fn_Date[1] = strtol(tmp + 1, &tmp, 0);
  244.                 fn->fn_Date[2] = strtol(tmp + 2, &tmp, 0);
  245.                 break;
  246.             case DBTYPE_BMS_AMI_COMMENT:    /*  w/offset 0 data   */
  247.                 if (fn->fn_Comment)
  248.                 zfree(fn->fn_Comment);
  249.                 fn->fn_Comment = data;
  250.                 data = NULL;
  251.                 break;
  252.             case DBTYPE_BMS_DATA:        /*  data          */
  253.                 fc.fc_Bytes = size;
  254.                 file_data = data;
  255.                 data = NULL;
  256.                 break;
  257.             }
  258.             if (data)
  259.                 zfree(data);
  260.             }
  261.  
  262.             /*
  263.              *    Incorporate what we got
  264.              */
  265.  
  266.             IncorporateRecFileNode(cn, fn, &fc, file_data);
  267.             if (file_data)
  268.             zfree(file_data);
  269.         }
  270.         break;
  271.         case DBTYPE_BMS_DIR:
  272.         {
  273.             record_t sid;
  274.             FileNode *fn = MakeFindRecFileNode(cn, ptr, 1);
  275.             char *tmp;
  276.  
  277.             fn->fn_Flags |= FNF_DONE | FNF_DIR | GF_MODIFIED;
  278.  
  279.             for (sid = ScanSubRecord(db, rid, &size, &type); sid; sid = ScanNextRecord(db, sid, &size, &type)) {
  280.             char *data = LoadRecord(db, sid, NULL, size + 1);
  281.  
  282.             switch(type) {
  283.             case DBTYPE_BMS_FILENO:
  284.                 fn->fn_FileNo = strtol(data, NULL, 0);
  285.                 break;
  286.             case DBTYPE_BMS_TTLFILES:
  287.                 fn->fn_Files = strtol(data, NULL, 0);
  288.                 if (DebugOpt)
  289.                 printf("Total %d\n", fn->fn_Files);
  290.                 break;
  291.             case DBTYPE_BMS_NOFILE:
  292.                 fn->fn_Flags |= FNF_BAD | FNF_SIZE;
  293.                 break;
  294.             case DBTYPE_BMS_NOACCESS:
  295.                 fn->fn_Flags |= FNF_ILLEGAL | FNF_SIZE;
  296.                 break;
  297.             case DBTYPE_BMS_AMI_PROTECTION: /*  w/offset 0 data   */
  298.                 fn->fn_Protection = strtol(data, NULL, 0);
  299.                 break;
  300.             case DBTYPE_BMS_AMI_TIMESTAMP:    /*  w/offset 0 data   */
  301.                 fn->fn_Date[0] = strtol(data, &tmp, 0);
  302.                 fn->fn_Date[1] = strtol(tmp + 1, &tmp, 0);
  303.                 fn->fn_Date[2] = strtol(tmp + 2, &tmp, 0);
  304.                 break;
  305.             case DBTYPE_BMS_AMI_COMMENT:    /*  w/offset 0 data   */
  306.                 fn->fn_Comment = data;
  307.                 data = NULL;
  308.                 break;
  309.             }
  310.             if (data)
  311.                 zfree(data);
  312.             }
  313.  
  314.             /*
  315.              *    Incorporate what we got
  316.              */
  317.  
  318.             IncorporateRecFileNode(cn, fn, NULL, NULL);
  319.         }
  320.         break;
  321.         case DBTYPE_BMS_DEQUEUE:
  322.         EnterDequeueState(rn, cn, &cn->cn_Comm, (ptr) ? ptr : "Remote Dequeued Transfer", 0);
  323.         break;
  324.         case DBTYPE_BMS_DONE:
  325.         /*
  326.          *  Shorten retry timeout when we receive a done
  327.          */
  328.         bmslog("Get", "Received DONE!");
  329.         SetCommTimeout(&cn->cn_Comm, ONEDAY);
  330.         break;
  331.         case DBTYPE_BMS_STOP:
  332.         break;
  333.         case DBTYPE_BMS_START:
  334.         break;
  335.         }
  336.         zfree(ptr);
  337.     }
  338.  
  339.     /*
  340.      *  Check if done
  341.      */
  342.  
  343.     if (CheckGetDone(rn, cn) == 0) {
  344.         SetCommTimeout(&cn->cn_Comm, ONEWEEK * 2);
  345.         SetCommMessage(&cn->cn_Comm, "RXDONE-WAIT");
  346.         if (cn->cn_Comm.com_State != 3)
  347.         SendMailResults(rn, cn, "GET OPERATION COMPLETED");
  348.         cn->cn_Comm.com_State = 3;
  349.  
  350.     }
  351.     break;
  352.  
  353.     /*
  354.      *    SERVER - RECEIVE CONTROLS WHILE WE ARE SENDING DATA
  355.      */
  356.  
  357.     case 8:
  358.     case 9:
  359.     case 10:
  360.     case 11:
  361.     for (rid = ScanSubRecord(db, rid, &size, &type); rid; rid = ScanNextRecord(db, rid, &size, &type)) {
  362.         char *ptr = LoadRecord(db, rid, NULL, size + 1);
  363.  
  364.         switch(type) {
  365.         case DBTYPE_BMS_DEQUEUE:
  366.         EnterDequeueState(rn, cn, &cn->cn_Comm, (ptr) ? ptr : "Remote Dequeued Transfer", 0);
  367.         break;
  368.         case DBTYPE_BMS_DONE:   /* XXX does not occur   */
  369.         break;
  370.         case DBTYPE_BMS_STOP:
  371.         break;
  372.         case DBTYPE_BMS_START:
  373.         break;
  374.         case DBTYPE_BMS_RETRY:
  375.         ProcessRetryPacket(db, rid, rn, cn);
  376.         break;
  377.         }
  378.         zfree(ptr);
  379.     }
  380.     break;
  381.     }
  382. }
  383.  
  384. void
  385. SMTxGet(db, rid, rn, cn)
  386. DBHan *db;
  387. record_t rid;
  388. RegNode *rn;
  389. CtlNode *cn;
  390. {
  391.     cn->cn_Comm.com_Flags |= GF_MODIFIED;
  392.     cn->cn_Flags |= GF_MODIFIED;
  393.  
  394.     if (DebugOpt)
  395.     printf("TXGET STATE %d\n", cn->cn_Comm.com_State);
  396.  
  397.     switch(cn->cn_Comm.com_State) {
  398.  
  399.     /*
  400.      *    CLIENT    (we want to get a file)
  401.      */
  402.  
  403.     case 0:    /*  initialize operation, send request    */
  404.     /*
  405.      *  Initialization for our get operation involves nothing
  406.      */
  407.     ++cn->cn_Comm.com_State;
  408.     /* fall through */
  409.  
  410.     case 1:    /*  [re]send request            */
  411.     /*
  412.      *  Our request is sent on the registration channel, the
  413.      *  acknowledge will occur on our private ctl channel
  414.      *
  415.      *  CHANNEL
  416.      *    BMS_OPEN_GET(source_chan_no)
  417.      *        BMS_NODE(file/dirName)
  418.      *        ...
  419.      *
  420.      */
  421.  
  422.     if (++cn->cn_Comm.com_Retries == 4) {
  423.         EnterDequeueState(rn, cn, &cn->cn_Comm, "Initiate, Retries Exceeded", 0);
  424.         break;
  425.     }
  426.  
  427.     {
  428.         char buf[64];
  429.         FileNode *fn;
  430.  
  431.         sprintf(buf, "0x%lx", cn->cn_Comm.com_SID);
  432.         rid = WriteBMSChannelRecord(db, 0, rid, &rn->rn_Comm);
  433.         rid = WriteRecord(db, rid, 0, DBTYPE_BMS_OPEN_GET, buf, strlen(buf));
  434.  
  435.         for (fn = GetHead(&cn->cn_FileList); fn; fn = GetSucc(&fn->fn_Node)) {
  436.         WriteRecord(db, rid, 0, DBTYPE_BMS_NODE, fn->fn_RemFileName, strlen(fn->fn_RemFileName));
  437.         }
  438.     }
  439.     SetCommTimeout(&cn->cn_Comm, ONEDAY * 4);
  440.     SetCommMessage(&cn->cn_Comm, "GET-REQ-SENT");
  441.     break;
  442.     case 2:    /*  receiver timed out, send a retry    */
  443.     /*
  444.      *  We timed out expecting more data, send a retry.  We got
  445.      *  some data.
  446.      */
  447.  
  448.     if (++cn->cn_Comm.com_Retries == 4) {
  449.         EnterDequeueState(rn, cn, &cn->cn_Comm, "Retries Exceeded", 0);
  450.         break;
  451.     }
  452.     SetCommTimeout(&cn->cn_Comm, ONEWEEK);
  453.     SetCommMessage(&cn->cn_Comm, "GET-TIMEOUT-SEND-RETRY");
  454.     ConstructRetryPacket(db, rid, rn, cn);
  455.     break;
  456.     case 3:    /*  done state, no operation        */
  457.     /*
  458.      *  Timeout has already been set for two weeks by receive side
  459.      */
  460.     break;
  461.  
  462.     /*
  463.      *    SERVER
  464.      */
  465.  
  466.     case 8:    /*  initialize operation            */
  467.     ++cn->cn_Comm.com_State;
  468.     /* fall through */
  469.     case 9:    /*  send acknowledge                */
  470.     /*
  471.      *  Send acknowledge, calculate bytes remaining,
  472.      *  set request size and ALERT flag again.  We can't
  473.      *  fall through because we do not know how much we
  474.      *  are allowed to send.
  475.      *
  476.      *  XXX what about files that are not available?
  477.      *  XXX include text msg in acknowledgement (bytes being sent...)
  478.      *  XXX up above, request should contain maximum bpd/bpw
  479.      */
  480.  
  481.     rid = WriteBMSChannelRecord(db, 0, rid, &cn->cn_Comm);
  482.     rid = WriteRecord(db, rid, 0, DBTYPE_BMS_ACK_OPEN_GET, NULL, 0);
  483.     CalculateBytesToSend(rn, cn);
  484.     ++cn->cn_Comm.com_State;
  485.     break;
  486.     case 10:    /*  send data (or retries)                  */
  487.     /*
  488.      *  We are allowed to send cn->cn_Comm.com_TxOk bytes
  489.      */
  490.  
  491.     rid = WriteBMSChannelRecord(db, 0, rid, &cn->cn_Comm);
  492.     {
  493.         FileNode  *fn;
  494.         FileChunk *fc;
  495.         char *ptr;
  496.         record_t sid;
  497.         char buf[64];
  498.  
  499.         while (ptr = SelectChunkForSend(rn, cn, &fn, &fc)) {
  500.         sid = WriteRecord(db, rid, 0, ((fc) ? DBTYPE_BMS_FILE : DBTYPE_BMS_DIR), fn->fn_LocFileName, strlen(fn->fn_LocFileName));
  501.  
  502.         /*
  503.          *  File number & total # of files always sent as this is
  504.          *  how the receiver determines if/when it has gotten
  505.          *  everything.
  506.          */
  507.  
  508.         sprintf(buf, "%d", fn->fn_FileNo);
  509.         WriteRecord(db, sid, 0, DBTYPE_BMS_FILENO, buf, strlen(buf));
  510.         sprintf(buf, "%d", fn->fn_Files);
  511.         WriteRecord(db, sid, 0, DBTYPE_BMS_TTLFILES, buf, strlen(buf));
  512.  
  513.         /*
  514.          *  For a directory (fc == NULL) or the first chunk of a
  515.          *  file (offset == 0), send protection, timestamp, and
  516.          *  comment information.
  517.          *
  518.          *  For a file send additional info -- the data chunk!
  519.          */
  520.  
  521.         if (DebugOpt)
  522.             printf("SEND FILE NODE FLAGS %04lx\n", fn->fn_Flags);
  523.  
  524.         if (fn->fn_Flags & FNF_BAD) {
  525.             WriteRecord(db, sid, 0, DBTYPE_BMS_NOFILE, NULL, 0);
  526.         } else if (fn->fn_Flags & FNF_ILLEGAL) {
  527.             WriteRecord(db, sid, 0, DBTYPE_BMS_NOACCESS, NULL, 0);
  528.         } else {
  529.             if ((fc && fc->fc_Offset == 0) || fc == NULL) {
  530.             sprintf(buf, "0x%lx", fn->fn_Protection);
  531.             WriteRecord(db, sid, 0, DBTYPE_BMS_AMI_PROTECTION, buf, strlen(buf));
  532.             sprintf(buf, "0x%lx:0x%lx:0x%lx", fn->fn_Date[0], fn->fn_Date[1], fn->fn_Date[2]);
  533.             WriteRecord(db, sid, 0, DBTYPE_BMS_AMI_TIMESTAMP, buf, strlen(buf));
  534.             if (fn->fn_Comment)
  535.                 WriteRecord(db, sid, 0, DBTYPE_BMS_AMI_COMMENT, fn->fn_Comment, strlen(fn->fn_Comment));
  536.             }
  537.             if (fc) {
  538.             if (fc->fc_Offset == 0) {
  539.                 sprintf(buf, "%d",fn->fn_Size);
  540.                 WriteRecord(db, sid, 0, DBTYPE_BMS_SIZE, buf, strlen(buf));
  541.             } else {
  542.                 sprintf(buf, "%d", fc->fc_Offset);
  543.                 WriteRecord(db, sid, 0, DBTYPE_BMS_OFFSET, buf, strlen(buf));
  544.             }
  545.             WriteRecord(db, sid, 0, DBTYPE_BMS_DATA, ptr, fc->fc_Bytes);
  546.             }
  547.         }
  548.         zfree(ptr);
  549.         }
  550.     }
  551.     CalculateBytesToSend(rn, cn);
  552.     if (cn->cn_Flags & CNF_DONE) {
  553.         cn->cn_Comm.com_Flags |= GF_ALERT;
  554.         ++cn->cn_Comm.com_State;
  555.         SetCommMessage(&cn->cn_Comm, "OUTGOING-COMPLETE");
  556.     }
  557.     break;
  558.     case 11:    /*  send done, then wait            */
  559.     rid = WriteBMSChannelRecord(db, 0, rid, &cn->cn_Comm);
  560.     WriteRecord(db, rid, 0, DBTYPE_BMS_DONE, NULL, 0);
  561.     SetCommTimeout(&cn->cn_Comm, ONEWEEK * 2);
  562.     SetCommMessage(&cn->cn_Comm, "OUTGOING-DONE-WAIT");
  563.     cn->cn_Comm.com_TxReq = 0;
  564.     cn->cn_Comm.com_TxOk  = 0;
  565.     break;
  566.     }
  567. }
  568.  
  569. void
  570. SMToGet(db, rid, rn, cn)
  571. DBHan *db;
  572. record_t rid;
  573. RegNode *rn;
  574. CtlNode *cn;
  575. {
  576.     switch(cn->cn_Comm.com_State) {
  577.  
  578.     /*
  579.      *    CLIENT
  580.      */
  581.  
  582.     case 0:    /*  timeout on initiate shouldn't happen    */
  583.     case 1:    /*  timeout on initiate, retry initiate     */
  584.     case 2:    /*  timeout on receive data, send retry     */
  585.     SMTxGet(db, rid, rn, cn);
  586.     break;
  587.     case 3:    /*  done state timed out, clear structure   */
  588.     cn->cn_Flags |= GF_DELETED | GF_MODIFIED;
  589.     break;
  590.  
  591.     /*
  592.      *    SERVER
  593.      */
  594.  
  595.     case 8:    /*  should not happen                */
  596.     case 9:    /*  should not happen                */
  597.     case 10:    /*  should not happen                */
  598.     case 11:    /*  done state timed out, clear structure   */
  599.     cn->cn_Flags |= GF_DELETED | GF_MODIFIED;
  600.     break;
  601.     }
  602. }
  603.  
  604. /*
  605.  *  Check that the GET operation has been completed.  Find our master
  606.  *  node (FNF_MASTER)
  607.  */
  608.  
  609. bool_t
  610. CheckGetDone(rn, cn)
  611. RegNode *rn;
  612. CtlNode *cn;
  613. {
  614.     FileNode *fn;
  615.     FileNode *fn2;
  616.     long cnt = 0;
  617.     long n = 0;
  618.  
  619.     for (fn = GetHead(&cn->cn_FileList); fn; fn = GetSucc(&fn->fn_Node)) {
  620.     if (n == 0)
  621.         n = fn->fn_Files;
  622.     if (fn->fn_Flags & FNF_DONE)
  623.         ++cnt;
  624.     }
  625.     if (n == 0)     /*  haven't gotten a file count from remote */
  626.     return(-1);
  627.     if (n == cnt)   /*  all files are competely in              */
  628.     return(0);
  629.     return(-1);
  630. }
  631.  
  632. /*
  633.  *  construct a retry packet    (DBTPE_BMS_RETRY)
  634.  *
  635.  *  - If we don't know how many files we expect then send BMS_TTLFILES=0
  636.  *  - Otherwise send BMS_FILENO for each file we do NOT have, with a sub
  637.  *    tree indicating which *parts* of the file we do not have
  638.  *
  639.  *    BMS_FILENO
  640.  *    BMS_RETRY_CHUNK
  641.  *            contains offset/bytes of chunks we did NOT receive
  642.  *            format: offset:bytes
  643.  */
  644.  
  645.  
  646. void
  647. ConstructRetryPacket(db, rid, rn, cn)
  648. DBHan *db;
  649. record_t rid;
  650. RegNode *rn;
  651. CtlNode *cn;
  652. {
  653.     char buf[64];
  654.     FileNode *fn;
  655.     long ttlFiles = 0;
  656.     long n;
  657.  
  658.     rid = WriteBMSChannelRecord(db, 0, rid, &cn->cn_Comm);
  659.     rid = WriteRecord(db, rid, 0, DBTYPE_BMS_RETRY, NULL, 0);
  660.  
  661.     /*
  662.      *    Attempt to find the total number of files we expect.  If we
  663.      *    do not know then send BMS_TTLFILES=0
  664.      */
  665.  
  666.     for (fn = GetHead(&cn->cn_FileList); fn; fn = GetSucc(&fn->fn_Node)) {
  667.     if (fn->fn_Files) {
  668.         ttlFiles = fn->fn_Files;
  669.         break;
  670.     }
  671.     }
  672.  
  673.     if (ttlFiles == 0) {
  674.     WriteRecord(db, rid, 0, DBTYPE_BMS_TTLFILES, "0", 1);
  675.     return;
  676.     }
  677.  
  678.     /*
  679.      *    We know how many files we expect, now look for the file ID's
  680.      *    for each file.
  681.      */
  682.  
  683.     for (n = 0; n < ttlFiles; ++n) {
  684.     record_t sid;
  685.     FileChunk *fc;
  686.     long offset;
  687.  
  688.     for (fn = GetHead(&cn->cn_FileList); fn; fn = GetSucc(&fn->fn_Node)) {
  689.         if (fn->fn_FileNo == n)
  690.         break;
  691.     }
  692.  
  693.     /*
  694.      *  We don't have file number <n> at all!
  695.      */
  696.  
  697.     if (fn == NULL) {
  698.         sprintf(buf, "%d", n);
  699.         WriteRecord(db, rid, 0, DBTYPE_BMS_FILENO, buf, strlen(buf));
  700.         continue;
  701.     }
  702.  
  703.     /*
  704.      *  We have file number <n>, do we have all of it?
  705.      */
  706.  
  707.     if (fn->fn_Flags & FNF_DONE)
  708.         continue;
  709.  
  710.     /*
  711.      *  No, tell remote which chunks we need.  Write an initial
  712.      *  dummy chunk record to indicate to the remote that we have
  713.      *  <part> of the file, even if due to a fluke we wind up
  714.      *  having all of it (e.g. FNF_DONE was not set properly) and
  715.      *  thus do not generate any additional chunks.
  716.      */
  717.  
  718.     sprintf(buf, "%d", n);
  719.     sid = WriteRecord(db, rid, 0, DBTYPE_BMS_FILENO, buf, strlen(buf));
  720.  
  721.     sprintf(buf, "0:0");
  722.     WriteRecord(db, sid, 0, DBTYPE_BMS_RETRY_CHUNK, buf, strlen(buf));
  723.  
  724.     offset = 0;
  725.     for (fc = GetHead(&fn->fn_ChunkList); fc; fc = GetSucc(&fc->fc_Node)) {
  726.         if (fc->fc_Flags & GF_DELETED)
  727.         continue;
  728.         if (offset < fc->fc_Offset) {
  729.         bmslog("TxRetry", "Chunk %s(%d-%d)", fn->fn_RemFileName, offset, fc->fc_Offset);
  730.         sprintf(buf, "%d:%d", offset, fc->fc_Offset - offset);
  731.         WriteRecord(db, sid, 0, DBTYPE_BMS_RETRY_CHUNK, buf, strlen(buf));
  732.         }
  733.         offset = fc->fc_Offset + fc->fc_Bytes;
  734.     }
  735.     if (offset < fn->fn_Size) {
  736.         bmslog("TxRetry", "Chunk %s(%d-%d)", fn->fn_RemFileName, offset, fn->fn_Size);
  737.         sprintf(buf, "%d:%d", offset, fn->fn_Size - offset);
  738.         WriteRecord(db, sid, 0, DBTYPE_BMS_RETRY_CHUNK, buf, strlen(buf));
  739.     }
  740.     }
  741. }
  742.  
  743.  
  744.  
  745. /*
  746.  *  Process a retry packet    (DBTPE_BMS_RETRY)
  747.  *
  748.  *  - If we get BMS_TTLFILES=0 (value ignored) we requeue the entire
  749.  *    file set.
  750.  *
  751.  *  - if we get BMS_FILENO with no subtree BMS_RETRY_CHUNK we requeue
  752.  *    that entire file.
  753.  *
  754.  *  - if we get BMS_FILENO with BMS_RETRY_CHUNK we requeue the specified
  755.  *    chunks.
  756.  */
  757.  
  758.  
  759. void
  760. ProcessRetryPacket(db, rid, rn, cn)
  761. DBHan *db;
  762. record_t rid;
  763. RegNode *rn;
  764. CtlNode *cn;
  765. {
  766.     FileNode *fn;
  767.     record_t sid;
  768.     long size;
  769.     long type;
  770.     short requeue = 0;
  771.     char *ptr;
  772.  
  773.     for (rid = ScanSubRecord(db, rid, &size, &type); rid; rid = ScanNextRecord(db, rid, &size, &type)) {
  774.     switch(type) {
  775.     case DBTYPE_BMS_TTLFILES:
  776.         requeue = 1;
  777.         for (fn = GetHead(&cn->cn_FileList); fn; fn = GetSucc(&fn->fn_Node)) {
  778.         FileChunk *fc;
  779.         for (fc = GetHead(&fn->fn_ChunkList); fc; fc = GetSucc(&fc->fc_Node))
  780.             fc->fc_Flags |= GF_DELETED;
  781.         fn->fn_Flags &= ~FNF_DONE;
  782.         fn->fn_Flags |= GF_MODIFIED;
  783.         }
  784.         break;
  785.     case DBTYPE_BMS_FILENO:
  786.         requeue = 1;
  787.         ptr = LoadRecord(db, rid, NULL, size + 1);
  788.  
  789.         for (fn = GetHead(&cn->cn_FileList); fn; fn = GetSucc(&fn->fn_Node)) {
  790.         if (fn->fn_FileNo == strtol(ptr, NULL, 0)) {
  791.             short chunks = 0;
  792.  
  793.             /*
  794.              *    found node, remote specified chunks (if any)
  795.              */
  796.             for (sid = ScanSubRecord(db, rid, &size, &type); sid; sid = ScanNextRecord(db, sid, &size, &type)) {
  797.             long offset = 0;
  798.             long bytes = 0;
  799.             FileChunk *fc;
  800.  
  801.             switch(type) {
  802.             case DBTYPE_BMS_RETRY_CHUNK:
  803.                 ++chunks;
  804.                 {
  805.                 char *str = LoadRecord(db, sid, NULL, size + 2);
  806.                 char *tmp;
  807.  
  808.                 offset = strtol(str, &tmp, 0);
  809.                 bytes = strtol(tmp + 1, &tmp, 0);
  810.                 zfree(str);
  811.                 }
  812.  
  813.                 /*
  814.                  *    Scan for chunks to reclaim.  To ease calculations
  815.                  *    remove the entire chunk if it overlaps at all.
  816.                  *    since the file was sent in the granularity of the
  817.                  *    chunks anyway this will be dead on 99% of the time.
  818.                  */
  819.  
  820.                 for (fc = GetHead(&fn->fn_ChunkList); fc; fc = GetSucc(&fc->fc_Node)) {
  821.                 if (fc->fc_Flags & GF_DELETED)
  822.                     continue;
  823.                 if (bytes) {
  824.                     if (offset < fc->fc_Offset + fc->fc_Bytes && offset + bytes > fc->fc_Offset) {
  825.                     fc->fc_Flags |= GF_DELETED;
  826.                     fn->fn_Flags |= GF_MODIFIED;
  827.                     fn->fn_Flags &= ~FNF_DONE;
  828.                     bmslog("RxRetry", "Chunk %s(%d-%d)", fn->fn_LocFileName, fc->fc_Offset, fc->fc_Offset + fc->fc_Bytes);
  829.                     }
  830.                 }
  831.                 }
  832.                 break;
  833.             }
  834.             }
  835.             if (chunks == 0) {
  836.             FileChunk *fc;
  837.             for (fc = GetHead(&fn->fn_ChunkList); fc; fc = GetSucc(&fc->fc_Node)) {
  838.                 fc->fc_Flags |= GF_DELETED;
  839.                 fn->fn_Flags |= GF_MODIFIED;
  840.                 fn->fn_Flags &= ~FNF_DONE;
  841.             }
  842.             }
  843.         }
  844.         }
  845.         zfree(ptr);
  846.         break;
  847.     }
  848.     }
  849.  
  850.     /*
  851.      *    If requeue then go back to proper state and hit the transmitter
  852.      */
  853.  
  854.     if (requeue) {
  855.     cn->cn_Comm.com_Flags |= GF_ALERT | GF_MODIFIED;
  856.     cn->cn_Flags &= ~CNF_DONE;
  857.     cn->cn_Flags |= GF_MODIFIED;
  858.     if (cn->cn_Comm.com_State == 11) {
  859.         cn->cn_Comm.com_State = 10;
  860.     }
  861.     SetCommMessage(&cn->cn_Comm, "OUTGOING-RETRY");
  862.     SetCommTimeout(&cn->cn_Comm, ONEWEEK * 2);
  863.     }
  864. }
  865.  
  866.