static pa_strlist *prepend_per_user(pa_strlist *l) {
    char *ufn;

    /* The per-user instance */
    if ((ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET))) {
        l = pa_strlist_prepend(l, ufn);
        pa_xfree(ufn);
    }

    return l;
}
Beispiel #2
0
static void unref(pa_bool_t after_fork) {

    pa_assert(n_ref > 0);
    pa_assert(pipe_fd[0] >= 0);
    pa_assert(pipe_fd[1] >= 0);
    pa_assert(lock_fd_mutex);

    n_ref--;

    if (n_ref > 0)
        return;

    if (thread) {
        pa_thread_free(thread);
        thread = NULL;
    }

    pa_mutex_lock(lock_fd_mutex);

    pa_assert(state != STATE_TAKEN);

    if (state == STATE_OWNING) {

        pa_assert(lock_fd >= 0);

        if (after_fork)
            pa_close(lock_fd);
        else {
            char *lf;

            if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK)))
                pa_log_warn(_("Cannot access autospawn lock."));

            pa_unlock_lockfile(lf, lock_fd);
            pa_xfree(lf);
        }
    }

    lock_fd = -1;
    state = STATE_IDLE;

    pa_mutex_unlock(lock_fd_mutex);

    pa_mutex_free(lock_fd_mutex);
    lock_fd_mutex = NULL;

    pa_close(pipe_fd[0]);
    pa_close(pipe_fd[1]);
    pipe_fd[0] = pipe_fd[1] = -1;
}
Beispiel #3
0
static void thread_func(void *u) {
    int fd;
    char *lf;
    sigset_t fullset;

    /* No signals in this thread please */
    sigfillset(&fullset);
    pthread_sigmask(SIG_BLOCK, &fullset, NULL);

    if (!(lf = pa_runtime_path(AUTOSPAWN_LOCK))) {
        pa_log_warn(_("Cannot access autospawn lock."));
        goto fail;
    }

    if ((fd = pa_lock_lockfile(lf)) < 0)
        goto fail;

    pa_mutex_lock(lock_fd_mutex);
    pa_assert(state == STATE_IDLE);
    lock_fd = fd;
    state = STATE_OWNING;
    pa_mutex_unlock(lock_fd_mutex);

    goto finish;

fail:
    pa_mutex_lock(lock_fd_mutex);
    pa_assert(state == STATE_IDLE);
    state = STATE_FAILED;
    pa_mutex_unlock(lock_fd_mutex);

finish:
    pa_xfree(lf);

    ping();
}
int pa__init(pa_module *m) {
    struct userdata *u;
    struct stat st;
    pa_sample_spec ss;
    pa_channel_map map;
    pa_modargs *ma;
    struct pollfd *pollfd;
    pa_source_new_data data;

    pa_assert(m);

    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
        pa_log("Failed to parse module arguments.");
        goto fail;
    }

    ss = m->core->default_sample_spec;
    map = m->core->default_channel_map;
    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
        pa_log("Invalid sample format specification or channel map");
        goto fail;
    }

    m->userdata = u = pa_xnew0(struct userdata, 1);
    u->core = m->core;
    u->module = m;
    pa_memchunk_reset(&u->memchunk);
    u->rtpoll = pa_rtpoll_new();

    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
        pa_log("pa_thread_mq_init() failed.");
        goto fail;
    }

    u->filename = pa_runtime_path(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME));

    if (mkfifo(u->filename, 0666) < 0) {
        pa_log("mkfifo('%s'): %s", u->filename, pa_cstrerror(errno));
        goto fail;
    }
    if ((u->fd = pa_open_cloexec(u->filename, O_RDWR, 0)) < 0) {
        pa_log("open('%s'): %s", u->filename, pa_cstrerror(errno));
        goto fail;
    }

    pa_make_fd_nonblock(u->fd);

    if (fstat(u->fd, &st) < 0) {
        pa_log("fstat('%s'): %s", u->filename, pa_cstrerror(errno));
        goto fail;
    }

    if (!S_ISFIFO(st.st_mode)) {
        pa_log("'%s' is not a FIFO.", u->filename);
        goto fail;
    }

    pa_source_new_data_init(&data);
    data.driver = __FILE__;
    data.module = m;
    pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->filename);
    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Unix FIFO source %s", u->filename);
    pa_source_new_data_set_sample_spec(&data, &ss);
    pa_source_new_data_set_channel_map(&data, &map);

    if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
        pa_log("Invalid properties");
        pa_source_new_data_done(&data);
        goto fail;
    }

    u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY);
    pa_source_new_data_done(&data);

    if (!u->source) {
        pa_log("Failed to create source.");
        goto fail;
    }

    u->source->parent.process_msg = source_process_msg;
    u->source->userdata = u;

    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
    pa_source_set_rtpoll(u->source, u->rtpoll);
    pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(pa_pipe_buf(u->fd), &u->source->sample_spec));

    u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
    pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
    pollfd->fd = u->fd;
    pollfd->events = pollfd->revents = 0;

    if (!(u->thread = pa_thread_new("pipe-source", thread_func, u))) {
        pa_log("Failed to create thread.");
        goto fail;
    }

    pa_source_put(u->source);

    pa_modargs_free(ma);

    return 0;

