summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmil Renner Berthing <esmil@mailme.dk>2013-01-12 00:16:52 +0100
committerEmil Renner Berthing <esmil@mailme.dk>2013-01-12 00:57:32 +0100
commit13c16668dceefaf5072527df5484abf81282eed5 (patch)
treeaa9c00a32ee2d316a707403acb5bfa756a557b2e
parentafaf7c661b955de75ac0112d17a93fddfb71dd3f (diff)
downloadlem-13c16668dceefaf5072527df5484abf81282eed5.tar.gz
lem-13c16668dceefaf5072527df5484abf81282eed5.tar.xz
lem-13c16668dceefaf5072527df5484abf81282eed5.zip
io: add support for unix sockets
-rw-r--r--lem/io/core.c19
-rw-r--r--lem/io/unix.c232
2 files changed, 251 insertions, 0 deletions
diff --git a/lem/io/core.c b/lem/io/core.c
index e57e46b..0b30dc1 100644
--- a/lem/io/core.c
+++ b/lem/io/core.c
@@ -31,8 +31,13 @@
#include <netdb.h>
#if defined(__FreeBSD__) || defined(__APPLE__)
+#include <sys/un.h>
#include <netinet/in.h>
+#ifndef UNIX_PATH_MAX
+#define UNIX_PATH_MAX 104
+#endif
#else
+#include <linux/un.h>
#include <sys/sendfile.h>
#endif
@@ -66,6 +71,7 @@ io_strerror(lua_State *T, int err)
#include "stream.c"
#include "server.c"
#include "tcp.c"
+#include "unix.c"
static int
module_index(lua_State *T)
@@ -361,6 +367,19 @@ luaopen_lem_io_core(lua_State *L)
/* insert the tcp table */
lua_setfield(L, -2, "tcp");
+ /* create unix table */
+ lua_createtable(L, 0, 0);
+ /* insert the connect function */
+ lua_getfield(L, -2, "Stream"); /* upvalue 1 = Stream */
+ lua_pushcclosure(L, unix_connect, 1);
+ lua_setfield(L, -2, "connect");
+ /* insert the listen function */
+ lua_getfield(L, -2, "Server"); /* upvalue 1 = Server */
+ lua_pushcclosure(L, unix_listen, 1);
+ lua_setfield(L, -2, "listen");
+ /* insert the tcp table */
+ lua_setfield(L, -2, "unix");
+
/* create metatable for the module */
lua_newtable(L);
/* insert the index function */
diff --git a/lem/io/unix.c b/lem/io/unix.c
new file mode 100644
index 0000000..a7464f0
--- /dev/null
+++ b/lem/io/unix.c
@@ -0,0 +1,232 @@
+/*
+ * This file is part of LEM, a Lua Event Machine.
+ * Copyright 2013 Emil Renner Berthing
+ *
+ * LEM is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * LEM is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with LEM. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+struct unix_create {
+ struct lem_async a;
+ const char *path;
+ size_t len;
+ int sock;
+ int err;
+};
+
+static void
+unix_connect_work(struct lem_async *a)
+{
+ struct unix_create *u = (struct unix_create *)a;
+ struct sockaddr_un addr;
+ int sock;
+
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0) {
+ u->sock = -1;
+ u->err = errno;
+ return;
+ }
+
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, u->path);
+
+ if (connect(sock, (struct sockaddr *)&addr,
+ offsetof(struct sockaddr_un, sun_path) + u->len)) {
+ u->sock = -2;
+ u->err = errno;
+ goto error;
+ }
+
+ /* make the socket non-blocking */
+ if (fcntl(sock, F_SETFL, O_NONBLOCK)) {
+ u->sock = -3;
+ u->err = errno;
+ goto error;
+ }
+
+ u->sock = sock;
+ return;
+
+error:
+ close(sock);
+}
+
+static void
+unix_connect_reap(struct lem_async *a)
+{
+ struct unix_create *u = (struct unix_create *)a;
+ lua_State *T = u->a.T;
+ int sock = u->sock;
+
+ if (sock >= 0) {
+ free(u);
+
+ stream_new(T, sock, 2);
+ lem_queue(T, 1);
+ return;
+ }
+
+ lua_pushnil(T);
+ switch (-sock) {
+ case 1:
+ lua_pushfstring(T, "error creating socket: %s",
+ strerror(u->err));
+ break;
+ case 2:
+ lua_pushfstring(T, "error connecting to '%s': %s",
+ u->path, strerror(u->err));
+ break;
+ case 3:
+ lua_pushfstring(T, "error making socket non-blocking: %s",
+ strerror(u->err));
+ break;
+ }
+ lem_queue(T, 2);
+ free(u);
+}
+
+static int
+unix_connect(lua_State *T)
+{
+ size_t len;
+ const char *path = luaL_checklstring(T, 1, &len);
+ struct unix_create *u;
+
+ if (len >= UNIX_PATH_MAX) /* important. strcpy is used later */
+ return luaL_argerror(T, 1, "path too long");
+
+ u = lem_xmalloc(sizeof(struct unix_create));
+ u->path = path;
+ u->len = len;
+ lem_async_do(&u->a, T, unix_connect_work, unix_connect_reap);
+
+ lua_settop(T, 1);
+ lua_pushvalue(T, lua_upvalueindex(1));
+ return lua_yield(T, 2);
+}
+
+static void
+unix_listen_work(struct lem_async *a)
+{
+ struct unix_create *u = (struct unix_create *)a;
+ struct sockaddr_un addr;
+ int sock;
+
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0) {
+ u->sock = -1;
+ u->err = errno;
+ return;
+ }
+
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, u->path);
+
+ if (bind(sock, (struct sockaddr *)&addr,
+ offsetof(struct sockaddr_un, sun_path) + u->len)) {
+ u->sock = -2;
+ u->err = errno;
+ goto error;
+ }
+
+ if (listen(sock, u->err)) {
+ u->sock = -3;
+ u->err = errno;
+ goto error;
+ }
+
+ /* make the socket non-blocking */
+ if (fcntl(sock, F_SETFL, O_NONBLOCK)) {
+ u->sock = -4;
+ u->err = errno;
+ goto error;
+ }
+
+ u->sock = sock;
+ return;
+
+error:
+ close(sock);
+}
+
+static void
+unix_listen_reap(struct lem_async *a)
+{
+ struct unix_create *u = (struct unix_create *)a;
+ lua_State *T = u->a.T;
+ int sock = u->sock;
+
+ if (sock >= 0) {
+ struct ev_io *w;
+
+ free(u);
+
+ /* create userdata and set the metatable */
+ w = lua_newuserdata(T, sizeof(struct ev_io));
+ lua_pushvalue(T, 2);
+ lua_setmetatable(T, -2);
+
+ /* initialize userdata */
+ ev_io_init(w, NULL, sock, EV_READ);
+ w->data = NULL;
+
+ lem_queue(T, 1);
+ return;
+ }
+
+ lua_pushnil(T);
+ switch (-sock) {
+ case 1:
+ lua_pushfstring(T, "error creating socket: %s",
+ strerror(u->err));
+ break;
+ case 2:
+ lua_pushfstring(T, "error binding to '%s': %s",
+ u->path, strerror(u->err));
+ break;
+ case 3:
+ lua_pushfstring(T, "error listening on '%s': %s",
+ u->path, strerror(u->err));
+ break;
+
+ case 4:
+ lua_pushfstring(T, "error making socket non-blocking: %s",
+ strerror(u->err));
+ break;
+ }
+ lem_queue(T, 2);
+ free(u);
+}
+
+static int
+unix_listen(lua_State *T)
+{
+ size_t len;
+ const char *path = luaL_checklstring(T, 1, &len);
+ int backlog = (int)luaL_optnumber(T, 2, MAXPENDING);
+ struct unix_create *u;
+
+ if (len >= UNIX_PATH_MAX) /* important. strcpy is used later */
+ return luaL_argerror(T, 1, "path too long");
+
+ u = lem_xmalloc(sizeof(struct unix_create));
+ u->path = path;
+ u->len = len;
+ u->err = backlog;
+ lem_async_do(&u->a, T, unix_listen_work, unix_listen_reap);
+
+ lua_settop(T, 1);
+ lua_pushvalue(T, lua_upvalueindex(1));
+ return lua_yield(T, 2);
+}