From 63b35d002aa6902618235e1a30dca37de52ff65e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 17 Sep 2004 19:45:44 +0000 Subject: new configuration subsystem git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@210 fefdeb5f-60dc-0310-8127-8f9354f1896f --- doc/todo | 1 + polyp/Makefile.am | 15 ++- polyp/client.conf | 39 +++++++ polyp/conf.c | 279 +++++++++++++++++----------------------------- polyp/conf.h | 1 + polyp/config | 61 ---------- polyp/config-client.c | 138 +++++++++++++++++++++++ polyp/config-client.h | 36 ++++++ polyp/confparser.c | 168 ++++++++++++++++++++++++++++ polyp/confparser.h | 37 ++++++ polyp/core.c | 3 + polyp/core.h | 2 + polyp/daemon.conf | 67 +++++++++++ polyp/main.c | 6 +- polyp/polyplib-context.c | 63 +++++++---- polyp/polyplib-internal.h | 9 +- polyp/polyplib-scache.c | 2 +- polyp/polyplib-stream.c | 6 +- polyp/resampler.c | 4 +- polyp/resampler.h | 2 +- polyp/sink-input.c | 2 +- polyp/source-output.c | 2 +- polyp/util.c | 17 +++ polyp/util.h | 1 + 24 files changed, 678 insertions(+), 283 deletions(-) create mode 100644 polyp/client.conf delete mode 100644 polyp/config create mode 100644 polyp/config-client.c create mode 100644 polyp/config-client.h create mode 100644 polyp/confparser.c create mode 100644 polyp/confparser.h create mode 100644 polyp/daemon.conf diff --git a/doc/todo b/doc/todo index bcc78f51b..fe06df780 100644 --- a/doc/todo +++ b/doc/todo @@ -5,6 +5,7 @@ - fix tcp/native in regard to latencies - add client config file - remove autospawn stuff in conf.c +- make resampler configurable *** 0.6 **** - per-channel volume diff --git a/polyp/Makefile.am b/polyp/Makefile.am index fc3fb18d2..d49c7c481 100644 --- a/polyp/Makefile.am +++ b/polyp/Makefile.am @@ -25,14 +25,14 @@ modlibdir=$(libdir)/polypaudio-@PA_MAJORMINOR@ AM_CFLAGS=-D_GNU_SOURCE -I$(top_srcdir) $(PTHREAD_CFLAGS) AM_CFLAGS+=-DDLSEARCHPATH=\"$(modlibdir)\" AM_CFLAGS+=-DDEFAULT_SCRIPT_FILE=\"$(polypconfdir)/default.pa\" -AM_CFLAGS+=-DDEFAULT_CONFIG_FILE=\"$(polypconfdir)/default.conf\" -AM_CFLAGS+=-DAUTOSPAWN_CONFIG_FILE=\"$(polypconfdir)/autospawn.conf\" +AM_CFLAGS+=-DDEFAULT_CONFIG_FILE=\"$(polypconfdir)/daemon.conf\" +AM_CFLAGS+=-DDEFAULT_CLIENT_CONFIG_FILE=\"$(polypconfdir)/client.conf\" AM_CFLAGS+=-DPOLYPAUDIO_BINARY=\"$(bindir)/polypaudio\" AM_LDADD=$(PTHREAD_LIBS) -lm AM_LIBADD=$(PTHREAD_LIBS) -lm -EXTRA_DIST = default.pa config depmod.py esdcompat.sh.in +EXTRA_DIST = default.pa daemon.conf client.conf config depmod.py esdcompat.sh.in bin_PROGRAMS = polypaudio pacat pactl bin_SCRIPTS = esdcompat.sh noinst_PROGRAMS = \ @@ -42,7 +42,7 @@ noinst_PROGRAMS = \ cpulimit-test \ cpulimit-test2 -polypconf_DATA=default.pa config +polypconf_DATA=default.pa daemon.conf client.conf BUILT_SOURCES=polyplib-version.h @@ -158,7 +158,8 @@ polypaudio_SOURCES = idxset.c idxset.h \ gcc-printf.h \ modinfo.c modinfo.h \ conf.c conf.h \ - dumpmodules.c dumpmodules.h + dumpmodules.c dumpmodules.h \ + conparser.h confparser.c polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) polypaudio_INCLUDES = $(INCLTDL) @@ -332,7 +333,9 @@ libpolyp_@PA_MAJORMINOR@_la_SOURCES = polyplib.h \ cdecl.h \ llist.h \ log.c log.h \ - gcc-printf.h + gcc-printf.h \ + config-client.c config-client.h \ + confparser.c confparser.h libpolyp_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) libpolyp_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0 diff --git a/polyp/client.conf b/polyp/client.conf new file mode 100644 index 000000000..070d75bd2 --- /dev/null +++ b/polyp/client.conf @@ -0,0 +1,39 @@ +# $Id$ +# +# This file is part of polypaudio. +# +# polypaudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# polypaudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with polypaudio; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA. + +## Configuration file for polypaudio clients. Default values are +## commented out. Use either ; or # for commenting + +## Path to the polypaudio daemon to run when autospawning. +; daemon_binary = @POLYPAUDIO_BINARY + +## Extra arguments to pass to the polypaudio daemon +; extra_arguments = --daemonize=yes --log-target=syslog + +## The default sink to connect to +; default_sink = + +## The default source to connect to +; default_source = + +## The default sever to connect to +; default_server = + +## Autospawn daemons? +; autospawn = 0 diff --git a/polyp/conf.c b/polyp/conf.c index b74a5ede2..efc471af7 100644 --- a/polyp/conf.c +++ b/polyp/conf.c @@ -27,12 +27,34 @@ #include #include #include -#include +#include +#include #include "conf.h" #include "util.h" #include "xmalloc.h" #include "strbuf.h" +#include "confparser.h" + +#ifndef DEFAULT_SCRIPT_FILE +#define DEFAULT_SCRIPT_FILE "/etc/polypaudio/default.pa" +#endif + +#ifndef DEFAULT_SCRIPT_FILE_USER +#define DEFAULT_SCRIPT_FILE_USER ".polypaudio/default.pa" +#endif + +#ifndef DEFAULT_CONFIG_FILE +#define DEFAULT_CONFIG_FILE "/etc/polypaudio/daemon.conf" +#endif + +#ifndef DEFAULT_CONFIG_FILE_USER +#define DEFAULT_CONFIG_FILE_USER ".polypaudio/daemon.conf" +#endif + +#define ENV_SCRIPT_FILE "POLYP_SCRIPT" +#define ENV_CONFIG_FILE "POLYP_CONFIG" +#define ENV_DL_SEARCH_PATH "POLYP_DLPATH" static const struct pa_conf default_conf = { .cmd = PA_CMD_DAEMON, @@ -49,28 +71,9 @@ static const struct pa_conf default_conf = { .dl_search_path = NULL, .default_script_file = NULL, .log_target = PA_LOG_SYSLOG, + .resample_method = SRC_SINC_FASTEST }; -#define ENV_SCRIPT_FILE "POLYP_SCRIPT" -#define ENV_CONFIG_FILE "POLYP_CONFIG" -#define ENV_AUTOSPAWNED "POLYP_AUTOSPAWNED" - -#ifndef DEFAULT_SCRIPT_FILE -#define DEFAULT_SCRIPT_FILE "/etc/polypaudio/default.pa" -#endif - -#ifndef DEFAULT_CONFIG_FILE -#define DEFAULT_CONFIG_FILE "/etc/polypaudio/default.conf" -#endif - -#ifndef AUTOSPAWN_CONFIG_FILE -#define AUTOSPAWN_CONFIG_FILE "/etc/polypaudio/autospawn.conf" -#endif - -#define DEFAULT_SCRIPT_FILE_LOCAL ".polypaudio.pa" -#define DEFAULT_CONFIG_FILE_LOCAL ".polypaudio.conf" -#define AUTOSPAWN_CONFIG_FILE_LOCAL ".polypaudio-autospawn.conf" - char* default_file(const char *envvar, const char *global, const char *local) { char *p, *h; @@ -80,9 +83,8 @@ char* default_file(const char *envvar, const char *global, const char *local) { return pa_xstrdup(p); if ((h = getenv("HOME"))) { - struct stat st; p = pa_sprintf_malloc("%s/%s", h, local); - if (stat(p, &st) >= 0) + if (!access(p, F_OK)) return p; pa_xfree(p); @@ -91,26 +93,12 @@ char* default_file(const char *envvar, const char *global, const char *local) { return pa_xstrdup(global); } -char *default_config_file(void) { - char *b; - int autospawned = 0; - - if ((b = getenv(ENV_AUTOSPAWNED))) - autospawned = pa_parse_boolean(b) > 0; - - return default_file(ENV_CONFIG_FILE, - autospawned ? AUTOSPAWN_CONFIG_FILE : DEFAULT_CONFIG_FILE, - autospawned ? AUTOSPAWN_CONFIG_FILE_LOCAL : DEFAULT_CONFIG_FILE_LOCAL); - -} - -char *default_script_file(void) { - return default_file(ENV_SCRIPT_FILE, DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_LOCAL); -} - struct pa_conf* pa_conf_new(void) { struct pa_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf)); - c->default_script_file = default_script_file(); + c->default_script_file = default_file(ENV_SCRIPT_FILE, DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER); +#ifdef DLSEARCHPATH + c->dl_search_path = pa_xstrdup(DLSEARCHPATH); +#endif return c; } @@ -122,166 +110,104 @@ void pa_conf_free(struct pa_conf *c) { pa_xfree(c); } -#define WHITESPACE " \t\n" -#define COMMENTS "#;\n" - -#define PARSE_BOOLEAN(t, v) \ - do { \ - if (!strcmp(lvalue, t)) { \ - int b; \ - if ((b = pa_parse_boolean(rvalue)) < 0) \ - goto fail; \ - c->v = b; \ - return 0; \ - } \ - } while (0) - -#define PARSE_STRING(t, v) \ - do { \ - if (!strcmp(lvalue, t)) { \ - pa_xfree(c->v); \ - c->v = *rvalue ? pa_xstrdup(rvalue) : NULL; \ - return 0; \ - } \ - } while (0) - -#define PARSE_INTEGER(t, v) \ - do { \ - if (!strcmp(lvalue, t)) { \ - char *x = NULL; \ - int i = strtol(rvalue, &x, 0); \ - if (!x || *x) \ - goto fail; \ - c->v = i; \ - return 0; \ - } \ - } while(0) - -static int next_assignment(struct pa_conf *c, char *lvalue, char *rvalue, unsigned n) { - PARSE_BOOLEAN("daemonize", daemonize); - PARSE_BOOLEAN("fail", fail); - PARSE_BOOLEAN("verbose", verbose); - PARSE_BOOLEAN("high-priority", high_priority); - PARSE_BOOLEAN("disallow-module-loading", disallow_module_loading); - - PARSE_INTEGER("exit-idle-time", exit_idle_time); - PARSE_INTEGER("module-idle-time", module_idle_time); - PARSE_INTEGER("scache-idle-time", scache_idle_time); +int parse_log_target(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) { + struct pa_conf *c = data; + assert(filename && lvalue && rvalue && data); - PARSE_STRING("dl-search-path", dl_search_path); - PARSE_STRING("default-script-file", default_script_file); - - if (!strcmp(lvalue, "log-target")) { - if (!strcmp(rvalue, "auto")) - c->auto_log_target = 1; - else if (!strcmp(rvalue, "syslog")) { - c->auto_log_target = 0; - c->log_target = PA_LOG_SYSLOG; - } else if (!strcmp(rvalue, "stderr")) { - c->auto_log_target = 0; - c->log_target = PA_LOG_STDERR; - } else - goto fail; - - return 0; + if (!strcmp(rvalue, "auto")) + c->auto_log_target = 1; + else if (!strcmp(rvalue, "syslog")) { + c->auto_log_target = 0; + c->log_target = PA_LOG_SYSLOG; + } else if (!strcmp(rvalue, "stderr")) { + c->auto_log_target = 0; + c->log_target = PA_LOG_STDERR; + } else { + pa_log(__FILE__": [%s:%u] Invalid log target '%s'.\n", filename, line, rvalue); + return -1; } - -fail: - pa_log(__FILE__": line %u: parse error.\n", n); - return -1; -} - -#undef PARSE_STRING -#undef PARSE_BOOLEAN - -static int in_string(char c, const char *s) { - for (; *s; s++) - if (*s == c) - return 1; return 0; } -static char *strip(char *s) { - char *b = s+strspn(s, WHITESPACE); - char *e, *l = NULL; - - for (e = b; *e; e++) - if (!in_string(*e, WHITESPACE)) - l = e; - - if (l) - *(l+1) = 0; - - return b; -} - -static int parse_line(struct pa_conf *conf, char *l, unsigned n) { - char *e, *c, *b = l+strspn(l, WHITESPACE); - - if ((c = strpbrk(b, COMMENTS))) - *c = 0; - - if (!*b) - return 0; - - if (!(e = strchr(b, '='))) { - pa_log(__FILE__": line %u: missing '='.\n", n); +int parse_resample_method(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) { + struct pa_conf *c = data; + assert(filename && lvalue && rvalue && data); + + if (!strcmp(rvalue, "sinc-best-quality")) + c->resample_method = SRC_SINC_BEST_QUALITY; + else if (!strcmp(rvalue, "sinc-medium-quality")) + c->resample_method = SRC_SINC_MEDIUM_QUALITY; + else if (!strcmp(rvalue, "sinc-fastest")) + c->resample_method = SRC_SINC_FASTEST; + else if (!strcmp(rvalue, "zero-order-hold")) + c->resample_method = SRC_ZERO_ORDER_HOLD; + else if (!strcmp(rvalue, "linear")) + c->resample_method = SRC_LINEAR; + else { + pa_log(__FILE__": [%s:%u] Inavalid resample method '%s'.\n", filename, line, rvalue); return -1; } - *e = 0; - e++; - - return next_assignment(conf, strip(b), strip(e), n); + return 0; } - int pa_conf_load(struct pa_conf *c, const char *filename) { - FILE *f; - int r = 0; - unsigned n = 0; char *def = NULL; - assert(c); + int r; + const struct pa_config_item table[] = { + { "verbose", pa_config_parse_bool, &c->verbose }, + { "daemonize", pa_config_parse_bool, &c->daemonize }, + { "fail", pa_config_parse_bool, &c->fail }, + { "high-priority", pa_config_parse_bool, &c->high_priority }, + { "disallow-module-loading", pa_config_parse_bool, &c->disallow_module_loading }, + { "exit-idle-time", pa_config_parse_int, &c->exit_idle_time }, + { "module-idle-time", pa_config_parse_int, &c->module_idle_time }, + { "scache-idle-time", pa_config_parse_int, &c->scache_idle_time }, + { "dl-search-path", pa_config_parse_string, &c->dl_search_path }, + { "default-script-file", pa_config_parse_string, &c->default_script_file }, + { "log-target", parse_log_target, c }, + { "resample-method", parse_resample_method, c }, + { NULL, NULL, NULL }, + }; + if (!filename) - filename = def = default_config_file(); + filename = def = default_file(ENV_CONFIG_FILE, DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER); + + r = pa_config_parse(filename, table, NULL); + pa_xfree(def); + return r; +} - if (!(f = fopen(filename, "r"))) { - if (errno != ENOENT) - pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s\n", filename, strerror(errno)); +int pa_conf_env(struct pa_conf *c) { + char *e; - goto finish; + if ((e = getenv(ENV_DL_SEARCH_PATH))) { + pa_xfree(c->dl_search_path); + c->dl_search_path = pa_xstrdup(e); } - - while (!feof(f)) { - char l[256]; - if (!fgets(l, sizeof(l), f)) { - if (!feof(f)) - pa_log(__FILE__": WARNING: failed to read configuration file '%s': %s\n", filename, strerror(errno)); - - break; - } - - if (parse_line(c, l, ++n) < 0) - r = -1; + if ((e = getenv(ENV_SCRIPT_FILE))) { + pa_xfree(c->default_script_file); + c->default_script_file = pa_xstrdup(e); } - -finish: - if (f) - fclose(f); - - pa_xfree(def); - - return r; + return 0; } char *pa_conf_dump(struct pa_conf *c) { struct pa_strbuf *s = pa_strbuf_new(); char *d; - d = default_config_file(); + static const char const* resample_methods[] = { + "sinc-best-quality", + "sinc-medium-quality", + "sinc-fastest", + "zero-order-hold", + "linear" + }; + + d = default_file(ENV_CONFIG_FILE, DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER); pa_strbuf_printf(s, "### Default configuration file: %s ###\n", d); pa_strbuf_printf(s, "verbose = %i\n", !!c->verbose); @@ -295,6 +221,9 @@ char *pa_conf_dump(struct pa_conf *c) { pa_strbuf_printf(s, "dl-search-path = %s\n", c->dl_search_path ? c->dl_search_path : ""); pa_strbuf_printf(s, "default-script-file = %s\n", c->default_script_file); pa_strbuf_printf(s, "log-target = %s\n", c->auto_log_target ? "auto" : (c->log_target == PA_LOG_SYSLOG ? "syslog" : "stderr")); + + assert(c->resample_method <= 4 && c->resample_method >= 0); + pa_strbuf_printf(s, "resample-method = %s\n", resample_methods[c->resample_method]); pa_xfree(d); diff --git a/polyp/conf.h b/polyp/conf.h index dafb3797a..ace5396d4 100644 --- a/polyp/conf.h +++ b/polyp/conf.h @@ -45,6 +45,7 @@ struct pa_conf { auto_log_target; char *script_commands, *dl_search_path, *default_script_file; enum pa_log_target log_target; + int resample_method; }; struct pa_conf* pa_conf_new(void); diff --git a/polyp/config b/polyp/config deleted file mode 100644 index b404bc408..000000000 --- a/polyp/config +++ /dev/null @@ -1,61 +0,0 @@ -# $Id$ -# -# This file is part of polypaudio. -# -# polypaudio is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# polypaudio is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with polypaudio; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -# USA. - -## Configuration file for polypaudio. Default values are commented out. -## Use either ; or # for commenting - -# Extra verbositiy -; verbose = 0 - -## Daemonize after startup -; daemonize = 0 - -## Quit if startup fails -; fail = 1 - -## Renice the daemon to level -15 and try to get SCHED_FIFO -## scheduling. This a good idea if you hear annyoing noise in the -## playback. However, this is a certain security issue, since it works -## when called SUID root only. root is dropped immediately after gaining -## the nice level and SCHED_FIFO scheduling on startup. -high-priority = 0 - -## Disallow module loading after startup -; disallow-module-loading = 0 - -## Terminate the daemon after the last client quit and this time -## passed. Use a negative value to disable this feature. -; exit-idle-time = -1 - -## Unload autoloaded modules after being idle for this time -module-idle-time = 20 - -## The path were to look for dynamic shared objects (DSOs aka plugins). -## Specify an empty string for the default search path. You may specify -## more than one path seperated by colons. -; dl-search-path = - -## The default script file to load. Specify an empty string for not -## loading a default script file -; default-script-file = /etc/polyp/default.pa - -## The default log target. Use either "stderr", "syslog" or -## "auto". The latter is equivalent to "sylog" in case daemonize is -## true, otherwise to "stderr". -; log-target = auto diff --git a/polyp/config-client.c b/polyp/config-client.c new file mode 100644 index 000000000..758927f3a --- /dev/null +++ b/polyp/config-client.c @@ -0,0 +1,138 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include +#include + +#include "config-client.h" +#include "xmalloc.h" +#include "log.h" +#include "confparser.h" +#include "util.h" + +#ifndef DEFAULT_CLIENT_CONFIG_FILE +#define DEFAULT_CLIENT_CONFIG_FILE "/etc/polypaudio/client.conf" +#endif + +#ifndef DEFAULT_CLIENT_CONFIG_FILE_USER +#define DEFAULT_CLIENT_CONFIG_FILE_USER ".polypaudio/client.conf" +#endif + +#define ENV_CLIENT_CONFIG_FILE "POLYP_CLIENTCONFIG" +#define ENV_DEFAULT_SINK "POLYP_SINK" +#define ENV_DEFAULT_SOURCE "POLYP_SOURCE" +#define ENV_DEFAULT_SERVER "POLYP_SERVER" +#define ENV_DAEMON_BINARY "POLYP_BINARY" + + +static const struct pa_client_conf default_conf = { + .daemon_binary = NULL, + .extra_arguments = NULL, + .default_sink = NULL, + .default_source = NULL, + .default_server = NULL, + .autospawn = 0 +}; + +struct pa_client_conf *pa_client_conf_new(void) { + struct pa_client_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf)); + + c->daemon_binary = pa_xstrdup(POLYPAUDIO_BINARY); + c->extra_arguments = pa_xstrdup("--daemonize=yes --log-target=syslog"); + + return c; +} + +void pa_client_conf_free(struct pa_client_conf *c) { + assert(c); + pa_xfree(c->daemon_binary); + pa_xfree(c->extra_arguments); + pa_xfree(c->default_sink); + pa_xfree(c->default_source); + pa_xfree(c->default_server); + pa_xfree(c); +} +int pa_client_conf_load(struct pa_client_conf *c, const char *filename) { + char *def = NULL; + int r; + + const struct pa_config_item table[] = { + { "daemon-binary", pa_config_parse_string, &c->daemon_binary }, + { "extra-arguments", pa_config_parse_string, &c->extra_arguments }, + { "default-sink", pa_config_parse_string, &c->default_sink }, + { "default-source", pa_config_parse_string, &c->default_source }, + { "default-server", pa_config_parse_string, &c->default_server }, + { "autospawn", pa_config_parse_bool, &c->autospawn }, + { NULL, NULL, NULL }, + }; + + if (!filename) + filename = getenv(ENV_CLIENT_CONFIG_FILE); + + if (!filename) { + char *h; + + if ((h = getenv("HOME"))) { + def = pa_sprintf_malloc("%s/%s", h, DEFAULT_CLIENT_CONFIG_FILE_USER); + + if (!access(def, F_OK)) + filename = def; + else { + pa_xfree(def); + def = NULL; + } + } + } + + if (!filename) + filename = DEFAULT_CLIENT_CONFIG_FILE; + + r = pa_config_parse(filename, table, NULL); + pa_xfree(def); + return r; +} + +int pa_client_conf_env(struct pa_client_conf *c) { + char *e; + + if ((e = getenv(ENV_DEFAULT_SINK))) { + pa_xfree(c->default_sink); + c->default_sink = pa_xstrdup(e); + } + + if ((e = getenv(ENV_DEFAULT_SOURCE))) { + pa_xfree(c->default_source); + c->default_source = pa_xstrdup(e); + } + + if ((e = getenv(ENV_DEFAULT_SERVER))) { + pa_xfree(c->default_server); + c->default_server = pa_xstrdup(e); + } + + if ((e = getenv(ENV_DAEMON_BINARY))) { + pa_xfree(c->daemon_binary); + c->daemon_binary = pa_xstrdup(e); + } + + return 0; +} diff --git a/polyp/config-client.h b/polyp/config-client.h new file mode 100644 index 000000000..1b1c519dd --- /dev/null +++ b/polyp/config-client.h @@ -0,0 +1,36 @@ +#ifndef fooconfigclienthfoo +#define fooconfigclienthfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +struct pa_client_conf { + char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server; + int autospawn; +}; + +struct pa_client_conf *pa_client_conf_new(void); +void pa_client_conf_free(struct pa_client_conf *c); + +int pa_client_conf_load(struct pa_client_conf *c, const char *filename); +int pa_client_conf_env(struct pa_client_conf *c); + +#endif diff --git a/polyp/confparser.c b/polyp/confparser.c new file mode 100644 index 000000000..8f551b954 --- /dev/null +++ b/polyp/confparser.c @@ -0,0 +1,168 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include +#include +#include + +#include "confparser.h" +#include "log.h" +#include "util.h" +#include "xmalloc.h" + +#define WHITESPACE " \t\n" +#define COMMENTS "#;\n" + +static int next_assignment(const char *filename, unsigned line, const struct pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) { + assert(filename && t && lvalue && rvalue); + + for (; t->parse; t++) + if (!strcmp(lvalue, t->lvalue)) + return t->parse(filename, line, lvalue, rvalue, t->data, userdata); + + pa_log(__FILE__": [%s:%u] Unknown lvalue '%s'.\n", filename, line, lvalue); + + return -1; +} + +static int in_string(char c, const char *s) { + assert(s); + + for (; *s; s++) + if (*s == c) + return 1; + + return 0; +} + +static char *strip(char *s) { + char *b = s+strspn(s, WHITESPACE); + char *e, *l = NULL; + + for (e = b; *e; e++) + if (!in_string(*e, WHITESPACE)) + l = e; + + if (l) + *(l+1) = 0; + + return b; +} + +static int parse_line(const char *filename, unsigned line, const struct pa_config_item *t, char *l, void *userdata) { + char *e, *c, *b = l+strspn(l, WHITESPACE); + + if ((c = strpbrk(b, COMMENTS))) + *c = 0; + + if (!*b) + return 0; + + if (!(e = strchr(b, '='))) { + pa_log(__FILE__": [%s:%u] Missing '='.\n", filename, line); + return -1; + } + + *e = 0; + e++; + + return next_assignment(filename, line, t, strip(b), strip(e), userdata); +} + + +int pa_config_parse(const char *filename, const struct pa_config_item *t, void *userdata) { + FILE *f; + int r = -1; + unsigned line = 0; + assert(filename && t); + + if (!(f = fopen(filename, "r"))) { + if (errno == ENOENT) { + r = 0; + goto finish; + } + + pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s\n", filename, strerror(errno)); + goto finish; + } + + while (!feof(f)) { + char l[256]; + if (!fgets(l, sizeof(l), f)) { + if (feof(f)) + break; + + pa_log(__FILE__": WARNING: failed to read configuration file '%s': %s\n", filename, strerror(errno)); + goto finish; + } + + if (parse_line(filename, ++line, t, l, userdata) < 0) + goto finish; + } + + r = 0; + +finish: + + if (f) + fclose(f); + + return r; +} + +int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) { + int *i = data, k; + char *x = NULL; + assert(filename && lvalue && rvalue && data); + + k = strtol(rvalue, &x, 0); + if (!*rvalue || !x || *x) { + pa_log(__FILE__": [%s:%u] Failed to parse numeric value: %s\n", filename, line, rvalue); + return -1; + } + + *i = k; + return 0; +} + +int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) { + int *b = data, k; + assert(filename && lvalue && rvalue && data); + + if ((k = pa_parse_boolean(rvalue)) < 0) { + pa_log(__FILE__": [%s:%u] Failed to parse boolean value: %s\n", filename, line, rvalue); + return -1; + } + + *b = k; + + return 0; +} + +int pa_config_parse_string(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) { + char **s = data; + assert(filename && lvalue && rvalue && data); + + pa_xfree(*s); + *s = *rvalue ? pa_xstrdup(rvalue) : NULL; + return 0; +} diff --git a/polyp/confparser.h b/polyp/confparser.h new file mode 100644 index 000000000..a0eb52d0d --- /dev/null +++ b/polyp/confparser.h @@ -0,0 +1,37 @@ +#ifndef fooconfparserhfoo +#define fooconfparserhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +struct pa_config_item { + const char *lvalue; + int (*parse)(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); + void *data; +}; + +int pa_config_parse(const char *filename, const struct pa_config_item *t, void *userdata); + +int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); +int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); +int pa_config_parse_string(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); + +#endif diff --git a/polyp/core.c b/polyp/core.c index 58035087a..cf2d383c4 100644 --- a/polyp/core.c +++ b/polyp/core.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "core.h" #include "module.h" @@ -79,6 +80,8 @@ struct pa_core* pa_core_new(struct pa_mainloop_api *m) { c->exit_idle_time = -1; c->module_idle_time = 20; c->scache_idle_time = 20; + + c->resample_method = SRC_SINC_FASTEST; pa_check_signal_is_blocked(SIGPIPE); diff --git a/polyp/core.h b/polyp/core.h index 980888f62..62959d0af 100644 --- a/polyp/core.h +++ b/polyp/core.h @@ -53,6 +53,8 @@ struct pa_core { struct pa_time_event *quit_event; struct pa_time_event *scache_auto_unload_event; + + int resample_method; }; struct pa_core* pa_core_new(struct pa_mainloop_api *m); diff --git a/polyp/daemon.conf b/polyp/daemon.conf new file mode 100644 index 000000000..a277bb4fd --- /dev/null +++ b/polyp/daemon.conf @@ -0,0 +1,67 @@ +# $Id$ +# +# This file is part of polypaudio. +# +# polypaudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# polypaudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with polypaudio; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA. + +## Configuration file for the polypaudio daemon. Default values are +## commented out. Use either ; or # for commenting + +# Extra verbositiy +; verbose = 0 + +## Daemonize after startup +; daemonize = 0 + +## Quit if startup fails +; fail = 1 + +## Renice the daemon to level -15 and try to get SCHED_FIFO +## scheduling. This a good idea if you hear annyoing noise in the +## playback. However, this is a certain security issue, since it works +## when called SUID root only. root is dropped immediately after gaining +## the nice level and SCHED_FIFO scheduling on startup. +high-priority = 0 + +## Disallow module loading after startup +; disallow-module-loading = 0 + +## Terminate the daemon after the last client quit and this time +## passed. Use a negative value to disable this feature. +; exit-idle-time = -1 + +## Unload autoloaded modules after being idle for this time +module-idle-time = 20 + +## The path were to look for dynamic shared objects (DSOs aka +## plugins). You may specify more than one path seperated by +## colons. +; dl-search-path = @DLSEARCHPATH@ + +## The default script file to load. Specify an empty string for not +## loading a default script file. The +; default-script-file = @DEFAULT_CONFIG_FILE@ + +## The default log target. Use either "stderr", "syslog" or +## "auto". The latter is equivalent to "sylog" in case daemonize is +## true, otherwise to "stderr". +; log-target = auto + +## The resampling algorithm to use. Use one of sinc-best-quality, +## sinc-medium-quality, sinc-fastest, zero-order-hold, linear. See +## the documentation of libsamplerate for an explanation fot the +## different methods. +; resample-method = sinc-fastest diff --git a/polyp/main.c b/polyp/main.c index e44fc0134..3b25a030e 100644 --- a/polyp/main.c +++ b/polyp/main.c @@ -119,10 +119,6 @@ int main(int argc, char *argv[]) { if (conf->dl_search_path) lt_dlsetsearchpath(conf->dl_search_path); -#ifdef DLSEARCHPATH - else - lt_dlsetsearchpath(DLSEARCHPATH); -#endif switch (conf->cmd) { case PA_CMD_DUMP_MODULES: @@ -245,6 +241,8 @@ int main(int argc, char *argv[]) { c->disallow_module_loading = conf->disallow_module_loading; c->exit_idle_time = conf->exit_idle_time; c->module_idle_time = conf->module_idle_time; + c->scache_idle_time = conf->scache_idle_time; + c->resample_method = conf->resample_method; pa_log(__FILE__": Daemon startup complete.\n"); if (pa_mainloop_run(mainloop, &retval) < 0) diff --git a/polyp/polyplib-context.c b/polyp/polyplib-context.c index 64f9074c7..04ee3d897 100644 --- a/polyp/polyplib-context.c +++ b/polyp/polyplib-context.c @@ -47,6 +47,9 @@ #include "util.h" #include "xmalloc.h" #include "log.h" +#include "config-client.h" + +#define DEFAULT_SERVER "/tmp/polypaudio/native" static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = { [PA_COMMAND_REQUEST] = { pa_command_request }, @@ -87,6 +90,11 @@ struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char * c->local = -1; pa_check_signal_is_blocked(SIGPIPE); + + c->conf = pa_client_conf_new(); + pa_client_conf_load(c->conf, NULL); + pa_client_conf_env(c->conf); + return c; } @@ -114,6 +122,9 @@ static void context_free(struct pa_context *c) { pa_dynarray_free(c->playback_streams, NULL, NULL); pa_memblock_stat_unref(c->memblock_stat); + + if (c->conf) + pa_client_conf_free(c->conf); pa_xfree(c->name); pa_xfree(c); @@ -366,7 +377,7 @@ static struct sockaddr *resolve_server(const char *server, size_t *len) { return sa; } -static int is_running(void) { +static int default_server_is_running(void) { struct stat st; if (DEFAULT_SERVER[0] != '/') @@ -404,24 +415,39 @@ static int context_connect_spawn(struct pa_context *c, const struct pa_spawn_api goto fail; } else if (!pid) { - char t[128]; - char *p; /* Child */ + + char t[128]; + const char *state = NULL; +#define MAX_ARGS 64 + char *argv[MAX_ARGS+1]; + int n = 0; close(fds[0]); if (api && api->atfork) api->atfork(); - if (!(p = getenv(ENV_DEFAULT_BINARY))) - p = POLYPAUDIO_BINARY; - snprintf(t, sizeof(t), "%s=1", ENV_AUTOSPAWNED); - putenv(t); - + putenv(t); + + argv[n++] = c->conf->daemon_binary; + snprintf(t, sizeof(t), "-Lmodule-native-protocol-fd fd=%i", fds[1]); - execl(p, p, "--daemonize=yes", "--log-target=syslog", t, NULL); - + argv[n++] = pa_xstrdup(t); + + while (n < MAX_ARGS) { + char *a; + + if (!(a = pa_split_spaces(c->conf->extra_arguments, &state))) + break; + + argv[n++] = a; + } + + argv[n++] = NULL; + + execv(argv[0], argv); exit(1); } @@ -468,19 +494,12 @@ int pa_context_connect(struct pa_context *c, const char *server, int spawn, cons assert(c && c->ref >= 1 && c->state == PA_CONTEXT_UNCONNECTED); if (!server) - if (!(server = getenv(ENV_DEFAULT_SERVER))) { - if (spawn && !is_running()) { - char *b; - - if ((b = getenv(ENV_DISABLE_AUTOSPAWN))) - if (pa_parse_boolean(b) > 1) - return -1; - - return context_connect_spawn(c, api); - } + server = c->conf->default_server; - server = DEFAULT_SERVER; - } + if (!server && spawn && c->conf->autospawn && !default_server_is_running()) + return context_connect_spawn(c, api); + + server = DEFAULT_SERVER; pa_context_ref(c); diff --git a/polyp/polyplib-internal.h b/polyp/polyplib-internal.h index 4c44ee98d..1d0e41d8a 100644 --- a/polyp/polyplib-internal.h +++ b/polyp/polyplib-internal.h @@ -33,6 +33,7 @@ #include "polyplib-operation.h" #include "llist.h" #include "native-common.h" +#include "config-client.h" #define DEFAULT_TLENGTH (10240*8) #define DEFAULT_MAXLENGTH ((DEFAULT_TLENGTH*3)/2) @@ -41,14 +42,8 @@ #define DEFAULT_FRAGSIZE 1024 #define DEFAULT_TIMEOUT (5*60) -#define DEFAULT_SERVER "/tmp/polypaudio/native" #define DEFAULT_PORT "4713" -#define ENV_DEFAULT_SINK "POLYP_SINK" -#define ENV_DEFAULT_SOURCE "POLYP_SOURCE" -#define ENV_DEFAULT_SERVER "POLYP_SERVER" -#define ENV_DEFAULT_BINARY "POLYP_BINARY" -#define ENV_DISABLE_AUTOSPAWN "POLYP_NOAUTOSPAWN" #define ENV_AUTOSPAWNED "POLYP_AUTOSPAWNED" struct pa_context { @@ -80,6 +75,8 @@ struct pa_context { struct pa_memblock_stat *memblock_stat; int local; + + struct pa_client_conf *conf; }; struct pa_stream { diff --git a/polyp/polyplib-scache.c b/polyp/polyplib-scache.c index 8215eaec1..7221420c8 100644 --- a/polyp/polyplib-scache.c +++ b/polyp/polyplib-scache.c @@ -88,7 +88,7 @@ struct pa_operation * pa_context_play_sample(struct pa_context *c, const char *n o->userdata = userdata; if (!dev) - dev = getenv(ENV_DEFAULT_SINK); + dev = c->conf->default_sink; t = pa_tagstruct_new(NULL, 0); assert(t); diff --git a/polyp/polyplib-stream.c b/polyp/polyplib-stream.c index 532d1700f..6055a2204 100644 --- a/polyp/polyplib-stream.c +++ b/polyp/polyplib-stream.c @@ -217,7 +217,7 @@ static void create_stream(struct pa_stream *s, const char *dev, const struct pa_ assert(s && s->ref >= 1 && s->state == PA_STREAM_DISCONNECTED); pa_stream_ref(s); - + if (attr) s->buffer_attr = *attr; else { @@ -235,9 +235,9 @@ static void create_stream(struct pa_stream *s, const char *dev, const struct pa_ if (!dev) { if (s->direction == PA_STREAM_PLAYBACK) - dev = getenv(ENV_DEFAULT_SINK); + dev = s->context->conf->default_sink; else - dev = getenv(ENV_DEFAULT_SOURCE); + dev = s->context->conf->default_source; } pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_STREAM); diff --git a/polyp/resampler.c b/polyp/resampler.c index 4742ce212..e8dd01b9f 100644 --- a/polyp/resampler.c +++ b/polyp/resampler.c @@ -47,7 +47,7 @@ struct pa_resampler { struct pa_memblock_stat *memblock_stat; }; -struct pa_resampler* pa_resampler_new(const struct pa_sample_spec *a, const struct pa_sample_spec *b, struct pa_memblock_stat *s) { +struct pa_resampler* pa_resampler_new(const struct pa_sample_spec *a, const struct pa_sample_spec *b, struct pa_memblock_stat *s, int resample_method) { struct pa_resampler *r = NULL; int err; assert(a && b && pa_sample_spec_valid(a) && pa_sample_spec_valid(b)); @@ -67,7 +67,7 @@ struct pa_resampler* pa_resampler_new(const struct pa_sample_spec *a, const stru r->i_buf = r->o_buf = NULL; r->i_alloc = r->o_alloc = 0; - r->src_state = src_new(SRC_SINC_FASTEST, r->channels, &err); + r->src_state = src_new(resample_method, r->channels, &err); if (err != 0 || !r->src_state) goto fail; diff --git a/polyp/resampler.h b/polyp/resampler.h index a6ef30dff..b984a4f6d 100644 --- a/polyp/resampler.h +++ b/polyp/resampler.h @@ -28,7 +28,7 @@ struct pa_resampler; -struct pa_resampler* pa_resampler_new(const struct pa_sample_spec *a, const struct pa_sample_spec *b, struct pa_memblock_stat *s); +struct pa_resampler* pa_resampler_new(const struct pa_sample_spec *a, const struct pa_sample_spec *b, struct pa_memblock_stat *s, int resample_method); void pa_resampler_free(struct pa_resampler *r); size_t pa_resampler_request(struct pa_resampler *r, size_t out_length); diff --git a/polyp/sink-input.c b/polyp/sink-input.c index 2541c8217..486a20443 100644 --- a/polyp/sink-input.c +++ b/polyp/sink-input.c @@ -49,7 +49,7 @@ struct pa_sink_input* pa_sink_input_new(struct pa_sink *s, const char *name, con } if (variable_rate || !pa_sample_spec_equal(spec, &s->sample_spec)) - if (!(resampler = pa_resampler_new(spec, &s->sample_spec, s->core->memblock_stat))) + if (!(resampler = pa_resampler_new(spec, &s->sample_spec, s->core->memblock_stat, s->core->resample_method))) return NULL; i = pa_xmalloc(sizeof(struct pa_sink_input)); diff --git a/polyp/source-output.c b/polyp/source-output.c index 1db88d3cc..252c155cf 100644 --- a/polyp/source-output.c +++ b/polyp/source-output.c @@ -45,7 +45,7 @@ struct pa_source_output* pa_source_output_new(struct pa_source *s, const char *n } if (!pa_sample_spec_equal(&s->sample_spec, spec)) - if (!(resampler = pa_resampler_new(&s->sample_spec, spec, s->core->memblock_stat))) + if (!(resampler = pa_resampler_new(&s->sample_spec, spec, s->core->memblock_stat, s->core->resample_method))) return NULL; o = pa_xmalloc(sizeof(struct pa_source_output)); diff --git a/polyp/util.c b/polyp/util.c index 24773a7bc..86f18f258 100644 --- a/polyp/util.c +++ b/polyp/util.c @@ -390,6 +390,23 @@ char *pa_split(const char *c, const char *delimiter, const char**state) { return pa_xstrndup(current, l); } +#define WHITESPACE " \t\n" + +char *pa_split_spaces(const char *c, const char **state) { + const char *current = *state ? *state : c; + size_t l; + + if (*current) + return NULL; + + current += strspn(current, WHITESPACE); + l = strcspn(current, WHITESPACE); + + *state = current+l; + + return pa_xstrndup(current, l); +} + const char *pa_strsignal(int sig) { switch(sig) { case SIGINT: return "SIGINT"; diff --git a/polyp/util.h b/polyp/util.h index f5cda2006..07072df6f 100644 --- a/polyp/util.h +++ b/polyp/util.h @@ -60,6 +60,7 @@ int pa_fd_set_cloexec(int fd, int b); int pa_parse_boolean(const char *s); char *pa_split(const char *c, const char*delimiters, const char **state); +char *pa_split_spaces(const char *c, const char **state); const char *pa_strsignal(int sig); -- cgit v1.2.3