'Creating two child processes to write to separate files based on input from parent

I need to write a program which creates two child processes. The parent of these processes will take in numbers from the user, and the first child will write the odd numbers input to a file called odd.txt while the second child will write the even numbers to a file called even.txt. (I have to make use of system calls to work with the files and not fopen etc)

The program stops when the user inputs -1.

I'm having many issues with my code below. While it allows me to input numbers and stops when -1 is entered, the output to the files is gibberish. In the sample output shown here, the child process only prints the first number entered (if it was odd, only child 1 prints and vice-versa).

sample output 1

I also have the problem that is 0 is entered, the whole program breaks, with segmentation faults and broken pipes if I try to input numbers after the 0. I'm guessing this somehow must have to do with hasData, but not sure how.

entering 0 as a value breaks things

I'm really at a loss here, not sure what it is that I am doing wrong. Any help much appreciated thanks! This was a question for an exam which I did not manage to get to work.

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

int main(int argc, char const *argv[])
{
    int p1 = fork();

    // Create a pipe
    int fd[2];
    if (pipe(fd) == -1){
        printf("Could not create a pipe.");
        return 1;
    }

    if (p1 > 0){
        // Parent
        int p2 = fork();

        if (p2 > 0){
            // Parent
            close(fd[0]);

            int hasData;
            int num;

            do 
            {   printf("Enter a num: ");
                scanf("%d", &num);

                if (num == -1){
                    hasData = 0;
                    write(fd[1], &hasData, sizeof(int));
                    printf("Thanks for your numbers!");
                    close(fd[1]);    
                    exit(0);
                }

                hasData = 1;
                write(fd[1], &hasData, sizeof(int));
                write(fd[1], &num, sizeof(num));

            } while (num!= -1);      

        }
        else if (p2 == 0){
            // Second child
            sleep(1);
            close(fd[1]);         

            int hasData = 0;
            int num2;

            read(fd[0], &hasData, sizeof(int));
            read(fd[0], &num2, sizeof(int));

            int even_fd = open("even.txt", O_WRONLY | O_CREAT);

            while (hasData){
                if (num2 % 2 == 0){
                    printf("(from child 2) %d ", num2);
                    write(even_fd, &num2, sizeof(int));                
                }

                read(fd[0], &hasData, sizeof(int));
            } 

            close(even_fd);
            close(fd[0]);
        }

    } else if (p1 == 0){
        // First child
        sleep(1);
        close(fd[1]);

        int hasData = 0;
        int num1;

        read(fd[0], &hasData, sizeof(int));
        read(fd[0], &num1, sizeof(int));

        int odd_fd = open("odd.txt", O_WRONLY | O_CREAT);

        while (hasData){
            if (num1 % 2 != 0){
                printf("\n(from child 1) %d ", num1);
                write(odd_fd, &num1, sizeof(int));                
            }

            read(fd[0], &hasData, sizeof(int));
        } 

        close(odd_fd);
        close(fd[0]);
        
    }

    return 0;
}

