B20.1: Problem in exec (NT 4.0)
Ken Bowler
khb@nortelnetworks.com
Wed Aug 11 13:34:00 GMT 1999
I'm having a problem with pipelines form sh.exe when the sh.exe is started
via CreateProcess from a non cygwin process which sets stdin, stdout and
stderr for the cygwin process (sh.exe) to one end of a connected TCP/IP
socket pair. A command pipeline like "/bin/ls . | /bin/cat" results in cat
failing with the message "cat: write error: Bad file number". By replacing
/bin/cat with a program that I wrote, I determined that cat was started with
the stdout fd set to -1.
I produced two small programs to demonstrate the problem. The first is a
non-cygwin program that creates the socket pair and spawns the second
program. It then reads the output from the spawned program from the socket
not passed as stdin, stdout and stderr to the child process. This program
should be compiled using MSVC (e.g. cl prog1.c -link wsock32.lib).
The second is a cygwin program that emulates "sh" for the pipeline described
above. It produces messages showing what is occurring as it proceeds. This
should be compiled using gcc (e.g. gcc -o prog2.exe prog2.c)
To demonstrate the problem issue the command:
prog1 .\prog2.exe
The output will be as below:
prog1: Port assigned is 4977
prog2: Here we are about to create the pipeline
prog2: First we spawn the '/bin/cat'
prog2: Now we spawn '/bin/ls .'
prog2: Child prog=/bin/cat, stdin=0 (duped from 4), stdout=1 (same as
parent), stderr=2 (same as parent)
prog2: Child prog=/bin/ls, stdin=0 (same as parent), stdout=1 (duped from
5), stderr=2 (same as parent)
cat: write error: Bad file number
prog2: Pipeline returns with exit code 0
(assuming prog1.exe and prog2.exe are in the same directory)
If you invoke prog2 directly from the command line it works correctly.
After the fork, and before the exec, the child process in prog2 has the
stdout fd with a value of 1
Additional info:
System: Dell Opt1Plex GX1 with a 400MZ Pentium II and 128 MB of
Memory
OS: Windows/NT 4.0 with SP5 installed on D:
Cygwin: Beta 20.1 with the binaries installed in d:\bin
Program 1:
----------------------------------------------------------------------cut
here------------------------------------------------------------------------
-----------
#include <stdio.h>
#include <windows.h>
#include <io.h>
static char errmsg[1024];
static char progname[64];
char *GetErrorMsg(int errnum)
{
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
errnum,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)errmsg,
sizeof(errmsg),
NULL) == 0)
{
sprintf(errmsg, "Win32 error number %ld", errnum);
}
else
{
errmsg[strlen(errmsg)-2] = '\0'; /* remove the cr-lf */
}
return errmsg;
}
static int sockpair(SOCKET sv[2])
{
struct sockaddr_in local_addr, bound_addr;
int addrlen;
SOCKET s1, s2, s3;
int origOpenType, syncOpenType;
int optLen;
int rc;
int saverrno;
unsigned long loop_addr;
optLen = sizeof(origOpenType);
/* get current socket open type setting */
if ((rc = getsockopt(INVALID_SOCKET,
SOL_SOCKET,
SO_OPENTYPE,
(char *)&origOpenType,
&optLen)) != NO_ERROR)
{
saverrno = WSAGetLastError();
fprintf(stderr, "%s: Unable to obtain current socket opentype
options: Winsock error number %d.\n", progname, saverrno);
return FALSE;
}
/* Now set open type so we get synchroinous socket handles suitable for
stdin etc. */
syncOpenType = SO_SYNCHRONOUS_NONALERT;
if ((rc = setsockopt(INVALID_SOCKET,
SOL_SOCKET,
SO_OPENTYPE,
(char *)&syncOpenType,
sizeof(syncOpenType))) != NO_ERROR)
{
saverrno = WSAGetLastError();
fprintf(stderr, "%s: Unable to set synchronous socket opentype
options: Winsock error number %d.\n", progname, saverrno);
return FALSE;
}
if ((s1 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
saverrno = WSAGetLastError();
fprintf(stderr, "%s: Unable to create a socket: Winsock error number
%d.\n", progname, saverrno);
goto sckpr_error;
}
if ((s2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
saverrno = WSAGetLastError();
fprintf(stderr, "%s: Unable to create a socket: Winsock error number
%d.\n", progname, saverrno);
closesocket(s1);
goto sckpr_error;
}
loop_addr = inet_addr("127.0.0.1");
memset(&local_addr, '\0', sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_port = 0;
memcpy(&local_addr.sin_addr, &loop_addr, sizeof(loop_addr));
if (bind(s2, (LPSOCKADDR)&local_addr, sizeof(local_addr)) != 0)
{
saverrno = WSAGetLastError();
fprintf(stderr, "%s: Unable to bind socket to loopback address:
Winsock error number %d.\n", progname, saverrno);
closesocket(s1);
closesocket(s2);
goto sckpr_error;
}
/* determine what address and port got assigned */
addrlen = sizeof(bound_addr);
if (getsockname(s2, (LPSOCKADDR)&bound_addr, &addrlen) != 0)
{
saverrno = WSAGetLastError();
fprintf(stderr, "%s: Unable to determine port assigned by bind:
Winsock error number %d.\n", progname, saverrno);
closesocket(s1);
closesocket(s2);
goto sckpr_error;
}
local_addr.sin_port = bound_addr.sin_port;
printf("%s: Port assigned is %d\n", progname,
ntohs(local_addr.sin_port));
listen(s2, 1); /* set up to receive connection */
/*
* Since the socket s1 is not non-blocking, the following connect should
block
* but it doesn't (probably because of a bug).
*/
if (connect(s1, (struct sockaddr *)&bound_addr, sizeof(bound_addr)) < 0)
{
saverrno = errno;
fprintf(stderr, "%s: Unable to connect the two sockets: Winsock
error number %d.\n", progname, saverrno);
closesocket(s1);
closesocket(s2);
goto sckpr_error;
}
if ((s3 = accept(s2, (struct sockaddr *)&local_addr, (size_t
*)&addrlen)) < 0)
{
saverrno = WSAGetLastError();
fprintf(stderr, "%s: Unable to accept connection: Winsock error
number %d.\n", progname, saverrno);
closesocket(s1);
closesocket(s2);
goto sckpr_error;
}
Sleep(1000);
closesocket(s2);
setsockopt(INVALID_SOCKET,
SOL_SOCKET,
SO_OPENTYPE,
(char *)&origOpenType,
sizeof(origOpenType));
sv[0] = s1;
sv[1] = s3;
return TRUE;
sckpr_error:
setsockopt(INVALID_SOCKET,
SOL_SOCKET,
SO_OPENTYPE,
(char *)&origOpenType,
sizeof(origOpenType));
return FALSE;
}
static SOCKET Spawn_Process(char *cmdline)
{
STARTUPINFO st_info;
PROCESS_INFORMATION proc_info;
BOOL success;
int saverrno;
SOCKET sv[2];
SOCKET ni_sock;
if (!sockpair(sv)) /* create a pair of connected sockets */
return FALSE;
/* Make one of thses sockets non-inheritable so the chile process does
not see our end of the connection */
if (!DuplicateHandle(GetCurrentProcess(),
(HANDLE)sv[0],
GetCurrentProcess(),
(HANDLE *)&ni_sock,
0,
FALSE, /* This one not
inherited */
DUPLICATE_SAME_ACCESS))
{
saverrno = GetLastError();
fprintf(stderr, "%s: Unable to duplicate a non-inheritable server
socket: %s\n", progname, GetErrorMsg(saverrno));
return FALSE;
}
closesocket(sv[0]); /* Close the inheritable copy */
st_info.cb = sizeof(STARTUPINFO);
st_info.lpReserved = NULL;
st_info.lpReserved2 = NULL;
st_info.cbReserved2 = 0;
st_info.lpDesktop = NULL;
st_info.lpTitle = NULL;
st_info.dwFlags = STARTF_USESTDHANDLES;
st_info.hStdInput = (HANDLE)sv[1];
st_info.hStdOutput = (HANDLE)sv[1];
st_info.hStdError = (HANDLE)sv[1];
success = CreateProcess(NULL, /* executable path */
cmdline, /* command line */
NULL, /* default security attributes
for process */
NULL, /* default security attributes
for primary thread */
TRUE, /* proc inherits handles */
0, /* creation flags */
NULL, /* process environment */
NULL, /* working directory to use */
&st_info,
&proc_info);
if (!success)
{
saverrno = GetLastError();
fprintf(stderr, "%s: Unable to spawn process: %s\n", progname,
GetErrorMsg(saverrno));
closesocket(ni_sock);
closesocket(sv[1]);
return INVALID_SOCKET;
}
CloseHandle(proc_info.hThread); /* we don't need this at
all */
closesocket(sv[1]);
return ni_sock;
}
int main(int argc, char *argv[])
{
SOCKET sock;
int len;
char buff[256];
WORD wsck_version;
WSADATA wsck_data;
int exit_code = 1;
int saverrno;
char *cp;
if ((cp = strrchr(argv[0], '/')) == NULL)
{
if ((cp = strrchr(argv[0], '\\')) == NULL)
cp = argv[0];
else
cp++;
}
else
cp++;
strcpy(progname, cp);
len = strlen(cp);
if ((len > 4) && (strcmp(&progname[len-4], ".exe") == 0))
progname[len-4] = '\0';
if (argc < 2)
{
fprintf(stderr, "Usage: %s path_of_executable\n", progname);
ExitProcess(exit_code);
}
wsck_version = MAKEWORD(1, 0);
if (WSAStartup(wsck_version, &wsck_data) != 0)
{
fprintf(stderr, "%s: InitializeWinsock: The winsock DLL fails to
initialize as vers 1.0\n", progname);
goto done;
}
if ((sock = Spawn_Process(argv[1])) == INVALID_SOCKET)
goto done;
for (;;)
{
len = recv(sock, buff, sizeof(buff), 0);
if (len > 0)
write(1, buff, len);
else if (len == 0)
break;
else
{
saverrno = WSAGetLastError();
fprintf(stderr, "%s: Error reading process socket: Winsock error
number %d.\n", progname, saverrno);
goto done;
}
}
exit_code = 0;
done:
WSACleanup();
ExitProcess(exit_code);
}
----------------------------------------------------------------------cut
here------------------------------------------------------------------------
-----------
Program 2:
----------------------------------------------------------------------cut
here------------------------------------------------------------------------
-----------
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <strings.h>
static char progname[64];
static FILE *errmsg;
static int spawnit(int stdin_fd, int stdout_fd, int stderr_fd, char *prog,
char *args[])
{
int pid;
int saverrno;
int fd;
char ch_stdin[32], ch_stdout[32], ch_stderr[32];
fflush(stderr);
fflush(stdout);
if ((pid = fork()) > 0)
return pid;
else if (pid == 0)
{
if (stderr_fd != 2)
{
close(2);
if (stderr_fd != -1)
{
if ((fd = dup(stderr_fd)) != 2)
{
saverrno = errno;
if (fd < 0)
fprintf(errmsg, "%s: Dup for stderr fails: %s\n",
progname, strerror(saverrno));
else
{
fprintf(errmsg, "%s: Dup for stderr produces wrong
fd, %d instead of 2\n", progname, fd);
close(fd);
}
strcpy(ch_stderr, "-1 (dup failed)");
}
else
sprintf(ch_stderr, "2 (duped from %d)", stderr_fd);
close(stderr_fd);
}
else
strcpy(ch_stderr, "-1 (requested)");
}
else
sprintf(ch_stderr, "2 (same as parent)");
if (stdout_fd != 1)
{
close(1);
if (stdout_fd != -1)
{
if ((fd = dup(stdout_fd)) != 1)
{
saverrno = errno;
if (fd < 0)
fprintf(errmsg, "%s: Dup for stdout fails: %s\n",
progname, strerror(saverrno));
else
{
fprintf(errmsg, "%s: Dup for stdout produces wrong
fd, %d instead of 1\n", progname, fd);
close(fd);
}
strcpy(ch_stdout, "-1 (dup failed)");
}
else
sprintf(ch_stdout, "1 (duped from %d)", stdout_fd);
close(stdout_fd);
}
else
strcpy(ch_stdout, "1 (requested)");
}
else
strcpy(ch_stdout, "1 (same as parent)");
if (stdin_fd != 0)
{
close(0);
if (stdin_fd != -1)
{
if ((fd = dup(stdin_fd)) != 0)
{
saverrno = errno;
if (fd < 0)
fprintf(errmsg, "%s: Dup for stdin fails: %s\n",
progname, strerror(saverrno));
else
{
fprintf(errmsg, "%s: Dup for stdin produces wrong
fd, %d instead of 0\n", progname, fd);
close(fd);
}
strcpy(ch_stdin, "-1 (dup failed)");
}
else
sprintf(ch_stdin, "0 (duped from %d)", stdin_fd);
close(stdin_fd);
}
else
strcpy(ch_stdin, "0 (requested)");
}
else
strcpy(ch_stdin, "0 (same as parent)");
fprintf(errmsg, "%s: Child prog=%s, stdin=%s, stdout=%s,
stderr=%s\n", progname, prog, ch_stdin, ch_stdout, ch_stderr);
fflush(errmsg);
execv(prog, args);
saverrno = errno;
fprintf(errmsg, "%s: Execv of %s fails: %s\n", progname, prog,
strerror(saverrno));
fflush(errmsg);
exit(1);
}
else
return -1;
}
int main(int argc, char *argv[])
{
int errfd;
int pipes[2];
char *args[10];
int pid;
int status;
char *cp;
int len;
int saverrno;
if ((cp = strrchr(argv[0], '/')) == NULL)
{
if ((cp = strrchr(argv[0], '\\')) == NULL)
cp = argv[0];
else
cp++;
}
else
cp++;
strcpy(progname, cp);
len = strlen(cp);
if ((len > 4) && (strcmp(&progname[len-4], ".exe") == 0))
progname[len-4] = '\0';
errfd = dup(2);
errmsg = fdopen(errfd, "w");
printf("%s: Here we are about to create the pipeline\n", progname);
printf("%s: First we spawn the '/bin/cat'\n", progname);
if (pipe(pipes) != 0)
{
saverrno = errno;
fprintf(stderr, "%s: Pipe creation fails: %s\n", progname,
strerror(saverrno));
goto errdone;
}
args[0] = "cat";
args[1] = NULL;
if (spawnit(pipes[0], 1, 2, "/bin/cat", args) < 0)
{
saverrno = errno;
fprintf(stderr, "%s: Spawn of '/bin/cat' fails: %s\n", progname,
strerror(saverrno));
close(pipes[0]);
close(pipes[1]);
goto errdone;
}
close(pipes[0]);
args[0] = "ls";
args[1] = ".";
args[2] = NULL;
printf("%s: Now we spawn '/bin/ls .'\n", progname);
if ((pid = spawnit(0, pipes[1], 2, "/bin/ls", args)) < 0)
{
saverrno = errno;
fprintf(stderr, "%s: Spawn of '/bin/ls' fails: %s\n", progname,
strerror(saverrno));
close(pipes[1]);
goto errdone;
}
close(pipes[1]);
waitpid(pid, &status, 0);
sleep(5); /* Just so the follwoing message usually follows output
from /bin/cat */
printf("%s: Pipeline returns with exit code %d\n", progname,
WEXITSTATUS(status));
exit(WEXITSTATUS(status));
errdone:
fprintf(stderr, "%s: Test fails\n", progname);
exit(-1);
}
----------------------------------------------------------------------cut
here------------------------------------------------------------------------
-----------
--
Want to unsubscribe from this list?
Send a message to cygwin-unsubscribe@sourceware.cygnus.com
More information about the Cygwin
mailing list