static int stream_close(lua_State *T) { struct stream *s; luaL_checktype(T, 1, LUA_TUSERDATA); s = lua_touserdata(T, 1); if (!s->open) return io_closed(T); if (s->r.data != NULL) { ev_io_stop(LEM_ &s->r); lem_queue(s->r.data, io_closed(s->r.data)); s->r.data = NULL; } if (s->w.data != NULL) { ev_io_stop(LEM_ &s->w); lem_queue(s->w.data, io_closed(s->w.data)); s->w.data = NULL; } s->open = 0; if (close(s->r.fd)) return io_strerror(T, errno); lua_pushboolean(T, 1); return 1; }
static void file_write_reap(struct lem_async *a) { struct file *f = (struct file *)a; lua_State *T = f->T; int top; if (f->ret) { f->T = NULL; lem_queue(T, io_strerror(T, f->ret)); return; } top = lua_gettop(T); do { if (f->write.idx == top) { f->T = NULL; lua_pushboolean(T, 1); lem_queue(T, 1); return; } f->write.str = lua_tolstring(T, ++f->write.idx, &f->write.len); } while (f->write.len == 0); lem_async_run(&f->a); }
static void stmt_step_reap(struct lem_async *a) { struct db *db = (struct db *)a; lua_State *T = db->T; sqlite3_stmt *stmt = db->req.prep.stmt; int ret; switch (db->ret) { case SQLITE_ROW: lem_debug("SQLITE_ROW"); pushrow(T, stmt); ret = 1; break; case SQLITE_DONE: lem_debug("SQLITE_DONE"); (void)sqlite3_reset(stmt); (void)sqlite3_clear_bindings(stmt); lua_pushboolean(T, 1); ret = 1; break; default: lem_debug("Ack!"); lua_pushnil(T); lua_pushstring(T, sqlite3_errmsg(db->handle)); ret = 2; } lem_queue(T, ret); db->T = NULL; }
static void stream_readp_cb(EV_P_ struct ev_io *w, int revents) { struct stream *s = STREAM_FROM_WATCH(w, r); lua_State *T = s->r.data; int ret; (void)revents; if (!s->open) { ret = 0; if (s->p->destroy) ret = s->p->destroy(T, &s->buf, LEM_PCLOSED); if (ret <= 0) ret = io_closed(T); } else { ret = stream__readp(T, s); if (ret == 0) return; } ev_io_stop(EV_A_ &s->r); s->r.data = NULL; lem_queue(T, ret); }
static int sleeper_wakeup(lua_State *T) { struct ev_timer *w; lua_State *S; int nargs; luaL_checktype(T, 1, LUA_TUSERDATA); w = lua_touserdata(T, 1); S = w->data; if (S == NULL) { lua_pushnil(T); lua_pushliteral(T, "not sleeping"); return 2; } ev_timer_stop(LEM_ w); nargs = lua_gettop(T) - 1; lua_settop(S, 0); lua_xmove(T, S, nargs); lem_queue(S, nargs); w->data = NULL; /* return true */ lua_pushboolean(T, 1); return 1; }
static int queue_file(int argc, char *argv[], int fidx) { lua_State *T = lem_newthread(); const char *filename; int i; if (fidx < argc) filename = argv[fidx]; else filename = LEM_LDIR "lem/repl.lua"; switch (luaL_loadfile(T, filename)) { case LUA_OK: /* success */ break; case LUA_ERRMEM: oom(); default: lem_log_error("lem: %s", lua_tostring(T, 1)); return -1; } lua_createtable(T, argc, 0); for (i = 0; i < argc; i++) { lua_pushstring(T, argv[i]); lua_rawseti(T, -2, i - fidx); } lua_setglobal(T, "arg"); lem_queue(T, 0); return 0; }
static void file_seek_reap(struct lem_async *a) { struct file *f = (struct file *)a; lua_State *T = f->T; f->T = NULL; if (f->ret) { lem_queue(T, io_strerror(T, f->ret)); return; } lua_pushnumber(T, f->seek.offset); lem_queue(T, 1); }
static void db_open_reap(struct lem_async *a) { struct db *db = (struct db *)a; lua_State *T = db->T; int ret; if (db->handle == NULL) { lem_debug("db->handle == NULL"); lua_pushnil(T); lua_pushliteral(T, "out of memory"); db_unref(db); ret = 2; } else if (db->ret != SQLITE_OK) { lem_debug("db->ret != SQLITE_OK"); lua_pushnil(T); lua_pushstring(T, sqlite3_errmsg(db->handle)); db_unref(db); ret = 2; } else { struct box *box = lua_newuserdata(T, sizeof(struct box)); lem_debug("Success!"); box->db = db; /* set metatable */ lua_pushvalue(T, 2); lua_setmetatable(T, -2); ret = 1; } lem_queue(T, ret); db->T = NULL; }
static DBusHandlerResult signal_handler(lua_State *S, DBusMessage *msg) { lua_State *T; const char *path = dbus_message_get_path(msg); const char *interface = dbus_message_get_interface(msg); const char *member = dbus_message_get_member(msg); lem_debug("received signal\n %s\n %s\n %s(%s)", path, interface, member, dbus_message_get_signature(msg)); /* NOTE: this magic string representation of an * incoming signal must match the one in the Lua code */ lua_pushfstring(S, "%s\n%s\n%s", path ? path : "", interface ? interface : "", member ? member : ""); lua_rawget(S, LEM_DBUS_SIGNAL_TABLE); if (lua_type(S, -1) != LUA_TFUNCTION) { lua_settop(S, LEM_DBUS_TOP); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } /* create new thread */ T = lem_newthread(); lua_xmove(S, T, 1); lem_queue(T, lem_dbus_push_arguments(T, msg)); return DBUS_HANDLER_RESULT_HANDLED; }
static int server_close(lua_State *T) { struct ev_io *w; int ret; luaL_checktype(T, 1, LUA_TUSERDATA); w = lua_touserdata(T, 1); if (w->fd < 0) return io_closed(T); if (w->data != NULL) { lem_debug("interrupting listen"); ev_io_stop(LEM_ w); lua_pushnil(w->data); lua_pushliteral(w->data, "interrupted"); lem_queue(w->data, 2); w->data = NULL; } lem_debug("closing server.."); ret = close(w->fd); w->fd = -1; if (ret) return io_strerror(T, errno); lua_pushboolean(T, 1); return 1; }
static void file_close_reap(struct lem_async *a) { struct file *f = (struct file *)a; lua_State *T = f->T; f->T = NULL; f->fd = -1; if (f->ret) { lem_queue(T, io_strerror(T, f->ret)); return; } lua_pushboolean(T, 1); lem_queue(T, 1); }
static void sleep_handler(EV_P_ struct ev_timer *w, int revents) { lua_State *T = w->data; (void)revents; /* return nil, "timeout" */ lem_queue(T, 2); w->data = NULL; }
static void signal_child_handler(EV_P_ struct ev_child *w, int revents) { lua_State *S; int status; int pid = w->pid; int rpid = w->rpid; (void)revents; S = lem_newthread(); lua_pushlightuserdata(S, &sigmap); lua_rawget(S, LUA_REGISTRYINDEX); if (lua_type(S, 1) != LUA_TFUNCTION) { lem_forgetthread(S); return; } lua_pushinteger(S, SIGCHLD); status = w->rstatus; lua_createtable(S, 0, 5); lua_pushinteger(S, pid); lua_setfield(S, -2, "pid"); lua_pushinteger(S, rpid); lua_setfield(S, -2, "rpid"); if (WIFEXITED(status)) { lua_pushinteger(S, WEXITSTATUS(status)); lua_setfield(S, -2, "status"); lua_pushstring(S, "exited"); } else if (WIFSIGNALED(status)) { lua_pushinteger(S, WTERMSIG(status)); lua_setfield(S, -2, "signal"); #ifdef WCOREDUMP lua_pushboolean(S, WCOREDUMP(status)); lua_setfield(S, -2, "coredumped"); #endif lua_pushstring(S, "signaled"); } else if (WIFSTOPPED(status)) { lua_pushinteger(S, WSTOPSIG(status)); lua_setfield(S, -2, "signal"); lua_pushstring(S, "stopped"); } else if (WIFCONTINUED(status)) { lua_pushstring(S, "continued"); } else { assert(0); /* XXX do something more graceful */ } lua_setfield(S, -2, "type"); lem_queue(S, 2); }
static DBusHandlerResult method_call_handler(lua_State *S, DBusMessage *msg) { lua_State *T; struct message_object *m; const char *path = dbus_message_get_path(msg); const char *interface = dbus_message_get_interface(msg); const char *member = dbus_message_get_member(msg); lem_debug("received call\n %s\n %s\n %s(%s)", path, interface, member, dbus_message_get_signature(msg)); lua_pushstring(S, path ? path : ""); lua_rawget(S, LEM_DBUS_OBJECT_TABLE); if (lua_type(S, -1) != LUA_TTABLE) { lua_settop(S, LEM_DBUS_TOP); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } lua_pushfstring(S, "%s.%s", interface ? interface : "", member ? member : ""); lua_rawget(S, -2); if (lua_type(S, -1) != LUA_TFUNCTION) { lua_settop(S, LEM_DBUS_TOP); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } /* create new thread */ T = lem_newthread(); lua_pushvalue(S, LEM_DBUS_BUS_OBJECT); lua_xmove(S, T, 2); lua_settop(S, LEM_DBUS_TOP); /* push the send_reply function */ m = lua_newuserdata(T, sizeof(struct message_object)); m->msg = msg; dbus_message_ref(msg); /* set metatable */ lua_pushvalue(S, LEM_DBUS_MESSAGE_META); lua_xmove(S, T, 1); lua_setmetatable(T, -2); lua_pushcclosure(T, message_reply, 2); lem_queue(T, lem_dbus_push_arguments(T, msg) + 1); return DBUS_HANDLER_RESULT_HANDLED; }
static int utils_resume(lua_State *T) { int args; lua_State *S; luaL_checktype(T, 1, LUA_TTHREAD); S = lua_tothread(T, 1); args = lua_gettop(T) - 1; lua_xmove(T, S, args); lem_queue(S, args); return 0; }
static void file_readp_reap(struct lem_async *a) { struct file *f = (struct file *)a; lua_State *T = f->T; int ret; if (f->ret) { enum lem_preason res = f->ret < 0 ? LEM_PCLOSED : LEM_PERROR; f->T = NULL; if (f->readp.p->destroy && (ret = f->readp.p->destroy(T, &f->buf, res)) > 0) { lem_queue(T, ret); return; } lua_pushnil(T); if (res == LEM_PCLOSED) lua_pushliteral(T, "eof"); else lua_pushstring(T, strerror(errno)); lem_queue(T, 2); return; } ret = f->readp.p->process(T, &f->buf); if (ret > 0) { f->T = NULL; lem_queue(T, ret); return; } lem_async_run(&f->a); }
static void connect_handler(EV_P_ struct ev_io *w, int revents) { struct lem_ssl_stream *s = (struct lem_ssl_stream *)w; int ret; (void)revents; ret = stream_check_error(s->T, s, SSL_connect(s->ssl), "error establishing SSL connection: %s"); if (ret == 0) return; stream_io_unregister(s); lem_queue(s->T, ret); s->T = NULL; }
static void db_exec_step_reap(struct lem_async *a) { struct db *db = (struct db *)a; lua_State *T = db->T; int ret; lem_debug("db->ret = %s", db->ret == SQLITE_ROW ? "SQLITE_ROW" : db->ret == SQLITE_DONE ? "SQLITE_DONE" : "error"); switch (db->ret) { case SQLITE_ROW: case SQLITE_DONE: break; default: lua_pushnil(T); lua_pushstring(T, sqlite3_errmsg(db->handle)); (void)sqlite3_finalize(db->req.prep.stmt); ret = 2; goto out; } if (sqlite3_finalize(db->req.prep.stmt) != SQLITE_OK) { lua_pushnil(T); lua_pushstring(T, sqlite3_errmsg(db->handle)); ret = 2; goto out; } if (db->req.prep.tail == NULL) { lua_pushboolean(T, 1); ret = 1; goto out; } db->req.prep.len -= db->req.prep.tail - db->req.prep.sql; db->req.prep.sql = db->req.prep.tail; db->a.work = db_prepare_work; db->a.reap = db_exec_prepare_reap; lem_async_run(&db->a); return; out: lem_queue(T, ret); db->T = NULL; }
static int utils_spawn(lua_State *T) { lua_State *S; int nargs; luaL_checktype(T, 1, LUA_TFUNCTION); S = lem_newthread(); nargs = lua_gettop(T); lua_xmove(T, S, nargs); lem_queue(S, nargs - 1); lua_pushboolean(T, 1); return 1; }
static void signal_os_handler(EV_P_ struct ev_signal *w, int revents) { lua_State *S; (void)revents; S = lem_newthread(); lua_pushlightuserdata(S, &sigmap); lua_rawget(S, LUA_REGISTRYINDEX); if (lua_type(S, 1) != LUA_TFUNCTION) { lem_forgetthread(S); return; } lua_pushinteger(S, w->signum); lem_queue(S, 1); }
static void method_return_handler(DBusPendingCall *pending, void *data) { lua_State *T = data; DBusMessage *msg = dbus_pending_call_steal_reply(pending); int nargs; dbus_pending_call_unref(pending); lem_debug("received return(%s)", dbus_message_get_signature(msg)); if (msg == NULL) { lua_pushnil(T); lua_pushliteral(T, "null reply"); nargs = 2; } else { switch (dbus_message_get_type(msg)) { case DBUS_MESSAGE_TYPE_METHOD_RETURN: nargs = lem_dbus_push_arguments(T, msg); break; case DBUS_MESSAGE_TYPE_ERROR: lua_pushnil(T); { DBusError err; dbus_error_init(&err); dbus_set_error_from_message(&err, msg); lua_pushstring(T, err.message); dbus_error_free(&err); } nargs = 2; break; default: lua_pushnil(T); lua_pushliteral(T, "unknown reply"); nargs = 2; } dbus_message_unref(msg); } lem_queue(T, nargs); }
static void server_accept_cb(EV_P_ struct ev_io *w, int revents) { int ret; (void)revents; ret = server__accept(w->data, w, 2); if (ret == 0) return; w->data = NULL; ev_io_stop(EV_A_ w); if (ret == 2) { close(w->fd); w->fd = -1; } lem_queue(w->data, ret); }
static void stream_write_cb(EV_P_ struct ev_io *w, int revents) { struct stream *s = STREAM_FROM_WATCH(w, w); lua_State *T = s->w.data; int ret; (void)revents; if (!s->open) ret = io_closed(T); else { ret = stream__write(T, s); if (ret == 0) return; } ev_io_stop(EV_A_ &s->w); s->w.data = NULL; lem_queue(T, ret); }
static void db_exec_prepare_reap(struct lem_async *a) { struct db *db = (struct db *)a; lua_State *T = db->T; int ret; if (db->ret != SQLITE_OK) { lem_debug("db->ret != SQLITE_OK"); lua_pushnil(T); lua_pushstring(T, sqlite3_errmsg(db->handle)); ret = 2; goto out; } lem_debug("db->ret == SQLITE_OK"); if (db->req.prep.stmt == NULL) { lem_debug("db->req.prep.stmt == NULL"); lua_pushboolean(T, 1); ret = 1; goto out; } if (lua_gettop(T) > 1 && bindtable(T, db->req.prep.stmt)) { (void)sqlite3_finalize(db->req.prep.stmt); lua_pushnil(T); lua_insert(T, -2); ret = 2; goto out; } db->a.work = db_step_work; db->a.reap = db_exec_step_reap; lem_async_run(&db->a); return; out: lem_queue(T, ret); db->T = NULL; }
static int sleeper_sleep(lua_State *T) { struct ev_timer *w; luaL_checktype(T, 1, LUA_TUSERDATA); w = lua_touserdata(T, 1); if (w->data != NULL) { lua_pushnil(T); lua_pushliteral(T, "busy"); return 2; } if (lua_gettop(T) > 1 && !lua_isnil(T, 2)) { ev_tstamp delay = (ev_tstamp)luaL_checknumber(T, 2); if (delay <= 0) { /* return nil, "timeout" * ..but yield the thread */ lua_pushnil(T); lua_pushvalue(T, lua_upvalueindex(1)); lem_queue(T, 2); return lua_yield(T, 2); } ev_timer_set(w, delay, 0); ev_timer_start(LEM_ w); } w->data = T; /* yield sleeper, nil, "timeout" */ lua_settop(T, 1); lua_pushnil(T); lua_pushvalue(T, lua_upvalueindex(1)); return lua_yield(T, 3); }
static void stream_sendfile_reap(struct lem_async *a) { struct sfhandle *sf = (struct sfhandle *)a; lua_State *T = sf->T; struct stream *s = sf->s; int ret; if (sf->ret == 0) { lua_pushinteger(T, sf->size); ret = 1; } else { if (s->open) close(s->w.fd); s->open = 0; ret = io_strerror(T, sf->ret); } free(sf); s->w.data = NULL; lem_queue(T, ret); }
static int server_interrupt(lua_State *T) { struct ev_io *w; luaL_checktype(T, 1, LUA_TUSERDATA); w = lua_touserdata(T, 1); if (w->data == NULL) { lua_pushnil(T); lua_pushliteral(T, "not busy"); return 2; } lem_debug("interrupting listening"); ev_io_stop(LEM_ w); lua_pushnil(w->data); lua_pushliteral(w->data, "interrupted"); lem_queue(w->data, 2); w->data = NULL; lua_pushboolean(T, 1); return 1; }
static void db_prepare_reap(struct lem_async *a) { struct db *db = (struct db *)a; lua_State *T = db->T; int ret; if (db->ret != SQLITE_OK) { lem_debug("db->ret != SQLITE_OK"); lua_pushnil(T); lua_pushstring(T, sqlite3_errmsg(db->handle)); ret = 2; db_unref(db); } else if (db->req.prep.stmt == NULL) { lem_debug("db->req.prep.stmt == NULL"); lua_pushnil(T); lua_pushliteral(T, "nosql"); ret = 2; db_unref(db); } else { struct stmt *stmt = lua_newuserdata(T, sizeof(struct stmt)); lem_debug("Success!"); stmt->handle = db->req.prep.stmt; stmt->db = db; /* set metatable */ lua_pushvalue(T, 3); lua_setmetatable(T, -2); ret = 1; } lem_queue(T, ret); db->T = NULL; }
static void server_autospawn_cb(EV_P_ struct ev_io *w, int revents) { lua_State *T = w->data; int sock; lua_State *S; (void)revents; /* dequeue the incoming connection */ #ifdef SOCK_CLOEXEC sock = accept4(w->fd, NULL, NULL, SOCK_CLOEXEC | SOCK_NONBLOCK); #else sock = accept(w->fd, NULL, NULL); #endif if (sock < 0) { switch (errno) { case EAGAIN: case EINTR: case ECONNABORTED: case ENETDOWN: case EPROTO: case ENOPROTOOPT: case EHOSTDOWN: #ifdef ENONET case ENONET: #endif case EHOSTUNREACH: case EOPNOTSUPP: case ENETUNREACH: return; } lua_pushnil(T); lua_pushfstring(T, "error accepting connection: %s", strerror(errno)); goto error; } #ifndef SOCK_CLOEXEC /* set FD_CLOEXEC and make the socket non-blocking */ if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1 || fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { close(sock); lua_pushnil(T); lua_pushfstring(T, "error setting socket flags: %s", strerror(errno)); goto error; } #endif S = lem_newthread(); /* copy handler function */ lua_pushvalue(T, 2); /* create stream */ stream_new(T, sock, 3); /* move function and stream to new thread */ lua_xmove(T, S, 2); lem_queue(S, 1); return; error: ev_io_stop(EV_A_ w); close(w->fd); w->fd = -1; w->data = NULL; lem_queue(T, 2); }
static int utils_yield(lua_State *T) { lem_queue(T, 0); return lua_yield(T, 0); }