Add --admin argument.
authorJelmer Vernooij <jelmer@samba.org>
Fri, 8 Dec 2006 15:54:36 +0000 (16:54 +0100)
committerJelmer Vernooij <jelmer@samba.org>
Fri, 8 Dec 2006 15:54:36 +0000 (16:54 +0100)
configure.ac
src/admin.c
src/ctrlproxy.h
src/internals.h
src/main.c

index cc015bc473ab940431e0262a3ef175babf09e7c0..f6c472fd5763bd64fa81a39cb8168c2dd57d2bc3 100644 (file)
@@ -70,7 +70,10 @@ PKG_CHECK_MODULES(GNUTLS, gnutls, [
                AC_SUBST(SSL_OBJS)
                ], [ AC_MSG_WARN([GNUTLS not found, SSL will not be available]) ])
 
-AC_CHECK_LIB(readline, readline, [ BINS="$BINS linestack-cmd$ac_cv_exeext" ])
+AC_CHECK_LIB(readline, readline, [ 
+       AC_DEFINE(HAVE_READLINE, 1, [Whether readline is available])
+       BINS="$BINS linestack-cmd$ac_cv_exeext" 
+])
 
 ###############################################################################
 # IPv6 support
index e0560bd64cf1152f9f7e8e02fb94f1b7cef28415..e3667855271ef4b0542470d9a0bd8b8fb7d66687 100644 (file)
 
 #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"
@@ -677,3 +681,178 @@ void init_admin(void)
 
        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;
+}
index 3b88504cf908cb72bd485cc6795c481caa0e97f5..70ab746a238eec307c027cdd30a704cf3056de8d 100644 (file)
@@ -69,6 +69,9 @@ struct global {
 
        GIOChannel *unix_incoming;
        gint unix_incoming_id;
+
+       GIOChannel *admin_incoming;
+       gint admin_incoming_id;
 };
 
 struct plugin_ops {
index 0e0843bbccb6aec69e41451f3f8b7a753b9d0d2b..7411ed6351106d44ae22fbf563a3c8616c7ab276 100644 (file)
@@ -114,6 +114,9 @@ void nickserv_identify_me(struct network *network, char *nick);
 void init_admin(void);
 gboolean admin_process_command(struct client *c, struct line *l, int cmdoffset);
 void admin_log(enum log_level level, const struct network *n, const struct client *c, const char *data);
+gboolean start_admin_socket(struct global *global);
+gboolean stop_admin_socket(struct global *global);
+gboolean admin_socket_prompt(const char *config_dir);
 
 /* settings.c */
 gboolean create_configuration(const char *config_dir);
index 9af18b76066cd5cb6345858d155ae0bfba968c35..9ddacb25ecea9ff9a09e5f66683354f5cabb6f7a 100644 (file)
@@ -92,6 +92,7 @@ static void clean_exit()
                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);
 
@@ -168,6 +169,7 @@ int main(int argc, char **argv)
        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;
@@ -181,6 +183,7 @@ int main(int argc, char **argv)
                {"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 }
        };
 
@@ -227,6 +230,12 @@ int main(int argc, char **argv)
                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);
@@ -296,6 +305,7 @@ int main(int argc, char **argv)
 #endif
 
        start_unix_socket(my_global);
+       start_admin_socket(my_global);
 
        autoconnect_networks(my_global);