[2/8] protocol-native: add message sending capability

Submitted by Georg Chini on April 9, 2018, 5:35 p.m.

Details

Message ID 20180409173525.18219-3-georg@chini.tk
State New
Headers show
Series "core: Add message sending/receiving" ( rev: 1 ) in PulseAudio

Not browsing as part of any series.

Commit Message

Georg Chini April 9, 2018, 5:35 p.m.
This patch adds the PA_COMMAND_SEND_OBJECT_MESSAGE command to protocol-native
so that clients can use the messaging feature introduced in the previous patch.

Sending messages can in effect replace the extension system for modules. The
approach is more flexible than the extension interface because a generic string
format is used to exchange information. Furthermore the messaging system can be
used for any object, not only for modules, and is easier to implement than
extensions.
---
 PROTOCOL                        | 14 +++++++++
 configure.ac                    |  2 +-
 src/map-file                    |  1 +
 src/pulse/introspect.c          | 64 +++++++++++++++++++++++++++++++++++++++++
 src/pulse/introspect.h          | 16 +++++++++++
 src/pulsecore/native-common.h   |  3 ++
 src/pulsecore/pdispatch.c       |  3 ++
 src/pulsecore/protocol-native.c | 52 +++++++++++++++++++++++++++++++++
 8 files changed, 154 insertions(+), 1 deletion(-)

Patch hide | download patch | download mbox

diff --git a/PROTOCOL b/PROTOCOL
index 546998b7..f693cd3d 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -420,6 +420,20 @@  memfd support only to 10.0+ clients.
 
 Check commit 451d1d676237c81 for further details.
 
+## v33, implemented by >= 13.0
+
+Added new command for communication with objects.
+
+PA_COMMAND_SEND_OBJECT_MESSAGE:
+sends a message to an object identified by an object path
+
+parameters:
+    string object_path - unique path identifying the object
+    string message - message command
+    string message_parameters - additional parameters if required
+
+The command returns a string.
+
 #### If you just changed the protocol, read this
 ## module-tunnel depends on the sink/source/sink-input/source-input protocol
 ## internals, so if you changed these, you might have broken module-tunnel.
diff --git a/configure.ac b/configure.ac
index 57cb3a92..5537d921 100644
--- a/configure.ac
+++ b/configure.ac
@@ -40,7 +40,7 @@  AC_SUBST(PA_MINOR, pa_minor)
 AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor)
 
 AC_SUBST(PA_API_VERSION, 12)
-AC_SUBST(PA_PROTOCOL_VERSION, 32)
+AC_SUBST(PA_PROTOCOL_VERSION, 33)
 
 # The stable ABI for client applications, for the version info x:y:z
 # always will hold y=z
diff --git a/src/map-file b/src/map-file
index 9b6cba22..4d747f19 100644
--- a/src/map-file
+++ b/src/map-file
@@ -87,6 +87,7 @@  pa_context_remove_autoload_by_name;
 pa_context_remove_sample;
 pa_context_rttime_new;
 pa_context_rttime_restart;
+pa_context_send_message_to_object;
 pa_context_set_card_profile_by_index;
 pa_context_set_card_profile_by_name;
 pa_context_set_default_sink;
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index 510d784a..76bfee41 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -2184,3 +2184,67 @@  pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, in
 
     return o;
 }
