]> code.delx.au - pulseaudio/blobdiff - src/pulsecore/cli-command.c
def: Hide server-side sink/source flags
[pulseaudio] / src / pulsecore / cli-command.c
index 5e45c1aa041fce3afc5a3bbc5a748e1defdcef07..6a2e5ced6f42a65843dc431fbff41086ca1b6ed5 100644 (file)
@@ -6,7 +6,7 @@
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
+  by the Free Software Foundation; either version 2.1 of the License,
   or (at your option) any later version.
 
   PulseAudio is distributed in the hope that it will be useful, but
   or (at your option) any later version.
 
   PulseAudio is distributed in the hope that it will be useful, but
 #include <errno.h>
 #include <unistd.h>
 #include <ltdl.h>
 #include <errno.h>
 #include <unistd.h>
 #include <ltdl.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <time.h>
 
 #include <pulse/xmalloc.h>
 
 #include <pulse/xmalloc.h>
+#include <pulse/error.h>
 
 #include <pulsecore/module.h>
 #include <pulsecore/sink.h>
 
 #include <pulsecore/module.h>
 #include <pulsecore/sink.h>
@@ -44,7 +48,6 @@
 #include <pulsecore/namereg.h>
 #include <pulsecore/cli-text.h>
 #include <pulsecore/core-scache.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/cli-text.h>
 #include <pulsecore/core-scache.h>
-#include <pulsecore/sample-util.h>
 #include <pulsecore/sound-file.h>
 #include <pulsecore/play-memchunk.h>
 #include <pulsecore/sound-file-stream.h>
 #include <pulsecore/sound-file.h>
 #include <pulsecore/play-memchunk.h>
 #include <pulsecore/sound-file-stream.h>
@@ -52,6 +55,7 @@
 #include <pulsecore/core-util.h>
 #include <pulsecore/core-error.h>
 #include <pulsecore/modinfo.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/core-error.h>
 #include <pulsecore/modinfo.h>
+#include <pulsecore/dynarray.h>
 
 #include "cli-command.h"
 
 
 #include "cli-command.h"
 
@@ -124,6 +128,8 @@ static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa
 static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 
 /* A method table for all available commands */
 
 
 /* A method table for all available commands */
 
@@ -174,11 +180,13 @@ static const struct command commands[] = {
     { "suspend-sink",            pa_cli_command_suspend_sink,       "Suspend sink (args: index|name, bool)", 3},
     { "suspend-source",          pa_cli_command_suspend_source,     "Suspend source (args: index|name, bool)", 3},
     { "suspend",                 pa_cli_command_suspend,            "Suspend all sinks and all sources (args: bool)", 2},
     { "suspend-sink",            pa_cli_command_suspend_sink,       "Suspend sink (args: index|name, bool)", 3},
     { "suspend-source",          pa_cli_command_suspend_source,     "Suspend source (args: index|name, bool)", 3},
     { "suspend",                 pa_cli_command_suspend,            "Suspend all sinks and all sources (args: bool)", 2},
-    { "set-card-profile",        pa_cli_command_card_profile,       "Change the profile of a card (aargs: index, name)", 3},
+    { "set-card-profile",        pa_cli_command_card_profile,       "Change the profile of a card (args: index, name)", 3},
+    { "set-sink-port",           pa_cli_command_sink_port,          "Change the port of a sink (args: index, name)", 3},
+    { "set-source-port",         pa_cli_command_source_port,        "Change the port of a source (args: index, name)", 3},
     { "set-log-level",           pa_cli_command_log_level,          "Change the log level (args: numeric level)", 2},
     { "set-log-meta",            pa_cli_command_log_meta,           "Show source code location in log messages (args: bool)", 2},
     { "set-log-time",            pa_cli_command_log_time,           "Show timestamps in log messages (args: bool)", 2},
     { "set-log-level",           pa_cli_command_log_level,          "Change the log level (args: numeric level)", 2},
     { "set-log-meta",            pa_cli_command_log_meta,           "Show source code location in log messages (args: bool)", 2},
     { "set-log-time",            pa_cli_command_log_time,           "Show timestamps in log messages (args: bool)", 2},
-    { "set-log-backtrace",       pa_cli_command_log_backtrace,      "Show bakctrace in log messages (args: frames)", 2},
+    { "set-log-backtrace",       pa_cli_command_log_backtrace,      "Show backtrace in log messages (args: frames)", 2},
     { NULL, NULL, NULL, 0 }
 };
 
     { NULL, NULL, NULL, 0 }
 };
 
