All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] add bt command line tool
@ 2012-10-03 17:34 Gustavo Padovan
  2012-10-03 17:34 ` [PATCH 1/5] bt tool: initial tool handling Gustavo Padovan
                   ` (4 more replies)
  0 siblings, 5 replies; 7+ messages in thread
From: Gustavo Padovan @ 2012-10-03 17:34 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: travis.reitter, Gustavo Padovan

From: Gustavo Padovan <gustavo.padovan@collabora.co.uk>

The following patches add a new tool to handle BlueZ from the comand line.
Today the only code we have for this is the new python scripts. A proper tool
written in C was missing.

This work has two purposes provide a nice tool to use BlueZ and provide a
very nice sample on how to write code that uses the BlueZ's API. You will find
a well documented code in the commits here.

More work is planned on that, I already started some code to handle adapter
and device API's but it is still a work in progress. Should hit them mailing
list any time soon.

After applying this we should consider remove the test/agent.c code since this
one is a much better piece of code.

Thanks to everyone tat helped on this, about a year ago Bruno Dilly and me
started the very first bits of this. And to Travis Reitter that helped a lot
to make this possible.

Gustavo Padovan (2):
  bt tool: initial tool handling
  bt tool: add cmd 'agent'

Travis Reitter (3):
  bt tool: add cmd 'discover'
  bt tool: add cmd 'pair'
  bt tool: add cmd 'remove'

 Makefile.tools  |    7 +-
 tools/bt-tool.c | 1232 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1238 insertions(+), 1 deletion(-)
 create mode 100644 tools/bt-tool.c

-- 
1.7.11.4


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/5] bt tool: initial tool handling
  2012-10-03 17:34 [PATCH 0/5] add bt command line tool Gustavo Padovan
