/*
 * This file has been modified for the cdrkit suite.
 *
 * The behaviour and appearence of the program code below can differ to a major
 * extent from the version distributed by the original author(s).
 *
 * For details, see Changelog file distributed with the cdrkit package. If you
 * received this file from another source then ask the distributing person for
 * a log of modifications.
 *
 */

/*
 * Copyright 1998-2002 Heiko Eissfeldt
 */

#define IPCTST
#undef IPCTST
/* -------------------------------------------------------------------- */
/*        semshm.c                                                      */
/* -------------------------------------------------------------------- */
/*               int seminstall(key,amount)                             */
/*               int semrequest(semid,semnum)                           */
/*               int semrelease(semid,semnum)                           */
/* -------------------------------------------------------------------- */

#include "config.h"

#if     !defined(HAVE_SMMAP) && !defined(HAVE_USGSHM) && !defined(HAVE_DOSALLOCSHAREDMEM) && !defined(HAVE_AREAS)
#undef  FIFO                    /* We cannot have a FIFO on this platform */
#endif

#if !defined(USE_MMAP) && !defined(USE_USGSHM)
#define USE_MMAP
#endif

#if	!defined HAVE_SMMAP && defined FIFO
#       undef   USE_MMAP
#       define  USE_USGSHM      /* SYSV shared memory is the default    */
#endif

#ifdef  USE_MMAP                /* Only want to have one implementation */
#       undef   USE_USGSHM      /* mmap() is preferred                  */
#endif

#ifdef	HAVE_DOSALLOCSHAREDMEM
#       undef   USE_MMAP
#       undef   USE_USGSHM
#	define	USE_OS2SHM
#	undef	USE_BEOS_AREAS
#endif

#ifdef	HAVE_AREAS
#       undef   USE_MMAP
#       undef   USE_USGSHM
#	undef	USE_OS2SHM
#	define	USE_BEOS_AREAS
#endif

#include <stdio.h>
#include <stdxlib.h>
#include <unixstd.h>
#include <fctldefs.h>
#include <errno.h>
#include <standard.h>
#include <schily.h>

#if defined(HAVE_SEMGET) && defined(USE_SEMAPHORES)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#endif

#if defined(HAVE_SHMAT) && (HAVE_SHMAT == 1)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#endif

#ifdef  USE_MMAP
#if defined(HAVE_SMMAP) && defined(USE_MMAP)
#include <mmapdefs.h>
#endif
#endif

#include <usal/scsitransp.h>

#ifdef	USE_BEOS_AREAS
#include	<be/kernel/OS.h>
#endif

#include "mytype.h"
#include "interface.h"
#include "ringbuff.h"
#include "global.h"
#include "exitcodes.h"
#include "semshm.h"

#ifdef DEBUG_SHM
char *start_of_shm;
char *end_of_shm;
#endif

int flush_buffers(void);


/*------ Semaphore interfacing (for special cases only) ----------*/
/*------ Synchronization with pipes is preferred        ----------*/

#if defined(HAVE_SEMGET) && defined(USE_SEMAPHORES)

int sem_id;
static int seminstall(key_t key, int amount);

static int seminstall(key_t key, int amount)
{
  int           ret_val;
  int           semflag;

  semflag = IPC_CREAT | 0600;
#ifdef IPCTST
  fprintf(stderr,"seminstall: key: %d, #sems %d, flags %4x\n",
          key,amount,semflag);
#endif
  ret_val = semget(key,amount,semflag);
  if ( ret_val == -1 )
  {
    fprintf(stderr,"semget: (Key %lx, #%d) failed: ",
            (long)key,amount);
    perror("");
  }
  return ret_val;
}

/*-----------------------------------------------------------------*/
int semrequest(int semid, int semnum)
{
  struct sembuf sops[1];
  int    ret_val;

#ifdef IPCTST
  fprintf(stderr,"pid %d, ReQuest id:num %d:%d\n",getpid(),semid,semnum);
#endif
  sops[0].sem_op  = -1;
  sops[0].sem_num = (short) semnum;
  sops[0].sem_flg = 0;

  do {
    errno = 0;
    ret_val = semop(semid,sops,1);
    if (ret_val == -1 && errno != EAGAIN && errno != EINTR)
      {
	fprintf(stderr,"Request Sema %d(%d) failed: ",semid,semnum);
	perror("");
      }
  } while (errno == EAGAIN || errno == EINTR);
  return(ret_val);
}

