Mail Archives: cygwin/2006/04/30/07:41:58
--------------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
>>>><http://support.microsoft.com/?id=190351>.
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 <stdio.h>
#include <windows.h>
/* 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--
- Raw text -