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