pipewire/src/modules/module-protocol-native.c

1182 lines
30 KiB
C
Raw Normal View History

2017-05-23 19:15:33 +02:00
/* PipeWire
*
* Copyright © 2018 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* 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. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/file.h>
#include <spa/pod/iter.h>
#include <spa/utils/result.h>
2018-08-14 12:33:53 +02:00
#include <spa/debug/pod.h>
#include <spa/debug/types.h>
#include "config.h"
2018-10-18 09:50:43 +02:00
#ifdef HAVE_SYSTEMD_DAEMON
#include <systemd/sd-daemon.h>
#endif
#include <pipewire/impl.h>
#include <extensions/protocol-native.h>
protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node.
2019-10-08 22:52:25 +02:00
#include "pipewire/private.h"
2017-07-11 15:57:20 +02:00
#include "modules/module-protocol-native/connection.h"
#include "modules/module-protocol-native/defs.h"
2019-08-14 11:44:16 +02:00
#define NAME "protocol-native"
#ifndef UNIX_PATH_MAX
#define UNIX_PATH_MAX 108
#endif
2018-06-01 11:23:02 +02:00
static const struct spa_dict_item module_props[] = {
{ PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
{ PW_KEY_MODULE_DESCRIPTION, "Native protocol using unix sockets" },
{ PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
2018-06-01 11:23:02 +02:00
};
static bool debug_messages = 0;
#define LOCK_SUFFIX ".lock"
#define LOCK_SUFFIXLEN 5
void pw_protocol_native_init(struct pw_protocol *protocol);
protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node.
2019-10-08 22:52:25 +02:00
void pw_protocol_native0_init(struct pw_protocol *protocol);
struct protocol_data {
2019-12-11 11:57:56 +01:00
struct pw_impl_module *module;
struct spa_hook module_listener;
struct pw_protocol *protocol;
struct server *local;
};
struct client {
struct pw_protocol_client this;
struct pw_context *context;
struct spa_source *source;
2019-06-19 16:22:22 +02:00
struct pw_protocol_native_connection *connection;
struct spa_hook conn_listener;
2019-06-19 16:22:22 +02:00
unsigned int disconnecting:1;
unsigned int flushing:1;
unsigned int paused:1;
};
struct server {
struct pw_protocol_server this;
2017-05-26 08:05:01 +02:00
int fd_lock;
struct sockaddr_un addr;
char lock_addr[UNIX_PATH_MAX + LOCK_SUFFIXLEN];
struct pw_loop *loop;
struct spa_source *source;
struct spa_hook hook;
2019-06-19 16:22:22 +02:00
unsigned int activated:1;
2017-05-23 19:15:33 +02:00
};
struct client_data {
2019-12-11 11:21:43 +01:00
struct pw_impl_client *client;
struct spa_hook client_listener;
protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node.
2019-10-08 22:52:25 +02:00
struct spa_list protocol_link;
2017-05-26 08:05:01 +02:00
struct spa_source *source;
struct pw_protocol_native_connection *connection;
protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node.
2019-10-08 22:52:25 +02:00
struct spa_hook conn_listener;
2019-06-19 16:22:22 +02:00
unsigned int busy:1;
unsigned int need_flush:1;
protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node.
2019-10-08 22:52:25 +02:00
struct protocol_compat_v2 compat_v2;
2017-05-23 19:15:33 +02:00
};
static int
process_messages(struct client_data *data)
{
struct pw_protocol_native_connection *conn = data->connection;
2019-12-11 11:21:43 +01:00
struct pw_impl_client *client = data->client;
struct pw_context *context = client->context;
const struct pw_protocol_native_message *msg;
struct pw_resource *resource;
2019-07-17 15:29:18 +02:00
int res;
context->current_client = client;
/* when the client is busy processing an async action, stop processing messages
* for the client until it finishes the action */
while (!data->busy) {
const struct pw_protocol_native_demarshal *demarshal;
const struct pw_protocol_marshal *marshal;
uint32_t permissions, required;
protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node.
2019-10-08 22:52:25 +02:00
res = pw_protocol_native_connection_get_next(conn, &msg);
if (res < 0) {
if (res == -EAGAIN)
break;
goto error;
}
if (res == 0)
break;
protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node.
2019-10-08 22:52:25 +02:00
if (client->core_resource == NULL) {
res = -EPROTO;
goto error;
}
client->recv_seq = msg->seq;
2019-08-14 11:44:16 +02:00
pw_log_trace(NAME" %p: got message %d from %u", client->protocol,
msg->opcode, msg->id);
if (debug_messages) {
2019-07-17 15:29:18 +02:00
fprintf(stderr, "<<<<<<<<< in: id:%d op:%d size:%d seq:%d\n",
msg->id, msg->opcode, msg->size, msg->seq);
spa_debug_pod(0, NULL, (struct spa_pod *)msg->data);
}
2019-12-11 11:21:43 +01:00
resource = pw_impl_client_find_resource(client, msg->id);
if (resource == NULL) {
2019-08-14 11:44:16 +02:00
pw_log_error(NAME" %p: unknown resource %u op:%u",
2019-07-17 15:29:18 +02:00
client->protocol, msg->id, msg->opcode);
pw_resource_errorf(client->core_resource,
2019-07-17 15:29:18 +02:00
-EINVAL, "unknown resource %u op:%u", msg->id, msg->opcode);
continue;
}
marshal = pw_resource_get_marshal(resource);
if (marshal == NULL || msg->opcode >= marshal->n_client_methods) {
res = -EINVAL;
goto invalid_method;
}
demarshal = marshal->server_demarshal;
2019-07-17 15:29:18 +02:00
if (!demarshal[msg->opcode].func) {
res = -ENOENT;
goto invalid_message;
2019-07-17 15:29:18 +02:00
}
permissions = pw_resource_get_permissions(resource);
required = demarshal[msg->opcode].permissions | PW_PERM_X;
if ((required & permissions) != required) {
2019-08-14 11:44:16 +02:00
pw_log_error(NAME" %p: method %u on %u requires %08x, have %08x",
client->protocol, msg->opcode, msg->id, required, permissions);
pw_resource_errorf(resource,
-EACCES, "no permission to call method %u on %u", msg->opcode, msg->id);
continue;
}
2019-07-17 15:29:18 +02:00
if ((res = demarshal[msg->opcode].func(resource, msg)) < 0)
goto invalid_message;
}
res = 0;
2019-06-20 11:04:34 +02:00
done:
context->current_client = NULL;
return res;
2019-06-20 11:04:34 +02:00
invalid_method:
2019-08-14 11:44:16 +02:00
pw_log_error(NAME" %p: invalid method id:%u op:%u",
2019-07-17 15:29:18 +02:00
client->protocol, msg->id, msg->opcode);
pw_resource_errorf(resource, res, "invalid method id:%u op:%u",
2019-07-17 15:29:18 +02:00
msg->id, msg->opcode);
goto done;
2019-06-20 11:04:34 +02:00
invalid_message:
2019-08-14 11:44:16 +02:00
pw_log_error(NAME" %p: invalid message received id:%u op:%u (%s)",
2019-07-17 15:29:18 +02:00
client->protocol, msg->id, msg->opcode, spa_strerror(res));
pw_resource_errorf(resource, res, "invalid message received id:%u op:%u (%s)",
2019-07-17 15:29:18 +02:00
msg->id, msg->opcode, spa_strerror(res));
spa_debug_pod(0, NULL, (struct spa_pod *)msg->data);
goto done;
protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node.
2019-10-08 22:52:25 +02:00
error:
pw_log_error(NAME" %p: client error (%s)", client->protocol, spa_strerror(res));
goto done;
}
static void
client_busy_changed(void *data, bool busy)
{
struct client_data *c = data;
2019-12-11 11:21:43 +01:00
struct pw_impl_client *client = c->client;
uint32_t mask = c->source->mask;
c->busy = busy;
SPA_FLAG_UPDATE(mask, SPA_IO_IN, !busy);
2019-08-14 11:44:16 +02:00
pw_log_debug(NAME" %p: busy changed %d", client->protocol, busy);
pw_loop_update_io(client->context->main_loop, c->source, mask);
if (!busy)
process_messages(c);
}
static void
connection_data(void *data, int fd, uint32_t mask)
{
struct client_data *this = data;
2019-12-11 11:21:43 +01:00
struct pw_impl_client *client = this->client;
int res;
2017-05-26 08:05:01 +02:00
if (mask & SPA_IO_HUP) {
res = -EPIPE;
goto error;
2017-05-26 08:05:01 +02:00
}
if (mask & SPA_IO_ERR) {
res = -EIO;
goto error;
}
if (mask & SPA_IO_OUT) {
res = pw_protocol_native_connection_flush(this->connection);
if (res >= 0) {
int mask = this->source->mask;
SPA_FLAG_CLEAR(mask, SPA_IO_OUT);
pw_loop_update_io(client->context->main_loop,
this->source, mask);
} else if (res != EAGAIN)
goto error;
}
if (mask & SPA_IO_IN) {
if ((res = process_messages(this)) < 0)
goto error;
}
return;
error:
if (res == -EPIPE)
pw_log_info(NAME" %p: client %p disconnected", client->protocol, client);
else
pw_log_error(NAME" %p: client %p error %d (%s)", client->protocol,
client, res, spa_strerror(res));
pw_impl_client_destroy(client);
}
static void client_free(void *data)
{
struct client_data *this = data;
2019-12-11 11:21:43 +01:00
struct pw_impl_client *client = this->client;
spa_list_remove(&this->protocol_link);
if (this->source)
pw_loop_destroy_source(client->context->main_loop, this->source);
if (this->connection)
pw_protocol_native_connection_destroy(this->connection);
protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node.
2019-10-08 22:52:25 +02:00
pw_map_clear(&this->compat_v2.types);
}
2019-12-11 11:21:43 +01:00
static const struct pw_impl_client_events client_events = {
PW_VERSION_IMPL_CLIENT_EVENTS,
.free = client_free,
.busy_changed = client_busy_changed,
};
protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node.
2019-10-08 22:52:25 +02:00
static void on_start(void *data, uint32_t version)
{
struct client_data *this = data;
2019-12-11 11:21:43 +01:00
struct pw_impl_client *client = this->client;
protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node.
2019-10-08 22:52:25 +02:00
pw_log_debug("version %d", version);
if (pw_global_bind(pw_impl_core_get_global(client->core), client,
protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node.
2019-10-08 22:52:25 +02:00
PW_PERM_RWX, version, 0) < 0)
return;
if (version == 0)
client->compat_v2 = &this->compat_v2;
return;
}
static const struct pw_protocol_native_connection_events server_conn_events = {
PW_VERSION_PROTOCOL_NATIVE_CONNECTION_EVENTS,
.start = on_start,
};
static struct client_data *client_new(struct server *s, int fd)
{
struct client_data *this;
2019-12-11 11:21:43 +01:00
struct pw_impl_client *client;
struct pw_protocol *protocol = s->this.protocol;
2017-05-26 08:05:01 +02:00
socklen_t len;
struct ucred ucred;
struct pw_context *context = protocol->context;
2017-09-04 20:19:27 +02:00
struct pw_properties *props;
char buffer[1024];
struct protocol_data *d = pw_protocol_get_user_data(protocol);
int res;
props = pw_properties_new(PW_KEY_PROTOCOL, "protocol-native", NULL);
if (props == NULL)
goto exit;
2017-05-26 08:05:01 +02:00
#ifndef __FreeBSD__
len = sizeof(ucred);
if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) {
2019-08-14 11:44:16 +02:00
pw_log_error("server %p: no peercred: %m", s);
} else {
pw_properties_setf(props, PW_KEY_SEC_PID, "%d", ucred.pid);
pw_properties_setf(props, PW_KEY_SEC_UID, "%d", ucred.uid);
pw_properties_setf(props, PW_KEY_SEC_GID, "%d", ucred.gid);
}
len = sizeof(buffer);
if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, buffer, &len) < 0) {
pw_log_warn("server %p: no peersec: %m", s);
} else {
pw_properties_setf(props, PW_KEY_SEC_LABEL, "%s", buffer);
}
#endif
2017-09-04 20:19:27 +02:00
pw_properties_setf(props, PW_KEY_MODULE_ID, "%d", d->module->global->id);
client = pw_context_create_client(s->this.core,
protocol, props, sizeof(struct client_data));
if (client == NULL)
goto exit;
2017-05-26 08:05:01 +02:00
2019-12-11 11:21:43 +01:00
this = pw_impl_client_get_user_data(client);
spa_list_append(&s->this.client_list, &this->protocol_link);
this->client = client;
this->source = pw_loop_add_io(pw_context_get_main_loop(context),
fd, SPA_IO_ERR | SPA_IO_HUP, true,
connection_data, this);
if (this->source == NULL) {
res = -errno;
goto cleanup_client;
}
2017-05-26 08:05:01 +02:00
this->connection = pw_protocol_native_connection_new(protocol->context, fd);
if (this->connection == NULL) {
res = -errno;
goto cleanup_client;
}
2017-05-26 08:05:01 +02:00
protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node.
2019-10-08 22:52:25 +02:00
pw_map_init(&this->compat_v2.types, 0, 32);
2017-05-26 08:05:01 +02:00
protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node.
2019-10-08 22:52:25 +02:00
pw_protocol_native_connection_add_listener(this->connection,
&this->conn_listener,
&server_conn_events,
this);
2019-12-11 11:21:43 +01:00
pw_impl_client_add_listener(client, &this->client_listener, &client_events, this);
2019-12-11 11:21:43 +01:00
if ((res = pw_impl_client_register(client, NULL)) < 0)
goto cleanup_client;
if (!client->busy)
pw_loop_update_io(pw_context_get_main_loop(context),
this->source, this->source->mask | SPA_IO_IN);
return this;
2017-05-26 08:05:01 +02:00
2019-06-20 11:04:34 +02:00
cleanup_client:
2019-12-11 11:21:43 +01:00
pw_impl_client_destroy(client);
errno = -res;
2019-06-20 11:04:34 +02:00
exit:
2017-05-26 08:05:01 +02:00
return NULL;
}
2019-06-19 10:59:00 +02:00
static int init_socket_name(struct server *s, const char *name)
{
2017-05-26 08:05:01 +02:00
int name_size;
const char *runtime_dir;
2019-12-06 17:54:05 +01:00
bool path_is_absolute;
2017-05-26 08:05:01 +02:00
2019-12-06 17:54:05 +01:00
path_is_absolute = name[0] == '/';
runtime_dir = getenv("XDG_RUNTIME_DIR");
if (runtime_dir == NULL && !path_is_absolute) {
2019-08-14 11:44:16 +02:00
pw_log_error("server %p: XDG_RUNTIME_DIR not set in the environment", s);
2019-12-06 17:54:05 +01:00
return -ENOENT;
2017-05-26 08:05:01 +02:00
}
s->addr.sun_family = AF_LOCAL;
2019-12-06 17:54:05 +01:00
if (path_is_absolute)
name_size = snprintf(s->addr.sun_path, sizeof(s->addr.sun_path),
"%s", name) + 1;
else
name_size = snprintf(s->addr.sun_path, sizeof(s->addr.sun_path),
2017-05-26 08:05:01 +02:00
"%s/%s", runtime_dir, name) + 1;
if (name_size > (int) sizeof(s->addr.sun_path)) {
2019-12-06 17:54:05 +01:00
if (path_is_absolute)
pw_log_error("server %p: socket path \"%s\" plus null terminator exceeds %i bytes",
s, name, (int) sizeof(s->addr.sun_path));
else
pw_log_error("server %p: socket path \"%s/%s\" plus null terminator exceeds %i bytes",
s, runtime_dir, name, (int) sizeof(s->addr.sun_path));
*s->addr.sun_path = 0;
2019-06-19 10:59:00 +02:00
return -ENAMETOOLONG;
2017-05-26 08:05:01 +02:00
}
2019-06-19 10:59:00 +02:00
return 0;
}
2019-06-19 10:59:00 +02:00
static int lock_socket(struct server *s)
{
2019-06-19 10:59:00 +02:00
int res;
snprintf(s->lock_addr, sizeof(s->lock_addr), "%s%s", s->addr.sun_path, LOCK_SUFFIX);
2017-05-26 08:05:01 +02:00
s->fd_lock = open(s->lock_addr, O_CREAT | O_CLOEXEC,
2017-05-26 08:05:01 +02:00
(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP));
if (s->fd_lock < 0) {
2019-06-19 10:59:00 +02:00
res = -errno;
2019-08-14 11:44:16 +02:00
pw_log_error("server %p: unable to open lockfile '%s': %m", s, s->lock_addr);
2017-05-26 08:05:01 +02:00
goto err;
}
if (flock(s->fd_lock, LOCK_EX | LOCK_NB) < 0) {
2019-06-19 10:59:00 +02:00
res = -errno;
2019-08-14 11:44:16 +02:00
pw_log_error("server %p: unable to lock lockfile '%s': %m"
" (maybe another daemon is running)",
s, s->lock_addr);
2017-05-26 08:05:01 +02:00
goto err_fd;
}
2019-06-19 10:59:00 +02:00
return 0;
2017-05-26 08:05:01 +02:00
2019-06-20 11:04:34 +02:00
err_fd:
close(s->fd_lock);
s->fd_lock = -1;
2019-06-20 11:04:34 +02:00
err:
*s->lock_addr = 0;
*s->addr.sun_path = 0;
2019-06-19 10:59:00 +02:00
return res;
}
static void
socket_data(void *data, int fd, uint32_t mask)
{
struct server *s = data;
struct client_data *client;
2017-05-26 08:05:01 +02:00
struct sockaddr_un name;
socklen_t length;
int client_fd;
length = sizeof(name);
client_fd = accept4(fd, (struct sockaddr *) &name, &length, SOCK_CLOEXEC);
if (client_fd < 0) {
2019-08-14 11:44:16 +02:00
pw_log_error("server %p: failed to accept: %m", s);
2017-05-26 08:05:01 +02:00
return;
}
client = client_new(s, client_fd);
2017-05-26 08:05:01 +02:00
if (client == NULL) {
2019-08-14 11:44:16 +02:00
pw_log_error("server %p: failed to create client", s);
2017-05-26 08:05:01 +02:00
close(client_fd);
return;
}
}
2019-06-19 10:59:00 +02:00
static int add_socket(struct pw_protocol *protocol, struct server *s)
{
2017-05-26 08:05:01 +02:00
socklen_t size;
2019-06-19 10:59:00 +02:00
int fd = -1, res;
2018-10-18 09:50:43 +02:00
bool activated = false;
#ifdef HAVE_SYSTEMD_DAEMON
{
int i, n = sd_listen_fds(0);
for (i = 0; i < n; ++i) {
if (sd_is_socket_unix(SD_LISTEN_FDS_START + i, SOCK_STREAM,
1, s->addr.sun_path, 0) > 0) {
fd = SD_LISTEN_FDS_START + i;
activated = true;
2019-08-14 11:44:16 +02:00
pw_log_info("server %p: Found socket activation socket for '%s'",
s, s->addr.sun_path);
2018-10-18 09:50:43 +02:00
break;
}
}
}
#endif
2017-05-26 08:05:01 +02:00
2018-10-18 09:50:43 +02:00
if (fd < 0) {
struct stat socket_stat;
2019-06-19 10:59:00 +02:00
if ((fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0) {
res = -errno;
2018-10-18 09:50:43 +02:00
goto error;
2019-06-19 10:59:00 +02:00
}
if (stat(s->addr.sun_path, &socket_stat) < 0) {
if (errno != ENOENT) {
res = -errno;
pw_log_error("server %p: stat %s failed with error: %m",
s, s->addr.sun_path);
goto error_close;
}
} else if (socket_stat.st_mode & S_IWUSR || socket_stat.st_mode & S_IWGRP) {
unlink(s->addr.sun_path);
}
2017-05-26 08:05:01 +02:00
2018-10-18 09:50:43 +02:00
size = offsetof(struct sockaddr_un, sun_path) + strlen(s->addr.sun_path);
if (bind(fd, (struct sockaddr *) &s->addr, size) < 0) {
2019-06-19 10:59:00 +02:00
res = -errno;
2019-08-14 11:44:16 +02:00
pw_log_error("server %p: bind() failed with error: %m", s);
2018-10-18 09:50:43 +02:00
goto error_close;
}
2017-05-26 08:05:01 +02:00
2018-10-18 09:50:43 +02:00
if (listen(fd, 128) < 0) {
2019-06-19 10:59:00 +02:00
res = -errno;
2019-08-14 11:44:16 +02:00
pw_log_error("server %p: listen() failed with error: %m", s);
2018-10-18 09:50:43 +02:00
goto error_close;
}
2017-05-26 08:05:01 +02:00
}
2019-06-19 10:59:00 +02:00
s->activated = activated;
s->loop = pw_context_get_main_loop(protocol->context);
2019-06-19 10:59:00 +02:00
if (s->loop == NULL) {
res = -errno;
goto error_close;
}
s->source = pw_loop_add_io(s->loop, fd, SPA_IO_IN, true, socket_data, s);
2019-06-19 10:59:00 +02:00
if (s->source == NULL) {
res = -errno;
goto error_close;
2019-06-19 10:59:00 +02:00
}
return 0;
2019-06-20 11:04:34 +02:00
error_close:
close(fd);
2019-06-20 11:04:34 +02:00
error:
2019-06-19 10:59:00 +02:00
return res;
}
static int impl_steal_fd(struct pw_protocol_client *client)
{
struct client *impl = SPA_CONTAINER_OF(client, struct client, this);
int fd;
if (impl->source == NULL)
return -EIO;
fd = dup(impl->source->fd);
if (fd < 0)
return -errno;
pw_protocol_client_disconnect(client);
return fd;
}
static int
process_remote(struct client *impl)
{
const struct pw_protocol_native_message *msg;
struct pw_protocol_native_connection *conn = impl->connection;
struct pw_core *this = impl->this.core;
int res = 0;
while (!impl->disconnecting && !impl->paused) {
struct pw_proxy *proxy;
const struct pw_protocol_native_demarshal *demarshal;
const struct pw_protocol_marshal *marshal;
res = pw_protocol_native_connection_get_next(conn, &msg);
if (res < 0) {
if (res == -EAGAIN)
break;
return res;
}
if (res == 0)
break;
pw_log_trace(NAME" %p: got message %d from %u seq:%d",
this, msg->opcode, msg->id, msg->seq);
this->recv_seq = msg->seq;
if (debug_messages) {
fprintf(stderr, "<<<<<<<<< in: id:%d op:%d size:%d seq:%d\n",
msg->id, msg->opcode, msg->size, msg->seq);
spa_debug_pod(0, NULL, (struct spa_pod *)msg->data);
}
proxy = pw_core_find_proxy(this, msg->id);
if (proxy == NULL || proxy->zombie) {
if (proxy == NULL)
pw_log_error(NAME" %p: could not find proxy %u", this, msg->id);
else
pw_log_debug(NAME" %p: zombie proxy %u", this, msg->id);
/* FIXME close fds */
continue;
}
marshal = pw_proxy_get_marshal(proxy);
if (marshal == NULL || msg->opcode >= marshal->n_server_methods) {
pw_log_error(NAME" %p: invalid method %u for %u (%d)",
this, msg->opcode, msg->id,
marshal ? marshal->n_server_methods : (uint32_t)-1);
continue;
}
demarshal = marshal->client_demarshal;
if (!demarshal[msg->opcode].func) {
pw_log_error(NAME" %p: function %d not implemented on %u",
this, msg->opcode, msg->id);
continue;
}
proxy->refcount++;
res = demarshal[msg->opcode].func(proxy, msg);
pw_proxy_unref(proxy);
if (res < 0) {
pw_log_error (NAME" %p: invalid message received %u for %u",
this, msg->opcode, msg->id);
continue;
}
}
return 0;
}
static void
on_remote_data(void *data, int fd, uint32_t mask)
{
struct client *impl = data;
struct pw_core *this = impl->this.core;
struct pw_protocol_native_connection *conn = impl->connection;
struct pw_context *context = pw_core_get_context(this);
struct pw_loop *loop = pw_context_get_main_loop(context);
int res;
if (mask & (SPA_IO_ERR | SPA_IO_HUP)) {
res = -EPIPE;
goto error;
}
if (mask & SPA_IO_OUT) {
res = pw_protocol_native_connection_flush(conn);
if (res >= 0) {
int mask = impl->source->mask;
SPA_FLAG_CLEAR(mask, SPA_IO_OUT);
pw_loop_update_io(loop,
impl->source, mask);
impl->flushing = false;
} else if (res != EAGAIN)
goto error;
}
if (mask & SPA_IO_IN) {
if ((res = process_remote(impl)) < 0)
goto error;
}
return;
error:
2019-08-14 11:44:16 +02:00
pw_log_error(NAME" %p: got connection error %d (%s)", impl, res, spa_strerror(res));
pw_proxy_notify((struct pw_proxy*)this,
struct pw_core_events, error, 0, 0,
this->recv_seq, res, "connection error");
pw_loop_destroy_source(loop, impl->source);
impl->source = NULL;
}
static void on_need_flush(void *data)
{
struct client *impl = data;
if (!impl->flushing && impl->source) {
int mask = impl->source->mask;
impl->flushing = true;
SPA_FLAG_SET(mask, SPA_IO_OUT);
pw_loop_update_io(impl->context->main_loop,
impl->source, mask);
}
}
protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node.
2019-10-08 22:52:25 +02:00
static const struct pw_protocol_native_connection_events client_conn_events = {
PW_VERSION_PROTOCOL_NATIVE_CONNECTION_EVENTS,
.need_flush = on_need_flush,
};
static int impl_connect_fd(struct pw_protocol_client *client, int fd, bool do_close)
{
struct client *impl = SPA_CONTAINER_OF(client, struct client, this);
2019-06-19 10:59:00 +02:00
int res;
impl->disconnecting = false;
pw_protocol_native_connection_set_fd(impl->connection, fd);
impl->flushing = true;
impl->source = pw_loop_add_io(impl->context->main_loop,
2019-06-19 10:59:00 +02:00
fd,
SPA_IO_IN | SPA_IO_OUT | SPA_IO_HUP | SPA_IO_ERR,
2019-06-19 10:59:00 +02:00
do_close, on_remote_data, impl);
if (impl->source == NULL) {
res = -errno;
goto error_cleanup;
}
pw_protocol_native_connection_add_listener(impl->connection,
&impl->conn_listener,
protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node.
2019-10-08 22:52:25 +02:00
&client_conn_events,
impl);
return 0;
2019-06-19 10:59:00 +02:00
error_cleanup:
if (impl->connection) {
pw_protocol_native_connection_destroy(impl->connection);
impl->connection = NULL;
}
return res;
}
static void impl_disconnect(struct pw_protocol_client *client)
{
struct client *impl = SPA_CONTAINER_OF(client, struct client, this);
impl->disconnecting = true;
if (impl->source)
pw_loop_destroy_source(impl->context->main_loop, impl->source);
impl->source = NULL;
if (impl->connection)
pw_protocol_native_connection_destroy(impl->connection);
impl->connection = NULL;
}
static void impl_destroy(struct pw_protocol_client *client)
{
struct client *impl = SPA_CONTAINER_OF(client, struct client, this);
impl_disconnect(client);
spa_list_remove(&client->link);
free(impl);
}
static int impl_set_paused(struct pw_protocol_client *client, bool paused)
{
struct client *impl = SPA_CONTAINER_OF(client, struct client, this);
uint32_t mask;
if (impl->source == NULL)
return -EIO;
mask = impl->source->mask;
impl->paused = paused;
SPA_FLAG_UPDATE(mask, SPA_IO_IN, !paused);
pw_log_debug(NAME" %p: paused %d", client->protocol, paused);
pw_loop_update_io(impl->context->main_loop, impl->source, mask);
return paused ? 0 : process_remote(impl);
}
static int pw_protocol_native_connect_internal(struct pw_protocol_client *client,
const struct spa_dict *props,
void (*done_callback) (void *data, int res),
void *data)
{
int res, sv[2];
struct pw_protocol *protocol = client->protocol;
struct protocol_data *d = pw_protocol_get_user_data(protocol);
struct server *s = d->local;
struct pw_permission permissions[1];
struct client_data *c;
pw_log_debug("server %p: internal connect", s);
if (socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, sv) < 0) {
res = -errno;
pw_log_error("server %p: socketpair() failed with error: %m", s);
goto error;
}
c = client_new(s, sv[0]);
if (c == NULL) {
res = -errno;
pw_log_error("server %p: failed to create client: %m", s);
goto error_close;
}
permissions[0] = PW_PERMISSION_INIT(PW_ID_ANY, PW_PERM_RWX);
2019-12-11 11:21:43 +01:00
pw_impl_client_update_permissions(c->client, 1, permissions);
res = pw_protocol_client_connect_fd(client, sv[1], true);
done:
if (done_callback)
done_callback(data, res);
return res;
error_close:
close(sv[0]);
close(sv[1]);
error:
goto done;
}
static struct pw_protocol_client *
impl_new_client(struct pw_protocol *protocol,
struct pw_core *core,
const struct spa_dict *props)
{
struct client *impl;
struct pw_protocol_client *this;
const char *str = NULL;
int res;
if ((impl = calloc(1, sizeof(struct client))) == NULL)
return NULL;
pw_log_debug(NAME" %p: new client %p", protocol, impl);
this = &impl->this;
this->protocol = protocol;
this->core = core;
impl->context = protocol->context;
impl->connection = pw_protocol_native_connection_new(protocol->context, -1);
if (impl->connection == NULL) {
res = -errno;
goto error_free;
}
if (props) {
str = spa_dict_lookup(props, PW_KEY_REMOTE_INTENTION);
if (str == NULL &&
(str = spa_dict_lookup(props, PW_KEY_REMOTE_NAME)) != NULL &&
strcmp(str, "internal") == 0)
str = "internal";
}
if (str == NULL)
str = "generic";
pw_log_debug(NAME" %p: connect %s", protocol, str);
if (!strcmp(str, "screencast"))
this->connect = pw_protocol_native_connect_portal_screencast;
else if (!strcmp(str, "internal"))
this->connect = pw_protocol_native_connect_internal;
else
this->connect = pw_protocol_native_connect_local_socket;
this->steal_fd = impl_steal_fd;
this->connect_fd = impl_connect_fd;
this->disconnect = impl_disconnect;
this->destroy = impl_destroy;
this->set_paused = impl_set_paused;
spa_list_append(&protocol->client_list, &this->link);
return this;
error_free:
free(impl);
errno = -res;
return NULL;
}
static void destroy_server(struct pw_protocol_server *server)
{
struct server *s = SPA_CONTAINER_OF(server, struct server, this);
struct client_data *data, *tmp;
spa_list_remove(&server->link);
2019-06-19 10:59:00 +02:00
spa_hook_remove(&s->hook);
spa_list_for_each_safe(data, tmp, &server->client_list, protocol_link)
pw_impl_client_destroy(data->client);
if (s->source) {
spa_hook_remove(&s->hook);
pw_loop_destroy_source(s->loop, s->source);
}
2018-10-18 09:50:43 +02:00
if (s->addr.sun_path[0] && !s->activated)
unlink(s->addr.sun_path);
if (s->lock_addr[0])
unlink(s->lock_addr);
if (s->fd_lock != -1)
close(s->fd_lock);
free(s);
}
static void on_before_hook(void *_data)
{
struct server *server = _data;
struct pw_protocol_server *this = &server->this;
struct client_data *data, *tmp;
int res;
spa_list_for_each_safe(data, tmp, &this->client_list, protocol_link) {
res = pw_protocol_native_connection_flush(data->connection);
if (res == -EAGAIN) {
int mask = data->source->mask;
SPA_FLAG_SET(mask, SPA_IO_OUT);
pw_loop_update_io(data->client->context->main_loop,
data->source, mask);
} else if (res < 0) {
pw_log_warn("client %p: could not flush: %s",
data->client, spa_strerror(res));
pw_impl_client_destroy(data->client);
}
}
}
static const struct spa_loop_control_hooks impl_hooks = {
SPA_VERSION_LOOP_CONTROL_HOOKS,
.before = on_before_hook,
};
static const char *
get_name(const struct spa_dict *props)
{
const char *name = NULL;
if (props)
name = spa_dict_lookup(props, PW_KEY_CORE_NAME);
if (name == NULL)
name = getenv("PIPEWIRE_CORE");
if (name == NULL)
name = "pipewire-0";
return name;
}
static struct server *
create_server(struct pw_protocol *protocol,
struct pw_impl_core *core,
const struct spa_dict *props)
{
struct pw_protocol_server *this;
struct pw_context *context = protocol->context;
struct server *s;
if ((s = calloc(1, sizeof(struct server))) == NULL)
return NULL;
s->fd_lock = -1;
this = &s->this;
this->protocol = protocol;
this->core = core;
spa_list_init(&this->client_list);
this->destroy = destroy_server;
spa_list_append(&protocol->server_list, &this->link);
pw_loop_add_hook(pw_context_get_main_loop(context), &s->hook, &impl_hooks, s);
pw_log_info(NAME" %p: created server %p", protocol, this);
return s;
}
static struct pw_protocol_server *
impl_add_server(struct pw_protocol *protocol,
struct pw_impl_core *core,
const struct spa_dict *props)
{
struct pw_protocol_server *this;
struct server *s;
const char *name;
int res;
if ((s = create_server(protocol, core, props)) == NULL)
return NULL;
this = &s->this;
name = get_name(props);
2019-06-19 10:59:00 +02:00
if ((res = init_socket_name(s, name)) < 0)
2017-05-26 08:05:01 +02:00
goto error;
2019-06-19 10:59:00 +02:00
if ((res = lock_socket(s)) < 0)
2017-05-26 08:05:01 +02:00
goto error;
2019-06-19 10:59:00 +02:00
if ((res = add_socket(protocol, s)) < 0)
goto error;
pw_log_info(NAME" %p: Listening on '%s'", protocol, name);
return this;
2019-06-19 10:59:00 +02:00
error:
destroy_server(this);
2019-06-19 10:59:00 +02:00
errno = -res;
2017-05-26 08:05:01 +02:00
return NULL;
}
const static struct pw_protocol_implementaton protocol_impl = {
PW_VERSION_PROTOCOL_IMPLEMENTATION,
.new_client = impl_new_client,
.add_server = impl_add_server,
};
static struct spa_pod_builder *
impl_ext_begin_proxy(struct pw_proxy *proxy, uint8_t opcode, struct pw_protocol_native_message **msg)
{
struct client *impl = SPA_CONTAINER_OF(proxy->core->conn, struct client, this);
return pw_protocol_native_connection_begin(impl->connection, proxy->id, opcode, msg);
}
static uint32_t impl_ext_add_proxy_fd(struct pw_proxy *proxy, int fd)
{
struct client *impl = SPA_CONTAINER_OF(proxy->core->conn, struct client, this);
return pw_protocol_native_connection_add_fd(impl->connection, fd);
}
static int impl_ext_get_proxy_fd(struct pw_proxy *proxy, uint32_t index)
{
struct client *impl = SPA_CONTAINER_OF(proxy->core->conn, struct client, this);
return pw_protocol_native_connection_get_fd(impl->connection, index);
}
static int impl_ext_end_proxy(struct pw_proxy *proxy,
struct spa_pod_builder *builder)
{
struct pw_core *core = proxy->core;
struct client *impl = SPA_CONTAINER_OF(core->conn, struct client, this);
return core->send_seq = pw_protocol_native_connection_end(impl->connection, builder);
}
static struct spa_pod_builder *
impl_ext_begin_resource(struct pw_resource *resource,
uint8_t opcode, struct pw_protocol_native_message **msg)
{
struct client_data *data = resource->client->user_data;
return pw_protocol_native_connection_begin(data->connection, resource->id, opcode, msg);
}
static uint32_t impl_ext_add_resource_fd(struct pw_resource *resource, int fd)
{
struct client_data *data = resource->client->user_data;
return pw_protocol_native_connection_add_fd(data->connection, fd);
}
static int impl_ext_get_resource_fd(struct pw_resource *resource, uint32_t index)
{
struct client_data *data = resource->client->user_data;
return pw_protocol_native_connection_get_fd(data->connection, index);
}
static int impl_ext_end_resource(struct pw_resource *resource,
struct spa_pod_builder *builder)
{
struct client_data *data = resource->client->user_data;
2019-12-11 11:21:43 +01:00
struct pw_impl_client *client = resource->client;
return client->send_seq = pw_protocol_native_connection_end(data->connection, builder);
}
const static struct pw_protocol_native_ext protocol_ext_impl = {
PW_VERSION_PROTOCOL_NATIVE_EXT,
.begin_proxy = impl_ext_begin_proxy,
.add_proxy_fd = impl_ext_add_proxy_fd,
.get_proxy_fd = impl_ext_get_proxy_fd,
.end_proxy = impl_ext_end_proxy,
.begin_resource = impl_ext_begin_resource,
.add_resource_fd = impl_ext_add_resource_fd,
.get_resource_fd = impl_ext_get_resource_fd,
.end_resource = impl_ext_end_resource,
};
static void module_destroy(void *data)
{
struct protocol_data *d = data;
spa_hook_remove(&d->module_listener);
pw_protocol_destroy(d->protocol);
}
2019-12-11 11:57:56 +01:00
static const struct pw_impl_module_events module_events = {
PW_VERSION_IMPL_MODULE_EVENTS,
.destroy = module_destroy,
};
static int need_server(struct pw_context *context, const struct spa_dict *props)
{
const char *val;
val = getenv("PIPEWIRE_DAEMON");
if (val == NULL)
val = spa_dict_lookup(props, PW_KEY_CORE_DAEMON);
if (val && pw_properties_parse_bool(val))
return 1;
return 0;
}
SPA_EXPORT
2019-12-11 11:57:56 +01:00
int pipewire__module_init(struct pw_impl_module *module, const char *args)
{
2019-12-11 11:57:56 +01:00
struct pw_context *context = pw_impl_module_get_context(module);
struct pw_protocol *this;
struct protocol_data *d;
const struct pw_properties *props;
2019-06-19 10:59:00 +02:00
int res;
if (pw_context_find_protocol(context, PW_TYPE_INFO_PROTOCOL_Native) != NULL)
return 0;
this = pw_protocol_new(context, PW_TYPE_INFO_PROTOCOL_Native, sizeof(struct protocol_data));
if (this == NULL)
2019-06-19 10:59:00 +02:00
return -errno;
debug_messages = pw_debug_is_category_enabled("connection");
this->implementation = &protocol_impl;
this->extension = &protocol_ext_impl;
pw_protocol_native_init(this);
protocol: add v0 compatibility For flatpaks we need to be able to support older v0 protocol clients. To handle this we have: - the connection detects an old client when it receives the first message. It can do this by checking the sequence number, on old versions it contains the message size and is never 0, on new clients the sequence number is 0. - We add a new signal at the start of the connection with the detected version number. This installs the right version of the core proxy. We also move the binding of the client until the hello message is received. This way we can have a new client connect (portal), hand over the connection to an old client, which then removes the client binding again in the hello request with a v0 version. There are some changes to the passing of fds in v0 vs v3 which need to investigated some more. - bump version of our interfaces to 3. This makes it possible to have v0 and v3 protocol marshal functions. - Add version number in the proxy. This is mostly automatically done internally based on the version numbers the library is compiled with. Where the version number was in the API before, it is now actually used to look up the right protocol marshal functions. For Proxies there is usually just 1 version, the current one. It is the server that will support different versions. - Add v0 compat marshal functions to convert from and to v0 format. This has some complications. v0 has a type map it keeps in sync with the server. For this we have a static type map with mappings to our own v3 types. Pods are mostly the same except for objects that used to have arbitrary pods in v0 vs spa_pod_prop in v3. Also convert between v0 spa_pod_prop and v3 spa_pod_choice. Formats and commands are also slightly different so handle those mappings as well. We only have marshal functions for the server side (resource) v0 functions. - Add v0 compatible client-node again. It's a bit tricky to map, v0 client-node basically lets the server to the mixing and teeing and just does the processing of the internal node.
2019-10-08 22:52:25 +02:00
pw_protocol_native0_init(this);
pw_log_debug(NAME" %p: new debug:%d", this, debug_messages);
d = pw_protocol_get_user_data(this);
d->protocol = this;
d->module = module;
props = pw_context_get_properties(context);
d->local = create_server(this, context->core, &props->dict);
if (need_server(context, &props->dict)) {
if (impl_add_server(this, context->core, &props->dict) == NULL) {
2019-06-19 10:59:00 +02:00
res = -errno;
goto error_cleanup;
}
}
2019-12-11 11:57:56 +01:00
pw_impl_module_add_listener(module, &d->module_listener, &module_events, d);
2019-12-11 11:57:56 +01:00
pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));
2018-06-01 11:23:02 +02:00
return 0;
2019-06-19 10:59:00 +02:00
error_cleanup:
pw_protocol_destroy(this);
return res;
}