ioleg19029700
ioleg19029700

Reputation: 107

How to send output of previous programme in pipe to the next programme correctly?

The goal is to implement terminal command prog1 > file && prog2 | prog3 given the arguments prog1, file, prog2 and prog3.

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    int fd,status,pipefd[2];
    pipe(pipefd);
    /* S_IRWXU - full acces to file */
    fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
if (!fork()){
    dup2(fd, 1); /* redirection to file */
    close(fd); /* it's useless now */
    execlp(argv[1], argv[1], NULL);
}
else {
    wait(&status);
    if (!status){
        if (fork()){
            dup2(pipefd[1],1);
            close(pipefd[1]);
            close(pipefd[0]);
            execlp(argv[3], argv[3],NULL);
        }
    wait(NULL);
    dup2(pipefd[0],0);
    close(pipefd[0]);
    close(pipefd[1]);
    execlp(argv[4], argv[4],NULL);  
    }
}
return 0;
}

I can't make this code to show data that last programme got from previous one. For example (assume programme's name is prog):

./prog ps f date echo

doesn't show current date. And I don't understand why. Can you explain?

Upvotes: 0

Views: 72

Answers (2)

Jonathan Leffler
Jonathan Leffler

Reputation: 753495

If you run:

date | echo

at the command line prompt of a shell, you will get a newline and no date. Change echo to cat and you will see the date:

$ date | echo

$ date | cat
Mon Dec 11 16:35:54 PST 2017
$

You could fix your code so it works better on errors:

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    if (argc != 5)
    {
        fprintf(stderr, "Usage: %s prog1 file prog2 prog3\n", argv[0]);
        return 1;
    }
    int pid = fork();
    if (pid < 0)
    {
        fprintf(stderr, "%s: failed to fork\n", argv[0]);
        return 1;
    }
    else if (pid == 0)
    {
        /* S_IRWXU - full access to file */
        int fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
        if (fd < 0)
        {
            fprintf(stderr, "%s: failed to open file %s for writing\n", argv[0], argv[2]);
            return 1;
        }
        dup2(fd, 1); /* redirection to file */
        close(fd);   /* it's not needed now */
        execlp(argv[1], argv[1], NULL);
        fprintf(stderr, "%s: failed to execute %s\n", argv[0], argv[1]);
        return 1;
    }
    else
    {
        int status;
        wait(&status);
        if (status == 0)
        {
            int pipefd[2];
            pipe(pipefd);       // Error check?
            pid = fork();
            if (pid < 0)
            {
                fprintf(stderr, "%s: failed to fork\n", argv[0]);
                return 1;
            }
            else if (pid == 0)
            {
                dup2(pipefd[1], 1);
                close(pipefd[1]);
                close(pipefd[0]);
                execlp(argv[3], argv[3], NULL);
                fprintf(stderr, "%s: failed to execute %s\n", argv[0], argv[3]);
                return 1;
            }
            else
            {
                dup2(pipefd[0], 0);
                close(pipefd[0]);
                close(pipefd[1]);
                execlp(argv[4], argv[4], NULL);
                fprintf(stderr, "%s: failed to execute %s\n", argv[0], argv[4]);
                return 1;
            }
        }
    }
    return 0;
}

A lot of C programming is about error handling. Having a function library that makes error reporting simpler helps enormously. For example, I have a library with header file stderr.h and implementation in stderr.c — source available from GitHub. It simplifies (and improves) the error reporting:

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "stderr.h"

int main(int argc, char **argv)
{
    err_setarg0(argv[0]);
    if (argc != 5)
        err_usage("prog1 file prog2 prog3");
    int pid = fork();
    if (pid < 0)
        err_syserr("failed to fork: ");
    else if (pid == 0)
    {
        /* S_IRWXU - full access to file */
        int fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
        if (fd < 0)
            err_syserr("failed to open file %s for writing: ", argv[2]);
        dup2(fd, 1); /* redirection to file */
        close(fd);   /* it's not needed now */
        execlp(argv[1], argv[1], NULL);
        err_syserr("failed to execute %s: ", argv[1]);
    }
    else
    {
        int status;
        wait(&status);
        if (status == 0)
        {
            int pipefd[2];
            pipe(pipefd);       // Error check?
            pid = fork();
            if (pid < 0)
                err_syserr("failed to fork");
            else if (pid == 0)
            {
                dup2(pipefd[1], 1);
                close(pipefd[1]);
                close(pipefd[0]);
                execlp(argv[3], argv[3], NULL);
                err_syserr("failed to execute %s: ", argv[3]);
            }
            else
            {
                dup2(pipefd[0], 0);
                close(pipefd[0]);
                close(pipefd[1]);
                execlp(argv[4], argv[4], NULL);
                err_syserr("failed to execute %s: ", argv[4]);
            }
        }
    }
    return 0;
}

For example, the err_syserr() function reports the system error number and message as well as the command name (prog41) and the message specified in the call. (And yes, there are ways to compress both programs a bit more — the else on the outer if block could be removed, leaving the following code one level less indented (and the code three lines shorter), for example.)

This program was prog41, and the test output could be:

$ ./prog41 rigmarole /dev/not-there fudge pumpkin
prog41: failed to open file /dev/not-there for writing: error (1) Operation not permitted
$ ./prog41 rigmarole not-there fudge pumpkin
prog41: failed to execute rigmarole: error (2) No such file or directory
$ ./prog41 ps not-there fudge pumpkin
prog41: failed to execute pumpkin: error (2) No such file or directory
prog41: failed to execute fudge: error (2) No such file or directory
$ ./prog41 ps not-there date pumpkin
prog41: failed to execute pumpkin: error (2) No such file or directory
$ ./prog41 ps not-there date cat
Mon Dec 11 16:49:11 PST 2017
$ ./prog41 proxy mangler
Usage: prog41 prog1 file prog2 prog3
$

After running the program successfully, the file not-there was there, of course.

Upvotes: 2

Flo Sufmoe
Flo Sufmoe

Reputation: 29

You have three exec's, but 2 forks. I personally would do three forks (one for each exec), and have 2 pipes to communicate within the processes.

Upvotes: -1

Related Questions