summaryrefslogtreecommitdiffstats
path: root/src/daemon
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:03:18 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:03:18 +0000
commit2dd5bc6a074165ddfbd57c4bd52c2d2dac8f47a1 (patch)
tree465b29cb405d3af0b0ad50c78e1dccc636594fec /src/daemon
parentInitial commit. (diff)
downloadpulseaudio-2dd5bc6a074165ddfbd57c4bd52c2d2dac8f47a1.tar.xz
pulseaudio-2dd5bc6a074165ddfbd57c4bd52c2d2dac8f47a1.zip
Adding upstream version 14.2.upstream/14.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/daemon')
-rw-r--r--src/daemon/.gitignore2
-rw-r--r--src/daemon/caps.c99
-rw-r--r--src/daemon/caps.h29
-rw-r--r--src/daemon/cmdline.c420
-rw-r--r--src/daemon/cmdline.h33
-rw-r--r--src/daemon/cpulimit.c244
-rw-r--r--src/daemon/cpulimit.h32
-rw-r--r--src/daemon/daemon-conf.c872
-rw-r--r--src/daemon/daemon-conf.h169
-rw-r--r--src/daemon/daemon.conf.in97
-rwxr-xr-xsrc/daemon/default.pa.in175
-rw-r--r--src/daemon/dumpmodules.c154
-rw-r--r--src/daemon/dumpmodules.h29
-rwxr-xr-xsrc/daemon/esdcompat.in94
-rw-r--r--src/daemon/ltdl-bind-now.c159
-rw-r--r--src/daemon/ltdl-bind-now.h27
-rw-r--r--src/daemon/main.c1247
-rw-r--r--src/daemon/meson.build159
-rw-r--r--src/daemon/pulseaudio-system.conf31
-rw-r--r--src/daemon/pulseaudio.desktop.in9
-rw-r--r--src/daemon/server-lookup.c492
-rw-r--r--src/daemon/server-lookup.h38
-rwxr-xr-xsrc/daemon/start-pulseaudio-x11.in37
-rwxr-xr-xsrc/daemon/system.pa.in62
-rw-r--r--src/daemon/systemd/user/pulseaudio.service.in34
-rw-r--r--src/daemon/systemd/user/pulseaudio.socket11
26 files changed, 4755 insertions, 0 deletions
diff --git a/src/daemon/.gitignore b/src/daemon/.gitignore
new file mode 100644
index 0000000..0efa55b
--- /dev/null
+++ b/src/daemon/.gitignore
@@ -0,0 +1,2 @@
+org.pulseaudio.policy
+pulseaudio.desktop
diff --git a/src/daemon/caps.c b/src/daemon/caps.c
new file mode 100644
index 0000000..fd135c0
--- /dev/null
+++ b/src/daemon/caps.c
@@ -0,0 +1,99 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ 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/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
+
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#endif
+
+#include "caps.h"
+
+/* Glibc <= 2.2 has broken unistd.h */
+#if defined(__linux__) && (__GLIBC__ <= 2 && __GLIBC_MINOR__ <= 2)
+int setresgid(gid_t r, gid_t e, gid_t s);
+int setresuid(uid_t r, uid_t e, uid_t s);
+#endif
+
+/* Drop root rights when called SUID root */
+void pa_drop_root(void) {
+
+#ifdef HAVE_GETUID
+ uid_t uid;
+ gid_t gid;
+
+ pa_log_debug("Cleaning up privileges.");
+ uid = getuid();
+ gid = getgid();
+
+#if defined(HAVE_SETRESUID)
+ pa_assert_se(setresuid(uid, uid, uid) >= 0);
+ pa_assert_se(setresgid(gid, gid, gid) >= 0);
+#elif defined(HAVE_SETREUID)
+ pa_assert_se(setreuid(uid, uid) >= 0);
+ pa_assert_se(setregid(gid, gid) >= 0);
+#else
+ pa_assert_se(setuid(uid) >= 0);
+ pa_assert_se(seteuid(uid) >= 0);
+ pa_assert_se(setgid(gid) >= 0);
+ pa_assert_se(setegid(gid) >= 0);
+#endif
+
+ pa_assert_se(getuid() == uid);
+ pa_assert_se(geteuid() == uid);
+ pa_assert_se(getgid() == gid);
+ pa_assert_se(getegid() == gid);
+
+ if (uid != 0)
+ pa_drop_caps();
+#endif
+}
+
+void pa_drop_caps(void) {
+#ifdef HAVE_SYS_CAPABILITY_H
+#if defined(__linux__)
+ cap_t caps;
+ pa_assert_se(caps = cap_init());
+ pa_assert_se(cap_clear(caps) == 0);
+ pa_assert_se(cap_set_proc(caps) == 0);
+ pa_assert_se(cap_free(caps) == 0);
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ /* FreeBSD doesn't have this functionality, even though sys/capability.h is
+ * available. See https://bugs.freedesktop.org/show_bug.cgi?id=72580 */
+ pa_log_warn("FreeBSD cannot drop extra capabilities, implementation needed.");
+#else
+#error "Don't know how to do capabilities on your system. Please send a patch."
+#endif /* __linux__ */
+#else /* HAVE_SYS_CAPABILITY_H */
+ pa_log_warn("Normally all extra capabilities would be dropped now, but "
+ "that's impossible because PulseAudio was built without "
+ "capabilities support.");
+#endif
+}
diff --git a/src/daemon/caps.h b/src/daemon/caps.h
new file mode 100644
index 0000000..9b88241
--- /dev/null
+++ b/src/daemon/caps.h
@@ -0,0 +1,29 @@
+#ifndef foocapshfoo
+#define foocapshfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004 Lennart Poettering
+
+ 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/>.
+***/
+
+#include <pulsecore/macro.h>
+
+void pa_drop_root(void);
+
+void pa_drop_caps(void);
+
+#endif
diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c
new file mode 100644
index 0000000..b325596
--- /dev/null
+++ b/src/daemon/cmdline.c
@@ -0,0 +1,420 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+
+ 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/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/macro.h>
+
+#include "cmdline.h"
+
+/* Argument codes for getopt_long() */
+enum {
+ ARG_HELP = 256,
+ ARG_VERSION,
+ ARG_DUMP_CONF,
+ ARG_DUMP_MODULES,
+ ARG_DAEMONIZE,
+ ARG_FAIL,
+ ARG_LOG_LEVEL,
+ ARG_HIGH_PRIORITY,
+ ARG_REALTIME,
+ ARG_DISALLOW_MODULE_LOADING,
+ ARG_DISALLOW_EXIT,
+ ARG_EXIT_IDLE_TIME,
+ ARG_SCACHE_IDLE_TIME,
+ ARG_LOG_TARGET,
+ ARG_LOG_META,
+ ARG_LOG_TIME,
+ ARG_LOG_BACKTRACE,
+ ARG_LOAD,
+ ARG_FILE,
+ ARG_DL_SEARCH_PATH,
+ ARG_RESAMPLE_METHOD,
+ ARG_KILL,
+ ARG_USE_PID_FILE,
+ ARG_CHECK,
+ ARG_NO_CPU_LIMIT,
+ ARG_DISABLE_SHM,
+ ARG_ENABLE_MEMFD,
+ ARG_DUMP_RESAMPLE_METHODS,
+ ARG_SYSTEM,
+ ARG_CLEANUP_SHM,
+ ARG_START
+};
+
+/* Table for getopt_long() */
+static const struct option long_options[] = {
+ {"help", 0, 0, ARG_HELP},
+ {"version", 0, 0, ARG_VERSION},
+ {"dump-conf", 0, 0, ARG_DUMP_CONF},
+ {"dump-modules", 0, 0, ARG_DUMP_MODULES},
+ {"daemonize", 2, 0, ARG_DAEMONIZE},
+ {"fail", 2, 0, ARG_FAIL},
+ {"verbose", 2, 0, ARG_LOG_LEVEL},
+ {"log-level", 2, 0, ARG_LOG_LEVEL},
+ {"high-priority", 2, 0, ARG_HIGH_PRIORITY},
+ {"realtime", 2, 0, ARG_REALTIME},
+ {"disallow-module-loading", 2, 0, ARG_DISALLOW_MODULE_LOADING},
+ {"disallow-exit", 2, 0, ARG_DISALLOW_EXIT},
+ {"exit-idle-time", 1, 0, ARG_EXIT_IDLE_TIME},
+ {"scache-idle-time", 1, 0, ARG_SCACHE_IDLE_TIME},
+ {"log-target", 1, 0, ARG_LOG_TARGET},
+ {"log-meta", 2, 0, ARG_LOG_META},
+ {"log-time", 2, 0, ARG_LOG_TIME},
+ {"log-backtrace", 1, 0, ARG_LOG_BACKTRACE},
+ {"load", 1, 0, ARG_LOAD},
+ {"file", 1, 0, ARG_FILE},
+ {"dl-search-path", 1, 0, ARG_DL_SEARCH_PATH},
+ {"resample-method", 1, 0, ARG_RESAMPLE_METHOD},
+ {"kill", 0, 0, ARG_KILL},
+ {"start", 0, 0, ARG_START},
+ {"use-pid-file", 2, 0, ARG_USE_PID_FILE},
+ {"check", 0, 0, ARG_CHECK},
+ {"system", 2, 0, ARG_SYSTEM},
+ {"no-cpu-limit", 2, 0, ARG_NO_CPU_LIMIT},
+ {"disable-shm", 2, 0, ARG_DISABLE_SHM},
+ {"enable-memfd", 2, 0, ARG_ENABLE_MEMFD},
+ {"dump-resample-methods", 2, 0, ARG_DUMP_RESAMPLE_METHODS},
+ {"cleanup-shm", 2, 0, ARG_CLEANUP_SHM},
+ {NULL, 0, 0, 0}
+};
+
+void pa_cmdline_help(const char *argv0) {
+ pa_assert(argv0);
+
+ printf(_("%s [options]\n\n"
+ "COMMANDS:\n"
+ " -h, --help Show this help\n"
+ " --version Show version\n"
+ " --dump-conf Dump default configuration\n"
+ " --dump-modules Dump list of available modules\n"
+ " --dump-resample-methods Dump available resample methods\n"
+ " --cleanup-shm Cleanup stale shared memory segments\n"
+ " --start Start the daemon if it is not running\n"
+ " -k --kill Kill a running daemon\n"
+ " --check Check for a running daemon (only returns exit code)\n\n"
+
+ "OPTIONS:\n"
+ " --system[=BOOL] Run as system-wide instance\n"
+ " -D, --daemonize[=BOOL] Daemonize after startup\n"
+ " --fail[=BOOL] Quit when startup fails\n"
+ " --high-priority[=BOOL] Try to set high nice level\n"
+ " (only available as root, when SUID or\n"
+ " with elevated RLIMIT_NICE)\n"
+ " --realtime[=BOOL] Try to enable realtime scheduling\n"
+ " (only available as root, when SUID or\n"
+ " with elevated RLIMIT_RTPRIO)\n"
+ " --disallow-module-loading[=BOOL] Disallow user requested module\n"
+ " loading/unloading after startup\n"
+ " --disallow-exit[=BOOL] Disallow user requested exit\n"
+ " --exit-idle-time=SECS Terminate the daemon when idle and this\n"
+ " time passed\n"
+ " --scache-idle-time=SECS Unload autoloaded samples when idle and\n"
+ " this time passed\n"
+ " --log-level[=LEVEL] Increase or set verbosity level\n"
+ " -v --verbose Increase the verbosity level\n"
+ " --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n"
+ " Specify the log target\n"
+ " --log-meta[=BOOL] Include code location in log messages\n"
+ " --log-time[=BOOL] Include timestamps in log messages\n"
+ " --log-backtrace=FRAMES Include a backtrace in log messages\n"
+ " -p, --dl-search-path=PATH Set the search path for dynamic shared\n"
+ " objects (plugins)\n"
+ " --resample-method=METHOD Use the specified resampling method\n"
+ " (See --dump-resample-methods for\n"
+ " possible values)\n"
+ " --use-pid-file[=BOOL] Create a PID file\n"
+ " --no-cpu-limit[=BOOL] Do not install CPU load limiter on\n"
+ " platforms that support it.\n"
+ " --disable-shm[=BOOL] Disable shared memory support.\n"
+ " --enable-memfd[=BOOL] Enable memfd shared memory support.\n\n"
+
+ "STARTUP SCRIPT:\n"
+ " -L, --load=\"MODULE ARGUMENTS\" Load the specified plugin module with\n"
+ " the specified argument\n"
+ " -F, --file=FILENAME Run the specified script\n"
+ " -C Open a command line on the running TTY\n"
+ " after startup\n\n"
+
+ " -n Don't load default script file\n"),
+ pa_path_get_filename(argv0));
+}
+
+int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d) {
+ pa_strbuf *buf = NULL;
+ int c;
+ int b;
+
+ pa_assert(conf);
+ pa_assert(argc > 0);
+ pa_assert(argv);
+
+ buf = pa_strbuf_new();
+
+ if (conf->script_commands)
+ pa_strbuf_puts(buf, conf->script_commands);
+
+ while ((c = getopt_long(argc, argv, "L:F:ChDnp:kv", long_options, NULL)) != -1) {
+ switch (c) {
+ case ARG_HELP:
+ case 'h':
+ conf->cmd = PA_CMD_HELP;
+ break;
+
+ case ARG_VERSION:
+ conf->cmd = PA_CMD_VERSION;
+ break;
+
+ case ARG_DUMP_CONF:
+ conf->cmd = PA_CMD_DUMP_CONF;
+ break;
+
+ case ARG_DUMP_MODULES:
+ conf->cmd = PA_CMD_DUMP_MODULES;
+ break;
+
+ case ARG_DUMP_RESAMPLE_METHODS:
+ conf->cmd = PA_CMD_DUMP_RESAMPLE_METHODS;
+ break;
+
+ case ARG_CLEANUP_SHM:
+ conf->cmd = PA_CMD_CLEANUP_SHM;
+ break;
+
+ case 'k':
+ case ARG_KILL:
+ conf->cmd = PA_CMD_KILL;
+ break;
+
+ case ARG_START:
+ conf->cmd = PA_CMD_START;
+ conf->daemonize = true;
+ break;
+
+ case ARG_CHECK:
+ conf->cmd = PA_CMD_CHECK;
+ break;
+
+ case ARG_LOAD:
+ case 'L':
+ pa_strbuf_printf(buf, "load-module %s\n", optarg);
+ break;
+
+ case ARG_FILE:
+ case 'F': {
+ char *p;
+ pa_strbuf_printf(buf, ".include %s\n", p = pa_make_path_absolute(optarg));
+ pa_xfree(p);
+ break;
+ }
+
+ case 'C':
+ pa_strbuf_puts(buf, "load-module module-cli exit_on_eof=1\n");
+ break;
+
+ case ARG_DAEMONIZE:
+ case 'D':
+ if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ pa_log(_("--daemonize expects boolean argument"));
+ goto fail;
+ }
+ conf->daemonize = !!b;
+ break;
+
+ case ARG_FAIL:
+ if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ pa_log(_("--fail expects boolean argument"));
+ goto fail;
+ }
+ conf->fail = !!b;
+ break;
+
+ case 'v':
+ case ARG_LOG_LEVEL:
+
+ if (optarg) {
+ if (pa_daemon_conf_set_log_level(conf, optarg) < 0) {
+ pa_log(_("--log-level expects log level argument (either numeric in range 0..4 or one of debug, info, notice, warn, error)."));
+ goto fail;
+ }
+ } else {
+ if (conf->log_level < PA_LOG_LEVEL_MAX-1)
+ conf->log_level++;
+ }
+
+ break;
+
+ case ARG_HIGH_PRIORITY:
+ if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ pa_log(_("--high-priority expects boolean argument"));
+ goto fail;
+ }
+ conf->high_priority = !!b;
+ break;
+
+ case ARG_REALTIME:
+ if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ pa_log(_("--realtime expects boolean argument"));
+ goto fail;
+ }
+ conf->realtime_scheduling = !!b;
+ break;
+
+ case ARG_DISALLOW_MODULE_LOADING:
+ if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ pa_log(_("--disallow-module-loading expects boolean argument"));
+ goto fail;
+ }
+ conf->disallow_module_loading = !!b;
+ break;
+
+ case ARG_DISALLOW_EXIT:
+ if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ pa_log(_("--disallow-exit expects boolean argument"));
+ goto fail;
+ }
+ conf->disallow_exit = !!b;
+ break;
+
+ case ARG_USE_PID_FILE:
+ if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ pa_log(_("--use-pid-file expects boolean argument"));
+ goto fail;
+ }
+ conf->use_pid_file = !!b;
+ break;
+
+ case 'p':
+ case ARG_DL_SEARCH_PATH:
+ pa_xfree(conf->dl_search_path);
+ conf->dl_search_path = pa_xstrdup(optarg);
+ break;
+
+ case 'n':
+ conf->load_default_script_file = false;
+ break;
+
+ case ARG_LOG_TARGET:
+ if (pa_daemon_conf_set_log_target(conf, optarg) < 0) {
+#ifdef HAVE_SYSTEMD_JOURNAL
+ pa_log(_("Invalid log target: use either 'syslog', 'journal', 'stderr' or 'auto' or a valid file name 'file:<path>', 'newfile:<path>'."));
+#else
+ pa_log(_("Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file name 'file:<path>', 'newfile:<path>'."));
+#endif
+ goto fail;
+ }
+ break;
+
+ case ARG_LOG_TIME:
+ if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ pa_log(_("--log-time expects boolean argument"));
+ goto fail;
+ }
+ conf->log_time = !!b;
+ break;
+
+ case ARG_LOG_META:
+ if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ pa_log(_("--log-meta expects boolean argument"));
+ goto fail;
+ }
+ conf->log_meta = !!b;
+ break;
+
+ case ARG_LOG_BACKTRACE:
+ conf->log_backtrace = (unsigned) atoi(optarg);
+ break;
+
+ case ARG_EXIT_IDLE_TIME:
+ conf->exit_idle_time = atoi(optarg);
+ break;
+
+ case ARG_SCACHE_IDLE_TIME:
+ conf->scache_idle_time = atoi(optarg);
+ break;
+
+ case ARG_RESAMPLE_METHOD:
+ if (pa_daemon_conf_set_resample_method(conf, optarg) < 0) {
+ pa_log(_("Invalid resample method '%s'."), optarg);
+ goto fail;
+ }
+ break;
+
+ case ARG_SYSTEM:
+ if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ pa_log(_("--system expects boolean argument"));
+ goto fail;
+ }
+ conf->system_instance = !!b;
+ break;
+
+ case ARG_NO_CPU_LIMIT:
+ if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ pa_log(_("--no-cpu-limit expects boolean argument"));
+ goto fail;
+ }
+ conf->no_cpu_limit = !!b;
+ break;
+
+ case ARG_DISABLE_SHM:
+ if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ pa_log(_("--disable-shm expects boolean argument"));
+ goto fail;
+ }
+ conf->disable_shm = !!b;
+ break;
+
+ case ARG_ENABLE_MEMFD:
+ if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ pa_log(_("--enable-memfd expects boolean argument"));
+ goto fail;
+ }
+ conf->disable_memfd = !b;
+ break;
+
+ default:
+ goto fail;
+ }
+ }
+
+ pa_xfree(conf->script_commands);
+ conf->script_commands = pa_strbuf_to_string_free(buf);
+
+ *d = optind;
+
+ return 0;
+
+fail:
+ if (buf)
+ pa_strbuf_free(buf);
+
+ return -1;
+}
diff --git a/src/daemon/cmdline.h b/src/daemon/cmdline.h
new file mode 100644
index 0000000..771c14d
--- /dev/null
+++ b/src/daemon/cmdline.h
@@ -0,0 +1,33 @@
+#ifndef foocmdlinehfoo
+#define foocmdlinehfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+
+ 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/>.
+***/
+
+#include "daemon-conf.h"
+
+/* Parse the command line and store its data in *c. Return the index
+ * of the first unparsed argument in *d. */
+int pa_cmdline_parse(pa_daemon_conf*c, int argc, char *const argv [], int *d);
+
+/* Show the command line help. The command name is extracted from
+ * argv[0] which should be passed in argv0. */
+void pa_cmdline_help(const char *argv0);
+
+#endif
diff --git a/src/daemon/cpulimit.c b/src/daemon/cpulimit.c
new file mode 100644
index 0000000..a3deaf2
--- /dev/null
+++ b/src/daemon/cpulimit.c
@@ -0,0 +1,244 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+
+ 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/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+
+#include "cpulimit.h"
+
+#ifdef HAVE_SIGXCPU
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+/* This module implements a watchdog that makes sure that the current
+ * process doesn't consume more than 70% CPU time for 10 seconds. This
+ * is very useful when using SCHED_FIFO scheduling which effectively
+ * disables multitasking. */
+
+/* Method of operation: Using SIGXCPU a signal handler is called every
+ * 10s process CPU time. That function checks if less than 14s system
+ * time have passed. In that case, it tries to contact the main event
+ * loop through a pipe. After two additional seconds it is checked
+ * whether the main event loop contact was successful. If not, the
+ * program is terminated forcibly. */
+
+/* Utilize this much CPU time at maximum */
+#define CPUTIME_PERCENT 70
+
+/* Check every 10s */
+#define CPUTIME_INTERVAL_SOFT (10)
+
+/* Recheck after 5s */
+#define CPUTIME_INTERVAL_HARD (5)
+
+/* Time of the last CPU load check */
+static pa_usec_t last_time = 0;
+
+/* Pipe for communicating with the main loop */
+static int the_pipe[2] = {-1, -1};
+
+/* Main event loop and IO event for the FIFO */
+static pa_mainloop_api *api = NULL;
+static pa_io_event *io_event = NULL;
+
+/* Saved sigaction struct for SIGXCPU */
+static struct sigaction sigaction_prev;
+
+/* Nonzero after pa_cpu_limit_init() */
+static bool installed = false;
+
+/* The current state of operation */
+static enum {
+ PHASE_IDLE, /* Normal state */
+ PHASE_SOFT /* After CPU overload has been detected */
+} phase = PHASE_IDLE;
+
+/* Reset the SIGXCPU timer to the next t seconds */
+static void reset_cpu_time(int t) {
+ long n;
+ struct rlimit rl;
+ struct rusage ru;
+
+ /* Get the current CPU time of the current process */
+ pa_assert_se(getrusage(RUSAGE_SELF, &ru) >= 0);
+
+ n = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec + t;
+ pa_assert_se(getrlimit(RLIMIT_CPU, &rl) >= 0);
+
+ rl.rlim_cur = (rlim_t) n;
+ pa_assert_se(setrlimit(RLIMIT_CPU, &rl) >= 0);
+}
+
+/* A simple, thread-safe puts() work-alike */
+static void write_err(const char *p) {
+ pa_loop_write(2, p, strlen(p), NULL);
+}
+
+/* The signal handler, called on every SIGXCPU */
+static void signal_handler(int sig) {
+ int saved_errno;
+
+ saved_errno = errno;
+ pa_assert(sig == SIGXCPU);
+
+ if (phase == PHASE_IDLE) {
+ pa_usec_t now, elapsed;
+
+#ifdef PRINT_CPU_LOAD
+ char t[256];
+#endif
+
+ now = pa_rtclock_now();
+ elapsed = now - last_time;
+
+#ifdef PRINT_CPU_LOAD
+ pa_snprintf(t, sizeof(t), "Using %0.1f%% CPU\n", ((double) CPUTIME_INTERVAL_SOFT * (double) PA_USEC_PER_SEC) / (double) elapsed * 100.0);
+ write_err(t);
+#endif
+
+ if (((double) CPUTIME_INTERVAL_SOFT * (double) PA_USEC_PER_SEC) >= ((double) elapsed * (double) CPUTIME_PERCENT / 100.0)) {
+ static const char c = 'X';
+
+ write_err("Soft CPU time limit exhausted, terminating.\n");
+
+ /* Try a soft cleanup */
+ (void) pa_write(the_pipe[1], &c, sizeof(c), NULL);
+ phase = PHASE_SOFT;
+ reset_cpu_time(CPUTIME_INTERVAL_HARD);
+
+ } else {
+
+ /* Everything's fine */
+ reset_cpu_time(CPUTIME_INTERVAL_SOFT);
+ last_time = now;
+ }
+
+ } else if (phase == PHASE_SOFT) {
+ write_err("Hard CPU time limit exhausted, terminating forcibly.\n");
+ abort(); /* Forced exit */
+ }
+
+ errno = saved_errno;
+}
+
+/* Callback for IO events on the FIFO */
+static void callback(pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata) {
+ char c;
+ pa_assert(m);
+ pa_assert(e);
+ pa_assert(f == PA_IO_EVENT_INPUT);
+ pa_assert(e == io_event);
+ pa_assert(fd == the_pipe[0]);
+
+ pa_log("Received request to terminate due to CPU overload.");
+
+ (void) pa_read(the_pipe[0], &c, sizeof(c), NULL);
+ m->quit(m, 1); /* Quit the main loop */
+}
+
+/* Initializes CPU load limiter */
+int pa_cpu_limit_init(pa_mainloop_api *m) {
+ struct sigaction sa;
+
+ pa_assert(m);
+ pa_assert(!api);
+ pa_assert(!io_event);
+ pa_assert(the_pipe[0] == -1);
+ pa_assert(the_pipe[1] == -1);
+ pa_assert(!installed);
+
+ last_time = pa_rtclock_now();
+
+ /* Prepare the main loop pipe */
+ if (pa_pipe_cloexec(the_pipe) < 0) {
+ pa_log("pipe() failed: %s", pa_cstrerror(errno));
+ return -1;
+ }
+
+ pa_make_fd_nonblock(the_pipe[0]);
+ pa_make_fd_nonblock(the_pipe[1]);
+
+ api = m;
+ io_event = api->io_new(m, the_pipe[0], PA_IO_EVENT_INPUT, callback, NULL);
+
+ phase = PHASE_IDLE;
+
+ /* Install signal handler for SIGXCPU */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = signal_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+
+ if (sigaction(SIGXCPU, &sa, &sigaction_prev) < 0) {
+ pa_cpu_limit_done();
+ return -1;
+ }
+
+ installed = true;
+
+ reset_cpu_time(CPUTIME_INTERVAL_SOFT);
+
+ return 0;
+}
+
+/* Shutdown CPU load limiter */
+void pa_cpu_limit_done(void) {
+
+ if (io_event) {
+ pa_assert(api);
+ api->io_free(io_event);
+ io_event = NULL;
+ api = NULL;
+ }
+
+ pa_close_pipe(the_pipe);
+
+ if (installed) {
+ pa_assert_se(sigaction(SIGXCPU, &sigaction_prev, NULL) >= 0);
+ installed = false;
+ }
+}
+
+#else /* HAVE_SIGXCPU */
+
+int pa_cpu_limit_init(pa_mainloop_api *m) {
+ return 0;
+}
+
+void pa_cpu_limit_done(void) {
+}
+
+#endif
diff --git a/src/daemon/cpulimit.h b/src/daemon/cpulimit.h
new file mode 100644
index 0000000..460f8c5
--- /dev/null
+++ b/src/daemon/cpulimit.h
@@ -0,0 +1,32 @@
+#ifndef foocpulimithfoo
+#define foocpulimithfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+
+ 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/>.
+***/
+
+#include <pulse/mainloop-api.h>
+
+/* This kills the pulseaudio process if it eats more than 70% of the
+ * CPU time. This is build around setrlimit() and SIGXCPU. It is handy
+ * in case of using SCHED_FIFO which may freeze the whole machine */
+
+int pa_cpu_limit_init(pa_mainloop_api *m);
+void pa_cpu_limit_done(void);
+
+#endif
diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
new file mode 100644
index 0000000..bcf7329
--- /dev/null
+++ b/src/daemon/daemon-conf.c
@@ -0,0 +1,872 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ 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/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_SCHED_H
+#include <sched.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulse/version.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/conf-parser.h>
+#include <pulsecore/resampler.h>
+#include <pulsecore/macro.h>
+
+#include "daemon-conf.h"
+
+#define DEFAULT_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "default.pa"
+#define DEFAULT_SCRIPT_FILE_USER PA_PATH_SEP "default.pa"
+#define DEFAULT_SYSTEM_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "system.pa"
+
+#define DEFAULT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "daemon.conf"
+#define DEFAULT_CONFIG_FILE_USER PA_PATH_SEP "daemon.conf"
+
+#define ENV_SCRIPT_FILE "PULSE_SCRIPT"
+#define ENV_CONFIG_FILE "PULSE_CONFIG"
+#define ENV_DL_SEARCH_PATH "PULSE_DLPATH"
+
+static const pa_daemon_conf default_conf = {
+ .cmd = PA_CMD_DAEMON,
+ .daemonize = false,
+ .fail = true,
+ .high_priority = true,
+ .nice_level = -11,
+ .realtime_scheduling = true,
+ .realtime_priority = 5, /* Half of JACK's default rtprio */
+ .disallow_module_loading = false,
+ .disallow_exit = false,
+ .flat_volumes = false,
+ .rescue_streams = true,
+ .exit_idle_time = 20,
+ .scache_idle_time = 20,
+ .script_commands = NULL,
+ .dl_search_path = NULL,
+ .load_default_script_file = true,
+ .default_script_file = NULL,
+ .log_target = NULL,
+ .log_level = PA_LOG_NOTICE,
+ .log_backtrace = 0,
+ .log_meta = false,
+ .log_time = false,
+ .resample_method = PA_RESAMPLER_AUTO,
+ .avoid_resampling = false,
+ .disable_remixing = false,
+ .remixing_use_all_sink_channels = true,
+ .remixing_produce_lfe = false,
+ .remixing_consume_lfe = false,
+ .lfe_crossover_freq = 0,
+ .config_file = NULL,
+ .use_pid_file = true,
+ .system_instance = false,
+#ifdef HAVE_DBUS
+ .local_server_type = PA_SERVER_TYPE_UNSET, /* The actual default is _USER, but we have to detect when the user doesn't specify this option. */
+#endif
+ .no_cpu_limit = true,
+ .disable_shm = false,
+ .disable_memfd = false,
+ .lock_memory = false,
+ .deferred_volume = true,
+ .default_n_fragments = 4,
+ .default_fragment_size_msec = 25,
+ .deferred_volume_safety_margin_usec = 8000,
+ .deferred_volume_extra_delay_usec = 0,
+ .default_sample_spec = { .format = PA_SAMPLE_S16NE, .rate = 44100, .channels = 2 },
+ .alternate_sample_rate = 48000,
+ .default_channel_map = { .channels = 2, .map = { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } },
+ .shm_size = 0
+#ifdef HAVE_SYS_RESOURCE_H
+ ,.rlimit_fsize = { .value = 0, .is_set = false },
+ .rlimit_data = { .value = 0, .is_set = false },
+ .rlimit_stack = { .value = 0, .is_set = false },
+ .rlimit_core = { .value = 0, .is_set = false }
+#ifdef RLIMIT_RSS
+ ,.rlimit_rss = { .value = 0, .is_set = false }
+#endif
+#ifdef RLIMIT_NPROC
+ ,.rlimit_nproc = { .value = 0, .is_set = false }
+#endif
+#ifdef RLIMIT_NOFILE
+ ,.rlimit_nofile = { .value = 256, .is_set = true }
+#endif
+#ifdef RLIMIT_MEMLOCK
+ ,.rlimit_memlock = { .value = 0, .is_set = false }
+#endif
+#ifdef RLIMIT_AS
+ ,.rlimit_as = { .value = 0, .is_set = false }
+#endif
+#ifdef RLIMIT_LOCKS
+ ,.rlimit_locks = { .value = 0, .is_set = false }
+#endif
+#ifdef RLIMIT_SIGPENDING
+ ,.rlimit_sigpending = { .value = 0, .is_set = false }
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ ,.rlimit_msgqueue = { .value = 0, .is_set = false }
+#endif
+#ifdef RLIMIT_NICE
+ ,.rlimit_nice = { .value = 31, .is_set = true } /* nice level of -11 */
+#endif
+#ifdef RLIMIT_RTPRIO
+ ,.rlimit_rtprio = { .value = 9, .is_set = true } /* One below JACK's default for the server */
+#endif
+#ifdef RLIMIT_RTTIME
+ ,.rlimit_rttime = { .value = 200*PA_USEC_PER_MSEC, .is_set = true } /* rtkit's limit is 200 ms */
+#endif
+#endif
+};
+
+pa_daemon_conf *pa_daemon_conf_new(void) {
+ pa_daemon_conf *c;
+
+ c = pa_xnewdup(pa_daemon_conf, &default_conf, 1);
+
+#ifdef OS_IS_WIN32
+ c->dl_search_path = pa_sprintf_malloc("%s" PA_PATH_SEP "lib" PA_PATH_SEP "pulse-%d.%d" PA_PATH_SEP "modules",
+ pa_win32_get_toplevel(NULL), PA_MAJOR, PA_MINOR);
+#else
+#ifdef HAVE_RUNNING_FROM_BUILD_TREE
+ if (pa_run_from_build_tree()) {
+ pa_log_notice("Detected that we are run from the build tree, fixing search path.");
+#ifdef MESON_BUILD
+ c->dl_search_path = pa_xstrdup(PA_BUILDDIR PA_PATH_SEP "src" PA_PATH_SEP "modules");
+#else
+ c->dl_search_path = pa_xstrdup(PA_BUILDDIR);
+#endif // Endof #ifdef MESON_BUILD
+ } else
+#endif // Endof #ifdef HAVE_RUNNING_FROM_BUILD_TREE
+ c->dl_search_path = pa_xstrdup(PA_DLSEARCHPATH);
+#endif // Endof #ifdef OS_IS_WIN32
+
+ return c;
+}
+
+void pa_daemon_conf_free(pa_daemon_conf *c) {
+ pa_assert(c);
+
+ pa_xfree(c->script_commands);
+ pa_xfree(c->dl_search_path);
+ pa_xfree(c->default_script_file);
+
+ if (c->log_target)
+ pa_log_target_free(c->log_target);
+
+ pa_xfree(c->config_file);
+ pa_xfree(c);
+}
+
+int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string) {
+ pa_log_target *log_target = NULL;
+
+ pa_assert(c);
+ pa_assert(string);
+
+ if (!pa_streq(string, "auto")) {
+ log_target = pa_log_parse_target(string);
+
+ if (!log_target)
+ return -1;
+ }
+
+ c->log_target = log_target;
+
+ return 0;
+}
+
+int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string) {
+ uint32_t u;
+ pa_assert(c);
+ pa_assert(string);
+
+ if (pa_atou(string, &u) >= 0) {
+ if (u >= PA_LOG_LEVEL_MAX)
+ return -1;
+
+ c->log_level = (pa_log_level_t) u;
+ } else if (pa_startswith(string, "debug"))
+ c->log_level = PA_LOG_DEBUG;
+ else if (pa_startswith(string, "info"))
+ c->log_level = PA_LOG_INFO;
+ else if (pa_startswith(string, "notice"))
+ c->log_level = PA_LOG_NOTICE;
+ else if (pa_startswith(string, "warn"))
+ c->log_level = PA_LOG_WARN;
+ else if (pa_startswith(string, "err"))
+ c->log_level = PA_LOG_ERROR;
+ else
+ return -1;
+
+ return 0;
+}
+
+int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string) {
+ int m;
+ pa_assert(c);
+ pa_assert(string);
+
+ if ((m = pa_parse_resample_method(string)) < 0)
+ return -1;
+
+ c->resample_method = m;
+ return 0;
+}
+
+int pa_daemon_conf_set_local_server_type(pa_daemon_conf *c, const char *string) {
+ pa_assert(c);
+ pa_assert(string);
+
+ if (pa_streq(string, "user"))
+ c->local_server_type = PA_SERVER_TYPE_USER;
+ else if (pa_streq(string, "system")) {
+ c->local_server_type = PA_SERVER_TYPE_SYSTEM;
+ } else if (pa_streq(string, "none")) {
+ c->local_server_type = PA_SERVER_TYPE_NONE;
+ } else
+ return -1;
+
+ return 0;
+}
+
+static int parse_log_target(pa_config_parser_state *state) {
+ pa_daemon_conf *c;
+
+ pa_assert(state);
+
+ c = state->data;
+
+ if (pa_daemon_conf_set_log_target(c, state->rvalue) < 0) {
+ pa_log(_("[%s:%u] Invalid log target '%s'."), state->filename, state->lineno, state->rvalue);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int parse_log_level(pa_config_parser_state *state) {
+ pa_daemon_conf *c;
+
+ pa_assert(state);
+
+ c = state->data;
+
+ if (pa_daemon_conf_set_log_level(c, state->rvalue) < 0) {
+ pa_log(_("[%s:%u] Invalid log level '%s'."), state->filename, state->lineno, state->rvalue);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int parse_resample_method(pa_config_parser_state *state) {
+ pa_daemon_conf *c;
+
+ pa_assert(state);
+
+ c = state->data;
+
+ if (pa_daemon_conf_set_resample_method(c, state->rvalue) < 0) {
+ pa_log(_("[%s:%u] Invalid resample method '%s'."), state->filename, state->lineno, state->rvalue);
+ return -1;
+ }
+
+ return 0;
+}
+
+#ifdef HAVE_SYS_RESOURCE_H
+static int parse_rlimit(pa_config_parser_state *state) {
+ struct pa_rlimit *r;
+
+ pa_assert(state);
+
+ r = state->data;
+
+ if (state->rvalue[strspn(state->rvalue, "\t ")] == 0) {
+ /* Empty string */
+ r->is_set = 0;
+ r->value = 0;
+ } else {
+ int32_t k;
+ if (pa_atoi(state->rvalue, &k) < 0) {
+ pa_log(_("[%s:%u] Invalid rlimit '%s'."), state->filename, state->lineno, state->rvalue);
+ return -1;
+ }
+ r->is_set = k >= 0;
+ r->value = k >= 0 ? (rlim_t) k : 0;
+ }
+
+ return 0;
+}
+#endif
+
+static int parse_sample_format(pa_config_parser_state *state) {
+ pa_daemon_conf *c;
+ pa_sample_format_t f;
+
+ pa_assert(state);
+
+ c = state->data;
+
+ if ((f = pa_parse_sample_format(state->rvalue)) < 0) {
+ pa_log(_("[%s:%u] Invalid sample format '%s'."), state->filename, state->lineno, state->rvalue);
+ return -1;
+ }
+
+ c->default_sample_spec.format = f;
+ return 0;
+}
+
+static int parse_sample_rate(pa_config_parser_state *state) {
+ pa_daemon_conf *c;
+ uint32_t r;
+
+ pa_assert(state);
+
+ c = state->data;
+
+ if (pa_atou(state->rvalue, &r) < 0 || !pa_sample_rate_valid(r)) {
+ pa_log(_("[%s:%u] Invalid sample rate '%s'."), state->filename, state->lineno, state->rvalue);
+ return -1;
+ }
+
+ c->default_sample_spec.rate = r;
+ return 0;
+}
+
+static int parse_alternate_sample_rate(pa_config_parser_state *state) {
+ pa_daemon_conf *c;
+ uint32_t r;
+
+ pa_assert(state);
+
+ c = state->data;
+
+ if (pa_atou(state->rvalue, &r) < 0 || !pa_sample_rate_valid(r)) {
+ pa_log(_("[%s:%u] Invalid sample rate '%s'."), state->filename, state->lineno, state->rvalue);
+ return -1;
+ }
+
+ c->alternate_sample_rate = r;
+ return 0;
+}
+
+struct channel_conf_info {
+ pa_daemon_conf *conf;
+ bool default_sample_spec_set;
+ bool default_channel_map_set;
+};
+
+static int parse_sample_channels(pa_config_parser_state *state) {
+ struct channel_conf_info *i;
+ int32_t n;
+
+ pa_assert(state);
+
+ i = state->data;
+
+ if (pa_atoi(state->rvalue, &n) < 0 || !pa_channels_valid(n)) {
+ pa_log(_("[%s:%u] Invalid sample channels '%s'."), state->filename, state->lineno, state->rvalue);
+ return -1;
+ }
+
+ i->conf->default_sample_spec.channels = (uint8_t) n;
+ i->default_sample_spec_set = true;
+ return 0;
+}
+
+static int parse_channel_map(pa_config_parser_state *state) {
+ struct channel_conf_info *i;
+
+ pa_assert(state);
+
+ i = state->data;
+
+ if (!pa_channel_map_parse(&i->conf->default_channel_map, state->rvalue)) {
+ pa_log(_("[%s:%u] Invalid channel map '%s'."), state->filename, state->lineno, state->rvalue);
+ return -1;
+ }
+
+ i->default_channel_map_set = true;
+ return 0;
+}
+
+static int parse_fragments(pa_config_parser_state *state) {
+ pa_daemon_conf *c;
+ int32_t n;
+
+ pa_assert(state);
+
+ c = state->data;
+
+ if (pa_atoi(state->rvalue, &n) < 0 || n < 2) {
+ pa_log(_("[%s:%u] Invalid number of fragments '%s'."), state->filename, state->lineno, state->rvalue);
+ return -1;
+ }
+
+ c->default_n_fragments = (unsigned) n;
+ return 0;
+}
+
+static int parse_fragment_size_msec(pa_config_parser_state *state) {
+ pa_daemon_conf *c;
+ int32_t n;
+
+ pa_assert(state);
+
+ c = state->data;
+
+ if (pa_atoi(state->rvalue, &n) < 0 || n < 1) {
+ pa_log(_("[%s:%u] Invalid fragment size '%s'."), state->filename, state->lineno, state->rvalue);
+ return -1;
+ }
+
+ c->default_fragment_size_msec = (unsigned) n;
+ return 0;
+}
+
+static int parse_nice_level(pa_config_parser_state *state) {
+ pa_daemon_conf *c;
+ int32_t level;
+
+ pa_assert(state);
+
+ c = state->data;
+
+ if (pa_atoi(state->rvalue, &level) < 0 || level < -20 || level > 19) {
+ pa_log(_("[%s:%u] Invalid nice level '%s'."), state->filename, state->lineno, state->rvalue);
+ return -1;
+ }
+
+ c->nice_level = (int) level;
+ return 0;
+}
+
+static int parse_rtprio(pa_config_parser_state *state) {
+#if !defined(OS_IS_WIN32) && defined(HAVE_SCHED_H)
+ pa_daemon_conf *c;
+ int32_t rtprio;
+#endif
+
+ pa_assert(state);
+
+#ifdef OS_IS_WIN32
+ pa_log("[%s:%u] Realtime priority not available on win32.", state->filename, state->lineno);
+#else
+# ifdef HAVE_SCHED_H
+ c = state->data;
+
+ if (pa_atoi(state->rvalue, &rtprio) < 0 || rtprio < sched_get_priority_min(SCHED_FIFO) || rtprio > sched_get_priority_max(SCHED_FIFO)) {
+ pa_log("[%s:%u] Invalid realtime priority '%s'.", state->filename, state->lineno, state->rvalue);
+ return -1;
+ }
+
+ c->realtime_priority = (int) rtprio;
+# endif
+#endif /* OS_IS_WIN32 */
+
+ return 0;
+}
+
+static int parse_disable_lfe_remix(pa_config_parser_state *state) {
+ pa_daemon_conf *c;
+ int k;
+
+ pa_assert(state);
+ c = state->data;
+
+ if ((k = pa_parse_boolean(state->rvalue)) < 0) {
+ pa_log("[%s:%u] Failed to parse boolean value: %s", state->filename, state->lineno, state->rvalue);
+ return -1;
+ }
+
+ c->remixing_produce_lfe = c->remixing_consume_lfe = !k;
+
+ pa_log("[%s:%u] Deprecated option 'disable-lfe-remixing' found.", state->filename, state->lineno);
+ pa_log("[%s:%u] Please migrate to 'remixing-produce-lfe' and 'remixing-consume-lfe', set both to '%s'.",
+ state->filename, state->lineno, pa_yes_no(c->remixing_produce_lfe));
+
+ return 0;
+}
+
+static int parse_enable_lfe_remix(pa_config_parser_state *state) {
+ pa_daemon_conf *c;
+ int k;
+
+ pa_assert(state);
+ c = state->data;
+
+ if ((k = pa_parse_boolean(state->rvalue)) < 0) {
+ pa_log("[%s:%u] Failed to parse boolean value: %s", state->filename, state->lineno, state->rvalue);
+ return -1;
+ }
+
+ c->remixing_produce_lfe = c->remixing_consume_lfe = k;
+
+ pa_log("[%s:%u] Deprecated option 'enable-lfe-remixing' found.", state->filename, state->lineno);
+ pa_log("[%s:%u] Please migrate to 'remixing-produce-lfe' and 'remixing-consume-lfe', set both to '%s'.",
+ state->filename, state->lineno, pa_yes_no(c->remixing_produce_lfe));
+
+ return 0;
+}
+
+#ifdef HAVE_DBUS
+static int parse_server_type(pa_config_parser_state *state) {
+ pa_daemon_conf *c;
+
+ pa_assert(state);
+
+ c = state->data;
+
+ if (pa_daemon_conf_set_local_server_type(c, state->rvalue) < 0) {
+ pa_log(_("[%s:%u] Invalid server type '%s'."), state->filename, state->lineno, state->rvalue);
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
+ int r = -1;
+ FILE *f = NULL;
+ struct channel_conf_info ci;
+ pa_config_item table[] = {
+ { "daemonize", pa_config_parse_bool, &c->daemonize, NULL },
+ { "fail", pa_config_parse_bool, &c->fail, NULL },
+ { "high-priority", pa_config_parse_bool, &c->high_priority, NULL },
+ { "realtime-scheduling", pa_config_parse_bool, &c->realtime_scheduling, NULL },
+ { "disallow-module-loading", pa_config_parse_bool, &c->disallow_module_loading, NULL },
+ { "allow-module-loading", pa_config_parse_not_bool, &c->disallow_module_loading, NULL },
+ { "disallow-exit", pa_config_parse_bool, &c->disallow_exit, NULL },
+ { "allow-exit", pa_config_parse_not_bool, &c->disallow_exit, NULL },
+ { "use-pid-file", pa_config_parse_bool, &c->use_pid_file, NULL },
+ { "system-instance", pa_config_parse_bool, &c->system_instance, NULL },
+#ifdef HAVE_DBUS
+ { "local-server-type", parse_server_type, c, NULL },
+#endif
+ { "no-cpu-limit", pa_config_parse_bool, &c->no_cpu_limit, NULL },
+ { "cpu-limit", pa_config_parse_not_bool, &c->no_cpu_limit, NULL },
+ { "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL },
+ { "enable-shm", pa_config_parse_not_bool, &c->disable_shm, NULL },
+ { "enable-memfd", pa_config_parse_not_bool, &c->disable_memfd, NULL },
+ { "flat-volumes", pa_config_parse_bool, &c->flat_volumes, NULL },
+ { "rescue-streams", pa_config_parse_bool, &c->rescue_streams, NULL },
+ { "lock-memory", pa_config_parse_bool, &c->lock_memory, NULL },
+ { "enable-deferred-volume", pa_config_parse_bool, &c->deferred_volume, NULL },
+ { "exit-idle-time", pa_config_parse_int, &c->exit_idle_time, NULL },
+ { "scache-idle-time", pa_config_parse_int, &c->scache_idle_time, NULL },
+ { "realtime-priority", parse_rtprio, c, NULL },
+ { "dl-search-path", pa_config_parse_string, &c->dl_search_path, NULL },
+ { "default-script-file", pa_config_parse_string, &c->default_script_file, NULL },
+ { "log-target", parse_log_target, c, NULL },
+ { "log-level", parse_log_level, c, NULL },
+ { "verbose", parse_log_level, c, NULL },
+ { "resample-method", parse_resample_method, c, NULL },
+ { "default-sample-format", parse_sample_format, c, NULL },
+ { "default-sample-rate", parse_sample_rate, c, NULL },
+ { "alternate-sample-rate", parse_alternate_sample_rate, c, NULL },
+ { "default-sample-channels", parse_sample_channels, &ci, NULL },
+ { "default-channel-map", parse_channel_map, &ci, NULL },
+ { "default-fragments", parse_fragments, c, NULL },
+ { "default-fragment-size-msec", parse_fragment_size_msec, c, NULL },
+ { "deferred-volume-safety-margin-usec",
+ pa_config_parse_unsigned, &c->deferred_volume_safety_margin_usec, NULL },
+ { "deferred-volume-extra-delay-usec",
+ pa_config_parse_int, &c->deferred_volume_extra_delay_usec, NULL },
+ { "nice-level", parse_nice_level, c, NULL },
+ { "avoid-resampling", pa_config_parse_bool, &c->avoid_resampling, NULL },
+ { "disable-remixing", pa_config_parse_bool, &c->disable_remixing, NULL },
+ { "enable-remixing", pa_config_parse_not_bool, &c->disable_remixing, NULL },
+ { "remixing-use-all-sink-channels",
+ pa_config_parse_bool, &c->remixing_use_all_sink_channels, NULL },
+ { "disable-lfe-remixing", parse_disable_lfe_remix, c, NULL },
+ { "enable-lfe-remixing", parse_enable_lfe_remix, c, NULL },
+ { "remixing-produce-lfe", pa_config_parse_bool, &c->remixing_produce_lfe, NULL },
+ { "remixing-consume-lfe", pa_config_parse_bool, &c->remixing_consume_lfe, NULL },
+ { "lfe-crossover-freq", pa_config_parse_unsigned, &c->lfe_crossover_freq, NULL },
+ { "load-default-script-file", pa_config_parse_bool, &c->load_default_script_file, NULL },
+ { "shm-size-bytes", pa_config_parse_size, &c->shm_size, NULL },
+ { "log-meta", pa_config_parse_bool, &c->log_meta, NULL },
+ { "log-time", pa_config_parse_bool, &c->log_time, NULL },
+ { "log-backtrace", pa_config_parse_unsigned, &c->log_backtrace, NULL },
+#ifdef HAVE_SYS_RESOURCE_H
+ { "rlimit-fsize", parse_rlimit, &c->rlimit_fsize, NULL },
+ { "rlimit-data", parse_rlimit, &c->rlimit_data, NULL },
+ { "rlimit-stack", parse_rlimit, &c->rlimit_stack, NULL },
+ { "rlimit-core", parse_rlimit, &c->rlimit_core, NULL },
+#ifdef RLIMIT_RSS
+ { "rlimit-rss", parse_rlimit, &c->rlimit_rss, NULL },
+#endif
+#ifdef RLIMIT_NOFILE
+ { "rlimit-nofile", parse_rlimit, &c->rlimit_nofile, NULL },
+#endif
+#ifdef RLIMIT_AS
+ { "rlimit-as", parse_rlimit, &c->rlimit_as, NULL },
+#endif
+#ifdef RLIMIT_NPROC
+ { "rlimit-nproc", parse_rlimit, &c->rlimit_nproc, NULL },
+#endif
+#ifdef RLIMIT_MEMLOCK
+ { "rlimit-memlock", parse_rlimit, &c->rlimit_memlock, NULL },
+#endif
+#ifdef RLIMIT_LOCKS
+ { "rlimit-locks", parse_rlimit, &c->rlimit_locks, NULL },
+#endif
+#ifdef RLIMIT_SIGPENDING
+ { "rlimit-sigpending", parse_rlimit, &c->rlimit_sigpending, NULL },
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ { "rlimit-msgqueue", parse_rlimit, &c->rlimit_msgqueue, NULL },
+#endif
+#ifdef RLIMIT_NICE
+ { "rlimit-nice", parse_rlimit, &c->rlimit_nice, NULL },
+#endif
+#ifdef RLIMIT_RTPRIO
+ { "rlimit-rtprio", parse_rlimit, &c->rlimit_rtprio, NULL },
+#endif
+#ifdef RLIMIT_RTTIME
+ { "rlimit-rttime", parse_rlimit, &c->rlimit_rttime, NULL },
+#endif
+#endif
+ { NULL, NULL, NULL, NULL },
+ };
+
+ pa_xfree(c->config_file);
+ c->config_file = NULL;
+
+ f = filename ?
+ pa_fopen_cloexec(c->config_file = pa_xstrdup(filename), "r") :
+ pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file);
+
+ if (!f && errno != ENOENT) {
+ pa_log_warn(_("Failed to open configuration file: %s"), pa_cstrerror(errno));
+ goto finish;
+ }
+
+ ci.default_channel_map_set = ci.default_sample_spec_set = false;
+ ci.conf = c;
+
+ r = f ? pa_config_parse(c->config_file, f, table, NULL, true, NULL) : 0;
+
+ if (r >= 0) {
+
+ /* Make sure that channel map and sample spec fit together */
+
+ if (ci.default_sample_spec_set &&
+ ci.default_channel_map_set &&
+ c->default_channel_map.channels != c->default_sample_spec.channels) {
+ pa_log_error(_("The specified default channel map has a different number of channels than the specified default number of channels."));
+ r = -1;
+ goto finish;
+ } else if (ci.default_sample_spec_set)
+ pa_channel_map_init_extend(&c->default_channel_map, c->default_sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
+ else if (ci.default_channel_map_set)
+ c->default_sample_spec.channels = c->default_channel_map.channels;
+ }
+
+finish:
+ if (f)
+ fclose(f);
+
+ return r;
+}
+
+int pa_daemon_conf_env(pa_daemon_conf *c) {
+ char *e;
+ pa_assert(c);
+
+ if ((e = getenv(ENV_DL_SEARCH_PATH))) {
+ pa_xfree(c->dl_search_path);
+ c->dl_search_path = pa_xstrdup(e);
+ }
+ if ((e = getenv(ENV_SCRIPT_FILE))) {
+ pa_xfree(c->default_script_file);
+ c->default_script_file = pa_xstrdup(e);
+ }
+
+ return 0;
+}
+
+const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c) {
+ pa_assert(c);
+
+ if (!c->default_script_file) {
+ if (c->system_instance)
+ c->default_script_file = pa_find_config_file(DEFAULT_SYSTEM_SCRIPT_FILE, NULL, ENV_SCRIPT_FILE);
+ else
+ c->default_script_file = pa_find_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE);
+ }
+
+ return c->default_script_file;
+}
+
+FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c) {
+ FILE *f;
+ pa_assert(c);
+
+ if (!c->default_script_file) {
+ if (c->system_instance)
+ f = pa_open_config_file(DEFAULT_SYSTEM_SCRIPT_FILE, NULL, ENV_SCRIPT_FILE, &c->default_script_file);
+ else
+ f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file);
+ } else
+ f = pa_fopen_cloexec(c->default_script_file, "r");
+
+ return f;
+}
+
+char *pa_daemon_conf_dump(pa_daemon_conf *c) {
+ static const char* const log_level_to_string[] = {
+ [PA_LOG_DEBUG] = "debug",
+ [PA_LOG_INFO] = "info",
+ [PA_LOG_NOTICE] = "notice",
+ [PA_LOG_WARN] = "warning",
+ [PA_LOG_ERROR] = "error"
+ };
+
+#ifdef HAVE_DBUS
+ static const char* const server_type_to_string[] = {
+ [PA_SERVER_TYPE_UNSET] = "!!UNSET!!",
+ [PA_SERVER_TYPE_USER] = "user",
+ [PA_SERVER_TYPE_SYSTEM] = "system",
+ [PA_SERVER_TYPE_NONE] = "none"
+ };
+#endif
+
+ pa_strbuf *s;
+ char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ char *log_target = NULL;
+
+ pa_assert(c);
+
+ s = pa_strbuf_new();
+
+ if (c->config_file)
+ pa_strbuf_printf(s, _("### Read from configuration file: %s ###\n"), c->config_file);
+
+ pa_assert(c->log_level < PA_LOG_LEVEL_MAX);
+
+ if (c->log_target)
+ log_target = pa_log_target_to_string(c->log_target);
+
+ pa_strbuf_printf(s, "daemonize = %s\n", pa_yes_no(c->daemonize));
+ pa_strbuf_printf(s, "fail = %s\n", pa_yes_no(c->fail));
+ pa_strbuf_printf(s, "high-priority = %s\n", pa_yes_no(c->high_priority));
+ pa_strbuf_printf(s, "nice-level = %i\n", c->nice_level);
+ pa_strbuf_printf(s, "realtime-scheduling = %s\n", pa_yes_no(c->realtime_scheduling));
+ pa_strbuf_printf(s, "realtime-priority = %i\n", c->realtime_priority);
+ pa_strbuf_printf(s, "allow-module-loading = %s\n", pa_yes_no(!c->disallow_module_loading));
+ pa_strbuf_printf(s, "allow-exit = %s\n", pa_yes_no(!c->disallow_exit));
+ pa_strbuf_printf(s, "use-pid-file = %s\n", pa_yes_no(c->use_pid_file));
+ pa_strbuf_printf(s, "system-instance = %s\n", pa_yes_no(c->system_instance));
+#ifdef HAVE_DBUS
+ pa_strbuf_printf(s, "local-server-type = %s\n", server_type_to_string[c->local_server_type]);
+#endif
+ pa_strbuf_printf(s, "cpu-limit = %s\n", pa_yes_no(!c->no_cpu_limit));
+ pa_strbuf_printf(s, "enable-shm = %s\n", pa_yes_no(!c->disable_shm));
+ pa_strbuf_printf(s, "flat-volumes = %s\n", pa_yes_no(c->flat_volumes));
+ pa_strbuf_printf(s, "rescue-streams = %s\n", pa_yes_no(c->rescue_streams));
+ pa_strbuf_printf(s, "lock-memory = %s\n", pa_yes_no(c->lock_memory));
+ pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time);
+ pa_strbuf_printf(s, "scache-idle-time = %i\n", c->scache_idle_time);
+ pa_strbuf_printf(s, "dl-search-path = %s\n", pa_strempty(c->dl_search_path));
+ pa_strbuf_printf(s, "default-script-file = %s\n", pa_strempty(pa_daemon_conf_get_default_script_file(c)));
+ pa_strbuf_printf(s, "load-default-script-file = %s\n", pa_yes_no(c->load_default_script_file));
+ pa_strbuf_printf(s, "log-target = %s\n", pa_strempty(log_target));
+ pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]);
+ pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method));
+ pa_strbuf_printf(s, "avoid-resampling = %s\n", pa_yes_no(c->avoid_resampling));
+ pa_strbuf_printf(s, "enable-remixing = %s\n", pa_yes_no(!c->disable_remixing));
+ pa_strbuf_printf(s, "remixing-use-all-sink-channels = %s\n", pa_yes_no(c->remixing_use_all_sink_channels));
+ pa_strbuf_printf(s, "remixing-produce-lfe = %s\n", pa_yes_no(c->remixing_produce_lfe));
+ pa_strbuf_printf(s, "remixing-consume-lfe = %s\n", pa_yes_no(c->remixing_consume_lfe));
+ pa_strbuf_printf(s, "lfe-crossover-freq = %u\n", c->lfe_crossover_freq);
+ pa_strbuf_printf(s, "default-sample-format = %s\n", pa_sample_format_to_string(c->default_sample_spec.format));
+ pa_strbuf_printf(s, "default-sample-rate = %u\n", c->default_sample_spec.rate);
+ pa_strbuf_printf(s, "alternate-sample-rate = %u\n", c->alternate_sample_rate);
+ pa_strbuf_printf(s, "default-sample-channels = %u\n", c->default_sample_spec.channels);
+ pa_strbuf_printf(s, "default-channel-map = %s\n", pa_channel_map_snprint(cm, sizeof(cm), &c->default_channel_map));
+ pa_strbuf_printf(s, "default-fragments = %u\n", c->default_n_fragments);
+ pa_strbuf_printf(s, "default-fragment-size-msec = %u\n", c->default_fragment_size_msec);
+ pa_strbuf_printf(s, "enable-deferred-volume = %s\n", pa_yes_no(c->deferred_volume));
+ pa_strbuf_printf(s, "deferred-volume-safety-margin-usec = %u\n", c->deferred_volume_safety_margin_usec);
+ pa_strbuf_printf(s, "deferred-volume-extra-delay-usec = %d\n", c->deferred_volume_extra_delay_usec);
+ pa_strbuf_printf(s, "shm-size-bytes = %lu\n", (unsigned long) c->shm_size);
+ pa_strbuf_printf(s, "log-meta = %s\n", pa_yes_no(c->log_meta));
+ pa_strbuf_printf(s, "log-time = %s\n", pa_yes_no(c->log_time));
+ pa_strbuf_printf(s, "log-backtrace = %u\n", c->log_backtrace);
+#ifdef HAVE_SYS_RESOURCE_H
+ pa_strbuf_printf(s, "rlimit-fsize = %li\n", c->rlimit_fsize.is_set ? (long int) c->rlimit_fsize.value : -1);
+ pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1);
+ pa_strbuf_printf(s, "rlimit-stack = %li\n", c->rlimit_stack.is_set ? (long int) c->rlimit_stack.value : -1);
+ pa_strbuf_printf(s, "rlimit-core = %li\n", c->rlimit_core.is_set ? (long int) c->rlimit_core.value : -1);
+#ifdef RLIMIT_RSS
+ pa_strbuf_printf(s, "rlimit-rss = %li\n", c->rlimit_rss.is_set ? (long int) c->rlimit_rss.value : -1);
+#endif
+#ifdef RLIMIT_AS
+ pa_strbuf_printf(s, "rlimit-as = %li\n", c->rlimit_as.is_set ? (long int) c->rlimit_as.value : -1);
+#endif
+#ifdef RLIMIT_NPROC
+ pa_strbuf_printf(s, "rlimit-nproc = %li\n", c->rlimit_nproc.is_set ? (long int) c->rlimit_nproc.value : -1);
+#endif
+#ifdef RLIMIT_NOFILE
+ pa_strbuf_printf(s, "rlimit-nofile = %li\n", c->rlimit_nofile.is_set ? (long int) c->rlimit_nofile.value : -1);
+#endif
+#ifdef RLIMIT_MEMLOCK
+ pa_strbuf_printf(s, "rlimit-memlock = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_memlock.value : -1);
+#endif
+#ifdef RLIMIT_LOCKS
+ pa_strbuf_printf(s, "rlimit-locks = %li\n", c->rlimit_locks.is_set ? (long int) c->rlimit_locks.value : -1);
+#endif
+#ifdef RLIMIT_SIGPENDING
+ pa_strbuf_printf(s, "rlimit-sigpending = %li\n", c->rlimit_sigpending.is_set ? (long int) c->rlimit_sigpending.value : -1);
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ pa_strbuf_printf(s, "rlimit-msgqueue = %li\n", c->rlimit_msgqueue.is_set ? (long int) c->rlimit_msgqueue.value : -1);
+#endif
+#ifdef RLIMIT_NICE
+ pa_strbuf_printf(s, "rlimit-nice = %li\n", c->rlimit_nice.is_set ? (long int) c->rlimit_nice.value : -1);
+#endif
+#ifdef RLIMIT_RTPRIO
+ pa_strbuf_printf(s, "rlimit-rtprio = %li\n", c->rlimit_rtprio.is_set ? (long int) c->rlimit_rtprio.value : -1);
+#endif
+#ifdef RLIMIT_RTTIME
+ pa_strbuf_printf(s, "rlimit-rttime = %li\n", c->rlimit_rttime.is_set ? (long int) c->rlimit_rttime.value : -1);
+#endif
+#endif
+
+ pa_xfree(log_target);
+
+ return pa_strbuf_to_string_free(s);
+}
diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h
new file mode 100644
index 0000000..fa713b9
--- /dev/null
+++ b/src/daemon/daemon-conf.h
@@ -0,0 +1,169 @@
+#ifndef foodaemonconfhfoo
+#define foodaemonconfhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ 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/>.
+***/
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core.h>
+#include <pulsecore/core-util.h>
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+/* The actual command to execute */
+typedef enum pa_daemon_conf_cmd {
+ PA_CMD_DAEMON, /* the default */
+ PA_CMD_START,
+ PA_CMD_HELP,
+ PA_CMD_VERSION,
+ PA_CMD_DUMP_CONF,
+ PA_CMD_DUMP_MODULES,
+ PA_CMD_KILL,
+ PA_CMD_CHECK,
+ PA_CMD_DUMP_RESAMPLE_METHODS,
+ PA_CMD_CLEANUP_SHM
+} pa_daemon_conf_cmd_t;
+
+#ifdef HAVE_SYS_RESOURCE_H
+typedef struct pa_rlimit {
+ rlim_t value;
+ bool is_set;
+} pa_rlimit;
+#endif
+
+/* A structure containing configuration data for the PulseAudio server . */
+typedef struct pa_daemon_conf {
+ pa_daemon_conf_cmd_t cmd;
+ bool daemonize,
+ fail,
+ high_priority,
+ realtime_scheduling,
+ disallow_module_loading,
+ use_pid_file,
+ system_instance,
+ no_cpu_limit,
+ disable_shm,
+ disable_memfd,
+ avoid_resampling,
+ disable_remixing,
+ remixing_use_all_sink_channels,
+ remixing_produce_lfe,
+ remixing_consume_lfe,
+ load_default_script_file,
+ disallow_exit,
+ log_meta,
+ log_time,
+ flat_volumes,
+ rescue_streams,
+ lock_memory,
+ deferred_volume;
+ pa_server_type_t local_server_type;
+ int exit_idle_time,
+ scache_idle_time,
+ realtime_priority,
+ nice_level,
+ resample_method;
+ char *script_commands, *dl_search_path, *default_script_file;
+ pa_log_target *log_target;
+ pa_log_level_t log_level;
+ unsigned log_backtrace;
+ char *config_file;
+
+#ifdef HAVE_SYS_RESOURCE_H
+ pa_rlimit rlimit_fsize, rlimit_data, rlimit_stack, rlimit_core;
+#ifdef RLIMIT_RSS
+ pa_rlimit rlimit_rss;
+#endif
+#ifdef RLIMIT_NOFILE
+ pa_rlimit rlimit_nofile;
+#endif
+#ifdef RLIMIT_AS
+ pa_rlimit rlimit_as;
+#endif
+#ifdef RLIMIT_NPROC
+ pa_rlimit rlimit_nproc;
+#endif
+#ifdef RLIMIT_MEMLOCK
+ pa_rlimit rlimit_memlock;
+#endif
+#ifdef RLIMIT_LOCKS
+ pa_rlimit rlimit_locks;
+#endif
+#ifdef RLIMIT_SIGPENDING
+ pa_rlimit rlimit_sigpending;
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ pa_rlimit rlimit_msgqueue;
+#endif
+#ifdef RLIMIT_NICE
+ pa_rlimit rlimit_nice;
+#endif
+#ifdef RLIMIT_RTPRIO
+ pa_rlimit rlimit_rtprio;
+#endif
+#ifdef RLIMIT_RTTIME
+ pa_rlimit rlimit_rttime;
+#endif
+#endif
+
+ unsigned default_n_fragments, default_fragment_size_msec;
+ unsigned deferred_volume_safety_margin_usec;
+ int deferred_volume_extra_delay_usec;
+ unsigned lfe_crossover_freq;
+ pa_sample_spec default_sample_spec;
+ uint32_t alternate_sample_rate;
+ pa_channel_map default_channel_map;
+ size_t shm_size;
+} pa_daemon_conf;
+
+/* Allocate a new structure and fill it with sane defaults */
+pa_daemon_conf* pa_daemon_conf_new(void);
+void pa_daemon_conf_free(pa_daemon_conf*c);
+
+/* Load configuration data from the specified file overwriting the
+ * current settings in *c. If filename is NULL load the default daemon
+ * configuration file */
+int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename);
+
+/* Pretty print the current configuration data of the daemon. The
+ * returned string has to be freed manually. The output of this
+ * function may be parsed with pa_daemon_conf_load(). */
+char *pa_daemon_conf_dump(pa_daemon_conf *c);
+
+/* Load the configuration data from the process' environment
+ * overwriting the current settings in *c. */
+int pa_daemon_conf_env(pa_daemon_conf *c);
+
+/* Set these configuration variables in the structure by passing a string */
+int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string);
+int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string);
+int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string);
+int pa_daemon_conf_set_local_server_type(pa_daemon_conf *c, const char *string);
+
+const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c);
+FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c);
+
+#endif
diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in
new file mode 100644
index 0000000..7409976
--- /dev/null
+++ b/src/daemon/daemon.conf.in
@@ -0,0 +1,97 @@
+# This file is part of PulseAudio.
+#
+# 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 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/>.
+
+## Configuration file for the PulseAudio daemon. See pulse-daemon.conf(5) for
+## more information. Default values are commented out. Use either ; or # for
+## commenting.
+changequote(`[', `]')dnl Set up m4 quoting
+
+; daemonize = no
+; fail = yes
+; allow-module-loading = yes
+; allow-exit = yes
+; use-pid-file = yes
+; system-instance = no
+ifelse(@HAVE_DBUS@, 1, [dnl
+; local-server-type = user
+])dnl
+; enable-shm = yes
+; enable-memfd = yes
+; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB
+; lock-memory = no
+; cpu-limit = no
+
+; high-priority = yes
+; nice-level = -11
+
+; realtime-scheduling = yes
+; realtime-priority = 5
+
+; exit-idle-time = 20
+; scache-idle-time = 20
+
+; dl-search-path = (depends on architecture)
+
+; load-default-script-file = yes
+; default-script-file = @PA_DEFAULT_CONFIG_DIR@/default.pa
+
+; log-target = auto
+; log-level = notice
+; log-meta = no
+; log-time = no
+; log-backtrace = 0
+
+; resample-method = speex-float-1
+; avoid-resampling = false
+; enable-remixing = yes
+; remixing-use-all-sink-channels = yes
+; remixing-produce-lfe = no
+; remixing-consume-lfe = no
+; lfe-crossover-freq = 0
+
+; flat-volumes = no
+
+; rescue-streams = yes
+
+ifelse(@HAVE_SYS_RESOURCE_H@, 1, [dnl
+; rlimit-fsize = -1
+; rlimit-data = -1
+; rlimit-stack = -1
+; rlimit-core = -1
+; rlimit-as = -1
+; rlimit-rss = -1
+; rlimit-nproc = -1
+; rlimit-nofile = 256
+; rlimit-memlock = -1
+; rlimit-locks = -1
+; rlimit-sigpending = -1
+; rlimit-msgqueue = -1
+; rlimit-nice = 31
+; rlimit-rtprio = 9
+; rlimit-rttime = 200000
+])dnl
+
+; default-sample-format = s16le
+; default-sample-rate = 44100
+; alternate-sample-rate = 48000
+; default-sample-channels = 2
+; default-channel-map = front-left,front-right
+
+; default-fragments = 4
+; default-fragment-size-msec = 25
+
+; enable-deferred-volume = yes
+; deferred-volume-safety-margin-usec = 8000
+; deferred-volume-extra-delay-usec = 0
diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in
new file mode 100755
index 0000000..030334f
--- /dev/null
+++ b/src/daemon/default.pa.in
@@ -0,0 +1,175 @@
+#!@PA_BINARY@ -nF
+#
+# This file is part of PulseAudio.
+#
+# 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 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/>.
+
+# This startup script is used only if PulseAudio is started per-user
+# (i.e. not in system mode)
+changequote(`[', `]')dnl Set up m4 quoting
+
+.fail
+
+### Automatically restore the volume of streams and devices
+load-module module-device-restore
+load-module module-stream-restore
+load-module module-card-restore
+
+### Automatically augment property information from .desktop files
+### stored in /usr/share/application
+load-module module-augment-properties
+
+### Should be after module-*-restore but before module-*-detect
+load-module module-switch-on-port-available
+
+### Load audio drivers statically
+### (it's probably better to not load these drivers manually, but instead
+### use module-udev-detect -- see below -- for doing this automatically)
+ifelse(@HAVE_ALSA@, 1, [dnl
+#load-module module-alsa-sink
+#load-module module-alsa-source device=hw:1,0
+])dnl
+ifelse(@HAVE_OSS_OUTPUT@, 1, [dnl
+#load-module module-oss device="/dev/dsp" sink_name=output source_name=input
+#load-module module-oss-mmap device="/dev/dsp" sink_name=output source_name=input
+])dnl
+ifelse(@HAVE_WAVEOUT@, 1, [dnl
+load-module module-waveout sink_name=output source_name=input
+])dnl
+#load-module module-null-sink
+ifelse(@HAVE_MKFIFO@, 1, [dnl
+#load-module module-pipe-sink
+])dnl
+
+### Automatically load driver modules depending on the hardware available
+ifelse(@HAVE_UDEV@, 1, [dnl
+.ifexists module-udev-detect@PA_SOEXT@
+load-module module-udev-detect
+.else
+], @HAVE_COREAUDIO@, 1, [dnl
+.ifexists module-coreaudio-detect@PA_SOEXT@
+load-module module-coreaudio-detect
+.else
+], [dnl
+.ifexists module-detect@PA_SOEXT@
+])dnl
+### Use the static hardware detection module (for systems that lack udev support)
+load-module module-detect
+.endif
+
+### Automatically connect sink and source if JACK server is present
+.ifexists module-jackdbus-detect@PA_SOEXT@
+.nofail
+load-module module-jackdbus-detect channels=2
+.fail
+.endif
+
+ifelse(@HAVE_BLUEZ@, 1, [dnl
+### Automatically load driver modules for Bluetooth hardware
+.ifexists module-bluetooth-policy@PA_SOEXT@
+load-module module-bluetooth-policy
+.endif
+
+.ifexists module-bluetooth-discover@PA_SOEXT@
+load-module module-bluetooth-discover
+.endif
+])dnl
+
+ifelse(@HAVE_AF_UNIX@, 1, [dnl
+### Load several protocols
+.ifexists module-esound-protocol-unix@PA_SOEXT@
+load-module module-esound-protocol-unix
+.endif
+load-module module-native-protocol-unix
+])dnl
+
+### Network access (may be configured with paprefs, so leave this commented
+### here if you plan to use paprefs)
+#load-module module-esound-protocol-tcp
+#load-module module-native-protocol-tcp
+ifelse(@HAVE_AVAHI@, 1, [dnl
+#load-module module-zeroconf-publish
+])dnl
+
+ifelse(@OS_IS_WIN32@, 0, [dnl
+### Load the RTP receiver module (also configured via paprefs, see above)
+#load-module module-rtp-recv
+
+### Load the RTP sender module (also configured via paprefs, see above)
+#load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100 sink_properties="device.description='RTP Multicast Sink'"
+#load-module module-rtp-send source=rtp.monitor
+
+ifelse(@HAVE_GSETTINGS@, 1, [dnl
+### Load additional modules from GSettings. This can be configured with the paprefs tool.
+### Please keep in mind that the modules configured by paprefs might conflict with manually
+### loaded modules.
+.ifexists module-gsettings@PA_SOEXT@
+.nofail
+load-module module-gsettings
+.fail
+.endif
+])dnl
+
+ifelse(@HAVE_GCONF@, 1, [dnl
+### Load additional modules from GConf settings. This can be configured with the paprefs tool.
+### Please keep in mind that the modules configured by paprefs might conflict with manually
+### loaded modules.
+.ifexists module-gconf@PA_SOEXT@
+.nofail
+load-module module-gconf
+.fail
+.endif
+])dnl
+
+### Automatically restore the default sink/source when changed by the user
+### during runtime
+### NOTE: This should be loaded as early as possible so that subsequent modules
+### that look up the default sink/source get the right value
+load-module module-default-device-restore
+
+### Make sure we always have a sink around, even if it is a null sink.
+load-module module-always-sink
+
+### Honour intended role device property
+load-module module-intended-roles
+
+### Automatically suspend sinks/sources that become idle for too long
+load-module module-suspend-on-idle
+
+### If autoexit on idle is enabled we want to make sure we only quit
+### when no local session needs us anymore.
+.ifexists module-console-kit@PA_SOEXT@
+load-module module-console-kit
+.endif
+.ifexists module-systemd-login@PA_SOEXT@
+load-module module-systemd-login
+.endif
+
+### Enable positioned event sounds
+load-module module-position-event-sounds
+
+### Cork music/video streams when a phone stream is active
+load-module module-role-cork
+
+### Modules to allow autoloading of filters (such as echo cancellation)
+### on demand. module-filter-heuristics tries to determine what filters
+### make sense, and module-filter-apply does the heavy-lifting of
+### loading modules and rerouting streams.
+load-module module-filter-heuristics
+load-module module-filter-apply
+])dnl
+
+### Make some devices default
+#set-default-sink output
+#set-default-source input
diff --git a/src/daemon/dumpmodules.c b/src/daemon/dumpmodules.c
new file mode 100644
index 0000000..8410bbc
--- /dev/null
+++ b/src/daemon/dumpmodules.c
@@ -0,0 +1,154 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ 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/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <ltdl.h>
+
+#include <pulse/util.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/i18n.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/modinfo.h>
+
+#include "dumpmodules.h"
+
+#define PREFIX "module-"
+
+static void short_info(const char *name, const char *path, pa_modinfo *i) {
+ pa_assert(name);
+ pa_assert(i);
+
+ printf("%-40s%s\n", name, i->description ? i->description : "n/a");
+}
+
+static void long_info(const char *name, const char *path, pa_modinfo *i) {
+ static int nl = 0;
+ pa_assert(name);
+ pa_assert(i);
+
+ if (nl)
+ printf("\n");
+
+ nl = 1;
+
+ printf(_("Name: %s\n"), name);
+
+ if (!i->description && !i->version && !i->author && !i->usage)
+ printf(_("No module information available\n"));
+ else {
+ if (i->version)
+ printf(_("Version: %s\n"), i->version);
+ if (i->description)
+ printf(_("Description: %s\n"), i->description);
+ if (i->author)
+ printf(_("Author: %s\n"), i->author);
+ if (i->usage)
+ printf(_("Usage: %s\n"), i->usage);
+ printf(_("Load Once: %s\n"), pa_yes_no(i->load_once));
+ if (i->deprecated)
+ printf(_("DEPRECATION WARNING: %s\n"), i->deprecated);
+ }
+
+ if (path)
+ printf(_("Path: %s\n"), path);
+}
+
+static void show_info(const char *name, const char *path, void (*info)(const char *name, const char *path, pa_modinfo*i)) {
+ pa_modinfo *i;
+
+ pa_assert(name);
+
+ if ((i = pa_modinfo_get_by_name(path ? path : name))) {
+ info(name, path, i);
+ pa_modinfo_free(i);
+ }
+}
+
+static int is_preloaded(const char *name) {
+ const lt_dlsymlist *l;
+
+ for (l = lt_preloaded_symbols; l->name; l++) {
+ char buf[64], *e;
+
+ if (l->address)
+ continue;
+
+ pa_snprintf(buf, sizeof(buf), "%s", l->name);
+ if ((e = strrchr(buf, '.')))
+ *e = 0;
+
+ if (pa_streq(name, buf))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int callback(const char *path, lt_ptr data) {
+ const char *e;
+ pa_daemon_conf *c = (data);
+
+ e = pa_path_get_filename(path);
+
+ if (strlen(e) <= sizeof(PREFIX)-1 || strncmp(e, PREFIX, sizeof(PREFIX)-1))
+ return 0;
+
+ if (is_preloaded(e))
+ return 0;
+
+ show_info(e, path, c->log_level >= PA_LOG_INFO ? long_info : short_info);
+ return 0;
+}
+
+void pa_dump_modules(pa_daemon_conf *c, int argc, char * const argv[]) {
+ pa_assert(c);
+
+ if (argc > 0) {
+ int i;
+ for (i = 0; i < argc; i++)
+ show_info(argv[i], NULL, long_info);
+ } else {
+ const lt_dlsymlist *l;
+
+ for (l = lt_preloaded_symbols; l->name; l++) {
+ char buf[64], *e;
+
+ if (l->address)
+ continue;
+
+ if (strlen(l->name) <= sizeof(PREFIX)-1 || strncmp(l->name, PREFIX, sizeof(PREFIX)-1))
+ continue;
+
+ pa_snprintf(buf, sizeof(buf), "%s", l->name);
+ if ((e = strrchr(buf, '.')))
+ *e = 0;
+
+ show_info(buf, NULL, c->log_level >= PA_LOG_INFO ? long_info : short_info);
+ }
+
+ lt_dlforeachfile(NULL, callback, c);
+ }
+}
diff --git a/src/daemon/dumpmodules.h b/src/daemon/dumpmodules.h
new file mode 100644
index 0000000..289f35d
--- /dev/null
+++ b/src/daemon/dumpmodules.h
@@ -0,0 +1,29 @@
+#ifndef foodumpmoduleshfoo
+#define foodumpmoduleshfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+
+ 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/>.
+***/
+
+#include "daemon-conf.h"
+
+/* Dump all available modules to STDOUT. If argc > 0 print information
+ * about the modules specified in argv[] instead. */
+void pa_dump_modules(pa_daemon_conf *c, int argc, char * const argv[]);
+
+#endif
diff --git a/src/daemon/esdcompat.in b/src/daemon/esdcompat.in
new file mode 100755
index 0000000..361ca63
--- /dev/null
+++ b/src/daemon/esdcompat.in
@@ -0,0 +1,94 @@
+#!/bin/sh
+
+# This file is part of PulseAudio.
+#
+# 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 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/>.
+
+VERSION_STRING="@PACKAGE_NAME@ esd wrapper @PACKAGE_VERSION@"
+
+fail() {
+ echo "ERROR: $1"
+ exit 1
+}
+
+ARGS=" --log-target=syslog"
+
+while [ "$#" -gt "0" ]; do
+
+ case "$1" in
+ "")
+ ;;
+
+ -v|--version)
+ echo "$VERSION_STRING"
+ exit 0
+ ;;
+
+ -h|--help)
+ cat <<EOF
+$VERSION_STRING
+
+Usage: $0 [options]
+
+ -v --version print version information
+ -h --help show this help
+
+Ignored directives:
+
+ -tcp use tcp/ip sockets in addition to unix domain
+ -promiscuous don't require authentication
+ -d DEVICE force esd to use sound device DEVICE
+ -b run server in 8 bit sound mode
+ -r RATE run server at sample rate of RATE
+ -as SECS free audio device after SECS of inactivity
+ -unix use unix domain sockets instead of tcp/ip
+ -public make tcp/ip access public (other than localhost)
+ -terminate terminate esd daemone after last client exits
+ -nobeeps disable startup beeps
+ -trust start esd even if use of /tmp/.esd can be insecure
+ -port PORT listen for connections at PORT (only for tcp/ip)
+ -bind ADDRESS binds to ADDRESS (only for tcp/ip)
+EOF
+ exit 0
+ ;;
+
+ -spawnpid)
+ shift
+ ARGS="$ARGS '-Lmodule-esound-compat-spawnpid pid=$1'"
+ ;;
+
+ -spawnfd)
+ shift
+ ARGS="$ARGS '-Lmodule-esound-compat-spawnfd fd=$1'"
+ ;;
+
+ -unix|-b|-public|-terminate|-nobeeps|-trust|-tcp|-promiscuous)
+ # Ignore these commands
+ ;;
+
+ -d|-r|-as|-port|-bind)
+ # Ignore these commands and their arguments
+ shift
+
+ ;;
+
+ *)
+ fail "Unknown command: $1"
+ ;;
+ esac
+
+ shift
+done
+
+eval "exec '@PA_BINARY@'$ARGS"
diff --git a/src/daemon/ltdl-bind-now.c b/src/daemon/ltdl-bind-now.c
new file mode 100644
index 0000000..3492571
--- /dev/null
+++ b/src/daemon/ltdl-bind-now.c
@@ -0,0 +1,159 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2008 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ 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/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#ifdef HAVE_SYS_DL_H
+#include <sys/dl.h>
+#endif
+
+#include <string.h>
+
+#include <ltdl.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
+
+#include "ltdl-bind-now.h"
+
+#ifdef RTLD_NOW
+#define PA_BIND_NOW RTLD_NOW
+#elif defined(DL_NOW)
+#define PA_BIND_NOW DL_NOW
+#else
+#undef PA_BIND_NOW
+#endif
+
+#ifdef OS_IS_WIN32
+#undef PA_BIND_NOW
+#endif
+
+#ifdef PA_BIND_NOW
+
+/*
+ To avoid lazy relocations during runtime in our RT threads we add
+ our own shared object loader with uses RTLD_NOW if it is
+ available. The standard ltdl loader prefers RTLD_LAZY.
+
+ Please note that this loader doesn't have any influence on
+ relocations on any libraries that are already loaded into our
+ process, i.e. because the pulseaudio binary links directly to
+ them. To disable lazy relocations for those libraries it is possible
+ to set $LT_BIND_NOW before starting the pulseaudio binary.
+*/
+
+static lt_module bind_now_open(lt_user_data d, const char *fname, lt_dladvise advise) {
+ lt_module m;
+
+ pa_assert(fname);
+
+ if (!(m = dlopen(fname, PA_BIND_NOW))) {
+ pa_log(_("Failed to open module %s: %s"), fname, dlerror());
+ lt_dlseterror(LT_ERROR_CANNOT_OPEN);
+ return NULL;
+ }
+
+ return m;
+}
+
+static int bind_now_close(lt_user_data d, lt_module m) {
+
+ pa_assert(m);
+
+ if (dlclose(m) != 0) {
+ lt_dlseterror(LT_ERROR_CANNOT_CLOSE);
+ return 1;
+ }
+
+ return 0;
+}
+
+static lt_ptr bind_now_find_sym(lt_user_data d, lt_module m, const char *symbol) {
+ lt_ptr ptr;
+
+ pa_assert(m);
+ pa_assert(symbol);
+
+ if (!(ptr = dlsym(m, symbol))) {
+ lt_dlseterror(LT_ERROR_SYMBOL_NOT_FOUND);
+ return NULL;
+ }
+
+ return ptr;
+}
+
+static lt_dlvtable *bindnow_loader = NULL;
+#endif
+
+void pa_ltdl_init(void) {
+
+#ifdef PA_BIND_NOW
+ const lt_dlvtable *dlopen_loader;
+#endif
+
+ pa_assert_se(lt_dlinit() == 0);
+
+#ifdef PA_BIND_NOW
+ /* Already initialised */
+ if (bindnow_loader)
+ return;
+
+ if (!(dlopen_loader = lt_dlloader_find((char*) "lt_dlopen"))) {
+ pa_log_warn(_("Failed to find original lt_dlopen loader."));
+ return;
+ }
+
+ if (!(bindnow_loader = malloc(sizeof(lt_dlvtable)))) {
+ pa_log_error(_("Failed to allocate new dl loader."));
+ return;
+ }
+
+ memcpy(bindnow_loader, dlopen_loader, sizeof(*bindnow_loader));
+ bindnow_loader->name = "bind-now-loader";
+ bindnow_loader->module_open = bind_now_open;
+ bindnow_loader->module_close = bind_now_close;
+ bindnow_loader->find_sym = bind_now_find_sym;
+ bindnow_loader->priority = LT_DLLOADER_PREPEND;
+
+ /* Add our BIND_NOW loader as the default module loader. */
+ if (lt_dlloader_add(bindnow_loader) != 0) {
+ pa_log_warn(_("Failed to add bind-now-loader."));
+ free(bindnow_loader);
+ bindnow_loader = NULL;
+ }
+#endif
+}
+
+void pa_ltdl_done(void) {
+ pa_assert_se(lt_dlexit() == 0);
+
+#ifdef PA_BIND_NOW
+ /* lt_dlexit() will free our loader vtable, hence reset our
+ * pointer to it here */
+ bindnow_loader = NULL;
+#endif
+}
diff --git a/src/daemon/ltdl-bind-now.h b/src/daemon/ltdl-bind-now.h
new file mode 100644
index 0000000..bb6d83f
--- /dev/null
+++ b/src/daemon/ltdl-bind-now.h
@@ -0,0 +1,27 @@
+#ifndef foopulsecoreltdlbindnowhfoo
+#define foopulsecoreltdlbindnowhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+
+ 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/>.
+***/
+
+void pa_ltdl_init(void);
+void pa_ltdl_done(void);
+
+#endif
+
diff --git a/src/daemon/main.c b/src/daemon/main.c
new file mode 100644
index 0000000..30ef499
--- /dev/null
+++ b/src/daemon/main.c
@@ -0,0 +1,1247 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ 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/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stddef.h>
+#include <ltdl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <locale.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#ifdef HAVE_LIBWRAP
+#include <syslog.h>
+#include <tcpd.h>
+#endif
+
+#ifdef HAVE_DBUS
+#include <dbus/dbus.h>
+#endif
+
+#ifdef HAVE_SYSTEMD_DAEMON
+#include <systemd/sd-daemon.h>
+#endif
+
+#include <pulse/client-conf.h>
+#include <pulse/mainloop.h>
+#include <pulse/mainloop-signal.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/i18n.h>
+#include <pulsecore/lock-autospawn.h>
+#include <pulsecore/socket.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-scache.h>
+#include <pulsecore/core.h>
+#include <pulsecore/module.h>
+#include <pulsecore/cli-command.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/sioman.h>
+#include <pulsecore/cli-text.h>
+#include <pulsecore/pid.h>
+#include <pulsecore/random.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/shm.h>
+#include <pulsecore/memtrap.h>
+#include <pulsecore/strlist.h>
+#ifdef HAVE_DBUS
+#include <pulsecore/dbus-shared.h>
+#endif
+#include <pulsecore/cpu.h>
+
+#include "cmdline.h"
+#include "cpulimit.h"
+#include "daemon-conf.h"
+#include "dumpmodules.h"
+#include "caps.h"
+#include "ltdl-bind-now.h"
+#include "server-lookup.h"
+
+#ifdef DISABLE_LIBTOOL_PRELOAD
+/* FIXME: work around a libtool bug by making sure we have 2 elements. Bug has
+ * been reported: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=29576 */
+const lt_dlsymlist lt_preloaded_symbols[] = {
+ { "@PROGRAM@", NULL },
+ { NULL, NULL }
+};
+#endif
+
+#ifdef HAVE_LIBWRAP
+/* Only one instance of these variables */
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_WARNING;
+#endif
+
+#ifdef HAVE_OSS_WRAPPER
+/* padsp looks for this symbol in the running process and disables
+ * itself if it finds it and it is set to 7 (which is actually a bit
+ * mask). For details see padsp. */
+int __padsp_disabled__ = 7;
+#endif
+
+static void signal_callback(pa_mainloop_api* m, pa_signal_event *e, int sig, void *userdata) {
+ pa_module *module = NULL;
+
+ pa_log_info("Got signal %s.", pa_sig2str(sig));
+
+ switch (sig) {
+#ifdef SIGUSR1
+ case SIGUSR1:
+ pa_module_load(&module, userdata, "module-cli", NULL);
+ break;
+#endif
+
+#ifdef SIGUSR2
+ case SIGUSR2:
+ pa_module_load(&module, userdata, "module-cli-protocol-unix", NULL);
+ break;
+#endif
+
+#ifdef SIGHUP
+ case SIGHUP: {
+ char *c = pa_full_status_string(userdata);
+ pa_log_notice("%s", c);
+ pa_xfree(c);
+ return;
+ }
+#endif
+
+ case SIGINT:
+ case SIGTERM:
+ default:
+ pa_log_info("Exiting.");
+ m->quit(m, 0);
+ break;
+ }
+}
+
+#if defined(HAVE_PWD_H) && defined(HAVE_GRP_H)
+
+static int change_user(void) {
+ struct passwd *pw;
+ struct group * gr;
+ int r;
+
+ /* This function is called only in system-wide mode. It creates a
+ * runtime dir in /var/run/ with proper UID/GID and drops privs
+ * afterwards. */
+
+ if (!(pw = getpwnam(PA_SYSTEM_USER))) {
+ pa_log(_("Failed to find user '%s'."), PA_SYSTEM_USER);
+ return -1;
+ }
+
+ if (!(gr = getgrnam(PA_SYSTEM_GROUP))) {
+ pa_log(_("Failed to find group '%s'."), PA_SYSTEM_GROUP);
+ return -1;
+ }
+
+ pa_log_info("Found user '%s' (UID %lu) and group '%s' (GID %lu).",
+ PA_SYSTEM_USER, (unsigned long) pw->pw_uid,
+ PA_SYSTEM_GROUP, (unsigned long) gr->gr_gid);
+
+ if (pw->pw_gid != gr->gr_gid) {
+ pa_log(_("GID of user '%s' and of group '%s' don't match."), PA_SYSTEM_USER, PA_SYSTEM_GROUP);
+ return -1;
+ }
+
+ if (!pa_streq(pw->pw_dir, PA_SYSTEM_RUNTIME_PATH))
+ pa_log_warn(_("Home directory of user '%s' is not '%s', ignoring."), PA_SYSTEM_USER, PA_SYSTEM_RUNTIME_PATH);
+
+ if (pa_make_secure_dir(PA_SYSTEM_RUNTIME_PATH, 0755, pw->pw_uid, gr->gr_gid, true) < 0) {
+ pa_log(_("Failed to create '%s': %s"), PA_SYSTEM_RUNTIME_PATH, pa_cstrerror(errno));
+ return -1;
+ }
+
+ if (pa_make_secure_dir(PA_SYSTEM_STATE_PATH, 0700, pw->pw_uid, gr->gr_gid, true) < 0) {
+ pa_log(_("Failed to create '%s': %s"), PA_SYSTEM_STATE_PATH, pa_cstrerror(errno));
+ return -1;
+ }
+
+ /* We don't create the config dir here, because we don't need to write to it */
+
+ if (initgroups(PA_SYSTEM_USER, gr->gr_gid) != 0) {
+ pa_log(_("Failed to change group list: %s"), pa_cstrerror(errno));
+ return -1;
+ }
+
+#if defined(HAVE_SETRESGID)
+ r = setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid);
+#elif defined(HAVE_SETEGID)
+ if ((r = setgid(gr->gr_gid)) >= 0)
+ r = setegid(gr->gr_gid);
+#elif defined(HAVE_SETREGID)
+ r = setregid(gr->gr_gid, gr->gr_gid);
+#else
+#error "No API to drop privileges"
+#endif
+
+ if (r < 0) {
+ pa_log(_("Failed to change GID: %s"), pa_cstrerror(errno));
+ return -1;
+ }
+
+#if defined(HAVE_SETRESUID)
+ r = setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid);
+#elif defined(HAVE_SETEUID)
+ if ((r = setuid(pw->pw_uid)) >= 0)
+ r = seteuid(pw->pw_uid);
+#elif defined(HAVE_SETREUID)
+ r = setreuid(pw->pw_uid, pw->pw_uid);
+#else
+#error "No API to drop privileges"
+#endif
+
+ if (r < 0) {
+ pa_log(_("Failed to change UID: %s"), pa_cstrerror(errno));
+ return -1;
+ }
+
+ pa_drop_caps();
+
+ pa_set_env("USER", PA_SYSTEM_USER);
+ pa_set_env("USERNAME", PA_SYSTEM_USER);
+ pa_set_env("LOGNAME", PA_SYSTEM_USER);
+ pa_set_env("HOME", PA_SYSTEM_RUNTIME_PATH);
+
+ /* Relevant for pa_runtime_path() */
+ if (!getenv("PULSE_RUNTIME_PATH"))
+ pa_set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH);
+
+ if (!getenv("PULSE_CONFIG_PATH"))
+ pa_set_env("PULSE_CONFIG_PATH", PA_SYSTEM_CONFIG_PATH);
+
+ if (!getenv("PULSE_STATE_PATH"))
+ pa_set_env("PULSE_STATE_PATH", PA_SYSTEM_STATE_PATH);
+
+ pa_log_info("Successfully changed user to \"" PA_SYSTEM_USER "\".");
+
+ return 0;
+}
+
+#else /* HAVE_PWD_H && HAVE_GRP_H */
+
+static int change_user(void) {
+ pa_log(_("System wide mode unsupported on this platform."));
+ return -1;
+}
+
+#endif /* HAVE_PWD_H && HAVE_GRP_H */
+
+#ifdef HAVE_SYS_RESOURCE_H
+
+static int set_one_rlimit(const pa_rlimit *r, int resource, const char *name) {
+ struct rlimit rl;
+ pa_assert(r);
+
+ if (!r->is_set)
+ return 0;
+
+ rl.rlim_cur = rl.rlim_max = r->value;
+
+ if (setrlimit(resource, &rl) < 0) {
+ pa_log_info("setrlimit(%s, (%u, %u)) failed: %s", name, (unsigned) r->value, (unsigned) r->value, pa_cstrerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static void set_all_rlimits(const pa_daemon_conf *conf) {
+ set_one_rlimit(&conf->rlimit_fsize, RLIMIT_FSIZE, "RLIMIT_FSIZE");
+ set_one_rlimit(&conf->rlimit_data, RLIMIT_DATA, "RLIMIT_DATA");
+ set_one_rlimit(&conf->rlimit_stack, RLIMIT_STACK, "RLIMIT_STACK");
+ set_one_rlimit(&conf->rlimit_core, RLIMIT_CORE, "RLIMIT_CORE");
+#ifdef RLIMIT_RSS
+ set_one_rlimit(&conf->rlimit_rss, RLIMIT_RSS, "RLIMIT_RSS");
+#endif
+#ifdef RLIMIT_NPROC
+ set_one_rlimit(&conf->rlimit_nproc, RLIMIT_NPROC, "RLIMIT_NPROC");
+#endif
+#ifdef RLIMIT_NOFILE
+ set_one_rlimit(&conf->rlimit_nofile, RLIMIT_NOFILE, "RLIMIT_NOFILE");
+#endif
+#ifdef RLIMIT_MEMLOCK
+ set_one_rlimit(&conf->rlimit_memlock, RLIMIT_MEMLOCK, "RLIMIT_MEMLOCK");
+#endif
+#ifdef RLIMIT_AS
+ set_one_rlimit(&conf->rlimit_as, RLIMIT_AS, "RLIMIT_AS");
+#endif
+#ifdef RLIMIT_LOCKS
+ set_one_rlimit(&conf->rlimit_locks, RLIMIT_LOCKS, "RLIMIT_LOCKS");
+#endif
+#ifdef RLIMIT_SIGPENDING
+ set_one_rlimit(&conf->rlimit_sigpending, RLIMIT_SIGPENDING, "RLIMIT_SIGPENDING");
+#endif
+#ifdef RLIMIT_MSGQUEUE
+ set_one_rlimit(&conf->rlimit_msgqueue, RLIMIT_MSGQUEUE, "RLIMIT_MSGQUEUE");
+#endif
+#ifdef RLIMIT_NICE
+ set_one_rlimit(&conf->rlimit_nice, RLIMIT_NICE, "RLIMIT_NICE");
+#endif
+#ifdef RLIMIT_RTPRIO
+ set_one_rlimit(&conf->rlimit_rtprio, RLIMIT_RTPRIO, "RLIMIT_RTPRIO");
+#endif
+#ifdef RLIMIT_RTTIME
+ set_one_rlimit(&conf->rlimit_rttime, RLIMIT_RTTIME, "RLIMIT_RTTIME");
+#endif
+}
+#endif
+
+static char *check_configured_address(void) {
+ char *default_server = NULL;
+ pa_client_conf *c = pa_client_conf_new();
+
+ pa_client_conf_load(c, true, true);
+
+ if (c->default_server && *c->default_server)
+ default_server = pa_xstrdup(c->default_server);
+
+ pa_client_conf_free(c);
+
+ return default_server;
+}
+
+#ifdef HAVE_DBUS
+static pa_dbus_connection *register_dbus_name(pa_core *c, DBusBusType bus, const char* name) {
+ DBusError error;
+ pa_dbus_connection *conn;
+
+ dbus_error_init(&error);
+
+ if (!(conn = pa_dbus_bus_get(c, bus, &error)) || dbus_error_is_set(&error)) {
+ pa_log_warn("Unable to contact D-Bus: %s: %s", error.name, error.message);
+ goto fail;
+ }
+
+ if (dbus_bus_request_name(pa_dbus_connection_get(conn), name, DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+ pa_log_debug("Got %s!", name);
+ return conn;
+ }
+
+ if (dbus_error_is_set(&error))
+ pa_log_error("Failed to acquire %s: %s: %s", name, error.name, error.message);
+ else
+ pa_log_error("D-Bus name %s already taken.", name);
+
+ /* PA cannot be started twice by the same user and hence we can
+ * ignore mostly the case that a name is already taken. */
+
+fail:
+ if (conn)
+ pa_dbus_connection_unref(conn);
+
+ dbus_error_free(&error);
+ return NULL;
+}
+#endif
+
+int main(int argc, char *argv[]) {
+ pa_core *c = NULL;
+ pa_strbuf *buf = NULL;
+ pa_daemon_conf *conf = NULL;
+ pa_mainloop *mainloop = NULL;
+ char *s;
+ char *configured_address;
+ int r = 0, retval = 1, d = 0;
+ bool valid_pid_file = false;
+ bool ltdl_init = false;
+ int n_fds = 0, *passed_fds = NULL;
+ const char *e;
+#ifdef HAVE_FORK
+ int daemon_pipe[2] = { -1, -1 };
+ int daemon_pipe2[2] = { -1, -1 };
+#endif
+ int autospawn_fd = -1;
+ bool autospawn_locked = false;
+#ifdef HAVE_DBUS
+ pa_dbusobj_server_lookup *server_lookup = NULL; /* /org/pulseaudio/server_lookup */
+ pa_dbus_connection *lookup_service_bus = NULL; /* Always the user bus. */
+ pa_dbus_connection *server_bus = NULL; /* The bus where we reserve org.pulseaudio.Server, either the user or the system bus. */
+ bool start_server;
+#endif
+
+ pa_log_set_ident("pulseaudio");
+ pa_log_set_level(PA_LOG_NOTICE);
+ pa_log_set_flags(PA_LOG_COLORS|PA_LOG_PRINT_FILE|PA_LOG_PRINT_LEVEL, PA_LOG_RESET);
+
+#if !defined(HAVE_BIND_NOW) && defined(__linux__) && defined(__OPTIMIZE__)
+ /*
+ Disable lazy relocations to make usage of external libraries
+ more deterministic for our RT threads. We abuse __OPTIMIZE__ as
+ a check whether we are a debug build or not. This all is
+ admittedly a bit snake-oilish.
+ */
+
+ if (!getenv("LD_BIND_NOW")) {
+ char *rp;
+ char *canonical_rp;
+
+ /* We have to execute ourselves, because the libc caches the
+ * value of $LD_BIND_NOW on initialization. */
+
+ pa_set_env("LD_BIND_NOW", "1");
+
+ if ((canonical_rp = pa_realpath(PA_BINARY))) {
+
+ if ((rp = pa_readlink("/proc/self/exe"))) {
+
+ if (pa_streq(rp, canonical_rp))
+ pa_assert_se(execv(rp, argv) == 0);
+ else
+ pa_log_warn("/proc/self/exe does not point to %s, cannot self execute. Are you playing games?", canonical_rp);
+
+ pa_xfree(rp);
+
+ } else
+ pa_log_warn("Couldn't read /proc/self/exe, cannot self execute. Running in a chroot()?");
+
+ pa_xfree(canonical_rp);
+
+ } else
+ pa_log_warn("Couldn't canonicalize binary path, cannot self execute.");
+ }
+#endif
+
+#ifdef HAVE_SYSTEMD_DAEMON
+ n_fds = sd_listen_fds(0);
+ if (n_fds > 0) {
+ int i = n_fds;
+
+ passed_fds = pa_xnew(int, n_fds+2);
+ passed_fds[n_fds] = passed_fds[n_fds+1] = -1;
+ while (i--)
+ passed_fds[i] = SD_LISTEN_FDS_START + i;
+ }
+#endif
+
+ if (!passed_fds) {
+ n_fds = 0;
+ passed_fds = pa_xnew(int, 2);
+ passed_fds[0] = passed_fds[1] = -1;
+ }
+
+ if ((e = getenv("PULSE_PASSED_FD"))) {
+ int passed_fd = atoi(e);
+ if (passed_fd > 2)
+ passed_fds[n_fds] = passed_fd;
+ }
+
+ /* We might be autospawned, in which case have no idea in which
+ * context we have been started. Let's cleanup our execution
+ * context as good as possible */
+
+ pa_reset_personality();
+ pa_drop_root();
+ pa_close_allv(passed_fds);
+ pa_xfree(passed_fds);
+ pa_reset_sigs(-1);
+ pa_unblock_sigs(-1);
+ pa_reset_priority();
+
+ setlocale(LC_ALL, "");
+ pa_init_i18n();
+
+ conf = pa_daemon_conf_new();
+
+ if (pa_daemon_conf_load(conf, NULL) < 0)
+ goto finish;
+
+ if (pa_daemon_conf_env(conf) < 0)
+ goto finish;
+
+ if (pa_cmdline_parse(conf, argc, argv, &d) < 0) {
+ pa_log(_("Failed to parse command line."));
+ goto finish;
+ }
+
+ if (conf->log_target)
+ pa_log_set_target(conf->log_target);
+ else {
+ pa_log_target target = { .type = PA_LOG_STDERR, .file = NULL };
+ pa_log_set_target(&target);
+ }
+
+ pa_log_set_level(conf->log_level);
+ if (conf->log_meta)
+ pa_log_set_flags(PA_LOG_PRINT_META, PA_LOG_SET);
+ if (conf->log_time)
+ pa_log_set_flags(PA_LOG_PRINT_TIME, PA_LOG_SET);
+ pa_log_set_show_backtrace(conf->log_backtrace);
+
+#ifdef HAVE_DBUS
+ /* conf->system_instance and conf->local_server_type control almost the
+ * same thing; make them agree about what is requested. */
+ switch (conf->local_server_type) {
+ case PA_SERVER_TYPE_UNSET:
+ conf->local_server_type = conf->system_instance ? PA_SERVER_TYPE_SYSTEM : PA_SERVER_TYPE_USER;
+ break;
+ case PA_SERVER_TYPE_USER:
+ case PA_SERVER_TYPE_NONE:
+ conf->system_instance = false;
+ break;
+ case PA_SERVER_TYPE_SYSTEM:
+ conf->system_instance = true;
+ break;
+ default:
+ pa_assert_not_reached();
+ }
+
+ start_server = conf->local_server_type == PA_SERVER_TYPE_USER || (getuid() == 0 && conf->local_server_type == PA_SERVER_TYPE_SYSTEM);
+
+ if (!start_server && conf->local_server_type == PA_SERVER_TYPE_SYSTEM) {
+ pa_log_notice(_("System mode refused for non-root user. Only starting the D-Bus server lookup service."));
+ conf->system_instance = false;
+ }
+#endif
+
+ LTDL_SET_PRELOADED_SYMBOLS();
+ pa_ltdl_init();
+ ltdl_init = true;
+
+ if (conf->dl_search_path)
+ lt_dlsetsearchpath(conf->dl_search_path);
+
+#ifdef OS_IS_WIN32
+ {
+ WSADATA data;
+ WSAStartup(MAKEWORD(2, 0), &data);
+ }
+#endif
+
+ pa_random_seed();
+
+ switch (conf->cmd) {
+ case PA_CMD_DUMP_MODULES:
+ pa_dump_modules(conf, argc-d, argv+d);
+ retval = 0;
+ goto finish;
+
+ case PA_CMD_DUMP_CONF: {
+
+ if (d < argc) {
+ pa_log("Too many arguments.");
+ goto finish;
+ }
+
+ s = pa_daemon_conf_dump(conf);
+ fputs(s, stdout);
+ pa_xfree(s);
+ retval = 0;
+ goto finish;
+ }
+
+ case PA_CMD_DUMP_RESAMPLE_METHODS: {
+ int i;
+
+ if (d < argc) {
+ pa_log("Too many arguments.");
+ goto finish;
+ }
+
+ for (i = 0; i < PA_RESAMPLER_MAX; i++)
+ if (pa_resample_method_supported(i))
+ printf("%s\n", pa_resample_method_to_string(i));
+
+ retval = 0;
+ goto finish;
+ }
+
+ case PA_CMD_HELP :
+ pa_cmdline_help(argv[0]);
+ retval = 0;
+ goto finish;
+
+ case PA_CMD_VERSION :
+
+ if (d < argc) {
+ pa_log("Too many arguments.");
+ goto finish;
+ }
+
+ printf(PACKAGE_NAME" "PACKAGE_VERSION"\n");
+ retval = 0;
+ goto finish;
+
+ case PA_CMD_CHECK: {
+ pid_t pid;
+
+ if (d < argc) {
+ pa_log("Too many arguments.");
+ goto finish;
+ }
+
+ if (pa_pid_file_check_running(&pid, "pulseaudio") < 0)
+ pa_log_info("Daemon not running");
+ else {
+ pa_log_info("Daemon running as PID %u", pid);
+ retval = 0;
+ }
+
+ goto finish;
+
+ }
+ case PA_CMD_KILL:
+
+ if (d < argc) {
+ pa_log("Too many arguments.");
+ goto finish;
+ }
+
+ if (pa_pid_file_kill(SIGINT, NULL, "pulseaudio") < 0)
+ pa_log(_("Failed to kill daemon: %s"), pa_cstrerror(errno));
+ else
+ retval = 0;
+
+ goto finish;
+
+ case PA_CMD_CLEANUP_SHM:
+
+ if (d < argc) {
+ pa_log("Too many arguments.");
+ goto finish;
+ }
+
+ if (pa_shm_cleanup() >= 0)
+ retval = 0;
+
+ goto finish;
+
+ default:
+ pa_assert(conf->cmd == PA_CMD_DAEMON || conf->cmd == PA_CMD_START);
+ }
+
+ if (d < argc) {
+ pa_log("Too many arguments.");
+ goto finish;
+ }
+
+#ifdef HAVE_GETUID
+ if (getuid() == 0 && !conf->system_instance)
+ pa_log_warn(_("This program is not intended to be run as root (unless --system is specified)."));
+#ifndef HAVE_DBUS /* A similar, only a notice worthy check was done earlier, if D-Bus is enabled. */
+ else if (getuid() != 0 && conf->system_instance) {
+ pa_log(_("Root privileges required."));
+ goto finish;
+ }
+#endif
+#endif /* HAVE_GETUID */
+
+ if (conf->cmd == PA_CMD_START && conf->system_instance) {
+ pa_log(_("--start not supported for system instances."));
+ goto finish;
+ }
+
+ if (conf->cmd == PA_CMD_START && (configured_address = check_configured_address())) {
+ /* There is an server address in our config, but where did it come from?
+ * By default a standard X11 login will load module-x11-publish which will
+ * inject PULSE_SERVER X11 property. If the PA daemon crashes, we will end
+ * up hitting this code path. So we have to check to see if our configured_address
+ * is the same as the value that would go into this property so that we can
+ * recover (i.e. autospawn) from a crash.
+ */
+ char *ufn;
+ bool start_anyway = false;
+
+ if ((ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET))) {
+ char *id;
+
+ if ((id = pa_machine_id())) {
+ pa_strlist *server_list;
+ char formatted_ufn[256];
+
+ pa_snprintf(formatted_ufn, sizeof(formatted_ufn), "{%s}unix:%s", id, ufn);
+ pa_xfree(id);
+
+ if ((server_list = pa_strlist_parse(configured_address))) {
+ char *u = NULL;
+
+ /* We only need to check the first server */
+ server_list = pa_strlist_pop(server_list, &u);
+ pa_strlist_free(server_list);
+
+ start_anyway = (u && pa_streq(formatted_ufn, u));
+ pa_xfree(u);
+ }
+ }
+ pa_xfree(ufn);
+ }
+
+ if (!start_anyway) {
+ pa_log_notice(_("User-configured server at %s, refusing to start/autospawn."), configured_address);
+ pa_xfree(configured_address);
+ retval = 0;
+ goto finish;
+ }
+
+ pa_log_notice(_("User-configured server at %s, which appears to be local. Probing deeper."), configured_address);
+ pa_xfree(configured_address);
+ }
+
+ if (conf->system_instance && !conf->disallow_exit)
+ pa_log_warn(_("Running in system mode, but --disallow-exit not set."));
+
+ if (conf->system_instance && !conf->disallow_module_loading)
+ pa_log_warn(_("Running in system mode, but --disallow-module-loading not set."));
+
+ if (conf->system_instance && !conf->disable_shm) {
+ pa_log_notice(_("Running in system mode, forcibly disabling SHM mode."));
+ conf->disable_shm = true;
+ }
+
+ if (conf->system_instance && conf->exit_idle_time >= 0) {
+ pa_log_notice(_("Running in system mode, forcibly disabling exit idle time."));
+ conf->exit_idle_time = -1;
+ }
+
+ if (conf->cmd == PA_CMD_START) {
+ /* If we shall start PA only when it is not running yet, we
+ * first take the autospawn lock to make things
+ * synchronous. */
+
+ /* This locking and thread synchronisation code doesn't work reliably
+ * on kFreeBSD (Debian bug #705435), or in upstream FreeBSD ports
+ * (bug reference: ports/128947, patched in SVN r231972). */
+#if !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
+ if ((autospawn_fd = pa_autospawn_lock_init()) < 0) {
+ pa_log("Failed to initialize autospawn lock");
+ goto finish;
+ }
+
+ if ((pa_autospawn_lock_acquire(true) < 0)) {
+ pa_log("Failed to acquire autospawn lock");
+ goto finish;
+ }
+
+ autospawn_locked = true;
+#endif
+ }
+
+ if (conf->daemonize) {
+#ifdef HAVE_FORK
+ pid_t child;
+#endif
+
+ if (pa_stdio_acquire() < 0) {
+ pa_log(_("Failed to acquire stdio."));
+ goto finish;
+ }
+
+#ifdef HAVE_FORK
+ if (pipe(daemon_pipe) < 0) {
+ pa_log(_("pipe() failed: %s"), pa_cstrerror(errno));
+ goto finish;
+ }
+
+ if ((child = fork()) < 0) {
+ pa_log(_("fork() failed: %s"), pa_cstrerror(errno));
+ pa_close_pipe(daemon_pipe);
+ goto finish;
+ }
+
+ if (child != 0) {
+ ssize_t n;
+ /* Father */
+
+ pa_assert_se(pa_close(daemon_pipe[1]) == 0);
+ daemon_pipe[1] = -1;
+
+ if ((n = pa_loop_read(daemon_pipe[0], &retval, sizeof(retval), NULL)) != sizeof(retval)) {
+
+ if (n < 0)
+ pa_log(_("read() failed: %s"), pa_cstrerror(errno));
+
+ retval = 1;
+ }
+
+ if (retval)
+ pa_log(_("Daemon startup failed."));
+ else
+ pa_log_info("Daemon startup successful.");
+
+ goto finish;
+ }
+
+ if (autospawn_fd >= 0) {
+ /* The lock file is unlocked from the parent, so we need
+ * to close it in the child */
+
+ pa_autospawn_lock_release();
+ pa_autospawn_lock_done(true);
+
+ autospawn_locked = false;
+ autospawn_fd = -1;
+ }
+
+ pa_assert_se(pa_close(daemon_pipe[0]) == 0);
+ daemon_pipe[0] = -1;
+#endif
+
+ if (!conf->log_target) {
+#ifdef HAVE_SYSTEMD_JOURNAL
+ pa_log_target target = { .type = PA_LOG_JOURNAL, .file = NULL };
+#else
+ pa_log_target target = { .type = PA_LOG_SYSLOG, .file = NULL };
+#endif
+ pa_log_set_target(&target);
+ }
+
+#ifdef HAVE_SETSID
+ if (setsid() < 0) {
+ pa_log(_("setsid() failed: %s"), pa_cstrerror(errno));
+ goto finish;
+ }
+#endif
+
+#ifdef HAVE_FORK
+ /* We now are a session and process group leader. Let's fork
+ * again and let the father die, so that we'll become a
+ * process that can never acquire a TTY again, in a session and
+ * process group without leader */
+
+ if (pipe(daemon_pipe2) < 0) {
+ pa_log(_("pipe() failed: %s"), pa_cstrerror(errno));
+ goto finish;
+ }
+
+ if ((child = fork()) < 0) {
+ pa_log(_("fork() failed: %s"), pa_cstrerror(errno));
+ pa_close_pipe(daemon_pipe2);
+ goto finish;
+ }
+
+ if (child != 0) {
+ ssize_t n;
+ /* Father */
+
+ pa_assert_se(pa_close(daemon_pipe2[1]) == 0);
+ daemon_pipe2[1] = -1;
+
+ if ((n = pa_loop_read(daemon_pipe2[0], &retval, sizeof(retval), NULL)) != sizeof(retval)) {
+
+ if (n < 0)
+ pa_log(_("read() failed: %s"), pa_cstrerror(errno));
+
+ retval = 1;
+ }
+
+ /* We now have to take care of signalling the first fork with
+ * the return value we've received from this fork... */
+ pa_assert(daemon_pipe[1] >= 0);
+
+ pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
+ pa_close(daemon_pipe[1]);
+ daemon_pipe[1] = -1;
+
+ goto finish;
+ }
+
+ pa_assert_se(pa_close(daemon_pipe2[0]) == 0);
+ daemon_pipe2[0] = -1;
+
+ /* We no longer need the (first) daemon_pipe as it's handled in our child above */
+ pa_close_pipe(daemon_pipe);
+#endif
+
+#ifdef SIGTTOU
+ signal(SIGTTOU, SIG_IGN);
+#endif
+#ifdef SIGTTIN
+ signal(SIGTTIN, SIG_IGN);
+#endif
+#ifdef SIGTSTP
+ signal(SIGTSTP, SIG_IGN);
+#endif
+
+ pa_nullify_stdfds();
+ }
+
+ pa_set_env_and_record("PULSE_INTERNAL", "1");
+ pa_assert_se(chdir("/") == 0);
+ umask(0077);
+
+#ifdef HAVE_SYS_RESOURCE_H
+ set_all_rlimits(conf);
+#endif
+ pa_rtclock_hrtimer_enable();
+
+ if (conf->high_priority)
+ pa_raise_priority(conf->nice_level);
+
+ if (conf->system_instance)
+ if (change_user() < 0)
+ goto finish;
+
+ pa_set_env_and_record("PULSE_SYSTEM", conf->system_instance ? "1" : "0");
+
+ pa_log_info("This is PulseAudio %s", PACKAGE_VERSION);
+ pa_log_debug("Compilation CFLAGS: %s", PA_CFLAGS);
+
+#ifdef HAVE_LIBSAMPLERATE
+ pa_log_warn("Compiled with DEPRECATED libsamplerate support!");
+#endif
+
+ s = pa_uname_string();
+ pa_log_debug("Running on host: %s", s);
+ pa_xfree(s);
+
+ pa_log_debug("Found %u CPUs.", pa_ncpus());
+
+ pa_log_info("Page size is %zu bytes", pa_page_size());
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+ pa_log_debug("Compiled with Valgrind support: yes");
+#else
+ pa_log_debug("Compiled with Valgrind support: no");
+#endif
+
+ pa_log_debug("Running in valgrind mode: %s", pa_yes_no(pa_in_valgrind()));
+
+ pa_log_debug("Running in VM: %s", pa_yes_no(pa_running_in_vm()));
+
+#ifdef HAVE_RUNNING_FROM_BUILD_TREE
+ pa_log_debug("Running from build tree: %s", pa_yes_no(pa_run_from_build_tree()));
+#else
+ pa_log_debug("Running from build tree: no");
+#endif
+
+#ifdef __OPTIMIZE__
+ pa_log_debug("Optimized build: yes");
+#else
+ pa_log_debug("Optimized build: no");
+#endif
+
+#ifdef NDEBUG
+ pa_log_debug("NDEBUG defined, all asserts disabled.");
+#elif defined(FASTPATH)
+ pa_log_debug("FASTPATH defined, only fast path asserts disabled.");
+#else
+ pa_log_debug("All asserts enabled.");
+#endif
+
+ if (!(s = pa_machine_id())) {
+ pa_log(_("Failed to get machine ID"));
+ goto finish;
+ }
+ pa_log_info("Machine ID is %s.", s);
+ pa_xfree(s);
+
+ if ((s = pa_session_id())) {
+ pa_log_info("Session ID is %s.", s);
+ pa_xfree(s);
+ }
+
+ if (!(s = pa_get_runtime_dir()))
+ goto finish;
+ pa_log_info("Using runtime directory %s.", s);
+ pa_xfree(s);
+
+ if (!(s = pa_get_state_dir()))
+ goto finish;
+ pa_log_info("Using state directory %s.", s);
+ pa_xfree(s);
+
+ pa_log_info("Using modules directory %s.", conf->dl_search_path);
+
+ pa_log_info("Running in system mode: %s", pa_yes_no(pa_in_system_mode()));
+
+ if (pa_in_system_mode())
+ pa_log_warn(_("OK, so you are running PA in system mode. Please make sure that you actually do want to do that.\n"
+ "Please read http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system mode is usually a bad idea."));
+
+ if (conf->use_pid_file) {
+ int z;
+
+ if ((z = pa_pid_file_create("pulseaudio")) != 0) {
+
+ if (conf->cmd == PA_CMD_START && z > 0) {
+ /* If we are already running and with are run in
+ * --start mode, then let's return this as success. */
+
+ retval = 0;
+ goto finish;
+ }
+
+ pa_log(_("pa_pid_file_create() failed."));
+ goto finish;
+ }
+
+ valid_pid_file = true;
+ }
+
+ pa_disable_sigpipe();
+
+ if (pa_rtclock_hrtimer())
+ pa_log_info("System supports high resolution timers");
+ else
+ pa_log_info("System appears to not support high resolution timers");
+
+ if (conf->lock_memory) {
+#if defined(HAVE_SYS_MMAN_H) && !defined(__ANDROID__)
+ if (mlockall(MCL_FUTURE) < 0)
+ pa_log_warn("mlockall() failed: %s", pa_cstrerror(errno));
+ else
+ pa_log_info("Successfully locked process into memory.");
+#else
+ pa_log_warn("Memory locking requested but not supported on platform.");
+#endif
+ }
+
+ pa_memtrap_install();
+
+ pa_assert_se(mainloop = pa_mainloop_new());
+
+ if (!(c = pa_core_new(pa_mainloop_get_api(mainloop), !conf->disable_shm,
+ !conf->disable_shm && !conf->disable_memfd && pa_memfd_is_locally_supported(),
+ conf->shm_size))) {
+ pa_log(_("pa_core_new() failed."));
+ goto finish;
+ }
+
+ c->default_sample_spec = conf->default_sample_spec;
+ c->alternate_sample_rate = conf->alternate_sample_rate;
+ c->default_channel_map = conf->default_channel_map;
+ c->default_n_fragments = conf->default_n_fragments;
+ c->default_fragment_size_msec = conf->default_fragment_size_msec;
+ c->deferred_volume_safety_margin_usec = conf->deferred_volume_safety_margin_usec;
+ c->deferred_volume_extra_delay_usec = conf->deferred_volume_extra_delay_usec;
+ c->lfe_crossover_freq = conf->lfe_crossover_freq;
+ c->exit_idle_time = conf->exit_idle_time;
+ c->scache_idle_time = conf->scache_idle_time;
+ c->resample_method = conf->resample_method;
+ c->realtime_priority = conf->realtime_priority;
+ c->realtime_scheduling = conf->realtime_scheduling;
+ c->avoid_resampling = conf->avoid_resampling;
+ c->disable_remixing = conf->disable_remixing;
+ c->remixing_use_all_sink_channels = conf->remixing_use_all_sink_channels;
+ c->remixing_produce_lfe = conf->remixing_produce_lfe;
+ c->remixing_consume_lfe = conf->remixing_consume_lfe;
+ c->deferred_volume = conf->deferred_volume;
+ c->running_as_daemon = conf->daemonize;
+ c->disallow_exit = conf->disallow_exit;
+ c->flat_volumes = conf->flat_volumes;
+ c->rescue_streams = conf->rescue_streams;
+#ifdef HAVE_DBUS
+ c->server_type = conf->local_server_type;
+#endif
+
+ pa_core_check_idle(c);
+
+ c->state = PA_CORE_RUNNING;
+
+ pa_cpu_init(&c->cpu_info);
+
+ pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0);
+ pa_signal_new(SIGINT, signal_callback, c);
+ pa_signal_new(SIGTERM, signal_callback, c);
+#ifdef SIGUSR1
+ pa_signal_new(SIGUSR1, signal_callback, c);
+#endif
+#ifdef SIGUSR2
+ pa_signal_new(SIGUSR2, signal_callback, c);
+#endif
+#ifdef SIGHUP
+ pa_signal_new(SIGHUP, signal_callback, c);
+#endif
+
+ if (!conf->no_cpu_limit)
+ pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0);
+
+ buf = pa_strbuf_new();
+
+#ifdef HAVE_DBUS
+ pa_assert_se(dbus_threads_init_default());
+
+ if (start_server)
+#endif
+ {
+ const char *command_source = NULL;
+
+ if (conf->load_default_script_file) {
+ FILE *f;
+
+ if ((f = pa_daemon_conf_open_default_script_file(conf))) {
+ r = pa_cli_command_execute_file_stream(c, f, buf, &conf->fail);
+ fclose(f);
+ command_source = pa_daemon_conf_get_default_script_file(conf);
+ }
+ }
+
+ if (r >= 0) {
+ r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail);
+ command_source = _("command line arguments");
+ }
+
+ pa_log_error("%s", s = pa_strbuf_to_string_free(buf));
+ pa_xfree(s);
+
+ if (r < 0 && conf->fail) {
+ pa_log(_("Failed to initialize daemon due to errors while executing startup commands. Source of commands: %s"), command_source);
+ goto finish;
+ }
+
+ if (!c->modules || pa_idxset_size(c->modules) == 0) {
+ pa_log(_("Daemon startup without any loaded modules, refusing to work."));
+ goto finish;
+ }
+#ifdef HAVE_DBUS
+ } else {
+ /* When we just provide the D-Bus server lookup service, we don't want
+ * any modules to be loaded. We haven't loaded any so far, so one might
+ * think there's no way to contact the server, but receiving certain
+ * signals could still cause modules to load. */
+ conf->disallow_module_loading = true;
+#endif
+ }
+
+ /* We completed the initial module loading, so let's disable it
+ * from now on, if requested */
+ c->disallow_module_loading = conf->disallow_module_loading;
+
+#ifdef HAVE_DBUS
+ if (!conf->system_instance) {
+ if ((server_lookup = pa_dbusobj_server_lookup_new(c))) {
+ if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.PulseAudio1")))
+ goto finish;
+ }
+ }
+
+ if (start_server)
+ server_bus = register_dbus_name(c, conf->system_instance ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, "org.pulseaudio.Server");
+#endif
+
+#ifdef HAVE_FORK
+ if (daemon_pipe2[1] >= 0) {
+ int ok = 0;
+ pa_loop_write(daemon_pipe2[1], &ok, sizeof(ok), NULL);
+ pa_close(daemon_pipe2[1]);
+ daemon_pipe2[1] = -1;
+ }
+#endif
+
+ pa_log_info("Daemon startup complete.");
+
+#ifdef HAVE_SYSTEMD_DAEMON
+ sd_notify(0, "READY=1");
+#endif
+
+ retval = 0;
+ if (pa_mainloop_run(mainloop, &retval) < 0)
+ goto finish;
+
+ pa_log_info("Daemon shutdown initiated.");
+
+#ifdef HAVE_SYSTEMD_DAEMON
+ sd_notify(0, "STOPPING=1");
+#endif
+
+finish:
+#ifdef HAVE_DBUS
+ if (server_bus)
+ pa_dbus_connection_unref(server_bus);
+ if (lookup_service_bus)
+ pa_dbus_connection_unref(lookup_service_bus);
+ if (server_lookup)
+ pa_dbusobj_server_lookup_free(server_lookup);
+#endif
+
+ if (autospawn_fd >= 0) {
+ if (autospawn_locked)
+ pa_autospawn_lock_release();
+
+ pa_autospawn_lock_done(false);
+ }
+
+ if (c) {
+ /* Ensure all the modules/samples are unloaded when the core is still ref'ed,
+ * as unlink callback hooks in modules may need the core to be ref'ed */
+ pa_module_unload_all(c);
+ pa_scache_free_all(c);
+
+ pa_core_unref(c);
+ pa_log_info("Daemon terminated.");
+ }
+
+ if (!conf->no_cpu_limit)
+ pa_cpu_limit_done();
+
+ pa_signal_done();
+
+#ifdef HAVE_FORK
+ /* If we have daemon_pipe[1] still open, this means we've failed after
+ * the first fork, but before the second. Therefore just write to it. */
+ if (daemon_pipe[1] >= 0)
+ pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
+ else if (daemon_pipe2[1] >= 0)
+ pa_loop_write(daemon_pipe2[1], &retval, sizeof(retval), NULL);
+
+ pa_close_pipe(daemon_pipe2);
+ pa_close_pipe(daemon_pipe);
+#endif
+
+ if (mainloop)
+ pa_mainloop_free(mainloop);
+
+ if (conf)
+ pa_daemon_conf_free(conf);
+
+ if (valid_pid_file)
+ pa_pid_file_remove();
+
+ /* This has no real purpose except making things valgrind-clean */
+ pa_unset_env_recorded();
+
+#ifdef OS_IS_WIN32
+ WSACleanup();
+#endif
+
+ if (ltdl_init)
+ pa_ltdl_done();
+
+#ifdef HAVE_DBUS
+ dbus_shutdown();
+#endif
+
+ return retval;
+}
diff --git a/src/daemon/meson.build b/src/daemon/meson.build
new file mode 100644
index 0000000..9c9f807
--- /dev/null
+++ b/src/daemon/meson.build
@@ -0,0 +1,159 @@
+pulseaudio_sources = [
+ 'caps.c',
+ 'cmdline.c',
+ 'cpulimit.c',
+ 'daemon-conf.c',
+ 'dumpmodules.c',
+ 'ltdl-bind-now.c',
+ 'main.c',
+]
+
+pulseaudio_headers = [
+ 'caps.h',
+ 'cmdline.h',
+ 'cpulimit.h',
+ 'daemon-conf.h',
+ 'dumpmodules.h',
+ 'ltdl-bind-now.h',
+]
+
+if dbus_dep.found()
+ pulseaudio_sources += 'server-lookup.c'
+ pulseaudio_headers += 'server-lookup.h'
+endif
+
+# FIXME: dependencies
+executable('pulseaudio',
+ pulseaudio_sources,
+ pulseaudio_headers,
+ install: true,
+ install_rpath : privlibdir,
+ include_directories : [configinc, topinc],
+ link_args : ['-ffast-math'],
+ link_with : [libpulsecore, libpulsecommon, libpulse],
+ dependencies : [ltdl_dep, cap_dep, dbus_dep, libsystemd_dep, dl_dep, libintl_dep],
+ c_args : pa_c_args,
+)
+
+if x11_dep.found()
+ conf = configuration_data()
+ conf.set('PACTL_BINARY', join_paths(bindir, 'pactl'))
+
+ configure_file(
+ input : 'start-pulseaudio-x11.in',
+ output : 'start-pulseaudio-x11',
+ configuration : conf,
+ install : true,
+ install_dir : bindir,
+ )
+
+ desktop_file = i18n.merge_file(
+ input : 'pulseaudio.desktop.in',
+ output : 'pulseaudio.desktop',
+ po_dir : po_dir,
+ type : 'desktop',
+ install : true,
+ install_dir : join_paths(sysconfdir, 'xdg', 'autostart'),
+ )
+
+ desktop_utils = find_program('desktop-file-validate', required: false)
+ if desktop_utils.found()
+ test('Validate desktop file', desktop_utils,
+ args: [ desktop_file ],
+ )
+ endif
+endif
+
+# Configuration files
+
+m4 = find_program('m4', required: true)
+
+daemon_conf = configuration_data()
+daemon_conf.merge_from(cdata)
+daemon_conf.set('PA_DEFAULT_CONFIG_DIR', cdata.get_unquoted('PA_DEFAULT_CONFIG_DIR'))
+
+daemon_template_file = configure_file(
+ input : 'daemon.conf.in',
+ output : 'daemon.conf.tmp',
+ configuration : daemon_conf,
+)
+
+custom_target('daemon.conf',
+ input : daemon_template_file,
+ output : 'daemon.conf',
+ capture : true,
+ command : [m4, '@INPUT@'],
+ build_by_default : true,
+ install : true,
+ install_dir : pulsesysconfdir,
+)
+
+default_conf = configuration_data()
+default_conf.merge_from(cdata)
+default_conf.set('PA_BINARY', cdata.get_unquoted('PA_BINARY'))
+default_conf.set('PA_SOEXT', cdata.get_unquoted('PA_SOEXT'))
+default_conf.set10('HAVE_AF_UNIX', cc.has_header('sys/un.h'))
+default_conf.set10('OS_IS_WIN32', host_machine.system() == 'windows')
+default_conf.set10('HAVE_MKFIFO', cc.has_function('mkfifo'))
+# We don't support the deprecated GConf option in meson
+default_conf.set10('HAVE_GCONF', 0)
+
+default_template_file = configure_file(
+ input : 'default.pa.in',
+ output : 'default.pa.tmp',
+ configuration : default_conf,
+)
+
+custom_target('default.pa',
+ input : default_template_file,
+ output : 'default.pa',
+ capture : true,
+ command : [m4, '@INPUT@'],
+ build_by_default : true,
+ install : true,
+ install_dir : pulsesysconfdir,
+)
+
+system_conf = configuration_data()
+system_conf.merge_from(cdata)
+system_conf.set('PA_BINARY', cdata.get_unquoted('PA_BINARY'))
+system_conf.set('PA_SOEXT', cdata.get_unquoted('PA_SOEXT'))
+
+system_template_file = configure_file(
+ input : 'system.pa.in',
+ output : 'system.pa.tmp',
+ configuration : system_conf,
+)
+
+custom_target('system.pa',
+ input : system_template_file,
+ output : 'system.pa',
+ capture : true,
+ command : [m4, '@INPUT@'],
+ build_by_default : true,
+ install : true,
+ install_dir : pulsesysconfdir,
+)
+
+if dbus_dep.found()
+ install_data('pulseaudio-system.conf',
+ install_dir : join_paths(sysconfdir, 'dbus-1', 'system.d')
+ )
+endif
+
+if systemd_dep.found()
+ sd_user_service_conf = configuration_data()
+ sd_user_service_conf.set('PA_BINARY', cdata.get_unquoted('PA_BINARY'))
+
+ sd_user_service_file = configure_file(
+ input : 'systemd/user/pulseaudio.service.in',
+ output : 'pulseaudio.service',
+ configuration : sd_user_service_conf,
+ install : true,
+ install_dir : systemduserunitdir,
+ )
+
+ install_data('systemd/user/pulseaudio.socket',
+ install_dir: systemduserunitdir,
+ )
+endif
diff --git a/src/daemon/pulseaudio-system.conf b/src/daemon/pulseaudio-system.conf
new file mode 100644
index 0000000..2eb342e
--- /dev/null
+++ b/src/daemon/pulseaudio-system.conf
@@ -0,0 +1,31 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+
+<!--
+This file is part of PulseAudio.
+
+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/>.
+-->
+
+<busconfig>
+
+ <!-- System-wide PulseAudio runs as 'pulse' user. This fragment is
+ not necessary for user PulseAudio instances. -->
+
+ <policy user="pulse">
+ <allow own="org.pulseaudio.Server"/>
+ </policy>
+
+</busconfig>
diff --git a/src/daemon/pulseaudio.desktop.in b/src/daemon/pulseaudio.desktop.in
new file mode 100644
index 0000000..2e85202
--- /dev/null
+++ b/src/daemon/pulseaudio.desktop.in
@@ -0,0 +1,9 @@
+[Desktop Entry]
+Version=1.0
+Name=PulseAudio Sound System
+Comment=Start the PulseAudio Sound System
+Exec=start-pulseaudio-x11
+Terminal=false
+Type=Application
+X-GNOME-Autostart-Phase=Initialization
+X-KDE-autostart-phase=1
diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c
new file mode 100644
index 0000000..ca390c7
--- /dev/null
+++ b/src/daemon/server-lookup.c
@@ -0,0 +1,492 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulse/client-conf.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-shared.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "server-lookup.h"
+
+#define OBJECT_PATH "/org/pulseaudio/server_lookup1"
+#define INTERFACE "org.PulseAudio.ServerLookup1"
+
+struct pa_dbusobj_server_lookup {
+ pa_core *core;
+ pa_dbus_connection *conn;
+ bool path_registered;
+};
+
+static const char introspection[] =
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
+ "<node>"
+ " <!-- If you are looking for documentation make sure to check out\n"
+ " http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/ -->\n"
+ " <interface name=\"" INTERFACE "\">\n"
+ " <property name=\"Address\" type=\"s\" access=\"read\"/>\n"
+ " </interface>\n"
+ " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"
+ " <method name=\"Introspect\">\n"
+ " <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
+ " </method>\n"
+ " </interface>\n"
+ " <interface name=\"" DBUS_INTERFACE_PROPERTIES "\">\n"
+ " <method name=\"Get\">\n"
+ " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+ " <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
+ " <arg name=\"value\" type=\"v\" direction=\"out\"/>\n"
+ " </method>\n"
+ " <method name=\"Set\">\n"
+ " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+ " <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
+ " <arg name=\"value\" type=\"v\" direction=\"in\"/>\n"
+ " </method>\n"
+ " <method name=\"GetAll\">\n"
+ " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+ " <arg name=\"props\" type=\"a{sv}\" direction=\"out\"/>\n"
+ " </method>\n"
+ " </interface>\n"
+ "</node>\n";
+
+static void unregister_cb(DBusConnection *conn, void *user_data) {
+ pa_dbusobj_server_lookup *sl = user_data;
+
+ pa_assert(sl);
+ pa_assert(sl->path_registered);
+
+ sl->path_registered = false;
+}
+
+static DBusHandlerResult handle_introspect(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
+ DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
+ const char *i = introspection;
+ DBusMessage *reply = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+
+ if (!(reply = dbus_message_new_method_return(msg))) {
+ r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto finish;
+ }
+ if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &i, DBUS_TYPE_INVALID)) {
+ r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto finish;
+ }
+ if (!dbus_connection_send(conn, reply, NULL)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+
+finish:
+ if (reply)
+ dbus_message_unref(reply);
+
+ return r;
+}
+
+enum get_address_result_t {
+ SUCCESS,
+ SERVER_FROM_TYPE_FAILED
+};
+
+/* Caller frees the returned address. */
+static enum get_address_result_t get_address(pa_server_type_t server_type, char **address) {
+ enum get_address_result_t r = SUCCESS;
+ pa_client_conf *conf = pa_client_conf_new();
+
+ *address = NULL;
+
+ pa_client_conf_load(conf, false, false);
+
+ if (conf->default_dbus_server)
+ *address = pa_xstrdup(conf->default_dbus_server);
+ else if (!(*address = pa_get_dbus_address_from_server_type(server_type))) {
+ r = SERVER_FROM_TYPE_FAILED;
+ goto finish;
+ }
+
+finish:
+ pa_client_conf_free(conf);
+ return r;
+}
+
+static DBusHandlerResult handle_get_address(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
+ DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
+ DBusMessage *reply = NULL;
+ char *address = NULL;
+ DBusMessageIter msg_iter;
+ DBusMessageIter variant_iter;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(sl);
+
+ switch (get_address(sl->core->server_type, &address)) {
+ case SUCCESS:
+ if (!(reply = dbus_message_new_method_return(msg))) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ dbus_message_iter_init_append(reply, &msg_iter);
+ if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &address)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ if (!dbus_message_iter_close_container(&msg_iter, &variant_iter)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ if (!dbus_connection_send(conn, reply, NULL)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ r = DBUS_HANDLER_RESULT_HANDLED;
+ goto finish;
+
+ case SERVER_FROM_TYPE_FAILED:
+ if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed."))) {
+ r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto finish;
+ }
+ if (!dbus_connection_send(conn, reply, NULL)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ r = DBUS_HANDLER_RESULT_HANDLED;
+ goto finish;
+
+ default:
+ pa_assert_not_reached();
+ }
+
+finish:
+ pa_xfree(address);
+ if (reply)
+ dbus_message_unref(reply);
+
+ return r;
+}
+
+static DBusHandlerResult handle_get(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
+ DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
+ const char* interface;
+ const char* property;
+ DBusMessage *reply = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(sl);
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) {
+ if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
+ r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto finish;
+ }
+ if (!dbus_connection_send(conn, reply, NULL)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ r = DBUS_HANDLER_RESULT_HANDLED;
+ goto finish;
+ }
+
+ if (*interface && !pa_streq(interface, INTERFACE)) {
+ r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto finish;
+ }
+
+ if (!pa_streq(property, "Address")) {
+ if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) {
+ r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto finish;
+ }
+ if (!dbus_connection_send(conn, reply, NULL)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ r = DBUS_HANDLER_RESULT_HANDLED;
+ goto finish;
+ }
+
+ r = handle_get_address(conn, msg, sl);
+
+finish:
+ if (reply)
+ dbus_message_unref(reply);
+
+ return r;
+}
+
+static DBusHandlerResult handle_set(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
+ DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
+ const char* interface;
+ const char* property;
+ DBusMessage *reply = NULL;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(sl);
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) {
+ if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
+ r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto finish;
+ }
+ if (!dbus_connection_send(conn, reply, NULL)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ r = DBUS_HANDLER_RESULT_HANDLED;
+ goto finish;
+ }
+
+ if (*interface && !pa_streq(interface, INTERFACE)) {
+ r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto finish;
+ }
+
+ if (!pa_streq(property, "Address")) {
+ if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) {
+ r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto finish;
+ }
+ if (!dbus_connection_send(conn, reply, NULL)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ r = DBUS_HANDLER_RESULT_HANDLED;
+ goto finish;
+ }
+
+ if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) {
+ r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto finish;
+ }
+ if (!dbus_connection_send(conn, reply, NULL)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ r = DBUS_HANDLER_RESULT_HANDLED;
+
+finish:
+ if (reply)
+ dbus_message_unref(reply);
+
+ return r;
+}
+
+static DBusHandlerResult handle_get_all(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
+ DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
+ DBusMessage *reply = NULL;
+ const char *property = "Address";
+ char *interface = NULL;
+ char *address = NULL;
+ DBusMessageIter msg_iter;
+ DBusMessageIter dict_iter;
+ DBusMessageIter dict_entry_iter;
+ DBusMessageIter variant_iter;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(sl);
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) {
+ if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
+ r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto finish;
+ }
+ if (!dbus_connection_send(conn, reply, NULL)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ r = DBUS_HANDLER_RESULT_HANDLED;
+ goto finish;
+ }
+
+ switch (get_address(sl->core->server_type, &address)) {
+ case SUCCESS:
+ if (!(reply = dbus_message_new_method_return(msg))) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ dbus_message_iter_init_append(reply, &msg_iter);
+ if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ if (!dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &property)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ if (!dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &address)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ if (!dbus_message_iter_close_container(&dict_entry_iter, &variant_iter)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ if (!dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ if (!dbus_message_iter_close_container(&msg_iter, &dict_iter)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ if (!dbus_connection_send(conn, reply, NULL)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ r = DBUS_HANDLER_RESULT_HANDLED;
+ goto finish;
+
+ case SERVER_FROM_TYPE_FAILED:
+ if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed."))) {
+ r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto finish;
+ }
+ if (!dbus_connection_send(conn, reply, NULL)) {
+ r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto finish;
+ }
+ r = DBUS_HANDLER_RESULT_HANDLED;
+ goto finish;
+
+ default:
+ pa_assert_not_reached();
+ }
+
+finish:
+ pa_xfree(address);
+ if (reply)
+ dbus_message_unref(reply);
+
+ return r;
+}
+
+static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void *user_data) {
+ pa_dbusobj_server_lookup *sl = user_data;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert(sl);
+
+ /* pa_log("Got message! type = %s path = %s iface = %s member = %s dest = %s", dbus_message_type_to_string(dbus_message_get_type(msg)), dbus_message_get_path(msg), dbus_message_get_interface(msg), dbus_message_get_member(msg), dbus_message_get_destination(msg)); */
+
+ if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (dbus_message_is_method_call(msg, DBUS_INTERFACE_INTROSPECTABLE, "Introspect") ||
+ (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Introspect")))
+ return handle_introspect(conn, msg, sl);
+
+ if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "Get") ||
+ (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Get")))
+ return handle_get(conn, msg, sl);
+
+ if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "Set") ||
+ (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Set")))
+ return handle_set(conn, msg, sl);
+
+ if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "GetAll") ||
+ (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "GetAll")))
+ return handle_get_all(conn, msg, sl);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusObjectPathVTable vtable = {
+ .unregister_function = unregister_cb,
+ .message_function = message_cb,
+ .dbus_internal_pad1 = NULL,
+ .dbus_internal_pad2 = NULL,
+ .dbus_internal_pad3 = NULL,
+ .dbus_internal_pad4 = NULL
+};
+
+pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c) {
+ pa_dbusobj_server_lookup *sl;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ sl = pa_xnew(pa_dbusobj_server_lookup, 1);
+ sl->core = c;
+ sl->path_registered = false;
+
+ if (!(sl->conn = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) {
+ pa_log_warn("Unable to contact D-Bus: %s: %s", error.name, error.message);
+ goto fail;
+ }
+
+ if (!dbus_connection_register_object_path(pa_dbus_connection_get(sl->conn), OBJECT_PATH, &vtable, sl)) {
+ pa_log("dbus_connection_register_object_path() failed for " OBJECT_PATH ".");
+ goto fail;
+ }
+
+ sl->path_registered = true;
+
+ return sl;
+
+fail:
+ dbus_error_free(&error);
+
+ pa_dbusobj_server_lookup_free(sl);
+
+ return NULL;
+}
+
+void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl) {
+ pa_assert(sl);
+
+ if (sl->path_registered) {
+ pa_assert(sl->conn);
+ if (!dbus_connection_unregister_object_path(pa_dbus_connection_get(sl->conn), OBJECT_PATH))
+ pa_log_debug("dbus_connection_unregister_object_path() failed for " OBJECT_PATH ".");
+ }
+
+ if (sl->conn)
+ pa_dbus_connection_unref(sl->conn);
+
+ pa_xfree(sl);
+}
diff --git a/src/daemon/server-lookup.h b/src/daemon/server-lookup.h
new file mode 100644
index 0000000..935baf6
--- /dev/null
+++ b/src/daemon/server-lookup.h
@@ -0,0 +1,38 @@
+#ifndef fooserverlookuphfoo
+#define fooserverlookuphfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2009 Tanu Kaskinen
+
+ 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/>.
+***/
+
+/* This object implements the D-Bus object at path
+ * /org/pulseaudio/server_lookup. Implemented interfaces
+ * are org.pulseaudio.ServerLookup and org.freedesktop.DBus.Introspectable.
+ *
+ * See http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/ConnectingToServer/
+ * for the ServerLookup interface documentation.
+ */
+
+#include <pulsecore/core.h>
+
+typedef struct pa_dbusobj_server_lookup pa_dbusobj_server_lookup;
+
+pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c);
+void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl);
+
+#endif
diff --git a/src/daemon/start-pulseaudio-x11.in b/src/daemon/start-pulseaudio-x11.in
new file mode 100755
index 0000000..0e84315
--- /dev/null
+++ b/src/daemon/start-pulseaudio-x11.in
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+# This file is part of PulseAudio.
+#
+# 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 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/>.
+
+set -e
+
+if [ x"$DISPLAY" != x ] ; then
+
+ @PACTL_BINARY@ load-module module-x11-publish "display=$DISPLAY xauthority=$XAUTHORITY" > /dev/null
+ @PACTL_BINARY@ load-module module-x11-cork-request "display=$DISPLAY xauthority=$XAUTHORITY" > /dev/null
+
+ # KDE plasma versions older than 5.17.0 use module-device-manager's routing API.
+ # Check for current plasma version and load module if it's necessary.
+ if [ x"$KDE_FULL_SESSION" = x"true" ]; then
+ plasmaversion="$(plasmashell -v 2>/dev/null | sed -n 's/^plasmashell \([0-9][0-9]*\)\.\([0-9][0-9]*\)\.\([0-9][0-9]*\)/\1*1000000+\2*1000+\3/p' | head -1)"
+ if [ -n "$plasmaversion" ] && [ "$(($plasmaversion))" -lt "5017000" ]; then
+ @PACTL_BINARY@ load-module module-device-manager "do_routing=1" > /dev/null
+ fi
+ fi
+
+ if [ x"$SESSION_MANAGER" != x ] ; then
+ @PACTL_BINARY@ load-module module-x11-xsmp "display=$DISPLAY xauthority=$XAUTHORITY session_manager=$SESSION_MANAGER" > /dev/null
+ fi
+fi
diff --git a/src/daemon/system.pa.in b/src/daemon/system.pa.in
new file mode 100755
index 0000000..73e39ec
--- /dev/null
+++ b/src/daemon/system.pa.in
@@ -0,0 +1,62 @@
+#!@PA_BINARY@ -nF
+#
+# This file is part of PulseAudio.
+#
+# 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 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/>.
+
+# This startup script is used only if PulseAudio is started in system
+# mode.
+changequote(`[', `]')dnl Set up m4 quoting
+
+### Automatically restore the volume of streams and devices
+load-module module-device-restore
+load-module module-stream-restore
+load-module module-card-restore
+
+### Automatically load driver modules depending on the hardware available
+ifelse(@HAVE_UDEV@, 1, [dnl
+.ifexists module-udev-detect@PA_SOEXT@
+load-module module-udev-detect
+.else
+], @HAVE_COREAUDIO@, 1, [dnl
+.ifexists module-coreaudio-detect@PA_SOEXT@
+load-module module-coreaudio-detect
+.else
+], [dnl
+.ifexists module-detect@PA_SOEXT@
+])dnl
+### Use the static hardware detection module (for systems that lack udev/hal support)
+load-module module-detect
+.endif
+
+### Load several protocols
+.ifexists module-esound-protocol-unix@PA_SOEXT@
+load-module module-esound-protocol-unix
+.endif
+load-module module-native-protocol-unix
+
+### Automatically restore the default sink/source when changed by the user
+### during runtime
+### NOTE: This should be loaded as early as possible so that subsequent modules
+### that look up the default sink/source get the right value
+load-module module-default-device-restore
+
+### Make sure we always have a sink around, even if it is a null sink.
+load-module module-always-sink
+
+### Automatically suspend sinks/sources that become idle for too long
+load-module module-suspend-on-idle
+
+### Enable positioned event sounds
+load-module module-position-event-sounds
diff --git a/src/daemon/systemd/user/pulseaudio.service.in b/src/daemon/systemd/user/pulseaudio.service.in
new file mode 100644
index 0000000..ae0caf8
--- /dev/null
+++ b/src/daemon/systemd/user/pulseaudio.service.in
@@ -0,0 +1,34 @@
+[Unit]
+Description=Sound Service
+
+# We require pulseaudio.socket to be active before starting the daemon, because
+# while it is possible to use the service without the socket, it is not clear
+# why it would be desirable.
+#
+# A user installing pulseaudio and doing `systemctl --user start pulseaudio`
+# will not get the socket started, which might be confusing and problematic if
+# the server is to be restarted later on, as the client autospawn feature
+# might kick in. Also, a start of the socket unit will fail, adding to the
+# confusion.
+#
+# After=pulseaudio.socket is not needed, as it is already implicit in the
+# socket-service relationship, see systemd.socket(5).
+Requires=pulseaudio.socket
+ConditionUser=!root
+
+[Service]
+ExecStart=@PA_BINARY@ --daemonize=no --log-target=journal
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
+Restart=on-failure
+RestrictNamespaces=yes
+SystemCallArchitectures=native
+SystemCallFilter=@system-service
+# Note that notify will only work if --daemonize=no
+Type=notify
+UMask=0077
+
+[Install]
+Also=pulseaudio.socket
+WantedBy=default.target
diff --git a/src/daemon/systemd/user/pulseaudio.socket b/src/daemon/systemd/user/pulseaudio.socket
new file mode 100644
index 0000000..98c1002
--- /dev/null
+++ b/src/daemon/systemd/user/pulseaudio.socket
@@ -0,0 +1,11 @@
+[Unit]
+Description=Sound System
+ConditionUser=!root
+
+[Socket]
+Priority=6
+Backlog=5
+ListenStream=%t/pulse/native
+
+[Install]
+WantedBy=sockets.target