@ 2012-10-03 17:34 ` Gustavo Padovan
  2012-10-04  9:47   ` Marcel Holtmann
  2012-10-03 17:34 ` [PATCH 2/5] bt tool: add cmd 'discover' Gustavo Padovan
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 7+ messages in thread
From: Gustavo Padovan @ 2012-10-03 17:34 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: travis.reitter, Gustavo Padovan

From: Gustavo Padovan <gustavo.padovan@collabora.co.uk>

All core code to using this tool is here, the nexts steps will add
commands like 'discover' 'pair' 'agent', etc.
---
 Makefile.tools  |   7 +-
 tools/bt-tool.c | 421 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 427 insertions(+), 1 deletion(-)
 create mode 100644 tools/bt-tool.c

diff --git a/Makefile.tools b/Makefile.tools
index 9637a9f..a7f5827 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -1,7 +1,8 @@
 
 if TOOLS
 bin_PROGRAMS += tools/rfcomm tools/l2ping \
-				tools/hcitool tools/sdptool tools/ciptool
+				tools/hcitool tools/sdptool tools/ciptool \
+				tools/bt
 
 sbin_PROGRAMS += tools/hciattach tools/hciconfig
 
@@ -30,6 +31,10 @@ tools_hcitool_SOURCES = tools/hcitool.c src/oui.h src/oui.c \
 						src/textfile.h src/textfile.c
 tools_hcitool_LDADD = lib/libbluetooth-private.la
 
+tools_bt_SOURCES = $(gdbus_sources) tools/bt-tool.c
+
+tools_bt_LDADD = lib/libbluetooth-private.la @DBUS_LIBS@ @GLIB_LIBS@
+
 tools_sdptool_SOURCES = tools/sdptool.c src/sdp-xml.h src/sdp-xml.c
 tools_sdptool_LDADD = lib/libbluetooth-private.la @GLIB_LIBS@
 
diff --git a/tools/bt-tool.c b/tools/bt-tool.c
new file mode 100644
index 0000000..4edc255
--- /dev/null
+++ b/tools/bt-tool.c
@@ -0,0 +1,421 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011 Bruno Dilly <bdilly@profusion.mobi>
+ *  Copyright (C) 2011 Gustavo Padovan <gustavo@padovan.org>
+ *  Copyright (C) 2012 Collabora Limited.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/*
+ * This code is meant, in part, to be a working example of a BlueZ client
+ * written in C.
+ *
+ * If you are new to BlueZ, we recommend you read the code breadth-first,
+ * starting with main(). The function and variable names should be fairly
+ * self-explanatory, with comments filling in details along the way.
+ *
+ * You will also want to reference the BlueZ D-Bus API, which is documented in
+ * /doc of this soruce tree, and the libdbus API, documented here:
+ *
+ *   http://dbus.freedesktop.org/doc/api/html/index.html
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#define BLUEZ_SERVICE	"org.bluez"
+#define BLUEZ_MANAGER	"org.bluez.Manager"
+#define BLUEZ_ADAPTER	"org.bluez.Adapter"
+#define BLUEZ_AGENT	"org.bluez.Agent"
+#define BLUEZ_ERROR	"org.bluez.Error"
+
+#define ARRAY_SIZE(x) (int)(sizeof(x)/sizeof(x[0]))
+
+#define ERR(fmt, ...)	fprintf(stderr, fmt "\n", ##__VA_ARGS__)
+
+struct cmd_param {
+	char	*path;
+	int	argc;
+	char	**argv;
+};
+
+struct cmd_struct {
+	const char	*cmd;
+	GSourceFunc	fn;
+};
+
+struct find_adapter_cb_data {
+	GSourceFunc		fn;
+	struct cmd_param	*param;
+};
+
+static GMainLoop *mainloop = NULL;
+static gchar *program_name = NULL;
+
+static bdaddr_t bdaddr;
+static DBusConnection *conn;
+
+static void show_help()
+{
+	printf("usage: %s [-i interface] <command> [<args>]\n"
+	"\n"
+	"options:\n"
+	"	-i <hci dev>, --interface <hci dev>	HCI device\n"
+	"	--help					This help\n"
+	"	--version				Version\n"
+	"\n"
+	"commands:\n"
+	"	discover				Scan for devices\n"
+	"	pair <device address>			Start pairing\n"
+	"	agent					Run BlueZ agent\n"
+							"\n", program_name);
+
+	if(mainloop != NULL)
+		g_main_loop_quit(mainloop);
+}
+
+static struct find_adapter_cb_data *find_adapter_cb_data_new(GSourceFunc fn,
+							struct cmd_param *param)
+{
+	struct find_adapter_cb_data *cb_data;
+
+	cb_data = g_new0(struct find_adapter_cb_data, 1);
+	cb_data->fn = fn;
+	cb_data->param = param;
+
+	return cb_data;
+}
+
+static DBusMessage* create_method_call(const char *adapter_path,
+							const char *interface,
+							const char *method_name)
+{
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(BLUEZ_SERVICE, adapter_path,
+						interface, method_name);
+	if (msg == NULL)
+		ERR("Can't allocate new method call");
+
+	return msg;
+}
+
+/* Utility function to call a D-Bus method and register a notify function to
+ * handle the reply */
+static gboolean send_with_reply_and_set_notify(DBusMessage *msg,
+				DBusPendingCallNotifyFunction notify_func,
+				gpointer user_data,
+				DBusFreeFunction user_data_free)
+{
+	DBusPendingCall *pending;
+	dbus_bool_t success;
+
+	success = dbus_connection_send_with_reply(conn, msg, &pending, -1);
+	if (pending) {
+		dbus_pending_call_set_notify(pending, notify_func, user_data,
+								user_data_free);
+	} else {
+		ERR("D-Bus connection lost before message could be sent");
+		return FALSE;
+	}
+
+	if (!success) {
+		ERR("Not enough memory to send D-Bus message");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void run_func(const char *adapter_path, GSourceFunc fn,
+							struct cmd_param *param)
+{
+	param->path = strdup(adapter_path);
+
+	/* track timeout source */
+	g_timeout_add(50, fn, param);
+}
+
+/* Handle the D-Bus method reply for FindAdapter or DefaultAdapter */
+static void get_adapter_reply(DBusPendingCall *pending, void *user_data)
+{
+	struct find_adapter_cb_data *cb_data = user_data;
+	DBusMessage *reply;
+	const char *adapter_path;
+
+	if (!pending)
+		return;
+
+	/* We must ensure that this object remains alive while we need it and
+	 * objects it references (such as the reply) */
+	dbus_pending_call_ref(pending);
+
+	/* By "stealing" the reply, we are accepting responsibility to
+	 * unreference it below. Otherwise, its memory will be leaked */
+	reply = dbus_pending_call_steal_reply(pending);
+	if (!reply) {
+		ERR("FindAdapter() failed.");
+		exit(1);
+	}
+
+	/* Ensure that the reply does not indicate an error */
+	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+		ERR("FindAdapter() failed");
+		exit(1);
+	} else {
+		struct DBusError err;
+		dbus_error_init(&err);
+
+		if (!dbus_message_get_args(reply, &err,
+					DBUS_TYPE_OBJECT_PATH, &adapter_path,
+					DBUS_TYPE_INVALID)) {
+			/* Ensure that there was not an error retrieving the
+			 * reply arguments */
+			if (dbus_error_is_set(&err)) {
+				ERR("FindAdapter() failed: %s", err.message);
+				dbus_error_free(&err);
+			} else {
+				ERR("FindAdapter() failed.");
+			}
+			exit(1);
+		}
+	}
+
+	/* In case we successfully retrieved the adapter object, execute our
+	 * core command using its path */
+	if (adapter_path != NULL && strlen(adapter_path) > 0) {
+		run_func(adapter_path, cb_data->fn, cb_data->param);
+	} else {
+		ERR("No such adapter");
+		exit(1);
+	}
+
+	/* Drop our references to these objects to avoid memory leaks */
+	dbus_message_unref(reply);
+	dbus_pending_call_unref(pending);
+}
+
+/* Looks up the adapter D-Bus object which corresponds to the Bluetooth device
+ * we will use (whether the user specified one or we fall back to the default.
+ *
+ * Once we have that object, we execute the core command. */
+static void get_adapter_and_run_func(GSourceFunc fn, struct cmd_param *param)
+{
+	DBusMessage *msg;
+	char *bt_str = NULL;
+	const char *method_name = NULL;
+	gpointer user_data;
+
+	/* The bacmp(), ba2str(), and other Bluetooth address utilities are
+	 * defined in bluetooth.h, in this source tree */
+	if (!bacmp(&bdaddr, BDADDR_ANY)) {
+		method_name = "DefaultAdapter";
+	} else {
+		method_name = "FindAdapter";
+
+		/* Allocate a string large enough for the biggest BT address */
+		bt_str = g_new(char, 18);
+		ba2str(&bdaddr, bt_str);
+	}
+
+	/* Create a new D-Bus method call */
+	if (!(msg = create_method_call("/", BLUEZ_MANAGER, method_name)))
+		exit(1);
+
+	/* In case we're making the FindAdapter D-Bus method call, we need to
+	 * append a method argument */
+	if (bt_str != NULL)
+		dbus_message_append_args(msg, DBUS_TYPE_STRING, &bt_str,
+							DBUS_TYPE_INVALID);
+
+	/* Finally, we need to asynchronously send and receive a response for
+	 * the D-Bus method call. This function does not block at all. Instead,
+	 * the GMainLoop will continue iterating until we get a response, which
+	 * will be passed to the notify function we specify here.
+	 *
+	 * We use only async D-Bus messaging because some of these D-Bus methods
+	 * could potentially take several seconds to complete, In the meantime,
+	 * if we were blocking, our application would be unresponsive and look
+	 * as if it (or * even the entire system upon which it is running) had
+	 * frozen. */
+	user_data = find_adapter_cb_data_new(fn, param);
+	if (!send_with_reply_and_set_notify(msg,  get_adapter_reply, user_data,
+									g_free))
+		exit(1);
+
+	dbus_message_unref(msg);
+}
+
+static void bluetoothd_disconnect(DBusConnection *conn, void *user_data)
+{
+	g_main_loop_quit(mainloop);
+
+	printf("Bluetooth daemon exited.\n");
+
+}
+
+static struct cmd_struct commands[] = {
+	{ NULL, NULL},
+};
+
+/* Returns FALSE in case the command could not be parsed. Any failures during
+ * execution will be handled later */
+static gboolean run_argv(int argc, char **argv)
+{
+	const char *cmd = argv[0];
+	struct cmd_param *param;
+	int i;
+
+	if (argc > 1 && g_str_equal(argv[1], "--help"))
+		cmd = "help";
+
+	for (i = 0; i < ARRAY_SIZE(commands); i++) {
+		struct cmd_struct *p = commands+i;
+		if (cmd == NULL || !g_str_equal(p->cmd, cmd))
+			continue;
+
+		param = malloc(sizeof(*param));
+		if (!param)
+			exit(1);
+
+		/* skip over the name of this program itself */
+		argc--;
+		argv++;
+
+		param->argc = argc;
+		param->argv = argv;
+
+		get_adapter_and_run_func(p->fn, param);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean handle_options(int *argc, char ***argv)
+{
+	gboolean terminal_opt = FALSE;
+
+	bacpy(&bdaddr, BDADDR_ANY);
+
+	while (*argc > 0) {
+		const char *cmd = (*argv)[0];
+		if (cmd != NULL && cmd[0] != '-')
+			break;
+
+		if (g_str_equal(cmd, "--help") ||
+						g_str_equal(cmd, "--version")) {
+			terminal_opt = TRUE;
+			break;
+		}
+
+		/*
+		 * Check remaining flags.
+		 */
+		if(g_str_equal(cmd, "-i") || g_str_equal(cmd, "--interface")) {
+			const char *iface;
+			(*argv)++;
+			(*argc)--;
+			iface = (*argv)[0];
+			if (!strncasecmp(iface, "hci", 3))
+				hci_devba(atoi(iface + 3), &bdaddr);
+			else
+				str2ba(iface, &bdaddr);
+		}
+
+		(*argv)++;
+		(*argc)--;
+	}
+
+	return terminal_opt;
+}
+
+int main(int argc, char **argv)
+{
+	DBusError err;
+	gboolean terminal_opt;
+
+	program_name = g_strdup(argv[0]);
+
+	argv++;
+	argc--;
+	terminal_opt = handle_options(&argc, &argv);
+
+	if (terminal_opt) {
+		if (g_str_equal(argv[0], "--help"))
+			show_help();
+		else if (g_str_equal(argv[0], "--version"))
+			printf("%s\n", VERSION);
+
+		exit(0);
+	}
+
+	mainloop = g_main_loop_new(NULL, FALSE);
+
+	/* This structure must be initialized before it can be used */
+	dbus_error_init(&err);
+
+	/* Set up our connection to the D-Bus system bus, which we will use
+	 * throughout this code.
+	 *
+	 * NOTE: the "g_dbus" namespace used in this file is due to BlueZ's
+	 * internal "gdbus" D-Bus client library (included by gdbus.h), which is
+	 * not to be confused with libgio's D-Bus client functionality with the
+	 * same namespace (which would be included by gio.h) */
+	conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, &err);
+	if (!conn) {
+		if (dbus_error_is_set(&err)) {
+			ERR("Can't connect to system bus: %s", err.message);
+			dbus_error_free(&err);
+		} else {
+			ERR("Can't connect to system bus");
+		}
+
+		exit(1);
+	}
+
+	/* Run the command given in the command line */
+	if (!run_argv(argc, argv)) {
+		ERR("%s: command not understood.", program_name);
+		show_help();
+		exit(1);
+	}
+
+	/* Set up handler in case the bluetooth daemon exits and it disappears
+	 * from the bus */
+	g_dbus_add_service_watch(conn, BLUEZ_SERVICE, NULL,
+					bluetoothd_disconnect, NULL, NULL);
+
+	g_main_loop_run(mainloop);
+
+	return 0;
+}
-- 
1.7.11.4


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/5] bt tool: add cmd 'discover'
  2012-10-03 17:34 [PATCH 0/5] add bt command line tool Gustavo Padovan
  2012-10-03 17:34 ` [PATCH 1/5] bt tool: initial tool handling Gustavo Padovan
@ 2012-10-03 17:34 ` Gustavo Padovan
  2012-10-03 17:34 ` [PATCH 3/5] bt tool: add cmd 'agent' Gustavo Padovan
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Gustavo Padovan @ 2012-10-03 17:34 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: travis.reitter

From: Travis Reitter <travis.reitter@collabora.co.uk>

 $ bt discover

will start a discovery for devices nearby and display them. We display
the device when we receive its first Device Found signal. If the
DeviceFound signal doesn't contains the device's name it will not be
displayed.

Steal bluetooth_parse_properties() from oFono code.
---
 tools/bt-tool.c | 242 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 241 insertions(+), 1 deletion(-)

diff --git a/tools/bt-tool.c b/tools/bt-tool.c
index 4edc255..48521fd 100644
--- a/tools/bt-tool.c
+++ b/tools/bt-tool.c
@@ -5,6 +5,7 @@
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
  *  Copyright (C) 2011 Bruno Dilly <bdilly@profusion.mobi>
  *  Copyright (C) 2011 Gustavo Padovan <gustavo@padovan.org>
+ *  Copyright (C) 2011 Intel Corporation. All rights reserved.
  *  Copyright (C) 2012 Collabora Limited.
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -84,6 +85,8 @@ static gchar *program_name = NULL;
 static bdaddr_t bdaddr;
 static DBusConnection *conn;
 
