Writing a term to bc via pipes in C

2019-07-29 04:11发布

问题:

I ran into a problem with this C exercise. The task is to create two processes. The two are connected with two pipes that terminate in the stdin and stdout of the child. The child process is then replaced with bc. I am then supposed to write a term (e.g. 1 + 2) from the parent to the child process (bc).

The pipes are doing what they're supposed to do, however, bc doesn't seem to like the input. When I write into the pipe, bc responds with multiple lines of the following:

(standard_in) 1: illegal character: ^@

This is my solution so far:

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

int main(int argc, char *argv[]) {

    /*Create two pipes:
        One from parent to child (pipe_pc)
        and one from child to parent (pipe_cp).
    */
    int pipe_pc[2], pipe_cp[2];

    int cid;

    if (pipe(pipe_pc) == -1 || pipe(pipe_cp) == -1) {
        printf("Could not pipe\n");
        exit(EXIT_FAILURE);
    }

    // Create child process
    cid = fork();

    if (cid == -1) {
        printf("Could not fork.\n");
        exit(EXIT_FAILURE);
    }

    // Child process
    if (cid == 0) {

        // Redirect pipes
        close(STDOUT_FILENO);
        close(STDIN_FILENO);
        close(pipe_pc[1]); // Close writing end
        close(pipe_cp[0]); // Close reading end

        dup2(pipe_pc[0], STDIN_FILENO); // Take stdin from parent
        dup2(pipe_cp[1], STDOUT_FILENO); // Give stdout to parent

        int err;

        // Replace child with bc
        err = execl("/usr/bin/bc", "bc --quiet", (char*) NULL);

        printf("%s %d\n", "Could not start bc:", err);
        exit(err);
    }

    // Parent Process
    else {

        char input[128] = "";
        char buffer[128] = "";

        printf("%s\n", "Parent process running");

        // Copy argv to a single string
        for(int i=1; i < argc; i++) {
            strcat(input, argv[i]);
        }

        // Write the input to the child's stdin
        write(pipe_pc[1], input, sizeof(input);

        // Read the child's stdout
        read(pipe_cp[0], buffer, sizeof(buffer));

        printf("Result: %s\n", buffer);

        return 0;
    }

}

Hints and help are greatly appreciated, thanks in advance!

回答1:

The problem was that you did not write correctly in the pipe, so bc received bad input.

I rewrote the else branch.

If you are not sure what you send in a pipe, replace temporarily the pipe descriptor with 0 (i/o fd) and check visually what you did.

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

int main(int argc, char *argv[]) {

    /* Create two pipes: One from parent to child (pipe_pc) and one
       from child to parent (pipe_cp).
    */
    int pipe_pc[2], pipe_cp[2];

    int cid;

    if (pipe(pipe_pc) == -1 || pipe(pipe_cp) == -1) {
        printf("Could not pipe\n");
        exit(EXIT_FAILURE);
    }

    // Create child process
    cid = fork();

    if (cid == -1) {
        printf("Could not fork.\n");
        exit(EXIT_FAILURE);
    }

    // Child process
    if (cid == 0) {

        // Redirect pipes
        close(STDOUT_FILENO);
        close(STDIN_FILENO);
        close(pipe_pc[1]); // Close writing end
        close(pipe_cp[0]); // Close reading end

        dup2(pipe_pc[0], STDIN_FILENO); // Take stdin from parent
        dup2(pipe_cp[1], STDOUT_FILENO); // Give stdout to parent

        int err;

        // Replace child with bc
        err = execl("/usr/bin/bc", "bc --quiet", (char*) NULL);

        printf("%s %d\n", "Could not start bc:", err);
        exit(err);
    }

    // Parent Process
    else {
        char buffer[128] = "";

        // printf("%s\n", "Parent process running");

        for(int i=1; i < argc; i++)
            write(pipe_pc[1], argv[i], strlen(argv[i]));

        write(pipe_pc[1], "\n", 1);

        read(pipe_cp[0], buffer, sizeof(buffer));

        printf("Result: %s\n", buffer);

        return 0;
    }

}


回答2:

Your code has a lot of undefined behavior.


write(pipe_pc[1], input, sizeof(input);

You write all input array. But you need to only write what you copy with strcat(). Use strlen.

write(pipe_pc[1], input, strlen(input));

strcat(input, argv[i]);

here you should verify that you don't exceed 128 octet.

if (strlen(input) + strlen(argv[i]) + 1 > 128) {
  break;
}
strcat(input, argv[i]);

read(pipe_cp[0], buffer, sizeof(buffer));

here you don't verify that buffer is a valid c string.

ssize_t ret = read(pipe_cp[0], buffer, sizeof(buffer) - 1);
if (ret < 0)
  return 1;
buffer[ret] = '\0';

You forget to close pipe in the parent

close(pipe_pc[0]);
close(pipe_cp[1]);

and after your read/write

close(pipe_pc[1]);
close(pipe_cp[0]);

You forget to close pipe in child after your dup2()

close(pipe_pc[1]);
close(pipe_cp[0]);

You don't need to close your old fd before dup2, he do it for you. And you don't check error of dup2()



标签: c pipe bc