moonbridge

view moonbridge_io.c @ 87:1d91c6eedf18

Close and reset method for I/O library
author jbe
date Tue Apr 07 01:17:55 2015 +0200 (2015-04-07)
parents 9fa3a36733ff
children fca51922b708
line source
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <stdint.h>
5 #include <errno.h>
6 #include <string.h>
7 #include <sys/socket.h>
8 #include <sys/select.h>
9 #include <fcntl.h>
11 #include <lua.h>
12 #include <lauxlib.h>
13 #include <lualib.h>
15 #define MOONBR_IO_MAXSTRERRORLEN 80
16 #define MOONBR_IO_READBUFLEN 4096
17 #define MOONBR_IO_WRITEBUFLEN 4096
19 #define moonbr_io_errmsg() \
20 char errmsg[MOONBR_IO_MAXSTRERRORLEN]; \
21 strerror_r(errno, errmsg, MOONBR_IO_MAXSTRERRORLEN)
23 #define MOONBR_IO_HANDLE_MT_REGKEY "moonbridge_io_handle"
24 #define MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY "moonbridge_io_handle_public"
26 typedef struct {
27 int fd;
28 int issocket;
29 int nonblocking;
30 int readerr;
31 int readbufcnt;
32 int writeerr;
33 size_t writeleft;
34 #if LUA_VERSION_NUM >= 503
35 lua_Integer writeqin;
36 lua_Integer writeqout;
37 #else
38 int writeqin;
39 int writeqout;
40 #endif
41 size_t writeqoff;
42 int writebufcnt;
43 char readbuf[MOONBR_IO_READBUFLEN];
44 char writebuf[MOONBR_IO_WRITEBUFLEN];
45 } moonbr_io_handle_t;
47 static void moonbr_io_handle_set_nonblocking(lua_State *L, moonbr_io_handle_t *handle, int nonblocking) {
48 if (handle->nonblocking != nonblocking) {
49 int flags;
50 flags = fcntl(handle->fd, F_GETFL, 0);
51 if (flags == -1) {
52 moonbr_io_errmsg();
53 luaL_error(L, "Unexpected error in fcntl call: %s", errmsg);
54 }
55 if (nonblocking) flags |= O_NONBLOCK;
56 else flags &= ~O_NONBLOCK;
57 if (fcntl(handle->fd, F_SETFL, flags) == -1) {
58 moonbr_io_errmsg();
59 luaL_error(L, "Unexpected error in fcntl call: %s", errmsg);
60 }
61 }
62 }
64 static void moonbr_io_handle_set_linger(lua_State *L, moonbr_io_handle_t *handle, int timeout) {
65 struct linger lingerval = { 0, };
66 if (!handle->issocket) return;
67 if (timeout >= 0) {
68 lingerval.l_onoff = 1;
69 lingerval.l_linger = timeout;
70 }
71 if (setsockopt(handle->fd, SOL_SOCKET, SO_LINGER, &lingerval, sizeof(lingerval))) {
72 moonbr_io_errmsg();
73 luaL_error(L, "Unexpected error in setsockopt call: %s", errmsg);
74 }
75 }
77 static int moonbr_io_read_impl(lua_State *L, int nonblocking, int drain) {
78 moonbr_io_handle_t *handle;
79 lua_Integer maxread;
80 const char *terminatorstr;
81 size_t terminatorlen;
82 char terminator;
83 luaL_Buffer luabuf;
84 size_t luabufcnt = 0;
85 int endcnt;
86 char *terminatorpos;
87 ssize_t result;
88 handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
89 maxread = luaL_optinteger(L, 2, 0);
90 terminatorstr = luaL_optlstring(L, 3, "", &terminatorlen);
91 if (terminatorlen) {
92 luaL_argcheck(L, terminatorlen == 1, 3, "single byte expected");
93 terminator = terminatorstr[0];
94 }
95 lua_settop(L, 1); /* return handle on drain, terminator string may be garbage collected */
96 if (handle->fd < 0) luaL_error(L, "Attempt to read from a closed I/O handle");
97 if (handle->readerr) {
98 lua_pushnil(L);
99 lua_pushliteral(L, "Previous read error");
100 return 2;
101 }
102 moonbr_io_handle_set_nonblocking(L, handle, nonblocking);
103 if (!drain) luaL_buffinit(L, &luabuf);
104 while (1) {
105 endcnt = -1;
106 if (maxread > 0 && handle->readbufcnt >= maxread - luabufcnt) {
107 endcnt = maxread - luabufcnt;
108 } else if (terminatorlen) {
109 terminatorpos = memchr(handle->readbuf, terminator, handle->readbufcnt);
110 if (terminatorpos) endcnt = 1 + (terminatorpos - handle->readbuf);
111 }
112 if (endcnt >= 0) {
113 if (!drain) {
114 luaL_addlstring(&luabuf, handle->readbuf, endcnt);
115 luaL_pushresult(&luabuf);
116 }
117 handle->readbufcnt -= endcnt;
118 memmove(handle->readbuf, handle->readbuf + endcnt, handle->readbufcnt);
119 return 1;
120 }
121 if (!drain) luaL_addlstring(&luabuf, handle->readbuf, handle->readbufcnt);
122 luabufcnt += handle->readbufcnt;
123 handle->readbufcnt = 0;
124 do {
125 result = read(handle->fd, handle->readbuf, MOONBR_IO_READBUFLEN);
126 } while (result < 0 && (errno == EINTR));
127 if (result == 0 || (nonblocking && result < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))) break;
128 if (result < 0) {
129 moonbr_io_errmsg();
130 handle->readerr = 1;
131 lua_pushnil(L);
132 lua_pushstring(L, errmsg);
133 return 2;
134 }
135 handle->readbufcnt += result;
136 }
137 if (!drain) {
138 luaL_addlstring(&luabuf, handle->readbuf, handle->readbufcnt);
139 luaL_pushresult(&luabuf);
140 }
141 handle->readbufcnt = 0;
142 return 1;
143 }
145 static int moonbr_io_read(lua_State *L) {
146 return moonbr_io_read_impl(L, 0, 0);
147 }
149 static int moonbr_io_read_nb(lua_State *L) {
150 return moonbr_io_read_impl(L, 1, 0);
151 }
153 static int moonbr_io_drain(lua_State *L) {
154 return moonbr_io_read_impl(L, 0, 1);
155 }
157 static int moonbr_io_drain_nb(lua_State *L) {
158 return moonbr_io_read_impl(L, 1, 1);
159 }
161 static int moonbr_io_write_impl(lua_State *L, int nonblocking, int flush) {
162 moonbr_io_handle_t *handle;
163 int i, top;
164 const char *str;
165 size_t strlen, strpos;
166 size_t written;
167 ssize_t result;
168 handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
169 if (handle->fd < 0) luaL_error(L, "Attempt to write to a closed I/O handle");
170 if (handle->writeerr) {
171 lua_pushnil(L);
172 lua_pushliteral(L, "Previous write error");
173 return 2;
174 }
175 moonbr_io_handle_set_nonblocking(L, handle, nonblocking);
176 top = lua_gettop(L);
177 lua_getuservalue(L, 1);
178 lua_getfield(L, -1, "writebuf");
179 for (i=2; i<=top; i++) {
180 luaL_checklstring(L, i, &strlen);
181 lua_pushvalue(L, i);
182 lua_rawseti(L, -2, handle->writeqin++);
183 handle->writeleft += strlen;
184 }
185 while (handle->writeqout != handle->writeqin) {
186 lua_rawgeti(L, -1, handle->writeqout);
187 str = lua_tolstring(L, -1, &strlen);
188 strpos = handle->writeqoff;
189 while (strpos < strlen) {
190 if (strlen - strpos < MOONBR_IO_WRITEBUFLEN - handle->writebufcnt) {
191 memcpy(handle->writebuf + handle->writebufcnt, str + strpos, strlen - strpos);
192 handle->writebufcnt += strlen - strpos;
193 break;
194 } else {
195 written = 0;
196 memcpy(handle->writebuf + handle->writebufcnt, str + strpos, MOONBR_IO_WRITEBUFLEN - handle->writebufcnt);
197 strpos += MOONBR_IO_WRITEBUFLEN - handle->writebufcnt;
198 while (written < MOONBR_IO_WRITEBUFLEN) {
199 result = write(handle->fd, handle->writebuf + written, MOONBR_IO_WRITEBUFLEN - written);
200 if (result < 0) {
201 if (nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) {
202 if (written) {
203 handle->writebufcnt -= written;
204 memmove(handle->writebuf, handle->writebuf + written, handle->writebufcnt);
205 }
206 handle->writeqoff = strpos;
207 goto moonbr_io_write_impl_block;
208 } else if (errno != EINTR) {
209 moonbr_io_errmsg();
210 handle->writeerr = 1;
211 lua_pushnil(L);
212 lua_pushstring(L, errmsg);
213 return 2;
214 }
215 } else {
216 written += result;
217 handle->writeleft -= result;
218 }
219 }
220 handle->writebufcnt = 0;
221 }
222 }
223 handle->writeqoff = 0;
224 lua_pop(L, 1);
225 lua_pushnil(L);
226 lua_rawseti(L, -2, handle->writeqout++);
227 }
228 if (flush) {
229 written = 0;
230 while (written < handle->writebufcnt) {
231 result = write(handle->fd, handle->writebuf + written, handle->writebufcnt - written);
232 if (result < 0) {
233 if (nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) {
234 if (written) {
235 handle->writebufcnt -= written;
236 memmove(handle->writebuf, handle->writebuf + written, handle->writebufcnt);
237 }
238 goto moonbr_io_write_impl_block;
239 } else if (errno != EINTR) {
240 moonbr_io_errmsg();
241 handle->writeerr = -1;
242 lua_pushnil(L);
243 lua_pushstring(L, errmsg);
244 return 2;
245 }
246 } else {
247 written += result;
248 handle->writeleft -= result;
249 }
250 }
251 handle->writebufcnt = 0;
252 if (nonblocking) lua_pushinteger(L, 0);
253 } else {
254 if (nonblocking) lua_pushinteger(L, handle->writeleft - handle->writebufcnt);
255 }
256 if (!nonblocking) lua_pushvalue(L, 1);
257 return 1;
258 moonbr_io_write_impl_block:
259 if (flush) lua_pushinteger(L, handle->writeleft);
260 else lua_pushinteger(L, handle->writeleft - handle->writebufcnt);
261 return 1;
262 }
264 static int moonbr_io_write(lua_State *L) {
265 return moonbr_io_write_impl(L, 0, 0);
266 }
268 static int moonbr_io_write_nb(lua_State *L) {
269 return moonbr_io_write_impl(L, 1, 0);
270 }
272 static int moonbr_io_flush(lua_State *L) {
273 return moonbr_io_write_impl(L, 0, 1);
274 }
276 static int moonbr_io_flush_nb(lua_State *L) {
277 return moonbr_io_write_impl(L, 1, 1);
278 }
280 static int moonbr_io_close(lua_State *L) {
281 moonbr_io_handle_t *handle;
282 int timeout;
283 handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
284 timeout = luaL_optinteger(L, 2, -1);
285 if (handle->fd < 0) luaL_error(L, "Attempt to close a closed I/O handle");
286 if (timeout != 0) {
287 if (handle->writeleft) {
288 lua_pushcfunction(L, moonbr_io_flush);
289 lua_pushvalue(L, 1);
290 lua_call(L, 1, 2);
291 if (!lua_toboolean(L, -2)) {
292 close(handle->fd);
293 handle->fd = -1;
294 return 2;
295 }
296 }
297 moonbr_io_handle_set_linger(L, handle, timeout);
298 }
299 if (close(handle->fd)) {
300 moonbr_io_errmsg();
301 handle->fd = -1;
302 lua_pushnil(L);
303 lua_pushstring(L, errmsg);
304 return 2;
305 }
306 handle->fd = -1;
307 lua_pushboolean(L, 1);
308 return 1;
310 }
312 static int moonbr_io_reset(lua_State *L) {
313 luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
314 lua_settop(L, 1);
315 lua_pushcfunction(L, moonbr_io_close);
316 lua_insert(L, 1);
317 lua_pushinteger(L, 0);
318 lua_call(L, 2, LUA_MULTRET);
319 return lua_gettop(L);
320 }
322 void moonbr_io_pushhandle(lua_State *L, int fd, int issocket) {
323 moonbr_io_handle_t *handle;
324 handle = lua_newuserdata(L, sizeof(moonbr_io_handle_t));
325 handle->fd = fd;
326 handle->issocket = issocket;
327 handle->nonblocking = -1;
328 handle->readerr = 0;
329 handle->readbufcnt = 0;
330 handle->writeerr = 0;
331 handle->writeleft = 0;
332 handle->writeqin = 0;
333 handle->writeqout = 0;
334 handle->writeqoff = 0;
335 handle->writebufcnt = 0;
336 moonbr_io_handle_set_linger(L, handle, 0);
337 luaL_getmetatable(L, MOONBR_IO_HANDLE_MT_REGKEY);
338 lua_setmetatable(L, -2);
339 lua_newtable(L); // uservalue
340 lua_newtable(L);
341 lua_setfield(L, -2, "writebuf");
342 lua_newtable(L); // public
343 luaL_getmetatable(L, MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY);
344 lua_setmetatable(L, -2);
345 lua_setfield(L, -2, "public");
346 lua_setuservalue(L, -2);
347 }
349 static int moonbr_io_handleindex(lua_State *L) {
350 luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
351 lua_getuservalue(L, 1);
352 lua_getfield(L, -1, "public");
353 lua_pushvalue(L, 2);
354 lua_gettable(L, -2);
355 return 1;
356 }
358 static int moonbr_io_handlenewindex(lua_State *L) {
359 luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
360 lua_getuservalue(L, 1);
361 lua_getfield(L, -1, "public");
362 lua_pushvalue(L, 2);
363 lua_pushvalue(L, 3);
364 lua_settable(L, -3);
365 return 0;
366 }
368 static const struct luaL_Reg moonbr_io_handle_methods[] = {
369 {"read", moonbr_io_read},
370 {"read_nb", moonbr_io_read_nb},
371 {"drain", moonbr_io_drain},
372 {"drain_nb", moonbr_io_drain_nb},
373 {"write", moonbr_io_write},
374 {"write_nb", moonbr_io_write_nb},
375 {"flush", moonbr_io_flush},
376 {"flush_nb", moonbr_io_flush_nb},
377 {"close", moonbr_io_close},
378 {"reset", moonbr_io_reset},
379 {NULL, NULL}
380 };
382 static const struct luaL_Reg moonbr_io_handle_metamethods[] = {
383 {"__index", moonbr_io_handleindex},
384 {"__newindex", moonbr_io_handlenewindex},
385 {"__gc", moonbr_io_reset},
386 {NULL, NULL}
387 };
389 static const struct luaL_Reg moonbr_io_module_funcs[] = {
390 {NULL, NULL}
391 };
393 int luaopen_moonbridge_io(lua_State *L) {
395 lua_newtable(L); // module
397 lua_newtable(L); // public metatable
398 lua_newtable(L); // handle methods
399 luaL_setfuncs(L, moonbr_io_handle_methods, 0);
400 lua_pushvalue(L, -1);
401 lua_setfield(L, -4, "handle");
402 lua_setfield(L, -2, "__index");
403 lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY);
405 lua_newtable(L); // handle metatable
406 luaL_setfuncs(L, moonbr_io_handle_metamethods, 0);
407 lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_HANDLE_MT_REGKEY);
409 luaL_setfuncs(L, moonbr_io_module_funcs, 0);
410 return 1;
412 }

Impressum / About Us