]> code.delx.au - pulseaudio/commitdiff
Merge branch 'master' into dbus-work
authorTanu Kaskinen <tanuk@iki.fi>
Mon, 29 Jun 2009 15:35:06 +0000 (18:35 +0300)
committerTanu Kaskinen <tanuk@iki.fi>
Mon, 29 Jun 2009 15:35:06 +0000 (18:35 +0300)
Conflicts:
src/daemon/daemon-conf.c
src/daemon/daemon-conf.h
src/daemon/main.c
src/pulsecore/dbus-util.h

1  2 
configure.ac
src/Makefile.am
src/daemon/daemon-conf.c
src/daemon/daemon-conf.h
src/daemon/daemon.conf.in
src/daemon/default.pa.in
src/daemon/main.c
src/modules/module-dbus-protocol.c
src/pulsecore/core.h
src/pulsecore/dbus-util.c
src/pulsecore/dbus-util.h

diff --cc configure.ac
index a3f9f0ef62ce987078eb6f12466767ab486cfbcf,d2d80157b2221a394ecbf32175ad074bfdf21b80..185f13bbab6f5a2780749c27055dd5a44c04f4a3
@@@ -22,8 -22,8 +22,7 @@@
  
  AC_PREREQ(2.63)
  
--AC_INIT([pulseaudio], m4_esyscmd([./git-version-gen .tarball-version]),
--      [mzchyfrnhqvb (at) 0pointer (dot) net])
++AC_INIT([pulseaudio],[m4_esyscmd(./git-version-gen .tarball-version)],[mzchyfrnhqvb (at) 0pointer (dot) net])
  AC_CONFIG_SRCDIR([src/daemon/main.c])
  AC_CONFIG_MACRO_DIR([m4])
  AC_CONFIG_HEADERS([config.h])
diff --cc src/Makefile.am
index d28ffa1e30bb3578e7b64a6e81524e0deffed2a5,ae90ae8f70c34dd0193e4843004e92694d4b58a6..b1bc49766b95c8f541d4961dae78183ebe5bcdd7
@@@ -796,10 -816,8 +817,9 @@@ libpulsecore_@PA_MAJORMINORMICRO@_la_SO
                pulsecore/object.c pulsecore/object.h \
                pulsecore/play-memblockq.c pulsecore/play-memblockq.h \
                pulsecore/play-memchunk.c pulsecore/play-memchunk.h \
 +              pulsecore/protocol-dbus.h \
                pulsecore/resampler.c pulsecore/resampler.h \
                pulsecore/rtpoll.c pulsecore/rtpoll.h \
-               pulsecore/rtsig.c pulsecore/rtsig.h \
                pulsecore/sample-util.c pulsecore/sample-util.h \
                pulsecore/sconv-s16be.c pulsecore/sconv-s16be.h \
                pulsecore/sconv-s16le.c pulsecore/sconv-s16le.h \
@@@ -1074,10 -1117,14 +1121,15 @@@ modlibexec_LTLIBRARIES += 
                module-hal-detect.la
  endif
  
+ if HAVE_UDEV
+ modlibexec_LTLIBRARIES += \
+               module-udev-detect.la
+ endif
  if HAVE_DBUS
  modlibexec_LTLIBRARIES += \
 -              module-rygel-media-server.la
 +              module-rygel-media-server.la \
 +              module-dbus-protocol.la
  endif
  
  if HAVE_BLUEZ
index e6fa8c64643f3843096c317a6c5401a50abc5655,9010f2f6528d085e6df7fb450bbfc5422f6660ca..ace460edc05b6357dd580a0d6ab25c3884a5ead2
@@@ -83,11 -83,9 +83,12 @@@ static const pa_daemon_conf default_con
      .config_file = NULL,
      .use_pid_file = TRUE,
      .system_instance = FALSE,
 +#ifdef HAVE_DBUS
 +    .local_server_type = PA_SERVER_TYPE_UNSET, /* The actual default is _USER, but we have to detect when the user doesn't specify this option. */
 +#endif
      .no_cpu_limit = FALSE,
      .disable_shm = FALSE,
+     .lock_memory = FALSE,
      .default_n_fragments = 4,
      .default_fragment_size_msec = 25,
      .default_sample_spec = { .format = PA_SAMPLE_S16NE, .rate = 44100, .channels = 2 },
@@@ -633,23 -597,14 +635,22 @@@ FILE *pa_daemon_conf_open_default_scrip
      return f;
  }
  
