/***************************************************************************
 *   Copyright (C) 2005 by Brian Lauber   *
 *   bml8@case.edu   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <unistd.h>     // Forking and such
#include <sys/types.h>  // This key_t
#include <sys/sem.h>
#include <sys/shm.h>


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


#include "blError.h"

/*******************************************
Unix fails to define this anywhere, so we
need to define it within our program
*******************************************/
union semun
{
  int              val;
  struct semid_ds *buf;
  unsigned short  *array;
};


/*******************************************
By default, shared memory is just a block
of bytes.  In order to structure the data
in variables, we need to "overlay" the
shared memory with a data structure.
See shmat() statement.
*******************************************/
struct SharedVars
{
  int CarsWaiting;
  int TrainsCrossing;
  int Light;
};


/*******************************************
This enum allows us to refer to semaphores
by names instead of index numbers
*******************************************/
enum eSEMAPHORES {sCAR, sTRAIN};



/*******************************************
This program has _ purposes:
1. Create the shared resources
2. Initialize the shared resource values
3. Export the group id's
4. Create the child processes
5. Kill child processes after user presses enter
6. Clean shared resources
*******************************************/
int main(int argc, char *argv[])
{
  /********************************************
                    VARIABLES
  ********************************************/
  key_t S_Grp; // Semaphore group
  key_t M_Grp; // Memory group

  struct SharedVars *shared = NULL; // The shared memory will be overlayed w/ this structure

  // Initially, the crossing can contain 4 cars
  // or 1 train.  Therefore, we're guessing that
  // we need to set the following semaphore values
  unsigned short SemLimits[] = {4, 1};
  union    semun UnionThing; // Needed to pass SemLimits to semctl()

  /********************************************
                    PROGRAM
  ********************************************/
  S_Grp = Error(semget(IPC_PRIVATE, 2, IPC_CREAT | 0666));
  M_Grp = Error(shmget(IPC_PRIVATE, sizeof(struct SharedVars), IPC_CREAT | 0666));

  UnionThing.array = SemLimits;
  Error(semctl(S_Grp, 0, SETALL, UnionThing) );

  Error(shared = shmat(M_Grp, 0, 0));

  // Find the address of the shared memory
  printf("%X\n", shared);

  // Try to set a shared variable and print its contents
  shared->TrainsCrossing = 3;
  printf("%i Trains crossing\n", shared->TrainsCrossing);



  // Detach the memory from the program.
  Error(shmdt(shared));

  // Detaching the memory is not the same as setting the pointer
  // to NULL, as can be seen here.  The pointer still points to
  // the same block of memory, but that block is no longer mapped
  // to the shared memory (this will make more sense once you cover
  // virtual memory)
  printf("%X\n", shared);

  /*

  // This will cause the process to crash
  // because the shared memory segment no longer exists
  shared->TrainsCrossing = 7;
  printf("%i Trains crossing\n", shared->TrainsCrossing);

  */


  Error(semctl(S_Grp, 0, IPC_RMID));
  Error(shmctl(M_Grp, IPC_RMID, NULL));
  return EXIT_SUCCESS;
}
