 /*
  * Khoros: $Id: Shm.c,v 1.3 1992/03/20 23:38:09 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: Shm.c,v 1.3 1992/03/20 23:38:09 dkhoros Exp $";
#endif

 /*
  * $Log: Shm.c,v $
 * Revision 1.3  1992/03/20  23:38:09  dkhoros
 * VirtualPatch5
 *
  */ 


/*
 *----------------------------------------------------------------------
 *
 * Copyright 1990, University of New Mexico.  All rights reserved.
 * Permission to copy and modify this software and its documen-
 * tation only for internal use in your organization is hereby
 * granted, provided that this notice is retained thereon and
 * on all copies.  UNM makes no representations as too the sui-
 * tability and operability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 * 
 * UNM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
 * NESS.  IN NO EVENT SHALL UNM BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY OTHER DAMAGES WHAT-
 * SOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PER-
 * FORMANCE OF THIS SOFTWARE.
 * 
 * No other rights, including for example, the right to redis-
 * tribute this software and its documentation or the right to
 * prepare derivative works, are granted unless specifically
 * provided in a separate license agreement.
 *---------------------------------------------------------------------
 */

#include "unmcopyright.h"	 /* Copyright 1990 by UNM */


/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>								<<<<
   >>>>	    file name: Shm.c					<<<<
   >>>>								<<<<
   >>>>   description: Khoros Shared Memory drivers		<<<<
   >>>>								<<<<
   >>>>      routines: shm_tempnam				<<<<
   >>>>      	       shm_open					<<<<
   >>>>		       shm_close				<<<<
   >>>>		       shm_read					<<<<
   >>>>		       shm_write				<<<<
   >>>>		       shm_lseek				<<<<
   >>>>		       shm_tell					<<<<
   >>>>		       shm_unlink				<<<<
   >>>>		       shm_access				<<<<
   >>>>		       shm_lock					<<<<
   >>>>								<<<<
   >>>> modifications:						<<<<
   >>>>								<<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */


#ifdef KSHM

#include "internals.h"	
#include "Shm.h"	



/**************************************************************
*
* MODULE NAME: shm_tempnam
*
*     PURPOSE: This function initializes a tempnam for a "file"
*	       type transport.  Given a request with a filename of
*	       the following syntax:
*
*			"shm=XXXXXXXXXXX"
*			     ...etc....
*
*
*       INPUT: 
*
*        
*      OUTPUT:  depending whether we sucessfully created the template
*      		returns 0 or -1 if an error occurs
*
* CALLED FROM:  internal routine called from ktempnam()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int shm_tempnam(identifier, template, result)

char *identifier;
char *template;
char *result;
{
	caddr_t shmat();
	static  int shmkey = -1;
	int	shmid, numtrys = 0;



	/*
	 *  Initialize the Shared memory id to something unqiue and then
	 *  keep trying it as a unique key.  If we fail then increment the
	 *  key and try again.  We attempt 256 times before giving up...
	 */
	if (shmkey == -1)
	   shmkey = getpid();

	while ((shmid = shmget(shmkey, DefaultPageSize, 0600)) >= 0 &&
	       numtrys < 256)
	{
	   shmkey++; numtrys++;
	}

	/*
	 *  Opps...  We failed to find a unique key.  Error and return
	 *  failure.
	 */
	if (shmid >= 0)
	{
	   perror("shm_tempnam:  Unable to get unique shared memory key....");
	   return(-1);
	}

	/*
	 *  Store the unique key so that we can use it later.
	 */
	if (identifier == NULL)
	   (void) sprintf(result,"shm=%d", shmkey);
	else
	   (void) sprintf(result,"%s=%d", identifier, shmkey);

	/*
	 *  Initialize and create
	 */
	if (shm_initialize(shmkey, 0666 | IPC_CREAT, NULL) < -1)
	   return(-1);

	shmkey++;
	return(0);
}



