aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Feist <shabble@metavore.org>2011-04-13 11:05:59 +0000
committerTom Feist <shabble@metavore.org>2011-04-13 11:05:59 +0000
commit0b960e5d3cea20058ccd1e1bbd92d49dfffc325d (patch)
tree7018300e0aa4bf31a2f89a67f331e9fcce8bf6a7
parentadded exec.pl, a pseudo-replacement for teh native /exec since it's broken and I (diff)
downloadirssi-scripts-0b960e5d3cea20058ccd1e1bbd92d49dfffc325d.tar.gz
irssi-scripts-0b960e5d3cea20058ccd1e1bbd92d49dfffc325d.zip
exec.pl: now mostly working with IPC::Open3. Needs cleanup
Diffstat (limited to '')
-rw-r--r--feature-tests/exec.pl182
1 files 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();