/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2007 Marcel Holtmann * * * 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 * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dbus.h" #include "dbus-helper.h" #include "hcid.h" #include "dbus-common.h" #include "dbus-hci.h" #include "dbus-adapter.h" #include "dbus-error.h" #include "dbus-sdp.h" #include "dbus-rfcomm.h" /* Waiting for udev to create the device node */ #define MAX_OPEN_TRIES 5 #define OPEN_WAIT 300 /* ms */ static int rfcomm_ctl = -1; struct rfcomm_node { int16_t id; /* Device id */ char name[16]; /* Node filename */ DBusConnection *conn; /* for name listener handling */ /* The following members are only valid for connected nodes */ GIOChannel *io; /* IO Channel for the connection */ guint io_id; /* ID for IO channel */ char *owner; /* D-Bus name that created the node */ }; struct pending_connect { DBusConnection *conn; DBusMessage *msg; GIOChannel *io; char *svc; int canceled; struct sockaddr_rc laddr; struct sockaddr_rc raddr; /* Used only when we wait for udev to create the device node */ struct rfcomm_node *node; int ntries; }; static GSList *pending_connects = NULL; static GSList *connected_nodes = NULL; static GSList *bound_nodes = NULL; static char *rfcomm_node_name_from_id(int16_t id, char *dev, size_t len) { snprintf(dev, len, "/dev/rfcomm%d", id); return dev; } static void rfcomm_node_free(struct rfcomm_node *node) { g_free(node->owner); if (node->io) { g_source_remove(node->io_id); g_io_channel_unref(node->io); } if (node->conn) dbus_connection_unref(node->conn); g_free(node); } static struct rfcomm_node *find_node_by_name(GSList *nodes, const char *name) { GSList *l; for (l = nodes; l != NULL; l = l->next) { struct rfcomm_node *node = l->data; if (!strcmp(node->name, name)) return node; } return NULL; } static struct pending_connect *find_pending_connect_by_channel(const char *bda, uint8_t ch) { GSList *l; bdaddr_t dba; str2ba(bda, &dba); for (l = pending_connects; l != NULL; l = l->next) { struct pending_connect *pending = l->data; if (!bacmp(&dba, &pending->raddr.rc_bdaddr) && pending->raddr.rc_channel == ch) return pending; } return NULL; } static struct pending_connect *find_pending_connect_by_service(const char *bda, const char *svc) { GSList *l; bdaddr_t dba; str2ba(bda, &dba); for (l = pending_connects; l != NULL; l = l->next) { struct pending_connect *pending = l->data; if (!bacmp(&dba, &pending->raddr.rc_bdaddr) && !strcmp(pending->svc, svc)) return pending; } return NULL; } static void pending_connect_free(struct pending_connect *c) { g_free(c->svc); if (c->io) g_io_channel_unref(c->io); if (c->msg) dbus_message_unref(c->msg); if (c->conn) dbus_connection_unref(c->conn); g_free(c); } static int rfcomm_release(struct rfcomm_node *node, int *err) { struct rfcomm_dev_req req; debug("rfcomm_release(%s)", node->name); memset(&req, 0, sizeof(req)); req.dev_id = node->id; #if 0 /* * We are hitting a kernel bug inside RFCOMM code when * RFCOMM_HANGUP_NOW bit is set on request's flags passed to * ioctl(RFCOMMRELEASEDEV)! */ req.flags = (1 << RFCOMM_HANGUP_NOW); #endif if (ioctl(rfcomm_ctl, RFCOMMRELEASEDEV, &req) < 0) { if (err) *err = errno; error("Can't release device %d: %s (%d)", node->id, strerror(errno), errno); return -1; } return 0; } static void rfcomm_connect_req_exit(const char *name, void *data) { struct rfcomm_node *node = data; debug("Connect requestor %s exited. Releasing %s node", name, node->name); rfcomm_release(node, NULL); connected_nodes = g_slist_remove(connected_nodes, node); rfcomm_node_free(node); } static gboolean rfcomm_disconnect_cb(GIOChannel *io, GIOCondition cond, struct rfcomm_node *node) { debug("RFCOMM node %s was disconnected", node->name); name_listener_remove(node->conn, node->owner, rfcomm_connect_req_exit, node); connected_nodes = g_slist_remove(connected_nodes, node); rfcomm_node_free(node); return FALSE; } static void rfcomm_connect_cb_devnode_opened(int fd, struct pending_connect *c, struct rfcomm_node *node) { DBusMessage *reply = NULL; char *ptr; reply = dbus_message_new_method_return(c->msg); if (!reply) { error_failed(c->conn, c->msg, ENOMEM); goto failed; } ptr = node->name; if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &ptr, DBUS_TYPE_INVALID)) { error_failed(c->conn, c->msg, ENOMEM); goto failed; } node->owner = g_strdup(dbus_message_get_sender(c->msg)); /* Check if the caller is still present */ if (!dbus_bus_name_has_owner(c->conn, node->owner, NULL)) { error("RFCOMM.Connect requestor %s exited", node->owner); goto failed; } node->io = g_io_channel_unix_new(fd); g_io_channel_set_close_on_unref(node->io, TRUE); node->io_id = g_io_add_watch(node->io, G_IO_ERR | G_IO_HUP, (GIOFunc) rfcomm_disconnect_cb, node); send_message_and_unref(c->conn, reply); connected_nodes = g_slist_append(connected_nodes, node); node->conn = dbus_connection_ref(c->conn); name_listener_add(node->conn, node->owner, rfcomm_connect_req_exit, node); goto done; failed: close(fd); rfcomm_release(node, NULL); rfcomm_node_free(node); if (reply) dbus_message_unref(reply); done: pending_connects = g_slist_remove(pending_connects, c); pending_connect_free(c); } static gboolean rfcomm_connect_cb_continue(void *data) { struct pending_connect *c = data; struct rfcomm_node *node = c->node; int fd; if (c->canceled) { error_connect_canceled(c->conn, c->msg); goto failed; } fd = open(node->name, O_RDONLY | O_NOCTTY); if (fd < 0) { if (++c->ntries >= MAX_OPEN_TRIES) { int err = errno; error("Could not open %s: %s (%d)", node->name, strerror(err), err); error_connection_attempt_failed(c->conn, c->msg, err); goto failed; } return TRUE; } rfcomm_connect_cb_devnode_opened(fd, c, node); return FALSE; failed: rfcomm_release(node, NULL); rfcomm_node_free(node); pending_connects = g_slist_remove(pending_connects, c); pending_connect_free(c); return FALSE; } static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct pending_connect *c) { struct rfcomm_node *node = NULL; struct rfcomm_dev_req req; int sk, ret, err, fd = -1; socklen_t len; if (c->canceled) { error_connect_canceled(c->conn, c->msg); goto failed; } sk = g_io_channel_unix_get_fd(chan); len = sizeof(ret); if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { err = errno; error("getsockopt(SO_ERROR): %s (%d)", strerror(err), err); error_failed(c->conn, c->msg, err); goto failed; } if (ret != 0) { error("connect(): %s (%d)", strerror(ret), ret); error_connection_attempt_failed(c->conn, c->msg, ret); goto failed; } debug("rfcomm_connect_cb: connected"); len = sizeof(c->laddr); if (getsockname(sk, (struct sockaddr *) &c->laddr, &len) < 0) { err = errno; error_failed(c->conn, c->msg, err); error("getsockname: %s (%d)", strerror(err), err); goto failed; } node = g_new0(struct rfcomm_node, 1); /* Create the rfcomm device node */ memset(&req, 0, sizeof(req)); req.dev_id = -1; req.flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP); bacpy(&req.src, &c->laddr.rc_bdaddr); bacpy(&req.dst, &c->raddr.rc_bdaddr); req.channel = c->raddr.rc_channel; node->id = ioctl(sk, RFCOMMCREATEDEV, &req); if (node->id < 0) { err = errno; error("ioctl(RFCOMMCREATEDEV): %s (%d)", strerror(errno), err); error_failed(c->conn, c->msg, err); goto failed; } rfcomm_node_name_from_id(node->id, node->name, sizeof(node->name)); fd = open(node->name, O_RDONLY | O_NOCTTY); if (fd < 0) { c->node = node; c->ntries = 0; g_timeout_add(OPEN_WAIT, rfcomm_connect_cb_continue, c); return FALSE; } rfcomm_connect_cb_devnode_opened(fd, c, node); return FALSE; failed: if (node) rfcomm_node_free(node); pending_connects = g_slist_remove(pending_connects, c); pending_connect_free(c); return FALSE; } static int rfcomm_connect(DBusConnection *conn, DBusMessage *msg, bdaddr_t *src, const char *bda, const char *svc, uint8_t ch, int *err) { int sk = -1; struct pending_connect *c = NULL; c = g_new0(struct pending_connect, 1); if (svc) c->svc = g_strdup(svc); c->laddr.rc_family = AF_BLUETOOTH; bacpy(&c->laddr.rc_bdaddr, src); c->laddr.rc_channel = 0; c->raddr.rc_family = AF_BLUETOOTH; str2ba(bda, &c->raddr.rc_bdaddr); c->raddr.rc_channel = ch; sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); if (sk < 0) { if (err) *err = errno; goto failed; } if (bind(sk, (struct sockaddr *) &c->laddr, sizeof(c->laddr)) < 0) { if (err) *err = errno; goto failed; } if (set_nonblocking(sk) < 0) { *err = errno; goto failed; } /* So we can reply to the message later */ c->msg = dbus_message_ref(msg); c->conn = dbus_connection_ref(conn); c->io = g_io_channel_unix_new(sk); g_io_channel_set_close_on_unref(c->io, TRUE); if (connect(sk, (struct sockaddr *) &c->raddr, sizeof(c->raddr)) < 0) { /* BlueZ returns EAGAIN eventhough it should return EINPROGRESS */ if (!(errno == EAGAIN || errno == EINPROGRESS)) { if (err) *err = errno; error("connect() failed: %s (%d)", strerror(errno), errno); goto failed; } debug("Connect in progress"); g_io_add_watch(c->io, G_IO_OUT, (GIOFunc) rfcomm_connect_cb, c); pending_connects = g_slist_append(pending_connects, c); } else { debug("Connect succeeded with first try"); (void) rfcomm_connect_cb(c->io, G_IO_OUT, c); } return 0; failed: if (c) pending_connect_free(c); if (sk >= 0) close(sk); return -1; } static void rfcomm_bind_req_exit(const char *name, void *data) { struct rfcomm_node *node = data; debug("Bind requestor %s exited. Releasing %s node", name, node->name); rfcomm_release(node, NULL); bound_nodes = g_slist_remove(bound_nodes, node); rfcomm_node_free(node); } static struct rfcomm_node *rfcomm_bind(bdaddr_t *src, const char *bda, uint8_t ch, DBusConnection *conn, const char *owner, int *err) { struct rfcomm_dev_req req; struct rfcomm_node *node; debug("rfcomm_bind(%s, %d)", bda, ch); memset(&req, 0, sizeof(req)); req.dev_id = -1; req.flags = 0; bacpy(&req.src, src); str2ba(bda, &req.dst); req.channel = ch; node = g_new0(struct rfcomm_node, 1); node->owner = g_strdup(owner); node->id = ioctl(rfcomm_ctl, RFCOMMCREATEDEV, &req); if (node->id < 0) { if (err) *err = errno; error("RFCOMMCREATEDEV failed: %s (%d)", strerror(errno), errno); rfcomm_node_free(node); return NULL; } rfcomm_node_name_from_id(node->id, node->name, sizeof(node->name)); bound_nodes = g_slist_append(bound_nodes, node); node->conn = dbus_connection_ref(conn); name_listener_add(node->conn, node->owner, rfcomm_bind_req_exit, node); return node; } typedef struct { DBusConnection *conn; DBusMessage *msg; char *dst; char *svc; struct adapter *adapter; } rfcomm_continue_data_t; static rfcomm_continue_data_t *rfcomm_continue_data_new(DBusConnection *conn, DBusMessage *msg, const char *dst, const char *svc, struct adapter *adapter) { rfcomm_continue_data_t *new; new = g_new(rfcomm_continue_data_t, 1); new->dst = g_strdup(dst); if (!new->dst) { g_free(new); return NULL; } new->svc = g_strdup(svc); new->conn = dbus_connection_ref(conn); new->msg = dbus_message_ref(msg); new->adapter = adapter; return new; } static void rfcomm_continue_data_free(rfcomm_continue_data_t *d) { dbus_connection_unref(d->conn); dbus_message_unref(d->msg); g_free(d->svc); g_free(d->dst); g_free(d); } static void rfcomm_conn_req_continue(sdp_record_t *rec, void *data, int err) { rfcomm_continue_data_t *cdata = data; int ch = -1, conn_err; sdp_list_t *protos; bdaddr_t bdaddr; if (err || !rec) { error_record_does_not_exist(cdata->conn, cdata->msg); goto failed; } if (!sdp_get_access_protos(rec, &protos)) { ch = sdp_get_proto_port(protos, RFCOMM_UUID); sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(protos, NULL); } if (ch == -1) { error_record_does_not_exist(cdata->conn, cdata->msg); goto failed; } if (find_pending_connect_by_channel(cdata->dst, ch)) { error_connect_in_progress(cdata->conn, cdata->msg); goto failed; } hci_devba(cdata->adapter->dev_id, &bdaddr); if (rfcomm_connect(cdata->conn, cdata->msg, &bdaddr, cdata->dst, cdata->svc, ch, &conn_err) < 0) error_failed(cdata->conn, cdata->msg, conn_err); failed: rfcomm_continue_data_free(cdata); } static DBusHandlerResult rfcomm_connect_req(DBusConnection *conn, DBusMessage *msg, void *data) { struct adapter *adapter = data; rfcomm_continue_data_t *cdata; uint32_t handle; uuid_t uuid; const char *string; const char *dst; int err; if (!adapter->up) return error_not_ready(conn, msg); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &dst, DBUS_TYPE_STRING, &string, DBUS_TYPE_INVALID)) return error_invalid_arguments(conn, msg); cdata = rfcomm_continue_data_new(conn, msg, dst, string, adapter); if (!cdata) return DBUS_HANDLER_RESULT_NEED_MEMORY; if (str2uuid(&uuid, string) == 0) err = get_record_with_uuid(conn, msg, adapter->dev_id, dst, &uuid, rfcomm_conn_req_continue, cdata); else if ((handle = strtol(string, NULL, 0))) err = get_record_with_handle(conn, msg, adapter->dev_id, dst, handle, rfcomm_conn_req_continue, cdata); else { rfcomm_continue_data_free(cdata); return error_invalid_arguments(conn, msg); } if (!err) return DBUS_HANDLER_RESULT_HANDLED; rfcomm_continue_data_free(cdata); if (err == -ENOMEM) return DBUS_HANDLER_RESULT_NEED_MEMORY; return error_failed(conn, msg, err); } static DBusHandlerResult rfcomm_cancel_connect_req(DBusConnection *conn, DBusMessage *msg, void *data) { struct pending_connect *pending; DBusMessage *reply; const char *string; const char *dst; struct adapter *adapter = data; if (!adapter->up) return error_not_ready(conn, msg); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &dst, DBUS_TYPE_STRING, &string, DBUS_TYPE_INVALID)) return error_invalid_arguments(conn, msg); pending = find_pending_connect_by_service(dst, string); if (!pending) return error_connect_not_in_progress(conn, msg); reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; pending->canceled = 1; return send_message_and_unref(conn, reply); } static DBusHandlerResult rfcomm_connect_by_ch_req(DBusConnection *conn, DBusMessage *msg, void *data) { bdaddr_t bdaddr; const char *dst; uint8_t ch; int err; struct adapter *adapter = data; if (!adapter->up) return error_not_ready(conn, msg); hci_devba(adapter->dev_id, &bdaddr); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &dst, DBUS_TYPE_BYTE, &ch, DBUS_TYPE_INVALID)) return error_invalid_arguments(conn, msg); if (find_pending_connect_by_channel(dst, ch)) return error_connect_in_progress(conn, msg); if (rfcomm_connect(conn, msg, &bdaddr, dst, NULL, ch, &err) < 0) return error_failed(conn, msg, err); return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult rfcomm_cancel_connect_by_ch_req(DBusConnection *conn, DBusMessage *msg, void *data) { const char *dst; uint8_t ch; DBusMessage *reply; struct pending_connect *pending; struct adapter *adapter = data; if (!adapter->up) return error_not_ready(conn, msg); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &dst, DBUS_TYPE_BYTE, &ch, DBUS_TYPE_INVALID)) return error_invalid_arguments(conn, msg); pending = find_pending_connect_by_channel(dst, ch); if (!pending) return error_connect_not_in_progress(conn, msg); reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; pending->canceled = 1; return send_message_and_unref(conn, reply); } static DBusHandlerResult rfcomm_disconnect_req(DBusConnection *conn, DBusMessage *msg, void *data) { struct rfcomm_node *node; DBusMessage *reply; const char *name; int err; struct adapter *adapter = data; if (!adapter->up) return error_not_ready(conn, msg); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) return error_invalid_arguments(conn, msg); node = find_node_by_name(connected_nodes, name); if (!node) return error_not_connected(conn, msg); if (strcmp(node->owner, dbus_message_get_sender(msg))) return error_not_authorized(conn, msg); reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; if (rfcomm_release(node, &err) < 0) { dbus_message_unref(reply); return error_failed(conn, msg, err); } name_listener_remove(node->conn, node->owner, rfcomm_connect_req_exit, node); connected_nodes = g_slist_remove(connected_nodes, node); rfcomm_node_free(node); return send_message_and_unref(conn, reply); } static void rfcomm_bind_req_continue(sdp_record_t *rec, void *data, int err) { rfcomm_continue_data_t *cdata = data; struct rfcomm_node *node = NULL; DBusMessage *reply = NULL; int ch = -1, bind_err; sdp_list_t *protos; const char *name; bdaddr_t bdaddr; if (err || !rec) { error_record_does_not_exist(cdata->conn, cdata->msg); goto failed; } if (!sdp_get_access_protos(rec, &protos)) { ch = sdp_get_proto_port(protos, RFCOMM_UUID); sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(protos, NULL); } if (ch == -1) { error_record_does_not_exist(cdata->conn, cdata->msg); goto failed; } hci_devba(cdata->adapter->dev_id, &bdaddr); node = rfcomm_bind(&bdaddr, cdata->dst, ch, cdata->conn, dbus_message_get_sender(cdata->msg), &bind_err); if (!node) { error_failed(cdata->conn, cdata->msg, bind_err); goto failed; } reply = dbus_message_new_method_return(cdata->msg); if (!reply) { error_failed(cdata->conn, cdata->msg, ENOMEM); goto failed; } name = node->name; if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) { error_failed(cdata->conn, cdata->msg, ENOMEM); goto failed; } send_message_and_unref(cdata->conn, reply); rfcomm_continue_data_free(cdata); return; failed: if (reply) dbus_message_unref(reply); if (node) { bound_nodes = g_slist_remove(bound_nodes, node); rfcomm_release(node, NULL); rfcomm_node_free(node); } rfcomm_continue_data_free(cdata); } static DBusHandlerResult rfcomm_bind_req(DBusConnection *conn, DBusMessage *msg, void *data) { struct adapter *adapter = data; rfcomm_continue_data_t *cdata; uint32_t handle; uuid_t uuid; const char *string; const char *dst; int err; if (!adapter->up) return error_not_ready(conn, msg); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &dst, DBUS_TYPE_STRING, &string, DBUS_TYPE_INVALID)) return error_invalid_arguments(conn, msg); cdata = rfcomm_continue_data_new(conn, msg, dst, string, adapter); if (!cdata) return DBUS_HANDLER_RESULT_NEED_MEMORY; if (str2uuid(&uuid, string) == 0) err = get_record_with_uuid(conn, msg, adapter->dev_id, dst, &uuid, rfcomm_bind_req_continue, cdata); else if ((handle = strtol(string, NULL, 0))) err = get_record_with_handle(conn, msg, adapter->dev_id, dst, handle, rfcomm_bind_req_continue, cdata); else { rfcomm_continue_data_free(cdata); return error_invalid_arguments(conn, msg); } if (!err) return DBUS_HANDLER_RESULT_HANDLED; rfcomm_continue_data_free(cdata); if (err == -ENOMEM) return DBUS_HANDLER_RESULT_NEED_MEMORY; return error_failed(conn, msg, err); } static DBusHandlerResult rfcomm_bind_by_ch_req(DBusConnection *conn, DBusMessage *msg, void *data) { bdaddr_t bdaddr; DBusMessage *reply = NULL; uint8_t ch; int err; const char *dst, *name; struct adapter *adapter = data; struct rfcomm_node *node = NULL; if (!adapter->up) return error_not_ready(conn, msg); hci_devba(adapter->dev_id, &bdaddr); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &dst, DBUS_TYPE_BYTE, &ch, DBUS_TYPE_INVALID)) return error_invalid_arguments(conn, msg); node = rfcomm_bind(&bdaddr, dst, ch, conn, dbus_message_get_sender(msg), &err); if (!node) return error_failed(conn, msg, err); reply = dbus_message_new_method_return(msg); if (!reply) goto need_memory; name = node->name; if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) goto need_memory; return send_message_and_unref(conn, reply); need_memory: if (reply) dbus_message_unref(reply); if (node) { bound_nodes = g_slist_remove(bound_nodes, node); rfcomm_release(node, NULL); rfcomm_node_free(node); } return DBUS_HANDLER_RESULT_NEED_MEMORY; } static DBusHandlerResult rfcomm_release_req(DBusConnection *conn, DBusMessage *msg, void *data) { DBusMessage *reply; const char *name; struct rfcomm_node *node; int err; struct adapter *adapter = data; if (!adapter->up) return error_not_ready(conn, msg); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) return error_invalid_arguments(conn, msg); node = find_node_by_name(bound_nodes, name); if (!node) return error_binding_does_not_exist(conn, msg); if (strcmp(node->owner, dbus_message_get_sender(msg))) return error_not_authorized(conn, msg); reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; if (rfcomm_release(node, &err) < 0) { dbus_message_unref(reply); return error_failed(conn, msg, err); } name_listener_remove(node->conn, node->owner, rfcomm_bind_req_exit, node); bound_nodes = g_slist_remove(bound_nodes, node); rfcomm_node_free(node); return send_message_and_unref(conn, reply); } static DBusHandlerResult rfcomm_list_bindings_req(DBusConnection *conn, DBusMessage *msg, void *data) { bdaddr_t bdaddr; DBusMessage *reply; DBusMessageIter iter, sub; struct adapter *adapter = data; GSList *l; if (!adapter->up) return error_not_ready(conn, msg); hci_devba(adapter->dev_id, &bdaddr); reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; dbus_message_iter_init_append(reply, &iter); if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) { dbus_message_unref(reply); return DBUS_HANDLER_RESULT_NEED_MEMORY; } for (l = bound_nodes; l != NULL; l = l->next) { struct rfcomm_node *node = l->data; struct rfcomm_dev_info di = { id: node->id }; char *name = node->name; if (ioctl(rfcomm_ctl, RFCOMMGETDEVINFO, &di) < 0) { error("RFCOMMGETDEVINFO(%d): %s (%d)", node->id, strerror(errno), errno); continue; } /* Ignore nodes not specific to this adapter */ if (bacmp(&di.src, &bdaddr)) continue; dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name); } if (!dbus_message_iter_close_container(&iter, &sub)) { dbus_message_unref(reply); return DBUS_HANDLER_RESULT_NEED_MEMORY; } return send_message_and_unref(conn, reply); } static DBusMethodVTable rfcomm_methods[] = { { "Connect", rfcomm_connect_req, "ss", "s" }, { "CancelConnect", rfcomm_cancel_connect_req, "ss", "" }, { "ConnectByChannel", rfcomm_connect_by_ch_req, "sy", "s" }, { "CancelConnectByChannel", rfcomm_cancel_connect_by_ch_req, "sy", "" }, { "Disconnect", rfcomm_disconnect_req, "s", "" }, { "Bind", rfcomm_bind_req, "ss", "s" }, { "BindByChannel", rfcomm_bind_by_ch_req, "sy", "s" }, { "Release", rfcomm_release_req, "s", "" }, { "ListBindings", rfcomm_list_bindings_req, "", "as" }, { NULL, NULL, NULL, NULL } }; dbus_bool_t rfcomm_init(DBusConnection *conn, const char *path) { if (rfcomm_ctl < 0) { rfcomm_ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM); if (rfcomm_ctl < 0) return FALSE; } return dbus_connection_register_interface(conn, path, RFCOMM_INTERFACE, rfcomm_methods, NULL, NULL); }