#include "storage.h"
#include "tree234.h"
-#define WM_AGENT_CALLBACK (WM_XUSER + 4)
-
-#define MAX_STDIN_BACKLOG 4096
+#define WM_AGENT_CALLBACK (WM_APP + 4)
struct agent_callback {
void (*callback)(void *, void *, int);
vfprintf(stderr, p, ap);
va_end(ap);
fputc('\n', stderr);
+ if (logctx) {
+ log_free(logctx);
+ logctx = NULL;
+ }
cleanup_exit(1);
}
void modalfatalbox(char *p, ...)
vfprintf(stderr, p, ap);
va_end(ap);
fputc('\n', stderr);
+ if (logctx) {
+ log_free(logctx);
+ logctx = NULL;
+ }
cleanup_exit(1);
}
+void nonfatal(char *p, ...)
+{
+ va_list ap;
+ fprintf(stderr, "ERROR: ");
+ va_start(ap, p);
+ vfprintf(stderr, p, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+ if (logctx) {
+ log_free(logctx);
+ logctx = NULL;
+ }
+}
void connection_fatal(void *frontend, char *p, ...)
{
va_list ap;
vfprintf(stderr, p, ap);
va_end(ap);
fputc('\n', stderr);
+ if (logctx) {
+ log_free(logctx);
+ logctx = NULL;
+ }
cleanup_exit(1);
}
void cmdline_error(char *p, ...)
}
HANDLE inhandle, outhandle, errhandle;
+struct handle *stdin_handle, *stdout_handle, *stderr_handle;
DWORD orig_console_mode;
+int connopen;
WSAEVENT netevent;
static Backend *back;
static void *backhandle;
-static Config cfg;
+static Conf *conf;
int term_ldisc(Terminal *term, int mode)
{
SetConsoleMode(inhandle, mode);
}
-struct input_data {
- DWORD len;
- char buffer[4096];
- HANDLE event, eventback;
-};
+char *get_ttymode(void *frontend, const char *mode) { return NULL; }
-static DWORD WINAPI stdin_read_thread(void *param)
+int from_backend(void *frontend_handle, int is_stderr,
+ const char *data, int len)
{
- struct input_data *idata = (struct input_data *) param;
- HANDLE inhandle;
-
- inhandle = GetStdHandle(STD_INPUT_HANDLE);
-
- while (ReadFile(inhandle, idata->buffer, sizeof(idata->buffer),
- &idata->len, NULL) && idata->len > 0) {
- SetEvent(idata->event);
- WaitForSingleObject(idata->eventback, INFINITE);
+ if (is_stderr) {
+ handle_write(stderr_handle, data, len);
+ } else {
+ handle_write(stdout_handle, data, len);
}
- idata->len = 0;
- SetEvent(idata->event);
-
- return 0;
+ return handle_backlog(stdout_handle) + handle_backlog(stderr_handle);
}
-struct output_data {
- DWORD len, lenwritten;
- int writeret;
- char *buffer;
- int is_stderr, done;
- HANDLE event, eventback;
- int busy;
-};
-
-static DWORD WINAPI stdout_write_thread(void *param)
+int from_backend_untrusted(void *frontend_handle, const char *data, int len)
{
- struct output_data *odata = (struct output_data *) param;
- HANDLE outhandle, errhandle;
-
- outhandle = GetStdHandle(STD_OUTPUT_HANDLE);
- errhandle = GetStdHandle(STD_ERROR_HANDLE);
-
- while (1) {
- WaitForSingleObject(odata->eventback, INFINITE);
- if (odata->done)
- break;
- odata->writeret =
- WriteFile(odata->is_stderr ? errhandle : outhandle,
- odata->buffer, odata->len, &odata->lenwritten, NULL);
- SetEvent(odata->event);
- }
-
- return 0;
+ /*
+ * No "untrusted" output should get here (the way the code is
+ * currently, it's all diverted by FLAG_STDERR).
+ */
+ assert(!"Unexpected call to from_backend_untrusted()");
+ return 0; /* not reached */
}
-bufchain stdout_data, stderr_data;
-struct output_data odata, edata;
-
-void try_output(int is_stderr)
+int from_backend_eof(void *frontend_handle)
{
- struct output_data *data = (is_stderr ? &edata : &odata);
- void *senddata;
- int sendlen;
-
- if (!data->busy) {
- bufchain_prefix(is_stderr ? &stderr_data : &stdout_data,
- &senddata, &sendlen);
- data->buffer = senddata;
- data->len = sendlen;
- SetEvent(data->eventback);
- data->busy = 1;
- }
+ handle_write_eof(stdout_handle);
+ return FALSE; /* do not respond to incoming EOF with outgoing */
}
-int from_backend(void *frontend_handle, int is_stderr,
- const char *data, int len)
+int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
{
- int osize, esize;
-
- if (is_stderr) {
- bufchain_add(&stderr_data, data, len);
- try_output(1);
- } else {
- bufchain_add(&stdout_data, data, len);
- try_output(0);
- }
-
- osize = bufchain_size(&stdout_data);
- esize = bufchain_size(&stderr_data);
-
- return osize + esize;
+ int ret;
+ ret = cmdline_get_passwd_input(p, in, inlen);
+ if (ret == -1)
+ ret = console_get_userpass_input(p, in, inlen);
+ return ret;
}
static DWORD main_thread_id;
printf(" -pgpfp print PGP key fingerprints and exit\n");
printf(" -v show verbose messages\n");
printf(" -load sessname Load settings from saved session\n");
- printf(" -ssh -telnet -rlogin -raw\n");
+ printf(" -ssh -telnet -rlogin -raw -serial\n");
printf(" force use of a particular protocol\n");
printf(" -P port connect to specified port\n");
printf(" -l user connect with specified username\n");
printf(" -4 -6 force use of IPv4 or IPv6\n");
printf(" -C enable compression\n");
printf(" -i key private key file for authentication\n");
+ printf(" -noagent disable use of Pageant\n");
+ printf(" -agent enable use of Pageant\n");
printf(" -m file read remote command(s) from file\n");
printf(" -s remote command is an SSH subsystem (SSH-2 only)\n");
printf(" -N don't start a shell/command (SSH-2 only)\n");
+ printf(" -nc host:port\n");
+ printf(" open tunnel in place of session (SSH-2 only)\n");
+ printf(" -sercfg configuration-string (e.g. 19200,8,n,1,X)\n");
+ printf(" Specify the serial configuration (serial only)\n");
exit(1);
}
return NULL;
}
+int stdin_gotdata(struct handle *h, void *data, int len)
+{
+ if (len < 0) {
+ /*
+ * Special case: report read error.
+ */
+ char buf[4096];
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, -len, 0,
+ buf, lenof(buf), NULL);
+ buf[lenof(buf)-1] = '\0';
+ if (buf[strlen(buf)-1] == '\n')
+ buf[strlen(buf)-1] = '\0';
+ fprintf(stderr, "Unable to read from standard input: %s\n", buf);
+ cleanup_exit(0);
+ }
+ noise_ultralight(len);
+ if (connopen && back->connected(backhandle)) {
+ if (len > 0) {
+ return back->send(backhandle, data, len);
+ } else {
+ back->special(backhandle, TS_EOF);
+ return 0;
+ }
+ } else
+ return 0;
+}
+
+void stdouterr_sent(struct handle *h, int new_backlog)
+{
+ if (new_backlog < 0) {
+ /*
+ * Special case: report write error.
+ */
+ char buf[4096];
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, -new_backlog, 0,
+ buf, lenof(buf), NULL);
+ buf[lenof(buf)-1] = '\0';
+ if (buf[strlen(buf)-1] == '\n')
+ buf[strlen(buf)-1] = '\0';
+ fprintf(stderr, "Unable to write to standard %s: %s\n",
+ (h == stdout_handle ? "output" : "error"), buf);
+ cleanup_exit(0);
+ }
+ if (connopen && back->connected(backhandle)) {
+ back->unthrottle(backhandle, (handle_backlog(stdout_handle) +
+ handle_backlog(stderr_handle)));
+ }
+}
+
int main(int argc, char **argv)
{
- WSAEVENT stdinevent, stdoutevent, stderrevent;
- HANDLE handles[4];
- DWORD in_threadid, out_threadid, err_threadid;
- struct input_data idata;
- int reading = FALSE;
int sending;
int portnumber = -1;
SOCKET *sklist;
int skcount, sksize;
- int connopen;
int exitcode;
int errors;
+ int got_host = FALSE;
int use_subsystem = 0;
- long now, next;
-
- ssh_get_line = console_get_line;
+ unsigned long now, next, then;
sklist = NULL;
skcount = sksize = 0;
/*
* Process the command line.
*/
- do_defaults(NULL, &cfg);
+ conf = conf_new();
+ do_defaults(NULL, conf);
loaded_session = FALSE;
- default_protocol = cfg.protocol;
- default_port = cfg.port;
+ default_protocol = conf_get_int(conf, CONF_protocol);
+ default_port = conf_get_int(conf, CONF_port);
errors = 0;
{
/*
* Override the default protocol if PLINK_PROTOCOL is set.
*/
char *p = getenv("PLINK_PROTOCOL");
- int i;
if (p) {
- for (i = 0; backends[i].backend != NULL; i++) {
- if (!strcmp(backends[i].name, p)) {
- default_protocol = cfg.protocol = backends[i].protocol;
- default_port = cfg.port =
- backends[i].backend->default_port;
- break;
- }
+ const Backend *b = backend_from_name(p);
+ if (b) {
+ default_protocol = b->protocol;
+ default_port = b->default_port;
+ conf_set_int(conf, CONF_protocol, default_protocol);
+ conf_set_int(conf, CONF_port, default_port);
}
}
}
char *p = *++argv;
if (*p == '-') {
int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),
- 1, &cfg);
+ 1, conf);
if (ret == -2) {
fprintf(stderr,
"plink: option \"%s\" requires an argument\n", p);
} else if (!strcmp(p, "-batch")) {
console_batch_mode = 1;
} else if (!strcmp(p, "-s")) {
- /* Save status to write to cfg later. */
+ /* Save status to write to conf later. */
use_subsystem = 1;
- } else if (!strcmp(p, "-V")) {
+ } else if (!strcmp(p, "-V") || !strcmp(p, "--version")) {
version();
+ } else if (!strcmp(p, "--help")) {
+ usage();
} else if (!strcmp(p, "-pgpfp")) {
pgp_fingerprints();
exit(1);
errors = 1;
}
} else if (*p) {
- if (!*cfg.host) {
+ if (!conf_launchable(conf) || !(got_host || loaded_session)) {
char *q = p;
/*
* If the hostname starts with "telnet:", set the
q += 7;
if (q[0] == '/' && q[1] == '/')
q += 2;
- cfg.protocol = PROT_TELNET;
+ conf_set_int(conf, CONF_protocol, PROT_TELNET);
p = q;
while (*p && *p != ':' && *p != '/')
p++;
if (*p)
*p++ = '\0';
if (c == ':')
- cfg.port = atoi(p);
+ conf_set_int(conf, CONF_port, atoi(p));
else
- cfg.port = -1;
- strncpy(cfg.host, q, sizeof(cfg.host) - 1);
- cfg.host[sizeof(cfg.host) - 1] = '\0';
+ conf_set_int(conf, CONF_port, -1);
+ conf_set_str(conf, CONF_host, q);
+ got_host = TRUE;
} else {
char *r, *user, *host;
/*
*/
r = strchr(p, ',');
if (r) {
- int i, j;
- for (i = 0; backends[i].backend != NULL; i++) {
- j = strlen(backends[i].name);
- if (j == r - p &&
- !memcmp(backends[i].name, p, j)) {
- default_protocol = cfg.protocol =
- backends[i].protocol;
- portnumber =
- backends[i].backend->default_port;
- p = r + 1;
- break;
- }
+ const Backend *b;
+ *r = '\0';
+ b = backend_from_name(p);
+ if (b) {
+ default_protocol = b->protocol;
+ conf_set_int(conf, CONF_protocol,
+ default_protocol);
+ portnumber = b->default_port;
}
+ p = r + 1;
}
/*
* same name as the hostname.
*/
{
- Config cfg2;
- do_defaults(host, &cfg2);
- if (loaded_session || cfg2.host[0] == '\0') {
+ Conf *conf2 = conf_new();
+ do_defaults(host, conf2);
+ if (loaded_session || !conf_launchable(conf2)) {
/* No settings for this host; use defaults */
/* (or session was already loaded with -load) */
- strncpy(cfg.host, host, sizeof(cfg.host) - 1);
- cfg.host[sizeof(cfg.host) - 1] = '\0';
- cfg.port = default_port;
+ conf_set_str(conf, CONF_host, host);
+ conf_set_int(conf, CONF_port, default_port);
+ got_host = TRUE;
} else {
- cfg = cfg2;
+ conf_copy_into(conf, conf2);
+ loaded_session = TRUE;
}
+ conf_free(conf2);
}
if (user) {
/* Patch in specified username. */
- strncpy(cfg.username, user,
- sizeof(cfg.username) - 1);
- cfg.username[sizeof(cfg.username) - 1] = '\0';
+ conf_set_str(conf, CONF_username, user);
}
}
}
if (cmdlen) command[--cmdlen]='\0';
/* change trailing blank to NUL */
- cfg.remote_cmd_ptr = command;
- cfg.remote_cmd_ptr2 = NULL;
- cfg.nopty = TRUE; /* command => no terminal */
+ conf_set_str(conf, CONF_remote_cmd, command);
+ conf_set_str(conf, CONF_remote_cmd2, "");
+ conf_set_int(conf, CONF_nopty, TRUE); /* command => no tty */
break; /* done with cmdline */
}
if (errors)
return 1;
- if (!*cfg.host) {
+ if (!conf_launchable(conf) || !(got_host || loaded_session)) {
usage();
}
/*
- * Trim leading whitespace off the hostname if it's there.
+ * Muck about with the hostname in various ways.
*/
{
- int space = strspn(cfg.host, " \t");
- memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
- }
+ char *hostbuf = dupstr(conf_get_str(conf, CONF_host));
+ char *host = hostbuf;
+ char *p, *q;
+
+ /*
+ * Trim leading whitespace.
+ */
+ host += strspn(host, " \t");
- /* See if host is of the form user@host */
- if (cfg.host[0] != '\0') {
- char *atsign = strrchr(cfg.host, '@');
- /* Make sure we're not overflowing the user field */
- if (atsign) {
- if (atsign - cfg.host < sizeof cfg.username) {
- strncpy(cfg.username, cfg.host, atsign - cfg.host);
- cfg.username[atsign - cfg.host] = '\0';
+ /*
+ * See if host is of the form user@host, and separate out
+ * the username if so.
+ */
+ if (host[0] != '\0') {
+ char *atsign = strrchr(host, '@');
+ if (atsign) {
+ *atsign = '\0';
+ conf_set_str(conf, CONF_username, host);
+ host = atsign + 1;
}
- memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
}
+
+ /*
+ * Trim off a colon suffix if it's there.
+ */
+ host[strcspn(host, ":")] = '\0';
+
+ /*
+ * Remove any remaining whitespace.
+ */
+ p = hostbuf;
+ q = host;
+ while (*q) {
+ if (*q != ' ' && *q != '\t')
+ *p++ = *q;
+ q++;
+ }
+ *p = '\0';
+
+ conf_set_str(conf, CONF_host, hostbuf);
+ sfree(hostbuf);
}
/*
* Perform command-line overrides on session configuration.
*/
- cmdline_run_saved(&cfg);
+ cmdline_run_saved(conf);
/*
* Apply subsystem status.
*/
if (use_subsystem)
- cfg.ssh_subsys = TRUE;
+ conf_set_int(conf, CONF_ssh_subsys, TRUE);
- /*
- * Trim a colon suffix off the hostname if it's there.
- */
- cfg.host[strcspn(cfg.host, ":")] = '\0';
-
- /*
- * Remove any remaining whitespace from the hostname.
- */
- {
- int p1 = 0, p2 = 0;
- while (cfg.host[p2] != '\0') {
- if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {
- cfg.host[p1] = cfg.host[p2];
- p1++;
- }
- p2++;
- }
- cfg.host[p1] = '\0';
- }
-
- if (!cfg.remote_cmd_ptr && !*cfg.remote_cmd)
+ if (!*conf_get_str(conf, CONF_remote_cmd) &&
+ !*conf_get_str(conf, CONF_remote_cmd2) &&
+ !*conf_get_str(conf, CONF_ssh_nc_host))
flags |= FLAG_INTERACTIVE;
/*
* Select protocol. This is farmed out into a table in a
* separate file to enable an ssh-free variant.
*/
- {
- int i;
- back = NULL;
- for (i = 0; backends[i].backend != NULL; i++)
- if (backends[i].protocol == cfg.protocol) {
- back = backends[i].backend;
- break;
- }
- if (back == NULL) {
- fprintf(stderr,
- "Internal fault: Unsupported protocol found\n");
- return 1;
- }
+ back = backend_from_proto(conf_get_int(conf, CONF_protocol));
+ if (back == NULL) {
+ fprintf(stderr,
+ "Internal fault: Unsupported protocol found\n");
+ return 1;
}
/*
* Select port.
*/
if (portnumber != -1)
- cfg.port = portnumber;
+ conf_set_int(conf, CONF_port, portnumber);
sk_init();
if (p_WSAEventSelect == NULL) {
return 1;
}
+ logctx = log_init(NULL, conf);
+ console_provide_logctx(logctx);
+
/*
* Start up the connection.
*/
const char *error;
char *realhost;
/* nodelay is only useful if stdin is a character device (console) */
- int nodelay = cfg.tcp_nodelay &&
+ int nodelay = conf_get_int(conf, CONF_tcp_nodelay) &&
(GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR);
- error = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port,
- &realhost, nodelay, cfg.tcp_keepalives);
+ error = back->init(NULL, &backhandle, conf,
+ conf_get_str(conf, CONF_host),
+ conf_get_int(conf, CONF_port),
+ &realhost, nodelay,
+ conf_get_int(conf, CONF_tcp_keepalives));
if (error) {
fprintf(stderr, "Unable to open connection:\n%s", error);
return 1;
}
- logctx = log_init(NULL, &cfg);
back->provide_logctx(backhandle, logctx);
- console_provide_logctx(logctx);
sfree(realhost);
}
connopen = 1;
- stdinevent = CreateEvent(NULL, FALSE, FALSE, NULL);
- stdoutevent = CreateEvent(NULL, FALSE, FALSE, NULL);
- stderrevent = CreateEvent(NULL, FALSE, FALSE, NULL);
-
inhandle = GetStdHandle(STD_INPUT_HANDLE);
outhandle = GetStdHandle(STD_OUTPUT_HANDLE);
errhandle = GetStdHandle(STD_ERROR_HANDLE);
- GetConsoleMode(inhandle, &orig_console_mode);
- SetConsoleMode(inhandle, ENABLE_PROCESSED_INPUT);
-
- main_thread_id = GetCurrentThreadId();
/*
* Turn off ECHO and LINE input modes. We don't care if this
* call fails, because we know we aren't necessarily running in
* a console.
*/
- handles[0] = netevent;
- handles[1] = stdinevent;
- handles[2] = stdoutevent;
- handles[3] = stderrevent;
- sending = FALSE;
+ GetConsoleMode(inhandle, &orig_console_mode);
+ SetConsoleMode(inhandle, ENABLE_PROCESSED_INPUT);
/*
- * Create spare threads to write to stdout and stderr, so we
- * can arrange asynchronous writes.
+ * Pass the output handles to the handle-handling subsystem.
+ * (The input one we leave until we're through the
+ * authentication process.)
*/
- odata.event = stdoutevent;
- odata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL);
- odata.is_stderr = 0;
- odata.busy = odata.done = 0;
- if (!CreateThread(NULL, 0, stdout_write_thread,
- &odata, 0, &out_threadid)) {
- fprintf(stderr, "Unable to create output thread\n");
- cleanup_exit(1);
- }
- edata.event = stderrevent;
- edata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL);
- edata.is_stderr = 1;
- edata.busy = edata.done = 0;
- if (!CreateThread(NULL, 0, stdout_write_thread,
- &edata, 0, &err_threadid)) {
- fprintf(stderr, "Unable to create error output thread\n");
- cleanup_exit(1);
- }
+ stdout_handle = handle_output_new(outhandle, stdouterr_sent, NULL, 0);
+ stderr_handle = handle_output_new(errhandle, stdouterr_sent, NULL, 0);
+
+ main_thread_id = GetCurrentThreadId();
+
+ sending = FALSE;
now = GETTICKCOUNT();
while (1) {
+ int nhandles;
+ HANDLE *handles;
int n;
DWORD ticks;
if (!sending && back->sendok(backhandle)) {
- /*
- * Create a separate thread to read from stdin. This is
- * a total pain, but I can't find another way to do it:
- *
- * - an overlapped ReadFile or ReadFileEx just doesn't
- * happen; we get failure from ReadFileEx, and
- * ReadFile blocks despite being given an OVERLAPPED
- * structure. Perhaps we can't do overlapped reads
- * on consoles. WHY THE HELL NOT?
- *
- * - WaitForMultipleObjects(netevent, console) doesn't
- * work, because it signals the console when
- * _anything_ happens, including mouse motions and
- * other things that don't cause data to be readable
- * - so we're back to ReadFile blocking.
- */
- idata.event = stdinevent;
- idata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (!CreateThread(NULL, 0, stdin_read_thread,
- &idata, 0, &in_threadid)) {
- fprintf(stderr, "Unable to create input thread\n");
- cleanup_exit(1);
- }
+ stdin_handle = handle_input_new(inhandle, stdin_gotdata, NULL,
+ 0);
sending = TRUE;
- reading = TRUE;
}
if (run_timers(now, &next)) {
- ticks = next - GETTICKCOUNT();
- if (ticks < 0) ticks = 0; /* just in case */
+ then = now;
+ now = GETTICKCOUNT();
+ if (now - then > next - then)
+ ticks = 0;
+ else
+ ticks = next - now;
} else {
ticks = INFINITE;
}
- n = MsgWaitForMultipleObjects(4, handles, FALSE, ticks,
+ handles = handle_get_events(&nhandles);
+ handles = sresize(handles, nhandles+1, HANDLE);
+ handles[nhandles] = netevent;
+ n = MsgWaitForMultipleObjects(nhandles+1, handles, FALSE, ticks,
QS_POSTMESSAGE);
- if (n == WAIT_OBJECT_0 + 0) {
+ if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) {
+ handle_got_event(handles[n - WAIT_OBJECT_0]);
+ } else if (n == WAIT_OBJECT_0 + nhandles) {
WSANETWORKEVENTS things;
SOCKET socket;
extern SOCKET first_socket(int *), next_socket(int *);
}
}
}
- } else if (n == WAIT_OBJECT_0 + 1) {
- reading = 0;
- noise_ultralight(idata.len);
- if (connopen && back->socket(backhandle) != NULL) {
- if (idata.len > 0) {
- back->send(backhandle, idata.buffer, idata.len);
- } else {
- back->special(backhandle, TS_EOF);
- }
- }
- } else if (n == WAIT_OBJECT_0 + 2) {
- odata.busy = 0;
- if (!odata.writeret) {
- fprintf(stderr, "Unable to write to standard output\n");
- cleanup_exit(0);
- }
- bufchain_consume(&stdout_data, odata.lenwritten);
- if (bufchain_size(&stdout_data) > 0)
- try_output(0);
- if (connopen && back->socket(backhandle) != NULL) {
- back->unthrottle(backhandle, bufchain_size(&stdout_data) +
- bufchain_size(&stderr_data));
- }
- } else if (n == WAIT_OBJECT_0 + 3) {
- edata.busy = 0;
- if (!edata.writeret) {
- fprintf(stderr, "Unable to write to standard output\n");
- cleanup_exit(0);
- }
- bufchain_consume(&stderr_data, edata.lenwritten);
- if (bufchain_size(&stderr_data) > 0)
- try_output(1);
- if (connopen && back->socket(backhandle) != NULL) {
- back->unthrottle(backhandle, bufchain_size(&stdout_data) +
- bufchain_size(&stderr_data));
- }
- } else if (n == WAIT_OBJECT_0 + 4) {
+ } else if (n == WAIT_OBJECT_0 + nhandles + 1) {
MSG msg;
while (PeekMessage(&msg, INVALID_HANDLE_VALUE,
WM_AGENT_CALLBACK, WM_AGENT_CALLBACK,
now = GETTICKCOUNT();
}
- if (!reading && back->sendbuffer(backhandle) < MAX_STDIN_BACKLOG) {
- SetEvent(idata.eventback);
- reading = 1;
- }
- if ((!connopen || back->socket(backhandle) == NULL) &&
- bufchain_size(&stdout_data) == 0 &&
- bufchain_size(&stderr_data) == 0)
+ sfree(handles);
+
+ if (sending)
+ handle_unthrottle(stdin_handle, back->sendbuffer(backhandle));
+
+ if ((!connopen || !back->connected(backhandle)) &&
+ handle_backlog(stdout_handle) + handle_backlog(stderr_handle) == 0)
break; /* we closed the connection */
}
exitcode = back->exitcode(backhandle);