X-Spam-Check-By: sourceware.org Message-ID: <4454A26A.7020802@gmail.com> Date: Sun, 30 Apr 2006 12:41:30 +0100 User-Agent: Mozilla Thunderbird 1.0.7 (X11/20050923) MIME-Version: 1.0 To: cygwin AT cygwin DOT com Subject: Re: Redirecting bash stdin References: <31DDB7BE4BF41D4888D41709C476B657041687CB AT NIHCESMLBX5 DOT nih DOT gov> In-Reply-To: <31DDB7BE4BF41D4888D41709C476B657041687CB@NIHCESMLBX5.nih.gov> Content-Type: multipart/mixed; boundary="------------060509030504040309010904" From: Dave Mailing-List: contact cygwin-help AT cygwin DOT com; run by ezmlm List-Subscribe: List-Archive: List-Post: List-Help: , Sender: cygwin-owner AT cygwin DOT com Mail-Followup-To: cygwin AT cygwin DOT com Delivered-To: mailing list cygwin AT cygwin DOT com --------------060509030504040309010904 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit >>>According to Dave on 4/21/2006 11:45 AM: >>> >>>>I'm trying to get a mingw GUI application to pipe commands to >>>>cygwins bash by redirecting its stdin as described here >>>>. Buchbinder, Barry (NIH/NIAID) [E] wrote: > Have you tried piping into "bash -s"? Probably won't be any different > than the other piping you've tried, but maybe it will. I've just tried this - it doesn't affect the behaviour. >>Eric Blake wrote: >>> A simple test case is a necessity if you expect help debugging this. I've finally got the test case to show the behaviour I'm seeing in my application. Find it attached. Instructions to reproduce ------------------------- 1. Compile the testcase as: g++ -g -mno-cygwin -pedantic -Wall -W -o min_tc.exe min_tc.cc 2. Confirm you have Windows notepad in your path :) which notepad 3. Identify a text file in your home directory to open. I'll use gdbtk.ini 4. Execute the 'working' case: ./min_tc notepad gdbtk.ini - a few seconds pause while the bash login shell is starting - notepad opens with your textfile (or a messagebox saying it can't fnid it). You can leave this open, or close it. - 10 second pause while the test case sleeps - returned to your prompt 5. Execute the 'non-working' case: ./min_tc notepad \`cygpath -u gdbtk.ini\` - a long pause (bash startup + 10s sleep) - almost simultaneous opening of notepad and return to shell NB In order to clearly see the difference in behaviour, it is useful to run the command from a shell which only displays its prompt after the command has completed. This rules out command.com and cmd.exe, although they exhibit the same issue. ------------- Any help figuring out what's causing the delayed execution is much appreciated. Thanks, Dave. PS now running bash 3.1-6 PPS yes, that is as minimal as I could get the test case. Apologies. --------------060509030504040309010904 Content-Type: text/x-c++src; name="min_tc.cc" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="min_tc.cc" #include #include /* Macros normally mapped to some logging function */ /* Just print on sterr */ #define SYSTEM_ERROR(text, err, pri) \ fprintf(stderr,text) #define APP_ERROR(text, pri) \ fprintf(stderr,text) /** Handle of the write end of the pipe * connected to the standard input of the shell */ HANDLE sh_stdin; /** Handle of the read end of the pipe * connected to the standard output of the shell */ HANDLE sh_stdout; /** TRUE is a shell is running waiting for input */ bool bShell_running; static bool LaunchRedirectedChild( HANDLE hChildStdOut, HANDLE hChildStdIn, HANDLE hChildStdErr, char* cmd) { PROCESS_INFORMATION pi; STARTUPINFO si; bool rv = true; // Set up the start up info struct. memset(&si,0,sizeof(si)); si.cb = sizeof(si); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.hStdOutput = NULL;//hChildStdOut; si.hStdInput = hChildStdIn; si.hStdError = hChildStdErr; si.wShowWindow = SW_HIDE; // Don't show the login shell if (!CreateProcess(NULL,cmd,NULL,NULL,TRUE, CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi)) { char* msg = new char[strlen(cmd)+ 77]; sprintf(msg, "LaunchRedirectedChild " "CreateProcess failed to start child process.\n" "Command: %s\n", cmd); SYSTEM_ERROR(msg, GetLastError(), UNRECOVERABLE); delete[] msg; rv = false; } else { // Set global child process handle to cause threads to exit. //hChildProcess = pi.hProcess; // Close any unnecessary handles. if (!CloseHandle(pi.hThread)) { SYSTEM_ERROR("LaunchRedirectedChild " "CloseHandle can't close child thread handle\n", GetLastError(), RECOVERABLE); } if (!CloseHandle(pi.hProcess)) { SYSTEM_ERROR("LaunchRedirectedChild " "CloseHandle can't close child process handle\n", GetLastError(), RECOVERABLE); } } return rv; } bool start_shell(void) { char login_cmd[MAX_PATH + FILENAME_MAX] = "c:\\cygwin\\bin\\bash.exe -l"; HANDLE hOutputReadTmp,hOutputRead,hOutputWrite; HANDLE hInputWriteTmp,hInputRead,hInputWrite; HANDLE hErrorWrite; HANDLE hThread; DWORD ThreadId; SECURITY_ATTRIBUTES sa; /* Create an anonymous pipe and attach it to stdin * Then start the login shell * * Think about redirecting output somewhere as well. */ // Set up the security attributes struct. sa.nLength= sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; // Create the child output pipe. if (!CreatePipe(&hOutputReadTmp, &hOutputWrite, &sa, 0)) { SYSTEM_ERROR("Shell::start_shell " "CreatePipe can't create output pipe\n", GetLastError(), UNRECOVERABLE); } // Create a duplicate of the output write handle for the std error // write handle. This is necessary in case the child application // closes one of its std output handles. if (!DuplicateHandle(GetCurrentProcess(), hOutputWrite, GetCurrentProcess(), &hErrorWrite, 0, TRUE, DUPLICATE_SAME_ACCESS)) { SYSTEM_ERROR("Shell::start_shell " "DuplicateHandle can't duplicate output write handle\n", GetLastError(), UNRECOVERABLE); } // Create the child input pipe. if (!CreatePipe(&hInputRead, &hInputWriteTmp, &sa, 0)) { SYSTEM_ERROR("Shell::start_shell " "CreatePipe can't create input pipe\n", GetLastError(), UNRECOVERABLE); } // Create new output read handle and the input write handles. Set // the Properties to FALSE. Otherwise, the child inherits the // properties and, as a result, non-closeable handles to the pipes // are created. if (!DuplicateHandle(GetCurrentProcess(), hOutputReadTmp, GetCurrentProcess(), &hOutputRead, // Address of new handle. 0, FALSE, // Make it uninheritable. DUPLICATE_SAME_ACCESS)) { SYSTEM_ERROR("Shell::start_shell " "DuplicateHandle can't duplicate output read handle\n", GetLastError(), UNRECOVERABLE); } if (!DuplicateHandle(GetCurrentProcess(), hInputWriteTmp, GetCurrentProcess(), &hInputWrite, // Address of new handle. 0, FALSE, // Make it uninheritable. DUPLICATE_SAME_ACCESS)) { SYSTEM_ERROR("Shell::start_shell " "DuplicateHandle can't duplicate input write handle\n", GetLastError(), UNRECOVERABLE); } // Close inheritable copies of the handles you do not want to be // inherited. if (!CloseHandle(hOutputReadTmp)) { SYSTEM_ERROR("Shell::start " "CloseHandle can't close output read handle\n", GetLastError(), RECOVERABLE); } if (!CloseHandle(hInputWriteTmp)) { SYSTEM_ERROR("Shell::start " "CloseHandle can't close input write handle\n", GetLastError(), RECOVERABLE); } bShell_running = LaunchRedirectedChild(hOutputWrite, hInputRead, hErrorWrite, login_cmd); sh_stdin = hInputWrite; sh_stdout = hOutputRead; // Close pipe handles (do not continue to modify the parent). // You need to make sure that no handles to the write end of the // output pipe are maintained in this process or else the pipe will // not close when the child process exits and the ReadFile will hang. if (!CloseHandle(hOutputWrite)) { SYSTEM_ERROR("Shell::start " "CloseHandle can't close the output write handle\n", GetLastError(), RECOVERABLE); } if (!CloseHandle(hInputRead)) { SYSTEM_ERROR("Shell::start " "CloseHandle can't close the input read handle\n", GetLastError(), RECOVERABLE); } if (!CloseHandle(hErrorWrite)) { SYSTEM_ERROR("Shell::start " "CloseHandle can't close the error write handle\n", GetLastError(), RECOVERABLE); } return bShell_running; } bool send_command(const char* cmd) { bool rv = true; if (bShell_running) { DWORD len = strlen(cmd) + 1; /* send the null terminator */ DWORD written; if (! WriteFile(sh_stdin, cmd, len, &written, NULL)) { INT err = GetLastError(); if (err == ERROR_NO_DATA) { /* Pipe was closed */ APP_ERROR("Shell::send_command " "Pipe was closed.", UNRECOVERABLE); } else { SYSTEM_ERROR("Shell::send_command " "WriteFile failed to write to pipe.\n", GetLastError(), UNRECOVERABLE); } rv = false; } /** \todo for some reason the buffer isn't flushing on the client side. * Try flush this end. */ FlushFileBuffers(sh_stdin); fprintf (stderr, "Command sent to shell: %s\n", cmd); } else { /* shell not available */ rv = false; } return rv; } void exit_shell(void) { if (bShell_running) { if(send_command("exit")) { bShell_running = false; } else { APP_ERROR("Shell::exit_shell " "send_command failed", RECOVERABLE); } } } bool run_command(const char* cmd) { bool rv; /* All commands need to be run in the background, * so make sure that an & is appended */ INT len = strlen(cmd); INT n = len; char *cmd_ptr = NULL; /* First find the last character in the command */ do { n--; } while ((n >= 0) && ((cmd[n] == ' ') || (cmd[n] == '\t') || (cmd[n] == '\n'))); n++; /* Ensure we're terminated properly */ if (n > 0) { cmd_ptr = new char[n+3]; strncpy(cmd_ptr, cmd, n); if (cmd[n-1] != '&') { /* Need to append & */ cmd_ptr[n++]='&'; } cmd_ptr[n++]='\n'; /* Run command */ cmd_ptr[n]='\0'; /* Terminate string */ rv = send_command(cmd_ptr); delete[] cmd_ptr; } else { /* no command to run */ rv = true; } return rv; } int main (int argc, char** argv) { char command[1024] = ""; int n; /* Set up the command to run straight off our command line */ for (n = 1; n < argc; n++) { fprintf(stderr, "Arg %d is %s\n", n, argv[n]); strcat(command, argv[n]); strcat(command, " "); } fprintf(stderr, "Command to be piped is: %s\n", command); /* Start a shell */ if (start_shell()) { /* and run the command */ run_command(command); /* wait 10s */ Sleep(10000); /* Clean up */ exit_shell(); } else { APP_ERROR("Couldn't start shell", UNRECOVERABLE); } return 0; } --------------060509030504040309010904 Content-Type: text/plain; charset=us-ascii -- Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple Problem reports: http://cygwin.com/problems.html Documentation: http://cygwin.com/docs.html FAQ: http://cygwin.com/faq/ --------------060509030504040309010904--