@@ -321,8 +329,10 @@ static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf
 }
 
 static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
 }
 
 static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
-    char s[256];
-    const pa_mempool_stat *stat;
+    char ss[PA_SAMPLE_SPEC_SNPRINT_MAX];
+    char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    char bytes[PA_BYTES_SNPRINT_MAX];
+    const pa_mempool_stat *mstat;
     unsigned k;
     pa_sink *def_sink;
     pa_source *def_source;
     unsigned k;
     pa_sink *def_sink;
     pa_source *def_source;
@@ -341,29 +351,32 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
     pa_assert(buf);
     pa_assert(fail);
 
     pa_assert(buf);
     pa_assert(fail);
 
-    stat = pa_mempool_get_stat(c->mempool);
+    mstat = pa_mempool_get_stat(c->mempool);
 
     pa_strbuf_printf(buf, "Memory blocks currently allocated: %u, size: %s.\n",
 
     pa_strbuf_printf(buf, "Memory blocks currently allocated: %u, size: %s.\n",
-                     (unsigned) pa_atomic_load(&stat->n_allocated),
-                     pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->allocated_size)));
+                     (unsigned) pa_atomic_load(&mstat->n_allocated),
+                     pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_atomic_load(&mstat->allocated_size)));
 
     pa_strbuf_printf(buf, "Memory blocks allocated during the whole lifetime: %u, size: %s.\n",
 
     pa_strbuf_printf(buf, "Memory blocks allocated during the whole lifetime: %u, size: %s.\n",
-                     (unsigned) pa_atomic_load(&stat->n_accumulated),
-                     pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->accumulated_size)));
+                     (unsigned) pa_atomic_load(&mstat->n_accumulated),
+                     pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_atomic_load(&mstat->accumulated_size)));
 
     pa_strbuf_printf(buf, "Memory blocks imported from other processes: %u, size: %s.\n",
 
     pa_strbuf_printf(buf, "Memory blocks imported from other processes: %u, size: %s.\n",
-                     (unsigned) pa_atomic_load(&stat->n_imported),
-                     pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->imported_size)));
+                     (unsigned) pa_atomic_load(&mstat->n_imported),
+                     pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_atomic_load(&mstat->imported_size)));
 
     pa_strbuf_printf(buf, "Memory blocks exported to other processes: %u, size: %s.\n",
 
     pa_strbuf_printf(buf, "Memory blocks exported to other processes: %u, size: %s.\n",
-                     (unsigned) pa_atomic_load(&stat->n_exported),
-                     pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->exported_size)));
+                     (unsigned) pa_atomic_load(&mstat->n_exported),
+                     pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_atomic_load(&mstat->exported_size)));
 
     pa_strbuf_printf(buf, "Total sample cache size: %s.\n",
 
     pa_strbuf_printf(buf, "Total sample cache size: %s.\n",
-                     pa_bytes_snprint(s, sizeof(s), (unsigned) pa_scache_total_size(c)));
+                     pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_scache_total_size(c)));
 
     pa_strbuf_printf(buf, "Default sample spec: %s\n",
 
     pa_strbuf_printf(buf, "Default sample spec: %s\n",
-                     pa_sample_spec_snprint(s, sizeof(s), &c->default_sample_spec));
+                     pa_sample_spec_snprint(ss, sizeof(ss), &c->default_sample_spec));
+
+    pa_strbuf_printf(buf, "Default channel map: %s\n",
+                     pa_channel_map_snprint(cm, sizeof(cm), &c->default_channel_map));
 
     def_sink = pa_namereg_get_default_sink(c);
     def_source = pa_namereg_get_default_source(c);
 
     def_sink = pa_namereg_get_default_sink(c);
     def_source = pa_namereg_get_default_source(c);
