From: simon Date: Mon, 28 Apr 2003 13:59:32 +0000 (+0000) Subject: Asynchronous agent requests on Windows. Actually, I've kept the X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/commitdiff_plain/c44bf5bd7bf680c21356864ac5ae72ab29e55ce6?hp=839f10dbef33ef2982689a05f8305690aba92734 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 --- 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