From c6752d86b8a161d5ee3c4b008eb78a008b755e40 Mon Sep 17 00:00:00 2001 From: Tom Feist Date: Wed, 13 Apr 2011 09:30:08 +0100 Subject: added exec.pl, a pseudo-replacement for teh native /exec since it's broken and I can't figure out why. --- feature-tests/exec.pl | 248 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 feature-tests/exec.pl (limited to 'feature-tests/exec.pl') diff --git a/feature-tests/exec.pl b/feature-tests/exec.pl new file mode 100644 index 0000000..e268291 --- /dev/null +++ b/feature-tests/exec.pl @@ -0,0 +1,248 @@ +# exec.pl +# a (currently stupid) alternative to the built-in /exec, because it's broken +# on OSX. This thing stll needs a whole bunch of actual features, but for now, +# you can actually run commands. + +# Obviously, that's pretty dangerous. Use at your own risk. + +# EXEC [-] [-nosh] [-out | -msg | -notice ] [-name ] +# EXEC -out | -window | -msg | -notice | -close | - % +# EXEC -in % +# +# -: Don't print "process terminated ..." message +# +# -nosh: Don't start command through /bin/sh +# +# -out: Send output to active channel/query +# +# -msg: Send output to specified nick/channel +# +# -notice: Send output to specified nick/channel as notices +# +# -name: Name the process so it could be accessed easier +# +# -window: Move the output of specified process to active window +# +# -close: Forcibly close (or "forget") a process that doesn't die. +# This only removes all information from irssi concerning the +# process, it doesn't send SIGKILL or any other signal +# to the process. +# +# -: Send a signal to process. can be either numeric +# or one of the few most common ones (hup, term, kill, ...) +# +# -in: Send text to standard input of the specified process +# +# -interactive: Creates a query-like window item. Text written to it is +# sent to executed process, like /EXEC -in. +# +# Execute specified command in background. Output of process is printed to +# active window by default, but can be also sent as messages or notices to +# specified nick or channel. +# +# Processes can be accessed either by their ID or name if you named it. Process +# identifier must always begin with '%' character, like %0 or %name. +# +# Once the process is started, its output can still be redirected elsewhere with +# the -window, -msg, etc. options. You can send text to standard input of the +# process with -in option. +# +# -close option shouldn't probably be used if there's a better way to kill the +# process. It is meant to remove the processes that don't die even with +# SIGKILL. This option just closes the pipes used to communicate with the +# process and frees all memory it used. +# +# EXEC without any arguments displays the list of started processes. +# + + + +use 5.010; # 5.10 or above, necessary to get the return value from a command. + +use strict; +use warnings; +use English '-no_match_vars'; + +use Irssi; +use POSIX; +use Time::HiRes qw/sleep/; +use IO::Handle; +use IO::Pipe; +use IPC::Open3; + + +use Data::Dumper; + +our $VERSION = '0.1'; +our %IRSSI = ( + authors => 'shabble', + contact => 'shabble+irssi@metavore.org', + name => 'exec.pl', + description => '', + license => 'Public Domain', + ); + + +my $forked = 0; + +my $command; +my $command_options; + + +sub parse_options { + my ($args) = @_; + my @options = Irssi::command_parse_options($command, $args); + if (@options) { + my $opt_hash = $options[0]; + my $rest = $options[1]; + + print Dumper($opt_hash); + return ($opt_hash, $rest); + } else { + _error("Error parsing $command options"); + return (); + } +} + + + +sub do_fork_and_exec { + my ($options, $cmd) = @_; + + my $stdout_pipe = IO::Pipe->new; + my $stderr_pipe = IO::Pipe->new; + +# return if $forked; + + #my $pid = fork(); + + if (not defined $pid) { + _error("Fork failed: $! Aborting"); + $_->close for $stdout_pipe->handles; + undef $stdout_pipe; + return; + } + +# $forked = 1; + + if ($pid > 0) { # this is the parent (Irssi) + my $tag; + + Irssi::pidwait_add($pid); + + my $stdout_reader = $stdout_pipe->reader; + $stdout_reader->autoflush; + + my @args = ($stdout_reader, \$tag, $pid, $cmd, $options); + $tag = Irssi::input_add($stdout_reader->fileno, + Irssi::INPUT_READ, + \&child_output, + \@args); + + } 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}; + + $stdout_fh->print($_) for @data; + + 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; + + POSIX::_exit(1); + } +} +sub drop_privs { + my @temp = ($EUID, $EGID); + my $orig_uid = $UID; + my $orig_gid = $GID; + $EUID = $UID; + $EGID = $GID; + # Drop privileges + $UID = $orig_uid; + $GID = $orig_gid; + # Make sure privs are really gone + ($EUID, $EGID) = @temp; + die "Can't drop privileges" + unless $UID == $EUID && $GID eq $EGID; +} + +sub child_output { + my $args = shift; + my ($stdout_reader, $tag_ref, $pid, $cmd, $options) = @$args; + + my $return_value = 0; + + while (defined(my $data = <$stdout_reader>)) { + + chomp $data; + + # TODO: do we want to remove empty lines? + #return unless length $data; + + if ($data =~ m/^__DONE__(\d+)$/) { + $return_value = $1; + last; + } else { + _msg("$data"); + } + } + + if (not exists $options->{'-'}) { + _msg("process %d (%s) terminated with return code %d", + $pid, $cmd, $return_value); + } + + $stdout_reader->close; + Irssi::input_remove($$tag_ref); +} + +sub _error { + my ($msg) = @_; + my $win = Irssi::active_win(); + $win->print($msg, Irssi::MSGLEVEL_CLIENTERROR); +} + +sub _msg { + my ($msg, @params) = @_; + my $win = Irssi::active_win(); + my $str = sprintf($msg, @params); + $win->print($str, Irssi::MSGLEVEL_CLIENTCRAP); +} + +sub cmd_exec { + + my ($args, $server, $witem) = @_; + # TODO: parse some options here. + Irssi::signal_stop; + + my @options = parse_options($args); + if (@options) { + do_fork_and_exec(@options) + } + +} + +sub exec_init { + $command = "exec"; + $command_options = join ' ', + ( + '!-', 'interactive', 'nosh', '+name', '+msg', + '+notice', 'window', 'close', '+level', 'quiet' + ); + + Irssi::command_bind($command, \&cmd_exec); + Irssi::command_set_options($command, $command_options); +} + +exec_init(); -- cgit v1.2.3 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(-) (limited to 'feature-tests/exec.pl') 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 From 5af03a3997260b48cc3b3689e5f9e62d777831ab Mon Sep 17 00:00:00 2001 From: Tom Feist Date: Wed, 13 Apr 2011 22:17:41 +0100 Subject: exec.pl: using open3, but horribly broken. Still not sure why. --- feature-tests/exec.pl | 302 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 219 insertions(+), 83 deletions(-) (limited to 'feature-tests/exec.pl') diff --git a/feature-tests/exec.pl b/feature-tests/exec.pl index ca8c6a2..f6d6377 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 Symbol 'geniosym'; use Data::Dumper; @@ -82,15 +82,105 @@ our %IRSSI = ( license => 'Public Domain', ); +my @processes = (); +sub get_processes { return @processes } -my $pid = 0; - +# the /exec command, nothing to do with the actual command being run. my $command; my $command_options; -my ($sin, $serr, $sout) = (new IO::Handle, new IO::Handle, new IO::Handle); -my ($stdout_tag, $stderr_tag); +sub get_new_id { + my $i = 1; + foreach my $proc (@processes) { + if ($proc->{id} != $i) { + next; + } + $i++; + } + return $i; +} + +sub add_process { + #my ($pid) = @_; + my $id = get_new_id(); + + my $new = { + id => $id, + pid => 0, + in_tag => 0, + out_tag => 0, + err_tag => 0, + s_in => geniosym(), #IO::Handle->new, + s_err => geniosym(), #IO::Handle->new, + s_out => geniosym(), #IO::Handle->new, + cmd => '', + opts => {}, + }; + + # $new->{s_in}->autoflush(1); + # $new->{s_out}->autoflush(1); + # $new->{s_err}->autoflush(1); + + push @processes, $new; + + _msg("New process item created: $id"); + return $new; +} + +sub find_process_by_id { + my ($id) = @_; + my @matches = grep { $_->{id} == $id } @processes; + _error("wtf, multiple id matches for $id. BUG") if @matches > 1; + return $matches[0]; + +} +sub find_process_by_pid { + my ($pid) = @_; + my @matches = grep { $_->{pid} == $pid } @processes; + _error("wtf, multiple pid matches for $pid. BUG") if @matches > 1; + + return $matches[0]; +} + +sub remove_process { + my ($id, $verbose) = @_; + my $del_index = 0; + foreach my $proc (@processes) { + if ($id == $proc->{id}) { + last; + } + $del_index++; + } + print "remove: del index: $del_index"; + if ($del_index <= $#processes) { + my $dead = splice(@processes, $del_index, 1, ()); + #_msg("removing " . Dumper($dead)); + + Irssi::input_remove($dead->{err_tag}); + Irssi::input_remove($dead->{out_tag}); + + close $dead->{s_out}; + close $dead->{s_in}; + close $dead->{s_err}; + + } else { + $verbose = 1; + if ($verbose) { + print "remove: No such process with ID $id"; + } + } +} + +sub show_current_processes { + if (@processes == 0) { + print "No processes running"; + return; + } + foreach my $p (@processes) { + printf("ID: %d, PID: %d, Command: %s", $p->{id}, $p->{pid}, $p->{cmd}); + } +} sub parse_options { my ($args) = @_; @@ -101,8 +191,13 @@ sub parse_options { $rest =~ s/^\s*(.*?)\s*$/$1/; # trim surrounding space. - print Dumper([$opt_hash, $rest]); - return ($opt_hash, $rest); + #print Dumper([$opt_hash, $rest]); + if (length $rest) { + return ($opt_hash, $rest); + } else { + show_current_processes(); + return (); + } } else { _error("Error parsing $command options"); return (); @@ -115,21 +210,19 @@ sub schedule_cleanup { } sub do_fork_and_exec { - my ($options, $cmd) = @_; + my ($rec) = @_; #Irssi::timeout_add_once(100, sub { die }, {}); - return unless $cmd; - - #_msg("type of siin is %s, out is %s, err is %s", ref $sin, ref $sout, ref $serr); - - $sin->autoflush; - $sout->autoflush; - $serr->autoflush; - + return unless exists $rec->{cmd}; drop_privs(); - $pid = open3($sin, $sout, $serr, $cmd); + _msg("Executing command " . join(", ", @{ $rec->{cmd} })); + my $c = join(" ", @{ $rec->{cmd} }); + my $pid = open3($rec->{s_sin}, $rec->{s_out}, $rec->{s_err}, $c); + + _msg("PID is $pid"); + $rec->{pid} = $pid; # _msg("Pid %s, in: %s, out: %s, err: %s, cmd: %s", # $pid, $sin, $sout, $serr, $cmd); @@ -138,10 +231,11 @@ sub do_fork_and_exec { # $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); + close($_) for ($rec->{s_in}, $rec->{s_err}, $rec->{s_out}); + undef($_) for ($rec->{s_in}, $rec->{s_err}, $rec->{s_out}); return; } @@ -149,22 +243,24 @@ sub do_fork_and_exec { # parent if ($pid) { +# eval { + print "fileno is " . fileno($rec->{s_out}); + $rec->{out_tag} = Irssi::input_add( fileno($rec->{s_out}), + Irssi::INPUT_READ, + \&child_output, + $rec); + #die unless $rec->{out_tag}; - 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; + $rec->{err_tag} = Irssi::input_add(fileno($rec->{s_err}), + Irssi::INPUT_READ, + \&child_error, + $rec); + #die unless $rec->{err_tag}; - my @err_args = ($serr, $cmd, $options); - $stderr_tag = Irssi::input_add($serr->fileno, Irssi::INPUT_READ, - \&child_error, \@err_args); - die unless $stderr_tag; + # }; - }; Irssi::pidwait_add($pid); - die "input_add failed to initialise: $@" if $@; } } @@ -185,69 +281,75 @@ sub drop_privs { } sub child_error { - my $args = shift; - my ($stderr_reader, $cmd, $options) = @$args; - - 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; + my $rec = shift; + + my $err_fh = $rec->{s_err}; + + my $done = 0; + + while (not $done) { + my $data = ''; + _msg("Stderr: starting sysread"); + my $bytes_read = sysread($err_fh, $data, 256); + if (not defined $bytes_read) { + _error("stderr: sysread failed:: $!"); + $done = 1; + } elsif ($bytes_read == 0) { + _msg("stderr: sysread got EOF"); + $done = 1; + } 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); + } } + _msg('removing input stderr tag'); + Irssi::input_remove($rec->{err_tag}); + } sub sig_pidwait { my ($pidwait, $status) = @_; - if ($pidwait == $pid) { + my @matches = grep { $_->{pid} == $pidwait } @processes; + foreach my $m (@matches) { _msg("PID %d has terminated. Status %d (or maybe %d .... %d)", $pidwait, $status, $?, ${^CHILD_ERROR_NATIVE} ); - $pid = 0; - - _msg('removing input stdout tag'); - Irssi::input_remove($stdout_tag); - - _msg('removing input stderr tag'); - Irssi::input_remove($stderr_tag); + remove_process($m->{id}); } } sub child_output { - my $args = shift; - my ($stdout_reader, $tag_ref, $pid, $cmd, $options) = @$args; - - my $return_value = 0; - - 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; + my $rec = shift; + my $out_fh = $rec->{s_out}; + + my $done = 0; + + while (not $done) { + my $data = ''; + _msg("Stdout: starting sysread"); + my $bytes_read = sysread($out_fh, $data, 256); + if (not defined $bytes_read) { + _error("stdout: sysread failed:: $!"); + $done = 1; + } elsif ($bytes_read == 0) { + _msg("stdout: sysread got EOF"); + $done = 1; + } 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); + } } + _msg('removing input stdout tag'); + Irssi::input_remove($rec->{out_tag}); + #schedule_cleanup($stdout_reader); #$stdout_reader->close; } @@ -269,20 +371,41 @@ sub _msg { sub cmd_exec { my ($args, $server, $witem) = @_; - # TODO: parse some options here. Irssi::signal_stop; - my @options = parse_options($args); + if (@options) { - do_fork_and_exec(@options) + my $rec = add_process(); + my ($options, $cmd) = @options; + + $cmd = [split ' ', $cmd]; + + if (not exists $options->{nosh}) { + unshift @$cmd, ("/bin/sh -c"); + } + + $rec->{opts} = $options; + $rec->{cmd} = $cmd; + + do_fork_and_exec($rec) } } sub cmd_input { my ($args) = @_; - if ($pid) { - print $sin "$args\n"; + my $rec = $processes[0]; # HACK, make them specify. + if ($rec->{pid}) { + print "INput writing to $rec->{pid}"; + my $fh = $rec->{s_in}; + + my $ret = syswrite($fh, "$args\n"); + if (not defined $ret) { + print "Error writing to process $rec->{pid}: $!"; + } else { + print "Wrote $ret bytes to $rec->{pid}"; + } + } else { _error("no execs are running to accept input"); } @@ -304,3 +427,16 @@ sub exec_init { } exec_init(); + +package Irssi::UI; + +{ + no warnings 'redefine'; + + sub processes() { + return Irssi::Script::exec::get_processes(); + } + +} + +1; -- cgit v1.2.3