@@ -376,8 +389,8 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
         pa_strbuf_printf(buf,
                          "Memory blocks of type %s: %u allocated/%u accumulated.\n",
                          type_table[k],
         pa_strbuf_printf(buf,
                          "Memory blocks of type %s: %u allocated/%u accumulated.\n",
                          type_table[k],
-                         (unsigned) pa_atomic_load(&stat->n_allocated_by_type[k]),
-                         (unsigned) pa_atomic_load(&stat->n_accumulated_by_type[k]));
+                         (unsigned) pa_atomic_load(&mstat->n_allocated_by_type[k]),
+                         (unsigned) pa_atomic_load(&mstat->n_accumulated_by_type[k]));
 
     return 0;
 }
 
     return 0;
 }
@@ -477,6 +490,8 @@ static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
             if (i->usage)
                 pa_strbuf_printf(buf, "Usage: %s\n", i->usage);
             pa_strbuf_printf(buf, "Load Once: %s\n", pa_yes_no(i->load_once));
             if (i->usage)
                 pa_strbuf_printf(buf, "Usage: %s\n", i->usage);
             pa_strbuf_printf(buf, "Load Once: %s\n", pa_yes_no(i->load_once));
+            if (i->deprecated)
+                pa_strbuf_printf(buf, "Warning, deprecated: %s\n", i->deprecated);
         }
 
         pa_modinfo_free(i);
         }
 
         pa_modinfo_free(i);
@@ -512,12 +527,17 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
         return -1;
     }
 
         return -1;
     }
 
+    if (!PA_VOLUME_IS_VALID(volume)) {
+        pa_strbuf_puts(buf, "Volume outside permissible range.\n");
+        return -1;
+    }
+
     if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
         pa_strbuf_puts(buf, "No sink found by this name or index.\n");
         return -1;
     }
 
     if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
         pa_strbuf_puts(buf, "No sink found by this name or index.\n");
         return -1;
     }
 
-    pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume);
+    pa_cvolume_set(&cvolume, 1, volume);
     pa_sink_set_volume(sink, &cvolume, TRUE, TRUE);
     return 0;
 }
     pa_sink_set_volume(sink, &cvolume, TRUE, TRUE);
     return 0;
 }
@@ -554,13 +574,23 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb
         return -1;
     }
 
         return -1;
     }
 
-    if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
+    if (!PA_VOLUME_IS_VALID(volume)) {
+        pa_strbuf_puts(buf, "Volume outside permissible range.\n");
+        return -1;
+    }
+
+    if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx))) {
         pa_strbuf_puts(buf, "No sink input found with this index.\n");
         return -1;
     }
 
         pa_strbuf_puts(buf, "No sink input found with this index.\n");
         return -1;
     }
 
-    pa_cvolume_set(&cvolume, si->sample_spec.channels, volume);
-    pa_sink_input_set_volume(si, &cvolume, TRUE);
+    if (!si->volume_writable) {
+        pa_strbuf_puts(buf, "This sink input's volume can't be changed.\n");
+        return -1;
+    }
+
+    pa_cvolume_set(&cvolume, 1, volume);
+    pa_sink_input_set_volume(si, &cvolume, TRUE, TRUE);
     return 0;
 }
 
     return 0;
 }
 
@@ -590,13 +620,18 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *
         return -1;
     }
 
         return -1;
     }
 
+    if (!PA_VOLUME_IS_VALID(volume)) {
+        pa_strbuf_puts(buf, "Volume outside permissible range.\n");
+        return -1;
+    }
+
     if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
         pa_strbuf_puts(buf, "No source found by this name or index.\n");
         return -1;
     }
 
     if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
         pa_strbuf_puts(buf, "No source found by this name or index.\n");
         return -1;
     }
 