fail:
    if (ma)
        pa_modargs_free(ma);

    pa__done(m);

    return -1;
}
Beispiel #5
0
/* Create a new PID file for the current process. */
int pa_pid_file_create(const char *procname) {
    int fd = -1;
    int ret = -1;
    char t[20];
    pid_t pid;
    size_t l;
    char *fn;

#ifdef OS_IS_WIN32
    HANDLE process;
#endif

    if (!(fn = pa_runtime_path("pid")))
        goto fail;

    if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0)
        goto fail;

    if ((pid = read_pid(fn, fd)) == (pid_t) -1)
        pa_log_warn("Corrupt PID file, overwriting.");
    else if (pid > 0) {
        int ours = 1;

#ifdef OS_IS_WIN32
        if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)) != NULL) {
            CloseHandle(process);
#else
        if (kill(pid, 0) >= 0 || errno != ESRCH) {
#endif

            if (procname)
                if ((ours = proc_name_ours(pid, procname)) < 0) {
                    pa_log_warn("Could not check to see if pid %lu is a pulseaudio process. "
                                "Asssuming it is and the daemon is already running.", (unsigned long) pid);
                    goto fail;
                }

            if (ours) {
                pa_log("Daemon already running.");
                ret = 1;
                goto fail;
            }
        }

        pa_log_warn("Stale PID file, overwriting.");
    }

    /* Overwrite the current PID file */
    if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1 || ftruncate(fd, (off_t) 0) < 0) {
        pa_log("Failed to truncate PID file '%s': %s", fn, pa_cstrerror(errno));
        goto fail;
    }

    pa_snprintf(t, sizeof(t), "%lu\n", (unsigned long) getpid());
    l = strlen(t);

    if (pa_loop_write(fd, t, l, NULL) != (ssize_t) l) {
        pa_log("Failed to write PID file.");
        goto fail;
    }

    ret = 0;

fail:
    if (fd >= 0) {
        pa_lock_fd(fd, 0);

        if (pa_close(fd) < 0) {
            pa_log("Failed to close PID file '%s': %s", fn, pa_cstrerror(errno));
            ret = -1;
        }
    }

    pa_xfree(fn);

    return ret;
}

/* Remove the PID file, if it is ours */
int pa_pid_file_remove(void) {
    int fd = -1;
    char *fn;
    int ret = -1;
    pid_t pid;

    if (!(fn = pa_runtime_path("pid")))
        goto fail;

    if ((fd = open_pid_file(fn, O_RDWR)) < 0) {
        pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno));
        goto fail;
    }

    if ((pid = read_pid(fn, fd)) == (pid_t) -1)
        goto fail;

    if (pid != getpid()) {
        pa_log("PID file '%s' not mine!", fn);
        goto fail;
    }

    if (ftruncate(fd, (off_t) 0) < 0) {
        pa_log_warn("Failed to truncate PID file '%s': %s", fn, pa_cstrerror(errno));
        goto fail;
    }

#ifdef OS_IS_WIN32
    pa_lock_fd(fd, 0);
    pa_close(fd);
    fd = -1;
#endif

    if (unlink(fn) < 0) {
        pa_log_warn("Failed to remove PID file '%s': %s", fn, pa_cstrerror(errno));
        goto fail;
    }

    ret = 0;

