"Fossies" - the Fresh Open Source Software Archive

Member "pulseaudio-14.2/src/modules/module-device-restore.c" (16 Jan 2021, 37944 Bytes) of package /linux/misc/pulseaudio-14.2.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "module-device-restore.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.0_vs_14.2.

    1 /***
    2   This file is part of PulseAudio.
    3 
    4   Copyright 2006-2008 Lennart Poettering
    5   Copyright 2011 Colin Guthrie
    6 
    7   PulseAudio is free software; you can redistribute it and/or modify
    8   it under the terms of the GNU Lesser General Public License as published
    9   by the Free Software Foundation; either version 2.1 of the License,
   10   or (at your option) any later version.
   11 
   12   PulseAudio is distributed in the hope that it will be useful, but
   13   WITHOUT ANY WARRANTY; without even the implied warranty of
   14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   15   General Public License for more details.
   16 
   17   You should have received a copy of the GNU Lesser General Public License
   18   along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
   19 ***/
   20 
   21 #ifdef HAVE_CONFIG_H
   22 #include <config.h>
   23 #endif
   24 
   25 #include <unistd.h>
   26 #include <string.h>
   27 #include <errno.h>
   28 #include <sys/types.h>
   29 #include <stdio.h>
   30 #include <stdlib.h>
   31 
   32 #include <pulse/gccmacro.h>
   33 #include <pulse/xmalloc.h>
   34 #include <pulse/volume.h>
   35 #include <pulse/timeval.h>
   36 #include <pulse/rtclock.h>
   37 #include <pulse/format.h>
   38 #include <pulse/internal.h>
   39 
   40 #include <pulsecore/core-error.h>
   41 #include <pulsecore/module.h>
   42 #include <pulsecore/core-util.h>
   43 #include <pulsecore/modargs.h>
   44 #include <pulsecore/log.h>
   45 #include <pulsecore/core-subscribe.h>
   46 #include <pulsecore/sink.h>
   47 #include <pulsecore/source.h>
   48 #include <pulsecore/namereg.h>
   49 #include <pulsecore/protocol-native.h>
   50 #include <pulsecore/pstream.h>
   51 #include <pulsecore/pstream-util.h>
   52 #include <pulsecore/database.h>
   53 #include <pulsecore/tagstruct.h>
   54 
   55 PA_MODULE_AUTHOR("Lennart Poettering");
   56 PA_MODULE_DESCRIPTION("Automatically restore the volume/mute state of devices");
   57 PA_MODULE_VERSION(PACKAGE_VERSION);
   58 PA_MODULE_LOAD_ONCE(true);
   59 PA_MODULE_USAGE(
   60         "restore_port=<Save/restore port?> "
   61         "restore_volume=<Save/restore volumes?> "
   62         "restore_muted=<Save/restore muted states?> "
   63         "restore_formats=<Save/restore saved formats?>");
   64 
   65 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
   66 
   67 static const char* const valid_modargs[] = {
   68     "restore_volume",
   69     "restore_muted",
   70     "restore_port",
   71     "restore_formats",
   72     NULL
   73 };
   74 
   75 struct userdata {
   76     pa_core *core;
   77     pa_module *module;
   78     pa_subscription *subscription;
   79     pa_time_event *save_time_event;
   80     pa_database *database;
   81 
   82     pa_native_protocol *protocol;
   83     pa_idxset *subscribed;
   84 
   85     bool restore_volume:1;
   86     bool restore_muted:1;
   87     bool restore_port:1;
   88     bool restore_formats:1;
   89 };
   90 
   91 /* Protocol extension commands */
   92 enum {
   93     SUBCOMMAND_TEST,
   94     SUBCOMMAND_SUBSCRIBE,
   95     SUBCOMMAND_EVENT,
   96     SUBCOMMAND_READ_FORMATS_ALL,
   97     SUBCOMMAND_READ_FORMATS,
   98     SUBCOMMAND_SAVE_FORMATS
   99 };
  100 
  101 #define ENTRY_VERSION 1
  102 
  103 struct entry {
  104     uint8_t version;
  105     bool port_valid;
  106     char *port;
  107 };
  108 
  109 #define PERPORTENTRY_VERSION 1
  110 
  111 struct perportentry {
  112     uint8_t version;
  113     bool muted_valid, volume_valid;
  114     bool muted;
  115     pa_channel_map channel_map;
  116     pa_cvolume volume;
  117     pa_idxset *formats;
  118 };
  119 
  120 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
  121     struct userdata *u = userdata;
  122 
  123     pa_assert(a);
  124     pa_assert(e);
  125     pa_assert(u);
  126 
  127     pa_assert(e == u->save_time_event);
  128     u->core->mainloop->time_free(u->save_time_event);
  129     u->save_time_event = NULL;
  130 
  131     pa_database_sync(u->database);
  132     pa_log_info("Synced.");
  133 }
  134 
  135 static void trigger_save(struct userdata *u, pa_device_type_t type, uint32_t sink_idx) {
  136     pa_native_connection *c;
  137     uint32_t idx;
  138 
  139     if (sink_idx != PA_INVALID_INDEX) {
  140         PA_IDXSET_FOREACH(c, u->subscribed, idx) {
  141             pa_tagstruct *t;
  142 
  143             t = pa_tagstruct_new();
  144             pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
  145             pa_tagstruct_putu32(t, 0);
  146             pa_tagstruct_putu32(t, u->module->index);
  147             pa_tagstruct_puts(t, u->module->name);
  148             pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
  149             pa_tagstruct_putu32(t, type);
  150             pa_tagstruct_putu32(t, sink_idx);
  151 
  152             pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
  153         }
  154     }
  155 
  156     if (u->save_time_event)
  157         return;
  158 
  159     u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
  160 }
  161 
  162 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
  163 /* Some forward declarations */
  164 static bool legacy_entry_read(struct userdata *u, pa_datum *data, struct entry **entry, struct perportentry **perportentry);
  165 static struct perportentry* perportentry_read(struct userdata *u, const char *basekeyname, const char *port);
  166 static bool perportentry_write(struct userdata *u, const char *basekeyname, const char *port, const struct perportentry *e);
  167 static void perportentry_free(struct perportentry* e);
  168 #endif
  169 
  170 static struct entry* entry_new(void) {
  171     struct entry *r = pa_xnew0(struct entry, 1);
  172     r->version = ENTRY_VERSION;
  173     return r;
  174 }
  175 
  176 static void entry_free(struct entry* e) {
  177     pa_assert(e);
  178 
  179     pa_xfree(e->port);
  180     pa_xfree(e);
  181 }
  182 
  183 static bool entry_write(struct userdata *u, const char *name, const struct entry *e) {
  184     pa_tagstruct *t;
  185     pa_datum key, data;
  186     bool r;
  187 
  188     pa_assert(u);
  189     pa_assert(name);
  190     pa_assert(e);
  191 
  192     t = pa_tagstruct_new();
  193     pa_tagstruct_putu8(t, e->version);
  194     pa_tagstruct_put_boolean(t, e->port_valid);
  195     pa_tagstruct_puts(t, e->port);
  196 
  197     key.data = (char *) name;
  198     key.size = strlen(name);
  199 
  200     data.data = (void*)pa_tagstruct_data(t, &data.size);
  201 
  202     r = (pa_database_set(u->database, &key, &data, true) == 0);
  203 
  204     pa_tagstruct_free(t);
  205 
  206     return r;
  207 }
  208 
  209 static struct entry* entry_read(struct userdata *u, const char *name) {
  210     pa_datum key, data;
  211     struct entry *e = NULL;
  212     pa_tagstruct *t = NULL;
  213     const char* port;
  214 
  215     pa_assert(u);
  216     pa_assert(name);
  217 
  218     key.data = (char*) name;
  219     key.size = strlen(name);
  220 
  221     pa_zero(data);
  222 
  223     if (!pa_database_get(u->database, &key, &data)) {
  224         pa_log_debug("Database contains no data for key: %s", name);
  225         return NULL;
  226     }
  227 
  228     t = pa_tagstruct_new_fixed(data.data, data.size);
  229     e = entry_new();
  230 
  231     if (pa_tagstruct_getu8(t, &e->version) < 0 ||
  232         e->version > ENTRY_VERSION ||
  233         pa_tagstruct_get_boolean(t, &e->port_valid) < 0 ||
  234         pa_tagstruct_gets(t, &port) < 0) {
  235 
  236         goto fail;
  237     }
  238 
  239     if (!pa_tagstruct_eof(t))
  240         goto fail;
  241 
  242     e->port = pa_xstrdup(port);
  243 
  244     pa_tagstruct_free(t);
  245     pa_datum_free(&data);
  246 
  247     return e;
  248 
  249 fail:
  250 
  251     pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
  252 
  253     if (e)
  254         entry_free(e);
  255     if (t)
  256         pa_tagstruct_free(t);
  257 
  258 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
  259 {
  260     struct perportentry *ppe;
  261     pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name);
  262     if (legacy_entry_read(u, &data, &e, &ppe)) {
  263         bool written = false;
  264 
  265         pa_log_debug("Success. Saving new format for key: %s", name);
  266         written = entry_write(u, name, e);
  267 
  268         /* Now convert the legacy entry into per-port entries */
  269         if (0 == strncmp("sink:", name, 5)) {
  270             pa_sink *sink;
  271 
  272             if ((sink = pa_namereg_get(u->core, name+5, PA_NAMEREG_SINK))) {
  273                 /* Write a "null" port entry. The read code will automatically try this
  274                  * if it cannot find a specific port-named entry. */
  275                 written = perportentry_write(u, name, NULL, ppe) || written;
  276             }
  277         } else if (0 == strncmp("source:", name, 7)) {
  278             pa_source *source;
  279 
  280             if ((source = pa_namereg_get(u->core, name+7, PA_NAMEREG_SOURCE))) {
  281                 /* Write a "null" port entry. The read code will automatically try this
  282                  * if it cannot find a specific port-named entry. */
  283                 written = perportentry_write(u, name, NULL, ppe) || written;
  284             }
  285         }
  286         perportentry_free(ppe);
  287 
  288         if (written)
  289             /* NB The device type doesn't matter when we pass in an invalid index. */
  290             trigger_save(u, PA_DEVICE_TYPE_SINK, PA_INVALID_INDEX);
  291 
  292         pa_datum_free(&data);
  293         return e;
  294     }
  295     pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name);
  296 }
  297 #endif
  298 
  299     pa_datum_free(&data);
  300     return NULL;
  301 }
  302 
  303 static struct entry* entry_copy(const struct entry *e) {
  304     struct entry* r;
  305 
  306     pa_assert(e);
  307     r = entry_new();
  308     r->version = e->version;
  309     r->port_valid = e->port_valid;
  310     r->port = pa_xstrdup(e->port);
  311 
  312     return r;
  313 }
  314 
  315 static bool entries_equal(const struct entry *a, const struct entry *b) {
  316 
  317     pa_assert(a && b);
  318 
  319     if (a->port_valid != b->port_valid ||
  320         (a->port_valid && !pa_streq(a->port, b->port)))
  321         return false;
  322 
  323     return true;
  324 }
  325 
  326 static struct perportentry* perportentry_new(bool add_pcm_format) {
  327     struct perportentry *r = pa_xnew0(struct perportentry, 1);
  328     r->version = PERPORTENTRY_VERSION;
  329     r->formats = pa_idxset_new(NULL, NULL);
  330     if (add_pcm_format) {
  331         pa_format_info *f = pa_format_info_new();
  332         f->encoding = PA_ENCODING_PCM;
  333         pa_idxset_put(r->formats, f, NULL);
  334     }
  335     return r;
  336 }
  337 
  338 static void perportentry_free(struct perportentry* e) {
  339     pa_assert(e);
  340 
  341     pa_idxset_free(e->formats, (pa_free_cb_t) pa_format_info_free);
  342     pa_xfree(e);
  343 }
  344 
  345 static bool perportentry_write(struct userdata *u, const char *basekeyname, const char *port, const struct perportentry *e) {
  346     pa_tagstruct *t;
  347     pa_datum key, data;
  348     bool r;
  349     uint32_t i;
  350     pa_format_info *f;
  351     uint8_t n_formats;
  352     char *name;
  353 
  354     pa_assert(u);
  355     pa_assert(basekeyname);
  356     pa_assert(e);
  357 
  358     name = pa_sprintf_malloc("%s:%s", basekeyname, (port ? port : "null"));
  359 
  360     n_formats = pa_idxset_size(e->formats);
  361     pa_assert(n_formats > 0);
  362 
  363     t = pa_tagstruct_new();
  364     pa_tagstruct_putu8(t, e->version);
  365     pa_tagstruct_put_boolean(t, e->volume_valid);
  366     pa_tagstruct_put_channel_map(t, &e->channel_map);
  367     pa_tagstruct_put_cvolume(t, &e->volume);
  368     pa_tagstruct_put_boolean(t, e->muted_valid);
  369     pa_tagstruct_put_boolean(t, e->muted);
  370     pa_tagstruct_putu8(t, n_formats);
  371 
  372     PA_IDXSET_FOREACH(f, e->formats, i) {
  373         pa_tagstruct_put_format_info(t, f);
  374     }
  375 
  376     key.data = (char *) name;
  377     key.size = strlen(name);
  378 
  379     data.data = (void*)pa_tagstruct_data(t, &data.size);
  380 
  381     r = (pa_database_set(u->database, &key, &data, true) == 0);
  382 
  383     pa_tagstruct_free(t);
  384     pa_xfree(name);
  385 
  386     return r;
  387 }
  388 
  389 static struct perportentry* perportentry_read(struct userdata *u, const char *basekeyname, const char *port) {
  390     pa_datum key, data;
  391     struct perportentry *e = NULL;
  392     pa_tagstruct *t = NULL;
  393     uint8_t i, n_formats;
  394     char *name;
  395 
  396     pa_assert(u);
  397     pa_assert(basekeyname);
  398 
  399     name = pa_sprintf_malloc("%s:%s", basekeyname, (port ? port : "null"));
  400 
  401     key.data = name;
  402     key.size = strlen(name);
  403 
  404     pa_zero(data);
  405 
  406     if (!pa_database_get(u->database, &key, &data))
  407         goto fail;
  408 
  409     t = pa_tagstruct_new_fixed(data.data, data.size);
  410     e = perportentry_new(false);
  411 
  412     if (pa_tagstruct_getu8(t, &e->version) < 0 ||
  413         e->version > PERPORTENTRY_VERSION ||
  414         pa_tagstruct_get_boolean(t, &e->volume_valid) < 0 ||
  415         pa_tagstruct_get_channel_map(t, &e->channel_map) < 0 ||
  416         pa_tagstruct_get_cvolume(t, &e->volume) < 0 ||
  417         pa_tagstruct_get_boolean(t, &e->muted_valid) < 0 ||
  418         pa_tagstruct_get_boolean(t, &e->muted) < 0 ||
  419         pa_tagstruct_getu8(t, &n_formats) < 0 || n_formats < 1) {
  420 
  421         goto fail;
  422     }
  423 
  424     for (i = 0; i < n_formats; ++i) {
  425         pa_format_info *f = pa_format_info_new();
  426         if (pa_tagstruct_get_format_info(t, f) < 0) {
  427             pa_format_info_free(f);
  428             goto fail;
  429         }
  430         pa_idxset_put(e->formats, f, NULL);
  431     }
  432 
  433     if (!pa_tagstruct_eof(t))
  434         goto fail;
  435 
  436     if (e->volume_valid && !pa_channel_map_valid(&e->channel_map)) {
  437         pa_log_warn("Invalid channel map stored in database for device %s", name);
  438         goto fail;
  439     }
  440 
  441     if (e->volume_valid && (!pa_cvolume_valid(&e->volume) || !pa_cvolume_compatible_with_channel_map(&e->volume, &e->channel_map))) {
  442         pa_log_warn("Volume and channel map don't match in database entry for device %s", name);
  443         goto fail;
  444     }
  445 
  446     pa_tagstruct_free(t);
  447     pa_datum_free(&data);
  448     pa_xfree(name);
  449 
  450     return e;
  451 
  452 fail:
  453 
  454     if (e)
  455         perportentry_free(e);
  456     if (t)
  457         pa_tagstruct_free(t);
  458 
  459     pa_datum_free(&data);
  460 
  461 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
  462     /* Try again with a null port. This is used when dealing with migration from older versions */
  463     if (port) {
  464         pa_xfree(name);
  465         return perportentry_read(u, basekeyname, NULL);
  466     }
  467 #endif
  468 
  469     pa_log_debug("Database contains no (or invalid) data for key: %s", name);
  470 
  471     pa_xfree(name);
  472 
  473     return NULL;
  474 }
  475 
  476 static struct perportentry* perportentry_copy(const struct perportentry *e) {
  477     struct perportentry* r;
  478     uint32_t idx;
  479     pa_format_info *f;
  480 
  481     pa_assert(e);
  482     r = perportentry_new(false);
  483     r->version = e->version;
  484     r->muted_valid = e->muted_valid;
  485     r->volume_valid = e->volume_valid;
  486     r->muted = e->muted;
  487     r->channel_map = e->channel_map;
  488     r->volume = e->volume;
  489 
  490     PA_IDXSET_FOREACH(f, e->formats, idx) {
  491         pa_idxset_put(r->formats, pa_format_info_copy(f), NULL);
  492     }
  493     return r;
  494 }
  495 
  496 static bool perportentries_equal(const struct perportentry *a, const struct perportentry *b) {
  497     pa_cvolume t;
  498 
  499     pa_assert(a && b);
  500 
  501     if (a->muted_valid != b->muted_valid ||
  502         (a->muted_valid && (a->muted != b->muted)))
  503         return false;
  504 
  505     t = b->volume;
  506     if (a->volume_valid != b->volume_valid ||
  507         (a->volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->volume)))
  508         return false;
  509 
  510     if (pa_idxset_size(a->formats) != pa_idxset_size(b->formats))
  511         return false;
  512 
  513     /** TODO: Compare a bit better */
  514 
  515     return true;
  516 }
  517 
  518 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
  519 
  520 #define LEGACY_ENTRY_VERSION 2
  521 static bool legacy_entry_read(struct userdata *u, pa_datum *data, struct entry **entry, struct perportentry **perportentry) {
  522     struct legacy_entry {
  523         uint8_t version;
  524         bool muted_valid:1, volume_valid:1, port_valid:1;
  525         bool muted:1;
  526         pa_channel_map channel_map;
  527         pa_cvolume volume;
  528         char port[PA_NAME_MAX];
  529     } PA_GCC_PACKED;
  530     struct legacy_entry *le;
  531 
  532     pa_assert(u);
  533     pa_assert(data);
  534     pa_assert(entry);
  535     pa_assert(perportentry);
  536 
  537     if (data->size != sizeof(struct legacy_entry)) {
  538         pa_log_debug("Size does not match.");
  539         return false;
  540     }
  541 
  542     le = (struct legacy_entry*)data->data;
  543 
  544     if (le->version != LEGACY_ENTRY_VERSION) {
  545         pa_log_debug("Version mismatch.");
  546         return false;
  547     }
  548 
  549     if (!memchr(le->port, 0, sizeof(le->port))) {
  550         pa_log_warn("Port has missing NUL byte.");
  551         return false;
  552     }
  553 
  554     if (le->volume_valid && !pa_channel_map_valid(&le->channel_map)) {
  555         pa_log_warn("Invalid channel map.");
  556         return false;
  557     }
  558 
  559     if (le->volume_valid && (!pa_cvolume_valid(&le->volume) || !pa_cvolume_compatible_with_channel_map(&le->volume, &le->channel_map))) {
  560         pa_log_warn("Volume and channel map don't match.");
  561         return false;
  562     }
  563 
  564     *entry = entry_new();
  565     (*entry)->port_valid = le->port_valid;
  566     (*entry)->port = pa_xstrdup(le->port);
  567 
  568     *perportentry = perportentry_new(true);
  569     (*perportentry)->muted_valid = le->muted_valid;
  570     (*perportentry)->volume_valid = le->volume_valid;
  571     (*perportentry)->muted = le->muted;
  572     (*perportentry)->channel_map = le->channel_map;
  573     (*perportentry)->volume = le->volume;
  574 
  575     return true;
  576 }
  577 #endif
  578 
  579 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
  580     struct userdata *u = userdata;
  581     struct entry *e, *olde;
  582     struct perportentry *ppe, *oldppe;
  583     char *name;
  584     const char *port = NULL;
  585     pa_device_type_t type;
  586     bool written = false;
  587 
  588     pa_assert(c);
  589     pa_assert(u);
  590 
  591     if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
  592         t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
  593         t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
  594         t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
  595         return;
  596 
  597     if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
  598         pa_sink *sink;
  599 
  600         if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
  601             return;
  602 
  603         type = PA_DEVICE_TYPE_SINK;
  604         name = pa_sprintf_malloc("sink:%s", sink->name);
  605         if (sink->active_port)
  606             port = sink->active_port->name;
  607 
  608         if ((olde = entry_read(u, name)))
  609             e = entry_copy(olde);
  610         else
  611             e = entry_new();
  612 
  613         if (sink->save_port) {
  614             pa_xfree(e->port);
  615             e->port = pa_xstrdup(port ? port : "");
  616             e->port_valid = true;
  617         }
  618 
  619         if ((oldppe = perportentry_read(u, name, port)))
  620             ppe = perportentry_copy(oldppe);
  621         else
  622             ppe = perportentry_new(true);
  623 
  624         if (sink->save_volume) {
  625             ppe->channel_map = sink->channel_map;
  626             ppe->volume = *pa_sink_get_volume(sink, false);
  627             ppe->volume_valid = true;
  628         }
  629 
  630         if (sink->save_muted) {
  631             ppe->muted = pa_sink_get_mute(sink, false);
  632             ppe->muted_valid = true;
  633         }
  634     } else {
  635         pa_source *source;
  636 
  637         pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
  638 
  639         if (!(source = pa_idxset_get_by_index(c->sources, idx)))
  640             return;
  641 
  642         type = PA_DEVICE_TYPE_SOURCE;
  643         name = pa_sprintf_malloc("source:%s", source->name);
  644         if (source->active_port)
  645             port = source->active_port->name;
  646 
  647         if ((olde = entry_read(u, name)))
  648             e = entry_copy(olde);
  649         else
  650             e = entry_new();
  651 
  652         if (source->save_port) {
  653             pa_xfree(e->port);
  654             e->port = pa_xstrdup(port ? port : "");
  655             e->port_valid = true;
  656         }
  657 
  658         if ((oldppe = perportentry_read(u, name, port)))
  659             ppe = perportentry_copy(oldppe);
  660         else
  661             ppe = perportentry_new(true);
  662 
  663         if (source->save_volume) {
  664             ppe->channel_map = source->channel_map;
  665             ppe->volume = *pa_source_get_volume(source, false);
  666             ppe->volume_valid = true;
  667         }
  668 
  669         if (source->save_muted) {
  670             ppe->muted = pa_source_get_mute(source, false);
  671             ppe->muted_valid = true;
  672         }
  673     }
  674 
  675     pa_assert(e);
  676 
  677     if (olde) {
  678 
  679         if (entries_equal(olde, e)) {
  680             entry_free(olde);
  681             entry_free(e);
  682             e = NULL;
  683         } else
  684             entry_free(olde);
  685     }
  686 
  687     if (e) {
  688         pa_log_info("Storing port for device %s.", name);
  689 
  690         written = entry_write(u, name, e);
  691 
  692         entry_free(e);
  693     }
  694 
  695     pa_assert(ppe);
  696 
  697     if (oldppe) {
  698 
  699         if (perportentries_equal(oldppe, ppe)) {
  700             perportentry_free(oldppe);
  701             perportentry_free(ppe);
  702             ppe = NULL;
  703         } else
  704             perportentry_free(oldppe);
  705     }
  706 
  707     if (ppe) {
  708         pa_log_info("Storing volume/mute for device+port %s:%s.", name, (port ? port : "null"));
  709 
  710         written = perportentry_write(u, name, port, ppe) || written;
  711 
  712         perportentry_free(ppe);
  713     }
  714     pa_xfree(name);
  715 
  716     if (written)
  717         trigger_save(u, type, idx);
  718 }
  719 
  720 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
  721     char *name;
  722     struct entry *e;
  723 
  724     pa_assert(c);
  725     pa_assert(new_data);
  726     pa_assert(u);
  727     pa_assert(u->restore_port);
  728 
  729     name = pa_sprintf_malloc("sink:%s", new_data->name);
  730 
  731     if ((e = entry_read(u, name))) {
  732 
  733         if (e->port_valid) {
  734             if (!new_data->active_port) {
  735                 pa_log_info("Restoring port for sink %s.", name);
  736                 pa_sink_new_data_set_port(new_data, e->port);
  737                 new_data->save_port = true;
  738             } else
  739                 pa_log_debug("Not restoring port for sink %s, because already set.", name);
  740         }
  741 
  742         entry_free(e);
  743     }
  744 
  745     pa_xfree(name);
  746 
  747     return PA_HOOK_OK;
  748 }
  749 
  750 static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
  751     char *name;
  752     struct perportentry *e;
  753 
  754     pa_assert(c);
  755     pa_assert(new_data);
  756     pa_assert(u);
  757     pa_assert(u->restore_volume || u->restore_muted);
  758 
  759     name = pa_sprintf_malloc("sink:%s", new_data->name);
  760 
  761     if ((e = perportentry_read(u, name, new_data->active_port))) {
  762 
  763         if (u->restore_volume && e->volume_valid) {
  764 
  765             if (!new_data->volume_is_set) {
  766                 pa_cvolume v;
  767                 char buf[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
  768 
  769                 v = e->volume;
  770                 pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map);
  771                 pa_sink_new_data_set_volume(new_data, &v);
  772                 pa_log_info("Restoring volume for sink %s: %s", new_data->name,
  773                             pa_cvolume_snprint_verbose(buf, sizeof(buf), &new_data->volume, &new_data->channel_map, false));
  774 
  775                 new_data->save_volume = true;
  776             } else
  777                 pa_log_debug("Not restoring volume for sink %s, because already set.", new_data->name);
  778         }
  779 
  780         if (u->restore_muted && e->muted_valid) {
  781 
  782             if (!new_data->muted_is_set) {
  783                 pa_sink_new_data_set_muted(new_data, e->muted);
  784                 new_data->save_muted = true;
  785                 pa_log_info("Restoring mute state for sink %s: %smuted", new_data->name,
  786                             new_data->muted ? "" : "un");
  787             } else
  788                 pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data->name);
  789         }
  790 
  791         perportentry_free(e);
  792     }
  793 
  794     pa_xfree(name);
  795 
  796     return PA_HOOK_OK;
  797 }
  798 
  799 static pa_hook_result_t sink_port_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
  800     char *name;
  801     struct perportentry *e;
  802 
  803     pa_assert(c);
  804     pa_assert(sink);
  805     pa_assert(u);
  806     pa_assert(u->restore_volume || u->restore_muted);
  807 
  808     name = pa_sprintf_malloc("sink:%s", sink->name);
  809 
  810     if ((e = perportentry_read(u, name, (sink->active_port ? sink->active_port->name : NULL)))) {
  811 
  812         if (u->restore_volume && e->volume_valid) {
  813             pa_cvolume v;
  814 
  815             pa_log_info("Restoring volume for sink %s.", sink->name);
  816             v = e->volume;
  817             pa_cvolume_remap(&v, &e->channel_map, &sink->channel_map);
  818             pa_sink_set_volume(sink, &v, true, false);
  819 
  820             sink->save_volume = true;
  821         }
  822 
  823         if (u->restore_muted && e->muted_valid) {
  824 
  825             pa_log_info("Restoring mute state for sink %s.", sink->name);
  826             pa_sink_set_mute(sink, e->muted, false);
  827             sink->save_muted = true;
  828         }
  829 
  830         perportentry_free(e);
  831     }
  832 
  833     pa_xfree(name);
  834 
  835     return PA_HOOK_OK;
  836 }
  837 
  838 static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
  839     char *name;
  840     struct perportentry *e;
  841 
  842     pa_assert(c);
  843     pa_assert(sink);
  844     pa_assert(u);
  845     pa_assert(u->restore_formats);
  846 
  847     name = pa_sprintf_malloc("sink:%s", sink->name);
  848 
  849     if ((e = perportentry_read(u, name, (sink->active_port ? sink->active_port->name : NULL)))) {
  850 
  851         if (!pa_sink_set_formats(sink, e->formats))
  852             pa_log_debug("Could not set format on sink %s", sink->name);
  853 
  854         perportentry_free(e);
  855     }
  856 
  857     pa_xfree(name);
  858 
  859     return PA_HOOK_OK;
  860 }
  861 
  862 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
  863     char *name;
  864     struct entry *e;
  865 
  866     pa_assert(c);
  867     pa_assert(new_data);
  868     pa_assert(u);
  869     pa_assert(u->restore_port);
  870 
  871     name = pa_sprintf_malloc("source:%s", new_data->name);
  872 
  873     if ((e = entry_read(u, name))) {
  874 
  875         if (e->port_valid) {
  876             if (!new_data->active_port) {
  877                 pa_log_info("Restoring port for source %s.", name);
  878                 pa_source_new_data_set_port(new_data, e->port);
  879                 new_data->save_port = true;
  880             } else
  881                 pa_log_debug("Not restoring port for source %s, because already set.", name);
  882         }
  883 
  884         entry_free(e);
  885     }
  886 
  887     pa_xfree(name);
  888 
  889     return PA_HOOK_OK;
  890 }
  891 
  892 static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
  893     char *name;
  894     struct perportentry *e;
  895 
  896     pa_assert(c);
  897     pa_assert(new_data);
  898     pa_assert(u);
  899     pa_assert(u->restore_volume || u->restore_muted);
  900 
  901     name = pa_sprintf_malloc("source:%s", new_data->name);
  902 
  903     if ((e = perportentry_read(u, name, new_data->active_port))) {
  904 
  905         if (u->restore_volume && e->volume_valid) {
  906 
  907             if (!new_data->volume_is_set) {
  908                 pa_cvolume v;
  909                 char buf[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
  910 
  911                 v = e->volume;
  912                 pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map);
  913                 pa_source_new_data_set_volume(new_data, &v);
  914                 pa_log_info("Restoring volume for source %s: %s", new_data->name,
  915                             pa_cvolume_snprint_verbose(buf, sizeof(buf), &new_data->volume, &new_data->channel_map, false));
  916 
  917                 new_data->save_volume = true;
  918             } else
  919                 pa_log_debug("Not restoring volume for source %s, because already set.", new_data->name);
  920         }
  921 
  922         if (u->restore_muted && e->muted_valid) {
  923 
  924             if (!new_data->muted_is_set) {
  925                 pa_source_new_data_set_muted(new_data, e->muted);
  926                 new_data->save_muted = true;
  927                 pa_log_info("Restoring mute state for source %s: %smuted", new_data->name,
  928                             new_data->muted ? "" : "un");
  929             } else
  930                 pa_log_debug("Not restoring mute state for source %s, because already set.", new_data->name);
  931         }
  932 
  933         perportentry_free(e);
  934     }
  935 
  936     pa_xfree(name);
  937 
  938     return PA_HOOK_OK;
  939 }
  940 
  941 static pa_hook_result_t source_port_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
  942     char *name;
  943     struct perportentry *e;
  944 
  945     pa_assert(c);
  946     pa_assert(source);
  947     pa_assert(u);
  948     pa_assert(u->restore_volume || u->restore_muted);
  949 
  950     name = pa_sprintf_malloc("source:%s", source->name);
  951 
  952     if ((e = perportentry_read(u, name, (source->active_port ? source->active_port->name : NULL)))) {
  953 
  954         if (u->restore_volume && e->volume_valid) {
  955             pa_cvolume v;
  956 
  957             pa_log_info("Restoring volume for source %s.", source->name);
  958             v = e->volume;
  959             pa_cvolume_remap(&v, &e->channel_map, &source->channel_map);
  960             pa_source_set_volume(source, &v, true, false);
  961 
  962             source->save_volume = true;
  963         }
  964 
  965         if (u->restore_muted && e->muted_valid) {
  966 
  967             pa_log_info("Restoring mute state for source %s.", source->name);
  968             pa_source_set_mute(source, e->muted, false);
  969             source->save_muted = true;
  970         }
  971 
  972         perportentry_free(e);
  973     }
  974 
  975     pa_xfree(name);
  976 
  977     return PA_HOOK_OK;
  978 }
  979 
  980 #define EXT_VERSION 1
  981 
  982 static void read_sink_format_reply(struct userdata *u, pa_tagstruct *reply, pa_sink *sink) {
  983     struct perportentry *e;
  984     char *name;
  985 
  986     pa_assert(u);
  987     pa_assert(reply);
  988     pa_assert(sink);
  989 
  990     pa_tagstruct_putu32(reply, PA_DEVICE_TYPE_SINK);
  991     pa_tagstruct_putu32(reply, sink->index);
  992 
  993     /* Read or create an entry */
  994     name = pa_sprintf_malloc("sink:%s", sink->name);
  995     if (!(e = perportentry_read(u, name, (sink->active_port ? sink->active_port->name : NULL)))) {
  996         /* Fake a reply with PCM encoding supported */
  997         pa_format_info *f = pa_format_info_new();
  998 
  999         pa_tagstruct_putu8(reply, 1);
 1000         f->encoding = PA_ENCODING_PCM;
 1001         pa_tagstruct_put_format_info(reply, f);
 1002 
 1003         pa_format_info_free(f);
 1004     } else {
 1005         uint32_t idx;
 1006         pa_format_info *f;
 1007 
 1008         /* Write all the formats from the entry to the reply */
 1009         pa_tagstruct_putu8(reply, pa_idxset_size(e->formats));
 1010         PA_IDXSET_FOREACH(f, e->formats, idx) {
 1011             pa_tagstruct_put_format_info(reply, f);
 1012         }
 1013         perportentry_free(e);
 1014     }
 1015     pa_xfree(name);
 1016 }
 1017 
 1018 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
 1019     struct userdata *u;
 1020     uint32_t command;
 1021     pa_tagstruct *reply = NULL;
 1022 
 1023     pa_assert(p);
 1024     pa_assert(m);
 1025     pa_assert(c);
 1026     pa_assert(t);
 1027 
 1028     u = m->userdata;
 1029 
 1030     if (pa_tagstruct_getu32(t, &command) < 0)
 1031         goto fail;
 1032 
 1033     reply = pa_tagstruct_new();
 1034     pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
 1035     pa_tagstruct_putu32(reply, tag);
 1036 
 1037     switch (command) {
 1038         case SUBCOMMAND_TEST: {
 1039             if (!pa_tagstruct_eof(t))
 1040                 goto fail;
 1041 
 1042             pa_tagstruct_putu32(reply, EXT_VERSION);
 1043             break;
 1044         }
 1045 
 1046         case SUBCOMMAND_SUBSCRIBE: {
 1047 
 1048             bool enabled;
 1049 
 1050             if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
 1051                 !pa_tagstruct_eof(t))
 1052                 goto fail;
 1053 
 1054             if (enabled)
 1055                 pa_idxset_put(u->subscribed, c, NULL);
 1056             else
 1057                 pa_idxset_remove_by_data(u->subscribed, c, NULL);
 1058 
 1059             break;
 1060         }
 1061 
 1062         case SUBCOMMAND_READ_FORMATS_ALL: {
 1063             pa_sink *sink;
 1064             uint32_t idx;
 1065 
 1066             if (!pa_tagstruct_eof(t))
 1067                 goto fail;
 1068 
 1069             PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
 1070                 read_sink_format_reply(u, reply, sink);
 1071             }
 1072 
 1073             break;
 1074         }
 1075         case SUBCOMMAND_READ_FORMATS: {
 1076             pa_device_type_t type;
 1077             uint32_t sink_index;
 1078             pa_sink *sink;
 1079 
 1080             pa_assert(reply);
 1081 
 1082             /* Get the sink index and the number of formats from the tagstruct */
 1083             if (pa_tagstruct_getu32(t, &type) < 0 ||
 1084                 pa_tagstruct_getu32(t, &sink_index) < 0)
 1085                 goto fail;
 1086 
 1087             if (type != PA_DEVICE_TYPE_SINK) {
 1088                 pa_log("Device format reading is only supported on sinks");
 1089                 goto fail;
 1090             }
 1091 
 1092             if (!pa_tagstruct_eof(t))
 1093                 goto fail;
 1094 
 1095             /* Now find our sink */
 1096             if (!(sink = pa_idxset_get_by_index(u->core->sinks, sink_index)))
 1097                 goto fail;
 1098 
 1099             read_sink_format_reply(u, reply, sink);
 1100 
 1101             break;
 1102         }
 1103 
 1104         case SUBCOMMAND_SAVE_FORMATS: {
 1105 
 1106             struct perportentry *e;
 1107             pa_device_type_t type;
 1108             uint32_t sink_index;
 1109             char *name;
 1110             pa_sink *sink;
 1111             uint8_t i, n_formats;
 1112 
 1113             /* Get the sink index and the number of formats from the tagstruct */
 1114             if (pa_tagstruct_getu32(t, &type) < 0 ||
 1115                 pa_tagstruct_getu32(t, &sink_index) < 0 ||
 1116                 pa_tagstruct_getu8(t, &n_formats) < 0 || n_formats < 1) {
 1117 
 1118                 goto fail;
 1119             }
 1120 
 1121             if (type != PA_DEVICE_TYPE_SINK) {
 1122                 pa_log("Device format saving is only supported on sinks");
 1123                 goto fail;
 1124             }
 1125 
 1126             /* Now find our sink */
 1127             if (!(sink = pa_idxset_get_by_index(u->core->sinks, sink_index))) {
 1128                 pa_log("Could not find sink #%d", sink_index);
 1129                 goto fail;
 1130             }
 1131 
 1132             /* Read or create an entry */
 1133             name = pa_sprintf_malloc("sink:%s", sink->name);
 1134             if (!(e = perportentry_read(u, name, (sink->active_port ? sink->active_port->name : NULL))))
 1135                 e = perportentry_new(false);
 1136             else {
 1137                 /* Clean out any saved formats */
 1138                 pa_idxset_free(e->formats, (pa_free_cb_t) pa_format_info_free);
 1139                 e->formats = pa_idxset_new(NULL, NULL);
 1140             }
 1141 
 1142             /* Read all the formats from our tagstruct */
 1143             for (i = 0; i < n_formats; ++i) {
 1144                 pa_format_info *f = pa_format_info_new();
 1145                 if (pa_tagstruct_get_format_info(t, f) < 0) {
 1146                     pa_format_info_free(f);
 1147                     perportentry_free(e);
 1148                     pa_xfree(name);
 1149                     goto fail;
 1150                 }
 1151                 pa_idxset_put(e->formats, f, NULL);
 1152             }
 1153 
 1154             if (!pa_tagstruct_eof(t)) {
 1155                 perportentry_free(e);
 1156                 pa_xfree(name);
 1157                 goto fail;
 1158             }
 1159 
 1160             if (pa_sink_set_formats(sink, e->formats) && perportentry_write(u, name, (sink->active_port ? sink->active_port->name : NULL), e))
 1161                 trigger_save(u, type, sink_index);
 1162             else
 1163                 pa_log_warn("Could not save format info for sink %s", sink->name);
 1164 
 1165             pa_xfree(name);
 1166             perportentry_free(e);
 1167 
 1168             break;
 1169         }
 1170 
 1171         default:
 1172             goto fail;
 1173     }
 1174 
 1175     pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
 1176     return 0;
 1177 
 1178 fail:
 1179 
 1180     if (reply)
 1181         pa_tagstruct_free(reply);
 1182 
 1183     return -1;
 1184 }
 1185 
 1186 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
 1187     pa_assert(p);
 1188     pa_assert(c);
 1189     pa_assert(u);
 1190 
 1191     pa_idxset_remove_by_data(u->subscribed, c, NULL);
 1192     return PA_HOOK_OK;
 1193 }
 1194 
 1195 int pa__init(pa_module*m) {
 1196     pa_modargs *ma = NULL;
 1197     struct userdata *u;
 1198     char *state_path;
 1199     pa_sink *sink;
 1200     pa_source *source;
 1201     uint32_t idx;
 1202     bool restore_volume = true, restore_muted = true, restore_port = true, restore_formats = true;
 1203 
 1204     pa_assert(m);
 1205 
 1206     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
 1207         pa_log("Failed to parse module arguments");
 1208         goto fail;
 1209     }
 1210 
 1211     if (pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0 ||
 1212         pa_modargs_get_value_boolean(ma, "restore_muted", &restore_muted) < 0 ||
 1213         pa_modargs_get_value_boolean(ma, "restore_port", &restore_port) < 0 ||
 1214         pa_modargs_get_value_boolean(ma, "restore_formats", &restore_formats) < 0) {
 1215         pa_log("restore_port, restore_volume, restore_muted and restore_formats expect boolean arguments");
 1216         goto fail;
 1217     }
 1218 
 1219     if (!restore_muted && !restore_volume && !restore_port && !restore_formats)
 1220         pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring port enabled!");
 1221 
 1222     m->userdata = u = pa_xnew0(struct userdata, 1);
 1223     u->core = m->core;
 1224     u->module = m;
 1225     u->restore_volume = restore_volume;
 1226     u->restore_muted = restore_muted;
 1227     u->restore_port = restore_port;
 1228     u->restore_formats = restore_formats;
 1229 
 1230     u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 1231 
 1232     u->protocol = pa_native_protocol_get(m->core);
 1233     pa_native_protocol_install_ext(u->protocol, m, extension_cb);
 1234 
 1235     pa_module_hook_connect(m, &pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_CONNECTION_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) connection_unlink_hook_cb, u);
 1236 
 1237     u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
 1238 
 1239     if (restore_port) {
 1240         pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_new_hook_callback, u);
 1241         pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_new_hook_callback, u);
 1242     }
 1243 
 1244     if (restore_muted || restore_volume) {
 1245         pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_fixate_hook_callback, u);
 1246         pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) source_fixate_hook_callback, u);
 1247 
 1248         pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED], PA_HOOK_EARLY, (pa_hook_cb_t) sink_port_hook_callback, u);
 1249         pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_PORT_CHANGED], PA_HOOK_EARLY, (pa_hook_cb_t) source_port_hook_callback, u);
 1250     }
 1251 
 1252     if (restore_formats)
 1253         pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_EARLY, (pa_hook_cb_t) sink_put_hook_callback, u);
 1254 
 1255     if (!(state_path = pa_state_path(NULL, true)))
 1256         goto fail;
 1257 
 1258     if (!(u->database = pa_database_open(state_path, "device-volumes", true, true))) {
 1259         pa_xfree(state_path);
 1260         goto fail;
 1261     }
 1262 
 1263     pa_xfree(state_path);
 1264 
 1265     PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
 1266         subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
 1267 
 1268     PA_IDXSET_FOREACH(source, m->core->sources, idx)
 1269         subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
 1270 
 1271     pa_modargs_free(ma);
 1272     return 0;
 1273 
 1274 fail:
 1275     pa__done(m);
 1276 
 1277     if (ma)
 1278         pa_modargs_free(ma);
 1279 
 1280     return -1;
 1281 }
 1282 
 1283 void pa__done(pa_module*m) {
 1284     struct userdata* u;
 1285 
 1286     pa_assert(m);
 1287 
 1288     if (!(u = m->userdata))
 1289         return;
 1290 
 1291     if (u->subscription)
 1292         pa_subscription_free(u->subscription);
 1293 
 1294     if (u->save_time_event) {
 1295         u->core->mainloop->time_free(u->save_time_event);
 1296         pa_database_sync(u->database);
 1297     }
 1298 
 1299     if (u->database)
 1300         pa_database_close(u->database);
 1301 
 1302     if (u->protocol) {
 1303         pa_native_protocol_remove_ext(u->protocol, m);
 1304         pa_native_protocol_unref(u->protocol);
 1305     }
 1306 
 1307     if (u->subscribed)
 1308         pa_idxset_free(u->subscribed, NULL);
 1309 
 1310     pa_xfree(u);
 1311 }