/*
  CoreLinux++ 
  Copyright (C) 1999,2000 CoreLinux Consortium
  
   The CoreLinux++ Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The CoreLinux++ Library Library is distributed in the hope that it will 
   be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  
*/

#if   !defined(__COMMON_HPP)
#include <Common.hpp>
#endif

#if   !defined(__MEMORY_HPP)
#include <Memory.hpp>
#endif

#if   !defined(__MEMORYSTORAGE_HPP)
#include <MemoryStorage.hpp>
#endif

extern "C"
{
   #include <fcntl.h>
   #include <unistd.h>
   #include <sys/time.h>
   #include <sys/stat.h>
   #include <sys/types.h>
   #include <sys/ipc.h>
   #include <sys/shm.h>
   #include <errno.h>
   #include <stdio.h>
   #include <stdlib.h>
}

namespace   corelinux
{
   MemoryManager  Memory::theMemoryManager( new Memory );
   MemoryMap      Memory::theMemoryStorageMap;

   //
   // Checks to see that the file exists, or will create
   // based on create flags.
   // Returns 0 if exists already, 1 if created for this
   // operation, and -1 for error
   //

   static   const Dword NAMEBUFFERSIZE(256);
   static   CharCptr    tmpName= "/tmp/clmtemp.";

   // Default constructor

   Memory::Memory( void ) 
      throw( Assertion )
      :
      Synchronized()
   {
      if( theMemoryManager.instance() == NULLPTR )
      {
         theMemoryStorageMap.clear();
      }
      else
      {
         NEVER_GET_HERE;
      }
   }

   // Copy constructor not allowed

   Memory::Memory( MemoryCref )
      throw( Assertion )
      :
      Synchronized()
   {
      NEVER_GET_HERE;
   }
      
   // Destructor - Does not work yet

   Memory::~Memory( void )
   {
      //
      // For now, we only store what we created private so the
      // following is ok. When we get to true sharing this will
      // change.
      //

      if( theMemoryStorageMap.size() != 0 )
      {
         MemoryMapIterator begin = theMemoryStorageMap.begin();
         MemoryMapIterator end = theMemoryStorageMap.end();

         // For each entry, remove from os

         while( begin != end )
         {
            MemoryIdentifierCref  aMId( *(*begin).first );

            struct shmid_ds   aMemDesc;
            shmctl( aMId.getScalar(), IPC_STAT, &aMemDesc );

            if( shmdt( (*begin).first->getBasePointer() ) != (-1) )
            {
               if( aMemDesc.shm_nattch == 1 )
               {
                  shmctl( aMId.getScalar(), IPC_RMID, NULLPTR );
                  Environment::removeCommonAccess( (*begin).second );
               }
               else
               {
                  ;  // do nothing
               }
            }
            else
            {
               ;  // do nothing
            }
            delete (*begin).first;
            if( (*begin).second != NULLPTR )
            {
               delete (*begin).second;
            }
            ++begin;
         }
         theMemoryStorageMap.clear();
      }
      else
      {
         ;  // do nothing
      }
   }

   // Assignment

   MemoryRef Memory::operator=( MemoryCref ) throw( Assertion )
   {
      NEVER_GET_HERE;
      return (*this);
   }

   // Equality

   bool Memory::operator==( MemoryCref ) const
   {
      return false;
   }


   // Factory methods create

