mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-25 06:59:52 -05:00
update upstream-2021-08-15
This commit is contained in:
parent
c8653c13fa
commit
1703683def
297 changed files with 91782 additions and 54869 deletions
|
|
@ -21,7 +21,7 @@
|
|||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_ARPA_INET_H) && defined(OS_IS_WIN32)
|
||||
#if !defined(HAVE_ARPA_INET_H) && defined(OS_IS_WIN32) && (_WIN32_WINNT < 0x0600)
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@
|
|||
#elif defined(OS_IS_WIN32)
|
||||
|
||||
/* On Windows winsock2.h (here included via pulsecore/socket.h) provides most of the functionality of arpa/inet.h, except for
|
||||
* the inet_ntop and inet_pton functions, which are implemented here. */
|
||||
* the inet_ntop and inet_pton functions, which are implemented here on versions earlier than Vista. */
|
||||
|
||||
#include <pulsecore/socket.h>
|
||||
|
||||
#if (_WIN32_WINNT < 0x0600)
|
||||
|
||||
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);
|
||||
|
||||
int inet_pton(int af, const char *src, void *dst);
|
||||
|
|
@ -19,3 +21,5 @@ int inet_pton(int af, const char *src, void *dst);
|
|||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -218,39 +218,6 @@ static inline bool pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void*
|
|||
#include <sys/param.h>
|
||||
#include <machine/atomic.h>
|
||||
|
||||
#if __FreeBSD_version < 600000
|
||||
#if defined(__i386__) || defined(__amd64__)
|
||||
#if defined(__amd64__)
|
||||
#define atomic_load_acq_64 atomic_load_acq_long
|
||||
#endif
|
||||
static inline u_int atomic_fetchadd_int(volatile u_int *p, u_int v) {
|
||||
__asm __volatile(
|
||||
" " __XSTRING(MPLOCKED) " "
|
||||
" xaddl %0, %1 ; "
|
||||
"# atomic_fetchadd_int"
|
||||
: "+r" (v),
|
||||
"=m" (*p)
|
||||
: "m" (*p));
|
||||
|
||||
return (v);
|
||||
}
|
||||
#elif defined(__sparc__) && defined(__arch64__)
|
||||
#define atomic_load_acq_64 atomic_load_acq_long
|
||||
#define atomic_fetchadd_int atomic_add_int
|
||||
#elif defined(__ia64__)
|
||||
#define atomic_load_acq_64 atomic_load_acq_long
|
||||
static inline uint32_t
|
||||
atomic_fetchadd_int(volatile uint32_t *p, uint32_t v) {
|
||||
uint32_t value;
|
||||
|
||||
do {
|
||||
value = *p;
|
||||
} while (!atomic_cmpset_32(p, value, value + v));
|
||||
return (value);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef struct pa_atomic {
|
||||
volatile unsigned long value;
|
||||
} pa_atomic_t;
|
||||
|
|
|
|||
|
|
@ -28,14 +28,22 @@
|
|||
#include <pulse/xmalloc.h>
|
||||
#include <pulse/util.h>
|
||||
|
||||
#include <pulsecore/json.h>
|
||||
#include <pulsecore/log.h>
|
||||
#include <pulsecore/macro.h>
|
||||
#include <pulsecore/core-util.h>
|
||||
#include <pulsecore/namereg.h>
|
||||
#include <pulsecore/message-handler.h>
|
||||
#include <pulsecore/device-port.h>
|
||||
|
||||
#include "card.h"
|
||||
|
||||
static int card_message_handler(const char *object_path, const char *message, const pa_json_object *parameters, char **response, void *userdata);
|
||||
|
||||
static char* make_message_handler_path(const char *name) {
|
||||
return pa_sprintf_malloc("/card/%s", name);
|
||||
}
|
||||
|
||||
const char *pa_available_to_string(pa_available_t available) {
|
||||
switch (available) {
|
||||
case PA_AVAILABLE_UNKNOWN:
|
||||
|
|
@ -136,7 +144,8 @@ void pa_card_new_data_done(pa_card_new_data *data) {
|
|||
|
||||
pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
|
||||
pa_card *c;
|
||||
const char *name;
|
||||
const char *name, *tmp;
|
||||
char *object_path, *description;
|
||||
void *state;
|
||||
pa_card_profile *profile;
|
||||
pa_device_port *port;
|
||||
|
|
@ -186,6 +195,14 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
|
|||
pa_device_init_icon(c->proplist, true);
|
||||
pa_device_init_intended_roles(c->proplist);
|
||||
|
||||
object_path = make_message_handler_path(c->name);
|
||||
if (!(tmp = pa_proplist_gets(c->proplist, PA_PROP_DEVICE_DESCRIPTION)))
|
||||
tmp = c->name;
|
||||
description = pa_sprintf_malloc("Message handler for card \"%s\"", tmp);
|
||||
pa_message_handler_register(c->core, object_path, description, card_message_handler, (void *) c);
|
||||
pa_xfree(object_path);
|
||||
pa_xfree(description);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
|
@ -220,6 +237,7 @@ void pa_card_choose_initial_profile(pa_card *card) {
|
|||
|
||||
card->active_profile = best;
|
||||
card->save_profile = false;
|
||||
card->profile_is_sticky = false;
|
||||
pa_log_info("%s: active_profile: %s", card->name, card->active_profile->name);
|
||||
|
||||
/* Let policy modules override the default. */
|
||||
|
|
@ -239,6 +257,7 @@ void pa_card_put(pa_card *card) {
|
|||
|
||||
void pa_card_free(pa_card *c) {
|
||||
pa_core *core;
|
||||
char *object_path;
|
||||
|
||||
pa_assert(c);
|
||||
pa_assert(c->core);
|
||||
|
|
@ -253,6 +272,10 @@ void pa_card_free(pa_card *c) {
|
|||
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
|
||||
}
|
||||
|
||||
object_path = make_message_handler_path(c->name);
|
||||
pa_message_handler_unregister(core, object_path);
|
||||
pa_xfree(object_path);
|
||||
|
||||
pa_namereg_unregister(core, c->name);
|
||||
|
||||
pa_assert(pa_idxset_isempty(c->sinks));
|
||||
|
|
@ -305,6 +328,25 @@ static void update_port_preferred_profile(pa_card *c) {
|
|||
pa_device_port_set_preferred_profile(source->active_port, profile_name_for_dir(c->active_profile, PA_DIRECTION_INPUT));
|
||||
}
|
||||
|
||||
static int card_set_profile_is_sticky(pa_card *c, bool profile_is_sticky) {
|
||||
pa_assert(c);
|
||||
|
||||
if (c->profile_is_sticky == profile_is_sticky)
|
||||
return 0;
|
||||
|
||||
pa_log_debug("%s: profile_is_sticky: %s -> %s",
|
||||
c->name, pa_yes_no(c->profile_is_sticky), pa_yes_no(profile_is_sticky));
|
||||
|
||||
c->profile_is_sticky = profile_is_sticky;
|
||||
|
||||
if (c->linked) {
|
||||
pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGED], c);
|
||||
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pa_card_set_profile(pa_card *c, pa_card_profile *profile, bool save) {
|
||||
int r;
|
||||
|
||||
|
|
@ -423,3 +465,44 @@ int pa_card_suspend(pa_card *c, bool suspend, pa_suspend_cause_t cause) {
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int card_message_handler(const char *object_path, const char *message, const pa_json_object *parameters, char **response, void *userdata) {
|
||||
pa_card *c;
|
||||
char *message_handler_path;
|
||||
|
||||
pa_assert(c = (pa_card *) userdata);
|
||||
pa_assert(message);
|
||||
pa_assert(response);
|
||||
|
||||
message_handler_path = make_message_handler_path(c->name);
|
||||
|
||||
if (!object_path || !pa_streq(object_path, message_handler_path)) {
|
||||
pa_xfree(message_handler_path);
|
||||
return -PA_ERR_NOENTITY;
|
||||
}
|
||||
|
||||
pa_xfree(message_handler_path);
|
||||
|
||||
if (pa_streq(message, "get-profile-sticky")) {
|
||||
pa_json_encoder *encoder;
|
||||
encoder = pa_json_encoder_new();
|
||||
|
||||
pa_json_encoder_add_element_bool(encoder, c->profile_is_sticky);
|
||||
|
||||
*response = pa_json_encoder_to_string_free(encoder);
|
||||
|
||||
return PA_OK;
|
||||
} else if (pa_streq(message, "set-profile-sticky")) {
|
||||
|
||||
if (!parameters || pa_json_object_get_type(parameters) != PA_JSON_TYPE_BOOL) {
|
||||
pa_log_info("Card operation set-profile-sticky requires argument: \"true\" or \"false\"");
|
||||
return -PA_ERR_INVALID;
|
||||
}
|
||||
|
||||
card_set_profile_is_sticky(c, pa_json_object_get_bool(parameters));
|
||||
|
||||
return PA_OK;
|
||||
}
|
||||
|
||||
return -PA_ERR_NOTIMPLEMENTED;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ struct pa_card {
|
|||
pa_device_port *preferred_output_port;
|
||||
|
||||
bool save_profile:1;
|
||||
bool profile_is_sticky:1;
|
||||
|
||||
pa_suspend_cause_t suspend_cause;
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@
|
|||
#include <pulsecore/sound-file-stream.h>
|
||||
#include <pulsecore/shared.h>
|
||||
#include <pulsecore/core-util.h>
|
||||
#include <pulsecore/message-handler.h>
|
||||
#include <pulsecore/core-error.h>
|
||||
#include <pulsecore/modinfo.h>
|
||||
#include <pulsecore/dynarray.h>
|
||||
|
|
@ -135,6 +136,7 @@ static int pa_cli_command_sink_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
|
|||
static int pa_cli_command_source_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
|
||||
static int pa_cli_command_port_offset(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
|
||||
static int pa_cli_command_dump_volumes(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
|
||||
static int pa_cli_command_send_message_to_object(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
|
||||
|
||||
/* A method table for all available commands */
|
||||
|
||||
|
|
@ -191,6 +193,7 @@ static const struct command commands[] = {
|
|||
{ "set-log-meta", pa_cli_command_log_meta, "Show source code location in log messages (args: bool)", 2},
|
||||
{ "set-log-time", pa_cli_command_log_time, "Show timestamps in log messages (args: bool)", 2},
|
||||
{ "set-log-backtrace", pa_cli_command_log_backtrace, "Show backtrace in log messages (args: frames)", 2},
|
||||
{ "send-message", pa_cli_command_send_message_to_object, "Send a message to an object (args: recipient, message, message_parameters)", 4},
|
||||
{ "play-file", pa_cli_command_play_file, "Play a sound file (args: filename, sink|index)", 3},
|
||||
{ "dump", pa_cli_command_dump, "Dump daemon configuration", 1},
|
||||
{ "dump-volumes", pa_cli_command_dump_volumes, "Debug: Show the state of all volumes", 1 },
|
||||
|
|
@ -1784,6 +1787,47 @@ static int pa_cli_command_port_offset(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pa_cli_command_send_message_to_object(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
|
||||
const char *object_path, *message, *message_parameters;
|
||||
char *response = NULL;
|
||||
int ret;
|
||||
|
||||
pa_core_assert_ref(c);
|
||||
pa_assert(t);
|
||||
pa_assert(buf);
|
||||
pa_assert(fail);
|
||||
|
||||
|
||||
if (!(object_path = pa_tokenizer_get(t, 1))) {
|
||||
pa_strbuf_puts(buf, "You need to specify an object path as recipient for the message.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(message = pa_tokenizer_get(t, 2))) {
|
||||
pa_strbuf_puts(buf, "You need to specify a message name.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* parameters may be NULL */
|
||||
message_parameters = pa_tokenizer_get(t, 3);
|
||||
|
||||
ret = pa_message_handler_send_message(c, object_path, message, message_parameters, &response);
|
||||
|
||||
if (ret < 0) {
|
||||
pa_strbuf_printf(buf, "Send message failed: %s\n", pa_strerror(ret));
|
||||
ret = -1;
|
||||
|
||||
} else {
|
||||
if (response)
|
||||
pa_strbuf_puts(buf, response);
|
||||
pa_strbuf_puts(buf, "\n");
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
pa_xfree(response);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
|
||||
pa_module *m;
|
||||
pa_sink *sink;
|
||||
|
|
@ -2040,20 +2084,34 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b
|
|||
|
||||
if (l == sizeof(META_INCLUDE)-1 && !strncmp(cs, META_INCLUDE, l)) {
|
||||
struct stat st;
|
||||
const char *filename = cs+l+strspn(cs+l, whitespace);
|
||||
const char *fn = cs+l+strspn(cs+l, whitespace);
|
||||
|
||||
char *filename;
|
||||
#ifdef OS_IS_WIN32
|
||||
if (strncmp(fn, PA_DEFAULT_CONFIG_DIR, strlen(PA_DEFAULT_CONFIG_DIR)) == 0)
|
||||
filename = pa_sprintf_malloc("%s" PA_PATH_SEP "etc" PA_PATH_SEP "pulse" PA_PATH_SEP "%s",
|
||||
pa_win32_get_toplevel(NULL),
|
||||
fn + strlen(PA_DEFAULT_CONFIG_DIR));
|
||||
else
|
||||
#endif
|
||||
filename = pa_xstrdup(fn);
|
||||
|
||||
if (stat(filename, &st) < 0) {
|
||||
pa_log_warn("stat('%s'): %s", filename, pa_cstrerror(errno));
|
||||
if (*fail)
|
||||
if (*fail) {
|
||||
pa_xfree(filename);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
DIR *d;
|
||||
|
||||
if (!(d = opendir(filename))) {
|
||||
pa_log_warn("Failed to read '%s': %s", filename, pa_cstrerror(errno));
|
||||
if (*fail)
|
||||
if (*fail) {
|
||||
pa_xfree(filename);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
unsigned i, count;
|
||||
char **sorted_files;
|
||||
|
|
@ -2074,39 +2132,43 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b
|
|||
}
|
||||
|
||||
closedir(d);
|
||||
if ((count = pa_dynarray_size(files))) {
|
||||
sorted_files = pa_xnew(char*, count);
|
||||
for (i = 0; i < count; ++i)
|
||||
sorted_files[i] = pa_dynarray_get(files, i);
|
||||
pa_dynarray_free(files);
|
||||
|
||||
count = pa_dynarray_size(files);
|
||||
sorted_files = pa_xnew(char*, count);
|
||||
for (i = 0; i < count; ++i)
|
||||
sorted_files[i] = pa_dynarray_get(files, i);
|
||||
pa_dynarray_free(files);
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
for (unsigned j = 0; j < count; ++j) {
|
||||
if (strcmp(sorted_files[i], sorted_files[j]) < 0) {
|
||||
char *tmp = sorted_files[i];
|
||||
sorted_files[i] = sorted_files[j];
|
||||
sorted_files[j] = tmp;
|
||||
for (i = 0; i < count; ++i) {
|
||||
for (unsigned j = 0; j < count; ++j) {
|
||||
if (strcmp(sorted_files[i], sorted_files[j]) < 0) {
|
||||
char *tmp = sorted_files[i];
|
||||
sorted_files[i] = sorted_files[j];
|
||||
sorted_files[j] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
if (!failed) {
|
||||
if (pa_cli_command_execute_file(c, sorted_files[i], buf, fail) < 0 && *fail)
|
||||
failed = true;
|
||||
for (i = 0; i < count; ++i) {
|
||||
if (!failed) {
|
||||
if (pa_cli_command_execute_file(c, sorted_files[i], buf, fail) < 0 && *fail)
|
||||
failed = true;
|
||||
}
|
||||
|
||||
pa_xfree(sorted_files[i]);
|
||||
}
|
||||
pa_xfree(sorted_files);
|
||||
if (failed) {
|
||||
pa_xfree(filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pa_xfree(sorted_files[i]);
|
||||
}
|
||||
pa_xfree(sorted_files);
|
||||
if (failed)
|
||||
return -1;
|
||||
}
|
||||
} else if (pa_cli_command_execute_file(c, filename, buf, fail) < 0 && *fail) {
|
||||
pa_xfree(filename);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
pa_xfree(filename);
|
||||
} else if (l == sizeof(META_IFEXISTS)-1 && !strncmp(cs, META_IFEXISTS, l)) {
|
||||
if (!ifstate) {
|
||||
pa_strbuf_printf(buf, "Meta command %s is not valid in this context\n", cs);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
/* Every connection to the server should have a pa_client
|
||||
* attached. That way the user may generate a listing of all connected
|
||||
* clients easily and kill them if he wants.*/
|
||||
* clients easily and kill them if they want.*/
|
||||
|
||||
struct pa_client {
|
||||
uint32_t index;
|
||||
|
|
|
|||
|
|
@ -251,7 +251,7 @@ finish:
|
|||
LocalFree(msgbuf);
|
||||
} else {
|
||||
pa_log_warn("FindFirstFile(%s) failed with error %ld, ignoring.", pattern, err);
|
||||
pa_log_warn("FormatMessage failed with error %ld", GetLastError());
|
||||
pa_log_warn("FormatMessage failed with error %lu", GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@
|
|||
|
||||
#ifdef HAVE_WINDOWS_H
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
|
||||
#ifndef ENOTSUP
|
||||
|
|
@ -171,6 +172,15 @@ char *pa_win32_get_toplevel(HANDLE handle) {
|
|||
return toplevel;
|
||||
}
|
||||
|
||||
char *pa_win32_get_system_appdata() {
|
||||
static char appdata[MAX_PATH] = {0};
|
||||
|
||||
if (!*appdata && SHGetFolderPathAndSubDirA(NULL, CSIDL_COMMON_APPDATA|CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, "PulseAudio", appdata) != S_OK)
|
||||
return NULL;
|
||||
|
||||
return appdata;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void set_nonblock(int fd, bool nonblock) {
|
||||
|
|
@ -407,6 +417,8 @@ ssize_t pa_read(int fd, void *buf, size_t count, int *type) {
|
|||
|
||||
if (WSAGetLastError() != WSAENOTSOCK) {
|
||||
errno = WSAGetLastError();
|
||||
if (errno == WSAEWOULDBLOCK)
|
||||
errno = EAGAIN;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
@ -448,6 +460,8 @@ ssize_t pa_write(int fd, const void *buf, size_t count, int *type) {
|
|||
#ifdef OS_IS_WIN32
|
||||
if (WSAGetLastError() != WSAENOTSOCK) {
|
||||
errno = WSAGetLastError();
|
||||
if (errno == WSAEWOULDBLOCK)
|
||||
errno = EAGAIN;
|
||||
return r;
|
||||
}
|
||||
#else
|
||||
|
|
@ -755,7 +769,7 @@ int pa_raise_priority(int nice_level) {
|
|||
#ifdef OS_IS_WIN32
|
||||
if (nice_level < 0) {
|
||||
if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) {
|
||||
pa_log_warn("SetPriorityClass() failed: 0x%08X", GetLastError());
|
||||
pa_log_warn("SetPriorityClass() failed: 0x%08lX", GetLastError());
|
||||
errno = EPERM;
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -1321,7 +1335,7 @@ int pa_lock_fd(int fd, int b) {
|
|||
if (!b && UnlockFile(h, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF))
|
||||
return 0;
|
||||
|
||||
pa_log("%slock failed: 0x%08X", !b ? "un" : "", GetLastError());
|
||||
pa_log("%slock failed: 0x%08lX", !b ? "un" : "", GetLastError());
|
||||
|
||||
/* FIXME: Needs to set errno! */
|
||||
#endif
|
||||
|
|
@ -1568,6 +1582,70 @@ int pa_get_config_home_dir(char **_r) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int pa_get_data_home_dir(char **_r) {
|
||||
const char *e;
|
||||
char *home_dir;
|
||||
|
||||
pa_assert(_r);
|
||||
|
||||
e = getenv("XDG_DATA_HOME");
|
||||
if (e && *e) {
|
||||
if (pa_is_path_absolute(e)) {
|
||||
*_r = pa_sprintf_malloc("%s" PA_PATH_SEP "pulseaudio", e);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
pa_log_warn("Ignored non-absolute XDG_DATA_HOME value '%s'", e);
|
||||
}
|
||||
|
||||
home_dir = pa_get_home_dir_malloc();
|
||||
if (!home_dir)
|
||||
return -PA_ERR_NOENTITY;
|
||||
|
||||
*_r = pa_sprintf_malloc("%s" PA_PATH_SEP ".local" PA_PATH_SEP "share" PA_PATH_SEP "pulseaudio", home_dir);
|
||||
pa_xfree(home_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pa_get_data_dirs(pa_dynarray **_r) {
|
||||
const char *e;
|
||||
const char *def = "/usr/local/share/:/usr/share/";
|
||||
const char *p;
|
||||
const char *split_state = NULL;
|
||||
char *n;
|
||||
pa_dynarray *paths;
|
||||
|
||||
pa_assert(_r);
|
||||
|
||||
e = getenv("XDG_DATA_DIRS");
|
||||
p = e && *e ? e : def;
|
||||
|
||||
paths = pa_dynarray_new((pa_free_cb_t) pa_xfree);
|
||||
|
||||
while ((n = pa_split(p, ":", &split_state))) {
|
||||
char *path;
|
||||
|
||||
if (!pa_is_path_absolute(n)) {
|
||||
pa_log_warn("Ignored non-absolute path '%s' in XDG_DATA_DIRS", n);
|
||||
pa_xfree(n);
|
||||
continue;
|
||||
}
|
||||
|
||||
path = pa_sprintf_malloc("%s" PA_PATH_SEP "pulseaudio", n);
|
||||
pa_xfree(n);
|
||||
pa_dynarray_append(paths, path);
|
||||
}
|
||||
|
||||
if (pa_dynarray_size(paths) == 0) {
|
||||
pa_log_warn("XDG_DATA_DIRS contains no valid paths");
|
||||
pa_dynarray_free(paths);
|
||||
return -PA_ERR_INVALID;
|
||||
}
|
||||
|
||||
*_r = paths;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pa_append_to_config_home_dir(const char *path, char **_r) {
|
||||
int r;
|
||||
char *config_home_dir;
|
||||
|
|
@ -2189,7 +2267,7 @@ int pa_atoi(const char *s, int32_t *ret_i) {
|
|||
if (pa_atol(s, &l) < 0)
|
||||
return -1;
|
||||
|
||||
if ((int32_t) l != l) {
|
||||
if (l < INT32_MIN || l > INT32_MAX) {
|
||||
errno = ERANGE;
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -2199,6 +2277,90 @@ int pa_atoi(const char *s, int32_t *ret_i) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
enum numtype {
|
||||
NUMTYPE_UINT,
|
||||
NUMTYPE_INT,
|
||||
NUMTYPE_DOUBLE,
|
||||
};
|
||||
|
||||
/* A helper function for pa_atou() and friends. This does some common checks,
|
||||
* because our number parsing is more strict than the strtoX functions.
|
||||
*
|
||||
* Leading zeros are stripped from integers so that they don't get parsed as
|
||||
* octal (but "0x" is preserved for hexadecimal numbers). For NUMTYPE_INT the
|
||||
* zero stripping may involve allocating a new string, in which case it's
|
||||
* stored in tmp. Otherwise tmp is set to NULL. The caller needs to free tmp
|
||||
* after they're done with ret. When parsing other types than NUMTYPE_INT the
|
||||
* caller can pass NULL as tmp.
|
||||
*
|
||||
* The final string to parse is returned in ret. ret will point either inside
|
||||
* s or to tmp. */
|
||||
static int prepare_number_string(const char *s, enum numtype type, char **tmp, const char **ret) {
|
||||
const char *original = s;
|
||||
bool negative = false;
|
||||
|
||||
pa_assert(s);
|
||||
pa_assert(type != NUMTYPE_INT || tmp);
|
||||
pa_assert(ret);
|
||||
|
||||
if (tmp)
|
||||
*tmp = NULL;
|
||||
|
||||
/* The strtoX functions accept leading spaces, we don't. */
|
||||
if (isspace((unsigned char) s[0]))
|
||||
return -1;
|
||||
|
||||
/* The strtoX functions accept a plus sign, we don't. */
|
||||
if (s[0] == '+')
|
||||
return -1;
|
||||
|
||||
/* The strtoul and strtoull functions allow a minus sign even though they
|
||||
* parse an unsigned number. In case of a minus sign the original negative
|
||||
* number gets negated. We don't want that kind of behviour. */
|
||||
if (type == NUMTYPE_UINT && s[0] == '-')
|
||||
return -1;
|
||||
|
||||
/* The strtoX functions interpret the number as octal if it starts with
|
||||
* a zero. We prefer to use base 10, so we strip all leading zeros (if the
|
||||
* string starts with "0x", strtoul() interprets it as hexadecimal, which
|
||||
* is fine, because it's unambiguous unlike octal).
|
||||
*
|
||||
* While stripping the leading zeros, we have to remember to also handle
|
||||
* the case where the number is negative, which makes the zero skipping
|
||||
* code somewhat complex. */
|
||||
|
||||
/* Doubles don't need zero stripping, we can finish now. */
|
||||
if (type == NUMTYPE_DOUBLE)
|
||||
goto finish;
|
||||
|
||||
if (s[0] == '-') {
|
||||
negative = true;
|
||||
s++; /* Skip the minus sign. */
|
||||
}
|
||||
|
||||
/* Don't skip zeros if the string starts with "0x". */
|
||||
if (s[0] == '0' && s[1] != 'x') {
|
||||
while (s[0] == '0' && s[1])
|
||||
s++; /* Skip zeros. */
|
||||
}
|
||||
|
||||
if (negative) {
|
||||
s--; /* Go back one step, we need the minus sign back. */
|
||||
|
||||
/* If s != original, then we have skipped some zeros and we need to replace
|
||||
* the last skipped zero with a minus sign. */
|
||||
if (s != original) {
|
||||
*tmp = pa_xstrdup(s);
|
||||
*tmp[0] = '-';
|
||||
s = *tmp;
|
||||
}
|
||||
}
|
||||
|
||||
finish:
|
||||
*ret = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Convert the string s to an unsigned integer in *ret_u */
|
||||
int pa_atou(const char *s, uint32_t *ret_u) {
|
||||
char *x = NULL;
|
||||
|
|
@ -2207,17 +2369,7 @@ int pa_atou(const char *s, uint32_t *ret_u) {
|
|||
pa_assert(s);
|
||||
pa_assert(ret_u);
|
||||
|
||||
/* strtoul() ignores leading spaces. We don't. */
|
||||
if (isspace((unsigned char)*s)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* strtoul() accepts strings that start with a minus sign. In that case the
|
||||
* original negative number gets negated, and strtoul() returns the negated
|
||||
* result. We don't want that kind of behaviour. strtoul() also allows a
|
||||
* leading plus sign, which is also a thing that we don't want. */
|
||||
if (*s == '-' || *s == '+') {
|
||||
if (prepare_number_string(s, NUMTYPE_UINT, NULL, &s) < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -2233,7 +2385,7 @@ int pa_atou(const char *s, uint32_t *ret_u) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
if ((uint32_t) l != l) {
|
||||
if (l > UINT32_MAX) {
|
||||
errno = ERANGE;
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -2243,23 +2395,50 @@ int pa_atou(const char *s, uint32_t *ret_u) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Convert the string s to an unsigned 64 bit integer in *ret_u */
|
||||
int pa_atou64(const char *s, uint64_t *ret_u) {
|
||||
char *x = NULL;
|
||||
unsigned long long l;
|
||||
|
||||
pa_assert(s);
|
||||
pa_assert(ret_u);
|
||||
|
||||
if (prepare_number_string(s, NUMTYPE_UINT, NULL, &s) < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
l = strtoull(s, &x, 0);
|
||||
|
||||
/* If x doesn't point to the end of s, there was some trailing garbage in
|
||||
* the string. If x points to s, no conversion was done (empty string). */
|
||||
if (!x || *x || x == s || errno) {
|
||||
if (!errno)
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (l > UINT64_MAX) {
|
||||
errno = ERANGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*ret_u = (uint64_t) l;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Convert the string s to a signed long integer in *ret_l. */
|
||||
int pa_atol(const char *s, long *ret_l) {
|
||||
char *tmp;
|
||||
char *x = NULL;
|
||||
long l;
|
||||
|
||||
pa_assert(s);
|
||||
pa_assert(ret_l);
|
||||
|
||||
/* strtol() ignores leading spaces. We don't. */
|
||||
if (isspace((unsigned char)*s)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* strtol() accepts leading plus signs, but that's ugly, so we don't allow
|
||||
* that. */
|
||||
if (*s == '+') {
|
||||
if (prepare_number_string(s, NUMTYPE_INT, &tmp, &s) < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -2273,14 +2452,56 @@ int pa_atol(const char *s, long *ret_l) {
|
|||
if (!x || *x || x == s || errno) {
|
||||
if (!errno)
|
||||
errno = EINVAL;
|
||||
pa_xfree(tmp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pa_xfree(tmp);
|
||||
|
||||
*ret_l = l;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Convert the string s to a signed 64 bit integer in *ret_l. */
|
||||
int pa_atoi64(const char *s, int64_t *ret_l) {
|
||||
char *tmp;
|
||||
char *x = NULL;
|
||||
long long l;
|
||||
|
||||
pa_assert(s);
|
||||
pa_assert(ret_l);
|
||||
|
||||
if (prepare_number_string(s, NUMTYPE_INT, &tmp, &s) < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
l = strtoll(s, &x, 0);
|
||||
|
||||
/* If x doesn't point to the end of s, there was some trailing garbage in
|
||||
* the string. If x points to s, no conversion was done (at least an empty
|
||||
* string can trigger this). */
|
||||
if (!x || *x || x == s || errno) {
|
||||
if (!errno)
|
||||
errno = EINVAL;
|
||||
pa_xfree(tmp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pa_xfree(tmp);
|
||||
|
||||
*ret_l = l;
|
||||
|
||||
if (l < INT64_MIN || l > INT64_MAX) {
|
||||
errno = ERANGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_STRTOD_L
|
||||
static locale_t c_locale = NULL;
|
||||
|
||||
|
|
@ -2296,15 +2517,7 @@ int pa_atod(const char *s, double *ret_d) {
|
|||
pa_assert(s);
|
||||
pa_assert(ret_d);
|
||||
|
||||
/* strtod() ignores leading spaces. We don't. */
|
||||
if (isspace((unsigned char)*s)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* strtod() accepts leading plus signs, but that's ugly, so we don't allow
|
||||
* that. */
|
||||
if (*s == '+') {
|
||||
if (prepare_number_string(s, NUMTYPE_DOUBLE, NULL, &s) < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -2802,7 +3015,16 @@ void pa_set_env(const char *key, const char *value) {
|
|||
/* This is not thread-safe */
|
||||
|
||||
#ifdef OS_IS_WIN32
|
||||
SetEnvironmentVariable(key, value);
|
||||
int kl = strlen(key);
|
||||
int vl = strlen(value);
|
||||
char *tmp = pa_xmalloc(kl+vl+2);
|
||||
memcpy(tmp, key, kl);
|
||||
memcpy(tmp+kl+1, value, vl);
|
||||
tmp[kl] = '=';
|
||||
tmp[kl+1+vl] = '\0';
|
||||
putenv(tmp);
|
||||
/* Even though it should be safe to free it on Windows, we don't want to
|
||||
* rely on undocumented behaviour. */
|
||||
#else
|
||||
setenv(key, value, 1);
|
||||
#endif
|
||||
|
|
@ -2814,7 +3036,14 @@ void pa_unset_env(const char *key) {
|
|||
/* This is not thread-safe */
|
||||
|
||||
#ifdef OS_IS_WIN32
|
||||
SetEnvironmentVariable(key, NULL);
|
||||
int kl = strlen(key);
|
||||
char *tmp = pa_xmalloc(kl+2);
|
||||
memcpy(tmp, key, kl);
|
||||
tmp[kl] = '=';
|
||||
tmp[kl+1] = '\0';
|
||||
putenv(tmp);
|
||||
/* Even though it should be safe to free it on Windows, we don't want to
|
||||
* rely on undocumented behaviour. */
|
||||
#else
|
||||
unsetenv(key);
|
||||
#endif
|
||||
|
|
@ -3053,7 +3282,7 @@ char *pa_uname_string(void) {
|
|||
i.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
||||
pa_assert_se(GetVersionEx(&i));
|
||||
|
||||
return pa_sprintf_malloc("Windows %d.%d (%d) %s", i.dwMajorVersion, i.dwMinorVersion, i.dwBuildNumber, i.szCSDVersion);
|
||||
return pa_sprintf_malloc("Windows %lu.%lu (%lu) %s", i.dwMajorVersion, i.dwMinorVersion, i.dwBuildNumber, i.szCSDVersion);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include <pulsecore/i18n.h>
|
||||
#include <pulsecore/macro.h>
|
||||
#include <pulsecore/socket.h>
|
||||
#include <pulsecore/dynarray.h>
|
||||
|
||||
#ifndef PACKAGE
|
||||
#error "Please include config.h before including this file!"
|
||||
|
|
@ -142,6 +143,8 @@ char *pa_get_state_dir(void);
|
|||
char *pa_get_home_dir_malloc(void);
|
||||
int pa_append_to_home_dir(const char *path, char **_r);
|
||||
int pa_get_config_home_dir(char **_r);
|
||||
int pa_get_data_home_dir(char **_r);
|
||||
int pa_get_data_dirs(pa_dynarray **_r);
|
||||
int pa_append_to_config_home_dir(const char *path, char **_r);
|
||||
char *pa_get_binary_name_malloc(void);
|
||||
char *pa_runtime_path(const char *fn);
|
||||
|
|
@ -151,6 +154,8 @@ int pa_atoi(const char *s, int32_t *ret_i);
|
|||
int pa_atou(const char *s, uint32_t *ret_u);
|
||||
int pa_atol(const char *s, long *ret_l);
|
||||
int pa_atod(const char *s, double *ret_d);
|
||||
int pa_atoi64(const char *s, int64_t *ret_l);
|
||||
int pa_atou64(const char *s, uint64_t *ret_u);
|
||||
|
||||
size_t pa_snprintf(char *str, size_t size, const char *format, ...);
|
||||
size_t pa_vsnprintf(char *str, size_t size, const char *format, va_list ap);
|
||||
|
|
@ -305,6 +310,7 @@ bool pa_running_in_vm(void);
|
|||
|
||||
#ifdef OS_IS_WIN32
|
||||
char *pa_win32_get_toplevel(HANDLE handle);
|
||||
char *pa_win32_get_system_appdata();
|
||||
#endif
|
||||
|
||||
size_t pa_page_size(void);
|
||||
|
|
|
|||
|
|
@ -33,11 +33,13 @@
|
|||
#include <pulsecore/module.h>
|
||||
#include <pulsecore/core-rtclock.h>
|
||||
#include <pulsecore/core-util.h>
|
||||
#include <pulsecore/message-handler.h>
|
||||
#include <pulsecore/core-scache.h>
|
||||
#include <pulsecore/core-subscribe.h>
|
||||
#include <pulsecore/random.h>
|
||||
#include <pulsecore/log.h>
|
||||
#include <pulsecore/macro.h>
|
||||
#include <pulsecore/strbuf.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
|
|
@ -61,6 +63,44 @@ static int core_process_msg(pa_msgobject *o, int code, void *userdata, int64_t o
|
|||
|
||||
static void core_free(pa_object *o);
|
||||
|
||||
/* Returns a list of handlers. */
|
||||
static char *message_handler_list(pa_core *c) {
|
||||
pa_json_encoder *encoder;
|
||||
void *state = NULL;
|
||||
struct pa_message_handler *handler;
|
||||
|
||||
encoder = pa_json_encoder_new();
|
||||
|
||||
pa_json_encoder_begin_element_array(encoder);
|
||||
PA_HASHMAP_FOREACH(handler, c->message_handlers, state) {
|
||||
pa_json_encoder_begin_element_object(encoder);
|
||||
|
||||
pa_json_encoder_add_member_string(encoder, "name", handler->object_path);
|
||||
pa_json_encoder_add_member_string(encoder, "description", handler->description);
|
||||
|
||||
pa_json_encoder_end_object(encoder);
|
||||
}
|
||||
pa_json_encoder_end_array(encoder);
|
||||
|
||||
return pa_json_encoder_to_string_free(encoder);
|
||||
}
|
||||
|
||||
static int core_message_handler(const char *object_path, const char *message, const pa_json_object *parameters, char **response, void *userdata) {
|
||||
pa_core *c;
|
||||
|
||||
pa_assert(c = (pa_core *) userdata);
|
||||
pa_assert(message);
|
||||
pa_assert(response);
|
||||
pa_assert(pa_safe_streq(object_path, "/core"));
|
||||
|
||||
if (pa_streq(message, "list-handlers")) {
|
||||
*response = message_handler_list(c);
|
||||
return PA_OK;
|
||||
}
|
||||
|
||||
return -PA_ERR_NOTIMPLEMENTED;
|
||||
}
|
||||
|
||||
pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t shm_size) {
|
||||
pa_core* c;
|
||||
pa_mempool *pool;
|
||||
|
|
@ -105,6 +145,8 @@ pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t
|
|||
c->shared = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
|
||||
c->message_handlers = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
|
||||
|
||||
pa_message_handler_register(c, "/core", "Core message handler", core_message_handler, (void *) c);
|
||||
|
||||
c->default_source = NULL;
|
||||
c->default_sink = NULL;
|
||||
|
||||
|
|
@ -159,10 +201,6 @@ pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t
|
|||
pa_check_signal_is_blocked(SIGPIPE);
|
||||
#endif
|
||||
|
||||
pa_core_check_idle(c);
|
||||
|
||||
c->state = PA_CORE_RUNNING;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
|
@ -206,6 +244,8 @@ static void core_free(pa_object *o) {
|
|||
pa_assert(pa_hashmap_isempty(c->shared));
|
||||
pa_hashmap_free(c->shared);
|
||||
|
||||
pa_message_handler_unregister(c, "/core");
|
||||
|
||||
pa_assert(pa_hashmap_isempty(c->message_handlers));
|
||||
pa_hashmap_free(c->message_handlers);
|
||||
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ typedef enum pa_core_hook {
|
|||
PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED,
|
||||
PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED,
|
||||
PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED,
|
||||
PA_CORE_HOOK_SINK_INPUT_PREFERRED_SINK_CHANGED,
|
||||
PA_CORE_HOOK_SINK_INPUT_SEND_EVENT,
|
||||
PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
|
||||
PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
|
||||
|
|
@ -117,6 +118,7 @@ typedef enum pa_core_hook {
|
|||
PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED,
|
||||
PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED,
|
||||
PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED,
|
||||
PA_CORE_HOOK_SOURCE_OUTPUT_PREFERRED_SOURCE_CHANGED,
|
||||
PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT,
|
||||
PA_CORE_HOOK_CLIENT_NEW,
|
||||
PA_CORE_HOOK_CLIENT_PUT,
|
||||
|
|
|
|||
|
|
@ -114,16 +114,20 @@ bool pa_cpu_init_x86(pa_cpu_x86_flag_t *flags) {
|
|||
pa_cpu_get_x86_flags(flags);
|
||||
|
||||
/* activate various optimisations */
|
||||
#ifdef HAVE_MMX
|
||||
if (*flags & PA_CPU_X86_MMX) {
|
||||
pa_volume_func_init_mmx(*flags);
|
||||
pa_remap_func_init_mmx(*flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SSE
|
||||
if (*flags & (PA_CPU_X86_SSE | PA_CPU_X86_SSE2)) {
|
||||
pa_volume_func_init_sse(*flags);
|
||||
pa_remap_func_init_sse(*flags);
|
||||
pa_convert_func_init_sse(*flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
#else /* defined (__i386__) || defined (__amd64__) */
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
typedef struct pa_creds pa_creds;
|
||||
typedef struct pa_cmsg_ancil_data pa_cmsg_ancil_data;
|
||||
|
||||
#if defined(SCM_CREDENTIALS)
|
||||
#if defined(SCM_CREDENTIALS) || defined(SCM_CREDS)
|
||||
|
||||
#define HAVE_CREDS 1
|
||||
|
||||
|
|
|
|||
|
|
@ -59,17 +59,16 @@ void pa_datum_free(pa_datum *d) {
|
|||
pa_zero(d);
|
||||
}
|
||||
|
||||
pa_database* pa_database_open(const char *fn, bool for_write) {
|
||||
const char* pa_database_get_filename_suffix(void) {
|
||||
return ".gdbm";
|
||||
}
|
||||
|
||||
pa_database* pa_database_open_internal(const char *path, bool for_write) {
|
||||
GDBM_FILE f;
|
||||
int gdbm_cache_size;
|
||||
char *path;
|
||||
|
||||
pa_assert(fn);
|
||||
pa_assert(path);
|
||||
|
||||
/* We include the host identifier in the file name because gdbm
|
||||
* files are CPU dependent, and we don't want things to go wrong
|
||||
* if we are on a multiarch system. */
|
||||
path = pa_sprintf_malloc("%s."CANONICAL_HOST".gdbm", fn);
|
||||
errno = 0;
|
||||
|
||||
/* We need to set the block size explicitly here, since otherwise
|
||||
|
|
@ -80,8 +79,6 @@ pa_database* pa_database_open(const char *fn, bool for_write) {
|
|||
if (f)
|
||||
pa_log_debug("Opened GDBM database '%s'", path);
|
||||
|
||||
pa_xfree(path);
|
||||
|
||||
if (!f) {
|
||||
if (errno == 0)
|
||||
errno = EIO;
|
||||
|
|
|
|||
|
|
@ -222,14 +222,16 @@ static int fill_data(simple_data *db, FILE *f) {
|
|||
return pa_hashmap_size(db->map);
|
||||
}
|
||||
|
||||
pa_database* pa_database_open(const char *fn, bool for_write) {
|
||||
const char* pa_database_get_filename_suffix(void) {
|
||||
return ".simple";
|
||||
}
|
||||
|
||||
pa_database* pa_database_open_internal(const char *path, bool for_write) {
|
||||
FILE *f;
|
||||
char *path;
|
||||
simple_data *db;
|
||||
|
||||
pa_assert(fn);
|
||||
pa_assert(path);
|
||||
|
||||
path = pa_sprintf_malloc("%s."CANONICAL_HOST".simple", fn);
|
||||
errno = 0;
|
||||
|
||||
f = pa_fopen_cloexec(path, "r");
|
||||
|
|
@ -251,8 +253,6 @@ pa_database* pa_database_open(const char *fn, bool for_write) {
|
|||
db = NULL;
|
||||
}
|
||||
|
||||
pa_xfree(path);
|
||||
|
||||
return (pa_database*) db;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -97,18 +97,18 @@ finish:
|
|||
return c;
|
||||
}
|
||||
|
||||
pa_database* pa_database_open(const char *fn, bool for_write) {
|
||||
const char* pa_database_get_filename_suffix(void) {
|
||||
return ".tdb";
|
||||
}
|
||||
|
||||
pa_database* pa_database_open_internal(const char *path, bool for_write) {
|
||||
struct tdb_context *c;
|
||||
char *path;
|
||||
|
||||
pa_assert(fn);
|
||||
pa_assert(path);
|
||||
|
||||
path = pa_sprintf_malloc("%s.tdb", fn);
|
||||
if ((c = tdb_open_cloexec(path, 0, TDB_NOSYNC|TDB_NOLOCK, (for_write ? O_RDWR|O_CREAT : O_RDONLY), 0644)))
|
||||
pa_log_debug("Opened TDB database '%s'", path);
|
||||
|
||||
pa_xfree(path);
|
||||
|
||||
if (!c) {
|
||||
if (errno == 0)
|
||||
errno = EIO;
|
||||
|
|
|
|||
107
src/pulsecore/database.c
Normal file
107
src/pulsecore/database.c
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/***
|
||||
This file is part of PulseAudio.
|
||||
|
||||
Copyright 2020 Igor V. Kovalenko <igor.v.kovalenko@gmail.com>
|
||||
|
||||
PulseAudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation; either version 2.1 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
PulseAudio is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <pulse/xmalloc.h>
|
||||
#include <pulsecore/core-util.h>
|
||||
#include <pulsecore/log.h>
|
||||
|
||||
#include "database.h"
|
||||
#include "core-error.h"
|
||||
|
||||
pa_database* pa_database_open(const char *path, const char *fn, bool prependmid, bool for_write) {
|
||||
|
||||
const char *filename_suffix = pa_database_get_filename_suffix();
|
||||
|
||||
char *machine_id = NULL, *filename_prefix, *full_path;
|
||||
|
||||
DIR *database_dir = NULL;
|
||||
struct dirent *de;
|
||||
|
||||
pa_database *f;
|
||||
|
||||
pa_assert(filename_suffix && filename_suffix[0]);
|
||||
|
||||
if (prependmid && !(machine_id = pa_machine_id())) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Database file name starts with ${machine_id}-${fn} */
|
||||
if (machine_id)
|
||||
filename_prefix = pa_sprintf_malloc("%s-%s", machine_id, fn);
|
||||
else
|
||||
filename_prefix = pa_xstrdup(fn);
|
||||
|
||||
/* Search for existing database directory entry name matching architecture suffix and filename suffix. */
|
||||
database_dir = opendir(path);
|
||||
|
||||
if (database_dir) {
|
||||
for (;;) {
|
||||
errno = 0;
|
||||
de = readdir(database_dir);
|
||||
if (!de) {
|
||||
if (errno) {
|
||||
pa_log_warn("Unable to search for existing database file, readdir() failed: %s", pa_cstrerror(errno));
|
||||
/* can continue as if there is no matching database file candidate */
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (pa_startswith(de->d_name, filename_prefix)
|
||||
&& de->d_name[strlen(filename_prefix)] == '.'
|
||||
&& pa_endswith(de->d_name + strlen(filename_prefix) + 1, filename_suffix)) {
|
||||
|
||||
/* candidate filename found, replace filename_prefix with this one */
|
||||
|
||||
pa_log_debug("Found existing database file '%s/%s', using it", path, de->d_name);
|
||||
pa_xfree(filename_prefix);
|
||||
filename_prefix = pa_xstrndup(de->d_name, strlen(de->d_name) - strlen(filename_suffix));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(database_dir);
|
||||
} else {
|
||||
pa_log_warn("Unable to search for existing database file, failed to open directory %s: %s", path, pa_cstrerror(errno));
|
||||
}
|
||||
|
||||
full_path = pa_sprintf_malloc("%s" PA_PATH_SEP "%s%s", path, filename_prefix, filename_suffix);
|
||||
|
||||
f = pa_database_open_internal(full_path, for_write);
|
||||
|
||||
if (f)
|
||||
pa_log_info("Successfully opened '%s' database file '%s'.", fn, full_path);
|
||||
else
|
||||
pa_log("Failed to open '%s' database file '%s': %s", fn, full_path, pa_cstrerror(errno));
|
||||
|
||||
pa_xfree(full_path);
|
||||
pa_xfree(filename_prefix);
|
||||
|
||||
/* deallocate machine_id if it was used to construct file name */
|
||||
pa_xfree(machine_id);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
|
@ -38,8 +38,29 @@ typedef struct pa_datum {
|
|||
|
||||
void pa_datum_free(pa_datum *d);
|
||||
|
||||
/* This will append a suffix to the filename */
|
||||
pa_database* pa_database_open(const char *fn, bool for_write);
|
||||
/* Database implementation; returns non-empty database filename extension string */
|
||||
const char* pa_database_get_filename_suffix(void);
|
||||
|
||||
/* Opens a database file. The file is loaded from the directory indicated by
|
||||
* path. The file name is constructed by using fn as the base and then adding
|
||||
* several parts:
|
||||
* 1) If prependmid is true, the machine id is prepended to the file name.
|
||||
* 2) The database implementation specific suffix is added.
|
||||
* 3) Older versions of PulseAudio in some cases added the CPU architecture
|
||||
* to the file name, which was later deemed unnecessary, but for
|
||||
* compatibility reasons we still need to look for those files, so we scan
|
||||
* the directory for files that match the prefix (possible machine id plus
|
||||
* fn) and the suffix, and if any matches are found, we use the first one.
|
||||
*
|
||||
* When no existing file is found, we create a new file for the database
|
||||
* (without the CPU architecture part in the name).
|
||||
*
|
||||
* For a read-only database, set for_write to false. */
|
||||
|
||||
pa_database* pa_database_open(const char *path, const char *fn, bool prependmid, bool for_write);
|
||||
|
||||
/* Database implementation; opens specified database file using provided path. */
|
||||
pa_database* pa_database_open_internal(const char *path, bool for_write);
|
||||
void pa_database_close(pa_database *db);
|
||||
|
||||
pa_datum* pa_database_get(pa_database *db, const pa_datum *key, pa_datum* data);
|
||||
|
|
|
|||
|
|
@ -54,11 +54,11 @@ void pa_device_port_new_data_set_available(pa_device_port_new_data *data, pa_ava
|
|||
data->available = available;
|
||||
}
|
||||
|
||||
void pa_device_port_new_data_set_available_group(pa_device_port_new_data *data, const char *group) {
|
||||
void pa_device_port_new_data_set_availability_group(pa_device_port_new_data *data, const char *group) {
|
||||
pa_assert(data);
|
||||
|
||||
pa_xfree(data->available_group);
|
||||
data->available_group = pa_xstrdup(group);
|
||||
pa_xfree(data->availability_group);
|
||||
data->availability_group = pa_xstrdup(group);
|
||||
}
|
||||
|
||||
void pa_device_port_new_data_set_direction(pa_device_port_new_data *data, pa_direction_t direction) {
|
||||
|
|
@ -78,7 +78,7 @@ void pa_device_port_new_data_done(pa_device_port_new_data *data) {
|
|||
|
||||
pa_xfree(data->name);
|
||||
pa_xfree(data->description);
|
||||
pa_xfree(data->available_group);
|
||||
pa_xfree(data->availability_group);
|
||||
}
|
||||
|
||||
void pa_device_port_set_preferred_profile(pa_device_port *p, const char *new_pp) {
|
||||
|
|
@ -107,8 +107,18 @@ void pa_device_port_set_available(pa_device_port *p, pa_available_t status) {
|
|||
* be created before port objects, and then p->card could be non-NULL for
|
||||
* the whole lifecycle of pa_device_port. */
|
||||
if (p->card && p->card->linked) {
|
||||
pa_sink *sink;
|
||||
pa_source *source;
|
||||
|
||||
pa_subscription_post(p->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, p->card->index);
|
||||
pa_hook_fire(&p->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED], p);
|
||||
|
||||
sink = pa_device_port_get_sink(p);
|
||||
source = pa_device_port_get_source(p);
|
||||
if (sink)
|
||||
pa_subscription_post(p->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, sink->index);
|
||||
if (source)
|
||||
pa_subscription_post(p->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, source->index);
|
||||
|
||||
/* A sink or source whose active port is unavailable can't be the
|
||||
* default sink/source, so port availability changes may affect the
|
||||
* default sink/source choice. */
|
||||
|
|
@ -118,9 +128,6 @@ void pa_device_port_set_available(pa_device_port *p, pa_available_t status) {
|
|||
pa_core_update_default_source(p->core);
|
||||
|
||||
if (p->direction == PA_DIRECTION_OUTPUT) {
|
||||
pa_sink *sink;
|
||||
|
||||
sink = pa_device_port_get_sink(p);
|
||||
if (sink && p == sink->active_port) {
|
||||
if (sink->active_port->available == PA_AVAILABLE_NO) {
|
||||
if (p->core->rescue_streams)
|
||||
|
|
@ -129,9 +136,6 @@ void pa_device_port_set_available(pa_device_port *p, pa_available_t status) {
|
|||
pa_core_move_streams_to_newly_available_preferred_sink(p->core, sink);
|
||||
}
|
||||
} else {
|
||||
pa_source *source;
|
||||
|
||||
source = pa_device_port_get_source(p);
|
||||
if (source && p == source->active_port) {
|
||||
if (source->active_port->available == PA_AVAILABLE_NO) {
|
||||
if (p->core->rescue_streams)
|
||||
|
|
@ -140,6 +144,11 @@ void pa_device_port_set_available(pa_device_port *p, pa_available_t status) {
|
|||
pa_core_move_streams_to_newly_available_preferred_source(p->core, source);
|
||||
}
|
||||
}
|
||||
|
||||
/* This may cause the sink and source pointers to become invalid, if
|
||||
* the availability change causes the card profile to get switched. If
|
||||
* you add code after this line, remember to take that into account. */
|
||||
pa_hook_fire(&p->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED], p);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -158,7 +167,7 @@ static void device_port_free(pa_object *o) {
|
|||
if (p->profiles)
|
||||
pa_hashmap_free(p->profiles);
|
||||
|
||||
pa_xfree(p->available_group);
|
||||
pa_xfree(p->availability_group);
|
||||
pa_xfree(p->preferred_profile);
|
||||
pa_xfree(p->name);
|
||||
pa_xfree(p->description);
|
||||
|
|
@ -184,8 +193,8 @@ pa_device_port *pa_device_port_new(pa_core *c, pa_device_port_new_data *data, si
|
|||
p->card = NULL;
|
||||
p->priority = 0;
|
||||
p->available = data->available;
|
||||
p->available_group = data->available_group;
|
||||
data->available_group = NULL;
|
||||
p->availability_group = data->availability_group;
|
||||
data->availability_group = NULL;
|
||||
p->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
|
||||
p->direction = data->direction;
|
||||
p->type = data->type;
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ struct pa_device_port {
|
|||
|
||||
unsigned priority;
|
||||
pa_available_t available; /* PA_AVAILABLE_UNKNOWN, PA_AVAILABLE_NO or PA_AVAILABLE_YES */
|
||||
char *available_group; /* a string indentifier which determine the group of devices handling the available state simulteneously */
|
||||
char *availability_group; /* a string indentifier which determine the group of devices handling the available state simulteneously */
|
||||
|
||||
pa_proplist *proplist;
|
||||
pa_hashmap *profiles; /* Does not own the profiles */
|
||||
|
|
@ -69,7 +69,7 @@ typedef struct pa_device_port_new_data {
|
|||
char *name;
|
||||
char *description;
|
||||
pa_available_t available;
|
||||
char *available_group;
|
||||
char *availability_group;
|
||||
pa_direction_t direction;
|
||||
pa_device_port_type_t type;
|
||||
} pa_device_port_new_data;
|
||||
|
|
@ -78,7 +78,7 @@ pa_device_port_new_data *pa_device_port_new_data_init(pa_device_port_new_data *d
|
|||
void pa_device_port_new_data_set_name(pa_device_port_new_data *data, const char *name);
|
||||
void pa_device_port_new_data_set_description(pa_device_port_new_data *data, const char *description);
|
||||
void pa_device_port_new_data_set_available(pa_device_port_new_data *data, pa_available_t available);
|
||||
void pa_device_port_new_data_set_available_group(pa_device_port_new_data *data, const char *group);
|
||||
void pa_device_port_new_data_set_availability_group(pa_device_port_new_data *data, const char *group);
|
||||
void pa_device_port_new_data_set_direction(pa_device_port_new_data *data, pa_direction_t direction);
|
||||
void pa_device_port_new_data_set_type(pa_device_port_new_data *data, pa_device_port_type_t type);
|
||||
void pa_device_port_new_data_done(pa_device_port_new_data *data);
|
||||
|
|
|
|||
|
|
@ -151,26 +151,16 @@ static void flush(pa_fdsem *f) {
|
|||
uint64_t u;
|
||||
|
||||
if ((r = pa_read(f->efd, &u, sizeof(u), NULL)) != sizeof(u)) {
|
||||
|
||||
if (r >= 0 || errno != EINTR) {
|
||||
pa_log_error("Invalid read from eventfd: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
|
||||
pa_assert_not_reached();
|
||||
}
|
||||
|
||||
continue;
|
||||
pa_log_error("Invalid read from eventfd: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
|
||||
pa_assert_not_reached();
|
||||
}
|
||||
r = (ssize_t) u;
|
||||
} else
|
||||
#endif
|
||||
|
||||
if ((r = pa_read(f->fds[0], &x, sizeof(x), NULL)) <= 0) {
|
||||
|
||||
if (r >= 0 || errno != EINTR) {
|
||||
pa_log_error("Invalid read from pipe: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
|
||||
pa_assert_not_reached();
|
||||
}
|
||||
|
||||
continue;
|
||||
pa_log_error("Invalid read from pipe: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
|
||||
pa_assert_not_reached();
|
||||
}
|
||||
|
||||
} while (pa_atomic_sub(&f->data->in_pipe, (int) r) > (int) r);
|
||||
|
|
@ -194,23 +184,15 @@ void pa_fdsem_post(pa_fdsem *f) {
|
|||
uint64_t u = 1;
|
||||
|
||||
if ((r = pa_write(f->efd, &u, sizeof(u), &f->write_type)) != sizeof(u)) {
|
||||
if (r >= 0 || errno != EINTR) {
|
||||
pa_log_error("Invalid write to eventfd: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
|
||||
pa_assert_not_reached();
|
||||
}
|
||||
|
||||
continue;
|
||||
pa_log_error("Invalid write to eventfd: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
|
||||
pa_assert_not_reached();
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
|
||||
if ((r = pa_write(f->fds[1], &x, 1, &f->write_type)) != 1) {
|
||||
if (r >= 0 || errno != EINTR) {
|
||||
pa_log_error("Invalid write to pipe: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
|
||||
pa_assert_not_reached();
|
||||
}
|
||||
|
||||
continue;
|
||||
pa_log_error("Invalid write to pipe: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
|
||||
pa_assert_not_reached();
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
@ -238,13 +220,8 @@ void pa_fdsem_wait(pa_fdsem *f) {
|
|||
uint64_t u;
|
||||
|
||||
if ((r = pa_read(f->efd, &u, sizeof(u), NULL)) != sizeof(u)) {
|
||||
|
||||
if (r >= 0 || errno != EINTR) {
|
||||
pa_log_error("Invalid read from eventfd: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
|
||||
pa_assert_not_reached();
|
||||
}
|
||||
|
||||
continue;
|
||||
pa_log_error("Invalid read from eventfd: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
|
||||
pa_assert_not_reached();
|
||||
}
|
||||
|
||||
r = (ssize_t) u;
|
||||
|
|
@ -252,13 +229,8 @@ void pa_fdsem_wait(pa_fdsem *f) {
|
|||
#endif
|
||||
|
||||
if ((r = pa_read(f->fds[0], &x, sizeof(x), NULL)) <= 0) {
|
||||
|
||||
if (r >= 0 || errno != EINTR) {
|
||||
pa_log_error("Invalid read from pipe: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
|
||||
pa_assert_not_reached();
|
||||
}
|
||||
|
||||
continue;
|
||||
pa_log_error("Invalid read from pipe: %s", r < 0 ? pa_cstrerror(errno) : "EOF");
|
||||
pa_assert_not_reached();
|
||||
}
|
||||
|
||||
pa_atomic_sub(&f->data->in_pipe, (int) r);
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l) {
|
|||
return r; /* Fast path - we almost always successfully write everything */
|
||||
|
||||
if (r < 0) {
|
||||
if (errno == EINTR || errno == EAGAIN)
|
||||
if (errno == EAGAIN)
|
||||
r = 0;
|
||||
else
|
||||
return r;
|
||||
|
|
@ -261,6 +261,13 @@ ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) {
|
|||
|
||||
#ifdef HAVE_CREDS
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
typedef struct cmsgcred pa_ucred_t;
|
||||
#define SCM_CREDENTIALS SCM_CREDS
|
||||
#else
|
||||
typedef struct ucred pa_ucred_t;
|
||||
#endif
|
||||
|
||||
bool pa_iochannel_creds_supported(pa_iochannel *io) {
|
||||
struct {
|
||||
struct sockaddr sa;
|
||||
|
|
@ -284,15 +291,19 @@ bool pa_iochannel_creds_supported(pa_iochannel *io) {
|
|||
}
|
||||
|
||||
int pa_iochannel_creds_enable(pa_iochannel *io) {
|
||||
#if !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
|
||||
int t = 1;
|
||||
#endif
|
||||
|
||||
pa_assert(io);
|
||||
pa_assert(io->ifd >= 0);
|
||||
|
||||
#if !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
|
||||
if (setsockopt(io->ifd, SOL_SOCKET, SO_PASSCRED, &t, sizeof(t)) < 0) {
|
||||
pa_log_error("setsockopt(SOL_SOCKET, SO_PASSCRED): %s", pa_cstrerror(errno));
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -303,9 +314,9 @@ ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l
|
|||
struct iovec iov;
|
||||
union {
|
||||
struct cmsghdr hdr;
|
||||
uint8_t data[CMSG_SPACE(sizeof(struct ucred))];
|
||||
uint8_t data[CMSG_SPACE(sizeof(pa_ucred_t))];
|
||||
} cmsg;
|
||||
struct ucred *u;
|
||||
pa_ucred_t *u;
|
||||
|
||||
pa_assert(io);
|
||||
pa_assert(data);
|
||||
|
|
@ -317,12 +328,15 @@ ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l
|
|||
iov.iov_len = l;
|
||||
|
||||
pa_zero(cmsg);
|
||||
cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(struct ucred));
|
||||
cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(pa_ucred_t));
|
||||
cmsg.hdr.cmsg_level = SOL_SOCKET;
|
||||
cmsg.hdr.cmsg_type = SCM_CREDENTIALS;
|
||||
|
||||
u = (struct ucred*) CMSG_DATA(&cmsg.hdr);
|
||||
u = (pa_ucred_t*) CMSG_DATA(&cmsg.hdr);
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
// the kernel fills everything
|
||||
#else
|
||||
u->pid = getpid();
|
||||
if (ucred) {
|
||||
u->uid = ucred->uid;
|
||||
|
|
@ -331,6 +345,7 @@ ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l
|
|||
u->uid = getuid();
|
||||
u->gid = getgid();
|
||||
}
|
||||
#endif
|
||||
|
||||
pa_zero(mh);
|
||||
mh.msg_iov = &iov;
|
||||
|
|
@ -403,7 +418,7 @@ ssize_t pa_iochannel_read_with_ancil_data(pa_iochannel*io, void*data, size_t l,
|
|||
struct iovec iov;
|
||||
union {
|
||||
struct cmsghdr hdr;
|
||||
uint8_t data[CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(sizeof(int) * MAX_ANCIL_DATA_FDS)];
|
||||
uint8_t data[CMSG_SPACE(sizeof(pa_ucred_t)) + CMSG_SPACE(sizeof(int) * MAX_ANCIL_DATA_FDS)];
|
||||
} cmsg;
|
||||
|
||||
pa_assert(io);
|
||||
|
|
@ -439,12 +454,16 @@ ssize_t pa_iochannel_read_with_ancil_data(pa_iochannel*io, void*data, size_t l,
|
|||
continue;
|
||||
|
||||
if (cmh->cmsg_type == SCM_CREDENTIALS) {
|
||||
struct ucred u;
|
||||
pa_assert(cmh->cmsg_len == CMSG_LEN(sizeof(struct ucred)));
|
||||
memcpy(&u, CMSG_DATA(cmh), sizeof(struct ucred));
|
||||
|
||||
pa_ucred_t u;
|
||||
pa_assert(cmh->cmsg_len == CMSG_LEN(sizeof(pa_ucred_t)));
|
||||
memcpy(&u, CMSG_DATA(cmh), sizeof(pa_ucred_t));
|
||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
ancil_data->creds.gid = u.cmcred_gid;
|
||||
ancil_data->creds.uid = u.cmcred_uid;
|
||||
#else
|
||||
ancil_data->creds.gid = u.gid;
|
||||
ancil_data->creds.uid = u.uid;
|
||||
#endif
|
||||
ancil_data->creds_valid = true;
|
||||
}
|
||||
else if (cmh->cmsg_type == SCM_RIGHTS) {
|
||||
|
|
|
|||
1072
src/pulsecore/json.c
Normal file
1072
src/pulsecore/json.c
Normal file
File diff suppressed because it is too large
Load diff
112
src/pulsecore/json.h
Normal file
112
src/pulsecore/json.h
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
/***
|
||||
This file is part of PulseAudio.
|
||||
|
||||
Copyright 2016 Arun Raghavan <mail@arunraghavan.net>
|
||||
|
||||
PulseAudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2.1 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
PulseAudio is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <pulsecore/hashmap.h>
|
||||
|
||||
#define PA_DOUBLE_IS_EQUAL(x, y) (((x) - (y)) < 0.000001 && ((x) - (y)) > -0.000001)
|
||||
|
||||
typedef enum {
|
||||
PA_JSON_TYPE_INIT = 0,
|
||||
PA_JSON_TYPE_NULL,
|
||||
PA_JSON_TYPE_INT,
|
||||
PA_JSON_TYPE_DOUBLE,
|
||||
PA_JSON_TYPE_BOOL,
|
||||
PA_JSON_TYPE_STRING,
|
||||
PA_JSON_TYPE_ARRAY,
|
||||
PA_JSON_TYPE_OBJECT,
|
||||
} pa_json_type;
|
||||
|
||||
typedef struct pa_json_object pa_json_object;
|
||||
|
||||
pa_json_object* pa_json_parse(const char *str);
|
||||
pa_json_type pa_json_object_get_type(const pa_json_object *obj);
|
||||
void pa_json_object_free(pa_json_object *obj);
|
||||
|
||||
/* All pointer members that are returned are valid while the corresponding object is valid */
|
||||
|
||||
int64_t pa_json_object_get_int(const pa_json_object *o);
|
||||
double pa_json_object_get_double(const pa_json_object *o);
|
||||
bool pa_json_object_get_bool(const pa_json_object *o);
|
||||
const char* pa_json_object_get_string(const pa_json_object *o);
|
||||
|
||||
const pa_json_object* pa_json_object_get_object_member(const pa_json_object *o, const char *name);
|
||||
|
||||
/** Returns pa_hashmap (char* -> const pa_json_object*) to iterate over object members. \since 15.0 */
|
||||
const pa_hashmap *pa_json_object_get_object_member_hashmap(const pa_json_object *o);
|
||||
|
||||
int pa_json_object_get_array_length(const pa_json_object *o);
|
||||
const pa_json_object* pa_json_object_get_array_member(const pa_json_object *o, int index);
|
||||
|
||||
bool pa_json_object_equal(const pa_json_object *o1, const pa_json_object *o2);
|
||||
|
||||
/** @{ \name Write functions */
|
||||
|
||||
/** Structure which holds a JSON encoder. Wrapper for pa_strbuf and encoder context. \since 15.0 */
|
||||
typedef struct pa_json_encoder pa_json_encoder;
|
||||
|
||||
/** Create a new pa_json_encoder structure. \since 15.0 */
|
||||
pa_json_encoder *pa_json_encoder_new(void);
|
||||
/** Free a pa_json_encoder structure. \since 15.0 */
|
||||
void pa_json_encoder_free(pa_json_encoder *encoder);
|
||||
/** Convert pa_json_encoder to string, free pa_json_encoder structure.
|
||||
* The returned string needs to be freed with pa_xree(). \since 15.0 */
|
||||
char *pa_json_encoder_to_string_free(pa_json_encoder *encoder);
|
||||
/** Check if a pa_json_encoder is empty (nothing has been added). \since 16.0 */
|
||||
bool pa_json_encoder_is_empty(pa_json_encoder *encoder);
|
||||
|
||||
/** Start appending JSON object element by writing an opening brace. \since 15.0 */
|
||||
void pa_json_encoder_begin_element_object(pa_json_encoder *encoder);
|
||||
/** Start appending JSON object member to JSON object. \since 15.0 */
|
||||
void pa_json_encoder_begin_member_object(pa_json_encoder *encoder, const char *name);
|
||||
/** End appending JSON object element or member to JSON object. \since 15.0 */
|
||||
void pa_json_encoder_end_object(pa_json_encoder *encoder);
|
||||
/** Start appending JSON array element by writing an opening bracket. \since 15.0 */
|
||||
void pa_json_encoder_begin_element_array(pa_json_encoder *encoder);
|
||||
/** Start appending JSON array member to JSON object. \since 15.0 */
|
||||
void pa_json_encoder_begin_member_array(pa_json_encoder *encoder, const char *name);
|
||||
/** End appending JSON array element or member to JSON object. \since 15.0 */
|
||||
void pa_json_encoder_end_array(pa_json_encoder *encoder);
|
||||
/** Append null element to JSON. \since 15.0 */
|
||||
void pa_json_encoder_add_element_null(pa_json_encoder *encoder);
|
||||
/** Append null member to JSON object. \since 15.0 */
|
||||
void pa_json_encoder_add_member_null(pa_json_encoder *encoder, const char *name);
|
||||
/** Append boolean element to JSON. \since 15.0 */
|
||||
void pa_json_encoder_add_element_bool(pa_json_encoder *encoder, bool value);
|
||||
/** Append boolean member to JSON object. \since 15.0 */
|
||||
void pa_json_encoder_add_member_bool(pa_json_encoder *encoder, const char *name, bool value);
|
||||
/** Append string element to JSON. Value will be escaped. \since 15.0 */
|
||||
void pa_json_encoder_add_element_string(pa_json_encoder *encoder, const char *value);
|
||||
/** Append string member to JSON object. Value will be escaped. \since 15.0 */
|
||||
void pa_json_encoder_add_member_string(pa_json_encoder *encoder, const char *name, const char *value);
|
||||
/** Append integer element to JSON. \since 15.0 */
|
||||
void pa_json_encoder_add_element_int(pa_json_encoder *encoder, int64_t value);
|
||||
/** Append integer member to JSON object. \since 15.0 */
|
||||
void pa_json_encoder_add_member_int(pa_json_encoder *encoder, const char *name, int64_t value);
|
||||
/** Append double element to JSON. \since 15.0 */
|
||||
void pa_json_encoder_add_element_double(pa_json_encoder *encoder, double value, int precision);
|
||||
/** Append double member to JSON object. \since 15.0 */
|
||||
void pa_json_encoder_add_member_double(pa_json_encoder *encoder, const char *name, double value, int precision);
|
||||
/** Append raw json string element to JSON. String will be written as is. \since 15.0 */
|
||||
void pa_json_encoder_add_element_raw_json(pa_json_encoder *encoder, const char *raw_json_string);
|
||||
/** Append raw json string member to JSON object. String will be written as is. \since 15.0 */
|
||||
void pa_json_encoder_add_member_raw_json(pa_json_encoder *encoder, const char *name, const char *raw_json_string);
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
/* Method of operation: the user creates a new mcalign object by
|
||||
* calling pa_mcalign_new() with the appropriate aligning
|
||||
* granularity. After that he may call pa_mcalign_push() for an input
|
||||
* granularity. After that they may call pa_mcalign_push() for an input
|
||||
* memchunk. After exactly one memchunk the user has to call
|
||||
* pa_mcalign_pop() until it returns -1. If pa_mcalign_pop() returns
|
||||
* 0, the memchunk *c is valid and aligned to the granularity. Some
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ static inline bool pa_mem_type_is_shared(pa_mem_type_t t) {
|
|||
return (t == PA_MEM_TYPE_SHARED_POSIX) || (t == PA_MEM_TYPE_SHARED_MEMFD);
|
||||
}
|
||||
|
||||
static inline bool pa_memfd_is_locally_supported() {
|
||||
static inline bool pa_memfd_is_locally_supported(void) {
|
||||
#if defined(HAVE_CREDS) && defined(HAVE_MEMFD)
|
||||
return true;
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ void pa_memblock_unref(pa_memblock*b);
|
|||
pa_memblock* pa_memblock_ref(pa_memblock*b);
|
||||
|
||||
/* This special unref function has to be called by the owner of the
|
||||
memory of a static memory block when he wants to release all
|
||||
memory of a static memory block when they want to release all
|
||||
references to the memory. This causes the memory to be copied and
|
||||
converted into a pool of malloc'ed memory block. Please note that this
|
||||
function is not multiple caller safe, i.e. needs to be locked
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ libpulsecore_sources = [
|
|||
'cpu-orc.c',
|
||||
'cpu-x86.c',
|
||||
'device-port.c',
|
||||
'database.c',
|
||||
'ffmpeg/resample2.c',
|
||||
'filter/biquad.c',
|
||||
'filter/crossover.c',
|
||||
|
|
@ -171,22 +172,45 @@ endif
|
|||
|
||||
# FIXME: SIMD support (ORC)
|
||||
simd = import('unstable-simd')
|
||||
libpulsecore_simd = simd.check('libpulsecore_simd',
|
||||
mmx : ['remap_mmx.c', 'svolume_mmx.c'],
|
||||
sse : ['remap_sse.c', 'sconv_sse.c', 'svolume_sse.c'],
|
||||
neon : ['remap_neon.c', 'sconv_neon.c', 'mix_neon.c'],
|
||||
c_args : [pa_c_args],
|
||||
include_directories : [configinc, topinc],
|
||||
implicit_include_directories : false,
|
||||
compiler : cc)
|
||||
libpulsecore_simd_lib = libpulsecore_simd[0]
|
||||
cdata.merge_from(libpulsecore_simd[1])
|
||||
simd_variants = [
|
||||
{ 'mmx' : ['remap_mmx.c', 'svolume_mmx.c'] },
|
||||
{ 'sse' : ['remap_sse.c', 'sconv_sse.c', 'svolume_sse.c'] },
|
||||
{ 'neon' : ['remap_neon.c', 'sconv_neon.c', 'mix_neon.c'] },
|
||||
]
|
||||
|
||||
# FIXME: Implement Windows support
|
||||
#'mutex-win32.c',
|
||||
#'poll-win32.c',
|
||||
#'semaphore-win32.c',
|
||||
#'thread-win32.c',
|
||||
libpulsecore_simd_lib = []
|
||||
|
||||
foreach simd_kwargs : simd_variants
|
||||
|
||||
if host_machine.cpu_family() == 'arm' and 'neon' in simd_kwargs
|
||||
if not cc.compiles('''
|
||||
#include <arm_neon.h>
|
||||
int main() {
|
||||
return sizeof(uint8x8_t) + sizeof(int32x4_t) + sizeof(float32x4_t);
|
||||
}
|
||||
''', name : 'neon code')
|
||||
continue
|
||||
endif
|
||||
endif
|
||||
|
||||
libpulsecore_simd = simd.check('libpulsecore_simd',
|
||||
kwargs : simd_kwargs,
|
||||
c_args : [pa_c_args],
|
||||
include_directories : [configinc, topinc],
|
||||
implicit_include_directories : false,
|
||||
compiler : cc)
|
||||
|
||||
libpulsecore_simd_lib += libpulsecore_simd[0]
|
||||
cdata.merge_from(libpulsecore_simd[1])
|
||||
endforeach
|
||||
|
||||
if host_machine.system() == 'windows'
|
||||
libpulsecore_sources += ['mutex-win32.c',
|
||||
'poll-win32.c',
|
||||
'semaphore-win32.c',
|
||||
'thread-win32.c',
|
||||
]
|
||||
endif
|
||||
|
||||
libpulsecore = shared_library('pulsecore-' + pa_version_major_minor,
|
||||
libpulsecore_sources, libpulsecore_headers,
|
||||
|
|
@ -198,7 +222,7 @@ libpulsecore = shared_library('pulsecore-' + pa_version_major_minor,
|
|||
install_rpath : privlibdir,
|
||||
install_dir : privlibdir,
|
||||
link_with : libpulsecore_simd_lib,
|
||||
dependencies : [libm_dep, libpulsecommon_dep, ltdl_dep, shm_dep, sndfile_dep, database_dep, dbus_dep, libatomic_ops_dep, orc_dep, samplerate_dep, soxr_dep, speex_dep, x11_dep, libintl_dep],
|
||||
dependencies : [libm_dep, libpulsecommon_dep, ltdl_dep, shm_dep, sndfile_dep, database_dep, dbus_dep, libatomic_ops_dep, orc_dep, samplerate_dep, soxr_dep, speex_dep, x11_dep, libintl_dep, platform_dep, platform_socket_dep,],
|
||||
implicit_include_directories : false)
|
||||
|
||||
libpulsecore_dep = declare_dependency(link_with: libpulsecore)
|
||||
|
|
|
|||
|
|
@ -31,6 +31,39 @@
|
|||
|
||||
#include "message-handler.h"
|
||||
|
||||
/* Check if a path string starts with a / and only contains valid characters.
|
||||
* Also reject double slashes. */
|
||||
static bool object_path_is_valid(const char *test_string) {
|
||||
uint32_t i;
|
||||
|
||||
if (!test_string)
|
||||
return false;
|
||||
|
||||
/* Make sure the string starts with a / */
|
||||
if (test_string[0] != '/')
|
||||
return false;
|
||||
|
||||
for (i = 0; test_string[i]; i++) {
|
||||
|
||||
if ((test_string[i] >= 'a' && test_string[i] <= 'z') ||
|
||||
(test_string[i] >= 'A' && test_string[i] <= 'Z') ||
|
||||
(test_string[i] >= '0' && test_string[i] <= '9') ||
|
||||
test_string[i] == '.' ||
|
||||
test_string[i] == '_' ||
|
||||
test_string[i] == '-' ||
|
||||
(test_string[i] == '/' && test_string[i + 1] != '/'))
|
||||
continue;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Make sure the string does not end with a / */
|
||||
if (test_string[i - 1] == '/')
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Message handler functions */
|
||||
|
||||
/* Register message handler for the specified object. object_path must be a unique name starting with "/". */
|
||||
|
|
@ -42,8 +75,8 @@ void pa_message_handler_register(pa_core *c, const char *object_path, const char
|
|||
pa_assert(cb);
|
||||
pa_assert(userdata);
|
||||
|
||||
/* Ensure that the object path is not empty and starts with "/". */
|
||||
pa_assert(object_path[0] == '/');
|
||||
/* Ensure that object path is valid */
|
||||
pa_assert(object_path_is_valid(object_path));
|
||||
|
||||
handler = pa_xnew0(struct pa_message_handler, 1);
|
||||
handler->userdata = userdata;
|
||||
|
|
@ -71,6 +104,9 @@ void pa_message_handler_unregister(pa_core *c, const char *object_path) {
|
|||
/* Send a message to an object identified by object_path */
|
||||
int pa_message_handler_send_message(pa_core *c, const char *object_path, const char *message, const char *message_parameters, char **response) {
|
||||
struct pa_message_handler *handler;
|
||||
int ret;
|
||||
char *path_copy;
|
||||
pa_json_object *parameters = NULL;
|
||||
|
||||
pa_assert(c);
|
||||
pa_assert(object_path);
|
||||
|
|
@ -79,12 +115,33 @@ int pa_message_handler_send_message(pa_core *c, const char *object_path, const c
|
|||
|
||||
*response = NULL;
|
||||
|
||||
if (!(handler = pa_hashmap_get(c->message_handlers, object_path)))
|
||||
path_copy = pa_xstrdup(object_path);
|
||||
|
||||
/* Remove trailing / from path name if present */
|
||||
if (path_copy[strlen(path_copy) - 1] == '/')
|
||||
path_copy[strlen(path_copy) - 1] = 0;
|
||||
|
||||
if (!(handler = pa_hashmap_get(c->message_handlers, path_copy))) {
|
||||
pa_xfree(path_copy);
|
||||
return -PA_ERR_NOENTITY;
|
||||
}
|
||||
|
||||
pa_xfree(path_copy);
|
||||
|
||||
if (message_parameters) {
|
||||
parameters = pa_json_parse(message_parameters);
|
||||
|
||||
if (!parameters)
|
||||
return -PA_ERR_INVALID;
|
||||
}
|
||||
|
||||
/* The handler is expected to return an error code and may also
|
||||
return an error string in response */
|
||||
return handler->callback(handler->object_path, message, message_parameters, response, handler->userdata);
|
||||
ret = handler->callback(handler->object_path, message, parameters, response, handler->userdata);
|
||||
|
||||
if (parameters)
|
||||
pa_json_object_free(parameters);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set handler description */
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
***/
|
||||
|
||||
#include <pulsecore/core.h>
|
||||
#include <pulsecore/json.h>
|
||||
|
||||
/* Message handler types and functions */
|
||||
|
||||
|
|
@ -26,7 +27,7 @@
|
|||
typedef int (*pa_message_handler_cb_t)(
|
||||
const char *object_path,
|
||||
const char *message,
|
||||
const char *message_parameters,
|
||||
const pa_json_object *parameters,
|
||||
char **response,
|
||||
void *userdata);
|
||||
|
||||
|
|
|
|||
|
|
@ -272,6 +272,15 @@ int pa_modargs_append(pa_modargs *ma, const char *args, const char* const* valid
|
|||
return parse(ma, args, valid_keys, true);
|
||||
}
|
||||
|
||||
int pa_modargs_remove_key(pa_modargs *ma, const char *key) {
|
||||
if (pa_hashmap_remove_and_free(ma->unescaped, key) == 0) {
|
||||
pa_hashmap_remove_and_free(ma->raw, key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void pa_modargs_free(pa_modargs*ma) {
|
||||
pa_assert(ma);
|
||||
|
||||
|
|
@ -544,3 +553,20 @@ const char *pa_modargs_iterate(pa_modargs *ma, void **state) {
|
|||
|
||||
return e->key;
|
||||
}
|
||||
|
||||
int pa_modargs_merge_missing(pa_modargs *dst, pa_modargs *src, const char* const valid_keys[]) {
|
||||
void *state;
|
||||
const char *key, *value;
|
||||
int ret = 0;
|
||||
|
||||
for (state = NULL, key = pa_modargs_iterate(src, &state); key; key = pa_modargs_iterate(src, &state)) {
|
||||
value = pa_modargs_get_value(src, key, NULL);
|
||||
if (value && add_key_value(dst, pa_xstrdup(key), pa_xstrdup(value), valid_keys, true) < 0) {
|
||||
pa_log_warn("Failed to add module argument '%s=%s'", key, value);
|
||||
ret = -1;
|
||||
/* continue to gather all errors */
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,4 +95,11 @@ int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa
|
|||
* have any particular order. */
|
||||
const char *pa_modargs_iterate(pa_modargs *ma, void **state);
|
||||
|
||||
/* Remove entry by key. Returns 0 if successful, -1 otherwise */
|
||||
int pa_modargs_remove_key(pa_modargs *ma, const char *key);
|
||||
|
||||
/* Add all key/value pairs from src that are is not already present in dst, to dst.
|
||||
* Returns 0 if there were no errors, -1 otherwise. */
|
||||
int pa_modargs_merge_missing(pa_modargs *dst, pa_modargs *src, const char* const valid_keys[]);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
#define PA_SYMBOL_LOAD_ONCE "pa__load_once"
|
||||
#define PA_SYMBOL_GET_N_USED "pa__get_n_used"
|
||||
#define PA_SYMBOL_GET_DEPRECATE "pa__get_deprecated"
|
||||
#define PA_SYMBOL_GET_VERSION "pa__get_version"
|
||||
|
||||
bool pa_module_exists(const char *name) {
|
||||
const char *paths, *state = NULL;
|
||||
|
|
@ -113,10 +114,11 @@ void pa_module_hook_connect(pa_module *m, pa_hook *hook, pa_hook_priority_t prio
|
|||
|
||||
int pa_module_load(pa_module** module, pa_core *c, const char *name, const char *argument) {
|
||||
pa_module *m = NULL;
|
||||
const char *(*get_version)(void);
|
||||
bool (*load_once)(void);
|
||||
const char* (*get_deprecated)(void);
|
||||
pa_modinfo *mi;
|
||||
int errcode;
|
||||
int errcode, rval;
|
||||
|
||||
pa_assert(module);
|
||||
pa_assert(c);
|
||||
|
|
@ -147,6 +149,21 @@ int pa_module_load(pa_module** module, pa_core *c, const char *name, const char
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if ((get_version = (const char *(*)(void)) pa_load_sym(m->dl, name, PA_SYMBOL_GET_VERSION))) {
|
||||
const char *version = get_version();
|
||||
|
||||
if (!pa_safe_streq(version, PACKAGE_VERSION)) {
|
||||
pa_log("Module \"%s\" version (%s) doesn't match the expected version (%s).",
|
||||
name, pa_strnull(version), PACKAGE_VERSION);
|
||||
errcode = -PA_ERR_IO;
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
pa_log("Symbol \"%s\" not found in module \"%s\".", PA_SYMBOL_GET_VERSION, name);
|
||||
errcode = -PA_ERR_IO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((load_once = (bool (*)(void)) pa_load_sym(m->dl, name, PA_SYMBOL_LOAD_ONCE))) {
|
||||
|
||||
m->load_once = load_once();
|
||||
|
|
@ -188,7 +205,11 @@ int pa_module_load(pa_module** module, pa_core *c, const char *name, const char
|
|||
pa_assert_se(pa_idxset_put(c->modules, m, &m->index) >= 0);
|
||||
pa_assert(m->index != PA_IDXSET_INVALID);
|
||||
|
||||
if (m->init(m) < 0) {
|
||||
if ((rval = m->init(m)) < 0) {
|
||||
if (rval == -PA_MODULE_ERR_SKIP) {
|
||||
errcode = -PA_ERR_NOENTITY;
|
||||
goto fail;
|
||||
}
|
||||
pa_log_error("Failed to load module \"%s\" (argument: \"%s\"): initialization failed.", name, argument ? argument : "");
|
||||
errcode = -PA_ERR_IO;
|
||||
goto fail;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,11 @@ typedef struct pa_module pa_module;
|
|||
|
||||
#include <pulsecore/core.h>
|
||||
|
||||
enum {
|
||||
PA_MODULE_ERR_UNSPECIFIED = 1,
|
||||
PA_MODULE_ERR_SKIP = 2
|
||||
};
|
||||
|
||||
struct pa_module {
|
||||
pa_core *core;
|
||||
char *name, *argument;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@
|
|||
#include <errno.h>
|
||||
|
||||
#include <pulse/xmalloc.h>
|
||||
|
||||
#include <pulsecore/core-error.h>
|
||||
#include <pulsecore/macro.h>
|
||||
|
||||
#include "mutex.h"
|
||||
|
|
@ -103,9 +105,14 @@ bool pa_mutex_try_lock(pa_mutex *m) {
|
|||
}
|
||||
|
||||
void pa_mutex_unlock(pa_mutex *m) {
|
||||
int err;
|
||||
|
||||
pa_assert(m);
|
||||
|
||||
pa_assert_se(pthread_mutex_unlock(&m->mutex) == 0);
|
||||
if ((err = pthread_mutex_unlock(&m->mutex)) != 0) {
|
||||
pa_log("pthread_mutex_unlock() failed: %s", pa_cstrerror(err));
|
||||
pa_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
pa_cond *pa_cond_new(void) {
|
||||
|
|
|
|||
|
|
@ -187,6 +187,9 @@ enum {
|
|||
* BOTH DIRECTIONS */
|
||||
PA_COMMAND_REGISTER_MEMFD_SHMID,
|
||||
|
||||
/* Supported since protocol v34 (14.0) */
|
||||
PA_COMMAND_SEND_OBJECT_MESSAGE,
|
||||
|
||||
PA_COMMAND_MAX
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -105,8 +105,13 @@ int pa_parse_address(const char *name, pa_parsed_address *ret_p) {
|
|||
} else
|
||||
p = name;
|
||||
|
||||
#ifndef OS_IS_WIN32
|
||||
if (*p == '/')
|
||||
ret_p->type = PA_PARSED_ADDRESS_UNIX;
|
||||
#else
|
||||
if (strlen(p) >= 3 && p[1] == ':' && p[2] == '\\' && ((p[0] >= 'A' && p[0] <= 'Z') || (p[0] >= 'a' && p[0] <= 'z')))
|
||||
ret_p->type = PA_PARSED_ADDRESS_UNIX;
|
||||
#endif
|
||||
else if (pa_startswith(p, "unix:")) {
|
||||
ret_p->type = PA_PARSED_ADDRESS_UNIX;
|
||||
p += sizeof("unix:")-1;
|
||||
|
|
|
|||
|
|
@ -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 v35 (15.0) */
|
||||
[PA_COMMAND_SEND_OBJECT_MESSAGE] = "SEND_OBJECT_MESSAGE",
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
#if defined(HAVE_POLL_H)
|
||||
#include <poll.h>
|
||||
#elif OS_IS_WIN32 && HAVE_WINSOCK2_H && (_WIN32_WINNT >= 0x0600)
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
|
||||
/* Event types that can be polled for. These bits may be set in `events'
|
||||
|
|
|
|||
|
|
@ -45,14 +45,14 @@ extern char **environ;
|
|||
|
||||
#if defined(HAVE_GLIB) && defined(PA_GCC_WEAKREF)
|
||||
#include <glib.h>
|
||||
static G_CONST_RETURN gchar* _g_get_application_name(void) PA_GCC_WEAKREF(g_get_application_name);
|
||||
static const gchar* _g_get_application_name(void) PA_GCC_WEAKREF(g_get_application_name);
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_GTK) && defined(PA_GCC_WEAKREF)
|
||||
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdkx.h>
|
||||
static G_CONST_RETURN gchar* _gtk_window_get_default_icon_name(void) PA_GCC_WEAKREF(gtk_window_get_default_icon_name);
|
||||
static const gchar* _gtk_window_get_default_icon_name(void) PA_GCC_WEAKREF(gtk_window_get_default_icon_name);
|
||||
static Display *_gdk_display PA_GCC_WEAKREF(gdk_display);
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -515,7 +515,7 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa
|
|||
pa_assert_se(call_info.method = dbus_message_get_member(message));
|
||||
pa_assert_se(call_info.method_sig = dbus_message_get_signature(message));
|
||||
|
||||
if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") ||
|
||||
if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect") ||
|
||||
(!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) {
|
||||
pa_dbus_send_basic_value_reply(connection, message, DBUS_TYPE_STRING, &call_info.obj_entry->introspection);
|
||||
goto finish;
|
||||
|
|
|
|||
|
|
@ -1010,7 +1010,7 @@ static int do_read(connection *c) {
|
|||
((uint8_t*) &c->request) + c->read_data_length,
|
||||
sizeof(c->request) - c->read_data_length)) <= 0) {
|
||||
|
||||
if (r < 0 && (errno == EINTR || errno == EAGAIN))
|
||||
if (r < 0 && errno == EAGAIN)
|
||||
return 0;
|
||||
|
||||
pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
|
||||
|
|
@ -1066,7 +1066,7 @@ static int do_read(connection *c) {
|
|||
(uint8_t*) c->read_data + c->read_data_length,
|
||||
handler->data_length - c->read_data_length)) <= 0) {
|
||||
|
||||
if (r < 0 && (errno == EINTR || errno == EAGAIN))
|
||||
if (r < 0 && errno == EAGAIN)
|
||||
return 0;
|
||||
|
||||
pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
|
||||
|
|
@ -1097,7 +1097,7 @@ static int do_read(connection *c) {
|
|||
pa_memblock_release(c->scache.memchunk.memblock);
|
||||
|
||||
if (r <= 0) {
|
||||
if (r < 0 && (errno == EINTR || errno == EAGAIN))
|
||||
if (r < 0 && errno == EAGAIN)
|
||||
return 0;
|
||||
|
||||
pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
|
||||
|
|
@ -1165,7 +1165,7 @@ static int do_read(connection *c) {
|
|||
|
||||
if (r <= 0) {
|
||||
|
||||
if (r < 0 && (errno == EINTR || errno == EAGAIN))
|
||||
if (r < 0 && errno == EAGAIN)
|
||||
return 0;
|
||||
|
||||
pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -3208,7 +3209,7 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin
|
|||
if (c->version >= 24) {
|
||||
pa_tagstruct_putu32(t, p->available);
|
||||
if (c->version >= 34) {
|
||||
pa_tagstruct_puts(t, p->available_group);
|
||||
pa_tagstruct_puts(t, p->availability_group);
|
||||
pa_tagstruct_putu32(t, p->type);
|
||||
}
|
||||
}
|
||||
|
|
@ -3283,7 +3284,7 @@ static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s
|
|||
if (c->version >= 24) {
|
||||
pa_tagstruct_putu32(t, p->available);
|
||||
if (c->version >= 34) {
|
||||
pa_tagstruct_puts(t, p->available_group);
|
||||
pa_tagstruct_puts(t, p->availability_group);
|
||||
pa_tagstruct_putu32(t, p->type);
|
||||
}
|
||||
}
|
||||
|
|
@ -3371,7 +3372,7 @@ static void card_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_car
|
|||
if (c->version >= 27) {
|
||||
pa_tagstruct_puts64(t, port->latency_offset);
|
||||
if (c->version >= 34) {
|
||||
pa_tagstruct_puts(t, port->available_group);
|
||||
pa_tagstruct_puts(t, port->availability_group);
|
||||
pa_tagstruct_putu32(t, port->type);
|
||||
}
|
||||
}
|
||||
|
|
@ -4721,6 +4722,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;
|
||||
|
|
@ -4972,6 +5022,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
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ static int do_read(connection *c) {
|
|||
|
||||
if (r <= 0) {
|
||||
|
||||
if (r < 0 && (errno == EINTR || errno == EAGAIN))
|
||||
if (r < 0 && errno == EAGAIN)
|
||||
return 0;
|
||||
|
||||
pa_log_debug("read(): %s", r == 0 ? "EOF" : pa_cstrerror(errno));
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ struct pa_pstream {
|
|||
* @registered_memfd_ids: registered memfd pools SHM IDs. Check
|
||||
* pa_pstream_register_memfd_mempool() for more information. */
|
||||
bool use_shm, use_memfd;
|
||||
bool non_registered_memfd_id_error_logged;
|
||||
pa_idxset *registered_memfd_ids;
|
||||
|
||||
pa_memimport *import;
|
||||
|
|
@ -244,8 +245,16 @@ static void do_pstream_read_write(pa_pstream *p) {
|
|||
p->mainloop->defer_enable(p->defer_event, 0);
|
||||
|
||||
if (!p->dead && p->srb) {
|
||||
do_write(p);
|
||||
while (!p->dead && do_read(p, &p->readsrb) == 0);
|
||||
int r = 0;
|
||||
|
||||
if(do_write(p) < 0)
|
||||
goto fail;
|
||||
|
||||
while (!p->dead && r == 0) {
|
||||
r = do_read(p, &p->readsrb);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (!p->dead && pa_iochannel_is_readable(p->io)) {
|
||||
|
|
@ -669,9 +678,11 @@ static void prepare_next_write_item(pa_pstream *p) {
|
|||
flags |= PA_FLAG_SHMDATA_MEMFD_BLOCK;
|
||||
send_payload = false;
|
||||
} else {
|
||||
if (pa_log_ratelimit(PA_LOG_ERROR)) {
|
||||
if (!p->non_registered_memfd_id_error_logged) {
|
||||
pa_log("Cannot send block reference with non-registered memfd ID = %u", shm_id);
|
||||
pa_log("Fallig back to copying full block data over socket");
|
||||
pa_log("Falling back to copying full block data over socket");
|
||||
pa_log("There's a bug report about this: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/824");
|
||||
p->non_registered_memfd_id_error_logged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,11 +52,15 @@ static void remap_mono_to_stereo_float32ne_generic_arm(pa_remap_t *m, float *dst
|
|||
__asm__ __volatile__ (
|
||||
"ldm %[src]!, {r4,r6} \n\t"
|
||||
"mov r5, r4 \n\t"
|
||||
"mov r7, r6 \n\t"
|
||||
"stm %[dst]!, {r4-r7} \n\t"
|
||||
|
||||
/* We use r12 instead of r7 here, because r7 is reserved for the
|
||||
* frame pointer when using Thumb. */
|
||||
"mov r12, r6 \n\t"
|
||||
|
||||
"stm %[dst]!, {r4-r6,r12} \n\t"
|
||||
: [dst] "+r" (dst), [src] "+r" (src) /* output operands */
|
||||
: /* input operands */
|
||||
: "memory", "r4", "r5", "r6", "r7" /* clobber list */
|
||||
: "memory", "r4", "r5", "r6", "r12" /* clobber list */
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ static long long rtkit_get_int_property(DBusConnection *connection, const char*
|
|||
if (!(m = dbus_message_new_method_call(
|
||||
RTKIT_SERVICE_NAME,
|
||||
RTKIT_OBJECT_PATH,
|
||||
"org.freedesktop.DBus.Properties",
|
||||
DBUS_INTERFACE_PROPERTIES,
|
||||
"Get"))) {
|
||||
ret = -ENOMEM;
|
||||
goto finish;
|
||||
|
|
|
|||
|
|
@ -1888,6 +1888,22 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
|
|||
pa_sink_set_volume(i->sink, NULL, false, i->save_volume);
|
||||
}
|
||||
|
||||
/* Called from the main thread. */
|
||||
static void set_preferred_sink(pa_sink_input *i, const char *sink_name) {
|
||||
pa_assert(i);
|
||||
|
||||
if (pa_safe_streq(i->preferred_sink, sink_name))
|
||||
return;
|
||||
|
||||
pa_log_debug("Sink input %u: preferred_sink: %s -> %s",
|
||||
i->index, i->preferred_sink ? i->preferred_sink : "(unset)", sink_name ? sink_name : "(unset)");
|
||||
pa_xfree(i->preferred_sink);
|
||||
i->preferred_sink = pa_xstrdup(sink_name);
|
||||
|
||||
pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT | PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
|
||||
pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PREFERRED_SINK_CHANGED], i);
|
||||
}
|
||||
|
||||
/* Called from main context */
|
||||
int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, bool save) {
|
||||
struct volume_factor_entry *v;
|
||||
|
|
@ -1930,11 +1946,10 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, bool save) {
|
|||
/* save == true, means user is calling the move_to() and want to
|
||||
save the preferred_sink */
|
||||
if (save) {
|
||||
pa_xfree(i->preferred_sink);
|
||||
if (dest == dest->core->default_sink)
|
||||
i->preferred_sink = NULL;
|
||||
set_preferred_sink(i, NULL);
|
||||
else
|
||||
i->preferred_sink = pa_xstrdup(dest->name);
|
||||
set_preferred_sink(i, dest->name);
|
||||
}
|
||||
|
||||
pa_idxset_put(dest->inputs, pa_sink_input_ref(i), NULL);
|
||||
|
|
@ -2434,16 +2449,29 @@ void pa_sink_input_set_reference_ratio(pa_sink_input *i, const pa_cvolume *ratio
|
|||
pa_cvolume_snprint_verbose(new_ratio_str, sizeof(new_ratio_str), ratio, &i->channel_map, true));
|
||||
}
|
||||
|
||||
/* Called from the main thread. */
|
||||
/* Called from the main thread.
|
||||
*
|
||||
* This is called when e.g. module-stream-restore wants to change the preferred
|
||||
* sink. As a side effect the stream is moved to the new preferred sink. Note
|
||||
* that things can work also in the other direction: if the user moves
|
||||
* a stream, as a side effect the preferred sink is changed. This could cause
|
||||
* an infinite loop, but it's avoided by these two measures:
|
||||
* - When pa_sink_input_set_preferred_sink() is called, it calls
|
||||
* pa_sink_input_move_to() with save=false, which avoids the recursive
|
||||
* pa_sink_input_set_preferred_sink() call.
|
||||
* - When the primary operation is to move a stream,
|
||||
* pa_sink_input_finish_move() calls set_preferred_sink() instead of
|
||||
* pa_sink_input_set_preferred_sink(). set_preferred_sink() doesn't move
|
||||
* the stream as a side effect.
|
||||
*/
|
||||
void pa_sink_input_set_preferred_sink(pa_sink_input *i, pa_sink *s) {
|
||||
pa_assert(i);
|
||||
|
||||
pa_xfree(i->preferred_sink);
|
||||
if (s) {
|
||||
i->preferred_sink = pa_xstrdup(s->name);
|
||||
set_preferred_sink(i, s->name);
|
||||
pa_sink_input_move_to(i, s, false);
|
||||
} else {
|
||||
i->preferred_sink = NULL;
|
||||
set_preferred_sink(i, NULL);
|
||||
pa_sink_input_move_to(i, i->core->default_sink, false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -704,8 +704,8 @@ void pa_sink_put(pa_sink* s) {
|
|||
pa_cvolume_remap(&s->real_volume, &root_sink->channel_map, &s->channel_map);
|
||||
} else
|
||||
/* We assume that if the sink implementor changed the default
|
||||
* volume he did so in real_volume, because that is the usual
|
||||
* place where he is supposed to place his changes. */
|
||||
* volume they did so in real_volume, because that is the usual
|
||||
* place where they are supposed to place their changes. */
|
||||
s->reference_volume = s->real_volume;
|
||||
|
||||
s->thread_info.soft_volume = s->soft_volume;
|
||||
|
|
@ -784,6 +784,10 @@ void pa_sink_unlink(pa_sink* s) {
|
|||
j = i;
|
||||
}
|
||||
|
||||
/* Unlink monitor source before unlinking the sink */
|
||||
if (s->monitor_source)
|
||||
pa_source_unlink(s->monitor_source);
|
||||
|
||||
if (linked)
|
||||
/* It's important to keep the suspend cause unchanged when unlinking,
|
||||
* because if we remove the SESSION suspend cause here, the alsa sink
|
||||
|
|
@ -795,9 +799,6 @@ void pa_sink_unlink(pa_sink* s) {
|
|||
|
||||
reset_callbacks(s);
|
||||
|
||||
if (s->monitor_source)
|
||||
pa_source_unlink(s->monitor_source);
|
||||
|
||||
if (linked) {
|
||||
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
|
||||
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], s);
|
||||
|
|
@ -3430,6 +3431,8 @@ int pa_sink_set_port(pa_sink *s, const char *name, bool save) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
s->port_changing = true;
|
||||
|
||||
if (s->set_port(s, port) < 0)
|
||||
return -PA_ERR_NOENTITY;
|
||||
|
||||
|
|
@ -3447,6 +3450,8 @@ int pa_sink_set_port(pa_sink *s, const char *name, bool save) {
|
|||
|
||||
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED], s);
|
||||
|
||||
s->port_changing = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -3577,6 +3582,14 @@ unsigned pa_device_init_priority(pa_proplist *p) {
|
|||
|
||||
pa_assert(p);
|
||||
|
||||
/* JACK sinks and sources get very high priority so that we'll switch the
|
||||
* default devices automatically when jackd starts and
|
||||
* module-jackdbus-detect creates the jack sink and source. */
|
||||
if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_API))) {
|
||||
if (pa_streq(s, "jack"))
|
||||
priority += 10000;
|
||||
}
|
||||
|
||||
if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS))) {
|
||||
|
||||
if (pa_streq(s, "sound"))
|
||||
|
|
@ -3609,10 +3622,18 @@ unsigned pa_device_init_priority(pa_proplist *p) {
|
|||
|
||||
if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_NAME))) {
|
||||
|
||||
if (pa_startswith(s, "analog-"))
|
||||
if (pa_startswith(s, "analog-")) {
|
||||
priority += 9;
|
||||
|
||||
/* If an analog device has an intended role of "phone", it probably
|
||||
* co-exists with another device that is meant for everything else,
|
||||
* and that other device should have higher priority than the phone
|
||||
* device. */
|
||||
if (pa_str_in_list_spaces(pa_proplist_gets(p, PA_PROP_DEVICE_INTENDED_ROLES), "phone"))
|
||||
priority -= 1;
|
||||
}
|
||||
else if (pa_startswith(s, "iec958-"))
|
||||
priority += 8;
|
||||
priority += 7;
|
||||
}
|
||||
|
||||
return priority;
|
||||
|
|
@ -3973,6 +3994,10 @@ void pa_sink_move_streams_to_default_sink(pa_core *core, pa_sink *old_sink, bool
|
|||
if (!i->sink)
|
||||
continue;
|
||||
|
||||
/* Don't move sink-inputs which connect filter sinks to their target sinks */
|
||||
if (i->origin_sink)
|
||||
continue;
|
||||
|
||||
/* If default_sink_changed is false, the old sink became unavailable, so all streams must be moved. */
|
||||
if (pa_safe_streq(old_sink->name, i->preferred_sink) && default_sink_changed)
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ struct pa_sink {
|
|||
bool save_port:1;
|
||||
bool save_volume:1;
|
||||
bool save_muted:1;
|
||||
bool port_changing:1;
|
||||
|
||||
/* Saved volume state while we're in passthrough mode */
|
||||
pa_cvolume saved_volume;
|
||||
|
|
|
|||
|
|
@ -58,6 +58,12 @@ int deny_severity = LOG_WARNING;
|
|||
#include <systemd/sd-daemon.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_WINDOWS_H
|
||||
#include <windows.h>
|
||||
#include <aclapi.h>
|
||||
#include <sddl.h>
|
||||
#endif
|
||||
|
||||
#include <pulse/xmalloc.h>
|
||||
#include <pulse/util.h>
|
||||
|
||||
|
|
@ -220,6 +226,31 @@ pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *file
|
|||
* inodes. */
|
||||
chmod(filename, 0777);
|
||||
|
||||
#ifdef OS_IS_WIN32
|
||||
/* https://docs.microsoft.com/en-us/windows/win32/secauthz/ace-strings */
|
||||
/* https://docs.microsoft.com/en-us/windows/win32/secauthz/modifying-the-acls-of-an-object-in-c-- */
|
||||
/* https://docs.microsoft.com/en-us/windows/win32/api/sddl/nf-sddl-convertstringsecuritydescriptortosecuritydescriptora */
|
||||
PSECURITY_DESCRIPTOR sd;
|
||||
if (ConvertStringSecurityDescriptorToSecurityDescriptorA(
|
||||
"D:" /* DACL */
|
||||
"(A;;FRFW;;;WD)", /* allow all users to read/write */
|
||||
SDDL_REVISION_1, &sd, NULL
|
||||
)) {
|
||||
PACL acl;
|
||||
BOOL acl_present, acl_default;
|
||||
if (GetSecurityDescriptorDacl(sd, &acl_present, &acl, &acl_default)) {
|
||||
if (SetNamedSecurityInfo(filename, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, acl, NULL) != ERROR_SUCCESS) {
|
||||
pa_log_warn("Failed to set DACL for socket: failed to apply DACL: error %lu.", GetLastError());
|
||||
}
|
||||
LocalFree(acl);
|
||||
} else {
|
||||
pa_log_warn("Failed to set DACL for socket: failed to get security descriptor DACL: error %lu.", GetLastError());
|
||||
}
|
||||
} else {
|
||||
pa_log_warn("Failed to set DACL for socket: failed to parse security descriptor: error %lu.", GetLastError());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (listen(fd, 5) < 0) {
|
||||
pa_log("listen(): %s", pa_cstrerror(errno));
|
||||
goto fail;
|
||||
|
|
|
|||
|
|
@ -239,8 +239,13 @@ int pa_unix_socket_is_stale(const char *fn) {
|
|||
sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
|
||||
|
||||
if (connect(fd, (struct sockaddr*) &sa, sizeof(sa)) < 0) {
|
||||
#if !defined(OS_IS_WIN32)
|
||||
if (errno == ECONNREFUSED)
|
||||
ret = 1;
|
||||
#else
|
||||
if (WSAGetLastError() == WSAECONNREFUSED || WSAGetLastError() == WSAEINVAL)
|
||||
ret = 1;
|
||||
#endif
|
||||
} else
|
||||
ret = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,31 @@
|
|||
|
||||
typedef long suseconds_t;
|
||||
|
||||
/** Windows 10 supports AF_UNIX as of build 17603, with
|
||||
support provided in the header file <afunix.h>. However,
|
||||
only the latest Windows SDK provides this file; older SDKs and
|
||||
MinGW do not.
|
||||
|
||||
Hence we define SOCKADDR_UN here. We do not expect this definition to change
|
||||
as Windows has some pretty good binary backwards-compatibility guarantees.
|
||||
|
||||
This shouldn't pose a problem for older versions of Windows; we expect them to
|
||||
fail with an error whenever we try to make a socket of type AF_UNIX. */
|
||||
#define UNIX_PATH_MAX 108
|
||||
|
||||
typedef struct sockaddr_un
|
||||
{
|
||||
ADDRESS_FAMILY sun_family; /* AF_UNIX */
|
||||
char sun_path[UNIX_PATH_MAX]; /* pathname */
|
||||
} SOCKADDR_UN, *PSOCKADDR_UN;
|
||||
|
||||
#ifndef SUN_LEN
|
||||
#define SUN_LEN(ptr) \
|
||||
((size_t)(((struct sockaddr_un *) 0)->sun_path) + strlen((ptr)->sun_path))
|
||||
#endif
|
||||
|
||||
#define HAVE_SYS_UN_H
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_WS2TCPIP_H
|
||||
|
|
|
|||
|
|
@ -1512,6 +1512,16 @@ static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
|
|||
pa_source_output_set_volume_direct(o, &o->reference_ratio);
|
||||
o->real_ratio = o->reference_ratio;
|
||||
pa_sw_cvolume_multiply(&o->soft_volume, &o->real_ratio, &o->volume_factor);
|
||||
|
||||
/* If this is a virtual source stream, we have to apply the source volume
|
||||
* to the source output. */
|
||||
if (o->destination_source) {
|
||||
pa_cvolume vol;
|
||||
|
||||
vol = o->destination_source->real_volume;
|
||||
pa_cvolume_remap(&vol, &o->destination_source->channel_map, &o->channel_map);
|
||||
pa_source_output_set_volume(o, &vol, o->destination_source->save_volume, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1521,6 +1531,22 @@ static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
|
|||
pa_source_set_volume(o->source, NULL, false, o->save_volume);
|
||||
}
|
||||
|
||||
/* Called from the main thread. */
|
||||
static void set_preferred_source(pa_source_output *o, const char *source_name) {
|
||||
pa_assert(o);
|
||||
|
||||
if (pa_safe_streq(o->preferred_source, source_name))
|
||||
return;
|
||||
|
||||
pa_log_debug("Source output %u: preferred_source: %s -> %s",
|
||||
o->index, o->preferred_source ? o->preferred_source : "(unset)", source_name ? source_name : "(unset)");
|
||||
pa_xfree(o->preferred_source);
|
||||
o->preferred_source = pa_xstrdup(source_name);
|
||||
|
||||
pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT | PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
|
||||
pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PREFERRED_SOURCE_CHANGED], o);
|
||||
}
|
||||
|
||||
/* Called from main context */
|
||||
int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, bool save) {
|
||||
pa_source_output_assert_ref(o);
|
||||
|
|
@ -1560,11 +1586,10 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, bool save
|
|||
/* save == true, means user is calling the move_to() and want to
|
||||
save the preferred_source */
|
||||
if (save) {
|
||||
pa_xfree(o->preferred_source);
|
||||
if (dest == dest->core->default_source)
|
||||
o->preferred_source = NULL;
|
||||
set_preferred_source(o, NULL);
|
||||
else
|
||||
o->preferred_source = pa_xstrdup(dest->name);
|
||||
set_preferred_source(o, dest->name);
|
||||
}
|
||||
|
||||
pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL);
|
||||
|
|
@ -1897,16 +1922,29 @@ void pa_source_output_set_reference_ratio(pa_source_output *o, const pa_cvolume
|
|||
pa_cvolume_snprint_verbose(new_ratio_str, sizeof(new_ratio_str), ratio, &o->channel_map, true));
|
||||
}
|
||||
|
||||
/* Called from the main thread. */
|
||||
/* Called from the main thread.
|
||||
*
|
||||
* This is called when e.g. module-stream-restore wants to change the preferred
|
||||
* source. As a side effect the stream is moved to the new preferred source.
|
||||
* Note that things can work also in the other direction: if the user moves
|
||||
* a stream, as a side effect the preferred source is changed. This could cause
|
||||
* an infinite loop, but it's avoided by these two measures:
|
||||
* - When pa_source_output_set_preferred_source() is called, it calls
|
||||
* pa_source_output_move_to() with save=false, which avoids the recursive
|
||||
* pa_source_output_set_preferred_source() call.
|
||||
* - When the primary operation is to move a stream,
|
||||
* pa_source_output_finish_move() calls set_preferred_source() instead of
|
||||
* pa_source_output_set_preferred_source(). set_preferred_source() doesn't
|
||||
* move the stream as a side effect.
|
||||
*/
|
||||
void pa_source_output_set_preferred_source(pa_source_output *o, pa_source *s) {
|
||||
pa_assert(o);
|
||||
|
||||
pa_xfree(o->preferred_source);
|
||||
if (s) {
|
||||
o->preferred_source = pa_xstrdup(s->name);
|
||||
set_preferred_source(o, s->name);
|
||||
pa_source_output_move_to(o, s, false);
|
||||
} else {
|
||||
o->preferred_source = NULL;
|
||||
set_preferred_source(o, NULL);
|
||||
pa_source_output_move_to(o, o->core->default_source, false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -654,8 +654,8 @@ void pa_source_put(pa_source *s) {
|
|||
pa_cvolume_remap(&s->real_volume, &root_source->channel_map, &s->channel_map);
|
||||
} else
|
||||
/* We assume that if the sink implementor changed the default
|
||||
* volume he did so in real_volume, because that is the usual
|
||||
* place where he is supposed to place his changes. */
|
||||
* volume they did so in real_volume, because that is the usual
|
||||
* place where they are supposed to place their changes. */
|
||||
s->reference_volume = s->real_volume;
|
||||
|
||||
s->thread_info.soft_volume = s->soft_volume;
|
||||
|
|
@ -2696,6 +2696,8 @@ int pa_source_set_port(pa_source *s, const char *name, bool save) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
s->port_changing = true;
|
||||
|
||||
if (s->set_port(s, port) < 0)
|
||||
return -PA_ERR_NOENTITY;
|
||||
|
||||
|
|
@ -2713,6 +2715,8 @@ int pa_source_set_port(pa_source *s, const char *name, bool save) {
|
|||
|
||||
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PORT_CHANGED], s);
|
||||
|
||||
s->port_changing = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -3029,6 +3033,10 @@ void pa_source_move_streams_to_default_source(pa_core *core, pa_source *old_sour
|
|||
if (!o->source)
|
||||
continue;
|
||||
|
||||
/* Don't move source-outputs which connect sources to filter sources */
|
||||
if (o->destination_source)
|
||||
continue;
|
||||
|
||||
/* If default_source_changed is false, the old source became unavailable, so all streams must be moved. */
|
||||
if (pa_safe_streq(old_source->name, o->preferred_source) && default_source_changed)
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ struct pa_source {
|
|||
bool save_port:1;
|
||||
bool save_volume:1;
|
||||
bool save_muted:1;
|
||||
bool port_changing:1;
|
||||
|
||||
/* Saved volume state while we're in passthrough mode */
|
||||
pa_cvolume saved_volume;
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@
|
|||
|
||||
#include "x11wrap.h"
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
typedef struct pa_x11_internal pa_x11_internal;
|
||||
|
||||
struct pa_x11_internal {
|
||||
|
|
@ -51,6 +53,7 @@ struct pa_x11_wrapper {
|
|||
|
||||
pa_defer_event* defer_event;
|
||||
pa_io_event* io_event;
|
||||
pa_defer_event* cleanup_event;
|
||||
|
||||
PA_LLIST_HEAD(pa_x11_client, clients);
|
||||
PA_LLIST_HEAD(pa_x11_internal, internals);
|
||||
|
|
@ -64,6 +67,8 @@ struct pa_x11_client {
|
|||
void *userdata;
|
||||
};
|
||||
|
||||
static void x11_wrapper_kill(pa_x11_wrapper *w);
|
||||
|
||||
/* Dispatch all pending X11 events */
|
||||
static void work(pa_x11_wrapper *w) {
|
||||
pa_assert(w);
|
||||
|
|
@ -167,6 +172,38 @@ static void x11_watch(Display *display, XPointer userdata, int fd, Bool opening,
|
|||
x11_internal_remove(w, (pa_x11_internal*) *watch_data);
|
||||
}
|
||||
|
||||
static int x11_error_handler(Display* display, XErrorEvent* error_event) {
|
||||
pa_log_warn("X11 error handler called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int x11_io_error_handler(Display* display) {
|
||||
pa_log_warn("X11 I/O error handler called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void deferred_x11_teardown(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
|
||||
pa_x11_wrapper *w = userdata;
|
||||
|
||||
m->defer_enable(e, 0);
|
||||
|
||||
pa_log_debug("Start tearing down X11 modules after X11 I/O error");
|
||||
|
||||
x11_wrapper_kill(w);
|
||||
|
||||
pa_log_debug("Done tearing down X11 modules after X11 I/O error");
|
||||
}
|
||||
|
||||
#ifdef HAVE_XSETIOERROREXITHANDLER
|
||||
static void x11_io_error_exit_handler(Display* display, void *userdata) {
|
||||
pa_x11_wrapper *w = userdata;
|
||||
|
||||
pa_log_warn("X11 I/O error exit handler called, preparing to tear down X11 modules");
|
||||
|
||||
pa_x11_wrapper_kill_deferred(w);
|
||||
}
|
||||
#endif
|
||||
|
||||
static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char *t) {
|
||||
pa_x11_wrapper*w;
|
||||
Display *d;
|
||||
|
|
@ -187,11 +224,20 @@ static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char
|
|||
|
||||
w->defer_event = c->mainloop->defer_new(c->mainloop, defer_event, w);
|
||||
w->io_event = c->mainloop->io_new(c->mainloop, ConnectionNumber(d), PA_IO_EVENT_INPUT, display_io_event, w);
|
||||
w->cleanup_event = c->mainloop->defer_new(c->mainloop, deferred_x11_teardown, w);
|
||||
w->core->mainloop->defer_enable(w->cleanup_event, 0);
|
||||
|
||||
XSetErrorHandler(x11_error_handler);
|
||||
XSetIOErrorHandler(x11_io_error_handler);
|
||||
#ifdef HAVE_XSETIOERROREXITHANDLER
|
||||
XSetIOErrorExitHandler(d, x11_io_error_exit_handler, w);
|
||||
#endif
|
||||
XAddConnectionWatch(d, x11_watch, (XPointer) w);
|
||||
|
||||
pa_assert_se(pa_shared_set(c, w->property_name, w) >= 0);
|
||||
|
||||
pa_log_debug("Created X11 connection wrapper '%s'", w->property_name);
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
|
|
@ -202,9 +248,12 @@ static void x11_wrapper_free(pa_x11_wrapper*w) {
|
|||
|
||||
pa_assert(!w->clients);
|
||||
|
||||
pa_log_debug("Destroying X11 connection wrapper '%s'", w->property_name);
|
||||
|
||||
XRemoveConnectionWatch(w->display, x11_watch, (XPointer) w);
|
||||
XCloseDisplay(w->display);
|
||||
|
||||
w->core->mainloop->defer_free(w->cleanup_event);
|
||||
w->core->mainloop->io_free(w->io_event);
|
||||
w->core->mainloop->defer_free(w->defer_event);
|
||||
|
||||
|
|
@ -261,7 +310,15 @@ xcb_connection_t *pa_x11_wrapper_get_xcb_connection(pa_x11_wrapper *w) {
|
|||
return XGetXCBConnection(pa_x11_wrapper_get_display(w));
|
||||
}
|
||||
|
||||
void pa_x11_wrapper_kill(pa_x11_wrapper *w) {
|
||||
void pa_x11_wrapper_kill_deferred(pa_x11_wrapper *w) {
|
||||
pa_assert(w);
|
||||
|
||||
/* schedule X11 display teardown */
|
||||
w->core->mainloop->defer_enable(w->cleanup_event, 1);
|
||||
}
|
||||
|
||||
/* Kill the connection to the X11 display */
|
||||
static void x11_wrapper_kill(pa_x11_wrapper *w) {
|
||||
pa_x11_client *c, *n;
|
||||
|
||||
pa_assert(w);
|
||||
|
|
|
|||
|
|
@ -48,8 +48,8 @@ Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w);
|
|||
/* Return the XCB connection object for this connection */
|
||||
xcb_connection_t *pa_x11_wrapper_get_xcb_connection(pa_x11_wrapper *w);
|
||||
|
||||
/* Kill the connection to the X11 display */
|
||||
void pa_x11_wrapper_kill(pa_x11_wrapper *w);
|
||||
/* Initiate X11 connection teardown. */
|
||||
void pa_x11_wrapper_kill_deferred(pa_x11_wrapper *w);
|
||||
|
||||
/* Register an X11 client, that is called for each X11 event */
|
||||
pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, pa_x11_event_cb_t event_cb, pa_x11_kill_cb_t kill_cb, void *userdata);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue