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

  1. /*-------------------------------------------------------------------
  2.   PBXSRV.C -- PBX Packet Routing Service, main thread routine
  3.  
  4.   Description:
  5.  
  6.   PBX is general purpose, named-pipe packet router designed to
  7.   support virtual, named-pipe, peer-to-peer connections.  These
  8.   connections are virtual because all communications are through
  9.   the PBX Service.  However, they appear as a peer-to-peer named-
  10.   pipe connection to the clients (once a connection is established).
  11.   PBX performs this using a quick, protocol-unaware method and may,
  12.   therefore, be used by several clients each of which supports its
  13.   own internal protocol.
  14.  
  15.   PBX employs several threads to create and manage the named-pipes:
  16.  
  17.     PBXSRV.C ----  This is the main PBX thread.  It creates the
  18.                    child threads, processes incoming signals, and
  19.                    responds to broadcasts looking for the PBX
  20.                    service.
  21.  
  22.     MAKEPIPE.C --  This thread dynamically creates instances of the
  23.                    named-pipe and attempts to ensure that the
  24.                    minimum number of unowned pipes are always
  25.                    available.
  26.  
  27.     PIPEMGR.C ---  This thread manages pipe traffic until it becomes
  28.                    connected with another client.  For example,
  29.                    client registration and connection requests are
  30.                    processed by this thread.
  31.  
  32.     ROUTER.C ----  This thread manages pipes that are connected, its
  33.                    sole purpose is to transfer packets between
  34.                    connected clients.  The number of connections a
  35.                    Router thread will manage (and thus the total
  36.                    number of Router threads created) may be
  37.                    controlled by the CONNSPERTHREAD keyword (see
  38.                    below).
  39.  
  40.   PBX may configured via keywords supplied either in LANMAN.INI or
  41.   explicitly when PBX is started.  In LANMAN.INI PBX looks for these
  42.   keywords under the section named [PBX].  Listed below are the
  43.   keywords, their meanings, and valid ranges:
  44.  
  45.     LINES         :  Total number of pipe lines (pipe instances) to
  46.                      be created and managed.  The minimum value is
  47.                      2 and the maximum is 1000, it defaults to 20.
  48.  
  49.     LINEBUFSIZE   :  Size (in bytes) of the buffers associated with
  50.                      each pipe line (instance).  A buffer of this
  51.                      size is created for each pipe instance, for
  52.                      each Router thread, and for the PipeMgr thread.
  53.                      The minimum value is 512 and the maximum is
  54.                      65,535, it defaults to 4096.
  55.  
  56.     OPENLINES     :  The minimum number of non-connected pipe
  57.                      instances that PBX will try to maintain.  Since
  58.                      there are situations where the minimum cannot
  59.                      be maintained (e.g., all lines are allocated
  60.                      and used), this number is only a goal.  The
  61.                      minimum value is 10 or the total number of
  62.                      lines (whichever is smaller).  The maximum is
  63.                      the total number of lines.  It defaults to the
  64.                      minimum value.
  65.  
  66.     CONNSPERTHREAD:  Number of connections a single Router thread
  67.                      should manage, a connection always involves two
  68.                      pipes. Lower values should be used if heavy
  69.                      traffic is expected from the clients.  The
  70.                      minimum value is 1 and the maximum value is the
  71.                      total number of lines created, rounded up to
  72.                      the next even number, divided by two.  The
  73.                      default is for a single Router thread to manage
  74.                      all connections.
  75.  
  76.     AUDITING      :  Determines whether PBX writes audit records or
  77.                      not.  Valid values are Yes or No.  The default
  78.                      is Yes.
  79.                      The data portion of the audit records written
  80.                      by PBX is mapped by the PBXAUDIT structure in
  81.                      PBXSRV.H.
  82.  
  83.   It is possible to specify combinations that cannot work on the
  84.   local machine.  For example, requesting 1000 pipes (LINES=1000)
  85.   each with a 64k buffer (LINEBUFSIZE=65535) would require over
  86.   62 MB.  PBX does not check for these invalid combinations,
  87.   instead other errors will be received (e.g., DosAllocSeg will
  88.   fail).
  89.  
  90.   Author:  Brendan Dixon
  91.            Microsoft, inc.
  92.            LAN Manager Developer Support
  93.  
  94.   This code example is provided for demonstration purposes only.
  95.   Microsoft makes no warranty, either express or implied,
  96.   as to its usability in any given situation.
  97. -------------------------------------------------------------------*/
  98.  
  99. // Includes ---------------------------------------------------------
  100. #define   INCL_DOS
  101. #define   INCL_DOSERRORS
  102. #include  <os2.h>
  103.  
  104. #define   INCL_NETAUDIT
  105. #define   INCL_NETMAILSLOT
  106. #define   INCL_NETSERVICE
  107. #define   INCL_NETERRORS
  108. #include  <lan.h>
  109.  
  110. #include  <ctype.h>
  111. #include  <memory.h>
  112. #include  <stdio.h>
  113. #include  <stdlib.h>
  114. #include  <string.h>
  115.  
  116. #define   INCL_GLOBAL
  117. #include  "pbxsrv.h"
  118.  
  119. // Constants --------------------------------------------------------
  120. const CHAR  pszLines[]          = LINES;
  121. const CHAR  pszLineBufSize[]    = LINEBUFSIZE;
  122. const CHAR  pszOpenLines[]      = OPENLINES;
  123. const CHAR  pszConnsPerThread[] = CONNSPERTHREAD;
  124. const CHAR  pszAuditing[]       = AUDITING;
  125. const CHAR  pszYes[]            = "YES";
  126. const CHAR  pszNo[]             = "NO";
  127.  
  128. // Function Prototypes (for functions not in PBXSRV.H) --------------
  129. void  RedirectFiles (void);
  130. void  SetParms      (int argc, char _far *argv[]);
  131. void  Initialize    (int argc, char _far *argv[]);
  132.  
  133.  
  134. /* RedirectFiles ----------------------------------------------------
  135.   Description:  Redirect STDIN, STDOUT, and STDERR to the NUL device
  136.   Input      :  None
  137.   Output     :  None
  138. -------------------------------------------------------------------*/
  139. void RedirectFiles(void)
  140. {
  141.   HFILE   hFileNul;                       // Handle to NUL device
  142.   HFILE   hNewHandle;                     // Temporary file handle
  143.   USHORT  usAction;                       // Action taken by DosOpen
  144.   USHORT  usRetCode;                      // Return code
  145.  
  146.   // First, open the NUL device
  147.   usRetCode = DosOpen("NUL",                   // NUL device name
  148.                       &hFileNul,               // File handle
  149.                       &usAction,               // Action taken
  150.                       0L,                      // File size
  151.                       FILE_NORMAL,             // File attribute
  152.                       FILE_CREATE,             // Open action
  153.                       OPEN_ACCESS_READWRITE |
  154.                       OPEN_SHARE_DENYNONE,     // Open mode
  155.                       0L);
  156.   if (usRetCode) {
  157.     ERRRPT("DosOpen", usRetCode, Error);
  158.     ulExitCode = SERVICE_UIC_CODE(SERVICE_UIC_SYSTEM, usRetCode);
  159.     ExitHandler();
  160.   }
  161.  
  162.   // Then, redirect STDIN, STDOUT, and STDERR
  163.   for (hNewHandle=0; hNewHandle < 3; hNewHandle++) {
  164.     // If the handle is already directed to NUL, skip it
  165.     if (hFileNul != hNewHandle) DosDupHandle(hFileNul, &hNewHandle);
  166.   }
  167.  
  168.   // Lastly, close the extra handle to NUL
  169.   if (hFileNul > 2) DosClose(hFileNul);
  170.  
  171.   return;
  172. }
  173.  
  174.  
  175. /* SetParms ---------------------------------------------------------
  176.   Description:  Process PBX configuration parameters
  177.                 These will come from either the command line or
  178.                 predefined defaults.  Any keywords not entered by
  179.                 the user, but found in LANMAN.INI will be added to
  180.                 the command line by LAN Manager and passed to PBX.
  181.                 Therefore, the LANMAN.INI file does not need to be
  182.                 read.
  183.   Input      :  argc --- Number of command line parameters passed
  184.                          to PBX
  185.                 argv --- Array of command line parameters
  186.   Output     :  None (globals are set)
  187. -------------------------------------------------------------------*/
  188. void SetParms(int       argc,
  189.               char _far *argv[])
  190. {
  191.   BOOL      fLines,                       // LINES          supplied
  192.             fLineBufSize,                 // LINEBUFSIZE    supplied
  193.             fOpenLines,                   // OPENLINES      supplied
  194.             fConnsPerThread,              // CONNSPERTHREAD supplied
  195.             fAuditing;                    // AUDITING       supplied
  196.   int       sArg;                         // Argument number
  197.   CHAR _far *pszArg;                      // Argument pointer
  198.   CHAR      pszKeyword[20];               // Keyword value
  199.   CHAR      pszAudit[5];                  // Audit arg value
  200.   CHAR _far *pszTemp;                     // Temporary pointer
  201.   USHORT    usTemp;                       // Temporary variable
  202.   CHAR      pszMsg[80];                   // Error message buffer
  203.  
  204.   // Assume no keywords have been supplied
  205.   fLines          =
  206.   fLineBufSize    =
  207.   fOpenLines      =
  208.   fConnsPerThread =
  209.   fAuditing       = FALSE;
  210.  
  211.   // Scan the command lines arguments PBX keywords, unknown
  212.   // keywords will be treated as an error
  213.   // Note that LAN Manager will not pass LANMAN.INI keywords if the
  214.   // user entered the full keyword name on the command line as an
  215.   // override value.  But if the full keyword was not specified,
  216.   // then the LANMAN.INI keyword will also be passed (but after the
  217.   // values entered by the user).  So once a keyword is found, any
  218.   // subsequent occurances should be ignored (i.e., duplicate
  219.   // keywords will not be detected).
  220.   for (sArg=1; sArg < argc; sArg++) {
  221.     if ((pszArg=strpbrk(argv[sArg], "=")) != NULL) {
  222.       // Point to the keyword value and mark its end
  223.       pszArg++;
  224.       if ((pszTemp=strpbrk(pszArg, " ")) != NULL) *pszTemp = '\0';
  225.  
  226.       // Fold line to upper case and extract the keyword
  227.       strupr(argv[sArg]);
  228.       usTemp = MIN(sizeof(pszKeyword)-1, pszArg-(argv[sArg]+1));
  229.       strncpy(pszKeyword, argv[sArg], usTemp);
  230.       pszKeyword[usTemp] = '\0';
  231.  
  232.       // Check for the LINES keyword
  233.       if (strstr(pszLines, pszKeyword) == pszLines) {
  234.         // All characters in LINES are required since anything
  235.         // less is ambiguous with LINEBUFSIZE
  236.         if (strlen(pszKeyword) < strlen(pszLines)) {
  237.           strncpy(pszExitText, pszKeyword, sizeof(pszExitText)-1);
  238.           pszExitText[sizeof(pszExitText)-1] = '\0';
  239.           sprintf(pszMsg, "Ambiguous keyword %s", pszExitText);
  240.           ERRRPT(pszMsg, 0, Error);
  241.           ulExitCode = SERVICE_UIC_CODE(SERVICE_UIC_AMBIGPARM,
  242.                                         SERVICE_UIC_M_NULL);
  243.           ExitHandler();
  244.         }
  245.         else {
  246.           fLines = TRUE;
  247.           pbPBXMem->usLines = atoi(pszArg);
  248.         }
  249.       }
  250.  
  251.       // Check for LINEBUFSIZE
  252.       else if (strstr(pszLineBufSize, pszKeyword) ==
  253.                                                  pszLineBufSize) {
  254.         fLineBufSize = TRUE;
  255.         pbPBXMem->usLineBufSize = atoi(pszArg);
  256.       }
  257.  
  258.       // Check for OPENLINES
  259.       else if (strstr(pszOpenLines, pszKeyword) == pszOpenLines) {
  260.         fOpenLines = TRUE;
  261.         pbPBXMem->usOpenLines = atoi(pszArg);
  262.       }
  263.  
  264.       // Check for CONNSPERTHREAD
  265.       else if (strstr(pszConnsPerThread, pszKeyword) ==
  266.                                               pszConnsPerThread) {
  267.         fConnsPerThread = TRUE;
  268.         pbPBXMem->usConnsPerThread = atoi(pszArg);
  269.       }
  270.  
  271.       // Check for AUDITING
  272.       else if (strstr(pszAuditing, pszKeyword) == pszAuditing) {
  273.         fAuditing = TRUE;
  274.         // Copy the argument to a buffer
  275.         // One extra byte beyond the largest valid value is copied
  276.         // so invalid values can be detected
  277.         strncpy(pszAudit, pszArg, sizeof(pszAudit)-1);
  278.         pszAudit[sizeof(pszAudit)-1] = '\0';
  279.       }
  280.  
  281.       // The keyword is not valid for PBX, report the error
  282.       else {
  283.         strncpy(pszExitText, pszKeyword, sizeof(pszExitText)-1);
  284.         pszExitText[sizeof(pszExitText)-1] = '\0';
  285.         sprintf(pszMsg, "Unknown keyword %s", pszExitText);
  286.         ERRRPT(pszMsg, 0, Error);
  287.         ulExitCode = SERVICE_UIC_CODE(SERVICE_UIC_UNKPARM,
  288.                                       SERVICE_UIC_M_NULL);
  289.         ExitHandler();
  290.       }
  291.     }
  292.   }
  293.  
  294.   // Validity check the number lines PBX should support
  295.   if (fLines) {
  296.     if (pbPBXMem->usLines > MAXLINES ||
  297.         pbPBXMem->usLines < MINLINES   ) {
  298.       sprintf(pszMsg, "Invalid value for %s", pszLines);
  299.       ERRRPT(pszMsg, 0, Error);
  300.       strcpy(pszExitText, pszLines);
  301.       ulExitCode = SERVICE_UIC_CODE(SERVICE_UIC_BADPARMVAL,
  302.                                     SERVICE_UIC_M_NULL);
  303.       ExitHandler();
  304.     }
  305.   }
  306.   else
  307.     // Take default value
  308.     pbPBXMem->usLines = DEFLINES;
  309.  
  310.   // Validity check the buffer size PBX should use for each line
  311.   if (fLineBufSize) {
  312.     if (pbPBXMem->usLineBufSize > MAXLINEBUFSIZE ||
  313.         pbPBXMem->usLineBufSize < MINLINEBUFSIZE  ) {
  314.       sprintf(pszMsg, "Invalid value for %s", pszLineBufSize);
  315.       ERRRPT(pszMsg, 0, Error);
  316.       strcpy(pszExitText, pszLineBufSize);
  317.       ulExitCode = SERVICE_UIC_CODE(SERVICE_UIC_BADPARMVAL,
  318.                                     SERVICE_UIC_M_NULL);
  319.       ExitHandler();
  320.     }
  321.   }
  322.   else
  323.     // Take default value
  324.     pbPBXMem->usLineBufSize = DEFLINEBUFSIZE;
  325.  
  326.   // Validity check the minimum number of available lines PBX
  327.   // should try and maintain open
  328.   if (fOpenLines) {
  329.     if (pbPBXMem->usOpenLines > pbPBXMem->usLines            ||
  330.         pbPBXMem->usOpenLines < (USHORT)MIN(MINOPENLINES,
  331.                                            pbPBXMem->usLines) ) {
  332.       sprintf(pszMsg, "Invalid value for %s", pszOpenLines);
  333.       ERRRPT(pszMsg, 0, Error);
  334.       strcpy(pszExitText, pszOpenLines);
  335.       ulExitCode = SERVICE_UIC_CODE(SERVICE_UIC_BADPARMVAL,
  336.                                   SERVICE_UIC_M_NULL);
  337.       ExitHandler();
  338.     }
  339.   }
  340.   else
  341.     // Take the minimum number, but do not set it higher then
  342.     // the total number of lines available
  343.     pbPBXMem->usOpenLines = (USHORT)MIN(MINOPENLINES,
  344.                                         pbPBXMem->usLines);
  345.  
  346.   // Validity check the number of connections a Router thread
  347.   // should manage
  348.   if (fConnsPerThread) {
  349.     if (pbPBXMem->usConnsPerThread > ((pbPBXMem->usLines+1)/2) ||
  350.         pbPBXMem->usConnsPerThread < MINCONNSPERTHREAD          ) {
  351.       sprintf(pszMsg, "Invalid value for %s", pszConnsPerThread);
  352.       ERRRPT(pszMsg, 0, Error);
  353.       strcpy(pszExitText, pszConnsPerThread);
  354.       ulExitCode = SERVICE_UIC_CODE(SERVICE_UIC_BADPARMVAL,
  355.                                     SERVICE_UIC_M_NULL);
  356.       ExitHandler();
  357.     }
  358.   }
  359.   else
  360.     // All connections will be managed by a single Router thread
  361.     pbPBXMem->usConnsPerThread = (pbPBXMem->usLines+1) / 2;
  362.  
  363.   // Validity check the value supplied for the audit flag
  364.   if (fAuditing) {
  365.     if (strstr(pszYes, pszAudit) == pszYes)
  366.       pbPBXMem->fAuditing = TRUE;
  367.     else if (strstr(pszNo, pszAudit) == pszNo)
  368.       pbPBXMem->fAuditing = FALSE;
  369.     else {
  370.       sprintf(pszMsg, "Invalid value for %s", pszAuditing);
  371.       ERRRPT(pszMsg, 0, Error);
  372.       strcpy(pszExitText, pszAuditing);
  373.       ulExitCode = SERVICE_UIC_CODE(SERVICE_UIC_BADPARMVAL,
  374.                                     SERVICE_UIC_M_NULL);
  375.       ExitHandler();
  376.     }
  377.   }
  378.   else
  379.     // Take default value
  380.     pbPBXMem->fAuditing = TRUE;
  381.  
  382.   return;
  383. }
  384.  
  385.  
  386. /* Initialize -------------------------------------------------------
  387.   Description:  Prepare PBX for execution by allocating all global
  388.                 storage and creating the child threads
  389.   Input      :  argc ---- Count of input parameters passed to PBX
  390.                 argv ---- Input parameters passed to PBX
  391.   Output     :  None
  392. -------------------------------------------------------------------*/
  393. void Initialize(int       argc,
  394.                 char _far *argv[])
  395. {
  396.   CHAR    pszMsg[80];                     // Error message buffer
  397.   USHORT  usWCnt;                         // Uninstall wait counter
  398.   USHORT  usRetCode;                      // Return code
  399.  
  400.   // Inform LAN Manager that this service is being installed
  401.   usWCnt = 2;
  402.   ssStatus.svcs_status = SERVICE_INSTALL_PENDING;
  403.   ssStatus.svcs_code   = SERVICE_CCP_CODE(PBXINSTALLTIME, usWCnt++);
  404.   usRetCode = NetServiceStatus((char _far *)&ssStatus,
  405.                                sizeof(ssStatus));
  406.   if (usRetCode) {
  407.     ERRRPT("NetServiceStatus", usRetCode, Error);
  408.     ulExitCode = SERVICE_UIC_CODE(SERVICE_UIC_SYSTEM, usRetCode);
  409.     ExitHandler();
  410.   }
  411.  
  412.   // Allocate shared memory segment
  413.   selPBXMem = 0;
  414.   pbPBXMem  = NULL;
  415.   usRetCode = DosAllocShrSeg(sizeof(PBXMEM),  // Segment size
  416.                              PBXMEMNAME,      // Segment name
  417.                              &selPBXMem);     // Selector
  418.   if (usRetCode) {
  419.     sprintf(pszMsg, "Unable to allocate %s, DosAllocShrSeg",
  420.             PBXMEMNAME);
  421.     ERRRPT(pszMsg, usRetCode, Error);
  422.     ulExitCode = SERVICE_UIC_CODE(SERVICE_UIC_RESOURCE,
  423.                                   SERVICE_UIC_M_MEMORY);
  424.     ExitHandler();
  425.   }
  426.  
  427.   // Make shared memory segment pointer and initialize the segment
  428.   pbPBXMem = MAKEP(selPBXMem, 0);
  429.   memset(pbPBXMem, '\0', sizeof(PBXMEM));
  430.  
  431.   // Process configuration parameters
  432.   SetParms(argc, argv);
  433.  
  434.   // Provide LAN Manager with a status hint
  435.   ssStatus.svcs_status = SERVICE_INSTALL_PENDING;
  436.   ssStatus.svcs_code   = SERVICE_CCP_CODE(PBXINSTALLTIME,
  437.                                           usWCnt++);
  438.   usRetCode = NetServiceStatus((char _far *)&ssStatus,
  439.                                sizeof(ssStatus));
  440.   if (usRetCode) {
  441.     ERRRPT("NetServiceStatus", usRetCode, Error);
  442.     ulExitCode = SERVICE_UIC_CODE(SERVICE_UIC_SYSTEM, usRetCode);
  443.     ExitHandler();
  444.   }
  445.  
  446.   // Ensure that enough pipe handles exist
  447.   // Note, MINFHANDLES is a guess at how many handles are already in
  448.   // use.  Alternatively, a loop could be performed to determine the
  449.   // actual number of files handles in use.
  450.   usRetCode = DosSetMaxFH((pbPBXMem->usLines) + MINFHANDLES);
  451.   if (usRetCode) {
  452.     ERRRPT("DosSetMaxFH", usRetCode, Error);
  453.     ulExitCode = SERVICE_UIC_CODE(SERVICE_UIC_SYSTEM, usRetCode);
  454.     ExitHandler();
  455.   }
  456.  
  457.   // Allocate and initialize PipeMgr thread memory
  458.   AllocatePipeMgrMem();
  459.  
  460.   // Build Routing table
  461.   AllocateRoutingTable();
  462.  
  463.   // Set number of Router threads required to handle requested lines
  464.   pbPBXMem->usRThreadCnt = (pbPBXMem->usLines+1) /
  465.                                    (2 * pbPBXMem->usConnsPerThread);
  466.  
  467.   // Allocate and initialize Router thread structures
  468.   AllocateRouterMem();
  469.  
  470.   // Build the PBX message (used in broadcast messages)
  471.   BuildPBXMsg();
  472.  
  473.   // Create child threads
  474.   CreateThreads();
  475.  
  476.   return;
  477. }
  478.  
  479.  
  480. /* main -------------------------------------------------------------
  481.   Description:  See file header
  482.   Input      :  None (directly)
  483.   Output     :  None
  484. -------------------------------------------------------------------*/
  485. void _cdecl main(int argc, char *argv[])
  486. {
  487.   PIDINFO   pidInfo;                      // OS/2 PID info struct
  488.   PBXAUDIT  bPBXAudit;                    // PBX audit record
  489.   USHORT    usByteCnt;                    // Mailslot bytes read
  490.   USHORT    usNextCnt;                    // Mailslot next msg size
  491.   USHORT    usNextPriority;               // Mailslot next priority
  492.   USHORT    usRetCode;                    // Return Code
  493.  
  494.   // Initialize global variables
  495.   selPBXMem  = 0;
  496.   pbPBXMem   = NULL;
  497.   ulExitCode = SERVICE_UIC_CODE(SERVICE_UIC_NORMAL,
  498.                                 SERVICE_UIC_M_NULL);
  499.  
  500.   // Obtain the process ID of this process
  501.   usRetCode = DosGetPID(&pidInfo);
  502.   if (usRetCode) {
  503.     ERRRPT("DosGetPID", usRetCode, Error);
  504.     ulExitCode = SERVICE_UIC_CODE(SERVICE_UIC_SYSTEM, usRetCode);
  505.     ExitHandler();
  506.   }
  507.  
  508.   // Inform LAN Manager that this service is in installation
  509.   // Because the signal handler is not yet installed, the service
  510.   // cannot be uninstalled or paused
  511.   ssStatus.svcs_pid    = pidInfo.pid;
  512.   ssStatus.svcs_status = SERVICE_INSTALL_PENDING   |
  513.                          SERVICE_NOT_UNINSTALLABLE |
  514.                          SERVICE_NOT_PAUSABLE;
  515.   ssStatus.svcs_code   = SERVICE_CCP_CODE(PBXINSTALLTIME, 0);
  516.   usRetCode = NetServiceStatus((char _far *)&ssStatus,
  517.                                sizeof(ssStatus));
  518.   if (usRetCode != NERR_Success) {
  519.     ERRRPT("NetServiceStatus", usRetCode, Error);
  520.     ulExitCode = SERVICE_UIC_CODE(SERVICE_UIC_SYSTEM, usRetCode);
  521.     ExitHandler();
  522.   }
  523.  
  524.   // Install the signal handler
  525.   InstallSignals();
  526.  
  527.   // Indicate that the signal handler is now installed, inform LAN
  528.   // Manager that the service may be uninstalled
  529.   ssStatus.svcs_status = SERVICE_INSTALL_PENDING |
  530.                          SERVICE_UNINSTALLABLE   |
  531.                          SERVICE_NOT_PAUSABLE;
  532.   ssStatus.svcs_code   = SERVICE_CCP_CODE(PBXINSTALLTIME, 1);
  533.   usRetCode = NetServiceStatus((char _far *)&ssStatus,
  534.                                sizeof(ssStatus));
  535.   if (usRetCode) {
  536.     ERRRPT("NetServiceStatus", usRetCode, Error);
  537.     ulExitCode = SERVICE_UIC_CODE(SERVICE_UIC_SYSTEM, usRetCode);
  538.     ExitHandler();
  539.   }
  540.  
  541.   // Verify that the LAN Manager Server service is started
  542.   { struct service_info_1 si1;            // Level 1 info structure
  543.     USHORT  usByteCnt;                    // Byte count
  544.  
  545.     // Obtain Server service status from LAN Manager
  546.     usRetCode = NetServiceGetInfo(
  547.                           NULL,             // Local
  548.                           SERVICE_SERVER,   // Server service
  549.                           1,                // Level 1 info
  550.                           (char _far *)&si1,// Buffer
  551.                           sizeof(si1),      // Buffer size
  552.                           &usByteCnt);      // Bytes available
  553.     if (usRetCode                            &&
  554.         usRetCode != NERR_ServiceNotInstalled ) {
  555.       ERRRPT("NetServiceStatus", usRetCode, Error);
  556.       ulExitCode = SERVICE_UIC_CODE(SERVICE_UIC_SYSTEM, usRetCode);
  557.       ExitHandler();
  558.     }
  559.  
  560.     // Is the Server running?  If not, exit with an error
  561.     if (usRetCode                 == NERR_ServiceNotInstalled ||
  562.         (si1.svci1_status & 0x03) != SERVICE_INSTALLED         ) {
  563.       ulExitCode = SERVICE_UIC_CODE(SERVICE_UIC_CONFIG,
  564.                                     SERVICE_UIC_M_SERVER);
  565.       ExitHandler();
  566.     }
  567.   }
  568.  
  569.   // Redirect STDIN, STDOUT, and STDERR to the NUL device (since no
  570.   // console I/O is allowed from a LAN Manager service)
  571.   RedirectFiles();
  572.  
  573.   // Perform PBX specific initialization
  574.   Initialize(argc, argv);
  575.  
  576.   // Open the mailslot that clients can use to detect the presence
  577.   // of a PBX
  578.   usRetCode = DosMakeMailslot(ANNOUNCELINE,    // Mailslot name
  579.                               PBXMSGSIZE,      // Max message size
  580.                               0,               // Default size
  581.                               &(pbPBXMem->hMailslot)); // Handle
  582.   if (usRetCode) {
  583.     ERRRPT("NetServiceStatus", usRetCode, Error);
  584.     ulExitCode = SERVICE_UIC_CODE(SERVICE_UIC_SYSTEM, usRetCode);
  585.     ExitHandler();
  586.   }
  587.  
  588.   // If auditing has been enabled, write a start-up audit record
  589.   if (pbPBXMem->fAuditing) {
  590.     memset(&bPBXAudit, '\0', sizeof(PBXAUDIT));
  591.     bPBXAudit.fType = Start;
  592.     bPBXAudit.pbStart.usLines          = pbPBXMem->usLines;
  593.     bPBXAudit.pbStart.usLineBufSize    = pbPBXMem->usLineBufSize;
  594.     bPBXAudit.pbStart.usOpenLines      = pbPBXMem->usOpenLines;
  595.     bPBXAudit.pbStart.usConnsPerThread = pbPBXMem->usConnsPerThread;
  596.  
  597.     usRetCode = NetAuditWrite(
  598.                         PBXAUDITEVENT,            // Record type
  599.                         (char _far *)&bPBXAudit,  // Buffer
  600.                         sizeof(PBXAUDIT),         // Buffer size
  601.                         NULL, NULL);              // Required
  602.     if (usRetCode                     &&
  603.         usRetCode != NERR_LogOverflow  ) {
  604.       ERRRPT("NetAuditWrite", usRetCode, Warning);
  605.     }
  606.   }
  607.  
  608.   // Inform LAN Manager that this service is now installed
  609.   ssStatus.svcs_status = SERVICE_INSTALLED     |
  610.                          SERVICE_UNINSTALLABLE |
  611.                          SERVICE_PAUSABLE;
  612.   ssStatus.svcs_code   = SERVICE_UIC_NORMAL;
  613.   usRetCode = NetServiceStatus((char _far *)&ssStatus,
  614.                                sizeof(ssStatus));
  615.   if (usRetCode) {
  616.     ERRRPT("NetServiceStatus", usRetCode, Error);
  617.     ulExitCode = SERVICE_UIC_CODE(SERVICE_UIC_SYSTEM, usRetCode);
  618.     ExitHandler();
  619.   }
  620.  
  621.   // Run until PBX is shutdown
  622.   do {
  623.     // Wait until a signal is received or a message arrives on the
  624.     // mailslot (which means a client is looking for the PBX)
  625.     usRetCode = DosReadMailslot(
  626.                        pbPBXMem->hMailslot,   // Handle
  627.                        pbPBXMem->pbMSlotBuf,  // Buffer
  628.                        &usByteCnt,            // Bytes read
  629.                        &usNextCnt,            // Next msg size
  630.                        &usNextPriority,       // Next msg priority
  631.                        MAILSLOT_NO_TIMEOUT);  // Wait forever
  632.  
  633.     // If control has been returned because a client sent a message,
  634.     // return to the client the computer name of the machine on
  635.     // which PBX is executing
  636.     if (usRetCode == NERR_Success &&
  637.         usByteCnt != 0             ) {
  638.       strcat(pbPBXMem->pbMSlotBuf, ANNOUNCELINE);
  639.       usRetCode = DosWriteMailslot(
  640.                           pbPBXMem->pbMSlotBuf,    // Mailslot
  641.                           pbPBXMem->pszPBXMsg,     // Message
  642.                           pbPBXMem->usPBXMsgSize,  // Message size
  643.                           9,                       // Priority
  644.                           2,                       // 2nd class
  645.                           0L);                     // No wait
  646.     }
  647.  
  648.   } while (usRetCode == ERROR_INTERRUPT   ||
  649.            usRetCode == ERROR_SEM_TIMEOUT  );
  650.  
  651.   // Control never arrives here, the ExitHandler always exits PBX
  652.   return;
  653. }
  654.