   MemoryStoragePtr  Memory::createStorage( Size aByteSize, Int aPermission )
      throw( StorageException )
   {
      Guard    myGuard( theMemoryManager.instance()->access() );

      MemoryStoragePtr  aMPtr( NULLPTR );
      MemoryIdentifier  anIdentifier(0);
      VoidPtr           aBasePtr( NULLPTR );
      CharPtr           theName = new Char[NAMEBUFFERSIZE];

      memset(theName,0,NAMEBUFFERSIZE);
      strcpy(theName,tmpName);
      struct   timeval  tv;
      struct   timezone tz;
      gettimeofday(&tv,&tz);
      srand(tv.tv_sec);

      sprintf( &theName[strlen(tmpName)],"%ld%ld%d",tv.tv_sec,tv.tv_usec,rand() );

      //
      // In order to insure a valid token, we 
      // need a valid file. We use the creationFlag
      // to see that we have that, if one doesn't
      // exist already
      //

      if( Environment::setupCommonAccess(theName,FAIL_IF_EXISTS) == -1 )
      {
         delete [] theName;
         throw StorageException
            (
               "Invalid temporary name exception",
               LOCATION
            );
      }
      else
      {
         ;  // do nothing
      }

      Int   tokVal = ftok(theName,'z');

      if( tokVal == -1 )
      {
         delete [] theName;
         throw StorageException
            (
               "Invalid token creation",
               LOCATION
            );
      }
      else
      {
         ;  // do nothing
      }

      //
      // We attempt to allocate the block of storage as requested
      //

      anIdentifier = shmget
                        ( 
                           tokVal, 
                           aByteSize, 
                           IPC_CREAT | IPC_EXCL | aPermission 
                        );

      if(  anIdentifier == (-1) )
      {
         delete [] theName;
         throw StorageException("Unable to create storage", LOCATION );
      }
      else
      {
         // Now we attach it with read and write access

         aBasePtr = shmat( anIdentifier.getScalar(), 0 , 0 );

         if( Long( aBasePtr ) == (-1) )
         {
            delete [] theName;
            throw StorageException("Unable to attach storage", LOCATION );
         }
         else
         {
            struct shmid_ds   aMemDesc;
            shmctl( anIdentifier.getScalar(), IPC_STAT, &aMemDesc );
            aMPtr = new MemoryStorage( anIdentifier,aMemDesc.shm_segsz, aBasePtr );
            theMemoryStorageMap[aMPtr] = theName;
         }
      }

      return aMPtr;
   }

   // Create/open named object

   MemoryStoragePtr  Memory::createStorage
      (
         MemoryIdentifierCref aId,
         Size                 aByteSize, 
         CreateDisposition    disp,
         Int                  aRights,
         AddressingConstraint addressing
      )
   {
      Guard    myGuard( theMemoryManager.instance()->access() );

      MemoryStoragePtr  aMPtr( NULLPTR );
      MemoryIdentifier  anIdentifier(0);
      VoidPtr           aBasePtr( NULLPTR );   
      Int               createFlag(0);

      //
      // If we should fail in the prescence
      // of an existing group with the same
      // identifier we need to indicate that
      // and insure that there are positive
      // values for semaphore counts
      //

      if( disp == FAIL_IF_EXISTS )
      {
         createFlag = ( IPC_CREAT | IPC_EXCL );
      }
      else if( disp == CREATE_OR_REUSE )
      {
         createFlag = IPC_CREAT;
      }
      else
      {
         ;  // do nothing
      }

      anIdentifier = shmget
         (
            aId.getScalar(),
            aByteSize,
            createFlag | aRights
         );

      if(  anIdentifier == (-1) )
      {
         throw StorageException("Unable to create storage", LOCATION );
      }
      else
      {
         // Now we attach it with read and write access

         aBasePtr = shmat( anIdentifier.getScalar(), 0 , 0 );

         if( Long( aBasePtr ) == (-1) )
         {
            throw StorageException("Unable to attach storage", LOCATION );
         }
         else
         {
            struct shmid_ds   aMemDesc;
            shmctl( anIdentifier.getScalar(), IPC_STAT, &aMemDesc );
            aMPtr = new MemoryStorage( anIdentifier,aMemDesc.shm_segsz, aBasePtr );
            theMemoryStorageMap[aMPtr] = NULLPTR;
         }
      }
      return aMPtr;
   }

   // Create/open named object

