From e1c7b8dcaf5965e3b2894fa43132f731355afe10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Janouch?= Date: Tue, 5 Jan 2016 22:12:22 +0100 Subject: [PATCH] degesch: Lua: halfplement a connector wrapper You can't do anything reasonable with the socket now. --- degesch.c | 242 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 237 insertions(+), 5 deletions(-) diff --git a/degesch.c b/degesch.c index 5d1186c..c8aa67f 100644 --- a/degesch.c +++ b/degesch.c @@ -7877,7 +7877,7 @@ lua_schema_item_gc (lua_State *L) return 0; } -static luaL_Reg lua_schema_item_table[] = +static luaL_Reg lua_schema_table[] = { { "__gc", lua_schema_item_gc }, { NULL, NULL } @@ -8117,12 +8117,242 @@ lua_plugin_setup_config (lua_State *L) return 0; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +/// Identifier for the Lua metatable +#define XLUA_CONNECTION_METATABLE "connection" + +struct lua_connection +{ + struct lua_plugin *plugin; ///< The plugin we belong to + struct poller_fd socket_event; ///< Socket is ready + int socket_fd; ///< Underlying connected socket +}; + +static int +lua_connection_close (lua_State *L) +{ + struct lua_connection *self = + luaL_checkudata (L, 1, XLUA_CONNECTION_METATABLE); + + if (self->socket_fd != -1) + { + poller_fd_reset (&self->socket_event); + xclose (self->socket_fd); + self->socket_fd = -1; + } + + // Connection is dead, we don't need to hold onto any resources anymore + lua_cache_invalidate (L, self); + return 0; +} + +static luaL_Reg lua_connection_table[] = +{ + { "close", lua_connection_close }, + { "__gc", lua_connection_close }, + { NULL, NULL } +}; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +static void +lua_connection_on_ready (const struct pollfd *pfd, struct lua_connection *self) +{ + // TODO: handle the event, invoke on_data, on_close, on_error from + // our associated uservalue table as needed + lua_cache_invalidate (self->plugin->L, self); +} + +static struct lua_connection * +lua_plugin_push_connection (struct lua_plugin *plugin, int socket_fd) +{ + lua_State *L = plugin->L; + + struct lua_connection *self = lua_newuserdata (L, sizeof *self); + luaL_setmetatable (L, XLUA_CONNECTION_METATABLE); + memset (self, 0, sizeof *self); + self->plugin = plugin; + + poller_fd_init (&self->socket_event, &plugin->ctx->poller, + (self->socket_fd = socket_fd)); + self->socket_event.dispatcher = (poller_fd_fn) lua_connection_on_ready; + self->socket_event.user_data = self; + poller_fd_set (&self->socket_event, POLLIN); + + // Make sure the connection doesn't get garbage collected and return it + lua_cache_store (L, self, -1); + return self; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +/// Identifier for the Lua metatable +#define XLUA_CONNECTOR_METATABLE "connector" + +struct lua_connector +{ + struct lua_plugin *plugin; ///< The plugin we belong to + struct connector connector; ///< Connector object + bool active; ///< Whether the connector is alive + + int ref_on_success; ///< Reference to "on_success" callback + int ref_on_error; ///< Reference to "on_error" callback + + char *last_error; ///< Connecting error, if any +}; + +static void +lua_connector_discard (struct lua_connector *self) +{ + if (self->active) + { + connector_free (&self->connector); + self->active = false; + + luaL_unref (self->plugin->L, LUA_REGISTRYINDEX, self->ref_on_success); + luaL_unref (self->plugin->L, LUA_REGISTRYINDEX, self->ref_on_error); + self->ref_on_success = LUA_REFNIL; + self->ref_on_error = LUA_REFNIL; + } + + free (self->last_error); + self->last_error = NULL; + + lua_cache_invalidate (self->plugin->L, self); +} + +static int +lua_connector_abort (lua_State *L) +{ + struct lua_connector *self = + luaL_checkudata (L, 1, XLUA_CONNECTOR_METATABLE); + lua_connector_discard (self); + return 0; +} + +static luaL_Reg lua_connector_table[] = +{ + { "abort", lua_connector_abort }, + { "__gc", lua_connector_abort }, + { NULL, NULL } +}; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +static void +lua_connector_on_connected (void *user_data, int socket, const char *hostname) +{ + struct lua_connector *self = user_data; + struct lua_plugin *plugin = self->plugin; + lua_State *L = plugin->L; + + // TODO: use this for SNI once TLS is implemented + (void) hostname; + + if (self->ref_on_success != LUA_REFNIL) + { + lua_pushcfunction (L, lua_plugin_error_handler); + + lua_rawgeti (L, LUA_REGISTRYINDEX, self->ref_on_success); + lua_plugin_push_connection (plugin, socket); // 1: connection + + if (lua_pcall (L, 1, 0, -3)) + { + struct error *e = NULL; + (void) lua_plugin_process_error (plugin, lua_tostring (L, -1), &e); + lua_pop (L, 1); + + log_global_error (plugin->ctx, "Lua: plugin \"#s\": #s: #s", + plugin->super.name, "connector on_success", e->message); + error_free (e); + } + } + lua_connector_discard (self); +} + +static void +lua_connector_on_failure (void *user_data) +{ + struct lua_connector *self = user_data; + struct lua_plugin *plugin = self->plugin; + lua_State *L = plugin->L; + + if (self->ref_on_error != LUA_REFNIL) + { + lua_pushcfunction (L, lua_plugin_error_handler); + + lua_rawgeti (L, LUA_REGISTRYINDEX, self->ref_on_error); + lua_pushstring (L, self->last_error); // 1: error string + + if (lua_pcall (L, 1, 0, -3)) + { + struct error *e = NULL; + (void) lua_plugin_process_error (plugin, lua_tostring (L, -1), &e); + lua_pop (L, 1); + + log_global_error (plugin->ctx, "Lua: plugin \"#s\": #s: #s", + plugin->super.name, "connector on_error", e->message); + error_free (e); + } + } + lua_connector_discard (self); +} + +static void +lua_connector_on_error (void *user_data, const char *error) +{ + struct lua_connector *self = user_data; + free (self->last_error); + self->last_error = xstrdup (error); +} + +static int +lua_plugin_connect (lua_State *L) +{ + struct lua_plugin *plugin = lua_touserdata (L, lua_upvalueindex (1)); + const char *host = luaL_checkstring (L, 1); + const char *service = luaL_checkstring (L, 2); + luaL_checktype (L, 3, LUA_TTABLE); + + struct lua_connector *self = lua_newuserdata (L, sizeof *self); + luaL_setmetatable (L, XLUA_CONNECTOR_METATABLE); + memset (self, 0, sizeof *self); + + self->plugin = plugin; + self->ref_on_success = LUA_REFNIL; + self->ref_on_error = LUA_REFNIL; + + if (lua_plugin_check_field (L, 3, "on_success", LUA_TFUNCTION, true)) + self->ref_on_success = luaL_ref (L, -1); + if (lua_plugin_check_field (L, 3, "on_error", LUA_TFUNCTION, true)) + self->ref_on_error = luaL_ref (L, -1); + + lua_pop (L, 2); + + struct app_context *ctx = plugin->ctx; + struct connector *connector = &self->connector; + connector_init (connector, &ctx->poller); + connector_add_target (connector, host, service); + + connector->on_connected = lua_connector_on_connected; + connector->on_connecting = NULL; + connector->on_error = lua_connector_on_error; + connector->on_failure = lua_connector_on_failure; + connector->user_data = self; + + self->active = true; + lua_cache_store (L, self, -1); + return 1; +} + static luaL_Reg lua_plugin_library[] = { { "hook_input", lua_plugin_hook_input }, { "hook_irc", lua_plugin_hook_irc }, { "hook_timer", lua_plugin_hook_timer }, { "setup_config", lua_plugin_setup_config }, + { "connect", lua_plugin_connect }, { NULL, NULL }, }; @@ -8250,10 +8480,12 @@ lua_plugin_load (struct app_context *ctx, const char *filename, lua_setglobal (L, PROGRAM_NAME); // Create metatables for our objects - lua_plugin_create_meta (L, XLUA_HOOK_METATABLE, lua_hook_table); - lua_plugin_create_meta (L, XLUA_BUFFER_METATABLE, lua_buffer_table); - lua_plugin_create_meta (L, XLUA_SERVER_METATABLE, lua_server_table); - lua_plugin_create_meta (L, XLUA_SCHEMA_METATABLE, lua_schema_item_table); + lua_plugin_create_meta (L, XLUA_HOOK_METATABLE, lua_hook_table); + lua_plugin_create_meta (L, XLUA_BUFFER_METATABLE, lua_buffer_table); + lua_plugin_create_meta (L, XLUA_SERVER_METATABLE, lua_server_table); + lua_plugin_create_meta (L, XLUA_SCHEMA_METATABLE, lua_schema_table); + lua_plugin_create_meta (L, XLUA_CONNECTION_METATABLE, lua_connection_table); + lua_plugin_create_meta (L, XLUA_CONNECTOR_METATABLE, lua_connector_table); int ret; if (!(ret = luaL_loadfile (L, filename))