#include "plop.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "connection.h" #include "stream.h" struct plop plop = { .handler = PLOP_DEFAULT_HANDLER, .L = NULL, .fds = NULL, .data = NULL, .nfds = 0, }; /// Initializes new Lua state for the server. /// \return Lua state lua_State * plop_initialize_lua(void) { lua_State * L = luaL_newstate(); if (NULL == L) { return NULL; } luaL_openlibs(L); return L; } /// Tries to create, bind and start listening on INET server socket. /// \param node Hostname /// \param service Port /// \return File descriptor of the socket or -1 in case of an error /// \see getaddrinfo(3) // TODO: Handle UNIX sockets int open_server(const char * node, const char * service) { struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_flags = AI_PASSIVE, }; struct addrinfo * result; struct addrinfo * it; if (0 != getaddrinfo(node, service, &hints, &result)) { return -1; // TODO: Handle errors properly } int server; int optval = 1; for (it = result; it != NULL; it = it->ai_next) { server = socket(it->ai_family, it->ai_socktype, it->ai_protocol); if (-1 == server) continue; if (-1 == setsockopt(server, SOL_SOCKET, SO_REUSEADDR, (char *) &optval, sizeof(optval))) { close(server); continue; } if (-1 == fcntl(server, F_SETFL, (fcntl(server, F_GETFL, 0) | O_NONBLOCK))) { close(server); continue; } if (0 == bind(server, it->ai_addr,it->ai_addrlen)) break; close(server); } if (it == NULL) { server = -1; // TODO: Handle errors properly } freeaddrinfo(result); if (-1 == server || -1 == listen(server, 5)) { return -1; // TODO: Handle errors properly } return server; } /// Loads a file in path, executes it, then puts first result into package.loaded table and into global table. /// \param L Lua state to load the handler into /// \param path Path the script that returns handle is located at /// \param LUA_OK if successful, appropriate Lua error otherwise int plop_load_handler(lua_State * L, const char * path) { lua_getglobal(L, "package"); lua_pushliteral(L, "loaded"); int result = lua_rawget(L, -2); if (LUA_TTABLE != result) { lua_pop(L, 2); return LUA_ERRRUN; } result = luaL_loadfile(L, path) || lua_pcall(L, 0, 1, 0); if (LUA_OK != result) { lua_pop(L, 3); return result; } lua_pushliteral(L, "handler"); lua_pushvalue(L, -2); lua_rawset(L, -4); lua_setglobal(L, "handler"); lua_pop(L, 1); return LUA_OK; } /// Drops Lua thread referenced by *ref* in the registry of main Lua state. void plop_drop_thread(const int ref) { if (LUA_NOREF != ref) luaL_unref(plop.L, LUA_REGISTRYINDEX, ref); } /// Handles client events. /// \param c Connection associated with the client /// \return -1 if an error occured, 1 if expecting to continue int plop_handle_client(struct connection * c) { int nargs = 0; if (c->push) { lua_getglobal(c->L, "handler"); stream_push_new(c->L, c->fd); nargs = 1; c->push = 0; } int nresults = 0; int result = lua_resume(c->L, NULL, nargs, &nresults); switch (result) { case LUA_OK: { // TODO: If keep-alive, then reset c->push? plop_drop_thread(c->ref); connection_free(c); return 0; } case LUA_YIELD: { return 1; } default: { const char * err = lua_tostring(c->L, -1); if (NULL == err) { err = "Unknown error ocurred"; } dprintf(2, "%s\n", err); lua_pop(c->L, 1); plop_drop_thread(c->ref); connection_free(c); return -1; } } } /// Accepts awaiting connections if any. /// \param server File descriptor for server socket /// \return -1 if an error occured int plop_handle_server(const int server) { int i = 1; // Always skip server descriptor. for (; i <= plop.nfds; ++i) { if (-1 == plop.fds[i].fd) break; if (plop.nfds == i) return 0; } const int client = accept(server, NULL, NULL); if (-1 == client) { switch (errno) { case EAGAIN: #if EWOULDBLOCK != EAGAIN case EWOULDBLOCK: #endif case ENETDOWN: case EPROTO: case ENOPROTOOPT: case EHOSTDOWN: case ENONET: case EHOSTUNREACH: case EOPNOTSUPP: case ENETUNREACH: case ECONNABORTED: case EMFILE: case ENFILE: case ENOBUFS: case ENOMEM: { return 0; } default: { return -1; } } } if (-1 == fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) | O_NONBLOCK)) { close(client); return 0; // TODO: Revisit error handling in server. } struct connection * connection = connection_new(client); if (NULL == connection) return -1; connection->L = lua_newthread(plop.L); if (NULL == connection->L) return -1; // TODO: Revisit error handling. connection->ref = luaL_ref(plop.L, LUA_REGISTRYINDEX); connection->fd = client; connection->push = 1; plop.fds[i].fd = client; plop.data[i] = connection; return 0; }