+static GHashTable *device_addrs = NULL;
+
 static void show_help()
 {
 	printf("usage: %s [-i interface] <command> [<args>]\n"
@@ -115,6 +118,127 @@ static struct find_adapter_cb_data *find_adapter_cb_data_new(GSourceFunc fn,
 	return cb_data;
 }
 
+static void parse_bool(DBusMessageIter *iter, gpointer user_data)
+{
+	gboolean *boolean = user_data;
+	int arg_type = dbus_message_iter_get_arg_type(iter);
+
+	if (arg_type != DBUS_TYPE_BOOLEAN)
+		return;
+
+	dbus_message_iter_get_basic(iter, boolean);
+}
+
+static void parse_string(DBusMessageIter *iter, gpointer user_data)
+{
+	char **str = user_data;
+	int arg_type = dbus_message_iter_get_arg_type(iter);
+
+	if (arg_type != DBUS_TYPE_OBJECT_PATH && arg_type != DBUS_TYPE_STRING)
+		return;
+
+	dbus_message_iter_get_basic(iter, str);
+}
+
+typedef void (*PropertyHandler)(DBusMessageIter *iter, gpointer user_data);
+
+struct property_handler {
+	const char *property;
+	PropertyHandler callback;
+	gpointer user_data;
+};
+
+static gint property_handler_compare(gconstpointer a, gconstpointer b)
+{
+	const struct property_handler *handler = a;
+	const char *property = b;
+
+	return strcmp(handler->property, property);
+}
+
+/* Demarshall the content of a D-Bus message reply for a given property.
+ *
+ * After the first argument, all arguments must be triplets of:
+ *
+ *   const char* property_name,
+ *   void (parse_func*) (DBusMessageIter *iter, gpointer user_data),
+ *   void** result,
+ *
+ * result must be large enough to store the type of data corresponding to
+ * property_name.
+ *
+ * Finally, a NULL must be appended as the last argument.
+ */
+static void bluetooth_parse_properties(DBusMessage *reply,
+						const char *property, ...)
+{
+	va_list args;
+	GSList *prop_handlers = NULL;
+	DBusMessageIter array, dict;
+
+	va_start(args, property);
+
+	while (property != NULL) {
+		struct property_handler *handler = g_new0(struct property_handler, 1);
+
+		handler->property = property;
+		handler->callback = va_arg(args, PropertyHandler);
+		handler->user_data = va_arg(args, gpointer);
+
+		property = va_arg(args, const char *);
+
+		prop_handlers = g_slist_prepend(prop_handlers, handler);
+	}
+
+	va_end(args);
+
+	if (dbus_message_iter_init(reply, &array) == FALSE)
+		goto done;
+
+	if (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING)
+		dbus_message_iter_next(&array);
+
+	if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
+		goto done;
+
+	dbus_message_iter_recurse(&array, &dict);
+
+	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+		DBusMessageIter entry, value;
+		const char *key;
+		GSList *l;
+
+		dbus_message_iter_recurse(&dict, &entry);
+
+		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+			goto done;
+
+		dbus_message_iter_get_basic(&entry, &key);
+
+		dbus_message_iter_next(&entry);
+
+		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+			goto done;
+
+		dbus_message_iter_recurse(&entry, &value);
+
+		l = g_slist_find_custom(prop_handlers, key,
+					property_handler_compare);
+
+		if (l) {
+			struct property_handler *handler = l->data;
+
+			handler->callback(&value, handler->user_data);
+		}
+
+		dbus_message_iter_next(&dict);
+	}
+
+done:
+	g_slist_foreach(prop_handlers, (GFunc) g_free, NULL);
+	g_slist_free(prop_handlers);
+}
+
 static DBusMessage* create_method_call(const char *adapter_path,
 							const char *interface,
 							const char *method_name)
@@ -156,6 +280,122 @@ static gboolean send_with_reply_and_set_notify(DBusMessage *msg,
 	return TRUE;
 }
 
+/* Handle BlueZ's DeviceFound signal */
+static gboolean device_found(DBusConnection *conn, DBusMessage *message,
+							void *user_data)
+{
+	const char *alias = NULL;
+	const char *address = NULL;
+	gboolean paired, trusted;
+	DBusMessageIter array;
+
+	if (dbus_message_iter_init(message, &array) == FALSE)
+		return TRUE;
+
+	if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_STRING)
+		return TRUE;
+
+	bluetooth_parse_properties(message, "Alias", parse_string, &alias,
+				"Address", parse_string, &address,
+				"Paired", parse_bool, &paired,
+				"Trusted", parse_bool, &trusted,
+				NULL);
+
+	/* Only list new devices if we have not yet listed them. BlueZ will emit
+	 * this signal multiple times for the same device because it may receive
+	 * additional information from the device in subsequent transmissions */
+	if (!g_hash_table_contains(device_addrs, address)) {
+		gint alias_len;
+		const char *alias_status_gap = " ";
+
+		/* Make sure everything aligns under each header; this assumes
+		 * 8-character tabs and 17-character BT address */
+		alias_len = (alias == NULL ? 0 : strlen(alias));
+		if (FALSE) { }
+		else if (alias_len < 3)
+			alias_status_gap = "\t\t\t\t";
+		else if (alias_len < 11)
+			alias_status_gap = "\t\t\t";
+		else if (alias_len < 19)
+			alias_status_gap = "\t\t";
+		else
+			alias_status_gap = "\t";
+
+		g_hash_table_insert(device_addrs, g_strdup (address), NULL);
+		printf("%s    %s%s%s%s\n", address, alias, alias_status_gap,
+						paired ? "paired": "unpaired",
+						trusted ? ", trusted" : "");
+	}
+
+	return TRUE;
+}
+
+static void stop_discovery_reply(DBusPendingCall *pending, void *user_data)
+{
+	/* Once we're done discovering and displaying devices, exit gracefully
+	 */
+	g_main_loop_quit(mainloop);
+}
+
+static gboolean stop_discovery_cb(gpointer data)
+{
+	const char *path = data;
+	DBusMessage *msg;
+
+	if (!(msg = create_method_call(path, BLUEZ_ADAPTER, "StopDiscovery")))
+		return FALSE;
+	send_with_reply_and_set_notify(msg, stop_discovery_reply, NULL, NULL);
+
+	dbus_message_unref(msg);
+
+	return FALSE;
+}
+
+/* Listen for Bluetooth devices broadcasting their availability, then display
+ * the results */
+static gboolean cmd_discover(gpointer data)
+{
+	struct cmd_param *param = data;
+	dbus_bool_t success;
+	DBusMessage *msg;
+
+	/* We may recieve multiple messages for each device, because BlueZ
+	 * notifies us any time it gets additional information for a given
+	 * device, we need to prevent printing duplicate information.
+	 *
+	 * So, we use this hash table as a (unique) set to avoid displaying
+	 * information about a single device more than once. */
+	device_addrs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+									NULL);
+
+	g_dbus_add_signal_watch(conn, BLUEZ_SERVICE, NULL, BLUEZ_ADAPTER,
+				"DeviceFound", device_found, NULL, NULL);
+
+	if (!(msg = create_method_call(param->path, BLUEZ_ADAPTER,
+							"StartDiscovery")))
+		return FALSE;
+
+	printf("Device Address       Name\t\t\tStatus\n");
+	printf("--------------       ----\t\t\t------\n");
+
+	/* Send the message without registering a notify function. This means we
+	 * won't recieve indication of any errors (which may be acceptable in
+	 * certain instances, such as when discovering visible devices) */
+	success = dbus_connection_send(conn, msg, NULL);
+
+	/* We can't know for sure when we've heard back from all Bluetooth
+	 * devices within range, so we need to wait some number of seconds and
+	 * print details as they come in */
+	if (success)
+		g_timeout_add_seconds(12, stop_discovery_cb, param->path);
+	else
+		ERR("Not enough memory to send message");
+
+	dbus_message_unref(msg);
+
+	return FALSE;
+}
+
 static void run_func(const char *adapter_path, GSourceFunc fn,
 							struct cmd_param *param)
 {
@@ -284,7 +524,7 @@ static void bluetoothd_disconnect(DBusConnection *conn, void *user_data)
 }
 
 static struct cmd_struct commands[] = {
-	{ NULL, NULL},
+	{ "discover", cmd_discover},
 };
 
 /* Returns FALSE in case the command could not be parsed. Any failures during
-- 
1.7.11.4


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 3/5] bt tool: add cmd 'agent'
  2012-10-03 17:34 [PATCH 0/5] add bt command line tool Gustavo Padovan
  2012-10-03 17:34 ` [PATCH 1/5] bt tool: initial tool handling Gustavo Padovan
  2012-10-03 17:34 ` [PATCH 2/5] bt tool: add cmd 'discover' Gustavo Padovan
@ 2012-10-03 17:34 ` Gustavo Padovan
  2012-10-03 17:34 ` [PATCH 4/5] bt tool: add cmd 'pair' Gustavo Padovan
  2012-10-03 17:34 ` [PATCH 5/5] bt tool: add cmd 'remove' Gustavo Padovan
  4 siblings, 0 replies; 7+ messages in thread
From: Gustavo Padovan @ 2012-10-03 17:34 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: travis.reitter, Gustavo Padovan

From: Gustavo Padovan <gustavo.padovan@collabora.co.uk>

'bt agent' command register a agent in BlueZ that will loop waiting for
pairing and incoming connection requests.
---
 tools/bt-tool.c | 352 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 352 insertions(+)

diff --git a/tools/bt-tool.c b/tools/bt-tool.c
index 48521fd..6035eda 100644
--- a/tools/bt-tool.c
+++ b/tools/bt-tool.c
@@ -82,6 +82,8 @@ struct find_adapter_cb_data {
 static GMainLoop *mainloop = NULL;
 static gchar *program_name = NULL;
 
+static gboolean exit_on_release = TRUE;
+
 static bdaddr_t bdaddr;
 static DBusConnection *conn;
 
@@ -280,6 +282,334 @@ static gboolean send_with_reply_and_set_notify(DBusMessage *msg,
 	return TRUE;
 }
 
+static DBusMessage *agent_request_confirmation(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	DBusMessage *reply;
+	const char *path;
+	unsigned int passkey;
+	char conf[16];
+	int ret;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+						DBUS_TYPE_UINT32, &passkey,
+						DBUS_TYPE_INVALID)) {
+		ERR("Invalid arguments for RequestConfirmation method");
+		reply = dbus_message_new_error(msg,
+					BLUEZ_ERROR".InvalidArguments", "");
+		goto send;
+	}
+
+	printf("Pairing request confirmation for %s.\n"
+		"Confirm the passkey %6.6d (yes/no): ", path, passkey);
+	ret = scanf("%3s", conf);
+	if (!ret)
+		return NULL;
+
+	if (g_str_equal(conf, "yes"))
+		reply = dbus_message_new_method_return(msg);
+	else
+		reply = dbus_message_new_error(msg, BLUEZ_ERROR".Rejected",
+						"Passkey doesn't match");
+
+	if (!reply) {
+		ERR("Can't create reply message");
+		return NULL;
+	}
+
+send:
+	dbus_connection_send(conn, reply, NULL);
+
+	dbus_message_unref(reply);
+
+	return NULL;
+}
+
+static DBusMessage *agent_request_pincode(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	DBusMessage *reply;
+	const char *path;
+	char pin[64];
+	char *str;
+	int ret;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+						DBUS_TYPE_INVALID)) {
+		ERR("Invalid arguments for RequestPinCode method");
+		reply = dbus_message_new_error(msg,
+					BLUEZ_ERROR".InvalidArguments", "");
+		goto send;
+	}
+
+	printf("Pairing request for  %s.\nType the PIN code: ", path);
+	ret = scanf("%s", pin);
+	if (!ret)
+		return NULL;
+
+	if (!(reply = dbus_message_new_method_return(msg))) {
+		ERR("Not enough memory to construct message");
+		return NULL;
+	}
+
+	str = pin;
+	dbus_message_append_args(reply, DBUS_TYPE_STRING, &str,
+							DBUS_TYPE_INVALID);
+
+send:
+	dbus_connection_send(conn, reply, NULL);
+
+	dbus_message_unref(reply);
+
+	return NULL;
+}
+
+static DBusMessage *agent_request_passkey(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	DBusMessage *reply;
+	const char *path;
+	unsigned int passkey;
+	int ret;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+						DBUS_TYPE_INVALID)) {
+		ERR("Invalid arguments for RequestPasskey method");
+		reply = dbus_message_new_error(msg,
+					BLUEZ_ERROR".InvalidArguments", "");
+		goto send;
+	}
+
+	printf("Pairing request for  %s.\nType the Passkey: ", path);
+	ret = scanf("%u", &passkey);
+	if (!ret)
+		return NULL;
+
+	reply = dbus_message_new_method_return(msg);
+
+	dbus_message_append_args(reply, DBUS_TYPE_STRING, passkey,
+							DBUS_TYPE_INVALID);
+
+send:
+	if (!dbus_connection_send(conn, reply, NULL))
+		ERR("Not enough memory to send message");
+
+	dbus_message_unref(reply);
+
+	return NULL;
+}
+
+static DBusMessage *agent_authorize(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	DBusMessage *reply;
+	const char *path, *uuid;
+	char conf[16];
+	int ret;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+						DBUS_TYPE_STRING, &uuid,
+						DBUS_TYPE_INVALID)) {
+		ERR("Invalid arguments for Authorize method");
+		reply = dbus_message_new_error(msg,
+					BLUEZ_ERROR".InvalidArguments", "");
+		goto send;
+	}
+
+	printf("Incoming connection request for %s (%s).\n"
+		"Authorize (yes/no): ", path, uuid);
+	ret = scanf("%3s", conf);
+	if (!ret)
+		return NULL;
+
+	if (g_str_equal(conf, "yes"))
+		reply = dbus_message_new_method_return(msg);
+	else
+		reply = dbus_message_new_error(msg, BLUEZ_ERROR".Rejected",
+						"Connection Rejected");
+
+	if (!reply) {
+		ERR("Can't create reply message");
+		return NULL;
+	}
+
+send:
+	dbus_connection_send(conn, reply, NULL);
+
+	dbus_message_unref(reply);
+
+	return NULL;
+}
+
+static DBusMessage *agent_confirm_mode_change(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	DBusMessage *reply;
+	const char *mode;
+	char conf[16];
+	int ret;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &mode,
+							DBUS_TYPE_INVALID)) {
+		ERR("Invalid arguments for ConfirmModeChange");
+		reply = dbus_message_new_error(msg,
+					BLUEZ_ERROR".InvalidArguments", "");
+		goto send;
+	}
+
+	printf("Authorize mode change to %s (yes/no): ", mode);
+	ret = scanf("%3s", conf);
+	if (!ret)
+		return NULL;
+
+	if (g_str_equal(conf, "yes"))
+		reply = dbus_message_new_method_return(msg);
+	else
+		reply = dbus_message_new_error(msg, BLUEZ_ERROR".Rejected",
+						"Mode Change Rejected");
+
+	if (!reply) {
+		ERR("Can't create reply message");
+		return NULL;
+	}
+
+send:
+	dbus_connection_send(conn, reply, NULL);
+
+	dbus_message_unref(reply);
+
+	return NULL;
+}
+
+static DBusMessage *agent_cancel(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	DBusMessage *reply;
+
+	printf("Request canceled\n");
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply) {
+		ERR("Can't create reply message");
+		return NULL;
+	}
+
+	dbus_connection_send(conn, reply, NULL);
+
+	dbus_message_unref(reply);
+
+	g_main_loop_quit(mainloop);
+
+	return NULL;
+}
+
+static DBusMessage *agent_release(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	DBusMessage *reply;
+
+	printf("Agent released\n");
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply) {
+		ERR("Can't create reply message");
+		return NULL;
+	}
+
+	dbus_connection_send(conn, reply, NULL);
+
+	dbus_message_unref(reply);
+
+	/* Only exit the mainloop in case there are no other D-Bus methods, such
+	 * as CreatePairedDevice, pending */
+	if (exit_on_release)
+		g_main_loop_quit(mainloop);
+
+	return NULL;
+}
+
+/* Handle the D-Bus method reply for RegisterAgent. See comments on
+ * get_adapter_reply() for more details */
+static void register_agent_reply(DBusPendingCall *pending, void *user_data)
+{
+	DBusMessage *reply;
+
+	reply = dbus_pending_call_steal_reply(pending);
+
+	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+		ERR("Agent Registration failed.");
+		exit(1);
+	}
+
+	printf("Agent registered\n");
+}
+
+static int register_agent(DBusConnection *conn, const char *adapter_path,
+						const char *agent_path,
+						const char *capabilities)
+{
+	dbus_bool_t success;
+	DBusMessage *msg;
+	DBusPendingCall *pending;
+
+	if (!(msg = create_method_call(adapter_path, BLUEZ_ADAPTER,
+							"RegisterAgent")))
+		return -1;
+
+	dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
+					DBUS_TYPE_STRING, &capabilities,
+					DBUS_TYPE_INVALID);
+
+	success = dbus_connection_send_with_reply(conn, msg, &pending, -1);
+	if (pending)
+		dbus_pending_call_set_notify(pending, register_agent_reply,
+								NULL, NULL);
+
+	dbus_message_unref(msg);
+
+	if (!success) {
+		ERR("Not enough memory for message send");
+		return -1;
+	}
+
+	return 0;
+}
+
+/* This is the table of methods that our agent supports, including their name,
+ * and arguments (including their D-Bus types). See the D-Bus documentation for
+ * details on the type notation */
+static const GDBusMethodTable agent_methods[] = {
+	{ GDBUS_ASYNC_METHOD("RequestConfirmation",
+			GDBUS_ARGS({ "device", "o" }, { "passkey", "u" }),
+			NULL,
+			agent_request_confirmation) },
+	{ GDBUS_ASYNC_METHOD("RequestPinCode",
+			GDBUS_ARGS({ "device", "o" }),
+			GDBUS_ARGS({"pincode", "s"}),
+			agent_request_pincode) },
+	{ GDBUS_ASYNC_METHOD("RequestPasskey",
+			GDBUS_ARGS({ "device", "o" }),
+			GDBUS_ARGS({"passkey", "u"}),
+			agent_request_passkey) },
+	{ GDBUS_ASYNC_METHOD("Authorize",
+			GDBUS_ARGS({ "device", "o" }, { "uuid", "s" }),
+			NULL,
+			agent_authorize) },
+	{ GDBUS_ASYNC_METHOD("ConfirmModeChange",
+			GDBUS_ARGS( { "mode", "s" }),
+			NULL,
+			agent_confirm_mode_change) },
+	{ GDBUS_ASYNC_METHOD("Cancel",
+			NULL,
+			NULL,
+			agent_cancel) },
+	{ GDBUS_ASYNC_METHOD("Release",
+			NULL,
+			NULL,
+			agent_release) },
+	{ }
+};
+
 /* Handle BlueZ's DeviceFound signal */
 static gboolean device_found(DBusConnection *conn, DBusMessage *message,
 							void *user_data)
