]> code.delx.au - pulseaudio/blob - src/pulsecore/cli-command.c
Merge commit 'origin/master-tx'
[pulseaudio] / src / pulsecore / cli-command.c
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, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <unistd.h>
32 #include <ltdl.h>
33
34 #include <pulse/xmalloc.h>
35 #include <pulse/error.h>
36
37 #include <pulsecore/module.h>
38 #include <pulsecore/sink.h>
39 #include <pulsecore/source.h>
40 #include <pulsecore/client.h>
41 #include <pulsecore/sink-input.h>
42 #include <pulsecore/source-output.h>
43 #include <pulsecore/tokenizer.h>
44 #include <pulsecore/strbuf.h>
45 #include <pulsecore/namereg.h>
46 #include <pulsecore/cli-text.h>
47 #include <pulsecore/core-scache.h>
48 #include <pulsecore/sample-util.h>
49 #include <pulsecore/sound-file.h>
50 #include <pulsecore/play-memchunk.h>
51 #include <pulsecore/sound-file-stream.h>
52 #include <pulsecore/shared.h>
53 #include <pulsecore/core-util.h>
54 #include <pulsecore/core-error.h>
55 #include <pulsecore/modinfo.h>
56
57 #include "cli-command.h"
58
59 struct command {
60 const char *name;
61 int (*proc) (pa_core *c, pa_tokenizer*t, pa_strbuf *buf, pa_bool_t *fail);
62 const char *help;
63 unsigned args;
64 };
65
66 #define META_INCLUDE ".include"
67 #define META_FAIL ".fail"
68 #define META_NOFAIL ".nofail"
69 #define META_IFEXISTS ".ifexists"
70 #define META_ELSE ".else"
71 #define META_ENDIF ".endif"
72
73 enum {
74 IFSTATE_NONE = -1,
75 IFSTATE_FALSE = 0,
76 IFSTATE_TRUE = 1,
77 };
78
79 /* Prototypes for all available commands */
80 static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
81 static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
82 static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
83 static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
84 static int pa_cli_command_cards(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
85 static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
86 static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
87 static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
88 static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
89 static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
90 static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
91 static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
92 static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
93 static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
94 static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
95 static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
96 static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
97 static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
98 static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
99 static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
100 static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
101 static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
102 static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
103 static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
104 static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
105 static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
106 static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
107 static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
108 static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
109 static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
110 static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
111 static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
112 static int pa_cli_command_list_shared_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
113 static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
114 static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
115 static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
116 static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
117 static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
118 static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
119 static int pa_cli_command_log_level(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
120 static int pa_cli_command_log_meta(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
121 static int pa_cli_command_log_time(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
122 static int pa_cli_command_log_backtrace(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
123 static int pa_cli_command_update_sink_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
124 static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
125 static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
126 static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
127 static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
128
129 /* A method table for all available commands */
130
131 static const struct command commands[] = {
132 { "exit", pa_cli_command_exit, "Terminate the daemon", 1 },
133 { "help", pa_cli_command_help, "Show this help", 1 },
134 { "list-modules", pa_cli_command_modules, "List loaded modules", 1 },
135 { "list-sinks", pa_cli_command_sinks, "List loaded sinks", 1 },
136 { "list-sources", pa_cli_command_sources, "List loaded sources", 1 },
137 { "list-clients", pa_cli_command_clients, "List loaded clients", 1 },
138 { "list-sink-inputs", pa_cli_command_sink_inputs, "List sink inputs", 1 },
139 { "list-source-outputs", pa_cli_command_source_outputs, "List source outputs", 1 },
140 { "list-cards", pa_cli_command_cards, "List cards", 1 },
141 { "stat", pa_cli_command_stat, "Show memory block statistics", 1 },
142 { "info", pa_cli_command_info, "Show comprehensive status", 1 },
143 { "ls", pa_cli_command_info, NULL, 1 },
144 { "list", pa_cli_command_info, NULL, 1 },
145 { "load-module", pa_cli_command_load, "Load a module (args: name, arguments)", 3},
146 { "unload-module", pa_cli_command_unload, "Unload a module (args: index)", 2},
147 { "describe-module", pa_cli_command_describe, "Describe a module (arg: name)", 2},
148 { "set-sink-volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3},
149 { "set-sink-input-volume", pa_cli_command_sink_input_volume, "Set the volume of a sink input (args: index, volume)", 3},
150 { "set-source-volume", pa_cli_command_source_volume, "Set the volume of a source (args: index|name, volume)", 3},
151 { "set-sink-mute", pa_cli_command_sink_mute, "Set the mute switch of a sink (args: index|name, bool)", 3},
152 { "set-sink-input-mute", pa_cli_command_sink_input_mute, "Set the mute switch of a sink input (args: index, bool)", 3},
153 { "set-source-mute", pa_cli_command_source_mute, "Set the mute switch of a source (args: index|name, bool)", 3},
154 { "update-sink-proplist", pa_cli_command_update_sink_proplist, "Update the properties of a sink (args: index|name, properties)", 3},
155 { "update-source-proplist", pa_cli_command_update_source_proplist, "Update the properties of a source (args: index|name, properties)", 3},
156 { "update-sink-input-proplist", pa_cli_command_update_sink_input_proplist, "Update the properties of a sink input (args: index, properties)", 3},
157 { "update-source-output-proplist", pa_cli_command_update_source_output_proplist, "Update the properties of a source_output (args: index, properties)", 3},
158 { "set-default-sink", pa_cli_command_sink_default, "Set the default sink (args: index|name)", 2},
159 { "set-default-source", pa_cli_command_source_default, "Set the default source (args: index|name)", 2},
160 { "kill-client", pa_cli_command_kill_client, "Kill a client (args: index)", 2},
161 { "kill-sink-input", pa_cli_command_kill_sink_input, "Kill a sink input (args: index)", 2},
162 { "kill-source-output", pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2},
163 { "list-samples", pa_cli_command_scache_list, "List all entries in the sample cache", 1},
164 { "play-sample", pa_cli_command_scache_play, "Play a sample from the sample cache (args: name, sink|index)", 3},
165 { "remove-sample", pa_cli_command_scache_remove, "Remove a sample from the sample cache (args: name)", 2},
166 { "load-sample", pa_cli_command_scache_load, "Load a sound file into the sample cache (args: name, filename)", 3},
167 { "load-sample-lazy", pa_cli_command_scache_load, "Lazily load a sound file into the sample cache (args: name, filename)", 3},
168 { "load-sample-dir-lazy", pa_cli_command_scache_load_dir, "Lazily load all files in a directory into the sample cache (args: pathname)", 2},
169 { "play-file", pa_cli_command_play_file, "Play a sound file (args: filename, sink|index)", 3},
170 { "dump", pa_cli_command_dump, "Dump daemon configuration", 1},
171 { "shared", pa_cli_command_list_shared_props, NULL, 1},
172 { "move-sink-input", pa_cli_command_move_sink_input, "Move sink input to another sink (args: index, sink)", 3},
173 { "move-source-output", pa_cli_command_move_source_output, "Move source output to another source (args: index, source)", 3},
174 { "vacuum", pa_cli_command_vacuum, NULL, 1},
175 { "suspend-sink", pa_cli_command_suspend_sink, "Suspend sink (args: index|name, bool)", 3},
176 { "suspend-source", pa_cli_command_suspend_source, "Suspend source (args: index|name, bool)", 3},
177 { "suspend", pa_cli_command_suspend, "Suspend all sinks and all sources (args: bool)", 2},
178 { "set-card-profile", pa_cli_command_card_profile, "Change the profile of a card (args: index, name)", 3},
179 { "set-log-level", pa_cli_command_log_level, "Change the log level (args: numeric level)", 2},
180 { "set-log-meta", pa_cli_command_log_meta, "Show source code location in log messages (args: bool)", 2},
181 { "set-log-time", pa_cli_command_log_time, "Show timestamps in log messages (args: bool)", 2},
182 { "set-log-backtrace", pa_cli_command_log_backtrace, "Show bakctrace in log messages (args: frames)", 2},
183 { NULL, NULL, NULL, 0 }
184 };
185
186 static const char whitespace[] = " \t\n\r";
187 static const char linebreak[] = "\n\r";
188
189 static uint32_t parse_index(const char *n) {
190 uint32_t idx;
191
192 if (pa_atou(n, &idx) < 0)
193 return (uint32_t) PA_IDXSET_INVALID;
194
195 return idx;
196 }
197
198 static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
199 pa_core_assert_ref(c);
200 pa_assert(t);
201 pa_assert(buf);
202 pa_assert(fail);
203
204 if (pa_core_exit(c, FALSE, 0) < 0)
205 pa_strbuf_puts(buf, "Not allowed to terminate daemon.\n");
206
207 return 0;
208 }
209
210 static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
211 const struct command*command;
212
213 pa_core_assert_ref(c);
214 pa_assert(t);
215 pa_assert(buf);
216 pa_assert(fail);
217
218 pa_strbuf_puts(buf, "Available commands:\n");
219
220 for (command = commands; command->name; command++)
221 if (command->help)
222 pa_strbuf_printf(buf, " %-25s %s\n", command->name, command->help);
223 return 0;
224 }
225
226 static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
227 char *s;
228
229 pa_core_assert_ref(c);
230 pa_assert(t);
231 pa_assert(buf);
232 pa_assert(fail);
233
234 pa_assert_se(s = pa_module_list_to_string(c));
235 pa_strbuf_puts(buf, s);
236 pa_xfree(s);
237 return 0;
238 }
239
240 static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
241 char *s;
242
243 pa_core_assert_ref(c);
244 pa_assert(t);
245 pa_assert(buf);
246 pa_assert(fail);
247
248 pa_assert_se(s = pa_client_list_to_string(c));
249 pa_strbuf_puts(buf, s);
250 pa_xfree(s);
251 return 0;
252 }
253
254 static int pa_cli_command_cards(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
255 char *s;
256
257 pa_core_assert_ref(c);
258 pa_assert(t);
259 pa_assert(buf);
260 pa_assert(fail);
261
262 pa_assert_se(s = pa_card_list_to_string(c));
263 pa_strbuf_puts(buf, s);
264 pa_xfree(s);
265 return 0;
266 }
267
268 static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
269 char *s;
270
271 pa_core_assert_ref(c);
272 pa_assert(t);
273 pa_assert(buf);
274 pa_assert(fail);
275
276 pa_assert_se(s = pa_sink_list_to_string(c));
277 pa_strbuf_puts(buf, s);
278 pa_xfree(s);
279 return 0;
280 }
281
282 static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
283 char *s;
284
285 pa_core_assert_ref(c);
286 pa_assert(t);
287 pa_assert(buf);
288 pa_assert(fail);
289
290 pa_assert_se(s = pa_source_list_to_string(c));
291 pa_strbuf_puts(buf, s);
292 pa_xfree(s);
293 return 0;
294 }
295
296 static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
297 char *s;
298
299 pa_core_assert_ref(c);
300 pa_assert(t);
301 pa_assert(buf);
302 pa_assert(fail);
303
304 pa_assert_se(s = pa_sink_input_list_to_string(c));
305 pa_strbuf_puts(buf, s);
306 pa_xfree(s);
307 return 0;
308 }
309
310 static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
311 char *s;
312
313 pa_core_assert_ref(c);
314 pa_assert(t);
315 pa_assert(buf);
316 pa_assert(fail);
317
318 pa_assert_se(s = pa_source_output_list_to_string(c));
319 pa_strbuf_puts(buf, s);
320 pa_xfree(s);
321 return 0;
322 }
323
324 static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
325 char ss[PA_SAMPLE_SPEC_SNPRINT_MAX];
326 char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
327 char s[256];
328 const pa_mempool_stat *stat;
329 unsigned k;
330 pa_sink *def_sink;
331 pa_source *def_source;
332
333 static const char* const type_table[PA_MEMBLOCK_TYPE_MAX] = {
334 [PA_MEMBLOCK_POOL] = "POOL",
335 [PA_MEMBLOCK_POOL_EXTERNAL] = "POOL_EXTERNAL",
336 [PA_MEMBLOCK_APPENDED] = "APPENDED",
337 [PA_MEMBLOCK_USER] = "USER",
338 [PA_MEMBLOCK_FIXED] = "FIXED",
339 [PA_MEMBLOCK_IMPORTED] = "IMPORTED",
340 };
341
342 pa_core_assert_ref(c);
343 pa_assert(t);
344 pa_assert(buf);
345 pa_assert(fail);
346
347 stat = pa_mempool_get_stat(c->mempool);
348
349 pa_strbuf_printf(buf, "Memory blocks currently allocated: %u, size: %s.\n",
350 (unsigned) pa_atomic_load(&stat->n_allocated),
351 pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->allocated_size)));
352
353 pa_strbuf_printf(buf, "Memory blocks allocated during the whole lifetime: %u, size: %s.\n",
354 (unsigned) pa_atomic_load(&stat->n_accumulated),
355 pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->accumulated_size)));
356
357 pa_strbuf_printf(buf, "Memory blocks imported from other processes: %u, size: %s.\n",
358 (unsigned) pa_atomic_load(&stat->n_imported),
359 pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->imported_size)));
360
361 pa_strbuf_printf(buf, "Memory blocks exported to other processes: %u, size: %s.\n",
362 (unsigned) pa_atomic_load(&stat->n_exported),
363 pa_bytes_snprint(s, sizeof(s), (unsigned) pa_atomic_load(&stat->exported_size)));
364
365 pa_strbuf_printf(buf, "Total sample cache size: %s.\n",
366 pa_bytes_snprint(s, sizeof(s), (unsigned) pa_scache_total_size(c)));
367
368 pa_strbuf_printf(buf, "Default sample spec: %s\n",
369 pa_sample_spec_snprint(ss, sizeof(ss), &c->default_sample_spec));
370
371 pa_strbuf_printf(buf, "Default channel map: %s\n",
372 pa_channel_map_snprint(cm, sizeof(cm), &c->default_channel_map));
373
374 def_sink = pa_namereg_get_default_sink(c);
375 def_source = pa_namereg_get_default_source(c);
376 pa_strbuf_printf(buf, "Default sink name: %s\n"
377 "Default source name: %s\n",
378 def_sink ? def_sink->name : "none",
379 def_source ? def_source->name : "none");
380
381 for (k = 0; k < PA_MEMBLOCK_TYPE_MAX; k++)
382 pa_strbuf_printf(buf,
383 "Memory blocks of type %s: %u allocated/%u accumulated.\n",
384 type_table[k],
385 (unsigned) pa_atomic_load(&stat->n_allocated_by_type[k]),
386 (unsigned) pa_atomic_load(&stat->n_accumulated_by_type[k]));
387
388 return 0;
389 }
390
391 static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
392 pa_core_assert_ref(c);
393 pa_assert(t);
394 pa_assert(buf);
395 pa_assert(fail);
396
397 pa_cli_command_stat(c, t, buf, fail);
398 pa_cli_command_modules(c, t, buf, fail);
399 pa_cli_command_sinks(c, t, buf, fail);
400 pa_cli_command_sources(c, t, buf, fail);
401 pa_cli_command_clients(c, t, buf, fail);
402 pa_cli_command_cards(c, t, buf, fail);
403 pa_cli_command_sink_inputs(c, t, buf, fail);
404 pa_cli_command_source_outputs(c, t, buf, fail);
405 pa_cli_command_scache_list(c, t, buf, fail);
406 return 0;
407 }
408
409 static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
410 const char *name;
411
412 pa_core_assert_ref(c);
413 pa_assert(t);
414 pa_assert(buf);
415 pa_assert(fail);
416
417 if (!(name = pa_tokenizer_get(t, 1))) {
418 pa_strbuf_puts(buf, "You need to specify the module name and optionally arguments.\n");
419 return -1;
420 }
421
422 if (!pa_module_load(c, name, pa_tokenizer_get(t, 2))) {
423 pa_strbuf_puts(buf, "Module load failed.\n");
424 return -1;
425 }
426
427 return 0;
428 }
429
430 static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
431 pa_module *m;
432 uint32_t idx;
433 const char *i;
434 char *e;
435
436 pa_core_assert_ref(c);
437 pa_assert(t);
438 pa_assert(buf);
439 pa_assert(fail);
440
441 if (!(i = pa_tokenizer_get(t, 1))) {
442 pa_strbuf_puts(buf, "You need to specify the module index.\n");
443 return -1;
444 }
445
446 idx = (uint32_t) strtoul(i, &e, 10);
447 if (*e || !(m = pa_idxset_get_by_index(c->modules, idx))) {
448 pa_strbuf_puts(buf, "Invalid module index.\n");
449 return -1;
450 }
451
452 pa_module_unload_request(m, FALSE);
453 return 0;
454 }
455
456 static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
457 const char *name;
458 pa_modinfo *i;
459
460 pa_core_assert_ref(c);
461 pa_assert(t);
462 pa_assert(buf);
463 pa_assert(fail);
464
465 if (!(name = pa_tokenizer_get(t, 1))) {
466 pa_strbuf_puts(buf, "You need to specify the module name.\n");
467 return -1;
468 }
469
470 if ((i = pa_modinfo_get_by_name(name))) {
471
472 pa_strbuf_printf(buf, "Name: %s\n", name);
473
474 if (!i->description && !i->version && !i->author && !i->usage)
475 pa_strbuf_printf(buf, "No module information available\n");
476 else {
477 if (i->version)
478 pa_strbuf_printf(buf, "Version: %s\n", i->version);
479 if (i->description)
480 pa_strbuf_printf(buf, "Description: %s\n", i->description);
481 if (i->author)
482 pa_strbuf_printf(buf, "Author: %s\n", i->author);
483 if (i->usage)
484 pa_strbuf_printf(buf, "Usage: %s\n", i->usage);
485 pa_strbuf_printf(buf, "Load Once: %s\n", pa_yes_no(i->load_once));
486 }
487
488 pa_modinfo_free(i);
489 } else
490 pa_strbuf_puts(buf, "Failed to open module.\n");
491
492 return 0;
493 }
494
495 static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
496 const char *n, *v;
497 pa_sink *sink;
498 uint32_t volume;
499 pa_cvolume cvolume;
500
501 pa_core_assert_ref(c);
502 pa_assert(t);
503 pa_assert(buf);
504 pa_assert(fail);
505
506 if (!(n = pa_tokenizer_get(t, 1))) {
507 pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
508 return -1;
509 }
510
511 if (!(v = pa_tokenizer_get(t, 2))) {
512 pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
513 return -1;
514 }
515
516 if (pa_atou(v, &volume) < 0) {
517 pa_strbuf_puts(buf, "Failed to parse volume.\n");
518 return -1;
519 }
520
521 if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
522 pa_strbuf_puts(buf, "No sink found by this name or index.\n");
523 return -1;
524 }
525
526 pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume);
527 pa_sink_set_volume(sink, &cvolume, TRUE, TRUE, TRUE);
528 return 0;
529 }
530
531 static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
532 const char *n, *v;
533 pa_sink_input *si;
534 pa_volume_t volume;
535 pa_cvolume cvolume;
536 uint32_t idx;
537
538 pa_core_assert_ref(c);
539 pa_assert(t);
540 pa_assert(buf);
541 pa_assert(fail);
542
543 if (!(n = pa_tokenizer_get(t, 1))) {
544 pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
545 return -1;
546 }
547
548 if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
549 pa_strbuf_puts(buf, "Failed to parse index.\n");
550 return -1;
551 }
552
553 if (!(v = pa_tokenizer_get(t, 2))) {
554 pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
555 return -1;
556 }
557
558 if (pa_atou(v, &volume) < 0) {
559 pa_strbuf_puts(buf, "Failed to parse volume.\n");
560 return -1;
561 }
562
563 if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
564 pa_strbuf_puts(buf, "No sink input found with this index.\n");
565 return -1;
566 }
567
568 pa_cvolume_set(&cvolume, si->sample_spec.channels, volume);
569 pa_sink_input_set_volume(si, &cvolume, TRUE, TRUE);
570 return 0;
571 }
572
573 static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
574 const char *n, *v;
575 pa_source *source;
576 uint32_t volume;
577 pa_cvolume cvolume;
578
579 pa_core_assert_ref(c);
580 pa_assert(t);
581 pa_assert(buf);
582 pa_assert(fail);
583
584 if (!(n = pa_tokenizer_get(t, 1))) {
585 pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
586 return -1;
587 }
588
589 if (!(v = pa_tokenizer_get(t, 2))) {
590 pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n");
591 return -1;
592 }
593
594 if (pa_atou(v, &volume) < 0) {
595 pa_strbuf_puts(buf, "Failed to parse volume.\n");
596 return -1;
597 }
598
599 if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
600 pa_strbuf_puts(buf, "No source found by this name or index.\n");
601 return -1;
602 }
603
604 pa_cvolume_set(&cvolume, source->sample_spec.channels, volume);
605 pa_source_set_volume(source, &cvolume);
606 return 0;
607 }
608
609 static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
610 const char *n, *m;
611 pa_sink *sink;
612 int mute;
613
614 pa_core_assert_ref(c);
615 pa_assert(t);
616 pa_assert(buf);
617 pa_assert(fail);
618
619 if (!(n = pa_tokenizer_get(t, 1))) {
620 pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
621 return -1;
622 }
623
624 if (!(m = pa_tokenizer_get(t, 2))) {
625 pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
626 return -1;
627 }
628
629 if ((mute = pa_parse_boolean(m)) < 0) {
630 pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
631 return -1;
632 }
633
634 if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
635 pa_strbuf_puts(buf, "No sink found by this name or index.\n");
636 return -1;
637 }
638
639 pa_sink_set_mute(sink, mute);
640 return 0;
641 }
642
643 static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
644 const char *n, *m;
645 pa_source *source;
646 int mute;
647
648 pa_core_assert_ref(c);
649 pa_assert(t);
650 pa_assert(buf);
651 pa_assert(fail);
652
653 if (!(n = pa_tokenizer_get(t, 1))) {
654 pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
655 return -1;
656 }
657
658 if (!(m = pa_tokenizer_get(t, 2))) {
659 pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
660 return -1;
661 }
662
663 if ((mute = pa_parse_boolean(m)) < 0) {
664 pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
665 return -1;
666 }
667
668 if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
669 pa_strbuf_puts(buf, "No sink found by this name or index.\n");
670 return -1;
671 }
672
673 pa_source_set_mute(source, mute);
674 return 0;
675 }
676
677 static int pa_cli_command_update_sink_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
678 const char *n, *s;
679 pa_sink *sink;
680 pa_proplist *p;
681
682 pa_core_assert_ref(c);
683 pa_assert(t);
684 pa_assert(buf);
685 pa_assert(fail);
686
687 if (!(n = pa_tokenizer_get(t, 1))) {
688 pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
689 return -1;
690 }
691
692 if (!(s = pa_tokenizer_get(t, 2))) {
693 pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n");
694 return -1;
695 }
696
697 if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
698 pa_strbuf_puts(buf, "No sink found by this name or index.\n");
699 return -1;
700 }
701
702 if (!(p = pa_proplist_from_string(s))) {
703 pa_strbuf_puts(buf, "Failed to parse proplist.\n");
704 return -1;
705 }
706
707 pa_sink_update_proplist(sink, PA_UPDATE_REPLACE, p);
708
709 pa_proplist_free(p);
710
711 return 0;
712 }
713
714 static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
715 const char *n, *s;
716 pa_source *source;
717 pa_proplist *p;
718
719 pa_core_assert_ref(c);
720 pa_assert(t);
721 pa_assert(buf);
722 pa_assert(fail);
723
724 if (!(n = pa_tokenizer_get(t, 1))) {
725 pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
726 return -1;
727 }
728
729 if (!(s = pa_tokenizer_get(t, 2))) {
730 pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n");
731 return -1;
732 }
733
734 if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
735 pa_strbuf_puts(buf, "No source found by this name or index.\n");
736 return -1;
737 }
738
739 if (!(p = pa_proplist_from_string(s))) {
740 pa_strbuf_puts(buf, "Failed to parse proplist.\n");
741 return -1;
742 }
743
744 pa_source_update_proplist(source, PA_UPDATE_REPLACE, p);
745
746 pa_proplist_free(p);
747
748 return 0;
749 }
750
751 static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
752 const char *n, *s;
753 pa_sink_input *si;
754 uint32_t idx;
755 pa_proplist *p;
756
757 pa_core_assert_ref(c);
758 pa_assert(t);
759 pa_assert(buf);
760 pa_assert(fail);
761
762 if (!(n = pa_tokenizer_get(t, 1))) {
763 pa_strbuf_puts(buf, "You need to specify a sink input either by index.\n");
764 return -1;
765 }
766
767 if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
768 pa_strbuf_puts(buf, "Failed to parse index.\n");
769 return -1;
770 }
771
772 if (!(s = pa_tokenizer_get(t, 2))) {
773 pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n");
774 return -1;
775 }
776
777 if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
778 pa_strbuf_puts(buf, "No sink input found with this index.\n");
779 return -1;
780 }
781
782 if (!(p = pa_proplist_from_string(s))) {
783 pa_strbuf_puts(buf, "Failed to parse proplist.\n");
784 return -1;
785 }
786
787 pa_sink_input_update_proplist(si, PA_UPDATE_REPLACE, p);
788
789 pa_proplist_free(p);
790
791 return 0;
792 }
793
794 static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
795 const char *n, *s;
796 pa_source_output *so;
797 uint32_t idx;
798 pa_proplist *p;
799
800 pa_core_assert_ref(c);
801 pa_assert(t);
802 pa_assert(buf);
803 pa_assert(fail);
804
805 if (!(n = pa_tokenizer_get(t, 1))) {
806 pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
807 return -1;
808 }
809
810 if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
811 pa_strbuf_puts(buf, "Failed to parse index.\n");
812 return -1;
813 }
814
815 if (!(s = pa_tokenizer_get(t, 2))) {
816 pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n");
817 return -1;
818 }
819
820 if (!(so = pa_idxset_get_by_index(c->source_outputs, (uint32_t) idx))) {
821 pa_strbuf_puts(buf, "No source output found with this index.\n");
822 return -1;
823 }
824
825 if (!(p = pa_proplist_from_string(s))) {
826 pa_strbuf_puts(buf, "Failed to parse proplist.\n");
827 return -1;
828 }
829
830 pa_source_output_update_proplist(so, PA_UPDATE_REPLACE, p);
831
832 pa_proplist_free(p);
833
834 return 0;
835 }
836
837 static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
838 const char *n, *v;
839 pa_sink_input *si;
840 uint32_t idx;
841 int mute;
842
843 pa_core_assert_ref(c);
844 pa_assert(t);
845 pa_assert(buf);
846 pa_assert(fail);
847
848 if (!(n = pa_tokenizer_get(t, 1))) {
849 pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
850 return -1;
851 }
852
853 if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
854 pa_strbuf_puts(buf, "Failed to parse index.\n");
855 return -1;
856 }
857
858 if (!(v = pa_tokenizer_get(t, 2))) {
859 pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
860 return -1;
861 }
862
863 if ((mute = pa_parse_boolean(v)) < 0) {
864 pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
865 return -1;
866 }
867
868 if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
869 pa_strbuf_puts(buf, "No sink input found with this index.\n");
870 return -1;
871 }
872
873 pa_sink_input_set_mute(si, mute, TRUE);
874 return 0;
875 }
876
877 static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
878 const char *n;
879 pa_sink *s;
880
881 pa_core_assert_ref(c);
882 pa_assert(t);
883 pa_assert(buf);
884 pa_assert(fail);
885
886 if (!(n = pa_tokenizer_get(t, 1))) {
887 pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
888 return -1;
889 }
890
891 if ((s = pa_namereg_get(c, n, PA_NAMEREG_SINK)))
892 pa_namereg_set_default_sink(c, s);
893 else
894 pa_strbuf_printf(buf, "Sink %s does not exist.\n", n);
895
896 return 0;
897 }
898
899 static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
900 const char *n;
901 pa_source *s;
902
903 pa_core_assert_ref(c);
904 pa_assert(t);
905 pa_assert(buf);
906 pa_assert(fail);
907
908 if (!(n = pa_tokenizer_get(t, 1))) {
909 pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
910 return -1;
911 }
912
913 if ((s = pa_namereg_get(c, n, PA_NAMEREG_SOURCE)))
914 pa_namereg_set_default_source(c, s);
915 else
916 pa_strbuf_printf(buf, "Source %s does not exist.\n", n);
917 return 0;
918 }
919
920 static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
921 const char *n;
922 pa_client *client;
923 uint32_t idx;
924
925 pa_core_assert_ref(c);
926 pa_assert(t);
927 pa_assert(buf);
928 pa_assert(fail);
929
930 if (!(n = pa_tokenizer_get(t, 1))) {
931 pa_strbuf_puts(buf, "You need to specify a client by its index.\n");
932 return -1;
933 }
934
935 if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
936 pa_strbuf_puts(buf, "Failed to parse index.\n");
937 return -1;
938 }
939
940 if (!(client = pa_idxset_get_by_index(c->clients, idx))) {
941 pa_strbuf_puts(buf, "No client found by this index.\n");
942 return -1;
943 }
944
945 pa_client_kill(client);
946 return 0;
947 }
948
949 static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
950 const char *n;
951 pa_sink_input *sink_input;
952 uint32_t idx;
953
954 pa_core_assert_ref(c);
955 pa_assert(t);
956 pa_assert(buf);
957 pa_assert(fail);
958
959 if (!(n = pa_tokenizer_get(t, 1))) {
960 pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
961 return -1;
962 }
963
964 if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
965 pa_strbuf_puts(buf, "Failed to parse index.\n");
966 return -1;
967 }
968
969 if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, idx))) {
970 pa_strbuf_puts(buf, "No sink input found by this index.\n");
971 return -1;
972 }
973
974 pa_sink_input_kill(sink_input);
975 return 0;
976 }
977
978 static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
979 const char *n;
980 pa_source_output *source_output;
981 uint32_t idx;
982
983 pa_core_assert_ref(c);
984 pa_assert(t);
985 pa_assert(buf);
986 pa_assert(fail);
987
988 if (!(n = pa_tokenizer_get(t, 1))) {
989 pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
990 return -1;
991 }
992
993 if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
994 pa_strbuf_puts(buf, "Failed to parse index.\n");
995 return -1;
996 }
997
998 if (!(source_output = pa_idxset_get_by_index(c->source_outputs, idx))) {
999 pa_strbuf_puts(buf, "No source output found by this index.\n");
1000 return -1;
1001 }
1002
1003 pa_source_output_kill(source_output);
1004 return 0;
1005 }
1006
1007 static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
1008 char *s;
1009
1010 pa_core_assert_ref(c);
1011 pa_assert(t);
1012 pa_assert(buf);
1013 pa_assert(fail);
1014
1015 pa_assert_se(s = pa_scache_list_to_string(c));
1016 pa_strbuf_puts(buf, s);
1017 pa_xfree(s);
1018
1019 return 0;
1020 }
1021
1022 static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
1023 const char *n, *sink_name;
1024 pa_sink *sink;
1025 uint32_t idx;
1026
1027 pa_core_assert_ref(c);
1028 pa_assert(t);
1029 pa_assert(buf);
1030 pa_assert(fail);
1031
1032 if (!(n = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
1033 pa_strbuf_puts(buf, "You need to specify a sample name and a sink name.\n");
1034 return -1;
1035 }
1036
1037 if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK))) {
1038 pa_strbuf_puts(buf, "No sink by that name.\n");
1039 return -1;
1040 }
1041
1042 if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM, NULL, &idx) < 0) {
1043 pa_strbuf_puts(buf, "Failed to play sample.\n");
1044 return -1;
1045 }
1046
1047 pa_strbuf_printf(buf, "Playing on sink input #%i\n", idx);
1048
1049 return 0;
1050 }
1051
1052 static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
1053 const char *n;
1054
1055 pa_core_assert_ref(c);
1056 pa_assert(t);
1057 pa_assert(buf);
1058 pa_assert(fail);
1059
1060 if (!(n = pa_tokenizer_get(t, 1))) {
1061 pa_strbuf_puts(buf, "You need to specify a sample name.\n");
1062 return -1;
1063 }
1064
1065 if (pa_scache_remove_item(c, n) < 0) {
1066 pa_strbuf_puts(buf, "Failed to remove sample.\n");
1067 return -1;
1068 }
1069
1070 return 0;
1071 }
1072
1073 static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
1074 const char *fname, *n;
1075 int r;
1076
1077 pa_core_assert_ref(c);
1078 pa_assert(t);
1079 pa_assert(buf);
1080 pa_assert(fail);
1081
1082 if (!(fname = pa_tokenizer_get(t, 2)) || !(n = pa_tokenizer_get(t, 1))) {
1083 pa_strbuf_puts(buf, "You need to specify a file name and a sample name.\n");
1084 return -1;
1085 }
1086
1087 if (strstr(pa_tokenizer_get(t, 0), "lazy"))
1088 r = pa_scache_add_file_lazy(c, n, fname, NULL);
1089 else
1090 r = pa_scache_add_file(c, n, fname, NULL);
1091
1092 if (r < 0)
1093 pa_strbuf_puts(buf, "Failed to load sound file.\n");
1094
1095 return 0;
1096 }
1097
1098 static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
1099 const char *pname;
1100
1101 pa_core_assert_ref(c);
1102 pa_assert(t);
1103 pa_assert(buf);
1104 pa_assert(fail);
1105
1106 if (!(pname = pa_tokenizer_get(t, 1))) {
1107 pa_strbuf_puts(buf, "You need to specify a path name.\n");
1108 return -1;
1109 }
1110
1111 if (pa_scache_add_directory_lazy(c, pname) < 0) {
1112 pa_strbuf_puts(buf, "Failed to load directory.\n");
1113 return -1;
1114 }
1115
1116 return 0;
1117 }
1118
1119 static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
1120 const char *fname, *sink_name;
1121 pa_sink *sink;
1122
1123 pa_core_assert_ref(c);
1124 pa_assert(t);
1125 pa_assert(buf);
1126 pa_assert(fail);
1127
1128 if (!(fname = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
1129 pa_strbuf_puts(buf, "You need to specify a file name and a sink name.\n");
1130 return -1;
1131 }
1132
1133 if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK))) {
1134 pa_strbuf_puts(buf, "No sink by that name.\n");
1135 return -1;
1136 }
1137
1138
1139 return pa_play_file(sink, fname, NULL);
1140 }
1141
1142 static int pa_cli_command_list_shared_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
1143 pa_core_assert_ref(c);
1144 pa_assert(t);
1145 pa_assert(buf);
1146 pa_assert(fail);
1147
1148 pa_shared_dump(c, buf);
1149 return 0;
1150 }
1151
1152 static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
1153 pa_core_assert_ref(c);
1154 pa_assert(t);
1155 pa_assert(buf);
1156 pa_assert(fail);
1157
1158 pa_mempool_vacuum(c->mempool);
1159
1160 return 0;
1161 }
1162
1163 static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
1164 const char *n, *k;
1165 pa_sink_input *si;
1166 pa_sink *sink;
1167 uint32_t idx;
1168
1169 pa_core_assert_ref(c);
1170 pa_assert(t);
1171 pa_assert(buf);
1172 pa_assert(fail);
1173
1174 if (!(n = pa_tokenizer_get(t, 1))) {
1175 pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
1176 return -1;
1177 }
1178
1179 if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
1180 pa_strbuf_puts(buf, "Failed to parse index.\n");
1181 return -1;
1182 }
1183
1184 if (!(k = pa_tokenizer_get(t, 2))) {
1185 pa_strbuf_puts(buf, "You need to specify a sink.\n");
1186 return -1;
1187 }
1188
1189 if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
1190 pa_strbuf_puts(buf, "No sink input found with this index.\n");
1191 return -1;
1192 }
1193
1194 if (!(sink = pa_namereg_get(c, k, PA_NAMEREG_SINK))) {
1195 pa_strbuf_puts(buf, "No sink found by this name or index.\n");
1196 return -1;
1197 }
1198
1199 if (pa_sink_input_move_to(si, sink, TRUE) < 0) {
1200 pa_strbuf_puts(buf, "Moved failed.\n");
1201 return -1;
1202 }
1203 return 0;
1204 }
1205
1206 static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
1207 const char *n, *k;
1208 pa_source_output *so;
1209 pa_source *source;
1210 uint32_t idx;
1211
1212 pa_core_assert_ref(c);
1213 pa_assert(t);
1214 pa_assert(buf);
1215 pa_assert(fail);
1216
1217 if (!(n = pa_tokenizer_get(t, 1))) {
1218 pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
1219 return -1;
1220 }
1221
1222 if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
1223 pa_strbuf_puts(buf, "Failed to parse index.\n");
1224 return -1;
1225 }
1226
1227 if (!(k = pa_tokenizer_get(t, 2))) {
1228 pa_strbuf_puts(buf, "You need to specify a source.\n");
1229 return -1;
1230 }
1231
1232 if (!(so = pa_idxset_get_by_index(c->source_outputs, (uint32_t) idx))) {
1233 pa_strbuf_puts(buf, "No source output found with this index.\n");
1234 return -1;
1235 }
1236
1237 if (!(source = pa_namereg_get(c, k, PA_NAMEREG_SOURCE))) {
1238 pa_strbuf_puts(buf, "No source found by this name or index.\n");
1239 return -1;
1240 }
1241
1242 if (pa_source_output_move_to(so, source, TRUE) < 0) {
1243 pa_strbuf_puts(buf, "Moved failed.\n");
1244 return -1;
1245 }
1246 return 0;
1247 }
1248
1249 static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
1250 const char *n, *m;
1251 pa_sink *sink;
1252 int suspend, r;
1253
1254 pa_core_assert_ref(c);
1255 pa_assert(t);
1256 pa_assert(buf);
1257 pa_assert(fail);
1258
1259 if (!(n = pa_tokenizer_get(t, 1))) {
1260 pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
1261 return -1;
1262 }
1263
1264 if (!(m = pa_tokenizer_get(t, 2))) {
1265 pa_strbuf_puts(buf, "You need to specify a suspend switch setting (0/1).\n");
1266 return -1;
1267 }
1268
1269 if ((suspend = pa_parse_boolean(m)) < 0) {
1270 pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
1271 return -1;
1272 }
1273
1274 if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
1275 pa_strbuf_puts(buf, "No sink found by this name or index.\n");
1276 return -1;
1277 }
1278
1279 if ((r = pa_sink_suspend(sink, suspend)) < 0)
1280 pa_strbuf_printf(buf, "Failed to resume/suspend sink: %s\n", pa_strerror(r));
1281
1282 return 0;
1283 }
1284
1285 static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
1286 const char *n, *m;
1287 pa_source *source;
1288 int suspend, r;
1289
1290 pa_core_assert_ref(c);
1291 pa_assert(t);
1292 pa_assert(buf);
1293 pa_assert(fail);
1294
1295 if (!(n = pa_tokenizer_get(t, 1))) {
1296 pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
1297 return -1;
1298 }
1299
1300 if (!(m = pa_tokenizer_get(t, 2))) {
1301 pa_strbuf_puts(buf, "You need to specify a suspend switch setting (0/1).\n");
1302 return -1;
1303 }
1304
1305 if ((suspend = pa_parse_boolean(m)) < 0) {
1306 pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
1307 return -1;
1308 }
1309
1310 if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
1311 pa_strbuf_puts(buf, "No source found by this name or index.\n");
1312 return -1;
1313 }
1314
1315 if ((r = pa_source_suspend(source, suspend)) < 0)
1316 pa_strbuf_printf(buf, "Failed to resume/suspend source: %s\n", pa_strerror(r));
1317
1318 return 0;
1319 }
1320
1321 static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
1322 const char *m;
1323 int suspend, r;
1324
1325 pa_core_assert_ref(c);
1326 pa_assert(t);
1327 pa_assert(buf);
1328 pa_assert(fail);
1329
1330 if (!(m = pa_tokenizer_get(t, 1))) {
1331 pa_strbuf_puts(buf, "You need to specify a suspend switch setting (0/1).\n");
1332 return -1;
1333 }
1334
1335 if ((suspend = pa_parse_boolean(m)) < 0) {
1336 pa_strbuf_puts(buf, "Failed to parse suspend switch.\n");
1337 return -1;
1338 }
1339
1340 if ((r = pa_sink_suspend_all(c, suspend)) < 0)
1341 pa_strbuf_printf(buf, "Failed to resume/suspend all sinks: %s\n", pa_strerror(r));
1342
1343 if ((r = pa_source_suspend_all(c, suspend)) < 0)
1344 pa_strbuf_printf(buf, "Failed to resume/suspend all sources: %s\n", pa_strerror(r));
1345
1346 return 0;
1347 }
1348
1349 static int pa_cli_command_log_level(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
1350 const char *m;
1351 uint32_t level;
1352
1353 pa_core_assert_ref(c);
1354 pa_assert(t);
1355 pa_assert(buf);
1356 pa_assert(fail);
1357
1358 if (!(m = pa_tokenizer_get(t, 1))) {
1359 pa_strbuf_puts(buf, "You need to specify a log level (0..4).\n");
1360 return -1;
1361 }
1362
1363 if (pa_atou(m, &level) < 0 || level >= PA_LOG_LEVEL_MAX) {
1364 pa_strbuf_puts(buf, "Failed to parse log level.\n");
1365 return -1;
1366 }
1367
1368 pa_log_set_level(level);
1369
1370 return 0;
1371 }
1372
1373 static int pa_cli_command_log_meta(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
1374 const char *m;
1375 pa_bool_t b;
1376
1377 pa_core_assert_ref(c);
1378 pa_assert(t);
1379 pa_assert(buf);
1380 pa_assert(fail);
1381
1382 if (!(m = pa_tokenizer_get(t, 1))) {
1383 pa_strbuf_puts(buf, "You need to specify a boolean.\n");
1384 return -1;
1385 }
1386
1387 if ((b = pa_parse_boolean(m)) < 0) {
1388 pa_strbuf_puts(buf, "Failed to parse log meta switch.\n");
1389 return -1;
1390 }
1391
1392 pa_log_set_flags(PA_LOG_PRINT_META, b ? PA_LOG_SET : PA_LOG_UNSET);
1393
1394 return 0;
1395 }
1396
1397 static int pa_cli_command_log_time(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
1398 const char *m;
1399 pa_bool_t b;
1400
1401 pa_core_assert_ref(c);
1402 pa_assert(t);
1403 pa_assert(buf);
1404 pa_assert(fail);
1405
1406 if (!(m = pa_tokenizer_get(t, 1))) {
1407 pa_strbuf_puts(buf, "You need to specify a boolean.\n");
1408 return -1;
1409 }
1410
1411 if ((b = pa_parse_boolean(m)) < 0) {
1412 pa_strbuf_puts(buf, "Failed to parse log meta switch.\n");
1413 return -1;
1414 }
1415
1416 pa_log_set_flags(PA_LOG_PRINT_TIME, b ? PA_LOG_SET : PA_LOG_UNSET);
1417
1418 return 0;
1419 }
1420
1421 static int pa_cli_command_log_backtrace(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
1422 const char *m;
1423 uint32_t nframes;
1424
1425 pa_core_assert_ref(c);
1426 pa_assert(t);
1427 pa_assert(buf);
1428 pa_assert(fail);
1429
1430 if (!(m = pa_tokenizer_get(t, 1))) {
1431 pa_strbuf_puts(buf, "You need to specify a backtrace level.\n");
1432 return -1;
1433 }
1434
1435 if (pa_atou(m, &nframes) < 0 || nframes >= 1000) {
1436 pa_strbuf_puts(buf, "Failed to parse backtrace level.\n");
1437 return -1;
1438 }
1439
1440 pa_log_set_show_backtrace(nframes);
1441
1442 return 0;
1443 }
1444
1445 static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
1446 const char *n, *p;
1447 pa_card *card;
1448
1449 pa_core_assert_ref(c);
1450 pa_assert(t);
1451 pa_assert(buf);
1452 pa_assert(fail);
1453
1454 if (!(n = pa_tokenizer_get(t, 1))) {
1455 pa_strbuf_puts(buf, "You need to specify a card either by its name or its index.\n");
1456 return -1;
1457 }
1458
1459 if (!(p = pa_tokenizer_get(t, 2))) {
1460 pa_strbuf_puts(buf, "You need to specify a profile by its name.\n");
1461 return -1;
1462 }
1463
1464 if (!(card = pa_namereg_get(c, n, PA_NAMEREG_CARD))) {
1465 pa_strbuf_puts(buf, "No card found by this name or index.\n");
1466 return -1;
1467 }
1468
1469 if (pa_card_set_profile(card, p, TRUE) < 0) {
1470 pa_strbuf_printf(buf, "Failed to set card profile to '%s'.\n", p);
1471 return -1;
1472 }
1473
1474 return 0;
1475 }
1476
1477 static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
1478 pa_module *m;
1479 pa_sink *sink;
1480 pa_source *source;
1481 pa_card *card;
1482 int nl;
1483 uint32_t idx;
1484 char txt[256];
1485 time_t now;
1486
1487 pa_core_assert_ref(c);
1488 pa_assert(t);
1489 pa_assert(buf);
1490 pa_assert(fail);
1491
1492 time(&now);
1493
1494 #ifdef HAVE_CTIME_R
1495 pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime_r(&now, txt));
1496 #else
1497 pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime(&now));
1498 #endif
1499
1500 for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
1501
1502 pa_strbuf_printf(buf, "load-module %s", m->name);
1503
1504 if (m->argument)
1505 pa_strbuf_printf(buf, " %s", m->argument);
1506
1507 pa_strbuf_puts(buf, "\n");
1508 }
1509
1510 nl = 0;
1511
1512 for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
1513
1514 if (!nl) {
1515 pa_strbuf_puts(buf, "\n");
1516 nl = 1;
1517 }
1518
1519 pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink, FALSE, TRUE)));
1520 pa_strbuf_printf(buf, "set-sink-mute %s %s\n", sink->name, pa_yes_no(pa_sink_get_mute(sink, FALSE)));
1521 pa_strbuf_printf(buf, "suspend-sink %s %s\n", sink->name, pa_yes_no(pa_sink_get_state(sink) == PA_SINK_SUSPENDED));
1522 }
1523
1524 for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
1525
1526 if (!nl) {
1527 pa_strbuf_puts(buf, "\n");
1528 nl = 1;
1529 }
1530
1531 pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source, FALSE)));
1532 pa_strbuf_printf(buf, "set-source-mute %s %s\n", source->name, pa_yes_no(pa_source_get_mute(source, FALSE)));
1533 pa_strbuf_printf(buf, "suspend-source %s %s\n", source->name, pa_yes_no(pa_source_get_state(source) == PA_SOURCE_SUSPENDED));
1534 }
1535
1536 for (card = pa_idxset_first(c->cards, &idx); card; card = pa_idxset_next(c->cards, &idx)) {
1537
1538 if (!nl) {
1539 pa_strbuf_puts(buf, "\n");
1540 nl = 1;
1541 }
1542
1543 if (card->active_profile)
1544 pa_strbuf_printf(buf, "set-card-profile %s %s\n", card->name, card->active_profile->name);
1545 }
1546
1547 nl = 0;
1548
1549 if ((sink = pa_namereg_get_default_sink(c))) {
1550 if (!nl) {
1551 pa_strbuf_puts(buf, "\n");
1552 nl = 1;
1553 }
1554 pa_strbuf_printf(buf, "set-default-sink %s\n", sink->name);
1555 }
1556
1557 if ((source = pa_namereg_get_default_source(c))) {
1558 if (!nl) {
1559 pa_strbuf_puts(buf, "\n");
1560 nl = 1;
1561 }
1562 pa_strbuf_printf(buf, "set-default-source %s\n", source->name);
1563 }
1564
1565 pa_strbuf_puts(buf, "\n### EOF\n");
1566
1567 return 0;
1568 }
1569
1570 int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail, int *ifstate) {
1571 const char *cs;
1572
1573 pa_assert(c);
1574 pa_assert(s);
1575 pa_assert(buf);
1576
1577 cs = s+strspn(s, whitespace);
1578
1579 if (*cs == '#' || !*cs)
1580 return 0;
1581 else if (*cs == '.') {
1582 if (!strcmp(cs, META_ELSE)) {
1583 if (!ifstate || *ifstate == IFSTATE_NONE) {
1584 pa_strbuf_printf(buf, "Meta command %s is not valid in this context\n", cs);
1585 return -1;
1586 } else if (*ifstate == IFSTATE_TRUE)
1587 *ifstate = IFSTATE_FALSE;
1588 else
1589 *ifstate = IFSTATE_TRUE;
1590 return 0;
1591 } else if (!strcmp(cs, META_ENDIF)) {
1592 if (!ifstate || *ifstate == IFSTATE_NONE) {
1593 pa_strbuf_printf(buf, "Meta command %s is not valid in this context\n", cs);
1594 return -1;
1595 } else
1596 *ifstate = IFSTATE_NONE;
1597 return 0;
1598 }
1599 if (ifstate && *ifstate == IFSTATE_FALSE)
1600 return 0;
1601 if (!strcmp(cs, META_FAIL))
1602 *fail = TRUE;
1603 else if (!strcmp(cs, META_NOFAIL))
1604 *fail = FALSE;
1605 else {
1606 size_t l;
1607 l = strcspn(cs, whitespace);
1608
1609 if (l == sizeof(META_INCLUDE)-1 && !strncmp(cs, META_INCLUDE, l)) {
1610 const char *filename = cs+l+strspn(cs+l, whitespace);
1611 if (pa_cli_command_execute_file(c, filename, buf, fail) < 0)
1612 if (*fail)
1613 return -1;
1614 } else if (l == sizeof(META_IFEXISTS)-1 && !strncmp(cs, META_IFEXISTS, l)) {
1615 if (!ifstate) {
1616 pa_strbuf_printf(buf, "Meta command %s is not valid in this context\n", cs);
1617 return -1;
1618 } else if (*ifstate != IFSTATE_NONE) {
1619 pa_strbuf_printf(buf, "Nested %s commands not supported\n", cs);
1620 return -1;
1621 } else {
1622 const char *filename = cs+l+strspn(cs+l, whitespace);
1623
1624 /* Search DL_SEARCH_PATH unless the filename is absolute */
1625 if (filename[0] == PA_PATH_SEP_CHAR) {
1626
1627 *ifstate = access(filename, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE;
1628 pa_log_debug("Checking for existance of '%s': %s", filename, *ifstate == IFSTATE_TRUE ? "success" : "failure");
1629
1630 } else {
1631 const char *paths, *state = NULL;
1632 char *p;
1633
1634 if (!(paths = lt_dlgetsearchpath()))
1635 return -1;
1636
1637 while ((p = pa_split(paths, ":", &state))) {
1638 char *pathname;
1639
1640 pathname = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", p, filename);
1641 pa_xfree(p);
1642
1643 *ifstate = access(pathname, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE;
1644 pa_log_debug("Checking for existance of '%s': %s", pathname, *ifstate == IFSTATE_TRUE ? "success" : "failure");
1645
1646 pa_xfree(pathname);
1647
1648 if (*ifstate == IFSTATE_TRUE)
1649 break;
1650 }
1651 }
1652
1653 }
1654 } else {
1655 pa_strbuf_printf(buf, "Invalid meta command: %s\n", cs);
1656 if (*fail) return -1;
1657 }
1658 }
1659 } else {
1660 const struct command*command;
1661 int unknown = 1;
1662 size_t l;
1663
1664 if (ifstate && *ifstate == IFSTATE_FALSE)
1665 return 0;
1666
1667 l = strcspn(cs, whitespace);
1668
1669 for (command = commands; command->name; command++)
1670 if (strlen(command->name) == l && !strncmp(cs, command->name, l)) {
1671 int ret;
1672 pa_tokenizer *t = pa_tokenizer_new(cs, command->args);
1673 pa_assert(t);
1674 ret = command->proc(c, t, buf, fail);
1675 pa_tokenizer_free(t);
1676 unknown = 0;
1677
1678 if (ret < 0 && *fail)
1679 return -1;
1680
1681 break;
1682 }
1683
1684 if (unknown) {
1685 pa_strbuf_printf(buf, "Unknown command: %s\n", cs);
1686 if (*fail)
1687 return -1;
1688 }
1689 }
1690
1691 return 0;
1692 }
1693
1694 int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail) {
1695 return pa_cli_command_execute_line_stateful(c, s, buf, fail, NULL);
1696 }
1697
1698 int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, pa_bool_t *fail) {
1699 char line[1024];
1700 int ifstate = IFSTATE_NONE;
1701 int ret = -1;
1702 pa_bool_t _fail = TRUE;
1703
1704 pa_assert(c);
1705 pa_assert(f);
1706 pa_assert(buf);
1707
1708 if (!fail)
1709 fail = &_fail;
1710
1711 while (fgets(line, sizeof(line), f)) {
1712 pa_strip_nl(line);
1713
1714 if (pa_cli_command_execute_line_stateful(c, line, buf, fail, &ifstate) < 0 && *fail)
1715 goto fail;
1716 }
1717
1718 ret = 0;
1719
1720 fail:
1721
1722 return ret;
1723 }
1724
1725 int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_bool_t *fail) {
1726 FILE *f = NULL;
1727 int ret = -1;
1728 pa_bool_t _fail = TRUE;
1729
1730 pa_assert(c);
1731 pa_assert(fn);
1732 pa_assert(buf);
1733
1734 if (!fail)
1735 fail = &_fail;
1736
1737 if (!(f = fopen(fn, "r"))) {
1738 pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, pa_cstrerror(errno));
1739 if (!*fail)
1740 ret = 0;
1741 goto fail;
1742 }
1743
1744 ret = pa_cli_command_execute_file_stream(c, f, buf, fail);
1745
1746 ret = 0;
1747
1748 fail:
1749 if (f)
1750 fclose(f);
1751
1752 return ret;
1753 }
1754
1755 int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail) {
1756 const char *p;
1757 int ifstate = IFSTATE_NONE;
1758 pa_bool_t _fail = TRUE;
1759
1760 pa_assert(c);
1761 pa_assert(s);
1762 pa_assert(buf);
1763
1764 if (!fail)
1765 fail = &_fail;
1766
1767 p = s;
1768 while (*p) {
1769 size_t l = strcspn(p, linebreak);
1770 char *line = pa_xstrndup(p, l);
1771
1772 if (pa_cli_command_execute_line_stateful(c, line, buf, fail, &ifstate) < 0 && *fail) {
1773 pa_xfree(line);
1774 return -1;
1775 }
1776 pa_xfree(line);
1777
1778 p += l;
1779 p += strspn(p, linebreak);
1780 }
1781
1782 return 0;
1783 }