jbe@209: jbe@209: #ifndef __has_include jbe@209: #define __has_include(x) 0 jbe@209: #endif jbe@79: jbe@79: #include jbe@135: #include jbe@135: #include jbe@135: #include jbe@79: #include jbe@135: #include jbe@135: #include jbe@135: #include jbe@79: #include jbe@111: #include jbe@95: #include jbe@95: #include jbe@135: #include jbe@136: #include jbe@135: #include jbe@107: #include jbe@205: #include jbe@205: #include jbe@209: #if defined(__linux__) || __has_include() jbe@209: #include jbe@209: #endif jbe@211: jbe@243: #ifdef MOONBR_IO_USE_TLS jbe@243: #include jbe@243: #endif jbe@243: jbe@79: #include jbe@79: #include jbe@79: #include jbe@79: jbe@149: #include jbe@149: jbe@80: #define MOONBR_IO_MAXSTRERRORLEN 80 jbe@85: #define MOONBR_IO_READBUFLEN 4096 jbe@80: #define MOONBR_IO_WRITEBUFLEN 4096 jbe@80: jbe@108: #define MOONBR_IO_LISTEN_BACKLOG 1024 jbe@108: jbe@213: #define MOONBR_IO_STRERROR_R_MSG "Error detail unavailable due to noncompliant strerror_r() implementation" jbe@80: #define moonbr_io_errmsg() \ jbe@213: char errmsg[MOONBR_IO_MAXSTRERRORLEN] = MOONBR_IO_STRERROR_R_MSG; \ 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@108: #define MOONBR_IO_LISTENER_MT_REGKEY "moonbridge_io_listener" jbe@205: #define MOONBR_IO_CHILD_MT_REGKEY "moonbridge_io_child" jbe@205: #define MOONBR_IO_CHILD_PT_REGKEY "moonbridge_io_child_pt" jbe@79: jbe@243: #ifdef MOONBR_IO_USE_TLS jbe@243: #define MOONBR_IO_TLSCONF_MT_REGKEY "moonbridge_io_tlsconf" jbe@243: #endif jbe@243: jbe@79: typedef struct { jbe@79: int fd; jbe@107: int issock; jbe@107: sa_family_t addrfam; jbe@94: int finished; jbe@94: int closed; jbe@81: int nonblocking; jbe@95: int nopush; jbe@85: int readerr; jbe@105: int readbufin; jbe@105: int readbufout; jbe@81: int writeerr; jbe@83: size_t writeleft; jbe@132: size_t flushedleft; 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@103: int writebufin; jbe@103: int writebufout; jbe@85: char readbuf[MOONBR_IO_READBUFLEN]; jbe@80: char writebuf[MOONBR_IO_WRITEBUFLEN]; jbe@79: } moonbr_io_handle_t; jbe@79: jbe@108: typedef struct { jbe@108: int fd; jbe@118: sa_family_t addrfam; jbe@108: int nonblocking; jbe@108: } moonbr_io_listener_t; jbe@108: jbe@205: typedef struct { jbe@205: pid_t pid; jbe@205: } moonbr_io_child_t; jbe@205: jbe@148: static int moonbr_io_yield(lua_State *L) { jbe@149: return lua_yield(L, lua_gettop(L)); jbe@140: } jbe@140: jbe@140: #if LUA_VERSION_NUM >= 503 jbe@140: static int moonbr_io_cont_returnall(lua_State *L, int status, lua_KContext ctx) { jbe@140: #else jbe@140: static int moonbr_io_cont_returnall(lua_State *L) { jbe@140: #endif jbe@140: return lua_gettop(L); jbe@140: } jbe@140: jbe@145: #define moonbr_io_yield_wrapper(yieldfunc, callfunc) \ jbe@145: static int yieldfunc(lua_State *L) { \ jbe@145: int args; \ jbe@145: lua_pushcfunction(L, callfunc); \ jbe@145: lua_insert(L, 1); \ jbe@145: args = lua_gettop(L); \ jbe@148: lua_pushcfunction(L, moonbr_io_yield); \ jbe@145: lua_insert(L, 3); \ jbe@145: lua_callk(L, args, LUA_MULTRET, 0, moonbr_io_cont_returnall); \ jbe@145: return lua_gettop(L); \ jbe@145: } jbe@145: jbe@81: static void moonbr_io_handle_set_nonblocking(lua_State *L, moonbr_io_handle_t *handle, int nonblocking) { jbe@95: int flags; jbe@95: if (handle->nonblocking == nonblocking) return; jbe@95: flags = fcntl(handle->fd, F_GETFL, 0); jbe@95: if (flags == -1) { jbe@95: moonbr_io_errmsg(); jbe@96: close(handle->fd); jbe@96: handle->fd = -1; jbe@96: handle->closed = 1; jbe@95: luaL_error(L, "Unexpected error in fcntl call: %s", errmsg); jbe@81: } jbe@95: if (nonblocking) flags |= O_NONBLOCK; jbe@95: else flags &= ~O_NONBLOCK; jbe@95: if (fcntl(handle->fd, F_SETFL, flags) == -1) { jbe@95: moonbr_io_errmsg(); jbe@96: close(handle->fd); jbe@96: handle->fd = -1; jbe@96: handle->closed = 1; jbe@95: luaL_error(L, "Unexpected error in fcntl call: %s", errmsg); jbe@95: } jbe@95: handle->nonblocking = nonblocking; 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@107: if (!handle->issock) 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@96: close(handle->fd); jbe@96: handle->fd = -1; jbe@96: handle->closed = 1; jbe@95: luaL_error(L, "Unexpected error while setting SO_LINGER with setsockopt: %s", errmsg); jbe@87: } jbe@87: } jbe@87: jbe@133: static inline void moonbr_io_handle_set_nopush(lua_State *L, moonbr_io_handle_t *handle, int nopush) { jbe@96: #if defined(TCP_NOPUSH) || defined(TCP_CORK) jbe@107: if ( jbe@107: !(handle->addrfam == AF_INET6 || handle->addrfam == AF_INET) || jbe@107: handle->nopush == nopush jbe@107: ) return; jbe@96: #if defined(TCP_NOPUSH) jbe@96: if (setsockopt(handle->fd, IPPROTO_TCP, TCP_NOPUSH, &nopush, sizeof(nopush))) { jbe@104: #elif defined(TCP_CORK) jbe@104: if (setsockopt(handle->fd, IPPROTO_TCP, TCP_CORK, &nopush, sizeof(nopush))) { jbe@104: #endif jbe@96: moonbr_io_errmsg(); jbe@96: close(handle->fd); jbe@96: handle->fd = -1; jbe@96: handle->closed = 1; jbe@104: #if defined(TCP_NOPUSH) jbe@96: luaL_error(L, "Unexpected error while setting TCP_NOPUSH with setsockopt: %s", errmsg); jbe@96: #elif defined(TCP_CORK) jbe@95: luaL_error(L, "Unexpected error while setting TCP_CORK with setsockopt: %s", errmsg); jbe@104: #endif jbe@95: } jbe@95: handle->nopush = nopush; jbe@96: #else jbe@96: #warning Neither TCP_NOPUSH nor TCP_CORK is available jbe@96: #endif jbe@95: } jbe@95: 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@210: size_t terminatorlen; jbe@210: char terminator = 0; /* initialize to avoid compiler warning */ jbe@85: luaL_Buffer luabuf; jbe@85: size_t luabufcnt = 0; jbe@105: int remaining; jbe@85: char *terminatorpos; jbe@103: ssize_t bytesread; jbe@85: handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); jbe@153: maxread = luaL_optinteger(L, 2, -1); 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@94: if (handle->closed) 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@143: if (handle->fd < 0) { jbe@143: /* fake EOF to simulate shutdown */ jbe@143: if (!drain) lua_pushliteral(L, ""); jbe@143: else lua_pushinteger(L, 0); jbe@143: lua_pushliteral(L, "eof"); jbe@143: return 2; jbe@143: } jbe@105: handle->readerr = 1; jbe@85: moonbr_io_handle_set_nonblocking(L, handle, nonblocking); jbe@86: if (!drain) luaL_buffinit(L, &luabuf); jbe@85: while (1) { jbe@105: remaining = -1; jbe@142: terminatorpos = NULL; jbe@105: if ( jbe@153: maxread >= 0 && jbe@143: handle->readbufin - handle->readbufout >= (size_t)maxread - luabufcnt jbe@105: ) { jbe@142: remaining = (size_t)maxread - luabufcnt; jbe@193: if (terminatorlen) { jbe@193: terminatorpos = memchr( jbe@193: handle->readbuf + handle->readbufout, jbe@193: terminator, jbe@193: remaining jbe@193: ); jbe@193: } jbe@85: } else if (terminatorlen) { jbe@105: terminatorpos = memchr( jbe@105: handle->readbuf + handle->readbufout, jbe@105: terminator, jbe@105: handle->readbufin - handle->readbufout jbe@105: ); jbe@85: } jbe@142: if (terminatorpos) remaining = 1 + ( jbe@142: terminatorpos - (handle->readbuf + handle->readbufout) jbe@142: ); jbe@105: if (remaining >= 0) { jbe@86: if (!drain) { jbe@105: luaL_addlstring( jbe@105: &luabuf, jbe@105: handle->readbuf + handle->readbufout, jbe@105: remaining jbe@105: ); jbe@86: luaL_pushresult(&luabuf); jbe@90: } else { jbe@105: lua_pushinteger(L, luabufcnt + remaining); jbe@86: } jbe@143: if (terminatorpos) lua_pushliteral(L, "term"); jbe@143: else lua_pushliteral(L, "maxlen"); jbe@105: handle->readbufout += remaining; jbe@105: if (handle->readbufout == handle->readbufin) { jbe@105: handle->readbufin = 0; jbe@143: handle->readbufout = 0; jbe@105: } jbe@104: handle->readerr = 0; jbe@143: return 2; jbe@85: } jbe@105: if (!drain) luaL_addlstring( jbe@105: &luabuf, jbe@105: handle->readbuf + handle->readbufout, jbe@105: handle->readbufin - handle->readbufout jbe@105: ); jbe@105: luabufcnt += handle->readbufin - handle->readbufout; jbe@143: handle->readbufout = 0; jbe@85: do { jbe@103: bytesread = read(handle->fd, handle->readbuf, MOONBR_IO_READBUFLEN); jbe@103: } while (bytesread < 0 && (errno == EINTR)); jbe@105: if ( jbe@105: bytesread == 0 || ( jbe@105: nonblocking && jbe@105: bytesread < 0 && (errno == EAGAIN || errno == EWOULDBLOCK) jbe@105: ) jbe@105: ) { jbe@105: handle->readbufin = 0; jbe@143: if (!drain) luaL_pushresult(&luabuf); jbe@143: else lua_pushinteger(L, luabufcnt); jbe@143: if (bytesread == 0) lua_pushliteral(L, "eof"); jbe@143: else lua_pushliteral(L, "block"); jbe@105: handle->readerr = 0; jbe@143: return 2; jbe@105: } jbe@103: if (bytesread < 0) { jbe@85: moonbr_io_errmsg(); jbe@85: lua_pushnil(L); jbe@85: lua_pushstring(L, errmsg); jbe@85: return 2; jbe@85: } jbe@105: handle->readbufin = bytesread; jbe@86: } 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@140: #if LUA_VERSION_NUM >= 503 jbe@140: static int moonbr_io_read_cont(lua_State *L, int status, lua_KContext ctx) { jbe@140: #else jbe@140: static int moonbr_io_read_cont(lua_State *L) { jbe@140: #endif jbe@143: lua_Integer remaining; jbe@143: size_t len; jbe@140: #if !(LUA_VERSION_NUM >= 503) jbe@140: int ctx = 0; jbe@140: lua_getctx(L, &ctx); jbe@140: #endif jbe@140: remaining = lua_tointeger(L, 3); jbe@140: while (1) { jbe@140: lua_pushcfunction(L, moonbr_io_read_nb); jbe@140: lua_pushvalue(L, 1); jbe@140: lua_pushvalue(L, 3); jbe@140: lua_pushvalue(L, 4); jbe@140: lua_call(L, 3, 2); jbe@143: if (lua_isnil(L, -2)) return 2; jbe@143: lua_insert(L, -2); jbe@143: len = lua_rawlen(L, -1); jbe@143: if (ctx == 0) { jbe@143: lua_replace(L, 5); jbe@143: ctx = 1; jbe@143: } else if (ctx == 1) { jbe@143: lua_pushvalue(L, 5); jbe@143: lua_newtable(L); jbe@143: lua_replace(L, 5); jbe@143: lua_rawseti(L, 5, 2); jbe@143: lua_rawseti(L, 5, 1); jbe@143: ctx = 2; jbe@140: } else { jbe@143: lua_rawseti(L, 5, lua_rawlen(L, 5) + 1); jbe@140: } jbe@144: if (strcmp(lua_tostring(L, -1), "block") != 0) break; jbe@144: lua_pop(L, 1); jbe@144: if (remaining >= 0 && len) { jbe@143: remaining -= len; jbe@143: lua_pushinteger(L, remaining); jbe@143: lua_replace(L, 3); jbe@143: } jbe@140: lua_pushvalue(L, 2); jbe@152: lua_callk(L, 0, 0, ctx, moonbr_io_read_cont); jbe@140: } jbe@140: if (ctx == 1) { jbe@140: lua_pushvalue(L, 5); jbe@140: } else { jbe@140: luaL_Buffer buf; jbe@140: lua_Integer i, chunkcount; jbe@140: chunkcount = lua_rawlen(L, 5); jbe@140: luaL_buffinit(L, &buf); jbe@140: for (i=1; i<=chunkcount && i>0; i++) { jbe@140: lua_rawgeti(L, 5, i); jbe@140: luaL_addvalue(&buf); jbe@140: } jbe@140: luaL_pushresult(&buf); jbe@140: } jbe@143: lua_pushvalue(L, -2); jbe@143: return 2; jbe@140: } jbe@140: jbe@140: static int moonbr_io_read_call(lua_State *L) { jbe@140: lua_settop(L, 4); jbe@140: lua_pushnil(L); jbe@140: #if LUA_VERSION_NUM >= 503 jbe@140: return moonbr_io_read_cont(L, 0, 0); jbe@140: #else jbe@140: return moonbr_io_read_cont(L); jbe@140: #endif jbe@140: } jbe@140: jbe@145: moonbr_io_yield_wrapper(moonbr_io_read_yield, moonbr_io_read_call); jbe@140: jbe@144: #if LUA_VERSION_NUM >= 503 jbe@144: static int moonbr_io_drain_cont(lua_State *L, int status, lua_KContext ctx) { jbe@144: #else jbe@144: static int moonbr_io_drain_cont(lua_State *L) { jbe@144: #endif jbe@144: lua_Integer remaining, len; jbe@144: size_t totallen = 0; jbe@144: #if !(LUA_VERSION_NUM >= 503) jbe@144: int ctx = 0; jbe@144: lua_getctx(L, &ctx); jbe@144: #endif jbe@144: remaining = lua_tointeger(L, 3); jbe@144: while (1) { jbe@144: lua_pushcfunction(L, moonbr_io_drain_nb); jbe@144: lua_pushvalue(L, 1); jbe@144: lua_pushvalue(L, 3); jbe@144: lua_pushvalue(L, 4); jbe@144: lua_call(L, 3, 2); jbe@144: if (lua_isnil(L, -2)) return 2; jbe@144: lua_insert(L, -2); jbe@144: len = lua_tointeger(L, -1); jbe@144: lua_pop(L, 1); jbe@144: totallen += len; jbe@144: if (strcmp(lua_tostring(L, -1), "block") != 0) break; jbe@144: lua_pop(L, 1); jbe@144: if (remaining >= 0 && len) { jbe@144: remaining -= len; jbe@144: lua_pushinteger(L, remaining); jbe@144: lua_replace(L, 3); jbe@144: } jbe@144: lua_pushvalue(L, 2); jbe@152: lua_callk(L, 0, 0, ctx, moonbr_io_drain_cont); jbe@144: } jbe@144: lua_pushinteger(L, totallen); jbe@144: lua_pushvalue(L, -2); jbe@144: return 2; jbe@144: } jbe@144: jbe@144: static int moonbr_io_drain_call(lua_State *L) { jbe@144: #if LUA_VERSION_NUM >= 503 jbe@144: return moonbr_io_drain_cont(L, 0, 0); jbe@144: #else jbe@144: return moonbr_io_drain_cont(L); jbe@144: #endif jbe@144: } jbe@144: jbe@145: moonbr_io_yield_wrapper(moonbr_io_drain_yield, moonbr_io_drain_call); jbe@144: 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@92: size_t strlen; jbe@103: ssize_t written; jbe@80: handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); jbe@94: if (handle->closed) luaL_error(L, "Attempt to write to a closed I/O handle"); jbe@94: if (handle->finished) 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@103: handle->writeerr = 1; jbe@81: moonbr_io_handle_set_nonblocking(L, handle, nonblocking); jbe@84: top = lua_gettop(L); jbe@81: lua_getuservalue(L, 1); jbe@103: lua_getfield(L, -1, "writequeue"); 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@132: if (flush) handle->flushedleft = handle->writeleft; jbe@83: while (handle->writeqout != handle->writeqin) { jbe@83: lua_rawgeti(L, -1, handle->writeqout); jbe@81: str = lua_tolstring(L, -1, &strlen); jbe@92: while (handle->writeqoff < strlen) { jbe@103: if ( jbe@132: strlen - handle->writeqoff < jbe@103: MOONBR_IO_WRITEBUFLEN - handle->writebufin jbe@103: ) { jbe@103: memcpy( jbe@103: handle->writebuf + handle->writebufin, jbe@103: str + handle->writeqoff, jbe@103: strlen - handle->writeqoff jbe@103: ); jbe@103: handle->writebufin += strlen - handle->writeqoff; jbe@80: break; jbe@80: } else { jbe@103: memcpy( jbe@103: handle->writebuf + handle->writebufin, jbe@103: str + handle->writeqoff, jbe@103: MOONBR_IO_WRITEBUFLEN - handle->writebufin jbe@103: ); jbe@103: handle->writeqoff += MOONBR_IO_WRITEBUFLEN - handle->writebufin; jbe@228: handle->writebufin = MOONBR_IO_WRITEBUFLEN; jbe@103: while (handle->writebufout < MOONBR_IO_WRITEBUFLEN) { jbe@133: moonbr_io_handle_set_nopush(L, handle, 1); jbe@103: written = write( jbe@103: handle->fd, jbe@103: handle->writebuf + handle->writebufout, jbe@103: MOONBR_IO_WRITEBUFLEN - handle->writebufout jbe@103: ); jbe@103: if (written < 0) { jbe@81: if (nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) { jbe@81: goto moonbr_io_write_impl_block; jbe@81: } else if (errno != EINTR) { jbe@80: moonbr_io_errmsg(); jbe@80: lua_pushnil(L); jbe@80: lua_pushstring(L, errmsg); jbe@80: return 2; jbe@80: } jbe@103: } else { jbe@103: handle->writebufout += written; jbe@103: handle->writeleft -= written; jbe@133: if (handle->flushedleft) { jbe@133: if (written >= handle->flushedleft) { jbe@133: handle->flushedleft = 0; jbe@133: moonbr_io_handle_set_nopush(L, handle, 0); jbe@133: } else { jbe@133: handle->flushedleft -= written; jbe@133: } jbe@133: } jbe@80: } jbe@80: } jbe@103: handle->writebufin = 0; jbe@103: handle->writebufout = 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@132: while (handle->flushedleft) { jbe@133: moonbr_io_handle_set_nopush(L, handle, 1); jbe@132: written = write( jbe@132: handle->fd, jbe@132: handle->writebuf + handle->writebufout, jbe@132: handle->writebufin - handle->writebufout jbe@132: ); jbe@132: if (written < 0) { jbe@132: if (nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) { jbe@132: goto moonbr_io_write_impl_block; jbe@132: } else if (errno != EINTR) { jbe@132: moonbr_io_errmsg(); jbe@132: lua_pushnil(L); jbe@132: lua_pushstring(L, errmsg); jbe@132: return 2; jbe@81: } jbe@132: } else { jbe@132: handle->writebufout += written; jbe@132: handle->writeleft -= written; jbe@133: if (handle->flushedleft) { jbe@133: if (written >= handle->flushedleft) { jbe@133: handle->flushedleft = 0; jbe@133: moonbr_io_handle_set_nopush(L, handle, 0); jbe@133: } else { jbe@133: handle->flushedleft -= written; jbe@133: } jbe@133: } jbe@81: } jbe@132: } jbe@132: if (handle->writebufout == handle->writebufin) { jbe@103: handle->writebufin = 0; jbe@103: handle->writebufout = 0; jbe@81: } jbe@103: if (nonblocking) lua_pushinteger(L, 0); jbe@103: else lua_pushvalue(L, 1); jbe@103: handle->writeerr = 0; jbe@80: return 1; jbe@81: moonbr_io_write_impl_block: jbe@91: lua_pushinteger(L, handle->writeleft); jbe@103: handle->writeerr = 0; 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@145: #if LUA_VERSION_NUM >= 503 jbe@145: static int moonbr_io_write_cont(lua_State *L, int status, lua_KContext ctx) { jbe@145: #else jbe@145: static int moonbr_io_write_cont(lua_State *L) { jbe@145: #endif jbe@145: while (1) { jbe@145: lua_pushcfunction(L, moonbr_io_write_nb); jbe@145: lua_pushvalue(L, 1); jbe@145: lua_call(L, 1, 2); jbe@145: if (lua_isnil(L, -2)) return 2; jbe@145: if (!lua_tointeger(L, -2)) { jbe@145: lua_pushvalue(L, 1); jbe@145: return 1; jbe@145: } jbe@145: lua_pop(L, 2); jbe@145: lua_pushvalue(L, 2); jbe@152: lua_callk(L, 0, 0, 0, moonbr_io_write_cont); jbe@145: } jbe@145: } jbe@145: jbe@145: static int moonbr_io_write_call(lua_State *L) { jbe@145: lua_pushcfunction(L, moonbr_io_write_nb); jbe@145: lua_insert(L, 3); jbe@145: lua_pushvalue(L, 1); jbe@145: lua_insert(L, 4); jbe@145: lua_call(L, lua_gettop(L) - 3, 2); jbe@145: if (lua_isnil(L, -2)) return 2; jbe@145: if (!lua_tointeger(L, -2)) { jbe@145: lua_pushvalue(L, 1); jbe@145: return 1; jbe@145: } jbe@145: #if LUA_VERSION_NUM >= 503 jbe@145: return moonbr_io_write_cont(L, 0, 0); jbe@145: #else jbe@145: return moonbr_io_write_cont(L); jbe@145: #endif jbe@145: } jbe@145: jbe@145: moonbr_io_yield_wrapper(moonbr_io_write_yield, moonbr_io_write_call); jbe@145: jbe@145: static int moonbr_io_flush_call(lua_State *L) { jbe@145: lua_pushcfunction(L, moonbr_io_flush_nb); jbe@145: lua_insert(L, 3); jbe@145: lua_pushvalue(L, 1); jbe@145: lua_insert(L, 4); jbe@145: lua_call(L, lua_gettop(L) - 3, 2); jbe@145: if (lua_isnil(L, -2)) return 2; jbe@145: if (!lua_tointeger(L, -2)) { jbe@145: lua_pushvalue(L, 1); jbe@145: return 1; jbe@145: } jbe@145: #if LUA_VERSION_NUM >= 503 jbe@145: return moonbr_io_write_cont(L, 0, 0); jbe@145: #else jbe@145: return moonbr_io_write_cont(L); jbe@145: #endif jbe@145: } jbe@145: jbe@145: moonbr_io_yield_wrapper(moonbr_io_flush_yield, moonbr_io_flush_call); jbe@145: 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@94: if (handle->closed) luaL_error(L, "Attempt to finish a closed I/O handle"); jbe@94: if (handle->finished) luaL_error(L, "Attempt to finish a finished I/O handle"); jbe@94: if (handle->writeleft) { jbe@94: lua_pushcfunction(L, moonbr_io_flush); jbe@94: lua_pushvalue(L, 1); jbe@116: if (lua_pcall(L, 1, 2, 0)) { jbe@116: handle->finished = 1; jbe@116: lua_error(L); jbe@116: } jbe@94: if (!lua_toboolean(L, -2)) { jbe@94: handle->finished = 1; jbe@94: return 2; jbe@88: } jbe@94: } jbe@94: handle->finished = 1; jbe@107: if (handle->addrfam == AF_INET6 || handle->addrfam == AF_INET) { 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@94: } else { jbe@94: if (close(handle->fd)) { jbe@94: moonbr_io_errmsg(); jbe@94: handle->fd = -1; jbe@94: lua_pushnil(L); jbe@94: lua_pushstring(L, errmsg); jbe@94: return 2; jbe@94: } jbe@94: handle->fd = -1; /* fake EOF on read */ jbe@88: } jbe@88: lua_pushboolean(L, 1); jbe@88: return 1; jbe@88: } jbe@88: jbe@94: static int moonbr_io_close_impl(lua_State *L, int reset) { jbe@83: moonbr_io_handle_t *handle; jbe@83: handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY); jbe@94: if (handle->closed) luaL_error(L, "Attempt to close a closed I/O handle"); jbe@94: if (!reset) { jbe@87: if (handle->writeleft) { jbe@87: lua_pushcfunction(L, moonbr_io_flush); jbe@87: lua_pushvalue(L, 1); jbe@116: if (lua_pcall(L, 1, 2, 0)) { jbe@129: handle->closed = 1; jbe@116: close(handle->fd); jbe@116: handle->fd = -1; jbe@116: lua_error(L); jbe@116: } jbe@129: handle->closed = 1; jbe@87: if (!lua_toboolean(L, -2)) { jbe@87: close(handle->fd); jbe@87: handle->fd = -1; jbe@87: return 2; jbe@87: } jbe@129: } else { jbe@129: handle->closed = 1; jbe@129: moonbr_io_handle_set_linger(L, handle, -1); jbe@83: } jbe@129: } else { jbe@129: handle->closed = 1; jbe@83: } jbe@94: if (handle->fd >= 0) { jbe@94: if (close(handle->fd)) { jbe@94: moonbr_io_errmsg(); jbe@94: handle->fd = -1; jbe@94: lua_pushnil(L); jbe@94: lua_pushstring(L, errmsg); jbe@94: return 2; jbe@94: } jbe@104: handle->fd = -1; jbe@83: } jbe@83: lua_pushboolean(L, 1); jbe@83: return 1; jbe@84: jbe@83: } jbe@83: jbe@94: static int moonbr_io_close(lua_State *L) { jbe@94: return moonbr_io_close_impl(L, 0); jbe@94: } jbe@94: jbe@84: static int moonbr_io_reset(lua_State *L) { jbe@94: return moonbr_io_close_impl(L, 1); jbe@84: } jbe@84: jbe@108: static int moonbr_io_handlegc(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@218: lua_pushcfunction(L, moonbr_io_reset); 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@100: void moonbr_io_closehandle(lua_State *L, int idx, int reset) { jbe@88: moonbr_io_handle_t *handle; jbe@88: handle = luaL_checkudata(L, idx, MOONBR_IO_HANDLE_MT_REGKEY); jbe@100: if (!handle->closed) { jbe@100: lua_pushcfunction(L, reset ? moonbr_io_reset : moonbr_io_close); jbe@100: lua_pushvalue(L, idx < 0 ? idx-1 : idx); jbe@100: lua_call(L, 1, 0); jbe@88: } jbe@88: } jbe@88: jbe@208: static int moonbr_io_pushhandle_impl(lua_State *L) { jbe@208: int *fd; jbe@79: moonbr_io_handle_t *handle; jbe@107: struct sockaddr addr; jbe@107: socklen_t addrlen; jbe@208: fd = lua_touserdata(L, 1); jbe@79: handle = lua_newuserdata(L, sizeof(moonbr_io_handle_t)); jbe@208: handle->fd = -1; /* avoid closing incomplete handle */ jbe@107: addrlen = sizeof(addr); jbe@208: if (getsockname(*fd, &addr, &addrlen)) { jbe@107: if (errno != ENOTSOCK) { jbe@107: moonbr_io_errmsg(); jbe@107: luaL_error(L, "Unexpected error when examining socket: %s", errmsg); jbe@107: } jbe@107: handle->issock = 0; jbe@107: } else { jbe@107: handle->issock = 1; jbe@107: handle->addrfam = addr.sa_family; jbe@107: } jbe@94: handle->finished = 0; jbe@94: handle->closed = 0; jbe@81: handle->nonblocking = -1; jbe@95: handle->nopush = -1; jbe@85: handle->readerr = 0; jbe@105: handle->readbufin = 0; jbe@105: handle->readbufout = 0; jbe@81: handle->writeerr = 0; jbe@81: handle->writeleft = 0; jbe@132: handle->flushedleft = 0; jbe@83: handle->writeqin = 0; jbe@83: handle->writeqout = 0; jbe@81: handle->writeqoff = 0; jbe@103: handle->writebufin = 0; jbe@103: handle->writebufout = 0; jbe@208: handle->fd = *fd; /* required for set_linger call */ jbe@87: moonbr_io_handle_set_linger(L, handle, 0); jbe@208: handle->fd = -1; /* avoid closing incomplete handle */ jbe@238: luaL_setmetatable(L, MOONBR_IO_HANDLE_MT_REGKEY); jbe@79: lua_newtable(L); // uservalue jbe@81: lua_newtable(L); jbe@103: lua_setfield(L, -2, "writequeue"); jbe@79: lua_newtable(L); // public jbe@107: if (handle->addrfam == AF_INET6) { jbe@107: struct sockaddr_in6 addr_in6; jbe@107: char addrstrbuf[INET6_ADDRSTRLEN]; jbe@107: const char *addrstr; jbe@107: addrlen = sizeof(addr_in6); jbe@208: if (getsockname(*fd, (struct sockaddr *)&addr_in6, &addrlen)) { jbe@107: moonbr_io_errmsg(); jbe@107: luaL_error(L, "Could not determine local IP address/port: %s", errmsg); jbe@107: } jbe@107: if (addrlen > sizeof(addr_in6)) { jbe@107: luaL_error(L, "Could not determine local IP address/port: buffer size exceeded"); jbe@107: } jbe@107: addrstr = inet_ntop(AF_INET6, addr_in6.sin6_addr.s6_addr, addrstrbuf, sizeof(addrstrbuf)); jbe@107: if (!addrstr) { jbe@107: moonbr_io_errmsg(); jbe@107: luaL_error(L, "Could not format local IP address: %s", errmsg); jbe@107: } else { jbe@107: lua_pushstring(L, addrstr); jbe@107: lua_setfield(L, -2, "local_ip6"); jbe@107: } jbe@107: lua_pushinteger(L, ntohs(addr_in6.sin6_port)); jbe@107: lua_setfield(L, -2, "local_tcpport"); jbe@208: if (getpeername(*fd, (struct sockaddr *)&addr_in6, &addrlen)) { jbe@107: moonbr_io_errmsg(); jbe@107: luaL_error(L, "Could not determine remote IP address/port: %s", errmsg); jbe@107: } jbe@107: if (addrlen > sizeof(addr_in6)) { jbe@107: luaL_error(L, "Could not determine remote IP address/port: buffer size exceeded"); jbe@107: } jbe@107: addrstr = inet_ntop(AF_INET6, addr_in6.sin6_addr.s6_addr, addrstrbuf, sizeof(addrstrbuf)); jbe@107: if (!addrstr) { jbe@107: moonbr_io_errmsg(); jbe@107: luaL_error(L, "Could not format remote IP address: %s", errmsg); jbe@107: } else { jbe@107: lua_pushstring(L, addrstr); jbe@107: lua_setfield(L, -2, "remote_ip6"); jbe@107: } jbe@107: lua_pushinteger(L, ntohs(addr_in6.sin6_port)); jbe@107: lua_setfield(L, -2, "remote_tcpport"); jbe@107: } else if (handle->addrfam == AF_INET) { jbe@107: struct sockaddr_in addr_in; jbe@107: char addrstrbuf[INET_ADDRSTRLEN]; jbe@107: const char *addrstr; jbe@107: addrlen = sizeof(addr_in); jbe@208: if (getsockname(*fd, (struct sockaddr *)&addr_in, &addrlen)) { jbe@107: moonbr_io_errmsg(); jbe@107: luaL_error(L, "Could not determine local IP address/port: %s", errmsg); jbe@107: } jbe@107: if (addrlen > sizeof(addr_in)) { jbe@107: luaL_error(L, "Could not determine local IP address/port: buffer size exceeded"); jbe@107: } jbe@107: addrstr = inet_ntop(AF_INET, &addr_in.sin_addr.s_addr, addrstrbuf, sizeof(addrstrbuf)); jbe@107: if (!addrstr) { jbe@107: moonbr_io_errmsg(); jbe@107: luaL_error(L, "Could not format local IP address: %s", errmsg); jbe@107: } else { jbe@107: lua_pushstring(L, addrstr); jbe@107: lua_setfield(L, -2, "local_ip4"); jbe@107: } jbe@107: lua_pushinteger(L, ntohs(addr_in.sin_port)); jbe@107: lua_setfield(L, -2, "local_tcpport"); jbe@208: if (getpeername(*fd, (struct sockaddr *)&addr_in, &addrlen)) { jbe@107: moonbr_io_errmsg(); jbe@107: luaL_error(L, "Could not determine remote IP address/port: %s", errmsg); jbe@107: } jbe@107: if (addrlen > sizeof(addr_in)) { jbe@107: luaL_error(L, "Could not determine remote IP address/port: buffer size exceeded"); jbe@107: } jbe@107: addrstr = inet_ntop(AF_INET, &addr_in.sin_addr.s_addr, addrstrbuf, sizeof(addrstrbuf)); jbe@107: if (!addrstr) { jbe@107: moonbr_io_errmsg(); jbe@107: luaL_error(L, "Could not format remote IP address: %s", errmsg); jbe@107: } else { jbe@107: lua_pushstring(L, addrstr); jbe@107: lua_setfield(L, -2, "remote_ip4"); jbe@107: } jbe@107: lua_pushinteger(L, ntohs(addr_in.sin_port)); jbe@107: lua_setfield(L, -2, "remote_tcpport"); jbe@107: } jbe@238: luaL_setmetatable(L, MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY); jbe@79: lua_setfield(L, -2, "public"); jbe@79: lua_setuservalue(L, -2); jbe@208: handle->fd = *fd; jbe@208: *fd = -1; /* closing is now handled by garbage collection */ jbe@208: return 1; jbe@208: } jbe@208: jbe@208: void moonbr_io_pushhandle(lua_State *L, int fd) { jbe@208: lua_pushcfunction(L, moonbr_io_pushhandle_impl); jbe@208: lua_pushlightuserdata(L, &fd); jbe@208: if (lua_pcall(L, 1, 1, 0)) { jbe@208: if (fd != -1) close(fd); jbe@208: lua_error(L); jbe@208: } 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@205: luaL_checkany(L, 2); 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@205: luaL_checkany(L, 2); jbe@205: luaL_checkany(L, 3); 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@111: static int moonbr_io_localconnect_impl(lua_State *L, int nonblocking) { jbe@111: const char *path; jbe@111: struct sockaddr_un sockaddr = { .sun_family = AF_LOCAL }; jbe@111: const int path_maxlen = sizeof(struct sockaddr_un) - ( jbe@111: (void *)sockaddr.sun_path - (void *)&sockaddr jbe@111: ) - 1; /* one byte for termination */ jbe@111: int sock; jbe@111: path = luaL_checkstring(L, 1); jbe@111: if (strlen(path) > path_maxlen) luaL_error(L, "Path too long; only %i characters allowed", path_maxlen); jbe@111: strcpy(sockaddr.sun_path, path); jbe@111: sock = socket( jbe@111: PF_LOCAL, jbe@111: SOCK_STREAM | SOCK_CLOEXEC | (nonblocking ? SOCK_NONBLOCK : 0), jbe@111: 0 jbe@111: ); jbe@111: if (sock < 0) { jbe@111: moonbr_io_errmsg(); jbe@111: lua_pushnil(L); jbe@111: lua_pushstring(L, errmsg); jbe@111: return 2; jbe@111: } jbe@111: if (connect(sock, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) { jbe@111: if (!nonblocking && errno == EINTR) { jbe@111: moonbr_io_errmsg(); jbe@111: close(sock); jbe@111: lua_pushnil(L); jbe@111: lua_pushstring(L, errmsg); jbe@111: return 2; jbe@111: } else if (!(nonblocking && (errno == EINPROGRESS || errno == EINTR))) { jbe@111: moonbr_io_errmsg(); jbe@111: lua_pushnil(L); jbe@111: lua_pushstring(L, errmsg); jbe@111: return 2; jbe@111: } jbe@111: } jbe@111: moonbr_io_pushhandle(L, sock); jbe@111: return 1; jbe@111: } jbe@111: jbe@111: static int moonbr_io_localconnect(lua_State *L) { jbe@111: return moonbr_io_localconnect_impl(L, 0); jbe@111: } jbe@111: jbe@111: static int moonbr_io_localconnect_nb(lua_State *L) { jbe@111: return moonbr_io_localconnect_impl(L, 1); jbe@111: } jbe@111: jbe@99: static int moonbr_io_tcpconnect_impl(lua_State *L, int nonblocking) { jbe@98: const char *host, *port; jbe@98: struct addrinfo hints = { 0, }; jbe@98: struct addrinfo *res, *addrinfo; jbe@98: int errcode; jbe@98: int sock; jbe@98: host = luaL_checkstring(L, 1); jbe@98: port = luaL_checkstring(L, 2); jbe@98: hints.ai_family = AF_UNSPEC; jbe@98: hints.ai_socktype = SOCK_STREAM; jbe@98: hints.ai_protocol = IPPROTO_TCP; jbe@98: hints.ai_flags = AI_ADDRCONFIG; jbe@98: errcode = getaddrinfo(host, port, &hints, &res); jbe@98: if (errcode) { jbe@102: freeaddrinfo(res); jbe@98: if (errcode == EAI_SYSTEM) { jbe@98: moonbr_io_errmsg(); jbe@98: lua_pushnil(L); jbe@98: lua_pushfstring(L, "%s: %s", gai_strerror(errcode), errmsg); jbe@98: } else { jbe@98: lua_pushnil(L); jbe@98: lua_pushstring(L, gai_strerror(errcode)); jbe@98: } jbe@98: return 2; jbe@98: } jbe@98: for (addrinfo=res; addrinfo; addrinfo=addrinfo->ai_next) { jbe@134: if (addrinfo->ai_family == AF_INET6) goto moonbr_io_tcpconnect_found; jbe@98: } jbe@98: for (addrinfo=res; addrinfo; addrinfo=addrinfo->ai_next) { jbe@134: if (addrinfo->ai_family == AF_INET) goto moonbr_io_tcpconnect_found; jbe@98: } jbe@98: addrinfo = res; jbe@98: moonbr_io_tcpconnect_found: jbe@99: sock = socket( jbe@134: addrinfo->ai_family, /* NOTE: not correctly using PF_* but AF_* constants here */ jbe@99: addrinfo->ai_socktype | SOCK_CLOEXEC | (nonblocking ? SOCK_NONBLOCK : 0), jbe@99: addrinfo->ai_protocol jbe@99: ); jbe@98: if (sock < 0) { jbe@98: moonbr_io_errmsg(); jbe@108: freeaddrinfo(res); jbe@98: lua_pushnil(L); jbe@98: lua_pushstring(L, errmsg); jbe@108: return 2; jbe@98: } jbe@98: if (connect(sock, addrinfo->ai_addr, addrinfo->ai_addrlen)) { jbe@108: freeaddrinfo(res); jbe@99: if (!nonblocking && errno == EINTR) { jbe@99: moonbr_io_errmsg(); jbe@99: close(sock); jbe@99: lua_pushnil(L); jbe@99: lua_pushstring(L, errmsg); jbe@99: return 2; jbe@99: } else if (!(nonblocking && (errno == EINPROGRESS || errno == EINTR))) { jbe@99: moonbr_io_errmsg(); jbe@99: lua_pushnil(L); jbe@99: lua_pushstring(L, errmsg); jbe@99: return 2; jbe@99: } jbe@108: } else { jbe@108: freeaddrinfo(res); jbe@98: } jbe@107: moonbr_io_pushhandle(L, sock); jbe@98: return 1; jbe@98: } jbe@98: jbe@99: static int moonbr_io_tcpconnect(lua_State *L) { jbe@99: return moonbr_io_tcpconnect_impl(L, 0); jbe@99: } jbe@99: jbe@99: static int moonbr_io_tcpconnect_nb(lua_State *L) { jbe@99: return moonbr_io_tcpconnect_impl(L, 1); jbe@99: } jbe@99: jbe@112: static int moonbr_io_locallisten(lua_State *L) { jbe@112: moonbr_io_listener_t *listener; jbe@112: const char *path; jbe@118: struct stat sb; jbe@112: struct sockaddr_un sockaddr = { .sun_family = AF_LOCAL }; jbe@112: const int path_maxlen = sizeof(struct sockaddr_un) - ( jbe@112: (void *)sockaddr.sun_path - (void *)&sockaddr jbe@112: ) - 1; /* one byte for termination */ jbe@112: int sock; jbe@112: path = luaL_checkstring(L, 1); jbe@112: if (strlen(path) > path_maxlen) luaL_error(L, "Path too long; only %i characters allowed", path_maxlen); jbe@112: strcpy(sockaddr.sun_path, path); jbe@118: if (stat(path, &sb) == 0) { jbe@118: if (S_ISSOCK(sb.st_mode)) unlink(path); jbe@118: } jbe@112: listener = lua_newuserdata(L, sizeof(moonbr_io_listener_t)); jbe@117: listener->fd = -1; jbe@112: luaL_setmetatable(L, MOONBR_IO_LISTENER_MT_REGKEY); jbe@112: sock = socket( jbe@112: PF_LOCAL, jbe@112: SOCK_STREAM | SOCK_CLOEXEC, jbe@112: 0 jbe@112: ); jbe@112: if (sock < 0) { jbe@112: moonbr_io_errmsg(); jbe@112: lua_pushnil(L); jbe@112: lua_pushstring(L, errmsg); jbe@112: return 2; jbe@112: } jbe@112: if (bind(sock, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) { jbe@112: moonbr_io_errmsg(); jbe@112: close(sock); jbe@112: lua_pushnil(L); jbe@112: lua_pushstring(L, errmsg); jbe@112: return 2; jbe@112: } jbe@112: if (listen(sock, MOONBR_IO_LISTEN_BACKLOG)) { jbe@112: moonbr_io_errmsg(); jbe@112: close(sock); jbe@112: lua_pushnil(L); jbe@112: lua_pushstring(L, errmsg); jbe@112: return 2; jbe@112: } jbe@112: listener->fd = sock; jbe@118: listener->addrfam = AF_LOCAL; jbe@112: listener->nonblocking = -1; jbe@112: return 1; jbe@112: } jbe@112: jbe@108: static int moonbr_io_tcplisten(lua_State *L) { jbe@108: moonbr_io_listener_t *listener; jbe@108: const char *host, *port; jbe@108: struct addrinfo hints = { 0, }; jbe@108: struct addrinfo *res, *addrinfo; jbe@108: int errcode; jbe@108: int sock; jbe@108: host = luaL_optstring(L, 1, NULL); jbe@108: port = luaL_checkstring(L, 2); jbe@108: listener = lua_newuserdata(L, sizeof(moonbr_io_listener_t)); jbe@117: listener->fd = -1; jbe@108: luaL_setmetatable(L, MOONBR_IO_LISTENER_MT_REGKEY); jbe@108: hints.ai_family = AF_UNSPEC; jbe@108: hints.ai_socktype = SOCK_STREAM; jbe@108: hints.ai_protocol = IPPROTO_TCP; jbe@108: hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE; jbe@108: errcode = getaddrinfo(host, port, &hints, &res); jbe@108: if (errcode) { jbe@108: freeaddrinfo(res); jbe@108: if (errcode == EAI_SYSTEM) { jbe@108: moonbr_io_errmsg(); jbe@108: lua_pushnil(L); jbe@108: lua_pushfstring(L, "%s: %s", gai_strerror(errcode), errmsg); jbe@108: } else { jbe@108: lua_pushnil(L); jbe@108: lua_pushstring(L, gai_strerror(errcode)); jbe@108: } jbe@108: return 2; jbe@108: } jbe@108: for (addrinfo=res; addrinfo; addrinfo=addrinfo->ai_next) { jbe@134: if (addrinfo->ai_family == AF_INET6) goto moonbr_io_tcpconnect_found; jbe@108: } jbe@108: for (addrinfo=res; addrinfo; addrinfo=addrinfo->ai_next) { jbe@134: if (addrinfo->ai_family == AF_INET) goto moonbr_io_tcpconnect_found; jbe@108: } jbe@108: addrinfo = res; jbe@108: moonbr_io_tcpconnect_found: jbe@118: listener->addrfam = addrinfo->ai_family; jbe@108: sock = socket( jbe@134: addrinfo->ai_family, /* NOTE: not correctly using PF_* but AF_* constants here */ jbe@108: addrinfo->ai_socktype | SOCK_CLOEXEC, jbe@108: addrinfo->ai_protocol jbe@108: ); jbe@108: if (sock < 0) { jbe@108: moonbr_io_errmsg(); jbe@108: freeaddrinfo(res); jbe@108: lua_pushnil(L); jbe@108: lua_pushstring(L, errmsg); jbe@108: return 2; jbe@108: } jbe@120: { jbe@120: static const int reuseval = 1; jbe@120: if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseval, sizeof(reuseval))) { jbe@120: moonbr_io_errmsg(); jbe@120: freeaddrinfo(res); jbe@120: close(sock); jbe@120: lua_pushnil(L); jbe@120: lua_pushfstring(L, "Error while setting SO_REUSEADDR with setsockopt: %s", errmsg); jbe@120: return 2; jbe@120: } jbe@120: } jbe@108: if (bind(sock, addrinfo->ai_addr, addrinfo->ai_addrlen)) { jbe@108: moonbr_io_errmsg(); jbe@108: freeaddrinfo(res); jbe@108: close(sock); jbe@108: lua_pushnil(L); jbe@108: lua_pushstring(L, errmsg); jbe@108: return 2; jbe@108: } jbe@108: freeaddrinfo(res); jbe@108: if (listen(sock, MOONBR_IO_LISTEN_BACKLOG)) { jbe@108: moonbr_io_errmsg(); jbe@108: close(sock); jbe@108: lua_pushnil(L); jbe@108: lua_pushstring(L, errmsg); jbe@108: return 2; jbe@108: } jbe@108: listener->fd = sock; jbe@108: listener->nonblocking = -1; jbe@108: return 1; jbe@108: } jbe@108: jbe@108: static int moonbr_io_accept_impl(lua_State *L, int nonblocking) { jbe@108: moonbr_io_listener_t *listener; jbe@108: int fd; jbe@108: listener = luaL_checkudata(L, 1, MOONBR_IO_LISTENER_MT_REGKEY); jbe@108: if (listener->fd < 0) luaL_error(L, "Attempt to use a closed listener"); jbe@108: if (listener->nonblocking != nonblocking) { jbe@108: int flags; jbe@108: flags = fcntl(listener->fd, F_GETFL, 0); jbe@108: if (flags == -1) { jbe@108: moonbr_io_errmsg(); jbe@108: close(listener->fd); jbe@108: listener->fd = -1; jbe@108: luaL_error(L, "Unexpected error in fcntl call: %s", errmsg); jbe@108: } jbe@108: if (nonblocking) flags |= O_NONBLOCK; jbe@108: else flags &= ~O_NONBLOCK; jbe@108: if (fcntl(listener->fd, F_SETFL, flags) == -1) { jbe@108: moonbr_io_errmsg(); jbe@108: close(listener->fd); jbe@108: listener->fd = -1; jbe@108: luaL_error(L, "Unexpected error in fcntl call: %s", errmsg); jbe@108: } jbe@108: listener->nonblocking = nonblocking; jbe@108: } jbe@108: while (1) { jbe@214: #if defined(__linux__) && !defined(_GNU_SOURCE) jbe@215: fd = accept(listener->fd, NULL, NULL); jbe@214: if (fd != -1) { jbe@214: if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { jbe@214: moonbr_io_errmsg(); jbe@214: close(listener->fd); jbe@214: listener->fd = -1; jbe@214: close(fd); jbe@214: luaL_error(L, "Unexpected error in fcntl call: %s", errmsg); jbe@214: } jbe@214: } jbe@214: #else jbe@108: fd = accept4(listener->fd, NULL, NULL, SOCK_CLOEXEC); jbe@214: #endif jbe@108: if (fd < 0) { jbe@108: if (nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) { jbe@108: lua_pushboolean(L, 0); jbe@108: lua_pushliteral(L, "No incoming connection pending"); jbe@108: return 2; jbe@108: } else if (errno != EINTR) { jbe@108: moonbr_io_errmsg(); jbe@108: lua_pushnil(L); jbe@108: lua_pushstring(L, errmsg); jbe@108: return 2; jbe@108: } jbe@108: } else { jbe@108: moonbr_io_pushhandle(L, fd); jbe@108: return 1; jbe@108: } jbe@108: } jbe@108: } jbe@108: jbe@108: static int moonbr_io_accept(lua_State *L) { jbe@108: return moonbr_io_accept_impl(L, 0); jbe@108: } jbe@108: jbe@108: static int moonbr_io_accept_nb(lua_State *L) { jbe@108: return moonbr_io_accept_impl(L, 1); jbe@108: } jbe@108: jbe@108: static int moonbr_io_unlisten(lua_State *L) { jbe@108: moonbr_io_listener_t *listener; jbe@118: struct sockaddr_un addr; jbe@118: socklen_t addrlen; jbe@118: struct stat sb; jbe@108: listener = luaL_checkudata(L, 1, MOONBR_IO_LISTENER_MT_REGKEY); jbe@108: if (listener->fd < 0) luaL_error(L, "Attempt to close a closed listener"); jbe@118: addrlen = sizeof(addr); jbe@118: if (getsockname(listener->fd, (struct sockaddr *)&addr, &addrlen)) addrlen = 0; jbe@108: if (close(listener->fd)) { jbe@108: moonbr_io_errmsg(); jbe@108: listener->fd = -1; jbe@118: if (addrlen && addrlen <= sizeof(addr)) { jbe@118: if (stat(addr.sun_path, &sb) == 0) { jbe@118: if (S_ISSOCK(sb.st_mode)) unlink(addr.sun_path); jbe@118: } jbe@118: } jbe@108: lua_pushnil(L); jbe@108: lua_pushstring(L, errmsg); jbe@108: return 2; jbe@108: } jbe@108: listener->fd = -1; jbe@118: if (addrlen && addrlen <= sizeof(addr)) { jbe@118: if (stat(addr.sun_path, &sb) == 0) { jbe@118: if (S_ISSOCK(sb.st_mode)) unlink(addr.sun_path); jbe@118: } jbe@118: } jbe@108: lua_pushboolean(L, 1); jbe@108: return 1; jbe@108: } jbe@108: jbe@108: static int moonbr_io_listenergc(lua_State *L) { jbe@108: moonbr_io_listener_t *listener; jbe@108: listener = luaL_checkudata(L, 1, MOONBR_IO_LISTENER_MT_REGKEY); jbe@117: if (listener->fd >= 0) close(listener->fd); jbe@108: listener->fd = -1; jbe@108: return 0; jbe@108: } jbe@108: jbe@205: static int moonbr_io_exec(lua_State *L) { jbe@205: char **argv; jbe@205: int i, argc; jbe@205: int sockin[2], sockout[2], sockerr[2]; jbe@205: volatile int errorcond = 0; jbe@213: volatile char errmsgbuf[MOONBR_IO_MAXSTRERRORLEN] = MOONBR_IO_STRERROR_R_MSG; jbe@205: moonbr_io_child_t *child; jbe@205: argc = lua_gettop(L); jbe@205: argv = lua_newuserdata(L, (argc + 1) * sizeof(char *)); jbe@205: for (i=0; ipid = 0; jbe@208: lua_newtable(L); jbe@208: lua_setuservalue(L, -2); jbe@238: luaL_setmetatable(L, MOONBR_IO_CHILD_MT_REGKEY); jbe@206: if (socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, sockin)) { jbe@205: moonbr_io_errmsg(); jbe@205: lua_pushnil(L); jbe@205: lua_pushfstring(L, "Could not create socket pair: %s", errmsg); jbe@205: return 2; jbe@205: } jbe@206: if (socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, sockout)) { jbe@205: moonbr_io_errmsg(); jbe@205: close(sockin[0]); jbe@205: close(sockin[1]); jbe@205: lua_pushnil(L); jbe@205: lua_pushfstring(L, "Could not create socket pair: %s", errmsg); jbe@205: return 2; jbe@205: } jbe@206: if (socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, sockerr)) { jbe@205: moonbr_io_errmsg(); jbe@205: close(sockin[0]); jbe@205: close(sockin[1]); jbe@205: close(sockout[0]); jbe@205: close(sockout[1]); jbe@205: lua_pushnil(L); jbe@205: lua_pushfstring(L, "Could not create socket pair: %s", errmsg); jbe@205: return 2; jbe@205: } jbe@205: child->pid = vfork(); jbe@205: if (child->pid == -1) { jbe@205: moonbr_io_errmsg(); jbe@205: close(sockin[0]); jbe@205: close(sockin[1]); jbe@205: close(sockout[0]); jbe@205: close(sockout[1]); jbe@205: close(sockerr[0]); jbe@205: close(sockerr[1]); jbe@205: lua_pushnil(L); jbe@205: lua_pushfstring(L, "Could not fork: %s", errmsg); jbe@205: return 2; jbe@205: } jbe@205: if (!child->pid) { jbe@208: if (dup2(sockin[1], 0) == -1) goto moonbr_io_exec_error1; jbe@208: if (dup2(sockout[1], 1) == -1) goto moonbr_io_exec_error1; jbe@208: if (dup2(sockerr[1], 2) == -1) goto moonbr_io_exec_error1; jbe@232: closefrom(3); jbe@208: if (fcntl(0, F_SETFD, 0) == -1) goto moonbr_io_exec_error1; jbe@208: if (fcntl(1, F_SETFD, 0) == -1) goto moonbr_io_exec_error1; jbe@208: if (fcntl(2, F_SETFD, 0) == -1) goto moonbr_io_exec_error1; jbe@205: if (execvp(argv[0], argv)) { jbe@205: errorcond = 2; jbe@205: strerror_r(errno, (char *)errmsgbuf, MOONBR_IO_MAXSTRERRORLEN); jbe@205: _exit(0); jbe@205: } jbe@208: moonbr_io_exec_error1: jbe@206: errorcond = 1; jbe@206: strerror_r(errno, (char *)errmsgbuf, MOONBR_IO_MAXSTRERRORLEN); jbe@206: _exit(0); jbe@205: } jbe@205: close(sockin[1]); jbe@205: close(sockout[1]); jbe@205: close(sockerr[1]); jbe@205: if (errorcond) { jbe@205: int status; jbe@205: close(sockin[0]); jbe@205: close(sockout[0]); jbe@205: close(sockerr[0]); jbe@205: while (waitpid(child->pid, &status, 0) == -1) { jbe@205: if (errno != EINTR) { jbe@205: moonbr_io_errmsg(); jbe@205: luaL_error(L, "Error in waitpid call after unsuccessful exec: %s", errmsg); jbe@205: } jbe@205: } jbe@208: child->pid = 0; jbe@205: lua_pushnil(L); jbe@205: if (errorcond == 2) lua_pushfstring(L, "Could not execute: %s", errmsgbuf); jbe@205: else lua_pushfstring(L, "Error in fork: %s", errmsgbuf); jbe@205: return 2; jbe@205: } jbe@208: lua_pushcfunction(L, moonbr_io_pushhandle_impl); jbe@208: lua_pushlightuserdata(L, &sockin[0]); jbe@208: if (lua_pcall(L, 1, 1, 0)) { jbe@208: if (sockin[0] != -1) close(sockin[0]); jbe@208: close(sockout[0]); jbe@208: close(sockerr[0]); jbe@208: goto moonbr_io_exec_error2; jbe@208: } jbe@205: lua_setfield(L, -2, "stdin"); jbe@208: lua_pushcfunction(L, moonbr_io_pushhandle_impl); jbe@208: lua_pushlightuserdata(L, &sockout[0]); jbe@208: if (lua_pcall(L, 1, 1, 0)) { jbe@208: if (sockout[0] != -1) close(sockout[0]); jbe@208: close(sockerr[0]); jbe@208: goto moonbr_io_exec_error2; jbe@208: } jbe@205: lua_setfield(L, -2, "stdout"); jbe@208: lua_pushcfunction(L, moonbr_io_pushhandle_impl); jbe@208: lua_pushlightuserdata(L, &sockerr[0]); jbe@208: if (lua_pcall(L, 1, 1, 0)) { jbe@208: if (sockerr[0] != -1) close(sockerr[0]); jbe@208: goto moonbr_io_exec_error2; jbe@208: } jbe@205: lua_setfield(L, -2, "stderr"); jbe@205: return 1; jbe@208: moonbr_io_exec_error2: jbe@208: { jbe@208: int status; jbe@208: while (waitpid(child->pid, &status, 0) == -1) { jbe@208: if (errno != EINTR) { jbe@208: moonbr_io_errmsg(); jbe@208: luaL_error(L, "Error in waitpid call after error creating socket handles: %s", errmsg); jbe@208: } jbe@208: } jbe@208: } jbe@208: child->pid = 0; jbe@208: return lua_error(L); jbe@205: } jbe@205: jbe@205: static int moonbr_io_childindex(lua_State *L) { jbe@205: luaL_checkudata(L, 1, MOONBR_IO_CHILD_MT_REGKEY); jbe@205: luaL_checkany(L, 2); jbe@205: lua_getuservalue(L, 1); jbe@205: lua_pushvalue(L, 2); jbe@205: lua_gettable(L, -2); jbe@205: if (lua_isnil(L, -1)) { jbe@205: luaL_getmetatable(L, MOONBR_IO_CHILD_PT_REGKEY); jbe@205: lua_pushvalue(L, 2); jbe@205: lua_gettable(L, -2); jbe@205: } jbe@205: return 1; jbe@205: } jbe@205: jbe@205: static int moonbr_io_childnewindex(lua_State *L) { jbe@205: luaL_checkudata(L, 1, MOONBR_IO_CHILD_MT_REGKEY); jbe@205: luaL_checkany(L, 2); jbe@205: luaL_checkany(L, 3); jbe@205: lua_getuservalue(L, 1); jbe@205: lua_pushvalue(L, 2); jbe@205: lua_pushvalue(L, 3); jbe@205: lua_settable(L, -3); jbe@205: return 0; jbe@205: } jbe@205: jbe@205: static int moonbr_io_childgc(lua_State *L) { jbe@205: moonbr_io_child_t *child; jbe@205: child = luaL_checkudata(L, 1, MOONBR_IO_CHILD_MT_REGKEY); jbe@205: if (child->pid) { jbe@205: int status; jbe@205: if (kill(child->pid, SIGKILL)) { jbe@205: moonbr_io_errmsg(); jbe@205: luaL_error(L, "Error in kill call during garbage collection: %s", errmsg); jbe@205: } jbe@205: while (waitpid(child->pid, &status, 0) == -1) { jbe@205: if (errno != EINTR) { jbe@205: moonbr_io_errmsg(); jbe@205: luaL_error(L, "Error in waitpid call during garbage collection: %s", errmsg); jbe@205: } jbe@205: } jbe@205: } jbe@205: return 0; jbe@205: } jbe@205: jbe@205: static int moonbr_io_kill(lua_State *L) { jbe@205: moonbr_io_child_t *child; jbe@205: int sig; jbe@205: child = luaL_checkudata(L, 1, MOONBR_IO_CHILD_MT_REGKEY); jbe@205: sig = luaL_optinteger(L, 2, SIGTERM); jbe@205: if (!child->pid) luaL_error(L, "Attempt to kill an already collected child process"); jbe@205: if (kill(child->pid, sig)) { jbe@205: moonbr_io_errmsg(); jbe@205: luaL_error(L, "Error in kill call: %s", errmsg); jbe@205: } jbe@205: lua_settop(L, 1); jbe@205: return 1; jbe@205: } jbe@205: jbe@205: static int moonbr_io_wait_impl(lua_State *L, int nonblocking) { jbe@205: moonbr_io_child_t *child; jbe@205: pid_t waitedpid; jbe@205: int status; jbe@205: child = luaL_checkudata(L, 1, MOONBR_IO_CHILD_MT_REGKEY); jbe@205: if (!child->pid) luaL_error(L, "Attempt to wait for an already collected child process"); jbe@205: while ((waitedpid = waitpid(child->pid, &status, nonblocking ? WNOHANG : 0)) == -1) { jbe@205: if (errno != EINTR) { jbe@205: moonbr_io_errmsg(); jbe@205: luaL_error(L, "Error in waitpid call: %s", errmsg); jbe@205: } jbe@205: } jbe@205: if (!waitedpid) { jbe@219: lua_pushboolean(L, 0); jbe@219: lua_pushliteral(L, "Process is still running"); jbe@219: return 2; jbe@205: } else { jbe@205: child->pid = 0; jbe@205: if (WIFEXITED(status)) { jbe@205: lua_pushinteger(L, WEXITSTATUS(status)); jbe@205: } else if (WIFSIGNALED(status)) { jbe@205: lua_pushinteger(L, -WTERMSIG(status)); jbe@205: } else { jbe@205: luaL_error(L, "Unexpected status value returned by waitpid call"); jbe@205: } jbe@219: return 1; jbe@205: } jbe@205: } jbe@205: jbe@205: static int moonbr_io_wait(lua_State *L) { jbe@205: return moonbr_io_wait_impl(L, 0); jbe@205: } jbe@205: jbe@205: static int moonbr_io_wait_nb(lua_State *L) { jbe@205: return moonbr_io_wait_impl(L, 1); jbe@205: } jbe@205: jbe@205: #if LUA_VERSION_NUM >= 503 jbe@205: static int moonbr_io_wait_cont(lua_State *L, int status, lua_KContext ctx) { jbe@205: #else jbe@205: static int moonbr_io_wait_cont(lua_State *L) { jbe@205: #endif jbe@205: #if !(LUA_VERSION_NUM >= 503) jbe@205: int ctx = 0; jbe@205: lua_getctx(L, &ctx); jbe@205: #endif jbe@205: while (1) { jbe@205: lua_pushcfunction(L, moonbr_io_wait_nb); jbe@205: lua_pushvalue(L, 1); jbe@205: lua_call(L, 1, 1); jbe@205: if (!lua_isnil(L, -1)) break; jbe@205: lua_pushvalue(L, 2); jbe@205: lua_callk(L, 0, 0, ctx, moonbr_io_wait_cont); jbe@205: } jbe@205: return 1; jbe@205: } jbe@205: jbe@205: static int moonbr_io_wait_call(lua_State *L) { jbe@205: lua_settop(L, 2); jbe@205: #if LUA_VERSION_NUM >= 503 jbe@205: return moonbr_io_wait_cont(L, 0, 0); jbe@205: #else jbe@205: return moonbr_io_wait_cont(L); jbe@205: #endif jbe@205: } jbe@205: jbe@205: moonbr_io_yield_wrapper(moonbr_io_wait_yield, moonbr_io_wait_call); jbe@205: jbe@106: static int moonbr_io_poll(lua_State *L) { jbe@106: moonbr_io_handle_t *handle; jbe@108: moonbr_io_listener_t *listener; jbe@106: int fd, isnum; jbe@106: int nfds = 0; jbe@106: fd_set readfds, writefds, exceptfds; jbe@106: struct timeval timeout = {0, }; jbe@106: int status; jbe@106: FD_ZERO(&readfds); jbe@106: FD_ZERO(&writefds); jbe@106: FD_ZERO(&exceptfds); jbe@106: if (!lua_isnoneornil(L, 1)) { jbe@106: luaL_checktype(L, 1, LUA_TTABLE); jbe@106: for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) { jbe@106: if (lua_toboolean(L, -1)) { jbe@106: handle = luaL_testudata(L, -2, MOONBR_IO_HANDLE_MT_REGKEY); jbe@106: if (handle) { jbe@109: if (handle->closed) luaL_error(L, "Attempt to poll a closed connection"); jbe@106: fd = handle->fd; jbe@122: if ( jbe@122: fd < 0 || /* fake EOF to simulate shutdown if fd < 0 */ jbe@122: handle->readbufin != handle->readbufout /* data pending in buffer */ jbe@122: ) { jbe@109: lua_pushboolean(L, 1); jbe@109: return 1; jbe@109: } jbe@106: } else { jbe@108: listener = luaL_testudata(L, -2, MOONBR_IO_LISTENER_MT_REGKEY); jbe@108: if (listener) { jbe@108: fd = listener->fd; jbe@108: if (fd < 0) luaL_error(L, "Attempt to poll a closed listener"); jbe@108: } else { jbe@108: fd = lua_tointegerx(L, -2, &isnum); jbe@108: if (!isnum) luaL_error(L, "Expected integer (file descriptor), I/O handle, or listener in table key"); jbe@108: } jbe@106: } jbe@138: if (fd < 0 || fd >= FD_SETSIZE) luaL_error(L, "File descriptor out of valid range"); jbe@106: FD_SET(fd, &readfds); jbe@106: if (fd+1 > nfds) nfds = fd+1; jbe@106: } jbe@106: } jbe@106: } jbe@106: if (!lua_isnoneornil(L, 2)) { jbe@106: luaL_checktype(L, 2, LUA_TTABLE); jbe@106: for (lua_pushnil(L); lua_next(L, 2); lua_pop(L, 1)) { jbe@106: if (lua_toboolean(L, -1)) { jbe@106: handle = luaL_testudata(L, -2, MOONBR_IO_HANDLE_MT_REGKEY); jbe@106: if (handle) { jbe@109: if (handle->closed) luaL_error(L, "Attempt to poll a closed connection"); jbe@109: if (handle->finished) luaL_error(L, "Attempt to write-poll a finished connection"); jbe@106: fd = handle->fd; jbe@106: } else { jbe@108: listener = luaL_testudata(L, -2, MOONBR_IO_LISTENER_MT_REGKEY); jbe@109: if (listener) luaL_error(L, "Attempt to write-poll a listener"); jbe@109: fd = lua_tointegerx(L, -2, &isnum); jbe@109: if (!isnum) luaL_error(L, "Expected integer (file descriptor) or I/O handle in table key"); jbe@106: } jbe@138: if (fd < 0 || fd >= FD_SETSIZE) luaL_error(L, "File descriptor out of valid range"); jbe@106: FD_SET(fd, &writefds); jbe@106: if (fd+1 > nfds) nfds = fd+1; jbe@106: } jbe@106: } jbe@106: } jbe@106: if (!lua_isnoneornil(L, 3)) { jbe@106: lua_Number n; jbe@106: n = lua_tonumberx(L, 3, &isnum); jbe@146: if (isnum && n<0) { jbe@146: lua_pushboolean(L, 0); jbe@146: lua_pushliteral(L, "Negative timeout"); jbe@146: return 2; jbe@146: } else if (isnum && n>=0 && n<100000000) { jbe@106: timeout.tv_sec = n; jbe@106: timeout.tv_usec = 1e6 * (n - timeout.tv_sec); jbe@106: } else { jbe@106: luaL_argcheck(L, 0, 3, "not a valid timeout"); jbe@106: } jbe@106: status = select(nfds, &readfds, &writefds, &exceptfds, &timeout); jbe@106: } else { jbe@106: status = select(nfds, &readfds, &writefds, &exceptfds, NULL); jbe@106: } jbe@106: if (status == -1) { jbe@106: if (errno == EINTR) { jbe@146: lua_pushnil(L); jbe@106: lua_pushliteral(L, "Signal received while polling file descriptors"); jbe@106: return 2; jbe@106: } else { jbe@106: moonbr_io_errmsg(); jbe@106: return luaL_error(L, "Unexpected error during \"select\" system call: %s", errmsg); jbe@106: } jbe@106: } else if (status == 0) { jbe@106: lua_pushboolean(L, 0); jbe@106: lua_pushliteral(L, "Timeout while polling file descriptors"); jbe@106: return 2; jbe@106: } else { jbe@106: lua_pushboolean(L, 1); jbe@106: return 1; jbe@106: } jbe@106: } jbe@106: jbe@115: static int moonbr_io_timeref(lua_State *L) { jbe@115: lua_Number sub; jbe@115: struct timespec tp; jbe@115: sub = luaL_optnumber(L, 1, 0); jbe@115: if (clock_gettime(CLOCK_MONOTONIC, &tp)) { jbe@115: return luaL_error(L, "Could not access CLOCK_MONOTONIC"); jbe@115: } jbe@115: lua_pushnumber(L, tp.tv_sec + tp.tv_nsec / 1.0e9 - sub); jbe@115: return 1; jbe@115: } jbe@115: jbe@243: #ifdef MOONBR_IO_USE_TLS jbe@243: jbe@243: #define moonbr_io_tlsconf_string(name, field, func) \ jbe@243: /* NOTE: use valuetype = lua_getfield(...) for LUA_VERSION_NUM >= 503 */ \ jbe@243: lua_getfield(L, 1, (field)); \ jbe@243: valuetype = lua_type(L, -1); \ jbe@243: if (valuetype != LUA_TNIL) { \ jbe@243: luaL_argcheck(L, valuetype == LUA_TSTRING, 1, "field \"" field "\" is not a string"); \ jbe@243: value = lua_tostring(L, -1); \ jbe@243: if (func(tlsconf, value)) { \ jbe@243: lua_pushnil(L); \ jbe@243: lua_pushfstring(L, "Could not set " name " \"%s\"", value); \ jbe@243: return 2; \ jbe@243: } \ jbe@243: } \ jbe@243: lua_pop(L, 1); jbe@243: jbe@243: #define moonbr_io_tlsconf_binary(name, field, func) \ jbe@243: /* NOTE: use valuetype = lua_getfield(...) for LUA_VERSION_NUM >= 503 */ \ jbe@243: lua_getfield(L, 1, (field)); \ jbe@243: valuetype = lua_type(L, -1); \ jbe@243: if (valuetype != LUA_TNIL) { \ jbe@243: luaL_argcheck(L, valuetype == LUA_TSTRING, 1, "field \"" field "\" is not a string"); \ jbe@243: value = lua_tolstring(L, -1, &valuelen); \ jbe@243: if (func(tlsconf, (void *)value, valuelen)) { \ jbe@243: lua_pushnil(L); \ jbe@243: lua_pushliteral(L, "Could not set " name); \ jbe@243: return 2; \ jbe@243: } \ jbe@243: } \ jbe@243: lua_pop(L, 1); jbe@243: jbe@243: static int moonbr_io_tlsconf(lua_State *L) { jbe@243: struct tls_config *tlsconf; jbe@243: int valuetype; jbe@243: const char *value; jbe@243: size_t valuelen; jbe@243: luaL_checktype(L, 1, LUA_TTABLE); jbe@243: tlsconf = tls_config_new(); jbe@243: if (!tlsconf) { jbe@243: return luaL_error(L, "Could not allocate memory for TLS configuration"); jbe@243: } jbe@243: lua_pushlightuserdata(L, tlsconf); jbe@243: luaL_setmetatable(L, MOONBR_IO_TLSCONF_MT_REGKEY); jbe@243: lua_pushvalue(L, 1); jbe@243: lua_setuservalue(L, -2); jbe@245: #if LUA_VERSION_NUM >= 503 jbe@245: if (lua_getfield(L, 1, "mode") == LUA_TSTRING) value = lua_tostring(L, -1); jbe@245: #else jbe@245: lua_getfield(L, 1, "mode"); jbe@245: if (lua_type(L, -1) == LUA_TSTRING) value = lua_tostring(L, -1); jbe@245: #endif jbe@245: else value = ""; jbe@245: if (strcmp(value, "server") && strcmp(value, "client")) { jbe@245: luaL_argcheck(L, 0, 1, "field \"mode\" must be set to \"server\" or \"client\""); jbe@245: } jbe@245: lua_pop(L, 1); jbe@243: moonbr_io_tlsconf_string("CA file", "ca_file", tls_config_set_ca_file); jbe@243: moonbr_io_tlsconf_string("CA path", "ca_path", tls_config_set_ca_path); jbe@243: moonbr_io_tlsconf_binary("CA", "ca_mem", tls_config_set_ca_mem); jbe@243: moonbr_io_tlsconf_string("certificate file", "cert_file", tls_config_set_cert_file); jbe@243: moonbr_io_tlsconf_binary("certificate", "cert_mem", tls_config_set_cert_mem); jbe@243: moonbr_io_tlsconf_string("key file", "key_file", tls_config_set_key_file); jbe@243: moonbr_io_tlsconf_binary("key", "key_mem", tls_config_set_key_mem); jbe@245: #if LUA_VERSION_NUM >= 503 jbe@245: valuetype = lua_getfield(L, 1, "verify_client"); jbe@245: #else jbe@244: lua_getfield(L, 1, "verify_client"); jbe@245: #endif jbe@244: if (lua_toboolean(L, -1)) { jbe@245: #if LUA_VERSION_NUM >= 503 jbe@245: if (valuetype == LUA_TSTRING) value = lua_tostring(L, -1); jbe@245: #else jbe@244: if (lua_type(L, -1) == LUA_TSTRING) value = lua_tostring(L, -1); jbe@245: #endif jbe@244: else value = ""; jbe@244: if (!strcmp(value, "required")) { jbe@244: tls_config_verify_client(tlsconf); jbe@244: } else if (!strcmp(value, "optional")) { jbe@244: tls_config_verify_client_optional(tlsconf); jbe@244: } else { jbe@244: luaL_argcheck(L, 0, 1, "field \"verify_client\" must be set to \"required\", \"optional\", or be false or nil"); jbe@244: } jbe@244: } jbe@244: lua_pop(L, 1); jbe@243: return 1; jbe@243: } jbe@243: jbe@243: static int moonbr_io_tlsconfindex(lua_State *L) { jbe@243: struct tls_config *tlsconf; jbe@243: tlsconf = luaL_checkudata(L, 1, MOONBR_IO_TLSCONF_MT_REGKEY); jbe@243: luaL_checkany(L, 2); jbe@243: #if LUA_VERSION_NUM >= 503 jbe@243: if (lua_getuservalue(L, 1) == LUA_TNIL) { jbe@243: #else jbe@243: lua_getuservalue(L, 1); jbe@243: if (lua_isnil(L, -1)) { jbe@243: #endif jbe@243: return luaL_error(L, "Attempt to use a destroyed TLS configuration"); jbe@243: } jbe@243: lua_pushvalue(L, 2); jbe@243: lua_gettable(L, -2); jbe@243: return 1; jbe@243: } jbe@243: jbe@243: static int moonbr_io_tlsconfgc(lua_State *L) { jbe@243: struct tls_config *tlsconf; jbe@243: tlsconf = luaL_checkudata(L, 1, MOONBR_IO_TLSCONF_MT_REGKEY); jbe@243: #if LUA_VERSION_NUM >= 503 jbe@243: if (lua_getuservalue(L, 1) == LUA_TNIL) return 0; jbe@243: #else jbe@243: lua_getuservalue(L, 1); jbe@243: if (lua_isnil(L, -1)) return 0; jbe@243: #endif jbe@243: tls_config_free(tlsconf); jbe@243: lua_pushnil(L); jbe@243: lua_setuservalue(L, 1); jbe@243: return 0; jbe@243: } jbe@243: jbe@243: #endif jbe@243: 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@140: {"read_call", moonbr_io_read_call}, jbe@140: {"read_yield", moonbr_io_read_yield}, jbe@86: {"drain", moonbr_io_drain}, jbe@86: {"drain_nb", moonbr_io_drain_nb}, jbe@144: {"drain_call", moonbr_io_drain_call}, jbe@144: {"drain_yield", moonbr_io_drain_yield}, jbe@80: {"write", moonbr_io_write}, jbe@81: {"write_nb", moonbr_io_write_nb}, jbe@145: {"write_call", moonbr_io_write_call}, jbe@145: {"write_yield", moonbr_io_write_yield}, jbe@80: {"flush", moonbr_io_flush}, jbe@81: {"flush_nb", moonbr_io_flush_nb}, jbe@145: {"flush_call", moonbr_io_flush_call}, jbe@145: {"flush_yield", moonbr_io_flush_yield}, 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@108: {"__gc", moonbr_io_handlegc}, jbe@108: {NULL, NULL} jbe@108: }; jbe@108: jbe@108: static const struct luaL_Reg moonbr_io_listener_methods[] = { jbe@108: {"accept", moonbr_io_accept}, jbe@108: {"accept_nb", moonbr_io_accept_nb}, jbe@108: {"close", moonbr_io_unlisten}, jbe@108: {NULL, NULL} jbe@108: }; jbe@108: jbe@108: static const struct luaL_Reg moonbr_io_listener_metamethods[] = { jbe@108: {"__gc", moonbr_io_listenergc}, jbe@79: {NULL, NULL} jbe@79: }; jbe@79: jbe@205: static const struct luaL_Reg moonbr_io_child_methods[] = { jbe@205: {"kill", moonbr_io_kill}, jbe@205: {"wait", moonbr_io_wait}, jbe@205: {"wait_nb", moonbr_io_wait_nb}, jbe@205: {"wait_call", moonbr_io_wait_call}, jbe@205: {"wait_yield", moonbr_io_wait_yield}, jbe@205: {NULL, NULL} jbe@205: }; jbe@205: jbe@205: static const struct luaL_Reg moonbr_io_child_metamethods[] = { jbe@205: {"__index", moonbr_io_childindex}, jbe@205: {"__newindex", moonbr_io_childnewindex}, jbe@205: {"__gc", moonbr_io_childgc}, jbe@205: {NULL, NULL} jbe@205: }; jbe@205: jbe@79: static const struct luaL_Reg moonbr_io_module_funcs[] = { jbe@111: {"localconnect", moonbr_io_localconnect}, jbe@111: {"localconnect_nb", moonbr_io_localconnect_nb}, jbe@98: {"tcpconnect", moonbr_io_tcpconnect}, jbe@99: {"tcpconnect_nb", moonbr_io_tcpconnect_nb}, jbe@112: {"locallisten", moonbr_io_locallisten}, jbe@108: {"tcplisten", moonbr_io_tcplisten}, jbe@205: {"exec", moonbr_io_exec}, jbe@106: {"poll", moonbr_io_poll}, jbe@115: {"timeref", moonbr_io_timeref}, jbe@243: #ifdef MOONBR_IO_USE_TLS jbe@243: {"tlsconf", moonbr_io_tlsconf}, jbe@243: #endif jbe@79: {NULL, NULL} jbe@79: }; jbe@79: jbe@243: #ifdef MOONBR_IO_USE_TLS jbe@243: static const struct luaL_Reg moonbr_io_tlsconf_metamethods[] = { jbe@243: {"__index", moonbr_io_tlsconfindex}, jbe@243: {"__gc", moonbr_io_tlsconfgc}, jbe@243: {NULL, NULL} jbe@243: }; jbe@243: #endif jbe@243: jbe@79: int luaopen_moonbridge_io(lua_State *L) { jbe@79: jbe@113: signal(SIGPIPE, SIG_IGN); /* generate I/O errors instead of signal 13 */ jbe@113: 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@146: lua_setfield(L, -4, "handle_pt"); 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@146: lua_pushvalue(L, -1); jbe@146: lua_setfield(L, -3, "handle_mt"); jbe@79: lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_HANDLE_MT_REGKEY); jbe@79: jbe@108: lua_newtable(L); // listener metatable jbe@108: luaL_setfuncs(L, moonbr_io_listener_metamethods, 0); jbe@108: lua_newtable(L); // listener methods jbe@108: luaL_setfuncs(L, moonbr_io_listener_methods, 0); jbe@108: lua_pushvalue(L, -1); jbe@146: lua_setfield(L, -4, "listener_pt"); jbe@108: lua_setfield(L, -2, "__index"); jbe@146: lua_pushvalue(L, -1); jbe@146: lua_setfield(L, -3, "listener_mt"); jbe@108: lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_LISTENER_MT_REGKEY); jbe@108: jbe@205: lua_newtable(L); // child methods jbe@205: luaL_setfuncs(L, moonbr_io_child_methods, 0); jbe@205: lua_pushvalue(L, -1); jbe@205: lua_setfield(L, -3, "child_pt"); jbe@205: lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_CHILD_PT_REGKEY); jbe@205: lua_newtable(L); // child metatable jbe@205: luaL_setfuncs(L, moonbr_io_child_metamethods, 0); jbe@205: lua_pushvalue(L, -1); jbe@205: lua_setfield(L, -3, "child_mt"); jbe@205: lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_CHILD_MT_REGKEY); jbe@205: jbe@243: #ifdef MOONBR_IO_USE_TLS jbe@243: if(tls_init()) { jbe@243: return luaL_error(L, "Could not initialize TLS library"); jbe@243: } jbe@243: lua_newtable(L); // tlsconf metatable jbe@243: luaL_setfuncs(L, moonbr_io_tlsconf_metamethods, 0); jbe@243: lua_pushvalue(L, -1); jbe@243: lua_setfield(L, -3, "tlsconf_mt"); jbe@243: lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_TLSCONF_MT_REGKEY); jbe@243: #endif jbe@243: jbe@205: moonbr_io_pushhandle(L, 0); jbe@205: lua_setfield(L, -2, "stdin"); jbe@205: moonbr_io_pushhandle(L, 1); jbe@205: lua_setfield(L, -2, "stdout"); jbe@205: moonbr_io_pushhandle(L, 2); jbe@205: lua_setfield(L, -2, "stderr"); jbe@205: jbe@79: luaL_setfuncs(L, moonbr_io_module_funcs, 0); jbe@79: return 1; jbe@79: jbe@79: } jbe@79: