1

I'm trying to get something like this to work in c using piping and fork : echo "an operation like 10+10" | bc

To be precise, I tried to create 2 pipes, one where the server will write the operation and bc will read and another where the result of the operation (by bc) will go and the server will read it and printf it. For this, I'm changing the output and input of the parent and child process.

Here is my code:

int main(){

int t1, t2;
char resultat[5];
int pipe1[2];
int pipe2[2];

pipe(pipe1);
pipe(pipe2);


int resultat_fork = fork();

if(resultat_fork == -1){
    exit(EXIT_FAILURE);
}

if(resultat_fork!=0){  
    printf("I am the parent\n");

    close(pipe1[0]);
    close(pipe2[1]);
    //We only want to write into pipe1 and read from pipe2, we can close the two other

    write(pipe1[1], "20*5\n", 20);
    //write on pipe1 the expression

    read(pipe2[0], resultat, sizeof(resultat));
    //read from pipe2 the answer (written by bc)

    printf("resultat : %s\n",resultat);

    close(pipe1[1]);
    close(pipe2[0]);


}else{  
    printf("I am the children\n");

    close(pipe1[1]);
    close(pipe2[0]);
    //We only want to write into pipe2 and read from pipe1, we can close the two other

    dup2(pipe1[0], 0);  
    //redirection standard input to pipe1[0]

    dup2(pipe2[1], 1);
    //redirection standard output to pipe2[1]

    execlp("bc", "bc", NULL);
    //execute bc, which normaly will read the operation from pipe1 and write the answer into pipe2, but I think it's here the problem come out

    close(pipe1[0]);
    close(pipe2[1]);
}


return 0;

}

I'm getting the correct answer but with the error : "(standard_in) 2: illegal character : ^@" "(standard_in) 2: illegal character : :" plus I have to CTRL C to quit. I guess it comes from BC but why...

What am I doing wrong? Thank you! I've already seen few exemple but only threw one pipe.

Karl
  • 15
  • 4
  • Your test for the parent/child is wrong. If `fork()` returns zero, you’re in the child. Otherwise, the value returned is the PID of the child which is essentially never the value 1. – Jonathan Leffler Dec 09 '17 at 14:45
  • Note that if you had printed any “I am here” messages at all, you’d have seen both processes saying “I am being childish” and neither saying “I’m being parental”. – Jonathan Leffler Dec 09 '17 at 14:47
  • Thank you for answering. What a shame. I'm really sorry for this. Now it keeps returning me "(standard_in) 1: illegal character : ^@". I believe it comes from the dup2 which aren't used properly, but i'm not sure – Karl Dec 09 '17 at 14:54
  • EDIT: I think it comes from bc – Karl Dec 09 '17 at 14:59
  • Your code isn’t an MCVE ([MCVE]), but it is easy to guess that you didn’t write an 80-character expression with a newline at the end. You need to send only the cutest length. Also note that the child won’t add a null byte at the end of its message. You have to add it, or not treat the reply as a string. – Jonathan Leffler Dec 09 '17 at 15:00
  • I've added the entire code, sorry for that. I can finally have my results but still with two errors : "(standard_in) 2: illegal character : ^@" "(standard_in) 2: illegal character : :". As you said, this would come from the char tab? – Karl Dec 09 '17 at 15:42
  • When you use `write(pipe1[1], "20*5\n", 20);`, how many bytes of information are written to the pipe? What do bytes 6-20 (one-based indexing) contain? How do you know they contain that? Don't write what you don't mean to write; write only what is needed. – Jonathan Leffler Dec 09 '17 at 15:59

1 Answers1

2

This works. Note how it specifies the size of the data to be written, and how it checks the writes and reads, and also how it closes file descriptors. (Remember: sizeof("string literal") counts the null byte, unlike strlen(). It's also a compile time constant. However, more general purpose code would use strlen() on the current expression string.)

Rule of Thumb: If you use dup2() to duplicate a pipe file descriptor to standard input or standard output, close both ends of the pipe.

That also applies if you use dup(), or fcntl() with F_DUPFD or F_DUPFD_CLOEXEC instead.

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

int main(void)
{
    char resultat[5];
    int pipe1[2];
    int pipe2[2];

    pipe(pipe1);
    pipe(pipe2);

    int resultat_fork = fork();

    if (resultat_fork == -1)
    {
        exit(EXIT_FAILURE);
    }

    if (resultat_fork != 0)
    {
        printf("I am the parent\n");

        close(pipe1[0]);
        close(pipe2[1]);

        if (write(pipe1[1], "20*5\n", sizeof("20*5\n") - 1) != sizeof("20*5\n") - 1)
            fprintf(stderr, "write to child failed\n");

        int nbytes = read(pipe2[0], resultat, sizeof(resultat));
        if (nbytes <= 0)
            fprintf(stderr, "read from child failed\n");
        else
            printf("resultat: [%.*s]\n", nbytes, resultat);

        close(pipe1[1]);
        close(pipe2[0]);
    }
    else
    {
        printf("I am the child\n");

        close(pipe1[1]);
        close(pipe2[0]);
        dup2(pipe1[0], 0);
        dup2(pipe2[1], 1);
        close(pipe1[0]);    /* More closes! */
        close(pipe2[1]);    /* More closes! */

        execlp("bc", "bc", NULL);
        fprintf(stderr, "Failed to execute bc\n");
        exit(EXIT_FAILURE);
    }

    return 0;
}

There is still room for improving the error handling; the code ploughs on after reporting some of the errors, which is probably not the best behaviour.

Output:

I am the parent
I am the child
resultat: [100
]

(Note: if you pipe the output of the program somewhere, you don't see the I am the child message. For the reasons why, see printf() anomaly after fork().)

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Thank you very much. I can't test the code right now, but as soon as I can I will try it and answer you back. – Karl Dec 09 '17 at 17:20