/// TKS_SharedBuffer_linux.cpp
///
/// (c) 2005-2008 Bastian Spiegel <bs@tkscript.de> 
///     - distributed under terms of the GNU general public license (GPL).
///
///

#include <string.h>
#include "tks.h"

#if defined(YAC_LINUX) || defined(YAC_MACOSX) || defined(YAC_QNX)

#include "PTN_Node.h"
#include "YAC_Buffer.h"
#include "TKS_SharedBuffer.h"

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>


void TKS_SharedBuffer::freeSharedMemory(void) {
   ::munmap( (void*) (buffer - TKS_SHAREDBUFFERHEADER_SIZE), (size_t) (*(sSI*) &buffer[TKS_SBH_SIZE]) );
   if(shmOwner)
   {
     YAC_String realFileName;
     realFileName.copy("/tmp/tks_mmap_");
     realFileName.append(&shmKey);
     ::unlink( (char*)realFileName.chars );
   }
   ::close( shmHandle.id );
}

sSI TKS_SharedBuffer::allocSharedMemoryByKey(YAC_String *_shmKey, sSI _size) {
   TKS_SharedBuffer::free();
   
   if(YAC_Is_String(_shmKey))
   {
      shmKey.yacCopy(_shmKey);
      YAC_String realFileName;
      realFileName.copy("/tmp/tks_mmap_");
      realFileName.append(&shmKey);

      int fdMapFile = ::open( (char*)realFileName.chars, O_RDWR | O_CREAT, S_IRWXU );

      if( fdMapFile == -1 )
      { 
         Dprintf("[---] SharedBuffer::allocSharedMemory(size=%i): Could not open mmap file. errno=%i (%m).\n", 
            _size, errno);
         shmKey.free();
         return 0;
      }

      // Immediately unlink the file xxx unfortunately this trick only works when using fork() to create the child process 
      ////::unlink( (char*) shmKey.chars );

      // Truncate the file to the desired shared memory size.
      ::ftruncate(fdMapFile, (_size + TKS_SHAREDBUFFERHEADER_SIZE) );

      // Now memory map the file 
      buffer = (sU8*) ::mmap(NULL, (_size + TKS_SHAREDBUFFERHEADER_SIZE),
                             PROT_READ|PROT_WRITE, MAP_SHARED, 
                             fdMapFile, 0);

      if(buffer == (sU8*)-1)
      { 
         Dprintf("[---] SharedBuffer::allocSharedMemory(size=%i): Could not mmap file. errno=%i (%m).\n", 
            _size, errno); 
         buffer = NULL;
	 ::unlink( (char*) realFileName.chars );
         ::close(fdMapFile);
         shmKey.free();
         return 0;
      }
      
      *(sUI*)(buffer + TKS_SBH_SIZE) = _size;
      buffer += TKS_SHAREDBUFFERHEADER_SIZE;
      
      shmHandle.id = fdMapFile;
      shmOwner = 1;
      size = _size;
      return 1;
   }
   else
   {
      Dprintf("[---] SharedBuffer::allocSharedMemory(): _name is not a String.\n");
      return 0;
   }
}


sSI TKS_SharedBuffer::visitSharedMemory (YAC_String *_shmKey, sSI _size) {
   TKS_SharedBuffer::free();
   
   if(YAC_Is_String(_shmKey))
   {
      shmKey.yacCopy(_shmKey);
      YAC_String realFileName;
      realFileName.copy("/tmp/tks_mmap_");
      realFileName.append(&shmKey);

      int fdMapFile = ::open( (char*)realFileName.chars, O_RDWR );

      if( fdMapFile == -1 )
      { 
         Dprintf("[---] SharedBuffer::visitSharedMemory(size=%i): Could not open mmap file. errno=%i (%m).\n", 
            _size, errno);
         shmKey.free();
         return 0;
      }

      // Now memory map the file 
      buffer = (sU8*) ::mmap(NULL, (_size + TKS_SHAREDBUFFERHEADER_SIZE),
                             PROT_READ|PROT_WRITE, MAP_SHARED, 
                             fdMapFile, 0);

      if(buffer == (sU8*)-1)
      { 
         Dprintf("[---] SharedBuffer::visitSharedMemory(size=%i): Could not mmap file. errno=%i (%m).\n", 
            _size, errno); 
         buffer = NULL;
         ::close(fdMapFile);
         shmKey.free();
         return 0;
      }

      // Check whether the requested size does not exceed the actual shared memory size (should never happen)
      sUI shmSize = *(sUI*) (buffer + TKS_SBH_SIZE);
      
      if( ((sUI)_size) > shmSize)
      {
         Dprintf("[---] SharedBuffer::visitSharedMemory(name=%s, size=%i): Requested size exceeds size of shared memory region (%i).\n", 
            (char*)shmKey.chars,
            _size,
            shmSize
            );

#ifdef YAC_LINUX
         ::munmap((caddr_t) buffer, (size_t) (_size + TKS_SHAREDBUFFERHEADER_SIZE) );
#else
         ::munmap((void*) buffer, (size_t) (_size + TKS_SHAREDBUFFERHEADER_SIZE) );
#endif // YAC_LINUX
         ::close( fdMapFile );
         shmKey.free();
         buffer = NULL;
         return 0;
      }
      
      buffer += TKS_SHAREDBUFFERHEADER_SIZE;
      shmHandle.id = fdMapFile;
      shmOwner = 0;
      size = _size;
      return 1;
   }
   else
   {
      Dprintf("[---] SharedBuffer::visitSharedMemory(): _name is not a String.\n");
      return 0;
   }
}


#endif // YAC_LINUX || YAC_MACOSX || YAC_QNX

