From 56f8c953dd609bc5c94011fe4acdd9ef6b875747 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 19 Jun 2004 01:01:09 +0000 Subject: some more work on the cli git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@24 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/Makefile.am | 27 ++++--- src/cli.c | 242 +++++++++++++++++++++++++++++++++++++++++++++++--------- src/dynarray.c | 73 +++++++++++++++++ src/dynarray.h | 16 ++++ src/mainloop.c | 33 +++++++- src/mainloop.h | 2 + src/module.c | 30 ++++++- src/module.h | 3 + src/sinkinput.c | 2 + src/strbuf.c | 12 ++- src/todo | 3 +- src/tokenizer.c | 64 +++++++++++++++ src/tokenizer.h | 11 +++ 13 files changed, 459 insertions(+), 59 deletions(-) create mode 100644 src/dynarray.c create mode 100644 src/dynarray.h create mode 100644 src/tokenizer.c create mode 100644 src/tokenizer.h diff --git a/src/Makefile.am b/src/Makefile.am index 5afc5bc0..9ec640c2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,7 +23,7 @@ bin_PROGRAMS = polypaudio pkglib_LTLIBRARIES=libprotocol-simple.la module-simple-protocol-tcp.la \ libsocket-server.la module-pipe-sink.la libpstream.la libiochannel.la \ libpacket.la module-oss.la module-oss-mmap.la liboss.la libioline.la \ - libcli.la module-cli.la + libcli.la module-cli.la libtokenizer.la libdynarray.la polypaudio_SOURCES = idxset.c idxset.h \ queue.c queue.h \ @@ -45,35 +45,42 @@ polypaudio_INCLUDES = $(INCLTDL) polypaudio_LDADD = $(LIBLTDL) polypaudio_LDFLAGS=-export-dynamic -libprotocol_simple_la_SOURCES = protocol-simple.c +libprotocol_simple_la_SOURCES = protocol-simple.c protocol-simple.h libprotocol_simple_la_LDFLAGS = -avoid-version libprotocol_simple_la_LIBADD = libsocket-server.la libiochannel.la -libsocket_server_la_SOURCES = socket-server.c +libsocket_server_la_SOURCES = socket-server.c socket-server.h libsocket_server_la_LDFLAGS = -avoid-version libsocket_server_la_LIBADD = libiochannel.la -libpstream_la_SOURCES = pstream.c +libpstream_la_SOURCES = pstream.c pstream.h libpstream_la_LDFLAGS = -avoid-version libpstream_la_LIBADD = libpacket.la -libiochannel_la_SOURCES = iochannel.c +libiochannel_la_SOURCES = iochannel.c iochannel.h libiochannel_la_LDFLAGS = -avoid-version -libpacket_la_SOURCES = packet.c +libpacket_la_SOURCES = packet.c packet.h libpacket_la_LDFLAGS = -avoid-version -liboss_la_SOURCES = oss.c +liboss_la_SOURCES = oss.c oss.h liboss_la_LDFLAGS = -avoid-version -libioline_la_SOURCES = ioline.c +libioline_la_SOURCES = ioline.c ioline.h libioline_la_LDFLAGS = -avoid-version libioline_la_LIBADD = libiochannel.la -libcli_la_SOURCES = cli.c +libcli_la_SOURCES = cli.c cli.h libcli_la_LDFLAGS = -avoid-version libcli_la_LIBADD = libiochannel.la libioline.la +libdynarray_la_SOURCES = dynarray.c dynarray.h +libdynarray_la_LDFLAGS = -avoid-version + +libtokenizer_la_SOURCES = tokenizer.c tokenizer.h +libtokenizer_la_LDFLAGS = -avoid-version +libtokenizer_la_LIBADD = libdynarray.la + module_simple_protocol_tcp_la_SOURCES = module-simple-protocol-tcp.c module_simple_protocol_tcp_la_LDFLAGS = -module -avoid-version module_simple_protocol_tcp_la_LIBADD = libprotocol-simple.la libiochannel.la @@ -92,4 +99,4 @@ module_oss_mmap_la_LIBADD = libiochannel.la liboss.la module_cli_la_SOURCES = module-cli.c module_cli_la_LDFLAGS = -module -avoid-version -module_cli_la_LIBADD = libcli.la libiochannel.la +module_cli_la_LIBADD = libcli.la libiochannel.la libtokenizer.la diff --git a/src/cli.c b/src/cli.c index 4a198141..4f67f8b7 100644 --- a/src/cli.c +++ b/src/cli.c @@ -11,6 +11,8 @@ #include "client.h" #include "sinkinput.h" #include "sourceoutput.h" +#include "tokenizer.h" +#include "strbuf.h" struct cli { struct core *core; @@ -20,8 +22,46 @@ struct cli { void *userdata; }; +struct command { + const char *name; + void (*proc) (struct cli *cli, struct tokenizer*t); + const char *help; + unsigned args; +}; + static void line_callback(struct ioline *line, const char *s, void *userdata); +static void cli_command_exit(struct cli *c, struct tokenizer *t); +static void cli_command_help(struct cli *c, struct tokenizer *t); +static void cli_command_modules(struct cli *c, struct tokenizer *t); +static void cli_command_clients(struct cli *c, struct tokenizer *t); +static void cli_command_sinks(struct cli *c, struct tokenizer *t); +static void cli_command_sources(struct cli *c, struct tokenizer *t); +static void cli_command_sink_inputs(struct cli *c, struct tokenizer *t); +static void cli_command_source_outputs(struct cli *c, struct tokenizer *t); +static void cli_command_stat(struct cli *c, struct tokenizer *t); +static void cli_command_info(struct cli *c, struct tokenizer *t); +static void cli_command_load(struct cli *c, struct tokenizer *t); +static void cli_command_unload(struct cli *c, struct tokenizer *t); + +static const struct command commands[] = { + { "exit", cli_command_exit, "Terminate the daemon", 1 }, + { "help", cli_command_help, "Show this help", 1 }, + { "modules", cli_command_modules, "List loaded modules", 1 }, + { "sinks", cli_command_sinks, "List loaded sinks", 1 }, + { "sources", cli_command_sources, "List loaded sources", 1 }, + { "clients", cli_command_clients, "List loaded clients", 1 }, + { "sink_inputs", cli_command_sink_inputs, "List sink inputs", 1 }, + { "source_outputs", cli_command_source_outputs, "List source outputs", 1 }, + { "stat", cli_command_stat, "Show memory block statistics", 1 }, + { "info", cli_command_info, "Show comprehensive status", 1 }, + { "load", cli_command_load, "Load a module (given by name and arguments)", 3 }, + { "unload", cli_command_unload, "Unload a module (specified by index)", 2 }, + { NULL, NULL, NULL, 0 } +}; + +static const char prompt[] = ">>> "; + struct cli* cli_new(struct core *core, struct iochannel *io) { struct cli *c; assert(io); @@ -36,7 +76,8 @@ struct cli* cli_new(struct core *core, struct iochannel *io) { c->eof_callback = NULL; ioline_set_callback(c->line, line_callback, c); - ioline_puts(c->line, "Welcome to polypaudio! Use \"help\" for usage information.\n> "); + ioline_puts(c->line, "Welcome to polypaudio! Use \"help\" for usage information.\n"); + ioline_puts(c->line, prompt); return c; } @@ -49,7 +90,8 @@ void cli_free(struct cli *c) { static void line_callback(struct ioline *line, const char *s, void *userdata) { struct cli *c = userdata; - char *t = NULL; + const char *cs; + const char delimiter[] = " \t\n\r"; assert(line && c); if (!s) { @@ -60,42 +102,29 @@ static void line_callback(struct ioline *line, const char *s, void *userdata) { return; } - if (!strcmp(s, "modules")) - ioline_puts(line, (t = module_list_to_string(c->core))); - else if (!strcmp(s, "sources")) - ioline_puts(line, (t = source_list_to_string(c->core))); - else if (!strcmp(s, "sinks")) - ioline_puts(line, (t = sink_list_to_string(c->core))); - else if (!strcmp(s, "clients")) - ioline_puts(line, (t = client_list_to_string(c->core))); - else if (!strcmp(s, "source_outputs")) - ioline_puts(line, (t = source_output_list_to_string(c->core))); - else if (!strcmp(s, "sink_inputs")) - ioline_puts(line, (t = sink_input_list_to_string(c->core))); - else if (!strcmp(s, "stat")) { - char txt[256]; - snprintf(txt, sizeof(txt), "Memory blocks allocated: %u, total size: %u bytes.\n", memblock_count, memblock_total); - ioline_puts(line, txt); - } else if (!strcmp(s, "exit")) { - assert(c->core && c->core->mainloop); - mainloop_quit(c->core->mainloop, -1); - } else if (!strcmp(s, "help")) - ioline_puts(line, - "Available commands:\n" - " modules\t\tlist modules\n" - " sinks\t\tlist sinks\n" - " sources\t\tlist sources\n" - " clients\t\tlist clients\n" - " source_outputs\tlist source outputs\n" - " sink_inputs\t\tlist sink inputs\n" - " stat\t\tshow memblock statistics\n" - " exit\t\tterminate the daemon\n" - " help\t\tshow this help\n"); - else if (*s) - ioline_puts(line, "Unknown command\n"); - - free(t); - ioline_puts(line, "> "); + cs = s+strspn(s, delimiter); + if (*cs && *cs != '#') { + const struct command*command; + int unknown = 1; + size_t l; + + l = strcspn(s, delimiter); + + for (command = commands; command->name; command++) + if (!strncmp(s, command->name, l)) { + struct tokenizer *t = tokenizer_new(s, command->args); + assert(t); + command->proc(c, t); + tokenizer_free(t); + unknown = 0; + break; + } + + if (unknown) + ioline_puts(line, "Unknown command\n"); + } + + ioline_puts(c->line, prompt); } void cli_set_eof_callback(struct cli *c, void (*cb)(struct cli*c, void *userdata), void *userdata) { @@ -103,3 +132,140 @@ void cli_set_eof_callback(struct cli *c, void (*cb)(struct cli*c, void *userdata c->eof_callback = cb; c->userdata = userdata; } + +static void cli_command_exit(struct cli *c, struct tokenizer *t) { + assert(c && c->core && c->core->mainloop && t); + mainloop_quit(c->core->mainloop, -1); +} + +static void cli_command_help(struct cli *c, struct tokenizer *t) { + const struct command*command; + struct strbuf *strbuf; + char *p; + assert(c && t); + + strbuf = strbuf_new(); + assert(strbuf); + + strbuf_puts(strbuf, "Available commands:\n"); + + for (command = commands; command->name; command++) + strbuf_printf(strbuf, " %-20s %s\n", command->name, command->help); + + ioline_puts(c->line, p = strbuf_tostring_free(strbuf)); + free(p); +} + +static void cli_command_modules(struct cli *c, struct tokenizer *t) { + char *s; + assert(c && t); + s = module_list_to_string(c->core); + assert(s); + ioline_puts(c->line, s); + free(s); +} + +static void cli_command_clients(struct cli *c, struct tokenizer *t) { + char *s; + assert(c && t); + s = client_list_to_string(c->core); + assert(s); + ioline_puts(c->line, s); + free(s); +} + +static void cli_command_sinks(struct cli *c, struct tokenizer *t) { + char *s; + assert(c && t); + s = sink_list_to_string(c->core); + assert(s); + ioline_puts(c->line, s); + free(s); +} + +static void cli_command_sources(struct cli *c, struct tokenizer *t) { + char *s; + assert(c && t); + s = source_list_to_string(c->core); + assert(s); + ioline_puts(c->line, s); + free(s); +} + +static void cli_command_sink_inputs(struct cli *c, struct tokenizer *t) { + char *s; + assert(c && t); + s = sink_input_list_to_string(c->core); + assert(s); + ioline_puts(c->line, s); + free(s); +} + +static void cli_command_source_outputs(struct cli *c, struct tokenizer *t) { + char *s; + assert(c && t); + s = source_output_list_to_string(c->core); + assert(s); + ioline_puts(c->line, s); + free(s); +} + +static void cli_command_stat(struct cli *c, struct tokenizer *t) { + char txt[256]; + assert(c && t); + snprintf(txt, sizeof(txt), "Memory blocks allocated: %u, total size: %u bytes.\n", memblock_count, memblock_total); + ioline_puts(c->line, txt); +} + +static void cli_command_info(struct cli *c, struct tokenizer *t) { + assert(c && t); + cli_command_stat(c, t); + cli_command_modules(c, t); + cli_command_sources(c, t); + cli_command_sinks(c, t); + cli_command_clients(c, t); + cli_command_sink_inputs(c, t); + cli_command_source_outputs(c, t); +} + +static void cli_command_load(struct cli *c, struct tokenizer *t) { + struct module *m; + const char *name; + char txt[256]; + assert(c && t); + + if (!(name = tokenizer_get(t, 1))) { + ioline_puts(c->line, "You need to specfiy the module name and optionally arguments.\n"); + return; + } + + if (!(m = module_load(c->core, name, tokenizer_get(t, 2)))) { + ioline_puts(c->line, "Module load failed.\n"); + return; + } + + snprintf(txt, sizeof(txt), "Module successfully loaded, index: %u.\n", m->index); + ioline_puts(c->line, txt); +} + +static void cli_command_unload(struct cli *c, struct tokenizer *t) { + struct module *m; + uint32_t index; + const char *i; + char *e; + assert(c && t); + + if (!(i = tokenizer_get(t, 1))) { + ioline_puts(c->line, "You need to specfiy the module index.\n"); + return; + } + + index = (uint32_t) strtoul(i, &e, 10); + if (*e || !(m = idxset_get_by_index(c->core->modules, index))) { + ioline_puts(c->line, "Invalid module index.\n"); + return; + } + + module_unload_request(c->core, m); +} + diff --git a/src/dynarray.c b/src/dynarray.c new file mode 100644 index 00000000..9a7060ee --- /dev/null +++ b/src/dynarray.c @@ -0,0 +1,73 @@ +#include +#include +#include + +#include "dynarray.h" + +struct dynarray { + void **data; + unsigned n_allocated, n_entries; +}; + +struct dynarray* dynarray_new(void) { + struct dynarray *a; + a = malloc(sizeof(struct dynarray)); + assert(a); + a->data = NULL; + a->n_entries = 0; + a->n_allocated = 0; + return a; +} + +void dynarray_free(struct dynarray* a, void (*func)(void *p, void *userdata), void *userdata) { + unsigned i; + assert(a); + + if (func) + for (i = 0; i < a->n_entries; i++) + if (a->data[i]) + func(a->data[i], userdata); + + free(a->data); + free(a); +} + +void dynarray_put(struct dynarray*a, unsigned i, void *p) { + assert(a); + + if (i >= a->n_allocated) { + unsigned n; + + if (!p) + return; + + n = i+100; + a->data = realloc(a->data, sizeof(void*)*n); + memset(a->data+a->n_allocated, 0, sizeof(void*)*(n-a->n_allocated)); + a->n_allocated = n; + } + + a->data[i] = p; + + if (i >= a->n_entries) + a->n_entries = i+1; +} + +unsigned dynarray_append(struct dynarray*a, void *p) { + unsigned i = a->n_entries; + dynarray_put(a, i, p); + return i; +} + +void *dynarray_get(struct dynarray*a, unsigned i) { + assert(a); + if (i >= a->n_allocated) + return NULL; + assert(a->data); + return a->data[i]; +} + +unsigned dynarray_ncontents(struct dynarray*a) { + assert(a); + return a->n_entries; +} diff --git a/src/dynarray.h b/src/dynarray.h new file mode 100644 index 00000000..9ab861ce --- /dev/null +++ b/src/dynarray.h @@ -0,0 +1,16 @@ +#ifndef foodynarrayhfoo +#define foodynarrayhfoo + +struct dynarray; + +struct dynarray* dynarray_new(void); +void dynarray_free(struct dynarray* a, void (*func)(void *p, void *userdata), void *userdata); + +void dynarray_put(struct dynarray*a, unsigned i, void *p); +unsigned dynarray_append(struct dynarray*a, void *p); + +void *dynarray_get(struct dynarray*a, unsigned i); + +unsigned dynarray_ncontents(struct dynarray*a); + +#endif diff --git a/src/mainloop.c b/src/mainloop.c index 37dbdb12..fba0461c 100644 --- a/src/mainloop.c +++ b/src/mainloop.c @@ -242,8 +242,8 @@ int mainloop_iterate(struct mainloop *m, int block) { free_sources(&m->signal_sources, 0); for (s = m->fixed_sources.sources; s; s = s->next) { - assert(!s->dead && s->type == MAINLOOP_SOURCE_TYPE_FIXED); - if (s->enabled) { + assert(s->type == MAINLOOP_SOURCE_TYPE_FIXED); + if (!s->dead && s->enabled) { assert(s->fixed.callback); s->fixed.callback(s, s->userdata); } @@ -264,8 +264,8 @@ int mainloop_iterate(struct mainloop *m, int block) { dispatch_pollfds(m); else if (c == 0) { for (s = m->idle_sources.sources; s; s = s->next) { - assert(!s->dead && s->type == MAINLOOP_SOURCE_TYPE_IDLE); - if (s->enabled) { + assert(s->type == MAINLOOP_SOURCE_TYPE_IDLE); + if (!s->dead && s->enabled) { assert(s->idle.callback); s->idle.callback(s, s->userdata); } @@ -448,3 +448,28 @@ struct mainloop *mainloop_source_get_mainloop(struct mainloop_source *s) { return s->mainloop; } + +struct once_info { + void (*callback)(void *userdata); + void *userdata; +}; + +static void once_callback(struct mainloop_source *s, void *userdata) { + struct once_info *i = userdata; + assert(s && i && i->callback); + i->callback(i->userdata); + mainloop_source_free(s); + free(i); +} + +void mainloop_once(struct mainloop*m, void (*callback)(void *userdata), void *userdata) { + struct once_info *i; + assert(m && callback); + + i = malloc(sizeof(struct once_info)); + assert(i); + i->callback = callback; + i->userdata = userdata; + + mainloop_source_new_fixed(m, once_callback, i); +} diff --git a/src/mainloop.h b/src/mainloop.h index 3fe26fd0..c1bfc62a 100644 --- a/src/mainloop.h +++ b/src/mainloop.h @@ -30,6 +30,8 @@ struct mainloop_source* mainloop_source_new_fixed(struct mainloop*m, void (*call struct mainloop_source* mainloop_source_new_idle(struct mainloop*m, void (*callback)(struct mainloop_source *s, void*userdata), void*userdata); struct mainloop_source* mainloop_source_new_signal(struct mainloop*m, int sig, void (*callback)(struct mainloop_source *s, int sig, void*userdata), void*userdata); +void mainloop_once(struct mainloop*m, void (*callback)(void *userdata), void *userdata); + void mainloop_source_free(struct mainloop_source*s); void mainloop_source_enable(struct mainloop_source*s, int b); diff --git a/src/module.c b/src/module.c index 0be7f5ed..c6de1751 100644 --- a/src/module.c +++ b/src/module.c @@ -17,6 +17,9 @@ struct module* module_load(struct core *c, const char *name, const char *argumen m = malloc(sizeof(struct module)); assert(m); + m->name = strdup(name); + m->argument = argument ? strdup(argument) : NULL; + if (!(m->dl = lt_dlopenext(name))) goto fail; @@ -26,8 +29,6 @@ struct module* module_load(struct core *c, const char *name, const char *argumen if (!(m->done = lt_dlsym(m->dl, "module_done"))) goto fail; - m->name = strdup(name); - m->argument = argument ? strdup(argument) : NULL; m->userdata = NULL; m->core = c; @@ -127,3 +128,28 @@ char *module_list_to_string(struct core *c) { return strbuf_tostring_free(s); } + + +struct once_info { + struct core *core; + uint32_t index; +}; + + +void module_unload_once_callback(void *userdata) { + struct once_info *i = userdata; + assert(i); + module_unload_by_index(i->core, i->index); + free(i); +} + +void module_unload_request(struct core *c, struct module *m) { + struct once_info *i; + assert(c && m); + + i = malloc(sizeof(struct once_info)); + assert(i); + i->core = c; + i->index = m->index; + mainloop_once(c->mainloop, module_unload_once_callback, i); +} diff --git a/src/module.h b/src/module.h index 709c7f55..cdb61347 100644 --- a/src/module.h +++ b/src/module.h @@ -27,4 +27,7 @@ void module_unload_all(struct core *c); char *module_list_to_string(struct core *c); +void module_unload_request(struct core *c, struct module *m); + + #endif diff --git a/src/sinkinput.c b/src/sinkinput.c index f589b4d9..2e6a8c36 100644 --- a/src/sinkinput.c +++ b/src/sinkinput.c @@ -22,6 +22,8 @@ struct sink_input* sink_input_new(struct sink *s, struct sample_spec *spec, cons i->get_latency = NULL; i->userdata = NULL; + i->volume = 0xFF; + assert(s->core); r = idxset_put(s->core->sink_inputs, i, &i->index); assert(r == 0 && i->index != IDXSET_INVALID); diff --git a/src/strbuf.c b/src/strbuf.c index 97c451c1..9ce67ec3 100644 --- a/src/strbuf.c +++ b/src/strbuf.c @@ -7,6 +7,7 @@ struct chunk { struct chunk *next; + size_t length; char text[]; }; @@ -43,12 +44,13 @@ char *strbuf_tostring(struct strbuf *sb) { assert(t); e = t; - *e = 0; for (c = sb->head; c; c = c->next) { - strcpy(e, c->text); - e = strchr(e, 0); + memcpy(e, c->text, c->length); + e += c->length; } + *e = 0; + return t; } @@ -70,7 +72,8 @@ void strbuf_puts(struct strbuf *sb, const char *t) { assert(c); c->next = NULL; - strcpy(c->text, t); + c->length = l; + memcpy(c->text, t, l); if (sb->tail) { assert(sb->head); @@ -101,6 +104,7 @@ int strbuf_printf(struct strbuf *sb, const char *format, ...) { va_end(ap); if (r > -1 && r < size) { + c->length = r; c->next = NULL; if (sb->tail) { diff --git a/src/todo b/src/todo index 2ff7cc6a..7e60ac3b 100644 --- a/src/todo +++ b/src/todo @@ -1,4 +1,5 @@ -- simple control protocol +- +- simple control protocol: kill client/input/output; set_volume - native protocol/library - resampling - esound protocol diff --git a/src/tokenizer.c b/src/tokenizer.c new file mode 100644 index 00000000..0d266a9a --- /dev/null +++ b/src/tokenizer.c @@ -0,0 +1,64 @@ +#include +#include +#include + +#include "tokenizer.h" +#include "dynarray.h" + +struct tokenizer { + struct dynarray *dynarray; +}; + +static void token_free(void *p, void *userdata) { + free(p); +} + +static void parse(struct dynarray*a, const char *s, unsigned args) { + int infty = 0; + const char delimiter[] = " \t\n\r"; + const char *p; + assert(a && s); + + if (args == 0) + infty = 1; + + p = s+strspn(s, delimiter); + while (*p && (infty || args >= 2)) { + size_t l = strcspn(p, delimiter); + char *n = strndup(p, l); + assert(n); + dynarray_append(a, n); + p += l; + p += strspn(p, delimiter); + args--; + } + + if (args && *p) { + char *n = strdup(p); + assert(n); + dynarray_append(a, n); + } +} + +struct tokenizer* tokenizer_new(const char *s, unsigned args) { + struct tokenizer *t; + + t = malloc(sizeof(struct tokenizer)); + assert(t); + t->dynarray = dynarray_new(); + assert(t->dynarray); + + parse(t->dynarray, s, args); + return t; +} + +void tokenizer_free(struct tokenizer *t) { + assert(t); + dynarray_free(t->dynarray, token_free, NULL); + free(t); +} + +const char *tokenizer_get(struct tokenizer *t, unsigned i) { + assert(t); + return dynarray_get(t->dynarray, i); +} diff --git a/src/tokenizer.h b/src/tokenizer.h new file mode 100644 index 00000000..c71ae790 --- /dev/null +++ b/src/tokenizer.h @@ -0,0 +1,11 @@ +#ifndef footokenizerhfoo +#define footokenizerhfoo + +struct tokenizer; + +struct tokenizer* tokenizer_new(const char *s, unsigned args); +void tokenizer_free(struct tokenizer *t); + +const char *tokenizer_get(struct tokenizer *t, unsigned i); + +#endif -- cgit