Index: /trunk/src/kmk/kmkbuiltin/redirect.c
===================================================================
--- /trunk/src/kmk/kmkbuiltin/redirect.c	(revision 3209)
+++ /trunk/src/kmk/kmkbuiltin/redirect.c	(revision 3210)
@@ -114,5 +114,5 @@
 {
     kmk_builtin_ctx_printf(pCtx, fIsErr,
-                           "usage: %s [-[rwa+tb]<fd> <file>] [-d<fd>=<src-fd>] [-c<fd>]\n"
+                           "usage: %s [-[rwa+tb]<fd> <file>] [-d<fd>=<src-fd>] [-c<fd>] [--stdin-pipe]\n"
                            "           [-Z] [-E <var=val>] [-C <dir>] [--wcc-brain-damage]\n"
                            "           [-v] -- <program> [args]\n"
@@ -127,8 +127,11 @@
                            "\n"
                            "The -d switch duplicate the right hand file descriptor (src-fd) to the left\n"
-                           "hand side one (fd). The latter is limited to standard handles on windows.\n"
+                           "hand side one (fd).  The latter is limited to standard handles on windows.\n"
                            "\n"
                            "The -c switch will close the specified file descriptor. Limited to standard\n"
                            "handles on windows.\n"
+                           "\n"
+                           "The --stdin-pipe switch will replace stdin with the read end of an anonymous\n"
+                           "pipe.  This is for tricking things like rsh.exe that blocks reading on stdin.\n"
                            "\n"
                            "The -Z switch zaps the environment.\n"
@@ -178,4 +181,6 @@
     /** The filename - NULL if close only. */
     const char *pszFilename;
+    /** The other pipe end, needs closing in cleanup. */
+    int         fdOtherPipeEnd;
 #ifndef USE_POSIX_SPAWN
     /** Saved file descriptor. */
@@ -197,4 +202,75 @@
     return K_FALSE;
 #endif
+}
+
+
+/**
+ * Creates a pair of pipe descriptors that does not conflict with any previous
+ * orders.
+ *
+ * The pipe is open with both descriptors being inherited by the child as it's
+ * supposed to be a dummy pipe for stdin that won't break.
+ *
+ * @returns 0 on success, exit code on failure (error message displayed).
+ * @param   pCtx                The command execution context.
+ * @param   paFds               Where to return the pipe descriptors
+ * @param   cOrders             The number of orders.
+ * @param   paOrders            The order array.
+ * @param   fdTarget            The target descriptor (0).
+ */
+static int kRedirectCreateStdInPipeWithoutConflict(PKMKBUILTINCTX pCtx, int paFds[2],
+                                                   unsigned cOrders, REDIRECTORDERS *paOrders, int fdTarget)
+{
+    struct
+    {
+        int     aFds[2];
+    }           aTries[32];
+    unsigned    cTries = 0;
+
+    while (cTries < K_ELEMENTS(aTries))
+    {
+#ifdef _MSC_VER
+        int rc = _pipe(aTries[cTries].aFds, 0, _O_BINARY);
+#else
+        int rc = pipe(aTries[cTries].aFds);
+#endif
+        if (rc >= 0)
+        {
+            if (   !kRedirectHasConflict(aTries[cTries].aFds[0], cOrders, paOrders)
+                && !kRedirectHasConflict(aTries[cTries].aFds[1], cOrders, paOrders)
+#ifndef _MSC_VER
+                && aTries[cTries].aFds[0] != fdTarget
+                && aTries[cTries].aFds[1] != fdTarget
+#endif
+                )
+            {
+                paFds[0] = aTries[cTries].aFds[0];
+                paFds[1] = aTries[cTries].aFds[1];
+
+                while (cTries-- > 0)
+                {
+                    close(aTries[cTries].aFds[0]);
+                    close(aTries[cTries].aFds[1]);
+                }
+                return 0;
+            }
+        }
+        else
+        {
+            err(pCtx, -1, "failed to create stdin pipe (try #%u)", cTries + 1);
+            break;
+        }
+        cTries++;
+    }
+    if (cTries >= K_ELEMENTS(aTries))
+        errx(pCtx, -1, "failed to find a conflict free pair of pipe descriptor for stdin!");
+
+    /* cleanup */
+    while (cTries-- > 0)
+    {
+        close(aTries[cTries].aFds[0]);
+        close(aTries[cTries].aFds[1]);
+    }
+    return 1;
 }
 
@@ -331,4 +407,11 @@
             close(paOrders[i].fdSource);
             paOrders[i].fdSource = -1;
+
+            if (paOrders[i].fdOtherPipeEnd >= 0)
+            {
+                close(paOrders[i].fdOtherPipeEnd);
+                paOrders[i].fdOtherPipeEnd = -1;
+            }
+
             if (   fFailure
                 && paOrders[i].fRemoveOnFailure
@@ -920,4 +1003,6 @@
                                    FALSE /*fInheritHandles*/, CREATE_SUSPENDED, pszzEnv, pszCwd, &StartupInfo, &ProcInfo))
                 {
+                    unsigned  i;
+
                     /* Inject the handles and try make it start executing. */
                     char szErrMsg[128];
@@ -927,4 +1012,16 @@
                     else if (!ResumeThread(ProcInfo.hThread))
                         rc = errx(pCtx, 10, "ResumeThread failed: %u", GetLastError());
+
+                    /* Duplicate the write end of any stdin pipe handles into the child. */
+                    for (i = 0; i < cOrders; i++)
+                        if (paOrders[cOrders].fdOtherPipeEnd >= 0)
+                        {
+                            HANDLE hIgnored = INVALID_HANDLE_VALUE;
+                            HANDLE hPipeW   = (HANDLE)_get_osfhandle(paOrders[i].fdOtherPipeEnd);
+                            if (!DuplicateHandle(GetCurrentProcess(), hPipeW, ProcInfo.hProcess, &hIgnored, 0 /*fDesiredAccess*/,
+                                                 TRUE /*fInheritable*/, DUPLICATE_SAME_ACCESS))
+                                rc = errx(pCtx, 10, "DuplicateHandle failed on other stdin pipe end %d/%p: %u",
+                                          paOrders[i].fdOtherPipeEnd, hPipeW, GetLastError());
+                        }
 
                     /* Kill it if any of that fails. */
@@ -1365,4 +1462,6 @@
                 else if (strcmp(pszArg, "verbose") == 0)
                     chOpt = 'v';
+                else if (strcmp(pszArg, "stdin-pipe") == 0)
+                    chOpt = 'I';
                 else
                 {
@@ -1560,5 +1659,5 @@
 
             /*
-             * Okay, it is some file descriptor opearation.  Make sure we've got room for it.
+             * Okay, it is some file descriptor operation.  Make sure we've got room for it.
              */
             if (cOrders + 1 < K_ELEMENTS(aOrders))
@@ -1569,4 +1668,5 @@
                 aOrders[cOrders].fRemoveOnFailure = 0;
                 aOrders[cOrders].pszFilename      = NULL;
+                aOrders[cOrders].fdOtherPipeEnd   = -1;
 #ifndef USE_POSIX_SPAWN
                 aOrders[cOrders].fdSaved          = -1;
@@ -1643,4 +1743,25 @@
 #endif
                     }
+                }
+            }
+            else if (chOpt == 'I')
+            {
+                /*
+                 * Replace stdin with the read end of an anonymous pipe.
+                 */
+                int aFds[2] = { -1, -1 };
+                rcExit = kRedirectCreateStdInPipeWithoutConflict(pCtx, aFds, cOrders, aOrders,  0 /*fdTarget*/);
+                if (rcExit == 0)
+                {
+                    aOrders[cOrders].enmOrder       = kRedirectOrder_Dup;
+                    aOrders[cOrders].fdTarget       = 0;
+                    aOrders[cOrders].fdSource       = aFds[0];
+                    aOrders[cOrders].fdOtherPipeEnd = aFds[1];
+                    cOrders++;
+#ifdef USE_POSIX_SPAWN
+                    rcExit = posix_spawn_file_actions_adddup2(&FileActions, aFds[0], 0);
+                    if (rcExit != 0)
+                        rcExit = errx(pCtx, 2, "posix_spawn_file_actions_addclose(%d) failed: %s", fd, strerror(rcExit));
+#endif
                 }
             }
