1
votes

Problem

I only get this in the terminal output. I believe the program is getting stuck at the fork() call but I don't know exactly why.

The name of the program is q9:

prompt>$ ./q9 inputString
Parent: writing to pipe 'inputString'

Task

  • read input from terminal into the parent-to-child pipe.
  • fork() to create a child process.
  • read input from parent-to-child pipe.
  • concatenate some other string to that string read in from the pipe.
  • write the newly concatenated string to the child-to-parent pipe.
  • in the parent, read from the child-to-parent pipe and print the output read from the pipe to the terminal.

Attempts

I have tried fixing this by:

  • attempting to close pipes in different places. I thought I may have missed something or left something open, but I don't think so.
  • placing a wait() in the parent because perhaps it wasn't letting the child run completely
  • attempted to print the output of the concatenated string just in case it was that messing up the prints.

Code

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

int main (int argc, char *argv[]) {
    // parent RUN
    if(argc == 1) {
        printf("usage: q9 <string>\n");
        return 0;
    }

    // create two way pipes
    int parent_fds[2], child_fds[2];

    // create strings to save too
    char fromParent[100];
    char fromChild[100];

    // read:[0] - write:[1]
    if (pipe(parent_fds) != 0 && pipe(child_fds) != 0) {
        fprintf(stderr, "pipes failed!\n");
    }

    // close unused pipe end by parent
    close(parent_fds[0]);
    close(child_fds[1]);
    close(child_fds[0]);

    // write from terminal to parent pipe FOR child to read
    printf("Parent: writing to pipe '%s'\n", argv[1]);
    write(parent_fds[1], argv[1], strlen(argv[1]));
    close(parent_fds[1]);

    // fork() child process
    int child = fork();

    // NEVER GETS PASSED HERE :(

    if (child < 0) {
        fprintf(stderr, "fork failed!");
        exit(1);
    } else if (child == 0) {
        printf("I reached the child :)");
        // close unwanted pipe ends by child
        close(child_fds[0]);
        close(parent_fds[1]);

        // read from parent pipe
        int n = read(parent_fds[0], fromParent, 100);
        fromParent[n] = 0;
        printf("Child: reading from parent pipe '%s'\n", fromParent);
        close(parent_fds[0]);

        // Concatinate to what was read in
        const char myText[14] = " (added this.)";
        strcat(fromParent, myText);

        write(child_fds[1], fromParent, strlen(fromParent));
        close(child_fds[1]);
        printf("Child: writing to pipe - '%s'\n", fromParent);
    } else {
        // read from child pipe
        int n = read(child_fds[0], fromChild, 100);
        fromChild[n] = 0;
        printf("Parent: reading from pipe - '%s'\n", fromChild);
    }

    return 0;
}

What is going wrong?

1
The order of your pipe operations are not correct. You need to fork first. Then do the respective parent and child close calls followed by the respective read and write calls.kaylum
When debugging, preferably write to stderr. When you write to stdout (in particular; also for writing to stderr), ensure that you end messages with a newline. Consider using fflush(stdout); to ensure the output is visible. Include PID numbers in output — use getpid() in the printing call to make sure you don't get stale values across fork() calls.Jonathan Leffler

1 Answers

3
votes

There were several problems, and your diagnostic messages were not guaranteed to appear. Make sure you end your messages with newlines.

  1. You only created one pipe because you used && instead of ||.
  2. You closed the pipes 'for the parent' before you'd created the child (also noted by kaylum in a comment).

There are multiple other cleanups in the code below. The code (still) does not ensure that the write-to-pipe operations succeed (they were failing before). It does ensure that the strings read from the pipes are not longer than the buffers in which they are placed; it doesn't ensure there's enough space to append the extra information in the child. The code shown waits for any child processes to complete before exiting. The child executes the wait() call but it immediately fails (and the child doesn't print anything) and it exits. The parent waits for the child to complete and reports on it doing so before exiting.

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

int main(int argc, char *argv[])
{
    if (argc == 1)
    {
        fprintf(stderr, "Usage: %s <string>\n", argv[0]);
        return EXIT_FAILURE;
    }

    // create two pipes:
    // - parent_fds used by parent to write to child
    // - child_fds used by child to write to parent
    int parent_fds[2], child_fds[2];

    // read:[0] - write:[1]
    if (pipe(parent_fds) != 0 || pipe(child_fds) != 0)  /* || not && */
    {
        fprintf(stderr, "pipes failed!\n");
        return EXIT_FAILURE;
    }

    // fork() child process
    int child = fork();

    if (child < 0)
    {
        fprintf(stderr, "fork failed!");
        return EXIT_FAILURE;
    }
    else if (child == 0)
    {
        printf("%d: I reached the child :)\n", (int)getpid());
        // close unwanted pipe ends by child
        close(child_fds[0]);
        close(parent_fds[1]);

        // read from parent pipe
        char fromParent[100];
        int n = read(parent_fds[0], fromParent, sizeof(fromParent) - 1);
        fromParent[n] = '\0';
        printf("%d: Child: read from parent pipe '%s'\n", (int)getpid(), fromParent);
        close(parent_fds[0]);

        // Append to what was read in
        strcat(fromParent, " (added this.)");

        write(child_fds[1], fromParent, strlen(fromParent));
        close(child_fds[1]);
        printf("%d: Child: writing to pipe - '%s'\n", (int)getpid(), fromParent);
    }
    else
    {
        // close unwanted pipe ends by parent
        close(parent_fds[0]);
        close(child_fds[1]);

        // write from terminal to parent pipe FOR child to read
        printf("%d: Parent: writing to pipe '%s'\n", (int)getpid(), argv[1]);
        write(parent_fds[1], argv[1], strlen(argv[1]));
        close(parent_fds[1]);
        // read from child pipe
        char fromChild[100];
        int n = read(child_fds[0], fromChild, sizeof(fromChild) - 1);
        fromChild[n] = '\0';
        close(child_fds[0]);
        printf("%d: Parent: read from pipe - '%s'\n", (int)getpid(), fromChild);
    }

    int corpse;
    int status;
    while ((corpse = wait(&status)) > 0)
        printf("%d: child PID %d exited with status 0x%.4X\n", (int)getpid(), corpse, status);

    return EXIT_SUCCESS;
}

Sample output (source pipe43.c, program pipe43):

$ pipe43 'Nobody expects the Spanish Inquisition!'
84543: Parent: writing to pipe 'Nobody expects the Spanish Inquisition!'
84544: I reached the child :)
84544: Child: read from parent pipe 'Nobody expects the Spanish Inquisition!'
84544: Child: writing to pipe - 'Nobody expects the Spanish Inquisition! (added this.)'
84543: Parent: read from pipe - 'Nobody expects the Spanish Inquisition! (added this.)'
84543: child PID 84544 exited with status 0x0000
$