From 7115c12fbc9aae1cd46fdf049697a27fb996181a Mon Sep 17 00:00:00 2001 From: Diego Nehab Date: Thu, 1 Jul 2004 03:32:09 +0000 Subject: [PATCH] Moving on to beta2. --- FIX | 7 +- TODO | 8 +- etc/tftp.lua | 25 ++-- samples/lpr.lua | 51 +++++++ src/buffer.c | 68 ++++------ src/io.c | 20 +-- src/io.h | 22 ++- src/select.c | 11 +- src/socket.h | 14 +- src/tcp.c | 6 +- src/timeout.c | 73 +++++----- src/timeout.h | 16 +-- src/udp.c | 107 +++++++-------- src/usocket.c | 338 +++++++++++++++++----------------------------- src/usocket.h | 6 +- test/testclnt.lua | 62 +++++---- 16 files changed, 391 insertions(+), 443 deletions(-) create mode 100644 samples/lpr.lua diff --git a/FIX b/FIX index 720c72b..0972403 100644 --- a/FIX +++ b/FIX @@ -1,8 +1,9 @@ +setup error messages in the default case. listen defaults to 32 backlog -smtp sends quit on exceptions -accept/connect interrupt safe +smtp/ftp/http fail gracefully +accept/connect/select interrupt safe accepted sockets are nonblocking -new timming functions. better sleep/gettime +new timming functions. higher resolution, no wrap around bug fixes in the manual getfd missing cast added unix domain support example diff --git a/TODO b/TODO index 437e1e5..c74e525 100644 --- a/TODO +++ b/TODO @@ -1,8 +1,8 @@ -setup error messages in the default case. +create the getstats method. -ajeitar o connect com a dica do mike - if ((err > 1 || !FD_ISSET(sock, &wfds)) && - recv(sock, &dummy, 0, 0) < 0 && errno != EWOULDBLOCK) ... + sent, received, age = sock:getstats() + +take a look at DB's smtp patch sort out the wrap around of gettime... use doubles all over diff --git a/etc/tftp.lua b/etc/tftp.lua index 2fc914e..70db050 100644 --- a/etc/tftp.lua +++ b/etc/tftp.lua @@ -72,50 +72,51 @@ local function tget(gett) local retries, dgram, sent, datahost, dataport, code local last = 0 local con = socket.try(socket.udp()) + local try = socket.newtry(function() con:close() end) -- convert from name to ip if needed - gett.host = socket.try(socket.dns.toip(gett.host)) + gett.host = try(socket.dns.toip(gett.host)) con:settimeout(1) -- first packet gives data host/port to be used for data transfers retries = 0 repeat - sent = socket.try(con:sendto(RRQ(gett.path, "octet"), + sent = try(con:sendto(RRQ(gett.path, "octet"), gett.host, gett.port)) dgram, datahost, dataport = con:receivefrom() retries = retries + 1 until dgram or datahost ~= "timeout" or retries > 5 - socket.try(dgram, datahost) + try(dgram, datahost) -- associate socket with data host/port - socket.try(con:setpeername(datahost, dataport)) + try(con:setpeername(datahost, dataport)) -- default sink local sink = gett.sink or ltn12.sink.null() -- process all data packets while 1 do -- decode packet code = get_OP(dgram) - socket.try(code ~= OP_ERROR, get_ERROR(dgram)) - socket.try(code == OP_DATA, "unhandled opcode " .. code) + try(code ~= OP_ERROR, get_ERROR(dgram)) + try(code == OP_DATA, "unhandled opcode " .. code) -- get data packet parts local block, data = split_DATA(dgram) -- if not repeated, write if block == last+1 then - socket.try(sink(data)) + try(sink(data)) last = block end -- last packet brings less than 512 bytes of data if string.len(data) < 512 then - socket.try(con:send(ACK(block))) - socket.try(con:close()) - socket.try(sink(nil)) + try(con:send(ACK(block))) + try(con:close()) + try(sink(nil)) return 1 end -- get the next packet retries = 0 repeat - sent = socket.try(con:send(ACK(last))) + sent = try(con:send(ACK(last))) dgram, err = con:receive() retries = retries + 1 until dgram or err ~= "timeout" or retries > 5 - socket.try(dgram, err) + try(dgram, err) end end diff --git a/samples/lpr.lua b/samples/lpr.lua new file mode 100644 index 0000000..77c354f --- /dev/null +++ b/samples/lpr.lua @@ -0,0 +1,51 @@ +local lp = require("lp") + +local function usage() + print('\nUsage: lp filename [keyword=val...]\n') + print('Valid keywords are :') + print( + ' host=remote host or IP address (default "localhost")\n' .. + ' queue=remote queue or printer name (default "printer")\n' .. + ' port=remote port number (default 515)\n' .. + ' user=sending user name\n' .. + ' format=["binary" | "text" | "ps" | "pr" | "fortran"] (default "binary")\n' .. + ' banner=true|false\n' .. + ' indent=number of columns to indent\n' .. + ' mail=email of address to notify when print is complete\n' .. + ' title=title to use for "pr" format\n' .. + ' width=width for "text" or "pr" formats\n' .. + ' class=\n' .. + ' job=\n' .. + ' name=\n' .. + ' localbind=true|false\n' + ) + return nil +end + +if not arg or not arg[1] then + return usage() +end + +do + local s="opt = {" + for i = 2 , table.getn(arg), 1 do + s = s .. string.gsub(arg[i],"[%s%c%p]*([%w]*)=([\"]?[%w%s_!@#$%%^&*()<>:;]+[\"]\?\.?)","%1%=\"%2\",\n") + end + s = s .. "};\n" + assert(loadstring(s))(); + if not arg[2] then + return usage() + end + if arg[1] ~= "query" then + r,e=lp.send(arg[1],opt) + io.stderr:write(tostring(r or e),'\n') + else + r,e=lp.query(opt) + io.stderr:write(tostring(r or e), '\n') + end +end + +-- trivial tests +--lua lp.lua lp.lua queue=default host=localhost +--lua lp.lua lp.lua queue=default host=localhost format=binary localbind=1 +--lua lp.lua query queue=default host=localhost diff --git a/src/buffer.c b/src/buffer.c index 60e42ae..aa50db0 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -33,8 +33,7 @@ static int sendraw(p_buf buf, const char *data, size_t count, size_t *sent); /*-------------------------------------------------------------------------*\ * Initializes module \*-------------------------------------------------------------------------*/ -int buf_open(lua_State *L) -{ +int buf_open(lua_State *L) { (void) L; return 0; } @@ -42,8 +41,7 @@ int buf_open(lua_State *L) /*-------------------------------------------------------------------------*\ * Initializes C structure \*-------------------------------------------------------------------------*/ -void buf_init(p_buf buf, p_io io, p_tm tm) -{ +void buf_init(p_buf buf, p_io io, p_tm tm) { buf->first = buf->last = 0; buf->io = io; buf->tm = tm; @@ -52,13 +50,11 @@ void buf_init(p_buf buf, p_io io, p_tm tm) /*-------------------------------------------------------------------------*\ * object:send() interface \*-------------------------------------------------------------------------*/ -int buf_meth_send(lua_State *L, p_buf buf) -{ +int buf_meth_send(lua_State *L, p_buf buf) { int top = lua_gettop(L); size_t total = 0; int arg, err = IO_DONE; - p_tm tm = buf->tm; - tm_markstart(tm); + p_tm tm = tm_markstart(buf->tm); for (arg = 2; arg <= top; arg++) { /* first arg is socket object */ size_t sent, count; const char *data = luaL_optlstring(L, arg, NULL, &count); @@ -69,7 +65,7 @@ int buf_meth_send(lua_State *L, p_buf buf) /* check if there was an error */ if (err != IO_DONE) { lua_pushnil(L); - io_pusherror(L, err); + io_pusherror(L, buf->io, err); lua_pushnumber(L, total); } else { lua_pushnumber(L, total); @@ -78,7 +74,7 @@ int buf_meth_send(lua_State *L, p_buf buf) } #ifdef LUASOCKET_DEBUG /* push time elapsed during operation as the last return value */ - lua_pushnumber(L, (tm_gettime() - tm_getstart(tm))/1000.0); + lua_pushnumber(L, tm_gettime() - tm_getstart(tm)); #endif return lua_gettop(L) - top; } @@ -86,13 +82,11 @@ int buf_meth_send(lua_State *L, p_buf buf) /*-------------------------------------------------------------------------*\ * object:receive() interface \*-------------------------------------------------------------------------*/ -int buf_meth_receive(lua_State *L, p_buf buf) -{ +int buf_meth_receive(lua_State *L, p_buf buf) { int err = IO_DONE, top = lua_gettop(L); - p_tm tm = buf->tm; + p_tm tm = tm_markstart(buf->tm); luaL_Buffer b; luaL_buffinit(L, &b); - tm_markstart(tm); /* receive all patterns */ if (!lua_isnumber(L, 2)) { static const char *patternnames[] = {"*l", "*a", NULL}; @@ -107,7 +101,7 @@ int buf_meth_receive(lua_State *L, p_buf buf) /* check if there was an error */ if (err != IO_DONE) { luaL_pushresult(&b); - io_pusherror(L, err); + io_pusherror(L, buf->io, err); lua_pushvalue(L, -2); lua_pushnil(L); lua_replace(L, -4); @@ -118,7 +112,7 @@ int buf_meth_receive(lua_State *L, p_buf buf) } #ifdef LUASOCKET_DEBUG /* push time elapsed during operation as the last return value */ - lua_pushnumber(L, (tm_gettime() - tm_getstart(tm))/1000.0); + lua_pushnumber(L, tm_gettime() - tm_getstart(tm)); #endif return lua_gettop(L) - top; } @@ -126,8 +120,7 @@ int buf_meth_receive(lua_State *L, p_buf buf) /*-------------------------------------------------------------------------*\ * Determines if there is any data in the read buffer \*-------------------------------------------------------------------------*/ -int buf_isempty(p_buf buf) -{ +int buf_isempty(p_buf buf) { return buf->first >= buf->last; } @@ -137,16 +130,14 @@ int buf_isempty(p_buf buf) /*-------------------------------------------------------------------------*\ * Sends a block of data (unbuffered) \*-------------------------------------------------------------------------*/ -static -int sendraw(p_buf buf, const char *data, size_t count, size_t *sent) -{ +static int sendraw(p_buf buf, const char *data, size_t count, size_t *sent) { p_io io = buf->io; p_tm tm = buf->tm; size_t total = 0; int err = IO_DONE; - while (total < count && (err == IO_DONE || err == IO_RETRY)) { + while (total < count && err == IO_DONE) { size_t done; - err = io->send(io->ctx, data+total, count-total, &done, tm_get(tm)); + err = io->send(io->ctx, data+total, count-total, &done, tm); total += done; } *sent = total; @@ -156,12 +147,10 @@ int sendraw(p_buf buf, const char *data, size_t count, size_t *sent) /*-------------------------------------------------------------------------*\ * Reads a fixed number of bytes (buffered) \*-------------------------------------------------------------------------*/ -static -int recvraw(p_buf buf, size_t wanted, luaL_Buffer *b) -{ - int err = IO_DONE; +static int recvraw(p_buf buf, size_t wanted, luaL_Buffer *b) { + int err = IO_DONE; size_t total = 0; - while (total < wanted && (err == IO_DONE || err == IO_RETRY)) { + while (total < wanted && err == IO_DONE) { size_t count; const char *data; err = buf_get(buf, &data, &count); count = MIN(count, wanted - total); @@ -175,11 +164,9 @@ int recvraw(p_buf buf, size_t wanted, luaL_Buffer *b) /*-------------------------------------------------------------------------*\ * Reads everything until the connection is closed (buffered) \*-------------------------------------------------------------------------*/ -static -int recvall(p_buf buf, luaL_Buffer *b) -{ +static int recvall(p_buf buf, luaL_Buffer *b) { int err = IO_DONE; - while (err == IO_DONE || err == IO_RETRY) { + while (err == IO_DONE) { const char *data; size_t count; err = buf_get(buf, &data, &count); luaL_addlstring(b, data, count); @@ -193,11 +180,9 @@ int recvall(p_buf buf, luaL_Buffer *b) * Reads a line terminated by a CR LF pair or just by a LF. The CR and LF * are not returned by the function and are discarded from the buffer \*-------------------------------------------------------------------------*/ -static -int recvline(p_buf buf, luaL_Buffer *b) -{ +static int recvline(p_buf buf, luaL_Buffer *b) { int err = IO_DONE; - while (err == IO_DONE || err == IO_RETRY) { + while (err == IO_DONE) { size_t count, pos; const char *data; err = buf_get(buf, &data, &count); pos = 0; @@ -219,9 +204,7 @@ int recvline(p_buf buf, luaL_Buffer *b) * Skips a given number of bytes from read buffer. No data is read from the * transport layer \*-------------------------------------------------------------------------*/ -static -void buf_skip(p_buf buf, size_t count) -{ +static void buf_skip(p_buf buf, size_t count) { buf->first += count; if (buf_isempty(buf)) buf->first = buf->last = 0; @@ -231,15 +214,13 @@ void buf_skip(p_buf buf, size_t count) * Return any data available in buffer, or get more data from transport layer * if buffer is empty \*-------------------------------------------------------------------------*/ -static -int buf_get(p_buf buf, const char **data, size_t *count) -{ +static int buf_get(p_buf buf, const char **data, size_t *count) { int err = IO_DONE; p_io io = buf->io; p_tm tm = buf->tm; if (buf_isempty(buf)) { size_t got; - err = io->recv(io->ctx, buf->data, BUF_SIZE, &got, tm_get(tm)); + err = io->recv(io->ctx, buf->data, BUF_SIZE, &got, tm); buf->first = 0; buf->last = got; } @@ -247,4 +228,3 @@ int buf_get(p_buf buf, const char **data, size_t *count) *data = buf->data + buf->first; return err; } - diff --git a/src/io.c b/src/io.c index 612454b..30595c7 100644 --- a/src/io.c +++ b/src/io.c @@ -12,34 +12,34 @@ /*-------------------------------------------------------------------------*\ * Initializes C structure \*-------------------------------------------------------------------------*/ -void io_init(p_io io, p_send send, p_recv recv, void *ctx) -{ +void io_init(p_io io, p_send send, p_recv recv, p_geterr geterr, void *ctx) { io->send = send; io->recv = recv; + io->geterr = geterr; io->ctx = ctx; } /*-------------------------------------------------------------------------*\ * Translate error codes to Lua \*-------------------------------------------------------------------------*/ -const char *io_strerror(int code) -{ +const char *io_strerror(int code) { switch (code) { case IO_DONE: return NULL; - case IO_TIMEOUT: return "timeout"; - case IO_RETRY: return "retry"; case IO_CLOSED: return "closed"; - case IO_REFUSED: return "refused"; + case IO_TIMEOUT: return "timeout"; + case IO_CLIPPED: return "clipped"; default: return "unknown error"; } } /*-------------------------------------------------------------------------*\ -* Translate error codes to Lua +* Push error message from code or from driver \*-------------------------------------------------------------------------*/ -void io_pusherror(lua_State *L, int code) +void io_pusherror(lua_State *L, p_io io, int code) { - const char *err = io_strerror(code); + const char *err = NULL; + if (code < IO_USER) err = io_strerror(code); + else err = io->geterr(io->ctx, code); if (err) lua_pushstring(L, err); else lua_pushnil(L); } diff --git a/src/io.h b/src/io.h index 495bdc6..72602dd 100644 --- a/src/io.h +++ b/src/io.h @@ -17,14 +17,15 @@ #include #include +#include "timeout.h" + /* IO error codes */ enum { IO_DONE, /* operation completed successfully */ - IO_RETRY, /* please try again */ IO_TIMEOUT, /* operation timed out */ IO_CLOSED, /* the connection has been closed */ - IO_REFUSED, /* transfer has been refused */ - IO_ERROR /* something else wrong... */ + IO_CLIPPED, /* maxium bytes count reached */ + IO_USER /* last element in enum is user custom error */ }; /* interface to send function */ @@ -33,7 +34,13 @@ typedef int (*p_send) ( const char *data, /* pointer to buffer with data to send */ size_t count, /* number of bytes to send from buffer */ size_t *sent, /* number of bytes sent uppon return */ - int timeout /* number of miliseconds left for transmission */ + p_tm tm /* timeout control */ +); + +/* returns an error string */ +typedef const char *(*p_geterr) ( + void *ctx, /* context needed by geterror */ + int code /* error code */ ); /* interface to recv function */ @@ -42,7 +49,7 @@ typedef int (*p_recv) ( char *data, /* pointer to buffer where data will be writen */ size_t count, /* number of bytes to receive into buffer */ size_t *got, /* number of bytes received uppon return */ - int timeout /* number of miliseconds left for transmission */ + p_tm tm /* timeout control */ ); /* IO driver definition */ @@ -50,11 +57,12 @@ typedef struct t_io_ { void *ctx; /* context needed by send/recv */ p_send send; /* send function pointer */ p_recv recv; /* receive function pointer */ + p_geterr geterr; /* receive function pointer */ } t_io; typedef t_io *p_io; const char *io_strerror(int code); -void io_pusherror(lua_State *L, int code); -void io_init(p_io io, p_send send, p_recv recv, void *ctx); +void io_pusherror(lua_State *L, p_io io, int code); +void io_init(p_io io, p_send send, p_recv recv, p_geterr geterr, void *ctx); #endif /* IO_H */ diff --git a/src/select.c b/src/select.c index 49730d1..3c01b06 100644 --- a/src/select.c +++ b/src/select.c @@ -10,6 +10,7 @@ #include #include "socket.h" +#include "timeout.h" #include "select.h" /*=========================================================================*\ @@ -48,19 +49,21 @@ int select_open(lua_State *L) { * Waits for a set of sockets until a condition is met or timeout. \*-------------------------------------------------------------------------*/ static int global_select(lua_State *L) { - int timeout, rtab, wtab, itab, max_fd, ret, ndirty; + int rtab, wtab, itab, max_fd, ret, ndirty; fd_set rset, wset; + t_tm tm; + double t = luaL_optnumber(L, 3, -1); FD_ZERO(&rset); FD_ZERO(&wset); lua_settop(L, 3); - timeout = lua_isnil(L, 3) ? -1 : (int)(luaL_checknumber(L, 3) * 1000); lua_newtable(L); itab = lua_gettop(L); lua_newtable(L); rtab = lua_gettop(L); lua_newtable(L); wtab = lua_gettop(L); max_fd = collect_fd(L, 1, -1, itab, &rset); ndirty = check_dirty(L, 1, rtab, &rset); - timeout = ndirty > 0? 0: timeout; + t = ndirty > 0? 0.0: t; + tm_init(&tm, t, -1); max_fd = collect_fd(L, 2, max_fd, itab, &wset); - ret = sock_select(max_fd+1, &rset, &wset, NULL, timeout); + ret = sock_select(max_fd+1, &rset, &wset, NULL, &tm); if (ret > 0 || (ret == 0 && ndirty > 0)) { return_fd(L, &rset, max_fd+1, itab, rtab, ndirty); return_fd(L, &wset, max_fd+1, itab, wtab, 0); diff --git a/src/socket.h b/src/socket.h index 5da1ccc..787b7a5 100644 --- a/src/socket.h +++ b/src/socket.h @@ -41,17 +41,15 @@ int sock_open(void); int sock_close(void); void sock_destroy(p_sock ps); void sock_shutdown(p_sock ps, int how); -int sock_send(p_sock ps, const char *data, size_t count, - size_t *sent, int timeout); -int sock_recv(p_sock ps, char *data, size_t count, - size_t *got, int timeout); +int sock_send(p_sock ps, const char *data, size_t count, size_t *sent, p_tm tm); +int sock_recv(p_sock ps, char *data, size_t count, size_t *got, p_tm tm); int sock_sendto(p_sock ps, const char *data, size_t count, - size_t *sent, SA *addr, socklen_t addr_len, int timeout); + size_t *sent, SA *addr, socklen_t addr_len, p_tm tm); int sock_recvfrom(p_sock ps, char *data, size_t count, - size_t *got, SA *addr, socklen_t *addr_len, int timeout); + size_t *got, SA *addr, socklen_t *addr_len, p_tm tm); void sock_setnonblocking(p_sock ps); void sock_setblocking(p_sock ps); -int sock_select(int n, fd_set *rfds, fd_set *wfds, fd_set *efds, int timeout); +int sock_select(int n, fd_set *rfds, fd_set *wfds, fd_set *efds, p_tm tm); const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len, p_tm tm); const char *sock_create(p_sock ps, int domain, int type, int protocol); @@ -60,6 +58,8 @@ const char *sock_listen(p_sock ps, int backlog); const char *sock_accept(p_sock ps, p_sock pa, SA *addr, socklen_t *addr_len, p_tm tm); +const char *sock_geterr(p_sock ps, int code); const char *sock_hoststrerror(void); +const char *sock_strerror(void); #endif /* SOCK_H */ diff --git a/src/tcp.c b/src/tcp.c index 6c58494..8ab7d62 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -163,7 +163,8 @@ static int meth_accept(lua_State *L) /* initialize structure fields */ sock_setnonblocking(&sock); clnt->sock = sock; - io_init(&clnt->io, (p_send)sock_send, (p_recv)sock_recv, &clnt->sock); + io_init(&clnt->io, (p_send) sock_send, (p_recv) sock_recv, + (p_geterr) sock_geterr, &clnt->sock); tm_init(&clnt->tm, -1, -1); buf_init(&clnt->buf, &clnt->io, &clnt->tm); return 1; @@ -313,7 +314,8 @@ static int global_create(lua_State *L) /* initialize remaining structure fields */ sock_setnonblocking(&sock); tcp->sock = sock; - io_init(&tcp->io, (p_send) sock_send, (p_recv) sock_recv, &tcp->sock); + io_init(&tcp->io, (p_send) sock_send, (p_recv) sock_recv, + (p_geterr) sock_geterr, &tcp->sock); tm_init(&tcp->tm, -1, -1); buf_init(&tcp->buf, &tcp->io, &tcp->tm); return 1; diff --git a/src/timeout.c b/src/timeout.c index dcc2105..74ba968 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -46,8 +46,7 @@ static luaL_reg func[] = { /*-------------------------------------------------------------------------*\ * Initialize structure \*-------------------------------------------------------------------------*/ -void tm_init(p_tm tm, int block, int total) -{ +void tm_init(p_tm tm, double block, double total) { tm->block = block; tm->total = total; } @@ -60,18 +59,17 @@ void tm_init(p_tm tm, int block, int total) * Returns * the number of ms left or -1 if there is no time limit \*-------------------------------------------------------------------------*/ -int tm_get(p_tm tm) -{ - if (tm->block < 0 && tm->total < 0) { +double tm_get(p_tm tm) { + if (tm->block < 0.0 && tm->total < 0.0) { return -1; - } else if (tm->block < 0) { - int t = tm->total - tm_gettime() + tm->start; - return MAX(t, 0); - } else if (tm->total < 0) { + } else if (tm->block < 0.0) { + double t = tm->total - tm_gettime() + tm->start; + return MAX(t, 0.0); + } else if (tm->total < 0.0) { return tm->block; } else { - int t = tm->total - tm_gettime() + tm->start; - return MIN(tm->block, MAX(t, 0)); + double t = tm->total - tm_gettime() + tm->start; + return MIN(tm->block, MAX(t, 0.0)); } } @@ -82,8 +80,7 @@ int tm_get(p_tm tm) * Returns * start field of structure \*-------------------------------------------------------------------------*/ -int tm_getstart(p_tm tm) -{ +double tm_getstart(p_tm tm) { return tm->start; } @@ -95,19 +92,18 @@ int tm_getstart(p_tm tm) * Returns * the number of ms left or -1 if there is no time limit \*-------------------------------------------------------------------------*/ -int tm_getretry(p_tm tm) -{ - if (tm->block < 0 && tm->total < 0) { +double tm_getretry(p_tm tm) { + if (tm->block < 0.0 && tm->total < 0.0) { return -1; - } else if (tm->block < 0) { - int t = tm->total - tm_gettime() + tm->start; - return MAX(t, 0); - } else if (tm->total < 0) { - int t = tm->block - tm_gettime() + tm->start; - return MAX(t, 0); + } else if (tm->block < 0.0) { + double t = tm->total - tm_gettime() + tm->start; + return MAX(t, 0.0); + } else if (tm->total < 0.0) { + double t = tm->block - tm_gettime() + tm->start; + return MAX(t, 0.0); } else { - int t = tm->total - tm_gettime() + tm->start; - return MIN(tm->block, MAX(t, 0)); + double t = tm->total - tm_gettime() + tm->start; + return MIN(tm->block, MAX(t, 0.0)); } } @@ -116,8 +112,7 @@ int tm_getretry(p_tm tm) * Input * tm: timeout control structure \*-------------------------------------------------------------------------*/ -p_tm tm_markstart(p_tm tm) -{ +p_tm tm_markstart(p_tm tm) { tm->start = tm_gettime(); return tm; } @@ -128,24 +123,23 @@ p_tm tm_markstart(p_tm tm) * time in ms. \*-------------------------------------------------------------------------*/ #ifdef _WIN32 -int tm_gettime(void) -{ - return GetTickCount(); +double tm_gettime(void) { + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + return ft.dwLowDateTime/1.0e7 + ft.dwHighDateTime*(4294967296.0/1.0e7); } #else -int tm_gettime(void) -{ +double tm_gettime(void) { struct timeval v; gettimeofday(&v, (struct timezone *) NULL); - return v.tv_sec * 1000 + v.tv_usec/1000; + return v.tv_sec + v.tv_usec/1.0e6; } #endif /*-------------------------------------------------------------------------*\ * Initializes module \*-------------------------------------------------------------------------*/ -int tm_open(lua_State *L) -{ +int tm_open(lua_State *L) { luaL_openlib(L, NULL, func, 0); return 0; } @@ -156,16 +150,15 @@ int tm_open(lua_State *L) * time: time out value in seconds * mode: "b" for block timeout, "t" for total timeout. (default: b) \*-------------------------------------------------------------------------*/ -int tm_meth_settimeout(lua_State *L, p_tm tm) -{ - int ms = lua_isnil(L, 2) ? -1 : (int) (luaL_checknumber(L, 2)*1000.0); +int tm_meth_settimeout(lua_State *L, p_tm tm) { + double t = luaL_optnumber(L, 2, -1); const char *mode = luaL_optstring(L, 3, "b"); switch (*mode) { case 'b': - tm->block = ms; + tm->block = t; break; case 'r': case 't': - tm->total = ms; + tm->total = t; break; default: luaL_argcheck(L, 0, 3, "invalid timeout mode"); @@ -183,7 +176,7 @@ int tm_meth_settimeout(lua_State *L, p_tm tm) \*-------------------------------------------------------------------------*/ static int tm_lua_gettime(lua_State *L) { - lua_pushnumber(L, tm_gettime()/1000.0); + lua_pushnumber(L, tm_gettime()); return 1; } diff --git a/src/timeout.h b/src/timeout.h index 6b105c3..817922f 100644 --- a/src/timeout.h +++ b/src/timeout.h @@ -10,19 +10,19 @@ /* timeout control structure */ typedef struct t_tm_ { - int total; /* total number of miliseconds for operation */ - int block; /* maximum time for blocking calls */ - int start; /* time of start of operation */ + double total; /* total number of miliseconds for operation */ + double block; /* maximum time for blocking calls */ + double start; /* time of start of operation */ } t_tm; typedef t_tm *p_tm; int tm_open(lua_State *L); -void tm_init(p_tm tm, int block, int total); -int tm_get(p_tm tm); -int tm_getretry(p_tm tm); +void tm_init(p_tm tm, double block, double total); +double tm_get(p_tm tm); +double tm_getretry(p_tm tm); p_tm tm_markstart(p_tm tm); -int tm_getstart(p_tm tm); -int tm_gettime(void); +double tm_getstart(p_tm tm); +double tm_gettime(void); int tm_meth_settimeout(lua_State *L, p_tm tm); #endif /* TM_H */ diff --git a/src/udp.c b/src/udp.c index d20d61b..a71be73 100644 --- a/src/udp.c +++ b/src/udp.c @@ -41,25 +41,26 @@ static int meth_settimeout(lua_State *L); static int meth_getfd(lua_State *L); static int meth_setfd(lua_State *L); static int meth_dirty(lua_State *L); +static void pusherror(lua_State *L, int code); /* udp object methods */ static luaL_reg udp[] = { - {"setpeername", meth_setpeername}, - {"setsockname", meth_setsockname}, - {"getsockname", meth_getsockname}, - {"getpeername", meth_getpeername}, - {"send", meth_send}, - {"sendto", meth_sendto}, - {"receive", meth_receive}, - {"receivefrom", meth_receivefrom}, - {"settimeout", meth_settimeout}, - {"close", meth_close}, - {"setoption", meth_setoption}, {"__gc", meth_close}, {"__tostring", aux_tostring}, - {"getfd", meth_getfd}, - {"setfd", meth_setfd}, + {"close", meth_close}, {"dirty", meth_dirty}, + {"getfd", meth_getfd}, + {"getpeername", meth_getpeername}, + {"getsockname", meth_getsockname}, + {"receive", meth_receive}, + {"receivefrom", meth_receivefrom}, + {"send", meth_send}, + {"sendto", meth_sendto}, + {"setfd", meth_setfd}, + {"setoption", meth_setoption}, + {"setpeername", meth_setpeername}, + {"setsockname", meth_setsockname}, + {"settimeout", meth_settimeout}, {NULL, NULL} }; @@ -99,35 +100,43 @@ int udp_open(lua_State *L) return 0; } + /*=========================================================================*\ * Lua methods \*=========================================================================*/ +/*-------------------------------------------------------------------------*\ +* Pushes the error message +\*-------------------------------------------------------------------------*/ +void pusherror(lua_State *L, int code) { + const char *err = code != IO_USER? io_strerror(code): "refused"; + err = err? err: sock_strerror(); + if (err) lua_pushstring(L, err); + else lua_pushnil(L); +} + /*-------------------------------------------------------------------------*\ * Send data through connected udp socket \*-------------------------------------------------------------------------*/ -static int meth_send(lua_State *L) -{ +static int meth_send(lua_State *L) { p_udp udp = (p_udp) aux_checkclass(L, "udp{connected}", 1); p_tm tm = &udp->tm; size_t count, sent = 0; int err; const char *data = luaL_checklstring(L, 2, &count); tm_markstart(tm); - do err = sock_send(&udp->sock, data, count, &sent, tm_getretry(tm)); - while (err == IO_RETRY); + err = sock_send(&udp->sock, data, count, &sent, tm); if (err == IO_DONE) lua_pushnumber(L, sent); else lua_pushnil(L); /* a 'closed' error on an unconnected means the target address was not * accepted by the transport layer */ - io_pusherror(L, err == IO_CLOSED ? IO_REFUSED : err); + pusherror(L, err == IO_CLOSED ? IO_USER : err); return 2; } /*-------------------------------------------------------------------------*\ * Send data through unconnected udp socket \*-------------------------------------------------------------------------*/ -static int meth_sendto(lua_State *L) -{ +static int meth_sendto(lua_State *L) { p_udp udp = (p_udp) aux_checkclass(L, "udp{unconnected}", 1); size_t count, sent = 0; const char *data = luaL_checklstring(L, 2, &count); @@ -142,22 +151,20 @@ static int meth_sendto(lua_State *L) addr.sin_family = AF_INET; addr.sin_port = htons(port); tm_markstart(tm); - do err = sock_sendto(&udp->sock, data, count, &sent, - (SA *) &addr, sizeof(addr), tm_get(tm)); - while (err == IO_RETRY); + err = sock_sendto(&udp->sock, data, count, &sent, + (SA *) &addr, sizeof(addr), tm); if (err == IO_DONE) lua_pushnumber(L, sent); else lua_pushnil(L); /* a 'closed' error on an unconnected means the target address was not * accepted by the transport layer */ - io_pusherror(L, err == IO_CLOSED ? IO_REFUSED : err); + pusherror(L, err == IO_CLOSED ? IO_USER : err); return 2; } /*-------------------------------------------------------------------------*\ * Receives data from a UDP socket \*-------------------------------------------------------------------------*/ -static int meth_receive(lua_State *L) -{ +static int meth_receive(lua_State *L) { p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1); char buffer[UDP_DATAGRAMSIZE]; size_t got, count = (size_t) luaL_optnumber(L, 2, sizeof(buffer)); @@ -165,19 +172,17 @@ static int meth_receive(lua_State *L) p_tm tm = &udp->tm; count = MIN(count, sizeof(buffer)); tm_markstart(tm); - do err = sock_recv(&udp->sock, buffer, count, &got, tm_get(tm)); - while (err == IO_RETRY); + err = sock_recv(&udp->sock, buffer, count, &got, tm); if (err == IO_DONE) lua_pushlstring(L, buffer, got); else lua_pushnil(L); - io_pusherror(L, err); + pusherror(L, err); return 2; } /*-------------------------------------------------------------------------*\ * Receives data and sender from a UDP socket \*-------------------------------------------------------------------------*/ -static int meth_receivefrom(lua_State *L) -{ +static int meth_receivefrom(lua_State *L) { p_udp udp = (p_udp) aux_checkclass(L, "udp{unconnected}", 1); struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); @@ -187,9 +192,8 @@ static int meth_receivefrom(lua_State *L) p_tm tm = &udp->tm; tm_markstart(tm); count = MIN(count, sizeof(buffer)); - do err = sock_recvfrom(&udp->sock, buffer, count, &got, - (SA *) &addr, &addr_len, tm_get(tm)); - while (err == IO_RETRY); + err = sock_recvfrom(&udp->sock, buffer, count, &got, + (SA *) &addr, &addr_len, tm); if (err == IO_DONE) { lua_pushlstring(L, buffer, got); lua_pushstring(L, inet_ntoa(addr.sin_addr)); @@ -197,7 +201,7 @@ static int meth_receivefrom(lua_State *L) return 3; } else { lua_pushnil(L); - io_pusherror(L, err); + pusherror(L, err); return 2; } } @@ -205,23 +209,20 @@ static int meth_receivefrom(lua_State *L) /*-------------------------------------------------------------------------*\ * Select support methods \*-------------------------------------------------------------------------*/ -static int meth_getfd(lua_State *L) -{ +static int meth_getfd(lua_State *L) { p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1); lua_pushnumber(L, (int) udp->sock); return 1; } /* this is very dangerous, but can be handy for those that are brave enough */ -static int meth_setfd(lua_State *L) -{ +static int meth_setfd(lua_State *L) { p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1); udp->sock = (t_sock) luaL_checknumber(L, 2); return 0; } -static int meth_dirty(lua_State *L) -{ +static int meth_dirty(lua_State *L) { p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1); (void) udp; lua_pushboolean(L, 0); @@ -231,14 +232,12 @@ static int meth_dirty(lua_State *L) /*-------------------------------------------------------------------------*\ * Just call inet methods \*-------------------------------------------------------------------------*/ -static int meth_getpeername(lua_State *L) -{ +static int meth_getpeername(lua_State *L) { p_udp udp = (p_udp) aux_checkclass(L, "udp{connected}", 1); return inet_meth_getpeername(L, &udp->sock); } -static int meth_getsockname(lua_State *L) -{ +static int meth_getsockname(lua_State *L) { p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1); return inet_meth_getsockname(L, &udp->sock); } @@ -246,8 +245,7 @@ static int meth_getsockname(lua_State *L) /*-------------------------------------------------------------------------*\ * Just call option handler \*-------------------------------------------------------------------------*/ -static int meth_setoption(lua_State *L) -{ +static int meth_setoption(lua_State *L) { p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1); return opt_meth_setoption(L, opt, &udp->sock); } @@ -255,8 +253,7 @@ static int meth_setoption(lua_State *L) /*-------------------------------------------------------------------------*\ * Just call tm methods \*-------------------------------------------------------------------------*/ -static int meth_settimeout(lua_State *L) -{ +static int meth_settimeout(lua_State *L) { p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1); return tm_meth_settimeout(L, &udp->tm); } @@ -264,8 +261,7 @@ static int meth_settimeout(lua_State *L) /*-------------------------------------------------------------------------*\ * Turns a master udp object into a client object. \*-------------------------------------------------------------------------*/ -static int meth_setpeername(lua_State *L) -{ +static int meth_setpeername(lua_State *L) { p_udp udp = (p_udp) aux_checkclass(L, "udp{unconnected}", 1); p_tm tm = &udp->tm; const char *address = luaL_checkstring(L, 2); @@ -289,8 +285,7 @@ static int meth_setpeername(lua_State *L) /*-------------------------------------------------------------------------*\ * Closes socket used by object \*-------------------------------------------------------------------------*/ -static int meth_close(lua_State *L) -{ +static int meth_close(lua_State *L) { p_udp udp = (p_udp) aux_checkgroup(L, "udp{any}", 1); sock_destroy(&udp->sock); return 0; @@ -299,8 +294,7 @@ static int meth_close(lua_State *L) /*-------------------------------------------------------------------------*\ * Turns a master object into a server object \*-------------------------------------------------------------------------*/ -static int meth_setsockname(lua_State *L) -{ +static int meth_setsockname(lua_State *L) { p_udp udp = (p_udp) aux_checkclass(L, "udp{unconnected}", 1); const char *address = luaL_checkstring(L, 2); unsigned short port = (unsigned short) luaL_checknumber(L, 3); @@ -320,8 +314,7 @@ static int meth_setsockname(lua_State *L) /*-------------------------------------------------------------------------*\ * Creates a master udp object \*-------------------------------------------------------------------------*/ -static int global_create(lua_State *L) -{ +static int global_create(lua_State *L) { t_sock sock; const char *err = inet_trycreate(&sock, SOCK_DGRAM); /* try to allocate a system socket */ diff --git a/src/usocket.c b/src/usocket.c index cf0458d..12a13a5 100644 --- a/src/usocket.c +++ b/src/usocket.c @@ -20,17 +20,10 @@ #include "socket.h" -static const char *sock_createstrerror(int err); -static const char *sock_bindstrerror(int err); -static const char *sock_connectstrerror(int err); -static const char *sock_acceptstrerror(int err); -static const char *sock_listenstrerror(int err); - /*-------------------------------------------------------------------------*\ * Initializes module \*-------------------------------------------------------------------------*/ -int sock_open(void) -{ +int sock_open(void) { #if DOESNT_COMPILE_TRY_THIS struct sigaction ignore; memset(&ignore, 0, sizeof(ignore)); @@ -45,16 +38,14 @@ int sock_open(void) /*-------------------------------------------------------------------------*\ * Close module \*-------------------------------------------------------------------------*/ -int sock_close(void) -{ +int sock_close(void) { return 1; } /*-------------------------------------------------------------------------*\ * Close and inutilize socket \*-------------------------------------------------------------------------*/ -void sock_destroy(p_sock ps) -{ +void sock_destroy(p_sock ps) { if (*ps != SOCK_INVALID) { sock_setblocking(ps); close(*ps); @@ -63,23 +54,26 @@ void sock_destroy(p_sock ps) } /*-------------------------------------------------------------------------*\ -* Select with int timeout in ms +* Select with timeout control \*-------------------------------------------------------------------------*/ -int sock_select(int n, fd_set *rfds, fd_set *wfds, fd_set *efds, int timeout) -{ - struct timeval tv; - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - return select(n, rfds, wfds, efds, timeout >= 0? &tv: NULL); +int sock_select(int n, fd_set *rfds, fd_set *wfds, fd_set *efds, p_tm tm) { + int ret; + do { + struct timeval tv; + double t = tm_getretry(tm); + tv.tv_sec = (int) t; + tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6); + ret = select(n, rfds, wfds, efds, t >= 0.0? &tv: NULL); + } while (ret < 0 && errno == EINTR); + return ret; } /*-------------------------------------------------------------------------*\ * Creates and sets up a socket \*-------------------------------------------------------------------------*/ -const char *sock_create(p_sock ps, int domain, int type, int protocol) -{ +const char *sock_create(p_sock ps, int domain, int type, int protocol) { t_sock sock = socket(domain, type, protocol); - if (sock == SOCK_INVALID) return sock_createstrerror(errno); + if (sock == SOCK_INVALID) return sock_strerror(); *ps = sock; return NULL; } @@ -87,50 +81,49 @@ const char *sock_create(p_sock ps, int domain, int type, int protocol) /*-------------------------------------------------------------------------*\ * Connects or returns error message \*-------------------------------------------------------------------------*/ -const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len, p_tm tm) -{ +const char *sock_connect(p_sock ps, SA *addr, socklen_t addr_len, p_tm tm) { t_sock sock = *ps; int err; /* don't call on closed socket */ - if (sock == SOCK_INVALID) return io_strerror(IO_CLOSED); + if (sock == SOCK_INVALID) return io_strerror(IO_CLOSED); /* ask system to connect */ do err = connect(sock, addr, addr_len); while (err < 0 && errno == EINTR); /* if no error, we're done */ if (err == 0) return NULL; /* make sure the system is trying to connect */ - if (errno != EINPROGRESS) return sock_connectstrerror(errno); + if (errno != EINPROGRESS) return sock_strerror(); + /* optimize for timeout = 0 */ + if (tm_get(tm) == 0.0) return io_strerror(IO_TIMEOUT); /* wait for a timeout or for the system's answer */ for ( ;; ) { - fd_set rfds, wfds, efds; + fd_set rfds, wfds; FD_ZERO(&rfds); FD_SET(sock, &rfds); FD_ZERO(&wfds); FD_SET(sock, &wfds); - FD_ZERO(&efds); FD_SET(sock, &efds); /* we run select to avoid busy waiting */ - do err = sock_select(sock+1, &rfds, &wfds, &efds, tm_getretry(tm)); - while (err < 0 && errno == EINTR); - /* if selects readable, try reading */ + err = sock_select(sock+1, &rfds, &wfds, NULL, tm); + /* if there was an event, check what happened */ if (err > 0) { char dummy; /* recv will set errno to the value a blocking connect would set */ - if (recv(sock, &dummy, 0, 0) < 0 && errno != EAGAIN) - return sock_connectstrerror(errno); + if (err > 1 && FD_ISSET(sock, &rfds) && + recv(sock, &dummy, 0, 0) < 0 && errno != EAGAIN) + return sock_strerror(); else return NULL; /* if no event happened, there was a timeout */ - } else return io_strerror(IO_TIMEOUT); + } else if (err == 0) return io_strerror(IO_TIMEOUT); } - return io_strerror(IO_TIMEOUT); /* can't get here */ + return sock_strerror(); } /*-------------------------------------------------------------------------*\ * Binds or returns error message \*-------------------------------------------------------------------------*/ -const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len) -{ +const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len) { const char *err = NULL; sock_setblocking(ps); - if (bind(*ps, addr, addr_len) < 0) err = sock_bindstrerror(errno); + if (bind(*ps, addr, addr_len) < 0) err = sock_strerror(); sock_setnonblocking(ps); return err; } @@ -138,12 +131,10 @@ const char *sock_bind(p_sock ps, SA *addr, socklen_t addr_len) /*-------------------------------------------------------------------------*\ * \*-------------------------------------------------------------------------*/ -const char* sock_listen(p_sock ps, int backlog) -{ +const char* sock_listen(p_sock ps, int backlog) { const char *err = NULL; sock_setblocking(ps); - if (listen(*ps, backlog)) - err = sock_listenstrerror(errno); + if (listen(*ps, backlog)) err = sock_strerror(); sock_setnonblocking(ps); return err; } @@ -151,8 +142,7 @@ const char* sock_listen(p_sock ps, int backlog) /*-------------------------------------------------------------------------*\ * \*-------------------------------------------------------------------------*/ -void sock_shutdown(p_sock ps, int how) -{ +void sock_shutdown(p_sock ps, int how) { sock_setblocking(ps); shutdown(*ps, how); sock_setnonblocking(ps); @@ -162,12 +152,11 @@ void sock_shutdown(p_sock ps, int how) * Accept with timeout \*-------------------------------------------------------------------------*/ const char *sock_accept(p_sock ps, p_sock pa, SA *addr, - socklen_t *addr_len, p_tm tm) -{ + socklen_t *addr_len, p_tm tm) { t_sock sock = *ps; SA dummy_addr; socklen_t dummy_len = sizeof(dummy_addr); - if (sock == SOCK_INVALID) return io_strerror(IO_CLOSED); + if (sock == SOCK_INVALID) return io_strerror(IO_CLOSED); if (!addr) addr = &dummy_addr; if (!addr_len) addr_len = &dummy_len; for (;;) { @@ -179,14 +168,16 @@ const char *sock_accept(p_sock ps, p_sock pa, SA *addr, /* if result is valid, we are done */ if (*pa != SOCK_INVALID) return NULL; /* find out if we failed for a fatal reason */ - if (errno != EAGAIN && errno != ECONNABORTED) - return sock_acceptstrerror(errno); + /* if connection was aborted, we can try again if we have time */ + if (errno != EAGAIN && errno != ECONNABORTED) return sock_strerror(); + /* optimize for timeout = 0 case */ + if (tm_get(tm) == 0.0) return io_strerror(IO_TIMEOUT); /* call select to avoid busy-wait. */ FD_ZERO(&fds); FD_SET(sock, &fds); - do err = sock_select(sock+1, &fds, NULL, NULL, tm_getretry(tm)); - while (err < 0 && errno == EINTR); + err = sock_select(sock+1, &fds, NULL, NULL, tm); if (err == 0) return io_strerror(IO_TIMEOUT); + else if (err < 0) return sock_strerror(); } return io_strerror(IO_TIMEOUT); /* can't get here */ } @@ -194,136 +185,136 @@ const char *sock_accept(p_sock ps, p_sock pa, SA *addr, /*-------------------------------------------------------------------------*\ * Send with timeout \*-------------------------------------------------------------------------*/ -int sock_send(p_sock ps, const char *data, size_t count, size_t *sent, - int timeout) +int sock_send(p_sock ps, const char *data, size_t count, size_t *sent, p_tm tm) { t_sock sock = *ps; - ssize_t put; /* avoid making system calls on closed sockets */ if (sock == SOCK_INVALID) return IO_CLOSED; - /* make sure we repeat in case the call was interrupted */ - do put = send(sock, data, count, 0); - while (put < 0 && errno == EINTR); - /* deal with failure */ - if (put <= 0) { + /* loop until we send something or we give up on error */ + for ( ;; ) { int ret; fd_set fds; - /* in any case, nothing has been sent */ + ssize_t put; + /* make sure we repeat in case the call was interrupted */ + do put = send(sock, data, count, 0); + while (put < 0 && errno == EINTR); + /* if we sent something, get out */ + if (put > 0) { + *sent = put; + return IO_DONE; + } + /* deal with failure */ *sent = 0; - /* only proceed to select if no error happened */ - if (errno != EAGAIN) return IO_ERROR; - /* optimize for the timeout = 0 case */ - if (timeout == 0) return IO_TIMEOUT; /* here we know the connection has been closed */ - if (errno == EPIPE) return IO_CLOSED; + if (put < 0 && errno == EPIPE) return IO_CLOSED; + /* send shouldn't return zero and we can only proceed if + * there was no serious error */ + if (put == 0 || errno != EAGAIN) return IO_USER; + /* optimize for the timeout = 0 case */ + if (tm_get(tm) == 0.0) return IO_TIMEOUT; /* run select to avoid busy wait */ FD_ZERO(&fds); FD_SET(sock, &fds); - ret = sock_select(sock+1, NULL, &fds, NULL, timeout); + ret = sock_select(sock+1, NULL, &fds, NULL, tm); if (ret == 0) return IO_TIMEOUT; - else if (ret > 0 || errno == EINTR) return IO_RETRY; - else return IO_ERROR; - /* here we successfully sent something */ - } else { - *sent = put; - return IO_DONE; - } + if (ret < 0) return IO_USER; + /* otherwise, try sending again */ + } } /*-------------------------------------------------------------------------*\ * Sendto with timeout \*-------------------------------------------------------------------------*/ int sock_sendto(p_sock ps, const char *data, size_t count, size_t *sent, - SA *addr, socklen_t addr_len, int timeout) + SA *addr, socklen_t addr_len, p_tm tm) { t_sock sock = *ps; - ssize_t put; + /* avoid making system calls on closed sockets */ if (sock == SOCK_INVALID) return IO_CLOSED; - do put = sendto(sock, data, count, 0, addr, addr_len); - while (put < 0 && errno == EINTR); - if (put <= 0) { + /* loop until we send something or we give up on error */ + for ( ;; ) { int ret; fd_set fds; + ssize_t put; + do put = sendto(sock, data, count, 0, addr, addr_len); + while (put < 0 && errno == EINTR); + if (put > 0) { + *sent = put; + return IO_DONE; + } *sent = 0; - if (errno != EAGAIN) return IO_ERROR; - if (timeout == 0) return IO_TIMEOUT; - if (errno == EPIPE) return IO_CLOSED; + if (put < 0 && errno == EPIPE) return IO_CLOSED; + if (put == 0 || errno != EAGAIN) return IO_USER; + if (tm_get(tm) == 0.0) return IO_TIMEOUT; FD_ZERO(&fds); FD_SET(sock, &fds); - ret = sock_select(sock+1, NULL, &fds, NULL, timeout); + ret = sock_select(sock+1, NULL, &fds, NULL, tm); if (ret == 0) return IO_TIMEOUT; - else if (ret > 0 || errno == EINTR) return IO_RETRY; - else return IO_ERROR; - } else { - *sent = put; - return IO_DONE; - } + if (ret < 0) return IO_USER; + } } /*-------------------------------------------------------------------------*\ * Receive with timeout \*-------------------------------------------------------------------------*/ -int sock_recv(p_sock ps, char *data, size_t count, size_t *got, int timeout) -{ +int sock_recv(p_sock ps, char *data, size_t count, size_t *got, p_tm tm) { t_sock sock = *ps; - ssize_t taken; if (sock == SOCK_INVALID) return IO_CLOSED; - do taken = read(sock, data, count); - while (taken < 0 && errno == EINTR); - if (taken <= 0) { + for ( ;; ) { fd_set fds; int ret; + ssize_t taken; + do taken = read(sock, data, count); + while (taken < 0 && errno == EINTR); + if (taken > 0) { + *got = taken; + return IO_DONE; + } *got = 0; if (taken == 0) return IO_CLOSED; - if (errno != EAGAIN) return IO_ERROR; - if (timeout == 0) return IO_TIMEOUT; + if (errno != EAGAIN) return IO_USER; + if (tm_get(tm) == 0.0) return IO_TIMEOUT; FD_ZERO(&fds); FD_SET(sock, &fds); - ret = sock_select(sock+1, &fds, NULL, NULL, timeout); + ret = sock_select(sock+1, &fds, NULL, NULL, tm); if (ret == 0) return IO_TIMEOUT; - else if (ret > 0 || errno == EINTR) return IO_RETRY; - else return IO_ERROR; - } else { - *got = taken; - return IO_DONE; - } + if (ret < 0) return IO_USER; + } } /*-------------------------------------------------------------------------*\ * Recvfrom with timeout \*-------------------------------------------------------------------------*/ int sock_recvfrom(p_sock ps, char *data, size_t count, size_t *got, - SA *addr, socklen_t *addr_len, int timeout) -{ + SA *addr, socklen_t *addr_len, p_tm tm) { t_sock sock = *ps; - ssize_t taken; if (sock == SOCK_INVALID) return IO_CLOSED; - do taken = recvfrom(sock, data, count, 0, addr, addr_len); - while (taken < 0 && errno == EINTR); - if (taken <= 0) { + for ( ;; ) { fd_set fds; int ret; + ssize_t taken; + do taken = recvfrom(sock, data, count, 0, addr, addr_len); + while (taken < 0 && errno == EINTR); + if (taken > 0) { + *got = taken; + return IO_DONE; + } *got = 0; if (taken == 0) return IO_CLOSED; - if (errno != EAGAIN) return IO_ERROR; - if (timeout == 0) return IO_TIMEOUT; + if (errno != EAGAIN) return IO_USER; + if (tm_get(tm) == 0.0) return IO_TIMEOUT; FD_ZERO(&fds); FD_SET(sock, &fds); - ret = sock_select(sock+1, &fds, NULL, NULL, timeout); + ret = sock_select(sock+1, &fds, NULL, NULL, tm); if (ret == 0) return IO_TIMEOUT; - else if (ret > 0 || errno == EINTR) return IO_RETRY; - else return IO_ERROR; - } else { - *got = taken; - return IO_DONE; - } + if (ret < 0) return IO_USER; + } } /*-------------------------------------------------------------------------*\ * Put socket into blocking mode \*-------------------------------------------------------------------------*/ -void sock_setblocking(p_sock ps) -{ +void sock_setblocking(p_sock ps) { int flags = fcntl(*ps, F_GETFL, 0); flags &= (~(O_NONBLOCK)); fcntl(*ps, F_SETFL, flags); @@ -332,8 +323,7 @@ void sock_setblocking(p_sock ps) /*-------------------------------------------------------------------------*\ * Put socket into non-blocking mode \*-------------------------------------------------------------------------*/ -void sock_setnonblocking(p_sock ps) -{ +void sock_setnonblocking(p_sock ps) { int flags = fcntl(*ps, F_GETFL, 0); flags |= O_NONBLOCK; fcntl(*ps, F_SETFL, flags); @@ -342,98 +332,20 @@ void sock_setnonblocking(p_sock ps) /*-------------------------------------------------------------------------*\ * Error translation functions \*-------------------------------------------------------------------------*/ -/* return error messages for the known errors reported by gethostbyname */ -const char *sock_hoststrerror(void) -{ - switch (h_errno) { - case HOST_NOT_FOUND: return "host not found"; - case NO_ADDRESS: return "valid host but no ip found"; - case NO_RECOVERY: return "name server error"; - case TRY_AGAIN: return "name server unavailable, try again later"; - default: return "unknown error"; +const char *sock_hoststrerror(void) { + return hstrerror(h_errno); +} + +/* make sure important error messages are standard */ +const char *sock_strerror(void) { + switch (errno) { + case EADDRINUSE: + return "address already in use"; + default: + return strerror(errno); } } -/* return error messages for the known errors reported by socket */ -static const char *sock_createstrerror(int err) -{ - switch (err) { - case EPROTONOSUPPORT: return "protocol not supported"; - case EACCES: return "access denied"; - case EMFILE: return "process file table is full"; - case ENFILE: return "kernel file table is full"; - case EINVAL: return "unknown protocol or family"; - case ENOBUFS: return "insuffucient buffer space"; - default: return "unknown error"; - } -} - -/* return error messages for the known errors reported by accept */ -static const char *sock_acceptstrerror(int err) -{ - switch (err) { - case EAGAIN: return io_strerror(IO_RETRY); - case EBADF: return "invalid descriptor"; - case ENOBUFS: case ENOMEM: return "insuffucient buffer space"; - case ENOTSOCK: return "descriptor not a socket"; - case EOPNOTSUPP: return "not supported"; - case EINTR: return "call interrupted"; - case ECONNABORTED: return "connection aborted"; - case EINVAL: return "not listening"; - case EMFILE: return "process file table is full"; - case ENFILE: return "kernel file table is full"; - case EFAULT: return "invalid memory address"; - default: return "unknown error"; - } -} - - -/* return error messages for the known errors reported by bind */ -static const char *sock_bindstrerror(int err) -{ - switch (err) { - case EBADF: return "invalid descriptor"; - case ENOTSOCK: return "descriptor not a socket"; - case EADDRNOTAVAIL: return "address unavailable in local host"; - case EADDRINUSE: return "address already in use"; - case EINVAL: return "already bound"; - case EACCES: return "access denied"; - case EFAULT: return "invalid memory address"; - case ENOMEM: return "out of memory"; - default: return "unknown error"; - } -} - -/* return error messages for the known errors reported by listen */ -static const char *sock_listenstrerror(int err) -{ - switch (err) { - case EADDRINUSE: return "local address already in use"; - case EBADF: return "invalid descriptor"; - case ENOTSOCK: return "descriptor not a socket"; - case EOPNOTSUPP: return "not supported"; - default: return "unknown error"; - } -} - -/* return error messages for the known errors reported by connect */ -static const char *sock_connectstrerror(int err) -{ - switch (err) { - case EBADF: return "invalid descriptor"; - case EFAULT: return "invalid memory address"; - case ENOTSOCK: return "descriptor not a socket"; - case EADDRNOTAVAIL: return "address not available in local host"; - case EISCONN: return "already connected"; - case ECONNREFUSED: return "connection refused"; - case ETIMEDOUT: return io_strerror(IO_TIMEOUT); - case ENETUNREACH: return "network is unreachable"; - case EADDRINUSE: return "local address already in use"; - case EINPROGRESS: return "would block"; - case EALREADY: return "connect already in progress"; - case EAGAIN: return "not enough free ports"; - case EAFNOSUPPORT: return "address family not supported"; - case EPERM: return "broadcast not enabled or firewall block"; - default: return "unknown error"; - } +const char *sock_geterr(p_sock ps, int code) { + return sock_strerror(); } diff --git a/src/usocket.h b/src/usocket.h index b9255cb..d3d4f48 100644 --- a/src/usocket.h +++ b/src/usocket.h @@ -16,14 +16,12 @@ #include /* fnctnl function and associated constants */ #include -/* struct timeval and CLK_TCK */ -#include -/* times function and struct tms */ -#include /* struct sockaddr */ #include /* socket function */ #include +/* struct timeval */ +#include /* gethostbyname and gethostbyaddr functions */ #include /* sigpipe handling */ diff --git a/test/testclnt.lua b/test/testclnt.lua index 9aa07fe..58a3574 100644 --- a/test/testclnt.lua +++ b/test/testclnt.lua @@ -228,6 +228,7 @@ function test_totaltimeoutsend(len, tm, sl) data:settimeout(tm, "total") str = string.rep("a", 2*len) total, err, partial, elapsed = data:send(str) +print(elapsed, "!") check_timeout(tm, sl, elapsed, err, "send", "total", total == 2*len) end @@ -400,27 +401,27 @@ function accept_errors() d:setfd(c:getfd()) d:settimeout(2) local r, e = d:accept() - assert(not r and e == "not listening", e) - print("ok") + assert(not r and e) + print("ok: ", e) io.stderr:write("not supported: ") local c, e = socket.udp() assert(c, e); d:setfd(c:getfd()) local r, e = d:accept() - assert(not r and e == "not supported" or e == "not listening", e) - print("ok") + assert(not r and e) + print("ok: ", e) end ------------------------------------------------------------------------ function connect_errors() io.stderr:write("connection refused: ") local c, e = socket.connect("localhost", 1); - assert(not c and e == "connection refused", e) - print("ok") + assert(not c and e) + print("ok: ", e) io.stderr:write("host not found: ") local c, e = socket.connect("host.is.invalid", 1); - assert(not c and e == "host not found", e) - print("ok") + assert(not c and e, e) + print("ok: ", e) end ------------------------------------------------------------------------ @@ -432,36 +433,25 @@ function rebind_test() s:setoption("reuseaddr", false) r, e = s:bind("localhost", p) assert(not r, "managed to rebind!") - assert(e == "address already in use") - print("ok") + assert(e) + print("ok: ", e) end ------------------------------------------------------------------------ - -test("character line") -test_asciiline(1) -test_asciiline(17) -test_asciiline(200) -test_asciiline(4091) -test_asciiline(80199) -test_asciiline(8000000) -test_asciiline(80199) -test_asciiline(4091) -test_asciiline(200) -test_asciiline(17) -test_asciiline(1) - test("method registration") test_methods(socket.tcp(), { - "accept", + "accept", "bind", "close", "connect", + "dirty", + "getfd", "getpeername", "getsockname", "listen", "receive", "send", + "setfd", "setoption", "setpeername", "setsockname", @@ -472,15 +462,19 @@ test_methods(socket.tcp(), { test_methods(socket.udp(), { "close", "getpeername", + "dirty", + "getfd", + "getpeername", "getsockname", "receive", "receivefrom", "send", "sendto", + "setfd", "setoption", "setpeername", "setsockname", - "settimeout", + "settimeout" }) test("select function") @@ -504,7 +498,18 @@ test("accept function: ") accept_timeout() accept_errors() - +test("character line") +test_asciiline(1) +test_asciiline(17) +test_asciiline(200) +test_asciiline(4091) +test_asciiline(80199) +test_asciiline(8000000) +test_asciiline(80199) +test_asciiline(4091) +test_asciiline(200) +test_asciiline(17) +test_asciiline(1) test("mixed patterns") test_mixed(1) @@ -566,9 +571,10 @@ test_raw(1) test("total timeout on send") test_totaltimeoutsend(800091, 1, 3) test_totaltimeoutsend(800091, 2, 3) -test_totaltimeoutsend(800091, 3, 2) +test_totaltimeoutsend(800091, 5, 2) test_totaltimeoutsend(800091, 3, 1) + test("total timeout on receive") test_totaltimeoutreceive(800091, 1, 3) test_totaltimeoutreceive(800091, 2, 3)