File Coverage

blib/lib/Unix/ScriptUtil.pm
Criterion Covered Total %
statement 52 53 98.1
branch 23 36 63.8
condition 6 11 54.5
subroutine 15 15 100.0
pod 9 9 100.0
total 105 124 84.6


line stmt bran cond sub pod time code
1             # -*- Perl -*-
2             #
3             # some utility routines for scripts
4              
5             package Unix::ScriptUtil;
6             our $VERSION = '0.02';
7              
8 4     4   367929 use 5.10.0;
  4         33  
9 4     4   17 use strict;
  4         5  
  4         68  
10 4     4   16 use warnings;
  4         26  
  4         273  
11 4     4   22 use Carp qw(croak);
  4         5  
  4         186  
12 4     4   1661 use POSIX qw(setsid);
  4         20984  
  4         17  
13              
14             require Exporter;
15             our @ISA = qw(Exporter);
16             our @EXPORT_OK =
17             qw(cd brun diropen fileopen pipe_close pipe_from pipe_to solitary timeout);
18              
19 6 100   6 1 3906 sub cd { chdir($_[0]) or croak("chdir $_[0] failed: $!") }
20 3 50   3 1 2763 sub brun { system({ $_[0] } @_) == 0 or croak("system '@_' failed: $?") }
  3         11571  
21              
22             sub diropen {
23 3 50   3 1 2748 opendir(my $dh, $_[0]) or croak("opendir $_[0] failed: $!");
24 3         27 return $dh;
25             }
26              
27             # fileopen - kind of like what File::Open provides?
28             {
29             # more or less stolen from fopen(3)
30             my %fomap = (
31             'r' => '<',
32             'w' => '>',
33             'r+' => '+<',
34             'w+' => '+>',
35             'a' => '>>',
36             'a+' => '+>>',
37             );
38              
39             sub fileopen {
40 22     22 1 25348 my ($file, $how) = @_;
41 22   100     361 my $way = $fomap{ $how // 'r' } // croak("unknown open method $how");
      66        
42 19 50       2701 open(my $fh, $way, $file) or croak("open $how '$file' failed: $!");
43 19         142 return $fh;
44             }
45             }
46              
47             sub pipe_close {
48 3 50   3 1 22104 close($_[0]) or croak($! ? "close failed: $!" : "pipe command failed: $?");
    50          
49             }
50              
51             # NOTE these next two may invoke sh in certain cases
52             sub pipe_from {
53 3 50   3 1 7446 open(my $fh, '-|', @_) or croak("exec '@_' failed: $!");
54 3         318 return $fh;
55             }
56              
57             sub pipe_to {
58 9 100   9 1 55167 open(my $fh, '|-', @_) or croak("exec '@_' failed: $!");
59 6         618 return $fh;
60             }
61              
62             # similar to disown (as seen in ZSH) but also with a chdir because where
63             # the process lives probably needs to be thought about (where *.core
64             # files may go, could be on a network mount, ...). the double-fork
65             # prevents the chdir/etc from altering the original process and should
66             # help disassociate things from any controlling terminal
67             sub solitary {
68 3     3 1 3312 my $dir = shift;
69 3   33     18971 my $pid = fork() // croak("fork failed: $!"); # child 1
70 3 100       145 if ($pid == 0) {
71 2 50       70 chdir($dir) or croak("chdir $dir failed: $!");
72 2 50       140 open(*STDIN, '<', '/dev/null') or croak("can read /dev/null: $!\n");
73 2 50       90 open(*STDOUT, '>', '/dev/null') or croak("can write /dev/null: $!\n");
74 2   33     1246 my $pid = fork() // croak("fork failed: $!\n"); # child 2
75 2 100       557 exit if $pid; # child 1
76              
77             # NOTE beyond here original process will have no idea whether
78             # the solitary process (child 2) fails any of these
79 1 50       76 setsid() == -1 and die("setsid failed: $!\n");
80 1 50       162 open(*STDERR, '>&', *STDOUT) or die("dup failed: $!\n");
81 1         15 exec { $_[0] } @_;
  1         0  
82 0         0 die("exec failed: $!\n");
83             } else {
84 1         224908 wait(); # for child 1
85 1 50       105 croak("child error: $?") if $? != 0;
86             }
87             }
88              
89             sub timeout {
90 2     2 1 2450 my ($duration, $fn) = @_;
91 2         3 eval {
92 2     1   72 local $SIG{ALRM} = sub { die("timeout\n") };
  1         76  
93 2         27 alarm($duration);
94 2         34 $fn->();
95 2         1000112 alarm(0);
96             };
97 2 100       29 if ($@) {
98 1 50       14 die unless $@ eq "timeout\n";
99 1         53 croak($@);
100             }
101             }
102              
103             1;
104             __END__