/* two processes choose consecutive IDs using shared memory and semaphores
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
#include <unistd.h>
#include <sys/sem.h>
#include <strings.h>

#define PERMS 0600

typedef struct {
     int id;
} sharedBuf;

int shmid=0;
int semid=0;
sharedBuf *rwBuf = NULL;
static struct sembuf semDown[1] = {0, -1, 0};
static struct sembuf semUp[1] = {0, 1, 0};

void releaseIPC(void) {
    printf("Cleaning up\n");

    if (NULL != rwBuf) {
      if (shmdt((char *) rwBuf) < 0) perror("error detaching shared memory");
      if (shmctl(shmid, IPC_RMID, NULL) < 0)
          perror("error deleting shared memory");
    }
    if (semid)
      if (0 != semctl(semid, 0, IPC_RMID, 0))
         perror("error deleting semaphore");
}

void sigHand(int sig) {
    exit(1);
}

void enterCS() {
    semop(semid, semDown, 1);
}

void exitCS() {
    semop(semid, semUp, 1);
}

int main(void) {
    int i, cpid;

/* take care of possible exits */
    atexit(releaseIPC);
    signal(SIGBUS, sigHand);
    signal(SIGSEGV, sigHand);
    signal(SIGQUIT, sigHand);
    signal(SIGINT, sigHand);

/* create shared memory */
    if ((shmid = shmget(IPC_PRIVATE, sizeof(sharedBuf), PERMS|IPC_CREAT|IPC_EXCL)) < 0) {
        perror("error getting shared memory");
        exit(1);
    }
/* attach shared memory */
    if ((rwBuf=(sharedBuf *) shmat(shmid, NULL, 0))  == NULL) {
        perror("cannot attach memory");
        exit(1);
    }
/* initialize shared memory */
    rwBuf->id = 0;

/* create semaphore */
    if ((semid = semget(IPC_PRIVATE, 1, IPC_CREAT|IPC_EXCL|PERMS)) < 0) {
        perror("error getting semaphore");
        exit(1);
    }
/* initialize semaphore */
    semop(semid, semUp, 1);

    if (0 == (cpid=fork())) { /* child */
      while(1) {
         enterCS();
         printf("child in CS; id = %d\n", rwBuf->id);
         rwBuf->id++;
         usleep(500000);   /* sleep inside the critical section */
         exitCS();
         usleep(100000);   /* sleep outside the critical section */
      }
      exit(1);
    }

/* parent */

    printf("child pid = %d\n", cpid);
    for (i=0; i < 10; i++) {
       enterCS();
       printf("parent in CS; id = %d\n", rwBuf->id);
       rwBuf->id++;
       usleep(750000);   /* sleep inside the critical section */
       exitCS();
       usleep(750000);   /* sleep outside the critical section */
    }

    sleep(1); /* give some chance to the child to finish */
    kill(cpid, SIGTERM);
    printf("Done\n");
}