/*-----------------------------------------------------------------*/
int semrelease(int semid, int semnum, int amount)
{
  struct sembuf sops[1];
  int    ret_val;

#ifdef IPCTST
  fprintf(stderr,"%d RL %d:%d\n",getpid(),semid,semnum);
#endif
  sops[0].sem_op  = amount;
  sops[0].sem_num = (short) semnum;
  sops[0].sem_flg = 0;
  ret_val = semop(semid,sops,1);
  if ( ret_val == -1 && errno != EAGAIN)
  {
    fprintf(stderr,"Release Sema %d(%d) failed: ",semid,semnum);
    perror("");
  }
  return(ret_val);
}

int flush_buffers()
{
	return 0;
}
#else
/*------ Synchronization with pipes ----------*/
int pipefdp2c[2];
int pipefdc2p[2];

void init_pipes()
{
  if (pipe(pipefdp2c) < 0) {
    perror("cannot create pipe parent to child");
    exit(PIPE_ERROR);
  }
  if (pipe(pipefdc2p) < 0) {
    perror("cannot create pipe child to parent");
    exit(PIPE_ERROR);
  }
}

void init_parent()
{
  close(pipefdp2c[0]);
  close(pipefdc2p[1]);
}

void init_child()
{
  close(pipefdp2c[1]);
  close(pipefdc2p[0]);
}

int semrequest(int dummy, int semnum)
{

  if (semnum == FREE_SEM /* 0 */)  {
      int retval;
    if ((*total_segments_read) - (*total_segments_written) >= global.buffers) {
      /* parent/reader waits for freed buffers from the child/writer */
      *parent_waits = 1;
      retval = read(pipefdp2c[0], &dummy, 1) != 1;
      return retval;
    }
  } else {
      int retval;

    if ((*total_segments_read) == (*total_segments_written)) {
      /* child/writer waits for defined buffers from the parent/reader */
      *child_waits = 1;
      retval = read(pipefdc2p[0], &dummy, 1) != 1;
      return retval;
    }
  }
  return 0;
}

/* ARGSUSED */
int semrelease(int dummy, int semnum, int amount)
{
  if (semnum == FREE_SEM /* 0 */)  {
    if (*parent_waits == 1) {
      int retval;
      /* child/writer signals freed buffer to the parent/reader */
      *parent_waits = 0;
      retval = write(pipefdp2c[1], "12345678901234567890", amount) != amount;
      return retval;
    }
  } else {
    if (*child_waits == 1) {
      int retval;
      /* parent/reader signals defined buffers to the child/writer */
      *child_waits = 0;
      retval = write(pipefdc2p[1], "12345678901234567890", amount) != amount;
      return retval;
    }
  }
  return 0;
}

int flush_buffers()
{
	if ((*total_segments_read) > (*total_segments_written)) {
		return write(pipefdc2p[1], "1", 1) != 1;
	}
	return 0;
}

#endif

/*------------------- Shared memory interfacing -----------------------*/



#if defined(HAVE_SHMAT) && (HAVE_SHMAT == 1)
static int shm_request_nommap(int size, unsigned char **memptr);

/* request a shared memory block */
static int shm_request_nommap(int size, unsigned char **memptr)
{
  int   ret_val;
  int   shmflag;
  int   SHMEM_ID;
  int    cmd;
  struct shmid_ds buf;
  key_t key = IPC_PRIVATE;

  shmflag = IPC_CREAT | 0600;
  ret_val = shmget(key,size,shmflag);
  if ( ret_val == -1 )
  {
    perror("shmget");
    return -1;
  }

  SHMEM_ID = ret_val;
  cmd = IPC_STAT;
  ret_val = shmctl(SHMEM_ID,cmd,&buf);
#ifdef IPCTST
  fprintf(stderr, "%d: shmctl STAT= %d, SHM_ID: %d, key %ld cuid %d cgid %d mode %3o size %d\n",
          getpid(),ret_val,SHMEM_ID,
          (long) buf.shm_perm.key,buf.shm_perm.cuid,buf.shm_perm.cgid,
          buf.shm_perm.mode,buf.shm_segsz);
#endif

  *memptr = (unsigned char *) shmat(SHMEM_ID, NULL, 0);
  if (*memptr == (unsigned char *) -1) {
    *memptr = NULL;
    fprintf( stderr, "shmat failed for %d bytes\n", size);
    return -1;
  }

  if (shmctl(SHMEM_ID, IPC_RMID, 0) < 0) {
    fprintf( stderr, "shmctl failed to detach shared memory segment\n");
    return -1;
  }


#ifdef DEBUG_SHM
  start_of_shm = *memptr;
  end_of_shm = (char *)(*memptr) + size;

  fprintf(stderr, "Shared memory from %p to %p (%d bytes)\n", start_of_shm, end_of_shm, size);
#endif
  return 0;
}