- static const char* const log_level_to_string[] = {
-     [PA_LOG_DEBUG] = "debug",
-     [PA_LOG_INFO] = "info",
-     [PA_LOG_NOTICE] = "notice",
-     [PA_LOG_WARN] = "warning",
-     [PA_LOG_ERROR] = "error"
- };
- static const char* const server_type_to_string[] = {
-     [PA_SERVER_TYPE_UNSET] = "!!UNSET!!",
-     [PA_SERVER_TYPE_USER] = "user",
-     [PA_SERVER_TYPE_SYSTEM] = "system",
-     [PA_SERVER_TYPE_NONE] = "none"
- };
+ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
+     static const char* const log_level_to_string[] = {
+         [PA_LOG_DEBUG] = "debug",
+         [PA_LOG_INFO] = "info",
+         [PA_LOG_NOTICE] = "notice",
+         [PA_LOG_WARN] = "warning",
+         [PA_LOG_ERROR] = "error"
+     };
 +
- char *pa_daemon_conf_dump(pa_daemon_conf *c) {
++    static const char* const server_type_to_string[] = {
++        [PA_SERVER_TYPE_UNSET] = "!!UNSET!!",
++        [PA_SERVER_TYPE_USER] = "user",
++        [PA_SERVER_TYPE_SYSTEM] = "system",
++        [PA_SERVER_TYPE_NONE] = "none"
++    };
 +
      pa_strbuf *s;
      char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
  
index c4f78cbef4415e5ee4ae57936472fe4337275be3,dd69e048f4f2de95fdda03ec754f7fbf2aec483b..41c3c4b7d047d66996ed65a3f06509770e051e72
@@@ -74,8 -73,8 +74,9 @@@ typedef struct pa_daemon_conf 
          disallow_exit,
          log_meta,
          log_time,
-         flat_volumes;
+         flat_volumes,
+         lock_memory;
 +    pa_server_type_t local_server_type;
      int exit_idle_time,
          scache_idle_time,
          auto_log_target,
index ecdb3a642a9ebbb1fd090e39839edc6f6ec751dc,6931359c1d1fb6aee15b160cebd1f6f83a52d75e..9bea6148c4da945d01555477c80a9d67b68b1467
  ; disallow-exit = no
  ; use-pid-file = yes
  ; system-instance = no
 +; local-server-type = user
  ; disable-shm = no
  ; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB
+ ; lock-memory = no
+ ; no-cpu-limit = no
  
  ; high-priority = yes
  ; nice-level = -11
Simple merge
index 62214a56afa9cfb7f67de166eef019116d6ca814,b58bb379de8aaa91aa32f534de2e8a0ce792fe11..c1730b7064b0f1014828b79112a4fdf0a25cf4bc
  #include "dumpmodules.h"
  #include "caps.h"
  #include "ltdl-bind-now.h"
- #include "polkit.h"
 +#include "server-lookup.h"
  
  #ifdef HAVE_LIBWRAP
  /* Only one instance of these variables */
@@@ -488,191 -461,6 +463,32 @@@ int main(int argc, char *argv[]) 
          pa_log_set_flags(PA_LOG_PRINT_TIME, PA_LOG_SET);
      pa_log_set_show_backtrace(conf->log_backtrace);
  
-     start_server = conf->local_server_type == PA_SERVER_TYPE_USER || (real_root && conf->local_server_type == PA_SERVER_TYPE_SYSTEM);
 +#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();
 +    }
 +
