home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / sdktools / winnt / remote / srvmain.c < prev    next >
C/C++ Source or Header  |  1997-10-12  |  33KB  |  1,202 lines

  1.  
  2. /******************************************************************************\
  3. *       This is a part of the Microsoft Source Code Samples. 
  4. *       Copyright 1995 - 1997 Microsoft Corporation.
  5. *       All rights reserved. 
  6. *       This source code is only intended as a supplement to 
  7. *       Microsoft Development Tools and/or WinHelp documentation.
  8. *       See these sources for detailed information regarding the 
  9. *       Microsoft samples programs.
  10. \******************************************************************************/
  11.  
  12. /*++
  13.  
  14. Copyright (c) 1997  Microsoft Corporation
  15.  
  16. Module Name:
  17.  
  18.     SrvMain.c
  19.  
  20. Abstract:
  21.  
  22.     The server component of Remote. It spawns a child process
  23.     and redirects the stdin/stdout/stderr of child to itself.
  24.     Waits for connections from clients - passing the
  25.     output of child process to client and the input from clients
  26.     to child process.
  27.  
  28.     This version uses overlapped I/O to do in one thread what
  29.     the original uses 9 for.  Almost.  Because there is no way to
  30.     get overlapped stdin/stdout handles, two threads sit around
  31.     doing blocking I/O on stdin and stdout.  3 is better than 9.
  32.  
  33.     Unfortunately there's no CreatePipe()
  34.     or equivalent option to open an overlapped handle to an anonymous
  35.     pipe, so I stole the source for NT CreatePipe and hacked it to
  36.     accept flags indicating overlapped for one or both ends of the
  37.     anonymous pipe.  In our usage the child end handles are not
  38.     overlapped but the server end handles are.
  39.  
  40.  
  41. Author:
  42.  
  43.     Dave Hart  30 May 1997 after Server.c by
  44.     Rajivendra Nath  2-Jan-1992
  45.  
  46. Environment:
  47.  
  48.     Console App. User mode.
  49.  
  50. Revision History:
  51.  
  52. --*/
  53.  
  54. #include <windows.h>
  55. #include <stdio.h>
  56. #include <stdlib.h>
  57. #include <process.h>
  58. #include <io.h>
  59. #include <string.h>
  60. #if DBG
  61.     #undef NDEBUG           // so asserts work on chk builds
  62. #endif
  63. #include "Remote.h"
  64. #define SERVER_H_NOEXTERN
  65. #include "Server.h"
  66.  
  67.  
  68. DWORD cbRemoteClient = sizeof(REMOTE_CLIENT);  // for debugging
  69.  
  70.  
  71.  
  72. /*************************************************************/
  73. int
  74. OverlappedServer(                    //Main routine for server.
  75.     char* pszChildCmd,
  76.     char* pszPipeNameArg
  77.     )
  78. {
  79.     int    i;
  80.     BOOL   b;
  81.     DWORD  cWait;
  82.     DWORD  dwWait;
  83.     PREMOTE_CLIENT pClientRemove;
  84.  
  85. #if DBG
  86.     // Trace = -1;   // all TR_ bits on (and then some)
  87. #endif
  88.  
  89.     //
  90.     // Initialize globals
  91.     //
  92.  
  93.     pszPipeName = pszPipeNameArg;
  94.  
  95.     dwNextClientID = 1;           // local client will be 1
  96.     cConnectIns = CONNECT_COUNT;
  97.     cWait = MAX_WAIT_HANDLES;
  98.  
  99.     hHeap = HeapCreate(
  100.                 0,
  101.                 3 * sizeof(REMOTE_CLIENT),    // initial size
  102.                 3000 * sizeof(REMOTE_CLIENT)  // max
  103.                 );
  104.  
  105.     OsVersionInfo.dwOSVersionInfoSize = sizeof OsVersionInfo;
  106.     b = GetVersionEx(&OsVersionInfo);
  107.     ASSERT( b );
  108.  
  109.     printf("**************************************\n");
  110.     printf("***********     REMOTE    ************\n");
  111.     printf("***********     SERVER    ************\n");
  112.     printf("**************************************\n");
  113.     fflush(stdout);
  114.  
  115.  
  116.     //
  117.     // Setup the ACLs we need, taking into account any /u switches
  118.     //
  119.  
  120.     SetupSecurityDescriptors();
  121.  
  122.  
  123.     printf("To Connect: Remote /C %s %s\n\n", HostName, pszPipeName);
  124.     fflush(stdout);
  125.  
  126.  
  127.     //
  128.     // runtime link to NT-only kernel32 APIs so we can
  129.     // load on Win95 for client use.
  130.     //
  131.  
  132.     RuntimeLinkAPIs();
  133.  
  134.  
  135.     //
  136.     // Setup our three lists of clients:  handshaking,
  137.     // connected, and closing/closed.
  138.     //
  139.  
  140.     InitializeClientLists();
  141.  
  142.  
  143.     //
  144.     // set _REMOTE environment variable to the pipe name (why?)
  145.     //
  146.  
  147.     SetEnvironmentVariable("_REMOTE", pszPipeName);
  148.  
  149.  
  150.     //
  151.     // Create a tempfile for storing Child process output.
  152.     //
  153.  
  154.     {
  155.         char szTempDirectory[MAX_PATH + 1];
  156.  
  157.         GetTempPath(sizeof(szTempDirectory), szTempDirectory);
  158.  
  159.         //
  160.         // Before we litter the temp directory with more REMnnn.TMP
  161.         // files, let's delete all the orphaned ones we can.  This
  162.         // will fail for temp files open by other remote servers.
  163.         //
  164.  
  165.         CleanupTempFiles(szTempDirectory);
  166.  
  167.         GetTempFileName(szTempDirectory, "REM", 0, SaveFileName);
  168.     }
  169.  
  170.     if ( ! (hWriteTempFile =
  171.             CreateFile(
  172.                 SaveFileName,                       /* name of the file  */
  173.                 GENERIC_READ | GENERIC_WRITE,       /* access (read/write) mode */
  174.                 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode   */
  175.                 NULL,                               /* security descriptor  */
  176.                 CREATE_ALWAYS,                      /* how to create    */
  177.                 FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_NORMAL,
  178.                 NULL
  179.                 ))) {
  180.  
  181.         ErrorExit("Could not Create Temp File");
  182.     }
  183.  
  184.  
  185.     //
  186.     // We don't want to have multiple IN pipes created and
  187.     // awaiting connection simultaneously if there are
  188.     // multiple remote server processes sharing different
  189.     // sessions under the same pipe name.  This would be
  190.     // hairy for several reasons including breaking the
  191.     // current round-robin behavior of connections, since
  192.     // the oldest server pipe is connected first.  So
  193.     // we create/open a named event based on the pipe name and
  194.     // set the event so that any other remote servers on the
  195.     // same pipe will fall back to a single IN pipe listening.
  196.     //
  197.  
  198.     {
  199.         char szPerPipeEventName[1024];
  200.  
  201.         sprintf(
  202.             szPerPipeEventName,
  203.             "MSRemoteSrv%s",
  204.             pszPipeName
  205.             );
  206.  
  207.         rghWait[WAITIDX_PER_PIPE_EVENT] =
  208.             CreateEvent(
  209.                     &saPublic,  // security
  210.                     TRUE,       // manual reset (synchronization)
  211.                     FALSE,      // initially nonsignaled
  212.                     szPerPipeEventName
  213.                     );
  214.  
  215.         if (! rghWait[WAITIDX_PER_PIPE_EVENT]) {
  216.  
  217.             ErrorExit("Unable to create per-pipe event.");
  218.         }
  219.  
  220.         if (ERROR_ALREADY_EXISTS == GetLastError()) {
  221.  
  222.             TRACE(CONNECT, ("Found previous server on '%s', using 1 listening pipe.\n", pszPipeName));
  223.  
  224.             SetEvent(rghWait[WAITIDX_PER_PIPE_EVENT]);
  225.  
  226.             for (i = 1; i < (int) cConnectIns; i++) {
  227.  
  228.                 rghPipeIn[i] = INVALID_HANDLE_VALUE;
  229.             }
  230.  
  231.             cWait = MAX_WAIT_HANDLES - cConnectIns + 1;
  232.             cConnectIns = 1;
  233.  
  234.             //
  235.             // We don't want to wait on the event handle, but it's easier
  236.             // to have a handle in its slot, so dupe a handle to our own
  237.             // process.  Note we toss the value of the created event handle
  238.             // without closing it -- we want it to stay around but we're
  239.             // done with it.
  240.             //
  241.  
  242.             DuplicateHandle(
  243.                 GetCurrentProcess(),
  244.                 GetCurrentProcess(),
  245.                 GetCurrentProcess(),
  246.                 &rghWait[WAITIDX_PER_PIPE_EVENT],
  247.                 0,
  248.                 FALSE,
  249.                 DUPLICATE_SAME_ACCESS
  250.                 );
  251.  
  252.         }
  253.     }
  254.  
  255.  
  256.     //
  257.     // Create the event for the OVERLAPPED structure
  258.     // used by the main server thread for WriteFileSynch calls.
  259.     //
  260.  
  261.     olMainThread.hEvent =
  262.         CreateEvent(
  263.             NULL,      // security
  264.             TRUE,      // auto-reset
  265.             FALSE,     // initially nonsignaled
  266.             NULL       // unnamed
  267.             );
  268.  
  269.  
  270.     //
  271.     // Create the events for the OVERLAPPED structures
  272.     // used for ConnectNamedPipe operations.
  273.     //
  274.  
  275.     olConnectOut.hEvent =
  276.         rghWait[WAITIDX_CONNECT_OUT] =
  277.             CreateEvent(
  278.                 NULL,    // security
  279.                 TRUE,    // manual reset as ConnectNamedPipe demands
  280.                 FALSE,   // initially nonsignaled
  281.                 NULL
  282.                 );
  283.  
  284.     for (i = 0;
  285.          i < (int) cConnectIns;
  286.          i++) {
  287.  
  288.         rgolConnectIn[i].hEvent =
  289.             rghWait[WAITIDX_CONNECT_IN_BASE + i] =
  290.                 CreateEvent(
  291.                     NULL,    // security
  292.                     TRUE,    // manual reset as ConnectNamedPipe demands
  293.                     FALSE,   // initially nonsignaled
  294.                     NULL
  295.                     );
  296.  
  297.     }
  298.  
  299.  
  300.     //
  301.     // Create a timer we'll use to detect 2-pipe clients connected to
  302.     // OUT without ever connecting to IN so we can recycle our single
  303.     // OUT instance and allow other two-pipe clients in again.
  304.     // NT 3.51 doesn't have waitable timers, so we don't do that
  305.     // error handling on that OS.  Same as old remote.exe.
  306.     //
  307.  
  308.     if (pfnCreateWaitableTimer) {
  309.         hConnectOutTimer =
  310.             pfnCreateWaitableTimer(
  311.                 NULL,               // security
  312.                 FALSE,              // bManualReset, we want auto-reset
  313.                 NULL                // unnamed
  314.                 );
  315.     } else {
  316.         hConnectOutTimer = INVALID_HANDLE_VALUE;
  317.     }
  318.  
  319.  
  320.     //
  321.     // Start the command as a child process
  322.     //
  323.  
  324.     if (hAttachedProcess != INVALID_HANDLE_VALUE) {
  325.  
  326.         ChldProc = hAttachedProcess;
  327.         hWriteChildStdIn = hAttachedWriteChildStdIn;
  328.         hReadChildOutput = hAttachedReadChildStdOut;
  329.  
  330.     } else {
  331.  
  332.         ChldProc =
  333.              ForkChildProcess(
  334.                  ChildCmd,
  335.                  &hWriteChildStdIn,
  336.                  &hReadChildOutput
  337.                  );
  338.     }
  339.  
  340.     rghWait[WAITIDX_CHILD_PROCESS] = ChldProc;
  341.  
  342.     //
  343.     // Set ^c/^break handler.  It will kill the child process on
  344.     // ^break and pass ^c through to it.
  345.     //
  346.  
  347.     SetConsoleCtrlHandler(SrvCtrlHand, TRUE);
  348.  
  349.  
  350.     //
  351.     // Setup local session and start first read against its input.
  352.     // This starts a chain of completion routines that continues
  353.     // until this server exits.
  354.     //
  355.  
  356.     StartLocalSession();
  357.  
  358.  
  359.     //
  360.     // Start a read operation on the child output pipe.
  361.     // This starts a chain of completion routines that continues
  362.     // until the child terminates.
  363.     //
  364.  
  365.     StartChildOutPipeRead();
  366.  
  367.  
  368.     //
  369.     // Start several async ConnectNamedPipe operations, to reduce the chance
  370.     // of a client getting pipe busy errors.  Since there is no
  371.     // completion port version of ConnectNamedPipe, we'll wait on the
  372.     // events in the main loop below that indicate completion.
  373.     //
  374.  
  375.     CreatePipeAndIssueConnect(OUT_PIPE);
  376.  
  377.     for (i = 0;
  378.          i < (int) cConnectIns;
  379.          i++) {
  380.  
  381.         CreatePipeAndIssueConnect(i);
  382.     }
  383.  
  384.  
  385.     InitAd(IsAdvertise);
  386.  
  387.  
  388.     //
  389.     // We may need to service the query pipe for remote /q clients.
  390.     //
  391.  
  392.     InitializeQueryServer();
  393.  
  394.  
  395.     //
  396.     // main loop of thread, waits for ConnectNamedPipe completions
  397.     // and handles them while remaining alertable for completion
  398.     // routines to get called.
  399.     //
  400.  
  401.     while (1) {
  402.  
  403.         dwWait =
  404.             WaitForMultipleObjectsEx(
  405.                 cWait,
  406.                 rghWait,
  407.                 FALSE,          // wait on any handle, not all
  408.                 30 * 1000,      // ms
  409.                 TRUE            // alertable (completion routines)
  410.                 );
  411.  
  412.  
  413.         if (WAIT_IO_COMPLETION == dwWait) {
  414.  
  415.             //
  416.             // A completion routine was called.
  417.             //
  418.  
  419.             continue;
  420.         }
  421.  
  422.  
  423.         if (WAIT_TIMEOUT == dwWait) {
  424.  
  425.             //
  426.             // Presumably since we've timed out for 30 seconds
  427.             // with no IO completion, closing clients have
  428.             // finished any pending IOs and the memory can be
  429.             // released.
  430.             //
  431.  
  432.             while (pClientRemove = RemoveFirstClientFromClosingList()) {
  433.  
  434.                 HeapFree(hHeap, 0, pClientRemove);
  435.             }
  436.  
  437.             continue;
  438.         }
  439.  
  440.  
  441.         if (WAITIDX_CONNECT_OUT == dwWait) {
  442.  
  443.             HandleOutPipeConnected();
  444.             continue;
  445.         }
  446.  
  447.  
  448.         if (WAITIDX_CONNECT_IN_BASE <= dwWait &&
  449.             (WAITIDX_CONNECT_IN_BASE + CONNECT_COUNT) > dwWait) {
  450.  
  451.             HandleInPipeConnected( dwWait - WAITIDX_CONNECT_IN_BASE );
  452.             continue;
  453.         }
  454.  
  455.  
  456.         if (WAITIDX_QUERYSRV_WAIT == dwWait ||
  457.             WAITIDX_QUERYSRV_WAIT + WAIT_ABANDONED_0 == dwWait ) {
  458.  
  459.             //
  460.             // The remote server which was handling the query pipe
  461.             // has gone away.  We'll try to take over.
  462.             //
  463.  
  464.             QueryWaitCompleted();
  465.  
  466.             continue;
  467.         }
  468.  
  469.  
  470.         if (WAITIDX_PER_PIPE_EVENT == dwWait) {
  471.  
  472.             //
  473.             // Another server is starting on this same
  474.             // pipename.  To be most compatible we need
  475.             // to fall back to listening on only one
  476.             // IN pipe instance.
  477.             //
  478.  
  479.             if (1 != cConnectIns) {
  480.  
  481.                 TRACE(CONNECT,
  482.                       ("Another server starting on '%s', falling back to 1 IN listening pipe.\n",
  483.                        pszPipeName
  484.                        ));
  485.  
  486.                 for (i = 1; i < (int) cConnectIns; i++) {
  487.  
  488.                     CANCELIO( rghPipeIn[i] );
  489.                     DisconnectNamedPipe( rghPipeIn[i] );
  490.                     CloseHandle( rghPipeIn[i] );
  491.                     rghPipeIn[i] = INVALID_HANDLE_VALUE;
  492.  
  493.                 }
  494.  
  495.                 cWait = MAX_WAIT_HANDLES - cConnectIns + 1;
  496.  
  497.                 cConnectIns = 1;
  498.  
  499.                 //
  500.                 // We don't want to wait on the event handle, but it's easier
  501.                 // to have a handle in its slot, so dupe a handle to our own
  502.                 // process.  We toss the event handle without closing it so
  503.                 // it will stay around for future remote servers on the same
  504.                 // pipe name.
  505.                 //
  506.  
  507.                 DuplicateHandle(
  508.                     GetCurrentProcess(),
  509.                     GetCurrentProcess(),
  510.                     GetCurrentProcess(),
  511.                     &rghWait[WAITIDX_PER_PIPE_EVENT],
  512.                     0,
  513.                     FALSE,
  514.                     DUPLICATE_SAME_ACCESS
  515.                     );
  516.             }
  517.  
  518.             continue;
  519.         }
  520.  
  521.         if (WAITIDX_CHILD_PROCESS == dwWait ||
  522.             WAITIDX_READ_STDIN_DONE == dwWait) {
  523.  
  524.             if (INVALID_HANDLE_VALUE != hConnectOutTimer) {
  525.  
  526.                 CloseHandle(hConnectOutTimer);
  527.                 hConnectOutTimer = INVALID_HANDLE_VALUE;
  528.             }
  529.  
  530.             //
  531.             // Cancel ConnectNamedPipe operations and close
  532.             // the pipes
  533.             //
  534.  
  535.             if (INVALID_HANDLE_VALUE != hPipeOut) {
  536.  
  537.                 DisconnectNamedPipe( hPipeOut );
  538.                 CANCELIO( hPipeOut );
  539.                 CloseHandle( rghWait[WAITIDX_CONNECT_OUT] );
  540.                 rghWait[WAITIDX_CONNECT_OUT] = INVALID_HANDLE_VALUE;
  541.             }
  542.  
  543.             for (i = 0;
  544.                  i < (int) cConnectIns;
  545.                  i++) {
  546.  
  547.                 if (INVALID_HANDLE_VALUE != rghPipeIn[i]) {
  548.  
  549.                     TRACE(CONNECT, ("Tearing down listening IN pipe #%d.\n", i + 1));
  550.  
  551.                     DisconnectNamedPipe( rghPipeIn[i] );
  552.                     CANCELIO( rghPipeIn[i] );
  553.                     CloseHandle( rghPipeIn[i] );
  554.                     rghPipeIn[i] = INVALID_HANDLE_VALUE;
  555.                 }
  556.  
  557.             }
  558.  
  559.             //
  560.             // Cancel read against child process in/out pipes
  561.             //
  562.  
  563.             if (INVALID_HANDLE_VALUE != hWriteChildStdIn) {
  564.  
  565.                 CANCELIO( hWriteChildStdIn );
  566.                 CloseHandle( hWriteChildStdIn );
  567.                 hWriteChildStdIn = INVALID_HANDLE_VALUE;
  568.             }
  569.  
  570.             if (INVALID_HANDLE_VALUE != hReadChildOutput) {
  571.  
  572.                 CANCELIO( hReadChildOutput );
  573.                 CloseHandle( hReadChildOutput );
  574.                 hReadChildOutput = INVALID_HANDLE_VALUE;
  575.             }
  576.  
  577.             //
  578.             // Cancel client I/Os
  579.             //
  580.  
  581.             bShuttingDownServer = TRUE;
  582.  
  583.             //
  584.             // Note that CloseClient will remove entries from this list,
  585.             // so we walk it starting at the head at each step.
  586.             //
  587.  
  588.             for (pClientRemove = (PREMOTE_CLIENT) ClientListHead.Flink;
  589.                  pClientRemove != (PREMOTE_CLIENT) &ClientListHead;
  590.                  pClientRemove = (PREMOTE_CLIENT)  ClientListHead.Flink ) {
  591.  
  592.                 CloseClient(pClientRemove);
  593.             }
  594.  
  595.             //
  596.             // on our way out...
  597.             //
  598.  
  599.             break;
  600.         }
  601.  
  602.         //
  603.         // Unexpected WaitForMulipleObjectsEx return
  604.         //
  605.  
  606.         printf("Remote: unknown wait return %d\n", dwWait);
  607.         ErrorExit("fix srvmain.c");
  608.  
  609.     } // endless loop
  610.  
  611.  
  612.     ShutAd(IsAdvertise);
  613.  
  614.     while (i = 0, GetExitCodeProcess(ChldProc, &i) &&
  615.            STILL_ACTIVE == i) {
  616.  
  617.         printf("\nRemote: Waiting for child to exit.\n");
  618.         WaitForSingleObjectEx(ChldProc, 10 * 1000, TRUE);
  619.     }
  620.  
  621.     //
  622.     // For some interesting reason when we're attached to
  623.     // a debugger like ntsd and it exits, our printf
  624.     // below comes out *after* the cmd.exe prompt, making
  625.     // it look like we hung on exit even though cmd.exe is
  626.     // patiently awaiting a command.  So suppress it.
  627.     //
  628.  
  629.     if (hAttachedProcess == INVALID_HANDLE_VALUE) {
  630.         printf("\nRemote exiting. Child (%s) exit code was %d.\n", ChildCmd, i);
  631.     }
  632.  
  633.     CANCELIO(hWriteTempFile);
  634.     CloseHandle(hWriteTempFile);
  635.     hWriteTempFile = INVALID_HANDLE_VALUE;
  636.  
  637.     //
  638.     // Flush any pending completion routines.
  639.     //
  640.  
  641.     while (WAIT_IO_COMPLETION == SleepEx(50, TRUE)) {
  642.         ;
  643.     }
  644.  
  645.     if (!DeleteFile(SaveFileName)) {
  646.  
  647.         printf("Remote: Temp File %s not deleted..\n",SaveFileName);
  648.     }
  649.  
  650.     return i;
  651. }
  652.  
  653.  
  654.  
  655. VOID
  656. FASTCALL
  657. StartLocalSession(
  658.     VOID
  659.     )
  660. {
  661.     DWORD dwThreadId;
  662.     char szHexAsciiId[9];
  663.  
  664.     pLocalClient = HeapAlloc(
  665.                        hHeap,
  666.                        HEAP_ZERO_MEMORY,
  667.                        sizeof(*pLocalClient)
  668.                        );
  669.  
  670.     if (!pLocalClient) {
  671.  
  672.         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  673.         ErrorExit("Unable to allocate local client.");
  674.     }
  675.  
  676.     pLocalClient->dwID = dwNextClientID++;
  677.     sprintf(szHexAsciiId, "%08x", pLocalClient->dwID);
  678.     CopyMemory(pLocalClient->HexAsciiId, szHexAsciiId, sizeof(pLocalClient->HexAsciiId));
  679.  
  680.     strcpy(pLocalClient->Name, "Local");
  681.     pLocalClient->ServerFlags = SFLG_LOCAL;
  682.  
  683.  
  684.     //
  685.     // we need overlapped handles to stdin/stdout,
  686.     // and woefully DuplicateHandle can't do it.
  687.     // So we'll create two anonymous pipes and two
  688.     // threads to shuffle data between stdin/stdout
  689.     // and the pipes.  The server end of the pipes
  690.     // is opened overlapped, the "client" end (used
  691.     // by the threads) is not overlapped.
  692.     //
  693.  
  694.  
  695.     rgCopyPipe[0].hRead = GetStdHandle(STD_INPUT_HANDLE);
  696.     if ( ! MyCreatePipeEx(&pLocalClient->PipeReadH, &rgCopyPipe[0].hWrite, NULL, 0, FILE_FLAG_OVERLAPPED, 0)) {
  697.         ErrorExit("Cannot create local input pipe");
  698.     }
  699.  
  700.     rgCopyPipe[1].hWrite = GetStdHandle(STD_OUTPUT_HANDLE);
  701.     if ( ! MyCreatePipeEx(&rgCopyPipe[1].hRead, &pLocalClient->PipeWriteH, NULL, 0, 0, FILE_FLAG_OVERLAPPED)) {
  702.         ErrorExit("Cannot create local output pipe");
  703.     }
  704.  
  705.     rghWait[WAITIDX_READ_STDIN_DONE] = (HANDLE)
  706.         _beginthreadex(
  707.             NULL,                    // security
  708.             0,                       // default stack size
  709.             CopyPipeToPipe,          // proc
  710.             (LPVOID) &rgCopyPipe[0], // parm
  711.             0,                       // flags
  712.             &dwThreadId
  713.             );
  714.  
  715.     CloseHandle( (HANDLE)
  716.         _beginthreadex(
  717.             NULL,                    // security
  718.             0,                       // default stack size
  719.             CopyPipeToPipe,          // proc
  720.             (LPVOID) &rgCopyPipe[1], // parm
  721.             0,                       // flags
  722.             &dwThreadId
  723.             )
  724.         );
  725.  
  726.  
  727.     StartSession( pLocalClient );
  728. }
  729.  
  730.  
  731. //
  732. // Two of these threads to deal with non-overlapped stdin/stdout.
  733. // CRT is OK.
  734. //
  735.  
  736. DWORD
  737. WINAPI
  738. CopyPipeToPipe(
  739.     LPVOID   lpCopyPipeData
  740.     )
  741. {
  742.     PCOPYPIPE psd = (PCOPYPIPE) lpCopyPipeData;
  743.     DWORD cb;
  744.     char achBuffer[BUFFSIZE];
  745.  
  746.     while (1) {
  747.         if ( ! ReadFile(
  748.                    psd->hRead,
  749.                    achBuffer,
  750.                    sizeof(achBuffer),
  751.                    &cb,
  752.                    NULL
  753.                    )) {
  754.  
  755.             TRACE(COPYPIPE, ("CopyPipeToPipe ReadFile %s failed, exiting thread.\n",
  756.                              (psd == &rgCopyPipe[0])
  757.                                  ? "stdin"
  758.                                  : "local client output pipe"));
  759.             break;
  760.         }
  761.  
  762.         if ( ! WriteFile(
  763.                    psd->hWrite,
  764.                    achBuffer,
  765.                    cb,
  766.                    &cb,
  767.                    NULL
  768.                    )) {
  769.  
  770.             TRACE(COPYPIPE, ("CopyPipeToPipe WriteFile %s failed, exiting thread.\n",
  771.                              (psd == &rgCopyPipe[0])
  772.                                  ? "local client input pipe"
  773.                                  : "stdout"));
  774.             break;
  775.         }
  776.     }
  777.  
  778.     return 0;
  779. }
  780.  
  781.  
  782. VOID
  783. FASTCALL
  784. StartSession(
  785.     PREMOTE_CLIENT pClient
  786.     )
  787. {
  788.     pClient->rSaveFile =
  789.         CreateFile(
  790.             SaveFileName,
  791.             GENERIC_READ,
  792.             FILE_SHARE_READ | FILE_SHARE_WRITE,
  793.             NULL,
  794.             OPEN_EXISTING,
  795.             FILE_FLAG_OVERLAPPED,
  796.             NULL
  797.             );
  798.  
  799.     if ( ! pClient->rSaveFile) {
  800.  
  801.         printf("Remote:Cannot open ReadHandle to temp file:%d\n",GetLastError());
  802.  
  803.     } else {
  804.  
  805.         pClient->UserName[0] = 0;
  806.  
  807.         GetNamedPipeHandleState(
  808.             pClient->PipeReadH,
  809.             NULL,
  810.             NULL,
  811.             NULL,
  812.             NULL,
  813.             pClient->UserName,
  814.             sizeof(pClient->UserName)
  815.             );
  816.  
  817.         //
  818.         // For every client except the local
  819.         // stdin/stdout client, there's a copy of remote.exe
  820.         // running in client mode on the other side.  Do
  821.         // handshaking with it to setup options and check
  822.         // versions.  HandshakeWithRemoteClient will start
  823.         // the "normal" I/O cycle once the handshake cycle is
  824.         // done.  Note it returns as soon as the first handshake
  825.         // I/O is submitted.
  826.         //
  827.  
  828.         if (pClient->ServerFlags & SFLG_LOCAL) {
  829.  
  830.             AddClientToHandshakingList(pClient);
  831.             MoveClientToNormalList(pClient);
  832.  
  833.             //
  834.             // Start read operation against this client's input.
  835.             //
  836.  
  837.             StartReadClientInput(pClient);
  838.  
  839.             //
  840.             // Start write cycle for client output from the temp
  841.             // file.
  842.             //
  843.  
  844.             StartReadTempFile(pClient);
  845.  
  846.         } else {
  847.  
  848.             HandshakeWithRemoteClient(pClient);
  849.         }
  850.     }
  851. }
  852.  
  853.  
  854.  
  855.  
  856. VOID
  857. FASTCALL
  858. CreatePipeAndIssueConnect(
  859.     int  nIndex   // IN pipe index or OUT_PIPE
  860.     )
  861. {
  862.     BOOL b;
  863.     DWORD dwError;
  864.     char szPipeName[BUFFSIZE];
  865.  
  866.  
  867.     if (OUT_PIPE == nIndex) {
  868.         TRACE(CONNECT, ("Creating listening OUT pipe.\n"));
  869.     } else {
  870.         TRACE(CONNECT, ("Creating listening IN pipe #%d.\n", nIndex + 1));
  871.     }
  872.  
  873.     if (OUT_PIPE == nIndex) {
  874.  
  875.         sprintf(szPipeName, SERVER_WRITE_PIPE, ".", pszPipeName);
  876.  
  877.         hPipeOut =
  878.             CreateNamedPipe(
  879.                 szPipeName,
  880.                 PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED,
  881.                 PIPE_TYPE_BYTE,
  882.                 PIPE_UNLIMITED_INSTANCES,
  883.                 0,
  884.                 0,
  885.                 0,
  886.                 &saPipe
  887.                 );
  888.  
  889.         if (INVALID_HANDLE_VALUE == hPipeOut) {
  890.  
  891.             ErrorExit("Unable to CreateNamedPipe OUT");
  892.         }
  893.  
  894.         b = ConnectNamedPipe(hPipeOut, &olConnectOut);
  895.  
  896.  
  897.         if ( ! b ) {
  898.  
  899.             dwError = GetLastError();
  900.  
  901.             if (ERROR_PIPE_CONNECTED == dwError) {
  902.  
  903.                 b = TRUE;
  904.             }
  905.         }
  906.  
  907.         if ( b ) {
  908.  
  909.             TRACE(CONNECT, ("Quick connect on OUT pipe.\n"));
  910.  
  911.             HandleOutPipeConnected();
  912.  
  913.         } else {
  914.  
  915.             if (ERROR_IO_PENDING != dwError) {
  916.  
  917.                 ErrorExit("ConnectNamedPipe out failed");
  918.             }
  919.         }
  920.  
  921.     } else {
  922.  
  923.         sprintf(szPipeName, SERVER_READ_PIPE, ".", pszPipeName);
  924.  
  925.         rghPipeIn[nIndex] =
  926.             CreateNamedPipe(
  927.                 szPipeName,
  928.                 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
  929.                 PIPE_TYPE_BYTE,
  930.                 PIPE_UNLIMITED_INSTANCES,
  931.                 0,
  932.                 0,
  933.                 0,
  934.                 &saPipe
  935.                 );
  936.  
  937.         if (INVALID_HANDLE_VALUE == rghPipeIn[nIndex]) {
  938.  
  939.             if (ERROR_ACCESS_DENIED == GetLastError()) {
  940.                 if (DaclNameCount) {
  941.                     ErrorExit("Unable to CreateNamedPipe, are YOU in the list of permitted users?");
  942.                 } else {
  943.                     ErrorExit("Unable to CreateNamedPipe, maybe old remote server on same pipe name?");
  944.                 }
  945.             } else {
  946.                 ErrorExit("Unable to CreateNamedPipe IN");
  947.             }
  948.         }
  949.  
  950.         b = ConnectNamedPipe(rghPipeIn[nIndex], &rgolConnectIn[nIndex]);
  951.  
  952.         if ( ! b ) {
  953.  
  954.             dwError = GetLastError();
  955.  
  956.             if (ERROR_PIPE_CONNECTED == dwError) {
  957.                 b = TRUE;
  958.             }
  959.         }
  960.  
  961.         if ( b ) {
  962.  
  963.             TRACE(CONNECT, ("Quick connect on IN pipe #%d.\n", nIndex));
  964.  
  965.             HandleInPipeConnected(nIndex);
  966.  
  967.         } else {
  968.  
  969.             if (ERROR_IO_PENDING != dwError) {
  970.  
  971.                 ErrorExit("ConnectNamedPipe in failed");
  972.             }
  973.         }
  974.  
  975.     }
  976.  
  977.     if (OUT_PIPE == nIndex) {
  978.         TRACE(CONNECT, ("Listening OUT pipe handle %x.\n", hPipeOut));
  979.     } else {
  980.         TRACE(CONNECT, ("Listening IN pipe #%d handle %x.\n", nIndex + 1, rghPipeIn[nIndex]));
  981.     }
  982. }
  983.  
  984.  
  985. VOID
  986. FASTCALL
  987. HandleOutPipeConnected(
  988.     VOID
  989.     )
  990. {
  991.     LARGE_INTEGER DueTime;
  992.  
  993.     ResetEvent(rghWait[WAITIDX_CONNECT_OUT]);
  994.  
  995.     bOutPipeConnected = TRUE;
  996.  
  997.     TRACE(CONNECT, ("Two-pipe caller connected to OUT pipe %x.\n",
  998.                     hPipeOut));
  999.  
  1000.     //
  1001.     // Start a 1 minute timer in case we don't get a connection
  1002.     // on an IN pipe from this client, we'll recycle the OUT
  1003.     // pipe.
  1004.     //
  1005.  
  1006.     if (INVALID_HANDLE_VALUE != hConnectOutTimer) {
  1007.  
  1008.         DueTime.QuadPart = Int32x32To64(60 * 1000, -10000);
  1009.  
  1010.         pfnSetWaitableTimer(
  1011.             hConnectOutTimer,
  1012.             &DueTime,
  1013.             0,                     // not periodic, single-fire
  1014.             ConnectOutTimerFired,
  1015.             0,                     // arg to compl. rtn
  1016.             TRUE
  1017.             );
  1018.     }
  1019. }
  1020.  
  1021.  
  1022. VOID
  1023. APIENTRY
  1024. ConnectOutTimerFired(
  1025.     LPVOID pArg,
  1026.     DWORD  dwTimerLo,
  1027.     DWORD  dwTimerHi
  1028.     )
  1029. {
  1030.     UNREFERENCED_PARAMETER( pArg );
  1031.     UNREFERENCED_PARAMETER( dwTimerLo );
  1032.     UNREFERENCED_PARAMETER( dwTimerHi );
  1033.  
  1034.     //
  1035.     // We've had a connected OUT pipe for a minute now,
  1036.     // only two-pipe clients connect to that and they
  1037.     // immediately connect to IN afterwards.  Presumably
  1038.     // the client died between these two operations.  Until
  1039.     // we recycle the OUT pipe all two-pipe clients are
  1040.     // unable to connect getting pipe busy errors.
  1041.     //
  1042.  
  1043.     if ( ! bOutPipeConnected ) {
  1044.  
  1045.         TRACE(CONNECT, ("ConnectOut timer fired but Out pipe not connected.\n"));
  1046.         return;
  1047.     }
  1048.  
  1049.     TRACE(CONNECT, ("Two-pipe caller hung for 1 minute, recycling OUT pipe %x.\n",
  1050.                     hPipeOut));
  1051.  
  1052.     bOutPipeConnected = FALSE;
  1053.  
  1054.     CANCELIO(hPipeOut);
  1055.     DisconnectNamedPipe(hPipeOut);
  1056.     CloseHandle(hPipeOut);
  1057.     hPipeOut = INVALID_HANDLE_VALUE;
  1058.  
  1059.     CreatePipeAndIssueConnect(OUT_PIPE);
  1060.  
  1061.     //
  1062.     // In order for things to work reliably for 2-pipe clients
  1063.     // when there are multiple remote servers on the same pipename,
  1064.     // we need to tear down the listening IN pipe and recreate it so
  1065.     // that the oldest listening OUT pipe will be from the same process
  1066.     // as the oldest listening IN pipe.
  1067.     //
  1068.  
  1069.     if (1 == cConnectIns) {
  1070.  
  1071.         TRACE(CONNECT, ("Recycling IN pipe %x as well for round-robin behavior.\n",
  1072.                         rghPipeIn[0]));
  1073.  
  1074.         CANCELIO(rghPipeIn[0]);
  1075.         DisconnectNamedPipe(rghPipeIn[0]);
  1076.         CloseHandle(rghPipeIn[0]);
  1077.         rghPipeIn[0] = INVALID_HANDLE_VALUE;
  1078.  
  1079.         CreatePipeAndIssueConnect(0);
  1080.     }
  1081. }
  1082.  
  1083.  
  1084. VOID
  1085. FASTCALL
  1086. HandleInPipeConnected(
  1087.     int nIndex
  1088.     )
  1089. {
  1090.     PREMOTE_CLIENT pClient;
  1091.     char szHexAsciiId[9];
  1092.  
  1093.     ResetEvent(rghWait[WAITIDX_CONNECT_IN_BASE + nIndex]);
  1094.  
  1095.     if (nIndex >= (int) cConnectIns) {
  1096.  
  1097.         //
  1098.         // The I/O was cancelled on the excess
  1099.         // listening pipes, causing the event to
  1100.         // fire.
  1101.         //
  1102.  
  1103.         ASSERT(INVALID_HANDLE_VALUE == rghPipeIn[nIndex]);
  1104.  
  1105.         TRACE(CONNECT, ("IN pipe #%d, handle %x listen cancelled.\n",
  1106.                         nIndex + 1, rghPipeIn[nIndex]));
  1107.  
  1108.         return;
  1109.     }
  1110.  
  1111.  
  1112.     TRACE(CONNECT, ("Caller connected to IN pipe #%d, handle %x.\n",
  1113.                     nIndex + 1, rghPipeIn[nIndex]));
  1114.  
  1115.     //
  1116.     // A client is fully connected, but we don't know if
  1117.     // it's a single-pipe or two-pipe client.  Until
  1118.     // we do its PipeWriteH will be invalid.  We'll figure
  1119.     // it out in ReadClientNameCompleted.
  1120.     //
  1121.  
  1122.     pClient = HeapAlloc(
  1123.                   hHeap,
  1124.                   HEAP_ZERO_MEMORY,
  1125.                   sizeof(*pClient)
  1126.                   );
  1127.  
  1128.     if ( ! pClient) {
  1129.  
  1130.         printf("Out of memory connecting client, hanging up.\n");
  1131.  
  1132.         CloseHandle( rghPipeIn[nIndex] );
  1133.         rghPipeIn[nIndex] = INVALID_HANDLE_VALUE;
  1134.         CreatePipeAndIssueConnect( nIndex );
  1135.  
  1136.  
  1137.         if (bOutPipeConnected) {
  1138.  
  1139.              //
  1140.              // Hang up on the two-pipe caller connected to the
  1141.              // OUT pipe as well -- it may be this client or it
  1142.              // may be another, no way to tell, and really no
  1143.              // great need to because if it's another caller
  1144.              // we probably wouldn't be able to allocate memory
  1145.              // for it either.
  1146.              //
  1147.              // Also if we're using a single IN pipe for
  1148.              // multiple-server round-robin behavior we
  1149.              // want to recycle both pipes at the same time.
  1150.              //
  1151.  
  1152.             TRACE(CONNECT, ("Also hanging up on connected two-pipe caller on OUT pipe %x.\n",
  1153.                             hPipeOut));
  1154.  
  1155.             bOutPipeConnected = FALSE;
  1156.  
  1157.             if (INVALID_HANDLE_VALUE != hConnectOutTimer) {
  1158.                  pfnCancelWaitableTimer(hConnectOutTimer);
  1159.             }
  1160.  
  1161.             DisconnectNamedPipe(hPipeOut);
  1162.             CloseHandle(hPipeOut);
  1163.             hPipeOut = INVALID_HANDLE_VALUE;
  1164.  
  1165.             CreatePipeAndIssueConnect( OUT_PIPE );
  1166.         }
  1167.  
  1168.     } else {
  1169.  
  1170.         //
  1171.         // Initialize the Client
  1172.         //
  1173.  
  1174.         pClient->dwID = dwNextClientID++;
  1175.         sprintf(szHexAsciiId, "%08x", pClient->dwID);
  1176.         CopyMemory(pClient->HexAsciiId, szHexAsciiId, sizeof(pClient->HexAsciiId));
  1177.  
  1178.         pClient->PipeReadH   = rghPipeIn[nIndex];
  1179.         rghPipeIn[nIndex] = INVALID_HANDLE_VALUE;
  1180.  
  1181.         pClient->PipeWriteH  = INVALID_HANDLE_VALUE;
  1182.  
  1183.         TRACE(CONNECT, ("Handshaking new client %d (%x) on IN pipe handle %x.\n",
  1184.                         pClient->dwID, pClient, pClient->PipeReadH));
  1185.  
  1186.         //
  1187.         // Start another connect operation to replace this completed one.
  1188.         //
  1189.  
  1190.         CreatePipeAndIssueConnect( nIndex );
  1191.  
  1192.         //
  1193.         // Start session I/Os with the new client.  This will link it
  1194.         // into the handshaking list.
  1195.         //
  1196.  
  1197.         StartSession( pClient );
  1198.  
  1199.     }
  1200.  
  1201. }
  1202.