@@ -396,6 +726,27 @@ static gboolean cmd_discover(gpointer data)
 	return FALSE;
 }
 
+/* Register this program as an agent itself, to handle other BlueZ clients'
+ * requests */
+static gboolean cmd_agent(gpointer data)
+{
+	struct cmd_param *param = data;
+
+	if (!g_dbus_register_interface(conn, "/tool/agent", BLUEZ_AGENT,
+					agent_methods, NULL, NULL, NULL,
+									NULL)) {
+		ERR("Agent interface init failed");
+		return FALSE;
+	}
+
+	if (register_agent(conn, param->path, "/tool/agent", "DisplayYesNo")
+									< 0) {
+		exit(1);
+	}
+
+	return FALSE;
+}
+
 static void run_func(const char *adapter_path, GSourceFunc fn,
 							struct cmd_param *param)
 {
@@ -525,6 +876,7 @@ static void bluetoothd_disconnect(DBusConnection *conn, void *user_data)
 
 static struct cmd_struct commands[] = {
 	{ "discover", cmd_discover},
+	{ "agent", cmd_agent},
 };
 
 /* Returns FALSE in case the command could not be parsed. Any failures during
-- 
1.7.11.4


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 4/5] bt tool: add cmd 'pair'
  2012-10-03 17:34 [PATCH 0/5] add bt command line tool Gustavo Padovan
                   ` (2 preceding siblings ...)
  2012-10-03 17:34 ` [PATCH 3/5] bt tool: add cmd 'agent' Gustavo Padovan
@ 2012-10-03 17:34 ` Gustavo Padovan
  2012-10-03 17:34 ` [PATCH 5/5] bt tool: add cmd 'remove' Gustavo Padovan
  4 siblings, 0 replies; 7+ messages in thread
From: Gustavo Padovan @ 2012-10-03 17:34 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: travis.reitter

From: Travis Reitter <travis.reitter@collabora.co.uk>

'bt pair <device address>' starts a pairing procedure with a remote
device.
---
 tools/bt-tool.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)

diff --git a/tools/bt-tool.c b/tools/bt-tool.c
index 6035eda..11d26c5 100644
--- a/tools/bt-tool.c
+++ b/tools/bt-tool.c
@@ -575,6 +575,65 @@ static int register_agent(DBusConnection *conn, const char *adapter_path,
 	return 0;
 }
 
+/* Handle the D-Bus method reply for CreatePairedDevice. See comments on
+ * get_adapter_reply() for more details */
+static void create_paired_device_reply(DBusPendingCall *pending,
+								void *user_data)
+{
+	const char *device = user_data;
+	const char *device_path;
+	DBusMessage *reply;
+	DBusError err;
+
+	reply = dbus_pending_call_steal_reply(pending);
+
+	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+		ERR("Pairing Failed.");
+		exit(1);
+	}
+
+	dbus_error_init(&err);
+	if (!dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH,
+					&device_path, DBUS_TYPE_INVALID)) {
+		if (dbus_error_is_set(&err)) {
+			ERR("Paring failed: %s", err.message);
+			dbus_error_free(&err);
+		}
+		exit(1);
+	}
+
+	printf("Device %s successfully paired.\n", device);
+	g_main_loop_quit(mainloop);
+}
+
+static int create_paired_device(DBusConnection *conn, const char *adapter_path,
+						const char *agent_path,
+						const char *capabilities,
+						const char *device)
+{
+	DBusMessage *msg;
+	int retval = 0;
+
+	if (!(msg = create_method_call(adapter_path, BLUEZ_ADAPTER,
+							"CreatePairedDevice")))
+		return -1;
+
+	dbus_message_append_args(msg, DBUS_TYPE_STRING, &device,
+					DBUS_TYPE_OBJECT_PATH, &agent_path,
+					DBUS_TYPE_STRING, &capabilities,
+					DBUS_TYPE_INVALID);
+
+	if (!send_with_reply_and_set_notify(msg, create_paired_device_reply,
+						g_strdup(device), g_free))
+		retval = -1;
+
+	dbus_message_unref(msg);
+
+	exit_on_release = FALSE;
+
+	return retval;
+}
+
 /* This is the table of methods that our agent supports, including their name,
  * and arguments (including their D-Bus types). See the D-Bus documentation for
  * details on the type notation */