/**************************************************************
*
* MODULE NAME: 	shm_open
*
*     PURPOSE: This function opens a "shared memory" connection.  It is the
*	       internal driver to open a file, which is called by
*	       the user when they call kopen().  kopen() calls when
*	       it gets an open request with a filename of the following
*	       syntax:
*
*			"shm=XXXXXXXXX"
*
*       INPUT: 
*
*        
*      OUTPUT:  returns 0 or -1 if an error occurs
*
* CALLED FROM:  internal routine called from kopen()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/

int shm_open(path, flags, mode, file)

char *path;
int  flags;
int  mode;
kfile *file;
{
	caddr_t	  addr, shmat();
	ResourceStruct *resources;
	char	  filename[LENGTH];
	int	  size, shmid, shmflag, key, prot = SHM_RND;


	/*
	 *  Get shared memory (shmat) protections from the open permission
	 *  flags...
	 */
	if (!(flags & O_WRONLY) && !(flags & O_RDWR))
	{
	   prot &= SHM_RDONLY;
	   mode = 0400;
	}
	else
	   mode = 0600;

	/*
	 *  Get the size of the current segment.  If the file is to be
	 *  created, etc...
	 */
	shmflag = mode;
	if (flags & O_EXCL) shmflag |= IPC_EXCL;
	if (flags & O_CREAT) shmflag |= IPC_CREAT;

	/*
	 *  Open the shared memory segment...
	 */
	if (sscanf(path, "%d", &key) == 0)
	{
	   if (sscanf(path, "%s", filename) == 0)
	   {
	      errno = ENOENT;
	      return(-1);
	   }
	}

	if ((shmid = shm_initialize(key, shmflag, &size)) < 0)
	   return(-1);

	/*
	 *  Attach to file the appropriate memory map space...
	 */
	if ((addr = shmat(shmid, 0, prot)) == (char *) -1)
	   return(-1);

	if (!(resources = (ResourceStruct *) calloc(1, sizeof(ResourceStruct))))
	{
	   (void) fprintf(stderr,"shm_open:  Not enough memory....\n\n");
	   (void) fprintf(stderr,"  Unable to malloc (%d) bytes for the khoros \
shared memory structure.\n", sizeof(ResourceStruct));
	   return(-1);
	}

	resources->addr    = addr;
	resources->shmid   = shmid;
	resources->shmflag = shmflag;
	resources->key     = key;

	resources->offset  = 0;
	resources->size    = size;

	file->resources = (caddr_t) resources;
	return(0);
}



/**************************************************************
*
* MODULE NAME: shm_close
*
*     PURPOSE: This function closes a "shared memory".  It is the internal
*	       driver to close a data shm segment, which is called by the user
*	       when they call kclose().  kclose() calls the "shared memory"
*	       internal drivers by the nature of the transport, which is
*	       dictated by the initial kopen().
*
*
*       INPUT:  file - the kfile structure.
*
*      OUTPUT:  returns whether we were able to close the file
*
* CALLED FROM:  internal routine called from kclose()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/

int shm_close(file)

kfile *file;
{
	char	  *addr;
	int	  status;
	ResourceStruct *resources = (ResourceStruct *) file->resources;


	/*
	 *  Simply call lseek() since "kfile" is just a regular file
	 */
	addr = resources->addr;

	status = shmdt(addr);
	return(status);
}



/**************************************************************
*
* MODULE NAME: shm_read
*
*     PURPOSE: This function reads a "shared memory".  It is the internal
*	       driver to read data from a shm, which is called by the user
*	       when they call kread().  kread() calls the "shared memory"
*	       internal drivers by the nature of the transport, which is
*	       dictated by the kopen().
*
*
*       INPUT:  file   - the kfile structure.
*		ptr    - the pointer to store the data into.
*		nbytes - the number of bytes to read.
*
*      OUTPUT:  returns the number of bytes read from the shm
*
* CALLED FROM:  internal routine called from kread()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/

int shm_read(file, ptr, nbytes)

kfile *file;
char  *ptr;
int   nbytes;
{
	caddr_t   addr;
	int	  size, offset;
	struct	  shmid_ds stats;
	ResourceStruct *resources = (ResourceStruct *) file->resources;


	/*
	 *  Simply call bcopy() since "kfile" is just a regular 
	 *  memory pointer.
	 */
	addr   = resources->addr;
	size   = resources->size;
	offset = resources->offset;

	/*
	 *  Make sure that we aren't trying to read more bytes than we
	 *  currently have.  If so then try re-opening the shared memory
	 *  segment to see if it's gotten bigger.
	 */
	if (offset+nbytes > size)
	{
           /*
            *  Get the size of the segment....
            */
           if (shmctl(resources->shmid, IPC_STAT, &stats) < 0)
              return(-1);

           resources->size = stats.shm_segsz;
	}

	if (offset+nbytes > size && offset != size)
	{
	   bcopy(addr+offset, ptr, size-offset);
	   nbytes = size-offset;
	}
	else if (offset != size)
	   bcopy(addr+offset, ptr, nbytes);
	else
	   return(EOF);

	resources->offset += nbytes;
	return(nbytes);
}



