# HG changeset patch # User Tero Marttila # Date 1238534859 -10800 # Node ID 454aea1e4f11b36b3be9ad8fbb37b51e2e496d36 # Parent af4190b743a5d88a136a714777d75786f6adbf90 implement nexus_shutdown, lua_modules/lua_module, and lua_nexus diff -r af4190b743a5 -r 454aea1e4f11 src/lua_objs.c --- 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); diff -r af4190b743a5 -r 454aea1e4f11 src/module.c --- 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 "_". */ @@ -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); +} + diff -r af4190b743a5 -r 454aea1e4f11 src/module.h --- 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 diff -r af4190b743a5 -r 454aea1e4f11 src/nexus.c --- 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; } diff -r af4190b743a5 -r 454aea1e4f11 src/nexus.h --- 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);