Hi, On Sun, May 18, 2014 at 07:24:25PM +0200, Guillem Jover wrote: > Well, I think going for now with the pure perl version would solve > our immediate problem, we could go over the rest separately. I just > mentioned it now because it would avoid dropping an then having to > reintroduce the XS code again.
Fine. I'm in the process of doing some clean-up and will send it wen done. > > a) add File::FcntlLock in XS form directly as Dpkg::Lock (or > > a simplified version since obviously only locks on whole > > files are required) > > b) add the pure Perl version > > c) add a version that is modified so that it determines the lay- > > out of the C flock struct somewhere in a BEGIN block by com- > > piling and running a C program and then using its output (as > > I can see libdpkg-perl requires the availability of a C com- > > piler). I'd write that for you if you like. > > Because the File::FcntlLock module is generally useful, I'd rather see > it improved, instead of forking a local copy for dpkg-dev alone. I don't > think option c) would solve the issue I described, though. Wouldn't it? It always evaluates the C flock struct whenever 'use'd, so it should get it right on the system used, even if the C fcntl(2) function should be modified. And if there's a new Perl version will update the dpkg-dev package and with this a Dpkg::FcntlLock submodule, wouldn't you? Well, as I said, all this is a bit above my level;-) But since I just got it finished and it seems to work I'll append it anyway. (I pared it back a bit to make it smaller, so it may now be a bit easier to read, and it's just a single .pm file meant to be dropped into the scripts/Dpkg directory of dpkg.) > I think the conventional way of doing what I was proposing might be: > > * Create a File::FcntlLock::XS module that loads the XS code, > which would only contain C_fcntl_lock. > * Either create another module, say File::FcntlLock::Perl or ::Pure > or similar name with the pure perl implementation, or embed the pure > perl code in the File::FcntlLock module at build time. The former > would in addition get rid of your CPAN upload concerns, as you could > ship the .pm module normally. > * In File::FcntlLock decide which implementation to use depending on > File::FcntlLock::XS being available or not through an evaled require. > * Then in Debian the File::FcntlLock::XS and corresponding .so file > could be split into a different package. > > Hope that clarifies. Yes, I guess so. Give me a bit of time for this. The build process could be a bit more tricky, though;-) Best regards, Jens -- \ Jens Thoms Toerring ________ j...@toerring.de \_______________________________ http://toerring.de
# -*- cperl -*- # # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. # # Copyright (C) 2002-2014 Jens Thoms Toerring <j...@toerring.de> package File::FcntlLock; use v5.6.1; use strict; use warnings; use Fcntl; use POSIX; use Errno; use Carp; use Config; use File::Temp qw/ /; use File::Spec; require Exporter; our @ISA = qw( Exporter ); # Items to export into callers namespace by default. our @EXPORT = qw( F_GETLK F_SETLK F_SETLKW F_RDLCK F_WRLCK F_UNLCK SEEK_SET SEEK_CUR SEEK_END ); our $VERSION = '0.15'; my ( $packstr, @member_list ); ########################################################### BEGIN { # Create a C file in the prefered directory for temporary files for # probing the layout of the C 'flock struct'. Since __DATA__ can't # be used in a BEGIN block we've got to do this via a HEREDOC. my $c_file = File::Temp->new( TEMPLATE => 'File_FcntlLock-XXXXXX', SUFFIX => '.c', DIR => File::Spec->tmpdir( ) ); print $c_file <<EOF; #include <stdio.h> #include <stddef.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <limits.h> #define membersize( type, member ) ( sizeof( ( ( type * ) NULL )->member ) ) #define NUM_ELEMS( p ) ( sizeof p / sizeof *p ) typedef struct { const char * name; size_t size; size_t offset; } Params; /*-------------------------------------------------* * Called from qsort() for sorting an array of Params structures * in ascending order of their 'offset' members *-------------------------------------------------*/ static int comp( const void * a, const void * b ) { if ( a == b ) return 0; return ( ( Params * ) a )->offset < ( ( Params * ) b )->offset ? -1 : 1; } /*-------------------------------------------------* *-------------------------------------------------*/ int main( void ) { Params params[ ] = { { "l_type", CHAR_BIT * membersize( struct flock, l_type ), CHAR_BIT * offsetof( struct flock, l_type ) }, { "l_whence", CHAR_BIT * membersize( struct flock, l_whence ), CHAR_BIT * offsetof( struct flock, l_whence ) }, { "l_start", CHAR_BIT * membersize( struct flock, l_start ), CHAR_BIT * offsetof( struct flock, l_start ) }, { "l_len", CHAR_BIT * membersize( struct flock, l_len ), CHAR_BIT * offsetof( struct flock, l_len ) }, { "l_pid", CHAR_BIT * membersize( struct flock, l_pid ), CHAR_BIT * offsetof( struct flock, l_pid ) } }; size_t size = CHAR_BIT * sizeof( struct flock ); size_t i; size_t pos = 0; char packstr[ 128 ] = ""; /* All sizes and offsets must be divisable by 8 and the sizes of the members must be either 8-, 16-, 32- or 64-bit values, otherwise there's no good way to pack them. */ if ( size % 8 ) exit( EXIT_FAILURE ); size /= 8; for ( i = 0; i < NUM_ELEMS( params ); ++i ) { if ( params[ i ].size % 8 || params[ i ].offset % 8 || ( params[ i ].size != 8 && params[ i ].size != 16 && params[ i ].size != 32 && params[ i ].size != 64 ) ) exit( EXIT_FAILURE ); params[ i ].size /= 8; params[ i ].offset /= 8; } /* Sort the array of structures for the members in ascending order of the offset */ qsort( params, NUM_ELEMS( params ), sizeof *params, comp ); /* Cobble together the template string to be passed to pack(), taking care of padding and also extra members we're not interested in. All the interesting members have signed integer types. */ for ( i = 0; i < NUM_ELEMS( params ); ++i ) { if ( pos != params[ i ].offset ) sprintf( packstr + strlen( packstr ), "x%lu", ( unsigned long )( params[ i ].offset - pos ) ); pos = params[ i ].offset; switch ( params[ i ].size ) { case 1 : strcat( packstr, "c" ); break; case 2 : strcat( packstr, "s" ); break; case 4 : strcat( packstr, "l" ); break; case 8 : strcat( packstr, "q" ); break; default : exit( EXIT_FAILURE ); } pos += params[ i ].size; } if ( pos < size ) sprintf( packstr + strlen( packstr ), "x%lu", (unsigned long ) ( size - pos ) ); printf( "%s\\n", packstr ); for ( i = 0; i < NUM_ELEMS( params ); ++i ) printf( "%s\\n", params[ i ].name ); return 0; } EOF # Try to compile the file. We close the resulting executable file since # it can't be run while it's still open, my $exec_file = File::Temp->new( TEMPLATE => 'File_FcntlLock-XXXXXX', DIR => File::Spec->tmpdir( ) ); close $exec_file; die "Failed to run the C compiler '$Config{cc}'\n" if system "$Config{cc} -o $exec_file $c_file"; # Run the program and read it's output, it writes out the template string # we need for packing and unpacking the binart C struct flock required for # fcntk() and then the members of the structures in the sequence they are # defined in there. open my $pipe, '-|', $exec_file or die "Failed to run a compiled program: $!\n"; chomp( $packstr = <$pipe> ); while ( <$pipe> ) { chomp; push @member_list, $_; } # Make sure we got all information needed die "Failed to obtain all needed data about the C struct flock\n" unless @member_list == 5; } ########################################################### sub new { my $inv = shift; my $pkg = ref( $inv ) || $inv; my $self = { l_type => F_RDLCK, l_whence => SEEK_SET, l_start => 0, l_len => 0, l_pid => 0 }; if ( @_ % 2 ) { carp "Missing value in key-value initializer list " . "in call of new method"; return; } while ( @_ ) { my $key = shift; no strict 'refs'; unless ( defined &$key ) { carp "Flock structure has no '$key' member " . "in call of new method"; return; } &$key( $self, shift ); use strict 'refs'; } bless $self, $pkg; } ########################################################### sub l_type { my $flock_struct = shift; if ( @_ ) { my $l_type = shift; unless ( $l_type == F_RDLCK or $l_type == F_WRLCK or $l_type == F_UNLCK ) { carp "Invalid argument in call of l_type method"; return; } $flock_struct->{ l_type } = $l_type; } return $flock_struct->{ l_type }; } ########################################################### sub l_whence { my $flock_struct = shift; if ( @_ ) { my $l_whence = shift; unless ( $l_whence == SEEK_SET or $l_whence == SEEK_CUR or $l_whence == SEEK_END ) { carp "Invalid argument in call of l_whence method"; return; } $flock_struct->{ l_whence } = $l_whence; } return $flock_struct->{ l_whence }; } ########################################################### sub l_start { my $flock_struct = shift; $flock_struct->{ l_start } = shift if @_; return $flock_struct->{ l_start }; } ########################################################### sub l_len { my $flock_struct = shift; $flock_struct->{ l_len } = shift if @_; return $flock_struct->{ l_len }; } ########################################################### sub l_pid { return shift->{ l_pid }; } ########################################################### sub lock { my ( $flock_struct, $fh, $action ) = @_; my $buf = pack_flock( $flock_struct ); my $ret = fcntl( $fh, $action, $buf ); unpack_flock( $flock_struct, $buf ) if $ret; return $ret; } ########################################################### # Method for packing the data from the 'flock_struct' into a # binary blob to be passed to fcntl(). sub pack_flock { my $fs = shift; my @args; push @args, $fs->{ $_ } for @member_list; return pack $packstr, @args; } ########################################################### # Method for unpacking the binary blob received from a call of # fcntl() into the 'flock_struct'. sub unpack_flock { my ( $fs, $data ) = @_; my @res = unpack $packstr, $data; my $i = 0; $fs->{ $_ } = $res[ $i++ ] for @member_list; } =cut 1;