/**************************************************************
*
* MODULE NAME: shm_write
*
*     PURPOSE: This function writes to a "shared memory".  It is the internal
*	       driver to write data from the supplied data array to a shm,
*	       which is called by the user when they call kwrite().  kwrite()
*	       calls the "shared memory" internal drivers by the nature of
*	       the transport, which is dictated by the kopen().
*
*
*       INPUT:  file   - the kfile structure.
*		ptr    - the pointer to store the data into.
*		nbytes - the number of bytes to read.
*
*      OUTPUT:  returns the number of bytes written to the kfile
*
* CALLED FROM:  internal routine called from kread()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/

int shm_write(file, ptr, nbytes)

kfile *file;
char  *ptr;
int   nbytes;
{
	caddr_t   addr;
	int	  size, offset;
	ResourceStruct *resources = (ResourceStruct *) file->resources;


	/*
	 *  Simply call bcopy() since "kfile" is just a regular 
	 *  memory pointer.
	 */
	addr   = resources->addr;
	size   = resources->size;
	offset = resources->offset;

	/*
	 *  Make sure that we aren't trying to read more bytes than we
	 *  currently have.  If so then try re-opening the file to see
	 *  if it's gotten bigger.
	 */
	if (offset+nbytes > size)
	{
	   shm_reopen(file, offset+nbytes);
	   addr   = resources->addr;
	   size   = resources->size;
	   offset = resources->offset;
	}

	if (offset+nbytes > size && offset != size)
	{
	   bcopy(ptr, addr+offset, size-offset);
	   nbytes = size-offset;
	}
	else if (offset != size)
	   bcopy(ptr, addr+offset, nbytes);
	else
	   return(EOF);

	resources->offset += nbytes;
	return(nbytes);
}



/**************************************************************
*
* MODULE NAME: shm_lseek
*
*     PURPOSE: This function is used to do a "lseek".  It is the internal
*	       driver to rewind to a specific point so that the data can be
*	       skipped or re-read.  This is called when the user calls
*	       klseek().  klseek() calls the "shared memory" internal drivers
*	       by the nature of the transport, which is dictated by the kopen().
*
*
*       INPUT:  file   - the kfile structure.
*		offset - the offset in which to seek
*		whence - the control of how the offset will be applied
*
*      OUTPUT:  returns the number of bytes written to the kfile
*
* CALLED FROM:  internal routine called from kread()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/

int shm_lseek(file, offset, whence)

kfile *file;
int   offset;
int   whence;
{
	ResourceStruct *resources = (ResourceStruct *) file->resources;


	/*
	 *  Simply call lseek() since "kfile" is just a regular shm
	 */
	if (whence == SEEK_SET)
	   resources->offset = offset;
	else if (whence == SEEK_CUR)
	   resources->offset += offset;
	else if (whence == SEEK_END)
	   resources->offset = resources->size + offset;
	else
	{
	   errno = EINVAL;
	   return(-1);
	}

	if (resources->offset > resources->size)
	{
	   /* not sure as of yet */
	}
	return(resources->offset);
}



/**************************************************************
*
* MODULE NAME: shm_tell
*
*     PURPOSE: This function is used to do a "lseek".  It is the internal
*	       driver to rewind to a specific point so that the data can be
*	       skipped or re-read.  This is called when the user calls
*	       ktell().  ktell() calls the "shared memory" internal drivers
*	       by the nature of the transport, which is dictated by the kopen().
*
*
*       INPUT:  file   - the kfile structure.
*		offset - the offset in which to seek
*		whence - the control of how the offset will be applied
*
*      OUTPUT:  returns the number of bytes written to the kfile
*
* CALLED FROM:  internal routine called from kread()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/

int shm_tell(file)

kfile *file;
{
	int	  offset;
	ResourceStruct *resources = (ResourceStruct *) file->resources;

	/*
	 *  Simply call tell() since "kfile" is just a regular shm
	 */
	offset = resources->offset;
	return(offset);
}



