/**
 * File: cleanup-orders-error-checking.c
 * --------------------------
 * The following program spawns off 8 children,
 * each of which returns a different exit status.
 * It then has 2 options to wait on the children
 * to finish.  This version includes more robust
 * error checking.
 */

#include <unistd.h>     // for fork
#include <stdio.h>      // for printf
#include <sys/wait.h>   // for waitpid
#include <errno.h>      // for errno, ECHILD
#include <stdbool.h>    // for 'true'
#include <stdlib.h>     // for exit

static const int kNumChildren = 8;
static const int kForkFailed = 1;
static const int kWaitFailed = 2;

void waitInForkOrder(pid_t children[], int count);
void waitInFinishOrder();


int main(int argc, char *argv[]) {
    // store all child PIDs
    pid_t children[kNumChildren];

    // Spawn kNumChildren children that just exit
    for (size_t i = 0; i < kNumChildren; i++) {
        children[i] = fork();  // omitted error checking
        
        // Optional error checking
        if (children[i] == -1) {
            fprintf(stderr, "Error: fork failed\n");
            return kForkFailed;
        }

        if (children[i] == 0) {
            // first sleeps longest, then 1sec less, etc.
            sleep(kNumChildren - i);
            // can just return since we are in main
            return 111 + i;
        }
    }

    // Uncomment exactly one of these to test cleaning up children in different orders
    waitInForkOrder(children, kNumChildren);
    //waitInFinishOrder();  // no need to pass in PIDs

    return 0;
}

// Wait for the children in the order they were spawned
void waitInForkOrder(pid_t children[], int count) {
    for (size_t i = 0; i < count; i++) {
        int status;
        pid_t pid = waitpid(children[i], &status, 0);

        // Optional error checking
        if (pid != children[i]) {
            fprintf(stderr, "Error: waitpid on child %d failed\n", children[i]);
            exit(kWaitFailed);
        }

        // Print out the child's exit information
        if (WIFEXITED(status)) {
            int exitStatus = WEXITSTATUS(status);
            if (exitStatus != 111 + i) {
                printf("Child with PID %d exited with unexpected status %d\n", children[i], exitStatus);
            } else {
                printf("Child with PID %d exited: status %d\n", children[i], exitStatus);
            }
        } else {
            printf("Child with PID %d exited abnormally.\n", children[i]);
        }
    }
}

// Wait for the children in the order they finish
void waitInFinishOrder() {
    while (true) {
        // Wait for one of our children
        int status;
        pid_t pid = waitpid(-1, &status, 0);

        /* If waitpid returns -1 and errno is ECHILD, that means no more children.
         * If waitpid returns -1 otherwise, that means an error occurred.
         */
        if (pid == -1 && errno == ECHILD) {
            break;
        } else if (pid == -1) {
            fprintf(stderr, "Error: waitpid failed\n");
            exit(kWaitFailed);
        }

        // Print out the child's exit information
        if (WIFEXITED(status)) {
            printf("Child with PID %d exited: status %d\n", pid, WEXITSTATUS(status));
        } else {
            printf("Child with PID %d exited abnormally.\n", pid);
        }
    }
}
