Socket inheritance with fork/dup2/exec

View: New views
8 Messages — Rating Filter:   Alert me  

Socket inheritance with fork/dup2/exec

by Jim Powers-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi,

I am redirecting the stdout of a child process to a socket via the standard
fork/dup2/exec paradigm and then reading and displaying the output.

This works fine if the exec'd child process is compiled using gcc under
cygwin. However, it fails with an "Invalid file handle" error when compiled
using VC8 under windows.

I've included both the parent and child code below.

I am running cygwin 1.5.24 and gcc3.4.4.

--
Jim Powers
Powers Consulting Services, Inc.
jim dot powers at powers-consulting dot com

--------
parent.c
--------
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <sys/fcntl.h>
#include <sys/socket.h>

#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif

#ifndef STDOUT_FILENO
#define STDOUT_FILENO 1
#endif

#ifndef STDERR_FILENO
#define STDERR_FILENO 2
#endif

void set_blocking(int fd);
void set_nonblocking(int fd);

int main(int argc, char **argv)
{
    int i;
    pid_t pid;
    int to_child_pipe[2];
    int from_child_pipe[2];
    int f_in, f_out, n;
    char buffer[512];

    if (socketpair(AF_UNIX, SOCK_STREAM, 0, to_child_pipe) ||
        socketpair(AF_UNIX, SOCK_STREAM, 0, from_child_pipe) ) {
        fprintf(stderr, "socketpair error (%d)\n", errno);
        exit(1);
    }

    pid = fork();
    if (pid == -1) {
        fprintf(stderr, "fork error (%d)\n", errno);
        exit(1);
    }

    if (pid == 0) { /* CHILD */
        if ( dup2(to_child_pipe[0], STDIN_FILENO) < 0 ||
             close(to_child_pipe[1]) < 0 ||
             close(from_child_pipe[0]) < 0 ||
             dup2(from_child_pipe[1], STDOUT_FILENO) < 0 )
        {
            fprintf(stderr, "dup2/close error (%d)\n", errno);
            exit(1);
        }
        if (to_child_pipe[0] != STDIN_FILENO)
                close(to_child_pipe[0]);
        if (from_child_pipe[1] != STDOUT_FILENO)
                close(from_child_pipe[1]);

        set_blocking(STDIN_FILENO);
        set_nonblocking(STDIN_FILENO);

        execvp(argv[1], &argv[1]);

        fprintf(stderr, "execvp error (%d) on '%s'", errno, argv[1]);

        exit(1);
    }

    /* PARENT */

    if (close(from_child_pipe[1]) < 0 || close(to_child_pipe[0]) < 0) {
        fprintf(stderr, "close error (%d) on child handles", errno);
        exit(1);
    }

    f_in  = from_child_pipe[0];
    f_out = to_child_pipe[1];

    /* read and display data from child process */
    while (1) {
        n = read(f_in, buffer, sizeof(buffer) - 1);
        if (n == 0)
            break;
        if (n == -1) {
            if (errno != ECONNABORTED && errno != ECONNRESET)
                fprintf(stderr, "read error (%d)\n", errno);
            break;
        }

        buffer[n] = '\0';

        printf("%s", buffer);
        fflush(NULL);
    }

    exit(1);
}

void set_blocking(int fd)
{
    int val;

    if ((val = fcntl(fd, F_GETFL, 0)) == -1)
        return;
    if (val & O_NONBLOCK) {
        val &= ~O_NONBLOCK;
        fcntl(fd, F_SETFL, val);
    }
}

void set_nonblocking(int fd)
{
    int val;

    if ((val = fcntl(fd, F_GETFL, 0)) == -1)
        return;
    if (!(val & O_NONBLOCK)) {
        val |= O_NONBLOCK;
        fcntl(fd, F_SETFL, val);
    }
}

-------
child.c
-------
#include <stdio.h>
#include <Windows.h>

void ErrorExit(LPTSTR lpszFunction);

main(int argc, char **argv)
{
    char str[] = "hello";
    int i;
    DWORD btw = (DWORD)strlen(str);
    DWORD bw;
    STARTUPINFO si;
    OVERLAPPED ol;
    HANDLE hStdout;

    ZeroMemory(&ol, sizeof(ol));

    hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

    for (i=0; i<5; i++) {
        if (! WriteFile(hStdout, str, btw, &bw, &ol) ) {
            ErrorExit("WriteFile");
            exit(1);
        }
        Sleep(1000);
    }

}

void ErrorExit(LPTSTR lpszFunction)
{
    LPVOID lpMsgBuf;
    DWORD dw = GetLastError();

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
        NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf, 0, NULL );

    fprintf(stderr, "%s error (%d): %s\n", lpszFunction, dw, lpMsgBuf);

    ExitProcess(dw);
}

--
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/


Re: Socket inheritance with fork/dup2/exec

by Brian Dessent :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Jim Powers wrote:

