moonbridge

view moonbridge_io.c @ 98:acaa85256c4b

Added moonbridge_io.tcpconnect(...) function
author jbe
date Wed Apr 08 04:00:41 2015 +0200 (2015-04-08)
parents 0561abcc68ee
children c9ffbdac1337
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>
10 #include <netinet/in.h>
11 #include <netinet/tcp.h>
12 #include <sys/types.h>
13 #include <netdb.h>
15 #include <lua.h>
16 #include <lauxlib.h>
17 #include <lualib.h>
19 #define MOONBR_IO_MAXSTRERRORLEN 80
20 #define MOONBR_IO_READBUFLEN 4096
21 #define MOONBR_IO_WRITEBUFLEN 4096
23 #define moonbr_io_errmsg() \
24 char errmsg[MOONBR_IO_MAXSTRERRORLEN]; \
25 strerror_r(errno, errmsg, MOONBR_IO_MAXSTRERRORLEN)
27 #define MOONBR_IO_HANDLE_MT_REGKEY "moonbridge_io_handle"
28 #define MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY "moonbridge_io_handle_public"
30 typedef struct {
31 int fd;
32 int isnetwork;
33 int finished;
34 int closed;
35 int nonblocking;
36 int nopush;
37 int readerr;
38 int readbufcnt;
39 int writeerr;
40 size_t writeleft;
41 #if LUA_VERSION_NUM >= 503
42 lua_Integer writeqin;
43 lua_Integer writeqout;
44 #else
45 int writeqin;
46 int writeqout;
47 #endif
48 size_t writeqoff;
49 int writebufcnt;
50 char readbuf[MOONBR_IO_READBUFLEN];
51 char writebuf[MOONBR_IO_WRITEBUFLEN];
52 } moonbr_io_handle_t;
54 static void moonbr_io_handle_set_nonblocking(lua_State *L, moonbr_io_handle_t *handle, int nonblocking) {
55 int flags;
56 if (handle->nonblocking == nonblocking) return;
57 flags = fcntl(handle->fd, F_GETFL, 0);
58 if (flags == -1) {
59 moonbr_io_errmsg();
60 close(handle->fd);
61 handle->fd = -1;
62 handle->closed = 1;
63 luaL_error(L, "Unexpected error in fcntl call: %s", errmsg);
64 }
65 if (nonblocking) flags |= O_NONBLOCK;
66 else flags &= ~O_NONBLOCK;
67 if (fcntl(handle->fd, F_SETFL, flags) == -1) {
68 moonbr_io_errmsg();
69 close(handle->fd);
70 handle->fd = -1;
71 handle->closed = 1;
72 luaL_error(L, "Unexpected error in fcntl call: %s", errmsg);
73 }
74 handle->nonblocking = nonblocking;
75 }
77 static void moonbr_io_handle_set_linger(lua_State *L, moonbr_io_handle_t *handle, int timeout) {
78 struct linger lingerval = { 0, };
79 if (!handle->isnetwork) return;
80 if (timeout >= 0) {
81 lingerval.l_onoff = 1;
82 lingerval.l_linger = timeout;
83 }
84 if (setsockopt(handle->fd, SOL_SOCKET, SO_LINGER, &lingerval, sizeof(lingerval))) {
85 moonbr_io_errmsg();
86 close(handle->fd);
87 handle->fd = -1;
88 handle->closed = 1;
89 luaL_error(L, "Unexpected error while setting SO_LINGER with setsockopt: %s", errmsg);
90 }
91 }
93 static void moonbr_io_handle_set_nopush(lua_State *L, moonbr_io_handle_t *handle, int nopush) {
94 #if defined(TCP_NOPUSH) || defined(TCP_CORK)
95 if (!handle->isnetwork || handle->nopush == nopush) return;
96 #if defined(TCP_NOPUSH)
97 if (setsockopt(handle->fd, IPPROTO_TCP, TCP_NOPUSH, &nopush, sizeof(nopush))) {
98 moonbr_io_errmsg();
99 close(handle->fd);
100 handle->fd = -1;
101 handle->closed = 1;
102 luaL_error(L, "Unexpected error while setting TCP_NOPUSH with setsockopt: %s", errmsg);
103 }
104 #elif defined(TCP_CORK)
105 if (setsockopt(handle->fd, IPPROTO_TCP, TCP_CORK, &nopush, sizeof(nopush))) {
106 moonbr_io_errmsg();
107 close(handle->fd);
108 handle->fd = -1;
109 handle->closed = 1;
110 luaL_error(L, "Unexpected error while setting TCP_CORK with setsockopt: %s", errmsg);
111 }
112 #endif
113 handle->nopush = nopush;
114 #else
115 #warning Neither TCP_NOPUSH nor TCP_CORK is available
116 #endif
117 }
119 static int moonbr_io_read_impl(lua_State *L, int nonblocking, int drain) {
120 moonbr_io_handle_t *handle;
121 lua_Integer maxread;
122 const char *terminatorstr;
123 size_t terminatorlen;
124 char terminator;
125 luaL_Buffer luabuf;
126 size_t luabufcnt = 0;
127 int endcnt;
128 char *terminatorpos;
129 ssize_t result;
130 handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
131 maxread = luaL_optinteger(L, 2, 0);
132 terminatorstr = luaL_optlstring(L, 3, "", &terminatorlen);
133 if (terminatorlen) {
134 luaL_argcheck(L, terminatorlen == 1, 3, "single byte expected");
135 terminator = terminatorstr[0];
136 }
137 lua_settop(L, 1); /* return handle on drain, terminator string may be garbage collected */
138 if (handle->closed) luaL_error(L, "Attempt to read from a closed I/O handle");
139 if (handle->fd < 0) goto moonbr_io_read_impl_eof; /* fake EOF to simulate shutdown */
140 if (handle->readerr) {
141 lua_pushnil(L);
142 lua_pushliteral(L, "Previous read error");
143 return 2;
144 }
145 moonbr_io_handle_set_nonblocking(L, handle, nonblocking);
146 if (!drain) luaL_buffinit(L, &luabuf);
147 while (1) {
148 endcnt = -1;
149 if (maxread > 0 && handle->readbufcnt >= maxread - luabufcnt) {
150 endcnt = maxread - luabufcnt;
151 } else if (terminatorlen) {
152 terminatorpos = memchr(handle->readbuf, terminator, handle->readbufcnt);
153 if (terminatorpos) endcnt = 1 + (terminatorpos - handle->readbuf);
154 }
155 if (endcnt >= 0) {
156 if (!drain) {
157 luaL_addlstring(&luabuf, handle->readbuf, endcnt);
158 luaL_pushresult(&luabuf);
159 } else {
160 luabufcnt += handle->readbufcnt;
161 lua_pushinteger(L, luabufcnt);
162 }
163 handle->readbufcnt -= endcnt;
164 memmove(handle->readbuf, handle->readbuf + endcnt, handle->readbufcnt);
165 return 1;
166 }
167 if (!drain) luaL_addlstring(&luabuf, handle->readbuf, handle->readbufcnt);
168 luabufcnt += handle->readbufcnt;
169 handle->readbufcnt = 0;
170 do {
171 result = read(handle->fd, handle->readbuf, MOONBR_IO_READBUFLEN);
172 } while (result < 0 && (errno == EINTR));
173 if (result == 0 || (nonblocking && result < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))) break;
174 if (result < 0) {
175 moonbr_io_errmsg();
176 handle->readerr = 1;
177 lua_pushnil(L);
178 lua_pushstring(L, errmsg);
179 return 2;
180 }
181 handle->readbufcnt += result;
182 }
183 if (!drain) {
184 luaL_addlstring(&luabuf, handle->readbuf, handle->readbufcnt);
185 luaL_pushresult(&luabuf);
186 }
187 luabufcnt += handle->readbufcnt;
188 handle->readbufcnt = 0;
189 if (!drain) {
190 if (!luabufcnt && result == 0) {
191 moonbr_io_read_impl_eof:
192 lua_pushboolean(L, 0);
193 lua_pushliteral(L, "End of file");
194 return 2;
195 }
196 } else {
197 if (!luabufcnt && result == 0) lua_pushboolean(L, 1);
198 else lua_pushboolean(L, luabufcnt);
199 }
200 return 1;
201 }
203 static int moonbr_io_read(lua_State *L) {
204 return moonbr_io_read_impl(L, 0, 0);
205 }
207 static int moonbr_io_read_nb(lua_State *L) {
208 return moonbr_io_read_impl(L, 1, 0);
209 }
211 static int moonbr_io_drain(lua_State *L) {
212 return moonbr_io_read_impl(L, 0, 1);
213 }
215 static int moonbr_io_drain_nb(lua_State *L) {
216 return moonbr_io_read_impl(L, 1, 1);
217 }
219 static int moonbr_io_write_impl(lua_State *L, int nonblocking, int flush) {
220 moonbr_io_handle_t *handle;
221 int i, top;
222 const char *str;
223 size_t strlen;
224 size_t written;
225 ssize_t result;
226 handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
227 if (handle->closed) luaL_error(L, "Attempt to write to a closed I/O handle");
228 if (handle->finished) luaL_error(L, "Attempt to write to a finished I/O handle");
229 if (handle->writeerr) {
230 lua_pushnil(L);
231 lua_pushliteral(L, "Previous write error");
232 return 2;
233 }
234 moonbr_io_handle_set_nonblocking(L, handle, nonblocking);
235 top = lua_gettop(L);
236 lua_getuservalue(L, 1);
237 lua_getfield(L, -1, "writebuf");
238 for (i=2; i<=top; i++) {
239 luaL_checklstring(L, i, &strlen);
240 lua_pushvalue(L, i);
241 lua_rawseti(L, -2, handle->writeqin++);
242 handle->writeleft += strlen;
243 }
244 while (handle->writeqout != handle->writeqin) {
245 lua_rawgeti(L, -1, handle->writeqout);
246 str = lua_tolstring(L, -1, &strlen);
247 while (handle->writeqoff < strlen) {
248 if (strlen - handle->writeqoff <= MOONBR_IO_WRITEBUFLEN - handle->writebufcnt) {
249 memcpy(handle->writebuf + handle->writebufcnt, str + handle->writeqoff, strlen - handle->writeqoff);
250 handle->writebufcnt += strlen - handle->writeqoff;
251 break;
252 } else {
253 moonbr_io_handle_set_nopush(L, handle, 1);
254 written = 0;
255 memcpy(handle->writebuf + handle->writebufcnt, str + handle->writeqoff, MOONBR_IO_WRITEBUFLEN - handle->writebufcnt);
256 handle->writeqoff += MOONBR_IO_WRITEBUFLEN - handle->writebufcnt;
257 while (written < MOONBR_IO_WRITEBUFLEN) {
258 result = write(handle->fd, handle->writebuf + written, MOONBR_IO_WRITEBUFLEN - written);
259 if (result < 0) {
260 if (nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) {
261 if (written) {
262 handle->writebufcnt -= written;
263 memmove(handle->writebuf, handle->writebuf + written, handle->writebufcnt);
264 goto moonbr_io_write_impl_continue;
265 }
266 goto moonbr_io_write_impl_block;
267 } else if (errno != EINTR) {
268 moonbr_io_errmsg();
269 handle->writeerr = 1;
270 lua_pushnil(L);
271 lua_pushstring(L, errmsg);
272 return 2;
273 }
274 }
275 written += result;
276 handle->writeleft -= result;
277 }
278 handle->writebufcnt = 0;
279 }
280 moonbr_io_write_impl_continue:;
281 }
282 handle->writeqoff = 0;
283 lua_pop(L, 1);
284 lua_pushnil(L);
285 lua_rawseti(L, -2, handle->writeqout++);
286 }
287 if (flush) {
288 moonbr_io_handle_set_nopush(L, handle, 0);
289 written = 0;
290 while (written < handle->writebufcnt) {
291 result = write(handle->fd, handle->writebuf + written, handle->writebufcnt - written);
292 if (result < 0) {
293 if (nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) {
294 if (written) {
295 handle->writebufcnt -= written;
296 memmove(handle->writebuf, handle->writebuf + written, handle->writebufcnt);
297 }
298 goto moonbr_io_write_impl_block;
299 } else if (errno != EINTR) {
300 moonbr_io_errmsg();
301 handle->writeerr = -1;
302 lua_pushnil(L);
303 lua_pushstring(L, errmsg);
304 return 2;
305 }
306 } else {
307 written += result;
308 handle->writeleft -= result;
309 }
310 }
311 handle->writebufcnt = 0;
312 if (nonblocking) lua_pushinteger(L, 0);
313 } else {
314 if (nonblocking) lua_pushinteger(L, handle->writeleft);
315 }
316 if (!nonblocking) lua_pushvalue(L, 1);
317 return 1;
318 moonbr_io_write_impl_block:
319 lua_pushinteger(L, handle->writeleft);
320 return 1;
321 }
323 static int moonbr_io_write(lua_State *L) {
324 return moonbr_io_write_impl(L, 0, 0);
325 }
327 static int moonbr_io_write_nb(lua_State *L) {
328 return moonbr_io_write_impl(L, 1, 0);
329 }
331 static int moonbr_io_flush(lua_State *L) {
332 return moonbr_io_write_impl(L, 0, 1);
333 }
335 static int moonbr_io_flush_nb(lua_State *L) {
336 return moonbr_io_write_impl(L, 1, 1);
337 }
339 static int moonbr_io_finish(lua_State *L) {
340 moonbr_io_handle_t *handle;
341 handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
342 if (handle->closed) luaL_error(L, "Attempt to finish a closed I/O handle");
343 if (handle->finished) luaL_error(L, "Attempt to finish a finished I/O handle");
344 if (handle->writeleft) {
345 lua_pushcfunction(L, moonbr_io_flush);
346 lua_pushvalue(L, 1);
347 lua_call(L, 1, 2);
348 if (!lua_toboolean(L, -2)) {
349 handle->finished = 1;
350 return 2;
351 }
352 }
353 handle->finished = 1;
354 if (handle->isnetwork) {
355 if (shutdown(handle->fd, SHUT_WR)) {
356 moonbr_io_errmsg();
357 lua_pushnil(L);
358 lua_pushstring(L, errmsg);
359 return 2;
360 }
361 } else {
362 if (close(handle->fd)) {
363 moonbr_io_errmsg();
364 handle->fd = -1;
365 lua_pushnil(L);
366 lua_pushstring(L, errmsg);
367 return 2;
368 }
369 handle->fd = -1; /* fake EOF on read */
370 }
371 lua_pushboolean(L, 1);
372 return 1;
373 }
375 static int moonbr_io_close_impl(lua_State *L, int reset) {
376 moonbr_io_handle_t *handle;
377 handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
378 if (handle->closed) luaL_error(L, "Attempt to close a closed I/O handle");
379 if (!reset) {
380 if (handle->writeleft) {
381 lua_pushcfunction(L, moonbr_io_flush);
382 lua_pushvalue(L, 1);
383 lua_call(L, 1, 2);
384 if (!lua_toboolean(L, -2)) {
385 close(handle->fd);
386 handle->fd = -1;
387 return 2;
388 }
389 }
390 moonbr_io_handle_set_linger(L, handle, -1);
391 }
392 if (handle->fd >= 0) {
393 if (close(handle->fd)) {
394 moonbr_io_errmsg();
395 handle->fd = -1;
396 lua_pushnil(L);
397 lua_pushstring(L, errmsg);
398 return 2;
399 }
400 }
401 handle->fd = -1;
402 lua_pushboolean(L, 1);
403 return 1;
405 }
407 static int moonbr_io_close(lua_State *L) {
408 return moonbr_io_close_impl(L, 0);
409 }
411 static int moonbr_io_reset(lua_State *L) {
412 return moonbr_io_close_impl(L, 1);
413 }
415 static int moonbr_io_gc(lua_State *L) {
416 moonbr_io_handle_t *handle;
417 handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
418 if (handle->fd >= 0) {
419 lua_pushcfunction(L, moonbr_io_close);
420 lua_pushvalue(L, 1);
421 lua_pushinteger(L, 0);
422 lua_call(L, 2, 0);
423 }
424 return 0;
425 }
427 void moonbr_io_closehandle(lua_State *L, int idx, int timeout) {
428 moonbr_io_handle_t *handle;
429 handle = luaL_checkudata(L, idx, MOONBR_IO_HANDLE_MT_REGKEY);
430 if (handle->fd >= 0) {
431 lua_pushcfunction(L, moonbr_io_close);
432 lua_pushvalue(L, idx);
433 lua_pushinteger(L, timeout);
434 lua_call(L, 2, 0);
435 }
436 }
438 void moonbr_io_pushhandle(lua_State *L, int fd, int isnetwork) {
439 moonbr_io_handle_t *handle;
440 handle = lua_newuserdata(L, sizeof(moonbr_io_handle_t));
441 handle->fd = fd;
442 handle->isnetwork = isnetwork;
443 handle->finished = 0;
444 handle->closed = 0;
445 handle->nonblocking = -1;
446 handle->nopush = -1;
447 handle->readerr = 0;
448 handle->readbufcnt = 0;
449 handle->writeerr = 0;
450 handle->writeleft = 0;
451 handle->writeqin = 0;
452 handle->writeqout = 0;
453 handle->writeqoff = 0;
454 handle->writebufcnt = 0;
455 moonbr_io_handle_set_linger(L, handle, 0);
456 luaL_getmetatable(L, MOONBR_IO_HANDLE_MT_REGKEY);
457 lua_setmetatable(L, -2);
458 lua_newtable(L); // uservalue
459 lua_newtable(L);
460 lua_setfield(L, -2, "writebuf");
461 lua_newtable(L); // public
462 luaL_getmetatable(L, MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY);
463 lua_setmetatable(L, -2);
464 lua_setfield(L, -2, "public");
465 lua_setuservalue(L, -2);
466 }
468 static int moonbr_io_handleindex(lua_State *L) {
469 luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
470 lua_getuservalue(L, 1);
471 lua_getfield(L, -1, "public");
472 lua_pushvalue(L, 2);
473 lua_gettable(L, -2);
474 return 1;
475 }
477 static int moonbr_io_handlenewindex(lua_State *L) {
478 luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
479 lua_getuservalue(L, 1);
480 lua_getfield(L, -1, "public");
481 lua_pushvalue(L, 2);
482 lua_pushvalue(L, 3);
483 lua_settable(L, -3);
484 return 0;
485 }
487 int moonbr_io_tcpconnect(lua_State *L) {
488 const char *host, *port;
489 struct addrinfo hints = { 0, };
490 struct addrinfo *res, *addrinfo;
491 int errcode;
492 int sock;
493 host = luaL_checkstring(L, 1);
494 port = luaL_checkstring(L, 2);
495 hints.ai_family = AF_UNSPEC;
496 hints.ai_socktype = SOCK_STREAM;
497 hints.ai_protocol = IPPROTO_TCP;
498 hints.ai_flags = AI_ADDRCONFIG;
499 errcode = getaddrinfo(host, port, &hints, &res);
500 if (errcode) {
501 if (errcode == EAI_SYSTEM) {
502 moonbr_io_errmsg();
503 lua_pushnil(L);
504 lua_pushfstring(L, "%s: %s", gai_strerror(errcode), errmsg);
505 } else {
506 lua_pushnil(L);
507 lua_pushstring(L, gai_strerror(errcode));
508 }
509 return 2;
510 }
511 for (addrinfo=res; addrinfo; addrinfo=addrinfo->ai_next) {
512 if (addrinfo->ai_family == PF_INET6) goto moonbr_io_tcpconnect_found;
513 }
514 for (addrinfo=res; addrinfo; addrinfo=addrinfo->ai_next) {
515 if (addrinfo->ai_family == PF_INET) goto moonbr_io_tcpconnect_found;
516 }
517 addrinfo = res;
518 moonbr_io_tcpconnect_found:
519 sock = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
520 if (sock < 0) {
521 moonbr_io_errmsg();
522 lua_pushnil(L);
523 lua_pushstring(L, errmsg);
524 }
525 if (connect(sock, addrinfo->ai_addr, addrinfo->ai_addrlen)) {
526 moonbr_io_errmsg();
527 lua_pushnil(L);
528 lua_pushstring(L, errmsg);
529 return 2;
530 }
531 moonbr_io_pushhandle(L, sock, 1);
532 return 1;
533 }
535 static const struct luaL_Reg moonbr_io_handle_methods[] = {
536 {"read", moonbr_io_read},
537 {"read_nb", moonbr_io_read_nb},
538 {"drain", moonbr_io_drain},
539 {"drain_nb", moonbr_io_drain_nb},
540 {"write", moonbr_io_write},
541 {"write_nb", moonbr_io_write_nb},
542 {"flush", moonbr_io_flush},
543 {"flush_nb", moonbr_io_flush_nb},
544 {"finish", moonbr_io_finish},
545 {"close", moonbr_io_close},
546 {"reset", moonbr_io_reset},
547 {NULL, NULL}
548 };
550 static const struct luaL_Reg moonbr_io_handle_metamethods[] = {
551 {"__index", moonbr_io_handleindex},
552 {"__newindex", moonbr_io_handlenewindex},
553 {"__gc", moonbr_io_gc},
554 {NULL, NULL}
555 };
557 static const struct luaL_Reg moonbr_io_module_funcs[] = {
558 {"tcpconnect", moonbr_io_tcpconnect},
559 {NULL, NULL}
560 };
562 int luaopen_moonbridge_io(lua_State *L) {
564 lua_newtable(L); // module
566 lua_newtable(L); // public metatable
567 lua_newtable(L); // handle methods
568 luaL_setfuncs(L, moonbr_io_handle_methods, 0);
569 lua_pushvalue(L, -1);
570 lua_setfield(L, -4, "handle");
571 lua_setfield(L, -2, "__index");
572 lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY);
574 lua_newtable(L); // handle metatable
575 luaL_setfuncs(L, moonbr_io_handle_metamethods, 0);
576 lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_HANDLE_MT_REGKEY);
578 luaL_setfuncs(L, moonbr_io_module_funcs, 0);
579 return 1;
581 }

Impressum / About Us