// By Zachary Lilley, Username: lill0017 - Intro to Operating Systems 9/22/24 --Modified 10/25/24 For Programming Assignment #2
#include <stdio.h> 
#include <stdlib.h>
#include <sys/ipc.h> 
#include <sys/shm.h>
#include <sys/wait.h>
#include <unistd.h> 
#include <semaphore.h> // Added Semaphore Library --10/25/24
#include <fcntl.h>  // O_CREAT & O_EXCL --10/25


// Declaring Semaphore Globally
sem_t *sem;

void inc(int *total, int target, int process_id) {
    for (int i = 0; i < target; i++) {
        sem_wait(sem); // Enter critical section
        (*total)++;
        sem_post(sem); // Exit critical section
    }
    printf("From Process %d: counter = %d.\n", process_id, *total);
    exit(0); // Exit after incrementing
}

int main() {
    int shmid;
    int *total;
    key_t key = IPC_PRIVATE;    // Only grants access to the 
                                // shared memory to parent proccess' children
    
    sem = sem_open("/zachs_sem", O_CREAT, 0666, 1); //Semaphore initialized with a value of 1
    if (sem == SEM_FAILED) {
        perror("semaphore failed");
        exit(1);
    }

    // Shared memory for total count
    shmid = shmget(key, sizeof(int), IPC_CREAT | 0666); //IPC_CREAT makes the memory segment if it doesn't exist already 0666 gives read, write and execution permissions
    if (shmid < 0) {  // Validate allocation
        perror("shmget failed");
        exit(1);
    }

    // Attach total to the shared memory
    total = (int *)shmat(shmid, NULL, 0); //cast to int 
    if (total == (int *) -1) {
        perror("shmat failed");
        exit(1);
    }

    *total = 0; // Initialize the shared variable once it's allocated

    // Fork 4 times
    for (int i = 1; i <= 4; i = i + 1) {

        int pid = fork();

        if (pid == -1) {        //Handle fork error and close out semaphore --10/25/24
        perror("fork failed");
        sem_close(sem);
        sem_unlink("/zachs_sem");
        exit(1);
        }

        // For each fork conduct an increment
        if (pid == 0) {
            // Set target values for each child process
            int target = 0;
            if (i == 1) target = 100000;
            if (i == 2) target = 200000;
            if (i == 3) target = 300000;
            if (i == 4) target = 500000;
            inc(total, target, getpid()); // Pass current PID to inc function
        }
    }

    // Parent processes wait for children
    for (int i = 1; i <= 4; i++) {
        int pid = wait(NULL);
        printf("Child ID: %d exited.\n", pid);
    }

    // Deallocate and release memory and the semaphore --10/25/24
    shmdt(total);
    shmctl(shmid, IPC_RMID, NULL);
    sem_close(sem);
    sem_unlink("/zachs_sem");

    printf("End of Simulation.\n");
    return 0;
}

