/*
  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(__SEMAPHOREGROUP_HPP)
#include <SemaphoreGroup.hpp>
#endif

#if   !defined(__SEMAPHORECOMMON_HPP)
#include <SemaphoreCommon.hpp>
#endif

extern   "C"
{
   #include <unistd.h>
   #include <sys/time.h>
   #include <sys/ipc.h>
   #include <sys/sem.h>
   #include <errno.h>
   #include <stdio.h>
   #include <stdlib.h>
}

namespace corelinux
{
   //
   // 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   CharCptr    tmpDir = "/tmp/";
   static   CharCptr    tmpName= "/tmp/clsgtemp.";

   //
   // Basic group constructor with access rights specified
   //

   SemaphoreGroup::SemaphoreGroup( Short aSemCount, Int aRightSet )
      throw(Assertion,SemaphoreException)
      :
      theIdentifier(-1),
      theNumberOfSemaphores( aSemCount ),
      theGroupCSA( NULLPTR )
   {
      REQUIRE( aSemCount >= 1 );

      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 )
      {
         throw SemaphoreException
            (
               theName,
               LOCATION,
               Exception::CONTINUABLE,
               false,
               Thread::getKernelError() 
            );
      }
      else
      {
         ;  // do nothing
      }

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

      if( tokVal == -1 )
      {
         throw SemaphoreException
            (
               theName,
               LOCATION,
               Exception::CONTINUABLE,
               false,
               Thread::getKernelError() 
            );
      }
      else
      {
         ;  // do nothing
      }

      theIdentifier = semget
         (
            tokVal,
            theNumberOfSemaphores,
            IPC_CREAT | IPC_EXCL | aRightSet
         );

      if( theIdentifier == (-1) )
      {
         theNumberOfSemaphores = 0;

         throw SemaphoreException
            (
               "SemaphoreGroup( Short , Int )::semget",
               LOCATION,
               Exception::CONTINUABLE,
               false,
               Thread::getKernelError()
            );
      }
      else
      {
         ;  // do nothing
      }
   }

   //
   // Constructor where the identifier has been formed already
   //

   SemaphoreGroup::SemaphoreGroup
      ( 
         Short                         aSemCount, 
         SemaphoreGroupIdentifierCref  aGID ,
         Int                           aRightSet,
         CreateDisposition             disp
      ) throw(Assertion,SemaphoreException)
      :
      theIdentifier(-1),
      theNumberOfSemaphores( aSemCount ),
      theGroupCSA( NULLPTR )
   {

      Int createFlag(0);
      memset(theName,0,NAMEBUFFERSIZE);

      //
      // 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 )
      {
         ENSURE( aSemCount >= 1 );
         createFlag = ( IPC_CREAT | IPC_EXCL );
      }
      else
      {
         ;  // do nothing
      }

      theIdentifier = semget
         (
            SemaphoreGroupIdentifierRef(aGID),
            theNumberOfSemaphores,
            createFlag | aRightSet
         );

      //
      // If the call failed
      //

      if( theIdentifier == (-1) )
      {
         theNumberOfSemaphores = 0;

         // If exists and it shouldn't

         if( disp == FAIL_IF_EXISTS && 
             Thread::getKernelError() == EEXIST )
         {
            throw SemaphoreException("Group Exists Exception",LOCATION);
         }

         // If doesn't exist and it should

         else if( disp == FAIL_IF_NOTEXISTS && 
                  Thread::getKernelError() == ENOENT )
         {
            throw SemaphoreException("Group Not Exists Exception",LOCATION);
         }

         // Or for any other reason (like access, deleted, etc.)

         else
         {
            throw SemaphoreException
               (
                  "SemaphoreGroup exception",
                  LOCATION,
                  Exception::CONTINUABLE,
                  false,
                  Thread::getKernelError()
               );
         }
      }

      //
      // Else it is open shared
      //

      else 
      {
         SemaphoreCommon::groupDefined(this);
      }

   }

   //
   // Constructor to form a group
   //

   SemaphoreGroup::SemaphoreGroup
      ( 
         Short                aSemCount, 
         CharCptr             aName,
         Int                  aRightSet,
         CreateDisposition    disp
      ) throw(Assertion,SemaphoreException)
      :
      theIdentifier(-1),
      theNumberOfSemaphores( aSemCount ),
      theGroupCSA( NULLPTR )
   {
      
      Int createFlag(0);
      memset(theName,0,NAMEBUFFERSIZE);

      //
      // 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 )
      {
         ENSURE( aSemCount >= 1 );
         createFlag = ( IPC_CREAT | IPC_EXCL );
      }
      else if( disp == CREATE_OR_REUSE )
      {
         ENSURE( aSemCount >= 1 );
         createFlag = IPC_CREAT;
      }
      else
      {
         ;  // do nothing
      }

      strcpy(theName,tmpDir);
      strcat(theName,aName);

      //
      // 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
      //

      Int   setupResults = Environment::setupCommonAccess(theName,disp);

      if( setupResults == -1 )
      {
         memset(theName,0,NAMEBUFFERSIZE);
         throw SemaphoreException
            (
               aName,
               LOCATION,
               Exception::CONTINUABLE,
               false,
               Thread::getKernelError() 
            );
      }
      else
      {
         ;  // do nothing
      }

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

      if( tokVal == -1 )
      {
         if( setupResults == 1 )
         {
            Environment::removeCommonAccess(theName);
         }
         else
         {
            ;  // do nothing
         }

         memset(theName,0,NAMEBUFFERSIZE);
         throw SemaphoreException
            (
               aName,
               LOCATION,
               Exception::CONTINUABLE,
               false,
               Thread::getKernelError() 
            );
      }
      else
      {
         ;  // do nothing
      }

      theIdentifier = semget
         (
            tokVal,
            theNumberOfSemaphores,
            createFlag | aRightSet
         );

      //
      // If the call failed
      //

      if( theIdentifier == (-1) )
      {
         int   keval = Thread::getKernelError();

         if( setupResults == 1 )
         {
            Environment::removeCommonAccess(theName);
         }
         else
         {
            ;  // do nothing
         }
         memset(theName,0,NAMEBUFFERSIZE);

         theNumberOfSemaphores = 0;

         // If exists and it shouldn't

         if( disp == FAIL_IF_EXISTS && keval == EEXIST )
         {
            throw SemaphoreException("Group Exists Exception",LOCATION);
         }

         // If doesn't exist and it should

         else if( disp == FAIL_IF_NOTEXISTS && keval == ENOENT )
         {
            throw SemaphoreException("Group Not Exists Exception",LOCATION);
         }

         // Or for any other reason (like access, deleted, etc.)

         else
         {
            throw SemaphoreException
               (
                  "SemaphoreGroup exception",
                  LOCATION,
                  Exception::CONTINUABLE,
                  false,
                  Thread::getKernelError() 
               );
         }
      }


      //
      // Else if ok
      //

      else 
      {
         strcpy(theName,aName);
         SemaphoreCommon::groupDefined(this);
         if( this->isPrivate() == false )
         {
            theNumberOfSemaphores = theGroupCSA->groupSemCount;
         }
         else
         {
            ;  // do nothing
         }
      }
   }

   //
   // Destructor
   //

   SemaphoreGroup::~SemaphoreGroup( void )
   {
      //
      // If the identifier is not invalid
      //

      char  buffer[254];


      if( theIdentifier != (-1) )
      {
         //
         // Deleting a set
         //

         if( theGroupCSA != NULLPTR )
         {
            //
            // If the number of references drops to zero
            //

            if( SemaphoreCommon::groupUnDefined(this) == 0 )
            {
               // And we have a name, remove it

               if( strlen(theName) != 0 )
               {
                  strcpy(buffer,tmpDir);
                  strcat(buffer,theName);
                  Environment::removeCommonAccess(buffer);
               }
               else
               {
                  ;  // nothing we can do
               }

               // Remove the control group

               int val = semctl( theIdentifier, 0 , IPC_RMID, 0 );

               if( val == -1 )
               {
                  throw SemaphoreException("Semaphore Group Destructor failed",LOCATION);
               }
               else
               {
                  ;  // do nothing
               }
            }
            else
            {
               ;  // still in use
            }
         }

         // If private

         else
         {
            // And we have a name, remove it

            if( strlen(theName) != 0 )
            {
               Environment::removeCommonAccess(theName);
            }
            else
            {
               ;  // nothing we can do
            }

            // Remove the control group

            int val = semctl( theIdentifier, 0 , IPC_RMID, 0 );

            if( val == -1 )
            {
               throw SemaphoreException("Semaphore Group Destructor failed",LOCATION);
            }
            else
            {
               ;  // do nothing
            }
         }

         theIdentifier = -1;
      }
      else
      {
         if( strlen(theName) != 0 )
         {
            if( theGroupCSA == NULLPTR )
            {
               Environment::removeCommonAccess(theName);
            }
            else
            {
               strcpy(buffer,tmpDir);
               strcat(buffer,theName);
               Environment::removeCommonAccess(buffer);
            }
         }
      }
   }

   //
   // Equality operator
   //

   bool  SemaphoreGroup::operator==
      ( 
         SemaphoreGroupCref aSemGroupRef 
      ) const
   {
      return ( theIdentifier == aSemGroupRef.getIdentifier() );
   }

   //
   // Return the count in the group
   //

   Short    SemaphoreGroup::getSemaphoreCount( void ) const
   {
      Short aVal(theNumberOfSemaphores);
      if( aVal == 0 )
      {
         if( this->isPrivate() == false )
         {
            aVal = theGroupCSA->groupSemCount;
         }
         else
         {
            ;  // do nothing
         }
      }
      else
      {
         ;  // do nothing
      }

      return aVal;
   }

}

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

