diff options
Diffstat (limited to 'src/bt-device.c')
-rw-r--r-- | src/bt-device.c | 1067 |
1 files changed, 675 insertions, 392 deletions
diff --git a/src/bt-device.c b/src/bt-device.c index 799ee32..3472331 100644 --- a/src/bt-device.c +++ b/src/bt-device.c @@ -25,27 +25,31 @@ #include <config.h> #endif +#include <errno.h> #include <locale.h> #include <stdlib.h> #include <string.h> #include <glib.h> +#include <gio/gio.h> #include "lib/dbus-common.h" #include "lib/helpers.h" +#include "lib/agent-helper.h" #include "lib/sdp.h" #include "lib/bluez-api.h" -enum { - REC, - ATTR, - SEQ, - ELEM, +enum +{ + REC, + ATTR, + SEQ, + ELEM, - ATTR_ID, - UUID_ID, + ATTR_ID, + UUID_ID, - LAST_E + LAST_E }; static int xml_t[LAST_E] = {0, 0, 0, 0, -1, -1}; @@ -68,411 +72,690 @@ static gboolean verbose_arg = FALSE; static gboolean is_verbose_attr(int attr_id) { - if ( - attr_id == SDP_ATTR_ID_SERVICE_CLASS_ID_LIST || - attr_id == SDP_ATTR_ID_PROTOCOL_DESCRIPTOR_LIST || - attr_id == SDP_ATTR_ID_BLUETOOTH_PROFILE_DESCRIPTOR_LIST || - attr_id == SDP_ATTR_ID_DOCUMENTATION_URL || - attr_id == SDP_ATTR_ID_SERVICE_NAME || - attr_id == SDP_ATTR_ID_SERVICE_DESCRIPTION || - attr_id == SDP_ATTR_ID_PROVIDER_NAME || - attr_id == SDP_ATTR_ID_SECURITY_DESCRIPTION - ) - return FALSE; - - return TRUE; + if ( + attr_id == SDP_ATTR_ID_SERVICE_CLASS_ID_LIST || + attr_id == SDP_ATTR_ID_PROTOCOL_DESCRIPTOR_LIST || + attr_id == SDP_ATTR_ID_BLUETOOTH_PROFILE_DESCRIPTOR_LIST || + attr_id == SDP_ATTR_ID_DOCUMENTATION_URL || + attr_id == SDP_ATTR_ID_SERVICE_NAME || + attr_id == SDP_ATTR_ID_SERVICE_DESCRIPTION || + attr_id == SDP_ATTR_ID_PROVIDER_NAME || + attr_id == SDP_ATTR_ID_SECURITY_DESCRIPTION + ) + return FALSE; + + return TRUE; } static const gchar *xml_get_attr_value(const gchar *attr_name, const gchar **attribute_names, const gchar **attribute_values) { - for (int i = 0; attribute_names[i] != NULL; i++) { - if (g_strcmp0(attribute_names[i], attr_name) == 0) { - return attribute_values[i]; - } - } - - return NULL; + for (int i = 0; attribute_names[i] != NULL; i++) + { + if (g_strcmp0(attribute_names[i], attr_name) == 0) + { + return attribute_values[i]; + } + } + + return NULL; } static void xml_start_element(GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - gpointer user_data, - GError **error) + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) { - const gchar *id_t = xml_get_attr_value("id", attribute_names, attribute_values); - const gchar *value_t = xml_get_attr_value("value", attribute_names, attribute_values); - - if (g_strcmp0(element_name, "record") == 0) { - xml_t[REC]++; - } else if (g_strcmp0(element_name, "attribute") == 0 && id_t) { - int attr_id = xtoi(id_t); - const gchar *attr_name = sdp_get_attr_id_name(attr_id); - - xml_t[ATTR]++; - xml_t[ATTR_ID] = attr_id; - - if (!verbose_arg && is_verbose_attr(xml_t[ATTR_ID])) return; - - if (attr_name == NULL) { - g_print("AttrID-%s: ", id_t); - } else { - g_print("%s: ", attr_name); - } - } else if (g_strcmp0(element_name, "sequence") == 0) { - xml_t[SEQ]++; - } else if (g_pattern_match_simple("*int*", element_name) && value_t) { - xml_t[ELEM]++; - - if (!verbose_arg && is_verbose_attr(xml_t[ATTR_ID])) return; - - if (xml_t[ELEM] == 1 && xml_t[SEQ] > 1) { - g_print("\n"); - for (int i = 0; i < xml_t[SEQ]; i++) g_print(" "); - } else if (xml_t[ELEM] > 1) { - g_print(", "); - } - - if (xml_t[UUID_ID] == SDP_UUID_RFCOMM) { - g_print("Channel: %d", xtoi(value_t)); - } else { - g_print("0x%x", xtoi(value_t)); - } - } else if (g_strcmp0(element_name, "uuid") == 0 && value_t) { - int uuid_id = -1; - const gchar *uuid_name; - - if (value_t[0] == '0' && value_t[1] == 'x') { - uuid_id = xtoi(value_t); - uuid_name = sdp_get_uuid_name(uuid_id); - } else { - uuid_name = uuid2name(value_t); - } - - xml_t[ELEM]++; - xml_t[UUID_ID] = uuid_id; - - if (!verbose_arg && is_verbose_attr(xml_t[ATTR_ID])) return; - - if (xml_t[ELEM] == 1 && xml_t[SEQ] > 1) { - g_print("\n"); - for (int i = 0; i < xml_t[SEQ]; i++) g_print(" "); - } else if (xml_t[ELEM] > 1) { - g_print(", "); - } - - if (uuid_name == NULL) { - g_print("\"UUID-%s\"", value_t); - } else { - g_print("\"%s\"", uuid_name); - } - } else if (g_strcmp0(element_name, "text") == 0 && value_t) { - xml_t[ELEM]++; - - if (!verbose_arg && is_verbose_attr(xml_t[ATTR_ID])) return; - - if (xml_t[ELEM] == 1 && xml_t[SEQ] > 1) { - g_print("\n"); - for (int i = 0; i < xml_t[SEQ]; i++) g_print(" "); - } else if (xml_t[ELEM] > 1) { - g_print(", "); - } - - g_print("\"%s\"", value_t); - } else if (g_strcmp0(element_name, "boolean") == 0 && value_t) { - xml_t[ELEM]++; - - if (!verbose_arg && is_verbose_attr(xml_t[ATTR_ID])) return; - - if (xml_t[ELEM] == 1 && xml_t[SEQ] > 1) { - g_print("\n"); - for (int i = 0; i < xml_t[SEQ]; i++) g_print(" "); - } else if (xml_t[ELEM] > 1) { - g_print(", "); - } - - g_print("%s", value_t); - } else { - if (error) - *error = g_error_new(G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Invalid XML element: %s", element_name); - } + const gchar *id_t = xml_get_attr_value("id", attribute_names, attribute_values); + const gchar *value_t = xml_get_attr_value("value", attribute_names, attribute_values); + + if (g_strcmp0(element_name, "record") == 0) + { + xml_t[REC]++; + } + else if (g_strcmp0(element_name, "attribute") == 0 && id_t) + { + int attr_id = xtoi(id_t); + const gchar *attr_name = sdp_get_attr_id_name(attr_id); + + xml_t[ATTR]++; + xml_t[ATTR_ID] = attr_id; + + if (!verbose_arg && is_verbose_attr(xml_t[ATTR_ID])) return; + + if (attr_name == NULL) + { + g_print("AttrID-%s: ", id_t); + } + else + { + g_print("%s: ", attr_name); + } + } + else if (g_strcmp0(element_name, "sequence") == 0) + { + xml_t[SEQ]++; + } + else if (g_pattern_match_simple("*int*", element_name) && value_t) + { + xml_t[ELEM]++; + + if (!verbose_arg && is_verbose_attr(xml_t[ATTR_ID])) return; + + if (xml_t[ELEM] == 1 && xml_t[SEQ] > 1) + { + g_print("\n"); + for (int i = 0; i < xml_t[SEQ]; i++) g_print(" "); + } + else if (xml_t[ELEM] > 1) + { + g_print(", "); + } + + if (xml_t[UUID_ID] == SDP_UUID_RFCOMM) + { + g_print("Channel: %d", xtoi(value_t)); + } + else + { + g_print("0x%x", xtoi(value_t)); + } + } + else if (g_strcmp0(element_name, "uuid") == 0 && value_t) + { + int uuid_id = -1; + const gchar *uuid_name; + + if (value_t[0] == '0' && value_t[1] == 'x') + { + uuid_id = xtoi(value_t); + uuid_name = sdp_get_uuid_name(uuid_id); + } + else + { + uuid_name = uuid2name(value_t); + } + + xml_t[ELEM]++; + xml_t[UUID_ID] = uuid_id; + + if (!verbose_arg && is_verbose_attr(xml_t[ATTR_ID])) return; + + if (xml_t[ELEM] == 1 && xml_t[SEQ] > 1) + { + g_print("\n"); + for (int i = 0; i < xml_t[SEQ]; i++) g_print(" "); + } + else if (xml_t[ELEM] > 1) + { + g_print(", "); + } + + if (uuid_name == NULL) + { + g_print("\"UUID-%s\"", value_t); + } + else + { + g_print("\"%s\"", uuid_name); + } + } + else if (g_strcmp0(element_name, "text") == 0 && value_t) + { + xml_t[ELEM]++; + + if (!verbose_arg && is_verbose_attr(xml_t[ATTR_ID])) return; + + if (xml_t[ELEM] == 1 && xml_t[SEQ] > 1) + { + g_print("\n"); + for (int i = 0; i < xml_t[SEQ]; i++) g_print(" "); + } + else if (xml_t[ELEM] > 1) + { + g_print(", "); + } + + g_print("\"%s\"", value_t); + } + else if (g_strcmp0(element_name, "boolean") == 0 && value_t) + { + xml_t[ELEM]++; + + if (!verbose_arg && is_verbose_attr(xml_t[ATTR_ID])) return; + + if (xml_t[ELEM] == 1 && xml_t[SEQ] > 1) + { + g_print("\n"); + for (int i = 0; i < xml_t[SEQ]; i++) g_print(" "); + } + else if (xml_t[ELEM] > 1) + { + g_print(", "); + } + + g_print("%s", value_t); + } + else + { + if (error) + *error = g_error_new(G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Invalid XML element: %s", element_name); + } } static void xml_end_element(GMarkupParseContext *context, - const gchar *element_name, - gpointer user_data, - GError **error) + const gchar *element_name, + gpointer user_data, + GError **error) { - if (g_strcmp0(element_name, "record") == 0) { - xml_t[ATTR] = 0; - xml_t[SEQ] = 0; - xml_t[ELEM] = 0; - - xml_t[ATTR_ID] = -1; - xml_t[UUID_ID] = -1; - } else if (g_strcmp0(element_name, "attribute") == 0) { - xml_t[SEQ] = 0; - xml_t[ELEM] = 0; - - int old_attr_id = xml_t[ATTR_ID]; - xml_t[ATTR_ID] = -1; - xml_t[UUID_ID] = -1; - - if (!verbose_arg && is_verbose_attr(old_attr_id)) return; - - g_print("\n"); - } else if (g_strcmp0(element_name, "sequence") == 0) { - xml_t[SEQ]--; - xml_t[ELEM] = 0; - - xml_t[UUID_ID] = -1; - } + if (g_strcmp0(element_name, "record") == 0) + { + xml_t[ATTR] = 0; + xml_t[SEQ] = 0; + xml_t[ELEM] = 0; + + xml_t[ATTR_ID] = -1; + xml_t[UUID_ID] = -1; + } + else if (g_strcmp0(element_name, "attribute") == 0) + { + xml_t[SEQ] = 0; + xml_t[ELEM] = 0; + + int old_attr_id = xml_t[ATTR_ID]; + xml_t[ATTR_ID] = -1; + xml_t[UUID_ID] = -1; + + if (!verbose_arg && is_verbose_attr(old_attr_id)) return; + + g_print("\n"); + } + else if (g_strcmp0(element_name, "sequence") == 0) + { + xml_t[SEQ]--; + xml_t[ELEM] = 0; + + xml_t[UUID_ID] = -1; + } } static void create_paired_device_done(gpointer data) { - g_assert(data != NULL); - GMainLoop *mainloop = data; - g_main_loop_quit(mainloop); + g_assert(data != NULL); + GMainLoop *mainloop = data; + g_main_loop_quit(mainloop); +} + +static void _bt_device_pair_callback(GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + g_assert(user_data != NULL); + GHashTable *dict = (GHashTable *) user_data; + Device *device = (Device *) g_hash_table_lookup(dict, "device"); + GError *error = NULL; + device_pair_finish(device, res, &error); + exit_if_error(error); + GMainLoop *mainloop = (GMainLoop *) g_hash_table_lookup(dict, "mainloop"); + g_main_loop_quit(mainloop); +} + + + +static GHashTable *_bt_device_sdp_browse(const gchar *device_path, const gchar *pattern) +{ + int pipefd[2]; + pipe(pipefd); + + // Fork the process into a child process to make an exec call + int pid = fork(); + + // Child (to call sdptool) + if (pid == 0) + { + // close reading end in the child + close(pipefd[0]); + + // send stdout to the pipe + dup2(pipefd[1], 1); + // send stderr to the pipe + dup2(pipefd[1], 2); + + // this descriptor is no longer needed + close(pipefd[1]); + + if(!g_file_test ("/bin/sdptool", G_FILE_TEST_EXISTS)) + { + write(2, "sdptool not found\n", sizeof("sdptool not found\n")); + exit(EXIT_FAILURE); + } + + if(pattern == NULL || strlen(pattern) == 0) + execl("/bin/sdptool", "/bin/sdptool", "browse", "--xml", device_path, (char *) 0); + else + execl("/bin/sdptool", "/bin/sdptool", "browse", "--xml", "--uuid", pattern, device_path, (char *) 0); + + } + if(pid == -1) + { + perror("forking process failed"); + exit(EXIT_FAILURE); + } + + close(pipefd[1]); // close the write end of the pipe in the parent + GInputStream *exec_output = (GInputStream *) g_unix_input_stream_new(pipefd[0], TRUE); + GDataInputStream *exec_data_input_stream = g_data_input_stream_new(exec_output); + GPtrArray *array = g_ptr_array_new(); + guint record_counter = 0; + guint n = 0; + GError *error = NULL; + GString *string_buffer = g_string_new(""); + while(TRUE) + { + n++; + gchar *line = g_data_input_stream_read_line_utf8(exec_data_input_stream, NULL, NULL, &error); + exit_if_error(error); + // If there is no content, then break out of the loop + if(!line) + break; + + if(n == 1) + { + if(g_regex_match_simple("Failed", line, 0, 0) || g_regex_match_simple("Error", line, 0, 0)) + { + g_print("%s\n", line); + exit(EXIT_FAILURE); + } + else if(g_regex_match_simple("not found", line, 0, 0)) + { + g_print("Failed to start SDP discovery. Please make sure you have bluez-utils installed on your system.\n"); + exit(EXIT_FAILURE); + } + + continue; + } + + if(g_regex_match_simple("<\\?xml(.*)\\?>", line, 0, 0)) + { + if(record_counter != 0) + { + g_ptr_array_add(array, g_string_free(string_buffer, FALSE)); + string_buffer = g_string_new(""); + } + record_counter++; + } + + g_string_append(string_buffer, line); + } + + g_ptr_array_add(array, g_string_free(string_buffer, FALSE)); + + GHashTable *sdp_hash_table = g_hash_table_new(g_int_hash, g_int_equal); + + int i = 0; + for(i = 0; i < array->len; i++) + { + GRegex *record_id_regex = g_regex_new("<attribute id=\"0x0000\">(\\s*<uint32 value=\"(.*)\" />\\s)</attribute>", G_REGEX_CASELESS | G_REGEX_MULTILINE | G_REGEX_NEWLINE_ANYCRLF, 0, &error); + exit_if_error(error); + GMatchInfo *match_info; + g_regex_match (record_id_regex, (gchar *) g_ptr_array_index(array, i), 0, &match_info); + if(g_match_info_matches(match_info)) + { + gchar *word = g_match_info_fetch(match_info, 2); + guint32 *key = g_new(guint32, 1); + *key = 0; + sscanf(word, "0x%x", key); + g_hash_table_insert(sdp_hash_table, key, (gchar *) g_ptr_array_index(array, i)); + g_free(word); + } + g_match_info_free(match_info); + g_regex_unref(record_id_regex); + } + + g_input_stream_close(exec_output, NULL, &error); + exit_if_error(error); + g_object_unref(exec_data_input_stream); + g_object_unref(exec_output); + + g_ptr_array_unref(array); + + return sdp_hash_table; } static GOptionEntry entries[] = { - {"adapter", 'a', 0, G_OPTION_ARG_STRING, &adapter_arg, "Adapter Name or MAC", "<name|mac>"}, - {"list", 'l', 0, G_OPTION_ARG_NONE, &list_arg, "List added devices", NULL}, - {"connect", 'c', 0, G_OPTION_ARG_STRING, &connect_arg, "Connect to the remote device", "<mac>"}, - {"disconnect", 'd', 0, G_OPTION_ARG_STRING, &disconnect_arg, "Disconnect the remote device", "<name|mac>"}, - {"remove", 'r', 0, G_OPTION_ARG_STRING, &remove_arg, "Remove device", "<name|mac>"}, - {"info", 'i', 0, G_OPTION_ARG_STRING, &info_arg, "Get info about device", "<name|mac>"}, - {"services", 's', 0, G_OPTION_ARG_NONE, &services_arg, "Discover device services", NULL}, - {"set", 0, 0, G_OPTION_ARG_NONE, &set_arg, "Set device property", NULL}, - {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_arg, "Verbosely display remote service records", NULL}, - {NULL} + {"adapter", 'a', 0, G_OPTION_ARG_STRING, &adapter_arg, "Adapter Name or MAC", "<name|mac>"}, + {"list", 'l', 0, G_OPTION_ARG_NONE, &list_arg, "List added devices", NULL}, + {"connect", 'c', 0, G_OPTION_ARG_STRING, &connect_arg, "Connect to the remote device", "<mac>"}, + {"disconnect", 'd', 0, G_OPTION_ARG_STRING, &disconnect_arg, "Disconnect the remote device", "<name|mac>"}, + {"remove", 'r', 0, G_OPTION_ARG_STRING, &remove_arg, "Remove device", "<name|mac>"}, + {"info", 'i', 0, G_OPTION_ARG_STRING, &info_arg, "Get info about device", "<name|mac>"}, + {"services", 's', 0, G_OPTION_ARG_NONE, &services_arg, "Discover device services", NULL}, + {"set", 0, 0, G_OPTION_ARG_NONE, &set_arg, "Set device property", NULL}, + {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_arg, "Verbosely display remote service records", NULL}, + {NULL} }; int main(int argc, char *argv[]) { - GError *error = NULL; - GOptionContext *context; - - /* Query current locale */ - setlocale(LC_CTYPE, ""); - - g_type_init(); - dbus_init(); - - context = g_option_context_new("- a bluetooth device manager"); - g_option_context_add_main_entries(context, entries, NULL); - g_option_context_set_summary(context, "Version "PACKAGE_VERSION); - g_option_context_set_description(context, - "Services Options:\n" - " -s, --services <name|mac> [<pattern>]\n" - " Where `pattern` is an optional specific UUID to search\n\n" - "Set Options:\n" - " --set <name|mac> <property> <value>\n" - " Where\n" - " `name|mac` is a device Name or MAC\n" - " `property` is one of:\n" - " Alias\n" - " Trusted\n" - " Blocked\n\n" - //"Report bugs to <"PACKAGE_BUGREPORT">." - "Project home page <"PACKAGE_URL">." - ); - - if (!g_option_context_parse(context, &argc, &argv, &error)) { - g_print("%s: %s\n", g_get_prgname(), error->message); - g_print("Try `%s --help` for more information.\n", g_get_prgname()); - exit(EXIT_FAILURE); - } else if (!list_arg && (!connect_arg || strlen(connect_arg) == 0) && (!disconnect_arg || strlen(disconnect_arg) == 0) && (!remove_arg || strlen(remove_arg) == 0) && (!info_arg || strlen(info_arg) == 0) && !services_arg && !set_arg) { - g_print("%s", g_option_context_get_help(context, FALSE, NULL)); - exit(EXIT_FAILURE); - } else if (services_arg && (argc != 2 || strlen(argv[1]) == 0) && (argc != 3 || strlen(argv[1]) == 0)) { - g_print("%s: Invalid arguments for --services\n", g_get_prgname()); - g_print("Try `%s --help` for more information.\n", g_get_prgname()); - exit(EXIT_FAILURE); - } else if (set_arg && (argc != 4 || strlen(argv[1]) == 0 || strlen(argv[2]) == 0 || strlen(argv[3]) == 0)) { - g_print("%s: Invalid arguments for --set\n", g_get_prgname()); - g_print("Try `%s --help` for more information.\n", g_get_prgname()); - exit(EXIT_FAILURE); - } - - g_option_context_free(context); - - if (!dbus_system_connect(&error)) { - g_printerr("Couldn't connect to DBus system bus: %s\n", error->message); - exit(EXIT_FAILURE); - } - - /* Check, that bluetooth daemon is running */ - if (!intf_supported(BLUEZ_DBUS_NAME, MANAGER_DBUS_PATH, MANAGER_DBUS_INTERFACE)) { - g_printerr("%s: bluez service is not found\n", g_get_prgname()); - g_printerr("Did you forget to run bluetoothd?\n"); - exit(EXIT_FAILURE); - } - - Adapter *adapter = find_adapter(adapter_arg, &error); - exit_if_error(error); - - if (list_arg) { - const GPtrArray *devices_list = adapter_get_devices(adapter); - g_assert(devices_list != NULL); - - if (devices_list->len == 0) { - g_print("No devices found\n"); - exit(EXIT_FAILURE); - } - - g_print("Added devices:\n"); - for (int i = 0; i < devices_list->len; i++) { - const gchar *device_path = g_ptr_array_index(devices_list, i); - Device *device = g_object_new(DEVICE_TYPE, "DBusObjectPath", device_path, NULL); - g_print("%s (%s)\n", device_get_alias(device), device_get_address(device)); - g_object_unref(device); - } - } else if (connect_arg) { - g_print("Connecting to: %s\n", connect_arg); - Agent *agent = g_object_new(AGENT_TYPE, NULL); - GMainLoop *mainloop = g_main_loop_new(NULL, FALSE); - - adapter_create_paired_device_begin(adapter, create_paired_device_done, mainloop, connect_arg, AGENT_DBUS_PATH, "DisplayYesNo"); - g_main_loop_run(mainloop); - gchar *created_device = adapter_create_paired_device_end(adapter, &error); - exit_if_error(error); - - g_print("Done\n"); - g_main_loop_unref(mainloop); - g_free(created_device); - g_object_unref(agent); - } else if (disconnect_arg) { - Device *device = find_device(adapter, disconnect_arg, &error); - exit_if_error(error); - - g_print("Disconnecting: %s\n", disconnect_arg); - device_disconnect(device, &error); - exit_if_error(error); - - g_print("Done\n"); - g_object_unref(device); - } else if (remove_arg) { - Device *device = find_device(adapter, remove_arg, &error); - exit_if_error(error); - - adapter_remove_device(adapter, device_get_dbus_object_path(device), &error); - exit_if_error(error); - - g_print("Done\n"); - g_object_unref(device); - } else if (info_arg) { - Device *device = find_device(adapter, info_arg, &error); - exit_if_error(error); - - g_print("[%s]\n", device_get_address(device)); - g_print(" Name: %s\n", device_get_name(device)); - g_print(" Alias: %s [rw]\n", device_get_alias(device)); - g_print(" Address: %s\n", device_get_address(device)); - g_print(" Icon: %s\n", device_get_icon(device)); - g_print(" Class: 0x%x\n", device_get_class(device)); - g_print(" Paired: %d\n", device_get_paired(device)); - g_print(" Trusted: %d [rw]\n", device_get_trusted(device)); - g_print(" Blocked: %d [rw]\n", device_get_blocked(device)); - g_print(" Connected: %d\n", device_get_connected(device)); - g_print(" UUIDs: ["); - const gchar **uuids = device_get_uuids(device); - for (int j = 0; uuids[j] != NULL; j++) { - if (j > 0) g_print(", "); - g_print("%s", uuid2name(uuids[j])); - } - g_print("]\n"); - - g_object_unref(device); - } else if (services_arg) { - services_device_arg = argv[1]; - if (argc == 3) { - services_pattern_arg = argv[2]; - } - - Device *device = find_device(adapter, services_device_arg, &error); - exit_if_error(error); - - g_print("Discovering services...\n"); - GHashTable *device_services = device_discover_services(device, name2uuid(services_pattern_arg), &error); - exit_if_error(error); - - GHashTableIter iter; - gpointer key, value; - - g_hash_table_iter_init(&iter, device_services); - int n = 0; - while (g_hash_table_iter_next(&iter, &key, &value)) { - n++; - if (n == 1) g_print("\n"); - g_print("[RECORD:%d]\n", (gint) key); - GMarkupParser xml_parser = {xml_start_element, xml_end_element, NULL, NULL, NULL}; - GMarkupParseContext *xml_parse_context = g_markup_parse_context_new(&xml_parser, 0, NULL, NULL); - g_markup_parse_context_parse(xml_parse_context, value, strlen(value), &error); - exit_if_error(error); - g_markup_parse_context_free(xml_parse_context); - g_print("\n"); - } - - g_print("Done\n"); - g_object_unref(device); - } else if (set_arg) { - set_device_arg = argv[1]; - set_property_arg = argv[2]; - set_value_arg = argv[3]; - - Device *device = find_device(adapter, set_device_arg, &error); - exit_if_error(error); - - GValue v = {0,}; - - if (g_strcmp0(set_property_arg, "Alias") == 0) { - g_value_init(&v, G_TYPE_STRING); - g_value_set_string(&v, set_value_arg); - } else if ( - g_strcmp0(set_property_arg, "Trusted") == 0 || - g_strcmp0(set_property_arg, "Blocked") == 0 - ) { - g_value_init(&v, G_TYPE_BOOLEAN); - - if (g_strcmp0(set_value_arg, "0") == 0 || g_ascii_strcasecmp(set_value_arg, "FALSE") == 0 || g_ascii_strcasecmp(set_value_arg, "OFF") == 0) { - g_value_set_boolean(&v, FALSE); - } else if (g_strcmp0(set_value_arg, "1") == 0 || g_ascii_strcasecmp(set_value_arg, "TRUE") == 0 || g_ascii_strcasecmp(set_value_arg, "ON") == 0) { - g_value_set_boolean(&v, TRUE); - } else { - g_print("%s: Invalid boolean value: %s\n", g_get_prgname(), set_value_arg); - g_print("Try `%s --help` for more information.\n", g_get_prgname()); - exit(EXIT_FAILURE); - } - } else { - g_print("%s: Invalid property: %s\n", g_get_prgname(), set_property_arg); - g_print("Try `%s --help` for more information.\n", g_get_prgname()); - exit(EXIT_FAILURE); - } - - GHashTable *props = device_get_properties(device, &error); - exit_if_error(error); - GValue *old_value = g_hash_table_lookup(props, set_property_arg); - g_assert(old_value != NULL); - if (G_VALUE_HOLDS_STRING(old_value)) { - g_print("%s: %s -> %s\n", set_property_arg, g_value_get_string(old_value), g_value_get_string(&v)); - } else if (G_VALUE_HOLDS_BOOLEAN(old_value)) { - g_print("%s: %d -> %d\n", set_property_arg, g_value_get_boolean(old_value), g_value_get_boolean(&v)); - } - g_hash_table_unref(props); - - device_set_property(device, set_property_arg, &v, &error); - exit_if_error(error); - - g_value_unset(&v); - g_object_unref(device); - } - - g_object_unref(adapter); - dbus_disconnect(); - - exit(EXIT_SUCCESS); + GError *error = NULL; + GOptionContext *context; + + /* Query current locale */ + setlocale(LC_CTYPE, ""); + + /* Deprecated */ + // g_type_init(); + dbus_init(); + + context = g_option_context_new("- a bluetooth device manager"); + g_option_context_add_main_entries(context, entries, NULL); + g_option_context_set_summary(context, "Version "PACKAGE_VERSION); + g_option_context_set_description(context, + "Services Options:\n" + " -s, --services <name|mac> [<pattern>]\n" + " Where `pattern` is an optional specific UUID to search\n\n" + "Set Options:\n" + " --set <name|mac> <property> <value>\n" + " Where\n" + " `name|mac` is a device Name or MAC\n" + " `property` is one of:\n" + " Alias\n" + " Trusted\n" + " Blocked\n\n" + "Report bugs to <"PACKAGE_BUGREPORT">." + "Project home page <"PACKAGE_URL">." + ); + + if (!g_option_context_parse(context, &argc, &argv, &error)) + { + g_print("%s: %s\n", g_get_prgname(), error->message); + g_print("Try `%s --help` for more information.\n", g_get_prgname()); + exit(EXIT_FAILURE); + } + else if (!list_arg && (!connect_arg || strlen(connect_arg) == 0) && (!disconnect_arg || strlen(disconnect_arg) == 0) && (!remove_arg || strlen(remove_arg) == 0) && (!info_arg || strlen(info_arg) == 0) && !services_arg && !set_arg) + { + g_print("%s", g_option_context_get_help(context, FALSE, NULL)); + exit(EXIT_FAILURE); + } + else if (services_arg && (argc != 2 || strlen(argv[1]) == 0) && (argc != 3 || strlen(argv[1]) == 0)) + { + g_print("%s: Invalid arguments for --services\n", g_get_prgname()); + g_print("Try `%s --help` for more information.\n", g_get_prgname()); + exit(EXIT_FAILURE); + } + else if (set_arg && (argc != 4 || strlen(argv[1]) == 0 || strlen(argv[2]) == 0 || strlen(argv[3]) == 0)) + { + g_print("%s: Invalid arguments for --set\n", g_get_prgname()); + g_print("Try `%s --help` for more information.\n", g_get_prgname()); + exit(EXIT_FAILURE); + } + + g_option_context_free(context); + + if (!dbus_system_connect(&error)) + { + g_printerr("Couldn't connect to DBus system bus: %s\n", error->message); + exit(EXIT_FAILURE); + } + + /* Check, that bluetooth daemon is running */ + if (!intf_supported(BLUEZ_DBUS_SERVICE_NAME, MANAGER_DBUS_PATH, MANAGER_DBUS_INTERFACE)) + { + g_printerr("%s: bluez service is not found\n", g_get_prgname()); + g_printerr("Did you forget to run bluetoothd?\n"); + exit(EXIT_FAILURE); + } + + Manager *manager = manager_new(); + Adapter *adapter = find_adapter(adapter_arg, &error); + exit_if_error(error); + + if (list_arg) + { + const gchar **devices_list = manager_get_devices(manager, adapter_get_dbus_object_path(adapter)); + + if (devices_list == NULL) + { + g_print("No devices found\n"); + exit(EXIT_FAILURE); + } + + g_print("Added devices:\n"); + const gchar **devices = NULL; + for (devices = devices_list; *devices != NULL; devices++) + { + const gchar *device_path = *devices; + Device *device = device_new(device_path); + g_print("%s (%s)\n", device_get_alias(device, &error), device_get_address(device, &error)); + exit_if_error(error); + g_object_unref(device); + } + } + else if (connect_arg) + { + g_print("Connecting to: %s\n", connect_arg); + GMainLoop *mainloop = g_main_loop_new(NULL, FALSE); + + AgentManager *agent_manager = agent_manager_new(); + + agent_need_unregister = TRUE; + + register_agent_callbacks(TRUE, NULL, mainloop, &error); + exit_if_error(error); + + agent_manager_register_agent(agent_manager, AGENT_PATH, "DisplayYesNo", &error); + exit_if_error(error); + + Device *device = find_device(adapter, connect_arg, &error); + exit_if_error(error); + if(!device) + { + g_printerr("Error: Device not found.\n"); + exit(EXIT_FAILURE); + } + + GHashTable *user_data_hash = g_hash_table_new(g_str_hash, g_str_equal); + g_hash_table_insert(user_data_hash, "device", device); + g_hash_table_insert(user_data_hash, "mainloop", mainloop); + + device_pair_async(device, (GAsyncReadyCallback) _bt_device_pair_callback, (gpointer) user_data_hash); + g_main_loop_run(mainloop); + + g_print("Done\n"); + g_hash_table_unref(user_data_hash); + g_main_loop_unref(mainloop); + g_object_unref(device); + unregister_agent_callbacks(&error); + } + else if (disconnect_arg) + { + Device *device = find_device(adapter, disconnect_arg, &error); + exit_if_error(error); + if(!device) + { + g_printerr("Error: Device not found.\n"); + exit(EXIT_FAILURE); + } + + g_print("Disconnecting: %s\n", disconnect_arg); + device_disconnect(device, &error); + exit_if_error(error); + + g_print("Done\n"); + g_object_unref(device); + } + else if (remove_arg) + { + Device *device = find_device(adapter, remove_arg, &error); + exit_if_error(error); + if(!device) + { + g_printerr("Error: Device not found.\n"); + exit(EXIT_FAILURE); + } + + adapter_remove_device(adapter, device_get_dbus_object_path(device), &error); + exit_if_error(error); + + g_print("Done\n"); + g_object_unref(device); + } + else if (info_arg) + { + Device *device = find_device(adapter, info_arg, &error); + exit_if_error(error); + if(!device) + { + g_printerr("Error: Device not found.\n"); + exit(EXIT_FAILURE); + } + + g_print("[%s]\n", device_get_address(device, &error)); + g_print(" Name: %s\n", device_get_name(device, &error)); + g_print(" Alias: %s [rw]\n", device_get_alias(device, &error)); + g_print(" Address: %s\n", device_get_address(device, &error)); + g_print(" Icon: %s\n", device_get_icon(device, &error)); + g_print(" Class: 0x%x\n", device_get_class(device, &error)); + g_print(" Paired: %d\n", device_get_paired(device, &error)); + g_print(" Trusted: %d [rw]\n", device_get_trusted(device, &error)); + g_print(" Blocked: %d [rw]\n", device_get_blocked(device, &error)); + g_print(" Connected: %d\n", device_get_connected(device, &error)); + g_print(" UUIDs: ["); + const gchar **uuids = device_get_uuids(device, &error); + for (int j = 0; uuids[j] != NULL; j++) + { + if (j > 0) g_print(", "); + g_print("%s", uuid2name(uuids[j])); + } + g_print("]\n"); + + g_object_unref(device); + } + else if (services_arg) + { + services_device_arg = argv[1]; + if (argc == 3) + { + services_pattern_arg = argv[2]; + } + + Device *device = find_device(adapter, services_device_arg, &error); + exit_if_error(error); + if(!device) + { + g_printerr("Error: Device not found.\n"); + exit(EXIT_FAILURE); + } + + const gchar *device_address = device_get_address(device, &error); + exit_if_error(error); + + g_print("Discovering services...\n"); + // Because BlueZ 5 removed the API call for discover services, we will use sdptool as an alternative + // GHashTable *device_services = device_discover_services(device, name2uuid(services_pattern_arg), &error); + GHashTable *device_services = _bt_device_sdp_browse(device_address, name2uuid(services_pattern_arg)); + exit_if_error(error); + + + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init(&iter, device_services); + int n = 0; + while (g_hash_table_iter_next(&iter, &key, &value)) + { + n++; + if (n == 1) g_print("\n"); + // g_print("[RECORD:%d]\n", (gint) key); + g_print("[RECORD:0x%x]\n", *(guint32 *) key); + GMarkupParser xml_parser = {xml_start_element, xml_end_element, NULL, NULL, NULL}; + GMarkupParseContext *xml_parse_context = g_markup_parse_context_new(&xml_parser, 0, NULL, NULL); + g_markup_parse_context_parse(xml_parse_context, value, strlen(value), &error); + exit_if_error(error); + g_markup_parse_context_free(xml_parse_context); + g_print("\n"); + } + + g_print("Done\n"); + g_hash_table_unref(device_services); + g_object_unref(device); + } + else if (set_arg) + { + set_device_arg = argv[1]; + set_property_arg = argv[2]; + set_value_arg = argv[3]; + + Device *device = find_device(adapter, set_device_arg, &error); + exit_if_error(error); + if(!device) + { + g_printerr("Error: Device not found.\n"); + exit(EXIT_FAILURE); + } + + GVariant *v = NULL; + + if (g_strcmp0(set_property_arg, "Alias") == 0) + { + v = g_variant_new_string(set_value_arg); + } + else if ( + g_strcmp0(set_property_arg, "Trusted") == 0 || + g_strcmp0(set_property_arg, "Blocked") == 0 + ) + { + if (g_strcmp0(set_value_arg, "0") == 0 || g_ascii_strcasecmp(set_value_arg, "FALSE") == 0 || g_ascii_strcasecmp(set_value_arg, "OFF") == 0) + { + v = g_variant_new_boolean(FALSE); + } + else if (g_strcmp0(set_value_arg, "1") == 0 || g_ascii_strcasecmp(set_value_arg, "TRUE") == 0 || g_ascii_strcasecmp(set_value_arg, "ON") == 0) + { + v = g_variant_new_boolean(TRUE); + } + else + { + g_print("%s: Invalid boolean value: %s\n", g_get_prgname(), set_value_arg); + g_print("Try `%s --help` for more information.\n", g_get_prgname()); + exit(EXIT_FAILURE); + } + } + else + { + g_print("%s: Invalid property: %s\n", g_get_prgname(), set_property_arg); + g_print("Try `%s --help` for more information.\n", g_get_prgname()); + exit(EXIT_FAILURE); + } + + GVariant *props = device_get_properties(device, &error); + exit_if_error(error); + + if(g_ascii_strcasecmp(set_property_arg, "Alias") == 0) + device_set_alias(device, g_variant_get_string(v, NULL), &error); + else if(g_ascii_strcasecmp(set_property_arg, "Blocked") == 0) + device_set_blocked(device, g_variant_get_boolean(v), &error); + else if(g_ascii_strcasecmp(set_property_arg, "Trusted") == 0) + device_set_trusted(device, g_variant_get_boolean(v), &error); + + exit_if_error(error); + + GVariant *old_value = g_variant_lookup_value(props, set_property_arg, NULL); + g_assert(old_value != NULL); + if (g_variant_type_equal(G_VARIANT_TYPE_STRING, g_variant_get_type(old_value))) + { + g_print("%s: %s -> %s\n", set_property_arg, g_variant_get_string(old_value, NULL), g_variant_get_string(v, NULL)); + } + else if (g_variant_type_equal(G_VARIANT_TYPE_BOOLEAN, g_variant_get_type(old_value))) + { + g_print("%s: %u -> %u\n", set_property_arg, g_variant_get_boolean(old_value), g_variant_get_boolean(v)); + } + g_variant_unref(props); + + // g_variant_unref(v); /* Floating references do not need to be unref */ + g_object_unref(device); + } + + g_object_unref(adapter); + dbus_disconnect(); + + exit(EXIT_SUCCESS); } - |