Here are revised versions of the patches. The first one no longer ignores luadoc.css, and the second uses SOCK_SEQPACKET instead of SOCK_DGRAM. I tried to find a way to use SOCK_SEQPACKET's MSG_EOR flag to send messages larger than 1023 bytes, but due to my inexperience with sockets and the very limited documentation available online I wasn't able to.

- Nathan

Nathan Weizenbaum wrote:
Julien Danjou wrote:
Hi Nathan,

Well, running "make" in the source dir should not create such file.
At least it does not here.
I think luadoc.css was an artifact of something else I was doing, but make definitely generates awesome and awesome-devel symlinks for me (using cmake 2.6-patch 1).
That's totally awesome.
But I think the 2 sockets approch is not so good; did you though about
using a bidirectionnal socket based on something like SOCK_SEQPAQUET?
No, I didn't. I hadn't worked with sockets in C before, so I just tried to duplicate the existing architecture. SOCK_SEQPACKET does sound better, though; I'll try re-implementing it using that.

- Nathan


>From a7899a5da275caeb49362c4864d6fce7ed34b79c Mon Sep 17 00:00:00 2001
From: Nathan Weizenbaum <[EMAIL PROTECTED]>
Date: Thu, 18 Sep 2008 21:09:50 -0700
Subject: [PATCH] gitignore: add binaries

Signed-off-by: Nathan Weizenbaum <[EMAIL PROTECTED]>
---
 .gitignore |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore
index 5bbaf3e..24481ab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,5 @@
 .*.sw?
 build
 .build*
+/awesome
+/awesome-client
-- 
1.6.0.1.267.gec3a.dirty

>From 308822ee1d1aa2bb505b30e80195e5f0e61fa5e4 Mon Sep 17 00:00:00 2001
From: Nathan Weizenbaum <[EMAIL PROTECTED]>
Date: Thu, 18 Sep 2008 21:48:55 -0700
Subject: [PATCH] awesome-client, socket, lua: make awesome-client into a true REPL

After recieving a command from awesome-client, awesome will send
the result of that command in return and awesome-client will print it out.

Signed-off-by: Nathan Weizenbaum <[EMAIL PROTECTED]>
---
 awesome-client.c |   81 +++++++++++++++++++++++++++++++---------
 common/socket.c  |    2 +-
 lua.c            |  108 +++++++++++++++++++++++++++++++++++++++++++++++++----
 3 files changed, 163 insertions(+), 28 deletions(-)

diff --git a/awesome-client.c b/awesome-client.c
index 3af9c87..15121ac 100644
--- a/awesome-client.c
+++ b/awesome-client.c
@@ -41,6 +41,32 @@
 #define MSG_NOSIGNAL 0
 #endif
 
+struct sockaddr_un *addr;
+int csfd;
+
+/** Initialize the client and server socket connections.
+ * If something goes wrong, preserves errno.
+ * \return 0 if everything worked, 1 otherwise.
+ */
+static int
+init_sockets(void)
+{
+    return (csfd = socket_getclient()) < 0 ||
+      !(addr = socket_getaddr(getenv("DISPLAY"))) ||
+      connect(csfd, addr, sizeof(struct sockaddr_un)) == -1;
+}
+
+
+/** Close the client and server socket connections.
+ */
+static void
+close_sockets(void)
+{
+    close(csfd);
+    p_delete(&addr);
+}
+
+
 /** Send a message to awesome.
  * \param msg The message.
  * \param msg_len The message length.
@@ -49,32 +75,43 @@
 static int
 send_msg(const char *msg, ssize_t msg_len)
 {
-    struct sockaddr_un *addr;
-    int csfd, ret_value = EXIT_SUCCESS;
-    csfd = socket_getclient();
-    addr = socket_getaddr(getenv("DISPLAY"));
-
-    if(!addr || csfd < 0)
-        return EXIT_FAILURE;
-
-    if(sendto(csfd, msg, msg_len, MSG_NOSIGNAL,
-              (const struct sockaddr *) addr, sizeof(struct sockaddr_un)) == -1)
+    if(send(csfd, msg, msg_len, MSG_NOSIGNAL | MSG_EOR) == -1)
     {
         switch (errno)
         {
           case ENOENT:
-              warn("can't write to %s", addr->sun_path);
-              break;
+            warn("can't write to %s", addr->sun_path);
+            break;
           default:
-              warn("error sending datagram: %s", strerror(errno));
-         }
-         ret_value = errno;
+            warn("error sending packet: %s", strerror(errno));
+        }
+        return errno;
     }
 
-    close(csfd);
+    return EXIT_SUCCESS;
+}
 
-    p_delete(&addr);
-    return ret_value;
+
+/** Recieve a message from awesome.
+ */
+static void
+recv_msg(void)
+{
+    int r;
+    char buf[1024];
+
+    r = recv(csfd, buf, sizeof(buf) - 1, MSG_TRUNC);
+    if (r < 0)
+    {
+        warn("error recieving from UNIX domain socket: %s", strerror(errno));
+        return;
+    }
+
+    if (r >= 0)
+    {
+        buf[r] = '\0';
+        puts(buf);
+    }
 }
 
 
