

server side : interpretting what comes in ...

========================
| getConsumerRequest() |
========================

In AcceptIP (from srvip.c, discussed in SystemCycle.txt)
getConsumerIPAddress() is regularly called for 
- something on the UDP server socket
- something on a single threaded TCP server socket

getConsumerIPAddress() will call getConsumerRequest() 


Multi-threaded TCP incoming request is handled on its own thread and will call
processTCPDataStream()

======================
| getConsumerRequest |
======================
///
/// this routine is far too long 
/// maybe it's a good time to split it up ?
///
int getConsumerRequest(ClnHdr *caller,BYTE *buf,int packetsize)
{
  ...
  /// byte swap/unpack the request header if necessary :
  /// Note: release 3 and release 4 headers will be mapped to a (modern) release 5 header !
  ///
  hprot = prepIncomingRequestHdr(&rh, buf);
  if (hprot < 0) ccerr(illegal_protocol);
  /// an unrecognized header protocol ...
  bytesread = PAYLOADHDR_SIZE(hprot);
  totalsize = rh.totalSizeInBytes;
  caller->tineProtocol = hprot;

  /* map incoming tineProtocol to the most modern : at the moment 6 */
  /// now swap/unpack the subscription header :
  ///
  if ((cc=prepIncomingSubRequestHdr(hprot, &subhdr, &buf[bytesread], &swallowOnErr)) != 0) ccout(cc);
  ...
  if (subhdr.extsize > 0 && subhdr.blknum == 1)
  {  /* extended (device name) string set (comes with first block) */
     /// really long (> 64 chars and up to 1024 chars) device names are allowed
	 ...
  }
  replaceContractAliasNames(&subhdr.contract);
  ...
  while (bytesread < totalsize && bytesread < (UINT32)packetsize) 
  { /* unpack request(s) */
    /// start off with totalsize = the header size and work our way thru the payload ...
    ...
    if ((fmtoutsize=fmtsizeof(subhdr.contract.EqmFormatOut)) > 0)
    {
	  /// caller wants something back 
	  /// check a few things ...
      ...
    }
    else 
    { /* either CF_NULL or not a valid format */
      if (subhdr.contract.EqmFormatOut != BFMT(CF_NULL)) ccerr(illegal_format);
      doutsize = 0;
      subhdr.contract.EqmFormatOut = BFMT(CF_NULL);
      subhdr.contract.EqmSizeOut = 0;
    }
    ...
    if ((fmtinsize=fmtsizeof(subhdr.contract.EqmFormatIn)) != 0) 
    { /* data input */
	  /// caller is sending input data ...
      ...
      dinsize = subhdr.contract.EqmSizeIn*fmtinsize;
      ...
      if (dinsize > subhdr.mtu)
      { /* incoming long datagram */
        if (subhdr.numblks == 1) ccerr(data_size_mismatch); /* sanity check */
        /// we're being sent more data than fits in one packet MTU 
		/// so we have to collect it !
		....
      }
      ...
      PrepSubData(dbuf,n,subhdr.contract.EqmFormatIn,RTOL);
	  /// we've got/collected all of the incoming data
	  /// do any necessary byte swapping ...
    }
    else 
    { /* no input data -> make sure the input tag is zeroed out */
      ...
    }
    ...
    if (LFMT(subhdr.contract.EqmFormatOut) == CF_DEFAULT)
    { /* caller wants default settings */
      if ((cc=GetDefaultFormatAndSize(&subhdr.contract,stsstr)) != 0) ccerr(cc);
      ...
    }
    ...
    for (clid=0; clid<ncontracts; clid++)            
    { /* is contract in list ? */
      ...
	  /// are we already doing this ?
    }
    if (clid == ncontracts)                    
    { /* not in list: add it */
      isNewContract = TRUE;
      ...
	  /// it's a new request ! is it legal ?
      if ((cc=IsIllegalProperty(cl->contract.EqmName,
                                cl->contract.EqmProperty,
                                cl->contract.EqmDeviceName,
                                cln,acc,&cl->exp,&cl->prp,stsstr)) != 0) ccerr(cc);
#     if !defined(VXWORKS) && !defined(DOS) && !defined(FS_RDONLY)
      if (PutInCmdLog(cl)) 
      { /* log it if this isn't a lock token renewal */
        /// very useful !
		...
      }
#     endif
      if (bmode > CM_SINGLE)
      { /* persistent link -> check a few things */
	    /// not just a one-time 'execute' ...
        if (IsMcaElement(&cl->contract,cl->prp,&idx,&dlen,&dnam) && 
            !IsMetaProperty(cl->contract.EqmProperty,NULL,NULL,FALSE))
        { /* check for mca access */
          ...
		  /// is this a request for an MCA element ?
        }
        ...
      }
      ...
      if (dinsize > 0 && dbuf) prepArrayInput(cl,dbuf,(size_t)dinsize);
	  /// special preparation for those non-fixed-length data types (e.g. CF_STRING, CF_AIMAGE, CF_ASPECTRUM
	  /// otherwise just a memcpy into the official contract table entry ...
      ...
    }
    else
    { /* contract was found in the list */
	  /// NOT a new contract (we're already doing this)
	  /// but there are still a few things to check ...
      ...
    }
    if (cl->expired == CONTRACT_EXPIRED && !(subhdr.mode & CM_RETRY)) 
    { /* is a new link attaching to an expired contract ? */
	  /// found the contract, but it's expired but not yet 'garbage collected'
	  /// reactivate ...
      ...
    }
    ...
    if (subhdr.mode & CM_MCAST)
    { /* is broadcast or multicast request ? */
	  /// contract results (payload) should be sent as a multicast to 'everybody' ...
      ...
    }
    if (WaitForSystemMutex(hClientTableMutex,-1) == 0) 
    { /* is client in contractList?    */
	  /// is this contract being deliverd to this client already or not ?
      for (c=cl->clnLst; c!=NULL && c->client!=cln; c=c->next);
      if (c == NULL) 
      { /* no, add to list */
        ...
      }
      else if (!cl->deliveryPending)
      {
        if (cl->nclients == 0)
        { /* this shouldn't happen */
          ...
        }
        resetClnCounters = TRUE;
      }
      ReleaseSystemMutex(hClientTableMutex); 
    }
    ...
    c->status.mode = subhdr.mode;
    switch (BASEMODE(subhdr.mode))
    {
      case CM_SINGLE :
        gSingleLinkCount++;
        c->status.counter = 1;
        if (cl->expired == CONTRACT_ACTIVE && cl->runInSeparateThread && !isNewClient)
        { /* impatient client */
          ccerr(0);
        }
        if (!(cl->expired == CONTRACT_EXPIRED && c->status.isRetry))
        { /* normal case: not expired and not a retry */
          if (!resendCurrentData) cl->lasttime.tv_sec = PRP_REQUEST_SIGNAL;  
        }
        else  
        { /* a RETRY or an expired contract */
          if (cl->expired == CONTRACT_EXPIRED) 
          { /* expired: should just resend last data, but reset FIRST and LAST just in case */
            cl->lasttime.tv_sec = (UINT32)time(NULL);
            cl->contract.EqmAccess |= (CA_FIRST|CA_LAST);
          }
          if (cl->dataSizeOut < cl->contract.EqmSizeOut)
            renegotiateContract(cl,cl->returnCode);
        }
        if (cl->pollingInterval == 0) cl->pollingInterval = c->status.pollingInterval;
        if (cl->nclients == 1)
        { /* turn on LAST bit */
          cl->contract.EqmAccess |= CA_LAST;
        }
        sendSingleDelivery = TRUE;
        tgtcon = cl;
        tgtcln = cln;
        break;
      case CM_TIMER:
      case CM_DATACHANGE:
      case CM_EVENT:
        if (c->status.counter < CTR_RENEWAL*renewalMultiplier - 1)
          c->status.counter = CTR_RENEWAL * renewalMultiplier;
        cl->pollingInterval = findpoll(subhdr.pollingInterval,cl->pollingInterval);
        break;
      default     :
      case CM_CANCEL :
        if (!strcmp(c->client->usr,"NETWORK")) break;
        if (cl->nclients == 1)
        {
          if (isNewContract)
          { /* cancelling a brand new link makes no sense ! */
            swallowOnErr = TRUE;
            ccerr(invalid_link);
          }
          if (c->status.counter) 
          { /* was active : turn on LAST bit */
            cl->contract.EqmAccess |= CA_LAST; 
          }
          cl->lasttime.tv_sec = PRP_CANCEL_SIGNAL;
          cl->deliveryPending = 0;
        }
        c->status.counter = 0;
        break;
    }
    if (tineDebug > 2)
      dbglog("link counter set to %d for %.16s (mode %d)",c->status.counter,c->client->usr,c->status.mode);
err:
    if (cc)
    { /* error condition */
      if (strlen(stsstr) == 0)
      { /* no stsstr yet known */
        switch (cc)
        {
          case server_redirection:
          case call_redirection:
          case property_is_mca:
          case property_is_mca_elevated:
          case invalid_datarequest:
            break; /* don't touch the contents of stsstr ! */
          default:
            strncpy(stsstr,cc2str(cc),STATUS_SHORTSIZE);
            break;
        }
      }
      if (!swallowOnErr) sendMessageToCaller(hprot,cln,&subhdr,cc,(BYTE *)stsstr,STATUS_SIZE);
      ...
bow:   /// Bottom Of While
    if (subhdr.msgsize <= 0) 
    { /* appears to be super corrupt ! */
      if (tineDebug || errorDebug) dbglog("corrupt message size : %d",subhdr.msgsize);
      break; 
    }
    ...
  } /* continue unpacking  */
  ...
  if (gServerCycleNeedsScheduler && sendSingleDelivery)
  {
    /// results can go back to client quickly !
    ...
    doScheduler(&thisCycleTime,gSystemTick,SCHED_RESPONSE,tgtcln,tgtcon);
    ...
  }
out:
  gOneTimeSyncPass = FALSE;
  if (cc && !swallowOnErr) 
  {
    if (strlen(stsstr) == 0) strncpy(stsstr,cc2str(cc),32);
    sendMessageToCaller(hprot,cln,&subhdr,cc,(BYTE *)stsstr,STATUS_SIZE);
  }
  if (cln != NULL && cln->ncontracts == 0) removeClient(cln);
  return cc;
}

/// notes: doScheduler() called directly here (sendSingleDelivery == TRUE) or in serverCycle()
