stderr over ssh -t


11

Questo invia output a STDERR, ma non propaga Ctrl+ C(cioè Ctrl+ Cucciderà sshma non il telecomando sleep):

$ ssh localhost 'sleep 100;echo foo ">&2"'

Questo propaga Ctrl+ C(cioè Ctrl+ Cucciderà sshe il telecomando sleep), ma invia STDERR a STDOUT:

$ ssh -tt localhost 'sleep 100;echo foo ">&2"'

Come posso forzare il secondo a inviare l'output STDERR a STDERR, pur continuando a propagare Ctrl+ C?

sfondo

GNU Parallel usa 'ssh -tt' per propagare Ctrl+ C. Ciò consente di terminare i lavori in esecuzione in remoto. Ma i dati inviati a STDERR dovrebbero continuare ad andare a STDERR alla fine della ricezione.

Risposte:


5

Non penso che tu possa aggirare quello.

Con -tt, sshdgenera uno pseudo-terminale e rende la parte slave lo stdin, lo stdout e lo stderr della shell che esegue il comando remoto.

sshdlegge ciò che viene dal suo (singolo) fd alla parte master dello pseudo-terminale e lo invia (tramite un singolo canale) al sshclient. Non esiste un secondo canale per stderr come è senza -t.

Inoltre si noti che la disciplina della linea terminale dello pseudo-terminale può (e per impostazione predefinita) alterare l'uscita. Ad esempio, l'LF verrà convertito in CRLF laggiù e non sul terminale locale, quindi potresti voler disabilitare la post-elaborazione dell'output.

$ ssh  localhost 'echo x' | hd
00000000  78 0a                                             |x.|
00000002
$ ssh -t localhost 'echo x' | hd
00000000  78 0d 0a                                          |x..|
00000003
$ ssh -t localhost 'stty -opost; echo x' | hd
00000000  78 0a                                             |x.|
00000002

Accadranno molte più cose sul lato input (come il ^Cpersonaggio che causerà un SIGINT, ma anche altri segnali, l'eco e tutta la gestione coinvolta nell'editor di linea canonica ).

Potresti eventualmente reindirizzare stderr a un FIFO e recuperarlo usando un secondo ssh:

ssh -tt host 'mkfifo fifo && cmd 2> fifo' &
ssh host 'cat fifo' >&2

Ma la migliore IMO sarebbe quella di evitare di usarla del -ttutto. Questo è davvero pensato solo per l'uso interattivo da un vero terminale.

Invece di fare affidamento sulla trasmissione di un ^ C per consentire al terminale remoto di chiudere la connessione, è possibile utilizzare un wrapper che esegue un poll()rilevamento della sshconnessione interrotta o chiusa.

Forse qualcosa di simile (semplificato, ti consigliamo di aggiungere un po 'di controllo degli errori):

LC_HUP_DETECTOR='
  use IO::Poll;
  $SIG{CHLD} = sub {$done = 1};
  $p = IO::Poll->new;
  $p->mask(STDOUT, POLLIN);
  $pid=fork; unless($pid) {setpgrp; exec @ARGV; die "exec: $!\n"}
  $p->poll;
  kill SIGHUP, -$pid unless $done;
  wait; exit ($?&127 ? 128+($?&127) : 1+$?>>8)
' ssh host 'perl -e "$LC_HUP_DETECTOR" some cmd'

Quanto $p->mask(STDOUT, POLLIN)sopra può sembrare sciocco, ma l'idea è di attendere un evento hang-hup (per chiudere la fine della lettura della pipe su stdout). POLLHUP come maschera richiesta viene ignorato. POLLHUP è significativo solo come evento restituito (per dire che la fine della scrittura è stata chiusa).

Dobbiamo fornire un valore diverso da zero per la maschera dell'evento. Se usiamo 0, perlnon chiamiamo nemmeno poll. Quindi qui usiamo POLLIN.

Su Linux, qualunque cosa tu richieda, se la pipe si rompe, poll () restituisce POLLERR.

Su Solaris e FreeBSD, dove le pipe sono bidirezionali, quando l'estremità di lettura della pipe (che è anche una fine di scrittura lì) è chiusa, ritorna con POLLHUP (e POLLIN su FreeBSD, dove è necessario richiedere POLLIN oppure $p->poll()no ritorno).

Non posso dire quanto sia portatile al di fuori di questi tre sistemi operativi.


Mi piace la tua idea, ma non posso fare in modo che il tuo wrapper rilevi alcun segnale a meno che non sia impostato '-tt'. Funziona parallel --tag -j1 'ssh -tt localhost perl/catch_wrap perl/catch_all_signals & sleep 1; killall -{} ssh' ::: {1..31}:, ma rimuovi '-tt' e quindi non funziona.
Ole Tange,

@OleTange Lo scopo del wrapper è che SIGHUP sia inviato al lavoro remoto quando muore ssh (dopo aver interrotto la connessione ssh). Non so cosa fanno i tuoi catch_all_signals, ma tutto ciò che otterrebbe è quel SIGHUP e solo dopo che la connessione ssh è caduta (quindi se stampa qualcosa su stdout, non lo vedrai).
Stéphane Chazelas,

catch_all_signals registra tutti i segnali in un file e, come detto, funziona con '-tt', ma fallisce senza. In altre parole: non riceve un SIGHUP da catch_wrap quando ssh muore.
Ole Tange,

Funziona ancora solo -ttdopo la modifica. Ricordare che se non si esegue il comando in parallelo, ssh erediterà il terminale dal quale lo si esegue.
Ole Tange,

@OleTange, non riesco a riprodurre, funziona per me, l'hai provato con il codice che ho pubblicato? Pubblica il tuo catch_wrap e catch_all_signals da qualche parte in modo che io possa dare un'occhiata. Con -t, mi aspetto che non funzioni.
Stéphane Chazelas,

1

Per farlo funzionare su altre piattaforme questa è diventata la soluzione finale. Verifica se il client ssh si è disconnesso e quindi il genitore è diventato pid 1:

$SIG{CHLD} = sub { $done = 1; };
$pid = fork;
unless($pid) {
    # Make own process group to be able to kill HUP it later
    setpgrp;
    exec $ENV{SHELL}, "-c", ($bashfunc."@ARGV");
    die "exec: $!\n";
}
do {
    # Parent is not init (ppid=1), so sshd is alive
    # Exponential sleep up to 1 sec
    $s = $s < 1 ? 0.001 + $s * 1.03 : $s;
    select(undef, undef, undef, $s);
} until ($done || getppid == 1);
# Kill HUP the process group if job not done
kill(SIGHUP, -${pid}) unless $done;
wait;
exit ($?&127 ? 128+($?&127) : 1+$?>>8)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.