@@ -114,6 +151,12 @@ main(int argc, char **argv)
     else if(argc > 2)
         exit_help(EXIT_SUCCESS);
 
+    if (init_sockets())
+    {
+        warn("can't connect to UNIX domain socket: %s", strerror(errno));
+        return EXIT_FAILURE;
+    }
+
     if(isatty(STDIN_FILENO))
     {
         char *display = getenv("DISPLAY");
@@ -126,6 +169,7 @@ main(int argc, char **argv)
                 msg[msg_len] = '\n';
                 msg[msg_len + 1] = '\0';
                 send_msg(msg, msg_len + 2);
+                recv_msg();
             }
     }
     else
@@ -155,6 +199,7 @@ main(int argc, char **argv)
         p_delete(&msg);
     }
 
+    close_sockets();
     return ret_value;
 }
 
diff --git a/common/socket.c b/common/socket.c
index c44bbc1..840029c 100644
--- a/common/socket.c
+++ b/common/socket.c
@@ -79,7 +79,7 @@ socket_getclient(void)
 {
     int csfd;
 
-    csfd = socket(AF_UNIX, SOCK_DGRAM, 0);
+    csfd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
 
     if(csfd < 0)
         warn("error opening UNIX domain socket: %s", strerror(errno));
diff --git a/lua.c b/lua.c
index 0e49393..7c8329b 100644
--- a/lua.c
+++ b/lua.c
@@ -48,6 +48,7 @@
 #include "event.h"
 #include "layouts/tile.h"
 #include "common/socket.h"
+#include "common/buffer.h"
 
 extern awesome_t globalconf;
 
@@ -71,6 +72,7 @@ extern const struct luaL_reg awesome_keybinding_meta[];
 
 static struct sockaddr_un *addr;
 static ev_io csio = { .fd = -1 };
+struct ev_io csio2 = { .fd = -1 };
 
 /** Add a global mouse binding. This binding will be available when you'll
  * click on root window.
@@ -714,44 +716,130 @@ luaA_parserc(const char *confpatharg)
 
 /** Parse a command.
  * \param cmd The buffer to parse.
- * \return true on succes, false on failure.
+ * \return the number of elements pushed on the stack by the last statement in cmd.
+ * If there's an error, the message is pushed onto the stack and this returns 1.
  */
-static void
+static int
 luaA_docmd(const char *cmd)
 {
     char *p;
+    int newtop, oldtop = lua_gettop(globalconf.L);
 
     while((p = strchr(cmd, '\n')))
     {
+        newtop = lua_gettop(globalconf.L);
+        lua_pop(globalconf.L, newtop - oldtop);
+        oldtop = newtop;
+
         *p = '\0';
-        luaA_dostring(globalconf.L, cmd);
+        if (luaL_dostring(globalconf.L, cmd))
+        {
+            warn("error executing Lua code: %s", lua_tostring(globalconf.L, -1));
+            return 1;
+        }
         cmd = p + 1;
     }
+    return lua_gettop(globalconf.L) - oldtop;
+}
+
+/** Pushes a Lua array containing the top n elements of the stack.
+ * \param n The number of elements to put in the array.
+ */
+static void
+luaA_array(int n)
+{
+    int i;
+    lua_createtable(globalconf.L, n, 0);
+    lua_insert(globalconf.L, -n - 1);
+
+    for (i = n; i > 0; i--)
+        lua_rawseti(globalconf.L, -i - 1, i);
+}
+
+/** Maps the top n elements of the stack to the result of
+ * applying a function to that element.
+ * \param n The number of elements to map.
+ * \luastack
+ * \lparam The function to map the elements by. This should be
+ * at position -(n + 1).
+ */
+static void
+luaA_map(int n)
+{
+    int i;
+    for (i = 0; i < n; i++)
+    {
+        lua_pushvalue(globalconf.L, -n - 1); /* copy of the function */
+        lua_pushvalue(globalconf.L, -n - 1); /* value to map */
+        lua_pcall(globalconf.L, 1, 1, 0);    /* call function */
+        lua_remove(globalconf.L, -n - 1);    /* remove old value */
+    }
+    lua_remove(globalconf.L, -n - 1); /* remove function */
+}
+
+static void luaA_conn_cleanup(EV_P_ ev_io *w)
+{
+    ev_ref(EV_DEFAULT_UC);
+    ev_io_stop(EV_DEFAULT_UC_ w);
+    if (close(w->fd))
+        warn("error closing UNIX domain socket: %s", strerror(errno));
+    p_delete(&w);
 }
 
 static void
 luaA_cb(EV_P_ ev_io *w, int revents)
 {
     char buf[1024];
-    int r;
+    int r, els;
+    const char *s;
+    size_t len;
 
     switch(r = recv(w->fd, buf, sizeof(buf)-1, MSG_TRUNC))
     {
       case -1:
         warn("error reading UNIX domain socket: %s", strerror(errno));
-        luaA_cs_cleanup();
-        break;
-      case 0:
+      case 0: /* 0 bytes are only transferred when the connection is closed */
+        luaA_conn_cleanup(EV_DEFAULT_UC_ w);
         break;
       default:
         if(r >= ssizeof(buf))
             break;
         buf[r] = '\0';
-        luaA_docmd(buf);
+        lua_getglobal(globalconf.L, "table");
+        lua_getfield(globalconf.L, -1, "concat");
+        lua_remove(globalconf.L, -2); /* remove table */
+
+        lua_getglobal(globalconf.L, "tostring");
+        els = luaA_docmd(buf);
+        luaA_map(els); /* map results to strings */
+        luaA_array(els); /* put strings in an array */
+
+        lua_pushstring(globalconf.L, "\t");
+        lua_pcall(globalconf.L, 2, 1, 0); /* concatenate results with tabs */
+
+        s = lua_tolstring(globalconf.L, -1, &len);
+
+        /* ignore ENOENT because the client doesn't create a socket for non-tty */
+        if (send(w->fd, s, len, 0) == -1 && errno != ENOENT)
+            warn("can't connect to client UNIX domain socket: %s", strerror(errno));
+
+        lua_pop(globalconf.L, 1); /* pop the string */
     }
     awesome_refresh(globalconf.connection);
 }
 
+
+static void
+luaA_conn_cb(EV_P_ ev_io *w, int revents)
+{
+    ev_io *csio_conn = p_new(ev_io, 1);
+    int csfd = accept(w->fd, NULL, NULL);
+
+    ev_io_init(csio_conn, &luaA_cb, csfd, EV_READ);
+    ev_io_start(EV_DEFAULT_UC_ csio_conn);
+    ev_unref(EV_DEFAULT_UC);
+}
+
 void
 luaA_cs_init(void)
 {
@@ -774,7 +862,9 @@ luaA_cs_init(void)
         else
             warn("error binding UNIX domain socket: %s", strerror(errno));
     }
-    ev_io_init(&csio, &luaA_cb, csfd, EV_READ);
+    listen(csfd, 10);
+
+    ev_io_init(&csio, &luaA_conn_cb, csfd, EV_READ);
     ev_io_start(EV_DEFAULT_UC_ &csio);
     ev_unref(EV_DEFAULT_UC);
 }
-- 
1.6.0.1.267.gec3a.dirty

Reply via email to