diff --git a/ssh.h b/ssh.h index c5c8cdb9..2858a14a 100644 --- a/ssh.h +++ b/ssh.h @@ -236,6 +236,9 @@ struct ConnectionLayerVtable { void (*sharing_queue_global_request)( ConnectionLayer *cl, ssh_sharing_connstate *connstate); + /* Indicate that the last downstream has disconnected */ + void (*sharing_no_more_downstreams)(ConnectionLayer *cl); + /* Query whether the connection layer is doing agent forwarding */ int (*agent_forwarding_permitted)(ConnectionLayer *cl); @@ -281,6 +284,8 @@ struct ConnectionLayer { ((cl)->vt->delete_sharing_channel(cl, ch)) #define ssh_sharing_queue_global_request(cl, cs) \ ((cl)->vt->sharing_queue_global_request(cl, cs)) +#define ssh_sharing_no_more_downstreams(cl) \ + ((cl)->vt->sharing_no_more_downstreams(cl)) #define ssh_agent_forwarding_permitted(cl) \ ((cl)->vt->agent_forwarding_permitted(cl)) #define ssh_terminal_size(cl, w, h) ((cl)->vt->terminal_size(cl, w, h)) diff --git a/ssh1connection.c b/ssh1connection.c index 3ae36bd3..86e16f90 100644 --- a/ssh1connection.c +++ b/ssh1connection.c @@ -119,6 +119,7 @@ static const struct ConnectionLayerVtable ssh1_connlayer_vtable = { NULL /* alloc_sharing_channel */, NULL /* delete_sharing_channel */, NULL /* sharing_queue_global_request */, + NULL /* sharing_no_more_downstreams */, ssh1_agent_forwarding_permitted, ssh1_terminal_size, ssh1_stdout_unthrottle, diff --git a/ssh2connection.c b/ssh2connection.c index ea03f575..71dd1007 100644 --- a/ssh2connection.c +++ b/ssh2connection.c @@ -127,6 +127,7 @@ static void ssh2_delete_sharing_channel( ConnectionLayer *cl, unsigned localid); static void ssh2_sharing_queue_global_request( ConnectionLayer *cl, ssh_sharing_connstate *share_ctx); +static void ssh2_sharing_no_more_downstreams(ConnectionLayer *cl); static int ssh2_agent_forwarding_permitted(ConnectionLayer *cl); static void ssh2_terminal_size(ConnectionLayer *cl, int width, int height); static void ssh2_stdout_unthrottle(ConnectionLayer *cl, int bufsize); @@ -144,6 +145,7 @@ static const struct ConnectionLayerVtable ssh2_connlayer_vtable = { ssh2_alloc_sharing_channel, ssh2_delete_sharing_channel, ssh2_sharing_queue_global_request, + ssh2_sharing_no_more_downstreams, ssh2_agent_forwarding_permitted, ssh2_terminal_size, ssh2_stdout_unthrottle, @@ -2096,6 +2098,13 @@ static void ssh2_sharing_queue_global_request( ssh2_queue_global_request_handler(s, ssh2_sharing_globreq_response, cs); } +static void ssh2_sharing_no_more_downstreams(ConnectionLayer *cl) +{ + struct ssh2_connection_state *s = + FROMFIELD(cl, struct ssh2_connection_state, cl); + queue_toplevel_callback(ssh2_check_termination_callback, s); +} + static struct X11FakeAuth *ssh2_add_sharing_x11_display( ConnectionLayer *cl, int authtype, ssh_sharing_connstate *share_cs, share_channel *share_chan) diff --git a/sshshare.c b/sshshare.c index 937b4da6..c425140a 100644 --- a/sshshare.c +++ b/sshshare.c @@ -905,12 +905,22 @@ static void share_try_cleanup(struct ssh_sharing_connstate *cs) if (count234(cs->halfchannels) == 0 && count234(cs->channels_by_us) == 0 && count234(cs->forwardings) == 0) { + struct ssh_sharing_state *sharestate = cs->parent; + /* * Now we're _really_ done, so we can get rid of cs completely. */ - del234(cs->parent->connections, cs); + del234(sharestate->connections, cs); log_downstream(cs, "disconnected"); share_connstate_free(cs); + + /* + * And if this was the last downstream, notify the connection + * layer, because it might now be time to wind up the whole + * SSH connection. + */ + if (count234(sharestate->connections) == 0 && sharestate->cl) + ssh_sharing_no_more_downstreams(sharestate->cl); } }