2017-05-23 19:15:33 +02:00
|
|
|
/* PipeWire
|
2017-04-17 16:32:25 +02:00
|
|
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
|
|
|
|
*
|
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU Library General Public
|
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This library 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
|
|
|
|
|
* Library General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Library General Public
|
|
|
|
|
* License along with this library; if not, write to the
|
|
|
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
|
|
|
* Boston, MA 02110-1301, USA.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
|
|
#include <dbus/dbus.h>
|
|
|
|
|
|
2017-07-11 15:57:20 +02:00
|
|
|
#include "pipewire/interfaces.h"
|
|
|
|
|
#include "pipewire/utils.h"
|
2017-06-12 18:20:59 +02:00
|
|
|
|
2017-07-11 15:57:20 +02:00
|
|
|
#include "pipewire/core.h"
|
|
|
|
|
#include "pipewire/module.h"
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-23 19:15:33 +02:00
|
|
|
struct impl {
|
2017-05-26 08:05:01 +02:00
|
|
|
struct pw_core *core;
|
|
|
|
|
struct pw_properties *properties;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
DBusConnection *bus;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
struct pw_listener global_added;
|
|
|
|
|
struct pw_listener global_removed;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
struct spa_list client_list;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
struct spa_source *dispatch_event;
|
2017-05-23 19:15:33 +02:00
|
|
|
};
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-23 19:15:33 +02:00
|
|
|
struct client_info {
|
2017-05-26 08:05:01 +02:00
|
|
|
struct impl *impl;
|
|
|
|
|
struct spa_list link;
|
|
|
|
|
struct pw_client *client;
|
|
|
|
|
bool is_sandboxed;
|
2017-06-12 18:20:59 +02:00
|
|
|
const struct pw_core_methods *old_methods;
|
|
|
|
|
struct pw_core_methods core_methods;
|
2017-05-26 08:05:01 +02:00
|
|
|
struct spa_list async_pending;
|
2017-06-15 17:54:55 +02:00
|
|
|
struct pw_listener resource_impl;
|
2017-06-12 18:20:59 +02:00
|
|
|
struct pw_listener resource_removed;
|
2017-05-23 19:15:33 +02:00
|
|
|
};
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-23 19:15:33 +02:00
|
|
|
struct async_pending {
|
2017-05-26 08:05:01 +02:00
|
|
|
struct spa_list link;
|
|
|
|
|
struct client_info *info;
|
2017-06-12 18:20:59 +02:00
|
|
|
bool handled;
|
2017-05-26 08:05:01 +02:00
|
|
|
char *handle;
|
2017-06-12 18:20:59 +02:00
|
|
|
struct pw_resource *resource;
|
|
|
|
|
char *factory_name;
|
|
|
|
|
char *name;
|
|
|
|
|
struct pw_properties *properties;
|
2017-07-11 19:50:26 +02:00
|
|
|
uint32_t version;
|
2017-06-12 18:20:59 +02:00
|
|
|
uint32_t new_id;
|
2017-05-23 19:15:33 +02:00
|
|
|
};
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
static struct client_info *find_client_info(struct impl *impl, struct pw_client *client)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
struct client_info *info;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
spa_list_for_each(info, &impl->client_list, link) {
|
|
|
|
|
if (info->client == client)
|
|
|
|
|
return info;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
static void close_request(struct async_pending *p)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
DBusMessage *m = NULL;
|
|
|
|
|
struct impl *impl = p->info->impl;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
pw_log_debug("pending %p: handle %s", p, p->handle);
|
2017-04-20 19:25:14 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
if (!(m = dbus_message_new_method_call("org.freedesktop.portal.Request",
|
|
|
|
|
p->handle,
|
|
|
|
|
"org.freedesktop.portal.Request", "Close"))) {
|
|
|
|
|
pw_log_error("Failed to create message");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
if (!dbus_connection_send(impl->bus, m, NULL))
|
|
|
|
|
pw_log_error("Failed to send message");
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
dbus_message_unref(m);
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
static struct async_pending *find_pending(struct client_info *cinfo, const char *handle)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
struct async_pending *p;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
spa_list_for_each(p, &cinfo->async_pending, link) {
|
|
|
|
|
if (strcmp(p->handle, handle) == 0)
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-06-12 18:20:59 +02:00
|
|
|
static void free_pending(struct async_pending *p)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
if (!p->handled)
|
|
|
|
|
close_request(p);
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
pw_log_debug("pending %p: handle %s", p, p->handle);
|
|
|
|
|
spa_list_remove(&p->link);
|
|
|
|
|
free(p->handle);
|
2017-06-12 18:20:59 +02:00
|
|
|
free(p->factory_name);
|
|
|
|
|
free(p->name);
|
|
|
|
|
if (p->properties)
|
|
|
|
|
pw_properties_free(p->properties);
|
|
|
|
|
free(p);
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
static void client_info_free(struct client_info *cinfo)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
struct async_pending *p, *tmp;
|
|
|
|
|
|
2017-06-12 18:20:59 +02:00
|
|
|
spa_list_for_each_safe(p, tmp, &cinfo->async_pending, link)
|
|
|
|
|
free_pending(p);
|
|
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
spa_list_remove(&cinfo->link);
|
|
|
|
|
free(cinfo);
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
static bool client_is_sandboxed(struct pw_client *cl)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
char data[2048], *ptr;
|
|
|
|
|
size_t n, size;
|
|
|
|
|
const char *state = NULL;
|
|
|
|
|
const char *current;
|
|
|
|
|
bool result;
|
|
|
|
|
int fd;
|
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
|
|
if (cl->ucred_valid) {
|
|
|
|
|
pw_log_info("client has trusted pid %d", cl->ucred.pid);
|
|
|
|
|
} else {
|
|
|
|
|
pw_log_info("no trusted pid found, assuming not sandboxed\n");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pid = cl->ucred.pid;
|
|
|
|
|
|
|
|
|
|
sprintf(data, "/proc/%u/cgroup", pid);
|
|
|
|
|
fd = open(data, O_RDONLY | O_CLOEXEC, 0);
|
|
|
|
|
if (fd == -1)
|
|
|
|
|
return false;
|
|
|
|
|
|
2017-06-21 16:42:10 +02:00
|
|
|
spa_zero(data);
|
2017-05-26 08:05:01 +02:00
|
|
|
size = sizeof(data);
|
|
|
|
|
ptr = data;
|
|
|
|
|
|
|
|
|
|
while (size > 0) {
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
if ((r = read(fd, data, size)) < 0) {
|
|
|
|
|
if (errno == EINTR)
|
|
|
|
|
continue;
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (r == 0)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
ptr += r;
|
|
|
|
|
size -= r;
|
|
|
|
|
}
|
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
|
|
result = false;
|
|
|
|
|
while ((current = pw_split_walk(data, "\n", &n, &state)) != NULL) {
|
|
|
|
|
if (strncmp(current, "1:name=systemd:", strlen("1:name=systemd:")) == 0) {
|
|
|
|
|
const char *p = strstr(current, "flatpak-");
|
|
|
|
|
if (p && p - current < n) {
|
|
|
|
|
pw_log_info("found a flatpak cgroup, assuming sandboxed\n");
|
|
|
|
|
result = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
2017-05-26 08:05:01 +02:00
|
|
|
check_global_owner(struct pw_core *core, struct pw_client *client, struct pw_global *global)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
if (global == NULL)
|
|
|
|
|
return false;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
if (global->owner == NULL)
|
|
|
|
|
return true;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-06-21 09:03:29 +02:00
|
|
|
if (global->owner->client->ucred.uid == client->ucred.uid)
|
2017-05-26 08:05:01 +02:00
|
|
|
return true;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
return false;
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-06-12 18:20:59 +02:00
|
|
|
static bool
|
|
|
|
|
do_global_filter(struct pw_global *global, struct pw_client *client, void *data)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
if (global->type == client->core->type.link) {
|
|
|
|
|
struct pw_link *link = global->object;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
/* we must be able to see both nodes */
|
|
|
|
|
if (link->output
|
|
|
|
|
&& !check_global_owner(client->core, client, link->output->node->global))
|
2017-06-12 18:20:59 +02:00
|
|
|
return false;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
if (link->input
|
|
|
|
|
&& !check_global_owner(client->core, client, link->input->node->global))
|
2017-06-12 18:20:59 +02:00
|
|
|
return false;
|
2017-05-26 08:05:01 +02:00
|
|
|
} else if (!check_global_owner(client->core, client, global))
|
2017-06-12 18:20:59 +02:00
|
|
|
return false;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-06-12 18:20:59 +02:00
|
|
|
return true;
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static DBusHandlerResult
|
2017-05-26 18:19:51 +02:00
|
|
|
portal_response(DBusConnection *connection, DBusMessage *msg, void *user_data)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
struct client_info *cinfo = user_data;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
if (dbus_message_is_signal(msg, "org.freedesktop.portal.Request", "Response")) {
|
|
|
|
|
uint32_t response = 2;
|
|
|
|
|
DBusError error;
|
|
|
|
|
struct async_pending *p;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
dbus_error_init(&error);
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
dbus_connection_remove_filter(connection, portal_response, cinfo);
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
if (!dbus_message_get_args
|
|
|
|
|
(msg, &error, DBUS_TYPE_UINT32, &response, DBUS_TYPE_INVALID)) {
|
|
|
|
|
pw_log_error("failed to parse Response: %s", error.message);
|
|
|
|
|
dbus_error_free(&error);
|
|
|
|
|
}
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
p = find_pending(cinfo, dbus_message_get_path(msg));
|
|
|
|
|
if (p == NULL)
|
|
|
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
p->handled = true;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
pw_log_debug("portal check result: %d", response);
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-06-12 18:20:59 +02:00
|
|
|
if (response == 0) {
|
|
|
|
|
cinfo->old_methods->create_node (p->resource,
|
|
|
|
|
p->factory_name,
|
|
|
|
|
p->name,
|
|
|
|
|
&p->properties->dict,
|
2017-07-11 19:50:26 +02:00
|
|
|
p->version,
|
2017-06-12 18:20:59 +02:00
|
|
|
p->new_id);
|
|
|
|
|
} else {
|
|
|
|
|
pw_core_notify_error(cinfo->client->core_resource,
|
|
|
|
|
p->resource->id, SPA_RESULT_NO_PERMISSION, "not allowed");
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
free_pending(p);
|
|
|
|
|
pw_client_set_busy(cinfo->client, false);
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
|
|
|
}
|
|
|
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-06-12 18:20:59 +02:00
|
|
|
|
|
|
|
|
static void do_create_node(void *object,
|
|
|
|
|
const char *factory_name,
|
|
|
|
|
const char *name,
|
|
|
|
|
const struct spa_dict *props,
|
2017-07-11 19:50:26 +02:00
|
|
|
uint32_t version,
|
2017-06-12 18:20:59 +02:00
|
|
|
uint32_t new_id)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-06-12 18:20:59 +02:00
|
|
|
struct pw_resource *resource = object;
|
|
|
|
|
struct client_info *cinfo = resource->access_private;
|
|
|
|
|
struct impl *impl = cinfo->impl;
|
|
|
|
|
struct pw_client *client = resource->client;
|
2017-05-26 08:05:01 +02:00
|
|
|
DBusMessage *m = NULL, *r = NULL;
|
|
|
|
|
DBusError error;
|
|
|
|
|
pid_t pid;
|
|
|
|
|
DBusMessageIter msg_iter;
|
|
|
|
|
DBusMessageIter dict_iter;
|
|
|
|
|
const char *handle;
|
|
|
|
|
const char *device;
|
2017-06-12 18:20:59 +02:00
|
|
|
struct async_pending *p;
|
2017-05-26 08:05:01 +02:00
|
|
|
|
|
|
|
|
if (!cinfo->is_sandboxed) {
|
2017-07-11 19:50:26 +02:00
|
|
|
cinfo->old_methods->create_node (object, factory_name, name, props, version, new_id);
|
2017-06-12 18:20:59 +02:00
|
|
|
return;
|
2017-05-26 08:05:01 +02:00
|
|
|
}
|
2017-06-02 12:38:11 +02:00
|
|
|
if (strcmp(factory_name, "client-node") != 0) {
|
2017-06-12 18:20:59 +02:00
|
|
|
pw_log_error("can only allow client-node");
|
|
|
|
|
goto not_allowed;
|
2017-06-02 12:38:11 +02:00
|
|
|
}
|
2017-05-26 08:05:01 +02:00
|
|
|
|
|
|
|
|
pw_log_info("ask portal for client %p", cinfo->client);
|
|
|
|
|
|
|
|
|
|
dbus_error_init(&error);
|
|
|
|
|
|
|
|
|
|
if (!(m = dbus_message_new_method_call("org.freedesktop.portal.Desktop",
|
|
|
|
|
"/org/freedesktop/portal/desktop",
|
|
|
|
|
"org.freedesktop.portal.Device", "AccessDevice")))
|
|
|
|
|
goto no_method_call;
|
|
|
|
|
|
|
|
|
|
device = "camera";
|
|
|
|
|
|
|
|
|
|
pid = cinfo->client->ucred.pid;
|
|
|
|
|
if (!dbus_message_append_args(m, DBUS_TYPE_UINT32, &pid, DBUS_TYPE_INVALID))
|
|
|
|
|
goto message_failed;
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_init_append(m, &msg_iter);
|
|
|
|
|
dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "s", &dict_iter);
|
|
|
|
|
dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, &device);
|
|
|
|
|
dbus_message_iter_close_container(&msg_iter, &dict_iter);
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter);
|
|
|
|
|
dbus_message_iter_close_container(&msg_iter, &dict_iter);
|
|
|
|
|
|
|
|
|
|
if (!(r = dbus_connection_send_with_reply_and_block(impl->bus, m, -1, &error)))
|
|
|
|
|
goto send_failed;
|
|
|
|
|
|
|
|
|
|
dbus_message_unref(m);
|
|
|
|
|
|
|
|
|
|
if (!dbus_message_get_args(r, &error, DBUS_TYPE_OBJECT_PATH, &handle, DBUS_TYPE_INVALID))
|
|
|
|
|
goto parse_failed;
|
|
|
|
|
|
|
|
|
|
dbus_message_unref(r);
|
|
|
|
|
|
|
|
|
|
dbus_bus_add_match(impl->bus,
|
|
|
|
|
"type='signal',interface='org.freedesktop.portal.Request'", &error);
|
|
|
|
|
dbus_connection_flush(impl->bus);
|
|
|
|
|
if (dbus_error_is_set(&error))
|
|
|
|
|
goto subscribe_failed;
|
|
|
|
|
|
|
|
|
|
dbus_connection_add_filter(impl->bus, portal_response, cinfo, NULL);
|
|
|
|
|
|
2017-06-12 18:20:59 +02:00
|
|
|
p = calloc(1, sizeof(struct async_pending));
|
|
|
|
|
p->info = cinfo;
|
|
|
|
|
p->handle = strdup(handle);
|
|
|
|
|
p->handled = false;
|
|
|
|
|
p->resource = resource;
|
|
|
|
|
p->factory_name = strdup(factory_name);
|
|
|
|
|
p->name = strdup(name);
|
|
|
|
|
p->properties = props ? pw_properties_new_dict(props) : NULL;
|
2017-07-11 19:50:26 +02:00
|
|
|
p->version = version;
|
2017-06-12 18:20:59 +02:00
|
|
|
p->new_id = new_id;
|
|
|
|
|
pw_client_set_busy(client, true);
|
|
|
|
|
|
|
|
|
|
pw_log_debug("pending %p: handle %s", p, handle);
|
|
|
|
|
spa_list_insert(cinfo->async_pending.prev, &p->link);
|
2017-05-26 08:05:01 +02:00
|
|
|
|
2017-06-12 18:20:59 +02:00
|
|
|
return;
|
2017-05-26 08:05:01 +02:00
|
|
|
|
|
|
|
|
no_method_call:
|
|
|
|
|
pw_log_error("Failed to create message");
|
2017-06-12 18:20:59 +02:00
|
|
|
goto not_allowed;
|
2017-05-26 08:05:01 +02:00
|
|
|
message_failed:
|
|
|
|
|
dbus_message_unref(m);
|
2017-06-12 18:20:59 +02:00
|
|
|
goto not_allowed;
|
2017-05-26 08:05:01 +02:00
|
|
|
send_failed:
|
|
|
|
|
pw_log_error("Failed to call portal: %s", error.message);
|
|
|
|
|
dbus_error_free(&error);
|
|
|
|
|
dbus_message_unref(m);
|
2017-06-12 18:20:59 +02:00
|
|
|
goto not_allowed;
|
2017-05-26 08:05:01 +02:00
|
|
|
parse_failed:
|
|
|
|
|
pw_log_error("Failed to parse AccessDevice result: %s", error.message);
|
|
|
|
|
dbus_error_free(&error);
|
|
|
|
|
dbus_message_unref(r);
|
2017-06-12 18:20:59 +02:00
|
|
|
goto not_allowed;
|
2017-05-26 08:05:01 +02:00
|
|
|
subscribe_failed:
|
|
|
|
|
pw_log_error("Failed to subscribe to Request signal: %s", error.message);
|
|
|
|
|
dbus_error_free(&error);
|
2017-06-12 18:20:59 +02:00
|
|
|
goto not_allowed;
|
|
|
|
|
not_allowed:
|
|
|
|
|
pw_core_notify_error(client->core_resource,
|
|
|
|
|
resource->id, SPA_RESULT_NO_PERMISSION, "not allowed");
|
|
|
|
|
return;
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-06-12 18:20:59 +02:00
|
|
|
static void
|
|
|
|
|
do_create_link(void *object,
|
2017-06-02 12:38:11 +02:00
|
|
|
uint32_t output_node_id,
|
|
|
|
|
uint32_t output_port_id,
|
|
|
|
|
uint32_t input_node_id,
|
|
|
|
|
uint32_t input_port_id,
|
|
|
|
|
const struct spa_format *filter,
|
2017-06-12 18:20:59 +02:00
|
|
|
const struct spa_dict *props,
|
|
|
|
|
uint32_t new_id)
|
2017-06-02 12:38:11 +02:00
|
|
|
{
|
2017-06-12 18:20:59 +02:00
|
|
|
struct pw_resource *resource = object;
|
|
|
|
|
struct client_info *cinfo = resource->access_private;
|
|
|
|
|
struct pw_client *client = resource->client;
|
2017-06-02 12:38:11 +02:00
|
|
|
|
2017-06-12 18:20:59 +02:00
|
|
|
if (cinfo->is_sandboxed) {
|
|
|
|
|
pw_core_notify_error(client->core_resource,
|
|
|
|
|
resource->id, SPA_RESULT_NO_PERMISSION, "not allowed");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
cinfo->old_methods->create_link (object,
|
|
|
|
|
output_node_id,
|
|
|
|
|
output_port_id,
|
|
|
|
|
input_node_id,
|
|
|
|
|
input_port_id,
|
|
|
|
|
filter,
|
|
|
|
|
props,
|
|
|
|
|
new_id);
|
2017-06-02 12:38:11 +02:00
|
|
|
}
|
|
|
|
|
|
2017-06-15 17:54:55 +02:00
|
|
|
static void on_resource_impl(struct pw_listener *listener,
|
|
|
|
|
struct pw_client *client,
|
|
|
|
|
struct pw_resource *resource)
|
2017-06-12 18:20:59 +02:00
|
|
|
{
|
2017-06-15 17:54:55 +02:00
|
|
|
struct client_info *cinfo = SPA_CONTAINER_OF(listener, struct client_info, resource_impl);
|
2017-06-12 18:20:59 +02:00
|
|
|
struct impl *impl = cinfo->impl;
|
|
|
|
|
|
|
|
|
|
if (resource->type == impl->core->type.core) {
|
|
|
|
|
cinfo->old_methods = resource->implementation;
|
|
|
|
|
cinfo->core_methods = *cinfo->old_methods;
|
|
|
|
|
resource->implementation = &cinfo->core_methods;
|
|
|
|
|
resource->access_private = cinfo;
|
|
|
|
|
cinfo->core_methods.create_node = do_create_node;
|
|
|
|
|
cinfo->core_methods.create_link = do_create_link;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-17 16:32:25 +02:00
|
|
|
|
|
|
|
|
static void
|
2017-05-26 08:05:01 +02:00
|
|
|
on_global_added(struct pw_listener *listener, struct pw_core *core, struct pw_global *global)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
struct impl *impl = SPA_CONTAINER_OF(listener, struct impl, global_added);
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
if (global->type == impl->core->type.client) {
|
|
|
|
|
struct pw_client *client = global->object;
|
|
|
|
|
struct client_info *cinfo;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
cinfo = calloc(1, sizeof(struct client_info));
|
|
|
|
|
cinfo->impl = impl;
|
|
|
|
|
cinfo->client = client;
|
|
|
|
|
cinfo->is_sandboxed = client_is_sandboxed(client);
|
|
|
|
|
spa_list_init(&cinfo->async_pending);
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-06-15 17:54:55 +02:00
|
|
|
pw_signal_add(&client->resource_impl, &cinfo->resource_impl, on_resource_impl);
|
2017-06-12 18:20:59 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
spa_list_insert(impl->client_list.prev, &cinfo->link);
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
pw_log_debug("module %p: client %p added", impl, client);
|
|
|
|
|
}
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2017-05-26 08:05:01 +02:00
|
|
|
on_global_removed(struct pw_listener *listener, struct pw_core *core, struct pw_global *global)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
struct impl *impl = SPA_CONTAINER_OF(listener, struct impl, global_removed);
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
if (global->type == impl->core->type.client) {
|
|
|
|
|
struct pw_client *client = global->object;
|
|
|
|
|
struct client_info *cinfo;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
if ((cinfo = find_client_info(impl, client)))
|
|
|
|
|
client_info_free(cinfo);
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
pw_log_debug("module %p: client %p removed", impl, client);
|
|
|
|
|
}
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
static void dispatch_cb(struct spa_loop_utils *utils, struct spa_source *source, void *userdata)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
struct impl *impl = userdata;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
if (dbus_connection_dispatch(impl->bus) == DBUS_DISPATCH_COMPLETE)
|
2017-07-11 12:24:03 +02:00
|
|
|
pw_loop_enable_idle(impl->core->main_loop, source, false);
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-05-26 18:19:51 +02:00
|
|
|
static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *userdata)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
struct impl *impl = userdata;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-07-11 12:24:03 +02:00
|
|
|
pw_loop_enable_idle(impl->core->main_loop,
|
2017-05-26 08:05:01 +02:00
|
|
|
impl->dispatch_event, status == DBUS_DISPATCH_COMPLETE ? false : true);
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-05-26 18:19:51 +02:00
|
|
|
static inline enum spa_io dbus_to_io(DBusWatch *watch)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
enum spa_io mask;
|
|
|
|
|
unsigned int flags;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
/* no watch flags for disabled watches */
|
|
|
|
|
if (!dbus_watch_get_enabled(watch))
|
|
|
|
|
return 0;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
flags = dbus_watch_get_flags(watch);
|
|
|
|
|
mask = SPA_IO_HUP | SPA_IO_ERR;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
if (flags & DBUS_WATCH_READABLE)
|
|
|
|
|
mask |= SPA_IO_IN;
|
|
|
|
|
if (flags & DBUS_WATCH_WRITABLE)
|
|
|
|
|
mask |= SPA_IO_OUT;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
return mask;
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
static inline unsigned int io_to_dbus(enum spa_io mask)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
unsigned int flags = 0;
|
|
|
|
|
|
|
|
|
|
if (mask & SPA_IO_IN)
|
|
|
|
|
flags |= DBUS_WATCH_READABLE;
|
|
|
|
|
if (mask & SPA_IO_OUT)
|
|
|
|
|
flags |= DBUS_WATCH_WRITABLE;
|
|
|
|
|
if (mask & SPA_IO_HUP)
|
|
|
|
|
flags |= DBUS_WATCH_HANGUP;
|
|
|
|
|
if (mask & SPA_IO_ERR)
|
|
|
|
|
flags |= DBUS_WATCH_ERROR;
|
|
|
|
|
return flags;
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2017-05-26 08:05:01 +02:00
|
|
|
handle_io_event(struct spa_loop_utils *utils,
|
|
|
|
|
struct spa_source *source, int fd, enum spa_io mask, void *userdata)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
DBusWatch *watch = userdata;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
if (!dbus_watch_get_enabled(watch)) {
|
|
|
|
|
pw_log_warn("Asked to handle disabled watch: %p %i", (void *) watch, fd);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
dbus_watch_handle(watch, io_to_dbus(mask));
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-05-26 18:19:51 +02:00
|
|
|
static dbus_bool_t add_watch(DBusWatch *watch, void *userdata)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
struct impl *impl = userdata;
|
|
|
|
|
struct spa_source *source;
|
|
|
|
|
|
|
|
|
|
pw_log_debug("add watch %p %d", watch, dbus_watch_get_unix_fd(watch));
|
|
|
|
|
|
|
|
|
|
/* we dup because dbus tends to add the same fd multiple times and our epoll
|
|
|
|
|
* implementation does not like that */
|
2017-07-11 12:24:03 +02:00
|
|
|
source = pw_loop_add_io(impl->core->main_loop,
|
2017-05-26 08:05:01 +02:00
|
|
|
dup(dbus_watch_get_unix_fd(watch)),
|
|
|
|
|
dbus_to_io(watch), true, handle_io_event, watch);
|
|
|
|
|
|
|
|
|
|
dbus_watch_set_data(watch, source, NULL);
|
|
|
|
|
return TRUE;
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-05-26 18:19:51 +02:00
|
|
|
static void remove_watch(DBusWatch *watch, void *userdata)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
struct impl *impl = userdata;
|
|
|
|
|
struct spa_source *source;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
if ((source = dbus_watch_get_data(watch)))
|
2017-07-11 12:24:03 +02:00
|
|
|
pw_loop_destroy_source(impl->core->main_loop, source);
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-05-26 18:19:51 +02:00
|
|
|
static void toggle_watch(DBusWatch *watch, void *userdata)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
struct impl *impl = userdata;
|
|
|
|
|
struct spa_source *source;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
source = dbus_watch_get_data(watch);
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-07-11 12:24:03 +02:00
|
|
|
pw_loop_update_io(impl->core->main_loop, source, dbus_to_io(watch));
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2017-05-26 08:05:01 +02:00
|
|
|
handle_timer_event(struct spa_loop_utils *utils, struct spa_source *source, void *userdata)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
DBusTimeout *timeout = userdata;
|
|
|
|
|
uint64_t t;
|
|
|
|
|
struct timespec ts;
|
|
|
|
|
|
|
|
|
|
if (dbus_timeout_get_enabled(timeout)) {
|
|
|
|
|
t = dbus_timeout_get_interval(timeout) * SPA_NSEC_PER_MSEC;
|
|
|
|
|
ts.tv_sec = t / SPA_NSEC_PER_SEC;
|
|
|
|
|
ts.tv_nsec = t % SPA_NSEC_PER_SEC;
|
|
|
|
|
spa_loop_utils_update_timer(utils, source, &ts, NULL, false);
|
|
|
|
|
|
|
|
|
|
dbus_timeout_handle(timeout);
|
|
|
|
|
}
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-05-26 18:19:51 +02:00
|
|
|
static dbus_bool_t add_timeout(DBusTimeout *timeout, void *userdata)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
struct impl *impl = userdata;
|
|
|
|
|
struct spa_source *source;
|
|
|
|
|
struct timespec ts;
|
|
|
|
|
uint64_t t;
|
|
|
|
|
|
|
|
|
|
if (!dbus_timeout_get_enabled(timeout))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
2017-07-11 12:24:03 +02:00
|
|
|
source = pw_loop_add_timer(impl->core->main_loop, handle_timer_event, timeout);
|
2017-05-26 08:05:01 +02:00
|
|
|
|
|
|
|
|
dbus_timeout_set_data(timeout, source, NULL);
|
|
|
|
|
|
|
|
|
|
t = dbus_timeout_get_interval(timeout) * SPA_NSEC_PER_MSEC;
|
|
|
|
|
ts.tv_sec = t / SPA_NSEC_PER_SEC;
|
|
|
|
|
ts.tv_nsec = t % SPA_NSEC_PER_SEC;
|
2017-07-11 12:24:03 +02:00
|
|
|
pw_loop_update_timer(impl->core->main_loop, source, &ts, NULL, false);
|
2017-05-26 08:05:01 +02:00
|
|
|
return TRUE;
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-05-26 18:19:51 +02:00
|
|
|
static void remove_timeout(DBusTimeout *timeout, void *userdata)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
struct impl *impl = userdata;
|
|
|
|
|
struct spa_source *source;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
if ((source = dbus_timeout_get_data(timeout)))
|
2017-07-11 12:24:03 +02:00
|
|
|
pw_loop_destroy_source(impl->core->main_loop, source);
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-05-26 18:19:51 +02:00
|
|
|
static void toggle_timeout(DBusTimeout *timeout, void *userdata)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
struct impl *impl = userdata;
|
|
|
|
|
struct spa_source *source;
|
|
|
|
|
struct timespec ts, *tsp;
|
|
|
|
|
|
|
|
|
|
source = dbus_timeout_get_data(timeout);
|
|
|
|
|
|
|
|
|
|
if (dbus_timeout_get_enabled(timeout)) {
|
|
|
|
|
uint64_t t = dbus_timeout_get_interval(timeout) * SPA_NSEC_PER_MSEC;
|
|
|
|
|
ts.tv_sec = t / SPA_NSEC_PER_SEC;
|
|
|
|
|
ts.tv_nsec = t % SPA_NSEC_PER_SEC;
|
|
|
|
|
tsp = &ts;
|
|
|
|
|
} else {
|
|
|
|
|
tsp = NULL;
|
|
|
|
|
}
|
2017-07-11 12:24:03 +02:00
|
|
|
pw_loop_update_timer(impl->core->main_loop, source, tsp, NULL, false);
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
static void wakeup_main(void *userdata)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
struct impl *impl = userdata;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-07-11 12:24:03 +02:00
|
|
|
pw_loop_enable_idle(impl->core->main_loop, impl->dispatch_event, true);
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
static struct impl *module_new(struct pw_core *core, struct pw_properties *properties)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
struct impl *impl;
|
|
|
|
|
DBusError error;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
dbus_error_init(&error);
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
impl = calloc(1, sizeof(struct impl));
|
|
|
|
|
pw_log_debug("module %p: new", impl);
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
impl->core = core;
|
|
|
|
|
impl->properties = properties;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
impl->bus = dbus_bus_get_private(DBUS_BUS_SESSION, &error);
|
|
|
|
|
if (impl->bus == NULL)
|
|
|
|
|
goto error;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-07-11 12:24:03 +02:00
|
|
|
impl->dispatch_event = pw_loop_add_idle(core->main_loop, false, dispatch_cb, impl);
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
dbus_connection_set_exit_on_disconnect(impl->bus, false);
|
|
|
|
|
dbus_connection_set_dispatch_status_function(impl->bus, dispatch_status, impl, NULL);
|
|
|
|
|
dbus_connection_set_watch_functions(impl->bus, add_watch, remove_watch, toggle_watch, impl,
|
|
|
|
|
NULL);
|
|
|
|
|
dbus_connection_set_timeout_functions(impl->bus, add_timeout, remove_timeout,
|
|
|
|
|
toggle_timeout, impl, NULL);
|
|
|
|
|
dbus_connection_set_wakeup_main_function(impl->bus, wakeup_main, impl, NULL);
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
spa_list_init(&impl->client_list);
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
pw_signal_add(&core->global_added, &impl->global_added, on_global_added);
|
|
|
|
|
pw_signal_add(&core->global_removed, &impl->global_removed, on_global_removed);
|
2017-06-12 18:20:59 +02:00
|
|
|
core->global_filter = do_global_filter;
|
|
|
|
|
core->global_filter_data = impl;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
return impl;
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
error:
|
|
|
|
|
pw_log_error("Failed to connect to system bus: %s", error.message);
|
|
|
|
|
dbus_error_free(&error);
|
|
|
|
|
return NULL;
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
2017-05-26 08:05:01 +02:00
|
|
|
static void module_destroy(struct impl *impl)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
pw_log_debug("module %p: destroy", impl);
|
2017-04-17 16:32:25 +02:00
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
dbus_connection_close(impl->bus);
|
|
|
|
|
dbus_connection_unref(impl->bus);
|
|
|
|
|
free(impl);
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2017-05-26 08:05:01 +02:00
|
|
|
bool pipewire__module_init(struct pw_module *module, const char *args)
|
2017-04-17 16:32:25 +02:00
|
|
|
{
|
2017-05-26 08:05:01 +02:00
|
|
|
module_new(module->core, NULL);
|
|
|
|
|
return true;
|
2017-04-17 16:32:25 +02:00
|
|
|
}
|