static void reserve_wrapper_free(pa_reserve_wrapper *r) { pa_assert(r); #ifdef HAVE_DBUS if (r->device) rd_release(r->device); if (r->connection) pa_dbus_connection_unref(r->connection); #endif pa_hook_done(&r->hook); if (r->shared_name) { pa_assert_se(pa_shared_remove(r->core, r->shared_name) >= 0); pa_xfree(r->shared_name); } pa_xfree(r); }
int rd_acquire( rd_device **_d, DBusConnection *connection, const char *device_name, const char *application_name, int32_t priority, rd_request_cb_t request_cb, DBusError *error) { rd_device *d = NULL; int r, k; DBusError _error; DBusMessage *m = NULL, *reply = NULL; dbus_bool_t good; if (!error) error = &_error; dbus_error_init(error); if (!_d) return -EINVAL; if (!connection) return -EINVAL; if (!device_name) return -EINVAL; if (!request_cb && priority != INT32_MAX) return -EINVAL; if (!(d = calloc(sizeof(rd_device), 1))) return -ENOMEM; d->ref = 1; if (!(d->device_name = strdup(device_name))) { r = -ENOMEM; goto fail; } if (!(d->application_name = strdup(application_name))) { r = -ENOMEM; goto fail; } d->priority = priority; d->connection = dbus_connection_ref(connection); d->request_cb = request_cb; if (!(d->service_name = malloc(sizeof(SERVICE_PREFIX) + strlen(device_name)))) { r = -ENOMEM; goto fail; } sprintf(d->service_name, SERVICE_PREFIX "%s", d->device_name); if (!(d->object_path = malloc(sizeof(OBJECT_PREFIX) + strlen(device_name)))) { r = -ENOMEM; goto fail; } sprintf(d->object_path, OBJECT_PREFIX "%s", d->device_name); if ((k = dbus_bus_request_name( d->connection, d->service_name, DBUS_NAME_FLAG_DO_NOT_QUEUE| (priority < INT32_MAX ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT : 0), error)) < 0) { r = -EIO; goto fail; } if (k == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) goto success; if (k != DBUS_REQUEST_NAME_REPLY_EXISTS) { r = -EIO; goto fail; } if (priority <= INT32_MIN) { r = -EBUSY; goto fail; } if (!(m = dbus_message_new_method_call( d->service_name, d->object_path, "org.freedesktop.ReserveDevice1", "RequestRelease"))) { r = -ENOMEM; goto fail; } if (!dbus_message_append_args( m, DBUS_TYPE_INT32, &d->priority, DBUS_TYPE_INVALID)) { r = -ENOMEM; goto fail; } if (!(reply = dbus_connection_send_with_reply_and_block( d->connection, m, 5000, /* 5s */ error))) { if (dbus_error_has_name(error, DBUS_ERROR_TIMED_OUT) || dbus_error_has_name(error, DBUS_ERROR_UNKNOWN_METHOD) || dbus_error_has_name(error, DBUS_ERROR_NO_REPLY)) { /* This must be treated as denied. */ r = -EBUSY; goto fail; } r = -EIO; goto fail; } if (!dbus_message_get_args( reply, error, DBUS_TYPE_BOOLEAN, &good, DBUS_TYPE_INVALID)) { r = -EIO; goto fail; } if (!good) { r = -EBUSY; goto fail; } if ((k = dbus_bus_request_name( d->connection, d->service_name, DBUS_NAME_FLAG_DO_NOT_QUEUE| (priority < INT32_MAX ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT : 0)| DBUS_NAME_FLAG_REPLACE_EXISTING, error)) < 0) { r = -EIO; goto fail; } if (k != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { r = -EIO; goto fail; } success: d->owning = 1; if (!(dbus_connection_register_object_path( d->connection, d->object_path, &vtable, d))) { r = -ENOMEM; goto fail; } d->registered = 1; if (!dbus_connection_add_filter( d->connection, filter_handler, d, NULL)) { r = -ENOMEM; goto fail; } d->filtering = 1; *_d = d; return 0; fail: if (m) dbus_message_unref(m); if (reply) dbus_message_unref(reply); if (&_error == error) dbus_error_free(&_error); if (d) rd_release(d); return r; }
static DBusHandlerResult filter_handler( DBusConnection *c, DBusMessage *m, void *userdata) { rd_device *d; DBusError error; char *name_owner = NULL; dbus_error_init(&error); d = userdata; assert(d->ref >= 1); if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameLost")) { const char *name; if (!dbus_message_get_args( m, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) goto invalid; if (strcmp(name, d->service_name) == 0 && d->owning) { /* Verify the actual owner of the name to avoid leaked NameLost * signals from previous reservations. The D-Bus daemon will send * all messages asynchronously in the correct order, but we could * potentially process them too late due to the pseudo-blocking * call mechanism used during both acquisition and release. This * can happen if we release the device and immediately after * reacquire it before NameLost is processed. */ if (!d->gave_up) { const char *un; if ((un = dbus_bus_get_unique_name(c)) && rd_dbus_get_name_owner(c, d->service_name, &name_owner, &error) == 0) if (name_owner && strcmp(name_owner, un) == 0) goto invalid; /* Name still owned by us */ } d->owning = 0; if (!d->gave_up) { d->ref++; if (d->request_cb) d->request_cb(d, 1); d->gave_up = 1; rd_release(d); } } } invalid: free(name_owner); dbus_error_free(&error); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; }
static DBusHandlerResult object_handler( DBusConnection *c, DBusMessage *m, void *userdata) { rd_device *d; DBusError error; DBusMessage *reply = NULL; dbus_error_init(&error); d = userdata; assert(d->ref >= 1); if (dbus_message_is_method_call( m, "org.freedesktop.ReserveDevice1", "RequestRelease")) { int32_t priority; dbus_bool_t ret; if (!dbus_message_get_args( m, &error, DBUS_TYPE_INT32, &priority, DBUS_TYPE_INVALID)) goto invalid; ret = FALSE; if (priority > d->priority && d->request_cb) { d->ref++; if (d->request_cb(d, 0) > 0) { ret = TRUE; d->gave_up = 1; } rd_release(d); } if (!(reply = dbus_message_new_method_return(m))) goto oom; if (!dbus_message_append_args( reply, DBUS_TYPE_BOOLEAN, &ret, DBUS_TYPE_INVALID)) goto oom; if (!dbus_connection_send(c, reply, NULL)) goto oom; dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_method_call( m, "org.freedesktop.DBus.Properties", "Get")) { const char *interface, *property; if (!dbus_message_get_args( m, &error, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) goto invalid; if (strcmp(interface, "org.freedesktop.ReserveDevice1") == 0) { const char *empty = ""; if (strcmp(property, "ApplicationName") == 0 && d->application_name) { if (!(reply = dbus_message_new_method_return(m))) goto oom; if (!add_variant( reply, DBUS_TYPE_STRING, d->application_name ? (const char * const *) &d->application_name : &empty)) goto oom; } else if (strcmp(property, "ApplicationDeviceName") == 0) { if (!(reply = dbus_message_new_method_return(m))) goto oom; if (!add_variant( reply, DBUS_TYPE_STRING, d->application_device_name ? (const char * const *) &d->application_device_name : &empty)) goto oom; } else if (strcmp(property, "Priority") == 0) { if (!(reply = dbus_message_new_method_return(m))) goto oom; if (!add_variant( reply, DBUS_TYPE_INT32, &d->priority)) goto oom; } else { if (!(reply = dbus_message_new_error_printf( m, DBUS_ERROR_UNKNOWN_METHOD, "Unknown property %s", property))) goto oom; } if (!dbus_connection_send(c, reply, NULL)) goto oom; dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } } else if (dbus_message_is_method_call( m, "org.freedesktop.DBus.Introspectable", "Introspect")) { const char *i = introspection; if (!(reply = dbus_message_new_method_return(m))) goto oom; if (!dbus_message_append_args( reply, DBUS_TYPE_STRING, &i, DBUS_TYPE_INVALID)) goto oom; if (!dbus_connection_send(c, reply, NULL)) goto oom; dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; invalid: if (reply) dbus_message_unref(reply); if (!(reply = dbus_message_new_error( m, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) goto oom; if (!dbus_connection_send(c, reply, NULL)) goto oom; dbus_message_unref(reply); dbus_error_free(&error); return DBUS_HANDLER_RESULT_HANDLED; oom: if (reply) dbus_message_unref(reply); dbus_error_free(&error); return DBUS_HANDLER_RESULT_NEED_MEMORY; }