fail:

    if (fd >= 0) {
        pa_lock_fd(fd, 0);

        if (pa_close(fd) < 0) {
            pa_log_warn("Failed to close PID file '%s': %s", fn, pa_cstrerror(errno));
            ret = -1;
        }
    }

    pa_xfree(fn);

    return ret;
}
int pa__init(pa_module*m) {
    pa_modargs *ma = NULL;
    struct userdata *u = NULL;

#if defined(USE_TCP_SOCKETS)
    uint32_t port = IPV4_PORT;
    pa_bool_t port_fallback = TRUE;
    const char *listen_on;
#else
    int r;
#endif

#if defined(USE_PROTOCOL_NATIVE) || defined(USE_PROTOCOL_HTTP)
    char t[256];
#endif

    pa_assert(m);

    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
        pa_log("Failed to parse module arguments");
        goto fail;
    }

    m->userdata = u = pa_xnew0(struct userdata, 1);
    u->module = m;

#if defined(USE_PROTOCOL_SIMPLE)
    u->simple_protocol = pa_simple_protocol_get(m->core);

    u->simple_options = pa_simple_options_new();
    if (pa_simple_options_parse(u->simple_options, m->core, ma) < 0)
        goto fail;
    u->simple_options->module = m;
#elif defined(USE_PROTOCOL_CLI)
    u->cli_protocol = pa_cli_protocol_get(m->core);
#elif defined(USE_PROTOCOL_HTTP)
    u->http_protocol = pa_http_protocol_get(m->core);
#elif defined(USE_PROTOCOL_NATIVE)
    u->native_protocol = pa_native_protocol_get(m->core);

    u->native_options = pa_native_options_new();
    if (pa_native_options_parse(u->native_options, m->core, ma) < 0)
        goto fail;
    u->native_options->module = m;
#else
    u->esound_protocol = pa_esound_protocol_get(m->core);

    u->esound_options = pa_esound_options_new();
    if (pa_esound_options_parse(u->esound_options, m->core, ma) < 0)
        goto fail;
    u->esound_options->module = m;
#endif

#if defined(USE_TCP_SOCKETS)

    if (pa_in_system_mode() || pa_modargs_get_value(ma, "port", NULL))
        port_fallback = FALSE;

    if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port < 1 || port > 0xFFFF) {
        pa_log("port= expects a numerical argument between 1 and 65535.");
        goto fail;
    }

    listen_on = pa_modargs_get_value(ma, "listen", NULL);

    if (listen_on) {
#  ifdef HAVE_IPV6
        u->socket_server_ipv6 = pa_socket_server_new_ipv6_string(m->core->mainloop, listen_on, (uint16_t) port, port_fallback, TCPWRAP_SERVICE);
#  endif
        u->socket_server_ipv4 = pa_socket_server_new_ipv4_string(m->core->mainloop, listen_on, (uint16_t) port, port_fallback, TCPWRAP_SERVICE);
    } else {
#  ifdef HAVE_IPV6
        u->socket_server_ipv6 = pa_socket_server_new_ipv6_any(m->core->mainloop, (uint16_t) port, port_fallback, TCPWRAP_SERVICE);
#  endif
        u->socket_server_ipv4 = pa_socket_server_new_ipv4_any(m->core->mainloop, (uint16_t) port, port_fallback, TCPWRAP_SERVICE);
    }

#  ifdef HAVE_IPV6
    if (!u->socket_server_ipv4 && !u->socket_server_ipv6)
#  else
    if (!u->socket_server_ipv4)
#  endif
        goto fail;

    if (u->socket_server_ipv4)
        pa_socket_server_set_callback(u->socket_server_ipv4, socket_server_on_connection_cb, u);
#  ifdef HAVE_IPV6
    if (u->socket_server_ipv6)
        pa_socket_server_set_callback(u->socket_server_ipv6, socket_server_on_connection_cb, u);
#  endif

#else

#  if defined(USE_PROTOCOL_ESOUND)

#    if defined(USE_PER_USER_ESOUND_SOCKET)
    u->socket_path = pa_sprintf_malloc("/tmp/.esd-%lu/socket", (unsigned long) getuid());
#    else
    u->socket_path = pa_xstrdup("/tmp/.esd/socket");
