implement nexus_shutdown, lua_modules/lua_module, and lua_nexus
authorTero Marttila <terom@fixme.fi>
Wed, 01 Apr 2009 00:27:39 +0300
changeset 103 454aea1e4f11
parent 102 af4190b743a5
child 104 fc196bb4bcc2
implement nexus_shutdown, lua_modules/lua_module, and lua_nexus
src/lua_objs.c
src/module.c
src/module.h
src/nexus.c
src/nexus.h
--- a/src/lua_objs.c	Wed Apr 01 00:27:03 2009 +0300
+++ b/src/lua_objs.c	Wed Apr 01 00:27:39 2009 +0300
@@ -39,6 +39,27 @@
 }
 
 /**
+ * Convenience function to create a new metatable for a type, a userdata for that type, and register it as a global
+ */
+static void* lua_obj_create_global_type (lua_State *L, const char *type_name, const struct luaL_Reg methods[], const char *global_name, size_t size)
+{
+    // allocate the global object
+    void *obj = lua_newuserdata(L, size);
+    
+    // create the type metatable
+    lua_obj_create_type(L, type_name, methods);
+
+    // set the userdata's metatable
+    lua_setmetatable(L, -2);
+
+    // store it as a global
+    lua_setglobal(L, global_name);
+    
+    // ok
+    return obj;
+}
+
+/**
  * Convenience function to get a userdata with the given type metatable name as the first argument for a function
  */
 static void* lua_obj_get_obj (lua_State *L, const char *func, const char *name)
@@ -48,6 +69,9 @@
     // validate the userdata arg
     if ((ud = luaL_checkudata(L, 1, name)) == NULL) {
         luaL_error(L, "bad type argument to %s: `%s` expected", func, name);
+        
+        // XXX: needs a __noreturn__ attribute
+        return NULL;
 
     } else {
         // ok
@@ -311,25 +335,184 @@
 };
 
 /**
- * Initializes a lua_irc_client wrapper for the given client in the give lua state. This registers a set of globals for
- * 'client' and 'networks'.
+ * Initialize the evirc.client type for lua_client, and registers an instance bound to the given irc_client at
+ * 'client'.
  */
 static void lua_client_init (lua_State *L, struct irc_client *client)
 {
     // allocate the global "client" object
-    struct lua_client *lua_client = lua_newuserdata(L, sizeof(*lua_client));
+    struct lua_client *lua_client = lua_obj_create_global_type(L, "evirc.client", lua_client_methods, "client", sizeof(*lua_client));
     
-    // create the type metatable
-    lua_obj_create_type(L, "evirc.client", lua_client_methods);
-
-    // set the client userdata's metatable
-    lua_setmetatable(L, -2);
-
     // initialize it
     lua_client->client = client;
+}
 
