File Coverage

blib/lib/File/FcntlLock/Inline.pm
Criterion Covered Total %
statement 54 55 98.1
branch 6 12 50.0
condition n/a
subroutine 12 12 100.0
pod 1 3 33.3
total 73 82 89.0


line stmt bran cond sub pod time code
1             # -*- cperl -*-
2             #
3             # This program is free software; you can redistribute it and/or
4             # modify it under the same terms as Perl itself.
5             #
6             # Copyright (C) 2002-2014 Jens Thoms Toerring
7              
8              
9             # Package for file locking with fcntl(2) in which the binary layout of
10             # the C flock struct is determined via compiling and running a C program
11             # each time the package is loaded
12              
13             package File::FcntlLock::Inline;
14              
15 4     4   2052 use 5.006001;
  4         8  
  4         124  
16 4     4   16 use strict;
  4         4  
  4         88  
17 4     4   16 use warnings;
  4         8  
  4         68  
18 4     4   16 use Fcntl;
  4         4  
  4         1380  
19 4     4   24 use Config;
  4         8  
  4         156  
20 4     4   5236 use File::Temp;
  4         102704  
  4         360  
21 4     4   32 use File::Spec;
  4         8  
  4         80  
22 4     4   20 use base qw( File::FcntlLock::Core );
  4         8  
  4         1792  