Edited code following advice from comments (still doesn't work though, but 0 input works fine now, got rid of hasData)

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

int main(int argc, char const *argv[])
{
    // Create a pipe
    int pipe1[2];
    if (pipe(pipe1) == -1){
        printf("Could not create a pipe.");
        return 1;
    }

    int p1 = fork();

    if (p1 > 0){
        // Parent
        int pipe2[2];
        if (pipe(pipe2) == -1){
        printf("Could not create a pipe.");
        return 2;
        }

        int p2 = fork();

        if (p2 > 0){
            // Parent
            close(pipe1[0]);
            close(pipe2[0]);

            int num;

            do 
            {   printf("Enter a num: ");
                scanf("%d", &num);
                
                if (num == -1){
                    write(pipe1[1], &num, sizeof(int));
                    write(pipe2[1], &num, sizeof(int));
                    printf("\nThanks for your numbers!\n");
                    close(pipe1[1]);    
                    close(pipe2[1]); 
                    exit(0);
                }

                if(num%2 != 0){
                    // odd number
                    write(pipe1[1], &num, sizeof(num));
                } else {
                    // even number
                    write(pipe2[1], &num, sizeof(num));                   
                }
                
            } while (num!= -1);      

        }
        else if (p2 == 0){
            // Second child
            close(pipe2[1]);
            close(pipe1[0]); 
            close(pipe1[1]);          

            int num2;

            read(pipe2[0], &num2, sizeof(int));

            int even_fd = open("even.txt", O_WRONLY | O_CREAT);

            while (num2 != -1){
                printf("\n(from child 2) %d\n", num2);
                write(even_fd, &num2, sizeof(int));  
                read(pipe2[0], &num2, sizeof(int));
            } 

            close(even_fd);
            close(pipe2[0]);
        }

    } else if (p1 == 0){
        // First child
        close(pipe1[1]);

        int num1;

        read(pipe1[0], &num1, sizeof(int));

        int odd_fd = open("odd.txt", O_WRONLY | O_CREAT);

        while (num1 != -1){

            printf("\n(from child 1) %d\n", num1);
            write(odd_fd, &num1, sizeof(int));          
            read(pipe1[0], &num1, sizeof(int));
        } 

        close(odd_fd);
        close(pipe1[0]);
        
    }

    return 0;
}


Solution 1:[1]

Here's the code I derived from yours. It works for me. I too got rid of hasData. The two child processes are largely symmetric — it took me a depressingly long time to spot the one piece of asymmetry I introduced. One 'major' change is to call the output files odd.bin and even.bin since they contain binary data, not text (so the names odd.txt and even.txt are singularly inappropriate). It would be easy and sensible to revise them to text files using standard I/O (fopen(), fprintf(), fclose()) to write the numbers. Another significant change is that it only prompts for "Enter a num:" if the input is a terminal — this is cleaner when driven from a pipe or data file.

There's a lot of diagnostic output — partly as I was trying to track down that annoying asymmetry that I mentioned (it was a case of == vs !=, as it happens). The signal handling in the parent is there also to help with the tracking.

/* SO 7130-7840 */
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

int main(void)
{
    int odd[2];
    int even[2];

    if (pipe(odd) != 0 || pipe(even) != 0)
    {
        fprintf(stderr, "Failed to create pipes: %d %s\n", errno, strerror(errno));
        exit(EXIT_FAILURE);
    }
    pid_t p1;
    pid_t p2;


    printf("Parent: even[R] = %d, even[W] = %d, odd[R] = %d, odd[W] = %d\n",
           even[0], even[1], odd[0], odd[1]);

    if ((p1 = fork()) < 0)
    {
        fprintf(stderr, "Failed to fork 1: %d %s\n", errno, strerror(errno));
        exit(EXIT_FAILURE);
    }
    else if (p1 == 0)
    {
        /* Child 1 - odd numbers */
        printf("Child 1 at work: %5d\n", (int)getpid());
        close(even[0]);
        close(even[1]);
        close(odd[1]);
        printf("Child 1: odd[R] = %d\n", odd[0]);

        const char *filename = "odd.bin";
        int odd_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
        if (odd_fd < 0)
        {
            fprintf(stderr, "Child 1: failed to open file %s: %d %s\n",
                    filename, errno, strerror(errno));
            exit(EXIT_FAILURE);
        }

        int counter = 0;
        int num1;
        int rc;
        while ((rc = read(odd[0], &num1, sizeof(num1))) == sizeof(num1))
        {
            counter++;
            if (num1 % 2 != 0)
            {
                printf("Child 1: (%d) %d\n", counter, num1);
                write(odd_fd, &num1, sizeof(num1));
            }
            else
            {
                fprintf(stderr, "Child 1: got even number %d\n", num1);
                exit(EXIT_FAILURE);
            }
        }
        if (rc < 0)
            printf("Child 1: read error (rc = %d; %d: %s)\n", rc, errno, strerror(errno));
        else
            printf("Child 1: end of loop (rc = %d)\n", rc);

        close(odd_fd);
        close(odd[0]);
        exit(EXIT_SUCCESS);
    }
    else if ((p2 = fork()) < 0)
    {
        fprintf(stderr, "Failed to fork 2: %d %s\n", errno, strerror(errno));
        exit(EXIT_FAILURE);
    }
    else if (p2 == 0)
    {
        /* Child 2 - even numbers */
        printf("Child 2 at work: %5d\n", (int)getpid());
        close(odd[0]);
        close(odd[1]);
        close(even[1]);
        printf("Child 2: even[R] = %d\n", even[0]);

        const char *filename = "even.bin";
        int even_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
        if (even_fd < 0)
        {
            fprintf(stderr, "Child 2: failed to open file %s: %d %s\n",
                    filename, errno, strerror(errno));
            exit(EXIT_FAILURE);
        }

        int counter = 0;
        int num2;
        int rc;
        while ((rc = read(even[0], &num2, sizeof(num2))) == sizeof(num2))
        {
            counter++;
            if (num2 % 2 == 0)
            {
                printf("Child 2: (%d) %d\n", counter, num2);
                write(even_fd, &num2, sizeof(num2));
            }
            else
            {
                fprintf(stderr, "Child 2: got odd number %d\n", num2);
                exit(EXIT_FAILURE);
            }
        }
        if (rc < 0)
            printf("Child 2: read error (rc = %d; %d: %s)\n", rc, errno, strerror(errno));
        else
            printf("Child 2: end of loop (rc = %d)\n", rc);

        close(even_fd);
        close(even[0]);
        exit(EXIT_SUCCESS);
    }
    else
    {
        /* Parent - read and distribute */
        printf("Child 1: %5d\n", p1);
        printf("Child 2: %5d\n", p2);

        signal(SIGPIPE, SIG_IGN);   /* Writing to broken pipe gives write error */

        close(odd[0]);
        close(even[0]);

        int counter = 0;
        int num;
        while ((!isatty(STDIN_FILENO) || printf("Enter a num: ") > 0) &&
               scanf("%d", &num) == 1)
        {
            counter++;
            if (num == -1)
            {
                printf("Thanks for your %d numbers!\n", counter);
                break;
            }
            else if (num % 2 == 0)
            {
                printf("Parent - to child 2: (%d) %d\n", counter, num);
                if (write(even[1], &num, sizeof(num)) != sizeof(num))
                {
                    fprintf(stderr, "Failed to write to child 2: %d %s\n",
                            errno, strerror(errno));
                    exit(EXIT_FAILURE);
                }
            }
            else
            {
                printf("Parent - to child 1: (%d) %d\n", counter, num);
                if (write(odd[1], &num, sizeof(num)) != sizeof(num))
                {
                    fprintf(stderr, "Failed to write to child 1: %d %s\n",
                            errno, strerror(errno));
                    exit(EXIT_FAILURE);
                }
            }
            fflush(stdout);
        }
        if (num != -1)
            printf("Parent: exited loop on read failure\n");
        close(even[1]);
        close(odd[1]);

        /* Wait for child processes to exit */
        int corpse;
        int status;
        while ((corpse = wait(&status)) > 0)
            printf("PID %5d exited with status 0x%.4X\n", corpse, status);
    }

    return 0;
}

I used a random number generator to generate a series of numbers in the range -1 to 100; it generated -1 at entry 63 with the seed I chose:

67 42 2 41 63 72 17 64 8 21 72 9 98 5 10 30 29 69 18 71 82
93 98 42 81 85 51 67 44 6 17 96 32 38 29 29 88 61 48 68 70 9
19 41 13 45 9 57 37 93 75 67 35 93 45 35 60 51 2 41 56 52 -1

One of the runs I got (source code: pipe67.c; program: pipe67; data file: pipe67.in) was:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -fno-common pipe67.c -o pipe67
$ pipe67 < pipe67.in
Parent: even[R] = 5, even[W] = 6, odd[R] = 3, odd[W] = 4
Child 1: 52237
Child 2: 52238
Child 1 at work: 52237
Parent - to child 1: (1) 67
Parent - to child 2: (2) 42
Child 1: odd[R] = 3
Parent - to child 2: (3) 2
Parent - to child 1: (4) 41
Parent - to child 1: (5) 63
Parent - to child 2: (6) 72
Parent - to child 1: (7) 17
Parent - to child 2: (8) 64
Parent - to child 2: (9) 8
Parent - to child 1: (10) 21
Parent - to child 2: (11) 72
Parent - to child 1: (12) 9
Parent - to child 2: (13) 98
Parent - to child 1: (14) 5
Parent - to child 2: (15) 10
Parent - to child 2: (16) 30
Parent - to child 1: (17) 29
Parent - to child 1: (18) 69
Parent - to child 2: (19) 18
Parent - to child 1: (20) 71
Parent - to child 2: (21) 82
Parent - to child 1: (22) 93
Parent - to child 2: (23) 98
Parent - to child 2: (24) 42
Parent - to child 1: (25) 81
Parent - to child 1: (26) 85
Parent - to child 1: (27) 51
Parent - to child 1: (28) 67
Parent - to child 2: (29) 44
Parent - to child 2: (30) 6
Parent - to child 1: (31) 17
Parent - to child 2: (32) 96
Parent - to child 2: (33) 32
Parent - to child 2: (34) 38
Parent - to child 1: (35) 29
Parent - to child 1: (36) 29
Parent - to child 2: (37) 88
Parent - to child 1: (38) 61
Parent - to child 2: (39) 48
Parent - to child 2: (40) 68
Parent - to child 2: (41) 70
Parent - to child 1: (42) 9
Parent - to child 1: (43) 19
Parent - to child 1: (44) 41
Child 2 at work: 52238
Parent - to child 1: (45) 13
Parent - to child 1: (46) 45
Parent - to child 1: (47) 9
Parent - to child 1: (48) 57
Parent - to child 1: (49) 37
Parent - to child 1: (50) 93
Child 2: even[R] = 5
Parent - to child 1: (51) 75
Parent - to child 1: (52) 67
Parent - to child 1: (53) 35
Parent - to child 1: (54) 93
Parent - to child 1: (55) 45
Parent - to child 1: (56) 35
Parent - to child 2: (57) 60
Parent - to child 1: (58) 51
Parent - to child 2: (59) 2
Parent - to child 1: (60) 41
Parent - to child 2: (61) 56
Parent - to child 2: (62) 52
Thanks for your 63 numbers!
Child 1: (1) 67
Child 2: (1) 42
Child 2: (2) 2
Child 1: (2) 41
Child 2: (3) 72
Child 1: (3) 63
Child 2: (4) 64
Child 1: (4) 17
Child 2: (5) 8
Child 1: (5) 21
Child 2: (6) 72
Child 1: (6) 9
Child 2: (7) 98
Child 1: (7) 5
Child 2: (8) 10
Child 1: (8) 29
Child 2: (9) 30
Child 1: (9) 69
Child 2: (10) 18
Child 1: (10) 71
Child 2: (11) 82
Child 1: (11) 93
Child 2: (12) 98
Child 1: (12) 81
Child 2: (13) 42
Child 1: (13) 85
Child 2: (14) 44
Child 1: (14) 51
Child 2: (15) 6
Child 1: (15) 67
Child 2: (16) 96
Child 1: (16) 17
Child 2: (17) 32
Child 1: (17) 29
Child 2: (18) 38
Child 1: (18) 29
Child 2: (19) 88
Child 1: (19) 61
Child 2: (20) 48
Child 1: (20) 9
Child 2: (21) 68
Child 1: (21) 19
Child 2: (22) 70
Child 1: (22) 41
Child 2: (23) 60
Child 1: (23) 13
Child 2: (24) 2
Child 1: (24) 45
Child 2: (25) 56
Child 1: (25) 9
Child 2: (26) 52
Child 1: (26) 57
Child 2: end of loop (rc = 0)
Child 1: (27) 37
Child 1: (28) 93
Child 1: (29) 75
Child 1: (30) 67
Child 1: (31) 35
Child 1: (32) 93
Child 1: (33) 45
Child 1: (34) 35
Child 1: (35) 51
Child 1: (36) 41
Child 1: end of loop (rc = 0)
PID 52238 exited with status 0x0000
PID 52237 exited with status 0x0000
$

I also wrote a 'dumper' program (source dump97.c compiled to dump97 — compilation analogous to the pipe67 compilation) to validate the two output files:

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

int main(int argc, char **argv)
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s bin-file\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    FILE *fp = fopen(argv[1], "rb");
    if (fp == NULL)
    {
        fprintf(stderr, "%s: failed to open file '%s' for reading\n", argv[0], argv[1]);
        exit(EXIT_FAILURE);
    }

    int counter = 0;
    int num;
    while (fread(&num, sizeof(num), 1, fp) == 1)
        printf("%d: %d\n", ++counter, num);

    fclose(fp);
    return 0;
}

Running those gave:

$ dump97 odd.bin
1: 67
2: 41
3: 63
4: 17
5: 21
6: 9
7: 5
8: 29
9: 69
10: 71
11: 93
12: 81
13: 85
14: 51
15: 67
16: 17
17: 29
18: 29
19: 61
20: 9
21: 19
22: 41
23: 13
24: 45
25: 9
26: 57
27: 37
28: 93
29: 75
30: 67
31: 35
32: 93
33: 45
34: 35
35: 51
36: 41
$ dump97 even.bin
1: 42
2: 2
3: 72
4: 64
5: 8
6: 72
7: 98
8: 10
9: 30
10: 18
11: 82
12: 98
13: 42
14: 44
15: 6
16: 96
17: 32
18: 38
19: 88
20: 48
21: 68
22: 70
23: 60
24: 2
25: 56
26: 52
$

That all seems consistent.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1