From c44bf5bd7bf680c21356864ac5ae72ab29e55ce6 Mon Sep 17 00:00:00 2001 From: simon Date: Mon, 28 Apr 2003 13:59:32 +0000 Subject: [PATCH] Asynchronous agent requests on Windows. Actually, I've kept the ability to do synchronous ones as well, because PSCP and PSFTP don't really need async ones and it would have been a serious pain to implement them. Also, Pageant itself when run as a client of its primary instance doesn't benefit noticeably from async agent requests. git-svn-id: svn://svn.tartarus.org/sgt/putty@3154 cda61777-01e9-0310-a592-d414129be87e --- pageant.c | 12 ++++++++ pageantc.c | 99 ++++++++++++++++++++++++++++++++++++++++++-------------------- plink.c | 36 ++++++++++++++++++++++- psftp.c | 12 +++++++- putty.h | 4 +++ scp.c | 12 +++++++- window.c | 27 +++++++++++++++++ winstuff.h | 12 ++++++++ 8 files changed, 180 insertions(+), 34 deletions(-) diff --git a/pageant.c b/pageant.c index 09a65681..e8428b03 100644 --- a/pageant.c +++ b/pageant.c @@ -1792,8 +1792,20 @@ void spawn_cmd(char *cmdline, char * args, int show) } } +/* + * This is a can't-happen stub, since Pageant never makes + * asynchronous agent requests. + */ +void agent_schedule_callback(void (*callback)(void *, void *, int), + void *callback_ctx, void *data, int len) +{ + assert(!"We shouldn't get here"); +} + void cleanup_exit(int code) { exit(code); } +int flags = FLAG_SYNCAGENT; + int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { WNDCLASS wndclass; diff --git a/pageantc.c b/pageantc.c index d7dbfc01..3d5e61f2 100644 --- a/pageantc.c +++ b/pageantc.c @@ -6,17 +6,11 @@ #include #include -#include "puttymem.h" +#include "putty.h" #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */ #define AGENT_MAX_MSGLEN 8192 -#ifdef TESTMODE -#define debug(x) (printf x) -#else -#define debug(x) -#endif - #define GET_32BIT(cp) \ (((unsigned long)(unsigned char)(cp)[0] << 24) | \ ((unsigned long)(unsigned char)(cp)[1] << 16) | \ @@ -33,11 +27,48 @@ int agent_exists(void) return TRUE; } +struct agent_query_data { + COPYDATASTRUCT cds; + unsigned char *mapping; + HANDLE handle; + char *mapname; + HWND hwnd; + void (*callback)(void *, void *, int); + void *callback_ctx; +}; + +DWORD WINAPI agent_query_thread(LPVOID param) +{ + struct agent_query_data *data = (struct agent_query_data *)param; + unsigned char *ret; + int id, retlen; + + id = SendMessage(data->hwnd, WM_COPYDATA, (WPARAM) NULL, + (LPARAM) &data->cds); + ret = NULL; + if (id > 0) { + retlen = 4 + GET_32BIT(data->mapping); + ret = snewn(retlen, unsigned char); + if (ret) { + memcpy(ret, data->mapping, retlen); + } + } + if (!ret) + retlen = 0; + UnmapViewOfFile(data->mapping); + CloseHandle(data->handle); + sfree(data->mapname); + + agent_schedule_callback(data->callback, data->callback_ctx, ret, retlen); + + return 0; +} + int agent_query(void *in, int inlen, void **out, int *outlen, void (*callback)(void *, void *, int), void *callback_ctx) { HWND hwnd; - char mapname[64]; + char *mapname; HANDLE filemap; unsigned char *p, *ret; int id, retlen; @@ -47,10 +78,9 @@ int agent_query(void *in, int inlen, void **out, int *outlen, *outlen = 0; hwnd = FindWindow("Pageant", "Pageant"); - debug(("hwnd is %p\n", hwnd)); if (!hwnd) return 1; /* *out == NULL, so failure */ - sprintf(mapname, "PageantRequest%08x", (unsigned)GetCurrentThreadId()); + mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId()); filemap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, AGENT_MAX_MSGLEN, mapname); if (!filemap) @@ -60,11 +90,37 @@ int agent_query(void *in, int inlen, void **out, int *outlen, cds.dwData = AGENT_COPYDATA_ID; cds.cbData = 1 + strlen(mapname); cds.lpData = mapname; + if (callback != NULL && !(flags & FLAG_SYNCAGENT)) { + /* + * We need an asynchronous Pageant request. Since I know of + * no way to stop SendMessage from blocking the thread it's + * called in, I see no option but to start a fresh thread. + * When we're done we'll PostMessage the result back to our + * main window, so that the callback is done in the primary + * thread to avoid concurrency. + */ + struct agent_query_data *data = snew(struct agent_query_data); + DWORD threadid; + data->mapping = p; + data->handle = filemap; + data->mapname = mapname; + data->callback = callback; + data->callback_ctx = callback_ctx; + data->cds = cds; /* structure copy */ + data->hwnd = hwnd; + if (CreateThread(NULL, 0, agent_query_thread, data, 0, &threadid)) + return 0; + sfree(data); + } + + /* + * The user either passed a null callback (indicating that the + * query is required to be synchronous) or CreateThread failed. + * Either way, we need a synchronous request. + */ id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) & cds); - debug(("return is %d\n", id)); if (id > 0) { retlen = 4 + GET_32BIT(p); - debug(("len is %d\n", retlen)); ret = snewn(retlen, unsigned char); if (ret) { memcpy(ret, p, retlen); @@ -74,24 +130,5 @@ int agent_query(void *in, int inlen, void **out, int *outlen, } UnmapViewOfFile(p); CloseHandle(filemap); - return 1; } - -#ifdef TESTMODE - -int main(void) -{ - void *msg; - int len; - int i; - - agent_query("\0\0\0\1\1", 5, &msg, &len); - debug(("%d:", len)); - for (i = 0; i < len; i++) - debug((" %02x", ((unsigned char *) msg)[i])); - debug(("\n")); - return 0; -} - -#endif diff --git a/plink.c b/plink.c index d61bbeb5..0f764cf3 100644 --- a/plink.c +++ b/plink.c @@ -16,8 +16,17 @@ #include "storage.h" #include "tree234.h" +#define WM_AGENT_CALLBACK (WM_XUSER + 4) + #define MAX_STDIN_BACKLOG 4096 +struct agent_callback { + void (*callback)(void *, void *, int); + void *callback_ctx; + void *data; + int len; +}; + void fatalbox(char *p, ...) { va_list ap; @@ -187,6 +196,19 @@ int from_backend(void *frontend_handle, int is_stderr, return osize + esize; } +static DWORD main_thread_id; + +void agent_schedule_callback(void (*callback)(void *, void *, int), + void *callback_ctx, void *data, int len) +{ + struct agent_callback *c = snew(struct agent_callback); + c->callback = callback; + c->callback_ctx = callback_ctx; + c->data = data; + c->len = len; + PostThreadMessage(main_thread_id, WM_AGENT_CALLBACK, 0, (LPARAM)c); +} + /* * Short description of parameters. */ @@ -565,6 +587,8 @@ int main(int argc, char **argv) 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 @@ -629,7 +653,8 @@ int main(int argc, char **argv) sending = TRUE; } - n = WaitForMultipleObjects(4, handles, FALSE, INFINITE); + n = MsgWaitForMultipleObjects(4, handles, FALSE, INFINITE, + QS_POSTMESSAGE); if (n == 0) { WSANETWORKEVENTS things; SOCKET socket; @@ -727,6 +752,15 @@ int main(int argc, char **argv) back->unthrottle(backhandle, bufchain_size(&stdout_data) + bufchain_size(&stderr_data)); } + } else if (n == 4) { + MSG msg; + while (PeekMessage(&msg, INVALID_HANDLE_VALUE, + WM_AGENT_CALLBACK, WM_AGENT_CALLBACK, + PM_REMOVE)) { + struct agent_callback *c = (struct agent_callback *)msg.lParam; + c->callback(c->callback_ctx, c->data, c->len); + sfree(c); + } } if (!reading && back->sendbuffer(backhandle) < MAX_STDIN_BACKLOG) { SetEvent(idata.eventback); diff --git a/psftp.c b/psftp.c index b9f63709..c93d7be3 100644 --- a/psftp.c +++ b/psftp.c @@ -1514,6 +1514,16 @@ char *do_select(SOCKET skt, int startup) extern int select_result(WPARAM, LPARAM); /* + * In psftp, all agent requests should be synchronous, so this is a + * never-called stub. + */ +void agent_schedule_callback(void (*callback)(void *, void *, int), + void *callback_ctx, void *data, int len) +{ + assert(!"We shouldn't be here"); +} + +/* * Receive a block of data from the SSH link. Block until all data * is available. * @@ -1862,7 +1872,7 @@ int main(int argc, char *argv[]) char *batchfile = NULL; int errors = 0; - flags = FLAG_STDERR | FLAG_INTERACTIVE; + flags = FLAG_STDERR | FLAG_INTERACTIVE | FLAG_SYNCAGENT; cmdline_tooltype = TOOLTYPE_FILETRANSFER; ssh_get_line = &console_get_line; init_winsock(); diff --git a/putty.h b/putty.h index cb58a459..b243cbbc 100644 --- a/putty.h +++ b/putty.h @@ -492,6 +492,10 @@ struct config_tag { * These flags describe the type of _application_ - they wouldn't * vary between individual sessions - and so it's OK to have this * variable be GLOBAL. + * + * Note that additional flags may be defined in platform-specific + * headers. It's probably best if those ones start from 0x1000, to + * avoid collision. */ #define FLAG_VERBOSE 0x0001 #define FLAG_STDERR 0x0002 diff --git a/scp.c b/scp.c index 8f21a7f1..21fe3322 100644 --- a/scp.c +++ b/scp.c @@ -305,6 +305,16 @@ char *do_select(SOCKET skt, int startup) extern int select_result(WPARAM, LPARAM); /* + * In pscp, all agent requests should be synchronous, so this is a + * never-called stub. + */ +void agent_schedule_callback(void (*callback)(void *, void *, int), + void *callback_ctx, void *data, int len) +{ + assert(!"We shouldn't be here"); +} + +/* * Receive a block of data from the SSH link. Block until all data * is available. * @@ -2178,7 +2188,7 @@ int main(int argc, char *argv[]) default_protocol = PROT_TELNET; - flags = FLAG_STDERR; + flags = FLAG_STDERR | FLAG_SYNCAGENT; cmdline_tooltype = TOOLTYPE_FILETRANSFER; ssh_get_line = &console_get_line; init_winsock(); diff --git a/window.c b/window.c index 3c3cdb68..32ed507f 100644 --- a/window.c +++ b/window.c @@ -55,6 +55,7 @@ #define WM_IGNORE_CLIP (WM_XUSER + 2) #define WM_FULLSCR_ON_MAX (WM_XUSER + 3) +#define WM_AGENT_CALLBACK (WM_XUSER + 4) /* Needed for Chinese support and apparently not always defined. */ #ifndef VK_PROCESSKEY @@ -122,6 +123,13 @@ Config cfg; /* exported to windlg.c */ extern struct sesslist sesslist; /* imported from windlg.c */ +struct agent_callback { + void (*callback)(void *, void *, int); + void *callback_ctx; + void *data; + int len; +}; + #define FONT_NORMAL 0 #define FONT_BOLD 1 #define FONT_UNDERLINE 2 @@ -2575,6 +2583,14 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, SetCursor(LoadCursor(NULL, IDC_ARROW)); return TRUE; } + break; + case WM_AGENT_CALLBACK: + { + struct agent_callback *c = (struct agent_callback *)lParam; + c->callback(c->callback_ctx, c->data, c->len); + sfree(c); + } + return 0; default: if (message == wm_mousewheel || message == WM_MOUSEWHEEL) { int shift_pressed=0, control_pressed=0; @@ -4626,3 +4642,14 @@ int from_backend(void *frontend, int is_stderr, const char *data, int len) { return term_data(term, is_stderr, data, len); } + +void agent_schedule_callback(void (*callback)(void *, void *, int), + void *callback_ctx, void *data, int len) +{ + struct agent_callback *c = snew(struct agent_callback); + c->callback = callback; + c->callback_ctx = callback_ctx; + c->data = data; + c->len = len; + PostMessage(hwnd, WM_AGENT_CALLBACK, 0, (LPARAM)c); +} diff --git a/winstuff.h b/winstuff.h index b94f25d8..33b604ff 100644 --- a/winstuff.h +++ b/winstuff.h @@ -319,4 +319,16 @@ void EnableSizeTip(int bEnable); struct unicode_data; void init_ucs(Config *, struct unicode_data *); +/* + * pageantc.c needs to schedule callbacks for asynchronous agent + * requests. This has to be done differently in GUI and console, so + * there's an exported function used for the purpose. + * + * Also, we supply FLAG_SYNCAGENT to force agent requests to be + * synchronous in pscp and psftp. + */ +void agent_schedule_callback(void (*callback)(void *, void *, int), + void *callback_ctx, void *data, int len); +#define FLAG_SYNCAGENT 0x1000 + #endif -- 2.11.0