#endif	/* #if defined(HAVE_SHMAT) && (HAVE_SHMAT == 1) */


static int shm_request(int size, unsigned char **memptr);

#ifdef  USE_USGSHM
/* request a shared memory block */
static int shm_request(int size, unsigned char **memptr)
{
	return shm_request_nommap(size, memptr);
}
#endif

/* release semaphores */
void free_sem(void);
void free_sem()
{
#if defined(HAVE_SEMGET) && defined(USE_SEMAPHORES)
  int   mycmd;
  union my_semun unused_arg;

  mycmd = IPC_RMID;

  /* HP-UX warns here, but 'unused_arg' is not used for this operation */
  /* This warning is difficult to avoid, since the structure of the union
   * generally is not known (os dependent). So we cannot initialize it
   * properly.
   */
  semctl(sem_id,0,mycmd,unused_arg);
#endif

}

#ifdef  USE_MMAP
#if defined(HAVE_SMMAP)

int shm_id;
/* request a shared memory block */
static int shm_request(int size, unsigned char **memptr)
{
	int     f;
	char    *addr;

#ifdef  MAP_ANONYMOUS   /* HP/UX */
	f = -1;
	addr = mmap(0, mmap_sizeparm(size), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, f, 0);
#else
	if ((f = open("/dev/zero", O_RDWR)) < 0)
		comerr("Cannot open '/dev/zero'.\n");
	addr = mmap(0, mmap_sizeparm(size), PROT_READ|PROT_WRITE, MAP_SHARED, f, 0);
#endif

	if (addr == (char *)-1) {
#if	defined HAVE_SHMAT && (HAVE_SHMAT == 1)
		unsigned char *address;
		/* fallback to alternate method */
		if (0 != shm_request_nommap(size, &address) || (addr = (char *)address) == NULL)
#endif
			comerr("Cannot get mmap for %d Bytes on /dev/zero.\n", size);
	}
	close(f);

	if (memptr != NULL)
		*memptr = (unsigned char *)addr;

	return 0;
}
#endif	/* HAVE_SMMAP */
#endif	/* USE_MMAP */

#ifdef	USE_OS2SHM

/* request a shared memory block */
static int shm_request(int size, unsigned char **memptr)
{
	char    *addr;

	/*
	 * The OS/2 implementation of shm (using shm.dll) limits the size of one
         * memory segment to 0x3fa000 (aprox. 4MBytes). Using OS/2 native API we
         * no such restriction so I decided to use it allowing fifos of arbitrary size
         */
	if(DosAllocSharedMem(&addr,NULL,size,0X100L | 0x1L | 0x2L | 0x10L))
      		comerr("DosAllocSharedMem() failed\n");

	if (memptr != NULL)
		*memptr = (unsigned char *)addr;

	return 0;
}
#endif

#ifdef	USE_BEOS_AREAS

/* request a shared memory block */
static int shm_request(int size, unsigned char **memptr)
{
	char    *addr;
	area_id aid;	/* positive id of the mapping */

	/* round up to a multiple of pagesize. */
	size = ((size - 1) | (B_PAGE_SIZE - 1)) + 1;
	/*
	 * 	request a shared memory area in user space.
         */
	aid = create_area(AREA_NAME,	/* name of the mapping */
		(void *)&addr,		/* address of shared memory */
		B_ANY_ADDRESS,		/* type of address constraint */
		size,			/* size in bytes (multiple of pagesize) */
		B_NO_LOCK, /* B_FULL_LOCK, */ /* memory locking */
		B_READ_AREA | B_WRITE_AREA);	/* read and write permissions */

	if (aid < B_OK)
      		comerrno(aid, "create_area() failed\n");

	if (memptr != NULL)
		*memptr = (unsigned char *)addr;

	return 0;
}
#endif

void *request_shm_sem(unsigned amount_of_sh_mem, unsigned char **pointer)
{
#if defined(HAVE_SEMGET) && defined(USE_SEMAPHORES)
    /* install semaphores for double buffer usage */
    sem_id = seminstall(IPC_PRIVATE,2);
    if ( sem_id == -1 ) {
      perror("seminstall");
      exit(SEMAPHORE_ERROR);
    }

#endif

#if defined(FIFO)
    if (-1 == shm_request(amount_of_sh_mem, pointer)) {
	perror("shm_request");
	exit(SHMMEM_ERROR);
    }

    return *pointer;
#else
    return NULL;
#endif
}
