From 37f4fbfe39aa5f9b1832acd1f55dc7ea17ff1da3 Mon Sep 17 00:00:00 2001 From: Nathan Weizenbaum 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 Signed-off-by: Julien Danjou --- awesome-client.c | 80 +++++++++++++++++++++++++++-------- common/socket.c | 2 +- lua.c | 108 +++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 162 insertions(+), 28 deletions(-) diff --git a/awesome-client.c b/awesome-client.c index 3af9c87b4..b608ea56b 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 +sockets_init(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 +sockets_close(void) +{ + close(csfd); + p_delete(&addr); +} + + /** Send a message to awesome. * \param msg The message. * \param msg_len The message length. @@ -49,32 +75,42 @@ 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) +{ + ssize_t 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; + } + else if(r > 0) + { + buf[r] = '\0'; + puts(buf); + } } @@ -114,6 +150,12 @@ main(int argc, char **argv) else if(argc > 2) exit_help(EXIT_SUCCESS); + if (sockets_init()) + { + warn("can't connect to UNIX domain socket: %s", strerror(errno)); + return EXIT_FAILURE; + } + if(isatty(STDIN_FILENO)) { char *display = getenv("DISPLAY"); @@ -126,6 +168,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 +198,7 @@ main(int argc, char **argv) p_delete(&msg); } + sockets_close(); return ret_value; } diff --git a/common/socket.c b/common/socket.c index c44bbc10c..840029ccc 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 0e49393a6..7c8329b80 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); }