I have some HP switches that I log into via ssh. The switches send a terminal command to disable line wrap ("rmam" in terminfo parlance), but then fail to reenable it, which screws up my terminal after I exit the ssh session. I can fix the terminal by running tput smam.

Is there any way to get ssh to automatically run that command after my ssh session ends?

It wouldn't kill me to run it as a shell autocommand, or alias ssh to always run that command afterwards, but I'd prefer to solve the problem via ssh so that I can limit the command to just be run after I connect to known-bad hosts.

My ssh client is OpenSSH_6.2p2, but I can change or update if there's a new feature somewhere.

  • I've never found a fix... use telnet ;) Or just use a separate terminal window. – ewwhite Oct 30 '14 at 16:39
  • For once off usage there is always the double ampersand. ssh 1.2.3.4 && tput smam – Hennes Oct 30 '14 at 18:18
  • @ewwhite: You mean you never found a way to automate the fix? If not, the fix is right there in the question. Also, while I know you're joking, it's not like the switch would avoid crapping on my terminal just because the connection is unencrypted. – wfaulk Oct 31 '14 at 0:25
up vote 5 down vote accepted

OpenSSH has an option called LocalCommand that runs a command on the client side when you make an ssh connection. Unfortunately, it runs the command before the ssh session is established, not afterwards. But that gave me the idea that I might somehow be able to get that preceding process to wait for the ssh session to end. Despite the fact that the ssh process is the parent PID of the LocalCommand, it turns out that it's still not all that easy.

However, I did find something that works for me under MacOS X, and ought to work on (other) BSDs, if not Linux. I wrote a small C program that uses the kqueue() interface to wait on its own ppid and then run a supplied command once that process exits. (Source code listing below, for those who are interested.)

Now I just have to reference this program in my ~/.ssh/config file:

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

And this seems to work just fine. Those of you on Linux … I guess you can try the same sort of thing by polling for LocalCommand's ppid and hope that that pid doesn't get reused. (See https://stackoverflow.com/questions/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);
}
  • 2
    Excellent! I used this script on Mac and added another system call so that I could have a local command run upfront (prologue) and then wait on the second command to run after the session (epilogue). – Kevin Lee Mar 17 '15 at 17:10
  • 1
    I took the idea and improved a bit the code. You can find the tool on GitHub – drinchev Apr 8 '17 at 10:39

You could make simple ssh wrapper for this and in it specify commands to be ran after ssh, e.g.

nox:~$ fssh foo
foo:~$ id
uid=0(root) gid=0(root) groups=0(root)
foo:~$ exit
logout
Connection to 1.2.3.4 closed.
Thu Oct 30 19:01:35 CET 2014
nox:~$ cat bin/fssh 
#!/bin/bash

eval ssh '$@'
rc=$?
date
exit "$rc"
  • 2
    Yeah, I said I could do this. (To be fair, I guess I did only say "alias" and not "wrapper script".) Regardless, I'm not sure why you want to eval that. It throws away the benefit of double-quoting $@. – wfaulk Oct 31 '14 at 0:21
  • Try it out; you can still use quotes normally (both single and double quotes) like you were talking with SSH directly. I used eval because I've had situations where capturing real exit code of constructed command was just not possible unless I did eval of whole constructed statement. – Hrvoje Špoljar Oct 31 '14 at 9:27

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.