ogarogar ogarogar - 11 days ago 6
C Question

Unnamed pipes in C, chaning stdin,stdout in fork()

I'm trying to write a program that will implement command:

ls -al | tr 'a-z' 'A-Z' | grep X > testing


Basically I want to learn how to use piping twice in C. There is a lot of post where we're using only one '|'. I made some attempts based on examples with only one '|' but sadly it doesn't work. There is a code.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#define MAX 512
int main(int argc,char** argv){
int pdesk[2];
int pipedesk[2];
if(pipe(pipedesk)==-1 || pipe(pdesk)==-1){
perror("Pipe");
exit(1);
}
switch(fork()){
case -1 :
perror("Creating process");
exit(1);
case 0:
dup2(pdesk[1],STDOUT_FILENO);
close(pdesk[1]);
execlp("ls","ls","-al",NULL);
perror("ls");
exit(1);
default: {
if(fork()==0){
if(fork()==0){
dup2(pdesk[0],STDIN_FILENO);
close(pdesk[0]);
dup2(pipedesk[1],STDOUT_FILENO);
close(pipedesk[1]);
execlp("tr","tr","a-z","A-Z",NULL);
perror("tr");
exit(1);
}
dup2(pipedesk[0],STDIN_FILENO);
close(pipedesk[0]);
int desk=open("testing",O_WRONLY|O_CREAT,0640);
if(desk==-1){
perror("opening file");
exit(1);
}
dup2(desk,STDOUT_FILENO);
if(close(desk)==-1){
perror("closing file");
exit(1);
}
execlp("grep","grep","X",NULL);
perror("grep");
exit(1);
}
// wait(NULL);

}
return 0;
}
}

Answer

I don't think that signal handling is relevant. What is relevant is making sure that pipes are closed properly. Processes won't get EOF on the reading end of a pipe if there's a process with the writing end open. That includes the current process; if a process has both the reading and writing ends of a pipe open, it will never get EOF on the reading end of the pipe because there is a process that could, in theory, write to it. Such a process therefore hangs permanently in a read() on the pipe.

This version of your code works fine for me:

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

int main(void)
{
    int pdesk[2];
    int pipedesk[2];
    int pid;

    if (pipe(pipedesk) == -1 || pipe(pdesk) == -1)
    {
        perror("Pipe");
        exit(1);
    }
    //fprintf(stderr, "PID %d: controller\n", (int)getpid());

    switch (pid = fork())
    {
    case -1:
        perror("Creating process");
        exit(1);
    case 0:
        //fprintf(stderr, "PID %d: will be 'ls'\n", (int)getpid());
        dup2(pdesk[1], STDOUT_FILENO);
        close(pdesk[0]);        // JL
        close(pdesk[1]);
        close(pipedesk[0]);     // JL
        close(pipedesk[1]);     // JL
        execlp("ls", "ls", "-al", (char *)NULL);
        perror("ls");
        exit(1);
    default:
        //fprintf(stderr, "PID %d: ls process PID = %d\n", (int)getpid(), pid);
        if ((pid = fork()) == 0)
        {
            //fprintf(stderr, "PID %d: about to fork 'tr'\n", (int)getpid());
            if ((pid = fork()) == 0)
            {
                //fprintf(stderr, "PID %d: will be 'tr'\n", (int)getpid());
                dup2(pdesk[0], STDIN_FILENO);
                close(pdesk[0]);
                close(pdesk[1]);        // JL
                dup2(pipedesk[1], STDOUT_FILENO);
                close(pipedesk[0]);     // JL
                close(pipedesk[1]);
                execlp("tr", "tr", "a-z", "A-Z", (char *)NULL);
                perror("tr");
                exit(1);
            }
            //fprintf(stderr, "PID %d: about to exec 'grep'\n", (int)getpid());
            dup2(pipedesk[0], STDIN_FILENO);
            close(pdesk[0]);        // JL
            close(pdesk[1]);        // JL
            close(pipedesk[0]);
            close(pipedesk[1]);     // JL
            int desk = open("testing", O_WRONLY | O_CREAT, 0644);
            if (desk == -1)
            {
                perror("opening file");
                exit(1);
            }
            dup2(desk, STDOUT_FILENO);
            if (close(desk) == -1)
            {
                perror("closing file");
                exit(1);
            }
            execlp("grep", "grep", "X", (char *)NULL);
            perror("grep");
            exit(1);
        }

        //fprintf(stderr, "PID %d: closing pipes\n", (int)getpid());
        close(pdesk[0]);        // JL
        close(pdesk[1]);        // JL
        close(pipedesk[0]);     // JL
        close(pipedesk[1]);     // JL

        break;
    }

    int status;
    int corpse;
    while ((corpse = wait(&status)) != -1)
        fprintf(stderr, "PID %d: child %d died 0x%.4X\n", (int)getpid(), corpse, status);

    return 0;
}

