diff options
Diffstat (limited to 'feature-tests')
| -rw-r--r-- | feature-tests/exec.pl | 302 | 
1 files changed, 219 insertions, 83 deletions
| 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; | 