-     pa_log_debug("Started as real root: %s, suid root: %s", pa_yes_no(real_root), pa_yes_no(suid_root));
- #ifdef HAVE_DBUS
-     /* XXX: Uhh, goto programming... as if this wasn't hard enough to follow
-      * already. But if we won't start the full server, we want to just skip all
-      * the capability stuff. */
-     if (!start_server) {
-         if (!real_root && pa_have_caps())
-             pa_drop_caps();
-         goto after_caps_setup;
-     }
- #endif
-     if (!real_root && pa_have_caps()) {
- #ifdef HAVE_SYS_RESOURCE_H
-         struct rlimit rl;
- #endif
-         pa_bool_t allow_high_priority = FALSE, allow_realtime = FALSE;
-         /* Let's better not enable high prio or RT by default */
-         if (conf->high_priority && !allow_high_priority) {
-             if (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) {
-                 pa_log_info(_("We're in the group '%s', allowing high-priority scheduling."), PA_REALTIME_GROUP);
-                 allow_high_priority = TRUE;
-             }
-         }
-         if (conf->realtime_scheduling && !allow_realtime) {
-             if (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) {
-                 pa_log_info(_("We're in the group '%s', allowing real-time scheduling."), PA_REALTIME_GROUP);
-                 allow_realtime = TRUE;
-             }
-         }
- #ifdef HAVE_POLKIT
-         if (conf->high_priority && !allow_high_priority) {
-             if (pa_polkit_check("org.pulseaudio.acquire-high-priority") > 0) {
-                 pa_log_info(_("PolicyKit grants us acquire-high-priority privilege."));
-                 allow_high_priority = TRUE;
-             } else
-                 pa_log_info(_("PolicyKit refuses acquire-high-priority privilege."));
-         }
-         if (conf->realtime_scheduling && !allow_realtime) {
-             if (pa_polkit_check("org.pulseaudio.acquire-real-time") > 0) {
-                 pa_log_info(_("PolicyKit grants us acquire-real-time privilege."));
-                 allow_realtime = TRUE;
-             } else
-                 pa_log_info(_("PolicyKit refuses acquire-real-time privilege."));
-         }
- #endif
-         if (!allow_high_priority && !allow_realtime) {
-             /* OK, there's no further need to keep CAP_NICE. Hence
-              * let's give it up early */
-             pa_drop_caps();
-         }
- #ifdef RLIMIT_RTPRIO
-         if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0)
-             if (rl.rlim_cur > 0) {
-                 pa_log_info("RLIMIT_RTPRIO is set to %u, allowing real-time scheduling.", (unsigned) rl.rlim_cur);
-                 allow_realtime = TRUE;
-             }
- #endif
- #ifdef RLIMIT_NICE
-         if (getrlimit(RLIMIT_NICE, &rl) >= 0)
-             if (rl.rlim_cur > 20 ) {
-                 pa_log_info("RLIMIT_NICE is set to %u, allowing high-priority scheduling.", (unsigned) rl.rlim_cur);
-                 allow_high_priority = TRUE;
-             }
- #endif
-         if ((conf->high_priority && !allow_high_priority) ||
-             (conf->realtime_scheduling && !allow_realtime))
-             pa_log_info(_("Called SUID root and real-time and/or high-priority scheduling was requested in the configuration. However, we lack the necessary privileges:\n"
-                             "We are not in group '%s', PolicyKit refuse to grant us the requested privileges and we have no increase RLIMIT_NICE/RLIMIT_RTPRIO resource limits.\n"
-                             "For enabling real-time/high-priority scheduling please acquire the appropriate PolicyKit privileges, or become a member of '%s', or increase the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user."),
-                           PA_REALTIME_GROUP, PA_REALTIME_GROUP);
-         if (!allow_realtime)
-             conf->realtime_scheduling = FALSE;
-         if (!allow_high_priority)
-             conf->high_priority = FALSE;
-     }
- #ifdef HAVE_SYS_RESOURCE_H
-     /* Reset resource limits. If we are run as root (for system mode)
-      * this might end up increasing the limits, which is intended
-      * behaviour. For all other cases, i.e. started as normal user, or
-      * SUID root at this point we should have no CAP_SYS_RESOURCE and
-      * increasing the limits thus should fail. Which is, too, intended
-      * behaviour */
-     set_all_rlimits(conf);
- #endif
-     if (conf->high_priority && !pa_can_high_priority()) {
-         pa_log_info(_("High-priority scheduling enabled in configuration but not allowed by policy."));
-         conf->high_priority = FALSE;
-     }
-     if (conf->high_priority && (conf->cmd == PA_CMD_DAEMON || conf->cmd == PA_CMD_START))
-         pa_raise_priority(conf->nice_level);
-     pa_log_debug("Can realtime: %s, can high-priority: %s", pa_yes_no(pa_can_realtime()), pa_yes_no(pa_can_high_priority()));
-     if (!real_root && pa_have_caps()) {
-         pa_bool_t drop;
-         drop = (conf->cmd != PA_CMD_DAEMON && conf->cmd != PA_CMD_START) || !conf->realtime_scheduling;
- #ifdef RLIMIT_RTPRIO
-         if (!drop) {
-             struct rlimit rl;
-             /* At this point we still have CAP_NICE if we were loaded
-              * SUID root. If possible let's acquire RLIMIT_RTPRIO
-              * instead and give CAP_NICE up. */
-             if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0) {
-                 if (rl.rlim_cur >= 9)
-                     drop = TRUE;
-                 else {
-                     rl.rlim_max = rl.rlim_cur = 9;
-                     if (setrlimit(RLIMIT_RTPRIO, &rl) >= 0) {
-                         pa_log_info(_("Successfully increased RLIMIT_RTPRIO"));
-                         drop = TRUE;
-                     } else
-                         pa_log_warn(_("RLIMIT_RTPRIO failed: %s"), pa_cstrerror(errno));
-                 }
-             }
-         }
- #endif
-         if (drop)  {
-             pa_log_info(_("Giving up CAP_NICE"));
-             pa_drop_caps();
-             suid_root = FALSE;
-         }
-     }
-     if (conf->realtime_scheduling && !pa_can_realtime()) {
-         pa_log_info(_("Real-time scheduling enabled in configuration but not allowed by policy."));
-         conf->realtime_scheduling = FALSE;
-     }
- #ifdef HAVE_DBUS
- after_caps_setup:
- #endif
-     pa_log_debug("Can realtime: %s, can high-priority: %s", pa_yes_no(pa_can_realtime()), pa_yes_no(pa_can_high_priority()));
++    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;
              pa_assert(conf->cmd == PA_CMD_DAEMON || conf->cmd == PA_CMD_START);
      }
  
