1/***
2  This file is part of PulseAudio.
3
4  Copyright 2004-2006 Lennart Poettering
5  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
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 <stdio.h>
26#include <string.h>
27#include <stdlib.h>
28#include <errno.h>
29#include <unistd.h>
30#include <ltdl.h>
31#include <sys/stat.h>
32#include <dirent.h>
33#include <time.h>
34#include <fcntl.h>
35#include <ctype.h>
36
37#include <pulse/xmalloc.h>
38#include <pulse/error.h>
39
40#include <pulsecore/module.h>
41#include <pulsecore/sink.h>
42#include <pulsecore/source.h>
43#include <pulsecore/client.h>
44#include <pulsecore/sink-input.h>
45#include <pulsecore/source-output.h>
46#include <pulsecore/tokenizer.h>
47#include <pulsecore/strbuf.h>
48#include <pulsecore/namereg.h>
49#include <pulsecore/cli-text.h>
50#include <pulsecore/core-scache.h>
51#include <pulsecore/sound-file.h>
52#include <pulsecore/play-memchunk.h>
53#include <pulsecore/sound-file-stream.h>
54#include <pulsecore/shared.h>
55#include <pulsecore/core-util.h>
56#include <pulsecore/message-handler.h>
57#include <pulsecore/core-error.h>
58#include <pulsecore/modinfo.h>
59#include <pulsecore/dynarray.h>
60
61#include "cli-command.h"
62
63struct command {
64    const char *name;
65    int (*proc) (pa_core *c, pa_tokenizer*t, pa_strbuf *buf, bool *fail);
66    const char *help;
67    unsigned args;
68};
69
70#define META_INCLUDE ".include"
71#define META_FAIL ".fail"
72#define META_NOFAIL ".nofail"
73#define META_IFEXISTS ".ifexists"
74#define META_ELSE ".else"
75#define META_ENDIF ".endif"
76
77enum {
78    IFSTATE_NONE = -1,
79    IFSTATE_FALSE = 0,
80    IFSTATE_TRUE = 1,
81};
82
83/* Prototypes for all available commands */
84static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
85static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
86static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
87static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
88static int pa_cli_command_cards(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
89static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
90static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
91static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
92static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
93static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
94static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
95static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
96static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
97static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
98static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
99static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
100static int pa_cli_command_source_output_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
101static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
102static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
103static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
104static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
105static int pa_cli_command_source_output_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
106static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
107static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
108static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
109static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
110static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
111static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
112static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
113static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
114static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
115static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
116static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
117static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
118static int pa_cli_command_list_shared_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
119static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
120static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
121static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
122static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
123static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
124static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
125static int pa_cli_command_log_target(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
126static int pa_cli_command_log_level(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
127static int pa_cli_command_log_meta(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
128static int pa_cli_command_log_time(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
129static int pa_cli_command_log_backtrace(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
130static int pa_cli_command_update_sink_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
131static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
132static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
133static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
134static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
135static int pa_cli_command_sink_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
136static int pa_cli_command_source_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
137static int pa_cli_command_port_offset(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
138static int pa_cli_command_dump_volumes(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
139static int pa_cli_command_send_message_to_object(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
140
141/* A method table for all available commands */
142
143static const struct command commands[] = {
144    { "help",                    pa_cli_command_help,               "Show this help",               1 },
145    { "list-modules",            pa_cli_command_modules,            "List loaded modules",          1 },
146    { "list-cards",              pa_cli_command_cards,              "List cards",                   1 },
147    { "list-sinks",              pa_cli_command_sinks,              "List loaded sinks",            1 },
148    { "list-sources",            pa_cli_command_sources,            "List loaded sources",          1 },
149    { "list-clients",            pa_cli_command_clients,            "List loaded clients",          1 },
150    { "list-sink-inputs",        pa_cli_command_sink_inputs,        "List sink inputs",             1 },
151    { "list-source-outputs",     pa_cli_command_source_outputs,     "List source outputs",          1 },
152    { "stat",                    pa_cli_command_stat,               "Show memory block statistics", 1 },
153    { "info",                    pa_cli_command_info,               "Show comprehensive status",    1 },
154    { "ls",                      pa_cli_command_info,               NULL,                           1 },
155    { "list",                    pa_cli_command_info,               NULL,                           1 },
156    { "load-module",             pa_cli_command_load,               "Load a module (args: name, arguments)", 3},
157    { "unload-module",           pa_cli_command_unload,             "Unload a module (args: index|name)", 2},
158    { "describe-module",         pa_cli_command_describe,           "Describe a module (arg: name)", 2},
159    { "set-sink-volume",         pa_cli_command_sink_volume,        "Set the volume of a sink (args: index|name, volume)", 3},
160    { "set-source-volume",       pa_cli_command_source_volume,      "Set the volume of a source (args: index|name, volume)", 3},
161    { "set-sink-mute",           pa_cli_command_sink_mute,          "Set the mute switch of a sink (args: index|name, bool)", 3},
162    { "set-source-mute",         pa_cli_command_source_mute,        "Set the mute switch of a source (args: index|name, bool)", 3},
163    { "set-sink-input-volume",   pa_cli_command_sink_input_volume,  "Set the volume of a sink input (args: index, volume)", 3},
164    { "set-source-output-volume",pa_cli_command_source_output_volume,"Set the volume of a source output (args: index, volume)", 3},
165    { "set-sink-input-mute",     pa_cli_command_sink_input_mute,    "Set the mute switch of a sink input (args: index, bool)", 3},
166    { "set-source-output-mute",  pa_cli_command_source_output_mute, "Set the mute switch of a source output (args: index, bool)", 3},
167    { "set-default-sink",        pa_cli_command_sink_default,       "Set the default sink (args: index|name)", 2},
168    { "set-default-source",      pa_cli_command_source_default,     "Set the default source (args: index|name)", 2},
169    { "set-card-profile",        pa_cli_command_card_profile,       "Change the profile of a card (args: index|name, profile-name)", 3},
170    { "set-sink-port",           pa_cli_command_sink_port,          "Change the port of a sink (args: index|name, port-name)", 3},
171    { "set-source-port",         pa_cli_command_source_port,        "Change the port of a source (args: index|name, port-name)", 3},
172    { "set-port-latency-offset", pa_cli_command_port_offset,        "Change the latency of a port (args: card-index|card-name, port-name, latency-offset)", 4},
173    { "suspend-sink",            pa_cli_command_suspend_sink,       "Suspend sink (args: index|name, bool)", 3},
174    { "suspend-source",          pa_cli_command_suspend_source,     "Suspend source (args: index|name, bool)", 3},
175    { "suspend",                 pa_cli_command_suspend,            "Suspend all sinks and all sources (args: bool)", 2},
176    { "move-sink-input",         pa_cli_command_move_sink_input,    "Move sink input to another sink (args: index, sink)", 3},
177    { "move-source-output",      pa_cli_command_move_source_output, "Move source output to another source (args: index, source)", 3},
178    { "update-sink-proplist",    pa_cli_command_update_sink_proplist, "Update the properties of a sink (args: index|name, properties)", 3},
179    { "update-source-proplist",  pa_cli_command_update_source_proplist, "Update the properties of a source (args: index|name, properties)", 3},
180    { "update-sink-input-proplist", pa_cli_command_update_sink_input_proplist, "Update the properties of a sink input (args: index, properties)", 3},
181    { "update-source-output-proplist", pa_cli_command_update_source_output_proplist, "Update the properties of a source output (args: index, properties)", 3},
182    { "list-samples",            pa_cli_command_scache_list,        "List all entries in the sample cache", 1},
183    { "play-sample",             pa_cli_command_scache_play,        "Play a sample from the sample cache (args: name, sink|index)", 3},
184    { "remove-sample",           pa_cli_command_scache_remove,      "Remove a sample from the sample cache (args: name)", 2},
185    { "load-sample",             pa_cli_command_scache_load,        "Load a sound file into the sample cache (args: name, filename)", 3},
186    { "load-sample-lazy",        pa_cli_command_scache_load,        "Lazily load a sound file into the sample cache (args: name, filename)", 3},
187    { "load-sample-dir-lazy",    pa_cli_command_scache_load_dir,    "Lazily load all files in a directory into the sample cache (args: pathname)", 2},
188    { "kill-client",             pa_cli_command_kill_client,        "Kill a client (args: index)", 2},
189    { "kill-sink-input",         pa_cli_command_kill_sink_input,    "Kill a sink input (args: index)", 2},
190    { "kill-source-output",      pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2},
191    { "set-log-target",          pa_cli_command_log_target,         "Change the log target (args: null|auto|syslog|stderr|file:PATH|newfile:PATH)", 2},
192    { "set-log-level",           pa_cli_command_log_level,          "Change the log level (args: numeric level)", 2},
193    { "set-log-meta",            pa_cli_command_log_meta,           "Show source code location in log messages (args: bool)", 2},
194    { "set-log-time",            pa_cli_command_log_time,           "Show timestamps in log messages (args: bool)", 2},
195    { "set-log-backtrace",       pa_cli_command_log_backtrace,      "Show backtrace in log messages (args: frames)", 2},
196    { "send-message",            pa_cli_command_send_message_to_object, "Send a message to an object (args: recipient, message, message_parameters)", 4},
197    { "play-file",               pa_cli_command_play_file,          "Play a sound file (args: filename, sink|index)", 3},
198    { "dump",                    pa_cli_command_dump,               "Dump daemon configuration", 1},
199    { "dump-volumes",            pa_cli_command_dump_volumes,       "Debug: Show the state of all volumes", 1 },
200    { "shared",                  pa_cli_command_list_shared_props,  "Debug: Show shared properties", 1},
201    { "exit",                    pa_cli_command_exit,               "Terminate the daemon",         1 },
202    { "vacuum",                  pa_cli_command_vacuum,             NULL, 1},
203    { NULL, NULL, NULL, 0 }
204};
205
206static const char whitespace[] = " \t\n\r";
207static const char linebreak[] = "\n\r";
208
209static uint32_t parse_index(const char *n) {
210    uint32_t idx;
211
212    if (pa_atou(n, &idx) < 0)
213        return (uint32_t) PA_IDXSET_INVALID;
214
215    return idx;
216}
217
218static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
219    pa_core_assert_ref(c);
220    pa_assert(t);
221    pa_assert(buf);
222    pa_assert(fail);
223
224    if (pa_core_exit(c, false, 0) < 0)
225        pa_strbuf_puts(buf, "Not allowed to terminate daemon.\n");
226
227    return 0;
228}
229
230static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
231    const struct command*command;
232
233    pa_core_assert_ref(c);
234    pa_assert(t);
235    pa_assert(buf);
236    pa_assert(fail);
237
238    pa_strbuf_puts(buf, "Available commands:\n");
239
240    for (command = commands; command->name; command++)
241        if (command->help)
242            pa_strbuf_printf(buf, "    %-25s %s\n", command->name, command->help);
243    return 0;
244}
245
246static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
247    char *s;
248
249    pa_core_assert_ref(c);
250    pa_assert(t);
251    pa_assert(buf);
252    pa_assert(fail);
253
254    pa_assert_se(s = pa_module_list_to_string(c));
255    pa_strbuf_puts(buf, s);
256    pa_xfree(s);
257    return 0;
258}
259
260static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
261    char *s;
262
263    pa_core_assert_ref(c);
264    pa_assert(t);
265    pa_assert(buf);
266    pa_assert(fail);
267
268    pa_assert_se(s = pa_client_list_to_string(c));
269    pa_strbuf_puts(buf, s);
270    pa_xfree(s);
271    return 0;
272}
273
274static int pa_cli_command_cards(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
275    char *s;
276
277    pa_core_assert_ref(c);
278    pa_assert(t);
279    pa_assert(buf);
280    pa_assert(fail);
281
282    pa_assert_se(s = pa_card_list_to_string(c));
283    pa_strbuf_puts(buf, s);
284    pa_xfree(s);
285    return 0;
286}
287
288static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
289    char *s;
290
291    pa_core_assert_ref(c);
292    pa_assert(t);
293    pa_assert(buf);
294    pa_assert(fail);
295
296    pa_assert_se(s = pa_sink_list_to_string(c));
297    pa_strbuf_puts(buf, s);
298    pa_xfree(s);
299    return 0;
300}
301
302static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
303    char *s;
304
305    pa_core_assert_ref(c);
306    pa_assert(t);
307    pa_assert(buf);
308    pa_assert(fail);
309
310    pa_assert_se(s = pa_source_list_to_string(c));
311    pa_strbuf_puts(buf, s);
312    pa_xfree(s);
313    return 0;
314}
315
316static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
317    char *s;
318
319    pa_core_assert_ref(c);
320    pa_assert(t);
321    pa_assert(buf);
322    pa_assert(fail);
323
324    pa_assert_se(s = pa_sink_input_list_to_string(c));
325    pa_strbuf_puts(buf, s);
326    pa_xfree(s);
327    return 0;
328}
329
330static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
331    char *s;
332
333    pa_core_assert_ref(c);
334    pa_assert(t);
335    pa_assert(buf);
336    pa_assert(fail);
337
338    pa_assert_se(s = pa_source_output_list_to_string(c));
339    pa_strbuf_puts(buf, s);
340    pa_xfree(s);
341    return 0;
342}
343
344static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
345    char ss[PA_SAMPLE_SPEC_SNPRINT_MAX];
346    char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
347    char bytes[PA_BYTES_SNPRINT_MAX];
348    const pa_mempool_stat *mstat;
349    unsigned k;
350
351    static const char* const type_table[PA_MEMBLOCK_TYPE_MAX] = {
352        [PA_MEMBLOCK_POOL] = "POOL",
353        [PA_MEMBLOCK_POOL_EXTERNAL] = "POOL_EXTERNAL",
354        [PA_MEMBLOCK_APPENDED] = "APPENDED",
355        [PA_MEMBLOCK_USER] = "USER",
356        [PA_MEMBLOCK_FIXED] = "FIXED",
357        [PA_MEMBLOCK_IMPORTED] = "IMPORTED",
358    };
359
360    pa_core_assert_ref(c);
361    pa_assert(t);
362    pa_assert(buf);
363    pa_assert(fail);
364
365    mstat = pa_mempool_get_stat(c->mempool);
366
367    pa_strbuf_printf(buf, "Memory blocks currently allocated: %u, size: %s.\n",
368                     (unsigned) pa_atomic_load(&mstat->n_allocated),
369                     pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_atomic_load(&mstat->allocated_size)));
370
371    pa_strbuf_printf(buf, "Memory blocks allocated during the whole lifetime: %u, size: %s.\n",
372                     (unsigned) pa_atomic_load(&mstat->n_accumulated),
373                     pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_atomic_load(&mstat->accumulated_size)));
374
375    pa_strbuf_printf(buf, "Memory blocks imported from other processes: %u, size: %s.\n",
376                     (unsigned) pa_atomic_load(&mstat->n_imported),
377                     pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_atomic_load(&mstat->imported_size)));
378
379    pa_strbuf_printf(buf, "Memory blocks exported to other processes: %u, size: %s.\n",
380                     (unsigned) pa_atomic_load(&mstat->n_exported),
381                     pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_atomic_load(&mstat->exported_size)));
382
383    pa_strbuf_printf(buf, "Total sample cache size: %s.\n",
384                     pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_scache_total_size(c)));
385
386    pa_strbuf_printf(buf, "Default sample spec: %s\n",
387                     pa_sample_spec_snprint(ss, sizeof(ss), &c->default_sample_spec));
388
389    pa_strbuf_printf(buf, "Default channel map: %s\n",
390                     pa_channel_map_snprint(cm, sizeof(cm), &c->default_channel_map));
391
392    pa_strbuf_printf(buf, "Default sink name: %s\n"
393                     "Default source name: %s\n",
394                     c->default_sink ? c->default_sink->name : "none",
395                     c->default_source ? c->default_source->name : "none");
396
397    for (k = 0; k < PA_MEMBLOCK_TYPE_MAX; k++)
398        pa_strbuf_printf(buf,
399                         "Memory blocks of type %s: %u allocated/%u accumulated.\n",
400                         type_table[k],
401                         (unsigned) pa_atomic_load(&mstat->n_allocated_by_type[k]),
402                         (unsigned) pa_atomic_load(&mstat->n_accumulated_by_type[k]));
403
404    return 0;
405}
406
407static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
408    pa_core_assert_ref(c);
409    pa_assert(t);
410    pa_assert(buf);
411    pa_assert(fail);
412
413    pa_cli_command_stat(c, t, buf, fail);
414    pa_cli_command_modules(c, t, buf, fail);
415    pa_cli_command_sinks(c, t, buf, fail);
416    pa_cli_command_sources(c, t, buf, fail);
417    pa_cli_command_clients(c, t, buf, fail);
418    pa_cli_command_cards(c, t, buf, fail);
419    pa_cli_command_sink_inputs(c, t, buf, fail);
420    pa_cli_command_source_outputs(c, t, buf, fail);
421    pa_cli_command_scache_list(c, t, buf, fail);
422    return 0;
423}
424
425static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
426    const char *name;
427    pa_error_code_t err;
428    pa_module *m = NULL;
429
430    pa_core_assert_ref(c);
431    pa_assert(t);
432    pa_assert(buf);
433    pa_assert(fail);
434
435    if (!(name = pa_tokenizer_get(t, 1))) {
436        pa_strbuf_puts(buf, "You need to specify the module name and optionally arguments.\n");
437        return -1;
438    }
439
440    if ((err = pa_module_load(&m, c, name,  pa_tokenizer_get(t, 2))) < 0) {
441        if (err == PA_ERR_EXIST) {
442            pa_strbuf_puts(buf, "Module already loaded; ignoring.\n");
443        } else {
444            pa_strbuf_puts(buf, "Module load failed.\n");
445            return -1;
446        }
447    }
448
449    return 0;
450}
451
452static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
453    pa_module *m;
454    uint32_t idx;
455    const char *i;
456    bool unloaded = false;
457
458    pa_core_assert_ref(c);
459    pa_assert(t);
460    pa_assert(buf);
461    pa_assert(fail);
462
463    if (!(i = pa_tokenizer_get(t, 1))) {
464        pa_strbuf_puts(buf, "You need to specify the module index or name.\n");
465        return -1;
466    }
467
468    if (pa_atou(i, &idx) >= 0) {
469        if (!(m = pa_idxset_get_by_index(c->modules, idx))) {
470            pa_strbuf_puts(buf, "Invalid module index.\n");
471            return -1;
472        }
473
474        pa_module_unload(m, false);
475
476    } else {
477        PA_IDXSET_FOREACH(m, c->modules, idx)
478            if (pa_streq(i, m->name)) {
479                unloaded = true;
480                pa_module_unload(m, false);
481            }
482
483        if (unloaded == false) {
484            pa_strbuf_printf(buf, "Module %s not loaded.\n", i);
485            return -1;
486        }
487    }
488
489    return 0;
490}
491
492static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
493    const char *name;
494    pa_modinfo *i;
495
496    pa_core_assert_ref(c);
497    pa_assert(t);
498    pa_assert(buf);
499    pa_assert(fail);
500
501    if (!(name = pa_tokenizer_get(t, 1))) {
502        pa_strbuf_puts(buf, "You need to specify the module name.\n");
503        return -1;
504    }
505
506    if ((i = pa_modinfo_get_by_name(name))) {
507
508        pa_strbuf_printf(buf, "Name: %s\n", name);
509
510        if (!i->description && !i->version && !i->author && !i->usage)
511            pa_strbuf_printf(buf, "No module information available\n");
512        else {
513            if (i->version)
514                pa_strbuf_printf(buf, "Version: %s\n", i->version);
515            if (i->description)
516                pa_strbuf_printf(buf, "Description: %s\n", i->description);
517            if (i->author)
518                pa_strbuf_printf(buf, "Author: %s\n", i->author);
519            if (i->usage)
520                pa_strbuf_printf(buf, "Usage: %s\n", i->usage);
521            pa_strbuf_printf(buf, "Load Once: %s\n", pa_yes_no(i->load_once));
522            if (i->deprecated)
523                pa_strbuf_printf(buf, "Warning, deprecated: %s\n", i->deprecated);
524        }
525
526        pa_modinfo_free(i);
527    } else
528        pa_strbuf_puts(buf, "Failed to open module.\n");
529
530    return 0;
531}
532
533static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
534    const char *n, *v;
535    pa_sink *sink;
536    uint32_t volume;
537    pa_cvolume cvolume;
538
539    pa_core_assert_ref(c);
540    pa_assert(t);
541    pa_assert(buf);
542    pa_assert(fail);
543
544    if (!(n = pa_tokenizer_get(t, 1))) {
545        pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
546        return -1;
547    }
548
549    if (!(v = pa_tokenizer_get(t, 2))) {
550        pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
551        return -1;
552    }
553
554    if (pa_atou(v, &volume) < 0) {
555        pa_strbuf_puts(buf, "Failed to parse volume.\n");
556        return -1;
557    }
558
559    if (!PA_VOLUME_IS_VALID(volume)) {
560        pa_strbuf_puts(buf, "Volume outside permissible range.\n");
561        return -1;
562    }
563
564    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
565        pa_strbuf_puts(buf, "No sink found by this name or index.\n");
566        return -1;
567    }
568
569    pa_cvolume_set(&cvolume, 1, volume);
570    pa_sink_set_volume(sink, &cvolume, true, true);
571    return 0;
572}
573
574static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
575    const char *n, *v;
576    pa_sink_input *si;
577    pa_volume_t volume;
578    pa_cvolume cvolume;
579    uint32_t idx;
580
581    pa_core_assert_ref(c);
582    pa_assert(t);
583    pa_assert(buf);
584    pa_assert(fail);
585
586    if (!(n = pa_tokenizer_get(t, 1))) {
587        pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
588        return -1;
589    }
590
591    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
592        pa_strbuf_puts(buf, "Failed to parse index.\n");
593        return -1;
594    }
595
596    if (!(v = pa_tokenizer_get(t, 2))) {
597        pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
598        return -1;
599    }
600
601    if (pa_atou(v, &volume) < 0) {
602        pa_strbuf_puts(buf, "Failed to parse volume.\n");
603        return -1;
604    }
605
606    if (!PA_VOLUME_IS_VALID(volume)) {
607        pa_strbuf_puts(buf, "Volume outside permissible range.\n");
608        return -1;
609    }
610
611    if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx))) {
612        pa_strbuf_puts(buf, "No sink input found with this index.\n");
613        return -1;
614    }
615
616    if (!si->volume_writable) {
617        pa_strbuf_puts(buf, "This sink input's volume can't be changed.\n");
618        return -1;
619    }
620
621    pa_cvolume_set(&cvolume, 1, volume);
622    pa_sink_input_set_volume(si, &cvolume, true, true);
623    return 0;
624}
625
626static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
627    const char *n, *v;
628    pa_source *source;
629    uint32_t volume;
630    pa_cvolume cvolume;
631
632    pa_core_assert_ref(c);
633    pa_assert(t);
634    pa_assert(buf);
635    pa_assert(fail);
636
637    if (!(n = pa_tokenizer_get(t, 1))) {
638        pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
639        return -1;
640    }
641
642    if (!(v = pa_tokenizer_get(t, 2))) {
643        pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
644        return -1;
645    }
646
647    if (pa_atou(v, &volume) < 0) {
648        pa_strbuf_puts(buf, "Failed to parse volume.\n");
649        return -1;
650    }
651
652    if (!PA_VOLUME_IS_VALID(volume)) {
653        pa_strbuf_puts(buf, "Volume outside permissible range.\n");
654        return -1;
655    }
656
657    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
658        pa_strbuf_puts(buf, "No source found by this name or index.\n");
659        return -1;
660    }
661
662    pa_cvolume_set(&cvolume, 1, volume);
663    pa_source_set_volume(source, &cvolume, true, true);
664    return 0;
665}
666
667static int pa_cli_command_source_output_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
668    const char *n, *v;
669    pa_source_output *so;
670    pa_volume_t volume;
671    pa_cvolume cvolume;
672    uint32_t idx;
673
674    pa_core_assert_ref(c);
675    pa_assert(t);
676    pa_assert(buf);
677    pa_assert(fail);
678
679    if (!(n = pa_tokenizer_get(t, 1))) {
680        pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
681        return -1;
682    }
683
684    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
685        pa_strbuf_puts(buf, "Failed to parse index.\n");
686        return -1;
687    }
688
689    if (!(v = pa_tokenizer_get(t, 2))) {
690        pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
691        return -1;
692    }
693
694    if (pa_atou(v, &volume) < 0) {
695        pa_strbuf_puts(buf, "Failed to parse volume.\n");
696        return -1;
697    }
698
699    if (!PA_VOLUME_IS_VALID(volume)) {
700        pa_strbuf_puts(buf, "Volume outside permissible range.\n");
701        return -1;
702    }
703
704    if (!(so = pa_idxset_get_by_index(c->source_outputs, idx))) {
705        pa_strbuf_puts(buf, "No source output found with this index.\n");
706        return -1;
707    }
708
709    if (!so->volume_writable) {
710        pa_strbuf_puts(buf, "This source output's volume can't be changed.\n");
711        return -1;
712    }
713
714    pa_cvolume_set(&cvolume, 1, volume);
715    pa_source_output_set_volume(so, &cvolume, true, true);
716    return 0;
717}
718
719static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
720    const char *n, *m;
721    pa_sink *sink;
722    int mute;
723
724    pa_core_assert_ref(c);
725    pa_assert(t);
726    pa_assert(buf);
727    pa_assert(fail);
728
729    if (!(n = pa_tokenizer_get(t, 1))) {
730        pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
731        return -1;
732    }
733
734    if (!(m = pa_tokenizer_get(t, 2))) {
735        pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
736        return -1;
737    }
738
739    if ((mute = pa_parse_boolean(m)) < 0) {
740        pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
741        return -1;
742    }
743
744    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
745        pa_strbuf_puts(buf, "No sink found by this name or index.\n");
746        return -1;
747    }
748
749    pa_sink_set_mute(sink, mute, true);
750    return 0;
751}
752
753static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
754    const char *n, *m;
755    pa_source *source;
756    int mute;
757
758    pa_core_assert_ref(c);
759    pa_assert(t);
760    pa_assert(buf);
761    pa_assert(fail);
762
763    if (!(n = pa_tokenizer_get(t, 1))) {
764        pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
765        return -1;
766    }
767
768    if (!(m = pa_tokenizer_get(t, 2))) {
769        pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
770        return -1;
771    }
772
773    if ((mute = pa_parse_boolean(m)) < 0) {
774        pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
775        return -1;
776    }
777
778    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
779        pa_strbuf_puts(buf, "No sink found by this name or index.\n");
780        return -1;
781    }
782
783    pa_source_set_mute(source, mute, true);
784    return 0;
785}
786
787static int pa_cli_command_update_sink_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
788    const char *n, *s;
789    pa_sink *sink;
790    pa_proplist *p;
791
792    pa_core_assert_ref(c);
793    pa_assert(t);
794    pa_assert(buf);
795    pa_assert(fail);
796
797    if (!(n = pa_tokenizer_get(t, 1))) {
798        pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
799        return -1;
800    }
801
802    if (!(s = pa_tokenizer_get(t, 2))) {
803        pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n");
804        return -1;
805    }
806
807    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
808        pa_strbuf_puts(buf, "No sink found by this name or index.\n");
809        return -1;
810    }
811
812    if (!(p = pa_proplist_from_string(s))) {
813        pa_strbuf_puts(buf, "Failed to parse proplist.\n");
814        return -1;
815    }
816
817    pa_sink_update_proplist(sink, PA_UPDATE_REPLACE, p);
818
819    pa_proplist_free(p);
820
821    return 0;
822}
823
824static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
825    const char *n, *s;
826    pa_source *source;
827    pa_proplist *p;
828
829    pa_core_assert_ref(c);
830    pa_assert(t);
831    pa_assert(buf);
832    pa_assert(fail);
833
834    if (!(n = pa_tokenizer_get(t, 1))) {
835        pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
836        return -1;
837    }
838
839    if (!(s = pa_tokenizer_get(t, 2))) {
840        pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n");
841        return -1;
842    }
843
844    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
845        pa_strbuf_puts(buf, "No source found by this name or index.\n");
846        return -1;
847    }
848
849    if (!(p = pa_proplist_from_string(s))) {
850        pa_strbuf_puts(buf, "Failed to parse proplist.\n");
851        return -1;
852    }
853
854    pa_source_update_proplist(source, PA_UPDATE_REPLACE, p);
855
856    pa_proplist_free(p);
857
858    return 0;
859}
860
861static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
862    const char *n, *s;
863    pa_sink_input *si;
864    uint32_t idx;
865    pa_proplist *p;
866
867    pa_core_assert_ref(c);
868    pa_assert(t);
869    pa_assert(buf);
870    pa_assert(fail);
871
872    if (!(n = pa_tokenizer_get(t, 1))) {
873        pa_strbuf_puts(buf, "You need to specify a sink input either by index.\n");
874        return -1;
875    }
876
877    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
878        pa_strbuf_puts(buf, "Failed to parse index.\n");
879        return -1;
880    }
881
882    if (!(s = pa_tokenizer_get(t, 2))) {
883        pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n");
884        return -1;
885    }
886
887    if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
888        pa_strbuf_puts(buf, "No sink input found with this index.\n");
889        return -1;
890    }
891
892    if (!(p = pa_proplist_from_string(s))) {
893        pa_strbuf_puts(buf, "Failed to parse proplist.\n");
894        return -1;
895    }
896
897    pa_sink_input_update_proplist(si, PA_UPDATE_REPLACE, p);
898
899    pa_proplist_free(p);
900
901    return 0;
902}
903
904static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
905    const char *n, *s;
906    pa_source_output *so;
907    uint32_t idx;
908    pa_proplist *p;
909
910    pa_core_assert_ref(c);
911    pa_assert(t);
912    pa_assert(buf);
913    pa_assert(fail);
914
915    if (!(n = pa_tokenizer_get(t, 1))) {
916        pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
917        return -1;
918    }
919
920    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
921        pa_strbuf_puts(buf, "Failed to parse index.\n");
922        return -1;
923    }
924
925    if (!(s = pa_tokenizer_get(t, 2))) {
926        pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n");
927        return -1;
928    }
929
930    if (!(so = pa_idxset_get_by_index(c->source_outputs, (uint32_t) idx))) {
931        pa_strbuf_puts(buf, "No source output found with this index.\n");
932        return -1;
933    }
934
935    if (!(p = pa_proplist_from_string(s))) {
936        pa_strbuf_puts(buf, "Failed to parse proplist.\n");
937        return -1;
938    }
939
940    pa_source_output_update_proplist(so, PA_UPDATE_REPLACE, p);
941
942    pa_proplist_free(p);
943
944    return 0;
945}
946
947static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
948    const char *n, *v;
949    pa_sink_input *si;
950    uint32_t idx;
951    int mute;
952
953    pa_core_assert_ref(c);
954    pa_assert(t);
955    pa_assert(buf);
956    pa_assert(fail);
957
958    if (!(n = pa_tokenizer_get(t, 1))) {
959        pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
960        return -1;
961    }
962
963    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
964        pa_strbuf_puts(buf, "Failed to parse index.\n");
965        return -1;
966    }
967
968    if (!(v = pa_tokenizer_get(t, 2))) {
969        pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
970        return -1;
971    }
972
973    if ((mute = pa_parse_boolean(v)) < 0) {
974        pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
975        return -1;
976    }
977
978    if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
979        pa_strbuf_puts(buf, "No sink input found with this index.\n");
980        return -1;
981    }
982
983    pa_sink_input_set_mute(si, mute, true);
984    return 0;
985}
986
987static int pa_cli_command_source_output_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
988    const char *n, *v;
989    pa_source_output *so;
990    uint32_t idx;
991    int mute;
992
993    pa_core_assert_ref(c);
994    pa_assert(t);
995    pa_assert(buf);
996    pa_assert(fail);
997
998    if (!(n = pa_tokenizer_get(t, 1))) {
999        pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
1000        return -1;
1001    }
1002
1003    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
1004        pa_strbuf_puts(buf, "Failed to parse index.\n");
1005        return -1;
1006    }
1007
1008    if (!(v = pa_tokenizer_get(t, 2))) {
1009        pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
1010        return -1;
1011    }
1012
1013    if ((mute = pa_parse_boolean(v)) < 0) {
1014        pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
1015        return -1;
1016    }
1017
1018    if (!(so = pa_idxset_get_by_index(c->source_outputs, (uint32_t) idx))) {
1019        pa_strbuf_puts(buf, "No source output found with this index.\n");
1020        return -1;
1021    }
1022
1023    pa_source_output_set_mute(so, mute, true);
1024    return 0;
1025}
1026
1027static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1028    const char *n;
1029    pa_sink *s;
1030
1031    pa_core_assert_ref(c);
1032    pa_assert(t);
1033    pa_assert(buf);
1034    pa_assert(fail);
1035
1036    if (!(n = pa_tokenizer_get(t, 1))) {
1037        pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
1038        return -1;
1039    }
1040
1041    if ((s = pa_namereg_get(c, n, PA_NAMEREG_SINK)))
1042        pa_core_set_configured_default_sink(c, s->name);
1043    else
1044        pa_strbuf_printf(buf, "Sink %s does not exist.\n", n);
1045
1046    return 0;
1047}
1048
1049static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1050    const char *n;
1051    pa_source *s;
1052
1053    pa_core_assert_ref(c);
1054    pa_assert(t);
1055    pa_assert(buf);
1056    pa_assert(fail);
1057
1058    if (!(n = pa_tokenizer_get(t, 1))) {
1059        pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
1060        return -1;
1061    }
1062
1063    if ((s = pa_namereg_get(c, n, PA_NAMEREG_SOURCE)))
1064        pa_core_set_configured_default_source(c, s->name);
1065    else
1066        pa_strbuf_printf(buf, "Source %s does not exist.\n", n);
1067    return 0;
1068}
1069
1070static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1071    const char *n;
1072    pa_client *client;
1073    uint32_t idx;
1074
1075    pa_core_assert_ref(c);
1076    pa_assert(t);
1077    pa_assert(buf);
1078    pa_assert(fail);
1079
1080    if (!(n = pa_tokenizer_get(t, 1))) {
1081        pa_strbuf_puts(buf, "You need to specify a client by its index.\n");
1082        return -1;
1083    }
1084
1085    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
1086        pa_strbuf_puts(buf, "Failed to parse index.\n");
1087        return -1;
1088    }
1089
1090    if (!(client = pa_idxset_get_by_index(c->clients, idx))) {
1091        pa_strbuf_puts(buf, "No client found by this index.\n");
1092        return -1;
1093    }
1094
1095    pa_client_kill(client);
1096    return 0;
1097}
1098
1099static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1100    const char *n;
1101    pa_sink_input *sink_input;
1102    uint32_t idx;
1103
1104    pa_core_assert_ref(c);
1105    pa_assert(t);
1106    pa_assert(buf);
1107    pa_assert(fail);
1108
1109    if (!(n = pa_tokenizer_get(t, 1))) {
1110        pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
1111        return -1;
1112    }
1113
1114    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
1115        pa_strbuf_puts(buf, "Failed to parse index.\n");
1116        return -1;
1117    }
1118
1119    if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, idx))) {
1120        pa_strbuf_puts(buf, "No sink input found by this index.\n");
1121        return -1;
1122    }
1123
1124    pa_sink_input_kill(sink_input);
1125    return 0;
1126}
1127
1128static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1129    const char *n;
1130    pa_source_output *source_output;
1131    uint32_t idx;
1132
1133    pa_core_assert_ref(c);
1134    pa_assert(t);
1135    pa_assert(buf);
1136    pa_assert(fail);
1137
1138    if (!(n = pa_tokenizer_get(t, 1))) {
1139        pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
1140        return -1;
1141    }
1142
1143    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
1144        pa_strbuf_puts(buf, "Failed to parse index.\n");
1145        return -1;
1146    }
1147
1148    if (!(source_output = pa_idxset_get_by_index(c->source_outputs, idx))) {
1149        pa_strbuf_puts(buf, "No source output found by this index.\n");
1150        return -1;
1151    }
1152
1153    pa_source_output_kill(source_output);
1154    return 0;
1155}
1156
1157static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1158    char *s;
1159
1160    pa_core_assert_ref(c);
1161    pa_assert(t);
1162    pa_assert(buf);
1163    pa_assert(fail);
1164
1165    pa_assert_se(s = pa_scache_list_to_string(c));
1166    pa_strbuf_puts(buf, s);
1167    pa_xfree(s);
1168
1169    return 0;
1170}
1171
1172static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1173    const char *n, *sink_name;
1174    pa_sink *sink;
1175    uint32_t idx;
1176
1177    pa_core_assert_ref(c);
1178    pa_assert(t);
1179    pa_assert(buf);
1180    pa_assert(fail);
1181
1182    if (!(n = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
1183        pa_strbuf_puts(buf, "You need to specify a sample name and a sink name.\n");
1184        return -1;
1185    }
1186
1187    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK))) {
1188        pa_strbuf_puts(buf, "No sink by that name.\n");
1189        return -1;
1190    }
1191
1192    if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM, NULL, &idx) < 0) {
1193        pa_strbuf_puts(buf, "Failed to play sample.\n");
1194        return -1;
1195    }
1196
1197    pa_strbuf_printf(buf, "Playing on sink input #%i\n", idx);
1198
1199    return 0;
1200}
1201
1202static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1203    const char *n;
1204
1205    pa_core_assert_ref(c);
1206    pa_assert(t);
1207    pa_assert(buf);
1208    pa_assert(fail);
1209
1210    if (!(n = pa_tokenizer_get(t, 1))) {
1211        pa_strbuf_puts(buf, "You need to specify a sample name.\n");
1212        return -1;
1213    }
1214
1215    if (pa_scache_remove_item(c, n) < 0) {
1216        pa_strbuf_puts(buf, "Failed to remove sample.\n");
1217        return -1;
1218    }
1219
1220    return 0;
1221}
1222
1223static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1224    const char *fname, *n;
1225    int r;
1226
1227    pa_core_assert_ref(c);
1228    pa_assert(t);
1229    pa_assert(buf);
1230    pa_assert(fail);
1231
1232    if (!(fname = pa_tokenizer_get(t, 2)) || !(n = pa_tokenizer_get(t, 1))) {
1233        pa_strbuf_puts(buf, "You need to specify a file name and a sample name.\n");
1234        return -1;
1235    }
1236
1237    if (strstr(pa_tokenizer_get(t, 0), "lazy"))
1238        r = pa_scache_add_file_lazy(c, n, fname, NULL);
1239    else
1240        r = pa_scache_add_file(c, n, fname, NULL);
1241
1242    if (r < 0)
1243        pa_strbuf_puts(buf, "Failed to load sound file.\n");
1244
1245    return 0;
1246}
1247
1248static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1249    const char *pname;
1250
1251    pa_core_assert_ref(c);
1252    pa_assert(t);
1253    pa_assert(buf);
1254    pa_assert(fail);
1255
1256    if (!(pname = pa_tokenizer_get(t, 1))) {
1257        pa_strbuf_puts(buf, "You need to specify a path name.\n");
1258        return -1;
1259    }
1260
1261    if (pa_scache_add_directory_lazy(c, pname) < 0) {
1262        pa_strbuf_puts(buf, "Failed to load directory.\n");
1263        return -1;
1264    }
1265
1266    return 0;
1267}
1268
1269static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1270    const char *fname, *sink_name;
1271    pa_sink *sink;
1272
1273    pa_core_assert_ref(c);
1274    pa_assert(t);
1275    pa_assert(buf);
1276    pa_assert(fail);
1277
1278    if (!(fname = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
1279        pa_strbuf_puts(buf, "You need to specify a file name and a sink name.\n");
1280        return -1;
1281    }
1282
1283    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK))) {
1284        pa_strbuf_puts(buf, "No sink by that name.\n");
1285        return -1;
1286    }
1287#ifdef SNDFILE_ENABLE
1288    if (pa_play_file(sink, fname, NULL) < 0) {
1289        pa_strbuf_puts(buf, "Failed to play sound file.\n");
1290        return -1;
1291    }
1292#else
1293    return -1;
1294#endif
1295    return 0;
1296}
1297
1298static int pa_cli_command_list_shared_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1299    pa_core_assert_ref(c);
1300    pa_assert(t);
1301    pa_assert(buf);
1302    pa_assert(fail);
1303
1304    pa_shared_dump(c, buf);
1305    return 0;
1306}
1307
1308static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1309    pa_core_assert_ref(c);
1310    pa_assert(t);
1311    pa_assert(buf);
1312    pa_assert(fail);
1313
1314    pa_mempool_vacuum(c->mempool);
1315
1316    return 0;
1317}
1318
1319static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1320    const char *n, *k;
1321    pa_sink_input *si;
1322    pa_sink *sink;
1323    uint32_t idx;
1324
1325    pa_core_assert_ref(c);
1326    pa_assert(t);
1327    pa_assert(buf);
1328    pa_assert(fail);
1329
1330    if (!(n = pa_tokenizer_get(t, 1))) {
1331        pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
1332        return -1;
1333    }
1334
1335    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
1336        pa_strbuf_puts(buf, "Failed to parse index.\n");
1337        return -1;
1338    }
1339
1340    if (!(k = pa_tokenizer_get(t, 2))) {
1341        pa_strbuf_puts(buf, "You need to specify a sink.\n");
1342        return -1;
1343    }
1344
1345    if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
1346        pa_strbuf_puts(buf, "No sink input found with this index.\n");
1347        return -1;
1348    }
1349
1350    if (!(sink = pa_namereg_get(c, k, PA_NAMEREG_SINK))) {
1351        pa_strbuf_puts(buf, "No sink found by this name or index.\n");
1352        return -1;
1353    }
1354
1355    if (pa_sink_input_move_to(si, sink, true) < 0) {
1356        pa_strbuf_puts(buf, "Moved failed.\n");
1357        return -1;
1358    }
1359    return 0;
1360}
1361
1362static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1363    const char *n, *k;
1364    pa_source_output *so;
1365    pa_source *source;
1366    uint32_t idx;
1367
1368    pa_core_assert_ref(c);
1369    pa_assert(t);
1370    pa_assert(buf);
1371    pa_assert(fail);
1372
1373    if (!(n = pa_tokenizer_get(t, 1))) {
1374        pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
1375        return -1;
1376    }
1377
1378    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
1379        pa_strbuf_puts(buf, "Failed to parse index.\n");
1380        return -1;
1381    }
1382
1383    if (!(k = pa_tokenizer_get(t, 2))) {
1384        pa_strbuf_puts(buf, "You need to specify a source.\n");
1385        return -1;
1386    }
1387
1388    if (!(so = pa_idxset_get_by_index(c->source_outputs, (uint32_t) idx))) {
1389        pa_strbuf_puts(buf, "No source output found with this index.\n");
1390        return -1;
1391    }
1392
1393    if (!(source = pa_namereg_get(c, k, PA_NAMEREG_SOURCE))) {
1394        pa_strbuf_puts(buf, "No source found by this name or index.\n");
1395        return -1;
1396    }
1397
1398    if (pa_source_output_move_to(so, source, true) < 0) {
1399        pa_strbuf_puts(buf, "Moved failed.\n");
1400        return -1;
1401    }
1402    return 0;
1403}
1404
1405static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1406    const char *n, *m;
1407    pa_sink *sink;
1408    int suspend, r;
1409
1410    pa_core_assert_ref(c);
1411    pa_assert(t);
1412    pa_assert(buf);
1413    pa_assert(fail);
1414
1415    if (!(n = pa_tokenizer_get(t, 1))) {
1416        pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
1417        return -1;
1418    }
1419
1420    if (!(m = pa_tokenizer_get(t, 2))) {
1421        pa_strbuf_puts(buf, "You need to specify a suspend switch setting (0/1).\n");
1422        return -1;
1423    }
1424
1425    if ((suspend = pa_parse_boolean(m)) < 0) {
1426        pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
1427        return -1;
1428    }
1429
1430    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
1431        pa_strbuf_puts(buf, "No sink found by this name or index.\n");
1432        return -1;
1433    }
1434
1435    pa_log_debug("%s of sink %s requested via CLI.", suspend ? "Suspending" : "Resuming", sink->name);
1436
1437    if ((r = pa_sink_suspend(sink, suspend, PA_SUSPEND_USER)) < 0)
1438        pa_strbuf_printf(buf, "Failed to resume/suspend sink: %s\n", pa_strerror(r));
1439
1440    return 0;
1441}
1442
1443static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1444    const char *n, *m;
1445    pa_source *source;
1446    int suspend, r;
1447
1448    pa_core_assert_ref(c);
1449    pa_assert(t);
1450    pa_assert(buf);
1451    pa_assert(fail);
1452
1453    if (!(n = pa_tokenizer_get(t, 1))) {
1454        pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
1455        return -1;
1456    }
1457
1458    if (!(m = pa_tokenizer_get(t, 2))) {
1459        pa_strbuf_puts(buf, "You need to specify a suspend switch setting (0/1).\n");
1460        return -1;
1461    }
1462
1463    if ((suspend = pa_parse_boolean(m)) < 0) {
1464        pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
1465        return -1;
1466    }
1467
1468    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
1469        pa_strbuf_puts(buf, "No source found by this name or index.\n");
1470        return -1;
1471    }
1472
1473    pa_log_debug("%s of source %s requested via CLI.", suspend ? "Suspending" : "Resuming", source->name);
1474
1475    if ((r = pa_source_suspend(source, suspend, PA_SUSPEND_USER)) < 0)
1476        pa_strbuf_printf(buf, "Failed to resume/suspend source: %s\n", pa_strerror(r));
1477
1478    return 0;
1479}
1480
1481static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1482    const char *m;
1483    int suspend, r;
1484
1485    pa_core_assert_ref(c);
1486    pa_assert(t);
1487    pa_assert(buf);
1488    pa_assert(fail);
1489
1490    if (!(m = pa_tokenizer_get(t, 1))) {
1491        pa_strbuf_puts(buf, "You need to specify a suspend switch setting (0/1).\n");
1492        return -1;
1493    }
1494
1495    if ((suspend = pa_parse_boolean(m)) < 0) {
1496        pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
1497        return -1;
1498    }
1499
1500    pa_log_debug("%s of all sinks and sources requested via CLI.", suspend ? "Suspending" : "Resuming");
1501
1502    if ((r = pa_sink_suspend_all(c, suspend, PA_SUSPEND_USER)) < 0)
1503        pa_strbuf_printf(buf, "Failed to resume/suspend all sinks: %s\n", pa_strerror(r));
1504
1505    if ((r = pa_source_suspend_all(c, suspend, PA_SUSPEND_USER)) < 0)
1506        pa_strbuf_printf(buf, "Failed to resume/suspend all sources: %s\n", pa_strerror(r));
1507
1508    return 0;
1509}
1510
1511static int pa_cli_command_log_target(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1512    const char *m;
1513    pa_log_target *log_target = NULL;
1514
1515    pa_core_assert_ref(c);
1516    pa_assert(t);
1517    pa_assert(buf);
1518    pa_assert(fail);
1519
1520    if (!(m = pa_tokenizer_get(t, 1))) {
1521        pa_strbuf_puts(buf, "You need to specify a log target (null|auto|syslog|stderr|file:PATH|newfile:PATH).\n");
1522        return -1;
1523    }
1524
1525    /* 'auto' is actually the effect with 'stderr' */
1526    if (pa_streq(m, "auto"))
1527        log_target = pa_log_target_new(PA_LOG_STDERR, NULL);
1528    else {
1529        log_target = pa_log_parse_target(m);
1530
1531        if (!log_target) {
1532            pa_strbuf_puts(buf, "Invalid log target.\n");
1533            return -1;
1534        }
1535    }
1536
1537    if (pa_log_set_target(log_target) < 0) {
1538        pa_strbuf_puts(buf, "Failed to set log target.\n");
1539        pa_log_target_free(log_target);
1540        return -1;
1541    }
1542
1543    pa_log_target_free(log_target);
1544
1545    return 0;
1546}
1547
1548static int pa_cli_command_log_level(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1549    const char *m;
1550    uint32_t level;
1551
1552    pa_core_assert_ref(c);
1553    pa_assert(t);
1554    pa_assert(buf);
1555    pa_assert(fail);
1556
1557    if (!(m = pa_tokenizer_get(t, 1))) {
1558        pa_strbuf_puts(buf, "You need to specify a log level (0..4).\n");
1559        return -1;
1560    }
1561
1562    if (pa_atou(m, &level) < 0 || level >= PA_LOG_LEVEL_MAX) {
1563        pa_strbuf_puts(buf, "Failed to parse log level.\n");
1564        return -1;
1565    }
1566
1567    pa_log_set_level(level);
1568
1569    return 0;
1570}
1571
1572static int pa_cli_command_log_meta(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1573    const char *m;
1574    int b;
1575
1576    pa_core_assert_ref(c);
1577    pa_assert(t);
1578    pa_assert(buf);
1579    pa_assert(fail);
1580
1581    if (!(m = pa_tokenizer_get(t, 1))) {
1582        pa_strbuf_puts(buf, "You need to specify a boolean.\n");
1583        return -1;
1584    }
1585
1586    if ((b = pa_parse_boolean(m)) < 0) {
1587        pa_strbuf_puts(buf, "Failed to parse log meta switch.\n");
1588        return -1;
1589    }
1590
1591    pa_log_set_flags(PA_LOG_PRINT_META, b ? PA_LOG_SET : PA_LOG_UNSET);
1592
1593    return 0;
1594}
1595
1596static int pa_cli_command_log_time(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1597    const char *m;
1598    int b;
1599
1600    pa_core_assert_ref(c);
1601    pa_assert(t);
1602    pa_assert(buf);
1603    pa_assert(fail);
1604
1605    if (!(m = pa_tokenizer_get(t, 1))) {
1606        pa_strbuf_puts(buf, "You need to specify a boolean.\n");
1607        return -1;
1608    }
1609
1610    if ((b = pa_parse_boolean(m)) < 0) {
1611        pa_strbuf_puts(buf, "Failed to parse log meta switch.\n");
1612        return -1;
1613    }
1614
1615    pa_log_set_flags(PA_LOG_PRINT_TIME, b ? PA_LOG_SET : PA_LOG_UNSET);
1616
1617    return 0;
1618}
1619
1620static int pa_cli_command_log_backtrace(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1621    const char *m;
1622    uint32_t nframes;
1623
1624    pa_core_assert_ref(c);
1625    pa_assert(t);
1626    pa_assert(buf);
1627    pa_assert(fail);
1628
1629    if (!(m = pa_tokenizer_get(t, 1))) {
1630        pa_strbuf_puts(buf, "You need to specify a backtrace level.\n");
1631        return -1;
1632    }
1633
1634    if (pa_atou(m, &nframes) < 0 || nframes >= 1000) {
1635        pa_strbuf_puts(buf, "Failed to parse backtrace level.\n");
1636        return -1;
1637    }
1638
1639    pa_log_set_show_backtrace(nframes);
1640
1641    return 0;
1642}
1643
1644static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1645    const char *n, *p;
1646    pa_card *card;
1647    pa_card_profile *profile;
1648
1649    pa_core_assert_ref(c);
1650    pa_assert(t);
1651    pa_assert(buf);
1652    pa_assert(fail);
1653
1654    if (!(n = pa_tokenizer_get(t, 1))) {
1655        pa_strbuf_puts(buf, "You need to specify a card either by its name or its index.\n");
1656        return -1;
1657    }
1658
1659    if (!(p = pa_tokenizer_get(t, 2))) {
1660        pa_strbuf_puts(buf, "You need to specify a profile by its name.\n");
1661        return -1;
1662    }
1663
1664    if (!(card = pa_namereg_get(c, n, PA_NAMEREG_CARD))) {
1665        pa_strbuf_puts(buf, "No card found by this name or index.\n");
1666        return -1;
1667    }
1668
1669    if (!(profile = pa_hashmap_get(card->profiles, p))) {
1670        pa_strbuf_printf(buf, "No such profile: %s\n", p);
1671        return -1;
1672    }
1673
1674    if (pa_card_set_profile(card, profile, true) < 0) {
1675        pa_strbuf_printf(buf, "Failed to set card profile to '%s'.\n", p);
1676        return -1;
1677    }
1678
1679    return 0;
1680}
1681
1682static int pa_cli_command_sink_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1683    const char *n, *p;
1684    pa_sink *sink;
1685
1686    pa_core_assert_ref(c);
1687    pa_assert(t);
1688    pa_assert(buf);
1689    pa_assert(fail);
1690
1691    if (!(n = pa_tokenizer_get(t, 1))) {
1692        pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
1693        return -1;
1694    }
1695
1696    if (!(p = pa_tokenizer_get(t, 2))) {
1697        pa_strbuf_puts(buf, "You need to specify a profile by its name.\n");
1698        return -1;
1699    }
1700
1701    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
1702        pa_strbuf_puts(buf, "No sink found by this name or index.\n");
1703        return -1;
1704    }
1705
1706    if (pa_sink_set_port(sink, p, true) < 0) {
1707        pa_strbuf_printf(buf, "Failed to set sink port to '%s'.\n", p);
1708        return -1;
1709    }
1710
1711    return 0;
1712}
1713
1714static int pa_cli_command_source_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1715    const char *n, *p;
1716    pa_source *source;
1717
1718    pa_core_assert_ref(c);
1719    pa_assert(t);
1720    pa_assert(buf);
1721    pa_assert(fail);
1722
1723    if (!(n = pa_tokenizer_get(t, 1))) {
1724        pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
1725        return -1;
1726    }
1727
1728    if (!(p = pa_tokenizer_get(t, 2))) {
1729        pa_strbuf_puts(buf, "You need to specify a profile by its name.\n");
1730        return -1;
1731    }
1732
1733    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
1734        pa_strbuf_puts(buf, "No source found by this name or index.\n");
1735        return -1;
1736    }
1737
1738    if (pa_source_set_port(source, p, true) < 0) {
1739        pa_strbuf_printf(buf, "Failed to set source port to '%s'.\n", p);
1740        return -1;
1741    }
1742
1743    return 0;
1744}
1745
1746static int pa_cli_command_port_offset(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1747    const char *n, *p, *l;
1748    pa_device_port *port;
1749    pa_card *card;
1750    int32_t offset;
1751
1752    pa_core_assert_ref(c);
1753    pa_assert(t);
1754    pa_assert(buf);
1755    pa_assert(fail);
1756
1757    if (!(n = pa_tokenizer_get(t, 1))) {
1758        pa_strbuf_puts(buf, "You need to specify a card either by its name or its index.\n");
1759        return -1;
1760    }
1761
1762    if (!(p = pa_tokenizer_get(t, 2))) {
1763        pa_strbuf_puts(buf, "You need to specify a port by its name.\n");
1764        return -1;
1765    }
1766
1767    if (!(l = pa_tokenizer_get(t, 3))) {
1768        pa_strbuf_puts(buf, "You need to specify a latency offset.\n");
1769        return -1;
1770    }
1771
1772    if (pa_atoi(l, &offset) < 0) {
1773        pa_strbuf_puts(buf, "Failed to parse the latency offset.\n");
1774        return -1;
1775    }
1776
1777    if (!(card = pa_namereg_get(c, n, PA_NAMEREG_CARD))) {
1778        pa_strbuf_puts(buf, "No card found by this name or index.\n");
1779        return -1;
1780    }
1781
1782    if (!(port = pa_hashmap_get(card->ports, p))) {
1783        pa_strbuf_puts(buf, "No port found by this name.\n");
1784        return -1;
1785    }
1786
1787    pa_device_port_set_latency_offset(port, offset);
1788
1789    return 0;
1790}
1791
1792static int pa_cli_command_send_message_to_object(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1793    const char *object_path, *message, *message_parameters;
1794    char *response = NULL;
1795    int ret;
1796
1797    pa_core_assert_ref(c);
1798    pa_assert(t);
1799    pa_assert(buf);
1800    pa_assert(fail);
1801
1802
1803    if (!(object_path = pa_tokenizer_get(t, 1))) {
1804        pa_strbuf_puts(buf, "You need to specify an object path as recipient for the message.\n");
1805           return -1;
1806    }
1807
1808    if (!(message = pa_tokenizer_get(t, 2))) {
1809        pa_strbuf_puts(buf, "You need to specify a message name.\n");
1810        return -1;
1811    }
1812
1813    /* parameters may be NULL */
1814    message_parameters = pa_tokenizer_get(t, 3);
1815
1816    ret = pa_message_handler_send_message(c, object_path, message, message_parameters, &response);
1817
1818    if (ret < 0) {
1819        pa_strbuf_printf(buf, "Send message failed: %s\n", pa_strerror(ret));
1820        ret = -1;
1821
1822    } else {
1823        if (response)
1824            pa_strbuf_puts(buf, response);
1825        pa_strbuf_puts(buf, "\n");
1826        ret = 0;
1827    }
1828
1829    pa_xfree(response);
1830    return ret;
1831}
1832
1833static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1834    pa_module *m;
1835    pa_sink *sink;
1836    pa_source *source;
1837    pa_card *card;
1838    bool nl;
1839    uint32_t idx;
1840    time_t now;
1841#ifdef HAVE_CTIME_R
1842    char txt[256];
1843#endif
1844
1845    pa_core_assert_ref(c);
1846    pa_assert(t);
1847    pa_assert(buf);
1848    pa_assert(fail);
1849
1850    time(&now);
1851
1852#ifdef HAVE_CTIME_R
1853    pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime_r(&now, txt));
1854#else
1855    pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime(&now));
1856#endif
1857
1858    PA_IDXSET_FOREACH(m, c->modules, idx) {
1859
1860        pa_strbuf_printf(buf, "load-module %s", m->name);
1861
1862        if (m->argument)
1863            pa_strbuf_printf(buf, " %s", m->argument);
1864
1865        pa_strbuf_puts(buf, "\n");
1866    }
1867
1868    nl = false;
1869    PA_IDXSET_FOREACH(sink, c->sinks, idx) {
1870
1871        if (!nl) {
1872            pa_strbuf_puts(buf, "\n");
1873            nl = true;
1874        }
1875
1876        pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_max(pa_sink_get_volume(sink, false)));
1877        pa_strbuf_printf(buf, "set-sink-mute %s %s\n", sink->name, pa_yes_no(pa_sink_get_mute(sink, false)));
1878        pa_strbuf_printf(buf, "suspend-sink %s %s\n", sink->name, pa_yes_no(sink->state == PA_SINK_SUSPENDED));
1879    }
1880
1881    nl = false;
1882    PA_IDXSET_FOREACH(source, c->sources, idx) {
1883
1884        if (!nl) {
1885            pa_strbuf_puts(buf, "\n");
1886            nl = true;
1887        }
1888
1889        pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_max(pa_source_get_volume(source, false)));
1890        pa_strbuf_printf(buf, "set-source-mute %s %s\n", source->name, pa_yes_no(pa_source_get_mute(source, false)));
1891        pa_strbuf_printf(buf, "suspend-source %s %s\n", source->name, pa_yes_no(source->state == PA_SOURCE_SUSPENDED));
1892    }
1893
1894    nl = false;
1895    PA_IDXSET_FOREACH(card, c->cards, idx) {
1896
1897        if (!nl) {
1898            pa_strbuf_puts(buf, "\n");
1899            nl = true;
1900        }
1901
1902        pa_strbuf_printf(buf, "set-card-profile %s %s\n", card->name, card->active_profile->name);
1903    }
1904
1905    nl = false;
1906    if (c->default_sink) {
1907        if (!nl) {
1908            pa_strbuf_puts(buf, "\n");
1909            nl = true;
1910        }
1911
1912        pa_strbuf_printf(buf, "set-default-sink %s\n", c->default_sink->name);
1913    }
1914
1915    if (c->default_source) {
1916        if (!nl)
1917            pa_strbuf_puts(buf, "\n");
1918
1919        pa_strbuf_printf(buf, "set-default-source %s\n", c->default_source->name);
1920    }
1921
1922    pa_strbuf_puts(buf, "\n### EOF\n");
1923
1924    return 0;
1925}
1926
1927static int pa_cli_command_dump_volumes(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
1928    pa_sink *s;
1929    pa_source *so;
1930    pa_sink_input *i;
1931    pa_source_output *o;
1932    uint32_t s_idx, i_idx;
1933    char v_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
1934    pa_channel_map *map;
1935
1936    pa_core_assert_ref(c);
1937    pa_assert(t);
1938    pa_assert(buf);
1939    pa_assert(fail);
1940
1941    PA_IDXSET_FOREACH(s, c->sinks, s_idx) {
1942        map = &s->channel_map;
1943        pa_strbuf_printf(buf, "Sink %d: ", s_idx);
1944        pa_strbuf_printf(buf,
1945                         "reference = %s, ",
1946                         pa_cvolume_snprint_verbose(v_str,
1947                                                    sizeof(v_str),
1948                                                    &s->reference_volume,
1949                                                    map,
1950                                                    s->flags & PA_SINK_DECIBEL_VOLUME));
1951        pa_strbuf_printf(buf,
1952                         "real = %s, ",
1953                         pa_cvolume_snprint_verbose(v_str,
1954                                                    sizeof(v_str),
1955                                                    &s->real_volume,
1956                                                    &s->channel_map,
1957                                                    s->flags & PA_SINK_DECIBEL_VOLUME));
1958        pa_strbuf_printf(buf, "soft = %s, ", pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &s->soft_volume, map, true));
1959        pa_strbuf_printf(buf,
1960                         "current_hw = %s, ",
1961                         pa_cvolume_snprint_verbose(v_str,
1962                                                    sizeof(v_str),
1963                                                    &s->thread_info.current_hw_volume,
1964                                                    map,
1965                                                    s->flags & PA_SINK_DECIBEL_VOLUME));
1966        pa_strbuf_printf(buf, "save = %s\n", pa_yes_no(s->save_volume));
1967
1968        PA_IDXSET_FOREACH(i, s->inputs, i_idx) {
1969            map = &i->channel_map;
1970            pa_strbuf_printf(buf, "\tInput %d: ", i_idx);
1971            pa_strbuf_printf(buf, "volume = %s, ", pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &i->volume, map, true));
1972            pa_strbuf_printf(buf,
1973                             "reference_ratio = %s, ",
1974                             pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &i->reference_ratio, map, true));
1975            pa_strbuf_printf(buf,
1976                             "real_ratio = %s, ",
1977                             pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &i->real_ratio, map, true));
1978            pa_strbuf_printf(buf, "soft = %s, ", pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &i->soft_volume, map, true));
1979            pa_strbuf_printf(buf,
1980                             "volume_factor = %s, ",
1981                             pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &i->volume_factor, map, true));
1982            pa_strbuf_printf(buf,
1983                             "volume_factor_sink = %s, ",
1984                             pa_cvolume_snprint_verbose(v_str,
1985                                                        sizeof(v_str),
1986                                                        &i->volume_factor_sink,
1987                                                        &i->sink->channel_map,
1988                                                        true));
1989            pa_strbuf_printf(buf, "save = %s\n", pa_yes_no(i->save_volume));
1990        }
1991    }
1992
1993    PA_IDXSET_FOREACH(so, c->sources, s_idx) {
1994        map = &so->channel_map;
1995        pa_strbuf_printf(buf, "Source %d: ", s_idx);
1996        pa_strbuf_printf(buf,
1997                         "reference = %s, ",
1998                         pa_cvolume_snprint_verbose(v_str,
1999                                                    sizeof(v_str),
2000                                                    &so->reference_volume,
2001                                                    map,
2002                                                    so->flags & PA_SOURCE_DECIBEL_VOLUME));
2003        pa_strbuf_printf(buf,
2004                         "real = %s, ",
2005                         pa_cvolume_snprint_verbose(v_str,
2006                                                    sizeof(v_str),
2007                                                    &so->real_volume,
2008                                                    map,
2009                                                    so->flags & PA_SOURCE_DECIBEL_VOLUME));
2010        pa_strbuf_printf(buf, "soft = %s, ", pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &so->soft_volume, map, true));
2011        pa_strbuf_printf(buf,
2012                         "current_hw = %s, ",
2013                         pa_cvolume_snprint_verbose(v_str,
2014                                                    sizeof(v_str),
2015                                                    &so->thread_info.current_hw_volume,
2016                                                    map,
2017                                                    so->flags & PA_SOURCE_DECIBEL_VOLUME));
2018        pa_strbuf_printf(buf, "save = %s\n", pa_yes_no(so->save_volume));
2019
2020        PA_IDXSET_FOREACH(o, so->outputs, i_idx) {
2021            map = &o->channel_map;
2022            pa_strbuf_printf(buf, "\tOutput %d: ", i_idx);
2023            pa_strbuf_printf(buf, "volume = %s, ", pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &o->volume, map, true));
2024            pa_strbuf_printf(buf,
2025                             "reference_ratio = %s, ",
2026                             pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &o->reference_ratio, map, true));
2027            pa_strbuf_printf(buf,
2028                             "real_ratio = %s, ",
2029                             pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &o->real_ratio, map, true));
2030            pa_strbuf_printf(buf, "soft = %s, ", pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &o->soft_volume, map, true));
2031            pa_strbuf_printf(buf,
2032                             "volume_factor = %s, ",
2033                             pa_cvolume_snprint_verbose(v_str, sizeof(v_str), &o->volume_factor, map, true));
2034            pa_strbuf_printf(buf,
2035                             "volume_factor_source = %s, ",
2036                             pa_cvolume_snprint_verbose(v_str,
2037                                                        sizeof(v_str),
2038                                                        &o->volume_factor_source,
2039                                                        &o->source->channel_map,
2040                                                        true));
2041            pa_strbuf_printf(buf, "save = %s\n", pa_yes_no(o->save_volume));
2042        }
2043    }
2044
2045    return 0;
2046}
2047
2048int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *buf, bool *fail, int *ifstate) {
2049    const char *cs;
2050
2051    pa_assert(c);
2052    pa_assert(s);
2053    pa_assert(buf);
2054
2055    cs = s+strspn(s, whitespace);
2056
2057    if (*cs == '#' || !*cs)
2058        return 0;
2059    else if (*cs == '.') {
2060        if (!strcmp(cs, META_ELSE)) {
2061            if (!ifstate || *ifstate == IFSTATE_NONE) {
2062                pa_strbuf_printf(buf, "Meta command %s is not valid in this context\n", cs);
2063                return -1;
2064            } else if (*ifstate == IFSTATE_TRUE)
2065                *ifstate = IFSTATE_FALSE;
2066            else
2067                *ifstate = IFSTATE_TRUE;
2068            return 0;
2069        } else if (!strcmp(cs, META_ENDIF)) {
2070            if (!ifstate || *ifstate == IFSTATE_NONE) {
2071                pa_strbuf_printf(buf, "Meta command %s is not valid in this context\n", cs);
2072                return -1;
2073            } else
2074                *ifstate = IFSTATE_NONE;
2075            return 0;
2076        }
2077        if (ifstate && *ifstate == IFSTATE_FALSE)
2078            return 0;
2079        if (!strcmp(cs, META_FAIL))
2080            *fail = true;
2081        else if (!strcmp(cs, META_NOFAIL))
2082            *fail = false;
2083        else {
2084            size_t l;
2085            l = strcspn(cs, whitespace);
2086
2087            if (l == sizeof(META_INCLUDE)-1 && !strncmp(cs, META_INCLUDE, l)) {
2088                struct stat st;
2089                const char *fn = cs+l+strspn(cs+l, whitespace);
2090
2091                char *filename;
2092#ifdef OS_IS_WIN32
2093                if (strncmp(fn, PA_DEFAULT_CONFIG_DIR, strlen(PA_DEFAULT_CONFIG_DIR)) == 0)
2094                    filename = pa_sprintf_malloc("%s" PA_PATH_SEP "etc" PA_PATH_SEP "pulse" PA_PATH_SEP "%s",
2095                                            pa_win32_get_toplevel(NULL),
2096                                            fn + strlen(PA_DEFAULT_CONFIG_DIR));
2097                else
2098#endif
2099                filename = pa_xstrdup(fn);
2100
2101                if (stat(filename, &st) < 0) {
2102                    pa_log_warn("stat('%s'): %s", filename, pa_cstrerror(errno));
2103                    if (*fail) {
2104                        pa_xfree(filename);
2105                        return -1;
2106                    }
2107                } else {
2108                    if (S_ISDIR(st.st_mode)) {
2109                        DIR *d;
2110
2111                        if (!(d = opendir(filename))) {
2112                            pa_log_warn("Failed to read '%s': %s", filename, pa_cstrerror(errno));
2113                            if (*fail) {
2114                                pa_xfree(filename);
2115                                return -1;
2116                            }
2117                        } else {
2118                            unsigned i, count;
2119                            char **sorted_files;
2120                            struct dirent *de;
2121                            bool failed = false;
2122                            pa_dynarray *files = pa_dynarray_new(NULL);
2123
2124                            while ((de = readdir(d))) {
2125                                char *extn;
2126                                size_t flen = strlen(de->d_name);
2127
2128                                if (flen < 4)
2129                                    continue;
2130
2131                                extn = &de->d_name[flen-3];
2132                                if (strncmp(extn, ".pa", 3) == 0)
2133                                    pa_dynarray_append(files, pa_sprintf_malloc("%s" PA_PATH_SEP "%s", filename, de->d_name));
2134                            }
2135
2136                            closedir(d);
2137                            if ((count = pa_dynarray_size(files))) {
2138                                sorted_files = pa_xnew(char*, count);
2139                                for (i = 0; i < count; ++i)
2140                                    sorted_files[i] = pa_dynarray_get(files, i);
2141                                pa_dynarray_free(files);
2142
2143                                for (i = 0; i < count; ++i) {
2144                                    for (unsigned j = 0; j < count; ++j) {
2145                                        if (strcmp(sorted_files[i], sorted_files[j]) < 0) {
2146                                            char *tmp = sorted_files[i];
2147                                            sorted_files[i] = sorted_files[j];
2148                                            sorted_files[j] = tmp;
2149                                        }
2150                                    }
2151                                }
2152
2153                                for (i = 0; i < count; ++i) {
2154                                    if (!failed) {
2155                                        if (pa_cli_command_execute_file(c, sorted_files[i], buf, fail) < 0 && *fail)
2156                                            failed = true;
2157                                    }
2158
2159                                    pa_xfree(sorted_files[i]);
2160                                }
2161                                pa_xfree(sorted_files);
2162                                if (failed) {
2163                                    pa_xfree(filename);
2164                                    return -1;
2165                                }
2166                            }
2167                        }
2168                    } else if (pa_cli_command_execute_file(c, filename, buf, fail) < 0 && *fail) {
2169                        pa_xfree(filename);
2170                        return -1;
2171                    }
2172                }
2173                pa_xfree(filename);
2174            } else if (l == sizeof(META_IFEXISTS)-1 && !strncmp(cs, META_IFEXISTS, l)) {
2175                if (!ifstate) {
2176                    pa_strbuf_printf(buf, "Meta command %s is not valid in this context\n", cs);
2177                    return -1;
2178                } else if (*ifstate != IFSTATE_NONE) {
2179                    pa_strbuf_printf(buf, "Nested %s commands not supported\n", cs);
2180                    return -1;
2181                } else {
2182                    const char *filename = cs+l+strspn(cs+l, whitespace);
2183                    *ifstate = pa_module_exists(filename) ? IFSTATE_TRUE : IFSTATE_FALSE;
2184                }
2185            } else {
2186                pa_strbuf_printf(buf, "Invalid meta command: %s\n", cs);
2187                if (*fail) return -1;
2188            }
2189        }
2190    } else {
2191        const struct command*command;
2192        int unknown = 1;
2193        size_t l;
2194
2195        if (ifstate && *ifstate == IFSTATE_FALSE)
2196            return 0;
2197
2198        l = strcspn(cs, whitespace);
2199
2200        for (command = commands; command->name; command++)
2201            if (strlen(command->name) == l && !strncmp(cs, command->name, l)) {
2202                int ret;
2203                pa_tokenizer *t = pa_tokenizer_new(cs, command->args);
2204                pa_assert(t);
2205                ret = command->proc(c, t, buf, fail);
2206                pa_tokenizer_free(t);
2207                unknown = 0;
2208
2209                if (ret < 0 && *fail)
2210                    return -1;
2211
2212                break;
2213            }
2214
2215        if (unknown) {
2216            pa_strbuf_printf(buf, "Unknown command: %s\n", cs);
2217            if (*fail)
2218                return -1;
2219        }
2220    }
2221
2222    return 0;
2223}
2224
2225int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, bool *fail) {
2226    return pa_cli_command_execute_line_stateful(c, s, buf, fail, NULL);
2227}
2228
2229int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, bool *fail) {
2230    char line[2048];
2231    int ifstate = IFSTATE_NONE;
2232    int ret = -1;
2233    bool _fail = true;
2234
2235    pa_assert(c);
2236    pa_assert(f);
2237    pa_assert(buf);
2238
2239    if (!fail)
2240        fail = &_fail;
2241
2242    while (fgets(line, sizeof(line), f)) {
2243        pa_strip_nl(line);
2244
2245        if (pa_cli_command_execute_line_stateful(c, line, buf, fail, &ifstate) < 0 && *fail)
2246            goto fail;
2247    }
2248
2249    ret = 0;
2250
2251fail:
2252
2253    return ret;
2254}
2255
2256int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, bool *fail) {
2257    FILE *f = NULL;
2258    int ret = -1;
2259    bool _fail = true;
2260
2261    pa_assert(c);
2262    pa_assert(fn);
2263    pa_assert(buf);
2264
2265    if (!fail)
2266        fail = &_fail;
2267
2268    if (!(f = pa_fopen_cloexec(fn, "r"))) {
2269        pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, pa_cstrerror(errno));
2270        if (!*fail)
2271            ret = 0;
2272        goto fail;
2273    }
2274
2275    pa_log_debug("Parsing script '%s'", fn);
2276    ret = pa_cli_command_execute_file_stream(c, f, buf, fail);
2277
2278fail:
2279    if (f)
2280        fclose(f);
2281
2282    return ret;
2283}
2284
2285int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, bool *fail) {
2286    const char *p;
2287    int ifstate = IFSTATE_NONE;
2288    bool _fail = true;
2289
2290    pa_assert(c);
2291    pa_assert(s);
2292    pa_assert(buf);
2293
2294    if (!fail)
2295        fail = &_fail;
2296
2297    p = s;
2298    while (*p) {
2299        size_t l = strcspn(p, linebreak);
2300        char *line = pa_xstrndup(p, l);
2301
2302        if (pa_cli_command_execute_line_stateful(c, line, buf, fail, &ifstate) < 0 && *fail) {
2303            pa_xfree(line);
2304            return -1;
2305        }
2306        pa_xfree(line);
2307
2308        p += l;
2309        p += strspn(p, linebreak);
2310    }
2311
2312    return 0;
2313}
2314