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 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; }
/* * stream:write() method */ static int stream__write(lua_State *T, struct stream *s) { ssize_t bytes; int err; while ((bytes = write(s->w.fd, s->out, s->out_len)) > 0) { lem_debug("wrote %ld bytes to fd %d", bytes, s->w.fd); s->out += bytes; s->out_len -= bytes; while (s->out_len == 0) { if (s->idx == lua_gettop(T)) { lua_pushboolean(T, 1); return 1; } s->out = lua_tolstring(T, ++s->idx, &s->out_len); } } err = errno; lem_debug("wrote %ld bytes to fd %d", bytes, s->w.fd); if (bytes < 0 && (err == EAGAIN || err == EINTR)) return 0; s->open = 0; close(s->w.fd); if (bytes == 0 || err == ECONNRESET || err == EPIPE) return io_closed(T); return io_strerror(T, err); }
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 void defer_enable(struct pa_defer_event *ev, int b) { if (b) { lem_debug("starting"); ev_idle_start(LEM_ &ev->w); } else { lem_debug("stopping"); ev_idle_stop(LEM_ &ev->w); } }
static void stream_sendfile_work(struct lem_async *a) { struct sfhandle *sf = (struct sfhandle *)a; struct stream *s = sf->s; /* make socket blocking */ if (fcntl(s->w.fd, F_SETFL, 0) == -1) { sf->ret = errno; return; } #ifdef __FreeBSD__ off_t written; int ret = sendfile(sf->fd, s->w.fd, sf->offset, sf->size, NULL, &written, SF_SYNC); if (ret == 0) { sf->ret = 0; sf->size = written; } else sf->ret = errno; lem_debug("wrote = %ld bytes", written); #else #ifdef __APPLE__ int ret = sendfile(sf->fd, s->w.fd, sf->offset, &sf->size, NULL, 0); if (ret == 0) sf->ret = 0; else sf->ret = errno; lem_debug("wrote = %lld bytes", sf->size); #else ssize_t ret = sendfile(s->w.fd, sf->fd, &sf->offset, sf->size); if (ret >= 0) { sf->ret = 0; sf->size = ret; } else sf->ret = errno; lem_debug("wrote = %ld bytes", ret); #endif #endif /* make socket non-blocking again */ if (fcntl(s->w.fd, F_SETFL, O_NONBLOCK) == -1) { sf->ret = errno; return; } }
static struct pa_io_event * io_new(pa_mainloop_api *a, int fd, pa_io_event_flags_t events, pa_io_event_cb_t cb, void *userdata) { struct pa_io_event *ev = lem_xmalloc(sizeof(struct pa_io_event)); int revents = 0; (void)a; lem_debug("flags = %d", events); if (events & PA_IO_EVENT_INPUT) revents |= EV_READ; if (events & PA_IO_EVENT_OUTPUT) revents |= EV_WRITE; if (events & PA_IO_EVENT_HANGUP) revents |= EV_READ; ev_io_init(&ev->w, io_handler, fd, revents); ev->w.data = userdata; ev->cb = cb; ev->destroy = NULL; if (revents) ev_io_start(LEM_ &ev->w); return ev; }
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 void loop_quit(pa_mainloop_api *a, int retval) { (void)a; (void)retval; lem_debug("Hmm.. pulseaudio wants us to quit with retval %d", retval); }
static void timeout_toggle(DBusTimeout *timeout, void *data) { struct timeout *t; (void)data; lem_debug("timeout = %p, interval = %d, enabled = %s", (void *)timeout, dbus_timeout_get_interval(timeout), dbus_timeout_get_enabled(timeout) ? "true" : "false"); t = dbus_timeout_get_data(timeout); if (dbus_timeout_get_enabled(timeout)) { ev_tstamp interval = ((ev_tstamp)dbus_timeout_get_interval(timeout))/1000.0; if (ev_is_active(&t->ev)) ev_timer_stop(EV_G_ &t->ev); ev_timer_set(&t->ev, interval, interval); ev_timer_start(EV_G_ &t->ev); } else ev_timer_stop(EV_G_ &t->ev); }
static int server_autospawn(lua_State *T) { struct ev_io *w; luaL_checktype(T, 1, LUA_TUSERDATA); luaL_checktype(T, 2, LUA_TFUNCTION); w = lua_touserdata(T, 1); if (w->fd < 0) return io_closed(T); if (w->data != NULL) return io_busy(T); w->cb = server_autospawn_cb; w->data = T; ev_io_start(LEM_ w); lem_debug("yielding"); /* yield server object, function and metatable*/ lua_settop(T, 2); lua_pushvalue(T, lua_upvalueindex(1)); return lua_yield(T, 3); }
/* * Bus:close() * * argument 1: bus object */ static int bus_close(lua_State *T) { struct bus_object *obj; luaL_checktype(T, 1, LUA_TUSERDATA); obj = lua_touserdata(T, 1); if (obj->conn == NULL) { lua_pushnil(T); lua_pushliteral(T, "closed"); return 2; } lem_debug("closing DBus connection"); dbus_connection_close(obj->conn); dbus_connection_unref(obj->conn); obj->conn = NULL; lua_getfenv(T, 1); lua_pushnil(T); lua_rawseti(T, -2, 1); lua_pushnil(T); lua_rawseti(T, -2, 2); lua_pushboolean(T, 1); return 1; }
static void time_handler(EV_P_ struct ev_timer *w, int revents) { struct pa_time_event *ev = (struct pa_time_event *)w; (void)revents; lem_debug("callback.."); ev->cb(&loop_api, ev, &ev->tv, ev->w.data); }
static void time_free(pa_time_event *ev) { lem_debug("freeing event %p", ev); ev_timer_stop(LEM_ &ev->w); if (ev->destroy) ev->destroy(&loop_api, ev, ev->w.data); free(ev); }
static void defer_free(struct pa_defer_event *ev) { lem_debug("freeing event %p", ev); if (ev->destroy) ev->destroy(&loop_api, ev, ev->w.data); ev_idle_stop(LEM_ &ev->w); free(ev); }
void lem_queue(lua_State *T, int nargs) { struct lem_runqueue_slot *slot; assert(T != NULL); lem_debug("enqueueing thread with %d argument%s", nargs, nargs == 1 ? "" : "s"); if (rq.first == rq.last) ev_idle_start(LEM_ &rq.w); slot = &rq.queue[rq.last]; slot->T = T; slot->nargs = nargs; rq.last++; rq.last &= rq.mask; if (rq.first == rq.last) { unsigned int i; unsigned int j; struct lem_runqueue_slot *new_queue; lem_debug("expanding queue to %u slots", 2*(rq.mask + 1)); new_queue = lem_xmalloc(2*(rq.mask + 1) * sizeof(struct lem_runqueue_slot)); i = 0; j = rq.first; do { new_queue[i] = rq.queue[j]; i++; j++; j &= rq.mask; } while (j != rq.first); free(rq.queue); rq.queue = new_queue; rq.first = 0; rq.last = i; rq.mask = 2*rq.mask + 1; } }
/* * Proxy:parse() * * upvalue 1: Method * upvalue 2: Signal * * argument 1: proxy * argument 2: xml string */ EXPORT int lem_dbus_proxy_parse(lua_State *L) { XML_Parser p; struct parsedata pd; const char *xml; /* drop extra arguments */ lua_settop(L, 2); /* get the xml string */ xml = luaL_checkstring(L, 2); /* put the object name on the stack */ lua_getfield(L, 1, "object"); if (lua_isnil(L, 3)) return luaL_argerror(L, 2, "no object set in the proxy"); /* create parser and initialise it */ p = XML_ParserCreate("UTF-8"); if (!p) { lua_pushnil(L); lua_pushliteral(L, "out of memory"); return 2; } pd.L = L; pd.level = 0; pd.interface = 0; pd.type = 0; pd.signature[0] = '\0'; pd.result[0] = '\0'; pd.sig_next = pd.signature; pd.res_next = pd.result; XML_SetUserData(p, &pd); XML_SetElementHandler(p, start_element_handler, end_element_handler); /* now parse the xml document inserting methods as we go */ if (!XML_Parse(p, xml, strlen(xml), 1)) { lem_debug("parse error at line %d:\n%s\n", (int)XML_GetCurrentLineNumber(p), XML_ErrorString(XML_GetErrorCode(p))); lua_pushnil(L); lua_pushliteral(L, "error parsing introspection data"); return 2; } /* free the parser */ XML_ParserFree(p); /* return true */ lua_pushboolean(L, 1); return 1; }
/* * Defer Events */ static void defer_handler(EV_P_ struct ev_idle *w, int revents) { struct pa_defer_event *ev = (struct pa_defer_event *)w; (void)revents; lem_debug("callback.."); ev->cb(&loop_api, ev, ev->w.data); }
/* * stream:readp() method */ static int stream__readp(lua_State *T, struct stream *s) { ssize_t bytes; int ret; int err; enum lem_preason res; while ((bytes = read(s->r.fd, s->buf.buf + s->buf.end, LEM_INPUTBUF_SIZE - s->buf.end)) > 0) { lem_debug("read %ld bytes from %d", bytes, s->r.fd); s->buf.end += bytes; ret = s->p->process(T, &s->buf); if (ret > 0) return ret; } err = errno; lem_debug("read %ld bytes from %d", bytes, s->r.fd); if (bytes < 0 && (err == EAGAIN || err == EINTR)) return 0; if (bytes == 0 || err == ECONNRESET || err == EPIPE) res = LEM_PCLOSED; else res = LEM_PERROR; s->open = 0; close(s->r.fd); if (s->p->destroy && (ret = s->p->destroy(T, &s->buf, res)) > 0) return ret; lua_settop(T, 0); if (res == LEM_PCLOSED) return io_closed(T); return io_strerror(T, err); }
static void db_unref(struct db *db) { db->refs--; if (db->refs) return; lem_debug("db->refs = %d, freeing", db->refs); (void)sqlite3_close(db->handle); free(db); }
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 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; }
/* * Bus:send_signal() * * argument 1: bus object * argument 2: path * argument 3: interface * argument 4: name * argument 5: signature (optional) * ... */ static int bus_send_signal(lua_State *T) { DBusConnection *conn; const char *path; const char *interface; const char *name; const char *signature; DBusMessage *msg; luaL_checktype(T, 1, LUA_TUSERDATA); path = luaL_checkstring(T, 2); interface = luaL_checkstring(T, 3); name = luaL_checkstring(T, 4); signature = luaL_optstring(T, 5, NULL); conn = bus_unbox(T, 1); if (conn == NULL) { lua_pushnil(T); lua_pushliteral(T, "closed"); return 2; } if (interface && interface[0] == '\0') interface = NULL; lem_debug("%s, %s, %s", path, interface ? interface : "(no interface)", name); msg = dbus_message_new_signal(path, interface, name); if (msg == NULL) goto oom; if (signature && signature[0] != '\0' && lem_dbus_add_arguments(T, 6, signature, msg)) return luaL_error(T, "%s", lua_tostring(T, -1)); if (!dbus_connection_send(conn, msg, NULL)) goto oom; dbus_message_unref(msg); lua_pushboolean(T, 1); return 1; oom: if (msg) dbus_message_unref(msg); lua_pushnil(T); lua_pushliteral(T, "out of memory"); return 2; }
/* * DBus.__gc() * * argument 1: bus object */ static int bus_gc(lua_State *T) { DBusConnection *conn = bus_unbox(T, 1); lem_debug("collecting DBus connection"); if (conn) { dbus_connection_close(conn); dbus_connection_unref(conn); } return 0; }
static void time_restart(pa_time_event *ev, const struct timeval *tv) { ev_tstamp timeout = timeval_to_stamp(tv); lem_debug("resetting to %f seconds", timeout - ev_now(LEM)); ev->tv.tv_sec = tv->tv_sec; ev->tv.tv_usec = tv->tv_usec; ev_timer_stop(LEM_ &ev->w); ev_timer_set(&ev->w, timeout - ev_now(LEM), 0); ev_timer_start(LEM_ &ev->w); }
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; }
/* * IO Events */ static void io_handler(EV_P_ ev_io *w, int revents) { struct pa_io_event *ev = (struct pa_io_event *)w; pa_io_event_flags_t events = PA_IO_EVENT_NULL; if (revents & EV_READ) events |= PA_IO_EVENT_INPUT; if (revents & EV_WRITE) events |= PA_IO_EVENT_OUTPUT; if (revents & EV_ERROR) events |= PA_IO_EVENT_ERROR; lem_debug("flags = %d", events); ev->cb(&loop_api, ev, ev->w.fd, events, ev->w.data); }
static int file_gc(lua_State *T) { struct file *f = lua_touserdata(T, 1); lem_debug("collecting %p, fd = %d", f, f->fd); if (f->fd >= 0) { struct file_gc *gc = lem_xmalloc(sizeof(struct file_gc)); gc->fd = f->fd; f->fd = -1; lem_async_do(&gc->a, file_gc_work, NULL); } return 0; }
static void timeout_remove(DBusTimeout *timeout, void *data) { struct timeout *t; (void)data; lem_debug("timeout = %p, interval = %d, enabled = %s", (void *)timeout, dbus_timeout_get_interval(timeout), dbus_timeout_get_enabled(timeout) ? "true" : "false"); t = dbus_timeout_get_data(timeout); ev_timer_stop(EV_G_ &t->ev); free(t); }
static struct pa_defer_event * defer_new(pa_mainloop_api *a, pa_defer_event_cb_t cb, void *userdata) { struct pa_defer_event *ev = lem_xmalloc(sizeof(struct pa_defer_event)); (void)a; lem_debug("new defer %p", ev); ev_idle_init(&ev->w, defer_handler); ev->w.data = userdata; ev->cb = cb; ev->destroy = NULL; ev_idle_start(LEM_ &ev->w); return ev; }