-    pa_cvolume_set(&cvolume, source->sample_spec.channels, volume);
-    pa_source_set_volume(source, &cvolume);
+    pa_cvolume_set(&cvolume, 1, volume);
+    pa_source_set_volume(source, &cvolume, TRUE, TRUE);
     return 0;
 }
 
     return 0;
 }
 
@@ -630,7 +665,7 @@ static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
         return -1;
     }
 
         return -1;
     }
 
-    pa_sink_set_mute(sink, mute);
+    pa_sink_set_mute(sink, mute, TRUE);
     return 0;
 }
 
     return 0;
 }
 
@@ -664,7 +699,7 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
         return -1;
     }
 
         return -1;
     }
 
-    pa_source_set_mute(source, mute);
+    pa_source_set_mute(source, mute, TRUE);
     return 0;
 }
 
     return 0;
 }
 
@@ -693,7 +728,10 @@ static int pa_cli_command_update_sink_proplist(pa_core *c, pa_tokenizer *t, pa_s
         return -1;
     }
 
         return -1;
     }
 
-    p = pa_proplist_from_string(s);
+    if (!(p = pa_proplist_from_string(s))) {
+        pa_strbuf_puts(buf, "Failed to parse proplist.\n");
+        return -1;
+    }
 
     pa_sink_update_proplist(sink, PA_UPDATE_REPLACE, p);
 
 
     pa_sink_update_proplist(sink, PA_UPDATE_REPLACE, p);
 
@@ -727,7 +765,10 @@ static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa
         return -1;
     }
 
         return -1;
     }
 
-    p = pa_proplist_from_string(s);
+    if (!(p = pa_proplist_from_string(s))) {
+        pa_strbuf_puts(buf, "Failed to parse proplist.\n");
+        return -1;
+    }
 
     pa_source_update_proplist(source, PA_UPDATE_REPLACE, p);
 
 
     pa_source_update_proplist(source, PA_UPDATE_REPLACE, p);
 
@@ -767,7 +808,10 @@ static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t
         return -1;
     }
 
         return -1;
     }
 
-    p = pa_proplist_from_string(s);
+    if (!(p = pa_proplist_from_string(s))) {
+        pa_strbuf_puts(buf, "Failed to parse proplist.\n");
+        return -1;
+    }
 
     pa_sink_input_update_proplist(si, PA_UPDATE_REPLACE, p);
 
 
     pa_sink_input_update_proplist(si, PA_UPDATE_REPLACE, p);
 
@@ -807,7 +851,10 @@ static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer
         return -1;
     }
 
         return -1;
     }
 
-    p = pa_proplist_from_string(s);
+    if (!(p = pa_proplist_from_string(s))) {
+        pa_strbuf_puts(buf, "Failed to parse proplist.\n");
+        return -1;
+    }
 
     pa_source_output_update_proplist(so, PA_UPDATE_REPLACE, p);
 
 
     pa_source_output_update_proplist(so, PA_UPDATE_REPLACE, p);
 
@@ -1231,7 +1278,7 @@ static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_str
 static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
     const char *n, *m;
     pa_sink *sink;
 static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
     const char *n, *m;
     pa_sink *sink;
-    int suspend;
+    int suspend, r;
 
     pa_core_assert_ref(c);
     pa_assert(t);
 
     pa_core_assert_ref(c);
     pa_assert(t);
@@ -1258,14 +1305,16 @@ static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *b
         return -1;
     }
 
         return -1;
     }
 
-    pa_sink_suspend(sink, suspend);
+    if ((r = pa_sink_suspend(sink, suspend, PA_SUSPEND_USER)) < 0)
+        pa_strbuf_printf(buf, "Failed to resume/suspend sink: %s\n", pa_strerror(r));
+
     return 0;
 }
 
 static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
     const char *n, *m;
     pa_source *source;
     return 0;
 }
 
 static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
     const char *n, *m;
     pa_source *source;
-    int suspend;
+    int suspend, r;
 
     pa_core_assert_ref(c);
     pa_assert(t);
 
     pa_core_assert_ref(c);
     pa_assert(t);
@@ -1292,14 +1341,15 @@ static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf
         return -1;
     }
 
         return -1;
     }
 
-    pa_source_suspend(source, suspend);
+    if ((r = pa_source_suspend(source, suspend, PA_SUSPEND_USER)) < 0)
+        pa_strbuf_printf(buf, "Failed to resume/suspend source: %s\n", pa_strerror(r));
+
     return 0;
 }
 
 static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
     const char *m;
     return 0;
 }
 
 static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
     const char *m;
-    int suspend;
-    int ret;
+    int suspend, r;
 
     pa_core_assert_ref(c);
     pa_assert(t);
 
     pa_core_assert_ref(c);
     pa_assert(t);
@@ -1316,12 +1366,11 @@ static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, p
         return -1;
     }
 
         return -1;
     }
 
-    ret = - (pa_sink_suspend_all(c, suspend) < 0);
-    if (pa_source_suspend_all(c, suspend) < 0)
-        ret = -1;
+    if ((r = pa_sink_suspend_all(c, suspend, PA_SUSPEND_USER)) < 0)
+        pa_strbuf_printf(buf, "Failed to resume/suspend all sinks: %s\n", pa_strerror(r));
 
 
-    if (ret < 0)
-        pa_strbuf_puts(buf, "Failed to resume/suspend all sinks/sources.\n");
+    if ((r = pa_source_suspend_all(c, suspend, PA_SUSPEND_USER)) < 0)
+        pa_strbuf_printf(buf, "Failed to resume/suspend all sources: %s\n", pa_strerror(r));
 
     return 0;
 }
 
     return 0;
 }
@@ -1345,7 +1394,7 @@ static int pa_cli_command_log_level(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
         return -1;
     }
 
         return -1;
     }
 
-    pa_log_set_maximal_level(level);
+    pa_log_set_level(level);
 
     return 0;
 }
 
     return 0;
 }
@@ -1369,7 +1418,7 @@ static int pa_cli_command_log_meta(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
         return -1;
     }
 
         return -1;
     }
 
-    pa_log_set_show_meta(b);
+    pa_log_set_flags(PA_LOG_PRINT_META, b ? PA_LOG_SET : PA_LOG_UNSET);
 
     return 0;
 }
 
     return 0;
 }
@@ -1393,7 +1442,7 @@ static int pa_cli_command_log_time(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
         return -1;
     }
 
         return -1;
     }
 
-    pa_log_set_show_time(b);
+    pa_log_set_flags(PA_LOG_PRINT_TIME, b ? PA_LOG_SET : PA_LOG_UNSET);
 
     return 0;
 }
 
     return 0;
 }
@@ -1446,7 +1495,7 @@ static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *b
         return -1;
     }
 
         return -1;
     }
 
-    if (pa_card_set_profile(card, p) < 0) {
+    if (pa_card_set_profile(card, p, TRUE) < 0) {
         pa_strbuf_printf(buf, "Failed to set card profile to '%s'.\n", p);
         return -1;
     }
         pa_strbuf_printf(buf, "Failed to set card profile to '%s'.\n", p);
         return -1;
     }
@@ -1454,15 +1503,81 @@ static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *b
     return 0;
 }
 
     return 0;
 }
 
