/*******************************************
        DML_ShMem.cpp
        
        provides a slightly easier (but with some weird syntax and requirements)
        interface to the QNX shared memory subsystem. The coolness factor of SharedMemory
        objects is improved. :-)
        
        Syntax:
        -------------
        i.e. to create a shared memory object out of a struct of type "dataStruct"
        with a local variable name of "data_shm" and a SharedMemory name of "sharedDataStruct"
        
        ShMem<dataStruct> * data_shm = new("sharedDataStruct") ShMem<dataStruct>;
        
        references to members of the original dataStruct type are accomplished with the 
        pointer structure dereference operator ->, for instance,
        
        if dataStruct has a member called "foo" for instance, this would reference it:
        data_shm->foo.
        
        
        Requirements:
        --------------
        DO NOT attempt to use this shared memory creation scheme for single-value types,
        i.e. don't try to create a ShMem<int> instance, or a ShMem<float> instance. Use it
        only for struct shared memories and class shared memories. For single data types, use
        the included functions create_shm, open_shm, and destroy_shm (defined in this header)
        
        The ShMem class template is ONLY usable by creating a pointer to the template instance
        and then using the "new" operator to allocate the space. Simply creating a (non-pointer)
        ShMem<Type> variable is sure to result in some type of error.
        
        History:
        ----------------------
        author:  Ryan Findley
        
        when    who             what
        5/12/99 ryan    created (feeling sneaky with this class)
		3/28/00 ryan	lots of modifications since then... seems totally stable
						(doesn't work with virtual functions)        
*********************************************************************/

//--------------------------- private include files -------------------
#include "DML_shmem.h"
#include <errno.h>
#include <sys/stat.h>
#include <limits.h>
#include <string.h>

//--------------------------- private definitions ---------------------

//--------------------------- module code -----------------------------

/************************
*
*       create_shm
*
*       Create a shared memory object & map it.
*       
*************************/
void * create_shm(const char * shm_name, size_t sz, int oflag)
{
    int shm_fd;
	void * shm_map;
	
	shm_fd = open_shm_fd( shm_name, sz, oflag );
	shm_map = map_shm( shm_fd, sz, oflag );
	
	return( shm_map );
}



/************************
	opens a shared memory object, returning a file descriptor.
*************************/
int open_shm_fd( const char * shm_name, size_t sz, int oflag)
{
	int shm_fd;
	
    errno = 0;
	//attempt to CREATE a new shared memory
    shm_fd = shm_open(shm_name, O_CREAT | O_EXCL | oflag, 0777);
    if(errno == EEXIST)
	    //it already existed, so just try to open it instead
        shm_fd = shm_open(shm_name, oflag, 0777);

    else if (shm_fd != -1)// set shm object size
    {
        if(ltrunc(shm_fd, (off_t)sz, SEEK_SET) == -1)
        {
	    	printf("error setting size of shared memory object: (%s)\n",shm_name);  
	    	return( -1 );
		}
    }   
    
    if(shm_fd == -1)
        printf("Shared memory open fail: (%s) \n",shm_name);
	
	return(shm_fd);
}


/********************************************************
	given an open shared memory object, maps it into
	local memory space.
**********************************************************/
void * map_shm( int shm_fd, size_t sz, int oflag )
{
	void * shm_map;
	// map shm memory object
	int flag;
	switch( oflag )
	{
		case O_RDWR: flag = PROT_READ|PROT_WRITE; break;
		case O_RDONLY: flag = PROT_READ; break;
		default: flag = PROT_NONE;
	}
    shm_map = mmap(0, sz, flag, MAP_SHARED, shm_fd, 0);
    if(shm_map == (void *) -1)
        printf("mmap failed for shared memory object\n");
      
    return shm_map;
}


/*******************************************************
	checks the shared memory object to see if
	it should be unlinked. If so, unlinks it.
	(found a bug in the way shm_unlink works, so this
	fixes it. shm_unlink doesn't check to see if other
	processes have opened up the shm file.)
********************************************************/	
void close_shm( const char * shm_name )
{
	static struct stat statBuf;
	char pathName[PATH_MAX+NAME_MAX+1] = "/dev/shmem/";

	strcat(pathName,shm_name);
	
	stat(pathName,&statBuf);
	if(statBuf.st_nlink == 1)	//for some reason it always has 1 link open.
		shm_unlink(shm_name);
}


/*********************************
	destroys the shared memory in question. 
	Will sever connections between any processes using
	this particular shared memory object for communication.
	Usually, use this in a signal handler to clean up after
	oneself.
***********************************/
void destroy_shm(const char * shm_name)
{
    if( shm_unlink(shm_name) == -1 )
	{
		switch(errno)
		{
			case ENOENT:	//the shared memory object didn't exist: 
							//someone must have already deleted it.
							break;
			case EACCES:
							printf("Error unlinking shared memory object \"%s\":\n", shm_name);
							printf("Not enough access privilege!!!\n");
							break;
			case ENAMETOOLONG:
							printf("Error unlinking shared memory object \"%s\":\n", shm_name);
							printf("Name was too long!!!\n");
							break;
			case ENOSYS:
							printf("Error unlinking shared memory object \"%s\":\n", shm_name);
							printf("Not supported by this system!!!\n");
							break;
			default:
							break;
		}
	}
}







