From b17683b096424c901fa5d6ddaa3b8ff2bdf95d2f Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Sun, 24 Apr 2016 23:30:30 +0100 Subject: [PATCH] with-authinfo-kludge: Change how ssh(1) signals that it's started. * In `server_listen', arrange for the remote shell echo something to stdout. Also, use `read' rather than cat(1), which saves a process on the remote system. And, finally, include a comment so that the ps(1) output is approximately useful. * In `wait_for_ssh', go into a loop waiting for the echoed stuff from the remote servers to arrive (or for them to report early EOF). Thanks to Ian Jackson for suggesting this approach: it's much better than the previous one. --- with-authinfo-kludge | 110 +++++++++++++++++++++++++++++---------------------- 1 file changed, 62 insertions(+), 48 deletions(-) diff --git a/with-authinfo-kludge b/with-authinfo-kludge index 651cd8c..112668e 100755 --- a/with-authinfo-kludge +++ b/with-authinfo-kludge @@ -600,7 +600,8 @@ sub fix_server_config ($) { $s->{"_proxy_server"} =~ s/:119$//; $s->{"_proxy_server"} =~ s/^\[(.*)\]$/$1/; $s->{"_sshkid"} = undef; - $s->{"_ssh_master"} = undef; + $s->{"_ssh_stdin"} = undef; + $s->{"_ssh_stdout"} = undef; } sub hack_noip_envvar ($$) { @@ -660,70 +661,83 @@ sub server_listen ($) { } $s->{"_proxy_sockdir"} = $sockdir; - ## This is quite awful. The `-L' option sets up the tunnel that we - ## actually wanted. The `-v' makes SSH spew stuff to stdout, which might - ## be useful if you're debugging. The `-S' has two effects: firstly, it - ## detaches OpenSSH from any other control master things which might be - ## going on, because they tend to interfere with forwarding (and, - ## besides, the existing master won't be under the same noip - ## configuration); and, secondly, it causes OpenSSH to make a socket in a - ## place we know, so we can tell when it's actually ready. The `cat' - ## will keep the tunnel open until we close our end, which we don't do - ## until we exit. + ## The `-L' option sets up the tunnel that we actually wanted. The `-v' + ## makes SSH spew stuff to stdout, which might be useful if you're + ## debugging. . The `-S' detaches OpenSSH from any control master + ## things which might be going on, because they tend to interfere with + ## forwarding (and, besides, the existing master won't be under the same + ## noip configuration). The `echo' will let us know that it's started + ## up, and the `read' will keep the tunnel open until we close our end, + ## which we do implicitly when we exit. inform " starting SSH tunnel"; - my @sshargs = ("ssh", "-L$sshbind:$remote"); + my @sshargs = ("ssh", "-L$sshbind:$remote", "-Snone"); $VERBOSE and push @sshargs, "-v"; - my $master = "$SESSDIR/ssh-master." . sequence; - push @sshargs, "-S$master", "-M"; - $s->{"_ssh_master"} = $master; - push @sshargs, $via, "cat"; - pipe my $rfd, my $wfd or sysfail "failed to create pipe: $!"; - set_cloexec $wfd; + push @sshargs, $via, < $server +set -e; echo started; read hunoz +EOF + pipe my $rin, my $win and pipe my $rout, my $wout + or sysfail "failed to create pipe: $!"; + set_cloexec $win; + set_cloexec $rout; set_nonblock $rout; defined (my $kid = myfork) or sysfail "failed to fork: $!"; if (!$kid) { - open STDIN, "<&", $rfd or sysfail "failed to dup pipe to stdin: $!"; - open STDOUT, ">", "/dev/null" - or sysfail "failed to redirect stdout to /dev/null: $!"; + open STDIN, "<&", $rin or sysfail "failed to dup pipe to stdin: $!"; + open STDOUT, "<&", $wout or sysfail "failed to dup pipe to stdout: $!"; hack_noip_env \%ssh_noip, $sockdir; exec @sshargs or sysfail "failed to exec SSH: $!"; } - close $rfd; + close $rin; + close $wout; $s->{"_sshkid"} = $kid; - $s->{"_ssh_pipe"} = $wfd; + $s->{"_ssh_stdin"} = $win; + $s->{"_ssh_stdout"} = $rout; $KIDMAP{$kid} = [$s, "SSH tunnel"]; } } sub wait_for_ssh () { - inform "waiting for SSH tunnels to start..."; - my $delay = 0.1; - my $max = 10; - my $mult = 1.3; + my $rfd_in = ""; - WAIT: for (;;) { - my $missing = 0; - KID: for my $kid (keys %KIDMAP) { - my ($s, $what) = @{$KIDMAP{$kid}}; - next KID unless $kid == $s->{"_sshkid"}; - if (-S $s->{"_ssh_master"}) { - inform " found socket from `$s->{_name}'"; - } else { - inform " no socket yet from `$s->{_name}'"; - $missing = 1; + ## Collect up all the `stdout' pipes. + my %fd = (); + SETUP: for my $s (values %S) { + next SETUP unless $s->{"_sshkid"}; + my $fd = fileno $s->{"_ssh_stdout"}; + vec($rfd_in, $fd, 1) = 1; + $fd{$fd} = [$s->{"_ssh_stdout"}, $s]; + } + unless (%fd) { + inform "no SSH tunnels to start"; + return; + } + + ## Wait for each of them to become readable, and try to read a thing. + ## Either we'll get a byte or EOF; either means that the respective tunnel + ## is as ready as it's ever going to be. + inform "waiting for SSH tunnels to start..."; + my $nbad = 0; + SELECT: while (%fd) { + my ($n, $t) = select my $rfd_out = $rfd_in, undef, undef, undef; + if ($n >= 0) { } + elsif ($! == EINTR) { next SELECT; } + else { sysfail "select failed: $!"; } + FD: for my $fd (keys %fd) { + next FD unless vec $rfd_out, $fd, 1; + my ($sk, $s) = @{$fd{$fd}}; + my $n = sysread $sk, my $hunoz, 128; + if (defined $n) { + vec($rfd_in, $fd, 1) = 0; + if ($n) { inform " tunnel to $s->{remote} started ok"; } + else { inform " tunnel to $s->{remote} FAILED"; $nbad++; } + delete $fd{$fd}; + } elsif ($! != EAGAIN && $! != EWOULDBLOCK) { + sysfail "failed to read from pipe: $!"; } } - unless ($missing) { - inform " all present and correct!"; - last WAIT; - } - if ($delay > $max) { - inform " bored now; giving up"; - last WAIT; - } - inform "waiting ${delay}s for stuff to happen..."; - select undef, undef, undef, $delay; - $delay *= $mult; } + if ($nbad) { inform " tunnels started; $nbad FAILED"; } + else { inform " all tunnels started ok"; } } $SIG{"CHLD"} = sub { -- 2.11.0