bitwiseworks / libcx

kLIBC Extension Library
GNU Lesser General Public License v2.1
11 stars 1 forks source link

spawn2() does not inherit other handles than standard IOs with P_2_THREADSAFE and/or simple redirection #105

Open komh opened 2 months ago

komh commented 2 months ago

Hi/2.

According to explanation of spawn2(), spawn2() should inherit other handles as well as standard IOs if P_2_NOINHERIT nor P_2_XREDIR is given.

However, if P_2_THREADSAFE is given or simple redirection mode is used, only standard handles are inherited by a child process even if P_2_NOINEHRIT is not given.

For example, if fd is given no. 10 by open(), it's not inherited by a child. Of course, it's possible to use P_2_XREDIR to pass the fd to a child explicitly. However, when implementing spawn-like functions with spawn2(), it's not easy to control it.

I think, this is a mis-behavior and a different behavior from a explanation of spawn2(). This should be fixed.

Here is the testcase. Pass 0 to 5 to the executable. If argument is none, 0 is default.

mode no. - explanation: result 0 - no thread safe, no redir: PASS 1 - thread safe, no redir: FAIL 2 - no thread safe, simple no redir: PASS 3 - thread safe, simple no redir: FAIL 4 - no thread safe, simple redir: FAIL 5 - thread safe, simple redir: FAIL

cxspawninherit.c.txt

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <process.h>

#include <io.h>
#include <fcntl.h>

#include <libcx/spawn2.h>

#define ERRFD   10

#define PASS_STR "Hello! This is a child."

static inline int set_cloexec( int fd )
{
    int fl = fcntl( fd, F_GETFD );

    return fl == -1 ? -1 : fcntl( fd, F_SETFD, fl | FD_CLOEXEC );
}

int main( int argc, char *argv[])
{
    int mode = 0;
    int p2flags = 0;
    int stdfds_noredir[] = { 0, 0, 0 };
    int stdfds_redir[] = { 0, 0, 0 };
    int *pstdfds = NULL;
    const char * args[] = { argv[ 0 ], "child", NULL };
    int outpipe[ 2 ];
    int saved_stdout;
    int pid;
    char msg[ 256 ];
    int len;
    int failed;

    if( argc > 1 && strcmp( argv[ 1 ], "child") == 0 )
    {
        FILE *errfp = fdopen( ERRFD, "w");

        if( !errfp )
        {
            fprintf( stdout, "Cannot open stderr from fd %d", ERRFD );
            return 1;
        }

        fprintf( stderr, "Child: pid = %x, ppid = %x\n", getpid(), getppid());

        fprintf( stdout, "%s", PASS_STR );

        return 0;
    }
    else if( argc == 2 )
    {
        mode = atoi( argv[ 1 ]);
        switch( mode )
        {
            case 1: /* thread-safe, no redir */
                p2flags = P_2_THREADSAFE;
                pstdfds = NULL;
                break;

            case 2: /* no thread-safe, simple but no redir */
                p2flags = 0;
                pstdfds = stdfds_noredir;
                break;

            case 3: /* thread-safe, simple but no redir */
                p2flags = P_2_THREADSAFE;
                pstdfds = stdfds_noredir;
                break;

            case 4: /* no thread-safe, simple redir */
                p2flags = 0;
                pstdfds = stdfds_redir;
                break;

            case 5: /* thread-safe, simple redir */
                p2flags = P_2_THREADSAFE;
                pstdfds = stdfds_redir;
                break;

            case 0: /* no thread-safe, no redir */
            defaut:
                p2flags = 0;
                pstdfds = NULL;
                break;
        }
    }

    printf("Test mode: [%s], [%s]\n",
           p2flags ? "THREAD-SAFE" : "NO THREAD-SAFE",
           pstdfds == stdfds_noredir ? "SIMPLE but NO REDIR" :
           pstdfds == stdfds_redir ? "SIMPLE REDIR" : "NO REDIR");

    if( pipe( outpipe ) == -1 )
    {
        perror("pipe");
        return 1;
    }

    set_cloexec( outpipe[ 0 ]);
    set_cloexec( outpipe[ 1 ]);

    if( dup2( fileno( stderr ), ERRFD ) != ERRFD )
    {
        perror("dup2");
        return 1;
    }

    stdfds_redir[ 1 ] = outpipe[ 1 ];

    saved_stdout = dup( 1 );

    dup2( outpipe[ 1 ], 1 );

    pid = spawn2( P_NOWAIT | p2flags, args[ 0 ], args, NULL, NULL, pstdfds );

    dup2( saved_stdout, 1 );
    close( saved_stdout );

    close( ERRFD );

    close( outpipe[ 1 ]);

    printf("Parent: child pid = %x, current pid = %x\n", pid, getpid());

    len = read( outpipe[ 0 ], msg, sizeof( msg ));
    if( len == -1 )
        perror("read");
    else
        msg[ len ] = '\0';

    close( outpipe[ 0 ]);

    printf("Message from a child: [%s]\n", msg );

    failed = strcmp( msg, PASS_STR ) != 0;

    printf("%s\n", failed ? "FAILED" : "PASSED");

    return failed;
}