//=========================================================================
// Summary  : */
// Filename : SharedData.cc
// Author   : */
// Project  : */
// Revision : 1
// Created  : 2000/08/19
// Modified : 2000/08/19
//=========================================================================
// Description :
//=========================================================================
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <malloc.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "SharedData.h"
#include "Syslog.h"

#define SH_MEM_OPEN_TRIES	3

SharedData::SharedData(const char *name,
		       int nDataBytes,
		       SharedData::Access access,
		       Boolean init)
{
  // Appears to be some problems with access to QNX shared
  // memory. So we always set it to ReadWrite for now.
  _access = access =  SharedData::ReadWrite;

  _name = _protectSemName = 0;
  Boolean debug = False;

  _name = strdup(name);

  _init = init;

  if (_init) {
      // Remove old shared memory segment if it exists
      shm_unlink(_name);
  }
  
  //  _access = NoAccess;
  _mapAddr = 0;
  _protectSem = 0;
  _mapSize = nDataBytes;
  
  // Initialize shared semaphore
  initShm();
  
  dprintf("_mapAddr = x%0x", _mapAddr);
}


SharedData::~SharedData()
{
  Boolean debug = False;
  dprintf("**** SharedData destructor for object %s ****", name());
  cleanup();
}


void SharedData::cleanup()
{
    //only for init
    if(_init) {
	sem_destroy(_protectSem);
	shm_unlink(_name);
    }
    munmap(_protectSem, sizeof(sem_t));
    munmap(_mapAddr, _mapSize);
    free((void *)_name);
}


void SharedData::initShm()
{
   
    void* temp;
    void* hold;
    char* it;
    //all this nastiness is because you can add to void pointers
    temp = map(_name, _mapSize, _access, _init);
    hold = temp;
    it = (char *)hold;
    _protectSem = (sem_t *)temp;
    it += sizeof(sem_t);
    _mapAddr = it;
    
    //only call seminit on initializer side
    if(_init) {
	if (sem_init(_protectSem, 1, 1) == -1) {
	    perror("SharedData::initSemaphore() - sem_init() of _protectSem");
	    throw Exception("SharedData::initshm() - sem_init() error");
	    //exit(1);
	}
    }
}

void SharedData::printAccess()
{
    Syslog::write("SharedData::printAccess() - access mode is: %d",_access);
}

void* SharedData::map(const char *mapName, int mapSize,
		     Access access, Boolean init)
{
  Boolean debug = False;

  void* buf;
  
  //fprintf(stdout, "Init: %d %s\n", init, mapName);

  int shmFlags;
  int mmapProtect;

  switch (access) {
  case Read:
      if(init)
	  shmFlags = O_CREAT | O_RDONLY;
      else
	  shmFlags = O_RDONLY;
      mmapProtect = PROT_READ;
      break;
  case Write:
      if(init)
	  shmFlags = O_CREAT | O_RDWR;
      else
	  shmFlags = O_RDWR;
      mmapProtect = PROT_READ | PROT_WRITE;
      break;
  case ReadWrite:
      if(init)
	  shmFlags = O_CREAT | O_RDWR;
      else
	  shmFlags = O_RDWR;
      mmapProtect = PROT_READ | PROT_WRITE;
      break;
  case NoAccess:
      mmapProtect = PROT_NONE;
      break;
  }

  // Get shared-memory file descriptor

  // TEST TEST TEST
  umask(0);

  int fd=-1, open_count = 0;
  dprintf("shm_open(): %s, flags=%d", mapName, shmFlags);

  while ( ( fd == -1 ) && ( open_count < SH_MEM_OPEN_TRIES ) )
  {
    if ((fd = shm_open(mapName, shmFlags, 0600)) == -1) {
		sleep(1);
    }
    open_count++;
  }
  
  if ( fd == -1 ) {
      Syslog::write("Problem with shm_open() name: %s init: %d",
		    mapName, init);
      perror("SharedData::map() - shm_open()");
      cleanup();
      throw Exception("SharedData::map() - shm_open() error");
      //exit(1);
  }


  // Set size of shared memory segment
  if (ltrunc(fd, mapSize, SEEK_SET) == -1) {

    /* If errno is EBUSY, it means someone else has already set the
       size, and that's fine. Otherwise, there's a serious problem. */
    if (errno != EBUSY) {
      perror("SharedData::map() - ltrunc()");
      cleanup();
      exit(1);
    }
  }

  dprintf("mmap(): mapSize=%d, mmapProtect=%d, fd=%d\n",
	  mapSize, mmapProtect, fd);

  // Map the shared memory
  buf = mmap(0, mapSize, mmapProtect, MAP_SHARED, fd, 0);

  if (buf == (void *)-1) {
    Syslog::write("SharedData::map() - errno=%d for map %s",
		  errno, mapName);

    perror("SharedData::map() - mmap()");
    cleanup();
    exit(1);
  }
  else {
    dprintf("mmap() successful for %s", mapName);
  }

  // Shared memory object remains after closing file descriptor
  close(fd);
  return buf;
}



void SharedData::read(void *data)
{
  Boolean debug = False;

  if (_access != Read && _access != ReadWrite) {
    // No permission to read; throw an exception here
    throw AccessError("SharedData::read()");
  }

  dprintf("SharedData::read() - before semwait");

  // Get semaphore (wait for other readers/writers)
  sem_wait(_protectSem);

  dprintf("SharedData::read() - memcpy()");
  // Read data from shared memory
  memcpy(data, _mapAddr, _mapSize);
  dprintf("SharedData::read() - done with memcpy()");

  // Release semaphore
  sem_post(_protectSem);

  dprintf("SharedData::read() - done with sempost()");

}


void SharedData::write(void *data)
{

    Boolean debug = False;

  if (_access != Write && _access != ReadWrite) {
    // No permission to write; throw an exception here
    throw AccessError("SharedData::write()");
  }

  dprintf("SharedData::write() - before semwait");

  // Get semaphore (wait for other readers/writers)
  sem_wait(_protectSem);

  dprintf("SharedData::write() - after semwait");

  // Copy data to shared memory
  memcpy(_mapAddr, data, _mapSize);

  dprintf("SharedData::write() - after memcpy");

  // Release semaphore
  sem_post(_protectSem);

  dprintf("SharedData::write() - after sempost");
}



int SharedData::mmapProtectFlags(Access access)
{
  int mmapProtect = 0;

  switch (access) {
  case Read:
    mmapProtect = PROT_READ;
    break;

  case Write:
    mmapProtect = PROT_READ | PROT_WRITE;
    break;

  case ReadWrite:
    mmapProtect = PROT_READ | PROT_WRITE;
    break;

  default:
    mmapProtect = PROT_NONE;
  }

  return mmapProtect;
}












