From 0b960e5d3cea20058ccd1e1bbd92d49dfffc325d Mon Sep 17 00:00:00 2001 From: Tom Feist Date: Wed, 13 Apr 2011 12:05:59 +0100 Subject: exec.pl: now mostly working with IPC::Open3. Needs cleanup --- feature-tests/exec.pl | 182 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 120 insertions(+), 62 deletions(-) diff --git a/feature-tests/exec.pl b/feature-tests/exec.pl index e268291..ca8c6a2 100644 --- a/feature-tests/exec.pl +++ b/feature-tests/exec.pl @@ -69,7 +69,7 @@ use Time::HiRes qw/sleep/; use IO::Handle; use IO::Pipe; use IPC::Open3; - +use Symbol qw/gensym geniosym/; use Data::Dumper; @@ -83,11 +83,14 @@ our %IRSSI = ( ); -my $forked = 0; +my $pid = 0; my $command; my $command_options; +my ($sin, $serr, $sout) = (new IO::Handle, new IO::Handle, new IO::Handle); +my ($stdout_tag, $stderr_tag); + sub parse_options { my ($args) = @_; @@ -96,7 +99,9 @@ sub parse_options { my $opt_hash = $options[0]; my $rest = $options[1]; - print Dumper($opt_hash); + $rest =~ s/^\s*(.*?)\s*$/$1/; # trim surrounding space. + + print Dumper([$opt_hash, $rest]); return ($opt_hash, $rest); } else { _error("Error parsing $command options"); @@ -104,64 +109,66 @@ sub parse_options { } } - +sub schedule_cleanup { + my $fd = shift; + Irssi::timeout_add_once(100, sub { $_[0]->close }, $fd); +} sub do_fork_and_exec { my ($options, $cmd) = @_; - my $stdout_pipe = IO::Pipe->new; - my $stderr_pipe = IO::Pipe->new; + #Irssi::timeout_add_once(100, sub { die }, {}); -# return if $forked; + return unless $cmd; - #my $pid = fork(); + #_msg("type of siin is %s, out is %s, err is %s", ref $sin, ref $sout, ref $serr); - if (not defined $pid) { - _error("Fork failed: $! Aborting"); - $_->close for $stdout_pipe->handles; - undef $stdout_pipe; - return; - } + $sin->autoflush; + $sout->autoflush; + $serr->autoflush; -# $forked = 1; + drop_privs(); - if ($pid > 0) { # this is the parent (Irssi) - my $tag; + $pid = open3($sin, $sout, $serr, $cmd); - Irssi::pidwait_add($pid); + # _msg("Pid %s, in: %s, out: %s, err: %s, cmd: %s", + # $pid, $sin, $sout, $serr, $cmd); + + # _msg("filenos, Pid %s, in: %s, out: %s, err: %s", + # $pid, $sin->fileno, $sout->fileno, $serr->fileno); + + if (not defined $pid) { + _error("open3 failed: $! Aborting"); + + $_->close for ($sin, $serr, $sout); + undef($_) for ($sin, $serr, $sout); - my $stdout_reader = $stdout_pipe->reader; - $stdout_reader->autoflush; + return; + } - my @args = ($stdout_reader, \$tag, $pid, $cmd, $options); - $tag = Irssi::input_add($stdout_reader->fileno, - Irssi::INPUT_READ, - \&child_output, - \@args); + # parent + if ($pid) { - } else { # child - # make up some data - block if we like. - drop_privs(); - my $stdout_fh = $stdout_pipe->writer; - $stdout_fh->autoflush; - my @data = qx/$cmd/; - my $retval = ${^CHILD_ERROR_NATIVE}; + eval { + my @out_args = ($sout, $cmd, $options); + $stdout_tag = Irssi::input_add( $sout->fileno, Irssi::INPUT_READ, + \&child_output, \@out_args); + die unless $stdout_tag; - $stdout_fh->print($_) for @data; + my @err_args = ($serr, $cmd, $options); + $stderr_tag = Irssi::input_add($serr->fileno, Irssi::INPUT_READ, + \&child_error, \@err_args); + die unless $stderr_tag; - my $done_str = "__DONE__$retval\n"; - if ($data[$#data] =~ m/\n$/) { - } else { - $done_str = "\n" . $done_str; - } - $stdout_fh->print($done_str); + }; - $stdout_fh->close; + Irssi::pidwait_add($pid); - POSIX::_exit(1); + die "input_add failed to initialise: $@" if $@; } } + sub drop_privs { my @temp = ($EUID, $EGID); my $orig_uid = $UID; @@ -177,40 +184,79 @@ sub drop_privs { unless $UID == $EUID && $GID eq $EGID; } -sub child_output { +sub child_error { my $args = shift; - my ($stdout_reader, $tag_ref, $pid, $cmd, $options) = @$args; + my ($stderr_reader, $cmd, $options) = @$args; - my $return_value = 0; + read_err_data_loop: + my $data = ''; + my $bytes_read = sysread($stderr_reader, $data, 256); + if (not defined $bytes_read) { + _error("stderr: sysread failed:: $!"); + + } elsif ($bytes_read == 0) { + _msg("stderr: sysread got EOF"); + + } elsif ($bytes_read < 256) { + # that's all, folks. + _msg("stderr: read %d bytes: %s", $bytes_read, $data); + } else { + # we maybe need to read some more + _msg("stderr: read %d bytes: %s, maybe more", $bytes_read, $data); + goto read_err_data_loop; + } + +} - while (defined(my $data = <$stdout_reader>)) { +sub sig_pidwait { + my ($pidwait, $status) = @_; + if ($pidwait == $pid) { + _msg("PID %d has terminated. Status %d (or maybe %d .... %d)", + $pidwait, $status, $?, ${^CHILD_ERROR_NATIVE} ); + $pid = 0; - chomp $data; + _msg('removing input stdout tag'); + Irssi::input_remove($stdout_tag); - # TODO: do we want to remove empty lines? - #return unless length $data; + _msg('removing input stderr tag'); + Irssi::input_remove($stderr_tag); - if ($data =~ m/^__DONE__(\d+)$/) { - $return_value = $1; - last; - } else { - _msg("$data"); - } } +} + +sub child_output { + my $args = shift; + my ($stdout_reader, $tag_ref, $pid, $cmd, $options) = @$args; + + my $return_value = 0; - if (not exists $options->{'-'}) { - _msg("process %d (%s) terminated with return code %d", - $pid, $cmd, $return_value); + read_out_data_loop: + my $data = ''; + my $bytes_read = sysread($stdout_reader, $data, 256); + if (not defined $bytes_read) { + _error("stdout: sysread failed:: $!"); + + } elsif ($bytes_read == 0) { + _msg("stdout: sysread got EOF"); + + } elsif ($bytes_read < 256) { + # that's all, folks. + _msg("stdout: read %d bytes: %s", $bytes_read, $data); + } else { + # we maybe need to read some more + _msg("stdout: read %d bytes: %s, maybe more", $bytes_read, $data); + goto read_out_data_loop; } - $stdout_reader->close; - Irssi::input_remove($$tag_ref); + #schedule_cleanup($stdout_reader); + #$stdout_reader->close; } sub _error { - my ($msg) = @_; + my ($msg, @params) = @_; my $win = Irssi::active_win(); - $win->print($msg, Irssi::MSGLEVEL_CLIENTERROR); + my $str = sprintf($msg, @params); + $win->print($str, Irssi::MSGLEVEL_CLIENTERROR); } sub _msg { @@ -233,6 +279,15 @@ sub cmd_exec { } +sub cmd_input { + my ($args) = @_; + if ($pid) { + print $sin "$args\n"; + } else { + _error("no execs are running to accept input"); + } +} + sub exec_init { $command = "exec"; $command_options = join ' ', @@ -243,6 +298,9 @@ sub exec_init { Irssi::command_bind($command, \&cmd_exec); Irssi::command_set_options($command, $command_options); + Irssi::command_bind('input', \&cmd_input); + + Irssi::signal_add('pidwait', \&sig_pidwait); } -exec_init(); + exec_init(); -- cgit v1.2.3