diff --git a/src/modules/meson.build b/src/modules/meson.build index 6d5d4fdbc..c27a391f9 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -197,6 +197,7 @@ pipewire_module_protocol_native = shared_library('pipewire-module-protocol-nativ 'module-protocol-native/portal-screencast.c', 'module-protocol-native/protocol-native.c', 'module-protocol-native/v0/protocol-native.c', + 'module-protocol-native/protocol-footer.c', 'module-protocol-native/connection.c' ], include_directories : [configinc], install : true, diff --git a/src/modules/module-protocol-native.c b/src/modules/module-protocol-native.c index b2874b592..6db586e50 100644 --- a/src/modules/module-protocol-native.c +++ b/src/modules/module-protocol-native.c @@ -57,6 +57,7 @@ #include "modules/module-protocol-native/connection.h" #include "modules/module-protocol-native/defs.h" +#include "modules/module-protocol-native/protocol-footer.h" #define NAME "protocol-native" @@ -115,6 +116,8 @@ struct client { int ref; + struct footer_proxy_global_state footer_state; + unsigned int connected:1; unsigned int disconnecting:1; unsigned int need_flush:1; @@ -151,6 +154,8 @@ struct client_data { struct pw_protocol_native_connection *connection; struct spa_hook conn_listener; + struct footer_resource_global_state footer_state; + unsigned int busy:1; unsigned int need_flush:1; @@ -164,7 +169,7 @@ static void debug_msg(const char *prefix, const struct pw_protocol_native_messag "%s: id:%d op:%d size:%d seq:%d", prefix, msg->id, msg->opcode, msg->size, msg->seq); - if ((pod = spa_pod_from_data(msg->data, msg->size, 0, msg->size)) != NULL) + if ((pod = get_first_pod_from_data(msg->data, msg->size, 0)) != NULL) spa_debug_pod(0, NULL, pod); else hex = true; @@ -172,6 +177,49 @@ static void debug_msg(const char *prefix, const struct pw_protocol_native_messag spa_debug_mem(0, msg->data, msg->size); } +static void pre_demarshal(struct pw_protocol_native_connection *conn, + const struct pw_protocol_native_message *msg, + void *object, const struct footer_demarshal *opcodes, size_t n_opcodes) +{ + struct spa_pod *footer = NULL; + struct spa_pod_parser parser; + struct spa_pod_frame f[2]; + uint32_t opcode; + int ret; + + footer = pw_protocol_native_connection_get_footer(conn, msg); + if (footer == NULL) + return; /* No valid footer. Ignore silently. */ + + /* + * Version 3 footer + * + * spa_pod Struct { [Id opcode, Struct { ... }]* } + */ + + spa_pod_parser_pod(&parser, footer); + if (spa_pod_parser_push_struct(&parser, &f[0]) < 0) { + pw_log_error("malformed message footer"); + return; + } + + while (1) { + if (spa_pod_parser_get_id(&parser, &opcode) < 0) + break; + if (spa_pod_parser_push_struct(&parser, &f[1]) < 0) + break; + if (opcode < n_opcodes) { + if ((ret = opcodes[opcode].demarshal(object, &parser)) < 0) + pw_log_error("failed processing message footer (opcode %u): %d (%s)", + opcode, ret, spa_strerror(ret)); + } else { + /* Ignore (don't log errors), in case we need to extend this later. */ + pw_log_debug("unknown message footer opcode %u", opcode); + } + spa_pod_parser_pop(&parser, &f[1]); + } +} + static int process_messages(struct client_data *data) { @@ -249,6 +297,8 @@ process_messages(struct client_data *data) } pw_protocol_native_connection_enter(conn); + pre_demarshal(conn, msg, resource, footer_resource_demarshal, + SPA_N_ELEMENTS(footer_resource_demarshal)); res = demarshal[msg->opcode].func(resource, msg); pw_protocol_native_connection_leave(conn); if (res < 0) { @@ -794,6 +844,8 @@ process_remote(struct client *impl) } proxy->refcount++; pw_protocol_native_connection_enter(conn); + pre_demarshal(conn, msg, proxy, footer_proxy_demarshal, + SPA_N_ELEMENTS(footer_proxy_demarshal)); res = demarshal[msg->opcode].func(proxy, msg); pw_protocol_native_connection_leave(conn); pw_proxy_unref(proxy); @@ -1230,6 +1282,7 @@ static int impl_ext_end_proxy(struct pw_proxy *proxy, { struct pw_core *core = proxy->core; struct client *impl = SPA_CONTAINER_OF(core->conn, struct client, this); + marshal_proxy_footers(&impl->footer_state, proxy, builder); return core->send_seq = pw_protocol_native_connection_end(impl->connection, builder); } @@ -1257,6 +1310,7 @@ static int impl_ext_end_resource(struct pw_resource *resource, { struct client_data *data = resource->client->user_data; struct pw_impl_client *client = resource->client; + marshal_resource_footers(&data->footer_state, resource, builder); return client->send_seq = pw_protocol_native_connection_end(data->connection, builder); } static const struct pw_protocol_native_ext protocol_ext_impl = { diff --git a/src/modules/module-protocol-native/connection.c b/src/modules/module-protocol-native/connection.c index 77b3a8e33..d5ebd27d5 100644 --- a/src/modules/module-protocol-native/connection.c +++ b/src/modules/module-protocol-native/connection.c @@ -45,6 +45,7 @@ PW_LOG_TOPIC_EXTERN(mod_topic_connection); #include #include "connection.h" +#include "defs.h" #define MAX_BUFFER_SIZE (1024 * 32) #define MAX_FDS 1024u @@ -598,6 +599,38 @@ pw_protocol_native_connection_get_next(struct pw_protocol_native_connection *con return 1; } +/** Get footer data from the tail of the current packet. + * + * \param conn the connection + * \param msg current message + * \return footer POD, or NULL if no valid footer present + * + * \memberof pw_protocol_native_connection + */ +struct spa_pod *pw_protocol_native_connection_get_footer(struct pw_protocol_native_connection *conn, + const struct pw_protocol_native_message *msg) +{ + struct impl *impl = SPA_CONTAINER_OF(conn, struct impl, this); + struct spa_pod *pod; + + if (impl->version != 3) + return NULL; + + /* + * Protocol version 3 footer: a single SPA POD + */ + + /* Footer immediately follows the message POD, if it is present */ + if ((pod = get_first_pod_from_data(msg->data, msg->size, 0)) == NULL) + return NULL; + pod = get_first_pod_from_data(msg->data, msg->size, SPA_POD_SIZE(pod)); + if (pod == NULL) + return NULL; + pw_log_trace("connection %p: recv message footer, size:%zu", + conn, (size_t)SPA_POD_SIZE(pod)); + return pod; +} + static inline void *begin_write(struct pw_protocol_native_connection *conn, uint32_t size) { struct impl *impl = SPA_CONTAINER_OF(conn, struct impl, this); diff --git a/src/modules/module-protocol-native/connection.h b/src/modules/module-protocol-native/connection.h index a486dfad8..d93829cb4 100644 --- a/src/modules/module-protocol-native/connection.h +++ b/src/modules/module-protocol-native/connection.h @@ -102,6 +102,9 @@ pw_protocol_native_connection_clear(struct pw_protocol_native_connection *conn); void pw_protocol_native_connection_enter(struct pw_protocol_native_connection *conn); void pw_protocol_native_connection_leave(struct pw_protocol_native_connection *conn); +struct spa_pod *pw_protocol_native_connection_get_footer(struct pw_protocol_native_connection *conn, + const struct pw_protocol_native_message *msg); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/modules/module-protocol-native/defs.h b/src/modules/module-protocol-native/defs.h index a1c9ba361..60adad027 100644 --- a/src/modules/module-protocol-native/defs.h +++ b/src/modules/module-protocol-native/defs.h @@ -30,3 +30,14 @@ int pw_protocol_native_connect_portal_screencast(struct pw_protocol_client *clie const struct spa_dict *props, void (*done_callback) (void *data, int res), void *data); + +static inline void *get_first_pod_from_data(void *data, size_t maxsize, off_t offset) +{ + void *pod; + if (offset + sizeof(struct spa_pod) > maxsize) + return NULL; + pod = SPA_PTROFF(data, offset, void); + if (offset + SPA_POD_SIZE(pod) > maxsize) + return NULL; + return pod; +} diff --git a/src/modules/module-protocol-native/protocol-footer.c b/src/modules/module-protocol-native/protocol-footer.c new file mode 100644 index 000000000..550114944 --- /dev/null +++ b/src/modules/module-protocol-native/protocol-footer.c @@ -0,0 +1,94 @@ +/* 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 +#include + +#include +#include +#include + +#include + +#include "connection.h" +#include "protocol-footer.h" +#include "defs.h" + +PW_LOG_TOPIC_EXTERN(mod_topic); +#define PW_LOG_TOPIC_DEFAULT mod_topic + +struct footer_builder { + struct spa_pod_builder *builder; + struct spa_pod_frame outer; + struct spa_pod_frame inner; + unsigned int started:1; +}; + +#define FOOTER_BUILDER_INIT(builder) (struct footer_builder) { builder } + +static void start_footer_entry(struct footer_builder *fb, uint32_t opcode) +{ + if (!fb->started) { + spa_pod_builder_push_struct(fb->builder, &fb->outer); + fb->started = true; + } + + spa_pod_builder_id(fb->builder, opcode); + spa_pod_builder_push_struct(fb->builder, &fb->inner); +} + +static void end_footer_entry(struct footer_builder *fb) +{ + spa_pod_builder_pop(fb->builder, &fb->inner); +} + +static void end_footer(struct footer_builder *fb) +{ + if (!fb->started) + return; + + spa_pod_builder_pop(fb->builder, &fb->outer); +} + +void marshal_proxy_footers(struct footer_proxy_global_state *state, struct pw_proxy *proxy, + struct spa_pod_builder *builder) +{ + struct footer_builder fb = FOOTER_BUILDER_INIT(builder); + + end_footer(&fb); +} + +void marshal_resource_footers(struct footer_resource_global_state *state, struct pw_resource *resource, + struct spa_pod_builder *builder) +{ + struct footer_builder fb = FOOTER_BUILDER_INIT(builder); + + end_footer(&fb); +} + +const struct footer_demarshal footer_proxy_demarshal[FOOTER_PROXY_OPCODE_LAST] = { +}; + +const struct footer_demarshal footer_resource_demarshal[FOOTER_RESOURCE_OPCODE_LAST] = { +}; diff --git a/src/modules/module-protocol-native/protocol-footer.h b/src/modules/module-protocol-native/protocol-footer.h new file mode 100644 index 000000000..f6232d5e9 --- /dev/null +++ b/src/modules/module-protocol-native/protocol-footer.h @@ -0,0 +1,56 @@ +/* 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. + */ + +/* + * Protocol footer. + * + * For passing around general state data that is not associated with + * messages sent to objects. + */ + +enum { + FOOTER_PROXY_OPCODE_LAST = 0, +}; + +enum { + FOOTER_RESOURCE_OPCODE_LAST = 0, +}; + +struct footer_proxy_global_state { +}; + +struct footer_resource_global_state { +}; + +struct footer_demarshal { + int (*demarshal)(void *object, struct spa_pod_parser *parser); +}; + +extern const struct footer_demarshal footer_proxy_demarshal[FOOTER_PROXY_OPCODE_LAST]; +extern const struct footer_demarshal footer_resource_demarshal[FOOTER_RESOURCE_OPCODE_LAST]; + +void marshal_proxy_footers(struct footer_proxy_global_state *state, struct pw_proxy *proxy, + struct spa_pod_builder *builder); +void marshal_resource_footers(struct footer_resource_global_state *state, struct pw_resource *resource, + struct spa_pod_builder *builder);