# HG changeset patch # User jbe # Date 1428590296 -7200 # Node ID 110493d29f909c04437694dafd4e6e7e6bac2d8c # Parent 06d965df8a0c1ecdfa0e391a979783bbab669c8f Fixes in moonbridge_io.tcpconnect(...); Added moonbridge_io.tcplisten(...) diff -r 06d965df8a0c -r 110493d29f90 moonbridge_io.c --- a/moonbridge_io.c Wed Apr 08 23:13:09 2015 +0200 +++ b/moonbridge_io.c Thu Apr 09 16:38:16 2015 +0200 @@ -21,12 +21,15 @@ #define MOONBR_IO_READBUFLEN 4096 #define MOONBR_IO_WRITEBUFLEN 4096 +#define MOONBR_IO_LISTEN_BACKLOG 1024 + #define moonbr_io_errmsg() \ char errmsg[MOONBR_IO_MAXSTRERRORLEN]; \ strerror_r(errno, errmsg, MOONBR_IO_MAXSTRERRORLEN) #define MOONBR_IO_HANDLE_MT_REGKEY "moonbridge_io_handle" #define MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY "moonbridge_io_handle_public" +#define MOONBR_IO_LISTENER_MT_REGKEY "moonbridge_io_listener" typedef struct { int fd; @@ -55,6 +58,11 @@ char writebuf[MOONBR_IO_WRITEBUFLEN]; } moonbr_io_handle_t; +typedef struct { + int fd; + int nonblocking; +} moonbr_io_listener_t; + static void moonbr_io_handle_set_nonblocking(lua_State *L, moonbr_io_handle_t *handle, int nonblocking) { int flags; if (handle->nonblocking == nonblocking) return; @@ -453,7 +461,7 @@ return moonbr_io_close_impl(L, 1); } -static int moonbr_io_gc(lua_State *L) { +static int moonbr_io_handlegc(lua_State *L) { moonbr_io_handle_t *handle; handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); if (handle->fd >= 0) { @@ -655,13 +663,15 @@ addrinfo->ai_socktype | SOCK_CLOEXEC | (nonblocking ? SOCK_NONBLOCK : 0), addrinfo->ai_protocol ); - freeaddrinfo(res); if (sock < 0) { moonbr_io_errmsg(); + freeaddrinfo(res); lua_pushnil(L); lua_pushstring(L, errmsg); + return 2; } if (connect(sock, addrinfo->ai_addr, addrinfo->ai_addrlen)) { + freeaddrinfo(res); if (!nonblocking && errno == EINTR) { moonbr_io_errmsg(); close(sock); @@ -674,6 +684,8 @@ lua_pushstring(L, errmsg); return 2; } + } else { + freeaddrinfo(res); } moonbr_io_pushhandle(L, sock); return 1; @@ -687,8 +699,154 @@ return moonbr_io_tcpconnect_impl(L, 1); } +static int moonbr_io_tcplisten(lua_State *L) { + moonbr_io_listener_t *listener; + const char *host, *port; + struct addrinfo hints = { 0, }; + struct addrinfo *res, *addrinfo; + int errcode; + int sock; + host = luaL_optstring(L, 1, NULL); + port = luaL_checkstring(L, 2); + listener = lua_newuserdata(L, sizeof(moonbr_io_listener_t)); + luaL_setmetatable(L, MOONBR_IO_LISTENER_MT_REGKEY); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE; + errcode = getaddrinfo(host, port, &hints, &res); + if (errcode) { + freeaddrinfo(res); + if (errcode == EAI_SYSTEM) { + moonbr_io_errmsg(); + lua_pushnil(L); + lua_pushfstring(L, "%s: %s", gai_strerror(errcode), errmsg); + } else { + lua_pushnil(L); + lua_pushstring(L, gai_strerror(errcode)); + } + return 2; + } + for (addrinfo=res; addrinfo; addrinfo=addrinfo->ai_next) { + if (addrinfo->ai_family == PF_INET6) goto moonbr_io_tcpconnect_found; + } + for (addrinfo=res; addrinfo; addrinfo=addrinfo->ai_next) { + if (addrinfo->ai_family == PF_INET) goto moonbr_io_tcpconnect_found; + } + addrinfo = res; + moonbr_io_tcpconnect_found: + sock = socket( + addrinfo->ai_family, + addrinfo->ai_socktype | SOCK_CLOEXEC, + addrinfo->ai_protocol + ); + if (sock < 0) { + moonbr_io_errmsg(); + freeaddrinfo(res); + lua_pushnil(L); + lua_pushstring(L, errmsg); + return 2; + } + if (bind(sock, addrinfo->ai_addr, addrinfo->ai_addrlen)) { + moonbr_io_errmsg(); + freeaddrinfo(res); + close(sock); + lua_pushnil(L); + lua_pushstring(L, errmsg); + return 2; + } + freeaddrinfo(res); + if (listen(sock, MOONBR_IO_LISTEN_BACKLOG)) { + moonbr_io_errmsg(); + close(sock); + lua_pushnil(L); + lua_pushstring(L, errmsg); + return 2; + } + listener->fd = sock; + listener->nonblocking = -1; + return 1; +} + +static int moonbr_io_accept_impl(lua_State *L, int nonblocking) { + moonbr_io_listener_t *listener; + int fd; + listener = luaL_checkudata(L, 1, MOONBR_IO_LISTENER_MT_REGKEY); + if (listener->fd < 0) luaL_error(L, "Attempt to use a closed listener"); + if (listener->nonblocking != nonblocking) { + int flags; + flags = fcntl(listener->fd, F_GETFL, 0); + if (flags == -1) { + moonbr_io_errmsg(); + close(listener->fd); + listener->fd = -1; + luaL_error(L, "Unexpected error in fcntl call: %s", errmsg); + } + if (nonblocking) flags |= O_NONBLOCK; + else flags &= ~O_NONBLOCK; + if (fcntl(listener->fd, F_SETFL, flags) == -1) { + moonbr_io_errmsg(); + close(listener->fd); + listener->fd = -1; + luaL_error(L, "Unexpected error in fcntl call: %s", errmsg); + } + listener->nonblocking = nonblocking; + } + while (1) { + fd = accept4(listener->fd, NULL, NULL, SOCK_CLOEXEC); + if (fd < 0) { + if (nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) { + lua_pushboolean(L, 0); + lua_pushliteral(L, "No incoming connection pending"); + return 2; + } else if (errno != EINTR) { + moonbr_io_errmsg(); + lua_pushnil(L); + lua_pushstring(L, errmsg); + return 2; + } + } else { + moonbr_io_pushhandle(L, fd); + return 1; + } + } +} + +static int moonbr_io_accept(lua_State *L) { + return moonbr_io_accept_impl(L, 0); +} + +static int moonbr_io_accept_nb(lua_State *L) { + return moonbr_io_accept_impl(L, 1); +} + +static int moonbr_io_unlisten(lua_State *L) { + moonbr_io_listener_t *listener; + listener = luaL_checkudata(L, 1, MOONBR_IO_LISTENER_MT_REGKEY); + if (listener->fd < 0) luaL_error(L, "Attempt to close a closed listener"); + if (close(listener->fd)) { + moonbr_io_errmsg(); + listener->fd = -1; + lua_pushnil(L); + lua_pushstring(L, errmsg); + return 2; + } + listener->fd = -1; + lua_pushboolean(L, 1); + return 1; +} + +static int moonbr_io_listenergc(lua_State *L) { + moonbr_io_listener_t *listener; + listener = luaL_checkudata(L, 1, MOONBR_IO_LISTENER_MT_REGKEY); + if (listener->fd) close(listener->fd); + listener->fd = -1; + return 0; +} + static int moonbr_io_poll(lua_State *L) { moonbr_io_handle_t *handle; + moonbr_io_listener_t *listener; int fd, isnum; int nfds = 0; fd_set readfds, writefds, exceptfds; @@ -704,10 +862,16 @@ handle = luaL_testudata(L, -2, MOONBR_IO_HANDLE_MT_REGKEY); if (handle) { fd = handle->fd; - if (fd < 0) luaL_error(L, "Handle in illegal state"); + if (fd < 0) luaL_error(L, "Handle in illegal state"); /* TODO: EOF simulation with finishing local sockets */ } else { - fd = lua_tointegerx(L, -2, &isnum); - if (!isnum) luaL_error(L, "File descriptor is not an integer"); + listener = luaL_testudata(L, -2, MOONBR_IO_LISTENER_MT_REGKEY); + if (listener) { + fd = listener->fd; + if (fd < 0) luaL_error(L, "Attempt to poll a closed listener"); + } else { + fd = lua_tointegerx(L, -2, &isnum); + if (!isnum) luaL_error(L, "Expected integer (file descriptor), I/O handle, or listener in table key"); + } } FD_SET(fd, &readfds); if (fd+1 > nfds) nfds = fd+1; @@ -723,8 +887,14 @@ fd = handle->fd; if (fd < 0) luaL_error(L, "Handle in illegal state"); } else { - fd = lua_tointegerx(L, -2, &isnum); - if (!isnum) luaL_error(L, "File descriptor is not an integer"); + listener = luaL_testudata(L, -2, MOONBR_IO_LISTENER_MT_REGKEY); + if (listener) { + fd = listener->fd; + if (fd < 0) luaL_error(L, "Attempt to poll a closed listener"); + } else { + fd = lua_tointegerx(L, -2, &isnum); + if (!isnum) luaL_error(L, "Expected integer (file descriptor), I/O handle, or listener in table key"); + } } FD_SET(fd, &writefds); if (fd+1 > nfds) nfds = fd+1; @@ -781,13 +951,26 @@ static const struct luaL_Reg moonbr_io_handle_metamethods[] = { {"__index", moonbr_io_handleindex}, {"__newindex", moonbr_io_handlenewindex}, - {"__gc", moonbr_io_gc}, + {"__gc", moonbr_io_handlegc}, + {NULL, NULL} +}; + +static const struct luaL_Reg moonbr_io_listener_methods[] = { + {"accept", moonbr_io_accept}, + {"accept_nb", moonbr_io_accept_nb}, + {"close", moonbr_io_unlisten}, + {NULL, NULL} +}; + +static const struct luaL_Reg moonbr_io_listener_metamethods[] = { + {"__gc", moonbr_io_listenergc}, {NULL, NULL} }; static const struct luaL_Reg moonbr_io_module_funcs[] = { {"tcpconnect", moonbr_io_tcpconnect}, {"tcpconnect_nb", moonbr_io_tcpconnect_nb}, + {"tcplisten", moonbr_io_tcplisten}, {"poll", moonbr_io_poll}, {NULL, NULL} }; @@ -800,7 +983,7 @@ lua_newtable(L); // handle methods luaL_setfuncs(L, moonbr_io_handle_methods, 0); lua_pushvalue(L, -1); - lua_setfield(L, -4, "handle"); + lua_setfield(L, -4, "prototype_handle"); lua_setfield(L, -2, "__index"); lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY); @@ -808,6 +991,15 @@ luaL_setfuncs(L, moonbr_io_handle_metamethods, 0); lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_HANDLE_MT_REGKEY); + lua_newtable(L); // listener metatable + luaL_setfuncs(L, moonbr_io_listener_metamethods, 0); + lua_newtable(L); // listener methods + luaL_setfuncs(L, moonbr_io_listener_methods, 0); + lua_pushvalue(L, -1); + lua_setfield(L, -4, "prototype_listener"); + lua_setfield(L, -2, "__index"); + lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_LISTENER_MT_REGKEY); + luaL_setfuncs(L, moonbr_io_module_funcs, 0); return 1;