[SOLVED] Pipes are not closing properly inside one of the child processes

Table of Contents

Issue

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

int funcone(){
    int i;
    char *arr[5] = {"One", "Two", "Three", "Four", "Five"};
    for(i = 0; i <= 5; i++){
        printf("%s\t%d\n", arr[i], i);
    }
    printf("\n~");
    return 1;
}

#define NCHAR 1024
#define DELIMIT "\t\n"
int functwo(){
    //prints only numbers from input received from funcone
    int c;
    int position = 0, nchar = NCHAR, i;
    char *arr = malloc(sizeof *arr * nchar);

    if (!arr)
    {
        fprintf(stderr, "Memory Exhausted.");
    }

    //reads input from stdin stream, supposedly piped from func1
    while ((c = fgetc(stdin)) != 126)
    {
        arr[position++] = c;

        if (position >= nchar) // reallocate space if needed
        {
            nchar += NCHAR;
            arr = realloc(arr, nchar + NCHAR);

            if (!arr)
            {
                fprintf(stderr, "Memory Exhausted.\n");
                break;
            }
        }
    }
    arr[position] = 0;

    char *token;
    //splits string by \t\n, and converts string into numbers
    token = strtok(arr, DELIMIT);
    while (token != NULL)
    {
        char *endptr;
        int x = strtol(token, &endptr, 10);
        if (x > 0)
        {
            printf("%d\n", x);
        }
        token = strtok(NULL, DELIMIT);
    }
    printf("\n~");

    free(arr);  

    return 1;
}

int main(){
    printf("piping begins\n");
    int fd[2];
    int childstatus = 0;
    
    if (pipe(fd) == -1)
    {
        perror("Pipe1 error");
        return 1;
    }

    int pid1 = fork();
    if (pid1 < 0)
    {
        perror("Pid1 error");
        return 1;
    }

    if (pid1 == 0)
    {
        //child 1 process
        dup2(fd[1], STDOUT_FILENO);
        close(fd[1]);
        close(fd[0]);
        int f1 = funcone();
        // int f1 = (*builtin_functions[aone])(argone);
        /*In the main program, this line runs a function 
         *with a corresponding index number, and takes in the
         *command keywords as arguments. Returns 1 to continue Shell loop, 0 to exit.*/
    }

    int pid2 = fork();
    if (pid2 < 0)
    {
        perror("Pid2 error");
        return 1;
    }

    if (pid2 == 0)
    {
        // child 2 process
        dup2(fd[0], STDIN_FILENO);
        dup2(fd[1], STDOUT_FILENO);
        close(fd[0]);
        close(fd[1]);
        int f2 = functwo();
        // int f1 = (*builtin_functions[aone])(argone);
    }

    // parent process

    waitpid(pid1, &childstatus, WNOHANG);
    waitpid(pid2, &childstatus, WNOHANG);

    dup2(fd[0], STDIN_FILENO);
    close(fd[0]);
    close(fd[1]);

    int f3 = functwo();
    //supposed to be functhree, but something similar to functwo so i reused it.
    // int f3 = (*builtin_functions[aone])(argone);
    printf("Piping process complete.\n");
}

I’m working on a mini shell in C and I’ve been trying to figure out for a while now as to why some pipes are stuck inside the child process and do not execute. The only way I get both child processes to stop is is I use waitpid( , ,WNOHANG), which kind of shows that something is stuck in there. This causes the duplicated STDOUT and STDIN streams to not close properly, causing the program to constantly loop my shell prompt into the input stream and ultimately making my program crash due to segmentation fault.

Is there any way to overcome this problem or identify exactly where in the child processes are the pipes stuck?

EDIT 1

So I tried creating two different pipes in the main function namely int fd1[2], fd2[2];, making sure to close all ends as shown below. I’ve also made all funcone() and functwo() void to not return anything. However, my program now runs infinitely while printing ���� on the terminal until I use CTRL + C. Could there be any other possible reasons as to why this is happening? Or could there be any alternative methods to simulating an Interprocess Communication (IPC)?

int main(){
printf("piping begins\n");
int fd1[2], fd2[2];
int childstatus = 0;

if (pipe(fd1) == -1)
{
    perror("Pipe1 error");
    return 1;
}

if (pipe(fd2) == -1)
{
    perror("Pipe2 error");
    return 1;
}

int pid1 = fork();
if (pid1 < 0)
{
    perror("Pid1 error");
    return 1;
}

if (pid1 == 0)
{
    //child 1 process
    dup2(fd1[1], STDOUT_FILENO);
    close(fd1[1]);
    close(fd1[0]);
    close(fd2[0]);
    close(fd2[1]);
    funcone();
    // int f1 = (*builtin_functions[aone])(argone);
}

int pid2 = fork();
if (pid2 < 0)
{
    perror("Pid2 error");
    return 1;
}

if (pid2 == 0)
{
    // child 2 process
    dup2(fd1[0], STDIN_FILENO);
    dup2(fd2[1], STDOUT_FILENO);
    close(fd1[0]);
    close(fd1[1]);
    close(fd2[0]);
    close(fd2[1]);
    functwo();
}

// parent process

waitpid(pid1, &childstatus, WNOHANG);
waitpid(pid2, &childstatus, WNOHANG);

dup2(fd2[0], STDIN_FILENO);
close(fd1[0]);
close(fd1[1]);
close(fd2[0]);
close(fd2[1]);

functwo();
printf("Piping process complete.\n");
return 0;

}

Solution

Your first child process is writing to the pipe. Your parent process and second child process are both reading from that same pipe. Each byte written to the pipe will be consumed by one of those latter two processes, but you have no direct control over which one. This is not inherently wrong, but it is rarely what you want, and it will not work reliably for you in the example program.

What likely is happening is that the parent process consumes the data written by the first process, or at least the end-of-message character, which you intend for the second child instead. The second child then waits forever to read data that will never arrive. It does not even detect end-of-file on the pipe when the first child terminates, because the second child itself holds the write end of the pipe open. The parent, in turn, waits forever for the second child to terminate.

Supposing that the intention is for the second child to consume all the data written by the first child, and for the parent to in turn consume all the data written by the second child, you should create a separate pipe for the child2 –> parent link. That’s usually the pattern you want: a separate pipe for each distinct ordered pair of endpoints. That includes a separate pipe for each direction if you want bidirectional communication between the same two processes.

Update:

Additionally, funcone() overruns the bounds of its local arr array. The valid indices are 0 through 4, but the for loop in that function runs from 0 through 5.

Answered By – John Bollinger

Answer Checked By – Jay B. (BugsFixing Admin)

Leave a Reply

Your email address will not be published.