]> code.delx.au - pulseaudio/blob - src/pulsecore/cli-command.c
make pa_mempool_stat thread-safe/lock-free
[pulseaudio] / src / pulsecore / cli-command.c
1 /* $Id$ */
2
3 /***
4 This file is part of PulseAudio.
5
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2 of the License,
9 or (at your option) any later version.
10
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <assert.h>
29 #include <stdlib.h>
30 #include <errno.h>
31
32 #include <pulse/xmalloc.h>
33
34 #include <pulsecore/module.h>
35 #include <pulsecore/sink.h>
36 #include <pulsecore/source.h>
37 #include <pulsecore/client.h>
38 #include <pulsecore/sink-input.h>
39 #include <pulsecore/source-output.h>
40 #include <pulsecore/tokenizer.h>
41 #include <pulsecore/strbuf.h>
42 #include <pulsecore/namereg.h>
43 #include <pulsecore/cli-text.h>
44 #include <pulsecore/core-scache.h>
45 #include <pulsecore/sample-util.h>
46 #include <pulsecore/sound-file.h>
47 #include <pulsecore/play-memchunk.h>
48 #include <pulsecore/autoload.h>
49 #include <pulsecore/sound-file-stream.h>
50 #include <pulsecore/props.h>
51 #include <pulsecore/core-util.h>
52 #include <pulsecore/core-error.h>
53
54 #include "cli-command.h"
55
56 struct command {
57 const char *name;
58 int (*proc) (pa_core *c, pa_tokenizer*t, pa_strbuf *buf, int *fail);
59 const char *help;
60 unsigned args;
61 };
62
63 #define INCLUDE_META ".include"
64 #define FAIL_META ".fail"
65 #define NOFAIL_META ".nofail"
66
67 /* Prototypes for all available commands */
68 static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
69 static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
70 static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
71 static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
72 static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
73 static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
74 static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
75 static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
76 static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
77 static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
78 static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
79 static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
80 static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
81 static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
82 static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
83 static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
84 static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
85 static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
86 static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
87 static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
88 static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
89 static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
90 static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
91 static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
92 static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
93 static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
94 static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
95 static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
96 static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
97 static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
98 static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
99 static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
100 static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
101 static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
102 static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
103 static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
104
105 /* A method table for all available commands */
106
107 static const struct command commands[] = {
108 { "exit", pa_cli_command_exit, "Terminate the daemon", 1 },
109 { "help", pa_cli_command_help, "Show this help", 1 },
110 { "list-modules", pa_cli_command_modules, "List loaded modules", 1 },
111 { "list-sinks", pa_cli_command_sinks, "List loaded sinks", 1 },
112 { "list-sources", pa_cli_command_sources, "List loaded sources", 1 },
113 { "list-clients", pa_cli_command_clients, "List loaded clients", 1 },
114 { "list-sink-inputs", pa_cli_command_sink_inputs, "List sink inputs", 1 },
115 { "list-source-outputs", pa_cli_command_source_outputs, "List source outputs", 1 },
116 { "stat", pa_cli_command_stat, "Show memory block statistics", 1 },
117 { "info", pa_cli_command_info, "Show comprehensive status", 1 },
118 { "ls", pa_cli_command_info, NULL, 1 },
119 { "list", pa_cli_command_info, NULL, 1 },
120 { "load-module", pa_cli_command_load, "Load a module (args: name, arguments)", 3},
121 { "unload-module", pa_cli_command_unload, "Unload a module (args: index)", 2},
122 { "set-sink-volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3},
123 { "set-sink-input-volume", pa_cli_command_sink_input_volume, "Set the volume of a sink input (args: index|name, volume)", 3},
124 { "set-source-volume", pa_cli_command_source_volume, "Set the volume of a source (args: index|name, volume)", 3},
125 { "set-sink-mute", pa_cli_command_sink_mute, "Set the mute switch of a sink (args: index|name, mute)", 3},
126 { "set-source-mute", pa_cli_command_source_mute, "Set the mute switch of a source (args: index|name, mute)", 3},
127 { "set-default-sink", pa_cli_command_sink_default, "Set the default sink (args: index|name)", 2},
128 { "set-default-source", pa_cli_command_source_default, "Set the default source (args: index|name)", 2},
129 { "kill-client", pa_cli_command_kill_client, "Kill a client (args: index)", 2},
130 { "kill-sink-input", pa_cli_command_kill_sink_input, "Kill a sink input (args: index)", 2},
131 { "kill-source-output", pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2},
132 { "list-samples", pa_cli_command_scache_list, "List all entries in the sample cache", 1},
133 { "play-sample", pa_cli_command_scache_play, "Play a sample from the sample cache (args: name, sink|index)", 3},
134 { "remove-sample", pa_cli_command_scache_remove, "Remove a sample from the sample cache (args: name)", 2},
135 { "load-sample", pa_cli_command_scache_load, "Load a sound file into the sample cache (args: name, filename)", 3},
136 { "load-sample-lazy", pa_cli_command_scache_load, "Lazily load a sound file into the sample cache (args: name, filename)", 3},
137 { "load-sample-dir-lazy", pa_cli_command_scache_load_dir, "Lazily load all files in a directory into the sample cache (args: pathname)", 2},
138 { "play-file", pa_cli_command_play_file, "Play a sound file (args: filename, sink|index)", 3},
139 { "list-autoload", pa_cli_command_autoload_list, "List autoload entries", 1},
140 { "add-autoload-sink", pa_cli_command_autoload_add, "Add autoload entry for a sink (args: sink, module name, arguments)", 4},
141 { "add-autoload-source", pa_cli_command_autoload_add, "Add autoload entry for a source (args: source, module name, arguments)", 4},
142 { "remove-autoload-sink", pa_cli_command_autoload_remove, "Remove autoload entry for a sink (args: name)", 2},
143 { "remove-autoload-source", pa_cli_command_autoload_remove, "Remove autoload entry for a source (args: name)", 2},
144 { "dump", pa_cli_command_dump, "Dump daemon configuration", 1},
145 { "list-props", pa_cli_command_list_props, NULL, 1},
146 { "move-sink-input", pa_cli_command_move_sink_input, "Move sink input to another sink (args: index, sink)", 3},
147 { "move-source-output", pa_cli_command_move_source_output, "Move source output to another source (args: index, source)", 3},
148 { "vacuum", pa_cli_command_vacuum, NULL, 1},
149 { NULL, NULL, NULL, 0 }
150 };
151
152 static const char whitespace[] = " \t\n\r";
153 static const char linebreak[] = "\n\r";
154
155 static uint32_t parse_index(const char *n) {
156 uint32_t idx;
157
158 if (pa_atou(n, &idx) < 0)
159 return (uint32_t) PA_IDXSET_INVALID;
160
161 return idx;
162 }
163
164 static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, PA_GCC_UNUSED pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
165 assert(c && c->mainloop && t);
166 c->mainloop->quit(c->mainloop, 0);
167 return 0;
168 }
169
170 static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
171 const struct command*command;
172 assert(c && t && buf);
173
174 pa_strbuf_puts(buf, "Available commands:\n");
175
176 for (command = commands; command->name; command++)
177 if (command->help)
178 pa_strbuf_printf(buf, " %-25s %s\n", command->name, command->help);
179 return 0;
180 }
181
182 static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
183 char *s;
184 assert(c && t);
185 s = pa_module_list_to_string(c);
186 assert(s);
187 pa_strbuf_puts(buf, s);
188 pa_xfree(s);
189 return 0;
190 }
191
192 static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
193 char *s;
194 assert(c && t);
195 s = pa_client_list_to_string(c);
196 assert(s);
197 pa_strbuf_puts(buf, s);
198 pa_xfree(s);
199 return 0;
200 }
201
202 static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
203 char *s;
204 assert(c && t);
205 s = pa_sink_list_to_string(c);
206 assert(s);
207 pa_strbuf_puts(buf, s);
208 pa_xfree(s);
209 return 0;
210 }
211
212 static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
213 char *s;
214 assert(c && t);
215 s = pa_source_list_to_string(c);
216 assert(s);
217 pa_strbuf_puts(buf, s);
218 pa_xfree(s);
219 return 0;
220 }
221
222 static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
223 char *s;
224 assert(c && t);
225 s = pa_sink_input_list_to_string(c);
226 assert(s);
227 pa_strbuf_puts(buf, s);
228 pa_xfree(s);
229 return 0;
230 }
231
232 static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
233 char *s;
234 assert(c && t);
235 s = pa_source_output_list_to_string(c);
236 assert(s);
237 pa_strbuf_puts(buf, s);
238 pa_xfree(s);
239 return 0;
240 }
241
242 static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
243 char s[256];
244 const pa_mempool_stat *stat;
245 unsigned k;
246
247 static const char* const type_table[PA_MEMBLOCK_TYPE_MAX] = {
248 [PA_MEMBLOCK_POOL] = "POOL",
249 [PA_MEMBLOCK_POOL_EXTERNAL] = "POOL_EXTERNAL",
250 [PA_MEMBLOCK_APPENDED] = "APPENDED",
251 [PA_MEMBLOCK_USER] = "USER",
252 [PA_MEMBLOCK_FIXED] = "FIXED",
253 [PA_MEMBLOCK_IMPORTED] = "IMPORTED",
254 };
255
256 assert(c);
257 assert(t);
258
259 stat = pa_mempool_get_stat(c->mempool);
260
261 pa_strbuf_printf(buf, "Memory blocks currently allocated: %u, size: %s.\n",
262 (unsigned) AO_load_acquire_read((AO_t*) &stat->n_allocated),
263 pa_bytes_snprint(s, sizeof(s), (size_t) AO_load_acquire_read((AO_t*) &stat->allocated_size)));
264
265 pa_strbuf_printf(buf, "Memory blocks allocated during the whole lifetime: %u, size: %s.\n",
266 (unsigned) AO_load_acquire_read((AO_t*) &stat->n_accumulated),
267 pa_bytes_snprint(s, sizeof(s), (size_t) AO_load_acquire_read((AO_t*) &stat->accumulated_size)));
268
269 pa_strbuf_printf(buf, "Memory blocks imported from other processes: %u, size: %s.\n",
270 (unsigned) AO_load_acquire_read((AO_t*) &stat->n_imported),
271 pa_bytes_snprint(s, sizeof(s), (size_t) AO_load_acquire_read((AO_t*) &stat->imported_size)));
272
273 pa_strbuf_printf(buf, "Memory blocks exported to other processes: %u, size: %s.\n",
274 (unsigned) AO_load_acquire_read((AO_t*) &stat->n_exported),
275 pa_bytes_snprint(s, sizeof(s), (size_t) AO_load_acquire_read((AO_t*) &stat->exported_size)));
276
277 pa_strbuf_printf(buf, "Total sample cache size: %s.\n",
278 pa_bytes_snprint(s, sizeof(s), pa_scache_total_size(c)));
279
280 pa_strbuf_printf(buf, "Default sample spec: %s\n",
281 pa_sample_spec_snprint(s, sizeof(s), &c->default_sample_spec));
282
283 pa_strbuf_printf(buf, "Default sink name: %s\n"
284 "Default source name: %s\n",
285 pa_namereg_get_default_sink_name(c),
286 pa_namereg_get_default_source_name(c));
287
288 for (k = 0; k < PA_MEMBLOCK_TYPE_MAX; k++)
289 pa_strbuf_printf(buf,
290 "Memory blocks of type %s: %u allocated/%u accumulated.\n",
291 type_table[k],
292 (unsigned) AO_load_acquire_read(&stat->n_allocated_by_type[k]),
293 (unsigned) AO_load_acquire_read(&stat->n_accumulated_by_type[k]));
294
295 return 0;
296 }
297
298 static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
299 assert(c && t);
300 pa_cli_command_stat(c, t, buf, fail);
301 pa_cli_command_modules(c, t, buf, fail);
302 pa_cli_command_sinks(c, t, buf, fail);
303 pa_cli_command_sources(c, t, buf, fail);
304 pa_cli_command_clients(c, t, buf, fail);
305 pa_cli_command_sink_inputs(c, t, buf, fail);
306 pa_cli_command_source_outputs(c, t, buf, fail);
307 pa_cli_command_scache_list(c, t, buf, fail);
308 pa_cli_command_autoload_list(c, t, buf, fail);
309 return 0;
310 }
311
312 static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
313 pa_module *m;
314 const char *name;
315 assert(c && t);
316
317 if (!(name = pa_tokenizer_get(t, 1))) {
318 pa_strbuf_puts(buf, "You need to specify the module name and optionally arguments.\n");
319 return -1;
320 }
321
322 if (!(m = pa_module_load(c, name, pa_tokenizer_get(t, 2)))) {
323 pa_strbuf_puts(buf, "Module load failed.\n");
324 return -1;
325 }
326
327 return 0;
328 }
329
330 static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
331 pa_module *m;
332 uint32_t idx;
333 const char *i;
334 char *e;
335 assert(c && t);
336
337 if (!(i = pa_tokenizer_get(t, 1))) {
338 pa_strbuf_puts(buf, "You need to specify the module index.\n");
339 return -1;
340 }
341
342 idx = (uint32_t) strtoul(i, &e, 10);
343 if (*e || !(m = pa_idxset_get_by_index(c->modules, idx))) {
344 pa_strbuf_puts(buf, "Invalid module index.\n");
345 return -1;
346 }
347
348 pa_module_unload_request(m);
349 return 0;
350 }
351
352 static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
353 const char *n, *v;
354 pa_sink *sink;
355 uint32_t volume;
356 pa_cvolume cvolume;
357
358 if (!(n = pa_tokenizer_get(t, 1))) {
359 pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
360 return -1;
361 }
362
363 if (!(v = pa_tokenizer_get(t, 2))) {
364 pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
365 return -1;
366 }
367
368 if (pa_atou(v, &volume) < 0) {
369 pa_strbuf_puts(buf, "Failed to parse volume.\n");
370 return -1;
371 }
372
373 if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
374 pa_strbuf_puts(buf, "No sink found by this name or index.\n");
375 return -1;
376 }
377
378 pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume);
379 pa_sink_set_volume(sink, PA_MIXER_HARDWARE, &cvolume);
380 return 0;
381 }
382
383 static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
384 const char *n, *v;
385 pa_sink_input *si;
386 pa_volume_t volume;
387 pa_cvolume cvolume;
388 uint32_t idx;
389
390 if (!(n = pa_tokenizer_get(t, 1))) {
391 pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
392 return -1;
393 }
394
395 if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
396 pa_strbuf_puts(buf, "Failed to parse index.\n");
397 return -1;
398 }
399
400 if (!(v = pa_tokenizer_get(t, 2))) {
401 pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
402 return -1;
403 }
404
405 if (pa_atou(v, &volume) < 0) {
406 pa_strbuf_puts(buf, "Failed to parse volume.\n");
407 return -1;
408 }
409
410 if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
411 pa_strbuf_puts(buf, "No sink input found with this index.\n");
412 return -1;
413 }
414
415 pa_cvolume_set(&cvolume, si->sample_spec.channels, volume);
416 pa_sink_input_set_volume(si, &cvolume);
417 return 0;
418 }
419
420 static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
421 const char *n, *v;
422 pa_source *source;
423 uint32_t volume;
424 pa_cvolume cvolume;
425
426 if (!(n = pa_tokenizer_get(t, 1))) {
427 pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
428 return -1;
429 }
430
431 if (!(v = pa_tokenizer_get(t, 2))) {
432 pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
433 return -1;
434 }
435
436 if (pa_atou(v, &volume) < 0) {
437 pa_strbuf_puts(buf, "Failed to parse volume.\n");
438 return -1;
439 }
440
441 if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) {
442 pa_strbuf_puts(buf, "No source found by this name or index.\n");
443 return -1;
444 }
445
446 pa_cvolume_set(&cvolume, source->sample_spec.channels, volume);
447 pa_source_set_volume(source, PA_MIXER_HARDWARE, &cvolume);
448 return 0;
449 }
450
451 static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
452 const char *n, *m;
453 pa_sink *sink;
454 int mute;
455
456 if (!(n = pa_tokenizer_get(t, 1))) {
457 pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
458 return -1;
459 }
460
461 if (!(m = pa_tokenizer_get(t, 2))) {
462 pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
463 return -1;
464 }
465
466 if (pa_atoi(m, &mute) < 0) {
467 pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
468 return -1;
469 }
470
471 if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
472 pa_strbuf_puts(buf, "No sink found by this name or index.\n");
473 return -1;
474 }
475
476 pa_sink_set_mute(sink, PA_MIXER_HARDWARE, mute);
477 return 0;
478 }
479
480 static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
481 const char *n, *m;
482 pa_source *source;
483 int mute;
484
485 if (!(n = pa_tokenizer_get(t, 1))) {
486 pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
487 return -1;
488 }
489
490 if (!(m = pa_tokenizer_get(t, 2))) {
491 pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
492 return -1;
493 }
494
495 if (pa_atoi(m, &mute) < 0) {
496 pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
497 return -1;
498 }
499
500 if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) {
501 pa_strbuf_puts(buf, "No sink found by this name or index.\n");
502 return -1;
503 }
504
505 pa_source_set_mute(source, PA_MIXER_HARDWARE, mute);
506 return 0;
507 }
508
509 static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
510 const char *n;
511 assert(c && t);
512
513 if (!(n = pa_tokenizer_get(t, 1))) {
514 pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
515 return -1;
516 }
517
518 pa_namereg_set_default(c, n, PA_NAMEREG_SINK);
519 return 0;
520 }
521
522 static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
523 const char *n;
524 assert(c && t);
525
526 if (!(n = pa_tokenizer_get(t, 1))) {
527 pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
528 return -1;
529 }
530
531 pa_namereg_set_default(c, n, PA_NAMEREG_SOURCE);
532 return 0;
533 }
534
535 static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
536 const char *n;
537 pa_client *client;
538 uint32_t idx;
539 assert(c && t);
540
541 if (!(n = pa_tokenizer_get(t, 1))) {
542 pa_strbuf_puts(buf, "You need to specify a client by its index.\n");
543 return -1;
544 }
545
546 if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
547 pa_strbuf_puts(buf, "Failed to parse index.\n");
548 return -1;
549 }
550
551 if (!(client = pa_idxset_get_by_index(c->clients, idx))) {
552 pa_strbuf_puts(buf, "No client found by this index.\n");
553 return -1;
554 }
555
556 pa_client_kill(client);
557 return 0;
558 }
559
560 static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
561 const char *n;
562 pa_sink_input *sink_input;
563 uint32_t idx;
564 assert(c && t);
565
566 if (!(n = pa_tokenizer_get(t, 1))) {
567 pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
568 return -1;
569 }
570
571 if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
572 pa_strbuf_puts(buf, "Failed to parse index.\n");
573 return -1;
574 }
575
576 if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, idx))) {
577 pa_strbuf_puts(buf, "No sink input found by this index.\n");
578 return -1;
579 }
580
581 pa_sink_input_kill(sink_input);
582 return 0;
583 }
584
585 static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
586 const char *n;
587 pa_source_output *source_output;
588 uint32_t idx;
589 assert(c && t);
590
591 if (!(n = pa_tokenizer_get(t, 1))) {
592 pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
593 return -1;
594 }
595
596 if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
597 pa_strbuf_puts(buf, "Failed to parse index.\n");
598 return -1;
599 }
600
601 if (!(source_output = pa_idxset_get_by_index(c->source_outputs, idx))) {
602 pa_strbuf_puts(buf, "No source output found by this index.\n");
603 return -1;
604 }
605
606 pa_source_output_kill(source_output);
607 return 0;
608 }
609
610 static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
611 char *s;
612 assert(c && t);
613 s = pa_scache_list_to_string(c);
614 assert(s);
615 pa_strbuf_puts(buf, s);
616 pa_xfree(s);
617 return 0;
618 }
619
620 static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
621 const char *n, *sink_name;
622 pa_sink *sink;
623 assert(c && t && buf && fail);
624
625 if (!(n = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
626 pa_strbuf_puts(buf, "You need to specify a sample name and a sink name.\n");
627 return -1;
628 }
629
630 if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) {
631 pa_strbuf_puts(buf, "No sink by that name.\n");
632 return -1;
633 }
634
635 if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM) < 0) {
636 pa_strbuf_puts(buf, "Failed to play sample.\n");
637 return -1;
638 }
639
640 return 0;
641 }
642
643 static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
644 const char *n;
645 assert(c && t && buf && fail);
646
647 if (!(n = pa_tokenizer_get(t, 1))) {
648 pa_strbuf_puts(buf, "You need to specify a sample name.\n");
649 return -1;
650 }
651
652 if (pa_scache_remove_item(c, n) < 0) {
653 pa_strbuf_puts(buf, "Failed to remove sample.\n");
654 return -1;
655 }
656
657 return 0;
658 }
659
660 static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
661 const char *fname, *n;
662 int r;
663 assert(c && t && buf && fail);
664
665 if (!(fname = pa_tokenizer_get(t, 2)) || !(n = pa_tokenizer_get(t, 1))) {
666 pa_strbuf_puts(buf, "You need to specify a file name and a sample name.\n");
667 return -1;
668 }
669
670 if (strstr(pa_tokenizer_get(t, 0), "lazy"))
671 r = pa_scache_add_file_lazy(c, n, fname, NULL);
672 else
673 r = pa_scache_add_file(c, n, fname, NULL);
674
675 if (r < 0)
676 pa_strbuf_puts(buf, "Failed to load sound file.\n");
677
678 return 0;
679 }
680
681 static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
682 const char *pname;
683 assert(c && t && buf && fail);
684
685 if (!(pname = pa_tokenizer_get(t, 1))) {
686 pa_strbuf_puts(buf, "You need to specify a path name.\n");
687 return -1;
688 }
689
690 if (pa_scache_add_directory_lazy(c, pname) < 0) {
691 pa_strbuf_puts(buf, "Failed to load directory.\n");
692 return -1;
693 }
694
695 return 0;
696 }
697
698 static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
699 const char *fname, *sink_name;
700 pa_sink *sink;
701 assert(c && t && buf && fail);
702
703 if (!(fname = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
704 pa_strbuf_puts(buf, "You need to specify a file name and a sink name.\n");
705 return -1;
706 }
707
708 if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) {
709 pa_strbuf_puts(buf, "No sink by that name.\n");
710 return -1;
711 }
712
713
714 return pa_play_file(sink, fname, NULL);
715 }
716
717 static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
718 const char *a, *b;
719 assert(c && t && buf && fail);
720
721 if (!(a = pa_tokenizer_get(t, 1)) || !(b = pa_tokenizer_get(t, 2))) {
722 pa_strbuf_puts(buf, "You need to specify a device name, a filename or a module name and optionally module arguments\n");
723 return -1;
724 }
725
726 pa_autoload_add(c, a, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, b, pa_tokenizer_get(t, 3), NULL);
727
728 return 0;
729 }
730
731 static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
732 const char *name;
733 assert(c && t && buf && fail);
734
735 if (!(name = pa_tokenizer_get(t, 1))) {
736 pa_strbuf_puts(buf, "You need to specify a device name\n");
737 return -1;
738 }
739
740 if (pa_autoload_remove_by_name(c, name, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE) < 0) {
741 pa_strbuf_puts(buf, "Failed to remove autload entry\n");
742 return -1;
743 }
744
745 return 0;
746 }
747
748 static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
749 char *s;
750 assert(c && t);
751 s = pa_autoload_list_to_string(c);
752 assert(s);
753 pa_strbuf_puts(buf, s);
754 pa_xfree(s);
755 return 0;
756 }
757
758 static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
759 assert(c && t);
760 pa_property_dump(c, buf);
761 return 0;
762 }
763
764 static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
765 assert(c);
766 assert(t);
767
768 pa_mempool_vacuum(c->mempool);
769
770 return 0;
771 }
772
773 static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
774 const char *n, *k;
775 pa_sink_input *si;
776 pa_sink *sink;
777 uint32_t idx;
778
779 if (!(n = pa_tokenizer_get(t, 1))) {
780 pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
781 return -1;
782 }
783
784 if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
785 pa_strbuf_puts(buf, "Failed to parse index.\n");
786 return -1;
787 }
788
789 if (!(k = pa_tokenizer_get(t, 2))) {
790 pa_strbuf_puts(buf, "You need to specify a sink.\n");
791 return -1;
792 }
793
794 if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
795 pa_strbuf_puts(buf, "No sink input found with this index.\n");
796 return -1;
797 }
798
799 if (!(sink = pa_namereg_get(c, k, PA_NAMEREG_SINK, 1))) {
800 pa_strbuf_puts(buf, "No sink found by this name or index.\n");
801 return -1;
802 }
803
804 if (pa_sink_input_move_to(si, sink, 0) < 0) {
805 pa_strbuf_puts(buf, "Moved failed.\n");
806 return -1;
807 }
808 return 0;
809 }
810
811 static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
812 const char *n, *k;
813 pa_source_output *so;
814 pa_source *source;
815 uint32_t idx;
816
817 if (!(n = pa_tokenizer_get(t, 1))) {
818 pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
819 return -1;
820 }
821
822 if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
823 pa_strbuf_puts(buf, "Failed to parse index.\n");
824 return -1;
825 }
826
827 if (!(k = pa_tokenizer_get(t, 2))) {
828 pa_strbuf_puts(buf, "You need to specify a source.\n");
829 return -1;
830 }
831
832 if (!(so = pa_idxset_get_by_index(c->source_outputs, (uint32_t) idx))) {
833 pa_strbuf_puts(buf, "No source output found with this index.\n");
834 return -1;
835 }
836
837 if (!(source = pa_namereg_get(c, k, PA_NAMEREG_SOURCE, 1))) {
838 pa_strbuf_puts(buf, "No source found by this name or index.\n");
839 return -1;
840 }
841
842 if (pa_source_output_move_to(so, source) < 0) {
843 pa_strbuf_puts(buf, "Moved failed.\n");
844 return -1;
845 }
846 return 0;
847 }
848
849 static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
850 pa_module *m;
851 pa_sink *sink;
852 pa_source *source;
853 int nl;
854 const char *p;
855 uint32_t idx;
856 char txt[256];
857 time_t now;
858 void *i;
859 pa_autoload_entry *a;
860
861 assert(c && t);
862
863 time(&now);
864
865 #ifdef HAVE_CTIME_R
866 pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime_r(&now, txt));
867 #else
868 pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime(&now));
869 #endif
870
871
872 for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
873 if (m->auto_unload)
874 continue;
875
876 pa_strbuf_printf(buf, "load-module %s", m->name);
877
878 if (m->argument)
879 pa_strbuf_printf(buf, " %s", m->argument);
880
881 pa_strbuf_puts(buf, "\n");
882 }
883
884 nl = 0;
885
886 for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
887 if (sink->owner && sink->owner->auto_unload)
888 continue;
889
890 if (!nl) {
891 pa_strbuf_puts(buf, "\n");
892 nl = 1;
893 }
894
895 pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink, PA_MIXER_HARDWARE)));
896 pa_strbuf_printf(buf, "set-sink-mute %s %d\n", sink->name, pa_sink_get_mute(sink, PA_MIXER_HARDWARE));
897 }
898
899 for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
900 if (source->owner && source->owner->auto_unload)
901 continue;
902
903 if (!nl) {
904 pa_strbuf_puts(buf, "\n");
905 nl = 1;
906 }
907
908 pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source, PA_MIXER_HARDWARE)));
909 pa_strbuf_printf(buf, "set-source-mute %s %d\n", source->name, pa_source_get_mute(source, PA_MIXER_HARDWARE));
910 }
911
912
913 if (c->autoload_hashmap) {
914 nl = 0;
915
916 i = NULL;
917 while ((a = pa_hashmap_iterate(c->autoload_hashmap, &i, NULL))) {
918
919 if (!nl) {
920 pa_strbuf_puts(buf, "\n");
921 nl = 1;
922 }
923
924 pa_strbuf_printf(buf, "add-autoload-%s %s %s", a->type == PA_NAMEREG_SINK ? "sink" : "source", a->name, a->module);
925
926 if (a->argument)
927 pa_strbuf_printf(buf, " %s", a->argument);
928
929 pa_strbuf_puts(buf, "\n");
930 }
931 }
932
933 nl = 0;
934
935 if ((p = pa_namereg_get_default_sink_name(c))) {
936 if (!nl) {
937 pa_strbuf_puts(buf, "\n");
938 nl = 1;
939 }
940 pa_strbuf_printf(buf, "set-default-sink %s\n", p);
941 }
942
943 if ((p = pa_namereg_get_default_source_name(c))) {
944 if (!nl) {
945 pa_strbuf_puts(buf, "\n");
946 nl = 1;
947 }
948 pa_strbuf_printf(buf, "set-default-source %s\n", p);
949 }
950
951 pa_strbuf_puts(buf, "\n### EOF\n");
952
953 return 0;
954 }
955
956 int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *fail) {
957 const char *cs;
958
959 cs = s+strspn(s, whitespace);
960
961 if (*cs == '#' || !*cs)
962 return 0;
963 else if (*cs == '.') {
964 if (!strcmp(cs, FAIL_META))
965 *fail = 1;
966 else if (!strcmp(cs, NOFAIL_META))
967 *fail = 0;
968 else {
969 size_t l;
970 l = strcspn(cs, whitespace);
971
972 if (l == sizeof(INCLUDE_META)-1 && !strncmp(cs, INCLUDE_META, l)) {
973 const char *filename = cs+l+strspn(cs+l, whitespace);
974
975 if (pa_cli_command_execute_file(c, filename, buf, fail) < 0)
976 if (*fail) return -1;
977 } else {
978 pa_strbuf_printf(buf, "Invalid meta command: %s\n", cs);
979 if (*fail) return -1;
980 }
981 }
982 } else {
983 const struct command*command;
984 int unknown = 1;
985 size_t l;
986
987 l = strcspn(cs, whitespace);
988
989 for (command = commands; command->name; command++)
990 if (strlen(command->name) == l && !strncmp(cs, command->name, l)) {
991 int ret;
992 pa_tokenizer *t = pa_tokenizer_new(cs, command->args);
993 assert(t);
994 ret = command->proc(c, t, buf, fail);
995 pa_tokenizer_free(t);
996 unknown = 0;
997
998 if (ret < 0 && *fail)
999 return -1;
1000
1001 break;
1002 }
1003
1004 if (unknown) {
1005 pa_strbuf_printf(buf, "Unknown command: %s\n", cs);
1006 if (*fail)
1007 return -1;
1008 }
1009 }
1010
1011 return 0;
1012 }
1013
1014 int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int *fail) {
1015 char line[256];
1016 FILE *f = NULL;
1017 int ret = -1;
1018 assert(c && fn && buf);
1019
1020 if (!(f = fopen(fn, "r"))) {
1021 pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, pa_cstrerror(errno));
1022 if (!*fail)
1023 ret = 0;
1024 goto fail;
1025 }
1026
1027 while (fgets(line, sizeof(line), f)) {
1028 char *e = line + strcspn(line, linebreak);
1029 *e = 0;
1030
1031 if (pa_cli_command_execute_line(c, line, buf, fail) < 0 && *fail)
1032 goto fail;
1033 }
1034
1035 ret = 0;
1036
1037 fail:
1038 if (f)
1039 fclose(f);
1040
1041 return ret;
1042 }
1043
1044 int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail) {
1045 const char *p;
1046 assert(c && s && buf && fail);
1047
1048 p = s;
1049 while (*p) {
1050 size_t l = strcspn(p, linebreak);
1051 char *line = pa_xstrndup(p, l);
1052
1053 if (pa_cli_command_execute_line(c, line, buf, fail) < 0&& *fail) {
1054 pa_xfree(line);
1055 return -1;
1056 }
1057 pa_xfree(line);
1058
1059 p += l;
1060 p += strspn(p, linebreak);
1061 }
1062
1063 return 0;
1064 }