File Coverage

blib/lib/IO/Pty/Easy.pm
Criterion Covered Total %
statement 163 185 88.1
branch 45 66 68.1
condition 2 6 33.3
subroutine 20 21 95.2
pod 10 10 100.0
total 240 288 83.3


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