dup2() and exec()

5.7k Views Asked by At
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

int
main( int argc, char **argv)
{
    int pfds[ 2], i;
    size_t pbytrd;
    pid_t childpid;
    char buffer[ 200];

    pipe( pfds);
    if( ( childpid = fork() ) == -1)
    {
            perror( "fork error: ");
            exit( 1);
    }
    else if( childpid == 0)
    {
            close( pfds[ 0]);
            dup2( pfds[1], 1);
            close( pfds[ 1]);
            for( i = 0; i < 10; i++)
               printf("Hello...");
            execlp( "xterm","xterm","-e","./sample_a", (char *) 0);
            exit( 0);
}
else
{

        close( pfds[ 1]);
        for( ; ; )
        {
            if( ( pbytrd = read( pfds[ 0], buffer, sizeof( buffer) ) ) == -1)
            {
                perror(" read error: ");
                exit( 1);
            }
            else if( pbytrd == 0)
            {

                write( fileno( stdout), "Cannot read from pipe...\n", strlen( "Cannot read from pipe...\n") );
                exit( 0);
            }
            else
            {
                write( fileno( stdout), "MESSAGE: ", strlen( "MESSAGE: ") );
                write( fileno( stdout), buffer, pbytrd);
            }
        }       
}   
return( 0);

}

My sample_a.c code is below:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

int
main( int argc, char **argv)
{
        int i;
        for( i= 0; i< 10; i++)
        write( fileno( stdout), "Hello...", strlen( "Hello...") );
    return( 0);
 }

In the above code, what I really want to do is: 1) Redirect output from the child process to pipe and have parent read from the pipe and print it out to stdout.

I am able to redirect child process output( "printf") from stdout to the pipe but I am not able to redirect execlp's child process, xterm, output to the pipe.

Can anybody help me this?

4

There are 4 best solutions below

1
On

xterm is a terminal emulator. It executes the program you provided (sample_a), connecting its input and outputs to itself, so that the program receives the user input in its standard input, and prints to the user whatever the it sends to its standard and error outputs.

What your program is doing is to connect the xterm output to the pipe, and trying to read that. But xterm is a GUI program, it does not normally write data to its outputs. Perhaps it is not really clear what exactly you are trying to achieve.

If you remove the xterm part, it should work as you expect, that is, the parent process will see what sample_a writes to the output.

        execlp("./sample_a", "./sample_a", (char *) 0);
0
On

xterm has no output, so there's no way to redirect it anywhere. xterm runs a child and redirects the child's output to itself, so if you want to redirect the child's output somewhere other than the xterm, you'll need to do that yourself in the child, AFTER xterm starts it. The easiest way to do that is probably to have xterm start a shell and use shell redirection. To redirect just stderr for the child, you'd use something like:

execlp("xterm", "xterm", "-e", "/bin/sh", "-c", "./sample_a 2>somewhere", 0);

replacing somewhere with something appropriate. Assuming you want to capture that in your original program, you probably want to create a temporary fifo with tempnam(3) and mkfifo(3) and open it for reading in your parent. You need to create it before forking and open it after forking, as the open will block until the shell runs and opens the write end. Once the open is complete, you can delete the fifo, and read from the now unnamed fifo.

0
On

The pipeFD array stores two file descriptions of in and out, once you created a xterm window. The child process do not know where to write. So you can use dup2:

dup2(pipeFD[1],100);
close(pipeFD[1]);

It maps the old write port to a new port(other numbers also work). And in the xterm you can write to the pipe by

write(100,"something you wanna to write").
0
On

I faced a similar situation. My goal was so send some information from child process that runs in a xterm. As also reported earlier, I was not able to use dup2. A workaround for this is to pass the fd to the new binary as xterm args.

A suggested approach (without error checks for brevity) is

if (childpid  == 0)
{
    /* child closes read*/
    close(pipeFD[0]);
    char writeFD[10]; // do consider space for '\0'
    /* Extract the write fd and put it in a char buffer 
       make compatible as per execlp args */
    sprintf(writeFD, "%d", pipeFD[1]); 
    /* just pass the writeFD as an arg */
    execlp("xterm", "xterm", "-e", "./binary", writeFD, (char *) 0))

}
else
{
    /* parent closes write */
    close(pipeFD[1]);
    if((bytesRead = read(pipeFD[0], buf, SIZE)) != -1)
    {
        printf("Recieved %s\n", buf);
        close(pipeFD[0]);
    }
 }

On the child process that runs in xterm, extract the fd

main(int argc, char** argv) {
   int fd = atoi(argv[1]);
   write(fd, buf,sizeof(buf));
}     

I don't know why dup2 doses'nt work with xterm, but this workaround solves the requirement.