/*
* This file is part of LEM, a Lua Event Machine.
* Copyright 2011-2013 Emil Renner Berthing
*
* LEM is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* LEM is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with LEM. If not, see .
*/
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if defined(__FreeBSD__) || defined(__APPLE__)
#include
#include
#include
#ifndef UNIX_PATH_MAX
#define UNIX_PATH_MAX 104
#endif
#else
#include
#include
#endif
#include
static int
io_closed(lua_State *T)
{
lua_pushnil(T);
lua_pushliteral(T, "closed");
return 2;
}
static int
io_busy(lua_State *T)
{
lua_pushnil(T);
lua_pushliteral(T, "busy");
return 2;
}
static int
io_strerror(lua_State *T, int err)
{
lua_pushnil(T);
lua_pushstring(T, strerror(err));
return 2;
}
static int
io_optperm(lua_State *T, int idx)
{
lua_Number n = luaL_optnumber(T, idx, -1);
int mode = n;
int octal;
int i;
if ((lua_Number)mode != n)
goto error;
if (mode == -1)
return -1;
if (mode < 0)
goto error;
octal = 0;
for (i = 1; i <= 64; i *= 8) {
int digit = mode % 10;
if (digit > 7)
goto error;
octal += digit * i;
mode /= 10;
}
if (mode != 0)
goto error;
return octal;
error:
return luaL_argerror(T, idx, "invalid permissions");
}
#include "file.c"
#include "stream.c"
#include "server.c"
#include "tcp.c"
#include "unix.c"
struct open {
struct lem_async a;
const char *path;
int fd;
int flags;
};
static void
io_open_work(struct lem_async *a)
{
struct open *o = (struct open *)a;
int fd;
struct stat st;
fd = open(o->path, o->flags
#ifdef O_CLOEXEC
| O_CLOEXEC
#endif
| O_NONBLOCK, o->fd >= 0 ? o->fd :
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if (fd < 0
#ifndef O_CLOXEC
|| fcntl(fd, F_SETFD, FD_CLOEXEC) == -1
#endif
) {
o->flags = -errno;
return;
}
if (fstat(fd, &st)) {
o->flags = -errno;
close(fd);
return;
}
o->fd = fd;
lem_debug("st.st_mode & S_IFMT = %o", st.st_mode & S_IFMT);
switch (st.st_mode & S_IFMT) {
case S_IFREG:
case S_IFBLK:
o->flags = 0;
break;
case S_IFCHR:
case S_IFIFO:
o->flags = 1;
break;
default:
o->flags = -EINVAL;
break;
}
}
static void
io_open_reap(struct lem_async *a)
{
struct open *o = (struct open *)a;
lua_State *T = o->a.T;
int fd = o->fd;
int ret = o->flags;
lem_debug("ret = %d", ret);
free(o);
switch (ret) {
case 0: file_new(T, fd, 2); break;
case 1: stream_new(T, fd, 3); break;
default:
lem_queue(T, io_strerror(T, -ret));
return;
}
lem_queue(T, 1);
}
static int
io_mode_to_flags(const char *mode)
{
int omode;
int oflags;
switch (*mode++) {
case 'r':
omode = O_RDONLY;
oflags = 0;
break;
case 'w':
omode = O_WRONLY;
oflags = O_CREAT | O_TRUNC;
break;
case 'a':
omode = O_WRONLY;
oflags = O_CREAT | O_APPEND;
break;
default:
return -1;
}
next:
switch (*mode++) {
case '\0':
break;
case '+':
omode = O_RDWR;
goto next;
case 'b':
/* this does nothing on *nix, but
* don't treat it as an error */
goto next;
case 'x':
oflags |= O_EXCL;
goto next;
default:
return -1;
}
return omode | oflags;
}
static int
io_open(lua_State *T)
{
const char *path = luaL_checkstring(T, 1);
int flags = io_mode_to_flags(luaL_optstring(T, 2, "r"));
int perm = io_optperm(T, 3);
struct open *o;
if (flags < 0)
return luaL_error(T, "invalid mode string");
o = lem_xmalloc(sizeof(struct open));
o->path = path;
o->fd = perm;
o->flags = flags;
lem_async_do(&o->a, T, io_open_work, io_open_reap);
lua_settop(T, 1);
lua_pushvalue(T, lua_upvalueindex(1));
lua_pushvalue(T, lua_upvalueindex(2));
return lua_yield(T, 3);
}
static void
push_stdstream(lua_State *L, int fd)
{
struct stream *s;
/* make the socket non-blocking */
if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1)
luaL_error(L, "error making fd %d non-blocking: %s",
fd, strerror(errno));
s = stream_new(L, fd, -2);
s->open = 2; /* don't close this in __gc() */
}
int
luaopen_lem_io_core(lua_State *L)
{
/* create module table */
lua_newtable(L);
/* create File metatable */
lua_newtable(L);
/* mt.__index = mt */
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
/* mt.__gc = */
lua_pushcfunction(L, file_gc);
lua_setfield(L, -2, "__gc");
/* mt.closed = */
lua_pushcfunction(L, file_closed);
lua_setfield(L, -2, "closed");
/* mt.close = */
lua_pushcfunction(L, file_close);
lua_setfield(L, -2, "close");
/* mt.readp = */
lua_pushcfunction(L, file_readp);
lua_setfield(L, -2, "readp");
/* mt.write = */
lua_pushcfunction(L, file_write);
lua_setfield(L, -2, "write");
/* mt.size = */
lua_pushcfunction(L, file_size);
lua_setfield(L, -2, "size");
/* mt.seek = */
lua_pushcfunction(L, file_seek);
lua_setfield(L, -2, "seek");
/* mt.lock = */
lua_pushcfunction(L, file_lock);
lua_setfield(L, -2, "lock");
/* insert table */
lua_setfield(L, -2, "File");
/* create Stream metatable */
lua_newtable(L);
/* mt.__index = mt */
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
/* mt.__gc = */
lua_pushcfunction(L, stream_gc);
lua_setfield(L, -2, "__gc");
/* mt.closed = */
lua_pushcfunction(L, stream_closed);
lua_setfield(L, -2, "closed");
/* mt.close = */
lua_pushcfunction(L, stream_close);
lua_setfield(L, -2, "close");
/* mt.readp = */
lua_pushcfunction(L, stream_readp);
lua_setfield(L, -2, "readp");
/* mt.write = */
lua_pushcfunction(L, stream_write);
lua_setfield(L, -2, "write");
/* mt.cork = */
lua_pushcfunction(L, stream_cork);
lua_setfield(L, -2, "cork");
/* mt.uncork = */
lua_pushcfunction(L, stream_uncork);
lua_setfield(L, -2, "uncork");
/* mt.getpeer = */
lua_pushcfunction(L, stream_getpeer);
lua_setfield(L, -2, "getpeer");
/* mt.sendfile = */
lua_pushcfunction(L, stream_sendfile);
lua_setfield(L, -2, "sendfile");
/* insert io.stdin stream */
push_stdstream(L, STDIN_FILENO);
lua_setfield(L, -3, "stdin");
/* insert io.stdout stream */
push_stdstream(L, STDOUT_FILENO);
lua_setfield(L, -3, "stdout");
/* insert io.stderr stream */
push_stdstream(L, STDERR_FILENO);
lua_setfield(L, -3, "stderr");
/* insert table */
lua_setfield(L, -2, "Stream");
/* create Server metatable */
lua_newtable(L);
/* mt.__index = mt */
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
/* mt.__gc = */
lua_pushcfunction(L, server_close);
lua_setfield(L, -2, "__gc");
/* mt.closed = */
lua_pushcfunction(L, server_closed);
lua_setfield(L, -2, "closed");
/* mt.busy = */
lua_pushcfunction(L, server_busy);
lua_setfield(L, -2, "busy");
/* mt.close = */
lua_pushcfunction(L, server_close);
lua_setfield(L, -2, "close");
/* mt.interrupt = */
lua_pushcfunction(L, server_interrupt);
lua_setfield(L, -2, "interrupt");
/* mt.accept = */
lua_getfield(L, -2, "Stream"); /* upvalue 1 = Stream */
lua_pushcclosure(L, server_accept, 1);
lua_setfield(L, -2, "accept");
/* mt.autospawn = */
lua_getfield(L, -2, "Stream"); /* upvalue 1 = Stream */
lua_pushcclosure(L, server_autospawn, 1);
lua_setfield(L, -2, "autospawn");
/* insert table */
lua_setfield(L, -2, "Server");
/* insert open function */
lua_getfield(L, -1, "File"); /* upvalue 1 = File */
lua_getfield(L, -2, "Stream"); /* upvalue 2 = Stream */
lua_pushcclosure(L, io_open, 2);
lua_setfield(L, -2, "open");
/* insert popen function */
lua_getfield(L, -1, "Stream"); /* upvalue 1 = Stream */
lua_pushcclosure(L, stream_popen, 1);
lua_setfield(L, -2, "popen");
/* create tcp table */
lua_createtable(L, 0, 0);
/* insert the connect function */
lua_getfield(L, -2, "Stream"); /* upvalue 1 = Stream */
lua_pushcclosure(L, tcp_connect, 1);
lua_setfield(L, -2, "connect");
/* insert the listen4 function */
lua_getfield(L, -2, "Server"); /* upvalue 1 = Server */
lua_pushcclosure(L, tcp_listen4, 1);
lua_setfield(L, -2, "listen4");
/* insert the listen6 function */
lua_getfield(L, -2, "Server"); /* upvalue 1 = Server */
lua_pushcclosure(L, tcp_listen6, 1);
lua_setfield(L, -2, "listen6");
/* insert the tcp table */
lua_setfield(L, -2, "tcp");
/* create unix table */
lua_createtable(L, 0, 0);
/* insert the connect function */
lua_getfield(L, -2, "Stream"); /* upvalue 1 = Stream */
lua_pushcclosure(L, unix_connect, 1);
lua_setfield(L, -2, "connect");
/* insert the listen function */
lua_getfield(L, -2, "Server"); /* upvalue 1 = Server */
lua_pushcclosure(L, unix_listen, 1);
lua_setfield(L, -2, "listen");
/* insert the unix table */
lua_setfield(L, -2, "unix");
return 1;
}