mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
Server prep: support stderr output on channels.
The vtable method underneath sshfwd_write now takes an is_stderr parameter, and in SSH-2, this is implemented by having separate stdout and stderr bufchains in each outgoing channel, and counting the size of both for the purposes of measuring backlog and so forth. To avoid making _most_ call sites more verbose, the usual macro wrapper hasn't changed its API; it just sets is_stderr=FALSE. To use the new feature, there's an sshfwd_write_ext macro that exposes the extra parameter.
This commit is contained in:
parent
61976b417e
commit
445030b3ea
@ -397,7 +397,8 @@ static void ssh1mainchan_hint_channel_is_simple(SshChannel *sc)
|
||||
{
|
||||
}
|
||||
|
||||
static int ssh1mainchan_write(SshChannel *sc, const void *data, int len)
|
||||
static int ssh1mainchan_write(
|
||||
SshChannel *sc, int is_stderr, const void *data, int len)
|
||||
{
|
||||
struct ssh1_connection_state *s =
|
||||
container_of(sc, struct ssh1_connection_state, mainchan_sc);
|
||||
|
@ -89,7 +89,8 @@ static const struct ConnectionLayerVtable ssh1_connlayer_vtable = {
|
||||
ssh1_set_wants_user_input,
|
||||
};
|
||||
|
||||
static int ssh1channel_write(SshChannel *c, const void *buf, int len);
|
||||
static int ssh1channel_write(
|
||||
SshChannel *c, int is_stderr, const void *buf, int len);
|
||||
static void ssh1channel_write_eof(SshChannel *c);
|
||||
static void ssh1channel_initiate_close(SshChannel *c, const char *err);
|
||||
static void ssh1channel_unthrottle(SshChannel *c, int bufsize);
|
||||
@ -581,7 +582,8 @@ static void ssh1channel_unthrottle(SshChannel *sc, int bufsize)
|
||||
}
|
||||
}
|
||||
|
||||
static int ssh1channel_write(SshChannel *sc, const void *buf, int len)
|
||||
static int ssh1channel_write(
|
||||
SshChannel *sc, int is_stderr, const void *buf, int len)
|
||||
{
|
||||
struct ssh1_channel *c = container_of(sc, struct ssh1_channel, sc);
|
||||
struct ssh1_connection_state *s = c->connlayer;
|
||||
|
@ -116,7 +116,8 @@ static char *ssh2_channel_open_failure_error_text(PktIn *pktin)
|
||||
return dupprintf("%s [%.*s]", reason_code_string, PTRLEN_PRINTF(reason));
|
||||
}
|
||||
|
||||
static int ssh2channel_write(SshChannel *c, const void *buf, int len);
|
||||
static int ssh2channel_write(
|
||||
SshChannel *c, int is_stderr, const void *buf, int len);
|
||||
static void ssh2channel_write_eof(SshChannel *c);
|
||||
static void ssh2channel_initiate_close(SshChannel *c, const char *err);
|
||||
static void ssh2channel_unthrottle(SshChannel *c, int bufsize);
|
||||
@ -215,6 +216,7 @@ struct outstanding_channel_request {
|
||||
static void ssh2_channel_free(struct ssh2_channel *c)
|
||||
{
|
||||
bufchain_clear(&c->outbuffer);
|
||||
bufchain_clear(&c->errbuffer);
|
||||
while (c->chanreq_head) {
|
||||
struct outstanding_channel_request *chanreq = c->chanreq_head;
|
||||
c->chanreq_head = c->chanreq_head->next;
|
||||
@ -741,6 +743,7 @@ static int ssh2_connection_filter_queue(struct ssh2_connection_state *s)
|
||||
* stuff.
|
||||
*/
|
||||
bufchain_clear(&c->outbuffer);
|
||||
bufchain_clear(&c->errbuffer);
|
||||
|
||||
/*
|
||||
* Send outgoing EOF.
|
||||
@ -980,7 +983,7 @@ static void ssh2_channel_try_eof(struct ssh2_channel *c)
|
||||
assert(c->pending_eof); /* precondition for calling us */
|
||||
if (c->halfopen)
|
||||
return; /* can't close: not even opened yet */
|
||||
if (bufchain_size(&c->outbuffer) > 0)
|
||||
if (bufchain_size(&c->outbuffer) > 0 || bufchain_size(&c->errbuffer) > 0)
|
||||
return; /* can't send EOF: pending outgoing data */
|
||||
|
||||
c->pending_eof = FALSE; /* we're about to send it */
|
||||
@ -1001,19 +1004,31 @@ static int ssh2_try_send(struct ssh2_channel *c)
|
||||
PktOut *pktout;
|
||||
int bufsize;
|
||||
|
||||
while (c->remwindow > 0 && bufchain_size(&c->outbuffer) > 0) {
|
||||
while (c->remwindow > 0 &&
|
||||
(bufchain_size(&c->outbuffer) > 0 ||
|
||||
bufchain_size(&c->errbuffer) > 0)) {
|
||||
int len;
|
||||
void *data;
|
||||
bufchain_prefix(&c->outbuffer, &data, &len);
|
||||
bufchain *buf = (bufchain_size(&c->errbuffer) > 0 ?
|
||||
&c->errbuffer : &c->outbuffer);
|
||||
|
||||
bufchain_prefix(buf, &data, &len);
|
||||
if ((unsigned)len > c->remwindow)
|
||||
len = c->remwindow;
|
||||
if ((unsigned)len > c->remmaxpkt)
|
||||
len = c->remmaxpkt;
|
||||
pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_CHANNEL_DATA);
|
||||
put_uint32(pktout, c->remoteid);
|
||||
if (buf == &c->errbuffer) {
|
||||
pktout = ssh_bpp_new_pktout(
|
||||
s->ppl.bpp, SSH2_MSG_CHANNEL_EXTENDED_DATA);
|
||||
put_uint32(pktout, c->remoteid);
|
||||
put_uint32(pktout, SSH2_EXTENDED_DATA_STDERR);
|
||||
} else {
|
||||
pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_CHANNEL_DATA);
|
||||
put_uint32(pktout, c->remoteid);
|
||||
}
|
||||
put_string(pktout, data, len);
|
||||
pq_push(s->ppl.out_pq, pktout);
|
||||
bufchain_consume(&c->outbuffer, len);
|
||||
bufchain_consume(buf, len);
|
||||
c->remwindow -= len;
|
||||
}
|
||||
|
||||
@ -1021,7 +1036,7 @@ static int ssh2_try_send(struct ssh2_channel *c)
|
||||
* After having sent as much data as we can, return the amount
|
||||
* still buffered.
|
||||
*/
|
||||
bufsize = bufchain_size(&c->outbuffer);
|
||||
bufsize = bufchain_size(&c->outbuffer) + bufchain_size(&c->errbuffer);
|
||||
|
||||
/*
|
||||
* And if there's no data pending but we need to send an EOF, send
|
||||
@ -1155,6 +1170,7 @@ void ssh2_channel_init(struct ssh2_channel *c)
|
||||
c->chanreq_head = NULL;
|
||||
c->throttle_state = UNTHROTTLED;
|
||||
bufchain_init(&c->outbuffer);
|
||||
bufchain_init(&c->errbuffer);
|
||||
c->sc.vt = &ssh2channel_vtable;
|
||||
c->sc.cl = &s->cl;
|
||||
c->localid = alloc_channel_id(s->channels, struct ssh2_channel);
|
||||
@ -1264,11 +1280,12 @@ static void ssh2channel_unthrottle(SshChannel *sc, int bufsize)
|
||||
}
|
||||
}
|
||||
|
||||
static int ssh2channel_write(SshChannel *sc, const void *buf, int len)
|
||||
static int ssh2channel_write(
|
||||
SshChannel *sc, int is_stderr, const void *buf, int len)
|
||||
{
|
||||
struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
|
||||
assert(!(c->closes & CLOSES_SENT_EOF));
|
||||
bufchain_add(&c->outbuffer, buf, len);
|
||||
bufchain_add(is_stderr ? &c->errbuffer : &c->outbuffer, buf, len);
|
||||
return ssh2_try_send(c);
|
||||
}
|
||||
|
||||
@ -1521,7 +1538,8 @@ static int ssh2_stdin_backlog(ConnectionLayer *cl)
|
||||
if (!s->mainchan)
|
||||
return 0;
|
||||
c = container_of(s->mainchan_sc, struct ssh2_channel, sc);
|
||||
return s->mainchan ? bufchain_size(&c->outbuffer) : 0;
|
||||
return s->mainchan ?
|
||||
bufchain_size(&c->outbuffer) + bufchain_size(&c->errbuffer) : 0;
|
||||
}
|
||||
|
||||
static void ssh2_throttle_all_channels(ConnectionLayer *cl, int throttled)
|
||||
|
@ -94,7 +94,7 @@ struct ssh2_channel {
|
||||
*/
|
||||
int throttled_by_backlog;
|
||||
|
||||
bufchain outbuffer;
|
||||
bufchain outbuffer, errbuffer;
|
||||
unsigned remwindow, remmaxpkt;
|
||||
/* locwindow is signed so we can cope with excess data. */
|
||||
int locwindow, locmaxwin;
|
||||
|
@ -104,7 +104,7 @@ Channel *zombiechan_new(void);
|
||||
*/
|
||||
|
||||
struct SshChannelVtable {
|
||||
int (*write)(SshChannel *c, const void *, int);
|
||||
int (*write)(SshChannel *c, int is_stderr, const void *, int);
|
||||
void (*write_eof)(SshChannel *c);
|
||||
void (*initiate_close)(SshChannel *c, const char *err);
|
||||
void (*unthrottle)(SshChannel *c, int bufsize);
|
||||
@ -160,7 +160,9 @@ struct SshChannel {
|
||||
ConnectionLayer *cl;
|
||||
};
|
||||
|
||||
#define sshfwd_write(c, buf, len) ((c)->vt->write(c, buf, len))
|
||||
#define sshfwd_write(c, buf, len) ((c)->vt->write(c, FALSE, buf, len))
|
||||
#define sshfwd_write_ext(c, stderr, buf, len) \
|
||||
((c)->vt->write(c, stderr, buf, len))
|
||||
#define sshfwd_write_eof(c) ((c)->vt->write_eof(c))
|
||||
#define sshfwd_initiate_close(c, err) ((c)->vt->initiate_close(c, err))
|
||||
#define sshfwd_unthrottle(c, bufsize) ((c)->vt->unthrottle(c, bufsize))
|
||||
|
Loading…
Reference in New Issue
Block a user