]> Sergey Matveev's repositories - public-inbox.git/commitdiff
lei q: support --alert=CMD for early MUA users
authorEric Wong <e@80x24.org>
Mon, 8 Feb 2021 09:05:12 +0000 (23:05 -1000)
committerEric Wong <e@80x24.org>
Mon, 8 Feb 2021 22:07:43 +0000 (22:07 +0000)
For --mua users writing to lock-free -o MFOLDER destinations;
we'll keep -WINCH and send an ASCII terminal bell when results
are complete.  This is intended to let early MUA spawners know
when lei2mail is done writing results.

We'll also support running arbitrary commands.  It may be used
to run play(1) (from SoX), handle pipelines+redirects
(e.g. "/bin/sh -c 'echo search done | wall'") or other commands.

lib/PublicInbox/LEI.pm
lib/PublicInbox/LeiOverview.pm

index e95a674b16f59c30d4a29713c4d8e50150dce120..7b2a3e6fe19a9460ef45c7367dd268957c8b5cbe 100644 (file)
@@ -112,7 +112,7 @@ our %CMD = ( # sorted in order of importance/use:
        save-as=s output|mfolder|o=s format|f=s dedupe|d=s threads|t augment|a
        sort|s=s reverse|r offset=i remote! local! external! pretty
        include|I=s@ exclude=s@ only=s@ jobs|j=s globoff|g stdin|
-       mua=s no-torsocks torsocks=s verbose|v+ quiet|q),
+       alert=s@ mua=s no-torsocks torsocks=s verbose|v+ quiet|q),
        PublicInbox::LeiQuery::curl_opt(), opt_dash('limit|n=i', '[0-9]+') ],
 
 'show' => [ 'MID|OID', 'show a given object (Message-ID or object ID)',
@@ -227,6 +227,11 @@ my %OPTDESC = (
 'show  threads|t' => 'display entire thread a message belongs to',
 'q     threads|t' =>
        'return all messages in the same threads as the actual match(es)',
+'alert=s@' => ['CMD,-WINCH,-bell,<any command>',
+       'run command(s) or perform ops when done writing to output ' .
+       '(default: "-WINCH,-bell" with --mua and Maildir/IMAP output, ' .
+       'nothing otherwise)' ],
+
 'augment|a' => 'augment --output destination instead of clobbering',
 
 'output|mfolder|o=s' => [ 'MFOLDER',
@@ -739,21 +744,43 @@ sub start_mua {
        if (my $sock = $self->{sock}) { # lei(1) client process runs it
                send($sock, exec_buf(\@cmd, {}), MSG_EOR);
        } elsif ($self->{oneshot}) {
-               $self->{"mua.pid.$self.$$"} = spawn(\@cmd);
+               $self->{"pid.$self.$$"}->{spawn(\@cmd)} = \@cmd;
        }
        if ($self->{lxs} && $self->{au_done}) { # kick wait_startq
                syswrite($self->{au_done}, 'q' x ($self->{lxs}->{jobs} // 0));
        }
+       $self->{opt}->{quiet} = 1;
+       delete $self->{-progress};
+       delete $self->{opt}->{verbose};
 }
 
 sub poke_mua { # forces terminal MUAs to wake up and hopefully notice new mail
        my ($self) = @_;
-       return unless $self->{opt}->{mua} && -t $self->{1};
-       # hit the process group that started the MUA
-       if (my $s = $self->{sock}) {
-               send($s, '-WINCH', MSG_EOR);
-       } elsif ($self->{oneshot}) {
-               kill('-WINCH', $$);
+       my $alerts = $self->{opt}->{alert} // return;
+       while (my $op = shift(@$alerts)) {
+               if ($op eq '-WINCH') {
+                       # hit the process group that started the MUA
+                       if ($self->{sock}) {
+                               send($self->{sock}, '-WINCH', MSG_EOR);
+                       } elsif ($self->{oneshot}) {
+                               kill('-WINCH', $$);
+                       }
+               } elsif ($op eq '-bell') {
+                       out($self, "\a");
+               } elsif ($op =~ /(?<!\\),/) { # bare ',' (not ',,')
+                       push @$alerts, split(/(?<!\\),/, $op);
+               } elsif ($op =~ m!\A([/a-z0-9A-Z].+)!) {
+                       my $cmd = $1; # run an arbitrary command
+                       require Text::ParseWords;
+                       $cmd = [ Text::ParseWords::shellwords($cmd) ];
+                       if (my $s = $self->{sock}) {
+                               send($s, exec_buf($cmd, {}), MSG_EOR);
+                       } elsif ($self->{oneshot}) {
+                               $self->{"pid.$self.$$"}->{spawn($cmd)} = $cmd;
+                       }
+               } else {
+                       err($self, "W: unsupported --alert=$op"); # non-fatal
+               }
        }
 }
 
@@ -776,8 +803,8 @@ sub start_pager {
                my $fds = [ map { fileno($_) } @$rdr{0..2} ];
                $send_cmd->($sock, $fds, exec_buf([$pager], $new_env), MSG_EOR);
        } elsif ($self->{oneshot}) {
-               $pgr->[0] = spawn([$pager], $new_env, $rdr);
-               $pgr->[3] = $$; # ew'll reap it
+               my $cmd = [$pager];
+               $self->{"pid.$self.$$"}->{spawn($cmd, $new_env, $rdr)} = $cmd;
        } else {
                die 'BUG: start_pager w/o socket';
        }
@@ -793,8 +820,6 @@ sub stop_pager {
        $self->{2} = $pgr->[2];
        # do not restore original stdout, just close it so we error out
        close(delete($self->{1})) if $self->{1};
-       my $pid = $pgr->[0];
-       dwaitpid($pid) if $pid && ($pgr->[3] // 0) == $$;
 }
 
 sub accept_dispatch { # Listener {post_accept} callback
@@ -1044,9 +1069,8 @@ sub DESTROY {
        my ($self) = @_;
        $self->{1}->autoflush(1) if $self->{1};
        stop_pager($self);
-       if (my $mua_pid = delete $self->{"mua.pid.$self.$$"}) {
-               waitpid($mua_pid, 0);
-       }
+       my $oneshot_pids = delete $self->{"pid.$self.$$"} or return;
+       waitpid($_, 0) for keys %$oneshot_pids;
 }
 
 1;
index f0ac468417cc5ea4666bc3cbb589a310ef83957e..98c89d12d197e0829fe39a68c510478eba71b128 100644 (file)
@@ -98,7 +98,10 @@ sub new {
                $opt->{'sort'} //= 'docid' if $dst ne '/dev/stdout';
                $lei->{l2m} = eval { PublicInbox::LeiToMail->new($lei) };
                return $lei->fail($@) if $@;
-               $lei->{early_mua} = 1 if $opt->{mua} && $lei->{l2m}->lock_free;
+               if ($opt->{mua} && $lei->{l2m}->lock_free) {
+                       $lei->{early_mua} = 1;
+                       $opt->{alert} //= [ '-WINCH,-bell' ] if -t $lei->{1};
+               }
        }
        $self;
 }