File Coverage

blib/lib/IO/Pty/Easy.pm
Criterion Covered Total %
statement 159 184 86.4
branch 41 66 62.1
condition 2 6 33.3
subroutine 19 20 95.0
pod 10 10 100.0
total 231 286 80.7


line stmt bran cond sub pod time code
1             package IO::Pty::Easy;
2             our $AUTHORITY = 'cpan:DOY';
3             $IO::Pty::Easy::VERSION = '0.10';
4 4     4   51078 use warnings;
  4         5  
  4         113  
5 4     4   13 use strict;
  4         4  
  4         66  
6             # ABSTRACT: Easy interface to IO::Pty
7              
8 4     4   9 use Carp;
  4         7  
  4         253  
9 4     4   1650 use POSIX ();
  4         22653  
  4         131  
10 4     4   29 use Scalar::Util qw(weaken);
  4         4  
  4         407  
11              
12 4     4   25 use base 'IO::Pty';
  4         7  
  4         2254  
13              
14              
15              
16             sub new {
17 5     5 1 62 my $class = shift;
18 5         12 my %args = @_;
19              
20 5         8 my $handle_pty_size = 1;
21             $handle_pty_size = delete $args{handle_pty_size}
22 5 50       17 if exists $args{handle_pty_size};
23 5 50       102 $handle_pty_size = 0 unless POSIX::isatty(*STDIN);
24 5         763 my $def_max_read_chars = 8192;
25             $def_max_read_chars = delete $args{def_max_read_chars}
26 5 50       23 if exists $args{def_max_read_chars};
27 5         6 my $raw = 1;
28             $raw = delete $args{raw}
29 5 50       14 if exists $args{raw};
30              
31 5         47 my $self = $class->SUPER::new(%args);
32 5         3462 bless $self, $class;
33 5         19 $self->handle_pty_size($handle_pty_size);
34 5         14 $self->def_max_read_chars($def_max_read_chars);
35 5         5 ${*{$self}}{io_pty_easy_raw} = $raw;
  5         5  
  5         11  
36 5         5 ${*{$self}}{io_pty_easy_final_output} = '';
  5         5  
  5         15  
37 5         5 ${*{$self}}{io_pty_easy_did_handle_pty_size} = 0;
  5         51  
  5         12  
38              
39 5         15 return $self;
40             }
41              
42              
43             sub spawn {
44 6     6 1 18 my $self = shift;
45 6         36 my $slave = $self->slave;
46              
47 6 50       409 croak "Attempt to spawn a subprocess when one is already running"
48             if $self->is_active;
49              
50             # set up a pipe to use for keeping track of the child process during exec
51 6         9 my ($readp, $writep);
52 6 50       123 unless (pipe($readp, $writep)) {
53 0         0 croak "Failed to create a pipe";
54             }
55 6         22 $writep->autoflush(1);
56              
57             # fork a child process
58             # if the exec fails, signal the parent by sending the errno across the pipe
59             # if the exec succeeds, perl will close the pipe, and the sysread will
60             # return due to EOF
61 6         4345 ${*{$self}}{io_pty_easy_pid} = fork;
  6         62  
  6         257  
62 6 100       113 unless ($self->pid) {
63 3         85 close $readp;
64 3         215 $self->make_slave_controlling_terminal;
65 3         1579 close $self;
66 3 50       34 $slave->clone_winsize_from(\*STDIN) if $self->handle_pty_size;
67 3 50       13 $slave->set_raw if ${*{$self}}{io_pty_easy_raw};
  3         6  
  3         98  
68             # reopen the standard file descriptors in the child to point to the
69             # pty rather than wherever they have been pointing during the script's
70             # execution
71 3 50       817 open(STDIN, '<&', $slave->fileno)
72             or carp "Couldn't reopen STDIN for reading";
73 3 50       163 open(STDOUT, '>&', $slave->fileno)
74             or carp "Couldn't reopen STDOUT for writing";
75 3 50       58 open(STDERR, '>&', $slave->fileno)
76             or carp "Couldn't reopen STDERR for writing";
77 3         48 close $slave;
78 3         9 { exec(@_) };
  3         0  
79 0         0 print $writep $! + 0;
80 0         0 carp "Cannot exec(@_): $!";
81 0         0 exit 1;
82             }
83              
84 3         50 close $writep;
85 3         114 $self->close_slave;
86             # this sysread will block until either we get an EOF from the other end of
87             # the pipe being closed due to the exec, or until the child process sends
88             # us the errno of the exec call after it fails
89 3         48 my $errno;
90 3         1895991 my $read_bytes = sysread($readp, $errno, 256);
91 3 50       35 unless (defined $read_bytes) {
92             # XXX: should alarm here and follow up with SIGKILL if the process
93             # refuses to die
94 0         0 kill TERM => $self->pid;
95 0         0 close $readp;
96 0         0 $self->_wait_for_inactive;
97 0         0 croak "Cannot sync with child: $!";
98             }
99 3         61 close $readp;
100 3 50       14 if ($read_bytes > 0) {
101 0         0 $errno = $errno + 0;
102 0         0 $self->_wait_for_inactive;
103 0         0 croak "Cannot exec(@_): $errno";
104             }
105              
106 3 50       28 if ($self->handle_pty_size) {
107 0         0 my $weakself = weaken($self);
108             $SIG{WINCH} = sub {
109 0 0   0   0 return unless $weakself;
110 0         0 $weakself->slave->clone_winsize_from(\*STDIN);
111 0 0       0 kill WINCH => $weakself->pid if $weakself->is_active;
112 0         0 };
113 0         0 ${*{$self}}{io_pty_easy_did_handle_pty_size} = 1;
  0         0  
  0         0  
114             }
115             }
116              
117              
118             sub read {
119 4     4 1 20 my $self = shift;
120 4         10 my ($timeout, $max_chars) = @_;
121 4   33     30 $max_chars ||= $self->def_max_read_chars;
122              
123 4         8 my $rin = '';
124 4         14 vec($rin, fileno($self), 1) = 1;
125 4         204758 my $nfound = select($rin, undef, undef, $timeout);
126 4         14 my $buf;
127 4 100       20 if ($nfound > 0) {
128 2         24 my $nchars = sysread($self, $buf, $max_chars);
129 2 50 33     18 $buf = '' if defined($nchars) && $nchars == 0;
130             }
131 4 50       6 if (length(${*{$self}}{io_pty_easy_final_output}) > 0) {
  4         6  
  4         46  
132 4     4   54598 no warnings 'uninitialized';
  4         8  
  4         2167  
133 0         0 $buf = ${*{$self}}{io_pty_easy_final_output} . $buf;
  0         0  
  0         0  
134 0         0 ${*{$self}}{io_pty_easy_final_output} = '';
  0         0  
  0         0  
135             }
136 4         76 return $buf;
137             }
138              
139              
140             sub write {
141 4     4 1 38 my $self = shift;
142 4         12 my ($text, $timeout) = @_;
143              
144 4         8 my $win = '';
145 4         31 vec($win, fileno($self), 1) = 1;
146 4         100210 my $nfound = select(undef, $win, undef, $timeout);
147 4         7 my $nchars;
148 4 100       16 if ($nfound > 0) {
149 3         999410 $nchars = syswrite($self, $text);
150             }
151 4         61 return $nchars;
152             }
153              
154              
155             sub is_active {
156 23     23 1 27 my $self = shift;
157              
158 23 100       48 return 0 unless defined $self->pid;
159              
160 9 50       34 if (defined(my $fd = fileno($self))) {
161             # XXX FreeBSD 7.0 will not allow a session leader to exit until the
162             # kernel tty output buffer is empty. Make it so.
163 9         15 my $rin = '';
164 9         32 vec($rin, $fd, 1) = 1;
165 9         54 my $nfound = select($rin, undef, undef, 0);
166 9 100       25 if ($nfound > 0) {
167 3         4 sysread($self, ${*{$self}}{io_pty_easy_final_output},
  3         19  
168             $self->def_max_read_chars,
169 3         8 length ${*{$self}}{io_pty_easy_final_output});
  3         5  
  3         42  
170             }
171             }
172              
173 9         20 my $active = kill 0 => $self->pid;
174 9 50       22 if ($active) {
175 9         19 my $pid = waitpid($self->pid, POSIX::WNOHANG());
176 9 100       15 $active = 0 if $pid == $self->pid;
177             }
178 9 100       22 if (!$active) {
179             $SIG{WINCH} = 'DEFAULT'
180 3 50       3 if ${*{$self}}{io_pty_easy_did_handle_pty_size};
  3         3  
  3         11  
181 3         4 ${*{$self}}{io_pty_easy_did_handle_pty_size} = 0;
  3         4  
  3         5  
182 3         5 delete ${*{$self}}{io_pty_easy_pid};
  3         3  
  3         7  
183             }
184 9         30337 return $active;
185             }
186              
187              
188             sub kill {
189 7     7 1 12 my $self = shift;
190 7         11 my ($sig, $non_blocking) = @_;
191 7 50       27 $sig = "TERM" unless defined $sig;
192              
193 7         6 my $kills;
194 7 100       22 $kills = kill $sig => $self->pid if $self->is_active;
195 7 50       40 $self->_wait_for_inactive unless $non_blocking;
196              
197 7         82 return $kills;
198             }
199              
200              
201             sub close {
202 4     4 1 9 my $self = shift;
203              
204 4         102 close $self;
205 4         16 $self->kill;
206             }
207              
208              
209             sub handle_pty_size {
210 11     11 1 22 my $self = shift;
211 11 100       35 ${*{$self}}{io_pty_easy_handle_pty_size} = $_[0] if @_;
  5         5  
  5         11  
212 11         17 ${*{$self}}{io_pty_easy_handle_pty_size};
  11         8  
  11         132  
213             }
214              
215              
216             sub def_max_read_chars {
217 12     12 1 16 my $self = shift;
218 12 100       31 ${*{$self}}{io_pty_easy_def_max_read_chars} = $_[0] if @_;
  5         5  
  5         9  
219 12         16 ${*{$self}}{io_pty_easy_def_max_read_chars};
  12         9  
  12         35  
220             }
221              
222              
223             sub pid {
224 59     59 1 57 my $self = shift;
225 59         41 ${*{$self}}{io_pty_easy_pid};
  59         45  
  59         384  
226             }
227              
228             sub _wait_for_inactive {
229 7     7   11 my $self = shift;
230              
231 7         13 select(undef, undef, undef, 0.01) while $self->is_active;
232             }
233              
234             sub DESTROY {
235 2     2   397 my $self = shift;
236 2         3 local $@;
237 2         7 local $?;
238 2         6 $self->close;
239             }
240              
241              
242             1;
243              
244             __END__