> I am redirecting the stdout of a child process to a socket via the standard
> fork/dup2/exec paradigm and then reading and displaying the output.
>
> This works fine if the exec'd child process is compiled using gcc under
> cygwin. However, it fails with an "Invalid file handle" error when compiled
> using VC8 under windows.
>
> I've included both the parent and child code below.

I'm fairly sure this isn't supposed to work.  Unix domain sockets
(AF_UNIX/AF_LOCAL) don't actually exist in any Windows API so Cygwin
emulates them with an underlying AF_INET socket.  So when your MSVCRT
program tries to WriteFile to a socket it fails, because you have to use
the Winsock API for that.  In short, Windows is not unix, and handles
aren't fds.

You can probably make this work if you use pipe() instead of a
socketpair() as that maps directly onto a Windows anonymous pipe, which
is a file handle that can be written to.  You could also use a named
pipe, which is the closest Windows has to a unix domain socket, but
there is no corresponding POSIX api for that and so you'd be breaking
abstraction as you'd have to deal with raw Win32 APIs in your POSIX
parent app, which is ugly (and not how Cygwin was designed to work
either.)

Brian

--
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/


Re: Socket inheritance with fork/dup2/exec

by Lev Bishop :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On 6/21/07, Brian Dessent wrote:

> Jim Powers wrote:
>
> > I am redirecting the stdout of a child process to a socket via the standard
> > fork/dup2/exec paradigm and then reading and displaying the output.
> >
> > This works fine if the exec'd child process is compiled using gcc under
> > cygwin. However, it fails with an "Invalid file handle" error when compiled
> > using VC8 under windows.
> >
> > I've included both the parent and child code below.
>
> I'm fairly sure this isn't supposed to work.  Unix domain sockets
> (AF_UNIX/AF_LOCAL) don't actually exist in any Windows API so Cygwin
> emulates them with an underlying AF_INET socket.  So when your MSVCRT
> program tries to WriteFile to a socket it fails, because you have to use
> the Winsock API for that.  In short, Windows is not unix, and handles
> aren't fds.

Actually, in windows API you can WriteFile() to a socket (as an
optional feature, but the default socket provider supports it). The
problem here is that cygwin opens the underlying socket in overlapped
mode, and the windows API requires treating overlapped handles
differently than non-overlapped. If you'll always be using the same
child, that you have full control over, then you can write the child
to use overlapped semantics. But if you want to be able to pass the
socket to an arbitrary child process then you're out of luck, because
the windows API provides no way for the child to discover that it
needs to use overlapped semantics,  forcing the child to attempt
non-overlapped semantics and fail.

I once wrote a patch to cygwin in an attempt to get around this. It
mostly worked, but windows API calls that were not documented to be
able to block (eg setsockopt()) started blocking on non-overlapped
sockets. Don't you just love the windows API? See:
http://cygwin.com/ml/cygwin-patches/2006-q2/msg00039.html

> You can probably make this work if you use pipe() instead of a
> socketpair() as that maps directly onto a Windows anonymous pipe, which
> is a file handle that can be written to.  You could also use a named
> pipe, which is the closest Windows has to a unix domain socket, but
> there is no corresponding POSIX api for that and so you'd be breaking
> abstraction as you'd have to deal with raw Win32 APIs in your POSIX
> parent app, which is ugly (and not how Cygwin was designed to work
> either.)

Actually, pipe() is implemented in cygwin using win32  named pipes,
not anonymous pipes, as I recall. But, you are right that using pipe()
should solve this particular problem.

Lev

--
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/


Re: Socket inheritance with fork/dup2/exec

by Brian Dessent :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Lev Bishop wrote:

> Actually, pipe() is implemented in cygwin using win32  named pipes,
> not anonymous pipes, as I recall. But, you are right that using pipe()
> should solve this particular problem.

Hmm, that does appear to be the case.

Unless I'm mistaken anonymous pipes are just a degenerate form of named
pipes created by the system in a given namespace
(\Device\NamedPipe\Win32Pipes.$DWORD.$DWORD ?) so I guess you could say
they're all the same thing anyway.  :)

Brian

--
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/


Re: Socket inheritance with fork/dup2/exec

by Corinna Vinschen-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Jun 21 22:45, Jim Powers wrote:

> Hi,
>
> I am redirecting the stdout of a child process to a socket via the standard
> fork/dup2/exec paradigm and then reading and displaying the output.
>
> This works fine if the exec'd child process is compiled using gcc under
> cygwin. However, it fails with an "Invalid file handle" error when compiled
> using VC8 under windows.
>
> I've included both the parent and child code below.
>
> I am running cygwin 1.5.24 and gcc3.4.4.

This is almost certainly a drawback of the method used for duplicating
sockets to child processes used in Cygwin 1.5.24 and before
(WSADuplicateSocket/WSASocket).  This only works reliable if the child
knows that the descriptor is a socket.  Usually server applications
using sockets don't duplicate the socket to the child processes, but
instead use pipes or pseudo terminals on the local connection, so that's
not a standard problem.

