home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / LANMAN.ZIP / PIPEMGR.C < prev    next >
C/C++ Source or Header  |  1991-01-01  |  26KB  |  690 lines

  1. /*-------------------------------------------------------------------
  2.   PIPEMGR.C -- PBX Pipe Manager routine
  3.  
  4.   Description:
  5.  
  6.   This thread manages pipes until they are connected, at which
  7.   point control is transferred to the next available Router thread.
  8.   The only data recognized by this thread are PBX directed packets
  9.   (i.e., Register, Connect, and ListQuery requests).
  10.  
  11.   Author:  Brendan Dixon
  12.            Microsoft, inc.
  13.            LAN Manager Developer Support
  14.  
  15.   This code example is provided for demonstration purposes only.
  16.   Microsoft makes no warranty, either express or implied,
  17.   as to its usability in any given situation.
  18. -------------------------------------------------------------------*/
  19.  
  20. // Includes ---------------------------------------------------------
  21. #define   INCL_DOS
  22. #define   INCL_DOSERRORS
  23. #include  <os2.h>
  24.  
  25. #define   INCL_NETAUDIT
  26. #define   INCL_NETSERVICE
  27. #define   INCL_NETERRORS
  28. #include  <lan.h>
  29.  
  30. #include  <errno.h>
  31. #include  <memory.h>
  32. #include  <process.h>
  33. #include  <stddef.h>
  34. #include  <stdio.h>
  35. #include  <string.h>
  36.  
  37. #include  "pbxsrv.h"
  38.  
  39. // Function Prototypes (for functions not in PBXSRV.H) --------------
  40. void      Register   (PBXPKT        *pbPkt,
  41.                       ROUTEENT _far *pbREnt);
  42. BOOL      Connect    (PBXPKT        *pbPkt,
  43.                       ROUTEENT _far *pbREnt);
  44. void      ListQuery  (PBXPKT        *pbPkt,
  45.                       ROUTEENT _far *pbREnt);
  46. USHORT    TblIndex   (PBXNAME       *pPBXName);
  47.  
  48.  
  49. /* ReadPKT ----------------------------------------------------------
  50.   Description:  Read data from a pipe
  51.   Input      :  pbBuf   ----- Pointer to buffer
  52.                 usSize  ----- Number of bytes to read
  53.                 pbREnt  ----- Pointer to local routing table entry
  54.   Output     :  True if the request succeeds, False otherwise
  55. -------------------------------------------------------------------*/
  56. BOOL ReadPKT(PCH           pbBuf,
  57.              USHORT        usSize,
  58.              ROUTEENT _far *pbREnt)
  59. {
  60.   BOOL    fState = TRUE;                  // Success/Failure flag
  61.   CHAR    pszMsg[80];                     // Error message buffer
  62.   USHORT  usByteCnt;                      // DosRead bytes read
  63.   USHORT  usRetCode;                      // Return code
  64.  
  65.   // Read the incoming data
  66.   usRetCode = DosRead(pbREnt->hLine,  // Line
  67.                       pbBuf,          // Data buffer
  68.                       usSize,         // Amount to read
  69.                       &usByteCnt);    // Amount read
  70.  
  71.   // Was the read successful?
  72.   if (usRetCode                    &&
  73.       usRetCode != ERROR_MORE_DATA &&
  74.       usRetCode != ERROR_NO_DATA    ) {
  75.     if (usRetCode != ERROR_BROKEN_PIPE)
  76.       ERRRPT("DosRead", usRetCode, Warning);
  77.     fState = FALSE;
  78.   }
  79.  
  80.   // Were all the bytes read?
  81.   else if (usByteCnt != usSize) {
  82.     sprintf(pszMsg,
  83.             "Read unexpected length:  Expected(%u), Actual(%u)",
  84.             usSize, usByteCnt);
  85.     ERRRPT(pszMsg, 0, Warning);
  86.     fState = FALSE;
  87.   }
  88.  
  89.   // Decrement number of bytes available by number of bytes read
  90.   pbREnt->usRData -= usByteCnt;
  91.  
  92.   return fState;
  93. }
  94.  
  95.  
  96. /* SendPKT ----------------------------------------------------------
  97.   Description:  Send data to a pipe
  98.   Input      :  pbBuf   ----- Pointer to buffer
  99.                 usSize  ----- Number of bytes to send
  100.                 pbREnt  ----- Pointer to local routing table entry
  101.   Output     :  True if the request succeeds, False otherwise
  102. -------------------------------------------------------------------*/
  103. BOOL SendPKT(PCH           pbBuf,
  104.              USHORT        usSize,
  105.              ROUTEENT _far *pbREnt)
  106. {
  107.   BOOL    fState = TRUE;                  // Success/Failure flag
  108.   CHAR    pszMsg[80];                     // Error message buffer
  109.   USHORT  usByteCnt;                      // Number of bytes written
  110.   USHORT  usRetCode;                      // Return code
  111.  
  112.   // Write the outgoing data
  113.   usRetCode = DosWrite(pbREnt->hLine,   // Line
  114.                        pbBuf,           // Data buffer
  115.                        usSize,          // Amount to write
  116.                        &usByteCnt);     // Amount written
  117.  
  118.   // Was the write successful?
  119.   if (usRetCode) {
  120.     if (usRetCode != ERROR_BROKEN_PIPE)
  121.       ERRRPT("DosWrite", usRetCode, Warning);
  122.     fState = FALSE;
  123.   }
  124.  
  125.   // Were all the bytes written?
  126.   else if (usByteCnt != usSize) {
  127.     sprintf(pszMsg,
  128.             "Write unexpected length:  Expected(%u), Actual(%u)",
  129.             usSize, usByteCnt);
  130.     ERRRPT(pszMsg, 0, Warning);
  131.     fState = FALSE;
  132.   }
  133.  
  134.   // Reduce the write space available by the number of bytes written
  135.   pbREnt->usWSpace -= usByteCnt;
  136.  
  137.   return fState;
  138. }
  139.  
  140.  
  141. /* TblIndex ---------------------------------------------------------
  142.   Description:  Locate a client name in the routing table and return
  143.                 its index
  144.   Input      :  pPBXName --- Pointer to a PBXNAME structure
  145.   Output     :  Table index of client name if found
  146.                 pbPBXMem->usLines if the client name is not found
  147. -------------------------------------------------------------------*/
  148. USHORT TblIndex(PBXNAME *pbPBXName)
  149. {
  150.   register USHORT  usLines;               // Total number of lines
  151.   register USHORT  usIndex;               // Index into Routing Table
  152.  
  153.   // Check all table entries for one which is owned and whose client
  154.   // name matches the one supplied by the caller
  155.   for (usIndex = 0, usLines = pbPBXMem->usLines;
  156.        usIndex < usLines;
  157.        usIndex++) {
  158.     if (pbPBXMem->abRTable[usIndex].fState > Open                &&
  159.         pbPBXMem->abRTable[usIndex].usClientType ==
  160.                                          pbPBXName->usClientType &&
  161.         !strcmp(pbPBXMem->abRTable[usIndex].pszName,
  162.                                          pbPBXName->pszName)      )
  163.       break;
  164.   }
  165.  
  166.   return usIndex;
  167. }
  168.  
  169.  
  170. /* Register ---------------------------------------------------------
  171.   Description:  Set client name and type in the routing table
  172.                 Additional Register requests can be used to change a
  173.                 client name and/or type
  174.   Input      :  pbPkt   ----- Pointer to PBX Packet
  175.                 pbREnt  ----- Pointer to local routing table entry
  176.   Output     :  None
  177. -------------------------------------------------------------------*/
  178. void Register (PBXPKT        *pbPkt,
  179.                ROUTEENT _far *pbREnt)
  180. {
  181.   PBXAUDIT  bPBXAudit;                    // Audit record buffer
  182.   CHAR      pszMsg[80];                   // Message buffer
  183.   USHORT    usTemp;                       // Temporary variable
  184.   USHORT    usRetCode;                    // Return code
  185.  
  186.   // Inform the MakePipe thread that more pipes may be needed
  187.   DosSemClear(&(pbPBXMem->semMakePipe));
  188.  
  189.   // Prepare client ACK packet
  190.   pbPkt->usPktID |= ACK;
  191.  
  192.   // Did the client supply too many names?
  193.   if (pbPkt->usNameCnt > 1) {
  194.     pbPkt->usRetCode = R_TOOMANYNAMES;
  195.     // Read extraneous information from the pipe
  196.     for (usTemp = pbPkt->usNameCnt - 1;
  197.          usTemp;
  198.          usTemp--)
  199.       ReadPKT((PCH)pbPBXMem->pbBuf, sizeof(PBXNAME), pbREnt);
  200.   }
  201.  
  202.   // Did the client supply a name entry?
  203.   else if (pbPkt->usNameCnt               < 1   ||
  204.            *(pbPkt->aPBXName[0].pszName) == '\0' ) {
  205.     pbPkt->usRetCode = R_NONAME;
  206.   }
  207.  
  208.   // Was a valid client type supplied?
  209.   else if (pbPkt->aPBXName[0].usClientType == 0) {
  210.       pbPkt->usRetCode = R_INVALIDCLIENTTYPE;
  211.   }
  212.  
  213.   // Is another entry in the routing table using the name?
  214.   else {
  215.     usTemp = TblIndex(&(pbPkt->aPBXName[0]));
  216.     if (usTemp <  pbPBXMem->usLines                     &&
  217.         usTemp != (USHORT)(pbREnt-(pbPBXMem->abRTable))  ) {
  218.       pbPkt->usRetCode = R_DUPLICATENAME;
  219.     }
  220.  
  221.     // Everything is Ok, assign current entry to the client
  222.     else {
  223.       strcpy(pbREnt->pszName, pbPkt->aPBXName[0].pszName);
  224.       pbREnt->usClientType = pbPkt->aPBXName[0].usClientType;
  225.       pbREnt->fState       = Owned;
  226.       pbPkt->usRetCode     = NO_ERROR;
  227.       DosSemRequest(&(pbPBXMem->semPBXMem), SEM_INDEFINITE_WAIT);
  228.       pbPBXMem->usLinesOwned++;
  229.       DosSemClear(&(pbPBXMem->semPBXMem));
  230.  
  231.       // If auditing has been enabled, write a RegUser audit record
  232.       if (pbPBXMem->fAuditing) {
  233.         memset(&bPBXAudit, '\0', sizeof(PBXAUDIT));
  234.         bPBXAudit.fType = RegUser;
  235.         strcpy(bPBXAudit.pbRegister.pszName, pbREnt->pszName);
  236.  
  237.         usRetCode = NetAuditWrite(
  238.                             PBXAUDITEVENT,            // Record type
  239.                             (char _far *)&bPBXAudit,  // Buffer
  240.                             sizeof(PBXAUDIT),         // Buffer size
  241.                             NULL, NULL);              // Required
  242.         if (usRetCode                     &&
  243.             usRetCode != NERR_LogOverflow  ) {
  244.           ERRRPT("NetAuditWrite", usRetCode, Warning);
  245.         }
  246.       }
  247.     }
  248.   }
  249.  
  250.   // Send client an acknowledgement and return
  251.   if (!SendPKT((PCH)pbPkt, sizeof(PBXPKT), pbREnt)) {
  252.     sprintf(pszMsg, "Unable to send Register ACK to %s",
  253.             pbREnt->pszName);
  254.     ERRRPT(pszMsg, 0, Warning);
  255.   }
  256.   return;
  257. }
  258.  
  259.  
  260. /* Deregister -------------------------------------------------------
  261.   Description:  Remove a line from the routing table
  262.   Input      :  pbREnt  ----- Pointer to local routing table entry
  263.   Output     :  None
  264. -------------------------------------------------------------------*/
  265. void Deregister (ROUTEENT _far *pbREnt)
  266. {
  267.   PBXAUDIT  bPBXAudit;                    // Audit record buffer
  268.   USHORT    usCIndex;                     // Client table index
  269.   USHORT    usTIndex;                     // Target table index
  270.   USHORT    usRetCode;                    // Return code
  271.  
  272.   // If a connection exists between this client and another,
  273.   // then also close the target pipe
  274.   usCIndex = pbREnt - pbPBXMem->abRTable;
  275.   usTIndex = pbREnt->usConnection;
  276.   if (pbREnt->fState                            == Busy     &&
  277.       pbPBXMem->abRTable[usTIndex].fState       == Busy     &&
  278.       pbPBXMem->abRTable[usTIndex].usConnection == usCIndex  ) {
  279.     // Shut down the pipe instance
  280.     DosDisConnectNmPipe(pbPBXMem->abRTable[usTIndex].hLine);
  281.     DosClose(pbPBXMem->abRTable[usTIndex].hLine);
  282.  
  283.     // If auditing has been enabled, write a DeregUser audit record
  284.     if (pbPBXMem->fAuditing) {
  285.       memset(&bPBXAudit, '\0', sizeof(PBXAUDIT));
  286.       bPBXAudit.fType = DeregUser;
  287.       strcpy(bPBXAudit.pbDeregister.pszName, pbREnt->pszName);
  288.  
  289.       usRetCode = NetAuditWrite(
  290.                           PBXAUDITEVENT,            // Record type
  291.                           (char _far *)&bPBXAudit,  // Buffer
  292.                           sizeof(PBXAUDIT),         // Buffer size
  293.                           NULL, NULL);              // Required
  294.       if (usRetCode                     &&
  295.           usRetCode != NERR_LogOverflow  ) {
  296.         ERRRPT("NetAuditWrite", usRetCode, Warning);
  297.       }
  298.     }
  299.  
  300.     // Update PBX counts
  301.     DosSemRequest(&(pbPBXMem->semPBXMem), SEM_INDEFINITE_WAIT);
  302.     pbPBXMem->usLinesCreated--;
  303.     pbPBXMem->usLinesOwned--;
  304.     DosSemClear(&(pbPBXMem->semPBXMem));
  305.     pbPBXMem->abRTable[usTIndex].fState = Available;
  306.   }
  307.  
  308.   // Close the pipe owned by the client
  309.   DosDisConnectNmPipe(pbREnt->hLine);
  310.   DosClose(pbREnt->hLine);
  311.  
  312.   // If auditing has been enabled, write a DeregUser audit record
  313.   if (pbPBXMem->fAuditing) {
  314.     memset(&bPBXAudit, '\0', sizeof(PBXAUDIT));
  315.     bPBXAudit.fType = DeregUser;
  316.     strcpy(bPBXAudit.pbDeregister.pszName, pbREnt->pszName);
  317.  
  318.     usRetCode = NetAuditWrite(
  319.                         PBXAUDITEVENT,            // Record type
  320.                         (char _far *)&bPBXAudit,  // Buffer
  321.                         sizeof(PBXAUDIT),         // Buffer size
  322.                         NULL, NULL);              // Required
  323.     if (usRetCode                     &&
  324.         usRetCode != NERR_LogOverflow  ) {
  325.       ERRRPT("NetAuditWrite", usRetCode, Warning);
  326.     }
  327.   }
  328.  
  329.   // Update PBX counts
  330.   DosSemRequest(&(pbPBXMem->semPBXMem), SEM_INDEFINITE_WAIT);
  331.   pbPBXMem->usLinesCreated--;
  332.   if (pbREnt->fState > Open)
  333.     pbPBXMem->usLinesOwned--;
  334.   DosSemClear(&(pbPBXMem->semPBXMem));
  335.   pbREnt->fState = Available;
  336.  
  337.   // Inform the MakePipe thread that more pipes may be needed
  338.   DosSemClear(&(pbPBXMem->semMakePipe));
  339.  
  340.   return;
  341. }
  342.  
  343.  
  344. /* Connect ----------------------------------------------------------
  345.   Description:  Establish a connection between two lines
  346.                 Once a connection is made, control of both lines
  347.                 will be transferred to a Router thread
  348.   Input      :  pbPkt   ----- Pointer to PBX Packet
  349.                 pbREnt  ----- Pointer to local routing table entry
  350.   Output     :  TRUE if connection completes, FALSE otherwise
  351. -------------------------------------------------------------------*/
  352. BOOL Connect (PBXPKT        *pbPkt,
  353.               ROUTEENT _far *pbREnt)
  354. {
  355.   PBXAUDIT      bPBXAudit;                // Audit record buffer
  356.   PBXPKT        bPkt;                     // Local PBX packet buffer
  357.   ROUTEENT _far *pbRTargetEnt;            // Target table entry
  358.   USHORT        usCIndex;                 // Client table index
  359.   USHORT        usTIndex;                 // Target table index
  360.   BOOL          fRAssigned = FALSE;       // Router assigned flag
  361.   USHORT        usRNum;                   // Router number
  362.   CHAR          pszMsg[80];               // Error message buffer
  363.   USHORT        usTemp;                   // Temporary variable
  364.   USHORT        usRetCode;                // Return code
  365.  
  366.   // Prepare client ACK packet
  367.   pbPkt->usPktID |= ACK;
  368.  
  369.   // Did the client supply too many names?
  370.   if (pbPkt->usNameCnt > 1) {
  371.     pbPkt->usRetCode = C_TOOMANYNAMES;
  372.     // Read extraneous information from the pipe
  373.     for (usTemp = pbPkt->usNameCnt - 1;
  374.          usTemp;
  375.          usTemp--)
  376.       ReadPKT((PCH)pbPBXMem->pbBuf, sizeof(PBXNAME), pbREnt);
  377.   }
  378.  
  379.   // Did client supply target name?
  380.   else if (pbPkt->usNameCnt               < 1   ||
  381.            *(pbPkt->aPBXName[0].pszName) == '\0' ) {
  382.     pbPkt->usRetCode = C_NONAME;
  383.   }
  384.  
  385.   // Does the target client name exist in the routing table?
  386.   else if ((usTIndex=TblIndex(&(pbPkt->aPBXName[0]))) >=
  387.                                             pbPBXMem->usLines) {
  388.     pbPkt->usRetCode = C_INVALIDNAME;
  389.   }
  390.  
  391.   // The client supplied valid information, check target information
  392.   else {
  393.     // Get pointer of target entry and determine client offset
  394.     pbRTargetEnt = pbPBXMem->abRTable + usTIndex;
  395.     usCIndex     = pbREnt - pbPBXMem->abRTable;
  396.  
  397.     // Is the target client not Busy?
  398.     if (pbRTargetEnt->fState != Owned) {
  399.       pbPkt->usRetCode = C_TARGETBUSY;
  400.     }
  401.  
  402.     // Both clients are open for a connection,
  403.     // complete the connection
  404.     else {
  405.       // Build connection packet to inform the target of
  406.       // the connection
  407.       bPkt.usPktID   = CONNECT;
  408.       bPkt.usNameCnt = 1;
  409.       strcpy(bPkt.aPBXName[0].pszName, pbREnt->pszName);
  410.       bPkt.aPBXName[0].usClientType = pbREnt->usClientType;
  411.  
  412.       // Send packet to target and complete the connection
  413.       if (SendPKT((PCH)&bPkt, sizeof(PBXPKT), pbRTargetEnt)) {
  414.  
  415.         // Determine which Router thread will handle the connection
  416.         for (usRNum=0;
  417.              usRNum < (pbPBXMem->usRThreadCnt-1);
  418.              usRNum++) {
  419.           if (pbPBXMem->abRouters[usRNum].cConnections <
  420.                                         pbPBXMem->usConnsPerThread)
  421.             break;
  422.         }
  423.  
  424.         // Update the routing table and send client the ACK
  425.         pbREnt->fState             = Busy;
  426.         pbRTargetEnt->fState       = Busy;
  427.         pbREnt->usConnection       = usTIndex;
  428.         pbRTargetEnt->usConnection = usCIndex;
  429.         pbPkt->usRetCode           = NO_ERROR;
  430.         if (!SendPKT((PCH)pbPkt, sizeof(PBXPKT), pbREnt)) {
  431.           sprintf(pszMsg, "Unable to send Connect ACK to %s",
  432.                   pbREnt->pszName);
  433.           ERRRPT(pszMsg, 0, Warning);
  434.         }
  435.  
  436.         // Increment the number of connections Router is managing
  437.         DosSemRequest(&(pbPBXMem->abRouters[usRNum].semRAccess),
  438.                       SEM_INDEFINITE_WAIT);
  439.         pbPBXMem->abRouters[usRNum].cConnections++;
  440.         DosSemClear(&(pbPBXMem->abRouters[usRNum].semRAccess));
  441.  
  442.         // If auditing has been enabled, write a ConnectUsers
  443.         // audit record
  444.         if (pbPBXMem->fAuditing) {
  445.           memset(&bPBXAudit, '\0', sizeof(PBXAUDIT));
  446.           bPBXAudit.fType = ConnectUsers;
  447.           strcpy(bPBXAudit.pbConnect.pszSource,
  448.                  pbREnt->pszName);
  449.           strcpy(bPBXAudit.pbConnect.pszTarget,
  450.                  pbRTargetEnt->pszName);
  451.  
  452.           usRetCode = NetAuditWrite(
  453.                               PBXAUDITEVENT,          // Record type
  454.                               (char _far *)&bPBXAudit,// Buffer
  455.                               sizeof(PBXAUDIT),       // Buffer size
  456.                               NULL, NULL);            // Required
  457.           if (usRetCode                     &&
  458.               usRetCode != NERR_LogOverflow  ) {
  459.             ERRRPT("NetAuditWrite", usRetCode, Warning);
  460.           }
  461.         }
  462.  
  463.         // Transfer control of both lines to the Router thread
  464.         DosSetNmPipeSem(pbREnt->hLine,
  465.                         pbPBXMem->abRouters[usRNum].hsemRouter,
  466.                         usCIndex);
  467.         DosSetNmPipeSem(pbRTargetEnt->hLine,
  468.                         pbPBXMem->abRouters[usRNum].hsemRouter,
  469.                         usTIndex);
  470.  
  471.         // Start the Router thread (in case additional data is in
  472.         // the pipe)
  473.         DosSemClear(pbPBXMem->abRouters[usRNum].hsemRouter);
  474.  
  475.         fRAssigned = TRUE;
  476.       }
  477.  
  478.       // An error occurred while informing the target client, do not
  479.       // complete the connection
  480.       else {
  481.         pbPkt->usRetCode = C_TARGETERR;
  482.       }
  483.     }
  484.   }
  485.  
  486.   // If the connection was not fully completed, then send the
  487.   // acknowledgement from here (the ACK for a completed connection
  488.   // is sent earlier)
  489.   if (!fRAssigned                                 &&
  490.       !SendPKT((PCH)pbPkt, sizeof(PBXPKT), pbREnt) ) {
  491.     sprintf(pszMsg, "Unable to send Connect ACK to %s",
  492.             pbREnt->pszName);
  493.     ERRRPT(pszMsg, 0, Warning);
  494.   }
  495.  
  496.   return fRAssigned;
  497. }
  498.  
  499.  
  500. /* ListQuery --------------------------------------------------------
  501.   Description:  Send a list of all registered clients
  502.   Input      :  pbPkt   ----- Pointer to PBX Packet
  503.                 pbREnt  ----- Pointer to local routing table entry
  504.   Output     :  None
  505. -------------------------------------------------------------------*/
  506. void ListQuery (PBXPKT        *pbPkt,
  507.                 ROUTEENT _far *pbREnt)
  508. {
  509.   PBXPKT  _far  *pbPBXPkt;                // PBXPKT  pointer
  510.   PBXNAME _far  *aPBXName;                // PBXNAME array
  511.   USHORT        usIndex;                  // Routing table index
  512.   USHORT        usMaxCnt;                 // Max names to return
  513.   USHORT        usNameCnt;                // Returned name count
  514.   USHORT        usOwnedCnt;               // Count of owned lines
  515.   CHAR          pszMsg[80];               // Error message buffer
  516.  
  517.   // Determine if the user is asking for more names than exist
  518.   // If they are, immediately return the request with no names
  519.   if (pbPkt->aLQNames.usFirstName > pbPBXMem->usLinesOwned) {
  520.     pbPkt->usPktID            |= ACK;
  521.     pbPkt->usPktSize           = sizeof(PBXPKT);
  522.     pbPkt->usRetCode           = NO_ERROR;
  523.     pbPkt->aLQNames.usNameCnt  = 0;
  524.     pbPkt->aLQNames.fMoreNames = FALSE;
  525.     if (!SendPKT((PCH)pbPkt, sizeof(PBXPKT), pbREnt)) {
  526.       sprintf(pszMsg, "Unable to send ListQuery ACK to %s",
  527.               pbREnt->pszName);
  528.       ERRRPT(pszMsg, 0, Warning);
  529.     }
  530.   }
  531.  
  532.   // Otherwise, return as many names as possible to the client
  533.   else {
  534.     // First determine how many names can be returned
  535.     // It will either be the total number of names in the table
  536.     // or the number of names that will fit in the available buffer
  537.     // space
  538.     usMaxCnt = MIN(pbPBXMem->usLinesOwned,
  539.                    ((pbREnt->usWSpace-sizeof(PBXPKT))/
  540.                                         sizeof(PBXNAME))+1);
  541.  
  542.     // Locate the first name to return
  543.     for (usIndex=0, usOwnedCnt=0;
  544.          usIndex    < pbPBXMem->usLines           &&
  545.          usOwnedCnt < pbPBXMem->usLinesOwned      &&
  546.          usOwnedCnt < pbPkt->aLQNames.usFirstName  ;
  547.          usIndex++)
  548.       if (pbPBXMem->abRTable[usIndex].fState > Open) usOwnedCnt++;
  549.  
  550.     // Move the names into the PipeMgr buffer (leaving space for
  551.     // the PBXPKT)
  552.     aPBXName = MAKEP(SELECTOROF(pbPBXMem->pbBuf),
  553.                      FIELDOFFSET(PBXPKT, aPBXName));
  554.     for (usNameCnt=0;
  555.          usIndex    < pbPBXMem->usLines      &&
  556.          usOwnedCnt < pbPBXMem->usLinesOwned &&
  557.          usNameCnt  < usMaxCnt                ;
  558.          usIndex++) {
  559.       if (pbPBXMem->abRTable[usIndex].fState > Open) {
  560.         usOwnedCnt++;
  561.         strcpy(aPBXName[usNameCnt].pszName,
  562.                pbPBXMem->abRTable[usIndex].pszName);
  563.         aPBXName[usNameCnt].usClientType =
  564.                pbPBXMem->abRTable[usIndex].usClientType;
  565.         usNameCnt++;
  566.       }
  567.     }
  568.  
  569.     // Complete the build of the packet and send it to the client
  570.     pbPBXPkt = (PBXPKT _far *)(pbPBXMem->pbBuf);
  571.     pbPBXPkt->usPktID             = ACK | LISTQUERY;
  572.     pbPBXPkt->usPktSize           = sizeof(PBXPKT) +
  573.                                     (sizeof(PBXNAME)*(usNameCnt-1));
  574.     pbPBXPkt->usRetCode           = NO_ERROR;
  575.     pbPBXPkt->aLQNames.usNameCnt  = usNameCnt;
  576.     pbPBXPkt->aLQNames.fMoreNames =
  577.                              (usOwnedCnt < pbPBXMem->usLinesOwned);
  578.     if (!SendPKT((PCH)pbPBXMem->pbBuf,
  579.                  pbPBXPkt->usPktSize, pbREnt)) {
  580.       sprintf(pszMsg, "Unable to send ListQuery ACK to %s",
  581.               pbREnt->pszName);
  582.       ERRRPT(pszMsg, 0, Warning);
  583.     }
  584.   }
  585.  
  586.   return;
  587. }
  588.  
  589.  
  590. /* PipeMgr ----------------------------------------------------------
  591.   Description:  See file header
  592.   Input      :  None
  593.   Output     :  None
  594. -------------------------------------------------------------------*/
  595. void _cdecl PipeMgr(void)
  596. {
  597.   PBXPKT        bPkt;                     // PBX packet buffer
  598.   CHAR          pszMsg[80];               // Error message buffer
  599.   ROUTEENT _far *pbREnt;                  // Routing entry pointer
  600.   USHORT        usCIndex;                 // Client table index
  601.   USHORT        usTemp;                   // Temporary variable
  602.   USHORT        usRetCode;                // Return code
  603.  
  604.   // Process all incoming requests until told to exit or pause
  605.   // The main thread will set semPause to pause and semExit to
  606.   // indicate a shutdown should occur
  607.   for (;;) {
  608.  
  609.     // Wait for input on one of the lines
  610.     DosSemRequest(pbPBXMem->hsemPipeMgr, SEM_INDEFINITE_WAIT);
  611.  
  612.     // Requests to pause or exit should prevent the PipeMgr thread
  613.     // from processing any addtional packets, so first check these
  614.     // semaphores
  615.  
  616.     // Check for shutdown
  617.     if (DosSemWait(&(pbPBXMem->semExit), SEM_IMMEDIATE_RETURN))
  618.       break;
  619.  
  620.     // Check for pause
  621.     DosSemWait(&(pbPBXMem->semPause), SEM_INDEFINITE_WAIT);
  622.  
  623.     // Read all of the PIPESEMSTATE records
  624.     usRetCode = DosQNmPipeSemState(pbPBXMem->hsemPipeMgr,
  625.                                    pbPBXMem->aPSemState,
  626.                                    pbPBXMem->usPSemStateSize);
  627.     if (usRetCode) {
  628.       ERRRPT("DosQNmPipeSemState", usRetCode, Warning);
  629.     }
  630.  
  631.     // Update the status of all managed pipes
  632.     else  {
  633.       for (usTemp=0;
  634.            pbPBXMem->aPSemState[usTemp].fStatus != NPSS_EOI;
  635.            usTemp++) {
  636.         // Get the routing table index for the client from the
  637.         // PIPESEMSTATE record and build a pointer into the
  638.         // routing table
  639.         usCIndex = pbPBXMem->aPSemState[usTemp].usKey;
  640.         pbREnt   = pbPBXMem->abRTable + usCIndex;
  641.  
  642.         // Process the record
  643.         switch (pbPBXMem->aPSemState[usTemp].fStatus) {
  644.  
  645.           // Read and immediately handle any incoming packets
  646.           case NPSS_RDATA : {
  647.             BOOL  fConnected = FALSE;
  648.  
  649.             // Process data in the pipe until it becomes connected
  650.             // to another pipe (the Router thread will handle any
  651.             // data from that point onwards)
  652.             pbREnt->usRData = pbPBXMem->aPSemState[usTemp].usAvail;
  653.             while (!fConnected && pbREnt->usRData) {
  654.               ReadPKT((PCH)&bPkt, sizeof(PBXPKT), pbREnt);
  655.               switch (bPkt.usPktID) {
  656.                 case CONNECT  : fConnected = Connect(&bPkt, pbREnt);
  657.                                 break;
  658.                 case REGISTER : Register (&bPkt, pbREnt); break;
  659.                 case LISTQUERY: ListQuery(&bPkt, pbREnt); break;
  660.               }
  661.             }
  662.             break;
  663.           }
  664.  
  665.           // Record amount of available buffer space
  666.           case NPSS_WSPACE:
  667.             pbREnt->usWSpace = pbPBXMem->aPSemState[usTemp].usAvail;
  668.             break;
  669.  
  670.           // A client has broken their pipe connection, remove them
  671.           // from the routing table
  672.           case NPSS_CLOSE :
  673.             Deregister(pbREnt);
  674.             break;
  675.  
  676.           default         :
  677.             sprintf(pszMsg,
  678.                     "Unknown DosQNmPipeSemState record type of %u",
  679.                     (USHORT)(pbPBXMem->aPSemState[usTemp].fStatus));
  680.             ERRRPT(pszMsg, 0, Warning);
  681.             break;
  682.         }
  683.       }
  684.     }
  685.   }
  686.  
  687.   // Terminate processing
  688.   _endthread();
  689. }
  690.