diff options
Diffstat (limited to 'serial/manager.c')
-rw-r--r-- | serial/manager.c | 107 |
1 files changed, 106 insertions, 1 deletions
diff --git a/serial/manager.c b/serial/manager.c index 795e5a45..e4a7c82b 100644 --- a/serial/manager.c +++ b/serial/manager.c @@ -100,6 +100,7 @@ static struct { static DBusConnection *connection = NULL; static GSList *connected_nodes = NULL; +static int rfcomm_ctl = -1; static void pending_connection_free(struct pending_connection *pc) { @@ -129,6 +130,11 @@ static void rfcomm_node_free(struct rfcomm_node *node) g_free(node); } +static int node_cmp_by_name(struct rfcomm_node *node, const char *name) +{ + return strcmp(node->name, name); +} + static uint16_t str2class(const char *pattern) { int i; @@ -149,6 +155,14 @@ static DBusHandlerResult err_connection_failed(DBusConnection *conn, SERIAL_ERROR_INTERFACE".ConnectionAttemptFailed", str)); } +static DBusHandlerResult err_does_not_exist(DBusConnection *conn, + DBusMessage *msg, const char *str) +{ + return send_message_and_unref(conn, + dbus_message_new_error(msg, + SERIAL_ERROR_INTERFACE ".DoesNotExist", str)); +} + static DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, const char *str) { @@ -165,6 +179,15 @@ static DBusHandlerResult err_invalid_args(DBusConnection *conn, SERIAL_ERROR_INTERFACE ".InvalidArguments", str)); } +static DBusHandlerResult err_not_authorized(DBusConnection *conn, + DBusMessage *msg) +{ + return send_message_and_unref(conn, + dbus_message_new_error(msg, + SERIAL_ERROR_INTERFACE ".NotAuthorized", + "Owner not allowed")); +} + static DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg) { @@ -174,6 +197,32 @@ static DBusHandlerResult err_not_supported(DBusConnection *conn, "The service is not supported by the remote device")); } +static int rfcomm_release(int16_t id) +{ + struct rfcomm_dev_req req; + + memset(&req, 0, sizeof(req)); + req.dev_id = 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) { + int err = errno; + error("Can't release device %d: %s (%d)", + id, strerror(err), err); + return -err; + } + + return 0; +} + static void send_signal(DBusConnection *conn, const char *sname, const char *node_name) { @@ -192,6 +241,8 @@ static void connect_service_exited(const char *name, struct rfcomm_node *node) debug("Connect requestor %s exited. Releasing %s node", name, node->name); + rfcomm_release(node->id); + send_signal(node->conn, "ServiceDisconnected", node->name); connected_nodes = g_slist_remove(connected_nodes, node); @@ -230,6 +281,8 @@ static int add_rfcomm_node(GIOChannel *io, int id, const char *name, node->io_id = g_io_add_watch(io, G_IO_ERR | G_IO_HUP, (GIOFunc) rfcomm_disconnect_cb, node); + connected_nodes = g_slist_append(connected_nodes, node); + return name_listener_add(node->conn, owner, (name_cb_t) connect_service_exited, node); } @@ -247,6 +300,7 @@ static gboolean rfcomm_connect_cb_continue(struct pending_connection *pc) /* Check if the caller is still present */ if (!dbus_bus_name_has_owner(pc->conn, owner, NULL)) { error("Connect requestor %s exited", owner); + rfcomm_release(pc->id); pending_connection_free(pc); return FALSE; } @@ -258,6 +312,7 @@ static gboolean rfcomm_connect_cb_continue(struct pending_connection *pc) error("Could not open %s: %s (%d)", node_name, strerror(err), err); if (++pc->ntries >= MAX_OPEN_TRIES) { + rfcomm_release(pc->id); err_connection_failed(pc->conn, pc->msg, strerror(err)); pending_connection_free(pc); return FALSE; @@ -704,7 +759,47 @@ static DBusHandlerResult connect_service(DBusConnection *conn, static DBusHandlerResult disconnect_service(DBusConnection *conn, DBusMessage *msg, void *data) { - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + DBusMessage *reply; + DBusError derr; + struct rfcomm_node *node; + const char *name; + GSList *l; + int err; + + dbus_error_init(&derr); + if (!dbus_message_get_args(msg, &derr, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)) { + err_invalid_args(conn, msg, derr.message); + dbus_error_free(&derr); + return DBUS_HANDLER_RESULT_HANDLED; + } + + l = g_slist_find_custom(connected_nodes, name, (GCompareFunc) node_cmp_by_name); + if (!l) + return err_does_not_exist(conn, msg, "Invalid node"); + + node = l->data; + + if (strcmp(node->owner, dbus_message_get_sender(msg)) != 0) + return err_not_authorized(conn, msg); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + err = rfcomm_release(node->id); + if (err < 0) { + dbus_message_unref(reply); + return err_failed(conn, msg, strerror(err)); + } + + name_listener_remove(node->conn, node->owner, + (name_cb_t) connect_service_exited, node); + connected_nodes = g_slist_remove(connected_nodes, node); + rfcomm_node_free(node); + + return send_message_and_unref(conn, reply); } static DBusHandlerResult cancel_connect_service(DBusConnection *conn, @@ -755,6 +850,13 @@ static const DBusObjectPathVTable manager_table = { int serial_init(DBusConnection *conn) { + + if (rfcomm_ctl < 0) { + rfcomm_ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM); + if (rfcomm_ctl < 0) + return -errno; + } + connection = dbus_connection_ref(conn); if (dbus_connection_register_object_path(connection, @@ -776,4 +878,7 @@ void serial_exit(void) dbus_connection_unref(connection); connection = NULL; + + if (rfcomm_ctl >= 0) + close(rfcomm_ctl); } |