# ==========================================================================
#
# ZoneMinder Shared Memory Access Module, $Date: 2007-08-29 19:11:09 +0100 (Wed, 29 Aug 2007) $, $Revision: 2175 $
# Copyright (C) 2001-2008  Philip Coombes
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# ==========================================================================
#
# This module contains the common definitions and functions used by the rest
# of the ZoneMinder scripts
#
package ZoneMinder::Memory::Shared;

use 5.006;
use strict;
use warnings;

require Exporter;
require ZoneMinder::Base;

our @ISA = qw(Exporter ZoneMinder::Base);

eval 'sub IPC_CREAT {0001000}' unless defined &IPC_CREAT;

# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.

# This allows declaration   use ZoneMinder ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = (
    'functions' => [ qw(
        zmMemKey
        zmMemAttach
        zmMemDetach
        zmMemGet
        zmMemPut
        zmMemClean
    ) ],
);
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

our @EXPORT = @EXPORT_OK;

our $VERSION = $ZoneMinder::Base::VERSION;

# ==========================================================================
#
# Shared Memory Facilities
#
# ==========================================================================

use ZoneMinder::Config qw(:all);
use ZoneMinder::Logger qw(:all);

sub zmMemKey
{
    my $monitor = shift;
    return( defined($monitor->{ShmKey})?$monitor->{ShmKey}:undef );
}

sub zmMemAttach
{
    my $monitor = shift;
    my $size = shift;
    if ( !defined($monitor->{ShmId}) )
    {
        my $shm_key = (hex($Config{ZM_SHM_KEY})&0xffff0000)|$monitor->{Id};
        my $shm_id = shmget( $shm_key, $size, &IPC_CREAT | 0777  );
        if ( !defined($shm_id) )
        {
            Error( sprintf( "Can't get shared memory id '%x', %d: $!\n"
                            , $shm_key
                            , $monitor->{Id}
                          )
            );
            return( undef );
        }
        $monitor->{ShmKey} = $shm_key;
        $monitor->{ShmId} = $shm_id;
    }
    return( !undef );
}

sub zmMemDetach
{
    my $monitor = shift;

    delete $monitor->{ShmId};
}

sub zmMemGet
{
    my $monitor = shift;
    my $offset = shift;
    my $size = shift;

    my $shm_key = $monitor->{ShmKey};
    my $shm_id = $monitor->{ShmId};

    my $data;
    if ( !shmread( $shm_id, $data, $offset, $size ) )
    {
        Error( sprintf( "Can't read from shared memory '%x/%d': $!"
                        , $shm_key
                        , $shm_id
                      )
        );
        return( undef );
    }
    return( $data );
}

sub zmMemPut
{
    my $monitor = shift;
    my $offset = shift;
    my $size = shift;
    my $data = shift;

    my $shm_key = $monitor->{ShmKey};
    my $shm_id = $monitor->{ShmId};

    if ( !shmwrite( $shm_id, $data, $offset, $size ) )
    {
        Error( sprintf( "Can't write to shared memory '%x/%d': $!"
                        , $shm_key
                        , $shm_id
                      )
        );
        return( undef );
    }
    return( !undef );
}

sub zmMemClean
{
    Debug(2, 'Removing shared memory');
    # Find ZoneMinder shared memory
    my $command = 'ipcs -m | grep \'^'
                  .substr( sprintf( '0x%x', hex($Config{ZM_SHM_KEY}) ), 0, -2 )
                  .'\''
    ;
    Debug(2, 'Checking for shared memory with '.$command);
    open( my $CMD, '<', $command.' |' )
        or Fatal( "Can't execute '$command': $!" );
    while( <$CMD> )
    {
        chomp;
        my ( $key, $id ) = split( /\s+/ );
        if ( $id =~ /^(\d+)/ )
        {
            $id = $1;
            $command = 'ipcrm shm '.$id;
            Debug(2, 'Removing shared memory with '.$command);
            qx( $command );
        }
    }
    close( $CMD );
}

1;
__END__