23              
24              
25             our $VERSION = File::FcntlLock::Core->VERSION;
26              
27             our @EXPORT = @File::FcntlLock::Core::EXPORT;
28              
29              
30             my ( $packstr, @member_list );
31              
32              
33             ###########################################################
34              
35             BEGIN {
36             # Create a C file in the preferred directory for temporary files for
37             # probing the layout of the C 'flock struct'. Since __DATA__ can't
38             # be used in a BEGIN block we've got to do with a HEREDOC.
39              
40 4     4   412 my $c_file = File::Temp->new( TEMPLATE => 'File-FcntlLock-XXXXXX',
41             SUFFIX => '.c',
42             DIR => File::Spec->tmpdir( ) );
43              
44 4         88768 print $c_file <
45             #include
46             #include
47             #include
48             #include
49             #include
50             #include
51              
52              
53             #define membersize( type, member ) ( sizeof( ( ( type * ) NULL )->member ) )
54             #define NUM_ELEMS( p ) ( sizeof p / sizeof *p )
55              
56             typedef struct {
57             const char * name;
58             size_t size;
59             size_t offset;
60             } Params;
61              
62              
63             /*-------------------------------------------------*
64             * Called from qsort() for sorting an array of Params structures
65             * in ascending order of their 'offset' members
66             *-------------------------------------------------*/
67              
68             static int
69             comp( const void * a,
70             const void * b )
71             {
72             if ( a == b )
73             return 0;
74             return ( ( Params * ) a )->offset < ( ( Params * ) b )->offset ? -1 : 1;
75             }
76              
77              
78             /*-------------------------------------------------*
79             *-------------------------------------------------*/
80              
81             int
82             main( void )
83             {
84             Params params[ ] = { { "l_type",
85             CHAR_BIT * membersize( struct flock, l_type ),
86             CHAR_BIT * offsetof( struct flock, l_type ) },
87             { "l_whence",
88             CHAR_BIT * membersize( struct flock, l_whence ),
89             CHAR_BIT * offsetof( struct flock, l_whence ) },
90             { "l_start",
91             CHAR_BIT * membersize( struct flock, l_start ),
92             CHAR_BIT * offsetof( struct flock, l_start ) },
93             { "l_len",
94             CHAR_BIT * membersize( struct flock, l_len ),
95             CHAR_BIT * offsetof( struct flock, l_len ) },
96             { "l_pid",
97             CHAR_BIT * membersize( struct flock, l_pid ),
98             CHAR_BIT * offsetof( struct flock, l_pid ) } };
99             size_t size = CHAR_BIT * sizeof( struct flock );
100             size_t i;
101             size_t pos = 0;
102             char packstr[ 128 ] = "";
103            
104             /* All sizes and offsets must be divisable by 8 and the sizes of the
105             members must be either 8-, 16-, 32- or 64-bit values, otherwise
106             there's no good way to pack them. */
107              
108             if ( size % 8 )
109             exit( EXIT_FAILURE );
110              
111             size /= 8;
112              
113             for ( i = 0; i < NUM_ELEMS( params ); ++i )
114             {
115             if ( params[ i ].size % 8
116             || params[ i ].offset % 8
117             || ( params[ i ].size != 8
118             && params[ i ].size != 16
119             && params[ i ].size != 32
120             && params[ i ].size != 64 ) )
121             exit( EXIT_FAILURE );
122              
123             params[ i ].size /= 8;
124             params[ i ].offset /= 8;
125             }
126              
127             /* Sort the array of structures for the members in ascending order of
128             the offset */
129              
130             qsort( params, NUM_ELEMS( params ), sizeof *params, comp );
131              
132             /* Cobble together the template string to be passed to pack(), taking
133             care of padding and also extra members we're not interested in. All
134             the interesting members have signed integer types. */
135              
136             for ( i = 0; i < NUM_ELEMS( params ); ++i )
137             {
138             if ( pos != params[ i ].offset )
139             sprintf( packstr + strlen( packstr ), "x%lu",
140             ( unsigned long )( params[ i ].offset - pos ) );
141             pos = params[ i ].offset;
142              
143             switch ( params[ i ].size )
144             {
145             case 1 :
146             strcat( packstr, "c" );
147             break;
148              
149             case 2 :
150             strcat( packstr, "s" );
151             break;
152              
153             case 4 :
154             strcat( packstr, "l" );
155             break;
156              
157             case 8 :
158             #if defined NO_Q_FORMAT
159             exit( EXIT_FAILURE );
160             #endif
161             strcat( packstr, "q" );
162             break;
163              
164             default :
165             exit( EXIT_FAILURE );
166             }
167              
168             pos += params[ i ].size;
169             }
170              
171             if ( pos < size )
172             sprintf( packstr + strlen( packstr ), "x%lu",
173             (unsigned long ) ( size - pos ) );
174              
175             printf( "%s\\n", packstr );
176             for ( i = 0; i < NUM_ELEMS( params ); ++i )
177             printf( "%s\\n", params[ i ].name );
178              
179             return 0;
180             }
181             EOF
182              
183 4         320 close $c_file;
184              
185             # Try to compile and link the file.
186              
187 4         124 my $exec_file = File::Temp->new( TEMPLATE => 'File=FcntlLock-XXXXXX',
188             DIR => File::Spec->tmpdir( ) );
189 4         1948 close $exec_file;
190              
191 4         8 my $qflag = eval { pack 'q', 1 };
  4         12  
192 4 50       20 $qflag = $@ ? '-DNO_Q_FORMAT' : '';
193              
194 4 50       3940 die "Failed to run the C compiler '$Config{cc}'\n"
195             if system "$Config{cc} $Config{ccflags} $qflag -o $exec_file $c_file";
196              
197             # Run the program and read it's output, it writes out the template string
198             # we need for packing and unpacking the binary C struct flock required for
199             # fcntl() and then the members of the structures in the sequence they are
200             # defined in there.
201              
202 4 50       798668 open my $pipe, '-|', $exec_file
203             or die "Failed to run a compiled program: $!\n";
204              
205 4         22840 chomp( $packstr = <$pipe> );
206 4         480 while ( my $line = <$pipe> ) {
207 20         44 chomp $line;
208 20         352 push @member_list, $line;
209             }
210              
211             # Make sure we got all information needed
212              
213 4 50       64 die "Your Perl version does not support the 'q' format for pack() "
214             . "and unpack()\n" unless defined $packstr;
215              
216 4 50       464 die "Failed to obtain all needed data about the C struct flock\n"
217             unless @member_list == 5;
218             }
219              
220              
221             ###########################################################
222             # Function for doing the actual fcntl() call: assembles the binary
223             # structure that must be passed to fcntl() from the File::FcntlLock
224             # object we get passed, calls it and then modifies the File::FcntlLock
225             # with the data from the flock structure
226              
227             sub lock {
228 17     17 1 2006014 my ( $self, $fh, $action ) = @_;
229              
230 17         93 my $buf = $self->pack_flock( );
231 17         219 my $ret = fcntl( $fh, $action, $buf );
232              
233 17 50       199 if ( $ret ) {
234 17         106 $self->unpack_flock( $buf );
235 17         90 $self->{ errno } = $self->{ error } = undef;
236             } else {
237 0         0 $self->get_error( $self->{ errno } = $! + 0 );
238             }
239              
240 17         89 return $ret;
241             }
242              
243              
244             ###########################################################
245             # Method for packing the data from the 'flock_struct' into a
246             # binary blob to be passed to fcntl().
247              
248             sub pack_flock {
249 17     17 0 34 my $self = shift;
250 17         60 my @args;
251 17         330 push @args, $self->{ $_ } for @member_list;
252 17         1136 return pack $packstr, @args;
253             }
254              
255              
256             ###########################################################
257             # Method for unpacking the binary blob received from a call of
258             # fcntl() into the 'flock_struct'.
259              
260             sub unpack_flock {
261 17     17 0 31 my ( $self, $data ) = @_;
262 17         142 my @res = unpack $packstr, $data;
263 17         1502 $self->{ $_ } = shift @res for @member_list;
264             }
265              
266              
267             =cut
268              
269              
270             1;
271              
272              
273             # Local variables:
274             # tab-width: 4
275             # indent-tabs-mode: nil
276             # End: