File Coverage

blib/lib/Archive/Tar/Wrapper/IPC/Cmd.pm
Criterion Covered Total %
statement 197 250 78.8
branch 89 158 56.3
condition 9 18 50.0
subroutine 29 33 87.8
pod 0 19 0.0
total 324 478 67.7


line stmt bran cond sub pod time code
1 6     6   824589 use strict;
  6         16  
  6         303  
2 6     6   34 use warnings;
  6         13  
  6         334  
3             package Archive::Tar::Wrapper::IPC::Cmd;
4             # ABSTRACT: Archive-Tar-Wrapper minus IPC::Run, IO::Pty
5 6     6   34 use File::Temp qw(tempdir);
  6         14  
  6         439  
6 6     6   1392 use Log::Log4perl qw(:easy);
  6         74688  
  6         58  
7 6     6   23184 use File::Spec::Functions;
  6         5860  
  6         709  
8 6     6   42 use File::Spec;
  6         12  
  6         122  
9 6     6   32 use File::Path;
  6         7  
  6         367  
10 6     6   6313 use File::Copy;
  6         16457  
  6         426  
11 6     6   44 use File::Find;
  6         13  
  6         331  
12 6     6   81 use File::Basename;
  6         15  
  6         396  
13 6     6   8219 use IPC::Cmd qw(run);
  6         453844  
  6         587  
14 6     6   78 use Cwd;
  6         14  
  6         63924  
