0

I'm trying to get a feeling about how pipe works in Linux. I wrote the following program, compiled it and then run it in terminal. The program exits normally without errors, but it didn't print out any message. Is there anything wrong?

PS: this code snippet is from MIT's xv6 operating system course.

#include <stdio.h>
#include <unistd.h>

int main() {
    int p[2];
    char *argv[2];
    argv[0] = "wc";
    argv[1] = 0;
    pipe(p);
    if (fork() == 0) {
        close(0);
        dup(p[0]);
        close(p[0]);
        close(p[1]);
        execvp("/bin/wc", argv);
    } else {
        write(p[1], "hello world\n", 12);
        close(p[0]);
        close(p[1]);
    }
}
Bicheng
  • 687
  • 8
  • 20
  • 1
    You should probably check return values, like from `execvp`. On my Ubuntu machine `command -v wc` returns `/usr/bin/wc`. Also see [How to check if a program exists from a Bash script?](https://stackoverflow.com/q/592620/608639) – jww Dec 22 '19 at 15:03
  • @jww Thanks! My system also has "wc" in `usr/bin/wc`. – Bicheng Dec 22 '19 at 15:13
  • @jww, The `exec*()` functions do not return anything unless the function fails. So if it returns should always call `perror()` then `exit( EXIT_FAILURE );` – user3629249 Dec 22 '19 at 16:57
  • the posted code fails to check for when `fork()` fails. In such a case, should call `perror()` and then `exit( EXIT_FAILURE );` – user3629249 Dec 22 '19 at 16:59
  • @user3629249- See the [`execvp (3)` man page](https://linux.die.net/man/3/execvp). I'm guessing (and it is just a guess), `execvp` is failing and `errno` is `ENOENT`. – jww Dec 22 '19 at 17:22
  • I've run the code with many modifications. The main problem is the piped string from 'main' is not getting to the 'wc' program. Also, 'wc' is expecting a file name, not a random string. – user3629249 Dec 22 '19 at 17:44
  • I tried your code unmodified, and it produced the same result as `echo "hello world" | wc` with the minor difference that the output sometimes comes after the new command line prompt. In addition to checking for errors in `execvp()`, `pipe()`, `fork()`, `dup()` and `write()`, you should call `waitpid()` on the child. And `int main()` is bad c. – HAL9000 Dec 22 '19 at 18:03

2 Answers2

2

the following proposed code:

  1. cleanly compiles
  2. performs the desired functionality
  3. passes the name of a local file, rather than some random string AND the file name is terminated with a NUL byte
  4. you will need to modify the passed file name to a file that is in your current directory
  5. notice the length passed to write() includes the trailing NUl byte

and now the proposed code:

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

#include <sys/types.h>
#include <sys/wait.h>

int main() {
    int p[2];
    char *argv[3];
    argv[0] = "wc";
    argv[1] = "--files0-from=-";
    argv[2] = (char *) NULL;
    if( pipe(p) != 0 )
    {
        perror( "pipe failed" );
        exit( EXIT_FAILURE );
    }
    pid_t pid = fork();
    switch( pid )
    { 
        case -1:
        perror( "fork failed" );
        exit( EXIT_FAILURE );
        break;

        case 0:
        close(0);
        if( dup2(p[0], 0) != 0 )
        {
            perror( "dup2 failed" );
            exit( EXIT_FAILURE );
        }
        close(p[0]);
        close(p[1]);
        execvp("/usr/bin/wc", argv);
        perror( "execvp failed" );
        exit( EXIT_FAILURE );
        break;

        default:
        write(p[1], "untitled2.c", 12);
        close(p[0]);
        wait( NULL );
        close(p[1]);
    }
}

Note: the untitled2.c is this source file.

a run of the program results in:

47 101 818 untitled2.c
user3629249
  • 16,402
  • 1
  • 16
  • 17
0

The command wc prints the file name after the number of rows, chars and words only if the argoument of wc is an actual file:

$ wc hello_word.txt 1 2 12 hello_word.txt

In fact neither doing the redirection from the terminal will print out the message

$ echo "hello word" | wc 1 2 11

If you prefer you can simply print out the message on stdout on the child process before writing on the pipe

if (fork() == 0) {
  .. 
} else {
    printf("Message: 'hello world'\n");
    write(p[1], "hello world\n", 12);
    close(p[0]);
    close(p[1]);
}
LucaBonadia
  • 91
  • 2
  • 12