Führen Sie den lokalen Befehl aus, nachdem die SSH-Sitzung beendet wurde

8

Ich habe einige HP Switches, bei denen ich mich über ssh anmelde. Die Switches senden einen Terminalbefehl, um den Zeilenumbruch zu deaktivieren ("rmam" im terminfo-Sprachgebrauch), können ihn dann aber nicht wieder aktivieren, wodurch mein Terminal nach dem Beenden der ssh-Sitzung kaputt geht. Ich kann das Terminal durch Ausführen reparieren tput smam.

Gibt es eine Möglichkeit, ssh dazu zu bringen, diesen Befehl nach dem Ende meiner ssh-Sitzung automatisch auszuführen?

Es würde mich nicht töten, es als Shell-Autobefehl oder Alias sshauszuführen, um diesen Befehl immer danach auszuführen, aber ich würde es vorziehen, das Problem über ssh zu lösen, damit ich den Befehl so beschränken kann, dass er nur ausgeführt wird, nachdem ich eine Verbindung zu bekannt hergestellt habe -bade Gastgeber.

Mein SSH-Client ist OpenSSH_6.2p2, aber ich kann ihn ändern oder aktualisieren, wenn irgendwo eine neue Funktion vorhanden ist.

wfaulk
quelle
Ich habe noch nie einen Fix gefunden ... benutze Telnet;) Oder benutze einfach ein separates Terminalfenster.
ewwhite
Für den einmaligen Gebrauch gibt es immer das doppelte kaufmännische Und. ssh 1.2.3.4 && tput smam
Hennes
@ewwhite: Du meinst, du hast nie einen Weg gefunden, das Update zu automatisieren? Wenn nicht, ist das Update genau dort in der Frage. Auch wenn ich weiß, dass Sie Witze machen, ist es nicht so, dass der Switch das Scheißen auf meinem Terminal vermeiden würde, nur weil die Verbindung unverschlüsselt ist.
wfaulk

Antworten:

8

OpenSSH hat eine Option namens LocalCommand, die einen Befehl auf der Clientseite ausführt, wenn Sie eine SSH-Verbindung herstellen. Leider wird der Befehl ausgeführt, bevor die SSH-Sitzung eingerichtet wurde, nicht danach. Aber das brachte mich auf die Idee, dass ich den vorhergehenden Prozess irgendwie dazu bringen könnte, auf das Ende der SSH-Sitzung zu warten. Trotz der Tatsache, dass der ssh-Prozess die übergeordnete PID des LocalCommand ist, stellt sich heraus, dass es immer noch nicht so einfach ist.

Ich habe jedoch etwas gefunden, das unter MacOS X für mich funktioniert, und sollte auf (anderen) BSDs funktionieren, wenn nicht auf Linux. Ich habe ein kleines C-Programm geschrieben, das die kqueue()Schnittstelle verwendet, um auf seine eigene ppid zu warten und dann einen bereitgestellten Befehl auszuführen, sobald dieser Prozess beendet wird. (Quellcode-Liste unten für Interessierte.)

Jetzt muss ich nur noch auf dieses Programm in meiner ~/.ssh/configDatei verweisen :

host hp-switch*
 PermitLocalCommand yes
 LocalCommand ~/bin/wait4parent 'tput smam'

Und das scheint gut zu funktionieren. Diejenigen von euch , auf Linux ... Ich denke , Sie können versuchen , die gleiche Art der Sache durch die Abfrage für LocalCommand‚s ppid und die Hoffnung , dass die pid nicht wiederverwendet erhalten. (Siehe /programming/1157700/how-to-wait-for-exit-of-non-children-processes )

wait4parent.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>

int main(int argc, char **argv) {
    pid_t ppid, fpid;
    struct kevent kev;
    int kq;
    int kret;
    struct timespec timeout;

    if ( argc > 2 ) {
        fprintf(stderr, "Please quote the command you want to run\n");
        exit(-1);
    }

    ppid = getppid();

    fpid = fork();
    if ( fpid == -1 ) {
        perror("fork");
        exit(-1);
    }

    if ( fpid != 0 ) {
        exit(0);
    }

    EV_SET(&kev, ppid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, 0);

    kq = kqueue();
    if ( kq == -1 ) {
        perror("kqueue");
        exit(-1);
    }

    kret = kevent(kq, &kev, 1, NULL, 0, NULL);
    if ( kret == -1 ) {
        perror("kevent");
        exit(-1);
    }

    timeout.tv_sec = ( 8 /*hours*/ * 60 /*minutes per hour*/ * 60 /*seconds per minute*/ );
    timeout.tv_nsec = 0;

    kret = kevent(kq, NULL, 0, &kev, 1, &timeout);
    if ( kret == -1 ) {
        perror("kevent");
        exit(-1);
    }

    if ( kret > 0 ) {
        system(argv[1]);
    }
    /* ( kret == 0 ) means timeout; don't do anything */

    exit(0);
}
wfaulk
quelle
2
Ausgezeichnet! Ich habe dieses Skript auf einem Mac verwendet und einen weiteren Systemaufruf hinzugefügt, damit ein lokaler Befehl im Voraus ausgeführt werden kann (Prolog) und dann auf den zweiten Befehl warten kann, der nach der Sitzung ausgeführt wird (Epilog).
Kevin Lee
1
Ich nahm die Idee und verbesserte den Code ein wenig. Sie finden das Tool auf GitHub
drinchev
4

Sie können hierfür einen einfachen SSH-Wrapper erstellen und darin Befehle angeben, die nach SSH ausgeführt werden sollen, z

nox: ~ $ fssh foo
foo: ~ $ id
uid = 0 (root) gid = 0 (root) groups = 0 (root)
foo: ~ $ exit
Ausloggen
Verbindung zu 1.2.3.4 geschlossen.
Do 30.10. 19:01:35 MEZ 2014
nox: ~ $ cat bin / fssh 
#! / bin / bash

eval ssh '$ @'
rc = $?
Datum
Beenden Sie "$ rc"
Hrvoje Špoljar
quelle
2
Ja, ich sagte, ich könnte das tun. (Um fair zu sein, ich habe wohl nur "Alias" und nicht "Wrapper-Skript" gesagt.) Unabhängig davon bin ich mir nicht sicher, warum Sie das wollen eval. Es wirft den Vorteil der doppelten Anführungszeichen weg $@.
wfaulk
Versuch es; Sie können weiterhin Anführungszeichen verwenden (sowohl einfache als auch doppelte Anführungszeichen), als hätten Sie direkt mit SSH gesprochen. Ich habe eval verwendet, weil ich Situationen hatte, in denen das Erfassen des realen Exit-Codes eines konstruierten Befehls nur möglich war, wenn ich die gesamte konstruierte Anweisung ausgewertet habe.
Hrvoje Špoljar