/**************************************************************
*
* MODULE NAME: shm_access
*
*     PURPOSE: This function initializes a tempnam for a "shm"
*	       type transport.  Given a request with a filename of
*	       the following syntax:
*
*			"shm=XXXXXXXXXX"
*
*
*       INPUT: 
*
*        
*      OUTPUT:  returns 0 or -1 depending whether we
*		sucessfully created the template
*
* CALLED FROM:  internal routine called from kunlink()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int shm_access(identifier, filename, mode)

char *identifier;
char *filename;
int  mode;
{
	int	shmkey;


	/*
	 *  Retrieve the shared memory key from the filename.  If it's not
	 *  a valid key then
	 */
	if (sscanf(filename, "%d", &shmkey) != 1)
	{
	   errno = ENOTDIR;
	   return(-1);
	}

	/*
	 *  Check to see if the key (filename) exists
	 */
	if (shm_initialize(shmkey, 0400, NULL) < 0)
	   return(-1);
	else
	   return(0);
}



/**************************************************************
*
* MODULE NAME: shm_unlink
*
*     PURPOSE: This function initializes a tempnam for a "shm"
*	       type transport.  Given a request with a filename of
*	       the following syntax:
*
*			"shm=XXXXXXXXXX"
*
*
*       INPUT: 
*
*        
*      OUTPUT:  returns 0 or -1 depending whether we
*		sucessfully created the template
*
* CALLED FROM:  internal routine called from kunlink()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int shm_unlink(identifier, filename)

char *identifier;
char *filename;
{
	int	shmid;
	int     shmkey;



	/*
	 *  Retrieve the shared memory key from the filename.  If it's not
	 *  a valid key then
	 */
	if (sscanf(filename, "%d", &shmkey) != 1)
	{
	   errno = ENOTDIR;
	   return(-1);
	}

	/*
	 *  Retrieve the shared memeory id so that we can delete it
	 *  from shared memory.
	 */
	if ((shmid = shm_initialize(shmkey, 0400, NULL)) < 0)
	   return(-1);

	if (shmctl(shmid, IPC_RMID, NULL) < 0)
	   return(-1);

	return(0);
}



/**************************************************************
*
* MODULE NAME: shm_lock
*
*     PURPOSE: This function locks a "shm" transport.  Given
*	       a request with a filename of the following syntax:
*
*			"shm=key"
*
*       INPUT: 
*
*        
*      OUTPUT:  returns 0 or -1 depending whether we
*		sucessfully created the template
*
* CALLED FROM:  internal routine called from kflock()
*
*  WRITTEN BY:  Mark Young
*
**************************************************************/


int shm_lock(file, operation)

kfile *file;
int   operation;
{
	int	status;
	ResourceStruct *resources = (ResourceStruct *) file->resources;


	return(-1);
}




/*********************************************************************
*
*		Private Shared Memory Routines
*
*********************************************************************/

shm_reopen(file, num_bytes)

kfile *file;
int   num_bytes;
{
	int	status, size;
	char	*addr, *temp;
	ResourceStruct *resources = (ResourceStruct *) file->resources;

	addr = (char *) malloc(resources->size);
	if (addr != NULL)
	{
	   bcopy(resources->addr, addr, resources->size);
	   status = shmdt(resources->addr);
	   if (shmctl(resources->shmid, IPC_RMID, NULL) < 0)
	      return(-1);

	   size = ceil(((double) num_bytes)/DefaultPageSize)*DefaultPageSize;
	   if ((resources->shmid = shmget(resources->key, size,
			resources->shmflag)) < 0)
	   {
	      return(-1);
	   }

	   /*
	    *  Attach to file the appropriate memory map space...
	    */
           if ((temp = shmat(resources->shmid, 0, SHM_RND)) == (char *) -1)
              return(-1);

	   /*
	    *  copy data back into the new shared memory segment.
	    */
	   bcopy(addr, temp, resources->size);
	   resources->addr = temp;
	   resources->size = num_bytes;
	   free(addr);
	   return(0);
	}
	else
	   return(-1);
}

int shm_initialize(key, shmflg, size)

int key;
int shmflg;
int *size;
{
	int	shmid;
	caddr_t	shmat();
	struct  shmid_ds stats;


	/*
	 *  Get the shared memory id.  We pass in DefaultPageSize just in case
	 *  we are going to be creating this segment.
	 */
	if ((shmid = shmget(key, DefaultPageSize, shmflg)) < 0)
	   return(-1);

	/*
	 *  Get the size of the segment in case anyone wants to know
	 */
	if (size != NULL)
	{
	   if (shmctl(shmid, IPC_STAT, &stats) < 0)
	      return(-1);

	   *size = stats.shm_segsz;
	}
	return(shmid);
}

#endif  /* KSHM */
/* don`t add after the endif */