-     if (real_root && !conf->system_instance)
+     if (getuid() == 0 && !conf->system_instance)
          pa_log_warn(_("This program is not intended to be run as root (unless --system is specified)."));
-     else if (!real_root && conf->system_instance) {
 +#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;
      }
      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
  
      pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0);
      pa_signal_new(SIGINT, signal_callback, c);
index 46f649211d3f2d45e74515ba7dc9a6a40c95ea74,0000000000000000000000000000000000000000..f7c1f0a997b87dcbfe8239b5979002c733c30ba8
mode 100644,000000..100644
--- /dev/null
@@@ -1,575 -1,0 +1,575 @@@
-     c->wrap_conn = pa_dbus_wrap_connection_new_from_existing(s->userdata->module->core->mainloop, new_connection);
 +/***
 +  This file is part of PulseAudio.
 +
 +  Copyright 2009 Tanu Kaskinen
 +  Copyright 2006 Lennart Poettering
 +  Copyright 2006 Shams E. King
 +
 +  PulseAudio is free software; you can redistribute it and/or modify
 +  it under the terms of the GNU Lesser General Public License as published
 +  by the Free Software Foundation; either version 2.1 of the License,
 +  or (at your option) any later version.
 +
 +  PulseAudio is distributed in the hope that it will be useful, but
 +  WITHOUT ANY WARRANTY; without even the implied warranty of
 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 +  General Public License for more details.
 +
 +  You should have received a copy of the GNU Lesser General Public License
 +  along with PulseAudio; if not, write to the Free Software
 +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 +  USA.
 +***/
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <dbus/dbus.h>
 +
 +#include <pulse/mainloop-api.h>
 +#include <pulse/timeval.h>
 +#include <pulse/xmalloc.h>
 +
 +#include <pulsecore/client.h>
 +#include <pulsecore/core-util.h>
 +#include <pulsecore/dbus-common.h>
 +#include <pulsecore/dbus-util.h>
 +#include <pulsecore/idxset.h>
 +#include <pulsecore/macro.h>
 +#include <pulsecore/modargs.h>
 +#include <pulsecore/module.h>
 +
 +#include <pulsecore/dbus-objs/core.h>
 +
 +#include "module-dbus-protocol-symdef.h"
 +
 +PA_MODULE_DESCRIPTION("D-Bus interface");
 +PA_MODULE_USAGE(
 +        "access=local|remote|local,remote "
 +        "tcp_port=<port number>");
 +PA_MODULE_LOAD_ONCE(TRUE);
 +PA_MODULE_AUTHOR("Tanu Kaskinen");
 +PA_MODULE_VERSION(PACKAGE_VERSION);
 +
 +#define CLEANUP_INTERVAL 10 /* seconds */
 +
 +enum server_type {
 +    SERVER_TYPE_LOCAL,
 +    SERVER_TYPE_TCP
 +};
 +
 +struct server;
 +struct connection;
 +
 +struct userdata {
 +    pa_module *module;
 +    pa_bool_t local_access;
 +    pa_bool_t remote_access;
 +    uint32_t tcp_port;
 +
 +    struct server *local_server;
 +    struct server *tcp_server;
 +
 +    pa_idxset *connections;
 +
 +    pa_time_event *cleanup_event;
 +
 +    pa_dbusobj_core *core_object;
 +};
 +
 +struct server {
 +    struct userdata *userdata;
 +    enum server_type type;
 +    DBusServer *dbus_server;
 +};
 +
 +struct connection {
 +    struct server *server;
 +    pa_dbus_wrap_connection *wrap_conn;
 +    pa_client *client;
 +};
 +
 +static const char* const valid_modargs[] = {
 +    "access",
 +    "tcp_port",
 +    NULL
 +};
 +
 +static void connection_free(struct connection *c) {
 +    pa_assert(c);
 +
 +    pa_assert_se(pa_dbus_unregister_connection(c->server->userdata->module->core, pa_dbus_wrap_connection_get(c->wrap_conn)) >= 0);
 +
 +    pa_client_free(c->client);
 +    pa_assert_se(pa_idxset_remove_by_data(c->server->userdata->connections, c, NULL));
 +    pa_dbus_wrap_connection_free(c->wrap_conn);
 +    pa_xfree(c);
 +}
 +
 +/* Called from pa_client_kill(). */
 +static void client_kill_cb(pa_client *c) {
 +    struct connection *conn;
 +
 +    pa_assert(c);
 +    pa_assert(c->userdata);
 +
 +    conn = c->userdata;
 +    connection_free(conn);
 +
 +    pa_log_info("Connection killed.");
 +}
 +
 +static dbus_bool_t user_check_cb(DBusConnection *connection, unsigned long uid, void *data) {
 +    pa_log_debug("Allowing connection by user %lu.", uid);
 +
 +    return TRUE;
 +}
 +
 +/* Called by D-Bus when a new client connection is received. */
 +static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_connection, void *data) {
 +    struct server *s = data;
 +    struct connection *c;
 +    pa_client_new_data new_data;
 +    pa_client *client;
 +
 +    pa_assert(new_connection);
 +    pa_assert(s);
 +
 +    pa_client_new_data_init(&new_data);
 +    new_data.module = s->userdata->module;
 +    new_data.driver = __FILE__;
 +    pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client"); /* TODO: It's probably possible to generate a fancier name. Other props? */
 +    client = pa_client_new(s->userdata->module->core, &new_data);
 +    pa_client_new_data_done(&new_data);
 +
 +    if (!client) {
 +        dbus_connection_close(new_connection);
 +        return;
 +    }
 +
 +    if (s->type == SERVER_TYPE_TCP || s->userdata->module->core->server_type == PA_SERVER_TYPE_SYSTEM) {
 +        /* FIXME: Here we allow anyone from anywhere to access the server,
 +         * anonymously. Access control should be configurable. */
 +        dbus_connection_set_unix_user_function(new_connection, user_check_cb, NULL, NULL);
 +        dbus_connection_set_allow_anonymous(new_connection, TRUE);
 +    }
 +
 +    c = pa_xnew(struct connection, 1);
 +    c->server = s;
++    c->wrap_conn = pa_dbus_wrap_connection_new_from_existing(s->userdata->module->core->mainloop, TRUE, new_connection);
 +    c->client = client;
 +
 +    c->client->kill = client_kill_cb;
 +    c->client->send_event = NULL; /* TODO: Implement this. */
 +    c->client->userdata = c;
 +
 +    pa_idxset_put(s->userdata->connections, c, NULL);
 +
 +    pa_assert_se(pa_dbus_register_connection(s->userdata->module->core, new_connection) >= 0);
 +}
 +
 +/* Called by PA mainloop when a D-Bus fd watch event needs handling. */
 +static void io_event_cb(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
 +    unsigned int flags = 0;
 +    DBusWatch *watch = userdata;
 +
 +#if HAVE_DBUS_WATCH_GET_UNIX_FD
 +    pa_assert(fd == dbus_watch_get_unix_fd(watch));
 +#else
 +    pa_assert(fd == dbus_watch_get_fd(watch));
 +#endif
 +
 +    if (!dbus_watch_get_enabled(watch)) {
 +        pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
 +        return;
 +    }
 +
 +    if (events & PA_IO_EVENT_INPUT)
 +        flags |= DBUS_WATCH_READABLE;
 +    if (events & PA_IO_EVENT_OUTPUT)
 +        flags |= DBUS_WATCH_WRITABLE;
 +    if (events & PA_IO_EVENT_HANGUP)
 +        flags |= DBUS_WATCH_HANGUP;
 +    if (events & PA_IO_EVENT_ERROR)
 +        flags |= DBUS_WATCH_ERROR;
 +
 +    dbus_watch_handle(watch, flags);
 +}
 +
 +/* Called by PA mainloop when a D-Bus timer event needs handling. */
 +static void time_event_cb(pa_mainloop_api *mainloop, pa_time_event* e, const struct timeval *tv, void *userdata) {
 +    DBusTimeout *timeout = userdata;
 +
 +    if (dbus_timeout_get_enabled(timeout)) {
 +        struct timeval next = *tv;
 +        dbus_timeout_handle(timeout);
 +
 +        /* restart it for the next scheduled time */
 +        pa_timeval_add(&next, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
 +        mainloop->time_restart(e, &next);
 +    }
 +}
 +
 +/* Translates D-Bus fd watch event flags to PA IO event flags. */
 +static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
 +    unsigned int flags;
 +    pa_io_event_flags_t events = 0;
 +
 +    pa_assert(watch);
 +
 +    flags = dbus_watch_get_flags(watch);
 +
 +    /* no watch flags for disabled watches */
 +    if (!dbus_watch_get_enabled(watch))
 +        return PA_IO_EVENT_NULL;
 +
 +    if (flags & DBUS_WATCH_READABLE)
 +        events |= PA_IO_EVENT_INPUT;
 +    if (flags & DBUS_WATCH_WRITABLE)
 +        events |= PA_IO_EVENT_OUTPUT;
 +
 +    return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
 +}
 +
 +/* Called by D-Bus when a D-Bus fd watch event is added. */
 +static dbus_bool_t watch_add_cb(DBusWatch *watch, void *data) {
 +    struct server *s = data;
 +    pa_mainloop_api *mainloop;
 +    pa_io_event *ev;
 +
 +    pa_assert(watch);
 +    pa_assert(s);
 +
 +    mainloop = s->userdata->module->core->mainloop;
 +
 +    ev = mainloop->io_new(
 +            mainloop,
 +#if HAVE_DBUS_WATCH_GET_UNIX_FD
 +            dbus_watch_get_unix_fd(watch),
 +#else
 +            dbus_watch_get_fd(watch),
 +#endif
 +            get_watch_flags(watch), io_event_cb, watch);
 +
 +    dbus_watch_set_data(watch, ev, NULL);
 +
 +    return TRUE;
 +}
 +
 +/* Called by D-Bus when a D-Bus fd watch event is removed. */
 +static void watch_remove_cb(DBusWatch *watch, void *data) {
 +    struct server *s = data;
 +    pa_io_event *ev;
 +
 +    pa_assert(watch);
 +    pa_assert(s);
 +
 +    if ((ev = dbus_watch_get_data(watch)))
 +        s->userdata->module->core->mainloop->io_free(ev);
 +}
 +
 +/* Called by D-Bus when a D-Bus fd watch event is toggled. */
 +static void watch_toggled_cb(DBusWatch *watch, void *data) {
 +    struct server *s = data;
 +    pa_io_event *ev;
 +
 +    pa_assert(watch);
 +    pa_assert(s);
 +
 +    pa_assert_se(ev = dbus_watch_get_data(watch));
 +
 +    /* get_watch_flags() checks if the watch is enabled */
 +    s->userdata->module->core->mainloop->io_enable(ev, get_watch_flags(watch));
 +}
 +
 +/* Called by D-Bus when a D-Bus timer event is added. */
 +static dbus_bool_t timeout_add_cb(DBusTimeout *timeout, void *data) {
 +    struct server *s = data;
 +    pa_mainloop_api *mainloop;
 +    pa_time_event *ev;
 +    struct timeval tv;
 +
 +    pa_assert(timeout);
 +    pa_assert(s);
 +
 +    if (!dbus_timeout_get_enabled(timeout))
 +        return FALSE;
 +
 +    mainloop = s->userdata->module->core->mainloop;
 +
 +    pa_gettimeofday(&tv);
 +    pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
 +
 +    ev = mainloop->time_new(mainloop, &tv, time_event_cb, timeout);
 +
 +    dbus_timeout_set_data(timeout, ev, NULL);
 +
 +    return TRUE;
 +}
 +
 +/* Called by D-Bus when a D-Bus timer event is removed. */
 +static void timeout_remove_cb(DBusTimeout *timeout, void *data) {
 +    struct server *s = data;
 +    pa_time_event *ev;
 +
 +    pa_assert(timeout);
 +    pa_assert(s);
 +
 +    if ((ev = dbus_timeout_get_data(timeout)))
 +        s->userdata->module->core->mainloop->time_free(ev);
 +}
 +
 +/* Called by D-Bus when a D-Bus timer event is toggled. */
 +static void timeout_toggled_cb(DBusTimeout *timeout, void *data) {
 +    struct server *s = data;
 +    pa_mainloop_api *mainloop;
 +    pa_time_event *ev;
 +
 +    pa_assert(timeout);
 +    pa_assert(s);
 +
 +    mainloop = s->userdata->module->core->mainloop;
 +
 +    pa_assert_se(ev = dbus_timeout_get_data(timeout));
 +
 +    if (dbus_timeout_get_enabled(timeout)) {
 +        struct timeval tv;
 +
 +        pa_gettimeofday(&tv);
 +        pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
 +
 +        mainloop->time_restart(ev, &tv);
 +    } else
 +        mainloop->time_restart(ev, NULL);
 +}
 +
 +static void server_free(struct server *s) {
 +    pa_assert(s);
 +
 +    if (s->dbus_server) {
 +        dbus_server_disconnect(s->dbus_server);
 +        dbus_server_unref(s->dbus_server);
 +    }
 +
 +    pa_xfree(s);
 +}
 +
 +static struct server *start_server(struct userdata *u, const char *address, enum server_type type) {
 +    /* XXX: We assume that when we unref the DBusServer instance at module
 +     * shutdown, nobody else holds any references to it. If we stop assuming
 +     * that someday, dbus_server_set_new_connection_function,
 +     * dbus_server_set_watch_functions and dbus_server_set_timeout_functions
 +     * calls should probably register free callbacks, instead of providing NULL
 +     * as they do now. */
 +
 +    struct server *s = NULL;
 +    DBusError error;
 +
 +    pa_assert(u);
 +    pa_assert(address);
 +
 +    dbus_error_init(&error);
 +
 +    s = pa_xnew0(struct server, 1);
 +    s->userdata = u;
 +    s->dbus_server = dbus_server_listen(address, &error);
 +
 +    if (dbus_error_is_set(&error)) {
 +        pa_log("dbus_server_listen() failed: %s: %s", error.name, error.message);
 +        goto fail;
 +    }
 +
 +    dbus_server_set_new_connection_function(s->dbus_server, connection_new_cb, s, NULL);
 +
 +    if (!dbus_server_set_watch_functions(s->dbus_server, watch_add_cb, watch_remove_cb, watch_toggled_cb, s, NULL)) {
 +        pa_log("dbus_server_set_watch_functions() ran out of memory.");
 +        goto fail;
 +    }
 +
 +    if (!dbus_server_set_timeout_functions(s->dbus_server, timeout_add_cb, timeout_remove_cb, timeout_toggled_cb, s, NULL)) {
 +        pa_log("dbus_server_set_timeout_functions() ran out of memory.");
 +        goto fail;
 +    }
 +
 +    return s;
 +
 +fail:
 +    if (s)
 +        server_free(s);
 +
 +    dbus_error_free(&error);
 +
 +    return NULL;
 +}
 +
 +static struct server *start_local_server(struct userdata *u) {
 +    struct server *s = NULL;
 +    char *address = NULL;
 +
 +    pa_assert(u);
 +
 +    address = pa_get_dbus_address_from_server_type(u->module->core->server_type);
 +
 +    s = start_server(u, address, SERVER_TYPE_LOCAL); /* May return NULL */
 +
 +    pa_xfree(address);
 +
 +    return s;
 +}
 +
 +static struct server *start_tcp_server(struct userdata *u) {
 +    struct server *s = NULL;
 +    char *address = NULL;
 +
 +    pa_assert(u);
 +
 +    address = pa_sprintf_malloc("tcp:host=127.0.0.1,port=%u", u->tcp_port);
 +
 +    s = start_server(u, address, SERVER_TYPE_TCP); /* May return NULL */
 +
 +    pa_xfree(address);
 +
 +    return s;
 +}
 +
 +static int get_access_arg(pa_modargs *ma, pa_bool_t *local_access, pa_bool_t *remote_access) {
 +    const char *value = NULL;
 +
 +    pa_assert(ma);
 +    pa_assert(local_access);
 +    pa_assert(remote_access);
 +
 +    if (!(value = pa_modargs_get_value(ma, "access", NULL)))
 +        return 0;
 +
 +    if (!strcmp(value, "local")) {
 +        *local_access = TRUE;
 +        *remote_access = FALSE;
 +    } else if (!strcmp(value, "remote")) {
 +        *local_access = FALSE;
 +        *remote_access = TRUE;
 +    } else if (!strcmp(value, "local,remote")) {
 +        *local_access = TRUE;
 +        *remote_access = TRUE;
 +    } else
 +        return -1;
 +
 +    return 0;
 +}
 +
 +/* Frees dead client connections. Called every CLEANUP_INTERVAL seconds. */
 +static void cleanup_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) {
 +    struct userdata *u = userdata;
 +    struct connection *conn = NULL;
 +    uint32_t idx;
 +    struct timeval cleanup_timeval;
 +    unsigned free_count = 0;
 +
 +    for (conn = pa_idxset_first(u->connections, &idx); conn; conn = pa_idxset_next(u->connections, &idx)) {
 +        if (!dbus_connection_get_is_connected(pa_dbus_wrap_connection_get(conn->wrap_conn))) {
 +            connection_free(conn);
 +            ++free_count;
 +        }
 +    }
 +
 +    if (free_count > 0)
 +        pa_log_debug("Freed %u dead D-Bus client connections.", free_count);
 +
 +    pa_gettimeofday(&cleanup_timeval);
 +    cleanup_timeval.tv_sec += CLEANUP_INTERVAL;
 +    u->module->core->mainloop->time_restart(e, &cleanup_timeval);
 +}
 +
 +int pa__init(pa_module *m) {
 +    struct userdata *u = NULL;
 +    pa_modargs *ma = NULL;
 +    struct timeval cleanup_timeval;
 +
 +    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;
 +    u->local_access = TRUE;
 +    u->remote_access = FALSE;
 +    u->tcp_port = PA_DBUS_DEFAULT_PORT;
 +
 +    if (get_access_arg(ma, &u->local_access, &u->remote_access) < 0) {
 +        pa_log("Invalid access argument: '%s'", pa_modargs_get_value(ma, "access", NULL));
 +        goto fail;
 +    }
 +
 +    if (pa_modargs_get_value_u32(ma, "tcp_port", &u->tcp_port) < 0 || u->tcp_port < 1 || u->tcp_port > 49150) {
 +        pa_log("Invalid tcp_port argument: '%s'", pa_modargs_get_value(ma, "tcp_port", NULL));
 +        goto fail;
 +    }
 +
 +    if (u->local_access && !(u->local_server = start_local_server(u))) {
 +        pa_log("Starting the local D-Bus server failed.");
 +        goto fail;
 +    }
 +
 +    if (u->remote_access && !(u->tcp_server = start_tcp_server(u))) {
 +        pa_log("Starting the D-Bus server for remote connections failed.");
 +        goto fail;
 +    }
 +
 +    u->connections = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 +
 +    pa_gettimeofday(&cleanup_timeval);
 +    cleanup_timeval.tv_sec += CLEANUP_INTERVAL;
 +    u->cleanup_event = m->core->mainloop->time_new(m->core->mainloop, &cleanup_timeval, cleanup_cb, u);
 +
 +    u->core_object = pa_dbusobj_core_new(m->core);
 +
 +    return 0;
 +
 +fail:
 +    if (ma)
 +        pa_modargs_free(ma);
 +
 +    pa__done(m);
 +
 +    return -1;
 +}
 +
 +/* Called by idxset when the connection set is freed. */
 +static void connection_free_cb(void *p, void *userdata) {
 +    struct connection *conn = p;
 +
 +    pa_assert(conn);
 +
 +    connection_free(conn);
 +}
 +
 +void pa__done(pa_module *m) {
 +    struct userdata *u;
 +
 +    pa_assert(m);
 +
 +    if (!(u = m->userdata))
 +        return;
 +
 +    if (u->core_object)
 +        pa_dbusobj_core_free(u->core_object);
 +
 +    if (u->cleanup_event)
 +        m->core->mainloop->time_free(u->cleanup_event);
 +
 +    if (u->connections)
 +        pa_idxset_free(u->connections, connection_free_cb, NULL);
 +
 +    if (u->tcp_server)
 +        server_free(u->tcp_server);
 +
 +    if (u->local_server)
 +        server_free(u->local_server);
 +
 +    pa_xfree(u);
 +    m->userdata = NULL;
 +}
