/*========================================================================
 * Copyright (C) GemTalk Systems 1986-2014.  All Rights Reserved..
 *
 * Name - shmem.c
 *
 * Description - Verify that shared memory is functional on a Unix host
 *
 * $Id: shmem.c 32752 2014-02-28 22:11:10Z stever $
 *
 *========================================================================
 */
#include "flag.ht"
#if !defined(FLG_MSWIN32)
#define HAS_SYSV_IPC
#endif

#ifdef HAS_SYSV_IPC
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#endif

#ifdef FLG_SOLARIS_UNIX
#include <sys/types.h>
#include <sys/mman.h>
#if !defined(MHA_MAPSIZE_VA) && !defined(MC_HAT_ADVISE)
  // defines to allow compile on Solaris 8
#define MC_HAT_ADVISE   7
#define MHA_MAPSIZE_VA          0x1
  struct memcntl_mha {
        uint_t          mha_cmd;        /* command(s) */
        uint_t          mha_flags;
        size_t          mha_pagesize;
  };
#endif
#endif

#include "global.ht"

#undef strerror 

#if defined(FLG_NO_SEMUN)
union semun {
  int             val;
  struct semid_ds *buf;
  ushort          *array;
  };
#endif

#if !defined(SHM_R)
#define SHM_R 0400   /* user read permission */
#endif
#if !defined(SHM_W)
#define SHM_W 0200   /* user write permission */
#endif

/* the group permissions are not defined at all in shm.h */
#define SHMG_R 0040  /* group read permission */
#define SHMG_W 0020  /* group write permission */

/* define permissions used for our system */
#define SHM_PERMISSIONS  (SHM_R | SHM_W | SHMG_R | SHMG_W)

static void showErrno(int num)
{
  printf("  errno = %d (%s)\n", num, strerror(num));
}

static int testMem(const char *path, int segSize, int numProcs)
{
  int theTok;
  int id;
  char *firstAddr;
  int numPages, totalSize, hashTableSize;

  /* Weird Unix junk to ID the memory */
  theTok = ftok(path, 1);
  if (theTok == -1) {
    fprintf(stderr, "ftok(\"%s\", 1) failure\n", path);
    showErrno(errno);
    return 1;
    }

  /* estimate the overhead */
  numPages = segSize / 16384;
  /* static component */
  totalSize = 313576;
  /* by numProcs */
  totalSize += 53488 * numProcs;
  /* by numPages */
  totalSize += 164 * numPages;
  totalSize += 16384 * (1 + numPages);
  /* by hashtable: a power of two greater than numPages */
  hashTableSize = 1; 
  while (hashTableSize < numPages) {
    hashTableSize = hashTableSize << 1;
    }
  totalSize += 128 * hashTableSize;
  totalSize += 128 * (hashTableSize + 4 );

  /* Create the memory */
  id = shmget(theTok, totalSize,
      SHM_PERMISSIONS | IPC_CREAT | IPC_EXCL);
  if (id == -1) {
    fprintf(stderr, "shmget() failure\n");
    showErrno(errno);
    return 1;
    }

  /* Now we must attach to it, even though we created it. */
  firstAddr = (char*) 0;
  char *loc = (char*)shmat(id, firstAddr, 0);
  if (loc == (char *)-1) {
    fprintf(stderr, "shmat() failure\n");
    showErrno(errno);
    /* _try_ to remove it */
    if (shmctl(id, IPC_RMID, NULL) < 0) {
      fprintf(stderr,
	  "painted into a corner!  shmctl(,IPC_RMID,) failed\n");
	  showErrno(errno);
      }
    return 1;
    }

  /* Undo everything now.  Try to detach. */
  if (shmdt(loc) < 0) {
    fprintf(stderr, "shmdt() failure\n");
    showErrno(errno);
    return 1;
    }

  /* Try to delete */
  if (shmctl(id, IPC_RMID, NULL) < 0) {
    fprintf(stderr, "shmctl(,IPC_RMID,) failed\n");
    showErrno(errno);
    return 1;
    }
  return 0;
}

