# HG changeset patch # User Tero Marttila # Date 1238503284 -10800 # Node ID 99661e5aac91e11a42a683de48c6bdbe3a010a89 # Parent bca23cbe1dcecf98bc019770052c867c54a86847 add a simple interactive readline console diff -r bca23cbe1dce -r 99661e5aac91 TODO --- a/TODO Mon Mar 30 16:44:00 2009 +0300 +++ b/TODO Tue Mar 31 15:41:24 2009 +0300 @@ -3,8 +3,9 @@ * sock_openssl, or improve sock_gnutls * tests... -irc_conn: - * ratelimit queue for outgoing messages +irc_queue: + * fix use of line_proto_send + * more expansive tests irc_net: * reconnect, maybe cycling servers? diff -r bca23cbe1dce -r 99661e5aac91 src/CMakeLists.txt --- a/src/CMakeLists.txt Mon Mar 30 16:44:00 2009 +0300 +++ b/src/CMakeLists.txt Tue Mar 31 15:41:24 2009 +0300 @@ -12,13 +12,13 @@ set (SOCK_SOURCES sock.c sock_tcp.c sock_gnutls.c sock_test.c line_proto.c) set (IRC_SOURCES irc_line.c irc_conn.c irc_net.c irc_chan.c chain.c irc_cmd.c irc_proto.c irc_client.c irc_user.c irc_queue.c) -set (NEXUS_SOURCES nexus.c ${CORE_SOURCES} ${SOCK_SOURCES} ${IRC_SOURCES} signals.c module.c config.c) +set (NEXUS_SOURCES nexus.c ${CORE_SOURCES} ${SOCK_SOURCES} ${IRC_SOURCES} signals.c module.c config.c console.c) set (TEST_SOURCES test.c ${CORE_SOURCES} ${SOCK_SOURCES} ${IRC_SOURCES}) set (IRC_LOG_SOURCES irc_log.c) # define our libraries set (MODULE_LIBRARIES "dl") -set (NEXUS_LIBRARIES ${LibEvent_LIBRARIES} ${GnuTLS_LIBRARIES} ${MODULE_LIBRARIES}) +set (NEXUS_LIBRARIES ${LibEvent_LIBRARIES} ${GnuTLS_LIBRARIES} ${MODULE_LIBRARIES} "readline") # compiler flags set (CFLAGS "-Wall -Wextra -std=gnu99") diff -r bca23cbe1dce -r 99661e5aac91 src/console.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/console.c Tue Mar 31 15:41:24 2009 +0300 @@ -0,0 +1,91 @@ +#include "console.h" + +#include +#include +#include +#include + +/** The global console state */ +static struct console _console; + +/** + * Our stdin input handler + */ +static void console_input (int fd, short what, void *arg) +{ + struct console *console = arg; + + (void) console; + (void) fd; + (void) what; + + // tell readline to process it + rl_callback_read_char(); +} + +/** + * Our readline line handler + */ +static void console_line (char *line) +{ + struct console *console = &_console; + + // invoke the console callback + console->callbacks->on_line(line, console->cb_arg); +} + +err_t console_init (struct console **console_ptr, struct event_base *ev_base, const struct console_config *config, + const struct console_callbacks *callbacks, void *cb_arg, struct error_info *err) +{ + struct console *console = &_console; + + // check it's not already initialized + assert(!console->initialized); + + // store + console->callbacks = callbacks; + console->cb_arg = cb_arg; + + // setup the input event + if ((console->ev = event_new(ev_base, STDIN_FILENO, EV_READ | EV_PERSIST, &console_input, console)) == NULL) + JUMP_SET_ERROR(err, ERR_EVENT_NEW); + + // setup readline + rl_callback_handler_install(config->prompt, &console_line); + + // mark it as initialized + console->initialized = true; + + // enable input + if (event_add(console->ev, NULL)) + JUMP_SET_ERROR(err, ERR_EVENT_ADD); + + // ok + *console_ptr = console; + + return SUCCESS; + +error: + console_destroy(console); + + return ERROR_CODE(err); +} + +void console_destroy (struct console *console) +{ + // remove the input event + if (console->ev) + event_free(console->ev); + + console->ev = NULL; + + // de-init rl? + if (console->initialized) + rl_callback_handler_remove(); + + console->initialized = false; + + // remove stored stuff + console->callbacks = console->cb_arg = NULL; +} + diff -r bca23cbe1dce -r 99661e5aac91 src/console.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/console.h Tue Mar 31 15:41:24 2009 +0300 @@ -0,0 +1,62 @@ +#ifndef CONSOLE_H +#define CONSOLE_H + +/** + * @file + * + * An interactive line-based console interface for runtime configuration. + * + * This uses the GNU readline library to implement the console, and hence, the console uses global state, and there + * can only be one console per process - not that it should matter, since the console requires a tty. + * + * XXX: the log module interferes with this + */ +#include "error.h" +#include +#include + +/** + * Callbacks for event-based actions + */ +struct console_callbacks { + /** A line was read from the console */ + void (*on_line) (char *line, void *arg); +}; + +/** + * Configuration info for console operation + */ +struct console_config { + /** The prompt to use when displaying lines */ + const char *prompt; +}; + +/** + * The console state... + */ +struct console { + /** The input event */ + struct event *ev; + + /** The callback functions */ + const struct console_callbacks *callbacks; + + /** The callback context argument */ + void *cb_arg; + + /** Already initialized? */ + bool initialized; +}; + +/** + * Initialize the console, setting up the TTY and input handler + */ +err_t console_init (struct console **console_ptr, struct event_base *ev_base, const struct console_config *config, + const struct console_callbacks *callbacks, void *cb_arg, struct error_info *err); + +/** + * Deinitialize the console, restoring the TTY and releasing resources + */ +void console_destroy (struct console *console); + +#endif /* CONSOLE_H */ diff -r bca23cbe1dce -r 99661e5aac91 src/nexus.c --- a/src/nexus.c Mon Mar 30 16:44:00 2009 +0300 +++ b/src/nexus.c Tue Mar 31 15:41:24 2009 +0300 @@ -26,7 +26,8 @@ /** Options without short names */ _OPT_EXT_BEGIN = 0x00ff, - + + OPT_CONSOLE, }; /** @@ -39,6 +40,7 @@ {"module", 1, NULL, OPT_MODULE }, {"config", 1, NULL, OPT_CONFIG }, {"debug", 0, NULL, OPT_DEBUG }, + {"console", 0, NULL, OPT_CONSOLE }, {0, 0, 0, 0 }, }; @@ -57,7 +59,9 @@ printf(" --module / -m add a module using ':' format\n"); printf(" --config / -C add a module configuration option using ':[:]' format\n"); printf(" --debug / -d set logging level to DEBUG\n"); - + printf(" --console use the interactive console\n"); + + // dump loaded module configs if (nexus && !TAILQ_EMPTY(&nexus->modules->list)) { struct module *module; @@ -232,6 +236,35 @@ return SUCCESS; } +static void on_line (char *line, void *arg) +{ + struct nexus *nexus = arg; + + (void) nexus; + + // just dump it + log_info("read line from console: '%s'", line); +} + +static struct console_callbacks nexus_console_callbacks = { + .on_line = &on_line, +}; + +/** + * Open the console + */ +static err_t apply_console (struct nexus *nexus, struct error_info *err) +{ + struct console_config config = { + .prompt = " > ", + }; + + log_info("initializing the console"); + + // just init it + return console_init(&nexus->console, nexus->ev_base, &config, &nexus_console_callbacks, nexus, err); +} + /** * Parse arguments and apply them to the given nexus */ @@ -275,6 +308,12 @@ case OPT_DEBUG: set_log_level(LOG_DEBUG); break; + + case OPT_CONSOLE: + if (apply_console(nexus, err)) + return ERROR_CODE(err); + + break; case '?': usage(nexus, argv[0]); @@ -295,6 +334,10 @@ log_info("Quitting..."); + // destroy the console + if (ctx->console) + console_destroy(ctx->console); + // unload the modules modules_unload(ctx->modules); @@ -319,17 +362,20 @@ if ((nexus->ev_base = event_base_new()) == NULL) FATAL("event_base_new"); + // initialize signal handlers if ((ERROR_CODE(&err) = signals_create(&nexus->signals, nexus->ev_base))) FATAL("signals_create"); - + // add our signal handlers if (signal_ignore(SIGPIPE, &err)) FATAL_ERROR(&err, "signals_ignore(SIGPIPE)"); - + + // XXX: add our SIGINT handler after console_init()? if ((ERROR_CODE(&err) = signals_add(nexus->signals, SIGINT, &on_sigint, nexus))) FATAL_ERROR(&err, "signals_add(SIGINT)"); + // initialize sock module if (sock_init(nexus->ev_base, &err)) FATAL_ERROR(&err, "sock_init"); @@ -342,10 +388,12 @@ if (irc_client_create(&nexus->client, &err)) FATAL_ERROR(&err, "irc_client_create"); + // parse args if (parse_args(nexus, argc, argv, &err)) FATAL_ERROR(&err, "parse_args"); - + + // run event loop log_info("entering event loop"); diff -r bca23cbe1dce -r 99661e5aac91 src/nexus.h --- a/src/nexus.h Mon Mar 30 16:44:00 2009 +0300 +++ b/src/nexus.h Tue Mar 31 15:41:24 2009 +0300 @@ -10,6 +10,7 @@ #include #include "signals.h" +#include "console.h" #include "module.h" #include "irc_client.h" @@ -23,6 +24,9 @@ /** Our signal handlers */ struct signals *signals; + /** Our console */ + struct console *console; + /** Our loaded modules */ struct modules *modules;