#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/wait.h>

/*Musste ich einbauen da bei mir keine union semun vorhanden war*/
#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
/*union semun ist in <sys/sem.h> definiert*/
#else /*Wir müssen es selbst definieren (siehe man-Page semctl)*/
  union semun{
  int val;
  struct semid_ds *buf;
  unsigned short int *array;
  struct seminfo *__buf;
} ;
#endif

#define NUM_PROCS 3 /* Anzahl der Prozesse die wir ausführen */
#define SEM_ID 250       /* kennungs-id für Semaphore */
#define FILE_NAME "PID-Buchfuehrung" /* Name der Datei in wir schreiben */


void update_file(int sem_set_id, char* file_path, int number)
{
  struct sembuf sem_op;
  FILE* file;

/*Wir sperren jetzt den Berreich*/
  sem_op.sem_num = 0; /*Nummer der Semaphorvariable in der Menge*/
  sem_op.sem_op = -1;   /*Wir subtrahieren den Wert der Semaphorvariable*/
  sem_op.sem_flg = 0;
  semop(sem_set_id, &sem_op, 1); /*Wir führen die Operation aus*/

  printf("Kritischer Berreich : lock\n");

/*Dieser Berreich ist nun gesperrt für andere Prozesse*/
  file = fopen(file_path, "a+");
  if (file)
    {
      fprintf(file, "%d\n", number);
      printf("%d\n", number);
      fclose(file);
    }
  sleep(1); /*Wir trödeln ein bisschen*/

  sem_op.sem_num = 0; /*Nummer der Semaphorvariable in der Menge*/
  sem_op.sem_op = 1;    /*Wir addieren den Wert der Semaphorvariable wieder*/
  sem_op.sem_flg = 0;
  semop(sem_set_id, &sem_op, 1); /*Wir führen die Operation aus*/
/*Jetzt müsste die Funktion zum öffnen und schreiben in die Datei */
/*file wieder für einen anderen Prozess zur Verfügung stehen*/
  printf("Fertig : unlock\n");
}


/*Diese Funktion wird von jedem Prozess einmal aufgerufen. Diese ruft wiederum*/
/*die Funktion update_file je 3x pro Prozesse auf. Lässt dabei aber ein wenig*/
/*Zeit verstreichen um die Prozesse "durchzumischen" */
void do_child_loop(int sem_set_id, char* file_name)
{
  pid_t pid = getpid();
  int i, j;

  for (i=0; i<=3; i++)
    {
      update_file(sem_set_id, file_name, pid);
      sleep(1);
    }
}


int main()
{
  int sem_set_id; /* kennungs_id*/
  union semun sem_val;
  int child_pid;    /* PID von Kindprozess */
  int i, rc;

/*Wir erstellen ein Semaphore mit mit dem Schlüssel 250 und Zugriffrechte
für den Eigentümer*/
  sem_set_id = semget(SEM_ID, 1, IPC_CREAT | 0600);
  if (sem_set_id == -1)
   {
     perror("semget");
     exit(1);
   }

/* Wir initialisieren die Semaphorvariable mit dem Wert 1*/
  sem_val.val = 1;
  rc = semctl(sem_set_id, 0, SETVAL, sem_val);
  if (rc == -1)
    {
      perror("main: semctl");
      exit(1);
    }

/* Wir erzeugen NUM_PROCS Prozesse*/
  for (i=0; i<=NUM_PROCS; i++)
    {
      child_pid = fork();
      switch(child_pid)
         {
            case -1: perror("fork");
                          exit(1);
            case 0: do_child_loop(sem_set_id, FILE_NAME); /*Kindprozess*/
                        exit(0);
            default: break; /*Elternprozess*/
         }
     }/*Ende for*/

/* Elternprozess wartet bis alle Kindprozesse fertig sind*/
  for (i=0; i<NUM_PROCS; i++)
   {
     int child_status;
     wait(&child_status);
   }

  printf("Alle Prozesse sind beendet\n");
  fflush(stdout);
  return 0;
}