--- 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?
--- 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")
--- /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 <unistd.h>
+#include <stdio.h>
+#include <readline/readline.h>
+#include <assert.h>
+
+/** 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;
+}
+
--- /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 <event2/event.h>
+#include <stdbool.h>
+
+/**
+ * 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 */
--- 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 '<name>:<path>' format\n");
printf(" --config / -C add a module configuration option using '<mod_name>:<name>[:<value>]' 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");
--- 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 <event2/event.h>
#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;