File Coverage

blib/lib/Proc/Spawn.pm
Criterion Covered Total %
statement 53 73 72.6
branch 11 26 42.3
condition n/a
subroutine 6 6 100.0
pod 0 2 0.0
total 70 107 65.4


line stmt bran cond sub pod time code
1              
2             require 5.6.0;
3             package Proc::Spawn;
4 3     3   3702 use strict;
  3         9  
  3         186  
5 3     3   2829 use POSIX;
  3         47496  
  3         51  
6 3     3   58278 use IO;
  3         8133  
  3         24  
7 3     3   343119 use IO::Pty;
  3         28629  
  3         3021  
8              
9             ## Module Version
10             our $VERSION = 1.03;
11              
12             require Exporter;
13             our @ISA = qw(Exporter);
14             our @EXPORT = qw(spawn spawn_pty);
15              
16              
17             # Spawn using a pty
18             #
19             # Use for running telnet/login/ftp and other programs which
20             # communicate with the user by opening /dev/tty.
21             #
22             # ($pid, $pty_fh) = spawn_pty(ARGS);
23             #
24             # Where ARGS are one of:
25             # "command and arguments"
26             # ["command","and","arguments"]
27             #
28             sub spawn_pty ($) {
29 2     2 0 392295 my ($cmd) = @_;
30              
31             # Get a pty to use for stdio
32 2         54 my $pty = new IO::Pty;
33 2 50       3375 die "Cannot find a pty\n" unless defined $pty;
34 2         14 $pty->autoflush(1);
35              
36             # Create a child to exec the command
37 2         5240 my $pid = fork;
38 2 50       223 die "Cannot fork: $!\n" unless defined $pid;
39              
40 2 50       54 unless ( $pid ) { # Child
41 0 0       0 &POSIX::setsid() or die "Failed to setsid: $!\n";
42 0         0 my $tty = $pty->slave;
43 0         0 close $pty;
44              
45             # Close/Reopen stdio
46 0         0 my $tty_no = fileno($tty);
47 0         0 close STDIN; open(STDIN, "<&$tty_no");
  0         0  
48 0         0 close STDOUT; open(STDOUT, ">&$tty_no");
  0         0  
49 0         0 close STDERR; open(STDERR, ">&$tty_no");
  0         0  
50 0         0 close $tty;
51              
52             # Sanity check
53 0 0       0 exit 1 unless fileno(STDERR) == 2;
54              
55             # Run the command
56 0 0       0 if ( ref($cmd) =~ /ARRAY/ ) {
57 0         0 exec @$cmd;
58 0         0 exit 1;
59             } else {
60 0         0 exec $cmd;
61 0         0 exit 1;
62             }
63             }
64              
65             # Parent
66 2         431 return ($pid, $pty);
67             }
68              
69              
70             # Spawn using pipes
71             #
72             # This should be used for programs which do not open /dev/tty, to
73             # avoid wasting ptys.
74             #
75             # ($pid, $in_fh, $out_fh, $err_fh) = spawn(ARGS);
76             #
77             # Where ARGS are one of:
78             # "command and arguments"
79             # ["command","and","arguments"]
80             #
81             sub spawn ($) {
82 5     5 0 949864 my ($cmd) = @_;
83              
84             # Create pipes to use for stdio
85 5         108 my ( $inC, $inP ) = POSIX::pipe();
86 5 50       29 die "Cannot create pipe: $!\n" unless defined $inC;
87 5         45 my ($outP, $outC) = POSIX::pipe();
88 5 50       20 die "Cannot create pipe: $!\n" unless defined $outP;
89 5         48 my ($errP, $errC) = POSIX::pipe();
90 5 50       20 die "Cannot create pipe: $!\n" unless defined $errP;
91              
92             # Create a child to exec the command
93 5         8083 my $pid = fork;
94 5 50       276 die "Cannot fork: $!\n" unless defined $pid;
95              
96 5 100       112 unless ( $pid ) { # Child
97             # Close shared stdio
98 2         248 close STDIN;
99 2         58 close STDOUT;
100 2         698 close STDERR;
101              
102             # Open stdio on pipes
103 2         307 open(STDIN, "<&$inC");
104 2         79 open(STDOUT,">&$outC");
105 2         43 open(STDERR,">&$errC");
106              
107             # Sanity check
108 2 50       65 die "Stdio not opened properly\n" unless fileno(STDERR) == 2;
109              
110             # Close unneeded filehandles
111 2         247 POSIX::close($inC);
112 2         38 POSIX::close($outC);
113 2         22 POSIX::close($errC);
114 2         19 POSIX::close($inP);
115 2         24 POSIX::close($outP);
116 2         120 POSIX::close($errP);
117              
118             # Run the command
119 2 50       86 if ( ref($cmd) =~ /ARRAY/ ) {
120 0         0 exec @$cmd;
121 0         0 die "Cannot exec @$cmd: $!\n";
122             } else {
123 2         0 exec $cmd;
124 0         0 die "Cannot exec $cmd: $!\n";
125             }
126             }
127              
128             # Parent
129 3         170 POSIX::close($inC);
130 3         23 POSIX::close($outC);
131 3         29 POSIX::close($errC);
132              
133 3         305 $inP = new_from_fd IO::Handle($inP, 'w');
134 3         1278 $outP = new_from_fd IO::Handle($outP, 'r');
135 3         434 $errP = new_from_fd IO::Handle($errP, 'r');
136              
137 3         299 $inP->autoflush(1);
138 3         521 return ($pid, $inP, $outP, $errP);
139             }
140              
141             1;
142              
143             __END__