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__ |