2004-08-13 13:22:44 +00:00
|
|
|
/***
|
2006-06-19 21:53:48 +00:00
|
|
|
This file is part of PulseAudio.
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
Copyright 2004-2008 Lennart Poettering
|
2007-02-13 15:35:19 +00:00
|
|
|
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
|
|
|
|
|
|
2006-06-19 21:53:48 +00:00
|
|
|
PulseAudio is free software; you can redistribute it and/or modify
|
2004-11-14 14:58:54 +00:00
|
|
|
it under the terms of the GNU Lesser General Public License as published
|
2009-03-03 20:23:02 +00:00
|
|
|
by the Free Software Foundation; either version 2.1 of the License,
|
2004-08-13 13:22:44 +00:00
|
|
|
or (at your option) any later version.
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-06-19 21:53:48 +00:00
|
|
|
PulseAudio is distributed in the hope that it will be useful, but
|
2004-08-13 13:22:44 +00:00
|
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
General Public License for more details.
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-11-14 14:58:54 +00:00
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
2014-11-26 14:14:51 +01:00
|
|
|
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
|
2004-08-13 13:22:44 +00:00
|
|
|
***/
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sys/types.h>
|
2004-09-01 21:12:27 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <errno.h>
|
2004-11-01 23:37:36 +00:00
|
|
|
#include <signal.h>
|
2006-01-10 17:51:06 +00:00
|
|
|
|
|
|
|
|
#ifdef HAVE_SYS_WAIT_H
|
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_NETDB_H
|
|
|
|
|
#include <netdb.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2006-06-19 21:53:48 +00:00
|
|
|
#include <pulse/version.h>
|
|
|
|
|
#include <pulse/xmalloc.h>
|
2008-05-15 23:34:41 +00:00
|
|
|
#include <pulse/util.h>
|
2009-06-20 16:52:41 +03:00
|
|
|
#include <pulse/mainloop.h>
|
|
|
|
|
#include <pulse/timeval.h>
|
2011-03-09 10:00:20 +01:00
|
|
|
#include <pulse/fork-detect.h>
|
|
|
|
|
#include <pulse/client-conf.h>
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2007-11-24 16:24:54 +00:00
|
|
|
#include <pulsecore/core-error.h>
|
2011-08-10 10:30:15 +02:00
|
|
|
#include <pulsecore/i18n.h>
|
2006-06-19 21:53:48 +00:00
|
|
|
#include <pulsecore/native-common.h>
|
|
|
|
|
#include <pulsecore/pdispatch.h>
|
|
|
|
|
#include <pulsecore/pstream.h>
|
2009-12-03 13:22:05 +02:00
|
|
|
#include <pulsecore/hashmap.h>
|
2006-06-19 21:53:48 +00:00
|
|
|
#include <pulsecore/socket-client.h>
|
|
|
|
|
#include <pulsecore/pstream-util.h>
|
2009-04-05 02:13:43 +03:00
|
|
|
#include <pulsecore/core-rtclock.h>
|
2006-06-19 21:53:48 +00:00
|
|
|
#include <pulsecore/core-util.h>
|
|
|
|
|
#include <pulsecore/log.h>
|
2011-01-04 17:03:13 +01:00
|
|
|
#include <pulsecore/socket.h>
|
2006-07-19 21:48:35 +00:00
|
|
|
#include <pulsecore/creds.h>
|
2007-10-28 19:13:50 +00:00
|
|
|
#include <pulsecore/macro.h>
|
2008-06-17 18:27:24 +00:00
|
|
|
#include <pulsecore/proplist-util.h>
|
2004-09-17 19:45:44 +00:00
|
|
|
|
2006-02-17 12:10:58 +00:00
|
|
|
#include "internal.h"
|
|
|
|
|
#include "context.h"
|
|
|
|
|
|
2008-08-04 19:02:20 +02:00
|
|
|
void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
|
2014-04-25 17:23:21 +02:00
|
|
|
static void pa_command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
|
|
|
|
|
static void pa_command_disable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
|
2016-03-13 01:12:18 +02:00
|
|
|
static void pa_command_register_memfd_shmid(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
|
2008-08-04 19:02:20 +02:00
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
|
2006-01-11 01:17:39 +00:00
|
|
|
[PA_COMMAND_REQUEST] = pa_command_request,
|
2006-02-20 04:05:16 +00:00
|
|
|
[PA_COMMAND_OVERFLOW] = pa_command_overflow_or_underflow,
|
|
|
|
|
[PA_COMMAND_UNDERFLOW] = pa_command_overflow_or_underflow,
|
2006-01-11 01:17:39 +00:00
|
|
|
[PA_COMMAND_PLAYBACK_STREAM_KILLED] = pa_command_stream_killed,
|
|
|
|
|
[PA_COMMAND_RECORD_STREAM_KILLED] = pa_command_stream_killed,
|
2007-11-21 01:30:40 +00:00
|
|
|
[PA_COMMAND_PLAYBACK_STREAM_MOVED] = pa_command_stream_moved,
|
|
|
|
|
[PA_COMMAND_RECORD_STREAM_MOVED] = pa_command_stream_moved,
|
|
|
|
|
[PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = pa_command_stream_suspended,
|
|
|
|
|
[PA_COMMAND_RECORD_STREAM_SUSPENDED] = pa_command_stream_suspended,
|
2008-05-15 23:34:41 +00:00
|
|
|
[PA_COMMAND_STARTED] = pa_command_stream_started,
|
2008-08-04 19:02:20 +02:00
|
|
|
[PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event,
|
2009-02-12 03:18:05 +01:00
|
|
|
[PA_COMMAND_EXTENSION] = pa_command_extension,
|
|
|
|
|
[PA_COMMAND_PLAYBACK_STREAM_EVENT] = pa_command_stream_event,
|
|
|
|
|
[PA_COMMAND_RECORD_STREAM_EVENT] = pa_command_stream_event,
|
2009-03-30 18:46:12 +02:00
|
|
|
[PA_COMMAND_CLIENT_EVENT] = pa_command_client_event,
|
|
|
|
|
[PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED] = pa_command_stream_buffer_attr,
|
2014-04-25 17:23:21 +02:00
|
|
|
[PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = pa_command_stream_buffer_attr,
|
|
|
|
|
[PA_COMMAND_ENABLE_SRBCHANNEL] = pa_command_enable_srbchannel,
|
|
|
|
|
[PA_COMMAND_DISABLE_SRBCHANNEL] = pa_command_disable_srbchannel,
|
2016-03-13 01:12:18 +02:00
|
|
|
[PA_COMMAND_REGISTER_MEMFD_SHMID] = pa_command_register_memfd_shmid,
|
2004-08-13 13:22:44 +00:00
|
|
|
};
|
2006-09-06 22:19:54 +00:00
|
|
|
static void context_free(pa_context *c);
|
2009-04-06 16:29:22 +10:00
|
|
|
|
|
|
|
|
#ifdef HAVE_DBUS
|
2009-03-20 16:51:28 +02:00
|
|
|
static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata);
|
2009-04-06 16:29:22 +10:00
|
|
|
#endif
|
2006-09-06 22:19:54 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {
|
2008-05-15 23:34:41 +00:00
|
|
|
return pa_context_new_with_proplist(mainloop, name, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void reset_callbacks(pa_context *c) {
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
|
|
|
|
|
c->state_callback = NULL;
|
|
|
|
|
c->state_userdata = NULL;
|
|
|
|
|
|
|
|
|
|
c->subscribe_callback = NULL;
|
|
|
|
|
c->subscribe_userdata = NULL;
|
2008-08-04 19:02:20 +02:00
|
|
|
|
2009-02-12 03:18:05 +01:00
|
|
|
c->event_callback = NULL;
|
|
|
|
|
c->event_userdata = NULL;
|
|
|
|
|
|
2009-06-27 22:08:07 +01:00
|
|
|
c->ext_device_manager.callback = NULL;
|
|
|
|
|
c->ext_device_manager.userdata = NULL;
|
|
|
|
|
|
2011-06-07 12:18:17 +02:00
|
|
|
c->ext_device_restore.callback = NULL;
|
|
|
|
|
c->ext_device_restore.userdata = NULL;
|
|
|
|
|
|
2008-08-04 19:02:20 +02:00
|
|
|
c->ext_stream_restore.callback = NULL;
|
|
|
|
|
c->ext_stream_restore.userdata = NULL;
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
2018-05-26 23:50:55 +01:00
|
|
|
pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, const pa_proplist *p) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_context *c;
|
2016-03-13 00:57:06 +02:00
|
|
|
pa_mem_type_t type;
|
2020-12-29 00:03:41 +03:00
|
|
|
const char *force_disable_shm_str;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(mainloop);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
if (pa_detect_fork())
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2008-08-06 18:54:13 +02:00
|
|
|
pa_init_i18n();
|
|
|
|
|
|
2010-01-08 20:06:21 +01:00
|
|
|
c = pa_xnew0(pa_context, 1);
|
2007-10-28 19:13:50 +00:00
|
|
|
PA_REFCNT_INIT(c);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2018-06-07 02:43:56 +01:00
|
|
|
c->error = pa_xnew0(pa_context_error, 1);
|
|
|
|
|
assert(c->error);
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
c->proplist = p ? pa_proplist_copy(p) : pa_proplist_new();
|
|
|
|
|
|
|
|
|
|
if (name)
|
|
|
|
|
pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
|
|
|
|
|
|
2009-04-06 16:29:22 +10:00
|
|
|
#ifdef HAVE_DBUS
|
2009-03-20 16:51:28 +02:00
|
|
|
c->system_bus = c->session_bus = NULL;
|
2009-04-06 16:29:22 +10:00
|
|
|
#endif
|
2004-08-13 13:22:44 +00:00
|
|
|
c->mainloop = mainloop;
|
2009-12-03 13:22:05 +02:00
|
|
|
c->playback_streams = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
|
|
|
|
|
c->record_streams = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
|
2008-05-15 23:34:41 +00:00
|
|
|
c->client_index = PA_INVALID_INDEX;
|
2009-06-22 23:49:40 +02:00
|
|
|
c->use_rtclock = pa_mainloop_is_our_api(mainloop);
|
2004-08-14 20:25:32 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
PA_LLIST_HEAD_INIT(pa_stream, c->streams);
|
|
|
|
|
PA_LLIST_HEAD_INIT(pa_operation, c->operations);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2018-06-07 02:43:56 +01:00
|
|
|
c->error->error = PA_OK;
|
2004-08-14 20:25:32 +00:00
|
|
|
c->state = PA_CONTEXT_UNCONNECTED;
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
reset_callbacks(c);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2006-07-17 11:26:29 +00:00
|
|
|
#ifndef MSG_NOSIGNAL
|
2007-01-04 13:43:45 +00:00
|
|
|
#ifdef SIGPIPE
|
2006-07-17 11:26:29 +00:00
|
|
|
pa_check_signal_is_blocked(SIGPIPE);
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
2004-09-17 19:45:44 +00:00
|
|
|
c->conf = pa_client_conf_new();
|
2014-06-08 16:32:54 +03:00
|
|
|
pa_client_conf_load(c->conf, true, true);
|
2006-04-19 11:54:43 +00:00
|
|
|
|
2020-12-29 00:03:41 +03:00
|
|
|
force_disable_shm_str = pa_proplist_gets(c->proplist, PA_PROP_CONTEXT_FORCE_DISABLE_SHM);
|
|
|
|
|
if (force_disable_shm_str) {
|
|
|
|
|
int b = pa_parse_boolean(force_disable_shm_str);
|
|
|
|
|
if (b < 0) {
|
|
|
|
|
pa_log_warn("Ignored invalid value for '%s' property: %s", PA_PROP_CONTEXT_FORCE_DISABLE_SHM, force_disable_shm_str);
|
|
|
|
|
} else if (b) {
|
|
|
|
|
c->conf->disable_shm = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-25 17:23:21 +02:00
|
|
|
c->srb_template.readfd = -1;
|
|
|
|
|
c->srb_template.writefd = -1;
|
|
|
|
|
|
2016-04-15 23:06:02 +02:00
|
|
|
c->memfd_on_local = (!c->conf->disable_memfd && pa_memfd_is_locally_supported());
|
|
|
|
|
|
|
|
|
|
type = (c->conf->disable_shm) ? PA_MEM_TYPE_PRIVATE :
|
|
|
|
|
((!c->memfd_on_local) ?
|
|
|
|
|
PA_MEM_TYPE_SHARED_POSIX : PA_MEM_TYPE_SHARED_MEMFD);
|
|
|
|
|
|
2016-03-13 01:09:39 +02:00
|
|
|
if (!(c->mempool = pa_mempool_new(type, c->conf->shm_size, true))) {
|
2006-09-06 22:19:54 +00:00
|
|
|
|
2016-03-13 00:57:06 +02:00
|
|
|
if (!c->conf->disable_shm) {
|
|
|
|
|
pa_log_warn("Failed to allocate shared memory pool. Falling back to a normal private one.");
|
2016-03-13 01:09:39 +02:00
|
|
|
c->mempool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, c->conf->shm_size, true);
|
2016-03-13 00:57:06 +02:00
|
|
|
}
|
2006-09-06 22:19:54 +00:00
|
|
|
|
|
|
|
|
if (!c->mempool) {
|
|
|
|
|
context_free(c);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-19 01:18:30 +00:00
|
|
|
|
2004-08-13 13:22:44 +00:00
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
static void context_unlink(pa_context *c) {
|
|
|
|
|
pa_stream *s;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
s = c->streams ? pa_stream_ref(c->streams) : NULL;
|
|
|
|
|
while (s) {
|
|
|
|
|
pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
|
|
|
|
|
pa_stream_set_state(s, c->state == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED);
|
|
|
|
|
pa_stream_unref(s);
|
|
|
|
|
s = n;
|
|
|
|
|
}
|
2004-11-11 21:18:33 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
while (c->operations)
|
|
|
|
|
pa_operation_cancel(c->operations);
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (c->pdispatch) {
|
2004-08-15 00:02:26 +00:00
|
|
|
pa_pdispatch_unref(c->pdispatch);
|
2008-05-15 23:34:41 +00:00
|
|
|
c->pdispatch = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2004-08-15 00:02:26 +00:00
|
|
|
if (c->pstream) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_pstream_unlink(c->pstream);
|
2004-08-15 00:02:26 +00:00
|
|
|
pa_pstream_unref(c->pstream);
|
2008-05-15 23:34:41 +00:00
|
|
|
c->pstream = NULL;
|
2004-08-15 00:02:26 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2014-04-25 17:23:21 +02:00
|
|
|
if (c->srb_template.memblock) {
|
|
|
|
|
pa_memblock_unref(c->srb_template.memblock);
|
|
|
|
|
c->srb_template.memblock = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (c->client) {
|
|
|
|
|
pa_socket_client_unref(c->client);
|
|
|
|
|
c->client = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reset_callbacks(c);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void context_free(pa_context *c) {
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
|
|
|
|
|
context_unlink(c);
|
|
|
|
|
|
2009-04-06 16:29:22 +10:00
|
|
|
#ifdef HAVE_DBUS
|
2009-03-20 16:51:28 +02:00
|
|
|
if (c->system_bus) {
|
2010-01-08 20:06:21 +01:00
|
|
|
if (c->filter_added)
|
|
|
|
|
dbus_connection_remove_filter(pa_dbus_wrap_connection_get(c->system_bus), filter_cb, c);
|
2009-03-20 16:51:28 +02:00
|
|
|
pa_dbus_wrap_connection_free(c->system_bus);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (c->session_bus) {
|
2010-01-08 20:06:21 +01:00
|
|
|
if (c->filter_added)
|
|
|
|
|
dbus_connection_remove_filter(pa_dbus_wrap_connection_get(c->session_bus), filter_cb, c);
|
2009-03-20 16:51:28 +02:00
|
|
|
pa_dbus_wrap_connection_free(c->session_bus);
|
|
|
|
|
}
|
2009-04-06 16:29:22 +10:00
|
|
|
#endif
|
2009-03-20 16:51:28 +02:00
|
|
|
|
2004-08-13 13:22:44 +00:00
|
|
|
if (c->record_streams)
|
2013-09-14 11:50:10 +05:30
|
|
|
pa_hashmap_free(c->record_streams);
|
2004-08-13 13:22:44 +00:00
|
|
|
if (c->playback_streams)
|
2013-09-14 11:50:10 +05:30
|
|
|
pa_hashmap_free(c->playback_streams);
|
2004-08-17 19:37:29 +00:00
|
|
|
|
2006-09-06 22:19:54 +00:00
|
|
|
if (c->mempool)
|
2016-03-13 00:51:12 +02:00
|
|
|
pa_mempool_unref(c->mempool);
|
2004-09-17 19:45:44 +00:00
|
|
|
|
|
|
|
|
if (c->conf)
|
|
|
|
|
pa_client_conf_free(c->conf);
|
2004-11-11 21:18:33 +00:00
|
|
|
|
|
|
|
|
pa_strlist_free(c->server_list);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (c->proplist)
|
|
|
|
|
pa_proplist_free(c->proplist);
|
|
|
|
|
|
2004-11-17 00:05:25 +00:00
|
|
|
pa_xfree(c->server);
|
2019-11-02 21:43:51 +03:00
|
|
|
pa_xfree(c->error);
|
2004-08-13 13:22:44 +00:00
|
|
|
pa_xfree(c);
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_context* pa_context_ref(pa_context *c) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
PA_REFCNT_INC(c);
|
2004-08-14 20:25:32 +00:00
|
|
|
return c;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
void pa_context_unref(pa_context *c) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2004-08-14 20:25:32 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
if (PA_REFCNT_DEC(c) <= 0)
|
2004-08-14 20:25:32 +00:00
|
|
|
context_free(c);
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-27 16:25:31 +00:00
|
|
|
void pa_context_set_state(pa_context *c, pa_context_state_t st) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
if (c->state == st)
|
2004-08-13 13:22:44 +00:00
|
|
|
return;
|
|
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_context_ref(c);
|
|
|
|
|
|
2006-04-24 19:29:15 +00:00
|
|
|
c->state = st;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2006-04-24 19:29:15 +00:00
|
|
|
if (c->state_callback)
|
|
|
|
|
c->state_callback(c, c->state_userdata);
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED)
|
|
|
|
|
context_unlink(c);
|
2004-08-14 20:25:32 +00:00
|
|
|
|
|
|
|
|
pa_context_unref(c);
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-18 11:13:40 +03:00
|
|
|
int pa_context_set_error(const pa_context *c, int error) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(error >= 0);
|
|
|
|
|
pa_assert(error < PA_ERR_MAX);
|
2006-02-20 04:05:16 +00:00
|
|
|
|
|
|
|
|
if (c)
|
2018-06-07 02:43:56 +01:00
|
|
|
c->error->error = error;
|
2006-02-20 04:05:16 +00:00
|
|
|
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
void pa_context_fail(pa_context *c, int error) {
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
|
|
|
|
|
|
|
|
|
pa_context_set_error(c, error);
|
|
|
|
|
pa_context_set_state(c, PA_CONTEXT_FAILED);
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
static void pstream_die_callback(pa_pstream *p, void *userdata) {
|
|
|
|
|
pa_context *c = userdata;
|
2006-02-20 22:41:02 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(c);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_context_fail(c, PA_ERR_CONNECTIONTERMINATED);
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2016-03-13 01:12:18 +02:00
|
|
|
static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, pa_cmsg_ancil_data *ancil_data, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_context *c = userdata;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(packet);
|
|
|
|
|
pa_assert(c);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_context_ref(c);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2014-08-06 07:48:19 +05:30
|
|
|
if (pa_pdispatch_run(c->pdispatch, packet, ancil_data, c) < 0)
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
2004-08-14 20:25:32 +00:00
|
|
|
|
|
|
|
|
pa_context_unref(c);
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2014-04-25 17:23:21 +02:00
|
|
|
static void handle_srbchannel_memblock(pa_context *c, pa_memblock *memblock) {
|
|
|
|
|
pa_srbchannel *sr;
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
|
|
|
|
|
/* Memblock sanity check */
|
2014-08-18 17:02:40 +02:00
|
|
|
if (!memblock) {
|
2014-04-25 17:23:21 +02:00
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
2014-08-18 17:02:40 +02:00
|
|
|
return;
|
|
|
|
|
} else if (pa_memblock_is_read_only(memblock)) {
|
2014-04-25 17:23:21 +02:00
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
2014-08-18 17:02:40 +02:00
|
|
|
return;
|
|
|
|
|
} else if (pa_memblock_is_ours(memblock)) {
|
2014-04-25 17:23:21 +02:00
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
2014-08-18 17:02:40 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2014-04-25 17:23:21 +02:00
|
|
|
|
|
|
|
|
/* Create the srbchannel */
|
|
|
|
|
c->srb_template.memblock = memblock;
|
|
|
|
|
pa_memblock_ref(memblock);
|
|
|
|
|
sr = pa_srbchannel_new_from_template(c->mainloop, &c->srb_template);
|
2014-08-18 16:55:01 +02:00
|
|
|
if (!sr) {
|
2015-12-10 16:22:54 +01:00
|
|
|
pa_log_warn("Failed to create srbchannel from template");
|
|
|
|
|
c->srb_template.readfd = -1;
|
|
|
|
|
c->srb_template.writefd = -1;
|
|
|
|
|
pa_memblock_unref(c->srb_template.memblock);
|
|
|
|
|
c->srb_template.memblock = NULL;
|
2014-08-18 16:55:01 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2014-04-25 17:23:21 +02:00
|
|
|
|
|
|
|
|
/* Ack the enable command */
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2014-04-25 17:23:21 +02:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_ENABLE_SRBCHANNEL);
|
|
|
|
|
pa_tagstruct_putu32(t, c->srb_setup_tag);
|
|
|
|
|
pa_pstream_send_tagstruct(c->pstream, t);
|
|
|
|
|
|
|
|
|
|
/* ...and switch over */
|
|
|
|
|
pa_pstream_set_srbchannel(c->pstream, sr);
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_context *c = userdata;
|
|
|
|
|
pa_stream *s;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(chunk);
|
2009-02-18 21:55:55 +01:00
|
|
|
pa_assert(chunk->length > 0);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_context_ref(c);
|
2006-04-12 23:57:25 +00:00
|
|
|
|
2014-04-25 17:23:21 +02:00
|
|
|
if (c->srb_template.readfd != -1 && c->srb_template.memblock == NULL) {
|
|
|
|
|
handle_srbchannel_memblock(c, chunk->memblock);
|
|
|
|
|
pa_context_unref(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-03 13:22:05 +02:00
|
|
|
if ((s = pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(channel)))) {
|
2004-11-17 00:05:25 +00:00
|
|
|
|
2009-02-18 21:55:55 +01:00
|
|
|
if (chunk->memblock) {
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_memblockq_seek(s->record_memblockq, offset, seek, true);
|
2009-02-18 21:55:55 +01:00
|
|
|
pa_memblockq_push_align(s->record_memblockq, chunk);
|
|
|
|
|
} else
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_memblockq_seek(s->record_memblockq, offset+chunk->length, seek, true);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
if (s->read_callback) {
|
|
|
|
|
size_t l;
|
2004-11-17 00:05:25 +00:00
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
if ((l = pa_memblockq_get_length(s->record_memblockq)) > 0)
|
|
|
|
|
s->read_callback(s, l, s->read_userdata);
|
2004-09-26 17:02:26 +00:00
|
|
|
}
|
2004-08-14 20:25:32 +00:00
|
|
|
}
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_context_unref(c);
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, bool fail) {
|
2008-05-15 23:34:41 +00:00
|
|
|
uint32_t err;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2004-08-14 20:25:32 +00:00
|
|
|
|
2004-08-13 13:22:44 +00:00
|
|
|
if (command == PA_COMMAND_ERROR) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(t);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-01-20 02:15:43 +01:00
|
|
|
if (pa_tagstruct_getu32(t, &err) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
2004-08-13 13:22:44 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
} else if (command == PA_COMMAND_TIMEOUT)
|
2008-05-15 23:34:41 +00:00
|
|
|
err = PA_ERR_TIMEOUT;
|
2004-08-14 20:25:32 +00:00
|
|
|
else {
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
2004-08-14 20:25:32 +00:00
|
|
|
return -1;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (err == PA_OK) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (err >= PA_ERR_MAX)
|
|
|
|
|
err = PA_ERR_UNKNOWN;
|
|
|
|
|
|
|
|
|
|
if (fail) {
|
2008-08-19 22:39:54 +02:00
|
|
|
pa_context_fail(c, (int) err);
|
2008-05-15 23:34:41 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-19 22:39:54 +02:00
|
|
|
pa_context_set_error(c, (int) err);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
return 0;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
|
|
|
|
pa_context *c = userdata;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(c->state == PA_CONTEXT_AUTHORIZING || c->state == PA_CONTEXT_SETTING_NAME);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_context_ref(c);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-13 13:22:44 +00:00
|
|
|
if (command != PA_COMMAND_REPLY) {
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_context_handle_error(c, command, t, true);
|
2004-08-14 20:25:32 +00:00
|
|
|
goto finish;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
switch(c->state) {
|
|
|
|
|
case PA_CONTEXT_AUTHORIZING: {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *reply;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool shm_on_remote = false;
|
2016-04-15 23:06:02 +02:00
|
|
|
bool memfd_on_remote = false;
|
2006-03-02 21:56:15 +00:00
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &c->version) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Minimum supported version */
|
|
|
|
|
if (c->version < 8) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_VERSION);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:27:24 +00:00
|
|
|
/* Starting with protocol version 13 the MSB of the version
|
|
|
|
|
tag reflects if shm is available for this connection or
|
|
|
|
|
not. */
|
2016-04-25 18:12:39 +05:30
|
|
|
if ((c->version & PA_PROTOCOL_VERSION_MASK) >= 13) {
|
|
|
|
|
shm_on_remote = !!(c->version & PA_PROTOCOL_FLAG_SHM);
|
2016-04-15 23:06:02 +02:00
|
|
|
|
|
|
|
|
/* Starting with protocol version 31, the second MSB of the version
|
|
|
|
|
* tag reflects whether memfd is supported on the other PA end. */
|
2016-04-25 18:12:39 +05:30
|
|
|
if ((c->version & PA_PROTOCOL_VERSION_MASK) >= 31)
|
|
|
|
|
memfd_on_remote = !!(c->version & PA_PROTOCOL_FLAG_MEMFD);
|
2016-04-15 23:06:02 +02:00
|
|
|
|
|
|
|
|
/* Reserve the two most-significant _bytes_ of the version tag
|
|
|
|
|
* for flags. */
|
2016-04-25 18:12:39 +05:30
|
|
|
c->version &= PA_PROTOCOL_VERSION_MASK;
|
2008-06-17 18:27:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_log_debug("Protocol version: remote %u, local %u", c->version, PA_PROTOCOL_VERSION);
|
|
|
|
|
|
2006-08-18 23:45:23 +00:00
|
|
|
/* Enable shared memory support if possible */
|
2008-06-17 18:27:24 +00:00
|
|
|
if (c->do_shm)
|
|
|
|
|
if (c->version < 10 || (c->version >= 13 && !shm_on_remote))
|
2013-06-27 19:28:09 +02:00
|
|
|
c->do_shm = false;
|
2008-06-17 18:27:24 +00:00
|
|
|
|
|
|
|
|
if (c->do_shm) {
|
2006-08-18 23:45:23 +00:00
|
|
|
|
|
|
|
|
/* Only enable SHM if both sides are owned by the same
|
|
|
|
|
* user. This is a security measure because otherwise
|
|
|
|
|
* data private to the user might leak. */
|
2006-08-22 07:12:50 +00:00
|
|
|
|
2007-01-04 13:43:45 +00:00
|
|
|
#ifdef HAVE_CREDS
|
2006-08-18 23:45:23 +00:00
|
|
|
const pa_creds *creds;
|
2008-06-17 18:27:24 +00:00
|
|
|
if (!(creds = pa_pdispatch_creds(pd)) || getuid() != creds->uid)
|
2013-06-27 19:28:09 +02:00
|
|
|
c->do_shm = false;
|
2006-08-22 07:12:50 +00:00
|
|
|
#endif
|
2006-08-18 23:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:27:24 +00:00
|
|
|
pa_log_debug("Negotiated SHM: %s", pa_yes_no(c->do_shm));
|
|
|
|
|
pa_pstream_enable_shm(c->pstream, c->do_shm);
|
|
|
|
|
|
2016-04-15 23:06:02 +02:00
|
|
|
c->shm_type = PA_MEM_TYPE_PRIVATE;
|
|
|
|
|
if (c->do_shm) {
|
|
|
|
|
if (c->version >= 31 && memfd_on_remote && c->memfd_on_local) {
|
|
|
|
|
const char *reason;
|
|
|
|
|
|
|
|
|
|
pa_pstream_enable_memfd(c->pstream);
|
|
|
|
|
if (pa_mempool_is_memfd_backed(c->mempool))
|
|
|
|
|
if (pa_pstream_register_memfd_mempool(c->pstream, c->mempool, &reason))
|
|
|
|
|
pa_log("Failed to regester memfd mempool. Reason: %s", reason);
|
|
|
|
|
|
|
|
|
|
/* Even if memfd pool registration fails, the negotiated SHM type
|
|
|
|
|
* shall remain memfd as both endpoints claim to support it. */
|
|
|
|
|
c->shm_type = PA_MEM_TYPE_SHARED_MEMFD;
|
|
|
|
|
} else
|
|
|
|
|
c->shm_type = PA_MEM_TYPE_SHARED_POSIX;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_log_debug("Memfd possible: %s", pa_yes_no(c->memfd_on_local));
|
|
|
|
|
pa_log_debug("Negotiated SHM type: %s", pa_mem_type_to_string(c->shm_type));
|
|
|
|
|
|
2006-02-23 01:24:16 +00:00
|
|
|
reply = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
if (c->version >= 13) {
|
|
|
|
|
pa_init_proplist(c->proplist);
|
|
|
|
|
pa_tagstruct_put_proplist(reply, c->proplist);
|
|
|
|
|
} else
|
|
|
|
|
pa_tagstruct_puts(reply, pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME));
|
|
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_pstream_send_tagstruct(c->pstream, reply);
|
2006-04-24 19:29:15 +00:00
|
|
|
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL);
|
2004-08-14 20:25:32 +00:00
|
|
|
|
|
|
|
|
pa_context_set_state(c, PA_CONTEXT_SETTING_NAME);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
case PA_CONTEXT_SETTING_NAME :
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
if ((c->version >= 13 && (pa_tagstruct_getu32(t, &c->client_index) < 0 ||
|
|
|
|
|
c->client_index == PA_INVALID_INDEX)) ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_context_set_state(c, PA_CONTEXT_READY);
|
|
|
|
|
break;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
default:
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert_not_reached();
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
finish:
|
|
|
|
|
pa_context_unref(c);
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
static void setup_context(pa_context *c, pa_iochannel *io) {
|
2014-03-19 12:19:08 +02:00
|
|
|
uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *t;
|
2004-08-13 13:22:44 +00:00
|
|
|
uint32_t tag;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(io);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_context_ref(c);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(!c->pstream);
|
2006-08-18 19:55:18 +00:00
|
|
|
c->pstream = pa_pstream_new(c->mainloop, io, c->mempool);
|
|
|
|
|
|
2004-08-13 13:22:44 +00:00
|
|
|
pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c);
|
2011-12-12 22:36:39 +00:00
|
|
|
pa_pstream_set_receive_packet_callback(c->pstream, pstream_packet_callback, c);
|
|
|
|
|
pa_pstream_set_receive_memblock_callback(c->pstream, pstream_memblock_callback, c);
|
2004-08-14 20:25:32 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(!c->pdispatch);
|
2009-04-05 02:13:43 +03:00
|
|
|
c->pdispatch = pa_pdispatch_new(c->mainloop, c->use_rtclock, command_table, PA_COMMAND_MAX);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2014-03-19 12:19:08 +02:00
|
|
|
if (pa_client_conf_load_cookie(c->conf, cookie, sizeof(cookie)) < 0)
|
2014-09-02 12:09:44 +02:00
|
|
|
pa_log_info("No cookie loaded. Attempting to connect without.");
|
2004-09-01 21:12:27 +00:00
|
|
|
|
2006-02-23 01:24:16 +00:00
|
|
|
t = pa_tagstruct_command(c, PA_COMMAND_AUTH, &tag);
|
2008-06-17 18:27:24 +00:00
|
|
|
|
|
|
|
|
c->do_shm =
|
|
|
|
|
pa_mempool_is_shared(c->mempool) &&
|
|
|
|
|
c->is_local;
|
|
|
|
|
|
|
|
|
|
pa_log_debug("SHM possible: %s", pa_yes_no(c->do_shm));
|
|
|
|
|
|
|
|
|
|
/* Starting with protocol version 13 we use the MSB of the version
|
2016-04-15 23:06:02 +02:00
|
|
|
* tag for informing the other side if we could do SHM or not.
|
|
|
|
|
* Starting from version 31, second MSB is used to flag memfd support. */
|
2016-04-25 18:12:39 +05:30
|
|
|
pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION | (c->do_shm ? PA_PROTOCOL_FLAG_SHM : 0) |
|
|
|
|
|
(c->memfd_on_local ? PA_PROTOCOL_FLAG_MEMFD: 0));
|
2014-03-19 12:19:08 +02:00
|
|
|
pa_tagstruct_put_arbitrary(t, cookie, sizeof(cookie));
|
2006-07-19 17:44:19 +00:00
|
|
|
|
2006-07-19 21:48:35 +00:00
|
|
|
#ifdef HAVE_CREDS
|
2006-07-19 17:44:19 +00:00
|
|
|
{
|
2006-07-19 21:48:35 +00:00
|
|
|
pa_creds ucred;
|
2006-07-19 17:44:19 +00:00
|
|
|
|
2006-08-18 23:45:23 +00:00
|
|
|
if (pa_iochannel_creds_supported(io))
|
|
|
|
|
pa_iochannel_creds_enable(io);
|
|
|
|
|
|
2006-07-19 17:44:19 +00:00
|
|
|
ucred.uid = getuid();
|
2006-07-19 21:48:35 +00:00
|
|
|
ucred.gid = getgid();
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-07-19 17:44:19 +00:00
|
|
|
pa_pstream_send_tagstruct_with_creds(c->pstream, t, &ucred);
|
|
|
|
|
}
|
|
|
|
|
#else
|
2006-08-22 07:18:07 +00:00
|
|
|
pa_pstream_send_tagstruct(c->pstream, t);
|
2006-07-19 17:44:19 +00:00
|
|
|
#endif
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-04-24 19:29:15 +00:00
|
|
|
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL);
|
2004-08-14 20:25:32 +00:00
|
|
|
|
|
|
|
|
pa_context_set_state(c, PA_CONTEXT_AUTHORIZING);
|
|
|
|
|
|
2004-09-01 21:12:27 +00:00
|
|
|
pa_context_unref(c);
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-05 03:22:13 +02:00
|
|
|
static pa_strlist *prepend_per_user(pa_strlist *l) {
|
|
|
|
|
char *ufn;
|
2009-02-18 16:51:37 +02:00
|
|
|
|
2008-09-05 03:22:13 +02:00
|
|
|
/* The per-user instance */
|
|
|
|
|
if ((ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET))) {
|
|
|
|
|
l = pa_strlist_prepend(l, ufn);
|
|
|
|
|
pa_xfree(ufn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return l;
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-09-05 03:22:13 +02:00
|
|
|
#ifndef OS_IS_WIN32
|
|
|
|
|
|
|
|
|
|
static int context_autospawn(pa_context *c) {
|
|
|
|
|
pid_t pid;
|
|
|
|
|
int status, r;
|
2009-07-24 18:22:13 +02:00
|
|
|
struct sigaction sa;
|
2008-09-05 03:22:13 +02:00
|
|
|
|
|
|
|
|
pa_context_ref(c);
|
2004-11-11 21:18:33 +00:00
|
|
|
|
2009-07-24 18:22:13 +02:00
|
|
|
if (sigaction(SIGCHLD, NULL, &sa) < 0) {
|
|
|
|
|
pa_log_debug("sigaction() failed: %s", pa_cstrerror(errno));
|
|
|
|
|
pa_context_fail(c, PA_ERR_INTERNAL);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-19 12:42:15 +00:00
|
|
|
#ifdef SA_NOCLDWAIT
|
2009-07-24 18:22:13 +02:00
|
|
|
if ((sa.sa_flags & SA_NOCLDWAIT) || sa.sa_handler == SIG_IGN) {
|
2012-03-19 12:42:15 +00:00
|
|
|
#else
|
|
|
|
|
if (sa.sa_handler == SIG_IGN) {
|
|
|
|
|
#endif
|
2009-07-24 18:22:13 +02:00
|
|
|
pa_log_debug("Process disabled waitpid(), cannot autospawn.");
|
|
|
|
|
pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_log_debug("Trying to autospawn...");
|
|
|
|
|
|
2004-11-11 21:18:33 +00:00
|
|
|
if (c->spawn_api.prefork)
|
|
|
|
|
c->spawn_api.prefork();
|
2004-09-15 13:03:25 +00:00
|
|
|
|
|
|
|
|
if ((pid = fork()) < 0) {
|
2008-08-06 18:54:13 +02:00
|
|
|
pa_log_error(_("fork(): %s"), pa_cstrerror(errno));
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_context_fail(c, PA_ERR_INTERNAL);
|
2004-09-15 13:03:25 +00:00
|
|
|
|
2004-11-11 21:18:33 +00:00
|
|
|
if (c->spawn_api.postfork)
|
|
|
|
|
c->spawn_api.postfork();
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-09-15 13:03:25 +00:00
|
|
|
goto fail;
|
|
|
|
|
} else if (!pid) {
|
|
|
|
|
/* Child */
|
2004-09-20 19:37:28 +00:00
|
|
|
|
2004-09-17 19:45:44 +00:00
|
|
|
const char *state = NULL;
|
2009-07-25 03:11:09 +02:00
|
|
|
const char * argv[32];
|
|
|
|
|
unsigned n = 0;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-11-11 21:18:33 +00:00
|
|
|
if (c->spawn_api.atfork)
|
|
|
|
|
c->spawn_api.atfork();
|
2004-09-15 13:03:25 +00:00
|
|
|
|
2009-08-12 21:40:12 +02:00
|
|
|
/* We leave most of the cleaning up of the process environment
|
|
|
|
|
* to the executable. We only clean up the file descriptors to
|
|
|
|
|
* make sure the executable can actually be loaded
|
|
|
|
|
* correctly. */
|
2008-09-05 03:22:13 +02:00
|
|
|
pa_close_all(-1);
|
|
|
|
|
|
2004-09-20 19:37:28 +00:00
|
|
|
/* Setup argv */
|
2004-09-17 19:45:44 +00:00
|
|
|
argv[n++] = c->conf->daemon_binary;
|
2008-09-05 03:22:13 +02:00
|
|
|
argv[n++] = "--start";
|
2004-09-17 19:45:44 +00:00
|
|
|
|
2009-07-25 03:11:09 +02:00
|
|
|
while (n < PA_ELEMENTSOF(argv)-1) {
|
2004-09-17 19:45:44 +00:00
|
|
|
char *a;
|
|
|
|
|
|
|
|
|
|
if (!(a = pa_split_spaces(c->conf->extra_arguments, &state)))
|
|
|
|
|
break;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-09-17 19:45:44 +00:00
|
|
|
argv[n++] = a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
argv[n++] = NULL;
|
2009-07-25 03:11:09 +02:00
|
|
|
pa_assert(n <= PA_ELEMENTSOF(argv));
|
2004-09-17 19:45:44 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
execv(argv[0], (char * const *) argv);
|
2004-09-20 19:37:28 +00:00
|
|
|
_exit(1);
|
2007-01-04 13:43:45 +00:00
|
|
|
}
|
2004-09-15 13:03:25 +00:00
|
|
|
|
|
|
|
|
/* Parent */
|
|
|
|
|
|
2004-11-11 21:18:33 +00:00
|
|
|
if (c->spawn_api.postfork)
|
|
|
|
|
c->spawn_api.postfork();
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-09-05 03:22:13 +02:00
|
|
|
do {
|
|
|
|
|
r = waitpid(pid, &status, 0);
|
|
|
|
|
} while (r < 0 && errno == EINTR);
|
|
|
|
|
|
2004-09-15 13:03:25 +00:00
|
|
|
if (r < 0) {
|
2009-07-25 01:29:36 +02:00
|
|
|
|
2017-04-09 00:15:03 +02:00
|
|
|
if (errno != ECHILD) {
|
2009-07-25 01:29:36 +02:00
|
|
|
pa_log(_("waitpid(): %s"), pa_cstrerror(errno));
|
|
|
|
|
pa_context_fail(c, PA_ERR_INTERNAL);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* hmm, something already reaped our child, so we assume
|
|
|
|
|
* startup worked, even if we cannot know */
|
|
|
|
|
|
2004-09-15 13:03:25 +00:00
|
|
|
} else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
|
2004-09-15 13:03:25 +00:00
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-15 19:16:57 +00:00
|
|
|
pa_context_unref(c);
|
|
|
|
|
|
2004-09-15 13:03:25 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
fail:
|
2004-11-11 21:18:33 +00:00
|
|
|
|
2004-09-15 19:16:57 +00:00
|
|
|
pa_context_unref(c);
|
|
|
|
|
|
2004-09-15 13:03:25 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-10 17:51:06 +00:00
|
|
|
#endif /* OS_IS_WIN32 */
|
|
|
|
|
|
2008-09-05 03:22:13 +02:00
|
|
|
static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata);
|
|
|
|
|
|
2009-04-06 16:29:22 +10:00
|
|
|
#ifdef HAVE_DBUS
|
2009-03-20 16:51:28 +02:00
|
|
|
static void track_pulseaudio_on_dbus(pa_context *c, DBusBusType type, pa_dbus_wrap_connection **conn) {
|
|
|
|
|
DBusError error;
|
|
|
|
|
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(conn);
|
|
|
|
|
|
|
|
|
|
dbus_error_init(&error);
|
2009-07-01 14:26:07 +02:00
|
|
|
|
2009-04-05 02:13:43 +03:00
|
|
|
if (!(*conn = pa_dbus_wrap_connection_new(c->mainloop, c->use_rtclock, type, &error)) || dbus_error_is_set(&error)) {
|
2009-03-20 16:51:28 +02:00
|
|
|
pa_log_warn("Unable to contact DBUS: %s: %s", error.name, error.message);
|
2009-07-01 14:26:07 +02:00
|
|
|
goto fail;
|
2009-03-20 16:51:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!dbus_connection_add_filter(pa_dbus_wrap_connection_get(*conn), filter_cb, c, NULL)) {
|
|
|
|
|
pa_log_warn("Failed to add filter function");
|
2009-07-01 14:26:07 +02:00
|
|
|
goto fail;
|
2009-03-20 16:51:28 +02:00
|
|
|
}
|
2013-06-27 19:28:09 +02:00
|
|
|
c->filter_added = true;
|
2009-03-20 16:51:28 +02:00
|
|
|
|
|
|
|
|
if (pa_dbus_add_matches(
|
|
|
|
|
pa_dbus_wrap_connection_get(*conn), &error,
|
2009-07-01 14:26:07 +02:00
|
|
|
"type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged',arg0='org.pulseaudio.Server',arg1=''", NULL) < 0) {
|
|
|
|
|
|
2009-03-30 21:07:23 +02:00
|
|
|
pa_log_warn("Unable to track org.pulseaudio.Server: %s: %s", error.name, error.message);
|
2009-07-01 14:26:07 +02:00
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
fail:
|
|
|
|
|
if (*conn) {
|
|
|
|
|
pa_dbus_wrap_connection_free(*conn);
|
|
|
|
|
*conn = NULL;
|
|
|
|
|
}
|
2009-03-20 16:51:28 +02:00
|
|
|
|
|
|
|
|
dbus_error_free(&error);
|
|
|
|
|
}
|
2009-04-06 16:29:22 +10:00
|
|
|
#endif
|
2009-03-20 16:51:28 +02:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
static int try_next_connection(pa_context *c) {
|
2004-11-11 21:18:33 +00:00
|
|
|
char *u = NULL;
|
2004-08-14 20:25:32 +00:00
|
|
|
int r = -1;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(!c->client);
|
2004-09-15 13:03:25 +00:00
|
|
|
|
2004-11-11 21:18:33 +00:00
|
|
|
for (;;) {
|
2006-02-20 22:41:02 +00:00
|
|
|
pa_xfree(u);
|
2004-11-11 21:18:33 +00:00
|
|
|
u = NULL;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-11-11 21:18:33 +00:00
|
|
|
c->server_list = pa_strlist_pop(c->server_list, &u);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-11-11 21:18:33 +00:00
|
|
|
if (!u) {
|
2004-09-17 19:45:44 +00:00
|
|
|
|
2006-01-10 17:51:06 +00:00
|
|
|
#ifndef OS_IS_WIN32
|
2004-11-11 21:18:33 +00:00
|
|
|
if (c->do_autospawn) {
|
2008-09-05 03:22:13 +02:00
|
|
|
|
|
|
|
|
if ((r = context_autospawn(c)) < 0)
|
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
|
|
/* Autospawn only once */
|
2013-06-27 19:28:09 +02:00
|
|
|
c->do_autospawn = false;
|
2008-09-05 03:22:13 +02:00
|
|
|
|
|
|
|
|
/* Connect only to per-user sockets this time */
|
|
|
|
|
c->server_list = prepend_per_user(c->server_list);
|
|
|
|
|
|
|
|
|
|
/* Retry connection */
|
|
|
|
|
continue;
|
2004-11-11 21:18:33 +00:00
|
|
|
}
|
2006-01-10 17:51:06 +00:00
|
|
|
#endif
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-04-06 16:29:22 +10:00
|
|
|
#ifdef HAVE_DBUS
|
2009-03-30 20:31:03 +00:00
|
|
|
if (c->no_fail && !c->server_specified) {
|
2009-03-20 16:51:28 +02:00
|
|
|
if (!c->session_bus)
|
|
|
|
|
track_pulseaudio_on_dbus(c, DBUS_BUS_SESSION, &c->session_bus);
|
2009-05-12 01:39:55 +03:00
|
|
|
if (!c->system_bus)
|
|
|
|
|
track_pulseaudio_on_dbus(c, DBUS_BUS_SYSTEM, &c->system_bus);
|
2014-11-15 15:24:22 +01:00
|
|
|
|
|
|
|
|
if (c->session_bus || c->system_bus) {
|
|
|
|
|
pa_log_debug("Waiting for PA on D-Bus...");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2009-03-20 16:51:28 +02:00
|
|
|
} else
|
2009-04-06 16:29:22 +10:00
|
|
|
#endif
|
2009-03-20 16:51:28 +02:00
|
|
|
pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
|
|
|
|
|
|
2004-11-11 21:18:33 +00:00
|
|
|
goto finish;
|
2004-09-28 22:47:48 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
|
|
|
|
pa_log_debug("Trying to connect to %s...", u);
|
2004-11-17 00:05:25 +00:00
|
|
|
|
|
|
|
|
pa_xfree(c->server);
|
|
|
|
|
c->server = pa_xstrdup(u);
|
2005-01-06 01:07:43 +00:00
|
|
|
|
2009-04-05 02:13:43 +03:00
|
|
|
if (!(c->client = pa_socket_client_new_string(c->mainloop, c->use_rtclock, u, PA_NATIVE_DEFAULT_PORT)))
|
2004-11-11 21:18:33 +00:00
|
|
|
continue;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2014-10-23 15:00:29 +02:00
|
|
|
c->is_local = pa_socket_client_is_local(c->client);
|
2004-11-11 21:18:33 +00:00
|
|
|
pa_socket_client_set_callback(c->client, on_connection, c);
|
|
|
|
|
break;
|
2004-09-28 22:47:48 +00:00
|
|
|
}
|
2004-11-11 21:18:33 +00:00
|
|
|
|
|
|
|
|
r = 0;
|
|
|
|
|
|
|
|
|
|
finish:
|
2006-02-20 22:41:02 +00:00
|
|
|
pa_xfree(u);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-11-11 21:18:33 +00:00
|
|
|
return r;
|
|
|
|
|
}
|
2004-09-15 13:03:25 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) {
|
|
|
|
|
pa_context *c = userdata;
|
2008-05-15 23:34:41 +00:00
|
|
|
int saved_errno = errno;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(client);
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(c->state == PA_CONTEXT_CONNECTING);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-11-11 21:18:33 +00:00
|
|
|
pa_context_ref(c);
|
2005-01-11 20:47:10 +00:00
|
|
|
|
2004-11-11 21:18:33 +00:00
|
|
|
pa_socket_client_unref(client);
|
|
|
|
|
c->client = NULL;
|
|
|
|
|
|
|
|
|
|
if (!io) {
|
2009-07-01 14:26:07 +02:00
|
|
|
/* Try the next item in the list */
|
2008-05-15 23:34:41 +00:00
|
|
|
if (saved_errno == ECONNREFUSED ||
|
|
|
|
|
saved_errno == ETIMEDOUT ||
|
|
|
|
|
saved_errno == EHOSTUNREACH) {
|
2004-11-11 21:18:33 +00:00
|
|
|
try_next_connection(c);
|
2004-08-14 20:25:32 +00:00
|
|
|
goto finish;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
2004-09-15 19:16:57 +00:00
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
|
2004-11-11 21:18:33 +00:00
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setup_context(c, io);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-11-11 21:18:33 +00:00
|
|
|
finish:
|
|
|
|
|
pa_context_unref(c);
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-06 16:29:22 +10:00
|
|
|
#ifdef HAVE_DBUS
|
2009-03-20 16:51:28 +02:00
|
|
|
static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) {
|
|
|
|
|
pa_context *c = userdata;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool is_session;
|
2009-03-20 16:51:28 +02:00
|
|
|
|
|
|
|
|
pa_assert(bus);
|
|
|
|
|
pa_assert(message);
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
|
|
|
|
|
if (c->state != PA_CONTEXT_CONNECTING)
|
|
|
|
|
goto finish;
|
|
|
|
|
|
2009-03-30 21:07:41 +02:00
|
|
|
if (!c->no_fail)
|
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
|
|
/* FIXME: We probably should check if this is actually the NameOwnerChanged we were looking for */
|
|
|
|
|
|
2009-05-12 01:39:55 +03:00
|
|
|
is_session = c->session_bus && bus == pa_dbus_wrap_connection_get(c->session_bus);
|
2009-07-01 14:26:07 +02:00
|
|
|
pa_log_debug("Rock!! PulseAudio might be back on %s bus", is_session ? "session" : "system");
|
2009-03-20 16:51:28 +02:00
|
|
|
|
2009-03-30 21:07:41 +02:00
|
|
|
if (is_session)
|
2009-03-20 16:51:28 +02:00
|
|
|
/* The user instance via PF_LOCAL */
|
|
|
|
|
c->server_list = prepend_per_user(c->server_list);
|
2009-03-30 21:07:41 +02:00
|
|
|
else
|
2009-03-20 16:51:28 +02:00
|
|
|
/* The system wide instance via PF_LOCAL */
|
|
|
|
|
c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET);
|
|
|
|
|
|
2009-04-17 15:47:27 +03:00
|
|
|
if (!c->client)
|
|
|
|
|
try_next_connection(c);
|
2009-03-20 16:51:28 +02:00
|
|
|
|
|
|
|
|
finish:
|
|
|
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
|
|
|
}
|
2009-04-06 16:29:22 +10:00
|
|
|
#endif
|
2009-03-20 16:51:28 +02:00
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
int pa_context_connect(
|
|
|
|
|
pa_context *c,
|
|
|
|
|
const char *server,
|
|
|
|
|
pa_context_flags_t flags,
|
|
|
|
|
const pa_spawn_api *api) {
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-11-11 21:18:33 +00:00
|
|
|
int r = -1;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2006-02-20 22:41:02 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY(c, !pa_detect_fork(), PA_ERR_FORKED);
|
2006-02-20 22:41:02 +00:00
|
|
|
PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_UNCONNECTED, PA_ERR_BADSTATE);
|
2009-03-20 16:51:28 +02:00
|
|
|
PA_CHECK_VALIDITY(c, !(flags & ~(PA_CONTEXT_NOAUTOSPAWN|PA_CONTEXT_NOFAIL)), PA_ERR_INVALID);
|
2006-02-20 23:31:38 +00:00
|
|
|
PA_CHECK_VALIDITY(c, !server || *server, PA_ERR_INVALID);
|
2004-11-11 21:18:33 +00:00
|
|
|
|
2009-04-13 22:25:09 +02:00
|
|
|
if (server)
|
2013-06-27 19:28:09 +02:00
|
|
|
c->conf->autospawn = false;
|
2009-04-13 22:25:09 +02:00
|
|
|
else
|
2004-11-11 21:18:33 +00:00
|
|
|
server = c->conf->default_server;
|
|
|
|
|
|
|
|
|
|
pa_context_ref(c);
|
|
|
|
|
|
2009-07-01 14:26:07 +02:00
|
|
|
c->no_fail = !!(flags & PA_CONTEXT_NOFAIL);
|
2009-03-30 20:31:03 +00:00
|
|
|
c->server_specified = !!server;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(!c->server_list);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-11-11 21:18:33 +00:00
|
|
|
if (server) {
|
|
|
|
|
if (!(c->server_list = pa_strlist_parse(server))) {
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_context_fail(c, PA_ERR_INVALIDSERVER);
|
2004-08-14 20:25:32 +00:00
|
|
|
goto finish;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
2008-08-09 03:49:42 +02:00
|
|
|
|
2004-11-11 21:18:33 +00:00
|
|
|
} else {
|
2008-09-05 03:22:13 +02:00
|
|
|
char *d;
|
2004-11-11 21:18:33 +00:00
|
|
|
|
|
|
|
|
/* Prepend in reverse order */
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-09-05 03:22:13 +02:00
|
|
|
/* Follow the X display */
|
2010-01-13 22:15:52 +01:00
|
|
|
if (c->conf->auto_connect_display) {
|
|
|
|
|
if ((d = getenv("DISPLAY"))) {
|
|
|
|
|
d = pa_xstrndup(d, strcspn(d, ":"));
|
2005-01-06 01:07:43 +00:00
|
|
|
|
2010-01-13 22:15:52 +01:00
|
|
|
if (*d)
|
|
|
|
|
c->server_list = pa_strlist_prepend(c->server_list, d);
|
2005-01-06 01:07:43 +00:00
|
|
|
|
2010-01-13 22:15:52 +01:00
|
|
|
pa_xfree(d);
|
|
|
|
|
}
|
2005-01-06 01:07:43 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-09-05 03:20:33 +02:00
|
|
|
/* Add TCP/IP on the localhost */
|
2010-01-13 22:08:59 +01:00
|
|
|
if (c->conf->auto_connect_localhost) {
|
2021-01-03 03:41:36 -05:00
|
|
|
#if defined(HAVE_IPV6) && !defined(OS_IS_WIN32)
|
|
|
|
|
/* FIXME: pa_socket_client does not support IPv6 on Windows */
|
2010-01-13 22:08:59 +01:00
|
|
|
c->server_list = pa_strlist_prepend(c->server_list, "tcp6:[::1]");
|
2021-01-03 03:41:36 -05:00
|
|
|
#endif
|
2010-01-13 22:08:59 +01:00
|
|
|
c->server_list = pa_strlist_prepend(c->server_list, "tcp4:127.0.0.1");
|
|
|
|
|
}
|
2006-07-19 21:48:35 +00:00
|
|
|
|
2008-09-05 03:22:13 +02:00
|
|
|
/* The system wide instance via PF_LOCAL */
|
2021-05-30 15:28:30 -04:00
|
|
|
#ifndef OS_IS_WIN32
|
2008-05-15 23:34:41 +00:00
|
|
|
c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET);
|
2021-05-30 15:28:30 -04:00
|
|
|
#else
|
|
|
|
|
/* see change_user in src/daemon/main.c */
|
|
|
|
|
char *run_path = pa_sprintf_malloc("%s" PA_PATH_SEP "run" PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET, pa_win32_get_system_appdata());
|
|
|
|
|
c->server_list = pa_strlist_prepend(c->server_list, run_path);
|
|
|
|
|
pa_xfree(run_path);
|
|
|
|
|
#endif
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2008-09-05 03:22:13 +02:00
|
|
|
/* The user instance via PF_LOCAL */
|
|
|
|
|
c->server_list = prepend_per_user(c->server_list);
|
2009-04-13 22:25:09 +02:00
|
|
|
}
|
2008-08-07 02:39:58 +02:00
|
|
|
|
2009-04-13 22:25:09 +02:00
|
|
|
/* Set up autospawning */
|
|
|
|
|
if (!(flags & PA_CONTEXT_NOAUTOSPAWN) && c->conf->autospawn) {
|
2008-08-09 03:49:42 +02:00
|
|
|
|
2011-01-06 00:51:33 +01:00
|
|
|
#ifdef HAVE_GETUID
|
2009-04-13 22:25:09 +02:00
|
|
|
if (getuid() == 0)
|
|
|
|
|
pa_log_debug("Not doing autospawn since we are root.");
|
|
|
|
|
else {
|
2013-06-27 19:28:09 +02:00
|
|
|
c->do_autospawn = true;
|
2008-08-09 03:49:42 +02:00
|
|
|
|
2009-04-13 22:25:09 +02:00
|
|
|
if (api)
|
|
|
|
|
c->spawn_api = *api;
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
2011-01-06 00:51:33 +01:00
|
|
|
#endif
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_context_set_state(c, PA_CONTEXT_CONNECTING);
|
2004-11-11 21:18:33 +00:00
|
|
|
r = try_next_connection(c);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
finish:
|
|
|
|
|
pa_context_unref(c);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
return r;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
void pa_context_disconnect(pa_context *c) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
if (pa_detect_fork())
|
|
|
|
|
return;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (PA_CONTEXT_IS_GOOD(c->state))
|
|
|
|
|
pa_context_set_state(c, PA_CONTEXT_TERMINATED);
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2018-05-27 06:24:17 +01:00
|
|
|
pa_context_state_t pa_context_get_state(const pa_context *c) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
return c->state;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2018-06-07 03:51:09 +01:00
|
|
|
int pa_context_errno(const pa_context *c) {
|
2009-09-17 01:37:23 +02:00
|
|
|
|
|
|
|
|
if (!c)
|
|
|
|
|
return PA_ERR_INVALID;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2018-06-07 02:43:56 +01:00
|
|
|
return c->error->error;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-20 16:01:53 +00:00
|
|
|
void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
if (pa_detect_fork())
|
|
|
|
|
return;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
|
|
|
|
|
return;
|
|
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
c->state_callback = cb;
|
|
|
|
|
c->state_userdata = userdata;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2009-02-12 03:18:05 +01:00
|
|
|
void pa_context_set_event_callback(pa_context *c, pa_context_event_cb_t cb, void *userdata) {
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
if (pa_detect_fork())
|
|
|
|
|
return;
|
|
|
|
|
|
2009-02-12 03:18:05 +01:00
|
|
|
if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
c->event_callback = cb;
|
|
|
|
|
c->event_userdata = userdata;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-07 03:52:50 +01:00
|
|
|
int pa_context_is_pending(const pa_context *c) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY(c, !pa_detect_fork(), PA_ERR_FORKED);
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_CHECK_VALIDITY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE);
|
2006-04-10 20:38:58 +00:00
|
|
|
|
2004-11-17 23:11:34 +00:00
|
|
|
return (c->pstream && pa_pstream_is_pending(c->pstream)) ||
|
|
|
|
|
(c->pdispatch && pa_pdispatch_is_pending(c->pdispatch)) ||
|
|
|
|
|
c->client;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
static void set_dispatch_callbacks(pa_operation *o);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void pdispatch_drain_callback(pa_pdispatch*pd, void *userdata) {
|
2004-08-13 13:22:44 +00:00
|
|
|
set_dispatch_callbacks(userdata);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void pstream_drain_callback(pa_pstream *s, void *userdata) {
|
2004-08-13 13:22:44 +00:00
|
|
|
set_dispatch_callbacks(userdata);
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
static void set_dispatch_callbacks(pa_operation *o) {
|
2004-08-14 20:25:32 +00:00
|
|
|
int done = 1;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(o);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(o) >= 1);
|
|
|
|
|
pa_assert(o->context);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(o->context) >= 1);
|
|
|
|
|
pa_assert(o->context->state == PA_CONTEXT_READY);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_pstream_set_drain_callback(o->context->pstream, NULL, NULL);
|
|
|
|
|
pa_pdispatch_set_drain_callback(o->context->pdispatch, NULL, NULL);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
if (pa_pdispatch_is_pending(o->context->pdispatch)) {
|
|
|
|
|
pa_pdispatch_set_drain_callback(o->context->pdispatch, pdispatch_drain_callback, o);
|
|
|
|
|
done = 0;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
if (pa_pstream_is_pending(o->context->pstream)) {
|
|
|
|
|
pa_pstream_set_drain_callback(o->context->pstream, pstream_drain_callback, o);
|
|
|
|
|
done = 0;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2006-04-23 20:59:43 +00:00
|
|
|
if (done) {
|
2004-08-14 20:25:32 +00:00
|
|
|
if (o->callback) {
|
2006-02-20 22:41:02 +00:00
|
|
|
pa_context_notify_cb_t cb = (pa_context_notify_cb_t) o->callback;
|
2004-08-14 20:25:32 +00:00
|
|
|
cb(o->context, o->userdata);
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_operation_done(o);
|
2006-04-23 20:59:43 +00:00
|
|
|
pa_operation_unref(o);
|
|
|
|
|
}
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-20 16:01:53 +00:00
|
|
|
pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_operation *o;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
|
2006-02-20 22:41:02 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, pa_context_is_pending(c), PA_ERR_BADSTATE);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
|
2004-08-14 20:25:32 +00:00
|
|
|
set_dispatch_callbacks(pa_operation_ref(o));
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
return o;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_operation *o = userdata;
|
2004-08-14 20:25:32 +00:00
|
|
|
int success = 1;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(o);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(o) >= 1);
|
2006-04-24 19:29:15 +00:00
|
|
|
|
|
|
|
|
if (!o->context)
|
|
|
|
|
goto finish;
|
2004-08-13 13:22:44 +00:00
|
|
|
|
|
|
|
|
if (command != PA_COMMAND_REPLY) {
|
2013-06-27 19:28:09 +02:00
|
|
|
if (pa_context_handle_error(o->context, command, t, false) < 0)
|
2004-08-14 20:25:32 +00:00
|
|
|
goto finish;
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
success = 0;
|
|
|
|
|
} else if (!pa_tagstruct_eof(t)) {
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_context_fail(o->context, PA_ERR_PROTOCOL);
|
2004-08-14 20:25:32 +00:00
|
|
|
goto finish;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
if (o->callback) {
|
2006-02-20 16:01:53 +00:00
|
|
|
pa_context_success_cb_t cb = (pa_context_success_cb_t) o->callback;
|
2004-08-14 20:25:32 +00:00
|
|
|
cb(o->context, success, o->userdata);
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
finish:
|
|
|
|
|
pa_operation_done(o);
|
|
|
|
|
pa_operation_unref(o);
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa_pdispatch_cb_t internal_cb, pa_operation_cb_t cb, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *t;
|
|
|
|
|
pa_operation *o;
|
2004-08-14 20:25:32 +00:00
|
|
|
uint32_t tag;
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2006-02-20 22:41:02 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
|
2006-02-20 22:41:02 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
o = pa_operation_new(c, NULL, cb, userdata);
|
2006-02-23 01:24:16 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
t = pa_tagstruct_command(c, command, &tag);
|
2006-02-20 22:41:02 +00:00
|
|
|
pa_pstream_send_tagstruct(c->pstream, t);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, internal_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
|
2006-02-20 22:41:02 +00:00
|
|
|
|
|
|
|
|
return o;
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2006-02-20 22:41:02 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
return pa_context_send_simple_command(c, PA_COMMAND_EXIT, pa_context_simple_ack_callback, (pa_operation_cb_t) cb, userdata);
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
2004-08-18 01:00:18 +00:00
|
|
|
|
2006-02-20 16:01:53 +00:00
|
|
|
pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *t;
|
|
|
|
|
pa_operation *o;
|
2004-09-06 21:55:09 +00:00
|
|
|
uint32_t tag;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2006-02-20 22:41:02 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
|
2006-02-20 22:41:02 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
|
2004-09-06 21:55:09 +00:00
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
|
2006-02-23 01:24:16 +00:00
|
|
|
t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SINK, &tag);
|
2004-09-06 21:55:09 +00:00
|
|
|
pa_tagstruct_puts(t, name);
|
|
|
|
|
pa_pstream_send_tagstruct(c->pstream, t);
|
2006-04-24 19:29:15 +00:00
|
|
|
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
|
2004-09-06 21:55:09 +00:00
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
return o;
|
2004-09-06 21:55:09 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-20 16:01:53 +00:00
|
|
|
pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *t;
|
|
|
|
|
pa_operation *o;
|
2004-09-06 21:55:09 +00:00
|
|
|
uint32_t tag;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2006-02-20 22:41:02 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
|
2006-02-20 22:41:02 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
|
2006-02-23 01:24:16 +00:00
|
|
|
t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SOURCE, &tag);
|
2004-09-06 21:55:09 +00:00
|
|
|
pa_tagstruct_puts(t, name);
|
|
|
|
|
pa_pstream_send_tagstruct(c->pstream, t);
|
2006-04-24 19:29:15 +00:00
|
|
|
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
|
2004-09-06 21:55:09 +00:00
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
return o;
|
2004-09-06 21:55:09 +00:00
|
|
|
}
|
2004-09-15 19:16:57 +00:00
|
|
|
|
2018-06-07 03:53:45 +01:00
|
|
|
int pa_context_is_local(const pa_context *c) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(c, !pa_detect_fork(), PA_ERR_FORKED, -1);
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, -1);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2014-10-23 15:00:29 +02:00
|
|
|
return c->is_local;
|
2004-09-15 19:16:57 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-20 16:01:53 +00:00
|
|
|
pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_operation *o;
|
2004-09-15 19:16:57 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
|
|
|
|
pa_assert(name);
|
2006-02-20 22:41:02 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
|
2006-02-20 22:41:02 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (c->version >= 13) {
|
|
|
|
|
pa_proplist *p = pa_proplist_new();
|
2004-09-15 19:16:57 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name);
|
|
|
|
|
o = pa_context_proplist_update(c, PA_UPDATE_REPLACE, p, cb, userdata);
|
|
|
|
|
pa_proplist_free(p);
|
|
|
|
|
} else {
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
uint32_t tag;
|
|
|
|
|
|
|
|
|
|
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
|
|
|
|
|
t = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
|
|
|
|
|
pa_tagstruct_puts(t, name);
|
|
|
|
|
pa_pstream_send_tagstruct(c->pstream, t);
|
|
|
|
|
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
|
|
|
|
|
}
|
2004-09-15 19:16:57 +00:00
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
return o;
|
2004-09-15 19:16:57 +00:00
|
|
|
}
|
2004-09-29 19:13:55 +00:00
|
|
|
|
|
|
|
|
const char* pa_get_library_version(void) {
|
2011-10-01 12:03:44 +01:00
|
|
|
return pa_get_headers_version();
|
2004-09-29 19:13:55 +00:00
|
|
|
}
|
|
|
|
|
|
2018-06-07 03:55:42 +01:00
|
|
|
const char* pa_context_get_server(const pa_context *c) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2004-11-17 00:05:25 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, c->server, PA_ERR_NOENTITY);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-11-17 00:05:25 +00:00
|
|
|
if (*c->server == '{') {
|
|
|
|
|
char *e = strchr(c->server+1, '}');
|
|
|
|
|
return e ? e+1 : c->server;
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-11-17 00:05:25 +00:00
|
|
|
return c->server;
|
|
|
|
|
}
|
2006-02-23 01:24:16 +00:00
|
|
|
|
2018-05-28 01:12:39 +01:00
|
|
|
uint32_t pa_context_get_protocol_version(const pa_context *c) {
|
2006-03-02 21:56:15 +00:00
|
|
|
return PA_PROTOCOL_VERSION;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-07 03:56:16 +01:00
|
|
|
uint32_t pa_context_get_server_protocol_version(const pa_context *c) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2006-03-02 21:56:15 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(c, !pa_detect_fork(), PA_ERR_FORKED, PA_INVALID_INDEX);
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, PA_INVALID_INDEX);
|
|
|
|
|
|
2006-03-02 21:56:15 +00:00
|
|
|
return c->version;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-23 01:24:16 +00:00
|
|
|
pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *tag) {
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(tag);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2006-02-23 01:24:16 +00:00
|
|
|
pa_tagstruct_putu32(t, command);
|
|
|
|
|
pa_tagstruct_putu32(t, *tag = c->ctag++);
|
|
|
|
|
|
|
|
|
|
return t;
|
|
|
|
|
}
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2018-06-07 03:56:51 +01:00
|
|
|
uint32_t pa_context_get_index(const pa_context *c) {
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(c, !pa_detect_fork(), PA_ERR_FORKED, PA_INVALID_INDEX);
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(c, c->version >= 13, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX);
|
|
|
|
|
|
|
|
|
|
return c->client_index;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-27 03:04:43 +01:00
|
|
|
pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, const pa_proplist *p, pa_context_success_cb_t cb, void *userdata) {
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_operation *o;
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
uint32_t tag;
|
|
|
|
|
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, PA_ERR_INVALID);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
|
|
|
|
|
|
|
|
|
|
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
|
|
|
|
|
|
|
|
|
|
t = pa_tagstruct_command(c, PA_COMMAND_UPDATE_CLIENT_PROPLIST, &tag);
|
|
|
|
|
pa_tagstruct_putu32(t, (uint32_t) mode);
|
|
|
|
|
pa_tagstruct_put_proplist(t, p);
|
|
|
|
|
|
|
|
|
|
pa_pstream_send_tagstruct(c->pstream, t);
|
|
|
|
|
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
|
|
|
|
|
|
|
|
|
|
/* Please note that we don't update c->proplist here, because we
|
|
|
|
|
* don't export that field */
|
|
|
|
|
|
|
|
|
|
return o;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata) {
|
|
|
|
|
pa_operation *o;
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
uint32_t tag;
|
|
|
|
|
const char * const *k;
|
|
|
|
|
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, keys && keys[0], PA_ERR_INVALID);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
|
|
|
|
|
|
|
|
|
|
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
|
|
|
|
|
|
|
|
|
|
t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_CLIENT_PROPLIST, &tag);
|
|
|
|
|
|
|
|
|
|
for (k = keys; *k; k++)
|
|
|
|
|
pa_tagstruct_puts(t, *k);
|
|
|
|
|
|
|
|
|
|
pa_tagstruct_puts(t, NULL);
|
|
|
|
|
|
|
|
|
|
pa_pstream_send_tagstruct(c->pstream, t);
|
|
|
|
|
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
|
|
|
|
|
|
|
|
|
|
/* Please note that we don't update c->proplist here, because we
|
|
|
|
|
* don't export that field */
|
|
|
|
|
|
|
|
|
|
return o;
|
|
|
|
|
}
|
2008-08-04 19:02:20 +02:00
|
|
|
|
|
|
|
|
void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
|
|
|
|
pa_context *c = userdata;
|
|
|
|
|
uint32_t idx;
|
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(command == PA_COMMAND_EXTENSION);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
|
|
|
|
|
|
|
|
|
pa_context_ref(c);
|
|
|
|
|
|
2009-02-12 03:18:05 +01:00
|
|
|
if (c->version < 15) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-04 19:02:20 +02:00
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
2008-08-05 19:01:25 +02:00
|
|
|
pa_tagstruct_gets(t, &name) < 0) {
|
2008-08-04 19:02:20 +02:00
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-07 12:18:17 +02:00
|
|
|
if (pa_streq(name, "module-device-manager"))
|
2009-06-27 22:08:07 +01:00
|
|
|
pa_ext_device_manager_command(c, tag, t);
|
2011-06-07 12:18:17 +02:00
|
|
|
else if (pa_streq(name, "module-device-restore"))
|
2011-08-08 23:29:47 +02:00
|
|
|
pa_ext_device_restore_command(c, tag, t);
|
2011-06-07 12:18:17 +02:00
|
|
|
else if (pa_streq(name, "module-stream-restore"))
|
|
|
|
|
pa_ext_stream_restore_command(c, tag, t);
|
2008-08-04 19:02:20 +02:00
|
|
|
else
|
2008-08-06 18:54:13 +02:00
|
|
|
pa_log(_("Received message for unknown extension '%s'"), name);
|
2008-08-04 19:02:20 +02:00
|
|
|
|
|
|
|
|
finish:
|
|
|
|
|
pa_context_unref(c);
|
|
|
|
|
}
|
2009-02-12 03:18:05 +01:00
|
|
|
|
2014-04-25 17:23:21 +02:00
|
|
|
static void pa_command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
|
|
|
|
pa_context *c = userdata;
|
2014-07-04 14:48:30 +02:00
|
|
|
|
|
|
|
|
#ifdef HAVE_CREDS
|
2016-03-13 01:12:18 +02:00
|
|
|
pa_cmsg_ancil_data *ancil = NULL;
|
2014-04-25 17:23:21 +02:00
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(command == PA_COMMAND_ENABLE_SRBCHANNEL);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
|
|
|
|
|
2016-03-13 01:12:18 +02:00
|
|
|
ancil = pa_pdispatch_take_ancil_data(pd);
|
|
|
|
|
if (!ancil)
|
|
|
|
|
goto fail;
|
|
|
|
|
|
2014-04-25 17:23:21 +02:00
|
|
|
/* Currently only one srb channel is supported, might change in future versions */
|
2016-03-13 01:12:18 +02:00
|
|
|
if (c->srb_template.readfd != -1)
|
|
|
|
|
goto fail;
|
2014-04-25 17:23:21 +02:00
|
|
|
|
2016-03-13 01:12:18 +02:00
|
|
|
if (ancil->nfd != 2 || ancil->fds[0] == -1 || ancil->fds[1] == -1)
|
|
|
|
|
goto fail;
|
2014-04-25 17:23:21 +02:00
|
|
|
|
|
|
|
|
pa_context_ref(c);
|
|
|
|
|
|
2016-03-13 01:12:18 +02:00
|
|
|
c->srb_template.readfd = ancil->fds[0];
|
|
|
|
|
c->srb_template.writefd = ancil->fds[1];
|
2014-04-25 17:23:21 +02:00
|
|
|
c->srb_setup_tag = tag;
|
|
|
|
|
|
|
|
|
|
pa_context_unref(c);
|
2014-07-04 14:48:30 +02:00
|
|
|
|
2016-03-13 01:12:18 +02:00
|
|
|
ancil->close_fds_on_cleanup = false;
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
fail:
|
|
|
|
|
if (ancil)
|
|
|
|
|
pa_cmsg_ancil_data_close_fds(ancil);
|
|
|
|
|
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
return;
|
2014-07-04 14:48:30 +02:00
|
|
|
#else
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
#endif
|
2014-04-25 17:23:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void pa_command_disable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
|
|
|
|
pa_context *c = userdata;
|
|
|
|
|
pa_tagstruct *t2;
|
|
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(command == PA_COMMAND_DISABLE_SRBCHANNEL);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
|
|
|
|
|
|
|
|
|
pa_pstream_set_srbchannel(c->pstream, NULL);
|
|
|
|
|
|
|
|
|
|
c->srb_template.readfd = -1;
|
|
|
|
|
c->srb_template.writefd = -1;
|
|
|
|
|
if (c->srb_template.memblock) {
|
|
|
|
|
pa_memblock_unref(c->srb_template.memblock);
|
|
|
|
|
c->srb_template.memblock = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Send disable command back again */
|
2014-10-23 16:09:45 +02:00
|
|
|
t2 = pa_tagstruct_new();
|
2014-04-25 17:23:21 +02:00
|
|
|
pa_tagstruct_putu32(t2, PA_COMMAND_DISABLE_SRBCHANNEL);
|
|
|
|
|
pa_tagstruct_putu32(t2, tag);
|
|
|
|
|
pa_pstream_send_tagstruct(c->pstream, t2);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-13 01:12:18 +02:00
|
|
|
static void pa_command_register_memfd_shmid(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
|
|
|
|
pa_context *c = userdata;
|
|
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(command == PA_COMMAND_REGISTER_MEMFD_SHMID);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
|
|
|
|
|
|
|
|
|
if (pa_common_command_register_memfd_shmid(c->pstream, pd, c->version, command, t))
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
}
|
2014-04-25 17:23:21 +02:00
|
|
|
|
2009-02-12 03:18:05 +01:00
|
|
|
void pa_command_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
|
|
|
|
pa_context *c = userdata;
|
|
|
|
|
pa_proplist *pl = NULL;
|
|
|
|
|
const char *event;
|
|
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(command == PA_COMMAND_CLIENT_EVENT);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
|
|
|
|
|
|
|
|
|
pa_context_ref(c);
|
|
|
|
|
|
|
|
|
|
if (c->version < 15) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pl = pa_proplist_new();
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_gets(t, &event) < 0 ||
|
|
|
|
|
pa_tagstruct_get_proplist(t, pl) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t) || !event) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (c->event_callback)
|
|
|
|
|
c->event_callback(c, event, pl, c->event_userdata);
|
|
|
|
|
|
|
|
|
|
finish:
|
|
|
|
|
pa_context_unref(c);
|
|
|
|
|
|
|
|
|
|
if (pl)
|
|
|
|
|
pa_proplist_free(pl);
|
|
|
|
|
}
|
2009-04-05 02:13:43 +03:00
|
|
|
|
2018-06-07 04:00:42 +01:00
|
|
|
pa_time_event* pa_context_rttime_new(const pa_context *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata) {
|
2009-04-05 02:13:43 +03:00
|
|
|
struct timeval tv;
|
|
|
|
|
|
|
|
|
|
pa_assert(c);
|
2009-10-28 23:26:48 +01:00
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2009-04-05 02:13:43 +03:00
|
|
|
pa_assert(c->mainloop);
|
|
|
|
|
|
2009-06-20 16:52:41 +03:00
|
|
|
if (usec == PA_USEC_INVALID)
|
|
|
|
|
return c->mainloop->time_new(c->mainloop, NULL, cb, userdata);
|
|
|
|
|
|
2009-04-05 02:13:43 +03:00
|
|
|
pa_timeval_rtstore(&tv, usec, c->use_rtclock);
|
|
|
|
|
|
|
|
|
|
return c->mainloop->time_new(c->mainloop, &tv, cb, userdata);
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-07 04:02:16 +01:00
|
|
|
void pa_context_rttime_restart(const pa_context *c, pa_time_event *e, pa_usec_t usec) {
|
2009-04-05 02:13:43 +03:00
|
|
|
struct timeval tv;
|
|
|
|
|
|
|
|
|
|
pa_assert(c);
|
2009-10-28 23:26:48 +01:00
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2009-04-05 02:13:43 +03:00
|
|
|
pa_assert(c->mainloop);
|
|
|
|
|
|
2009-06-20 16:52:41 +03:00
|
|
|
if (usec == PA_USEC_INVALID)
|
|
|
|
|
c->mainloop->time_restart(e, NULL);
|
|
|
|
|
else {
|
|
|
|
|
pa_timeval_rtstore(&tv, usec, c->use_rtclock);
|
|
|
|
|
c->mainloop->time_restart(e, &tv);
|
|
|
|
|
}
|
2009-04-05 02:13:43 +03:00
|
|
|
}
|
2009-10-28 23:26:48 +01:00
|
|
|
|
2018-06-07 04:03:45 +01:00
|
|
|
size_t pa_context_get_tile_size(const pa_context *c, const pa_sample_spec *ss) {
|
2009-10-28 23:26:48 +01:00
|
|
|
size_t fs, mbs;
|
|
|
|
|
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
|
|
|
|
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(c, !pa_detect_fork(), PA_ERR_FORKED, (size_t) -1);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(c, !ss || pa_sample_spec_valid(ss), PA_ERR_INVALID, (size_t) -1);
|
|
|
|
|
|
|
|
|
|
fs = ss ? pa_frame_size(ss) : 1;
|
|
|
|
|
mbs = PA_ROUND_DOWN(pa_mempool_block_size_max(c->mempool), fs);
|
|
|
|
|
return PA_MAX(mbs, fs);
|
|
|
|
|
}
|
2013-08-27 03:18:35 +02:00
|
|
|
|
|
|
|
|
int pa_context_load_cookie_from_file(pa_context *c, const char *cookie_file_path) {
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
|
|
|
|
|
|
|
|
|
PA_CHECK_VALIDITY(c, !pa_detect_fork(), PA_ERR_FORKED);
|
|
|
|
|
PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_UNCONNECTED, PA_ERR_BADSTATE);
|
2014-03-19 12:19:08 +02:00
|
|
|
PA_CHECK_VALIDITY(c, !cookie_file_path || *cookie_file_path, PA_ERR_INVALID);
|
2013-08-27 03:18:35 +02:00
|
|
|
|
2014-03-19 12:19:08 +02:00
|
|
|
pa_client_conf_set_cookie_file_from_application(c->conf, cookie_file_path);
|
|
|
|
|
|
|
|
|
|
return 0;
|
2013-08-27 03:18:35 +02:00
|
|
|
}
|