#include "plop.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "connection.h" #include "stream.h" #include "response.h" /// 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 plop_make_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; } /// Handles client events. /// \param L Server's Lua state /// \param event Event for client /// \return -1 if an error occured int plop_handle_client(lua_State * L, struct epoll_event * event) { struct connection * c = (struct connection *) event->data.ptr; int nargs = 0; if (NULL == c->L) { c->L = lua_newthread(L); if (NULL == c->L) { return -1; // TODO: Fail only this connection? } c->ref = luaL_ref(L, LUA_REGISTRYINDEX); lua_getglobal(c->L, "handler"); stream_push_new(c->L, c->fd); nargs = 1; } int result = lua_resume(c->L, NULL, nargs); switch (result) { case LUA_OK: { int n = lua_gettop(c->L); if (0 == n) { lua_pushnil(c->L); } response_send(c->L, c->fd); connection_free(L, c); return 0; } case LUA_YIELD: { return 0; } 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); connection_free(L, c); return -1; } } } /// Accepts awaiting connections if any. /// \param L Server's Lua state /// \param efd File descriptor for epoll's context /// \param server File descriptor for server socket /// \return -1 if an error occured int plop_handle_server(lua_State * L, const int efd, const int server) { struct epoll_event e; e.events = EPOLLIN; // TODO: Add EPOLLOUT? 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: Retriage this error at some point. } struct connection * connection = connection_new(L, client); if (NULL == connection) { return -1; } connection->fd = client; e.data.ptr = connection; if (-1 == epoll_ctl(efd, EPOLL_CTL_ADD, client, &e)) { return -1; } return 0; }