+
+/** Object response string processing **/
+
+static void context_string_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    const char *response;
+    int success = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
+            goto finish;
+
+        success = 0;
+        response = "";
+    } else if (pa_tagstruct_gets(t, &response)  < 0 ||
+               !pa_tagstruct_eof(t)) {
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!response)
+        response = "";
+
+    if (o->callback) {
+        pa_context_string_cb_t cb = (pa_context_string_cb_t) o->callback;
+        cb(o->context, success, response, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_send_message_to_object(pa_context *c, const char *object_path, const char *message, const char *message_parameters, pa_context_string_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SEND_OBJECT_MESSAGE, &tag);
+
+    pa_tagstruct_puts(t, object_path);
+    pa_tagstruct_puts(t, message);
+    pa_tagstruct_puts(t, message_parameters);
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_string_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index 43389b73..7e47e740 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -204,6 +204,12 @@ 
  * Server modules can be remotely loaded and unloaded using
  * pa_context_load_module() and pa_context_unload_module().
  *
+ * \subsection message_subsec Messages
+ *
+ * Server objects like sinks, sink inputs or modules can register a message
+ * handler to communicate with clients. A message can be sent to a named
+ * message handler using pa_context_send_message_to_object().
+ *
  * \subsection client_subsec Clients
  *
  * The only operation supported on clients is the possibility of kicking
@@ -440,6 +446,16 @@  pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_s
 
 /** @} */
 
+/** @{ \name Messages */
+
+/** Callback prototype for pa_context_send_message_to_object() */
+typedef void (*pa_context_string_cb_t)(pa_context *c, int success, const char *response, void *userdata);
+
+/** Send a message to an object that registered a message handler. */
+pa_operation* pa_context_send_message_to_object(pa_context *c, const char *recipient_name, const char *message, const char *message_parameters, pa_context_string_cb_t cb, void *userdata);
+
+/** @} */
+
 /** @{ \name Clients */
 
 /** Stores information about clients. Please note that this structure
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index 70338b9f..561746d7 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -187,6 +187,9 @@  enum {
      * BOTH DIRECTIONS */
     PA_COMMAND_REGISTER_MEMFD_SHMID,
 
+    /* Supported since protocol v33 (13.0) */
+    PA_COMMAND_SEND_OBJECT_MESSAGE,
+
     PA_COMMAND_MAX
 };
 
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
index ab632a5a..9f9dc981 100644
--- a/src/pulsecore/pdispatch.c
+++ b/src/pulsecore/pdispatch.c
@@ -199,6 +199,9 @@  static const char *command_names[PA_COMMAND_MAX] = {
     /* Supported since protocol v31 (9.0) */
     /* BOTH DIRECTIONS */
     [PA_COMMAND_REGISTER_MEMFD_SHMID] = "REGISTER_MEMFD_SHMID",
+
+    /* Supported since protocol v33 (13.0) */
+    [PA_COMMAND_SEND_OBJECT_MESSAGE] = "SEND_OBJECT_MESSAGE",
 };
 
 #endif
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 2d45a4cb..4e35d406 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -47,6 +47,7 @@ 
 #include <pulsecore/namereg.h>
 #include <pulsecore/core-scache.h>
 #include <pulsecore/core-subscribe.h>
+#include <pulsecore/message-handler.h>
 #include <pulsecore/log.h>
 #include <pulsecore/mem.h>
 #include <pulsecore/strlist.h>
@@ -4685,6 +4686,55 @@  static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag,
         protocol_error(c);
 }
 
+/* Send message to an object which registered a handler. Result must be returned as string. */
+static void command_send_object_message(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    const char *object_path = NULL;
+    const char *message = NULL;
+    const char *message_parameters = NULL;
+    const char *client_name;
+    char *response = NULL;
+    int ret;
+    pa_tagstruct *reply;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_gets(t, &object_path) < 0 ||
+        pa_tagstruct_gets(t, &message) < 0 ||
+        pa_tagstruct_gets(t, &message_parameters) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, object_path != NULL, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, pa_utf8_valid(object_path), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, message != NULL, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, pa_utf8_valid(message), tag, PA_ERR_INVALID);
+    if (message_parameters)
+        CHECK_VALIDITY(c->pstream, pa_utf8_valid(message_parameters), tag, PA_ERR_INVALID);
+
+    client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
+    pa_log_debug("Client %s sent message %s to path %s", client_name, message, object_path);
+    if (message_parameters)
+        pa_log_debug("Message parameters: %s", message_parameters);
+
+    ret = pa_message_handler_send_message(c->protocol->core, object_path, message, message_parameters, &response);
+
+    if (ret < 0) {
+        pa_pstream_send_error(c->pstream, tag, -ret);
+        return;
+    }
+
+    reply = reply_new(tag);
+    pa_tagstruct_puts(reply, response);
+    pa_xfree(response);
+
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
 static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx = PA_INVALID_INDEX;
@@ -4936,6 +4986,8 @@  static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
 
     [PA_COMMAND_REGISTER_MEMFD_SHMID] = command_register_memfd_shmid,
 
+    [PA_COMMAND_SEND_OBJECT_MESSAGE] = command_send_object_message,
+
     [PA_COMMAND_EXTENSION] = command_extension
 };
 

Comments

On Mon, 2018-04-09 at 19:35 +0200, Georg Chini wrote:
> This patch adds the PA_COMMAND_SEND_OBJECT_MESSAGE command to protocol-native
> so that clients can use the messaging feature introduced in the previous patch.
> 
> Sending messages can in effect replace the extension system for modules. The
> approach is more flexible than the extension interface because a generic string
> format is used to exchange information. Furthermore the messaging system can be
> used for any object, not only for modules, and is easier to implement than
> extensions.
> ---
>  PROTOCOL                        | 14 +++++++++
>  configure.ac                    |  2 +-
>  src/map-file                    |  1 +
>  src/pulse/introspect.c          | 64 +++++++++++++++++++++++++++++++++++++++++
>  src/pulse/introspect.h          | 16 +++++++++++
>  src/pulsecore/native-common.h   |  3 ++
>  src/pulsecore/pdispatch.c       |  3 ++
>  src/pulsecore/protocol-native.c | 52 +++++++++++++++++++++++++++++++++
>  8 files changed, 154 insertions(+), 1 deletion(-)

Looks good to me.