]> code.delx.au - pulseaudio/blob - src/cli.c
b7fc787a2641f1d4c54dceb5f4b3eda10957ce0d
[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, struct pa_module *m) {
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_socket_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 c->client->owner = m;
106
107 pa_ioline_set_callback(c->line, line_callback, c);
108 pa_ioline_puts(c->line, "Welcome to polypaudio! Use \"help\" for usage information.\n");
109 pa_ioline_puts(c->line, prompt);
110
111 return c;
112 }
113
114 void pa_cli_free(struct pa_cli *c) {
115 assert(c);
116 pa_ioline_free(c->line);
117 pa_client_free(c->client);
118 free(c);
119 }
120
121 static void client_kill(struct pa_client *client) {
122 struct pa_cli *c;
123 assert(client && client->userdata);
124 c = client->userdata;
125 fprintf(stderr, "CLI client killed.\n");
126
127 if (c->eof_callback)
128 c->eof_callback(c, c->userdata);
129 }
130
131 static void line_callback(struct pa_ioline *line, const char *s, void *userdata) {
132 struct pa_cli *c = userdata;
133 const char *cs;
134 const char delimiter[] = " \t\n\r";
135 assert(line && c);
136
137 if (!s) {
138 fprintf(stderr, "CLI got EOF from user.\n");
139 if (c->eof_callback)
140 c->eof_callback(c, c->userdata);
141
142 return;
143 }
144
145 cs = s+strspn(s, delimiter);
146 if (*cs && *cs != '#') {
147 const struct command*command;
148 int unknown = 1;
149 size_t l;
150
151 l = strcspn(s, delimiter);
152
153 for (command = commands; command->name; command++)
154 if (strlen(command->name) == l && !strncmp(s, command->name, l)) {
155 int ret;
156 struct pa_tokenizer *t = pa_tokenizer_new(s, command->args);
157 assert(t);
158 ret = command->proc(c, t);
159 pa_tokenizer_free(t);
160 unknown = 0;
161
162 /* A negative return value denotes that the cli object is probably invalid now */
163 if (ret < 0)
164 return;
165 break;
166 }
167
168 if (unknown)
169 pa_ioline_puts(line, "Unknown command\n");
170 }
171
172 pa_ioline_puts(line, prompt);
173 }
174
175 void pa_cli_set_eof_callback(struct pa_cli *c, void (*cb)(struct pa_cli*c, void *userdata), void *userdata) {
176 assert(c && cb);
177 c->eof_callback = cb;
178 c->userdata = userdata;
179 }
180
181 static uint32_t parse_index(const char *n) {
182 long index;
183 char *x;
184 index = strtol(n, &x, 0);
185 if (!x || *x != 0 || index < 0)
186 return (uint32_t) PA_IDXSET_INVALID;
187
188 return (uint32_t) index;
189 }
190
191 static int pa_cli_command_exit(struct pa_cli *c, struct pa_tokenizer *t) {
192 assert(c && c->core && c->core->mainloop && t);
193 c->core->mainloop->quit(c->core->mainloop, 0);
194 return 0;
195 }
196
197 static int pa_cli_command_help(struct pa_cli *c, struct pa_tokenizer *t) {
198 const struct command*command;
199 struct pa_strbuf *pa_strbuf;
200 char *p;
201 assert(c && t);
202
203 pa_strbuf = pa_strbuf_new();
204 assert(pa_strbuf);
205
206 pa_strbuf_puts(pa_strbuf, "Available commands:\n");
207
208 for (command = commands; command->name; command++)
209 if (command->help)
210 pa_strbuf_printf(pa_strbuf, " %-20s %s\n", command->name, command->help);
211
212 pa_ioline_puts(c->line, p = pa_strbuf_tostring_free(pa_strbuf));
213 free(p);
214 return 0;
215 }
216
217 static int pa_cli_command_modules(struct pa_cli *c, struct pa_tokenizer *t) {
218 char *s;
219 assert(c && t);
220 s = pa_module_list_to_string(c->core);
221 assert(s);
222 pa_ioline_puts(c->line, s);
223 free(s);
224 return 0;
225 }
226
227 static int pa_cli_command_clients(struct pa_cli *c, struct pa_tokenizer *t) {
228 char *s;
229 assert(c && t);
230 s = pa_client_list_to_string(c->core);
231 assert(s);
232 pa_ioline_puts(c->line, s);
233 free(s);
234 return 0;
235 }
236
237 static int pa_cli_command_sinks(struct pa_cli *c, struct pa_tokenizer *t) {
238 char *s;
239 assert(c && t);
240 s = pa_sink_list_to_string(c->core);
241 assert(s);
242 pa_ioline_puts(c->line, s);
243 free(s);
244 return 0;
245 }
246
247 static int pa_cli_command_sources(struct pa_cli *c, struct pa_tokenizer *t) {
248 char *s;
249 assert(c && t);
250 s = pa_source_list_to_string(c->core);
251 assert(s);
252 pa_ioline_puts(c->line, s);
253 free(s);
254 return 0;
255 }
256
257 static int pa_cli_command_sink_inputs(struct pa_cli *c, struct pa_tokenizer *t) {
258 char *s;
259 assert(c && t);
260 s = pa_sink_input_list_to_string(c->core);
261 assert(s);
262 pa_ioline_puts(c->line, s);
263 free(s);
264 return 0;
265 }
266
267 static int pa_cli_command_source_outputs(struct pa_cli *c, struct pa_tokenizer *t) {
268 char *s;
269 assert(c && t);
270 s = pa_source_output_list_to_string(c->core);
271 assert(s);
272 pa_ioline_puts(c->line, s);
273 free(s);
274 return 0;
275 }
276
277 static int pa_cli_command_stat(struct pa_cli *c, struct pa_tokenizer *t) {
278 char txt[256];
279 assert(c && t);
280 snprintf(txt, sizeof(txt), "Memory blocks allocated: %u, total size: %u bytes.\n", pa_memblock_count, pa_memblock_total);
281 pa_ioline_puts(c->line, txt);
282 return 0;
283 }
284
285 static int pa_cli_command_info(struct pa_cli *c, struct pa_tokenizer *t) {
286 assert(c && t);
287 pa_cli_command_stat(c, t);
288 pa_cli_command_modules(c, t);
289 pa_cli_command_sinks(c, t);
290 pa_cli_command_sources(c, t);
291 pa_cli_command_clients(c, t);
292 pa_cli_command_sink_inputs(c, t);
293 pa_cli_command_source_outputs(c, t);
294 return 0;
295 }
296
297 static int pa_cli_command_load(struct pa_cli *c, struct pa_tokenizer *t) {
298 struct pa_module *m;
299 const char *name;
300 char txt[256];
301 assert(c && t);
302
303 if (!(name = pa_tokenizer_get(t, 1))) {
304 pa_ioline_puts(c->line, "You need to specfiy the module name and optionally arguments.\n");
305 return 0;
306 }
307
308 if (!(m = pa_module_load(c->core, name, pa_tokenizer_get(t, 2)))) {
309 pa_ioline_puts(c->line, "Module load failed.\n");
310 return 0;
311 }
312
313 snprintf(txt, sizeof(txt), "Module successfully loaded, index: %u.\n", m->index);
314 pa_ioline_puts(c->line, txt);
315 return 0;
316 }
317
318 static int pa_cli_command_unload(struct pa_cli *c, struct pa_tokenizer *t) {
319 struct pa_module *m;
320 uint32_t index;
321 const char *i;
322 char *e;
323 assert(c && t);
324
325 if (!(i = pa_tokenizer_get(t, 1))) {
326 pa_ioline_puts(c->line, "You need to specfiy the module index.\n");
327 return 0;
328 }
329
330 index = (uint32_t) strtoul(i, &e, 10);
331 if (*e || !(m = pa_idxset_get_by_index(c->core->modules, index))) {
332 pa_ioline_puts(c->line, "Invalid module index.\n");
333 return 0;
334 }
335
336 pa_module_unload_request(c->core, m);
337 return 0;
338 }
339
340 static int pa_cli_command_sink_volume(struct pa_cli *c, struct pa_tokenizer *t) {
341 const char *n, *v;
342 char *x = NULL;
343 struct pa_sink *sink;
344 long volume;
345
346 if (!(n = pa_tokenizer_get(t, 1))) {
347 pa_ioline_puts(c->line, "You need to specify a sink either by its name or its index.\n");
348 return 0;
349 }
350
351 if (!(v = pa_tokenizer_get(t, 2))) {
352 pa_ioline_puts(c->line, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
353 return 0;
354 }
355
356 volume = strtol(v, &x, 0);
357 if (!x || *x != 0 || volume < 0) {
358 pa_ioline_puts(c->line, "Failed to parse volume.\n");
359 return 0;
360 }
361
362 if (!(sink = pa_namereg_get(c->core, n, PA_NAMEREG_SINK))) {
363 pa_ioline_puts(c->line, "No sink found by this name or index.\n");
364 return 0;
365 }
366
367 sink->volume = (uint32_t) volume;
368 return 0;
369 }
370
371 static int pa_cli_command_sink_input_volume(struct pa_cli *c, struct pa_tokenizer *t) {
372 const char *n, *v;
373 struct pa_sink_input *si;
374 long volume;
375 uint32_t index;
376 char *x;
377
378 if (!(n = pa_tokenizer_get(t, 1))) {
379 pa_ioline_puts(c->line, "You need to specify a sink input by its index.\n");
380 return 0;
381 }
382
383 if ((index = parse_index(n)) == PA_IDXSET_INVALID) {
384 pa_ioline_puts(c->line, "Failed to parse index.\n");
385 return 0;
386 }
387
388 if (!(v = pa_tokenizer_get(t, 2))) {
389 pa_ioline_puts(c->line, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
390 return 0;
391 }
392
393 x = NULL;
394 volume = strtol(v, &x, 0);
395 if (!x || *x != 0 || volume < 0) {
396 pa_ioline_puts(c->line, "Failed to parse volume.\n");
397 return 0;
398 }
399
400 if (!(si = pa_idxset_get_by_index(c->core->sink_inputs, (uint32_t) index))) {
401 pa_ioline_puts(c->line, "No sink input found with this index.\n");
402 return 0;
403 }
404
405 si->volume = (uint32_t) volume;
406 return 0;
407 }
408
409 static int pa_cli_command_sink_default(struct pa_cli *c, struct pa_tokenizer *t) {
410 const char *n;
411 struct pa_sink *sink;
412 assert(c && t);
413
414 if (!(n = pa_tokenizer_get(t, 1))) {
415 pa_ioline_puts(c->line, "You need to specify a sink either by its name or its index.\n");
416 return 0;
417 }
418
419 if (!(sink = pa_namereg_get(c->core, n, PA_NAMEREG_SINK))) {
420 pa_ioline_puts(c->line, "No sink found by this name or index.\n");
421 return 0;
422 }
423
424 c->core->default_sink_index = sink->index;
425 return 0;
426 }
427
428 static int pa_cli_command_source_default(struct pa_cli *c, struct pa_tokenizer *t) {
429 const char *n;
430 struct pa_source *source;
431 assert(c && t);
432
433 if (!(n = pa_tokenizer_get(t, 1))) {
434 pa_ioline_puts(c->line, "You need to specify a source either by its name or its index.\n");
435 return 0;
436 }
437
438 if (!(source = pa_namereg_get(c->core, n, PA_NAMEREG_SOURCE))) {
439 pa_ioline_puts(c->line, "No source found by this name or index.\n");
440 return 0;
441 }
442
443 c->core->default_source_index = source->index;
444 return 0;
445 }
446
447 static int pa_cli_command_kill_client(struct pa_cli *c, struct pa_tokenizer *t) {
448 const char *n;
449 struct pa_client *client;
450 uint32_t index;
451 int ret;
452 assert(c && t);
453
454 if (!(n = pa_tokenizer_get(t, 1))) {
455 pa_ioline_puts(c->line, "You need to specify a client by its index.\n");
456 return 0;
457 }
458
459 if ((index = parse_index(n)) == PA_IDXSET_INVALID) {
460 pa_ioline_puts(c->line, "Failed to parse index.\n");
461 return 0;
462 }
463
464 if (!(client = pa_idxset_get_by_index(c->core->clients, index))) {
465 pa_ioline_puts(c->line, "No client found by this index.\n");
466 return 0;
467 }
468
469 ret = (client->userdata == c) ? -1 : 0;
470 pa_client_kill(client);
471 return ret;
472 }
473
474 static int pa_cli_command_kill_sink_input(struct pa_cli *c, struct pa_tokenizer *t) {
475 const char *n;
476 struct pa_sink_input *sink_input;
477 uint32_t index;
478 assert(c && t);
479
480 if (!(n = pa_tokenizer_get(t, 1))) {
481 pa_ioline_puts(c->line, "You need to specify a sink input by its index.\n");
482 return 0;
483 }
484
485 if ((index = parse_index(n)) == PA_IDXSET_INVALID) {
486 pa_ioline_puts(c->line, "Failed to parse index.\n");
487 return 0;
488 }
489
490 if (!(sink_input = pa_idxset_get_by_index(c->core->sink_inputs, index))) {
491 pa_ioline_puts(c->line, "No sink input found by this index.\n");
492 return 0;
493 }
494
495 pa_sink_input_kill(sink_input);
496 return 0;
497 }
498
499 static int pa_cli_command_kill_source_output(struct pa_cli *c, struct pa_tokenizer *t) {
500 const char *n;
501 struct pa_source_output *source_output;
502 uint32_t index;
503 assert(c && t);
504
505 if (!(n = pa_tokenizer_get(t, 1))) {
506 pa_ioline_puts(c->line, "You need to specify a source output by its index.\n");
507 return 0;
508 }
509
510 if ((index = parse_index(n)) == PA_IDXSET_INVALID) {
511 pa_ioline_puts(c->line, "Failed to parse index.\n");
512 return 0;
513 }
514
515 if (!(source_output = pa_idxset_get_by_index(c->core->source_outputs, index))) {
516 pa_ioline_puts(c->line, "No source output found by this index.\n");
517 return 0;
518 }
519
520 pa_source_output_kill(source_output);
521 return 0;
522 }