diff options
Diffstat (limited to 'src/daemon/main.c')
| -rw-r--r-- | src/daemon/main.c | 1049 |
1 files changed, 791 insertions, 258 deletions
diff --git a/src/daemon/main.c b/src/daemon/main.c index 3ced3bf6..94c87d15 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -1,18 +1,19 @@ -/* $Id$ */ - /*** 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 of the License, + 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 @@ -30,18 +31,15 @@ #include <stdio.h> #include <signal.h> #include <stddef.h> -#include <assert.h> #include <ltdl.h> #include <limits.h> -#include <fcntl.h> #include <unistd.h> #include <locale.h> #include <sys/types.h> +#include <sys/stat.h> -#include <liboil/liboil.h> - -#ifdef HAVE_SYS_IOCTL_H -#include <sys/ioctl.h> +#ifdef HAVE_SYS_MMAN_H +#include <sys/mman.h> #endif #ifdef HAVE_PWD_H @@ -56,16 +54,25 @@ #include <tcpd.h> #endif -#include "../pulsecore/winsock.h" +#ifdef HAVE_DBUS +#include <dbus/dbus.h> +#endif +#include <pulse/client-conf.h> +#ifdef HAVE_X11 +#include <pulse/client-conf-x11.h> +#endif #include <pulse/mainloop.h> #include <pulse/mainloop-signal.h> #include <pulse/timeval.h> #include <pulse/xmalloc.h> +#include <pulse/i18n.h> +#include <pulsecore/lock-autospawn.h> +#include <pulsecore/socket.h> #include <pulsecore/core-error.h> +#include <pulsecore/core-rtclock.h> #include <pulsecore/core.h> -#include <pulsecore/memblock.h> #include <pulsecore/module.h> #include <pulsecore/cli-command.h> #include <pulsecore/log.h> @@ -73,14 +80,25 @@ #include <pulsecore/sioman.h> #include <pulsecore/cli-text.h> #include <pulsecore/pid.h> -#include <pulsecore/namereg.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-arm.h> +#include <pulsecore/cpu-x86.h> +#include <pulsecore/cpu-orc.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 HAVE_LIBWRAP /* Only one instance of these variables */ @@ -88,7 +106,7 @@ int allow_severity = LOG_INFO; int deny_severity = LOG_WARNING; #endif -#ifdef HAVE_OSS +#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. */ @@ -97,11 +115,11 @@ int __padsp_disabled__ = 7; #ifdef OS_IS_WIN32 -static void message_cb(pa_mainloop_api*a, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { +static void message_cb(pa_mainloop_api*a, pa_time_event*e, const struct timeval *tv, void *userdata) { MSG msg; struct timeval tvnext; - while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) raise(SIGTERM); else { @@ -116,8 +134,8 @@ static void message_cb(pa_mainloop_api*a, pa_time_event*e, PA_GCC_UNUSED const s #endif -static void signal_callback(pa_mainloop_api*m, PA_GCC_UNUSED pa_signal_event *e, int sig, void *userdata) { - pa_log_info(__FILE__": Got signal %s.", pa_strsignal(sig)); +static void signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) { + pa_log_info(_("Got signal %s."), pa_sig2str(sig)); switch (sig) { #ifdef SIGUSR1 @@ -125,7 +143,7 @@ static void signal_callback(pa_mainloop_api*m, PA_GCC_UNUSED pa_signal_event *e, pa_module_load(userdata, "module-cli", NULL); break; #endif - + #ifdef SIGUSR2 case SIGUSR2: pa_module_load(userdata, "module-cli-protocol-unix", NULL); @@ -144,22 +162,12 @@ static void signal_callback(pa_mainloop_api*m, PA_GCC_UNUSED pa_signal_event *e, case SIGINT: case SIGTERM: default: - pa_log_info(__FILE__": Exiting."); + pa_log_info(_("Exiting.")); m->quit(m, 1); break; } } -static void close_pipe(int p[2]) { - if (p[0] != -1) - close(p[0]); - if (p[1] != -1) - close(p[1]); - p[0] = p[1] = -1; -} - -#define set_env(key, value) putenv(pa_sprintf_malloc("%s=%s", (key), (value))) - #if defined(HAVE_PWD_H) && defined(HAVE_GRP_H) static int change_user(void) { @@ -170,36 +178,43 @@ static int change_user(void) { /* 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(__FILE__": Failed to find user '%s'.", PA_SYSTEM_USER); + pa_log(_("Failed to find user '%s'."), PA_SYSTEM_USER); return -1; } if (!(gr = getgrnam(PA_SYSTEM_GROUP))) { - pa_log(__FILE__": Failed to find group '%s'.", PA_SYSTEM_GROUP); + pa_log(_("Failed to find group '%s'."), PA_SYSTEM_GROUP); return -1; } - pa_log_info(__FILE__": Found user '%s' (UID %lu) and group '%s' (GID %lu).", + 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(__FILE__": GID of user '%s' and of group '%s' don't match.", PA_SYSTEM_USER, PA_SYSTEM_GROUP); + pa_log(_("GID of user '%s' and of group '%s' don't match."), PA_SYSTEM_USER, PA_SYSTEM_GROUP); return -1; } if (strcmp(pw->pw_dir, PA_SYSTEM_RUNTIME_PATH) != 0) - pa_log_warn(__FILE__": Warning: home directory of user '%s' is not '%s', ignoring.", PA_SYSTEM_USER, 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) < 0) { - pa_log(__FILE__": Failed to create '%s': %s", PA_SYSTEM_RUNTIME_PATH, pa_cstrerror(errno)); + 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) < 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(__FILE__": Failed to change group list: %s", pa_cstrerror(errno)); + pa_log(_("Failed to change group list: %s"), pa_cstrerror(errno)); return -1; } @@ -211,11 +226,11 @@ static int change_user(void) { #elif defined(HAVE_SETREGID) r = setregid(gr->gr_gid, gr->gr_gid); #else -#error "No API to drop priviliges" +#error "No API to drop privileges" #endif if (r < 0) { - pa_log(__FILE__": Failed to change GID: %s", pa_cstrerror(errno)); + pa_log(_("Failed to change GID: %s"), pa_cstrerror(errno)); return -1; } @@ -227,23 +242,30 @@ static int change_user(void) { #elif defined(HAVE_SETREUID) r = setreuid(pw->pw_uid, pw->pw_uid); #else -#error "No API to drop priviliges" +#error "No API to drop privileges" #endif if (r < 0) { - pa_log(__FILE__": Failed to change UID: %s", pa_cstrerror(errno)); + pa_log(_("Failed to change UID: %s"), pa_cstrerror(errno)); return -1; } - set_env("USER", PA_SYSTEM_USER); - set_env("LOGNAME", PA_SYSTEM_GROUP); - set_env("HOME", PA_SYSTEM_RUNTIME_PATH); + 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() */ - set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH); - set_env("PULSE_CONFIG_PATH", PA_SYSTEM_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(__FILE__": Successfully dropped root privileges."); + pa_log_info(_("Successfully dropped root privileges.")); return 0; } @@ -251,116 +273,218 @@ static int change_user(void) { #else /* HAVE_PWD_H && HAVE_GRP_H */ static int change_user(void) { - pa_log(__FILE__": System wide mode unsupported on this platform."); + pa_log(_("System wide mode unsupported on this platform.")); return -1; } #endif /* HAVE_PWD_H && HAVE_GRP_H */ -static int create_runtime_dir(void) { - char fn[PATH_MAX]; - - pa_runtime_path(NULL, fn, sizeof(fn)); - - /* This function is called only when the daemon is started in - * per-user mode. We create the runtime directory somewhere in - * /tmp/ with the current UID/GID */ - - if (pa_make_secure_dir(fn, 0700, (uid_t)-1, (gid_t)-1) < 0) { - pa_log(__FILE__": Failed to create '%s': %s", fn, pa_cstrerror(errno)); - return -1; - } - - return 0; -} - #ifdef HAVE_SYS_RESOURCE_H -static void set_one_rlimit(const pa_rlimit *r, int resource, const char *name) { +static int set_one_rlimit(const pa_rlimit *r, int resource, const char *name) { struct rlimit rl; - assert(r); + pa_assert(r); if (!r->is_set) - return; + return 0; rl.rlim_cur = rl.rlim_max = r->value; - if (setrlimit(resource, &rl) < 0) - pa_log_warn(__FILE__": setrlimit(%s, (%u, %u)) failed: %s", name, (unsigned) r->value, (unsigned) r->value, pa_cstrerror(errno)); + 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_as, RLIMIT_AS, "RLIMIT_AS"); - set_one_rlimit(&conf->rlimit_core, RLIMIT_CORE, "RLIMIT_CORE"); - set_one_rlimit(&conf->rlimit_data, RLIMIT_DATA, "RLIMIT_DATA"); set_one_rlimit(&conf->rlimit_fsize, RLIMIT_FSIZE, "RLIMIT_FSIZE"); - set_one_rlimit(&conf->rlimit_nofile, RLIMIT_NOFILE, "RLIMIT_NOFILE"); + 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 -int main(int argc, char *argv[]) { - pa_core *c; - pa_strbuf *buf = NULL; - pa_daemon_conf *conf; - pa_mainloop *mainloop; +static char *check_configured_address(void) { + char *default_server = NULL; + pa_client_conf *c = pa_client_conf_new(); - char *s; - int r, retval = 1, d = 0; - int daemon_pipe[2] = { -1, -1 }; - int suid_root, real_root; - int valid_pid_file = 0; + pa_client_conf_load(c, NULL); +#ifdef HAVE_X11 + pa_client_conf_from_x11(c, NULL); +#endif + pa_client_conf_env(c); -#ifdef HAVE_GETUID - gid_t gid = (gid_t) -1; + 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. Weird shit!", 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; + pa_bool_t valid_pid_file = FALSE; + pa_bool_t ltdl_init = FALSE; + int passed_fd = -1; + const char *e; +#ifdef HAVE_FORK + int daemon_pipe[2] = { -1, -1 }; + int daemon_pipe2[2] = { -1, -1 }; +#endif #ifdef OS_IS_WIN32 - pa_time_event *timer; - struct timeval tv; + pa_time_event *win32_timer; + struct timeval win32_tv; +#endif + int autospawn_fd = -1; + pa_bool_t 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. */ + pa_bool_t start_server; #endif - setlocale(LC_ALL, ""); + 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); - pa_limit_caps(); +#if 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. + */ -#ifdef HAVE_GETUID - real_root = getuid() == 0; - suid_root = !real_root && geteuid() == 0; - - if (suid_root && (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) <= 0 || gid >= 1000)) { - pa_log_warn(__FILE__": WARNING: called SUID root, but not in group '"PA_REALTIME_GROUP"'."); - pa_drop_root(); + 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."); } -#else - real_root = 0; - suid_root = 0; #endif - - LTDL_SET_PRELOADED_SYMBOLS(); - - r = lt_dlinit(); - assert(r == 0); -#ifdef OS_IS_WIN32 - { - WSADATA data; - WSAStartup(MAKEWORD(2, 0), &data); + if ((e = getenv("PULSE_PASSED_FD"))) { + passed_fd = atoi(e); + + if (passed_fd <= 2) + passed_fd = -1; } -#endif - pa_random_seed(); - - pa_log_set_ident("pulseaudio"); - + /* 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_all(passed_fd, -1); + 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; @@ -368,24 +492,60 @@ int main(int argc, char *argv[]) { goto finish; if (pa_cmdline_parse(conf, argc, argv, &d) < 0) { - pa_log(__FILE__": failed to parse command line."); + pa_log(_("Failed to parse command line.")); goto finish; } - pa_log_set_maximal_level(conf->log_level); - pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target, NULL); + pa_log_set_level(conf->log_level); + pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target); + 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(); + } - if (conf->high_priority && conf->cmd == PA_CMD_DAEMON) - pa_raise_priority(); + start_server = conf->local_server_type == PA_SERVER_TYPE_USER || (getuid() == 0 && conf->local_server_type == PA_SERVER_TYPE_SYSTEM); - pa_drop_caps(); + 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 - if (suid_root) - pa_drop_root(); + 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); @@ -393,6 +553,12 @@ int main(int argc, char *argv[]) { goto finish; case PA_CMD_DUMP_CONF: { + + if (d < argc) { + pa_log("Too many arguments.\n"); + goto finish; + } + s = pa_daemon_conf_dump(conf); fputs(s, stdout); pa_xfree(s); @@ -400,12 +566,34 @@ int main(int argc, char *argv[]) { goto finish; } + case PA_CMD_DUMP_RESAMPLE_METHODS: { + int i; + + if (d < argc) { + pa_log("Too many arguments.\n"); + 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.\n"); + goto finish; + } + printf(PACKAGE_NAME" "PACKAGE_VERSION"\n"); retval = 0; goto finish; @@ -413,10 +601,15 @@ int main(int argc, char *argv[]) { case PA_CMD_CHECK: { pid_t pid; - if (pa_pid_file_check_running(&pid) < 0) { - pa_log_info(__FILE__": daemon not running"); - } else { - pa_log_info(__FILE__": daemon running as PID %u", pid); + if (d < argc) { + pa_log("Too many arguments.\n"); + 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; } @@ -425,87 +618,253 @@ int main(int argc, char *argv[]) { } case PA_CMD_KILL: - if (pa_pid_file_kill(SIGINT, NULL) < 0) - pa_log(__FILE__": failed to kill daemon."); + if (d < argc) { + pa_log("Too many arguments.\n"); + 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.\n"); + goto finish; + } + + if (pa_shm_cleanup() >= 0) + retval = 0; + goto finish; - + default: - assert(conf->cmd == PA_CMD_DAEMON); + pa_assert(conf->cmd == PA_CMD_DAEMON || conf->cmd == PA_CMD_START); } - if (real_root && !conf->system_instance) { - pa_log_warn(__FILE__": This program is not intended to be run as root (unless --system is specified)."); - } else if (!real_root && conf->system_instance) { - pa_log(__FILE__": Root priviliges required."); + if (d < argc) { + pa_log("Too many arguments.\n"); 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; + pa_bool_t 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. */ + + 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; + } + if (conf->daemonize) { +#ifdef HAVE_FORK pid_t child; - int tty_fd; +#endif if (pa_stdio_acquire() < 0) { - pa_log(__FILE__": failed to acquire stdio."); + pa_log(_("Failed to acquire stdio.")); goto finish; } #ifdef HAVE_FORK if (pipe(daemon_pipe) < 0) { - pa_log(__FILE__": failed to create pipe."); + pa_log(_("pipe() failed: %s"), pa_cstrerror(errno)); goto finish; } - + if ((child = fork()) < 0) { - pa_log(__FILE__": fork() failed: %s", pa_cstrerror(errno)); + pa_log(_("fork() failed: %s"), pa_cstrerror(errno)); + pa_close_pipe(daemon_pipe); goto finish; } if (child != 0) { + ssize_t n; /* Father */ - close(daemon_pipe[1]); + pa_assert_se(pa_close(daemon_pipe[1]) == 0); daemon_pipe[1] = -1; - if (pa_loop_read(daemon_pipe[0], &retval, sizeof(retval), NULL) != sizeof(retval)) { - pa_log(__FILE__": read() failed: %s", pa_cstrerror(errno)); + 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(__FILE__": daemon startup failed."); + pa_log(_("Daemon startup failed.")); else - pa_log_info(__FILE__": daemon startup successful."); - + pa_log_info(_("Daemon startup successful.")); + goto finish; } - close(daemon_pipe[0]); + 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->auto_log_target) - pa_log_set_target(PA_LOG_SYSLOG, NULL); + pa_log_set_target(PA_LOG_SYSLOG); #ifdef HAVE_SETSID - setsid(); -#endif -#ifdef HAVE_SETPGID - setpgid(0,0); + if (setsid() < 0) { + pa_log(_("setsid() failed: %s"), pa_cstrerror(errno)); + goto finish; + } #endif -#ifndef OS_IS_WIN32 - close(0); - close(1); - close(2); +#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 */ - open("/dev/null", O_RDONLY); - open("/dev/null", O_WRONLY); - open("/dev/null", O_WRONLY); -#else - FreeConsole(); + 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 @@ -517,57 +876,174 @@ int main(int argc, char *argv[]) { #ifdef SIGTSTP signal(SIGTSTP, SIG_IGN); #endif - -#ifdef TIOCNOTTY - if ((tty_fd = open("/dev/tty", O_RDWR)) >= 0) { - ioctl(tty_fd, TIOCNOTTY, (char*) 0); - close(tty_fd); - } -#endif + + pa_nullify_stdfds(); } - chdir("/"); + pa_set_env_and_record("PULSE_INTERNAL", "1"); + pa_assert_se(chdir("/") == 0); umask(0022); - - if (conf->system_instance) { + +#ifdef HAVE_SYS_RESOURCE_H + set_all_rlimits(conf); +#endif + pa_rtclock_hrtimer_enable(); + + pa_raise_priority(conf->nice_level); + + if (conf->system_instance) if (change_user() < 0) goto finish; - } else if (create_runtime_dir() < 0) + + 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 host: %s"), CANONICAL_HOST); + pa_log_debug(_("Compilation CFLAGS: %s"), PA_CFLAGS); + + 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 %lu bytes"), (unsigned long) 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 __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 note that you most likely shouldn't be doing that.\n" + "If you do it nonetheless then it's your own fault if things don't work as expected.\n" + "Please read http://pulseaudio.org/wiki/WhatIsWrongWithSystemMode for an explanation why system mode is usually a bad idea.")); + if (conf->use_pid_file) { - if (pa_pid_file_create() < 0) { - pa_log(__FILE__": pa_pid_file_create() failed."); -#ifdef HAVE_FORK - if (conf->daemonize) - pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL); -#endif + 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 = 1; + valid_pid_file = TRUE; } -#ifdef HAVE_SYS_RESOURCE_H - set_all_rlimits(conf); -#endif - -#ifdef SIGPIPE - signal(SIGPIPE, SIG_IGN); + pa_disable_sigpipe(); + + if (pa_rtclock_hrtimer()) + pa_log_info(_("Fresh high-resolution timers available! Bon appetit!")); + else + pa_log_info(_("Dude, your kernel stinks! The chef's recommendation today is Linux with high-resolution timers enabled!")); + + if (conf->lock_memory) { +#ifdef HAVE_SYS_MMAN_H + 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()); - mainloop = pa_mainloop_new(); - assert(mainloop); + if (!(c = pa_core_new(pa_mainloop_get_api(mainloop), !conf->disable_shm, conf->shm_size))) { + pa_log(_("pa_core_new() failed.")); + goto finish; + } - c = pa_core_new(pa_mainloop_get_api(mainloop)); - assert(c); - c->is_system_instance = !!conf->system_instance; + c->default_sample_spec = conf->default_sample_spec; + 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->sync_volume_safety_margin_usec = conf->sync_volume_safety_margin_usec; + c->sync_volume_extra_delay_usec = conf->sync_volume_extra_delay_usec; + 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->disable_remixing = !!conf->disable_remixing; + c->disable_lfe_remixing = !!conf->disable_lfe_remixing; + c->sync_volume = !!conf->sync_volume; + c->running_as_daemon = !!conf->daemonize; + c->disallow_exit = conf->disallow_exit; + c->flat_volumes = conf->flat_volumes; +#ifdef HAVE_DBUS + c->server_type = conf->local_server_type; +#endif + + c->cpu_info.cpu_type = PA_CPU_UNDEFINED; + if (!getenv("PULSE_NO_SIMD")) { + if (pa_cpu_init_x86(&(c->cpu_info.flags.x86))) + c->cpu_info.cpu_type = PA_CPU_X86; + if (pa_cpu_init_arm(&(c->cpu_info.flags.arm))) + c->cpu_info.cpu_type = PA_CPU_ARM; + pa_cpu_init_orc(c->cpu_info); + } - r = pa_signal_init(pa_mainloop_get_api(mainloop)); - assert(r == 0); + 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 @@ -577,95 +1053,152 @@ int main(int argc, char *argv[]) { #ifdef SIGHUP pa_signal_new(SIGHUP, signal_callback, c); #endif - + #ifdef OS_IS_WIN32 - timer = pa_mainloop_get_api(mainloop)->time_new( - pa_mainloop_get_api(mainloop), pa_gettimeofday(&tv), message_cb, NULL); - assert(timer); + win32_timer = pa_mainloop_get_api(mainloop)->time_new(pa_mainloop_get_api(mainloop), pa_gettimeofday(&win32_tv), message_cb, NULL); #endif - if (conf->daemonize) - c->running_as_daemon = 1; + if (!conf->no_cpu_limit) + pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0); - oil_init(); - - r = pa_cpu_limit_init(pa_mainloop_get_api(mainloop)); - assert(r == 0); - buf = pa_strbuf_new(); - if (conf->default_script_file) - r = pa_cli_command_execute_file(c, conf->default_script_file, buf, &conf->fail); - if (r >= 0) - r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail); - pa_log_error("%s", s = pa_strbuf_tostring_free(buf)); - pa_xfree(s); - - if (r < 0 && conf->fail) { - pa_log(__FILE__": failed to initialize daemon."); -#ifdef HAVE_FORK - if (conf->daemonize) - pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL); -#endif - } else if (!c->modules || pa_idxset_size(c->modules) == 0) { - pa_log(__FILE__": daemon startup without any loaded modules, refusing to work."); -#ifdef HAVE_FORK - if (conf->daemonize) - pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL); +#ifdef HAVE_DBUS + if (start_server) { #endif + 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); + } + } + + if (r >= 0) + r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail); + + pa_log_error("%s", s = pa_strbuf_tostring_free(buf)); + pa_xfree(s); + + if (r < 0 && conf->fail) { + pa_log(_("Failed to initialize daemon.")); + 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))) + goto finish; + 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"))) + goto finish; +#endif - retval = 0; #ifdef HAVE_FORK - if (conf->daemonize) - pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL); + 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 - c->disallow_module_loading = conf->disallow_module_loading; - c->exit_idle_time = conf->exit_idle_time; - c->module_idle_time = conf->module_idle_time; - c->scache_idle_time = conf->scache_idle_time; - c->resample_method = conf->resample_method; + pa_log_info(_("Daemon startup complete.")); - if (c->default_sink_name && - pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, 1) == NULL) { - pa_log_error("%s : Fatal error. Default sink name (%s) does not exist in name register.", __FILE__, c->default_sink_name); - retval = 1; - } else { - pa_log_info(__FILE__": Daemon startup complete."); - if (pa_mainloop_run(mainloop, &retval) < 0) - retval = 1; - pa_log_info(__FILE__": Daemon shutdown initiated."); - } + retval = 0; + if (pa_mainloop_run(mainloop, &retval) < 0) + goto finish; + + pa_log_info(_("Daemon shutdown initiated.")); + +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); } #ifdef OS_IS_WIN32 - pa_mainloop_get_api(mainloop)->time_free(timer); + if (mainloop && win32_timer) + pa_mainloop_get_api(mainloop)->time_free(win32_timer); #endif - pa_core_free(c); + if (c) { + pa_core_unref(c); + pa_log_info(_("Daemon terminated.")); + } + + if (!conf->no_cpu_limit) + pa_cpu_limit_done(); - pa_cpu_limit_done(); pa_signal_done(); - pa_mainloop_free(mainloop); - - pa_log_info(__FILE__": Daemon terminated."); - -finish: + +#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(); - - close_pipe(daemon_pipe); + + /* This has no real purpose except making things valgrind-clean */ + pa_unset_env_recorded(); #ifdef OS_IS_WIN32 WSACleanup(); #endif - lt_dlexit(); - + if (ltdl_init) + pa_ltdl_done(); + +#ifdef HAVE_DBUS + dbus_shutdown(); +#endif + return retval; } |
