cforkxtermdup

Xterm pager - two terminal output - using pipe and dup2


I am trying to implement a pager in C, I want the code to open another terminal (xterm) and print some outputs in it.

So I first create a pipe and fork the main program, the child will execute xterm with the tail command, the main program will output things in the pipe and the child before executing xterm will dupplicate associate the pipe out with the stdin file descriptor of the child.

I may have misunderstood the usage of pipe and dup2 because my code doesn't work.

int p[2];
pipe(p);
char buff[512];
switch (fork()) {
    case -1:
        fprintf(stderr, "Fork error.\n");
        break;
    case 0:
        dup2(p[0], 0);
        close(p[0]);
        close(p[1]);
        execlp("xterm", "xterm", "tail", NULL);
        break; 
    default:
        scanf("%s", buff);
        write(p[1], buff, strlen(buff));
        getchar();
        break;
}

For now I type something in the parent and nothing is printed, in both process. So how do I create a communication between xterm and the parent process ?

Edit: a sample of my program:

#define VERBM_NOVERB 0
#define VERBM_STDOUT 1
#define VERBM_XTERMO 2

static int fd_xterm = -1;

void init_outputxterm() {
    mkfifo("/tmp/mypipe", 0600);
    switch (fork()) {
        case -1:
            fprintf(stderr, "Fork error.\n");
            break;
        case 0:
            execlp("xterm", "xterm", "-e", "/usr/bin/tail -f /tmp/mypipe", NULL);
            printf("FAILURE\n");
            exit(EXIT_FAILURE);
            break; 
        default:
            if ((fd_xterm = open("/tmp/mypipe", O_WRONLY)) == -1) {
                fprintf(stderr, "Can't open pipe");
                exit(1);
            }
            write(fd_xterm, "yayay\n", 6);
            dprintf(fd_xterm, "Hello world\n");
            getchar();
            break;
    }
}

void verbose_xterm(char *format, ...) {
    dprintf(fd_xterm, BOLD UNDERLINED "verbose - " RESET);
    va_list aptr;
    va_start(aptr, format);
    dprintf(fd_xterm, format, aptr);
    va_end(aptr);
}


void verbose_stdout(char *format, ...) {
    printf(BOLD UNDERLINED "verbose - " RESET);
    va_list aptr;
    va_start(aptr, format);
    vprintf(format, aptr);
    va_end(aptr);
}

void verbose_noverb(char *format, ...) {
}

void (*verbose)(char *format, ...) = verbose_stdout;


void (*verbose_mode[])(char *, ...) = { 
    verbose_noverb,
    verbose_stdout,
    verbose_xterm
};

void verbosity(int mode) {
    if (mode == VERBM_XTERMO)
        init_outputxterm();
    verbose = verbose_mode[mode];
}

In the main:

verbosity(VERBM_XTERMO);

Followed by several calls to verbose. You can see in init_outputxterm a tentavie to write in xterm just after the creation of xterm, like in the solution. But everything displayed only after I quit the program by closing the main terminal brutaly, making the child process orphan (if I quit with Ctrl-C the child is also killed).


Solution

  • Xterm itself doesn't read from stdin, so feeding data to it will not have an effect. But you could open a named pipe and read with tail -f /tmp/mypipe from it, for example like this:

    mkfifo("/tmp/mypipe", 0600);
    switch (fork()) {
        case -1:
            fprintf(stderr, "Fork error.\n");
            break;
        case 0:
            execlp("xterm", "xterm", "-e", "/usr/bin/tail -f /tmp/mypipe", NULL);
            exit(EXIT_FAILURE);
            break; 
        default:
            ;
            char buff[512];
            int fd = open("/tmp/mypipe", O_WRONLY);
            scanf("%s", buff);
            write(fd, buff, strlen(buff));
            getchar();
            break;
    }
    

    This will open a named pipe /tmp/mypipe. The parent process writes data to it and the tail -f-process in the xterm will output it subsequently.