3

I'm on my way to create a simple terminal. I have the following code to test bidirectional piping, where the parent process should only attempt to write when the child process expects it, however at the moment it doesn't even output anything. I guessed there might be something wrong with mixing read/write and scanf/printf being not buffered the first method, so I modified them to be buffered with fdopen, although I would prefer using simple read/write at the parent process.

Can you help me fix this code to achieve the main goal (eg. with returning true in can_write if the child expects an input)?

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

bool can_write(int parent_write)
{
    return true;
}


int main()
{
    int a[2], b[2], parent_read, parent_write, child_read, child_write;
    char output[11] = {0};
    pipe(a);
    pipe(b);
    parent_read = a[0];
    child_write = a[1];
    child_read = b[0];
    parent_write = b[1];
    FILE *parent_write_file = fdopen(parent_write, "w");
    FILE *parent_read_file = fdopen(parent_read, "r");

    if (fork() == 0) {
        char message[5] = {0};

        close(parent_read);
        close(parent_write);
        dup2(child_read, STDIN_FILENO);
        dup2(child_write, STDOUT_FILENO);

        //sleep(1);
        scanf("%4s", message);
        printf("%s %s\n", message, message);

        close(child_read);
        close(child_write);
        exit(0);
    }

    fcntl(parent_read, F_SETFL, fcntl(parent_read, F_GETFL, 0) | O_NONBLOCK);
    close(child_read);
    close(child_write);

    while(1) {
        if (can_write(parent_write)) {
            fprintf(parent_write_file, "test");
            //write(parent_write, "test", 4);
        }

        //if (read(parent_read, output, 10) > 0) {
        if (fread(output, 1, 10, parent_read_file) > 0) {
            printf("%s\n", output);
            exit(0);
        }
    }
}
6
  • Hint: poll() or select() are useful for this. Commented Nov 8 at 15:24
  • Beware that using pipes like this is prone to deadlocks. Pipes can only hold a fixed amount of data, and if both processes are writing and one of them fills up the pipe, writes will block waiting for the full pipe to be read from - but the other process is also writing so it will never read. Commented Nov 8 at 15:49
  • Reminder: In scanf("%s", message);, the unbounded %s specifier is as dangerous as gets. To avoid buffer overflows, limit the length (buffer size minus one: %4s), or use fgets. Commented Nov 8 at 15:56
  • 1
    @Shawn Neither of those will tell you when the receiver is expecting input. They just tell you whether you can write to the pipe without blocking, which is when the pipe buffer isn't full. Commented Nov 8 at 18:10
  • There's no built-in way to tell when the reader of any stream is actually trying to read from it. Tools like Expect look for prompts, which interactive programs send before they wait for input. Commented Nov 8 at 18:12

1 Answer 1

1

As @Barmar said in the comments, there is no built-in property to check if the child process is about to read from the pipe. Even standard terminals allow input during process execution. So when I run the following code, I can enter text that is as not being processed by the child process, the shell tries to execute after process termination.

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

int main()
{
    int a[2], b[2], child_read, child_write;
    char output[3] = {0};
    pipe(a);
    pipe(b);
    child_write = a[1];
    child_read = b[0];
    FILE *parent_write = fdopen(b[1], "w");
    FILE *parent_read = fdopen(a[0], "r");

    if (fork() == 0) {
        close(b[1]);
        close(a[0]);
        fclose(parent_read);
        fclose(parent_write);
        dup2(child_read, STDIN_FILENO);
        dup2(child_write, STDOUT_FILENO);

        sleep(3);
        printf("ok\n");

        close(child_read);
        close(child_write);
        exit(0);
    }

    close(child_read);
    close(child_write);

    if (fread(output, 1, 2, parent_read) > 0) {
        printf("%s\n", output);
    }
    
    fclose(parent_read);
    fclose(parent_write);
    close(b[1]);
    close(a[0]);
}
$ gcc -o test test.c ; ./test
ls
ok
$ ls
test  test.c
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.