#    endif

    /* This socket doesn't reside in our own runtime dir but in
     * /tmp/.esd/, hence we have to create the dir first */

    if (pa_make_secure_parent_dir(u->socket_path, pa_in_system_mode() ? 0755U : 0700U, (uid_t)-1, (gid_t)-1) < 0) {
        pa_log("Failed to create socket directory '%s': %s\n", u->socket_path, pa_cstrerror(errno));
        goto fail;
    }

#  else
    if (!(u->socket_path = pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET)))) {
        pa_log("Failed to generate socket path.");
        goto fail;
    }
#  endif

    if ((r = pa_unix_socket_remove_stale(u->socket_path)) < 0) {
        pa_log("Failed to remove stale UNIX socket '%s': %s", u->socket_path, pa_cstrerror(errno));
        goto fail;
    } else if (r > 0)
        pa_log_info("Removed stale UNIX socket '%s'.", u->socket_path);

    if (!(u->socket_server_unix = pa_socket_server_new_unix(m->core->mainloop, u->socket_path)))
        goto fail;

    pa_socket_server_set_callback(u->socket_server_unix, socket_server_on_connection_cb, u);

#endif

#if defined(USE_PROTOCOL_NATIVE)
#  if defined(USE_TCP_SOCKETS)
    if (u->socket_server_ipv4)
        if (pa_socket_server_get_address(u->socket_server_ipv4, t, sizeof(t)))
            pa_native_protocol_add_server_string(u->native_protocol, t);

#    ifdef HAVE_IPV6
    if (u->socket_server_ipv6)
        if (pa_socket_server_get_address(u->socket_server_ipv6, t, sizeof(t)))
            pa_native_protocol_add_server_string(u->native_protocol, t);
#    endif
#  else
    if (pa_socket_server_get_address(u->socket_server_unix, t, sizeof(t)))
        pa_native_protocol_add_server_string(u->native_protocol, t);

#  endif
#endif

#if defined(USE_PROTOCOL_HTTP)
#if defined(USE_TCP_SOCKETS)
    if (u->socket_server_ipv4)
        if (pa_socket_server_get_address(u->socket_server_ipv4, t, sizeof(t)))
            pa_http_protocol_add_server_string(u->http_protocol, t);

#ifdef HAVE_IPV6
    if (u->socket_server_ipv6)
        if (pa_socket_server_get_address(u->socket_server_ipv6, t, sizeof(t)))
            pa_http_protocol_add_server_string(u->http_protocol, t);
#endif /* HAVE_IPV6 */
#else /* USE_TCP_SOCKETS */
    if (pa_socket_server_get_address(u->socket_server_unix, t, sizeof(t)))
        pa_http_protocol_add_server_string(u->http_protocol, t);

#endif /* USE_TCP_SOCKETS */
#endif /* USE_PROTOCOL_HTTP */

    if (ma)
        pa_modargs_free(ma);

    return 0;

fail:

    if (ma)
        pa_modargs_free(ma);

    pa__done(m);

    return -1;
}
Beispiel #7
0
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 passed_fd = -1;
    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(__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

    if ((e = getenv("PULSE_PASSED_FD"))) {
        passed_fd = atoi(e);

        if (passed_fd <= 2)
            passed_fd = -1;
    }

    /* 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;

    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.\n");
                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.\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;

        case PA_CMD_CHECK: {
            pid_t 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;
            }

            goto finish;

        }
        case PA_CMD_KILL:

            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:
            pa_assert(conf->cmd == PA_CMD_DAEMON || conf->cmd == PA_CMD_START);
    }

    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;
        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. */

        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;
#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_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);
        }