/* the basic permissions for semaphores are not defined on all systems */
#if !defined(SEM_R)
#define SEM_R 0400  /* user read permission */
#endif
#if !defined(SEM_A)
#define SEM_A 0200  /* user alter permission */
#endif

/* the group permissions are not not defined at all in sem.h */
#define SEMG_R 0020 /* group read permission */
#define SEMG_A 0040 /* group alter permission */

/* define permissions used for our system */
#define SEM_PERMISSIONS  (SEM_R | SEM_A | SEMG_R | SEMG_A)


static int testSem(const char *path, int numSems)
{
  int theTok;
  int id;
  union semun semUnion;
  struct sembuf cmd;

  /* Weird Unix junk to ID the semaphore */
  theTok = ftok(path, 1);
  if (theTok == -1) {
    fprintf(stderr, "ftok(\"%s\", 1) failure\n", path);
    showErrno(errno);
    return 1;
    }

  /* Make the semaphore array */
  id = semget(theTok, numSems, SEM_PERMISSIONS | IPC_CREAT | IPC_EXCL);
  if (id == -1) {
    fprintf(stderr, "semget() failure path = %s, numSems = %d\n",
	path, numSems);
    showErrno(errno);
    return 1;
    }

  memset(&semUnion, 0, sizeof(semUnion));
  /* Fondle a semaphore, for kicks */
  cmd.sem_num = 1;
  cmd.sem_op = 1; /* clear */
  cmd.sem_flg = 0;
  if (semop(id, &cmd, 1) < 0) {
    fprintf(stderr, "semop() failer\n");
    showErrno(errno);
    if (semctl(id, 0, IPC_RMID, semUnion) < 0) {
      fprintf(stderr,
	  "painted in a corner!  semctl(, IPC_RMID) failed\n");
	  showErrno(errno);
      }
    return 1;
    }

  /* Destroy the array */
  memset((char *)&semUnion, 0, sizeof semUnion);
  if (semctl(id, 0, IPC_RMID, semUnion) < 0) {
    fprintf(stderr, "semctl(, IPC_RMID) failed\n");
    showErrno(errno);
    return 1;
    }

  /* success */
  return 0;
}

int main(int argc, char *argv[])
{
  int segSize;
  int semSize;
  int result = 0;
  
  if (argc != 4) {
    fprintf(stderr, "Usage: shmem <memfile> <kbytes> <numProc>\n");
    fprintf(stderr, "    memfile is any file (used for getting an id)\n");
    fprintf(stderr, "    kbytes is the shared memory size\n");
    fprintf(stderr, "    numProcs is the number of GemStone processes\n");
    fprintf(stderr, "    (See SHR_PAGE_CACHE_NUM_PROCS for details)\n");
    fprintf(stderr, "Tests validity of shared memory and semaphores.\n");
    fprintf(stderr, "To test default configuration: 'shmem shmem 75000 50'\n");
    return 1;
    }

  if (access(argv[1], R_OK | W_OK) != 0) {
    fprintf(stderr, "access() error on %s\n", argv[1]);
    showErrno(errno);
    return 1;
    }

  segSize = atoi(argv[2]) * 1024;
  if (segSize <= 0) {
    fprintf(stderr, "illegal memsize\n");
    return 1;
    }

  semSize = atoi(argv[3]) + 1;
  if (semSize <= 0) {
    fprintf(stderr, "illegal semsize\n");
    return 1;
    }

  if (testMem(argv[1], segSize, semSize) != 0) {
    fprintf(stderr, "Unable to use shared memory, size %d Kbytes\n",
	segSize >> 10);
    result = 1;
    }
  if (testSem(argv[1], semSize) != 0) {
    fprintf(stderr, "Unable to use semaphores, array size %d\n", semSize);
    result = 1;
    }

  return result;
}
