nixio linux IO/socket api (incomplete / WIP)
authorSteven Barth <steven@midlink.org>
Sun, 8 Feb 2009 14:01:48 +0000 (14:01 +0000)
committerSteven Barth <steven@midlink.org>
Sun, 8 Feb 2009 14:01:48 +0000 (14:01 +0000)
libs/nixio/Makefile [new file with mode: 0644]
libs/nixio/src/address.c [new file with mode: 0644]
libs/nixio/src/bind.c [new file with mode: 0644]
libs/nixio/src/nixio.c [new file with mode: 0644]
libs/nixio/src/nixio.h [new file with mode: 0644]
libs/nixio/src/select.c [new file with mode: 0644]
libs/nixio/src/socket.c [new file with mode: 0644]
libs/nixio/src/sockopt.c [new file with mode: 0644]

diff --git a/libs/nixio/Makefile b/libs/nixio/Makefile
new file mode 100644 (file)
index 0000000..a2ce5da
--- /dev/null
@@ -0,0 +1,14 @@
+include ../../build/config.mk
+include ../../build/module.mk
+include ../../build/gccconfig.mk
+
+%.o: %.c
+       $(COMPILE) $(LUA_CFLAGS) $(FPIC) -c -o $@ $< 
+
+compile: src/nixio.o src/socket.o src/sockopt.o src/bind.o src/address.o src/select.o
+       $(LINK) $(SHLIB_FLAGS) -o src/nixio.so src/*.o
+       mkdir -p dist$(LUA_LIBRARYDIR)
+       cp src/nixio.so dist$(LUA_LIBRARYDIR)/nixio.so
+
+clean: luaclean
+       rm -f src/*.o src/*.so
diff --git a/libs/nixio/src/address.c b/libs/nixio/src/address.c
new file mode 100644 (file)
index 0000000..ae01c19
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * nixio - Linux I/O library for lua
+ *
+ *   Copyright (C) 2009 Steven Barth <steven@midlink.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <netdb.h>
+#include "nixio.h"
+
+/**
+ * getaddrinfo(host, family, port)
+ */
+static int nixio_getaddrinfo(lua_State *L) {
+       const char *host = NULL;
+       if (!lua_isnoneornil(L, 1)) {
+               host = luaL_checklstring(L, 1, NULL);
+       }
+       const char *family = luaL_optlstring(L, 2, "any", NULL);
+       const char *port = lua_tolstring(L, 3, NULL);
+
+       struct addrinfo hints, *result, *rp;
+       memset(&hints, 0, sizeof(hints));
+
+       if (!strcmp(family, "any")) {
+               hints.ai_family = AF_UNSPEC;
+       } else if (!strcmp(family, "inet")) {
+               hints.ai_family = AF_INET;
+       } else if (!strcmp(family, "inet6")) {
+               hints.ai_family = AF_INET6;
+       } else {
+               return luaL_argerror(L, 2, "supported values: any, inet, inet6");
+       }
+
+       hints.ai_socktype = 0;
+       hints.ai_protocol = 0;
+
+       int aistat = getaddrinfo(host, port, &hints, &result);
+       if (aistat) {
+               lua_pushnil(L);
+               lua_pushinteger(L, aistat);
+               lua_pushstring(L, gai_strerror(aistat));
+               return 3;
+       }
+
+       /* create socket object */
+       lua_newtable(L);
+       int i = 1;
+
+       for (rp = result; rp != NULL; rp = rp->ai_next) {
+               /* avoid duplicate results */
+               if (!port && rp->ai_socktype != SOCK_STREAM) {
+                       continue;
+               }
+
+               if (rp->ai_family == AF_INET || rp->ai_family == AF_INET6) {
+                       lua_createtable(L, 0, port ? 4 : 2);
+                       if (rp->ai_family == AF_INET) {
+                               lua_pushliteral(L, "inet");
+                       } else if (rp->ai_family == AF_INET6) {
+                               lua_pushliteral(L, "inet6");
+                       }
+                       lua_setfield(L, -2, "family");
+
+                       if (port) {
+                               switch (rp->ai_socktype) {
+                                       case SOCK_STREAM:
+                                               lua_pushliteral(L, "stream");
+                                               break;
+                                       case SOCK_DGRAM:
+                                               lua_pushliteral(L, "dgram");
+                                               break;
+                                       case SOCK_RAW:
+                                               lua_pushliteral(L, "raw");
+                                               break;
+                                       default:
+                                               lua_pushnil(L);
+                                               break;
+                               }
+                               lua_setfield(L, -2, "socktype");
+                       }
+
+                       char ip[INET6_ADDRSTRLEN];
+                       void *binaddr = NULL;
+                       uint16_t binport = 0;
+
+                       if (rp->ai_family == AF_INET) {
+                               struct sockaddr_in *v4addr = (struct sockaddr_in*)rp->ai_addr;
+                               binport = v4addr->sin_port;
+                               binaddr = (void *)&v4addr->sin_addr;
+                       } else if (rp->ai_family == AF_INET6) {
+                               struct sockaddr_in6 *v6addr = (struct sockaddr_in6*)rp->ai_addr;
+                               binport = v6addr->sin6_port;
+                               binaddr = (void *)&v6addr->sin6_addr;
+                       }
+
+                       if (!inet_ntop(rp->ai_family, binaddr, ip, sizeof(ip))) {
+                               freeaddrinfo(result);
+                               return nixio__perror(L);
+                       }
+
+                       if (port) {
+                               lua_pushinteger(L, ntohs(binport));
+                               lua_setfield(L, -2, "port");
+                       }
+
+                       lua_pushstring(L, ip);
+                       lua_setfield(L, -2, "address");
+                       lua_rawseti(L, -2, i++);
+               }
+       }
+
+       freeaddrinfo(result);
+
+       return 1;
+}
+
+/**
+ * getnameinfo(address, family)
+ */
+static int nixio_getnameinfo(lua_State *L) {
+       const char *ip = luaL_checklstring(L, 1, NULL);
+       const char *family = luaL_optlstring(L, 2, "inet", NULL);
+       char host[NI_MAXHOST];
+
+       struct sockaddr *addr = NULL;
+       socklen_t alen = 0;
+       int res;
+
+       if (!strcmp(family, "inet")) {
+               struct sockaddr_in inetaddr;
+               memset(&inetaddr, 0, sizeof(inetaddr));
+               inetaddr.sin_family = AF_INET;
+               if (inet_pton(AF_INET, ip, &inetaddr.sin_addr) < 1) {
+                       return luaL_argerror(L, 1, "invalid address");
+               }
+               alen = sizeof(inetaddr);
+               addr = (struct sockaddr *)&inetaddr;
+       } else if (!strcmp(family, "inet6")) {
+               struct sockaddr_in6 inet6addr;
+               memset(&inet6addr, 0, sizeof(inet6addr));
+               inet6addr.sin6_family = AF_INET6;
+               if (inet_pton(AF_INET6, ip, &inet6addr.sin6_addr) < 1) {
+                       return luaL_argerror(L, 1, "invalid address");
+               }
+               alen = sizeof(inet6addr);
+               addr = (struct sockaddr *)&inet6addr;
+       } else {
+               return luaL_argerror(L, 2, "supported values: inet, inet6");
+       }
+
+       res = getnameinfo(addr, alen, host, sizeof(host), NULL, 0, NI_NAMEREQD);
+       if (res) {
+               lua_pushnil(L);
+               lua_pushinteger(L, res);
+               lua_pushstring(L, gai_strerror(res));
+               return 3;
+       } else {
+               lua_pushstring(L, host);
+               return 1;
+       }
+}
+
+/**
+ * getsockname() / getpeername() helper
+ */
+static int nixio_sock__getname(lua_State *L, int sock) {
+       int sockfd = nixio__checksockfd(L);
+       struct sockaddr_storage addr;
+       socklen_t addrlen = sizeof(addr);
+       char ipaddr[INET6_ADDRSTRLEN];
+       void *binaddr;
+       uint16_t port;
+
+       if (sock) {
+               if (getsockname(sockfd, (struct sockaddr*)&addr, &addrlen)) {
+                       return nixio__perror(L);
+               }
+       } else {
+               if (getpeername(sockfd, (struct sockaddr*)&addr, &addrlen)) {
+                       return nixio__perror(L);
+               }
+       }
+
+       if (addr.ss_family == AF_INET) {
+               struct sockaddr_in *inetaddr = (struct sockaddr_in*)&addr;
+               port = inetaddr->sin_port;
+               binaddr = &inetaddr->sin_addr;
+       } else if (addr.ss_family == AF_INET6) {
+               struct sockaddr_in6 *inet6addr = (struct sockaddr_in6*)&addr;
+               port = inet6addr->sin6_port;
+               binaddr = &inet6addr->sin6_addr;
+       } else {
+               return luaL_error(L, "unknown address family");
+       }
+
+       if (!inet_ntop(addr.ss_family, binaddr, ipaddr, sizeof(ipaddr))) {
+               return nixio__perror(L);
+       }
+
+       lua_pushstring(L, ipaddr);
+       lua_pushinteger(L, ntohs(port));
+       return 2;
+}
+
+/**
+ * getsockname()
+ */
+static int nixio_sock_getsockname(lua_State *L) {
+       return nixio_sock__getname(L, 1);
+}
+
+/**
+ * getpeername()
+ */
+static int nixio_sock_getpeername(lua_State *L) {
+       return nixio_sock__getname(L, 0);
+}
+
+
+/* module table */
+static const luaL_reg R[] = {
+       {"getaddrinfo", nixio_getaddrinfo},
+       {"getnameinfo", nixio_getnameinfo},
+       {NULL,                  NULL}
+};
+
+/* object table */
+static const luaL_reg M[] = {
+       {"getsockname", nixio_sock_getsockname},
+       {"getpeername", nixio_sock_getpeername},
+       {NULL,                  NULL}
+};
+
+void nixio_open_address(lua_State *L) {
+       luaL_register(L, NULL, R);
+
+       lua_pushvalue(L, -2);
+       luaL_register(L, NULL, M);
+       lua_pop(L, 1);
+}
diff --git a/libs/nixio/src/bind.c b/libs/nixio/src/bind.c
new file mode 100644 (file)
index 0000000..66337ee
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * nixio - Linux I/O library for lua
+ *
+ *   Copyright (C) 2009 Steven Barth <steven@midlink.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+#include "nixio.h"
+
+/**
+ * connect()/bind() shortcut
+ */
+static int nixio__bind_connect(lua_State *L, int do_bind) {
+       const char *host = NULL;
+       if (!lua_isnoneornil(L, 1)) {
+               host = luaL_checklstring(L, 1, NULL);
+       }
+       const char *port = luaL_checklstring(L, 2, NULL);
+       const char *family = luaL_optlstring(L, 3, "any", NULL);
+       const char *socktype = luaL_optlstring(L, 4, "stream", NULL);
+
+       struct addrinfo hints, *result, *rp;
+       memset(&hints, 0, sizeof(hints));
+
+       if (!strcmp(family, "any")) {
+               hints.ai_family = AF_UNSPEC;
+       } else if (!strcmp(family, "inet")) {
+               hints.ai_family = AF_INET;
+       } else if (!strcmp(family, "inet6")) {
+               hints.ai_family = AF_INET6;
+       } else {
+               return luaL_argerror(L, 3, "supported values: any, inet, inet6");
+       }
+
+       if (!strcmp(socktype, "any")) {
+               hints.ai_socktype = 0;
+       } else if (!strcmp(socktype, "stream")) {
+               hints.ai_socktype = SOCK_STREAM;
+       } else if (!strcmp(socktype, "dgram")) {
+               hints.ai_socktype = SOCK_DGRAM;
+       } else {
+               return luaL_argerror(L, 4, "supported values: any, stream, dgram");
+       }
+
+       if (do_bind) {
+               hints.ai_flags |= AI_PASSIVE;
+       }
+
+       hints.ai_protocol = 0;
+
+       int aistat = getaddrinfo(host, port, &hints, &result);
+       if (aistat) {
+               lua_pushnil(L);
+               lua_pushinteger(L, aistat);
+               lua_pushstring(L, gai_strerror(aistat));
+               return 3;
+       }
+
+       /* create socket object */
+       nixio_sock *sock = lua_newuserdata(L, sizeof(nixio_sock));
+       int status = -1;
+
+       for (rp = result; rp != NULL; rp = rp->ai_next) {
+               sock->fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+               if (sock->fd == -1) {
+                       continue;
+               }
+
+               if (do_bind) {
+                       status = bind(sock->fd, rp->ai_addr, rp->ai_addrlen);
+               } else {
+                       status = connect(sock->fd, rp->ai_addr, rp->ai_addrlen);
+               }
+
+               /* on success */
+               if (!status) {
+                       sock->domain = rp->ai_family;
+                       sock->type = rp->ai_socktype;
+                       sock->protocol = rp->ai_protocol;
+                       break;
+               }
+
+               close(sock->fd);
+       }
+
+       freeaddrinfo(result);
+
+       /* on failure */
+       if (status) {
+               return nixio__perror(L);
+       }
+
+       luaL_getmetatable(L, NIXIO_META);
+       lua_setmetatable(L, -2);
+
+       return 1;
+}
+
+/**
+ * bind(host, port, [family=any], [type=any]) shortcut
+ */
+static int nixio_bind(lua_State *L) {
+       return nixio__bind_connect(L, 1);
+}
+
+/**
+ * connect(host, port, [family=any], [type=any]) shortcut
+ */
+static int nixio_connect(lua_State *L) {
+       return nixio__bind_connect(L, 0);
+}
+
+/**
+ * bind()/connect() helper
+ */
+static int nixio_sock__bind_connect(lua_State *L, int do_bind) {
+       nixio_sock *sock = nixio__checksock(L);
+       int status = -1;
+
+       if (sock->domain == AF_INET || sock->domain == AF_INET6) {
+               const char *host = NULL;
+               if (!lua_isnoneornil(L, 2)) {
+                       host = luaL_checklstring(L, 2, NULL);
+               }
+               const char *port = luaL_checklstring(L, 3, NULL);
+
+               struct addrinfo hints, *result, *rp;
+
+               memset(&hints, 0, sizeof(hints));
+               hints.ai_family = sock->domain;
+               hints.ai_socktype = sock->type;
+               hints.ai_protocol = sock->protocol;
+
+               if (do_bind) {
+                       hints.ai_flags |= AI_PASSIVE;
+               }
+
+               int aistat = getaddrinfo(host, port, &hints, &result);
+               if (aistat) {
+                       lua_pushnil(L);
+                       lua_pushinteger(L, aistat);
+                       lua_pushstring(L, gai_strerror(aistat));
+                       return 3;
+               }
+
+               for (rp = result; rp != NULL; rp = rp->ai_next) {
+                       if (do_bind) {
+                               status = bind(sock->fd, rp->ai_addr, rp->ai_addrlen);
+                       } else {
+                               status = connect(sock->fd, rp->ai_addr, rp->ai_addrlen);
+                       }
+
+                       /* on success */
+                       if (!status) {
+                               break;
+                       }
+               }
+
+               freeaddrinfo(result);
+       } else if (sock->domain == AF_UNIX) {
+               size_t pathlen;
+               const char *path = luaL_checklstring(L, 2, &pathlen);
+
+               struct sockaddr_un addr;
+               addr.sun_family = AF_UNIX;
+               luaL_argcheck(L, pathlen < sizeof(addr.sun_path), 2, "out of range");
+               strncpy(addr.sun_path, path, sizeof(addr.sun_path));
+
+               if (do_bind) {
+                       status = bind(sock->fd, (struct sockaddr*)&addr, sizeof(addr));
+               } else {
+                       status = connect(sock->fd, (struct sockaddr*)&addr, sizeof(addr));
+               }
+       } else {
+               return luaL_error(L, "not supported");
+       }
+       return nixio__pstatus(L, !status);
+}
+
+/**
+ * bind()
+ */
+static int nixio_sock_bind(lua_State *L) {
+       return nixio_sock__bind_connect(L, 1);
+}
+
+/**
+ * connect()
+ */
+static int nixio_sock_connect(lua_State *L) {
+       return nixio_sock__bind_connect(L, 0);
+}
+
+/**
+ * listen()
+ */
+static int nixio_sock_listen(lua_State *L) {
+       int sockfd = nixio__checksockfd(L);
+       lua_Integer backlog = luaL_checkinteger(L, 2);
+       return nixio__pstatus(L, !listen(sockfd, backlog));
+}
+
+/**
+ * accept()
+ */
+static int nixio_sock_accept(lua_State *L) {
+       nixio_sock *sock = nixio__checksock(L);
+       struct sockaddr_storage addr;
+       socklen_t addrlen = sizeof(addr);
+       char ipaddr[INET6_ADDRSTRLEN];
+       void *binaddr;
+       uint16_t port;
+
+       int newfd = accept(sock->fd, (struct sockaddr *)&addr, &addrlen);
+       if (newfd < 0) {
+               return nixio__perror(L);
+       }
+
+       /* create userdata */
+       nixio_sock *clsock = lua_newuserdata(L, sizeof(nixio_sock));
+       luaL_getmetatable(L, NIXIO_META);
+       lua_setmetatable(L, -2);
+
+       memcpy(clsock, sock, sizeof(clsock));
+       clsock->fd = newfd;
+
+       if (addr.ss_family == AF_INET) {
+               struct sockaddr_in *inetaddr = (struct sockaddr_in*)&addr;
+               port = inetaddr->sin_port;
+               binaddr = &inetaddr->sin_addr;
+       } else if (addr.ss_family == AF_INET6) {
+               struct sockaddr_in6 *inet6addr = (struct sockaddr_in6*)&addr;
+               port = inet6addr->sin6_port;
+               binaddr = &inet6addr->sin6_addr;
+       } else {
+               return luaL_error(L, "unknown address family");
+       }
+
+       if (!inet_ntop(addr.ss_family, binaddr, ipaddr, sizeof(ipaddr))) {
+               return nixio__perror(L);
+       }
+
+       lua_pushstring(L, ipaddr);
+       lua_pushinteger(L, ntohs(port));
+       return 3;
+}
+
+/* module table */
+static const luaL_reg R[] = {
+       {"bind",                nixio_bind},
+       {"connect",             nixio_connect},
+       {NULL,                  NULL}
+};
+
+/* object table */
+static const luaL_reg M[] = {
+       {"bind",                nixio_sock_bind},
+       {"connect",             nixio_sock_connect},
+       {"listen",              nixio_sock_listen},
+       {"accept",              nixio_sock_accept},
+       {NULL,                  NULL}
+};
+
+void nixio_open_bind(lua_State *L) {
+       luaL_register(L, NULL, R);
+
+       lua_pushvalue(L, -2);
+       luaL_register(L, NULL, M);
+       lua_pop(L, 1);
+}
diff --git a/libs/nixio/src/nixio.c b/libs/nixio/src/nixio.c
new file mode 100644 (file)
index 0000000..1152f2c
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * nixio - Linux I/O library for lua
+ *
+ *   Copyright (C) 2009 Steven Barth <steven@midlink.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include "nixio.h"
+
+#define VERSION 0.1
+
+
+/* pushes nil, error number and errstring on the stack */
+int nixio__perror(lua_State *L) {
+    lua_pushnil(L);
+    lua_pushinteger(L, errno);
+    lua_pushstring(L, strerror(errno));
+    return 3;
+}
+
+/* pushes true, if operation succeeded, otherwise call nixio__perror */
+int nixio__pstatus(lua_State *L, int condition) {
+       if (condition) {
+               lua_pushboolean(L, 1);
+               return 1;
+       } else {
+               return nixio__perror(L);
+       }
+}
+
+/* checks whether the first argument is a socket and returns it */
+nixio_sock* nixio__checksock(lua_State *L) {
+    nixio_sock *sock = (nixio_sock*)luaL_checkudata(L, 1, NIXIO_META);
+    luaL_argcheck(L, sock->fd != -1, 1, "invalid socket object");
+    return sock;
+}
+
+/* read fd from nixio_sock object */
+int nixio__checksockfd(lua_State *L) {
+       return nixio__checksock(L)->fd;
+}
+
+/* return any possible fd, otherwise error out */
+int nixio__checkfd(lua_State *L, int ud) {
+       int fd = nixio__tofd(L, ud);
+       return (fd != -1) ? fd : luaL_argerror(L, ud, "invalid file descriptor");
+}
+
+/* return any possible fd */
+int nixio__tofd(lua_State *L, int ud) {
+       void *udata = lua_touserdata(L, ud);
+       int fd = -1;
+       if (udata && lua_getmetatable(L, ud)) {
+               luaL_getmetatable(L, NIXIO_META);
+               luaL_getmetatable(L, LUA_FILEHANDLE);
+               if (lua_rawequal(L, -2, -3)) {
+                       fd = ((nixio_sock*)udata)->fd;
+               } else if (lua_rawequal(L, -1, -3)) {
+                       fd = fileno(*((FILE **)udata));
+               }
+               lua_pop(L, 3);
+       }
+       return fd;
+}
+
+/* object table */
+static const luaL_reg R[] = {
+       {NULL,                  NULL}
+};
+
+/* entry point */
+LUALIB_API int luaopen_nixio(lua_State *L) {
+       /* create metatable */
+       luaL_newmetatable(L, NIXIO_META);
+
+       /* method table */
+       lua_newtable(L);
+       lua_pushvalue(L, -1);
+
+       /* metatable.__index = M */
+       lua_setfield(L, -3, "__index");
+       lua_remove(L, -2);
+
+       /* register module */
+       luaL_register(L, "nixio", R);
+
+       /* register methods */
+       nixio_open_socket(L);
+       nixio_open_sockopt(L);
+       nixio_open_bind(L);
+       nixio_open_address(L);
+       nixio_open_select(L);
+
+       /* module version */
+       lua_pushnumber(L, VERSION);
+       lua_setfield(L, -2, "version");
+
+       /* remove meta table */
+       lua_remove(L, -2);
+
+       return 1;
+}
diff --git a/libs/nixio/src/nixio.h b/libs/nixio/src/nixio.h
new file mode 100644 (file)
index 0000000..ce79c9a
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef NIXIO_H_
+#define NIXIO_H_
+
+#define NIXIO_META "nixio.socket"
+
+struct nixio_socket {
+       int fd;
+       int domain;
+       int type;
+       int protocol;
+};
+
+typedef struct nixio_socket nixio_sock;
+
+int nixio__perror(lua_State *L);
+int nixio__pstatus(lua_State *L, int condition);
+nixio_sock* nixio__checksock(lua_State *L);
+int nixio__checksockfd(lua_State *L);
+int nixio__checkfd(lua_State *L, int ud);
+int nixio__tofd(lua_State *L, int ud);
+
+/* Module functions */
+void nixio_open_socket(lua_State *L);
+void nixio_open_sockopt(lua_State *L);
+void nixio_open_bind(lua_State *L);
+void nixio_open_address(lua_State *L);
+void nixio_open_select(lua_State *L);
+
+/* Method functions */
+
+#endif /* NIXIO_H_ */
diff --git a/libs/nixio/src/select.c b/libs/nixio/src/select.c
new file mode 100644 (file)
index 0000000..e88075c
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * nixio - Linux I/O library for lua
+ *
+ *   Copyright (C) 2009 Steven Barth <steven@midlink.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+#include <sys/select.h>
+#include "nixio.h"
+
+static int nixio_select(lua_State *L) {
+       int nfds = 0, tmpfd = -1, i = 0, j = 0, o = 0, k, tlen;
+       fd_set rfds, wfds, xfds;
+       fd_set *fdsets[3] = {&rfds, &wfds, &xfds};
+       FD_ZERO(fdsets[0]);
+       FD_ZERO(fdsets[1]);
+       FD_ZERO(fdsets[2]);
+
+       struct timeval timeout;
+       timeout.tv_sec = luaL_optinteger(L, 4, 0);
+       timeout.tv_usec = luaL_optinteger(L, 5, 0);
+
+       /* create fdsets */
+       for (i=0; i<3; i++) {
+               o = i + 1;
+               if (lua_isnoneornil(L, o)) {
+                       fdsets[i] = NULL;
+                       continue;
+               }
+
+               luaL_checktype(L, o, LUA_TTABLE);
+               tlen = lua_objlen(L, o);
+               luaL_argcheck(L, tlen <= FD_SETSIZE, o, "too many fds");
+
+               for (j=1; j<=tlen; j++) {
+                       lua_rawgeti(L, o, j);
+                       tmpfd = nixio__checkfd(L, -1);
+                       FD_SET(tmpfd, fdsets[i]);
+                       if (tmpfd >= nfds) {
+                               nfds = tmpfd + 1;
+                       }
+                       lua_pop(L, 1);
+               }
+       }
+
+       int stat = select(nfds, fdsets[0], fdsets[1], fdsets[2], &timeout);
+
+       if (stat < 0) {
+               return nixio__perror(L);
+       } else if (stat == 0) {
+               lua_pushinteger(L, stat);
+               for (i=1; i<=3; i++) {
+                       if (lua_isnoneornil(L, i)) {
+                               lua_pushnil(L);
+                       } else {
+                               lua_newtable(L);
+                       }
+               }
+       } else {
+               lua_pushinteger(L, stat);
+
+               /* create return tables */
+               for (i=0; i<3; i++) {
+                       o = i + 1;
+                       if (lua_isnoneornil(L, o)) {
+                               lua_pushnil(L);
+                               continue;
+                       }
+
+                       lua_newtable(L);
+                       tlen = lua_objlen(L, o);
+                       k = 1;
+
+                       for (j=1; j<=tlen; j++) {
+                               lua_rawgeti(L, o, j);
+                               tmpfd = nixio__tofd(L, -1);
+                               if (FD_ISSET(tmpfd, fdsets[i])) {
+                                       lua_rawseti(L, -2, k++);
+                               } else {
+                                       lua_pop(L, 1);
+                               }
+                       }
+               }
+       }
+       return 4;
+}
+
+/* module table */
+static const luaL_reg R[] = {
+       {"select",      nixio_select},
+       {NULL,                  NULL}
+};
+
+void nixio_open_select(lua_State *L) {
+       luaL_register(L, NULL, R);
+}
diff --git a/libs/nixio/src/socket.c b/libs/nixio/src/socket.c
new file mode 100644 (file)
index 0000000..acfa9ae
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * nixio - Linux I/O library for lua
+ *
+ *   Copyright (C) 2009 Steven Barth <steven@midlink.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include "nixio.h"
+
+
+/**
+ * create new socket
+ */
+static int nixio_socket(lua_State *L) {
+       const char *domain = luaL_optlstring(L, 1, "", NULL);
+       const char *type   = luaL_optlstring(L, 2, "", NULL);
+       const char *proto  = lua_tolstring(L, 3, NULL);
+
+       nixio_sock *sock = lua_newuserdata(L, sizeof(nixio_sock));
+
+       if (!strcmp(domain, "inet")) {
+               sock->domain = AF_INET;
+       } else if (!strcmp(domain, "inet6")) {
+               sock->domain = AF_INET6;
+       } else if (!strcmp(domain, "unix")) {
+               sock->domain = AF_UNIX;
+       } else if (!strcmp(domain, "packet")) {
+               sock->domain = AF_PACKET;
+       } else {
+               return luaL_argerror(L, 1,
+                "supported values: inet, inet6, unix, packet"
+               );
+       }
+
+       if (!strcmp(type, "stream")) {
+               sock->type = SOCK_STREAM;
+       } else if (!strcmp(type, "dgram")) {
+               sock->type = SOCK_DGRAM;
+       } else if (!strcmp(type, "raw")) {
+               sock->type = SOCK_RAW;
+       } else {
+               return luaL_argerror(L, 2, "supported values: stream, dgram, raw");
+       }
+
+       if (!proto) {
+               sock->protocol = 0;
+       } else if (!strcmp(proto, "icmp")) {
+               sock->protocol = IPPROTO_ICMP;
+       } else if (!strcmp(proto, "icmpv6")) {
+               sock->protocol = IPPROTO_ICMPV6;
+       } else {
+               return luaL_argerror(L, 3, "supported values: [empty], icmp, icmpv6");
+       }
+
+       /* create userdata */
+       luaL_getmetatable(L, NIXIO_META);
+       lua_setmetatable(L, -2);
+
+       sock->fd = socket(sock->domain, sock->type, sock->protocol);
+
+       if (sock->fd < 0) {
+               return nixio__perror(L);
+       }
+
+       return 1;
+}
+
+/**
+ * close a socket
+ */
+static int nixio_sock_close(lua_State *L) {
+       nixio_sock *sock = nixio__checksock(L);
+       int sockfd = sock->fd;
+       sock->fd = -1;
+       return nixio__pstatus(L, !close(sockfd));
+}
+
+/**
+ * garbage collector
+ */
+static int nixio_sock__gc(lua_State *L) {
+       nixio_sock *sock = (nixio_sock*)luaL_checkudata(L, 1, NIXIO_META);
+       if (sock && sock->fd != -1) {
+               close(sock->fd);
+       }
+       return 0;
+}
+
+/**
+ * string representation
+ */
+static int nixio_sock__tostring(lua_State *L) {
+       lua_pushfstring(L, "nixio socket %d", nixio__checksockfd(L));
+       return 1;
+}
+
+/**
+ * shutdown a socket
+ */
+static int nixio_sock_shutdown(lua_State *L) {
+       int sockfd = nixio__checksockfd(L);
+       const char *what = luaL_optlstring(L, 2, "rdwr", NULL);
+       int how;
+
+       if (!strcmp(what, "rdwr") || !strcmp(what, "both")) {
+               how = SHUT_RDWR;
+       } else if (!strcmp(what, "rd") || !strcmp(what, "read")) {
+               how = SHUT_RD;
+       } else if (!strcmp(what, "wr") || !strcmp(what, "write")) {
+               how = SHUT_WR;
+       } else {
+               return luaL_argerror(L, 2, "supported values: both, read, write");
+       }
+
+       return nixio__pstatus(L, !shutdown(sockfd, how));
+}
+
+/* module table */
+static const luaL_reg R[] = {
+       {"socket",              nixio_socket},
+       {NULL,                  NULL}
+};
+
+/* object table */
+static const luaL_reg M[] = {
+       {"close",               nixio_sock_close},
+       {"shutdown",    nixio_sock_shutdown},
+       {NULL,                  NULL}
+};
+
+void nixio_open_socket(lua_State *L) {
+       luaL_getmetatable(L, NIXIO_META);
+       lua_pushcfunction(L, nixio_sock__gc);
+       lua_setfield(L, -2, "__gc");
+       lua_pushcfunction(L, nixio_sock__tostring);
+       lua_setfield(L, -2, "__tostring");
+       lua_pop(L, 1);
+
+       luaL_register(L, NULL, R);
+
+       lua_pushvalue(L, -2);
+       luaL_register(L, NULL, M);
+       lua_pop(L, 1);
+}
diff --git a/libs/nixio/src/sockopt.c b/libs/nixio/src/sockopt.c
new file mode 100644 (file)
index 0000000..f1a326c
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * nixio - Linux I/O library for lua
+ *
+ *   Copyright (C) 2009 Steven Barth <steven@midlink.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/time.h>
+#include <string.h>
+#include <fcntl.h>
+#include "nixio.h"
+
+/**
+ * setblocking()
+ */
+static int nixio_sock_setblocking(lua_State *L) {
+       int fd = nixio__checkfd(L, 1);
+       int set = lua_toboolean(L, 2);
+       int flags = fcntl(fd, F_GETFL);
+
+       if (flags == -1) {
+               return nixio__perror(L);
+       }
+
+       if (set) {
+               flags |= O_NONBLOCK;
+       } else {
+               flags &= ~O_NONBLOCK;
+       }
+
+       return nixio__pstatus(L, !fcntl(fd, F_SETFL, flags));
+}
+
+static int nixio__gso_int(lua_State *L, int fd, int level, int opt, int set) {
+       int value;
+       socklen_t optlen = sizeof(value);
+       if (!set) {
+               if (!getsockopt(fd, level, opt, &value, &optlen)) {
+                       lua_pushinteger(L, value);
+                       return 1;
+               }
+       } else {
+               value = luaL_checkinteger(L, set);
+               if (!setsockopt(fd, level, opt, &value, optlen)) {
+                       lua_pushboolean(L, 1);
+                       return 1;
+               }
+       }
+       return nixio__perror(L);
+}
+
+static int nixio__gso_ling(lua_State *L, int fd, int level, int opt, int set) {
+       struct linger value;
+       socklen_t optlen = sizeof(value);
+       if (!set) {
+               if (!getsockopt(fd, level, opt, &value, &optlen)) {
+                       lua_pushinteger(L, value.l_onoff ? value.l_linger : 0);
+                       return 1;
+               }
+       } else {
+               value.l_linger = luaL_checkinteger(L, set);
+               value.l_onoff = value.l_linger ? 1 : 0;
+               if (!setsockopt(fd, level, opt, &value, optlen)) {
+                       lua_pushboolean(L, 1);
+                       return 1;
+               }
+       }
+       return nixio__perror(L);
+}
+
+static int nixio__gso_timev(lua_State *L, int fd, int level, int opt, int set) {
+       struct timeval value;
+       socklen_t optlen = sizeof(value);
+       if (!set) {
+               if (!getsockopt(fd, level, opt, &value, &optlen)) {
+                       lua_pushinteger(L, value.tv_sec);
+                       lua_pushinteger(L, value.tv_usec);
+                       return 2;
+               }
+       } else {
+               value.tv_sec  = luaL_checkinteger(L, set);
+               value.tv_usec = luaL_optinteger(L, set + 1, 0);
+               if (!setsockopt(fd, level, opt, &value, optlen)) {
+                       lua_pushboolean(L, 1);
+                       return 1;
+               }
+       }
+       return nixio__perror(L);
+}
+
+/**
+ * get/setsockopt() helper
+ */
+static int nixio__getsetsockopt(lua_State *L, int set) {
+       nixio_sock *sock = nixio__checksock(L);
+       const char *level = luaL_optlstring(L, 2, "", NULL);
+       const char *option = luaL_optlstring(L, 3, "", NULL);
+       set = (set) ? 4 : 0;
+
+       if (!strcmp(level, "socket")) {
+               if (!strcmp(option, "keepalive")) {
+                       return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_KEEPALIVE, set);
+               } else if (!strcmp(option, "reuseaddr")) {
+                       return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_REUSEADDR, set);
+               } else if (!strcmp(option, "rcvbuf")) {
+                       return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_RCVBUF, set);
+               } else if (!strcmp(option, "sndbuf")) {
+                       return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_SNDBUF, set);
+               } else if (!strcmp(option, "priority")) {
+                       return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_PRIORITY, set);
+               } else if (!strcmp(option, "broadcast")) {
+                       return nixio__gso_int(L, sock->fd, SOL_SOCKET, SO_BROADCAST, set);
+               } else if (!strcmp(option, "linger")) {
+                       return nixio__gso_ling(L, sock->fd, SOL_SOCKET, SO_LINGER, set);
+               } else if (!strcmp(option, "sndtimeo")) {
+                       return nixio__gso_timev(L, sock->fd, SOL_SOCKET, SO_SNDTIMEO, set);
+               } else if (!strcmp(option, "rcvtimeo")) {
+                       return nixio__gso_timev(L, sock->fd, SOL_SOCKET, SO_RCVTIMEO, set);
+               } else {
+                       return luaL_argerror(L, 3, "supported values: keepalive, reuseaddr,"
+                        " sndbuf, rcvbuf, priority, broadcast, linger, sndtimeo, rcvtimeo"
+                       );
+               }
+       } else if (!strcmp(level, "tcp")) {
+               if (sock->type != SOCK_STREAM) {
+                       return luaL_error(L, "not a TCP socket");
+               }
+               if (!strcmp(option, "cork")) {
+                       return nixio__gso_int(L, sock->fd, SOL_TCP, TCP_CORK, set);
+               } else if (!strcmp(option, "nodelay")) {
+                       return nixio__gso_int(L, sock->fd, SOL_TCP, TCP_NODELAY, set);
+               } else {
+                       return luaL_argerror(L, 3, "supported values: cork, nodelay");
+               }
+       } else {
+               return luaL_argerror(L, 2, "supported values: socket, tcp");
+       }
+}
+
+/**
+ * getsockopt()
+ */
+static int nixio_sock_getsockopt(lua_State *L) {
+       return nixio__getsetsockopt(L, 0);
+}
+
+/**
+ * setsockopt()
+ */
+static int nixio_sock_setsockopt(lua_State *L) {
+       return nixio__getsetsockopt(L, 1);
+}
+
+/* module table */
+static const luaL_reg M[] = {
+       {"setblocking", nixio_sock_setblocking},
+       {"getsockopt",  nixio_sock_getsockopt},
+       {"setsockopt",  nixio_sock_setsockopt},
+       {NULL,                  NULL}
+};
+
+void nixio_open_sockopt(lua_State *L) {
+       lua_pushvalue(L, -2);
+       luaL_register(L, NULL, M);
+       lua_pop(L, 1);
+
+       luaL_getmetatable(L, LUA_FILEHANDLE);
+       lua_pushcfunction(L, nixio_sock_setblocking);
+       lua_setfield(L, -2, "setblocking");
+       lua_pop(L, 1);
+}