-    // store it as a global
-    lua_setglobal(L, "client");
+/**
+ * Wrapper for module
+ */
+struct lua_module {
+    struct module *module;
+};
+
+/**
+ * Create a lua_module userdata from the given module and push it onto the stack, returning 1.
+ */
+static int lua_module_create (lua_State *L, struct module *module)
+{
+    // create the new obj
+    struct lua_module *lua_module = lua_obj_create_obj(L, "spbot.module", sizeof(*lua_module));
+
+    // initialize
+    lua_module->module = module;
+
+    // ok
+    return 1;
+}
+
+static int lua_module_conf (lua_State *L)
+{
+    struct lua_module *lua_module = lua_obj_get_obj(L, __func__, "spbot.module");
+    const struct config_option *option;
+    struct config_value value;
+    struct error_info err;
+
+    // XXX: come up with some better way...
+    struct nexus *nexus = lua_module->module->modules->nexus;
+
+    // the config name
+    const char *conf_name = luaL_checkstring(L, 2);
+
+    // the config value
+    // XXX: support lua_chan
+    const char *conf_value = luaL_checkstring(L, 3);
+    
+    // mutable version of the conf_value
+    char conf_value_buf[strlen(conf_value) + 1];
+    strcpy(conf_value_buf, conf_value);
+
+    // look it up
+    if ((option = module_conf_lookup(lua_module->module, conf_name, &err)) == NULL)
+        return luaL_error(L, "module_conf_lookup: %s/%s: %s", module_name(lua_module->module), conf_name, error_msg(&err));
+    
+    // parse it
+    if (config_parse(option, nexus, &value, conf_value_buf, &err))
+        return luaL_error(L, "config_parse: %s/%s: %s", option->name, conf_value_buf, error_msg(&err));
+
+    // apply it
+    if (module_conf(lua_module->module, option, &value, &err))
+        return luaL_error(L, "module_conf: %s/%s/%s: %s", module_name(lua_module->module), option->name, conf_value_buf, error_msg(&err));
+    
+    // ok
+    return 0;
+}
+
+static const struct luaL_Reg lua_module_methods[] = {
+    {   "conf",         &lua_module_conf            },
+    {   NULL,           NULL                        }
+};
+
+/**
+ * Initialize the lua_module object type
+ */
+static void lua_module_init (lua_State *L)
+{
+    lua_obj_create_type(L, "spbot.module", lua_module_methods);
+}
+
+/**
+ * Wrapper for modules
+ */
+struct lua_modules {
+    struct modules *modules;
+};
+
+static int lua_modules_load (lua_State *L)
+{
+    struct lua_modules *lua_modules = lua_obj_get_obj(L, __func__, "spbot.modules");
+    struct module *module;
+    struct module_info info;
+    struct error_info err;
+
+    // the module name/path
+    info.name = luaL_checkstring(L, 2);
+    info.path = luaL_checkstring(L, 3);
+
+    // load
+    if (module_load(lua_modules->modules, &module, &info, &err))
+        return luaL_error(L, "module_load: %s/%s: %s", info.name, info.path, error_msg(&err));
+
+    // wrap
+    return lua_module_create(L, module);
+}
+
+static int lua_modules_module (lua_State *L)
+{
+    struct lua_modules *lua_modules = lua_obj_get_obj(L, __func__, "spbot.modules");
+    struct module *module;
+    
+    // the module name
+    const char *name = luaL_checkstring(L, 2);
+
+    // look it up
+    if ((module = module_get(lua_modules->modules, name)) == NULL)
+        return luaL_error(L, "module_get: %s: no such module", name);
+
+    // wrap
+    return lua_module_create(L, module);
+}
+
+static const struct luaL_Reg lua_modules_methods[] = {
+    {   "load",         &lua_modules_load           },
+    {   "module",       &lua_modules_module         },
+    {   NULL,           NULL                        }
+};
+
+/**
+ * Initialize the spbot.modules type for lua_modules, and registers an instance bound to the given modules list at
+ * 'modules'.
+ */
+static void lua_modules_init (lua_State *L, struct modules *modules)
+{
+    // allocate the global "modules" object
+    struct lua_modules *lua_modules = lua_obj_create_global_type(L, "spbot.modules", lua_modules_methods, "modules", sizeof(*lua_modules));
+    
+    // initialize it
+    lua_modules->modules = modules;
+}
+
+/**
+ * Wrapper for nexus
+ */
+struct lua_nexus {
+    struct nexus *nexus;
+};
+
+static int lua_nexus_shutdown (lua_State *L)
+{
+    struct lua_nexus *lua_nexus = lua_obj_get_obj(L, __func__, "spbot.nexus");
+
+    // just shut it down
+    nexus_shutdown(lua_nexus->nexus);
+
+    return 0;
+}
+
+static const struct luaL_Reg lua_nexus_methods[] = {
+    {   "shutdown",     &lua_nexus_shutdown         },
+    {   NULL,           NULL                        }
+};
+
+/**
+ * Initialize the spbot.nexus type for lua_nexus, and registers an instance bound to the given nexus list at
+ * 'nexus'.
+ */
+static void lua_nexus_init (lua_State *L, struct nexus *nexus)
+{
+    // allocate the global "nexus" object
+    struct lua_nexus *lua_nexus = lua_obj_create_global_type(L, "spbot.nexus", lua_nexus_methods, "nexus", sizeof(*lua_nexus));
+    
+    // initialize it
+    lua_nexus->nexus = nexus;
 }
 
 /**
@@ -344,6 +527,9 @@
         luaL_error(L, "lua_touserdata: NULL");
 
     // init the various bits
+    lua_nexus_init(L, nexus);
+    lua_modules_init(L, nexus->modules);
+    lua_module_init(L);
     lua_client_init(L, nexus->client);
     lua_net_init(L);
     lua_chan_init(L);
--- a/src/module.c	Wed Apr 01 00:27:03 2009 +0300
+++ b/src/module.c	Wed Apr 01 00:27:39 2009 +0300
@@ -26,6 +26,11 @@
     return SUCCESS;
 }
 
+const char* module_name (struct module *module)
+{
+    return module->info.name;
+}
+
 /**
  * Load the symbol named "<module>_<suffix>".
  */ 
@@ -189,3 +194,15 @@
     return SUCCESS;
 }
 
