// Copyright, 2001-2003, Astra Network Inc.  All Rights Reserved


// This source code has been published by Astra Network Inc. However, any
// use, reproduction, modification, distribution or transfer of this
// software, or any software which includes or is based upon any of this
// code, is only permitted if expressly authorized by a written license
// agreement from Astra. Contact your Astra representative directly for
// more information.


#include <olmod.h>
#include <unistd.h>
#include <syslog.h>
#include <fcntl.h>
#ifndef NO_MUTEX
#include <pthread.h>
#endif
#include <sys/mman.h>
#include <errno.h>
#include <string.h>

#if defined( __QNX__) && !defined(__QNXNTO__)
#define ftruncate chsize
#endif

typedef struct sharedvar_data_s
{
  char *name;			// name of shared memory region
  int size;			// number of 64 bit integers that are shared
  int fd;			// fd to mmap shared memory
#ifndef NO_MUTEX
  pthread_mutex_t *mutex;
#endif
  int64_t *data;
}
sharedvar_data_t;


olmod_error_t
sharedvar_module_init ()
{
  return kMod_OK;
}

olmod_blackbox_t *
sharedvar_module_inst (pid_t process, olmod_arglist_t * static_args)
{
  sharedvar_data_t *blackbox;
  int size;
#ifndef NO_MUTEX
  pthread_mutexattr_t attr;
#endif

  if (!(blackbox = (sharedvar_data_t *) malloc (sizeof (sharedvar_data_t))))
    {
      syslog (LOG_CRIT, "mod_sharedvar: malloc failed");
      return NULL;
    }
  blackbox->name = (char *) static_args[0];
  blackbox->size = QTOL (*(int64_t *) static_args[1]);
  blackbox->fd = shm_open (blackbox->name, O_RDWR | O_CREAT, 0777);
  size = blackbox->size * sizeof (int64_t);

#ifndef NO_MUTEX
  size += sizeof (pthread_mutex_t);
#endif

  if (blackbox->fd == -1)
    {
      syslog (LOG_CRIT,
	      "mod_sharedvar: unable to create shared object '%s' (%s)",
	      blackbox->name, strerror (errno));
      free (blackbox);
      return NULL;
    }
  if (ftruncate (blackbox->fd, size) == -1)
    {
      syslog (LOG_CRIT, "mod_sharedvar: unable to truncate shared object %s",
	      blackbox->name);
      close (blackbox->fd);
      shm_unlink (blackbox->name);
      free (blackbox);
      return NULL;
    }

#ifdef NO_MUTEX
  blackbox->data = mmap (0, size,
			 PROT_READ | PROT_WRITE, MAP_SHARED, blackbox->fd, 0);

  if (blackbox->data == MAP_FAILED)
    {
      syslog (LOG_CRIT, "mod_sharedvar: unable to mmap shared object %s",
	      blackbox->name);
      close (blackbox->fd);
      shm_unlink (blackbox->name);
      free (blackbox);
      return NULL;
    }

#else
  blackbox->mutex = (pthread_mutex_t *) mmap (0, size,
					      PROT_READ | PROT_WRITE,
					      MAP_SHARED, blackbox->fd, 0);
  if (blackbox->mutex == MAP_FAILED)
    {
      syslog (LOG_CRIT, "mod_sharedvar: unable to mmap shared object %s",
	      blackbox->name);
      close (blackbox->fd);
      shm_unlink (blackbox->name);
      free (blackbox);
      return NULL;
    }
  blackbox->data = (int64_t *) ((char *) blackbox->mutex +
				sizeof (pthread_mutex_t));

  if (pthread_mutexattr_init (&attr) != 0)
    {
      close (blackbox->fd);
      shm_unlink (blackbox->name);
      free (blackbox);
      return NULL;
    }
  if (pthread_mutexattr_setpshared (&attr, PTHREAD_PROCESS_SHARED) != 0)
    {
      close (blackbox->fd);
      shm_unlink (blackbox->name);
      free (blackbox);
      return NULL;
    }

  if (pthread_mutex_init (blackbox->mutex, &attr) != 0)
    {
      if (errno != EBUSY)	// mutex is already initialized
	{
	  syslog (LOG_CRIT, "mod_sharedvar: unable to init mutex (%s)",
		  strerror (errno));
	  munmap (blackbox->mutex,
		  sizeof (pthread_mutex_t) +
		  blackbox->size * sizeof (int64_t));
	  close (blackbox->fd);
	  shm_unlink (blackbox->name);
	  free (blackbox);
	  return NULL;
	}
    }
#endif
  return blackbox;
}

olmod_error_t
sharedvar_module_set (olmod_blackbox_t * data, int64_t var)
{
  sharedvar_data_t *blackbox = (sharedvar_data_t *) data;
#ifndef NO_MUTEX
  pthread_mutex_lock (blackbox->mutex);

  blackbox->data[0] = var;
  //  syslog( LOG_INFO, "mod_sharedvar: set value = %d", var );
  pthread_mutex_unlock (blackbox->mutex);
#else
  blackbox->data[0] = var;
#endif
  return kMod_OK;
}

int64_t
sharedvar_module_get (olmod_blackbox_t * data, olmod_arglist_t * dynamic_args)
{
  sharedvar_data_t *blackbox = (sharedvar_data_t *) data;
  int64_t var;
  int offset = QTOL (*(int64_t *) dynamic_args[0]);

#ifndef NO_MUTEX
  pthread_mutex_lock (blackbox->mutex);

  var = blackbox->data[offset];

  pthread_mutex_unlock (blackbox->mutex);
#else
  var = blackbox->data[offset];
#endif

  return var;
}

void
sharedvar_module_deinst (olmod_blackbox_t * data)
{
  sharedvar_data_t *blackbox = (sharedvar_data_t *) data;
#ifndef NO_MUTEX
  pthread_mutex_destroy (blackbox->mutex);
  munmap (blackbox->mutex, sizeof (pthread_mutex_t) +
#else
  munmap (blackbox->data,
#endif
	  blackbox->size * sizeof (int64_t));
  close (blackbox->fd);
  shm_unlink (blackbox->name);
  free (blackbox);

}

void
sharedvar_module_deinit ()
{
}
