简体   繁体   中英

Trying to replicate basic bash pipe but i get a stdin: Input/output error

I am currently working on a university project to basically built my own simple shell. Everything is working great so far. The only thing giving me trouble is pipes. To make it easier for myself to figure out why they are not working as intended I wrote this little testing program where I try to replicate the bash behaviour of cat | ls cat | ls . But i now sadly get this error cat: stdin: Input/output error and i really can't figure it out.

Here is my program:

#include <unistd.h>
#include <stdlib.h>

int     main(void)
{
    extern char **environ;
    char *argv1[] = {"cat",NULL};
    char *argv2[] = {"ls",NULL};
    int fd[2];
    pid_t pid;
    int ret;

    pipe(fd);
    pid = fork();
    if  (pid == 0) 
    {
        close(fd[0]);
        dup2(fd[1], STDOUT_FILENO);
        execve("/bin/cat", argv1, environ);
        exit (0);
    }
    else if (pid > 0)
    {
        close(fd[1]);
        dup2(fd[1], STDIN_FILENO);
        execve("/bin/ls", argv2, environ);
        waitpid(pid, &ret, 0);
    }
    return (0);
}

You want a pipe like:

ls | cat

But, you're setting this up like:

cat | ls

And, in your current code, for the ls side, you're doing:

close(fd[1]);
dup2(fd[1], STDIN_FILENO);

This is wrong for two reasons:

  1. You're closing the wrong side of the pipe, so the dup2 gets a closed fd as its first argument
  2. You're attaching the output side of the pipe to the command's input side

So, we need to reverse the pipe order and fix the closing.

Also, after doing dup2(X,...) we want to do close(X) .

Also, note that doing waitpid _after execve will have no effect unless the execve fails.

Here is the refactored and working code:

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

int
main(void)
{
    extern char **environ;
    char *argv_cat[] = { "cat", NULL };
    char *argv_ls[] = { "ls", NULL };
    int fd[2];
    pid_t pid;
    int ret;

    pipe(fd);

    pid = fork();

    // we want:
    //   ls | cat
    if (pid == 0) {
        close(fd[1]);
        dup2(fd[0], STDIN_FILENO);
        close(fd[0]);
        execve("/bin/cat", argv_cat, environ);
        exit(0);
    }

    else if (pid > 0) {
        close(fd[0]);
        dup2(fd[1], STDOUT_FILENO);
        close(fd[1]);
        execve("/bin/ls", argv_ls, environ);
        waitpid(pid, &ret, 0);
    }

    return (0);
}

UPDATE:

waitpid after execve is pointless. – William Pursell

Not quite. It reaps the [stuck] child process so that it doesn't become a child of the init/systemd process.

In the above example, I had forgotten to add a close(STDOUT_FILENO) before the waitpid to "release" the cat process.

Here is the adjusted code:

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

int opt_f;

int
main(int argc,char **argv)
{
    extern char **environ;
    char *argv_cat[] = { "cat", NULL };
    char *argv_ls[] = { "ls", NULL };
    int fd[2];
    pid_t pid;
    int status;

    --argc;
    ++argv;

    for (;  argc > 0;  --argc, ++argv) {
        char *cp = *argv;
        if (*cp != '-')
            break;

        cp += 2;
        switch (cp[-1]) {
        case 'f':
            opt_f = ! opt_f;
            break;
        }
    }

    pipe(fd);

    pid = fork();

    // we want:
    //   ls | cat
    if (pid == 0) {
        if (opt_f)
            fprintf(stderr,"cld: getpid=%d\n",getpid());
        close(fd[1]);
        dup2(fd[0], STDIN_FILENO);
        close(fd[0]);
        execve("/bin/cat", argv_cat, environ);
        exit(0);
    }

    else if (pid > 0) {
        close(fd[0]);
        dup2(fd[1], STDOUT_FILENO);
        close(fd[1]);

        const char *ls = opt_f ? "/bin/gooch" : "/bin/ls";
        execve(ls, argv_ls, environ);

        fprintf(stderr,"execve failure of '%s' -- %s\n",ls,strerror(errno));

        // release other process (cat)
        close(STDOUT_FILENO);

        // reap the child
        pid_t ret = waitpid(pid, &status, 0);

        fprintf(stderr,"ret=%d pid=%d status=%8.8X\n",ret,pid,status);
    }

    return (0);
}

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