+void modules_destroy (struct modules *modules)
+{
+    struct module *module;
+
+    // destroy each module
+    while ((module = TAILQ_FIRST(&modules->list)))
+        module_destroy(module);
+
+    // ourselves
+    free(modules);
+}
+
--- a/src/module.h	Wed Apr 01 00:27:03 2009 +0300
+++ b/src/module.h	Wed Apr 01 00:27:39 2009 +0300
@@ -135,6 +135,11 @@
 err_t modules_create (struct modules **modules_ptr, struct nexus *nexus);
 
 /**
+ * Return a module's name
+ */
+const char* module_name (struct module *module);
+
+/**
  * Load a new module
  *
  * @param modules the module-loading context
@@ -188,4 +193,11 @@
  */
 err_t modules_unload (struct modules *modules);
 
+/**
+ * Destroy all modules immediately.
+ *
+ * XXX: this doesn't leaves hanging module resources. The program will probably crash in soon
+ */
+void modules_destroy (struct modules *modules);
+
 #endif
--- a/src/nexus.c	Wed Apr 01 00:27:03 2009 +0300
+++ b/src/nexus.c	Wed Apr 01 00:27:39 2009 +0300
@@ -353,29 +353,61 @@
     return SUCCESS;
 }
 
+void nexus_shutdown (struct nexus *nexus)
+{
+    // destroy the console
+    if (nexus->lua_console)
+        lua_console_destroy(nexus->lua_console);
+    
+    nexus->lua_console = NULL;
+
+    // unload the modules
+    if (nexus->modules)
+       modules_unload(nexus->modules);
+
+    // quit the irc client
+    if (nexus->client)
+       irc_client_quit(nexus->client, "Goodbye, cruel world ;(");
+
+    // remove the signal handlers
+    if (nexus->signals)
+        signals_free(nexus->signals);
+
+    nexus->signals = NULL;
+
+    // now event_base_dispatch should return once everythings' shut down...
+    nexus->shutdown = true;
+}
+
+void nexus_destroy (struct nexus *nexus)
+{
+    // destroy the modules
+    if (nexus->modules)
+        modules_destroy(nexus->modules);
+
+    nexus->modules = NULL;
+
+    // destroy the irc client
+    if (nexus->client)
+        irc_client_destroy(nexus->client);
+
+    nexus->client = NULL;
+
+    // then force-quit the event loop
+    event_base_loopbreak(nexus->ev_base);
+}
+
 static void on_sigint (evutil_socket_t sig, short what, void *arg)
 {
-    struct nexus *ctx = arg;
+    struct nexus *nexus = arg;
 
     (void) sig;
     (void) what;
     
     log_info("Quitting...");
-
-    // destroy the console
-    if (ctx->lua_console)
-        lua_console_destroy(ctx->lua_console);
-
-    // unload the modules
-    modules_unload(ctx->modules);
-
-    // quit the irc client
-    irc_client_quit(ctx->client, "Goodbye, cruel world ;(");
     
-    // remove the signal handlers (ourself)
-    signals_free(ctx->signals);
-
-    // now event_base_dispatch should return once everythings' shut down...
+    // shutdown
+    nexus_shutdown(nexus);
 }
 
 int main (int argc, char **argv) 
@@ -425,9 +457,16 @@
     // run event loop
     log_info("entering event loop");
 
-    if (event_base_dispatch(nexus->ev_base))
-        FATAL("event_base_dispatch");
-    
+    if (event_base_dispatch(nexus->ev_base)) {
+        if (nexus->shutdown) {
+            log_info("clean shutdown completed");
+
+        } else {
+            FATAL("event_base_dispatch returned without shutdown");
+
+        }
+    }
+
     // ok, no cleanup
     return 0;
 }
--- a/src/nexus.h	Wed Apr 01 00:27:03 2009 +0300
+++ b/src/nexus.h	Wed Apr 01 00:27:39 2009 +0300
@@ -36,9 +36,26 @@
 
     /** The IRC client state */
     struct irc_client *client;
+
+    /** Shutting down? */
+    bool shutdown;
 };
 
 /**
+ * Shut down the nexus cleanly. It /should/ be safe to call this several times, but it won't do anything.
+ *
+ * Once everything has shut down nicely (which it should, unless there's something buggy), the event loop should exit,
+ * and the main() function return.
+ */
+void nexus_shutdown (struct nexus *nexus);
+
+/**
+ * Destroy the nexus in an ugly fashion. This will attempt to release as many resources as possible, and quit the event
+ * loop.
+ */
+void nexus_destroy (struct nexus *nexus);
+
+/**
  * The nexus main function, application entry point, etc.
  */
 int main (int argc, char **argv);