Asynchronous agent requests on Windows. Actually, I've kept the
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Mon, 28 Apr 2003 13:59:32 +0000 (13:59 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Mon, 28 Apr 2003 13:59:32 +0000 (13:59 +0000)
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
pageantc.c
plink.c
psftp.c
putty.h
scp.c
window.c
winstuff.h

index 09a6568..e8428b0 100644 (file)
--- 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;
index d7dbfc0..3d5e61f 100644 (file)
@@ -6,17 +6,11 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#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 d61bbeb..0f764cf 100644 (file)
--- a/plink.c
+++ b/plink.c
 #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 b9f6370..c93d7be 100644 (file)
--- 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 cb58a45..b243cbb 100644 (file)
--- 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 8f21a7f..21fe332 100644 (file)
--- 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();
index 3c3cdb6..32ed507 100644 (file)
--- 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);
+}
index b94f25d..33b604f 100644 (file)
@@ -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