From c59c6a8db99857b8e3aee8d620b3b4bfbe3f8fcc Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Fri, 8 May 2015 19:50:48 +0100 Subject: [PATCH] Unix Pageant: -T option, tying lifetime to controlling tty. This is intended to be a useful mode when you want to run an ssh agent in a terminal session with no X11 available. You just execute a command along the lines of eval $(pageant -T), and then Pageant will run in the background for the rest of that terminal session - and when the terminal session ends, so that Pageant loses its controlling tty, it will take that as the signal to shut down. So, no need to manually kill it, and unlike 'pageant --exec $SHELL', you can also do this half way through a session if you don't realise until later that you need an SSH agent, without losing any shell command history or other shell context that you've accumulated so far in the session. Unfortunately, I haven't been able to find any reliable way to actually implement this -T mode, short of having Pageant wake up at regular intervals and try to open /dev/tty to see if it's still there. I had hoped that I could arrange to reliably get SIGHUP, or select on /dev/tty for exceptional conditions, or some such, but nothing I've tried along those lines seems to work. --- unix/uxpgnt.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/unix/uxpgnt.c b/unix/uxpgnt.c index c0b63c78..eb7f0359 100644 --- a/unix/uxpgnt.c +++ b/unix/uxpgnt.c @@ -10,6 +10,7 @@ #include #include +#include #include #define PUTTY_DO_GLOBALS /* actually _define_ globals */ @@ -180,7 +181,7 @@ void pageant_print_env(int pid) socketname, (int)pid); } -void pageant_fork_and_print_env(void) +void pageant_fork_and_print_env(int retain_tty) { pid_t pid = fork(); if (pid == -1) { @@ -211,7 +212,16 @@ void pageant_fork_and_print_env(void) } close(0); close(1); - setsid(); + if (retain_tty) { + /* Get out of our previous process group, to avoid being + * blasted by passing signals. But keep our controlling tty, + * so we can keep checking to see if we still have one. */ + setpgrp(); + } else { + /* Do that, but also leave our entire session and detach from + * the controlling tty (if any). */ + setsid(); + } } int signalpipe[2]; @@ -222,6 +232,13 @@ void sigchld(int signum) /* not much we can do about it */; } +#define TTY_LIFE_POLL_INTERVAL (TICKSPERSEC * 30) +void *dummy_timer_ctx; +static void tty_life_timer(void *ctx, unsigned long now) +{ + schedule_timer(TTY_LIFE_POLL_INTERVAL, tty_life_timer, &dummy_timer_ctx); +} + int main(int argc, char **argv) { int *fdlist; @@ -234,7 +251,7 @@ int main(int argc, char **argv) struct pageant_listen_state *pl; Socket sock; enum { - LIFE_UNSPEC, LIFE_X11, LIFE_DEBUG, LIFE_PERM, LIFE_EXEC + LIFE_UNSPEC, LIFE_X11, LIFE_TTY, LIFE_DEBUG, LIFE_PERM, LIFE_EXEC } life = LIFE_UNSPEC; const char *display = NULL; int doing_opts = TRUE; @@ -261,6 +278,8 @@ int main(int argc, char **argv) pageant_logfp = stderr; } else if (!strcmp(p, "-X")) { life = LIFE_X11; + } else if (!strcmp(p, "-T")) { + life = LIFE_TTY; } else if (!strcmp(p, "--debug")) { life = LIFE_DEBUG; } else if (!strcmp(p, "--permanent")) { @@ -370,9 +389,13 @@ int main(int argc, char **argv) smemclr(greeting, greetinglen); sfree(greeting); - pageant_fork_and_print_env(); + pageant_fork_and_print_env(FALSE); + } else if (life == LIFE_TTY) { + schedule_timer(TTY_LIFE_POLL_INTERVAL, + tty_life_timer, &dummy_timer_ctx); + pageant_fork_and_print_env(TRUE); } else if (life == LIFE_PERM) { - pageant_fork_and_print_env(); + pageant_fork_and_print_env(FALSE); } else if (life == LIFE_DEBUG) { pageant_print_env(getpid()); pageant_logfp = stdout; @@ -491,6 +514,27 @@ int main(int argc, char **argv) exit(1); } + if (life == LIFE_TTY) { + /* + * Every time we wake up (whether it was due to tty_timer + * elapsing or for any other reason), poll to see if we + * still have a controlling terminal. If we don't, then + * our containing tty session has ended, so it's time to + * clean up and leave. + */ + int fd = open("/dev/tty", O_RDONLY); + if (fd < 0) { + if (errno != ENXIO) { + perror("/dev/tty: open"); + exit(1); + } + time_to_die = TRUE; + break; + } else { + close(fd); + } + } + for (i = 0; i < fdcount; i++) { fd = fdlist[i]; /* -- 2.45.2