diff options
Diffstat (limited to 'polyp')
-rw-r--r-- | polyp/Makefile.am | 27 | ||||
-rw-r--r-- | polyp/cmdline.c | 260 | ||||
-rw-r--r-- | polyp/cmdline.h | 21 | ||||
-rw-r--r-- | polyp/conf.c | 285 | ||||
-rw-r--r-- | polyp/conf.h | 51 | ||||
-rw-r--r-- | polyp/config | 61 | ||||
-rw-r--r-- | polyp/core.c | 9 | ||||
-rw-r--r-- | polyp/core.h | 3 | ||||
-rwxr-xr-x | polyp/default.pa (renamed from polyp/polypaudio.pa) | 0 | ||||
-rw-r--r-- | polyp/dumpmodules.c (renamed from polyp/pamodinfo.c) | 45 | ||||
-rw-r--r-- | polyp/dumpmodules.h | 29 | ||||
-rw-r--r-- | polyp/main.c | 85 | ||||
-rw-r--r-- | polyp/modargs.c | 9 | ||||
-rw-r--r-- | polyp/module.c | 4 | ||||
-rw-r--r-- | polyp/pacat.c | 5 | ||||
-rw-r--r-- | polyp/polyplib-def.h | 11 | ||||
-rw-r--r-- | polyp/polyplib-stream.c | 10 | ||||
-rw-r--r-- | polyp/socket-util.c | 24 | ||||
-rw-r--r-- | polyp/util.c | 26 | ||||
-rw-r--r-- | polyp/util.h | 3 |
20 files changed, 740 insertions, 228 deletions
diff --git a/polyp/Makefile.am b/polyp/Makefile.am index 99c89491..e9e1c295 100644 --- a/polyp/Makefile.am +++ b/polyp/Makefile.am @@ -24,23 +24,24 @@ modlibdir=$(libdir)/polypaudio-@PA_MAJORMINOR@ AM_CFLAGS=-D_GNU_SOURCE -I$(top_srcdir) $(PTHREAD_CFLAGS) AM_CFLAGS+=-DDLSEARCHPATH=\"$(modlibdir)\" -AM_CFLAGS+=-DDEFAULT_CONFIG_FILE=\"$(polypconfdir)/polypaudio.pa\" +AM_CFLAGS+=-DDEFAULT_SCRIPT_FILE=\"$(polypconfdir)/default.pa\" +AM_CFLAGS+=-DDEFAULT_CONFIG_FILE=\"$(polypconfdir)/config\" AM_CFLAGS+=-DPOLYPAUDIO_BINARY=\"$(bindir)/polypaudio\" AM_LDADD=$(PTHREAD_LIBS) -lm AM_LIBADD=$(PTHREAD_LIBS) -lm -EXTRA_DIST = polypaudio.pa depmod.py esdcompat.sh.in -bin_PROGRAMS = polypaudio pacat pactl pamodinfo +EXTRA_DIST = default.pa config depmod.py esdcompat.sh.in +bin_PROGRAMS = polypaudio pacat pactl bin_SCRIPTS = esdcompat.sh noinst_PROGRAMS = \ mainloop-test \ pacat-simple \ parec-simple \ cpulimit-test \ - cpulimit-test2 + cpulimit-test2 -polypconf_DATA=polypaudio.pa +polypconf_DATA=default.pa config BUILT_SOURCES=polyplib-version.h @@ -153,19 +154,15 @@ polypaudio_SOURCES = idxset.c idxset.h \ cpulimit.c cpulimit.h \ log.c log.h \ gcc-printf.h \ - modinfo.c modinfo.h + modinfo.c modinfo.h \ + conf.c conf.h \ + dumpmodules.c dumpmodules.h polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) polypaudio_INCLUDES = $(INCLTDL) -polypaudio_LDADD = $(AM_LDADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) +polypaudio_LDADD = $(AM_LDADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(LEXLIB) polypaudio_LDFLAGS=-export-dynamic -pamodinfo_SOURCES = log.c log.h pamodinfo.c pamodinfo.h modinfo.c modinfo.h util.c util.h xmalloc.c xmalloc.h -pamodinfo_CFLAGS = $(AM_CFLAGS) -pamodinfo_INCLUDES = $(INCLTDL) -pamodinfo_LDADD = $(AM_LDADD) $(LIBLTDL) -pamodinfo_LDFLAGS=-export-dynamic - libprotocol_simple_la_SOURCES = protocol-simple.c protocol-simple.h libprotocol_simple_la_LDFLAGS = -avoid-version libprotocol_simple_la_LIBADD = $(AM_LIBADD) libsocket-server.la libiochannel.la @@ -535,3 +532,7 @@ esdcompat.sh: esdcompat.sh.in Makefile sed -e 's,@PACKAGE_VERSION\@,$(PACKAGE_VERSION),g' \ -e 's,@PACKAGE_NAME\@,$(PACKAGE_NAME),g' \ -e 's,@POLYPAUDIO_BINARY\@,$(bindir)/polypaudio,g' < $< > $@ + +install-exec-hook: + chown root:root $(DESTDIR)$(bindir)/polypaudio + chmod u+s $(DESTDIR)$(bindir)/polypaudio diff --git a/polyp/cmdline.c b/polyp/cmdline.c index b4d58f1f..4e7cde48 100644 --- a/polyp/cmdline.c +++ b/polyp/cmdline.c @@ -35,29 +35,48 @@ #include "strbuf.h" #include "xmalloc.h" -#define ENV_CONFIG_FILE "POLYP_CONFIG" +enum { + ARG_HELP = 256, + ARG_VERSION, + ARG_DUMP_CONF, + ARG_DUMP_MODULES, + ARG_DAEMONIZE, + ARG_FAIL, + ARG_VERBOSE, + ARG_HIGH_PRIORITY, + ARG_STAY_ROOT, + ARG_DISALLOW_MODULE_LOADING, + ARG_EXIT_IDLE_TIME, + ARG_MODULE_IDLE_TIME, + ARG_LOG_TARGET, + ARG_LOAD, + ARG_FILE, + ARG_DL_SEARCH_PATH, +}; + +static struct option long_options[] = { + {"help", 0, 0, ARG_HELP}, + {"version", 0, 0, ARG_VERSION}, + {"dump-conf", 0, 0, ARG_DUMP_CONF}, + {"dump-modules", 0, 0, ARG_DUMP_MODULES}, + {"daemonize", 2, 0, ARG_DAEMONIZE}, + {"fail", 2, 0, ARG_FAIL}, + {"verbose", 2, 0, ARG_VERBOSE}, + {"high-priority", 2, 0, ARG_HIGH_PRIORITY}, + {"stay-root", 2, 0, ARG_STAY_ROOT}, + {"disallow-module-loading", 2, 0, ARG_DISALLOW_MODULE_LOADING}, + {"exit-idle-time", 2, 0, ARG_EXIT_IDLE_TIME}, + {"module-idle-time", 2, 0, ARG_MODULE_IDLE_TIME}, + {"log-target", 1, 0, ARG_LOG_TARGET}, + {"load", 1, 0, ARG_LOAD}, + {"file", 1, 0, ARG_FILE}, + {"dl-search-path", 1, 0, ARG_DL_SEARCH_PATH}, + {NULL, 0, 0, 0} +}; -char* config_file(void) { - char *p, *h; - - if ((p = getenv(ENV_CONFIG_FILE))) - return pa_xstrdup(p); - - if ((h = getenv("HOME"))) { - struct stat st; - p = pa_sprintf_malloc("%s/.polypaudio", h); - if (stat(p, &st) >= 0) - return p; - - pa_xfree(p); - } - - return pa_xstrdup(DEFAULT_CONFIG_FILE); -} void pa_cmdline_help(const char *argv0) { const char *e; - char *cfg = config_file(); if ((e = strrchr(argv0, '/'))) e++; @@ -65,133 +84,170 @@ void pa_cmdline_help(const char *argv0) { e = argv0; printf("%s [options]\n" - " -r Try to set high process priority (only available as root)\n" - " -R Don't drop root if SETUID root\n" - " -L MODULE Load the specified plugin module with the specified argument\n" - " -F FILE Run the specified script\n" - " -C Open a command line on the running TTY\n" - " -n Don't load configuration file (%s)\n" - " -D Daemonize after loading the modules\n" - " -d Disallow module loading after startup\n" - " -f Dont quit when the startup fails\n" - " -v Verbose startup\n" - " -X SECS Terminate the daemon after the last client quit and this time passed\n" - " -h Show this help\n" - " -l TARGET Specify the log target (syslog, stderr, auto)\n" - " -p DIR Append a directory to the search path for dynamic modules\n" - " -V Show version\n", e, cfg); - - pa_xfree(cfg); + " -h, --help Show this help\n" + " --version Show version\n" + " --dump-conf Dump default configuration\n" + " --dump-modules Dump list of available modules\n\n" + + " -D, --daemonize[=BOOL] Daemonize after startup\n" + " --fail[=BOOL] Quit when startup fails\n" + " --verbose[=BOOL] Be slightly more verbose\n" + " --high-priority[=BOOL] Try to set high process priority (only available as root)\n" + " --stay-root[=BOOL] Don't drop root if SETUID root\n" + " --disallow-module-loading[=BOOL] Disallow module loading after startup\n" + " --exit-idle-time=SECS Terminate the daemon when idle and this time passed\n" + " --module-idle-time=SECS Unload autoloaded modules when idle and this time passed\n" + " --log-target={auto,syslog,stderr} Specify the log target\n" + " -p, --dl-search-path=PATH Set the search path for dynamic shared objects (plugins)\n\n" + + " -L, --load=\"MODULE ARGUMENTS\" Load the specified plugin module with the specified argument\n" + " -F, --file=FILENAME Run the specified script\n" + " -C Open a command line on the running TTY after startup\n\n" + + " -n Don't load default script file\n", e); } -struct pa_cmdline* pa_cmdline_parse(int argc, char * const argv []) { - char c, *cfg; - struct pa_cmdline *cmdline = NULL; +int pa_cmdline_parse(struct pa_conf *conf, int argc, char *const argv [], int *d) { struct pa_strbuf *buf = NULL; - int no_default_config_file = 0; - assert(argc && argv); - - cmdline = pa_xmalloc(sizeof(struct pa_cmdline)); - cmdline->daemonize = - cmdline->help = - cmdline->verbose = - cmdline->high_priority = - cmdline->stay_root = - cmdline->version = - cmdline->disallow_module_loading = 0; - cmdline->fail = cmdline->auto_log_target = 1; - cmdline->quit_after_last_client_time = -1; - cmdline->log_target = -1; - cmdline->dl_search_path = NULL; + int c; + assert(conf && argc && argv); buf = pa_strbuf_new(); assert(buf); + + if (conf->script_commands) + pa_strbuf_puts(buf, conf->script_commands); - while ((c = getopt(argc, argv, "L:F:CDhfvrRVndX:l:p:")) != -1) { + while ((c = getopt_long(argc, argv, "L:F:ChDnp:", long_options, NULL)) != -1) { switch (c) { + case ARG_HELP: + case 'h': + conf->help = 1; + break; + + case ARG_VERSION: + conf->version = 1; + break; + + case ARG_DUMP_CONF: + conf->dump_conf = 1; + break; + + case ARG_DUMP_MODULES: + conf->dump_modules = 1; + break; + + case ARG_LOAD: case 'L': pa_strbuf_printf(buf, "load %s\n", optarg); break; + + case ARG_FILE: case 'F': pa_strbuf_printf(buf, ".include %s\n", optarg); break; + case 'C': pa_strbuf_puts(buf, "load module-cli\n"); break; + + case ARG_DAEMONIZE: case 'D': - cmdline->daemonize = 1; + if ((conf->daemonize = optarg ? pa_parse_boolean(optarg) : 1) < 0) { + pa_log(__FILE__": --daemonize expects boolean argument\n"); + goto fail; + } break; - case 'h': - cmdline->help = 1; + + case ARG_FAIL: + if ((conf->fail = optarg ? pa_parse_boolean(optarg) : 1) < 0) { + pa_log(__FILE__": --fail expects boolean argument\n"); + goto fail; + } break; - case 'f': - cmdline->fail = 0; + + case ARG_VERBOSE: + if ((conf->verbose = optarg ? pa_parse_boolean(optarg) : 1) < 0) { + pa_log(__FILE__": --verbose expects boolean argument\n"); + goto fail; + } break; - case 'v': - cmdline->verbose = 1; + + case ARG_HIGH_PRIORITY: + if ((conf->high_priority = optarg ? pa_parse_boolean(optarg) : 1) < 0) { + pa_log(__FILE__": --high-priority expects boolean argument\n"); + goto fail; + } break; - case 'r': - cmdline->high_priority = 1; + + case ARG_STAY_ROOT: + if ((conf->stay_root = optarg ? pa_parse_boolean(optarg) : 1) < 0) { + pa_log(__FILE__": --stay-root expects boolean argument\n"); + goto fail; + } break; - case 'R': - cmdline->stay_root = 1; + + case ARG_DISALLOW_MODULE_LOADING: + if ((conf->disallow_module_loading = optarg ? pa_parse_boolean(optarg) : 1) < 0) { + pa_log(__FILE__": --disallow-module-loading expects boolean argument\n"); + goto fail; + } break; - case 'V': - cmdline->version = 1; + + case 'p': + case ARG_DL_SEARCH_PATH: + pa_xfree(conf->dl_search_path); + conf->dl_search_path = *optarg ? pa_xstrdup(optarg) : NULL; break; + case 'n': - no_default_config_file = 1; - break; - case 'd': - cmdline->disallow_module_loading = 1; - break; - case 'X': - cmdline->quit_after_last_client_time = atoi(optarg); + pa_xfree(conf->default_script_file); + conf->default_script_file = NULL; break; - case 'p': - if (cmdline->dl_search_path) - pa_xfree(cmdline->dl_search_path); - cmdline->dl_search_path = pa_xstrdup(optarg); - break; - case 'l': + + case ARG_LOG_TARGET: if (!strcmp(optarg, "syslog")) { - cmdline->auto_log_target = 0; - cmdline->log_target = PA_LOG_SYSLOG; + conf->auto_log_target = 0; + conf->log_target = PA_LOG_SYSLOG; } else if (!strcmp(optarg, "stderr")) { - cmdline->auto_log_target = 0; - cmdline->log_target = PA_LOG_STDERR; + conf->auto_log_target = 0; + conf->log_target = PA_LOG_STDERR; } else if (!strcmp(optarg, "auto")) - cmdline->auto_log_target = 1; + conf->auto_log_target = 1; else { pa_log(__FILE__": Invalid log target: use either 'syslog', 'stderr' or 'auto'.\n"); goto fail; } break; + + case ARG_EXIT_IDLE_TIME: + conf->exit_idle_time = atoi(optarg); + break; + + case ARG_MODULE_IDLE_TIME: + conf->module_idle_time = atoi(optarg); + break; + default: goto fail; } } - if (!no_default_config_file) { - cfg = config_file(); - pa_strbuf_printf(buf, ".include %s\n", cfg); - pa_xfree(cfg); + pa_xfree(conf->script_commands); + conf->script_commands = pa_strbuf_tostring_free(buf); + + if (!conf->script_commands) { + pa_xfree(conf->script_commands); + conf->script_commands = NULL; } - cmdline->cli_commands = pa_strbuf_tostring_free(buf); - return cmdline; + *d = optind; + + return 0; fail: - if (cmdline) - pa_cmdline_free(cmdline); if (buf) pa_strbuf_free(buf); - return NULL; -} - -void pa_cmdline_free(struct pa_cmdline *cmd) { - assert(cmd); - pa_xfree(cmd->cli_commands); - pa_xfree(cmd->dl_search_path); - pa_xfree(cmd); + + return -1; } diff --git a/polyp/cmdline.h b/polyp/cmdline.h index 5dfe2e0d..e4bd8af6 100644 --- a/polyp/cmdline.h +++ b/polyp/cmdline.h @@ -22,26 +22,9 @@ USA. ***/ -#include "log.h" +#include "conf.h" -struct pa_cmdline { - int daemonize, - help, - fail, - verbose, - high_priority, - stay_root, - version, - disallow_module_loading, - quit_after_last_client_time, - auto_log_target; - char *cli_commands; - char *dl_search_path; - enum pa_log_target log_target; -}; - -struct pa_cmdline* pa_cmdline_parse(int argc, char * const argv []); -void pa_cmdline_free(struct pa_cmdline *cmd); +int pa_cmdline_parse(struct pa_conf*c, int argc, char *const argv [], int *d); void pa_cmdline_help(const char *argv0); diff --git a/polyp/conf.c b/polyp/conf.c new file mode 100644 index 00000000..4a11c480 --- /dev/null +++ b/polyp/conf.c @@ -0,0 +1,285 @@ +/* $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. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <sys/stat.h> + +#include "conf.h" +#include "util.h" +#include "xmalloc.h" +#include "strbuf.h" + +static const struct pa_conf default_conf = { + .help = 0, + .daemonize = 0, + .dump_conf = 0, + .dump_modules = 0, + .fail = 1, + .verbose = 0, + .high_priority = 0, + .stay_root = 0, + .version = 0, + .disallow_module_loading = 0, + .exit_idle_time = -1, + .module_idle_time = 20, + .auto_log_target = 1, + .script_commands = NULL, + .dl_search_path = NULL, + .default_script_file = NULL, + .log_target = PA_LOG_SYSLOG, +}; + +#define ENV_SCRIPT_FILE "POLYP_SCRIPT" +#define ENV_CONFIG_FILE "POLYP_CONFIG" + +#ifndef DEFAULT_SCRIPT_FILE +#define DEFAULT_SCRIPT_FILE "/etc/polypaudio/default.pa" +#endif + +#ifndef DEFAULT_CONFIG_FILE +#define DEFAULT_CONFIG_FILE "/etc/polypaudio/config" +#endif + +#define DEFAULT_SCRIPT_FILE_LOCAL ".polypaudio.pa" +#define DEFAULT_CONFIG_FILE_LOCAL ".polypaudio.conf" + +char* default_file(const char *envvar, const char *global, const char *local) { + char *p, *h; + + assert(envvar && global && local); + + if ((p = getenv(envvar))) + return pa_xstrdup(p); + + if ((h = getenv("HOME"))) { + struct stat st; + p = pa_sprintf_malloc("%s/%s", h, local); + if (stat(p, &st) >= 0) + return p; + + pa_xfree(p); + } + + return pa_xstrdup(global); +} + + +struct pa_conf* pa_conf_new(void) { + struct pa_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf)); + c->default_script_file = default_file(ENV_SCRIPT_FILE, DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_LOCAL); + return c; +} + +void pa_conf_free(struct pa_conf *c) { + assert(c); + pa_xfree(c->script_commands); + pa_xfree(c->dl_search_path); + pa_xfree(c->default_script_file); + 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("stay-root", stay_root); + 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_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; + } + +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); + return -1; + } + + *e = 0; + e++; + + return next_assignment(conf, strip(b), strip(e), n); +} + + +int pa_conf_load(struct pa_conf *c, const char *filename) { + FILE *f; + int r = 0; + unsigned n = 0; + char *def = NULL; + assert(c); + + if (!filename) + filename = def = default_file(ENV_CONFIG_FILE, DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_LOCAL); + + if (!(f = fopen(filename, "r"))) { + if (errno != ENOENT) + 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)) + 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; + } + +finish: + + if (f) + fclose(f); + + pa_xfree(def); + + return r; +} + +char *pa_conf_dump(struct pa_conf *c) { + struct pa_strbuf *s = pa_strbuf_new(); + char *d; + + d = default_file(ENV_CONFIG_FILE, DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_LOCAL); + pa_strbuf_printf(s, "### Default configuration file: %s ###\n\n", d); + + pa_strbuf_printf(s, "verbose = %i\n", !!c->verbose); + pa_strbuf_printf(s, "daemonize = %i\n", !!c->daemonize); + pa_strbuf_printf(s, "fail = %i\n", !!c->fail); + pa_strbuf_printf(s, "high-priority = %i\n", !!c->high_priority); + pa_strbuf_printf(s, "stay-root = %i\n", !!c->stay_root); + pa_strbuf_printf(s, "disallow-module-loading = %i\n", !!c->disallow_module_loading); + pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time); + pa_strbuf_printf(s, "module-idle-time = %i\n", c->module_idle_time); + 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")); + + pa_strbuf_printf(s, "\n### EOF ###\n"); + + pa_xfree(d); + + return pa_strbuf_tostring_free(s); +} diff --git a/polyp/conf.h b/polyp/conf.h new file mode 100644 index 00000000..bbe253c2 --- /dev/null +++ b/polyp/conf.h @@ -0,0 +1,51 @@ +#ifndef fooconfhfoo +#define fooconfhfoo + +/* $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 "log.h" + +struct pa_conf { + int help, + version, + dump_conf, + dump_modules, + daemonize, + fail, + verbose, + high_priority, + stay_root, + disallow_module_loading, + exit_idle_time, + module_idle_time, + auto_log_target; + char *script_commands, *dl_search_path, *default_script_file; + enum pa_log_target log_target; +}; + +struct pa_conf* pa_conf_new(void); +void pa_conf_free(struct pa_conf*c); + +int pa_conf_load(struct pa_conf *c, const char *filename); +char *pa_conf_dump(struct pa_conf *c); + +#endif diff --git a/polyp/config b/polyp/config new file mode 100644 index 00000000..e0f8de50 --- /dev/null +++ b/polyp/config @@ -0,0 +1,61 @@ +# $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. +; high-priority = 0 + +## Don't drop root rights on startup if called SUID root. +; stay-root = 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. +; 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/core.c b/polyp/core.c index 5d79a365..0b33c107 100644 --- a/polyp/core.c +++ b/polyp/core.c @@ -61,7 +61,6 @@ struct pa_core* pa_core_new(struct pa_mainloop_api *m) { c->default_sample_spec.rate = 44100; c->default_sample_spec.channels = 2; - c->auto_unload_time = 20; c->auto_unload_event = NULL; c->subscription_defer_event = NULL; @@ -73,7 +72,9 @@ struct pa_core* pa_core_new(struct pa_mainloop_api *m) { c->disallow_module_loading = 0; c->quit_event = NULL; - c->quit_after_last_client_time = -1; + + c->exit_idle_time = -1; + c->module_idle_time = 20; pa_check_for_sigpipe(); @@ -129,10 +130,10 @@ static void quit_callback(struct pa_mainloop_api*m, struct pa_time_event *e, con void pa_core_check_quit(struct pa_core *c) { assert(c); - if (!c->quit_event && c->quit_after_last_client_time >= 0 && pa_idxset_ncontents(c->clients) == 0) { + if (!c->quit_event && c->exit_idle_time >= 0 && pa_idxset_ncontents(c->clients) == 0) { struct timeval tv; gettimeofday(&tv, NULL); - tv.tv_sec+= c->quit_after_last_client_time; + tv.tv_sec+= c->exit_idle_time; c->quit_event = c->mainloop->time_new(c->mainloop, &tv, quit_callback, c); } else if (c->quit_event && pa_idxset_ncontents(c->clients) > 0) { c->mainloop->time_free(c->quit_event); diff --git a/polyp/core.h b/polyp/core.h index ddba6a83..a85dafd4 100644 --- a/polyp/core.h +++ b/polyp/core.h @@ -38,7 +38,6 @@ struct pa_core { char *default_source_name, *default_sink_name; struct pa_sample_spec default_sample_spec; - int auto_unload_time; struct pa_time_event *auto_unload_event; struct pa_defer_event *subscription_defer_event; @@ -48,7 +47,7 @@ struct pa_core { struct pa_memblock_stat *memblock_stat; int disallow_module_loading; - int quit_after_last_client_time; + int exit_idle_time, module_idle_time; struct pa_time_event *quit_event; }; diff --git a/polyp/polypaudio.pa b/polyp/default.pa index 15434627..15434627 100755 --- a/polyp/polypaudio.pa +++ b/polyp/default.pa diff --git a/polyp/pamodinfo.c b/polyp/dumpmodules.c index 6eb147f0..9ed89692 100644 --- a/polyp/pamodinfo.c +++ b/polyp/dumpmodules.c @@ -29,12 +29,11 @@ #include <stdio.h> #include <ltdl.h> +#include "dumpmodules.h" #include "modinfo.h" #define PREFIX "module-" -static int verbose = 0; - static void short_info(const char *name, const char *path, struct pa_modinfo *i) { assert(name && i); printf("%-40s%s\n", name, i->description ? i->description : "n/a"); @@ -79,6 +78,7 @@ static void show_info(const char *name, const char *path, void (*info)(const cha static int callback(const char *path, lt_ptr data) { const char *e; + struct pa_conf *c = (data); if ((e = (const char*) strrchr(path, '/'))) e++; @@ -86,41 +86,16 @@ static int callback(const char *path, lt_ptr data) { e = path; if (strlen(e) > sizeof(PREFIX)-1 && !strncmp(e, PREFIX, sizeof(PREFIX)-1)) - show_info(e, path, verbose ? long_info : short_info); + show_info(e, path, c->verbose ? long_info : short_info); return 0; } -int main(int argc, char *argv[]) { - int r = lt_dlinit(); - char *path = NULL; - int c; - assert(r == 0); - - while ((c = getopt(argc, argv, "p:v")) != -1) { - switch (c) { - case 'p': - path = optarg; - break; - case 'v': - verbose = 1; - break; - default: - return 1; - } - } - - if (path) - lt_dlsetsearchpath(path); -#ifdef DLSEARCHPATH - else - lt_dlsetsearchpath(DLSEARCHPATH); -#endif - - if (argc > optind) - show_info(argv[optind], NULL, long_info); - else - lt_dlforeachfile(NULL, callback, NULL); - - lt_dlexit(); +void pa_dump_modules(struct pa_conf *c, int argc, char * const argv[]) { + if (argc > 0) { + int i; + for (i = 0; i < argc; i++) + show_info(argv[i], NULL, long_info); + } else + lt_dlforeachfile(NULL, callback, c); } diff --git a/polyp/dumpmodules.h b/polyp/dumpmodules.h new file mode 100644 index 00000000..6b1bd858 --- /dev/null +++ b/polyp/dumpmodules.h @@ -0,0 +1,29 @@ +#ifndef foodumpmoduleshfoo +#define foodumpmoduleshfoo + +/* $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 "conf.h" + +void pa_dump_modules(struct pa_conf *c, int argc, char * const argv[]); + +#endif diff --git a/polyp/main.c b/polyp/main.c index a2b3d4c7..148dfac2 100644 --- a/polyp/main.c +++ b/polyp/main.c @@ -45,6 +45,8 @@ #include "xmalloc.h" #include "cpulimit.h" #include "log.h" +#include "conf.h" +#include "dumpmodules.h" static struct pa_mainloop *mainloop; @@ -99,40 +101,69 @@ static void close_pipe(int p[2]) { int main(int argc, char *argv[]) { struct pa_core *c; - struct pa_cmdline *cmdline = NULL; struct pa_strbuf *buf = NULL; + struct pa_conf *conf; char *s; - int r, retval = 1; + int r, retval = 1, d = 0; int daemon_pipe[2] = { -1, -1 }; + r = lt_dlinit(); + assert(r == 0); + pa_log_set_ident("polypaudio"); - if (!(cmdline = pa_cmdline_parse(argc, argv))) { + conf = pa_conf_new(); + + if (pa_conf_load(conf, NULL) < 0) + goto finish; + + if (pa_cmdline_parse(conf, argc, argv, &d) < 0) { pa_log(__FILE__": failed to parse command line.\n"); goto finish; } - pa_log_set_target(cmdline->auto_log_target ? PA_LOG_STDERR : cmdline->log_target, NULL); + pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target, NULL); + + if (conf->dl_search_path) + lt_dlsetsearchpath(conf->dl_search_path); +#ifdef DLSEARCHPATH + else + lt_dlsetsearchpath(DLSEARCHPATH); +#endif + + if (conf->dump_modules) { + pa_dump_modules(conf, argc-d, argv+d); + retval = 0; + goto finish; + } + + if (conf->dump_conf) { + char *s = pa_conf_dump(conf); + fputs(s, stdout); + pa_xfree(s); + retval = 0; + goto finish; + } - if (cmdline->help) { + if (conf->help) { pa_cmdline_help(argv[0]); retval = 0; goto finish; } - if (cmdline->version) { + if (conf->version) { printf(PACKAGE_NAME" "PACKAGE_VERSION"\n"); retval = 0; goto finish; } - if (cmdline->high_priority) + if (conf->high_priority) pa_raise_priority(); - if (!cmdline->stay_root) + if (!conf->stay_root) drop_root(); - if (cmdline->daemonize) { + if (conf->daemonize) { pid_t child; if (pa_stdio_acquire() < 0) { @@ -168,7 +199,7 @@ int main(int argc, char *argv[]) { daemon_pipe[0] = -1; - if (cmdline->auto_log_target) + if (conf->auto_log_target) pa_log_set_target(PA_LOG_SYSLOG, NULL); setsid(); @@ -178,15 +209,6 @@ int main(int argc, char *argv[]) { close(1); } - r = lt_dlinit(); - assert(r == 0); - - if (cmdline->dl_search_path) - lt_dlsetsearchpath(cmdline->dl_search_path); -#ifdef DLSEARCHPATH - else - lt_dlsetsearchpath(DLSEARCHPATH); -#endif pa_log(__FILE__": sizeof(pa_usec_t) = %u\n", sizeof(pa_usec_t)); @@ -210,25 +232,28 @@ int main(int argc, char *argv[]) { buf = pa_strbuf_new(); assert(buf); - r = pa_cli_command_execute(c, cmdline->cli_commands, buf, &cmdline->fail, &cmdline->verbose); + if (conf->default_script_file) + pa_cli_command_execute_file(c, conf->default_script_file, buf, &conf->fail, &conf->verbose); + r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail, &conf->verbose); pa_log(s = pa_strbuf_tostring_free(buf)); pa_xfree(s); - if (r < 0 && cmdline->fail) { + if (r < 0 && conf->fail) { pa_log(__FILE__": failed to initialize daemon.\n"); - if (cmdline->daemonize) + if (conf->daemonize) pa_loop_write(daemon_pipe[1], &retval, sizeof(retval)); } else if (!c->modules || pa_idxset_ncontents(c->modules) == 0) { pa_log(__FILE__": daemon startup without any loaded modules, refusing to work.\n"); - if (cmdline->daemonize) + if (conf->daemonize) pa_loop_write(daemon_pipe[1], &retval, sizeof(retval)); } else { retval = 0; - if (cmdline->daemonize) + if (conf->daemonize) pa_loop_write(daemon_pipe[1], &retval, sizeof(retval)); - c->disallow_module_loading = cmdline->disallow_module_loading; - c->quit_after_last_client_time = cmdline->quit_after_last_client_time; + c->disallow_module_loading = conf->disallow_module_loading; + c->exit_idle_time = conf->exit_idle_time; + c->module_idle_time = conf->module_idle_time; pa_log(__FILE__": Daemon startup complete.\n"); if (pa_mainloop_run(mainloop, &retval) < 0) @@ -242,16 +267,16 @@ int main(int argc, char *argv[]) { pa_signal_done(); pa_mainloop_free(mainloop); - lt_dlexit(); - pa_log(__FILE__": Daemon terminated.\n"); finish: - if (cmdline) - pa_cmdline_free(cmdline); + if (conf) + pa_conf_free(conf); close_pipe(daemon_pipe); + lt_dlexit(); + return retval; } diff --git a/polyp/modargs.c b/polyp/modargs.c index 4874d808..e1c2c9b8 100644 --- a/polyp/modargs.c +++ b/polyp/modargs.c @@ -36,6 +36,7 @@ #include "sink.h" #include "source.h" #include "xmalloc.h" +#include "util.h" struct pa_modargs; @@ -229,6 +230,7 @@ int pa_modargs_get_value_s32(struct pa_modargs *ma, const char *key, int32_t *va int pa_modargs_get_value_boolean(struct pa_modargs *ma, const char *key, int *value) { const char *v; + int r; assert(ma && key && value); if (!(v = pa_modargs_get_value(ma, key, NULL))) @@ -237,13 +239,10 @@ int pa_modargs_get_value_boolean(struct pa_modargs *ma, const char *key, int *va if (!*v) return -1; - if (!strcmp(v, "1") || !strcasecmp(v, "yes") || !strcasecmp(v, "y") || !strcasecmp(v, "on")) - *value = 1; - else if (!strcmp(v, "0") || !strcasecmp(v, "no") || !strcasecmp(v, "n") || !strcasecmp(v, "off")) - *value = 0; - else + if ((r = pa_parse_boolean(v)) < 0) return -1; + *value = r; return 0; } diff --git a/polyp/module.c b/polyp/module.c index c66faeb8..db21f790 100644 --- a/polyp/module.c +++ b/polyp/module.c @@ -38,7 +38,7 @@ #define PA_SYMBOL_INIT "pa__init" #define PA_SYMBOL_DONE "pa__done" -#define UNLOAD_POLL_TIME 10 +#define UNLOAD_POLL_TIME 2 static void timeout_callback(struct pa_mainloop_api *m, struct pa_time_event*e, const struct timeval *tv, void *userdata) { struct pa_core *c = userdata; @@ -193,7 +193,7 @@ static int unused_callback(void *p, uint32_t index, int *del, void *userdata) { time_t *now = userdata; assert(p && del && now); - if (m->n_used == 0 && m->auto_unload && m->last_used_time+m->core->auto_unload_time <= *now) { + if (m->n_used == 0 && m->auto_unload && m->last_used_time+m->core->module_idle_time <= *now) { pa_module_free(m); *del = 1; } diff --git a/polyp/pacat.c b/polyp/pacat.c index ed95c2ca..b499f71a 100644 --- a/polyp/pacat.c +++ b/polyp/pacat.c @@ -286,7 +286,10 @@ static void stream_get_latency_callback(struct pa_stream *s, const struct pa_lat return; } - fprintf(stderr, "Current latency is %f usecs.\n", (float) (i->buffer_usec+i->sink_usec+i->transport_usec)); + fprintf(stderr, "Latency: buffer: %0.0f usec; sink: %0.0f usec; transport: %0.0f usec; total: %0.0f usec; synchronized clocks: %s.\n", + (float) i->buffer_usec, (float) i->sink_usec, (float) i->transport_usec, + (float) (i->buffer_usec+i->sink_usec+i->transport_usec), + i->synchronized_clocks ? "yes" : "no"); } /* Someone requested that the latency is shown */ diff --git a/polyp/polyplib-def.h b/polyp/polyplib-def.h index 176e1d3b..4a49a1f8 100644 --- a/polyp/polyplib-def.h +++ b/polyp/polyplib-def.h @@ -144,7 +144,16 @@ struct pa_latency_info { pa_usec_t sink_usec; /**< Time in usecs a sample takes to be played on the sink. */ pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to the daemon. \since 0.5 */ int playing; /**< Non-zero when the stream is currently playing */ - uint32_t queue_length; /**< Queue size in bytes. */ + uint32_t queue_length; /**< Queue size in bytes. */ + int synchronized_clocks; /**< Non-zero if the local and the + * remote machine have synchronized + * clocks. If synchronized clocks are + * detected transport_usec becomes much + * more reliable. However, the code that + * detects synchronized clocks is very + * limited und unreliable itself. \since + * 0.5 */ + struct timeval timestamp; /**< The time when this latency info was current */ }; PA_C_DECL_END diff --git a/polyp/polyplib-stream.c b/polyp/polyplib-stream.c index 98610d61..b40b7f69 100644 --- a/polyp/polyplib-stream.c +++ b/polyp/polyplib-stream.c @@ -347,12 +347,18 @@ static void stream_get_latency_callback(struct pa_pdispatch *pd, uint32_t comman gettimeofday(&now, NULL); - if (pa_timeval_cmp(&local, &remote) < 0 && pa_timeval_cmp(&remote, &now)) + if (pa_timeval_cmp(&local, &remote) < 0 && pa_timeval_cmp(&remote, &now)) { /* local and remote seem to have synchronized clocks */ i.transport_usec = pa_timeval_diff(&remote, &local); - else + i.synchronized_clocks = 1; + i.timestamp = remote; + } else { /* clocks are not synchronized, let's estimate latency then */ i.transport_usec = pa_timeval_diff(&now, &local)/2; + i.synchronized_clocks = 0; + i.timestamp = local; + pa_timeval_add(&i.timestamp, i.transport_usec); + } if (o->callback) { void (*cb)(struct pa_stream *s, const struct pa_latency_info *i, void *userdata) = o->callback; diff --git a/polyp/socket-util.c b/polyp/socket-util.c index 2f082bfb..20380653 100644 --- a/polyp/socket-util.c +++ b/polyp/socket-util.c @@ -101,15 +101,15 @@ int pa_socket_low_delay(int fd) { } int pa_socket_tcp_low_delay(int fd) { - int ret, tos; + int ret, tos, on; assert(fd >= 0); ret = pa_socket_low_delay(fd); -/* on = 1; */ -/* if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) */ -/* ret = -1; */ + on = 1; + if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) + ret = -1; tos = IPTOS_LOWDELAY; if (setsockopt(fd, SOL_IP, IP_TOS, &tos, sizeof(tos)) < 0) @@ -122,10 +122,10 @@ int pa_socket_tcp_low_delay(int fd) { int pa_socket_set_rcvbuf(int fd, size_t l) { assert(fd >= 0); - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &l, sizeof(l)) < 0) { - pa_log(__FILE__": SO_RCVBUF: %s\n", strerror(errno)); - return -1; - } +/* if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &l, sizeof(l)) < 0) { */ +/* pa_log(__FILE__": SO_RCVBUF: %s\n", strerror(errno)); */ +/* return -1; */ +/* } */ return 0; } @@ -133,10 +133,10 @@ int pa_socket_set_rcvbuf(int fd, size_t l) { int pa_socket_set_sndbuf(int fd, size_t l) { assert(fd >= 0); - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &l, sizeof(l)) < 0) { - pa_log(__FILE__": SO_SNDBUF: %s\n", strerror(errno)); - return -1; - } +/* if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &l, sizeof(l)) < 0) { */ +/* pa_log(__FILE__": SO_SNDBUF: %s\n", strerror(errno)); */ +/* return -1; */ +/* } */ return 0; } diff --git a/polyp/util.c b/polyp/util.c index 1dbb8697..039ec264 100644 --- a/polyp/util.c +++ b/polyp/util.c @@ -268,6 +268,22 @@ pa_usec_t pa_age(const struct timeval *tv) { return pa_timeval_diff(&now, tv); } +void pa_timeval_add(struct timeval *tv, pa_usec_t v) { + unsigned long secs; + assert(tv); + + secs = (v/1000000); + tv->tv_sec += (unsigned long) secs; + v -= secs*1000000; + + tv->tv_usec += v; + + while (tv->tv_usec >= 1000000) { + tv->tv_sec++; + tv->tv_usec -= 1000000; + } +} + #define NICE_LEVEL (-15) void pa_raise_priority(void) { @@ -347,3 +363,13 @@ char *pa_path_get_filename(const char *p) { return (char*) p; } + +int pa_parse_boolean(const char *v) { + + if (!strcmp(v, "1") || !strcasecmp(v, "yes") || !strcasecmp(v, "y") || !strcasecmp(v, "on")) + return 1; + else if (!strcmp(v, "0") || !strcasecmp(v, "no") || !strcasecmp(v, "n") || !strcasecmp(v, "off")) + return 0; + + return -1; +} diff --git a/polyp/util.h b/polyp/util.h index f34ba4c0..42e0b22b 100644 --- a/polyp/util.h +++ b/polyp/util.h @@ -50,10 +50,13 @@ char *pa_path_get_filename(const char *p); pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b); int pa_timeval_cmp(const struct timeval *a, const struct timeval *b); pa_usec_t pa_age(const struct timeval *tv); +void pa_timeval_add(struct timeval *tv, pa_usec_t v); void pa_raise_priority(void); void pa_reset_priority(void); int pa_fd_set_cloexec(int fd, int b); +int pa_parse_boolean(const char *s); + #endif |