#include "internals.h"
#include <string.h>
+#include <sys/un.h>
+#ifdef HAVE_READLINE
+#include <readline/readline.h>
+#endif
#include "admin.h"
#define ADMIN_CHANNEL "#ctrlproxy"
markers = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)linestack_free_marker);
}
+
+static void iochannel_admin_out(admin_handle h, const char *data)
+{
+ gsize bytes_written;
+ GError *error = NULL;
+ GIOStatus status;
+
+ status = g_io_channel_write_chars(h->user_data, data, -1, &bytes_written, &error);
+
+ status = g_io_channel_write_chars(h->user_data, "\n", -1, &bytes_written, &error);
+
+ status = g_io_channel_flush(h->user_data, &error);
+}
+
+static gboolean handle_client_data(GIOChannel *channel,
+ GIOCondition condition, void *_global)
+{
+ char *raw;
+ GError *error = NULL;
+ GIOStatus status;
+ struct admin_handle ah;
+ gsize eol;
+
+ ah.global = _global;
+ ah.user_data = channel;
+ ah.send_fn = iochannel_admin_out;
+
+ if (condition & G_IO_IN) {
+ status = g_io_channel_read_line(channel, &raw, NULL, &eol, &error);
+ if (status == G_IO_STATUS_NORMAL) {
+ raw[eol] = '\0';
+ process_cmd(&ah, raw);
+ g_free(raw);
+ }
+ }
+
+ if (condition & G_IO_HUP) {
+ return FALSE;
+ }
+
+ if (condition & G_IO_ERR) {
+ log_global(LOG_WARNING, "Error from admin client");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean handle_new_client(GIOChannel *c_server,
+ GIOCondition condition, void *_global)
+{
+ GIOChannel *c;
+ int sock = accept(g_io_channel_unix_get_fd(c_server), NULL, 0);
+
+ if (sock < 0) {
+ log_global(LOG_WARNING, "Error accepting new connection: %s", strerror(errno));
+ return TRUE;
+ }
+
+ c = g_io_channel_unix_new(sock);
+
+ g_io_channel_set_close_on_unref(c, TRUE);
+ g_io_channel_set_encoding(c, NULL, NULL);
+ g_io_channel_set_flags(c, G_IO_FLAG_NONBLOCK, NULL);
+
+ g_io_add_watch(c, G_IO_IN | G_IO_ERR | G_IO_HUP, handle_client_data, _global);
+
+ g_io_channel_unref(c);
+
+ return TRUE;
+}
+
+gboolean start_admin_socket(struct global *global)
+{
+ int sock;
+ struct sockaddr_un un;
+
+ sock = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0) {
+ log_global(LOG_ERROR, "error creating unix socket: %s", strerror(errno));
+ return FALSE;
+ }
+
+ un.sun_family = AF_UNIX;
+ strncpy(un.sun_path, global->config->admin_socket, sizeof(un.sun_path));
+ unlink(un.sun_path);
+
+ if (bind(sock, (struct sockaddr *)&un, sizeof(un)) < 0) {
+ log_global(LOG_ERROR, "unable to bind to %s: %s", un.sun_path, strerror(errno));
+ return FALSE;
+ }
+
+ if (listen(sock, 5) < 0) {
+ log_global(LOG_ERROR, "error listening on socket: %s", strerror(errno));
+ return FALSE;
+ }
+
+ global->admin_incoming = g_io_channel_unix_new(sock);
+
+ if (!global->admin_incoming) {
+ log_global(LOG_ERROR, "Unable to create GIOChannel for unix server socket");
+ return FALSE;
+ }
+
+ g_io_channel_set_close_on_unref(global->admin_incoming, TRUE);
+
+ g_io_channel_set_encoding(global->admin_incoming, NULL, NULL);
+ global->admin_incoming_id = g_io_add_watch(global->admin_incoming, G_IO_IN, handle_new_client, global);
+ g_io_channel_unref(global->admin_incoming);
+
+ return TRUE;
+}
+
+gboolean stop_admin_socket(struct global *global)
+{
+ if (global->admin_incoming_id > 0)
+ g_source_remove(global->admin_incoming_id);
+ unlink(global->config->admin_socket);
+ return TRUE;
+}
+
+gboolean admin_socket_prompt(const char *config_dir)
+{
+ char *admin_dir = g_build_filename(config_dir, "admin", NULL);
+ int sock = socket(PF_UNIX, SOCK_STREAM, 0);
+ GIOChannel *ch;
+ GError *error = NULL;
+ GIOStatus status;
+ struct sockaddr_un un;
+
+ un.sun_family = AF_UNIX;
+ strncpy(un.sun_path, admin_dir, sizeof(un.sun_path));
+
+ if (connect(sock, (struct sockaddr *)&un, sizeof(un)) < 0) {
+ log_global(LOG_ERROR, "unable to connect to %s: %s", un.sun_path, strerror(errno));
+ g_free(admin_dir);
+ return FALSE;
+ }
+
+ ch = g_io_channel_unix_new(sock);
+
+#ifdef HAVE_READLINE
+ while (1) {
+ char *data = readline("ctrlproxy> ");
+ char *raw;
+
+ if (data == NULL)
+ break;
+
+ status = g_io_channel_write_chars(ch, data, -1, NULL, &error);
+ if (status != G_IO_STATUS_NORMAL) {
+ fprintf(stderr, "Error writing to admin socket: %s\n", error->message);
+ return FALSE;
+ }
+
+ status = g_io_channel_write_chars(ch, "\n", -1, NULL, &error);
+ if (status != G_IO_STATUS_NORMAL) {
+ fprintf(stderr, "Error writing to admin socket: %s\n", error->message);
+ return FALSE;
+ }
+
+ g_io_channel_flush(ch, &error);
+
+ g_free(data);
+
+ while (g_io_channel_read_line(ch, &raw, NULL, NULL, &error) == G_IO_STATUS_NORMAL)
+ {
+ printf("%s", raw);
+ }
+ }
+#endif
+ g_free(admin_dir);
+
+ return TRUE;
+}
save_configuration(my_global->config, path);
nickserv_save(my_global, path);
stop_unix_socket(my_global);
+ stop_admin_socket(my_global);
free_global(my_global);
extern gboolean no_log_timestamp;
const char *config_dir = NULL;
char *tmp;
+ gboolean admin = FALSE;
gboolean init = FALSE;
const char *inetd_client = NULL;
gboolean version = FALSE;
{"log", 'l', 0, G_OPTION_ARG_STRING, &logfile, ("Log messages to specified file"), ("FILE")},
{"config-dir", 'c', 0, G_OPTION_ARG_STRING, &config_dir, ("Override configuration directory"), ("DIR")},
{"version", 'v', 0, G_OPTION_ARG_NONE, &version, ("Show version information")},
+ {"admin", 'a', 0, G_OPTION_ARG_NONE, &admin, "Show administration prompt" },
{ NULL }
};
return 0;
}
+ if (admin) {
+ if (admin_socket_prompt(config_dir))
+ return 0;
+ return 1;
+ }
+
init_log(logfile);
log_global(LOG_INFO, "CtrlProxy %s starting", VERSION);
#endif
start_unix_socket(my_global);
+ start_admin_socket(my_global);
autoconnect_networks(my_global);