However, the next version of Cygwin will use standard DuplicateHandle
calls as for normal file handles.  Consequentially your
your test application appears to work with a Cygwin built from CVS:

  $ ./sock-cyg-win-parent ./sock-cyg-win-child.exe
  hellohellohellohellohello^C

You could try a developer snapshot from http://cygwin.com/snapshots/

As a workaround for Cygwin 1.5.x, use pipes, as already noted, or pseudo
terminals.


Corinna

--
Corinna Vinschen                  Please, send mails regarding Cygwin to
Cygwin Project Co-Leader          cygwin AT cygwin DOT com
Red Hat

--
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/


Re: Socket inheritance with fork/dup2/exec

by Lev Bishop :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On 6/22/07, Corinna Vinschen wrote:

> However, the next version of Cygwin will use standard DuplicateHandle
> calls as for normal file handles.  Consequentially your
> your test application appears to work with a Cygwin built from CVS:

But MSDN says:
 You should not use DuplicateHandle to duplicate handles to the
following objects:

     * I/O completion ports. No error is returned, but the duplicate
handle cannot be used.
     * Sockets. No error is returned, but the duplicate handle may not
be recognized by
 Winsock at the target process. Also, using DuplicateHandle interferes
with internal reference  counting on the underlying object. To
duplicate a socket handle, use the
 WSADuplicateSocket function.

--
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/


Re: Socket inheritance with fork/dup2/exec

by Jim Powers-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Corinna,

I took your suggestion and downloaded the latest sanpshot (cygwin1-
20070616.dll) and that fixed the problem.  The Win32 version of the client
compiled with VC8 now works fine.

Thanks!!

Jim

Corinna Vinschen <corinna-cygwin@...> said:

> On Jun 21 22:45, Jim Powers wrote:
> > Hi,
> >
> > I am redirecting the stdout of a child process to a socket via the
standard
> > fork/dup2/exec paradigm and then reading and displaying the output.
> >
> > This works fine if the exec'd child process is compiled using gcc under
> > cygwin. However, it fails with an "Invalid file handle" error when
compiled

> > using VC8 under windows.
> >
> > I've included both the parent and child code below.
> >
> > I am running cygwin 1.5.24 and gcc3.4.4.
>
> This is almost certainly a drawback of the method used for duplicating
> sockets to child processes used in Cygwin 1.5.24 and before
> (WSADuplicateSocket/WSASocket).  This only works reliable if the child
> knows that the descriptor is a socket.  Usually server applications
> using sockets don't duplicate the socket to the child processes, but
> instead use pipes or pseudo terminals on the local connection, so that's
> not a standard problem.
>
> However, the next version of Cygwin will use standard DuplicateHandle
> calls as for normal file handles.  Consequentially your
> your test application appears to work with a Cygwin built from CVS:
>
>   $ ./sock-cyg-win-parent ./sock-cyg-win-child.exe
>   hellohellohellohellohello^C
>
> You could try a developer snapshot from http://cygwin.com/snapshots/
>
> As a workaround for Cygwin 1.5.x, use pipes, as already noted, or pseudo
> terminals.
>
>
> Corinna
>
> --
> Corinna Vinschen                  Please, send mails regarding Cygwin to
> Cygwin Project Co-Leader          cygwin AT cygwin DOT com
> Red Hat
>
> --
> 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/
>



--
Jim Powers
Powers Consulting Services, Inc.
jim.powers@...
937-271-5523



--
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/


Re: Socket inheritance with fork/dup2/exec

by Corinna Vinschen-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Jun 22 11:34, Lev Bishop wrote:

> On 6/22/07, Corinna Vinschen wrote:
>
> >However, the next version of Cygwin will use standard DuplicateHandle
> >calls as for normal file handles.  Consequentially your
> >your test application appears to work with a Cygwin built from CVS:
>
> But MSDN says:
> You should not use DuplicateHandle to duplicate handles to the
> following objects:
>
>     * I/O completion ports. No error is returned, but the duplicate
> handle cannot be used.
>     * Sockets. No error is returned, but the duplicate handle may not
> be recognized by
> Winsock at the target process. Also, using DuplicateHandle interferes
> with internal reference  counting on the underlying object. To
> duplicate a socket handle, use the
> WSADuplicateSocket function.

I see this mentioned on the MSDN web pages, but I don't see this on my
very recent MSDN Library DVD.  Only I/O completion ports are mentioned
as not being duplicatable by DuplicateHandle.  Sockets are in the list
of duplicatable objects.  It would be really weird, too, since it was
never a problem to duplicate a socket on NT.  We only ever had problems on
95/98/Me.  These 95/98/Me problems were the reason to switch Cygwin
from using DuplicateHandle on sockets to WSADuplicateSocket/WSASocket
back in 2000.


Corinna

--
Corinna Vinschen                  Please, send mails regarding Cygwin to
Cygwin Project Co-Leader          cygwin AT cygwin DOT com
Red Hat

--
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/