15              
16             our $VERSION = "0.22";
17              
18             ###########################################
19             sub new {
20             ###########################################
21 13     13 0 24416 my($class, %options) = @_;
22              
23 13         324 my $self = {
24             tar => undef,
25             tmpdir => undef,
26             tar_read_options => '',
27             tar_write_options => '',
28             tar_gnu_read_options => [],
29             dirs => 0,
30             max_cmd_line_args => 512,
31             ramdisk => undef,
32             %options,
33             };
34              
35 13         1119 bless $self, $class;
36              
37 13 50       190 $self->{tar} = bin_find("tar") unless defined $self->{tar};
38 13 50       75 $self->{tar} = bin_find("gtar") unless defined $self->{tar};
39              
40 13 50       57 if( ! defined $self->{tar} ) {
41 0         0 LOGDIE "tar not found in PATH, please specify location";
42             }
43              
44 13 50       54 if(defined $self->{ramdisk}) {
45 0         0 my $rc = $self->ramdisk_mount( %{ $self->{ramdisk} } );
  0         0  
46 0 0       0 if(!$rc) {
47 0         0 LOGDIE "Mounting ramdisk failed";
48             }
49 0         0 $self->{tmpdir} = $self->{ramdisk}->{tmpdir};
50             } else {
51 13 100       171 $self->{tmpdir} = tempdir($self->{tmpdir} ?
52             (DIR => $self->{tmpdir}) : ());
53             }
54              
55 13         12632 $self->{tardir} = File::Spec->catfile($self->{tmpdir}, "tar");
56 13 50       3637 mkpath [$self->{tardir}], 0, 0755 or
57             LOGDIE "Cannot mkpath $self->{tardir} ($!)";
58              
59 13         75 $self->{objdir} = tempdir();
60              
61 13         6630 return $self;
62             }
63              
64             ###########################################
65             sub tardir {
66             ###########################################
67 0     0 0 0 my($self) = @_;
68              
69 0         0 return $self->{tardir};
70             }
71              
72             ###########################################
73             sub read {
74             ###########################################
75 12     12 0 2961 my($self, $tarfile, @files) = @_;
76              
77 12         130 my $cwd = getcwd();
78              
79 12 100       201 unless(File::Spec::Functions::file_name_is_absolute($tarfile)) {
80 9         212 $tarfile = File::Spec::Functions::rel2abs($tarfile, $cwd);
81             }
82              
83 12 50       747 chdir $self->{tardir} or
84             LOGDIE "Cannot chdir to $self->{tardir}";
85              
86 12         32 my $compr_opt = "";
87 12 100       66 $compr_opt = "z" if $self->is_compressed($tarfile);
88              
89 12         74 my $cmd = [$self->{tar}, "${compr_opt}x$self->{tar_read_options}",
90 12         51 @{$self->{tar_gnu_read_options}},
91             "-f", $tarfile, @files];
92              
93 12         213 DEBUG "Running @$cmd";
94              
95 12         222 my( $success, $error_message, $full_buf, $out, $err ) =
96             run( command => $cmd, verbose => 0 );
97 12 100       404323 if(!$success) {
98 1         33 ERROR "@$cmd failed: $err";
99 1 50       150 chdir $cwd or LOGDIE "Cannot chdir to $cwd";
100 1         28 return undef;
101             }
102              
103 11 50       396 WARN $err if $err;
104              
105 11 50       1018 chdir $cwd or LOGDIE "Cannot chdir to $cwd";
106              
107 11         357 return 1;
108             }
109              
110             ###########################################
111             sub is_compressed {
112             ###########################################
113 12     12 0 31 my($self, $tarfile) = @_;
114              
115 12 100       140 return 1 if $tarfile =~ /\.t?gz$/i;
116              
117             # Sloppy check for gzip files
118 9 50       1587 open FILE, "<$tarfile" or die "Cannot open $tarfile";
119 9         42 binmode FILE;
120 9 50       161 my $read = sysread(FILE, my $two, 2, 0) or die "Cannot sysread";
121 9         110 close FILE;
122 9 50 33     103 return 1 if
123             ord(substr($two, 0, 1)) eq 0x1F and
124             ord(substr($two, 1, 1)) eq 0x8B;
125              
126 9         52 return 0;
127             }
128              
129             ###########################################
130             sub locate {
131             ###########################################
132 13     13 0 10625 my($self, $rel_path) = @_;
133              
134 13         348 my $real_path = File::Spec->catfile($self->{tardir}, $rel_path);
135              
136 13 100       1248 if(-e $real_path) {
137 12         91 DEBUG "$real_path exists";
138 12         153 return $real_path;
139             }
140 1         7 DEBUG "$real_path doesn't exist";
141              
142 1         16 WARN "$rel_path not found in tarball";
143 1         11 return undef;
144             }
145              
146             ###########################################
147             sub add {
148             ###########################################
149 10     10 0 1405 my($self, $rel_path, $path_or_stringref, $opts) = @_;
150            
151 10 100       40 if($opts) {
152 2 50 33     49 if(!ref($opts) or ref($opts) ne 'HASH') {
153 0         0 LOGDIE "Option parameter given to add() not a hashref.";
154             }
155             }
156              
157 10 100       185 my $perm = $opts->{perm} if defined $opts->{perm};
158 10 50       42 my $uid = $opts->{uid} if defined $opts->{uid};
159 10 50       37 my $gid = $opts->{gid} if defined $opts->{gid};
160 10 100       48 my $binmode = $opts->{binmode} if defined $opts->{binmode};
161              
162 10         620 my $target = File::Spec->catfile($self->{tardir}, $rel_path);
163 10         523 my $target_dir = dirname($target);
164              
165 10 100       259 if( ! -d $target_dir ) {
166 5 100       25 if( ref($path_or_stringref) ) {
167 1         66 $self->add( dirname( $rel_path ), dirname( $target_dir ) );
168             } else {
169 4         241 $self->add( dirname( $rel_path ), dirname( $path_or_stringref ) );
170             }
171             }
172              
173 10 100       187 if(ref($path_or_stringref)) {
    100          
174 2 50       192 open FILE, ">$target" or LOGDIE "Can't open $target ($!)";
175 2 100       17 if(defined $binmode) {
176 1         11 binmode FILE, $binmode;
177             }
178 2         51 print FILE $$path_or_stringref;
179 2         85 close FILE;
180             } elsif( -d $path_or_stringref ) {
181             # perms will be fixed further down
182 5 50       1270 mkpath($target, 0, 0755) unless -d $target;
183             } else {
184 3 50       33 copy $path_or_stringref, $target or
185             LOGDIE "Can't copy $path_or_stringref to $target ($!)";
186             }
187              
188 10 50       1505 if(defined $uid) {
189 0 0       0 chown $uid, -1, $target or
190             LOGDIE "Can't chown $target uid to $uid ($!)";
191             }
192              
193 10 50       26 if(defined $gid) {
194 0 0       0 chown -1, $gid, $target or
195             LOGDIE "Can't chown $target gid to $gid ($!)";
196             }
197              
198 10 100       30 if(defined $perm) {
199 1 50       44 chmod $perm, $target or
200             LOGDIE "Can't chmod $target to $perm ($!)";
201             }
202              
203 10 100 33     110 if(!defined $uid and
      33        
      66        
204             !defined $gid and
205             !defined $perm and
206             !ref($path_or_stringref)) {
207 7 50       28 perm_cp($path_or_stringref, $target) or
208             LOGDIE "Can't perm_cp $path_or_stringref to $target ($!)";
209             }
210              
211 10         57 1;
212             }
213              
214             ######################################
215             sub perm_cp {
216             ######################################
217             # Lifted from Ben Okopnik's
218             # http://www.linuxgazette.com/issue87/misc/tips/cpmod.pl.txt
219              
220 7     7 0 23 my $perms = perm_get($_[0]);
221 7         57 perm_set($_[1], $perms);
222             }
223              
224             ######################################
225             sub perm_get {
226             ######################################
227 7     7 0 20 my($filename) = @_;
228              
229 7 50       188 my @stats = (stat $filename)[2,4,5] or
230             LOGDIE "Cannot stat $filename ($!)";
231              
232 7         29 return \@stats;
233             }
234              
235             ######################################
236             sub perm_set {
237             ######################################
238 7     7 0 24 my($filename, $perms) = @_;
239              
240 7 50       275 chown($perms->[1], $perms->[2], $filename) or
241             LOGDIE "Cannot chown $filename ($!)";
242 7 50       341 chmod($perms->[0] & 07777, $filename) or
243             LOGDIE "Cannot chmod $filename ($!)";
244             }
245              
246             ###########################################
247             sub remove {
248             ###########################################
249 0     0 0 0 my($self, $rel_path) = @_;
250              
251 0         0 my $target = File::Spec->catfile($self->{tardir}, $rel_path);
252              
253 0 0       0 rmtree($target) or LOGDIE "Can't rmtree $target ($!)";
254             }
255              
256             ###########################################
257             sub list_all {
258             ###########################################
259 4     4 0 37 my($self) = @_;
260              
261 4         17 my @entries = ();
262              
263 4         86 $self->list_reset();
264              
265 4         24 while(my $entry = $self->list_next()) {
266 12         166 push @entries, $entry;
267             }
268              
269 4         48 return \@entries;
270             }
271              
272             ###########################################
273             sub list_reset {
274             ###########################################
275 6     6 0 6478 my($self) = @_;
276              
277 6         165 my $list_file = File::Spec->catfile($self->{objdir}, "list");
278 6 50       1549 open FILE, ">$list_file" or LOGDIE "Can't open $list_file";
279              
280 6         63 my $cwd = getcwd();
281 6 50       240 chdir $self->{tardir} or LOGDIE "Can't chdir to $self->{tardir} ($!)";
282              
283             find(sub {
284 31     31   322 my $entry = $File::Find::name;
285 31         234 $entry =~ s#^\./##;
286 31 50       733 my $type = (-d $_ ? "d" :
    100          
287             -l $_ ? "l" :
288             "f"
289             );
290 31         3462 print FILE "$type $entry\n";
291 6         1452 }, ".");
292              
293 6 50       214 chdir $cwd or LOGDIE "Can't chdir to $cwd ($!)";
294              
295 6         862 close FILE;
296              
297 6         122 $self->offset(0);
298             }
299              
300             ###########################################
301             sub list_next {
302             ###########################################
303 25     25 0 102 my($self) = @_;
304              
305 25         69 my $offset = $self->offset();
306              
307 25         252 my $list_file = File::Spec->catfile($self->{objdir}, "list");
308 25 50       2590 open FILE, "<$list_file" or LOGDIE "Can't open $list_file";
309 25         194 seek FILE, $offset, 0;
310              
311 25         34 { my $line = ;
  37         249  
312              
313 37 100       145 return undef unless defined $line;
314              
315 31         50 chomp $line;
316 31         237 my($type, $entry) = split / /, $line, 2;
317 31 100 100     299 redo if $type eq "d" and ! $self->{dirs};
318 19         256 $self->offset(tell FILE);
319 19         604 return [$entry, File::Spec->catfile($self->{tardir}, $entry),
320             $type];
321             }
322             }
323              
324             ###########################################
325             sub offset {
326             ###########################################
327 50     50 0 85 my($self, $new_offset) = @_;
328              
329 50         663 my $offset_file = File::Spec->catfile($self->{objdir}, "offset");
330              
331 50 100       292 if(defined $new_offset) {
332 25 50       4340 open FILE, ">$offset_file" or LOGDIE "Can't open $offset_file";
333 25         276 print FILE "$new_offset\n";
334 25         1739 close FILE;
335             }
336              
337 50 50       3662 open FILE, "<$offset_file" or LOGDIE "Can't open $offset_file (Did you call list_next() without a previous list_reset()?)";
338 50         804 my $offset = ;
339 50         80 chomp $offset;
340 50         166 return $offset;
341 0         0 close FILE;
342             }
343              
344             ###########################################
345             sub write {
346             ###########################################
347 4     4 0 2291 my($self, $tarfile, $compress) = @_;
348              
349 4         50 my $cwd = getcwd();
350 4 50       118 chdir $self->{tardir} or LOGDIE "Can't chdir to $self->{tardir} ($!)";
351              
352 4 50       39 unless(File::Spec::Functions::file_name_is_absolute($tarfile)) {
353 0         0 $tarfile = File::Spec::Functions::rel2abs($tarfile, $cwd);
354             }
355              
356 4         84 my $compr_opt = "";
357 4 100       18 $compr_opt = "z" if $compress;
358              
359 4 50       170 opendir DIR, "." or LOGDIE "Cannot open $self->{tardir}";
360 4         158 my @top_entries = grep { $_ !~ /^\.\.?$/ } readdir DIR;
  13         61  
361 4         73 closedir DIR;
362              
363 4         10 my $cmd;
364              
365 4 50       22 if(@top_entries > $self->{max_cmd_line_args}) {
366 0         0 my $filelist_file = $self->{tmpdir}."/file-list";
367 0 0       0 open FLIST, ">$filelist_file" or
368             LOGDIE "Cannot open $filelist_file ($!)";
369 0         0 for(@top_entries) {
370 0         0 print FLIST "$_\n";
371             }
372 0         0 close FLIST;
373 0         0 $cmd = [$self->{tar}, "${compr_opt}cf$self->{tar_write_options}",
374             $tarfile, "-T", $filelist_file];
375             } else {
376 4         28 $cmd = [$self->{tar}, "${compr_opt}cf$self->{tar_write_options}",
377             $tarfile, @top_entries];
378             }
379              
380              
381 4         54 DEBUG "Running @$cmd";
382 4         71 my( $success, $error_message, $full_buf, $out, $err ) =
383             run( command => $cmd, verbose => 0 );
384 4 100       115439 if(!$success) {
385 1         24 ERROR "@$cmd failed: $err";
386 1 50       53 chdir $cwd or LOGDIE "Cannot chdir to $cwd";
387 1         25 return undef;
388             }
389              
390 3 50       70 WARN $err if $err;
391              
392 3 50       187 chdir $cwd or LOGDIE "Cannot chdir to $cwd";
393              
394 3         119 return 1;
395             }
396              
397             ###########################################
398             sub DESTROY {
399             ###########################################
400 13     13   7200 my($self) = @_;
401              
402 13 50       73 $self->ramdisk_unmount() if defined $self->{ramdisk};
403              
404 13 50       18656 rmtree($self->{objdir}) if defined $self->{objdir};
405 13 50       26630 rmtree($self->{tmpdir}) if defined $self->{tmpdir};
406             }
407              
408             ######################################
409             sub bin_find {
410             ######################################
411 13     13 0 85 my($exe) = @_;
412              
413 13         136 my @paths = split /:/, $ENV{PATH};
414              
415 13         278 push @paths,
416             "/usr/bin",
417             "/bin",
418             "/usr/sbin",
419             "/opt/bin",
420             "/ops/csw/bin",
421             ;
422              
423 13         39 for my $path ( @paths ) {
424 78         1000 my $full = File::Spec->catfile($path, $exe);
425 78 100       2645 return $full if -x $full;
426             }
427              
428 0         0 return undef;
429             }
430              
431             ###########################################
432             sub is_gnu {
433             ###########################################
434 1     1 0 19 my($self) = @_;
435              
436 1 50       6490 open PIPE, "$self->{tar} --version |" or
437             return 0;
438              
439 1         2030 my $output = join "\n", ;
440 1         313 close PIPE;
441              
442 1         47 return $output =~ /GNU/;
443             }
444              
445             ###########################################
446             sub ramdisk_mount {
447             ###########################################
448 0     0 0   my($self, %options) = @_;
449              
450             # mkdir -p /mnt/myramdisk
451             # mount -t tmpfs -o size=20m tmpfs /mnt/myramdisk
452              
453 0 0         $self->{mount} = bin_find("mount") unless $self->{mount};
454 0 0         $self->{umount} = bin_find("umount") unless $self->{umount};
455              
456 0           for (qw(mount umount)) {
457 0 0         if(!defined $self->{$_}) {
458 0           LOGWARN "No $_ command found in PATH";
459 0           return undef;
460             }
461             }
462              
463 0           $self->{ramdisk} = { %options };
464            
465 0 0         $self->{ramdisk}->{size} = "100m" unless
466             defined $self->{ramdisk}->{size};
467            
468 0 0         if(! defined $self->{ramdisk}->{tmpdir}) {
469 0           $self->{ramdisk}->{tmpdir} = tempdir( CLEANUP => 1 );
470             }
471            
472 0           my @cmd = ($self->{mount},
473             "-t", "tmpfs", "-o", "size=$self->{ramdisk}->{size}",
474             "tmpfs", $self->{ramdisk}->{tmpdir});
475              
476 0           INFO "Mounting ramdisk: @cmd";
477 0           my $rc = system( @cmd );
478            
479 0 0         if($rc) {
480 0           LOGWARN "Mount command '@cmd' failed: $?";
481 0           LOGWARN "Note that this only works on Linux and as root";
482 0           return;
483             }
484            
485 0           $self->{ramdisk}->{mounted} = 1;
486            
487 0           return 1;
488             }
489              
490             ###########################################
491             sub ramdisk_unmount {
492             ###########################################
493 0     0 0   my($self) = @_;
494              
495 0 0         return if !exists $self->{ramdisk}->{mounted};
496              
497 0           my @cmd = ($self->{umount}, $self->{ramdisk}->{tmpdir});
498              
499 0           INFO "Unmounting ramdisk: @cmd";
500              
501 0           my $rc = system( @cmd );
502            
503 0 0         if($rc) {
504 0           LOGWARN "Unmount command '@cmd' failed: $?";
505 0           return;
506             }
507              
508 0           delete $self->{ramdisk};
509 0           return 1;
510             }
511              
512             1;
513              
514             __END__