]> code.delx.au - pulseaudio/blob - src/cli.c
add kill_* and default_* commands to CLI
[pulseaudio] / src / cli.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <assert.h>
4 #include <stdlib.h>
5
6 #include "ioline.h"
7 #include "cli.h"
8 #include "module.h"
9 #include "sink.h"
10 #include "source.h"
11 #include "client.h"
12 #include "sinkinput.h"
13 #include "sourceoutput.h"
14 #include "tokenizer.h"
15 #include "strbuf.h"
16 #include "namereg.h"
17
18 struct pa_cli {
19 struct pa_core *core;
20 struct pa_ioline *line;
21
22 void (*eof_callback)(struct pa_cli *c, void *userdata);
23 void *userdata;
24
25 struct pa_client *client;
26 };
27
28 struct command {
29 const char *name;
30 int (*proc) (struct pa_cli *cli, struct pa_tokenizer*t);
31 const char *help;
32 unsigned args;
33 };
34
35 static void line_callback(struct pa_ioline *line, const char *s, void *userdata);
36
37 static int pa_cli_command_exit(struct pa_cli *c, struct pa_tokenizer *t);
38 static int pa_cli_command_help(struct pa_cli *c, struct pa_tokenizer *t);
39 static int pa_cli_command_modules(struct pa_cli *c, struct pa_tokenizer *t);
40 static int pa_cli_command_clients(struct pa_cli *c, struct pa_tokenizer *t);
41 static int pa_cli_command_sinks(struct pa_cli *c, struct pa_tokenizer *t);
42 static int pa_cli_command_sources(struct pa_cli *c, struct pa_tokenizer *t);
43 static int pa_cli_command_sink_inputs(struct pa_cli *c, struct pa_tokenizer *t);
44 static int pa_cli_command_source_outputs(struct pa_cli *c, struct pa_tokenizer *t);
45 static int pa_cli_command_stat(struct pa_cli *c, struct pa_tokenizer *t);
46 static int pa_cli_command_info(struct pa_cli *c, struct pa_tokenizer *t);
47 static int pa_cli_command_load(struct pa_cli *c, struct pa_tokenizer *t);
48 static int pa_cli_command_unload(struct pa_cli *c, struct pa_tokenizer *t);
49 static int pa_cli_command_sink_volume(struct pa_cli *c, struct pa_tokenizer *t);
50 static int pa_cli_command_sink_input_volume(struct pa_cli *c, struct pa_tokenizer *t);
51 static int pa_cli_command_sink_default(struct pa_cli *c, struct pa_tokenizer *t);
52 static int pa_cli_command_source_default(struct pa_cli *c, struct pa_tokenizer *t);
53 static int pa_cli_command_kill_client(struct pa_cli *c, struct pa_tokenizer *t);
54 static int pa_cli_command_kill_sink_input(struct pa_cli *c, struct pa_tokenizer *t);
55 static int pa_cli_command_kill_source_output(struct pa_cli *c, struct pa_tokenizer *t);
56
57 static const struct command commands[] = {
58 { "exit", pa_cli_command_exit, "Terminate the daemon", 1 },
59 { "help", pa_cli_command_help, "Show this help", 1 },
60 { "modules", pa_cli_command_modules, "List loaded modules", 1 },
61 { "sinks", pa_cli_command_sinks, "List loaded sinks", 1 },
62 { "sources", pa_cli_command_sources, "List loaded sources", 1 },
63 { "clients", pa_cli_command_clients, "List loaded clients", 1 },
64 { "sink_inputs", pa_cli_command_sink_inputs, "List sink inputs", 1 },
65 { "source_outputs", pa_cli_command_source_outputs, "List source outputs", 1 },
66 { "stat", pa_cli_command_stat, "Show memory block statistics", 1 },
67 { "info", pa_cli_command_info, "Show comprehensive status", 1 },
68 { "ls", pa_cli_command_info, NULL, 1 },
69 { "list", pa_cli_command_info, NULL, 1 },
70 { "load", pa_cli_command_load, "Load a module (args: name, arguments)", 3},
71 { "unload", pa_cli_command_unload, "Unload a module (args: index)", 2},
72 { "sink_volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3},
73 { "sink_input_volume", pa_cli_command_sink_input_volume, "Set the volume of a sink input (args: index|name, volume)", 3},
74 { "sink_default", pa_cli_command_sink_default, "Set the default sink (args: index|name)", 2},
75 { "source_default", pa_cli_command_source_default, "Set the default source (args: index|name)", 2},
76 { "kill_client", pa_cli_command_kill_client, "Kill a client (args: index)", 2},
77 { "kill_sink_input", pa_cli_command_kill_sink_input, "Kill a sink input (args: index)", 2},
78 { "kill_source_output", pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2},
79 { NULL, NULL, NULL, 0 }
80 };
81
82 static const char prompt[] = ">>> ";
83
84 static void client_kill(struct pa_client *c);
85
86 struct pa_cli* pa_cli_new(struct pa_core *core, struct pa_iochannel *io) {
87 char cname[256];
88 struct pa_cli *c;
89 assert(io);
90
91 c = malloc(sizeof(struct pa_cli));
92 assert(c);
93 c->core = core;
94 c->line = pa_ioline_new(io);
95 assert(c->line);
96
97 c->userdata = NULL;
98 c->eof_callback = NULL;
99
100 pa_iochannel_peer_to_string(io, cname, sizeof(cname));
101 c->client = pa_client_new(core, "CLI", cname);
102 assert(c->client);
103 c->client->kill = client_kill;
104 c->client->userdata = c;
105
106 pa_ioline_set_callback(c->line, line_callback, c);
107 pa_ioline_puts(c->line, "Welcome to polypaudio! Use \"help\" for usage information.\n");
108 pa_ioline_puts(c->line, prompt);
109
110 return c;
111 }
112
113 void pa_cli_free(struct pa_cli *c) {
114 assert(c);
115 pa_ioline_free(c->line);
116 pa_client_free(c->client);
117 free(c);
118 }
119
120 static void client_kill(struct pa_client *client) {
121 struct pa_cli *c;
122 assert(client && client->userdata);
123 c = client->userdata;
124 fprintf(stderr, "CLI client killed.\n");
125
126 if (c->eof_callback)
127 c->eof_callback(c, c->userdata);
128 }
129
130 static void line_callback(struct pa_ioline *line, const char *s, void *userdata) {
131 struct pa_cli *c = userdata;
132 const char *cs;
133 const char delimiter[] = " \t\n\r";
134 assert(line && c);
135
136 if (!s) {
137 fprintf(stderr, "CLI got EOF from user.\n");
138 if (c->eof_callback)
139 c->eof_callback(c, c->userdata);
140
141 return;
142 }
143
144 cs = s+strspn(s, delimiter);
145 if (*cs && *cs != '#') {
146 const struct command*command;
147 int unknown = 1;
148 size_t l;
149
150 l = strcspn(s, delimiter);
151
152 for (command = commands; command->name; command++)
153 if (strlen(command->name) == l && !strncmp(s, command->name, l)) {
154 int ret;
155 struct pa_tokenizer *t = pa_tokenizer_new(s, command->args);
156 assert(t);
157 ret = command->proc(c, t);
158 pa_tokenizer_free(t);
159 unknown = 0;
160
161 /* A negative return value denotes that the cli object is probably invalid now */
162 if (ret < 0)
163 return;
164 break;
165 }
166
167 if (unknown)
168 pa_ioline_puts(line, "Unknown command\n");
169 }
170
171 pa_ioline_puts(line, prompt);
172 }
173
174 void pa_cli_set_eof_callback(struct pa_cli *c, void (*cb)(struct pa_cli*c, void *userdata), void *userdata) {
175 assert(c && cb);
176 c->eof_callback = cb;
177 c->userdata = userdata;
178 }
179
180 static uint32_t parse_index(const char *n) {
181 long index;
182 char *x;
183 index = strtol(n, &x, 0);
184 if (!x || *x != 0 || index < 0)
185 return (uint32_t) PA_IDXSET_INVALID;
186
187 return (uint32_t) index;
188 }
189
190 static int pa_cli_command_exit(struct pa_cli *c, struct pa_tokenizer *t) {
191 assert(c && c->core && c->core->mainloop && t);
192 c->core->mainloop->quit(c->core->mainloop, 0);
193 return 0;
194 }
195
196 static int pa_cli_command_help(struct pa_cli *c, struct pa_tokenizer *t) {
197 const struct command*command;
198 struct pa_strbuf *pa_strbuf;
199 char *p;
200 assert(c && t);
201
202 pa_strbuf = pa_strbuf_new();
203 assert(pa_strbuf);
204
205 pa_strbuf_puts(pa_strbuf, "Available commands:\n");
206
207 for (command = commands; command->name; command++)
208 if (command->help)
209 pa_strbuf_printf(pa_strbuf, " %-20s %s\n", command->name, command->help);
210
211 pa_ioline_puts(c->line, p = pa_strbuf_tostring_free(pa_strbuf));
212 free(p);
213 return 0;
214 }
215
216 static int pa_cli_command_modules(struct pa_cli *c, struct pa_tokenizer *t) {
217 char *s;
218 assert(c && t);
219 s = pa_module_list_to_string(c->core);
220 assert(s);
221 pa_ioline_puts(c->line, s);
222 free(s);
223 return 0;
224 }
225
226 static int pa_cli_command_clients(struct pa_cli *c, struct pa_tokenizer *t) {
227 char *s;
228 assert(c && t);
229 s = pa_client_list_to_string(c->core);
230 assert(s);
231 pa_ioline_puts(c->line, s);
232 free(s);
233 return 0;
234 }
235
236 static int pa_cli_command_sinks(struct pa_cli *c, struct pa_tokenizer *t) {
237 char *s;
238 assert(c && t);
239 s = pa_sink_list_to_string(c->core);
240 assert(s);
241 pa_ioline_puts(c->line, s);
242 free(s);
243 return 0;
244 }
245
246 static int pa_cli_command_sources(struct pa_cli *c, struct pa_tokenizer *t) {
247 char *s;
248 assert(c && t);
249 s = pa_source_list_to_string(c->core);
250 assert(s);
251 pa_ioline_puts(c->line, s);
252 free(s);
253 return 0;
254 }
255
256 static int pa_cli_command_sink_inputs(struct pa_cli *c, struct pa_tokenizer *t) {
257 char *s;
258 assert(c && t);
259 s = pa_sink_input_list_to_string(c->core);
260 assert(s);
261 pa_ioline_puts(c->line, s);
262 free(s);
263 return 0;
264 }
265
266 static int pa_cli_command_source_outputs(struct pa_cli *c, struct pa_tokenizer *t) {
267 char *s;
268 assert(c && t);
269 s = pa_source_output_list_to_string(c->core);
270 assert(s);
271 pa_ioline_puts(c->line, s);
272 free(s);
273 return 0;
274 }
275
276 static int pa_cli_command_stat(struct pa_cli *c, struct pa_tokenizer *t) {
277 char txt[256];
278 assert(c && t);
279 snprintf(txt, sizeof(txt), "Memory blocks allocated: %u, total size: %u bytes.\n", pa_memblock_count, pa_memblock_total);
280 pa_ioline_puts(c->line, txt);
281 return 0;
282 }
283
284 static int pa_cli_command_info(struct pa_cli *c, struct pa_tokenizer *t) {
285 assert(c && t);
286 pa_cli_command_stat(c, t);
287 pa_cli_command_modules(c, t);
288 pa_cli_command_sinks(c, t);
289 pa_cli_command_sources(c, t);
290 pa_cli_command_clients(c, t);
291 pa_cli_command_sink_inputs(c, t);
292 pa_cli_command_source_outputs(c, t);
293 return 0;
294 }
295
296 static int pa_cli_command_load(struct pa_cli *c, struct pa_tokenizer *t) {
297 struct pa_module *m;
298 const char *name;
299 char txt[256];
300 assert(c && t);
301
302 if (!(name = pa_tokenizer_get(t, 1))) {
303 pa_ioline_puts(c->line, "You need to specfiy the module name and optionally arguments.\n");
304 return 0;
305 }
306
307 if (!(m = pa_module_load(c->core, name, pa_tokenizer_get(t, 2)))) {
308 pa_ioline_puts(c->line, "Module load failed.\n");
309 return 0;
310 }
311
312 snprintf(txt, sizeof(txt), "Module successfully loaded, index: %u.\n", m->index);
313 pa_ioline_puts(c->line, txt);
314 return 0;
315 }
316
317 static int pa_cli_command_unload(struct pa_cli *c, struct pa_tokenizer *t) {
318 struct pa_module *m;
319 uint32_t index;
320 const char *i;
321 char *e;
322 assert(c && t);
323
324 if (!(i = pa_tokenizer_get(t, 1))) {
325 pa_ioline_puts(c->line, "You need to specfiy the module index.\n");
326 return 0;
327 }
328
329 index = (uint32_t) strtoul(i, &e, 10);
330 if (*e || !(m = pa_idxset_get_by_index(c->core->modules, index))) {
331 pa_ioline_puts(c->line, "Invalid module index.\n");
332 return 0;
333 }
334
335 pa_module_unload_request(c->core, m);
336 return 0;
337 }
338
339 static int pa_cli_command_sink_volume(struct pa_cli *c, struct pa_tokenizer *t) {
340 const char *n, *v;
341 char *x = NULL;
342 struct pa_sink *sink;
343 long volume;
344
345 if (!(n = pa_tokenizer_get(t, 1))) {
346 pa_ioline_puts(c->line, "You need to specify a sink either by its name or its index.\n");
347 return 0;
348 }
349
350 if (!(v = pa_tokenizer_get(t, 2))) {
351 pa_ioline_puts(c->line, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
352 return 0;
353 }
354
355 volume = strtol(v, &x, 0);
356 if (!x || *x != 0 || volume < 0) {
357 pa_ioline_puts(c->line, "Failed to parse volume.\n");
358 return 0;
359 }
360
361 if (!(sink = pa_namereg_get(c->core, n, PA_NAMEREG_SINK))) {
362 pa_ioline_puts(c->line, "No sink found by this name or index.\n");
363 return 0;
364 }
365
366 sink->volume = (uint32_t) volume;
367 return 0;
368 }
369
370 static int pa_cli_command_sink_input_volume(struct pa_cli *c, struct pa_tokenizer *t) {
371 const char *n, *v;
372 struct pa_sink_input *si;
373 long volume;
374 uint32_t index;
375 char *x;
376
377 if (!(n = pa_tokenizer_get(t, 1))) {
378 pa_ioline_puts(c->line, "You need to specify a sink input by its index.\n");
379 return 0;
380 }
381
382 if ((index = parse_index(n)) == PA_IDXSET_INVALID) {
383 pa_ioline_puts(c->line, "Failed to parse index.\n");
384 return 0;
385 }
386
387 if (!(v = pa_tokenizer_get(t, 2))) {
388 pa_ioline_puts(c->line, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
389 return 0;
390 }
391
392 x = NULL;
393 volume = strtol(v, &x, 0);
394 if (!x || *x != 0 || volume < 0) {
395 pa_ioline_puts(c->line, "Failed to parse volume.\n");
396 return 0;
397 }
398
399 if (!(si = pa_idxset_get_by_index(c->core->sink_inputs, (uint32_t) index))) {
400 pa_ioline_puts(c->line, "No sink input found with this index.\n");
401 return 0;
402 }
403
404 si->volume = (uint32_t) volume;
405 return 0;
406 }
407
408 static int pa_cli_command_sink_default(struct pa_cli *c, struct pa_tokenizer *t) {
409 const char *n;
410 struct pa_sink *sink;
411 assert(c && t);
412
413 if (!(n = pa_tokenizer_get(t, 1))) {
414 pa_ioline_puts(c->line, "You need to specify a sink either by its name or its index.\n");
415 return 0;
416 }
417
418 if (!(sink = pa_namereg_get(c->core, n, PA_NAMEREG_SINK))) {
419 pa_ioline_puts(c->line, "No sink found by this name or index.\n");
420 return 0;
421 }
422
423 c->core->default_sink_index = sink->index;
424 return 0;
425 }
426
427 static int pa_cli_command_source_default(struct pa_cli *c, struct pa_tokenizer *t) {
428 const char *n;
429 struct pa_source *source;
430 assert(c && t);
431
432 if (!(n = pa_tokenizer_get(t, 1))) {
433 pa_ioline_puts(c->line, "You need to specify a source either by its name or its index.\n");
434 return 0;
435 }
436
437 if (!(source = pa_namereg_get(c->core, n, PA_NAMEREG_SOURCE))) {
438 pa_ioline_puts(c->line, "No source found by this name or index.\n");
439 return 0;
440 }
441
442 c->core->default_source_index = source->index;
443 return 0;
444 }
445
446 static int pa_cli_command_kill_client(struct pa_cli *c, struct pa_tokenizer *t) {
447 const char *n;
448 struct pa_client *client;
449 uint32_t index;
450 int ret;
451 assert(c && t);
452
453 if (!(n = pa_tokenizer_get(t, 1))) {
454 pa_ioline_puts(c->line, "You need to specify a client by its index.\n");
455 return 0;
456 }
457
458 if ((index = parse_index(n)) == PA_IDXSET_INVALID) {
459 pa_ioline_puts(c->line, "Failed to parse index.\n");
460 return 0;
461 }
462
463 if (!(client = pa_idxset_get_by_index(c->core->clients, index))) {
464 pa_ioline_puts(c->line, "No client found by this index.\n");
465 return 0;
466 }
467
468 ret = (client->userdata == c) ? -1 : 0;
469 pa_client_kill(client);
470 return ret;
471 }
472
473 static int pa_cli_command_kill_sink_input(struct pa_cli *c, struct pa_tokenizer *t) {
474 const char *n;
475 struct pa_sink_input *sink_input;
476 uint32_t index;
477 assert(c && t);
478
479 if (!(n = pa_tokenizer_get(t, 1))) {
480 pa_ioline_puts(c->line, "You need to specify a sink input by its index.\n");
481 return 0;
482 }
483
484 if ((index = parse_index(n)) == PA_IDXSET_INVALID) {
485 pa_ioline_puts(c->line, "Failed to parse index.\n");
486 return 0;
487 }
488
489 if (!(sink_input = pa_idxset_get_by_index(c->core->sink_inputs, index))) {
490 pa_ioline_puts(c->line, "No sink input found by this index.\n");
491 return 0;
492 }
493
494 pa_sink_input_kill(sink_input);
495 return 0;
496 }
497
498 static int pa_cli_command_kill_source_output(struct pa_cli *c, struct pa_tokenizer *t) {
499 const char *n;
500 struct pa_source_output *source_output;
501 uint32_t index;
502 assert(c && t);
503
504 if (!(n = pa_tokenizer_get(t, 1))) {
505 pa_ioline_puts(c->line, "You need to specify a source output by its index.\n");
506 return 0;
507 }
508
509 if ((index = parse_index(n)) == PA_IDXSET_INVALID) {
510 pa_ioline_puts(c->line, "Failed to parse index.\n");
511 return 0;
512 }
513
514 if (!(source_output = pa_idxset_get_by_index(c->core->source_outputs, index))) {
515 pa_ioline_puts(c->line, "No source output found by this index.\n");
516 return 0;
517 }
518
519 pa_source_output_kill(source_output);
520 return 0;
521 }