+static int pa_cli_command_sink_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n, *p;
+    pa_sink *sink;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(p = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a profile by its name.\n");
+        return -1;
+    }
+
+    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
+        pa_strbuf_puts(buf, "No sink found by this name or index.\n");
+        return -1;
+    }
+
+    if (pa_sink_set_port(sink, p, TRUE) < 0) {
+        pa_strbuf_printf(buf, "Failed to set sink port to '%s'.\n", p);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int pa_cli_command_source_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n, *p;
+    pa_source *source;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(p = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a profile by its name.\n");
+        return -1;
+    }
+
+    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
+        pa_strbuf_puts(buf, "No source found by this name or index.\n");
+        return -1;
+    }
+
+    if (pa_source_set_port(source, p, TRUE) < 0) {
+        pa_strbuf_printf(buf, "Failed to set source port to '%s'.\n", p);
+        return -1;
+    }
+
+    return 0;
+}
+
 static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
     pa_module *m;
     pa_sink *sink;
     pa_source *source;
     pa_card *card;
 static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
     pa_module *m;
     pa_sink *sink;
     pa_source *source;
     pa_card *card;
-    int nl;
+    pa_bool_t nl;
     uint32_t idx;
     uint32_t idx;
-    char txt[256];
     time_t now;
     time_t now;
+#ifdef HAVE_CTIME_R
+    char txt[256];
+#endif
 
     pa_core_assert_ref(c);
     pa_assert(t);
 
     pa_core_assert_ref(c);
     pa_assert(t);
@@ -1477,7 +1592,7 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
     pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime(&now));
 #endif
 
     pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime(&now));
 #endif
 
-    for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
+    PA_IDXSET_FOREACH(m, c->modules, idx) {
 
         pa_strbuf_printf(buf, "load-module %s", m->name);
 
 
         pa_strbuf_printf(buf, "load-module %s", m->name);
 
@@ -1487,58 +1602,58 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
         pa_strbuf_puts(buf, "\n");
     }
 
         pa_strbuf_puts(buf, "\n");
     }
 
-    nl = 0;
-
-    for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
+    nl = FALSE;
+    PA_IDXSET_FOREACH(sink, c->sinks, idx) {
 
         if (!nl) {
             pa_strbuf_puts(buf, "\n");
 
         if (!nl) {
             pa_strbuf_puts(buf, "\n");
-            nl = 1;
+            nl = TRUE;
         }
 
         }
 
-        pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink, FALSE)));
+        pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_max(pa_sink_get_volume(sink, FALSE)));
         pa_strbuf_printf(buf, "set-sink-mute %s %s\n", sink->name, pa_yes_no(pa_sink_get_mute(sink, FALSE)));
         pa_strbuf_printf(buf, "suspend-sink %s %s\n", sink->name, pa_yes_no(pa_sink_get_state(sink) == PA_SINK_SUSPENDED));
     }
 
         pa_strbuf_printf(buf, "set-sink-mute %s %s\n", sink->name, pa_yes_no(pa_sink_get_mute(sink, FALSE)));
         pa_strbuf_printf(buf, "suspend-sink %s %s\n", sink->name, pa_yes_no(pa_sink_get_state(sink) == PA_SINK_SUSPENDED));
     }
 
-    for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
+    nl = FALSE;
+    PA_IDXSET_FOREACH(source, c->sources, idx) {
 
         if (!nl) {
             pa_strbuf_puts(buf, "\n");
 
         if (!nl) {
             pa_strbuf_puts(buf, "\n");
-            nl = 1;
+            nl = TRUE;
         }
 
         }
 
-        pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source, FALSE)));
+        pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_max(pa_source_get_volume(source, FALSE)));
         pa_strbuf_printf(buf, "set-source-mute %s %s\n", source->name, pa_yes_no(pa_source_get_mute(source, FALSE)));
         pa_strbuf_printf(buf, "suspend-source %s %s\n", source->name, pa_yes_no(pa_source_get_state(source) == PA_SOURCE_SUSPENDED));
     }
 
         pa_strbuf_printf(buf, "set-source-mute %s %s\n", source->name, pa_yes_no(pa_source_get_mute(source, FALSE)));
         pa_strbuf_printf(buf, "suspend-source %s %s\n", source->name, pa_yes_no(pa_source_get_state(source) == PA_SOURCE_SUSPENDED));
     }
 
