]> asedeno.scripts.mit.edu Git - PuTTY.git/commitdiff
Support synchronous agent requests on Unix.
authorSimon Tatham <anakin@pobox.com>
Mon, 11 May 2015 16:12:40 +0000 (17:12 +0100)
committerSimon Tatham <anakin@pobox.com>
Mon, 11 May 2015 16:52:19 +0000 (17:52 +0100)
This is only intended for use in Unix Pageant; for any application
that's actually trying to get something else useful done at the same
time as the agent request is pending, it's much more sensible to use
the more rigorous existing approach of requesting a callback once the
agent request is answered.

Adding this mode is the easiest way to allow Unix Pageant's
command-line key loading to work, but it doesn't solve the underlying
problem that the supposedly cross-platform pageant_add_keyfile will
not work on a platform where we really _are_ constrained to do agent
requests asynchronously (perhaps because we're a GUI app in some
system that doesn't let us control our own top-level event loop).

If and when that situation arises, I'll have no choice but to turn
pageant_add_keyfile and friends (specifically, any function in
pageant.c that calls agent_query) into coroutine-structured functions,
and have clients call them repeatedly until they return 'finished'.

But for now, this is a lot easier!

unix/uxagentc.c

index 5734a7b0ea62c9f545b3cdb70b357d2f4682d871..326ee66b832439e35cb40a471339bc989074669c 100644 (file)
@@ -53,25 +53,23 @@ static int agent_connfind(void *av, void *bv)
     return 0;
 }
 
-static int agent_select_result(int fd, int event)
+/*
+ * Attempt to read from an agent socket fd. Returns 0 if the expected
+ * response is as yet incomplete; returns 1 if it's either complete
+ * (conn->retbuf non-NULL and filled with something useful) or has
+ * failed totally (conn->retbuf is NULL).
+ */
+static int agent_try_read(struct agent_connection *conn)
 {
     int ret;
-    struct agent_connection *conn;
-
-    assert(event == 1);                       /* not selecting for anything but R */
-
-    conn = find234(agent_connections, &fd, agent_connfind);
-    if (!conn) {
-       uxsel_del(fd);
-       return 1;
-    }
 
-    ret = read(fd, conn->retbuf+conn->retlen, conn->retsize-conn->retlen);
+    ret = read(conn->fd, conn->retbuf+conn->retlen,
+               conn->retsize-conn->retlen);
     if (ret <= 0) {
        if (conn->retbuf != conn->sizebuf) sfree(conn->retbuf);
        conn->retbuf = NULL;
        conn->retlen = 0;
-       goto done;
+        return 1;
     }
     conn->retlen += ret;
     if (conn->retsize == 4 && conn->retlen == 4) {
@@ -79,7 +77,7 @@ static int agent_select_result(int fd, int event)
        if (conn->retsize <= 0) {
            conn->retbuf = NULL;
            conn->retlen = 0;
-           goto done;
+            return -1;                 /* way too large */
        }
        assert(conn->retbuf == conn->sizebuf);
        conn->retbuf = snewn(conn->retsize, char);
@@ -89,7 +87,24 @@ static int agent_select_result(int fd, int event)
     if (conn->retlen < conn->retsize)
        return 0;                      /* more data to come */
 
-    done:
+    return 1;
+}
+
+static int agent_select_result(int fd, int event)
+{
+    struct agent_connection *conn;
+
+    assert(event == 1);                       /* not selecting for anything but R */
+
+    conn = find234(agent_connections, &fd, agent_connfind);
+    if (!conn) {
+       uxsel_del(fd);
+       return 1;
+    }
+
+    if (!agent_try_read(conn))
+       return 0;                      /* more data to come */
+
     /*
      * We have now completed the agent query. Do the callback, and
      * clean up. (Of course we don't free retbuf, since ownership
@@ -140,9 +155,6 @@ int agent_query(void *in, int inlen, void **out, int *outlen,
        done += ret;
     }
 
-    if (!agent_connections)
-       agent_connections = newtree234(agent_conncmp);
-
     conn = snew(struct agent_connection);
     conn->fd = sock;
     conn->retbuf = conn->sizebuf;
@@ -150,6 +162,34 @@ int agent_query(void *in, int inlen, void **out, int *outlen,
     conn->retlen = 0;
     conn->callback = callback;
     conn->callback_ctx = callback_ctx;
+
+    if (!callback) {
+        /*
+         * Bodge to permit making deliberately synchronous agent
+         * requests. Used by Unix Pageant in command-line client mode,
+         * which is legit because it really is true that no other part
+         * of the program is trying to get anything useful done
+         * simultaneously. But this special case shouldn't be used in
+         * any more general program.
+         */
+        no_nonblock(conn->fd);
+        while (!agent_try_read(conn))
+            /* empty loop body */;
+
+        *out = conn->retbuf;
+        *outlen = conn->retlen;
+        sfree(conn);
+        return 1;
+    }
+
+    /*
+     * Otherwise do it properly: add conn to the tree of agent
+     * connections currently in flight, return 0 to indicate that the
+     * response hasn't been received yet, and call the callback when
+     * select_result comes back to us.
+     */
+    if (!agent_connections)
+       agent_connections = newtree234(agent_conncmp);
     add234(agent_connections, conn);
 
     uxsel_set(sock, 1, agent_select_result);