Simple merge
index d8bd0e0a16db10a36897a5b34fc3c502802af8e3,4e6148f0770e43a84ea3a0e05e04038164e78fe1..e047dc311b94cfadaee33004c48ba4f880a49e01
@@@ -276,27 -290,6 +290,28 @@@ pa_dbus_wrap_connection* pa_dbus_wrap_c
      return pconn;
  }
  
- pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *m, DBusConnection *conn) {
++pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *m, pa_bool_t use_rtclock, DBusConnection *conn) {
 +    pa_dbus_wrap_connection *pconn;
 +
 +    pa_assert(m);
 +    pa_assert(conn);
 +
 +    pconn = pa_xnew(pa_dbus_wrap_connection, 1);
 +    pconn->mainloop = m;
 +    pconn->connection = dbus_connection_ref(conn);
++    pconn->use_rtclock = use_rtclock;
 +
 +    dbus_connection_set_exit_on_disconnect(conn, FALSE);
 +    dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL);
 +    dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, pconn, NULL);
 +    dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, pconn, NULL);
 +    dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
 +
 +    pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn);
 +
 +    return pconn;
 +}
 +
  void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) {
      pa_assert(c);
  
index cd08485d6871757ed810bc9a9e65c2892c3159f5,9ff298d866e340fc5763ef815386c48b81ff92dc..973287356e417886ca80dd742fdaa0f9558c2b39
@@@ -30,8 -30,7 +30,8 @@@
  /* A wrap connection is not shared or refcounted, it is available in client side */
  typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection;
  
- pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, DBusBusType type, DBusError *error);
- pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *mainloop, DBusConnection *conn);
+ pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, pa_bool_t use_rtclock, DBusBusType type, DBusError *error);
++pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *mainloop, pa_bool_t use_rtclock, DBusConnection *conn);
  void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn);
  
  DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn);