From 231e482fd2517a53c28cafc11bf73ccbaa45968a Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Fri, 7 Feb 2020 19:15:13 +0000 Subject: [PATCH] Factor out common code from Windows CLI main loops. There aren't quite as many of these as there are on Unix, but Windows Plink and PSFTP still share some suspiciously similar-looking code. Now they're both clients of wincliloop.c. --- Recipe | 6 +- windows/wincliloop.c | 129 ++++++++++++++++++++++++++++++++++++++ windows/winplink.c | 142 ++++++++---------------------------------- windows/winsftp.c | 144 +++++++++---------------------------------- windows/winstuff.h | 8 +++ 5 files changed, 197 insertions(+), 232 deletions(-) create mode 100644 windows/wincliloop.c diff --git a/Recipe b/Recipe index 7c251d30..14e26b3d 100644 --- a/Recipe +++ b/Recipe @@ -335,11 +335,11 @@ putty : [G] GUITERM NONSSH WINSSH W_BE_ALL WINMISC winx11 putty.res LIBS puttytel : [G] GUITERM NONSSH W_BE_NOSSH WINMISC puttytel.res nogss LIBS plink : [C] winplink wincons NONSSH WINSSH W_BE_ALL logging WINMISC + winx11 plink.res winnojmp sessprep noterm winnohlp winselcli - + clicons LIBS + + clicons wincliloop LIBS pscp : [C] pscp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC - + pscp.res winnojmp winnohlp winselcli clicons LIBS + + pscp.res winnojmp winnohlp winselcli clicons wincliloop LIBS psftp : [C] psftp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC - + psftp.res winnojmp winnohlp winselcli clicons LIBS + + psftp.res winnojmp winnohlp winselcli clicons wincliloop LIBS pageant : [G] winpgnt pageant sshrsa sshpubk sshdes ARITH sshmd5 version + tree234 MISC sshaes sshsha winsecur winpgntc aqsync sshdss sshsh256 diff --git a/windows/wincliloop.c b/windows/wincliloop.c new file mode 100644 index 00000000..0f1f5cd9 --- /dev/null +++ b/windows/wincliloop.c @@ -0,0 +1,129 @@ +#include "putty.h" + +void cli_main_loop(cliloop_pre_t pre, cliloop_post_t post, void *ctx) +{ + SOCKET *sklist = NULL; + size_t skcount = 0, sksize = 0; + unsigned long now, next, then; + now = GETTICKCOUNT(); + + while (true) { + int nhandles; + HANDLE *handles; + int n; + DWORD ticks; + + const HANDLE *extra_handles = NULL; + size_t n_extra_handles = 0; + if (!pre(ctx, &extra_handles, &n_extra_handles)) + break; + + if (toplevel_callback_pending()) { + ticks = 0; + next = now; + } else if (run_timers(now, &next)) { + then = now; + now = GETTICKCOUNT(); + if (now - then > next - then) + ticks = 0; + else + ticks = next - now; + } else { + ticks = INFINITE; + /* no need to initialise next here because we can never + * get WAIT_TIMEOUT */ + } + + handles = handle_get_events(&nhandles); + size_t winselcli_index = nhandles; + size_t extra_base = winselcli_index + 1; + size_t total_handles = extra_base + n_extra_handles; + handles = sresize(handles, total_handles, HANDLE); + handles[winselcli_index] = winselcli_event; + for (size_t i = 0; i < n_extra_handles; i++) + handles[extra_base + i] = extra_handles[i]; + + n = WaitForMultipleObjects(total_handles, handles, false, ticks); + + size_t extra_handle_index = n_extra_handles; + + 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; + int i, socketstate; + + /* + * We must not call select_result() for any socket + * until we have finished enumerating within the tree. + * This is because select_result() may close the socket + * and modify the tree. + */ + /* Count the active sockets. */ + i = 0; + for (socket = first_socket(&socketstate); + socket != INVALID_SOCKET; + socket = next_socket(&socketstate)) i++; + + /* Expand the buffer if necessary. */ + sgrowarray(sklist, sksize, i); + + /* Retrieve the sockets into sklist. */ + skcount = 0; + for (socket = first_socket(&socketstate); + socket != INVALID_SOCKET; + socket = next_socket(&socketstate)) { + sklist[skcount++] = socket; + } + + /* Now we're done enumerating; go through the list. */ + for (i = 0; i < skcount; i++) { + WPARAM wp; + socket = sklist[i]; + wp = (WPARAM) socket; + if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) { + static const struct { int bit, mask; } eventtypes[] = { + {FD_CONNECT_BIT, FD_CONNECT}, + {FD_READ_BIT, FD_READ}, + {FD_CLOSE_BIT, FD_CLOSE}, + {FD_OOB_BIT, FD_OOB}, + {FD_WRITE_BIT, FD_WRITE}, + {FD_ACCEPT_BIT, FD_ACCEPT}, + }; + int e; + + noise_ultralight(NOISE_SOURCE_IOID, socket); + + for (e = 0; e < lenof(eventtypes); e++) + if (things.lNetworkEvents & eventtypes[e].mask) { + LPARAM lp; + int err = things.iErrorCode[eventtypes[e].bit]; + lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err); + select_result(wp, lp); + } + } + } + } else if (n >= WAIT_OBJECT_0 + extra_base && + n < WAIT_OBJECT_0 + extra_base + n_extra_handles) { + extra_handle_index = n - (WAIT_OBJECT_0 + extra_base); + } + + run_toplevel_callbacks(); + + if (n == WAIT_TIMEOUT) { + now = next; + } else { + now = GETTICKCOUNT(); + } + + sfree(handles); + + if (!post(ctx, extra_handle_index)) + break; + } +} + +bool cliloop_null_pre(void *vctx, const HANDLE **eh, size_t *neh) +{ return true; } +bool cliloop_null_post(void *vctx, size_t ehi) { return true; } diff --git a/windows/winplink.c b/windows/winplink.c index 47f27c58..22f57cf0 100644 --- a/windows/winplink.c +++ b/windows/winplink.c @@ -231,23 +231,43 @@ const unsigned cmdline_tooltype = TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX | TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD; +static bool sending; + +static bool plink_mainloop_pre(void *vctx, const HANDLE **extra_handles, + size_t *n_extra_handles) +{ + if (!sending && backend_sendok(backend)) { + stdin_handle = handle_input_new(inhandle, stdin_gotdata, NULL, + 0); + sending = true; + } + + return true; +} + +static bool plink_mainloop_post(void *vctx, size_t extra_handle_index) +{ + if (sending) + handle_unthrottle(stdin_handle, backend_sendbuffer(backend)); + + if (!backend_connected(backend) && + handle_backlog(stdout_handle) + handle_backlog(stderr_handle) == 0) + return false; /* we closed the connection */ + + return true; +} + int main(int argc, char **argv) { - bool sending; - SOCKET *sklist; - size_t skcount, sksize; int exitcode; bool errors; bool use_subsystem = false; bool just_test_share_exists = false; enum TriState sanitise_stdout = AUTO, sanitise_stderr = AUTO; - unsigned long now, next, then; const struct BackendVtable *vt; dll_hijacking_protection(); - sklist = NULL; - skcount = sksize = 0; /* * Initialise port and protocol to sensible defaults. (These * will be overridden by more or less anything.) @@ -487,116 +507,8 @@ int main(int argc, char **argv) sending = false; - now = GETTICKCOUNT(); + cli_main_loop(plink_mainloop_pre, plink_mainloop_post, NULL); - while (1) { - int nhandles; - HANDLE *handles; - int n; - DWORD ticks; - - if (!sending && backend_sendok(backend)) { - stdin_handle = handle_input_new(inhandle, stdin_gotdata, NULL, - 0); - sending = true; - } - - if (toplevel_callback_pending()) { - ticks = 0; - next = now; - } else if (run_timers(now, &next)) { - then = now; - now = GETTICKCOUNT(); - if (now - then > next - then) - ticks = 0; - else - ticks = next - now; - } else { - ticks = INFINITE; - /* no need to initialise next here because we can never - * get WAIT_TIMEOUT */ - } - - handles = handle_get_events(&nhandles); - handles = sresize(handles, nhandles+1, HANDLE); - handles[nhandles] = winselcli_event; - n = WaitForMultipleObjects(nhandles+1, handles, false, ticks); - 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; - int i, socketstate; - - /* - * We must not call select_result() for any socket - * until we have finished enumerating within the tree. - * This is because select_result() may close the socket - * and modify the tree. - */ - /* Count the active sockets. */ - i = 0; - for (socket = first_socket(&socketstate); - socket != INVALID_SOCKET; - socket = next_socket(&socketstate)) i++; - - /* Expand the buffer if necessary. */ - sgrowarray(sklist, sksize, i); - - /* Retrieve the sockets into sklist. */ - skcount = 0; - for (socket = first_socket(&socketstate); - socket != INVALID_SOCKET; - socket = next_socket(&socketstate)) { - sklist[skcount++] = socket; - } - - /* Now we're done enumerating; go through the list. */ - for (i = 0; i < skcount; i++) { - WPARAM wp; - socket = sklist[i]; - wp = (WPARAM) socket; - if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) { - static const struct { int bit, mask; } eventtypes[] = { - {FD_CONNECT_BIT, FD_CONNECT}, - {FD_READ_BIT, FD_READ}, - {FD_CLOSE_BIT, FD_CLOSE}, - {FD_OOB_BIT, FD_OOB}, - {FD_WRITE_BIT, FD_WRITE}, - {FD_ACCEPT_BIT, FD_ACCEPT}, - }; - int e; - - noise_ultralight(NOISE_SOURCE_IOID, socket); - - for (e = 0; e < lenof(eventtypes); e++) - if (things.lNetworkEvents & eventtypes[e].mask) { - LPARAM lp; - int err = things.iErrorCode[eventtypes[e].bit]; - lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err); - select_result(wp, lp); - } - } - } - } - - run_toplevel_callbacks(); - - if (n == WAIT_TIMEOUT) { - now = next; - } else { - now = GETTICKCOUNT(); - } - - sfree(handles); - - if (sending) - handle_unthrottle(stdin_handle, backend_sendbuffer(backend)); - - if (!backend_connected(backend) && - handle_backlog(stdout_handle) + handle_backlog(stderr_handle) == 0) - break; /* we closed the connection */ - } exitcode = backend_exitcode(backend); if (exitcode < 0) { fprintf(stderr, "Remote process exit code unavailable\n"); diff --git a/windows/winsftp.c b/windows/winsftp.c index af4980bd..0c695d2e 100644 --- a/windows/winsftp.c +++ b/windows/winsftp.c @@ -461,123 +461,39 @@ char *dir_file_cat(const char *dir, const char *file) /* ---------------------------------------------------------------------- * Platform-specific network handling. */ +struct winsftp_cliloop_ctx { + HANDLE other_event; + int toret; +}; +static bool winsftp_cliloop_pre(void *vctx, const HANDLE **extra_handles, + size_t *n_extra_handles) +{ + struct winsftp_cliloop_ctx *ctx = (struct winsftp_cliloop_ctx *)vctx; + if (ctx->other_event != INVALID_HANDLE_VALUE) { + *extra_handles = &ctx->other_event; + *n_extra_handles = 1; + } + + return true; +} +static bool winsftp_cliloop_post(void *vctx, size_t extra_handle_index) +{ + struct winsftp_cliloop_ctx *ctx = (struct winsftp_cliloop_ctx *)vctx; + + if (ctx->other_event != INVALID_HANDLE_VALUE && + extra_handle_index == 0) + ctx->toret = 1; /* other_event was set */ + + return false; /* always run only one loop iteration */ +} int do_eventsel_loop(HANDLE other_event) { - int n, nhandles, nallhandles, netindex, otherindex; - unsigned long next, then; - long ticks; - HANDLE *handles; - SOCKET *sklist; - int skcount; - unsigned long now = GETTICKCOUNT(); - - if (toplevel_callback_pending()) { - ticks = 0; - next = now; - } else if (run_timers(now, &next)) { - then = now; - now = GETTICKCOUNT(); - if (now - then > next - then) - ticks = 0; - else - ticks = next - now; - } else { - ticks = INFINITE; - /* no need to initialise next here because we can never get - * WAIT_TIMEOUT */ - } - - handles = handle_get_events(&nhandles); - handles = sresize(handles, nhandles+2, HANDLE); - nallhandles = nhandles; - - if (winselcli_event != INVALID_HANDLE_VALUE) - handles[netindex = nallhandles++] = winselcli_event; - else - netindex = -1; - if (other_event != INVALID_HANDLE_VALUE) - handles[otherindex = nallhandles++] = other_event; - else - otherindex = -1; - - n = WaitForMultipleObjects(nallhandles, handles, false, ticks); - - if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) { - handle_got_event(handles[n - WAIT_OBJECT_0]); - } else if (netindex >= 0 && n == WAIT_OBJECT_0 + netindex) { - WSANETWORKEVENTS things; - SOCKET socket; - int i, socketstate; - - /* - * We must not call select_result() for any socket - * until we have finished enumerating within the - * tree. This is because select_result() may close - * the socket and modify the tree. - */ - /* Count the active sockets. */ - i = 0; - for (socket = first_socket(&socketstate); - socket != INVALID_SOCKET; - socket = next_socket(&socketstate)) i++; - - /* Expand the buffer if necessary. */ - sklist = snewn(i, SOCKET); - - /* Retrieve the sockets into sklist. */ - skcount = 0; - for (socket = first_socket(&socketstate); - socket != INVALID_SOCKET; - socket = next_socket(&socketstate)) { - sklist[skcount++] = socket; - } - - /* Now we're done enumerating; go through the list. */ - for (i = 0; i < skcount; i++) { - WPARAM wp; - socket = sklist[i]; - wp = (WPARAM) socket; - if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) { - static const struct { int bit, mask; } eventtypes[] = { - {FD_CONNECT_BIT, FD_CONNECT}, - {FD_READ_BIT, FD_READ}, - {FD_CLOSE_BIT, FD_CLOSE}, - {FD_OOB_BIT, FD_OOB}, - {FD_WRITE_BIT, FD_WRITE}, - {FD_ACCEPT_BIT, FD_ACCEPT}, - }; - int e; - - noise_ultralight(NOISE_SOURCE_IOID, socket); - - for (e = 0; e < lenof(eventtypes); e++) - if (things.lNetworkEvents & eventtypes[e].mask) { - LPARAM lp; - int err = things.iErrorCode[eventtypes[e].bit]; - lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err); - select_result(wp, lp); - } - } - } - - sfree(sklist); - } - - sfree(handles); - - run_toplevel_callbacks(); - - if (n == WAIT_TIMEOUT) { - now = next; - } else { - now = GETTICKCOUNT(); - } - - if (otherindex >= 0 && n == WAIT_OBJECT_0 + otherindex) - return 1; - - return 0; + struct winsftp_cliloop_ctx ctx[1]; + ctx->other_event = other_event; + ctx->toret = 0; + cli_main_loop(winsftp_cliloop_pre, winsftp_cliloop_post, ctx); + return ctx->toret; } /* diff --git a/windows/winstuff.h b/windows/winstuff.h index 4d31920e..f837a4b0 100644 --- a/windows/winstuff.h +++ b/windows/winstuff.h @@ -686,4 +686,12 @@ char *get_jumplist_registry_entries(void); /* In winmisc.c */ char *registry_get_string(HKEY root, const char *path, const char *leaf); +/* In wincliloop.c */ +typedef bool (*cliloop_pre_t)(void *vctx, const HANDLE **extra_handles, + size_t *n_extra_handles); +typedef bool (*cliloop_post_t)(void *vctx, size_t extra_handle_index); +void cli_main_loop(cliloop_pre_t pre, cliloop_post_t post, void *ctx); +bool cliloop_null_pre(void *vctx, const HANDLE **, size_t *); +bool cliloop_null_post(void *vctx, size_t); + #endif