| /* |
| * |
| * BlueZ - Bluetooth protocol stack for Linux |
| * |
| * Copyright (C) 2000-2001 Qualcomm Incorporated |
| * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com> |
| * Copyright (C) 2002-2004 Marcel Holtmann <marcel@holtmann.org> |
| * |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation; |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. |
| * IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY |
| * CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| * |
| * ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, |
| * COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS |
| * SOFTWARE IS DISCLAIMED. |
| * |
| * |
| * $Id$ |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <stdio.h> |
| #include <sys/socket.h> |
| #include <sys/syslog.h> |
| |
| #include <bluetooth/bluetooth.h> |
| #include <bluetooth/hci.h> |
| #include <bluetooth/hci_lib.h> |
| |
| #include <dbus/dbus.h> |
| |
| #include "glib-ectomy.h" |
| |
| #include "hcid.h" |
| |
| static DBusConnection *connection; |
| |
| #define TIMEOUT (30 * 1000) /* 30 seconds */ |
| |
| #define SERVICE_NAME "org.bluez.PinAgent" |
| #define INTERFACE_NAME SERVICE_NAME |
| #define REQUEST_NAME "PinRequest" |
| #define PATH_NAME "/org/bluez/PinAgent" |
| |
| #define WRONG_ARGS_ERROR "org.bluez.Error.WrongArgs" |
| |
| struct pin_request { |
| int dev; |
| bdaddr_t bda; |
| }; |
| |
| static void reply_handler_function(DBusPendingCall *call, void *user_data) |
| { |
| struct pin_request *req = (struct pin_request *) user_data; |
| pin_code_reply_cp pr; |
| DBusMessage *message; |
| DBusMessageIter iter; |
| int type; |
| size_t len; |
| char *pin; |
| |
| message = dbus_pending_call_get_reply(call); |
| |
| if (dbus_message_is_error(message, WRONG_ARGS_ERROR)) |
| goto error; |
| |
| dbus_message_iter_init(message, &iter); |
| |
| type = dbus_message_iter_get_arg_type(&iter); |
| if (type != DBUS_TYPE_STRING) |
| goto error; |
| |
| pin = dbus_message_iter_get_string(&iter); |
| len = strlen(pin); |
| |
| memset(&pr, 0, sizeof(pr)); |
| bacpy(&pr.bdaddr, &req->bda); |
| memcpy(pr.pin_code, pin, len); |
| pr.pin_len = len; |
| hci_send_cmd(req->dev, OGF_LINK_CTL, OCF_PIN_CODE_REPLY, |
| PIN_CODE_REPLY_CP_SIZE, &pr); |
| |
| return; |
| |
| error: |
| hci_send_cmd(req->dev, OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY, |
| 6, &req->bda); |
| } |
| |
| |
| static void free_pin_req(void *req) |
| { |
| free(req); |
| } |
| |
| void hcid_dbus_request_pin(int dev, struct hci_conn_info *ci) |
| { |
| DBusMessage *message; |
| DBusMessageIter iter; |
| DBusPendingCall *pending = NULL; |
| struct pin_request *req; |
| |
| message = dbus_message_new_method_call(SERVICE_NAME, PATH_NAME, |
| INTERFACE_NAME, REQUEST_NAME); |
| if (message == NULL) { |
| syslog(LOG_ERR, "Couldn't allocate D-BUS message"); |
| goto failed; |
| } |
| |
| req = malloc(sizeof(*req)); |
| req->dev = dev; |
| bacpy(&req->bda, &ci->bdaddr); |
| |
| dbus_message_append_iter_init(message, &iter); |
| |
| dbus_message_iter_append_boolean(&iter, ci->out); |
| dbus_message_iter_append_byte_array(&iter, |
| (unsigned char *) &ci->bdaddr, sizeof(ci->bdaddr)); |
| |
| if (dbus_connection_send_with_reply(connection, message, |
| &pending, TIMEOUT) == FALSE) { |
| syslog(LOG_ERR, "D-BUS send failed"); |
| goto failed; |
| } |
| |
| dbus_pending_call_set_notify(pending, reply_handler_function, |
| req, free_pin_req); |
| |
| dbus_connection_flush (connection); |
| |
| dbus_message_unref (message); |
| |
| return; |
| |
| failed: |
| dbus_message_unref (message); |
| hci_send_cmd(dev, OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY, |
| 6, &ci->bdaddr); |
| } |
| |
| gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data) |
| { |
| DBusWatch *watch = (DBusWatch *) data; |
| int flags = 0; |
| |
| if (cond & G_IO_IN) flags |= DBUS_WATCH_READABLE; |
| if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE; |
| if (cond & G_IO_HUP) flags |= DBUS_WATCH_HANGUP; |
| if (cond & G_IO_ERR) flags |= DBUS_WATCH_ERROR; |
| |
| dbus_watch_handle(watch, flags); |
| |
| dbus_connection_ref(connection); |
| |
| /* Dispatch messages */ |
| while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS); |
| |
| dbus_connection_unref(connection); |
| |
| return TRUE; |
| } |
| |
| dbus_bool_t add_watch(DBusWatch *watch, void *data) |
| { |
| GIOCondition cond = G_IO_HUP | G_IO_ERR; |
| GIOChannel *io; |
| guint id; |
| int fd, flags; |
| |
| if (!dbus_watch_get_enabled(watch)) |
| return TRUE; |
| |
| fd = dbus_watch_get_fd(watch); |
| io = g_io_channel_unix_new(fd); |
| flags = dbus_watch_get_flags(watch); |
| |
| if (flags & DBUS_WATCH_READABLE) cond |= G_IO_IN; |
| if (flags & DBUS_WATCH_WRITABLE) cond |= G_IO_OUT; |
| |
| id = g_io_add_watch(io, cond, watch_func, watch); |
| |
| dbus_watch_set_data(watch, (void *) id, NULL); |
| |
| return TRUE; |
| } |
| |
| static void remove_watch(DBusWatch *watch, void *data) |
| { |
| guint id = (guint) dbus_watch_get_data(watch); |
| |
| dbus_watch_set_data(watch, NULL, NULL); |
| |
| if (id) |
| g_io_remove_watch(id); |
| } |
| |
| static void watch_toggled(DBusWatch *watch, void *data) |
| { |
| /* Because we just exit on OOM, enable/disable is |
| * no different from add/remove |
| */ |
| if (dbus_watch_get_enabled(watch)) |
| add_watch(watch, data); |
| else |
| remove_watch(watch, data); |
| } |
| |
| gboolean hcid_dbus_init(void) |
| { |
| DBusError error; |
| |
| dbus_error_init(&error); |
| |
| connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error); |
| if (connection == NULL) { |
| fprintf(stderr, "Failed to open connection to system message bus: %s\n", |
| error.message); |
| dbus_error_free(&error); |
| return FALSE; |
| } |
| |
| dbus_connection_set_watch_functions(connection, |
| add_watch, remove_watch, watch_toggled, NULL, NULL); |
| |
| return TRUE; |
| } |