-    for (card = pa_idxset_first(c->cards, &idx); card; card = pa_idxset_next(c->cards, &idx)) {
+    nl = FALSE;
+    PA_IDXSET_FOREACH(card, c->cards, idx) {
 
         if (!nl) {
             pa_strbuf_puts(buf, "\n");
 
         if (!nl) {
             pa_strbuf_puts(buf, "\n");
-            nl = 1;
+            nl = TRUE;
         }
 
         if (card->active_profile)
             pa_strbuf_printf(buf, "set-card-profile %s %s\n", card->name, card->active_profile->name);
     }
 
         }
 
         if (card->active_profile)
             pa_strbuf_printf(buf, "set-card-profile %s %s\n", card->name, card->active_profile->name);
     }
 
-    nl = 0;
-
+    nl = FALSE;
     if ((sink = pa_namereg_get_default_sink(c))) {
         if (!nl) {
             pa_strbuf_puts(buf, "\n");
     if ((sink = pa_namereg_get_default_sink(c))) {
         if (!nl) {
             pa_strbuf_puts(buf, "\n");
-            nl = 1;
+            nl = TRUE;
         }
         }
+
         pa_strbuf_printf(buf, "set-default-sink %s\n", sink->name);
     }
 
     if ((source = pa_namereg_get_default_source(c))) {
         pa_strbuf_printf(buf, "set-default-sink %s\n", sink->name);
     }
 
     if ((source = pa_namereg_get_default_source(c))) {
-        if (!nl) {
+        if (!nl)
             pa_strbuf_puts(buf, "\n");
             pa_strbuf_puts(buf, "\n");
-            nl = 1;
-        }
+
         pa_strbuf_printf(buf, "set-default-source %s\n", source->name);
     }
 
         pa_strbuf_printf(buf, "set-default-source %s\n", source->name);
     }
 
