mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-25 01:02:24 +00:00
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. [originally from svn r4652]
This commit is contained in:
parent
1efa60e40d
commit
17b0d8c58e
@ -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
|
\C{using} Using PuTTY
|
||||||
|
|
||||||
@ -175,19 +175,27 @@ PuTTY can also be configured to send this when Ctrl-Z is typed; see
|
|||||||
|
|
||||||
In an SSH connection, the following special commands are available:
|
In an SSH connection, the following special commands are available:
|
||||||
|
|
||||||
\b \I{Break, SSH special command}Break
|
|
||||||
|
|
||||||
\lcont{
|
|
||||||
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{IGNORE message, SSH special command}\I{No-op, in SSH}IGNORE message
|
||||||
|
|
||||||
\lcont{
|
\lcont{
|
||||||
Should have no effect.
|
Should have no effect.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
\b \I{Break, SSH special command}Break
|
||||||
|
|
||||||
|
\lcont{
|
||||||
|
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{Signal, SSH special command}Signals (SIGINT, SIGTERM etc)
|
||||||
|
|
||||||
|
\lcont{
|
||||||
|
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
|
\S2{using-newsession} Starting new sessions
|
||||||
|
|
||||||
PuTTY's system menu provides some shortcut ways to start new
|
PuTTY's system menu provides some shortcut ways to start new
|
||||||
|
14
putty.h
14
putty.h
@ -127,13 +127,23 @@ struct unicode_data {
|
|||||||
#define LGTYP_PACKETS 3 /* logmode: SSH data packets */
|
#define LGTYP_PACKETS 3 /* logmode: SSH data packets */
|
||||||
|
|
||||||
typedef enum {
|
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_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_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;
|
} Telnet_Special;
|
||||||
|
|
||||||
struct telnet_special {
|
struct telnet_special {
|
||||||
const char *name; /* NULL==end, ""==separator */
|
const char *name;
|
||||||
int code;
|
int code;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
53
ssh.c
53
ssh.c
@ -6885,12 +6885,25 @@ static const struct telnet_special *ssh_get_specials(void *handle)
|
|||||||
{"IGNORE message", TS_NOP},
|
{"IGNORE message", TS_NOP},
|
||||||
};
|
};
|
||||||
static const struct telnet_special ssh2_session_specials[] = {
|
static const struct telnet_special ssh2_session_specials[] = {
|
||||||
{"", 0},
|
{NULL, TS_SEP},
|
||||||
{"Break", TS_BRK}
|
{"Break", TS_BRK},
|
||||||
/* XXX we should also support signals */
|
/* 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[] = {
|
static const struct telnet_special specials_end[] = {
|
||||||
{NULL, 0}
|
{NULL, TS_EXITMENU}
|
||||||
};
|
};
|
||||||
static struct telnet_special ssh_specials[lenof(ignore_special) +
|
static struct telnet_special ssh_specials[lenof(ignore_special) +
|
||||||
lenof(ssh2_session_specials) +
|
lenof(ssh2_session_specials) +
|
||||||
@ -6978,7 +6991,37 @@ static void ssh_special(void *handle, Telnet_Special code)
|
|||||||
ssh2_pkt_send(ssh);
|
ssh2_pkt_send(ssh);
|
||||||
}
|
}
|
||||||
} else {
|
} 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 */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
8
telnet.c
8
telnet.c
@ -963,6 +963,8 @@ static void telnet_special(void *handle, Telnet_Special code)
|
|||||||
telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
|
telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
|
||||||
}
|
}
|
||||||
break;
|
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},
|
{"Erase Line", TS_EL},
|
||||||
{"Go Ahead", TS_GA},
|
{"Go Ahead", TS_GA},
|
||||||
{"No Operation", TS_NOP},
|
{"No Operation", TS_NOP},
|
||||||
{"", 0},
|
{NULL, TS_SEP},
|
||||||
{"Abort Process", TS_ABORT},
|
{"Abort Process", TS_ABORT},
|
||||||
{"Abort Output", TS_AO},
|
{"Abort Output", TS_AO},
|
||||||
{"Interrupt Process", TS_IP},
|
{"Interrupt Process", TS_IP},
|
||||||
{"Suspend Process", TS_SUSP},
|
{"Suspend Process", TS_SUSP},
|
||||||
{"", 0},
|
{NULL, TS_SEP},
|
||||||
{"End Of Record", TS_EOR},
|
{"End Of Record", TS_EOR},
|
||||||
{"End Of File", TS_EOF},
|
{"End Of File", TS_EOF},
|
||||||
{NULL, 0}
|
{NULL, TS_EXITMENU}
|
||||||
};
|
};
|
||||||
return specials;
|
return specials;
|
||||||
}
|
}
|
||||||
|
43
unix/pterm.c
43
unix/pterm.c
@ -3160,22 +3160,51 @@ void update_specials_menu(void *frontend)
|
|||||||
else
|
else
|
||||||
specials = NULL;
|
specials = NULL;
|
||||||
|
|
||||||
|
/* I believe this disposes of submenus too. */
|
||||||
gtk_container_foreach(GTK_CONTAINER(inst->specialsmenu),
|
gtk_container_foreach(GTK_CONTAINER(inst->specialsmenu),
|
||||||
(GtkCallback)gtk_widget_destroy, NULL);
|
(GtkCallback)gtk_widget_destroy, NULL);
|
||||||
if (specials) {
|
if (specials) {
|
||||||
int i;
|
int i;
|
||||||
GtkWidget *menuitem;
|
GtkWidget *menu = inst->specialsmenu;
|
||||||
for (i = 0; specials[i].name; i++) {
|
/* A lame "stack" for submenus that will do for now. */
|
||||||
if (*specials[i].name) {
|
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);
|
menuitem = gtk_menu_item_new_with_label(specials[i].name);
|
||||||
gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",
|
gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",
|
||||||
GINT_TO_POINTER(specials[i].code));
|
GINT_TO_POINTER(specials[i].code));
|
||||||
gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
|
gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
|
||||||
GTK_SIGNAL_FUNC(special_menuitem), inst);
|
GTK_SIGNAL_FUNC(special_menuitem), inst);
|
||||||
} else
|
break;
|
||||||
menuitem = gtk_menu_item_new();
|
}
|
||||||
gtk_container_add(GTK_CONTAINER(inst->specialsmenu), menuitem);
|
if (menuitem) {
|
||||||
gtk_widget_show(menuitem);
|
gtk_container_add(GTK_CONTAINER(menu), menuitem);
|
||||||
|
gtk_widget_show(menuitem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
gtk_widget_show(inst->specialsitem1);
|
gtk_widget_show(inst->specialsitem1);
|
||||||
gtk_widget_show(inst->specialsitem2);
|
gtk_widget_show(inst->specialsitem2);
|
||||||
|
55
window.c
55
window.c
@ -111,6 +111,7 @@ static struct unicode_data ucsdata;
|
|||||||
static int session_closed;
|
static int session_closed;
|
||||||
|
|
||||||
static const struct telnet_special *specials;
|
static const struct telnet_special *specials;
|
||||||
|
static int n_specials;
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
HMENU menu;
|
HMENU menu;
|
||||||
@ -915,20 +916,48 @@ void update_specials_menu(void *frontend)
|
|||||||
specials = NULL;
|
specials = NULL;
|
||||||
|
|
||||||
if (specials) {
|
if (specials) {
|
||||||
p = CreateMenu();
|
/* We can't use Windows to provide a stack for submenus, so
|
||||||
for (i = 0; specials[i].name; i++) {
|
* 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);
|
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,
|
AppendMenu(p, MF_ENABLED, IDM_SPECIAL_MIN + 0x10 * i,
|
||||||
specials[i].name);
|
specials[i].name);
|
||||||
else
|
break;
|
||||||
AppendMenu(p, MF_SEPARATOR, 0, 0);
|
}
|
||||||
}
|
}
|
||||||
} else
|
/* Squirrel the highest special. */
|
||||||
|
n_specials = i - 1;
|
||||||
|
} else {
|
||||||
p = NULL;
|
p = NULL;
|
||||||
|
n_specials = 0;
|
||||||
|
}
|
||||||
|
|
||||||
for (j = 0; j < lenof(popup_menus); j++) {
|
for (j = 0; j < lenof(popup_menus); j++) {
|
||||||
if (menu_already_exists) {
|
if (menu_already_exists) {
|
||||||
|
/* XXX does this free up all submenus? */
|
||||||
DeleteMenu(popup_menus[j].menu,
|
DeleteMenu(popup_menus[j].menu,
|
||||||
popup_menus[j].specials_submenu_pos,
|
popup_menus[j].specials_submenu_pos,
|
||||||
MF_BYPOSITION);
|
MF_BYPOSITION);
|
||||||
@ -2088,20 +2117,16 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
|
|||||||
}
|
}
|
||||||
if (wParam >= IDM_SPECIAL_MIN && wParam <= IDM_SPECIAL_MAX) {
|
if (wParam >= IDM_SPECIAL_MIN && wParam <= IDM_SPECIAL_MAX) {
|
||||||
int i = (wParam - IDM_SPECIAL_MIN) / 0x10;
|
int i = (wParam - IDM_SPECIAL_MIN) / 0x10;
|
||||||
int j;
|
|
||||||
/*
|
/*
|
||||||
* Ensure we haven't been sent a bogus SYSCOMMAND
|
* Ensure we haven't been sent a bogus SYSCOMMAND
|
||||||
* which would cause us to reference invalid memory
|
* which would cause us to reference invalid memory
|
||||||
* and crash. Perhaps I'm just too paranoid here.
|
* and crash. Perhaps I'm just too paranoid here.
|
||||||
*/
|
*/
|
||||||
for (j = 0; j < i; j++)
|
if (i >= n_specials)
|
||||||
if (!specials || !specials[j].name)
|
break;
|
||||||
break;
|
if (back)
|
||||||
if (j == i) {
|
back->special(backhandle, specials[i].code);
|
||||||
if (back)
|
net_pending_errors();
|
||||||
back->special(backhandle, specials[i].code);
|
|
||||||
net_pending_errors();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user