简体   繁体   中英

pipe()-ing programs causes EIO

We have a homework question where we have to write a program that takes two arguments, executes them as shell commands pipeing the stdout of the first program into the stdin of the second program.

This is my solution to this homework question. The same file is reproduced below.

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

#define SHELL   "/bin/sh"

extern int  main(int, char*[]);
static int  readingend(int, int, const char*);
static int  writingend(int, int, const char*);
static int  usr1handler();

extern int
main(int argc, char *argv[])
{
    int pipefd[2], status;
    pid_t rpid, wpid;

    if (argc != 3) {
        fprintf(stderr, "Invalid argument count\n");
        return (EXIT_FAILURE);
    }

    if (pipe(pipefd)) {
        perror("Cannot make a pipe");
        return (EXIT_FAILURE);
    }

    signal(SIGUSR1, (void(*)(int))usr1handler);

    if ((rpid = fork()) < 0) {
        perror("Cannot fork");
        return (EXIT_FAILURE);
    }

    if (rpid == 0)
        return (readingend(pipefd[0], pipefd[1], argv[2]));


    if ((wpid = fork()) < 0) {
        perror("Cannot fork");
        kill(rpid, SIGKILL);
        return (EXIT_FAILURE);
    }

    if (wpid == 0)
        return (writingend(pipefd[0], pipefd[1], argv[1]));

    close(pipefd[0]);
    close(pipefd[1]);

    waitpid(rpid, &status, 0);
    return (status);
}

static int
readingend(int rfd, int wfd, const char *cmd)
{
    close(wfd);
    dup2(rfd, STDIN_FILENO);

    /* execl() only returns on error */
    execl(SHELL, SHELL, "-c", cmd, NULL);
    fprintf(stderr, "Cannot execute %s: ", cmd);
    perror(NULL);

    /* clean up */
    kill(getppid(), SIGUSR1);
    return (EXIT_FAILURE);
}

static int
writingend(int rfd, int wfd, const char *cmd)
{
    close(rfd);
    dup2(wfd, STDOUT_FILENO);

    execl(SHELL, SHELL, "-c", cmd, NULL);
    fprintf(stderr, "Cannot execute %s: ", cmd);
    perror(NULL);

    /* clean up */
    kill(getppid(), SIGUSR1);
    return (EXIT_FAILURE);
}

static int
usr1handler()
{
    /* child already printed diagnostic */
    exit(EXIT_FAILURE);
}

The problem is that in some invocations, the first process receives an EIO upon reading from stdin. This seems to happen only on some shells: It happens with ksh93 and bash but not with bosh . Such an error looks like this:

$ LANG=C ./mypipe cat true
$ cat: -: Input/output error

Can anybody tell me why this error occurs? What can I do to have that error not occur anymore? Clearly there must be a way as executing cat | true cat | true in an ordinary shell produces the desired result without any spurious EIO .

This issue happens because true instantly terminates, causing mypipe to terminate, too, leaving cat orphaned. If this happens, read() may fail:

EIO

The process is a member of a background process attempting to read from its controlling terminal, the process is ignoring or blocking the SIGTTIN signal, or the process group is orphaned. This error may also be generated for implementation-defined reasons.

This issue can be prevented by waiting on both processes like this:

extern int
main(int argc, char *argv[])
{
    int status, estatus;

    /* ... */


    /* wait for both processes to terminate */
    if (wait(&status) == rpid) {
        estatus = status;
        wait(&status);
    } else {
        wait(&status);
        estatus = status;
    }

    return (estatus);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM