jbe@79: jbe@79: #include jbe@79: #include jbe@79: #include jbe@79: #include jbe@79: #include jbe@79: #include jbe@79: #include jbe@81: #include jbe@79: jbe@79: #include jbe@79: #include jbe@79: #include jbe@79: jbe@80: #define MOONBR_IO_MAXSTRERRORLEN 80 jbe@85: #define MOONBR_IO_READBUFLEN 4096 jbe@80: #define MOONBR_IO_WRITEBUFLEN 4096 jbe@80: jbe@80: #define moonbr_io_errmsg() \ jbe@80: char errmsg[MOONBR_IO_MAXSTRERRORLEN]; \ jbe@80: strerror_r(errno, errmsg, MOONBR_IO_MAXSTRERRORLEN) jbe@80: jbe@79: #define MOONBR_IO_HANDLE_MT_REGKEY "moonbridge_io_handle" jbe@79: #define MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY "moonbridge_io_handle_public" jbe@79: jbe@79: typedef struct { jbe@79: int fd; jbe@84: int issocket; jbe@88: int canshutdown; jbe@88: int shutwr; jbe@81: int nonblocking; jbe@85: int readerr; jbe@85: int readbufcnt; jbe@81: int writeerr; jbe@83: size_t writeleft; jbe@83: #if LUA_VERSION_NUM >= 503 jbe@83: lua_Integer writeqin; jbe@83: lua_Integer writeqout; jbe@83: #else jbe@83: int writeqin; jbe@83: int writeqout; jbe@83: #endif jbe@83: size_t writeqoff; jbe@81: int writebufcnt; jbe@85: char readbuf[MOONBR_IO_READBUFLEN]; jbe@80: char writebuf[MOONBR_IO_WRITEBUFLEN]; jbe@79: } moonbr_io_handle_t; jbe@79: jbe@81: static void moonbr_io_handle_set_nonblocking(lua_State *L, moonbr_io_handle_t *handle, int nonblocking) { jbe@81: if (handle->nonblocking != nonblocking) { jbe@81: int flags; jbe@81: flags = fcntl(handle->fd, F_GETFL, 0); jbe@81: if (flags == -1) { jbe@81: moonbr_io_errmsg(); jbe@81: luaL_error(L, "Unexpected error in fcntl call: %s", errmsg); jbe@81: } jbe@81: if (nonblocking) flags |= O_NONBLOCK; jbe@81: else flags &= ~O_NONBLOCK; jbe@81: if (fcntl(handle->fd, F_SETFL, flags) == -1) { jbe@81: moonbr_io_errmsg(); jbe@81: luaL_error(L, "Unexpected error in fcntl call: %s", errmsg); jbe@81: } jbe@81: } jbe@81: } jbe@81: jbe@87: static void moonbr_io_handle_set_linger(lua_State *L, moonbr_io_handle_t *handle, int timeout) { jbe@87: struct linger lingerval = { 0, }; jbe@87: if (!handle->issocket) return; jbe@87: if (timeout >= 0) { jbe@87: lingerval.l_onoff = 1; jbe@87: lingerval.l_linger = timeout; jbe@87: } jbe@87: if (setsockopt(handle->fd, SOL_SOCKET, SO_LINGER, &lingerval, sizeof(lingerval))) { jbe@87: moonbr_io_errmsg(); jbe@87: luaL_error(L, "Unexpected error in setsockopt call: %s", errmsg); jbe@87: } jbe@87: } jbe@87: jbe@86: static int moonbr_io_read_impl(lua_State *L, int nonblocking, int drain) { jbe@85: moonbr_io_handle_t *handle; jbe@85: lua_Integer maxread; jbe@85: const char *terminatorstr; jbe@85: size_t terminatorlen; jbe@85: char terminator; jbe@85: luaL_Buffer luabuf; jbe@85: size_t luabufcnt = 0; jbe@86: int endcnt; jbe@85: char *terminatorpos; jbe@85: ssize_t result; jbe@85: handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); jbe@85: maxread = luaL_optinteger(L, 2, 0); jbe@85: terminatorstr = luaL_optlstring(L, 3, "", &terminatorlen); jbe@85: if (terminatorlen) { jbe@85: luaL_argcheck(L, terminatorlen == 1, 3, "single byte expected"); jbe@85: terminator = terminatorstr[0]; jbe@85: } jbe@86: lua_settop(L, 1); /* return handle on drain, terminator string may be garbage collected */ jbe@85: if (handle->fd < 0) luaL_error(L, "Attempt to read from a closed I/O handle"); jbe@85: if (handle->readerr) { jbe@85: lua_pushnil(L); jbe@85: lua_pushliteral(L, "Previous read error"); jbe@85: return 2; jbe@85: } jbe@85: moonbr_io_handle_set_nonblocking(L, handle, nonblocking); jbe@86: if (!drain) luaL_buffinit(L, &luabuf); jbe@85: while (1) { jbe@86: endcnt = -1; jbe@85: if (maxread > 0 && handle->readbufcnt >= maxread - luabufcnt) { jbe@86: endcnt = maxread - luabufcnt; jbe@85: } else if (terminatorlen) { jbe@85: terminatorpos = memchr(handle->readbuf, terminator, handle->readbufcnt); jbe@86: if (terminatorpos) endcnt = 1 + (terminatorpos - handle->readbuf); jbe@85: } jbe@86: if (endcnt >= 0) { jbe@86: if (!drain) { jbe@86: luaL_addlstring(&luabuf, handle->readbuf, endcnt); jbe@86: luaL_pushresult(&luabuf); jbe@90: } else { jbe@90: luabufcnt += handle->readbufcnt; jbe@90: lua_pushinteger(L, luabufcnt); jbe@86: } jbe@86: handle->readbufcnt -= endcnt; jbe@86: memmove(handle->readbuf, handle->readbuf + endcnt, handle->readbufcnt); jbe@85: return 1; jbe@85: } jbe@86: if (!drain) luaL_addlstring(&luabuf, handle->readbuf, handle->readbufcnt); jbe@85: luabufcnt += handle->readbufcnt; jbe@85: handle->readbufcnt = 0; jbe@85: do { jbe@85: result = read(handle->fd, handle->readbuf, MOONBR_IO_READBUFLEN); jbe@85: } while (result < 0 && (errno == EINTR)); jbe@85: if (result == 0 || (nonblocking && result < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))) break; jbe@85: if (result < 0) { jbe@85: moonbr_io_errmsg(); jbe@85: handle->readerr = 1; jbe@85: lua_pushnil(L); jbe@85: lua_pushstring(L, errmsg); jbe@85: return 2; jbe@85: } jbe@85: handle->readbufcnt += result; jbe@85: } jbe@86: if (!drain) { jbe@86: luaL_addlstring(&luabuf, handle->readbuf, handle->readbufcnt); jbe@86: luaL_pushresult(&luabuf); jbe@86: } jbe@90: luabufcnt += handle->readbufcnt; jbe@85: handle->readbufcnt = 0; jbe@90: if (!drain) { jbe@90: if (!luabufcnt && result == 0) { jbe@90: lua_pushboolean(L, 0); jbe@90: lua_pushliteral(L, "End of file"); jbe@90: return 2; jbe@90: } jbe@90: } else { jbe@90: if (!luabufcnt && result == 0) lua_pushboolean(L, 1); jbe@90: else lua_pushboolean(L, luabufcnt); jbe@90: } jbe@85: return 1; jbe@85: } jbe@85: jbe@85: static int moonbr_io_read(lua_State *L) { jbe@86: return moonbr_io_read_impl(L, 0, 0); jbe@85: } jbe@85: jbe@85: static int moonbr_io_read_nb(lua_State *L) { jbe@86: return moonbr_io_read_impl(L, 1, 0); jbe@86: } jbe@86: jbe@86: static int moonbr_io_drain(lua_State *L) { jbe@86: return moonbr_io_read_impl(L, 0, 1); jbe@86: } jbe@86: jbe@86: static int moonbr_io_drain_nb(lua_State *L) { jbe@86: return moonbr_io_read_impl(L, 1, 1); jbe@85: } jbe@85: jbe@81: static int moonbr_io_write_impl(lua_State *L, int nonblocking, int flush) { jbe@80: moonbr_io_handle_t *handle; jbe@80: int i, top; jbe@80: const char *str; jbe@81: size_t strlen, strpos; jbe@80: size_t written; jbe@80: ssize_t result; jbe@80: handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); jbe@83: if (handle->fd < 0) luaL_error(L, "Attempt to write to a closed I/O handle"); jbe@88: if (handle->shutwr) luaL_error(L, "Attempt to write to a finished I/O handle"); jbe@81: if (handle->writeerr) { jbe@80: lua_pushnil(L); jbe@80: lua_pushliteral(L, "Previous write error"); jbe@80: return 2; jbe@80: } jbe@81: moonbr_io_handle_set_nonblocking(L, handle, nonblocking); jbe@84: top = lua_gettop(L); jbe@81: lua_getuservalue(L, 1); jbe@81: lua_getfield(L, -1, "writebuf"); jbe@84: for (i=2; i<=top; i++) { jbe@84: luaL_checklstring(L, i, &strlen); jbe@84: lua_pushvalue(L, i); jbe@84: lua_rawseti(L, -2, handle->writeqin++); jbe@84: handle->writeleft += strlen; jbe@81: } jbe@83: while (handle->writeqout != handle->writeqin) { jbe@83: lua_rawgeti(L, -1, handle->writeqout); jbe@81: str = lua_tolstring(L, -1, &strlen); jbe@81: strpos = handle->writeqoff; jbe@81: while (strpos < strlen) { jbe@81: if (strlen - strpos < MOONBR_IO_WRITEBUFLEN - handle->writebufcnt) { jbe@81: memcpy(handle->writebuf + handle->writebufcnt, str + strpos, strlen - strpos); jbe@81: handle->writebufcnt += strlen - strpos; jbe@80: break; jbe@80: } else { jbe@80: written = 0; jbe@81: memcpy(handle->writebuf + handle->writebufcnt, str + strpos, MOONBR_IO_WRITEBUFLEN - handle->writebufcnt); jbe@81: strpos += MOONBR_IO_WRITEBUFLEN - handle->writebufcnt; jbe@80: while (written < MOONBR_IO_WRITEBUFLEN) { jbe@80: result = write(handle->fd, handle->writebuf + written, MOONBR_IO_WRITEBUFLEN - written); jbe@80: if (result < 0) { jbe@81: if (nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) { jbe@81: if (written) { jbe@81: handle->writebufcnt -= written; jbe@81: memmove(handle->writebuf, handle->writebuf + written, handle->writebufcnt); jbe@81: } jbe@81: handle->writeqoff = strpos; jbe@81: goto moonbr_io_write_impl_block; jbe@81: } else if (errno != EINTR) { jbe@80: moonbr_io_errmsg(); jbe@81: handle->writeerr = 1; jbe@80: lua_pushnil(L); jbe@80: lua_pushstring(L, errmsg); jbe@80: return 2; jbe@80: } jbe@80: } else { jbe@80: written += result; jbe@81: handle->writeleft -= result; jbe@80: } jbe@80: } jbe@81: handle->writebufcnt = 0; jbe@80: } jbe@80: } jbe@81: handle->writeqoff = 0; jbe@81: lua_pop(L, 1); jbe@81: lua_pushnil(L); jbe@83: lua_rawseti(L, -2, handle->writeqout++); jbe@80: } jbe@81: if (flush) { jbe@81: written = 0; jbe@81: while (written < handle->writebufcnt) { jbe@81: result = write(handle->fd, handle->writebuf + written, handle->writebufcnt - written); jbe@81: if (result < 0) { jbe@81: if (nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) { jbe@81: if (written) { jbe@81: handle->writebufcnt -= written; jbe@81: memmove(handle->writebuf, handle->writebuf + written, handle->writebufcnt); jbe@81: } jbe@81: goto moonbr_io_write_impl_block; jbe@81: } else if (errno != EINTR) { jbe@81: moonbr_io_errmsg(); jbe@81: handle->writeerr = -1; jbe@81: lua_pushnil(L); jbe@81: lua_pushstring(L, errmsg); jbe@81: return 2; jbe@81: } jbe@81: } else { jbe@81: written += result; jbe@81: handle->writeleft -= result; jbe@81: } jbe@81: } jbe@81: handle->writebufcnt = 0; jbe@82: if (nonblocking) lua_pushinteger(L, 0); jbe@81: } else { jbe@82: if (nonblocking) lua_pushinteger(L, handle->writeleft - handle->writebufcnt); jbe@81: } jbe@82: if (!nonblocking) lua_pushvalue(L, 1); jbe@80: return 1; jbe@81: moonbr_io_write_impl_block: jbe@81: if (flush) lua_pushinteger(L, handle->writeleft); jbe@81: else lua_pushinteger(L, handle->writeleft - handle->writebufcnt); jbe@81: return 1; jbe@81: } jbe@81: jbe@81: static int moonbr_io_write(lua_State *L) { jbe@81: return moonbr_io_write_impl(L, 0, 0); jbe@81: } jbe@81: jbe@81: static int moonbr_io_write_nb(lua_State *L) { jbe@81: return moonbr_io_write_impl(L, 1, 0); jbe@80: } jbe@80: jbe@80: static int moonbr_io_flush(lua_State *L) { jbe@81: return moonbr_io_write_impl(L, 0, 1); jbe@81: } jbe@81: jbe@81: static int moonbr_io_flush_nb(lua_State *L) { jbe@81: return moonbr_io_write_impl(L, 1, 1); jbe@80: } jbe@80: jbe@88: static int moonbr_io_finish(lua_State *L) { jbe@88: moonbr_io_handle_t *handle; jbe@88: handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); jbe@88: if (handle->fd < 0) luaL_error(L, "Attempt to finish a closed I/O handle"); jbe@88: if (handle->shutwr) luaL_error(L, "Attempt to finish a finished I/O handle"); jbe@88: handle->shutwr = 1; jbe@88: if (handle->canshutdown) { jbe@88: if (handle->writeleft) { jbe@88: lua_pushcfunction(L, moonbr_io_flush); jbe@88: lua_pushvalue(L, 1); jbe@88: lua_call(L, 1, 2); jbe@88: if (!lua_toboolean(L, -2)) return 2; jbe@88: } jbe@88: if (shutdown(handle->fd, SHUT_WR)) { jbe@88: moonbr_io_errmsg(); jbe@88: lua_pushnil(L); jbe@88: lua_pushstring(L, errmsg); jbe@88: return 2; jbe@88: } jbe@88: } jbe@88: lua_pushboolean(L, 1); jbe@88: return 1; jbe@88: } jbe@88: jbe@87: static int moonbr_io_close(lua_State *L) { jbe@83: moonbr_io_handle_t *handle; jbe@87: int timeout; jbe@83: handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); jbe@87: timeout = luaL_optinteger(L, 2, -1); jbe@83: if (handle->fd < 0) luaL_error(L, "Attempt to close a closed I/O handle"); jbe@87: if (timeout != 0) { jbe@87: if (handle->writeleft) { jbe@87: lua_pushcfunction(L, moonbr_io_flush); jbe@87: lua_pushvalue(L, 1); jbe@87: lua_call(L, 1, 2); jbe@87: if (!lua_toboolean(L, -2)) { jbe@87: close(handle->fd); jbe@87: handle->fd = -1; jbe@87: return 2; jbe@87: } jbe@83: } jbe@87: moonbr_io_handle_set_linger(L, handle, timeout); jbe@83: } jbe@83: if (close(handle->fd)) { jbe@83: moonbr_io_errmsg(); jbe@83: handle->fd = -1; jbe@83: lua_pushnil(L); jbe@83: lua_pushstring(L, errmsg); jbe@83: return 2; jbe@83: } jbe@83: handle->fd = -1; jbe@83: lua_pushboolean(L, 1); jbe@83: return 1; jbe@84: jbe@83: } jbe@83: jbe@84: static int moonbr_io_reset(lua_State *L) { jbe@87: luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); jbe@87: lua_settop(L, 1); jbe@87: lua_pushcfunction(L, moonbr_io_close); jbe@87: lua_insert(L, 1); jbe@87: lua_pushinteger(L, 0); jbe@87: lua_call(L, 2, LUA_MULTRET); jbe@87: return lua_gettop(L); jbe@84: } jbe@84: jbe@88: static int moonbr_io_gc(lua_State *L) { jbe@88: moonbr_io_handle_t *handle; jbe@88: handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); jbe@88: if (handle->fd >= 0) { jbe@88: lua_pushcfunction(L, moonbr_io_close); jbe@88: lua_pushvalue(L, 1); jbe@88: lua_pushinteger(L, 0); jbe@88: lua_call(L, 2, 0); jbe@88: } jbe@88: return 0; jbe@88: } jbe@88: jbe@88: void moonbr_io_closehandle(lua_State *L, int idx, int timeout) { jbe@88: moonbr_io_handle_t *handle; jbe@88: handle = luaL_checkudata(L, idx, MOONBR_IO_HANDLE_MT_REGKEY); jbe@88: if (handle->fd >= 0) { jbe@88: lua_pushcfunction(L, moonbr_io_close); jbe@88: lua_pushvalue(L, idx); jbe@88: lua_pushinteger(L, timeout); jbe@88: lua_call(L, 2, 0); jbe@88: } jbe@88: } jbe@88: jbe@88: void moonbr_io_pushhandle(lua_State *L, int fd, int issocket, int canshutdown) { jbe@79: moonbr_io_handle_t *handle; jbe@79: handle = lua_newuserdata(L, sizeof(moonbr_io_handle_t)); jbe@79: handle->fd = fd; jbe@84: handle->issocket = issocket; jbe@88: handle->canshutdown = canshutdown; jbe@88: handle->shutwr = 0; jbe@81: handle->nonblocking = -1; jbe@85: handle->readerr = 0; jbe@85: handle->readbufcnt = 0; jbe@81: handle->writeerr = 0; jbe@81: handle->writeleft = 0; jbe@83: handle->writeqin = 0; jbe@83: handle->writeqout = 0; jbe@81: handle->writeqoff = 0; jbe@81: handle->writebufcnt = 0; jbe@87: moonbr_io_handle_set_linger(L, handle, 0); jbe@79: luaL_getmetatable(L, MOONBR_IO_HANDLE_MT_REGKEY); jbe@79: lua_setmetatable(L, -2); jbe@79: lua_newtable(L); // uservalue jbe@81: lua_newtable(L); jbe@81: lua_setfield(L, -2, "writebuf"); jbe@79: lua_newtable(L); // public jbe@79: luaL_getmetatable(L, MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY); jbe@79: lua_setmetatable(L, -2); jbe@79: lua_setfield(L, -2, "public"); jbe@79: lua_setuservalue(L, -2); jbe@79: } jbe@79: jbe@79: static int moonbr_io_handleindex(lua_State *L) { jbe@80: luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); jbe@79: lua_getuservalue(L, 1); jbe@79: lua_getfield(L, -1, "public"); jbe@79: lua_pushvalue(L, 2); jbe@79: lua_gettable(L, -2); jbe@79: return 1; jbe@79: } jbe@79: jbe@79: static int moonbr_io_handlenewindex(lua_State *L) { jbe@80: luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); jbe@79: lua_getuservalue(L, 1); jbe@79: lua_getfield(L, -1, "public"); jbe@79: lua_pushvalue(L, 2); jbe@79: lua_pushvalue(L, 3); jbe@79: lua_settable(L, -3); jbe@79: return 0; jbe@79: } jbe@79: jbe@79: static const struct luaL_Reg moonbr_io_handle_methods[] = { jbe@85: {"read", moonbr_io_read}, jbe@85: {"read_nb", moonbr_io_read_nb}, jbe@86: {"drain", moonbr_io_drain}, jbe@86: {"drain_nb", moonbr_io_drain_nb}, jbe@80: {"write", moonbr_io_write}, jbe@81: {"write_nb", moonbr_io_write_nb}, jbe@80: {"flush", moonbr_io_flush}, jbe@81: {"flush_nb", moonbr_io_flush_nb}, jbe@88: {"finish", moonbr_io_finish}, jbe@87: {"close", moonbr_io_close}, jbe@85: {"reset", moonbr_io_reset}, jbe@79: {NULL, NULL} jbe@79: }; jbe@79: jbe@79: static const struct luaL_Reg moonbr_io_handle_metamethods[] = { jbe@79: {"__index", moonbr_io_handleindex}, jbe@79: {"__newindex", moonbr_io_handlenewindex}, jbe@88: {"__gc", moonbr_io_gc}, jbe@79: {NULL, NULL} jbe@79: }; jbe@79: jbe@79: static const struct luaL_Reg moonbr_io_module_funcs[] = { jbe@79: {NULL, NULL} jbe@79: }; jbe@79: jbe@79: int luaopen_moonbridge_io(lua_State *L) { jbe@79: jbe@80: lua_newtable(L); // module jbe@80: jbe@79: lua_newtable(L); // public metatable jbe@79: lua_newtable(L); // handle methods jbe@79: luaL_setfuncs(L, moonbr_io_handle_methods, 0); jbe@80: lua_pushvalue(L, -1); jbe@80: lua_setfield(L, -4, "handle"); jbe@79: lua_setfield(L, -2, "__index"); jbe@79: lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY); jbe@79: jbe@79: lua_newtable(L); // handle metatable jbe@79: luaL_setfuncs(L, moonbr_io_handle_metamethods, 0); jbe@79: lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_HANDLE_MT_REGKEY); jbe@79: jbe@79: luaL_setfuncs(L, moonbr_io_module_funcs, 0); jbe@79: return 1; jbe@79: jbe@79: } jbe@79: