mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-03 09:01:50 -05:00
daemon: Implement the DBus server lookup service.
This commit is contained in:
parent
c94e7421aa
commit
5babbaafb2
10 changed files with 490 additions and 37 deletions
|
|
@ -135,13 +135,14 @@ BUILT_SOURCES = \
|
||||||
bin_PROGRAMS = pulseaudio
|
bin_PROGRAMS = pulseaudio
|
||||||
|
|
||||||
pulseaudio_SOURCES = \
|
pulseaudio_SOURCES = \
|
||||||
daemon/caps.h daemon/caps.c \
|
daemon/caps.c daemon/caps.h \
|
||||||
daemon/cmdline.c daemon/cmdline.h \
|
daemon/cmdline.c daemon/cmdline.h \
|
||||||
daemon/cpulimit.c daemon/cpulimit.h \
|
daemon/cpulimit.c daemon/cpulimit.h \
|
||||||
daemon/daemon-conf.c daemon/daemon-conf.h \
|
daemon/daemon-conf.c daemon/daemon-conf.h \
|
||||||
daemon/dumpmodules.c daemon/dumpmodules.h \
|
daemon/dumpmodules.c daemon/dumpmodules.h \
|
||||||
daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h \
|
daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h \
|
||||||
daemon/main.c
|
daemon/main.c \
|
||||||
|
daemon/server-lookup.c daemon/server-lookup.h
|
||||||
|
|
||||||
pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSPEEX_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(LIBOIL_CFLAGS) $(DBUS_CFLAGS)
|
pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSPEEX_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(LIBOIL_CFLAGS) $(DBUS_CFLAGS)
|
||||||
pulseaudio_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSPEEX_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(LIBOIL_LIBS) $(DBUS_LIBS)
|
pulseaudio_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSPEEX_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(LIBOIL_LIBS) $(DBUS_LIBS)
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,9 @@ static const pa_daemon_conf default_conf = {
|
||||||
.config_file = NULL,
|
.config_file = NULL,
|
||||||
.use_pid_file = TRUE,
|
.use_pid_file = TRUE,
|
||||||
.system_instance = FALSE,
|
.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 = FALSE,
|
.no_cpu_limit = FALSE,
|
||||||
.disable_shm = FALSE,
|
.disable_shm = FALSE,
|
||||||
.default_n_fragments = 4,
|
.default_n_fragments = 4,
|
||||||
|
|
@ -203,6 +206,22 @@ int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int pa_daemon_conf_set_local_server_type(pa_daemon_conf *c, const char *string) {
|
||||||
|
pa_assert(c);
|
||||||
|
pa_assert(string);
|
||||||
|
|
||||||
|
if (!strcmp(string, "user"))
|
||||||
|
c->local_server_type = PA_SERVER_TYPE_USER;
|
||||||
|
else if (!strcmp(string, "system")) {
|
||||||
|
c->local_server_type = PA_SERVER_TYPE_SYSTEM;
|
||||||
|
} else if (!strcmp(string, "none")) {
|
||||||
|
c->local_server_type = PA_SERVER_TYPE_NONE;
|
||||||
|
} else
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int parse_log_target(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
|
static int parse_log_target(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
|
||||||
pa_daemon_conf *c = data;
|
pa_daemon_conf *c = data;
|
||||||
|
|
||||||
|
|
@ -430,6 +449,22 @@ static int parse_rtprio(const char *filename, unsigned line, const char *section
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int parse_server_type(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
|
||||||
|
pa_daemon_conf *c = data;
|
||||||
|
|
||||||
|
pa_assert(filename);
|
||||||
|
pa_assert(lvalue);
|
||||||
|
pa_assert(rvalue);
|
||||||
|
pa_assert(data);
|
||||||
|
|
||||||
|
if (pa_daemon_conf_set_local_server_type(c, rvalue) < 0) {
|
||||||
|
pa_log(_("[%s:%u] Invalid server type '%s'."), filename, line, rvalue);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
|
int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
|
||||||
int r = -1;
|
int r = -1;
|
||||||
FILE *f = NULL;
|
FILE *f = NULL;
|
||||||
|
|
@ -443,6 +478,9 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
|
||||||
{ "disallow-exit", pa_config_parse_bool, &c->disallow_exit, NULL },
|
{ "disallow-exit", pa_config_parse_bool, &c->disallow_exit, NULL },
|
||||||
{ "use-pid-file", pa_config_parse_bool, &c->use_pid_file, NULL },
|
{ "use-pid-file", pa_config_parse_bool, &c->use_pid_file, NULL },
|
||||||
{ "system-instance", pa_config_parse_bool, &c->system_instance, 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 },
|
{ "no-cpu-limit", pa_config_parse_bool, &c->no_cpu_limit, NULL },
|
||||||
{ "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL },
|
{ "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL },
|
||||||
{ "flat-volumes", pa_config_parse_bool, &c->flat_volumes, NULL },
|
{ "flat-volumes", pa_config_parse_bool, &c->flat_volumes, NULL },
|
||||||
|
|
@ -604,6 +642,13 @@ static const char* const log_level_to_string[] = {
|
||||||
[PA_LOG_ERROR] = "error"
|
[PA_LOG_ERROR] = "error"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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"
|
||||||
|
};
|
||||||
|
|
||||||
char *pa_daemon_conf_dump(pa_daemon_conf *c) {
|
char *pa_daemon_conf_dump(pa_daemon_conf *c) {
|
||||||
pa_strbuf *s;
|
pa_strbuf *s;
|
||||||
char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
|
char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
|
||||||
|
|
@ -627,6 +672,9 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
|
||||||
pa_strbuf_printf(s, "disallow-exit = %s\n", pa_yes_no(c->disallow_exit));
|
pa_strbuf_printf(s, "disallow-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, "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));
|
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, "no-cpu-limit = %s\n", pa_yes_no(c->no_cpu_limit));
|
pa_strbuf_printf(s, "no-cpu-limit = %s\n", pa_yes_no(c->no_cpu_limit));
|
||||||
pa_strbuf_printf(s, "disable-shm = %s\n", pa_yes_no(c->disable_shm));
|
pa_strbuf_printf(s, "disable-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, "flat-volumes = %s\n", pa_yes_no(c->flat_volumes));
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,13 @@ typedef enum pa_daemon_conf_cmd {
|
||||||
PA_CMD_CLEANUP_SHM
|
PA_CMD_CLEANUP_SHM
|
||||||
} pa_daemon_conf_cmd_t;
|
} pa_daemon_conf_cmd_t;
|
||||||
|
|
||||||
|
typedef enum pa_daemon_conf_server_type {
|
||||||
|
PA_SERVER_TYPE_UNSET,
|
||||||
|
PA_SERVER_TYPE_USER,
|
||||||
|
PA_SERVER_TYPE_SYSTEM,
|
||||||
|
PA_SERVER_TYPE_NONE
|
||||||
|
} pa_daemon_conf_server_type_t;
|
||||||
|
|
||||||
#ifdef HAVE_SYS_RESOURCE_H
|
#ifdef HAVE_SYS_RESOURCE_H
|
||||||
typedef struct pa_rlimit {
|
typedef struct pa_rlimit {
|
||||||
rlim_t value;
|
rlim_t value;
|
||||||
|
|
@ -74,6 +81,7 @@ typedef struct pa_daemon_conf {
|
||||||
log_meta,
|
log_meta,
|
||||||
log_time,
|
log_time,
|
||||||
flat_volumes;
|
flat_volumes;
|
||||||
|
pa_daemon_conf_server_type_t local_server_type;
|
||||||
int exit_idle_time,
|
int exit_idle_time,
|
||||||
scache_idle_time,
|
scache_idle_time,
|
||||||
auto_log_target,
|
auto_log_target,
|
||||||
|
|
@ -151,6 +159,7 @@ int pa_daemon_conf_env(pa_daemon_conf *c);
|
||||||
int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *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_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_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);
|
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);
|
FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c);
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
; disallow-exit = no
|
; disallow-exit = no
|
||||||
; use-pid-file = yes
|
; use-pid-file = yes
|
||||||
; system-instance = no
|
; system-instance = no
|
||||||
|
; local-server-type = user
|
||||||
; disable-shm = no
|
; disable-shm = no
|
||||||
; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB
|
; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,7 @@
|
||||||
#include "caps.h"
|
#include "caps.h"
|
||||||
#include "ltdl-bind-now.h"
|
#include "ltdl-bind-now.h"
|
||||||
#include "polkit.h"
|
#include "polkit.h"
|
||||||
|
#include "server-lookup.h"
|
||||||
|
|
||||||
#ifdef HAVE_LIBWRAP
|
#ifdef HAVE_LIBWRAP
|
||||||
/* Only one instance of these variables */
|
/* Only one instance of these variables */
|
||||||
|
|
@ -335,33 +336,31 @@ static void set_all_rlimits(const pa_daemon_conf *conf) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
static pa_dbus_connection *register_dbus(pa_core *c) {
|
static pa_dbus_connection *register_dbus_name(pa_core *c, DBusBusType bus, const char* name) {
|
||||||
DBusError error;
|
DBusError error;
|
||||||
pa_dbus_connection *conn;
|
pa_dbus_connection *conn;
|
||||||
|
|
||||||
dbus_error_init(&error);
|
dbus_error_init(&error);
|
||||||
|
|
||||||
if (!(conn = pa_dbus_bus_get(c, pa_in_system_mode() ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&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);
|
pa_log_warn("Unable to contact D-Bus: %s: %s", error.name, error.message);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dbus_bus_request_name(pa_dbus_connection_get(conn), "org.pulseaudio.Server", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
|
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 org.pulseaudio.Server!");
|
pa_log_debug("Got %s!", name);
|
||||||
return conn;
|
return conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dbus_error_is_set(&error))
|
if (dbus_error_is_set(&error))
|
||||||
pa_log_warn("Failed to acquire org.pulseaudio.Server: %s: %s", error.name, error.message);
|
pa_log_error("Failed to acquire %s: %s: %s", name, error.name, error.message);
|
||||||
else
|
else
|
||||||
pa_log_warn("D-Bus name org.pulseaudio.Server already taken. Weird shit!");
|
pa_log_error("D-Bus name %s already taken. Weird shit!", name);
|
||||||
|
|
||||||
/* PA cannot be started twice by the same user and hence we can
|
/* PA cannot be started twice by the same user and hence we can
|
||||||
* ignore mostly the case that org.pulseaudio.Server is already
|
* ignore mostly the case that a name is already taken. */
|
||||||
* taken. */
|
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
|
||||||
if (conn)
|
if (conn)
|
||||||
pa_dbus_connection_unref(conn);
|
pa_dbus_connection_unref(conn);
|
||||||
|
|
||||||
|
|
@ -393,7 +392,10 @@ int main(int argc, char *argv[]) {
|
||||||
int autospawn_fd = -1;
|
int autospawn_fd = -1;
|
||||||
pa_bool_t autospawn_locked = FALSE;
|
pa_bool_t autospawn_locked = FALSE;
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
pa_dbus_connection *dbus = NULL;
|
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. */
|
||||||
|
pa_bool_t start_server;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pa_log_set_ident("pulseaudio");
|
pa_log_set_ident("pulseaudio");
|
||||||
|
|
@ -486,8 +488,45 @@ int main(int argc, char *argv[]) {
|
||||||
pa_log_set_flags(PA_LOG_PRINT_TIME, PA_LOG_SET);
|
pa_log_set_flags(PA_LOG_PRINT_TIME, PA_LOG_SET);
|
||||||
pa_log_set_show_backtrace(conf->log_backtrace);
|
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 || (real_root && 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
|
||||||
|
|
||||||
pa_log_debug("Started as real root: %s, suid root: %s", pa_yes_no(real_root), pa_yes_no(suid_root));
|
pa_log_debug("Started as real root: %s, suid root: %s", pa_yes_no(real_root), pa_yes_no(suid_root));
|
||||||
|
|
||||||
|
#ifdef HAVE_DBUS
|
||||||
|
/* XXX: Uhh, goto programming... as if this wasn't hard enough to follow
|
||||||
|
* already. But if we won't start the full server, we want to just skip all
|
||||||
|
* the capability stuff. */
|
||||||
|
if (!start_server) {
|
||||||
|
if (!real_root && pa_have_caps())
|
||||||
|
pa_drop_caps();
|
||||||
|
goto after_caps_setup;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!real_root && pa_have_caps()) {
|
if (!real_root && pa_have_caps()) {
|
||||||
#ifdef HAVE_SYS_RESOURCE_H
|
#ifdef HAVE_SYS_RESOURCE_H
|
||||||
struct rlimit rl;
|
struct rlimit rl;
|
||||||
|
|
@ -628,6 +667,10 @@ int main(int argc, char *argv[]) {
|
||||||
conf->realtime_scheduling = FALSE;
|
conf->realtime_scheduling = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_DBUS
|
||||||
|
after_caps_setup:
|
||||||
|
#endif
|
||||||
|
|
||||||
pa_log_debug("Can realtime: %s, can high-priority: %s", pa_yes_no(pa_can_realtime()), pa_yes_no(pa_can_high_priority()));
|
pa_log_debug("Can realtime: %s, can high-priority: %s", pa_yes_no(pa_can_realtime()), pa_yes_no(pa_can_high_priority()));
|
||||||
|
|
||||||
LTDL_SET_PRELOADED_SYMBOLS();
|
LTDL_SET_PRELOADED_SYMBOLS();
|
||||||
|
|
@ -716,10 +759,12 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
if (real_root && !conf->system_instance)
|
if (real_root && !conf->system_instance)
|
||||||
pa_log_warn(_("This program is not intended to be run as root (unless --system is specified)."));
|
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 (!real_root && conf->system_instance) {
|
else if (!real_root && conf->system_instance) {
|
||||||
pa_log(_("Root privileges required."));
|
pa_log(_("Root privileges required."));
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (conf->cmd == PA_CMD_START && conf->system_instance) {
|
if (conf->cmd == PA_CMD_START && conf->system_instance) {
|
||||||
pa_log(_("--start not supported for system instances."));
|
pa_log(_("--start not supported for system instances."));
|
||||||
|
|
@ -1007,6 +1052,10 @@ int main(int argc, char *argv[]) {
|
||||||
pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0);
|
pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0);
|
||||||
|
|
||||||
buf = pa_strbuf_new();
|
buf = pa_strbuf_new();
|
||||||
|
|
||||||
|
#ifdef HAVE_DBUS
|
||||||
|
if (start_server) {
|
||||||
|
#endif
|
||||||
if (conf->load_default_script_file) {
|
if (conf->load_default_script_file) {
|
||||||
FILE *f;
|
FILE *f;
|
||||||
|
|
||||||
|
|
@ -1022,10 +1071,6 @@ int main(int argc, char *argv[]) {
|
||||||
pa_log_error("%s", s = pa_strbuf_tostring_free(buf));
|
pa_log_error("%s", s = pa_strbuf_tostring_free(buf));
|
||||||
pa_xfree(s);
|
pa_xfree(s);
|
||||||
|
|
||||||
/* We completed the initial module loading, so let's disable it
|
|
||||||
* from now on, if requested */
|
|
||||||
c->disallow_module_loading = !!conf->disallow_module_loading;
|
|
||||||
|
|
||||||
if (r < 0 && conf->fail) {
|
if (r < 0 && conf->fail) {
|
||||||
pa_log(_("Failed to initialize daemon."));
|
pa_log(_("Failed to initialize daemon."));
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|
@ -1035,6 +1080,19 @@ int main(int argc, char *argv[]) {
|
||||||
pa_log(_("Daemon startup without any loaded modules, refusing to work."));
|
pa_log(_("Daemon startup without any loaded modules, refusing to work."));
|
||||||
goto finish;
|
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_FORK
|
#ifdef HAVE_FORK
|
||||||
if (daemon_pipe[1] >= 0) {
|
if (daemon_pipe[1] >= 0) {
|
||||||
|
|
@ -1046,7 +1104,15 @@ int main(int argc, char *argv[]) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
dbus = register_dbus(c);
|
if (!conf->system_instance) {
|
||||||
|
if (!(server_lookup = pa_dbusobj_server_lookup_new(c, conf->local_server_type)))
|
||||||
|
goto finish;
|
||||||
|
if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.pulseaudio.PulseAudio")))
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start_server && !(server_bus = register_dbus_name(c, conf->system_instance ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, "org.pulseaudio.Server")))
|
||||||
|
goto finish;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pa_log_info(_("Daemon startup complete."));
|
pa_log_info(_("Daemon startup complete."));
|
||||||
|
|
@ -1059,8 +1125,12 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
if (dbus)
|
if (server_bus)
|
||||||
pa_dbus_connection_unref(dbus);
|
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
|
#endif
|
||||||
|
|
||||||
if (autospawn_fd >= 0) {
|
if (autospawn_fd >= 0) {
|
||||||
|
|
|
||||||
277
src/daemon/server-lookup.c
Normal file
277
src/daemon/server-lookup.c
Normal file
|
|
@ -0,0 +1,277 @@
|
||||||
|
/***
|
||||||
|
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, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||||
|
USA.
|
||||||
|
***/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <dbus/dbus.h>
|
||||||
|
|
||||||
|
#include <pulse/client-conf.h>
|
||||||
|
#include <pulse/xmalloc.h>
|
||||||
|
|
||||||
|
#include <pulsecore/core-util.h>
|
||||||
|
#include <pulsecore/dbus-shared.h>
|
||||||
|
#include <pulsecore/macro.h>
|
||||||
|
|
||||||
|
#include "server-lookup.h"
|
||||||
|
|
||||||
|
struct pa_dbusobj_server_lookup {
|
||||||
|
pa_dbus_connection *conn;
|
||||||
|
pa_bool_t path_registered;
|
||||||
|
pa_daemon_conf_server_type_t server_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
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://pulseaudio.org/wiki/DBusInterface -->\n"
|
||||||
|
" <interface name=\"org.pulseaudio.ServerLookup\">"
|
||||||
|
" <method name=\"GetDBusServers\">"
|
||||||
|
" <arg name=\"result\" type=\"s\" direction=\"out\"/>"
|
||||||
|
" </method>"
|
||||||
|
" </interface>"
|
||||||
|
" <interface name=\"org.freedesktop.DBus.Introspectable\">"
|
||||||
|
" <method name=\"Introspect\">"
|
||||||
|
" <arg name=\"data\" type=\"s\" direction=\"out\"/>"
|
||||||
|
" </method>"
|
||||||
|
" </interface>"
|
||||||
|
"</node>";
|
||||||
|
|
||||||
|
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) {
|
||||||
|
const char *i = introspection;
|
||||||
|
DBusMessage *reply = NULL;
|
||||||
|
|
||||||
|
pa_assert(conn);
|
||||||
|
pa_assert(msg);
|
||||||
|
|
||||||
|
if (!(reply = dbus_message_new_method_return(msg)))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &i, DBUS_TYPE_INVALID))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (!dbus_connection_send(conn, reply, NULL))
|
||||||
|
goto oom;
|
||||||
|
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
|
||||||
|
return DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (reply)
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
|
||||||
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
|
||||||
|
oom:
|
||||||
|
if (reply)
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
|
||||||
|
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Caller frees the string. */
|
||||||
|
static char *get_dbus_server_from_type(pa_daemon_conf_server_type_t server_type) {
|
||||||
|
char *server_string = NULL;
|
||||||
|
char *runtime_dir = NULL;
|
||||||
|
|
||||||
|
switch (server_type) {
|
||||||
|
case PA_SERVER_TYPE_USER:
|
||||||
|
runtime_dir = pa_get_runtime_dir();
|
||||||
|
|
||||||
|
if (!runtime_dir)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
server_string = pa_sprintf_malloc("unix:path=%s/dbus_socket", runtime_dir);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PA_SERVER_TYPE_SYSTEM:
|
||||||
|
server_string = pa_xstrdup("unix:path=/var/run/pulse/dbus_socket");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PA_SERVER_TYPE_NONE:
|
||||||
|
server_string = pa_xnew0(char, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
pa_assert_not_reached();
|
||||||
|
}
|
||||||
|
|
||||||
|
return server_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DBusHandlerResult handle_get_dbus_servers(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
|
||||||
|
DBusMessage *reply = NULL;
|
||||||
|
pa_client_conf *conf = NULL;
|
||||||
|
char *server_string = NULL;
|
||||||
|
|
||||||
|
pa_assert(conn);
|
||||||
|
pa_assert(msg);
|
||||||
|
pa_assert(sl);
|
||||||
|
|
||||||
|
conf = pa_client_conf_new();
|
||||||
|
|
||||||
|
if (pa_client_conf_load(conf, NULL) < 0) {
|
||||||
|
if (!(reply = dbus_message_new_error(msg, "org.pulseaudio.ClientConfLoadError", "Failed to load client.conf.")))
|
||||||
|
goto fail;
|
||||||
|
if (!dbus_connection_send(conn, reply, NULL))
|
||||||
|
goto oom;
|
||||||
|
return DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
server_string = pa_xstrdup(conf->default_dbus_server);
|
||||||
|
|
||||||
|
pa_client_conf_free(conf);
|
||||||
|
|
||||||
|
if (!server_string) {
|
||||||
|
if (!(server_string = get_dbus_server_from_type(sl->server_type))) {
|
||||||
|
if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "get_dbus_server_from_type() failed.")))
|
||||||
|
goto fail;
|
||||||
|
if (!dbus_connection_send(conn, reply, NULL))
|
||||||
|
goto oom;
|
||||||
|
return DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(reply = dbus_message_new_method_return(msg)))
|
||||||
|
goto oom;
|
||||||
|
|
||||||
|
if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &server_string, DBUS_TYPE_INVALID))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (!dbus_connection_send(conn, reply, NULL))
|
||||||
|
goto oom;
|
||||||
|
|
||||||
|
pa_log("Sent reply with server_string '%s'.", server_string);
|
||||||
|
|
||||||
|
pa_xfree(server_string);
|
||||||
|
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
|
||||||
|
return DBUS_HANDLER_RESULT_HANDLED;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (conf)
|
||||||
|
pa_client_conf_free(conf);
|
||||||
|
|
||||||
|
pa_xfree(server_string);
|
||||||
|
|
||||||
|
if (reply)
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
|
||||||
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||||
|
|
||||||
|
oom:
|
||||||
|
if (conf)
|
||||||
|
pa_client_conf_free(conf);
|
||||||
|
|
||||||
|
pa_xfree(server_string);
|
||||||
|
|
||||||
|
if (reply)
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
|
||||||
|
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect"))
|
||||||
|
return handle_introspect(conn, msg, sl);
|
||||||
|
|
||||||
|
if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetDBusServers"))
|
||||||
|
return handle_get_dbus_servers(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_daemon_conf_server_type_t server_type) {
|
||||||
|
pa_dbusobj_server_lookup *sl;
|
||||||
|
DBusError error;
|
||||||
|
|
||||||
|
dbus_error_init(&error);
|
||||||
|
|
||||||
|
sl = pa_xnew(pa_dbusobj_server_lookup, 1);
|
||||||
|
sl->path_registered = FALSE;
|
||||||
|
sl->server_type = server_type;
|
||||||
|
|
||||||
|
if (!(sl->conn = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) {
|
||||||
|
pa_log("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), "/org/pulseaudio/server_lookup", &vtable, sl)) {
|
||||||
|
pa_log("dbus_connection_register_object_path() failed for /org/pulseaudio/server_lookup.");
|
||||||
|
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), "/org/pulseaudio/server_lookup"))
|
||||||
|
pa_log_debug("dbus_connection_unregister_object_path() failed for /org/pulseaudio/server_lookup.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sl->conn)
|
||||||
|
pa_dbus_connection_unref(sl->conn);
|
||||||
|
|
||||||
|
pa_xfree(sl);
|
||||||
|
}
|
||||||
42
src/daemon/server-lookup.h
Normal file
42
src/daemon/server-lookup.h
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
#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, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||||
|
USA.
|
||||||
|
***/
|
||||||
|
|
||||||
|
/* 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://pulseaudio.org/wiki/DBusInterface for the ServerLookup interface
|
||||||
|
* documentation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <pulsecore/core.h>
|
||||||
|
|
||||||
|
#include "daemon-conf.h"
|
||||||
|
|
||||||
|
typedef struct pa_dbusobj_server_lookup pa_dbusobj_server_lookup;
|
||||||
|
|
||||||
|
pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c, pa_daemon_conf_server_type_t server_type);
|
||||||
|
void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -57,6 +57,7 @@ static const pa_client_conf default_conf = {
|
||||||
.default_sink = NULL,
|
.default_sink = NULL,
|
||||||
.default_source = NULL,
|
.default_source = NULL,
|
||||||
.default_server = NULL,
|
.default_server = NULL,
|
||||||
|
.default_dbus_server = NULL,
|
||||||
.autospawn = TRUE,
|
.autospawn = TRUE,
|
||||||
.disable_shm = FALSE,
|
.disable_shm = FALSE,
|
||||||
.cookie_file = NULL,
|
.cookie_file = NULL,
|
||||||
|
|
@ -81,6 +82,7 @@ void pa_client_conf_free(pa_client_conf *c) {
|
||||||
pa_xfree(c->default_sink);
|
pa_xfree(c->default_sink);
|
||||||
pa_xfree(c->default_source);
|
pa_xfree(c->default_source);
|
||||||
pa_xfree(c->default_server);
|
pa_xfree(c->default_server);
|
||||||
|
pa_xfree(c->default_dbus_server);
|
||||||
pa_xfree(c->cookie_file);
|
pa_xfree(c->cookie_file);
|
||||||
pa_xfree(c);
|
pa_xfree(c);
|
||||||
}
|
}
|
||||||
|
|
@ -97,6 +99,7 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {
|
||||||
{ "default-sink", pa_config_parse_string, &c->default_sink, NULL },
|
{ "default-sink", pa_config_parse_string, &c->default_sink, NULL },
|
||||||
{ "default-source", pa_config_parse_string, &c->default_source, NULL },
|
{ "default-source", pa_config_parse_string, &c->default_source, NULL },
|
||||||
{ "default-server", pa_config_parse_string, &c->default_server, NULL },
|
{ "default-server", pa_config_parse_string, &c->default_server, NULL },
|
||||||
|
{ "default-dbus-server", pa_config_parse_string, &c->default_dbus_server, NULL },
|
||||||
{ "autospawn", pa_config_parse_bool, &c->autospawn, NULL },
|
{ "autospawn", pa_config_parse_bool, &c->autospawn, NULL },
|
||||||
{ "cookie-file", pa_config_parse_string, &c->cookie_file, NULL },
|
{ "cookie-file", pa_config_parse_string, &c->cookie_file, NULL },
|
||||||
{ "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL },
|
{ "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL },
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,13 @@
|
||||||
USA.
|
USA.
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
#include <pulsecore/macro.h>
|
||||||
#include <pulsecore/native-common.h>
|
#include <pulsecore/native-common.h>
|
||||||
|
|
||||||
/* A structure containing configuration data for PulseAudio clients. */
|
/* A structure containing configuration data for PulseAudio clients. */
|
||||||
|
|
||||||
typedef struct pa_client_conf {
|
typedef struct pa_client_conf {
|
||||||
char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file;
|
char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *default_dbus_server, *cookie_file;
|
||||||
pa_bool_t autospawn, disable_shm;
|
pa_bool_t autospawn, disable_shm;
|
||||||
uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
|
uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
|
||||||
pa_bool_t cookie_valid; /* non-zero, when cookie is valid */
|
pa_bool_t cookie_valid; /* non-zero, when cookie is valid */
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
; default-sink =
|
; default-sink =
|
||||||
; default-source =
|
; default-source =
|
||||||
; default-server =
|
; default-server =
|
||||||
|
; default-dbus-server =
|
||||||
|
|
||||||
; autospawn = yes
|
; autospawn = yes
|
||||||
; daemon-binary = @PA_BINARY@
|
; daemon-binary = @PA_BINARY@
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue