more explicit ipc to work around child-exec issues
[scpubgit/System-Introspector.git] / lib / System / Introspector / Util.pm
1 use strictures 1;
2
3 package System::Introspector::Util;
4 use Exporter 'import';
5 use IPC::Run qw( run );
6 use IPC::Open2;
7 use File::Spec;
8 use Scalar::Util qw( blessed );
9
10 our @EXPORT_OK = qw(
11     handle_from_command
12     handle_from_file
13     output_from_command
14     output_from_file
15     lines_from_command
16     files_from_dir
17     transform_exceptions
18 );
19
20 do {
21     package System::Introspector::_Exception;
22     use Moo;
23     has message => (is => 'ro');
24 };
25
26 sub fail { die System::Introspector::_Exception->new(message => shift) }
27 sub is_report_exception { ref(shift) eq 'System::Introspector::_Exception' }
28
29 sub files_from_dir {
30     my ($dir) = @_;
31     my $dh;
32     opendir $dh, $dir
33         or fail "Unable to read directory $dir: $!";
34     my @files;
35     while (defined( my $item = readdir $dh )) {
36         next if -d "$dir/$item";
37         push @files, $item;
38     }
39     return @files;
40 }
41
42 sub transform_exceptions (&) {
43     my ($code) = @_;
44     my $result = eval { $code->() };
45     if (my $error = $@) {
46         return { error => $error->message }
47             if is_report_exception $error;
48         die $@;
49     }
50     return $result;
51 }
52
53 sub output_from_command {
54     my ($command, $in) = @_;
55     $in = ''
56         unless defined $in;
57     my ($out, $err) = ('', '');
58     my $ok = run($command, \$in, \$out, \$err);
59     return $out, $err, $ok
60         if wantarray;
61     $command = join ' ', @$command
62         if ref $command;
63     fail "Error running command ($command): $err"
64         unless $ok;
65     return $out;
66 }
67
68 sub lines_from_command {
69     my ($command) = @_;
70     my $output = output_from_command $command;
71     chomp $output;
72     return split m{\n}, $output;
73 }
74
75 sub handle_from_command {
76     my ($command) = @_;
77     my $pipe;
78     local $@;
79     my $ok = eval {
80         my $out;
81         my $child_pid = open2($out, File::Spec->devnull, $command);
82         my @lines = <$out>;
83         waitpid $child_pid, 0;
84         my $content = join '', @lines;
85         my $status = $? >> 8;
86         open $pipe, '<', \$content;
87         1;
88     };
89     unless ($ok) {
90         my $err = $@;
91         die $err
92             if blessed($err) and $err->isa('System::Introspector::_Exception');
93         fail "Error from command '$command': $err";
94     }
95     return $pipe;
96 }
97
98 sub handle_from_file {
99     my ($file) = @_;
100     open my $fh, '<', $file
101         or fail "Unable to read $file: $!";
102     return $fh;
103 }
104
105 sub output_from_file {
106     my ($file) = @_;
107     my $fh = handle_from_file $file;
108     return <$fh>
109         if wantarray;
110     return do { local $/; <$fh> };
111 }
112
113 1;