@@ -726,6 +785,29 @@ static gboolean cmd_discover(gpointer data)
 	return FALSE;
 }
 
+/* Pair (connect) our Bluetooth adapter with an external Bluetooth device */
+static gboolean cmd_pair(gpointer data)
+{
+	struct cmd_param *param = data;
+	char *device;
+
+	if (!g_dbus_register_interface(conn, "/tool/agent", BLUEZ_AGENT,
+					agent_methods, NULL, NULL, NULL,
+									NULL)) {
+		ERR("Adapter interface init failed on path %s", param->path);
+		return FALSE;
+	}
+
+	device = param->argv[0];
+
+	if (create_paired_device(conn, param->path, "/tool/agent",
+						"DisplayYesNo", device) < 0) {
+		exit(1);
+	}
+
+	return FALSE;
+}
+
 /* Register this program as an agent itself, to handle other BlueZ clients'
  * requests */
 static gboolean cmd_agent(gpointer data)
@@ -876,6 +958,7 @@ static void bluetoothd_disconnect(DBusConnection *conn, void *user_data)
 
 static struct cmd_struct commands[] = {
 	{ "discover", cmd_discover},
+	{ "pair", cmd_pair},
 	{ "agent", cmd_agent},
 };
 
-- 
1.7.11.4


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 5/5] bt tool: add cmd 'remove'
  2012-10-03 17:34 [PATCH 0/5] add bt command line tool Gustavo Padovan
                   ` (3 preceding siblings ...)
  2012-10-03 17:34 ` [PATCH 4/5] bt tool: add cmd 'pair' Gustavo Padovan