Beispiel #8
0
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 *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

    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(__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

    if ((e = getenv("PULSE_PASSED_FD"))) {
        passed_fd = atoi(e);

        if (passed_fd <= 2)
            passed_fd = -1;
    }

    /* 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;

    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;
    }

    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();
    }

    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.\n");
                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.\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;

        case PA_CMD_CHECK: {
            pid_t 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;
            }

            goto finish;

        }
        case PA_CMD_KILL:

            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:
            pa_assert(conf->cmd == PA_CMD_DAEMON || conf->cmd == PA_CMD_START);
    }

    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;
#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->auto_log_target)
            pa_log_set_target(PA_LOG_SYSLOG);

#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(0022);

#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;

    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) {
        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(_("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());

    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->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->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->deferred_volume = !!conf->deferred_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);
    }

    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

#ifdef OS_IS_WIN32
    win32_timer = pa_mainloop_get_api(mainloop)->time_new(pa_mainloop_get_api(mainloop), pa_gettimeofday(&win32_tv), message_cb, NULL);
#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
        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))) {
            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."));

    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
    if (mainloop && win32_timer)
        pa_mainloop_get_api(mainloop)->time_free(win32_timer);
#endif

    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;
}
Beispiel #9
0
int main(int argc, char*argv[]) {
    pid_t pid;
    int fd = -1;
    int ret = 1, i;
    struct sockaddr_un sa;
    char *ibuf = NULL;
    char *obuf = NULL;
    size_t buf_size, ibuf_size, ibuf_index, ibuf_length, obuf_size, obuf_index, obuf_length;
    char *cli;
    bool ibuf_eof, obuf_eof, ibuf_closed, obuf_closed;
    struct pollfd pollfd[3];
    struct pollfd *watch_socket, *watch_stdin, *watch_stdout;
    int stdin_type = 0, stdout_type = 0, fd_type = 0;

    char *bn = NULL;
    int c;

    static const struct option long_options[] = {
        {"version",     0, NULL, ARG_VERSION},
        {"help",        0, NULL, 'h'},
        {NULL,          0, NULL, 0}
    };

    setlocale(LC_ALL, "");
#ifdef ENABLE_NLS
    bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
#endif

    bn = pa_path_get_filename(argv[0]);

    while ((c = getopt_long(argc, argv, "h", long_options, NULL)) != -1) {
        switch (c) {
            case 'h' :
                help(bn);
                ret = 0;
                goto quit;
            case ARG_VERSION:
                printf(_("pacmd %s\n"
                         "Compiled with libpulse %s\n"
                         "Linked with libpulse %s\n"),
                       PACKAGE_VERSION,
                       pa_get_headers_version(),
                       pa_get_library_version());
                ret = 0;
                goto quit;
            default:
                goto quit;
        }
    }

    if (pa_pid_file_check_running(&pid, "pulseaudio") < 0) {
        pa_log(_("No PulseAudio daemon running, or not running as session daemon."));
        goto quit;
    }

    if ((fd = pa_socket_cloexec(PF_UNIX, SOCK_STREAM, 0)) < 0) {
        pa_log(_("socket(PF_UNIX, SOCK_STREAM, 0): %s"), strerror(errno));
        goto quit;
    }

    pa_zero(sa);
    sa.sun_family = AF_UNIX;

    if (!(cli = pa_runtime_path("cli")))
        goto quit;

    pa_strlcpy(sa.sun_path, cli, sizeof(sa.sun_path));
    pa_xfree(cli);

    for (i = 0; i < 5; i++) {
        int r;

        if ((r = connect(fd, (struct sockaddr*) &sa, sizeof(sa))) < 0 && (errno != ECONNREFUSED && errno != ENOENT)) {
            pa_log(_("connect(): %s"), strerror(errno));
            goto quit;
        }

        if (r >= 0)
            break;

        if (pa_pid_file_kill(SIGUSR2, NULL, "pulseaudio") < 0) {
            pa_log(_("Failed to kill PulseAudio daemon."));
            goto quit;
        }

        pa_msleep(300);
    }

    if (i >= 5) {
        pa_log(_("Daemon not responding."));
        goto quit;
    }

    buf_size = pa_pipe_buf(fd);
    ibuf_size = PA_MIN(buf_size, pa_pipe_buf(STDIN_FILENO));
    ibuf = pa_xmalloc(ibuf_size);
    obuf_size = PA_MIN(buf_size, pa_pipe_buf(STDOUT_FILENO));
    obuf = pa_xmalloc(obuf_size);
    ibuf_index = ibuf_length = obuf_index = obuf_length = 0;
    ibuf_eof = obuf_eof = ibuf_closed = obuf_closed = false;

    if (argc > 1) {
        for (i = 1; i < argc; i++) {
            size_t k;

            k = PA_MIN(ibuf_size - ibuf_length, strlen(argv[i]));
            memcpy(ibuf + ibuf_length, argv[i], k);
            ibuf_length += k;

            if (ibuf_length < ibuf_size) {
                ibuf[ibuf_length] = i < argc-1 ? ' ' : '\n';
                ibuf_length++;
            }
        }

        ibuf_eof = true;
    }

    if (!ibuf_eof && isatty(STDIN_FILENO)) {
        /* send hello to enable interactive mode (welcome message, prompt) */
        if (pa_write(fd, "hello\n", 6, &fd_type) < 0) {
            pa_log(_("write(): %s"), strerror(errno));
            goto quit;
        }
    }

    for (;;) {
        struct pollfd *p;

        if (ibuf_eof &&
            obuf_eof &&
            ibuf_length <= 0 &&
            obuf_length <= 0)
            break;

        if (ibuf_length <= 0 && ibuf_eof && !ibuf_closed) {
            shutdown(fd, SHUT_WR);
            ibuf_closed = true;
        }

        if (obuf_length <= 0 && obuf_eof && !obuf_closed) {
            shutdown(fd, SHUT_RD);
            obuf_closed = true;
        }

        pa_zero(pollfd);

        p = pollfd;

        if (ibuf_length > 0 || (!obuf_eof && obuf_length <= 0)) {
            watch_socket = p++;
            watch_socket->fd = fd;
            watch_socket->events =
                (ibuf_length > 0 ? POLLOUT : 0) |
                (!obuf_eof && obuf_length <= 0 ? POLLIN : 0);
        } else
            watch_socket = NULL;

        if (!ibuf_eof && ibuf_length <= 0) {
            watch_stdin = p++;
            watch_stdin->fd = STDIN_FILENO;
            watch_stdin->events = POLLIN;
        } else
            watch_stdin = NULL;

        if (obuf_length > 0) {
            watch_stdout = p++;
            watch_stdout->fd = STDOUT_FILENO;
            watch_stdout->events = POLLOUT;
        } else
            watch_stdout = NULL;

        if (pa_poll(pollfd, p-pollfd, -1) < 0) {

            if (errno == EINTR)
                continue;

            pa_log(_("poll(): %s"), strerror(errno));
            goto quit;
        }

        if (watch_stdin) {
            if (watch_stdin->revents & POLLIN) {
                ssize_t r;
                pa_assert(ibuf_length <= 0);

                if ((r = pa_read(STDIN_FILENO, ibuf, ibuf_size, &stdin_type)) <= 0) {
                    if (r < 0) {
                        pa_log(_("read(): %s"), strerror(errno));
                        goto quit;
                    }

                    ibuf_eof = true;
                } else {
                    ibuf_length = (size_t) r;
                    ibuf_index = 0;
                }
            } else if (watch_stdin->revents & POLLHUP)
                ibuf_eof = true;
        }

        if (watch_socket) {
            if (watch_socket->revents & POLLIN) {
                ssize_t r;
                pa_assert(obuf_length <= 0);

                if ((r = pa_read(fd, obuf, obuf_size, &fd_type)) <= 0) {
                    if (r < 0) {
                        pa_log(_("read(): %s"), strerror(errno));
                        goto quit;
                    }

                    obuf_eof = true;
                } else {
                    obuf_length = (size_t) r;
                    obuf_index = 0;
                }
            } else if (watch_socket->revents & POLLHUP)
                obuf_eof = true;
        }

        if (watch_stdout) {
            if (watch_stdout->revents & POLLHUP) {
                obuf_eof = true;
                obuf_length = 0;
            } else if (watch_stdout->revents & POLLOUT) {
                ssize_t r;
                pa_assert(obuf_length > 0);

                if ((r = pa_write(STDOUT_FILENO, obuf + obuf_index, obuf_length, &stdout_type)) < 0) {
                    pa_log(_("write(): %s"), strerror(errno));
                    goto quit;
                }

                obuf_length -= (size_t) r;
                obuf_index += obuf_index;
            }
        }

        if (watch_socket) {
            if (watch_socket->revents & POLLHUP) {
                ibuf_eof = true;
                ibuf_length = 0;
            } if (watch_socket->revents & POLLOUT) {
                ssize_t r;
                pa_assert(ibuf_length > 0);

                if ((r = pa_write(fd, ibuf + ibuf_index, ibuf_length, &fd_type)) < 0) {
                    pa_log(_("write(): %s"), strerror(errno));
                    goto quit;
                }

                ibuf_length -= (size_t) r;
                ibuf_index += obuf_index;
            }
        }
    }

    ret = 0;

quit:
    if (fd >= 0)
        pa_close(fd);

    pa_xfree(obuf);
    pa_xfree(ibuf);

    return ret;
}