From 8a05f2b1ac917f53336146eca4e6640061d3d26b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 12 Dec 2012 22:57:20 +0100 Subject: [PATCH] client: Add initial parts for the command line client --- Makefile.tools | 2 +- client/main.c | 255 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 256 insertions(+), 1 deletion(-) diff --git a/Makefile.tools b/Makefile.tools index b5df18a6b..143a0ee7a 100644 --- a/Makefile.tools +++ b/Makefile.tools @@ -1,7 +1,7 @@ if CLIENT noinst_PROGRAMS += client/bluetoothctl -client_bluetoothctl_SOURCES = client/main.c +client_bluetoothctl_SOURCES = $(gdbus_sources) client/main.c client_bluetoothctl_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ -lreadline endif diff --git a/client/main.c b/client/main.c index b38861ad6..45bee3fa7 100644 --- a/client/main.c +++ b/client/main.c @@ -25,7 +25,262 @@ #include #endif +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define COLOR_OFF "\x1B[0m" +#define COLOR_BLUE "\x1B[0;34m" + +static GMainLoop *main_loop; +static DBusConnection *dbus_conn; + +static void connect_handler(DBusConnection *connection, void *user_data) +{ + rl_set_prompt(COLOR_BLUE "[bluetooth]" COLOR_OFF "# "); + printf("\r"); + rl_on_new_line(); + rl_redisplay(); +} + +static void disconnect_handler(DBusConnection *connection, void *user_data) +{ + rl_set_prompt("[bluetooth]# "); + printf("\r"); + rl_on_new_line(); + rl_redisplay(); +} + +static void message_handler(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ +} + +static void cmd_quit(const void *arg) +{ + g_main_loop_quit(main_loop); +} + +static const struct { + const char *str; + void (*func) (const void *arg); + const char *desc; +} cmd_table[] = { + { "quit", cmd_quit, "Quit program" }, + { "exit", cmd_quit }, + { } +}; + +static void rl_handler(char *input) +{ + int i; + + if (!input) { + rl_insert_text("quit"); + rl_redisplay(); + rl_crlf(); + g_main_loop_quit(main_loop); + return; + } + + if (!strlen(input)) + return; + + add_history(input); + + for (i = 0; cmd_table[i].str; i++) { + if (strcmp(input, cmd_table[i].str)) + continue; + + cmd_table[i].func(cmd_table[i].str); + return; + } + + if (strcmp(input, "help")) { + printf("Invalid command\n"); + return; + } + + printf("Available commands:\n"); + + for (i = 0; cmd_table[i].str; i++) { + if (cmd_table[i].desc) + printf("\t%s\t%s\n", cmd_table[i].str, + cmd_table[i].desc); + } +} + +static gboolean input_handler(GIOChannel *channel, GIOCondition condition, + gpointer user_data) +{ + if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { + g_main_loop_quit(main_loop); + return FALSE; + } + + rl_callback_read_char(); + return TRUE; +} + +static guint setup_standard_input(void) +{ + GIOChannel *channel; + guint source; + + channel = g_io_channel_unix_new(fileno(stdin)); + + source = g_io_add_watch(channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + input_handler, NULL); + + g_io_channel_unref(channel); + + return source; +} + +static unsigned int __terminated = 0; + +static gboolean signal_handler(GIOChannel *channel, GIOCondition condition, + gpointer user_data) +{ + struct signalfd_siginfo si; + ssize_t result; + int fd; + + if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + g_main_loop_quit(main_loop); + return FALSE; + } + + fd = g_io_channel_unix_get_fd(channel); + + result = read(fd, &si, sizeof(si)); + if (result != sizeof(si)) + return FALSE; + + switch (si.ssi_signo) { + case SIGINT: + case SIGTERM: + if (__terminated == 0) { + rl_replace_line("", 0); + rl_crlf(); + g_main_loop_quit(main_loop); + } + + __terminated = 1; + break; + } + + return TRUE; +} + +static guint setup_signalfd(void) +{ + GIOChannel *channel; + guint source; + sigset_t mask; + int fd; + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + + if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { + perror("Failed to set signal mask"); + return 0; + } + + fd = signalfd(-1, &mask, 0); + if (fd < 0) { + perror("Failed to create signal descriptor"); + return 0; + } + + channel = g_io_channel_unix_new(fd); + + g_io_channel_set_close_on_unref(channel, TRUE); + g_io_channel_set_encoding(channel, NULL, NULL); + g_io_channel_set_buffered(channel, FALSE); + + source = g_io_add_watch(channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + signal_handler, NULL); + + g_io_channel_unref(channel); + + return source; +} + +static gboolean option_version = FALSE; + +static GOptionEntry options[] = { + { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, + "Show version information and exit" }, + { NULL }, +}; + int main(int argc, char *argv[]) { + GOptionContext *context; + GError *error = NULL; + GDBusClient *client; + guint signal, input; + + context = g_option_context_new(NULL); + g_option_context_add_main_entries(context, options, NULL); + + if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) { + if (error != NULL) { + g_printerr("%s\n", error->message); + g_error_free(error); + } else + g_printerr("An unknown error occurred\n"); + exit(1); + } + + g_option_context_free(context); + + if (option_version == TRUE) { + printf("%s\n", VERSION); + exit(0); + } + + main_loop = g_main_loop_new(NULL, FALSE); + dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL); + + rl_erase_empty_line = 1; + rl_callback_handler_install(NULL, rl_handler); + + rl_set_prompt("[bluetooth]# "); + rl_redisplay(); + + input = setup_standard_input(); + signal = setup_signalfd(); + client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez"); + + g_dbus_client_set_connect_watch(client, connect_handler, NULL); + g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL); + g_dbus_client_set_signal_watch(client, message_handler, NULL); + + g_main_loop_run(main_loop); + + g_dbus_client_unref(client); + g_source_remove(signal); + g_source_remove(input); + + rl_message(""); + rl_callback_handler_remove(); + + dbus_connection_unref(dbus_conn); + g_main_loop_unref(main_loop); + return 0; } -- 2.47.3