+ delete $self->{opt}->{verbose};
+}
+
+sub send_exec_cmd { # tell script/lei to execute a command
+ my ($self, $io, $cmd, $env) = @_;
+ my $sock = $self->{sock} // die 'lei client gone';
+ my $fds = [ map { fileno($_) } @$io ];
+ $send_cmd->($sock, $fds, exec_buf($cmd, $env), MSG_EOR);
+}
+
+sub poke_mua { # forces terminal MUAs to wake up and hopefully notice new mail
+ my ($self) = @_;
+ 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
+ }
+ }
+}
+
+my %path_to_fd = ('/dev/stdin' => 0, '/dev/stdout' => 1, '/dev/stderr' => 2);
+$path_to_fd{"/dev/fd/$_"} = $_ for (0..2);
+
+# this also normalizes the path
+sub path_to_fd {
+ my ($self, $path) = @_;
+ $path = rel2abs($self, $path);
+ $path =~ tr!/!/!s;
+ $path_to_fd{$path} // (
+ ($path =~ m!\A/(?:dev|proc/self)/fd/[0-9]+\z!) ?
+ fail($self, "cannot open $path from daemon") : -1
+ );