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