@@ -1587,10 +1702,74 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b
             l = strcspn(cs, whitespace);
 
             if (l == sizeof(META_INCLUDE)-1 && !strncmp(cs, META_INCLUDE, l)) {
             l = strcspn(cs, whitespace);
 
             if (l == sizeof(META_INCLUDE)-1 && !strncmp(cs, META_INCLUDE, l)) {
+                struct stat st;
                 const char *filename = cs+l+strspn(cs+l, whitespace);
                 const char *filename = cs+l+strspn(cs+l, whitespace);
-                if (pa_cli_command_execute_file(c, filename, buf, fail) < 0)
+
+                if (stat(filename, &st) < 0) {
+                    pa_log_warn("stat('%s'): %s", filename, pa_cstrerror(errno));
                     if (*fail)
                         return -1;
                     if (*fail)
                         return -1;
+                } else {
+                    if (S_ISDIR(st.st_mode)) {
+                        DIR *d;
+
+                        if (!(d = opendir(filename))) {
+                            pa_log_warn("Failed to read '%s': %s", filename, pa_cstrerror(errno));
+                            if (*fail)
+                                return -1;
+                        } else {
+                            unsigned i, count;
+                            char **sorted_files;
+                            struct dirent *de;
+                            pa_bool_t failed = FALSE;
+                            pa_dynarray *files = pa_dynarray_new();
+
+                            while ((de = readdir(d))) {
+                                char *extn;
+                                size_t flen = strlen(de->d_name);
+
+                                if (flen < 4)
+                                    continue;
+
+                                extn = &de->d_name[flen-3];
+                                if (strncmp(extn, ".pa", 3) == 0)
+                                    pa_dynarray_append(files, pa_sprintf_malloc("%s" PA_PATH_SEP "%s", filename, de->d_name));
+                            }
+
+                            closedir(d);
+
+                            count = pa_dynarray_size(files);
+                            sorted_files = pa_xnew(char*, count);
+                            for (i = 0; i < count; ++i)
+                                sorted_files[i] = pa_dynarray_get(files, i);
+                            pa_dynarray_free(files, NULL, NULL);
+
+                            for (i = 0; i < count; ++i) {
+                                for (unsigned j = 0; j < count; ++j) {
+                                    if (strcmp(sorted_files[i], sorted_files[j]) < 0) {
+                                        char *tmp = sorted_files[i];
+                                        sorted_files[i] = sorted_files[j];
+                                        sorted_files[j] = tmp;
+                                    }
+                                }
+                            }
+
+                            for (i = 0; i < count; ++i) {
+                                if (!failed) {
+                                    if (pa_cli_command_execute_file(c, sorted_files[i], buf, fail) < 0 && *fail)
+                                        failed = TRUE;
+                                }
+
+                                pa_xfree(sorted_files[i]);
+                            }
+                            pa_xfree(sorted_files);
+                            if (failed)
+                                return -1;
+                        }
+                    } else if (pa_cli_command_execute_file(c, filename, buf, fail) < 0 && *fail) {
+                        return -1;
+                    }
+                }
             } else if (l == sizeof(META_IFEXISTS)-1 && !strncmp(cs, META_IFEXISTS, l)) {
                 if (!ifstate) {
                     pa_strbuf_printf(buf, "Meta command %s is not valid in this context\n", cs);
             } else if (l == sizeof(META_IFEXISTS)-1 && !strncmp(cs, META_IFEXISTS, l)) {
                 if (!ifstate) {
                     pa_strbuf_printf(buf, "Meta command %s is not valid in this context\n", cs);
@@ -1605,7 +1784,7 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b
                     if (filename[0] == PA_PATH_SEP_CHAR) {
 
                         *ifstate = access(filename, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE;
                     if (filename[0] == PA_PATH_SEP_CHAR) {
 
                         *ifstate = access(filename, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE;
-                        pa_log_debug("Checking for existance of '%s': %s", filename, *ifstate == IFSTATE_TRUE ? "success" : "failure");
+                        pa_log_debug("Checking for existence of '%s': %s", filename, *ifstate == IFSTATE_TRUE ? "success" : "failure");
 
                     } else {
                         const char *paths, *state = NULL;
 
                     } else {
                         const char *paths, *state = NULL;
@@ -1621,7 +1800,7 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b
                             pa_xfree(p);
 
                             *ifstate = access(pathname, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE;
                             pa_xfree(p);
 
                             *ifstate = access(pathname, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE;
-                            pa_log_debug("Checking for existance of '%s': %s", pathname, *ifstate == IFSTATE_TRUE ? "success" : "failure");
+                            pa_log_debug("Checking for existence of '%s': %s", pathname, *ifstate == IFSTATE_TRUE ? "success" : "failure");
 
                             pa_xfree(pathname);
 
 
                             pa_xfree(pathname);
 
@@ -1676,7 +1855,7 @@ int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, pa_bo
 }
 
 int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, pa_bool_t *fail) {
 }
 
 int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, pa_bool_t *fail) {
-    char line[1024];
+    char line[2048];
     int ifstate = IFSTATE_NONE;
     int ret = -1;
     pa_bool_t _fail = TRUE;
     int ifstate = IFSTATE_NONE;
     int ret = -1;
     pa_bool_t _fail = TRUE;
@@ -1714,17 +1893,16 @@ int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_b
     if (!fail)
         fail = &_fail;
 
     if (!fail)
         fail = &_fail;
 
-    if (!(f = fopen(fn, "r"))) {
+    if (!(f = pa_fopen_cloexec(fn, "r"))) {
         pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, pa_cstrerror(errno));
         if (!*fail)
             ret = 0;
         goto fail;
     }
 
         pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, pa_cstrerror(errno));
         if (!*fail)
             ret = 0;
         goto fail;
     }
 
+    pa_log_debug("Parsing script '%s'", fn);
     ret = pa_cli_command_execute_file_stream(c, f, buf, fail);
 
     ret = pa_cli_command_execute_file_stream(c, f, buf, fail);
 
-    ret = 0;
-
 fail:
     if (f)
         fclose(f);
 fail:
     if (f)
         fclose(f);