mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
610 lines
18 KiB
C
610 lines
18 KiB
C
|
/*
|
||
|
* SSH main session channel handling.
|
||
|
*/
|
||
|
|
||
|
#include <assert.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#include "putty.h"
|
||
|
#include "ssh.h"
|
||
|
#include "sshppl.h"
|
||
|
#include "sshchan.h"
|
||
|
|
||
|
static void mainchan_free(Channel *chan);
|
||
|
static void mainchan_open_confirmation(Channel *chan);
|
||
|
static void mainchan_open_failure(Channel *chan, const char *errtext);
|
||
|
static int mainchan_send(Channel *chan, int is_stderr, const void *, int);
|
||
|
static void mainchan_send_eof(Channel *chan);
|
||
|
static void mainchan_set_input_wanted(Channel *chan, int wanted);
|
||
|
static char *mainchan_log_close_msg(Channel *chan);
|
||
|
static int mainchan_rcvd_exit_status(Channel *chan, int status);
|
||
|
static int mainchan_rcvd_exit_signal(
|
||
|
Channel *chan, ptrlen signame, int core_dumped, ptrlen msg);
|
||
|
static int mainchan_rcvd_exit_signal_numeric(
|
||
|
Channel *chan, int signum, int core_dumped, ptrlen msg);
|
||
|
static void mainchan_request_response(Channel *chan, int success);
|
||
|
|
||
|
static const struct ChannelVtable mainchan_channelvt = {
|
||
|
mainchan_free,
|
||
|
mainchan_open_confirmation,
|
||
|
mainchan_open_failure,
|
||
|
mainchan_send,
|
||
|
mainchan_send_eof,
|
||
|
mainchan_set_input_wanted,
|
||
|
mainchan_log_close_msg,
|
||
|
chan_no_eager_close,
|
||
|
mainchan_rcvd_exit_status,
|
||
|
mainchan_rcvd_exit_signal,
|
||
|
mainchan_rcvd_exit_signal_numeric,
|
||
|
mainchan_request_response,
|
||
|
};
|
||
|
|
||
|
typedef enum MainChanType {
|
||
|
MAINCHAN_SESSION, MAINCHAN_DIRECT_TCPIP
|
||
|
} MainChanType;
|
||
|
|
||
|
typedef struct mainchan {
|
||
|
SshChannel *sc;
|
||
|
Conf *conf;
|
||
|
PacketProtocolLayer *ppl;
|
||
|
ConnectionLayer *cl;
|
||
|
|
||
|
MainChanType type;
|
||
|
int is_simple;
|
||
|
|
||
|
int req_x11, req_agent, req_pty, req_cmd_primary, req_cmd_fallback;
|
||
|
int n_req_env, n_env_replies, n_env_fails;
|
||
|
int eof_pending, eof_sent, got_pty, ready;
|
||
|
|
||
|
int term_width, term_height;
|
||
|
|
||
|
Channel chan;
|
||
|
} mainchan;
|
||
|
|
||
|
mainchan *mainchan_new(
|
||
|
PacketProtocolLayer *ppl, ConnectionLayer *cl, Conf *conf,
|
||
|
int term_width, int term_height, int is_simple, SshChannel **sc_out)
|
||
|
{
|
||
|
mainchan *mc;
|
||
|
|
||
|
if (conf_get_int(conf, CONF_ssh_no_shell))
|
||
|
return NULL; /* no main channel at all */
|
||
|
|
||
|
mc = snew(mainchan);
|
||
|
memset(mc, 0, sizeof(mainchan));
|
||
|
mc->ppl = ppl;
|
||
|
mc->cl = cl;
|
||
|
mc->conf = conf_copy(conf);
|
||
|
mc->term_width = term_width;
|
||
|
mc->term_height = term_height;
|
||
|
mc->is_simple = is_simple;
|
||
|
|
||
|
mc->sc = NULL;
|
||
|
mc->chan.vt = &mainchan_channelvt;
|
||
|
mc->chan.initial_fixed_window_size = 0;
|
||
|
|
||
|
if (*conf_get_str(mc->conf, CONF_ssh_nc_host)) {
|
||
|
const char *host = conf_get_str(mc->conf, CONF_ssh_nc_host);
|
||
|
int port = conf_get_int(mc->conf, CONF_ssh_nc_port);
|
||
|
|
||
|
mc->sc = ssh_lportfwd_open(cl, host, port, "main channel", &mc->chan);
|
||
|
mc->type = MAINCHAN_DIRECT_TCPIP;
|
||
|
} else {
|
||
|
mc->sc = ssh_session_open(cl, &mc->chan);
|
||
|
mc->type = MAINCHAN_SESSION;
|
||
|
}
|
||
|
|
||
|
*sc_out = mc->sc;
|
||
|
return mc;
|
||
|
}
|
||
|
|
||
|
static void mainchan_free(Channel *chan)
|
||
|
{
|
||
|
assert(chan->vt == &mainchan_channelvt);
|
||
|
mainchan *mc = container_of(chan, mainchan, chan);
|
||
|
conf_free(mc->conf);
|
||
|
sfree(mc);
|
||
|
}
|
||
|
|
||
|
static void mainchan_try_fallback_command(mainchan *mc);
|
||
|
static void mainchan_ready(mainchan *mc);
|
||
|
|
||
|
static void mainchan_open_confirmation(Channel *chan)
|
||
|
{
|
||
|
mainchan *mc = container_of(chan, mainchan, chan);
|
||
|
PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
|
||
|
|
||
|
seat_update_specials_menu(mc->ppl->seat);
|
||
|
ppl_logevent(("Opened main channel"));
|
||
|
|
||
|
if (mc->is_simple)
|
||
|
sshfwd_hint_channel_is_simple(mc->sc);
|
||
|
|
||
|
if (mc->type == MAINCHAN_SESSION) {
|
||
|
/*
|
||
|
* Send the CHANNEL_REQUESTS for the main session channel.
|
||
|
*/
|
||
|
char *key, *val, *cmd;
|
||
|
struct X11Display *x11disp;
|
||
|
struct X11FakeAuth *x11auth;
|
||
|
int retry_cmd_now = FALSE;
|
||
|
|
||
|
if (conf_get_int(mc->conf, CONF_x11_forward)) {;
|
||
|
char *x11_setup_err;
|
||
|
if ((x11disp = x11_setup_display(
|
||
|
conf_get_str(mc->conf, CONF_x11_display),
|
||
|
mc->conf, &x11_setup_err)) == NULL) {
|
||
|
ppl_logevent(("X11 forwarding not enabled: unable to"
|
||
|
" initialise X display: %s", x11_setup_err));
|
||
|
sfree(x11_setup_err);
|
||
|
} else {
|
||
|
x11auth = ssh_add_x11_display(
|
||
|
mc->cl, conf_get_int(mc->conf, CONF_x11_auth), x11disp);
|
||
|
|
||
|
sshfwd_request_x11_forwarding(
|
||
|
mc->sc, TRUE, x11auth->protoname, x11auth->datastring,
|
||
|
x11disp->screennum, FALSE);
|
||
|
mc->req_x11 = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ssh_agent_forwarding_permitted(mc->cl)) {
|
||
|
sshfwd_request_agent_forwarding(mc->sc, TRUE);
|
||
|
mc->req_agent = TRUE;
|
||
|
}
|
||
|
|
||
|
if (!conf_get_int(mc->conf, CONF_nopty)) {
|
||
|
sshfwd_request_pty(
|
||
|
mc->sc, TRUE, mc->conf, mc->term_width, mc->term_height);
|
||
|
mc->req_pty = TRUE;
|
||
|
}
|
||
|
|
||
|
for (val = conf_get_str_strs(mc->conf, CONF_environmt, NULL, &key);
|
||
|
val != NULL;
|
||
|
val = conf_get_str_strs(mc->conf, CONF_environmt, key, &key)) {
|
||
|
sshfwd_send_env_var(mc->sc, TRUE, key, val);
|
||
|
mc->n_req_env++;
|
||
|
}
|
||
|
if (mc->n_req_env)
|
||
|
ppl_logevent(("Sent %d environment variables", mc->n_req_env));
|
||
|
|
||
|
cmd = conf_get_str(mc->conf, CONF_remote_cmd);
|
||
|
if (conf_get_int(mc->conf, CONF_ssh_subsys)) {
|
||
|
retry_cmd_now = !sshfwd_start_subsystem(mc->sc, TRUE, cmd);
|
||
|
} else if (*cmd) {
|
||
|
sshfwd_start_command(mc->sc, TRUE, cmd);
|
||
|
} else {
|
||
|
sshfwd_start_shell(mc->sc, TRUE);
|
||
|
}
|
||
|
|
||
|
if (retry_cmd_now)
|
||
|
mainchan_try_fallback_command(mc);
|
||
|
else
|
||
|
mc->req_cmd_primary = TRUE;
|
||
|
|
||
|
} else {
|
||
|
ssh_set_ldisc_option(mc->cl, LD_ECHO, TRUE);
|
||
|
ssh_set_ldisc_option(mc->cl, LD_EDIT, TRUE);
|
||
|
mainchan_ready(mc);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void mainchan_try_fallback_command(mainchan *mc)
|
||
|
{
|
||
|
const char *cmd = conf_get_str(mc->conf, CONF_remote_cmd2);
|
||
|
if (conf_get_int(mc->conf, CONF_ssh_subsys2)) {
|
||
|
sshfwd_start_subsystem(mc->sc, TRUE, cmd);
|
||
|
} else {
|
||
|
sshfwd_start_command(mc->sc, TRUE, cmd);
|
||
|
}
|
||
|
mc->req_cmd_fallback = TRUE;
|
||
|
}
|
||
|
|
||
|
static void mainchan_request_response(Channel *chan, int success)
|
||
|
{
|
||
|
assert(chan->vt == &mainchan_channelvt);
|
||
|
mainchan *mc = container_of(chan, mainchan, chan);
|
||
|
PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
|
||
|
|
||
|
if (mc->req_x11) {
|
||
|
mc->req_x11 = FALSE;
|
||
|
|
||
|
if (success) {
|
||
|
ppl_logevent(("X11 forwarding enabled"));
|
||
|
ssh_enable_x_fwd(mc->cl);
|
||
|
} else {
|
||
|
ppl_logevent(("X11 forwarding refused"));
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (mc->req_agent) {
|
||
|
mc->req_agent = FALSE;
|
||
|
|
||
|
if (success) {
|
||
|
ppl_logevent(("Agent forwarding enabled"));
|
||
|
ssh_enable_agent_fwd(mc->cl);
|
||
|
} else {
|
||
|
ppl_logevent(("Agent forwarding refused"));
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (mc->req_pty) {
|
||
|
mc->req_pty = FALSE;
|
||
|
|
||
|
if (success) {
|
||
|
ppl_logevent(("Allocated pty"));
|
||
|
mc->got_pty = TRUE;
|
||
|
} else {
|
||
|
ppl_logevent(("Server refused to allocate pty"));
|
||
|
ppl_printf(("Server refused to allocate pty\r\n"));
|
||
|
ssh_set_ldisc_option(mc->cl, LD_ECHO, TRUE);
|
||
|
ssh_set_ldisc_option(mc->cl, LD_EDIT, TRUE);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (mc->n_env_replies < mc->n_req_env) {
|
||
|
int j = mc->n_env_replies++;
|
||
|
if (!success) {
|
||
|
ppl_logevent(("Server refused to set environment variable %s",
|
||
|
conf_get_str_nthstrkey(mc->conf,
|
||
|
CONF_environmt, j)));
|
||
|
mc->n_env_fails++;
|
||
|
}
|
||
|
|
||
|
if (mc->n_env_replies == mc->n_req_env) {
|
||
|
if (mc->n_env_fails == 0) {
|
||
|
ppl_logevent(("All environment variables successfully set"));
|
||
|
} else if (mc->n_env_fails == mc->n_req_env) {
|
||
|
ppl_logevent(("All environment variables refused"));
|
||
|
ppl_printf(("Server refused to set environment "
|
||
|
"variables\r\n"));
|
||
|
} else {
|
||
|
ppl_printf(("Server refused to set all environment "
|
||
|
"variables\r\n"));
|
||
|
}
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (mc->req_cmd_primary) {
|
||
|
mc->req_cmd_primary = FALSE;
|
||
|
|
||
|
if (success) {
|
||
|
ppl_logevent(("Started a shell/command"));
|
||
|
mainchan_ready(mc);
|
||
|
} else if (*conf_get_str(mc->conf, CONF_remote_cmd2)) {
|
||
|
ppl_logevent(("Primary command failed; attempting fallback"));
|
||
|
mainchan_try_fallback_command(mc);
|
||
|
} else {
|
||
|
/*
|
||
|
* If there's no remote_cmd2 configured, then we have no
|
||
|
* fallback command, so we've run out of options.
|
||
|
*/
|
||
|
ssh_sw_abort(mc->ppl->ssh,
|
||
|
"Server refused to start a shell/command");
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (mc->req_cmd_fallback) {
|
||
|
mc->req_cmd_fallback = FALSE;
|
||
|
|
||
|
if (success) {
|
||
|
ppl_logevent(("Started a shell/command"));
|
||
|
ssh_got_fallback_cmd(mc->ppl->ssh);
|
||
|
mainchan_ready(mc);
|
||
|
} else {
|
||
|
ssh_sw_abort(mc->ppl->ssh,
|
||
|
"Server refused to start a shell/command");
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void mainchan_ready(mainchan *mc)
|
||
|
{
|
||
|
mc->ready = TRUE;
|
||
|
|
||
|
ssh_set_wants_user_input(mc->cl, TRUE);
|
||
|
ssh_ppl_got_user_input(mc->ppl); /* in case any is already queued */
|
||
|
|
||
|
/* If an EOF arrived before we were ready, handle it now. */
|
||
|
if (mc->eof_pending) {
|
||
|
mc->eof_pending = FALSE;
|
||
|
mainchan_special_cmd(mc, SS_EOF, 0);
|
||
|
}
|
||
|
|
||
|
ssh_ldisc_update(mc->ppl->ssh);
|
||
|
queue_idempotent_callback(&mc->ppl->ic_process_queue);
|
||
|
}
|
||
|
|
||
|
struct mainchan_open_failure_abort_ctx {
|
||
|
Ssh *ssh;
|
||
|
char *abort_message;
|
||
|
};
|
||
|
|
||
|
static void mainchan_open_failure_abort(void *vctx)
|
||
|
{
|
||
|
struct mainchan_open_failure_abort_ctx *ctx =
|
||
|
(struct mainchan_open_failure_abort_ctx *)vctx;
|
||
|
ssh_sw_abort(
|
||
|
ctx->ssh, "Server refused to open main channel: %s",
|
||
|
ctx->abort_message);
|
||
|
sfree(ctx->abort_message);
|
||
|
sfree(ctx);
|
||
|
}
|
||
|
|
||
|
static void mainchan_open_failure(Channel *chan, const char *errtext)
|
||
|
{
|
||
|
assert(chan->vt == &mainchan_channelvt);
|
||
|
mainchan *mc = container_of(chan, mainchan, chan);
|
||
|
|
||
|
struct mainchan_open_failure_abort_ctx *ctx =
|
||
|
snew(struct mainchan_open_failure_abort_ctx);
|
||
|
|
||
|
ctx->ssh = mc->ppl->ssh;
|
||
|
ctx->abort_message = dupstr(errtext);
|
||
|
queue_toplevel_callback(mainchan_open_failure_abort, ctx);
|
||
|
}
|
||
|
|
||
|
static int mainchan_send(Channel *chan, int is_stderr,
|
||
|
const void *data, int length)
|
||
|
{
|
||
|
assert(chan->vt == &mainchan_channelvt);
|
||
|
mainchan *mc = container_of(chan, mainchan, chan);
|
||
|
return seat_output(mc->ppl->seat, is_stderr, data, length);
|
||
|
}
|
||
|
|
||
|
static void mainchan_send_eof(Channel *chan)
|
||
|
{
|
||
|
assert(chan->vt == &mainchan_channelvt);
|
||
|
mainchan *mc = container_of(chan, mainchan, chan);
|
||
|
PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
|
||
|
|
||
|
if (!mc->eof_sent & (seat_eof(mc->ppl->seat) || mc->got_pty)) {
|
||
|
/*
|
||
|
* Either seat_eof told us that the front end wants us to
|
||
|
* close the outgoing side of the connection as soon as we see
|
||
|
* EOF from the far end, or else we've unilaterally decided to
|
||
|
* do that because we've allocated a remote pty and hence EOF
|
||
|
* isn't a particularly meaningful concept.
|
||
|
*/
|
||
|
sshfwd_write_eof(mc->sc);
|
||
|
ppl_logevent(("Sent EOF message"));
|
||
|
}
|
||
|
mc->eof_sent = TRUE;
|
||
|
ssh_set_wants_user_input(mc->cl, FALSE); /* now stop reading from stdin */
|
||
|
}
|
||
|
|
||
|
static void mainchan_set_input_wanted(Channel *chan, int wanted)
|
||
|
{
|
||
|
assert(chan->vt == &mainchan_channelvt);
|
||
|
mainchan *mc = container_of(chan, mainchan, chan);
|
||
|
|
||
|
/*
|
||
|
* This is the main channel of the SSH session, i.e. the one tied
|
||
|
* to the standard input (or GUI) of the primary SSH client user
|
||
|
* interface. So ssh->send_ok is how we control whether we're
|
||
|
* reading from that input.
|
||
|
*/
|
||
|
ssh_set_wants_user_input(mc->cl, wanted);
|
||
|
}
|
||
|
|
||
|
static char *mainchan_log_close_msg(Channel *chan)
|
||
|
{
|
||
|
return dupstr("Main session channel closed");
|
||
|
}
|
||
|
|
||
|
static int mainchan_rcvd_exit_status(Channel *chan, int status)
|
||
|
{
|
||
|
assert(chan->vt == &mainchan_channelvt);
|
||
|
mainchan *mc = container_of(chan, mainchan, chan);
|
||
|
PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
|
||
|
|
||
|
ssh_got_exitcode(mc->ppl->ssh, status);
|
||
|
ppl_logevent(("Session sent command exit status %d", status));
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static void mainchan_log_exit_signal_common(
|
||
|
mainchan *mc, const char *sigdesc,
|
||
|
int core_dumped, ptrlen msg)
|
||
|
{
|
||
|
PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
|
||
|
|
||
|
const char *core_msg = core_dumped ? " (core dumped)" : "";
|
||
|
const char *msg_pre = (msg.len ? " (" : "");
|
||
|
const char *msg_post = (msg.len ? ")" : "");
|
||
|
ppl_logevent(("Session exited on %s%s%s%.*s%s",
|
||
|
sigdesc, core_msg, msg_pre, PTRLEN_PRINTF(msg), msg_post));
|
||
|
}
|
||
|
|
||
|
static int mainchan_rcvd_exit_signal(
|
||
|
Channel *chan, ptrlen signame, int core_dumped, ptrlen msg)
|
||
|
{
|
||
|
assert(chan->vt == &mainchan_channelvt);
|
||
|
mainchan *mc = container_of(chan, mainchan, chan);
|
||
|
int exitcode;
|
||
|
char *signame_str;
|
||
|
|
||
|
/*
|
||
|
* Translate the signal description back into a locally
|
||
|
* meaningful number.
|
||
|
*/
|
||
|
|
||
|
if (0)
|
||
|
;
|
||
|
#define TRANSLATE_SIGNAL(s) \
|
||
|
else if (ptrlen_eq_string(signame, #s)) \
|
||
|
exitcode = 128 + SIG ## s
|
||
|
#ifdef SIGABRT
|
||
|
TRANSLATE_SIGNAL(ABRT);
|
||
|
#endif
|
||
|
#ifdef SIGALRM
|
||
|
TRANSLATE_SIGNAL(ALRM);
|
||
|
#endif
|
||
|
#ifdef SIGFPE
|
||
|
TRANSLATE_SIGNAL(FPE);
|
||
|
#endif
|
||
|
#ifdef SIGHUP
|
||
|
TRANSLATE_SIGNAL(HUP);
|
||
|
#endif
|
||
|
#ifdef SIGILL
|
||
|
TRANSLATE_SIGNAL(ILL);
|
||
|
#endif
|
||
|
#ifdef SIGINT
|
||
|
TRANSLATE_SIGNAL(INT);
|
||
|
#endif
|
||
|
#ifdef SIGKILL
|
||
|
TRANSLATE_SIGNAL(KILL);
|
||
|
#endif
|
||
|
#ifdef SIGPIPE
|
||
|
TRANSLATE_SIGNAL(PIPE);
|
||
|
#endif
|
||
|
#ifdef SIGQUIT
|
||
|
TRANSLATE_SIGNAL(QUIT);
|
||
|
#endif
|
||
|
#ifdef SIGSEGV
|
||
|
TRANSLATE_SIGNAL(SEGV);
|
||
|
#endif
|
||
|
#ifdef SIGTERM
|
||
|
TRANSLATE_SIGNAL(TERM);
|
||
|
#endif
|
||
|
#ifdef SIGUSR1
|
||
|
TRANSLATE_SIGNAL(USR1);
|
||
|
#endif
|
||
|
#ifdef SIGUSR2
|
||
|
TRANSLATE_SIGNAL(USR2);
|
||
|
#endif
|
||
|
#undef TRANSLATE_SIGNAL
|
||
|
else
|
||
|
exitcode = 128;
|
||
|
|
||
|
ssh_got_exitcode(mc->ppl->ssh, exitcode);
|
||
|
if (exitcode == 128)
|
||
|
signame_str = dupprintf("unrecognised signal \"%.*s\"",
|
||
|
PTRLEN_PRINTF(signame));
|
||
|
else
|
||
|
signame_str = dupprintf("signal SIG%.*s", PTRLEN_PRINTF(signame));
|
||
|
mainchan_log_exit_signal_common(mc, signame_str, core_dumped, msg);
|
||
|
sfree(signame_str);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static int mainchan_rcvd_exit_signal_numeric(
|
||
|
Channel *chan, int signum, int core_dumped, ptrlen msg)
|
||
|
{
|
||
|
assert(chan->vt == &mainchan_channelvt);
|
||
|
mainchan *mc = container_of(chan, mainchan, chan);
|
||
|
char *signum_str;
|
||
|
|
||
|
ssh_got_exitcode(mc->ppl->ssh, 128 + signum);
|
||
|
signum_str = dupprintf("signal %d", signum);
|
||
|
mainchan_log_exit_signal_common(mc, signum_str, core_dumped, msg);
|
||
|
sfree(signum_str);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* List of signal names defined by RFC 4254. These include all the ISO
|
||
|
* C signals, but are a subset of the POSIX required signals.
|
||
|
*
|
||
|
* The list macro takes parameters MAIN and SUB, which is an arbitrary
|
||
|
* UI decision to expose the signals we think users are most likely to
|
||
|
* want, with extra descriptive text, and relegate the less probable
|
||
|
* ones to a submenu for people who know what they're doing.
|
||
|
*/
|
||
|
#define SIGNAL_LIST(MAIN, SUB) \
|
||
|
MAIN(INT, "Interrupt") \
|
||
|
MAIN(TERM, "Terminate") \
|
||
|
MAIN(KILL, "Kill") \
|
||
|
MAIN(QUIT, "Quit") \
|
||
|
MAIN(HUP, "Hangup") \
|
||
|
SUB(ABRT) \
|
||
|
SUB(ALRM) \
|
||
|
SUB(FPE) \
|
||
|
SUB(ILL) \
|
||
|
SUB(PIPE) \
|
||
|
SUB(SEGV) \
|
||
|
SUB(USR1) \
|
||
|
SUB(USR2) \
|
||
|
/* end of list */
|
||
|
|
||
|
void mainchan_get_specials(
|
||
|
mainchan *mc, add_special_fn_t add_special, void *ctx)
|
||
|
{
|
||
|
/* FIXME: this _does_ depend on whether these services are supported */
|
||
|
|
||
|
add_special(ctx, "Break", SS_BRK, 0);
|
||
|
|
||
|
#define ADD_MAIN(name, desc) \
|
||
|
add_special(ctx, "SIG" #name " (" desc ")", SS_SIG ## name, 0);
|
||
|
#define ADD_SUB(name) \
|
||
|
add_special(ctx, "SIG" #name, SS_SIG ## name, 0);
|
||
|
|
||
|
#define NO_ADD_SUB(name)
|
||
|
#define NO_ADD_MAIN(name, desc)
|
||
|
|
||
|
SIGNAL_LIST(ADD_MAIN, NO_ADD_SUB);
|
||
|
add_special(ctx, "More signals", SS_SUBMENU, 0);
|
||
|
SIGNAL_LIST(NO_ADD_MAIN, ADD_SUB);
|
||
|
add_special(ctx, NULL, SS_EXITMENU, 0);
|
||
|
|
||
|
#undef ADD_MAIN
|
||
|
#undef ADD_SUB
|
||
|
#undef NO_ADD_MAIN
|
||
|
#undef NO_ADD_SUB
|
||
|
}
|
||
|
|
||
|
static const char *ssh_signal_lookup(SessionSpecialCode code)
|
||
|
{
|
||
|
#define CHECK_SUB(name) \
|
||
|
if (code == SS_SIG ## name) return #name;
|
||
|
#define CHECK_MAIN(name, desc) CHECK_SUB(name)
|
||
|
|
||
|
SIGNAL_LIST(CHECK_MAIN, CHECK_SUB);
|
||
|
return NULL;
|
||
|
|
||
|
#undef CHECK_MAIN
|
||
|
#undef CHECK_SUB
|
||
|
}
|
||
|
|
||
|
void mainchan_special_cmd(mainchan *mc, SessionSpecialCode code, int arg)
|
||
|
{
|
||
|
PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
|
||
|
const char *signame;
|
||
|
|
||
|
if (code == SS_EOF) {
|
||
|
if (!mc->ready) {
|
||
|
/*
|
||
|
* Buffer the EOF to send as soon as the main channel is
|
||
|
* fully set up.
|
||
|
*/
|
||
|
mc->eof_pending = TRUE;
|
||
|
} else if (!mc->eof_sent) {
|
||
|
sshfwd_write_eof(mc->sc);
|
||
|
mc->eof_sent = TRUE;
|
||
|
}
|
||
|
} else if (code == SS_BRK) {
|
||
|
sshfwd_send_serial_break(
|
||
|
mc->sc, FALSE, 0 /* default break length */);
|
||
|
} else if ((signame = ssh_signal_lookup(code)) != NULL) {
|
||
|
/* It's a signal. */
|
||
|
sshfwd_send_signal(mc->sc, FALSE, signame);
|
||
|
ppl_logevent(("Sent signal SIG%s", signame));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void mainchan_terminal_size(mainchan *mc, int width, int height)
|
||
|
{
|
||
|
mc->term_width = width;
|
||
|
mc->term_height = height;
|
||
|
|
||
|
if (mc->req_pty || mc->got_pty)
|
||
|
sshfwd_send_terminal_size_change(mc->sc, width, height);
|
||
|
}
|