Re: Bug#677865: Re: Bug#677865: dpkg-gencontrol warns about 'File::FcntlLock not available'
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 ________ jt@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 <jt@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;
Reply to: