From 6f2d0cde66cd10cdbcefb1401c422f0171a48549 Mon Sep 17 00:00:00 2001 From: jacob Date: Sun, 17 Oct 2004 21:22:22 +0000 Subject: [PATCH] Support the SSH-2 mechanism for sending signals to a running session. Neither of the SSH servers I conveniently have access to (Debian stable OpenSSH -- 3.4p1 -- and lshd) seem to take a blind bit of notice, but the channel requests look fine to me in the packet log. I've included all the signals explicitly defined by draft-ietf-secsh-connect-19, but I've put the more obscure ones in a submenu of the specials menu; there's therefore been some minor upheaval to support such submenus. git-svn-id: svn://svn.tartarus.org/sgt/putty@4652 cda61777-01e9-0310-a592-d414129be87e --- doc/using.but | 18 +++++++++++++----- putty.h | 14 ++++++++++++-- ssh.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++----- telnet.c | 8 +++++--- unix/pterm.c | 43 ++++++++++++++++++++++++++++++++++++------- window.c | 55 ++++++++++++++++++++++++++++++++++++++++--------------- 6 files changed, 154 insertions(+), 37 deletions(-) diff --git a/doc/using.but b/doc/using.but index cda9b3b4..03619f69 100644 --- a/doc/using.but +++ b/doc/using.but @@ -1,4 +1,4 @@ -\versionid $Id: using.but,v 1.34 2004/10/13 13:43:11 simon Exp $ +\versionid $Id: using.but,v 1.35 2004/10/17 21:22:22 jacob Exp $ \C{using} Using PuTTY @@ -175,17 +175,25 @@ PuTTY can also be configured to send this when Ctrl-Z is typed; see In an SSH connection, the following special commands are available: +\b \I{IGNORE message, SSH special command}\I{No-op, in SSH}IGNORE message + +\lcont{ +Should have no effect. +} + \b \I{Break, SSH special command}Break \lcont{ -Optional extension; may not be supported by server. PuTTY requests the -server's default break length. +Only available in SSH-2, and only during a session. Optional +extension; may not be supported by server. PuTTY requests the server's +default break length. } -\b \I{IGNORE message, SSH special command}\I{No-op, in SSH}IGNORE message +\b \I{Signal, SSH special command}Signals (SIGINT, SIGTERM etc) \lcont{ -Should have no effect. +Only available in SSH-2, and only during a session. Sends various +POSIX signals. Not honoured by all servers. } \S2{using-newsession} Starting new sessions diff --git a/putty.h b/putty.h index e5cce125..81cb5c5f 100644 --- a/putty.h +++ b/putty.h @@ -127,13 +127,23 @@ struct unicode_data { #define LGTYP_PACKETS 3 /* logmode: SSH data packets */ typedef enum { + /* Actual special commands. Originally Telnet, but some codes have + * been re-used for similar specials in other protocols. */ TS_AYT, TS_BRK, TS_SYNCH, TS_EC, TS_EL, TS_GA, TS_NOP, TS_ABORT, TS_AO, TS_IP, TS_SUSP, TS_EOR, TS_EOF, TS_LECHO, TS_RECHO, TS_PING, - TS_EOL + TS_EOL, + /* POSIX-style signals. (not Telnet) */ + TS_SIGABRT, TS_SIGALRM, TS_SIGFPE, TS_SIGHUP, TS_SIGILL, + TS_SIGINT, TS_SIGKILL, TS_SIGPIPE, TS_SIGQUIT, TS_SIGSEGV, + TS_SIGTERM, TS_SIGUSR1, TS_SIGUSR2, + /* Pseudo-specials used for constructing the specials menu. */ + TS_SEP, /* Separator */ + TS_SUBMENU, /* Start a new submenu with specified name */ + TS_EXITMENU /* Exit current submenu or end of specials */ } Telnet_Special; struct telnet_special { - const char *name; /* NULL==end, ""==separator */ + const char *name; int code; }; diff --git a/ssh.c b/ssh.c index 4fed0b8c..c127fc5c 100644 --- a/ssh.c +++ b/ssh.c @@ -6885,12 +6885,25 @@ static const struct telnet_special *ssh_get_specials(void *handle) {"IGNORE message", TS_NOP}, }; static const struct telnet_special ssh2_session_specials[] = { - {"", 0}, - {"Break", TS_BRK} - /* XXX we should also support signals */ + {NULL, TS_SEP}, + {"Break", TS_BRK}, + /* These are the signal names defined by draft-ietf-secsh-connect-19. + * They include all the ISO C signals, but are a subset of the POSIX + * required signals. */ + {"SIGINT (Interrupt)", TS_SIGINT}, + {"SIGTERM (Terminate)", TS_SIGTERM}, + {"SIGKILL (Kill)", TS_SIGKILL}, + {"SIGQUIT (Quit)", TS_SIGQUIT}, + {"SIGHUP (Hangup)", TS_SIGHUP}, + {"More signals", TS_SUBMENU}, + {"SIGABRT", TS_SIGABRT}, {"SIGALRM", TS_SIGALRM}, + {"SIGFPE", TS_SIGFPE}, {"SIGILL", TS_SIGILL}, + {"SIGPIPE", TS_SIGPIPE}, {"SIGSEGV", TS_SIGSEGV}, + {"SIGUSR1", TS_SIGUSR1}, {"SIGUSR2", TS_SIGUSR2}, + {NULL, TS_EXITMENU} }; static const struct telnet_special specials_end[] = { - {NULL, 0} + {NULL, TS_EXITMENU} }; static struct telnet_special ssh_specials[lenof(ignore_special) + lenof(ssh2_session_specials) + @@ -6978,7 +6991,37 @@ static void ssh_special(void *handle, Telnet_Special code) ssh2_pkt_send(ssh); } } else { - /* do nothing */ + /* Is is a POSIX signal? */ + char *signame = NULL; + if (code == TS_SIGABRT) signame = "ABRT"; + if (code == TS_SIGALRM) signame = "ALRM"; + if (code == TS_SIGFPE) signame = "FPE"; + if (code == TS_SIGHUP) signame = "HUP"; + if (code == TS_SIGILL) signame = "ILL"; + if (code == TS_SIGINT) signame = "INT"; + if (code == TS_SIGKILL) signame = "KILL"; + if (code == TS_SIGPIPE) signame = "PIPE"; + if (code == TS_SIGQUIT) signame = "QUIT"; + if (code == TS_SIGSEGV) signame = "SEGV"; + if (code == TS_SIGTERM) signame = "TERM"; + if (code == TS_SIGUSR1) signame = "USR1"; + if (code == TS_SIGUSR2) signame = "USR2"; + /* The SSH-2 protocol does in principle support arbitrary named + * signals, including signame@domain, but we don't support those. */ + if (signame) { + /* It's a signal. */ + if (ssh->version == 2 && ssh->mainchan) { + ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_REQUEST); + ssh2_pkt_adduint32(ssh, ssh->mainchan->remoteid); + ssh2_pkt_addstring(ssh, "signal"); + ssh2_pkt_addbool(ssh, 0); + ssh2_pkt_addstring(ssh, signame); + ssh2_pkt_send(ssh); + logeventf(ssh, "Sent signal SIG%s", signame); + } + } else { + /* Never heard of it. Do nothing */ + } } } diff --git a/telnet.c b/telnet.c index 540c6d2c..67db0fd9 100644 --- a/telnet.c +++ b/telnet.c @@ -963,6 +963,8 @@ static void telnet_special(void *handle, Telnet_Special code) telnet->bufsize = sk_write(telnet->s, (char *)b, 2); } break; + default: + break; /* never heard of it */ } } @@ -976,15 +978,15 @@ static const struct telnet_special *telnet_get_specials(void *handle) {"Erase Line", TS_EL}, {"Go Ahead", TS_GA}, {"No Operation", TS_NOP}, - {"", 0}, + {NULL, TS_SEP}, {"Abort Process", TS_ABORT}, {"Abort Output", TS_AO}, {"Interrupt Process", TS_IP}, {"Suspend Process", TS_SUSP}, - {"", 0}, + {NULL, TS_SEP}, {"End Of Record", TS_EOR}, {"End Of File", TS_EOF}, - {NULL, 0} + {NULL, TS_EXITMENU} }; return specials; } diff --git a/unix/pterm.c b/unix/pterm.c index da1bc8d5..ca2dab88 100644 --- a/unix/pterm.c +++ b/unix/pterm.c @@ -3160,22 +3160,51 @@ void update_specials_menu(void *frontend) else specials = NULL; + /* I believe this disposes of submenus too. */ gtk_container_foreach(GTK_CONTAINER(inst->specialsmenu), (GtkCallback)gtk_widget_destroy, NULL); if (specials) { int i; - GtkWidget *menuitem; - for (i = 0; specials[i].name; i++) { - if (*specials[i].name) { + GtkWidget *menu = inst->specialsmenu; + /* A lame "stack" for submenus that will do for now. */ + GtkWidget *saved_menu = NULL; + int nesting = 1; + for (i = 0; nesting > 0; i++) { + GtkWidget *menuitem = NULL; + switch (specials[i].code) { + case TS_SUBMENU: + assert (nesting < 2); + saved_menu = menu; /* XXX lame stacking */ + menu = gtk_menu_new(); + menuitem = gtk_menu_item_new_with_label(specials[i].name); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu); + gtk_container_add(GTK_CONTAINER(saved_menu), menuitem); + gtk_widget_show(menuitem); + menuitem = NULL; + nesting++; + break; + case TS_EXITMENU: + nesting--; + if (nesting) { + menu = saved_menu; /* XXX lame stacking */ + saved_menu = NULL; + } + break; + case TS_SEP: + menuitem = gtk_menu_item_new(); + break; + default: menuitem = gtk_menu_item_new_with_label(specials[i].name); gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", GINT_TO_POINTER(specials[i].code)); gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(special_menuitem), inst); - } else - menuitem = gtk_menu_item_new(); - gtk_container_add(GTK_CONTAINER(inst->specialsmenu), menuitem); - gtk_widget_show(menuitem); + break; + } + if (menuitem) { + gtk_container_add(GTK_CONTAINER(menu), menuitem); + gtk_widget_show(menuitem); + } } gtk_widget_show(inst->specialsitem1); gtk_widget_show(inst->specialsitem2); diff --git a/window.c b/window.c index b2ff8af3..9f1dbe33 100644 --- a/window.c +++ b/window.c @@ -111,6 +111,7 @@ static struct unicode_data ucsdata; static int session_closed; static const struct telnet_special *specials; +static int n_specials; static struct { HMENU menu; @@ -915,20 +916,48 @@ void update_specials_menu(void *frontend) specials = NULL; if (specials) { - p = CreateMenu(); - for (i = 0; specials[i].name; i++) { + /* We can't use Windows to provide a stack for submenus, so + * here's a lame "stack" that will do for now. */ + HMENU saved_menu = NULL; + int nesting = 1; + p = CreatePopupMenu(); + for (i = 0; nesting > 0; i++) { assert(IDM_SPECIAL_MIN + 0x10 * i < IDM_SPECIAL_MAX); - if (*specials[i].name) + switch (specials[i].code) { + case TS_SEP: + AppendMenu(p, MF_SEPARATOR, 0, 0); + break; + case TS_SUBMENU: + assert(nesting < 2); + nesting++; + saved_menu = p; /* XXX lame stacking */ + p = CreatePopupMenu(); + AppendMenu(saved_menu, MF_POPUP | MF_ENABLED, + (UINT) p, specials[i].name); + break; + case TS_EXITMENU: + nesting--; + if (nesting) { + p = saved_menu; /* XXX lame stacking */ + saved_menu = NULL; + } + break; + default: AppendMenu(p, MF_ENABLED, IDM_SPECIAL_MIN + 0x10 * i, specials[i].name); - else - AppendMenu(p, MF_SEPARATOR, 0, 0); + break; + } } - } else + /* Squirrel the highest special. */ + n_specials = i - 1; + } else { p = NULL; + n_specials = 0; + } for (j = 0; j < lenof(popup_menus); j++) { if (menu_already_exists) { + /* XXX does this free up all submenus? */ DeleteMenu(popup_menus[j].menu, popup_menus[j].specials_submenu_pos, MF_BYPOSITION); @@ -2088,20 +2117,16 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } if (wParam >= IDM_SPECIAL_MIN && wParam <= IDM_SPECIAL_MAX) { int i = (wParam - IDM_SPECIAL_MIN) / 0x10; - int j; /* * Ensure we haven't been sent a bogus SYSCOMMAND * which would cause us to reference invalid memory * and crash. Perhaps I'm just too paranoid here. */ - for (j = 0; j < i; j++) - if (!specials || !specials[j].name) - break; - if (j == i) { - if (back) - back->special(backhandle, specials[i].code); - net_pending_errors(); - } + if (i >= n_specials) + break; + if (back) + back->special(backhandle, specials[i].code); + net_pending_errors(); } } break; -- 2.11.0