@ 2012-10-03 17:34 ` Gustavo Padovan
  4 siblings, 0 replies; 7+ messages in thread
From: Gustavo Padovan @ 2012-10-03 17:34 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: travis.reitter

From: Travis Reitter <travis.reitter@collabora.co.uk>

'bt remove <device address>' will remove a previous paired device
---
 tools/bt-tool.c | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 136 insertions(+)

diff --git a/tools/bt-tool.c b/tools/bt-tool.c
index 11d26c5..aeb47e4 100644
--- a/tools/bt-tool.c
+++ b/tools/bt-tool.c
@@ -102,6 +102,7 @@ static void show_help()
 	"	discover				Scan for devices\n"
 	"	pair <device address>			Start pairing\n"
 	"	agent					Run BlueZ agent\n"
+	"	remove					Remove a paired device\n"
 							"\n", program_name);
 
 	if(mainloop != NULL)
@@ -740,6 +741,94 @@ static gboolean stop_discovery_cb(gpointer data)
 	return FALSE;
 }
 
+/* Handle the D-Bus method reply for RemoveDevice. See comments on
+ * get_adapter_reply() for more details */
+static void remove_device_reply(DBusPendingCall *pending, void *user_data)
+{
+	const char *device_path = user_data;
+	DBusMessage *reply;
+
+	reply = dbus_pending_call_steal_reply(pending);
+	if (!reply) {
+		ERR("Failed to get RemoveDevice reply");
+		exit(1);
+	}
+
+	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+		ERR("Remove device failed.");
+		exit(1);
+	}
+
+	printf("Device %s successfully removed.\n", device_path);
+	g_main_loop_quit(mainloop);
+}
+
+static int remove_device(DBusConnection *conn, const char *adapter_path,
+							const char *device_path)
+{
+	DBusMessage *msg;
+	int retval = 0;
+
+	if (!(msg = create_method_call(adapter_path, BLUEZ_ADAPTER,
+							"RemoveDevice")))
+		return -1;
+
+	dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &device_path,
+							DBUS_TYPE_INVALID);
+
+	if (!send_with_reply_and_set_notify(msg, remove_device_reply,
+						g_strdup(device_path), g_free))
+		retval = -1;
+
+	dbus_message_unref(msg);
+
+	return retval;
+}
+
+/* Handle the D-Bus method reply for FindDevice. See comments on
+ * get_adapter_reply() for more details */
+static void find_device_reply(DBusPendingCall *pending, void *user_data)
+{
+	const char *adapter_path = user_data;
+	DBusMessage *reply;
+	const char *device_path;
+
+	if (!pending)
+		return;
+
+	dbus_pending_call_ref(pending);
+
+	reply = dbus_pending_call_steal_reply(pending);
+	if (!reply) {
+		ERR("Failed to get FindDevice() reply");
+		exit(1);
+	}
+
+	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+		ERR("Failed to find device.");
+		exit(1);
+	} else {
+		struct DBusError err;
+		dbus_error_init(&err);
+
+		if (!dbus_message_get_args(reply, &err,
+					DBUS_TYPE_OBJECT_PATH, &device_path,
+					DBUS_TYPE_INVALID)) {
+			if (dbus_error_is_set(&err)) {
+				ERR("Failed to find device %s", err.message);
+				dbus_error_free(&err);
+			}
+			exit(1);
+		}
+	}
+
+	if (remove_device(conn, adapter_path, device_path) < 0)
+		exit(1);
+
+	dbus_message_unref(reply);
+	dbus_pending_call_unref(pending);
+}
+
 /* Listen for Bluetooth devices broadcasting their availability, then display
  * the results */
 static gboolean cmd_discover(gpointer data)
@@ -829,6 +918,52 @@ static gboolean cmd_agent(gpointer data)
 	return FALSE;
 }
 
+/* Remove a pairing with a given Bluetooth device */
+static gboolean cmd_remove(gpointer data)
+{
+	struct cmd_param *param = data;
+	char *device_addr;
+	DBusMessage *msg;
+	gboolean retval = FALSE;
+
+	device_addr = param->argv[0];
+	if (!device_addr) {
+		ERR("%s: missing device address paramenter", program_name);
+		show_help();
+		exit(1);
+	}
+
+	/* Register a D-Bus interface for our agent to handle our removal
+	 * request */
+	if (!g_dbus_register_interface(conn, "/tool/agent", BLUEZ_AGENT,
+					agent_methods, NULL, NULL, NULL,
+									NULL)) {
+		ERR("Adapter interface init failed for path %s", param->path);
+		return FALSE;
+	}
+
+	device_addr = param->argv[0];
+
+	if (!(msg = create_method_call(param->path, BLUEZ_ADAPTER,
+								"FindDevice")))
+		return FALSE;
+
+	dbus_message_append_args(msg, DBUS_TYPE_STRING, &device_addr,
+							DBUS_TYPE_INVALID);
+
+	/* Set up our notify function and provide it both the extra data it
+	 * requires and another function to free this data when our notify
+	 * function is done with it */
+	if (!send_with_reply_and_set_notify(msg, find_device_reply,
+							g_strdup(param->path),
+							g_free))
+		retval = FALSE;
+
+	dbus_message_unref(msg);
+
+	return retval;
+}
+
 static void run_func(const char *adapter_path, GSourceFunc fn,
 							struct cmd_param *param)
 {
@@ -960,6 +1095,7 @@ static struct cmd_struct commands[] = {
 	{ "discover", cmd_discover},
 	{ "pair", cmd_pair},
 	{ "agent", cmd_agent},
+	{ "remove", cmd_remove},
 };
 
 /* Returns FALSE in case the command could not be parsed. Any failures during
-- 
1.7.11.4


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/5] bt tool: initial tool handling
  2012-10-03 17:34 ` [PATCH 1/5] bt tool: initial tool handling Gustavo Padovan