Note all the lines marked // JL which are close operations. While it is probably true that you can get away without a few of them (the ones for the first child, for example, are probably 'optional'), you should as a matter of routine close all the pipes that you are not using. Note, in particular, the closes for the original parent process (the last four) — those are important.

Not closing enough pipe descriptors is one of the most common mistakes when working with multiple pipes. In general, if you use dup2() (or dup()) to duplicate a pipe descriptor to standard input or output, you should close both ends of the original pipe. And if your process is not using the pipe descriptors at all, you should close them.

Sample output from program:

$ ./pp61
PID 35665: child 35666 died 0x0000
PID 35665: child 35667 died 0x0000
$

Sample contents of testing file:

DRWXR-XR-X   44 JLEFFLER  STAFF   1496 NOV 23 17:28 .
DRWXR-XR-X  137 JLEFFLER  STAFF   4658 NOV 23 17:28 ..
DRWXR-XR-X   18 JLEFFLER  STAFF    612 OCT 23 20:00 .GIT
DR-XR-XR-X    4 JLEFFLER  STAFF    136 AUG 14 23:27 SAFE
DRWXR-XR-X   35 JLEFFLER  STAFF   1190 NOV 23 09:19 UNTRACKED
-RW-R--R--    1 JLEFFLER  STAFF     81 NOV 20 20:53 ANIMALS.TXT
DRWXR-XR-X    3 JLEFFLER  STAFF    102 SEP 12 00:03 DOC
DRWXR-XR-X    8 JLEFFLER  STAFF    272 NOV 10 20:58 ETC
DRWXR-XR-X   17 JLEFFLER  STAFF    578 JUL 15 19:06 INC
-RW-R--R--    1 JLEFFLER  STAFF   1217 NOV 21 21:26 IX37.SQL
DRWXR-XR-X    5 JLEFFLER  STAFF    170 JUL  9 23:47 LIB
-RWXR-XR-X    1 JLEFFLER  STAFF   9052 NOV 19 13:01 LL73
DRWXR-XR-X    3 JLEFFLER  STAFF    102 NOV  6 15:40 LL73.DSYM
-RWXR-XR-X    1 JLEFFLER  STAFF   8896 NOV  6 10:38 LL83
DRWXR-XR-X    3 JLEFFLER  STAFF    102 NOV  6 10:10 LL83.DSYM
-RW-R--R--    1 JLEFFLER  STAFF    108 NOV 20 20:53 NEWANIMAL.TXT
-RWXR-XR-X    1 JLEFFLER  STAFF   9124 NOV 20 20:38 PD43
DRWXR-XR-X    3 JLEFFLER  STAFF    102 NOV 20 20:38 PD43.DSYM
-RWXR-XR-X    1 JLEFFLER  STAFF   9148 NOV 23 17:28 PP61
DRWXR-XR-X    3 JLEFFLER  STAFF    102 NOV 23 14:11 PP61.DSYM
-RWXR-XR-X    1 JLEFFLER  STAFF   9016 NOV 23 11:07 RS19
DRWXR-XR-X    3 JLEFFLER  STAFF    102 NOV 23 10:03 RS19.DSYM
DRWXR-XR-X  146 JLEFFLER  STAFF   4964 OCT  9 17:06 SRC
-RWXR-XR-X    1 JLEFFLER  STAFF   8760 NOV 23 13:12 STP83
DRWXR-XR-X    3 JLEFFLER  STAFF    102 NOV 23 13:04 STP83.DSYM
DRWXR-XR-X    6 JLEFFLER  STAFF    204 NOV  6 21:52 TMP
-RWXR-XR-X    1 JLEFFLER  STAFF   8808 NOV 23 10:38 TR37
DRWXR-XR-X    3 JLEFFLER  STAFF    102 NOV 23 10:38 TR37.DSYM
-RWXR-XR-X    1 JLEFFLER  STAFF  26772 NOV 21 20:18 XY73
DRWXR-XR-X    3 JLEFFLER  STAFF    102 NOV 21 20:18 XY73.DSYM
-RW-R--R--    1 JLEFFLER  STAFF    467 NOV 21 20:18 XY73.L
Comments