   MemoryStoragePtr  Memory::createStorage
      (
         CharCptr             aName,
         Size                 aByteSize, 
         CreateDisposition    disp,
         Int                  aRights,
         AddressingConstraint addressing
      )
   {
      Guard    myGuard( theMemoryManager.instance()->access() );

      MemoryStoragePtr  aMPtr( NULLPTR );
      MemoryIdentifier  anIdentifier(0);
      VoidPtr           aBasePtr( NULLPTR );   
      Int               createFlag(0);
      CharPtr           theName = new Char[NAMEBUFFERSIZE];

      memset(theName,0,NAMEBUFFERSIZE);
      strcpy(theName,tmpName);
      strcat(theName,aName);

      //
      // If we should fail in the prescence
      // of an existing group with the same
      // identifier we need to indicate that
      // and insure that there are positive
      // values for semaphore counts
      //

      if( disp == FAIL_IF_EXISTS )
      {
         createFlag = ( IPC_CREAT | IPC_EXCL );
      }
      else if( disp == CREATE_OR_REUSE )
      {
         createFlag = IPC_CREAT;
      }
      else
      {
         ;  // do nothing
      }

      //
      // In order to insure a valid token, we 
      // need a valid file. We use the creationFlag
      // to see that we have that, if one doesn't
      // exist already
      //

      if( Environment::setupCommonAccess(theName,disp) == -1 )
      {
         delete [] theName;
         throw StorageException
            (
               aName,
               LOCATION
            );
      }
      else
      {
         ;  // do nothing
      }

      Int   tokVal = ftok(theName,'z');

      if( tokVal == -1 )
      {
         delete [] theName;
         throw StorageException
            (
               aName,
               LOCATION
            );
      }
      else
      {
         ;  // do nothing
      }

      anIdentifier = shmget
         (
            tokVal,
            aByteSize,
            createFlag | aRights
         );

      if(  anIdentifier == (-1) )
      {
         delete [] theName;
         throw StorageException("Unable to create storage", LOCATION );
      }
      else
      {
         // Now we attach it with read and write access

         aBasePtr = shmat( anIdentifier.getScalar(), 0 , 0 );

         if( Long( aBasePtr ) == (-1) )
         {
            delete [] theName;
            throw StorageException("Unable to attach storage", LOCATION );
         }
         else
         {
            struct shmid_ds   aMemDesc;
            shmctl( anIdentifier.getScalar(), IPC_STAT, &aMemDesc );
            aMPtr = new MemoryStorage( anIdentifier,aMemDesc.shm_segsz, aBasePtr );
            theMemoryStorageMap[aMPtr] = theName;
         }
      }
      return aMPtr;
   }


   // Factory method destroy

   void Memory::destroyStorage( MemoryStoragePtr aMPtr )
   {
      //Guard    myGuard( theMemoryManager.instance()->access() );

      REQUIRE( aMPtr != NULLPTR );

      VoidPtr  basePointer( aMPtr->getBasePointer() );

      REQUIRE( basePointer != NULLPTR );

      //
      // Auto detach
      //

      struct shmid_ds   aMemDesc;
      shmctl( MemoryIdentifierCref(*aMPtr).getScalar(), IPC_STAT, &aMemDesc );

      if( shmdt( basePointer ) != (-1) )
      {
         // Determine if we should remove now.

         if( aMemDesc.shm_nattch == 1 )
         {
            shmctl( MemoryIdentifierCref(*aMPtr).getScalar(), IPC_RMID, NULLPTR );
            MemoryMapIterator fItr( theMemoryStorageMap.find(aMPtr) );

            if( fItr != theMemoryStorageMap.end() )
            {
               Environment::removeCommonAccess( (*fItr).second );
               delete (*fItr).second;
               theMemoryStorageMap.erase( fItr );
               delete aMPtr;
            }
            else
            {
               ;  // do nothing
            }

         }
         else
         {
            ;  // do nothing
         }
      }
      else
      {
         throw StorageException("Unable to detach storage", LOCATION );
      }
   }

}


/*
   Common rcs information do not modify
   $Author: frankc $
   $Revision: 1.6 $
   $Date: 2001/04/15 01:09:28 $
   $Locker:  $
*/