@ 2012-10-04  9:47   ` Marcel Holtmann
  0 siblings, 0 replies; 7+ messages in thread
From: Marcel Holtmann @ 2012-10-04  9:47 UTC (permalink / raw)
  To: Gustavo Padovan; +Cc: linux-bluetooth, travis.reitter, Gustavo Padovan

Hi Gustavo,

> All core code to using this tool is here, the nexts steps will add
> commands like 'discover' 'pair' 'agent', etc.
> ---
>  Makefile.tools  |   7 +-
>  tools/bt-tool.c | 421 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 427 insertions(+), 1 deletion(-)
>  create mode 100644 tools/bt-tool.c

use client/main.c for this. Similar to how we do that inside ConnMan.
The tools directory is full of files by now.

Regards

Marcel



^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2012-10-04  9:47 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-10-03 17:34 [PATCH 0/5] add bt command line tool Gustavo Padovan
2012-10-03 17:34 ` [PATCH 1/5] bt tool: initial tool handling Gustavo Padovan
2012-10-04  9:47   ` Marcel Holtmann
2012-10-03 17:34 ` [PATCH 2/5] bt tool: add cmd 'discover' Gustavo Padovan
2012-10-03 17:34 ` [PATCH 3/5] bt tool: add cmd 'agent' Gustavo Padovan
2012-10-03 17:34 ` [PATCH 4/5] bt tool: add cmd 'pair' Gustavo Padovan
2012-10-03 17:34 ` [PATCH 5/5] bt tool: add cmd 'remove' Gustavo Padovan

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.