diff options
-rw-r--r-- | doc/todo | 15 | ||||
-rw-r--r-- | polyp/Makefile.am | 17 | ||||
-rw-r--r-- | polyp/caps.c | 4 | ||||
-rw-r--r-- | polyp/cli-command.c | 21 | ||||
-rw-r--r-- | polyp/cpulimit.c | 47 | ||||
-rw-r--r-- | polyp/debug.h | 29 | ||||
-rw-r--r-- | polyp/iochannel.c | 3 | ||||
-rw-r--r-- | polyp/mcalign-test.c | 68 | ||||
-rw-r--r-- | polyp/mcalign.c | 188 | ||||
-rw-r--r-- | polyp/mcalign.h | 77 | ||||
-rw-r--r-- | polyp/memblock.c | 19 | ||||
-rw-r--r-- | polyp/memblock.h | 5 | ||||
-rw-r--r-- | polyp/memblockq.c | 1 | ||||
-rw-r--r-- | polyp/memchunk.c | 118 | ||||
-rw-r--r-- | polyp/memchunk.h | 18 | ||||
-rw-r--r-- | polyp/module-oss-mmap.c | 4 | ||||
-rw-r--r-- | polyp/module-tunnel.c | 16 | ||||
-rw-r--r-- | polyp/polyplib-context.c | 37 | ||||
-rw-r--r-- | polyp/polyplib-context.h | 3 | ||||
-rw-r--r-- | polyp/polyplib-internal.h | 4 | ||||
-rw-r--r-- | polyp/polyplib-stream.c | 12 | ||||
-rw-r--r-- | polyp/protocol-esound.c | 1 | ||||
-rw-r--r-- | polyp/protocol-native.c | 2 | ||||
-rw-r--r-- | polyp/scache.c | 62 | ||||
-rw-r--r-- | polyp/scache.h | 2 | ||||
-rw-r--r-- | polyp/sink.c | 2 | ||||
-rw-r--r-- | polyp/socket-client.c | 21 | ||||
-rw-r--r-- | polyp/socket-util.c | 33 | ||||
-rw-r--r-- | polyp/socket-util.h | 2 | ||||
-rw-r--r-- | polyp/strbuf.c | 60 |
30 files changed, 627 insertions, 264 deletions
@@ -2,27 +2,22 @@ *** 0.7 **** - per-channel volume -- add sample directory -- make mcalign merge chunks - option to use default fragment size on alsa drivers - improve module-oss-mmap latency measurement -- filter capture data in client through alignment - add radio module -- add sync API - make most buffer sizes dependant on the sample type - -- X11: support for the X11 synchronization extension -- pass meta info for hearing impaired - limit all resources -- check getaddrinfo results +- commenting - non-fp mixing - non-fp resampling -- make module-tunnel use pa_socket_client_new_string() ** later *** +- pass meta info for hearing impaired +- add sync API +- X11: support for the X11 synchronization extension - xmlrpc/http - dbus -- slp/rendezvous +- rendezvous - make alsa modules use mmap *********** diff --git a/polyp/Makefile.am b/polyp/Makefile.am index 9ea6932a..506cdc34 100644 --- a/polyp/Makefile.am +++ b/polyp/Makefile.am @@ -27,6 +27,9 @@ AM_CFLAGS+=-DDLSEARCHPATH=\"$(modlibdir)\" AM_CFLAGS+=-DDEFAULT_CONFIG_DIR=\"$(polypconfdir)\" AM_CFLAGS+=-DPOLYPAUDIO_BINARY=\"$(bindir)/polypaudio\" +# This cool debug trap works on i386/gcc only +AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")' + AM_LIBADD=$(PTHREAD_LIBS) -lm AM_LDADD=$(PTHREAD_LIBS) -lm @@ -40,7 +43,8 @@ noinst_PROGRAMS = \ cpulimit-test \ cpulimit-test2 \ voltest \ - strlist-test + strlist-test \ + mcalign-test polypconf_DATA=default.pa daemon.conf client.conf @@ -196,7 +200,6 @@ polypaudio_SOURCES = idxset.c idxset.h \ autoload.c autoload.h \ xmalloc.c xmalloc.h \ subscribe.h subscribe.c \ - debug.h \ sound-file-stream.c sound-file-stream.h \ cpulimit.c cpulimit.h \ log.c log.h \ @@ -206,7 +209,8 @@ polypaudio_SOURCES = idxset.c idxset.h \ dumpmodules.c dumpmodules.h \ conf-parser.h conf-parser.c \ caps.h caps.c \ - props.h props.c + props.h props.c \ + mcalign.c mcalign.h polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) polypaudio_INCLUDES = $(INCLTDL) @@ -441,7 +445,8 @@ libpolyp_@PA_MAJORMINOR@_la_SOURCES = polyplib.h \ client-conf.c client-conf.h \ conf-parser.c conf-parser.h \ strlist.c strlist.h \ - strbuf.c strbuf.h + strbuf.c strbuf.h \ + mcalign.c mcalign.h libpolyp_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) libpolyp_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0 @@ -495,6 +500,10 @@ strlist_test_SOURCES = strlist-test.c strlist.c strlist.h strbuf.c strbuf.h util strlist_test_CFLAGS = $(AM_CFLAGS) strlist_test_LDADD = $(AM_LDADD) +mcalign_test_SOURCES = mcalign-test.c util.c util.h xmalloc.c xmalloc.h log.c log.h mcalign.c mcalign.h memchunk.c memchunk.h memblock.c memblock.h +mcalign_test_CFLAGS = $(AM_CFLAGS) +mcalign_test_LDADD = $(AM_LDADD) + cpulimit_test_SOURCES = cpulimit-test.c cpulimit.c util.c log.c cpulimit.h util.h log.h cpulimit_test_CFLAGS = $(AM_CFLAGS) cpulimit_test_LDADD = $(AM_LDADD) libpolyp-mainloop-@PA_MAJORMINOR@.la diff --git a/polyp/caps.c b/polyp/caps.c index daf0b916..d3719164 100644 --- a/polyp/caps.c +++ b/polyp/caps.c @@ -35,6 +35,7 @@ #include "log.h" #include "caps.h" +/* Drop root rights when called SUID root */ void pa_drop_root(void) { uid_t uid = getuid(); @@ -50,6 +51,7 @@ void pa_drop_root(void) { #ifdef HAVE_SYS_CAPABILITY_H +/* Limit capabilities set to CAPSYS_NICE */ int pa_limit_caps(void) { int r = -1; cap_t caps; @@ -76,6 +78,7 @@ fail: return r; } +/* Drop all capabilities, effectively becoming a normal user */ int pa_drop_caps(void) { cap_t caps; int r = -1; @@ -100,6 +103,7 @@ fail: #else +/* NOOPs in case capabilities are not available. */ int pa_limit_caps(void) { return 0; } diff --git a/polyp/cli-command.c b/polyp/cli-command.c index 0c71260d..dec877fb 100644 --- a/polyp/cli-command.c +++ b/polyp/cli-command.c @@ -79,6 +79,7 @@ static int pa_cli_command_scache_play(struct pa_core *c, struct pa_tokenizer *t, static int pa_cli_command_scache_remove(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); static int pa_cli_command_scache_list(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); static int pa_cli_command_scache_load(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_scache_load_dir(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); static int pa_cli_command_play_file(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); static int pa_cli_command_autoload_list(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); static int pa_cli_command_autoload_add(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); @@ -112,7 +113,8 @@ static const struct command commands[] = { { "play-sample", pa_cli_command_scache_play, "Play a sample from the sample cache (args: name, sink|index)", 3}, { "remove-sample", pa_cli_command_scache_remove, "Remove a sample from the sample cache (args: name)", 2}, { "load-sample", pa_cli_command_scache_load, "Load a sound file into the sample cache (args: name, filename)", 3}, - { "load-sample-lazy", pa_cli_command_scache_load, "Lazy load a sound file into the sample cache (args: name, filename)", 3}, + { "load-sample-lazy", pa_cli_command_scache_load, "Lazily load a sound file into the sample cache (args: name, filename)", 3}, + { "load-sample-dir-lazy", pa_cli_command_scache_load_dir, "Lazily load all files in a directory into the sample cache (args: pathname)", 2}, { "play-file", pa_cli_command_play_file, "Play a sound file (args: filename, sink|index)", 3}, { "list-autoload", pa_cli_command_autoload_list, "List autoload entries", 1}, { "add-autoload-sink", pa_cli_command_autoload_add, "Add autoload entry for a sink (args: sink, module name, arguments)", 4}, @@ -545,6 +547,23 @@ static int pa_cli_command_scache_load(struct pa_core *c, struct pa_tokenizer *t, return 0; } +static int pa_cli_command_scache_load_dir(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + const char *pname; + assert(c && t && buf && fail && verbose); + + if (!(pname = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a path name.\n"); + return -1; + } + + if (pa_scache_add_directory_lazy(c, pname) < 0) { + pa_strbuf_puts(buf, "Failed to load directory.\n"); + return -1; + } + + return 0; +} + static int pa_cli_command_play_file(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { const char *fname, *sink_name; struct pa_sink *sink; diff --git a/polyp/cpulimit.c b/polyp/cpulimit.c index fcbbaf3d..78dc5e1f 100644 --- a/polyp/cpulimit.c +++ b/polyp/cpulimit.c @@ -32,30 +32,58 @@ #include "util.h" #include "log.h" + +/* This module implements a watchdog that makes sure that the current + * process doesn't consume more than 70% CPU time for 10 seconds. This + * is very useful when using SCHED_FIFO scheduling which effectively + * disables multitasking. */ + +/* Method of operation: Using SIGXCPU a signal handler is called every + * 10s process CPU time. That function checks if less than 14s system + * time have passed. In that case, it tries to contact the main event + * loop through a pipe. After two additional seconds it is checked + * whether the main event loop contact was successful. If not, the + * program is terminated forcibly. */ + /* Utilize this much CPU time at maximum */ #define CPUTIME_PERCENT 70 +/* Check every 10s */ #define CPUTIME_INTERVAL_SOFT (10) + +/* Recheck after 2s */ #define CPUTIME_INTERVAL_HARD (2) +/* Time of the last CPU load check */ static time_t last_time = 0; + +/* Pipe for communicating with the main loop */ static int the_pipe[2] = {-1, -1}; + +/* Main event loop and IO event for the FIFO */ static struct pa_mainloop_api *api = NULL; static struct pa_io_event *io_event = NULL; + +/* Saved sigaction struct for SIGXCPU */ static struct sigaction sigaction_prev; -static int installed = 0; +/* Nonzero after pa_cpu_limit_init() */ +static int installed = 0; + +/* The current state of operation */ static enum { - PHASE_IDLE, - PHASE_SOFT + PHASE_IDLE, /* Normal state */ + PHASE_SOFT /* After CPU overload has been detected */ } phase = PHASE_IDLE; +/* Reset the SIGXCPU timer to the next t seconds */ static void reset_cpu_time(int t) { int r; long n; struct rlimit rl; struct rusage ru; + /* Get the current CPU time of the current process */ r = getrusage(RUSAGE_SELF, &ru); assert(r >= 0); @@ -69,10 +97,12 @@ static void reset_cpu_time(int t) { assert(r >= 0); } +/* A simple, thread-safe puts() work-alike */ static void write_err(const char *p) { pa_loop_write(2, p, strlen(p)); } +/* The signal handler, called on every SIGXCPU */ static void signal_handler(int sig) { assert(sig == SIGXCPU); @@ -109,23 +139,26 @@ static void signal_handler(int sig) { } else if (phase == PHASE_SOFT) { write_err("Hard CPU time limit exhausted, terminating forcibly.\n"); - _exit(1); + _exit(1); /* Forced exit */ } } +/* Callback for IO events on the FIFO */ static void callback(struct pa_mainloop_api*m, struct pa_io_event*e, int fd, enum pa_io_event_flags f, void *userdata) { char c; assert(m && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == the_pipe[0]); read(the_pipe[0], &c, sizeof(c)); - m->quit(m, 1); + m->quit(m, 1); /* Quit the main loop */ } +/* Initializes CPU load limiter */ int pa_cpu_limit_init(struct pa_mainloop_api *m) { struct sigaction sa; - assert(m && !api && !io_event && the_pipe[0] == -1 && the_pipe[1] == -1); + assert(m && !api && !io_event && the_pipe[0] == -1 && the_pipe[1] == -1 && !installed); time(&last_time); + /* Prepare the main loop pipe */ if (pipe(the_pipe) < 0) { pa_log(__FILE__": pipe() failed: %s\n", strerror(errno)); return -1; @@ -141,6 +174,7 @@ int pa_cpu_limit_init(struct pa_mainloop_api *m) { phase = PHASE_IDLE; + /* Install signal handler for SIGXCPU */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = signal_handler; sigemptyset(&sa.sa_mask); @@ -158,6 +192,7 @@ int pa_cpu_limit_init(struct pa_mainloop_api *m) { return 0; } +/* Shutdown CPU load limiter */ void pa_cpu_limit_done(void) { int r; diff --git a/polyp/debug.h b/polyp/debug.h deleted file mode 100644 index fcfa7142..00000000 --- a/polyp/debug.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef foodebughfoo -#define foodebughfoo - -/* $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 Lesser 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 Lesser 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. -***/ - -/* A nice trick for debuggers, working on x86 with GCC only */ - -#define DEBUG_TRAP __asm__("int $3") - -#endif diff --git a/polyp/iochannel.c b/polyp/iochannel.c index b93860a5..f174afd0 100644 --- a/polyp/iochannel.c +++ b/polyp/iochannel.c @@ -82,6 +82,9 @@ static void callback(struct pa_mainloop_api* m, struct pa_io_event *e, int fd, e if (e == io->input_event) { io->mainloop->io_free(io->input_event); io->input_event = NULL; + + if (io->output_event == e) + io->output_event = NULL; } if (e == io->output_event) { diff --git a/polyp/mcalign-test.c b/polyp/mcalign-test.c new file mode 100644 index 00000000..ab1f9aed --- /dev/null +++ b/polyp/mcalign-test.c @@ -0,0 +1,68 @@ +#include <assert.h> +#include <unistd.h> +#include <sys/types.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> + +#include "util.h" +#include "mcalign.h" + +int main(int argc, char *argv[]) { + struct pa_mcalign *a = pa_mcalign_new(11, NULL); + struct pa_memchunk c; + + pa_memchunk_reset(&c); + + srand(time(NULL)); + + for (;;) { + ssize_t r; + size_t l; + + if (!c.memblock) { + c.memblock = pa_memblock_new(2048, NULL); + c.index = c.length = 0; + } + + assert(c.index < c.memblock->length); + + l = c.memblock->length - c.index; + + l = l <= 1 ? l : rand() % (l-1) +1 ; + + if ((r = read(STDIN_FILENO, (uint8_t*) c.memblock->data + c.index, l)) <= 0) { + fprintf(stderr, "read() failed: %s\n", r < 0 ? strerror(errno) : "EOF"); + break; + } + + c.length = r; + pa_mcalign_push(a, &c); + fprintf(stderr, "Read %u bytes\n", r); + + c.index += r; + + if (c.index >= c.memblock->length) { + pa_memblock_unref(c.memblock); + pa_memchunk_reset(&c); + } + + for (;;) { + struct pa_memchunk t; + + if (pa_mcalign_pop(a, &t) < 0) + break; + + pa_loop_write(STDOUT_FILENO, (uint8_t*) t.memblock->data + t.index, t.length); + fprintf(stderr, "Wrote %u bytes.\n", t.length); + + pa_memblock_unref(t.memblock); + } + } + + pa_mcalign_free(a); + + if (c.memblock) + pa_memblock_unref(c.memblock); +} diff --git a/polyp/mcalign.c b/polyp/mcalign.c new file mode 100644 index 00000000..0b7f0db0 --- /dev/null +++ b/polyp/mcalign.c @@ -0,0 +1,188 @@ +/* $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 Lesser General Public License as + published by the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <string.h> + +#include "mcalign.h" +#include "xmalloc.h" + +struct pa_mcalign { + size_t base; + struct pa_memchunk leftover, current; + struct pa_memblock_stat *memblock_stat; +}; + +struct pa_mcalign *pa_mcalign_new(size_t base, struct pa_memblock_stat *s) { + struct pa_mcalign *m; + assert(base); + + m = pa_xmalloc(sizeof(struct pa_mcalign)); + m->base = base; + pa_memchunk_reset(&m->leftover); + pa_memchunk_reset(&m->current); + m->memblock_stat = s; + + return m; +} + +void pa_mcalign_free(struct pa_mcalign *m) { + assert(m); + + if (m->leftover.memblock) + pa_memblock_unref(m->leftover.memblock); + + if (m->current.memblock) + pa_memblock_unref(m->current.memblock); + + pa_xfree(m); +} + +void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c) { + assert(m && c && c->memblock && c->length); + + /* Append to the leftover memory block */ + if (m->leftover.memblock) { + assert(!m->current.memblock); + + /* Try to merge */ + if (m->leftover.memblock == c->memblock && + m->leftover.index + m->leftover.length == c->index) { + + /* Merge */ + m->leftover.length += c->length; + + /* If the new chunk is larger than m->base, move it to current */ + if (m->leftover.length >= m->base) { + m->current = m->leftover; + pa_memchunk_reset(&m->leftover); + } + + } else { + size_t l; + + /* We have to copy */ + assert(m->leftover.length < m->base); + l = m->base - m->leftover.length; + + if (l > c->length) + l = c->length; + + /* Can we use the current block? */ + pa_memchunk_make_writable(&m->leftover, m->memblock_stat, m->base); + + memcpy((uint8_t*) m->leftover.memblock->data + m->leftover.index + m->leftover.length, (uint8_t*) c->memblock->data + c->index, l); + m->leftover.length += l; + + assert(m->leftover.length <= m->base && m->leftover.length <= m->leftover.memblock->length); + + if (c->length > l) { + /* Save the remainder of the memory block */ + m->current = *c; + m->current.index += l; + m->current.length -= l; + pa_memblock_ref(m->current.memblock); + } + } + } else { + assert(!m->leftover.memblock && !m->current.memblock); + + /* Nothing to merge or copy, just store it */ + + if (c->length >= m->base) + m->current = *c; + else + m->leftover = *c; + + pa_memblock_ref(c->memblock); + } +} + +int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c) { + assert(m && c); + + /* First test if there's a leftover memory block available */ + if (m->leftover.memblock) { + assert(m->leftover.length > 0 && m->leftover.length <= m->base); + + /* The leftover memory block is not yet complete */ + if (m->leftover.length < m->base) + return -1; + + /* Return the leftover memory block */ + *c = m->leftover; + pa_memchunk_reset(&m->leftover); + + /* If the current memblock is too small move it the leftover */ + if (m->current.memblock && m->current.length < m->base) { + m->leftover = m->current; + pa_memchunk_reset(&m->current); + } + + return 0; + } + + /* Now let's see if there is other data available */ + if (m->current.memblock) { + size_t l; + assert(m->current.length >= m->base); + + /* The length of the returned memory block */ + l = m->current.length; + l /= m->base; + l *= m->base; + assert(l > 0); + + /* Prepare the returned block */ + *c = m->current; + pa_memblock_ref(c->memblock); + c->length = l; + + /* Drop that from the current memory block */ + assert(l <= m->current.length); + m->current.index += l; + m->current.length -= l; + + /* In case the whole block was dropped ... */ + if (m->current.length == 0) + pa_memblock_unref(m->current.memblock); + else { + /* Move the raimainder to leftover */ + assert(m->current.length < m->base && !m->leftover.memblock); + + m->leftover = m->current; + } + + pa_memchunk_reset(&m->current); + + return 0; + } + + /* There's simply nothing */ + return -1; + +} diff --git a/polyp/mcalign.h b/polyp/mcalign.h new file mode 100644 index 00000000..925f438a --- /dev/null +++ b/polyp/mcalign.h @@ -0,0 +1,77 @@ +#ifndef foomcalignhfoo +#define foomcalignhfoo + +/* $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 Lesser General Public License as + published by the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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 "memblock.h" +#include "memchunk.h" + +/* An alignment object, used for aligning memchunks to multiples of + * the frame size. */ + +/* Method of operation: the user creates a new mcalign object by + * calling pa_mcalign_new() with the appropriate aligning + * granularity. After that he may call pa_mcalign_push() for an input + * memchunk. After exactly one memchunk the user has to call + * pa_mcalign_pop() until it returns -1. If pa_mcalign_pop() returns + * 0, the memchunk *c is valid and aligned to the granularity. Some + * pseudocode illustrating this: + * + * struct pa_mcalign *a = pa_mcalign_new(4, NULL); + * + * for (;;) { + * struct pa_memchunk input; + * + * ... fill input ... + * + * pa_mcalign_push(m, &input); + * pa_memblock_unref(input.memblock); + * + * for (;;) { + * struct pa_memchunk output; + * + * if (pa_mcalign_pop(m, &output) < 0) + * break; + * + * ... consume output ... + * + * pa_memblock_unref(output.memblock); + * } + * } + * + * pa_memchunk_free(a); + * */ + +struct pa_mcalign; + +struct pa_mcalign *pa_mcalign_new(size_t base, struct pa_memblock_stat *s); +void pa_mcalign_free(struct pa_mcalign *m); + +/* Push a new memchunk into the aligner. The caller of this routine + * has to free the memchunk by himself. */ +void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c); + +/* Pop a new memchunk from the aligner. Returns 0 when sucessful, + * nonzero otherwise. */ +int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c); + +#endif diff --git a/polyp/memblock.c b/polyp/memblock.c index 8ddfb801..c070bee6 100644 --- a/polyp/memblock.c +++ b/polyp/memblock.c @@ -34,6 +34,11 @@ static void stat_add(struct pa_memblock*m, struct pa_memblock_stat *s) { assert(m); + if (!s) { + m->stat = NULL; + return; + } + m->stat = pa_memblock_stat_ref(s); s->total++; s->allocated++; @@ -61,33 +66,36 @@ struct pa_memblock *pa_memblock_new(size_t length, struct pa_memblock_stat*s) { b->length = length; b->data = b+1; b->free_cb = NULL; + b->read_only = 0; stat_add(b, s); return b; } -struct pa_memblock *pa_memblock_new_fixed(void *d, size_t length, struct pa_memblock_stat*s) { +struct pa_memblock *pa_memblock_new_dynamic(void *d, size_t length, struct pa_memblock_stat*s) { struct pa_memblock *b = pa_xmalloc(sizeof(struct pa_memblock)); - b->type = PA_MEMBLOCK_FIXED; + b->type = PA_MEMBLOCK_DYNAMIC; b->ref = 1; b->length = length; b->data = d; b->free_cb = NULL; + b->read_only = 0; stat_add(b, s); return b; } -struct pa_memblock *pa_memblock_new_dynamic(void *d, size_t length, struct pa_memblock_stat*s) { +struct pa_memblock *pa_memblock_new_fixed(void *d, size_t length, int read_only, struct pa_memblock_stat*s) { struct pa_memblock *b = pa_xmalloc(sizeof(struct pa_memblock)); - b->type = PA_MEMBLOCK_DYNAMIC; + b->type = PA_MEMBLOCK_FIXED; b->ref = 1; b->length = length; b->data = d; b->free_cb = NULL; + b->read_only = read_only; stat_add(b, s); return b; } -struct pa_memblock *pa_memblock_new_user(void *d, size_t length, void (*free_cb)(void *p), struct pa_memblock_stat*s) { +struct pa_memblock *pa_memblock_new_user(void *d, size_t length, void (*free_cb)(void *p), int read_only, struct pa_memblock_stat*s) { struct pa_memblock *b; assert(d && length && free_cb); b = pa_xmalloc(sizeof(struct pa_memblock)); @@ -96,6 +104,7 @@ struct pa_memblock *pa_memblock_new_user(void *d, size_t length, void (*free_cb) b->length = length; b->data = d; b->free_cb = free_cb; + b->read_only = read_only; stat_add(b, s); return b; } diff --git a/polyp/memblock.h b/polyp/memblock.h index 69a85a80..91612ac9 100644 --- a/polyp/memblock.h +++ b/polyp/memblock.h @@ -32,6 +32,7 @@ struct pa_memblock_stat; struct pa_memblock { enum pa_memblock_type type; unsigned ref; + int read_only; size_t length; void *data; void (*free_cb)(void *p); @@ -39,9 +40,9 @@ struct pa_memblock { }; struct pa_memblock *pa_memblock_new(size_t length, struct pa_memblock_stat*s); -struct pa_memblock *pa_memblock_new_fixed(void *data, size_t length, struct pa_memblock_stat*s); struct pa_memblock *pa_memblock_new_dynamic(void *data, size_t length, struct pa_memblock_stat*s); -struct pa_memblock *pa_memblock_new_user(void *data, size_t length, void (*free_cb)(void *p), struct pa_memblock_stat*s); +struct pa_memblock *pa_memblock_new_fixed(void *data, size_t length, int read_only, struct pa_memblock_stat*s); +struct pa_memblock *pa_memblock_new_user(void *data, size_t length, void (*free_cb)(void *p), int read_only, struct pa_memblock_stat*s); void pa_memblock_unref(struct pa_memblock*b); struct pa_memblock* pa_memblock_ref(struct pa_memblock*b); diff --git a/polyp/memblockq.c b/polyp/memblockq.c index 0fbeaad0..ff16f627 100644 --- a/polyp/memblockq.c +++ b/polyp/memblockq.c @@ -33,6 +33,7 @@ #include "memblockq.h" #include "xmalloc.h" #include "log.h" +#include "mcalign.h" struct memblock_list { struct memblock_list *next, *prev; diff --git a/polyp/memchunk.c b/polyp/memchunk.c index a8aeb881..d1c923f3 100644 --- a/polyp/memchunk.c +++ b/polyp/memchunk.c @@ -31,120 +31,28 @@ #include "memchunk.h" #include "xmalloc.h" -void pa_memchunk_make_writable(struct pa_memchunk *c, struct pa_memblock_stat *s) { +void pa_memchunk_make_writable(struct pa_memchunk *c, struct pa_memblock_stat *s, size_t min) { struct pa_memblock *n; + size_t l; assert(c && c->memblock && c->memblock->ref >= 1); - if (c->memblock->ref == 1) + if (c->memblock->ref == 1 && !c->memblock->read_only && c->memblock->length >= c->index+min) return; + + l = c->length; + if (l < min) + l = min; - n = pa_memblock_new(c->length, s); - assert(n); - memcpy(n->data, (uint8_t*) c->memblock->data+c->index, c->length); + n = pa_memblock_new(l, s); + memcpy(n->data, (uint8_t*) c->memblock->data + c->index, c->length); pa_memblock_unref(c->memblock); c->memblock = n; c->index = 0; } +void pa_memchunk_reset(struct pa_memchunk *c) { + assert(c); -struct pa_mcalign { - size_t base; - struct pa_memchunk chunk; - uint8_t *buffer; - size_t buffer_fill; - struct pa_memblock_stat *memblock_stat; -}; - -struct pa_mcalign *pa_mcalign_new(size_t base, struct pa_memblock_stat *s) { - struct pa_mcalign *m; - assert(base); - - m = pa_xmalloc(sizeof(struct pa_mcalign)); - m->base = base; - m->chunk.memblock = NULL; - m->chunk.length = m->chunk.index = 0; - m->buffer = NULL; - m->buffer_fill = 0; - m->memblock_stat = s; - return m; -} - -void pa_mcalign_free(struct pa_mcalign *m) { - assert(m); - - pa_xfree(m->buffer); - - if (m->chunk.memblock) - pa_memblock_unref(m->chunk.memblock); - - pa_xfree(m); -} - -void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c) { - assert(m && c && !m->chunk.memblock && c->memblock && c->length); - - m->chunk = *c; - pa_memblock_ref(m->chunk.memblock); -} - -int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c) { - int ret; - assert(m && c && m->base > m->buffer_fill); - - if (!m->chunk.memblock) - return -1; - - if (m->buffer_fill) { - size_t l = m->base - m->buffer_fill; - if (l > m->chunk.length) - l = m->chunk.length; - assert(m->buffer && l); - - memcpy((uint8_t*) m->buffer + m->buffer_fill, (uint8_t*) m->chunk.memblock->data + m->chunk.index, l); - m->buffer_fill += l; - m->chunk.index += l; - m->chunk.length -= l; - - if (m->chunk.length == 0) { - m->chunk.length = m->chunk.index = 0; - pa_memblock_unref(m->chunk.memblock); - m->chunk.memblock = NULL; - } - - assert(m->buffer_fill <= m->base); - if (m->buffer_fill == m->base) { - c->memblock = pa_memblock_new_dynamic(m->buffer, m->base, m->memblock_stat); - assert(c->memblock); - c->index = 0; - c->length = m->base; - m->buffer = NULL; - m->buffer_fill = 0; - - return 0; - } - - return -1; - } - - m->buffer_fill = m->chunk.length % m->base; - - if (m->buffer_fill) { - assert(!m->buffer); - m->buffer = pa_xmalloc(m->base); - m->chunk.length -= m->buffer_fill; - memcpy(m->buffer, (uint8_t*) m->chunk.memblock->data + m->chunk.index + m->chunk.length, m->buffer_fill); - } - - if (m->chunk.length) { - *c = m->chunk; - pa_memblock_ref(c->memblock); - ret = 0; - } else - ret = -1; - - m->chunk.length = m->chunk.index = 0; - pa_memblock_unref(m->chunk.memblock); - m->chunk.memblock = NULL; - - return ret; + c->memblock = NULL; + c->length = c->index = 0; } diff --git a/polyp/memchunk.h b/polyp/memchunk.h index e73b6f6e..a004c2e8 100644 --- a/polyp/memchunk.h +++ b/polyp/memchunk.h @@ -24,18 +24,22 @@ #include "memblock.h" +/* A memchunk is a part of a memblock. In contrast to the memblock, a + * memchunk is not allocated dynamically or reference counted, instead + * it is usually stored on the stack and copied around */ + struct pa_memchunk { struct pa_memblock *memblock; size_t index, length; }; -void pa_memchunk_make_writable(struct pa_memchunk *c, struct pa_memblock_stat *s); - -struct pa_mcalign; +/* Make a memchunk writable, i.e. make sure that the caller may have + * exclusive access to the memblock and it is not read_only. If needed + * the memblock in the structure is replaced by a copy. */ +void pa_memchunk_make_writable(struct pa_memchunk *c, struct pa_memblock_stat *s, size_t min); -struct pa_mcalign *pa_mcalign_new(size_t base, struct pa_memblock_stat *s); -void pa_mcalign_free(struct pa_mcalign *m); -void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c); -int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c); +/* Invalidate a memchunk. This does not free the cotaining memblock, + * but sets all members to zero. */ +void pa_memchunk_reset(struct pa_memchunk *c); #endif diff --git a/polyp/module-oss-mmap.c b/polyp/module-oss-mmap.c index 8aecd560..66daa77d 100644 --- a/polyp/module-oss-mmap.c +++ b/polyp/module-oss-mmap.c @@ -107,7 +107,7 @@ static void out_fill_memblocks(struct userdata *u, unsigned n) { if (u->out_memblocks[u->out_current]) pa_memblock_unref_fixed(u->out_memblocks[u->out_current]); - chunk.memblock = u->out_memblocks[u->out_current] = pa_memblock_new_fixed((uint8_t*)u->out_mmap+u->out_fragment_size*u->out_current, u->out_fragment_size, u->core->memblock_stat); + chunk.memblock = u->out_memblocks[u->out_current] = pa_memblock_new_fixed((uint8_t*)u->out_mmap+u->out_fragment_size*u->out_current, u->out_fragment_size, 1, u->core->memblock_stat); assert(chunk.memblock); chunk.length = chunk.memblock->length; chunk.index = 0; @@ -148,7 +148,7 @@ static void in_post_memblocks(struct userdata *u, unsigned n) { struct pa_memchunk chunk; if (!u->in_memblocks[u->in_current]) { - chunk.memblock = u->in_memblocks[u->in_current] = pa_memblock_new_fixed((uint8_t*) u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size, u->core->memblock_stat); + chunk.memblock = u->in_memblocks[u->in_current] = pa_memblock_new_fixed((uint8_t*) u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size, 1, u->core->memblock_stat); chunk.length = chunk.memblock->length; chunk.index = 0; diff --git a/polyp/module-tunnel.c b/polyp/module-tunnel.c index 368ae422..7497011d 100644 --- a/polyp/module-tunnel.c +++ b/polyp/module-tunnel.c @@ -610,19 +610,9 @@ int pa__init(struct pa_core *c, struct pa_module*m) { goto fail; } - if (u->server_name[0] == '/') - u->client = pa_socket_client_new_unix(c->mainloop, u->server_name); - else { - size_t len; - struct sockaddr *sa; - - if (!(sa = pa_resolve_server(u->server_name, &len, PA_NATIVE_DEFAULT_PORT))) { - pa_log(__FILE__": failed to resolve server '%s'\n", u->server_name); - goto fail; - } - - u->client = pa_socket_client_new_sockaddr(c->mainloop, sa, len); - pa_xfree(sa); + if (!(u->client = pa_socket_client_new_string(c->mainloop, u->server_name, PA_NATIVE_DEFAULT_PORT))) { + pa_log(__FILE__": failed to connect to server '%s'\n", u->server_name); + goto fail; } if (!u->client) diff --git a/polyp/polyplib-context.c b/polyp/polyplib-context.c index b15dd8e7..15ef60b9 100644 --- a/polyp/polyplib-context.c +++ b/polyp/polyplib-context.c @@ -56,8 +56,6 @@ #define AUTOSPAWN_LOCK "autospawn.lock" - - static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = { [PA_COMMAND_REQUEST] = { pa_command_request }, [PA_COMMAND_PLAYBACK_STREAM_KILLED] = { pa_command_stream_killed }, @@ -72,7 +70,6 @@ static void unlock_autospawn_lock_file(struct pa_context *c) { pa_unlock_lockfile(c->autospawn_lock_fd); c->autospawn_lock_fd = -1; } - } struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char *name) { @@ -106,6 +103,7 @@ struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char * c->memblock_stat = pa_memblock_stat_new(); c->local = -1; c->server_list = NULL; + c->server = NULL; c->autospawn_lock_fd = -1; memset(&c->spawn_api, 0, sizeof(c->spawn_api)); c->do_autospawn = 0; @@ -155,6 +153,7 @@ static void context_free(struct pa_context *c) { pa_strlist_free(c->server_list); pa_xfree(c->name); + pa_xfree(c->server); pa_xfree(c); } @@ -246,9 +245,20 @@ static void pstream_memblock_callback(struct pa_pstream *p, uint32_t channel, ui pa_context_ref(c); if ((s = pa_dynarray_get(c->record_streams, channel))) { - if (s->read_callback) { - s->read_callback(s, (uint8_t*) chunk->memblock->data + chunk->index, chunk->length, s->read_userdata); - s->counter += chunk->length; + pa_mcalign_push(s->mcalign, chunk); + + for (;;) { + struct pa_memchunk t; + + if (pa_mcalign_pop(s->mcalign, &t) < 0) + break; + + if (s->read_callback) { + s->read_callback(s, (uint8_t*) t.memblock->data + t.index, t.length, s->read_userdata); + s->counter += chunk->length; + } + + pa_memblock_unref(t.memblock); } } @@ -496,6 +506,9 @@ static int try_next_connection(struct pa_context *c) { } /* pa_log(__FILE__": Trying to connect to %s...\n", u); */ + + pa_xfree(c->server); + c->server = pa_xstrdup(u); if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT))) continue; @@ -811,3 +824,15 @@ const char* pa_get_library_version(void) { return PACKAGE_VERSION; } +const char* pa_context_get_server(struct pa_context *c) { + + if (!c->server) + return NULL; + + if (*c->server == '{') { + char *e = strchr(c->server+1, '}'); + return e ? e+1 : c->server; + } + + return c->server; +} diff --git a/polyp/polyplib-context.h b/polyp/polyplib-context.h index 565701b2..db4b121d 100644 --- a/polyp/polyplib-context.h +++ b/polyp/polyplib-context.h @@ -108,6 +108,9 @@ int pa_context_is_local(struct pa_context *c); /** Set a different application name for context on the server. \since 0.5 */ struct pa_operation* pa_context_set_name(struct pa_context *c, const char *name, void(*cb)(struct pa_context*c, int success, void *userdata), void *userdata); +/** Return the server name this context is connected to. \since 0.7 */ +const char* pa_context_get_server(struct pa_context *c); + PA_C_DECL_END #endif diff --git a/polyp/polyplib-internal.h b/polyp/polyplib-internal.h index d1b53633..68ba76a9 100644 --- a/polyp/polyplib-internal.h +++ b/polyp/polyplib-internal.h @@ -35,6 +35,7 @@ #include "native-common.h" #include "client-conf.h" #include "strlist.h" +#include "mcalign.h" #define DEFAULT_TLENGTH (44100*2*2/2) //(10240*8) #define DEFAULT_MAXLENGTH ((DEFAULT_TLENGTH*3)/2) @@ -77,6 +78,8 @@ struct pa_context { struct pa_strlist *server_list; + char *server; + struct pa_client_conf *conf; }; @@ -97,6 +100,7 @@ struct pa_stream { uint64_t counter; pa_usec_t previous_time; enum pa_stream_state state; + struct pa_mcalign *mcalign; int interpolate; int corked; diff --git a/polyp/polyplib-stream.c b/polyp/polyplib-stream.c index a97096b4..7d3d3a76 100644 --- a/polyp/polyplib-stream.c +++ b/polyp/polyplib-stream.c @@ -61,6 +61,8 @@ struct pa_stream *pa_stream_new(struct pa_context *c, const char *name, const st s->state = PA_STREAM_DISCONNECTED; memset(&s->buffer_attr, 0, sizeof(s->buffer_attr)); + s->mcalign = pa_mcalign_new(pa_frame_size(ss), c->memblock_stat); + s->counter = 0; s->previous_time = 0; @@ -83,6 +85,8 @@ static void stream_free(struct pa_stream *s) { assert(s->mainloop); s->mainloop->time_free(s->ipol_event); } + + pa_mcalign_free(s->mcalign); pa_xfree(s->name); pa_xfree(s); @@ -203,11 +207,13 @@ static void ipol_callback(struct pa_mainloop_api *m, struct pa_time_event *e, co struct pa_stream *s = userdata; pa_stream_ref(s); - pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); + if (s->state == PA_STREAM_READY) + pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); + gettimeofday(&tv2, NULL); tv2.tv_usec += LATENCY_IPOL_INTERVAL_USEC; - + m->time_restart(e, &tv2); pa_stream_unref(s); @@ -329,7 +335,7 @@ void pa_stream_write(struct pa_stream *s, const void *data, size_t length, void assert(s && s->context && data && length && s->state == PA_STREAM_READY && s->ref >= 1); if (free_cb) { - chunk.memblock = pa_memblock_new_user((void*) data, length, free_cb, s->context->memblock_stat); + chunk.memblock = pa_memblock_new_user((void*) data, length, free_cb, 1, s->context->memblock_stat); assert(chunk.memblock && chunk.memblock->data); } else { chunk.memblock = pa_memblock_new(length, s->context->memblock_stat); diff --git a/polyp/protocol-esound.c b/polyp/protocol-esound.c index 4da7e388..07c39e2a 100644 --- a/polyp/protocol-esound.c +++ b/polyp/protocol-esound.c @@ -42,7 +42,6 @@ #include "scache.h" #include "sample-util.h" #include "authkey.h" -#include "debug.h" #include "namereg.h" #include "xmalloc.h" #include "log.h" diff --git a/polyp/protocol-native.c b/polyp/protocol-native.c index 7d539014..02d81db3 100644 --- a/polyp/protocol-native.c +++ b/polyp/protocol-native.c @@ -1343,7 +1343,7 @@ static void command_get_server_info(struct pa_pdispatch *pd, uint32_t command, u pa_tagstruct_puts(reply, PACKAGE_NAME); pa_tagstruct_puts(reply, PACKAGE_VERSION); pa_tagstruct_puts(reply, pa_get_user_name(txt, sizeof(txt))); - pa_tagstruct_puts(reply, pa_get_host_name(txt, sizeof(txt))); + pa_tagstruct_puts(reply, pa_get_fqdn(txt, sizeof(txt))); pa_tagstruct_put_sample_spec(reply, &c->protocol->core->default_sample_spec); n = pa_namereg_get_default_sink_name(c->protocol->core); diff --git a/polyp/scache.c b/polyp/scache.c index 32a36289..ccdc7185 100644 --- a/polyp/scache.c +++ b/polyp/scache.c @@ -27,6 +27,12 @@ #include <stdlib.h> #include <string.h> #include <stdio.h> +#include <sys/types.h> +#include <dirent.h> +#include <sys/stat.h> +#include <errno.h> +#include <limits.h> +#include <glob.h> #include "scache.h" #include "sink-input.h" @@ -38,6 +44,7 @@ #include "namereg.h" #include "sound-file.h" #include "util.h" +#include "log.h" #define UNLOAD_POLL_TIME 2 @@ -291,3 +298,58 @@ void pa_scache_unload_unused(struct pa_core *c) { pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); } } + +static void add_file(struct pa_core *c, const char *pathname) { + struct stat st; + const char *e; + + if (!(e = strrchr(pathname, '/'))) + e = pathname; + else + e++; + + if (stat(pathname, &st) < 0) { + pa_log(__FILE__": stat('%s') failed: %s\n", pathname, strerror(errno)); + return; + } + + if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) + pa_scache_add_file_lazy(c, e, pathname, NULL); +} + +int pa_scache_add_directory_lazy(struct pa_core *c, const char *pathname) { + DIR *dir; + assert(c && pathname); + + /* First try to open this as directory */ + if (!(dir = opendir(pathname))) { + glob_t p; + unsigned int i; + /* If that fails, try to open it as shell glob */ + + if (glob(pathname, GLOB_ERR|GLOB_NOSORT, NULL, &p) < 0) { + pa_log(__FILE__": Failed to open directory: %s\n", strerror(errno)); + return -1; + } + + for (i = 0; i < p.gl_pathc; i++) + add_file(c, p.gl_pathv[i]); + + globfree(&p); + } else { + struct dirent *e; + + while ((e = readdir(dir))) { + char p[PATH_MAX]; + + if (e->d_name[0] == '.') + continue; + + snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name); + add_file(c, p); + } + } + + closedir(dir); + return 0; +} diff --git a/polyp/scache.h b/polyp/scache.h index afaba31a..f7043f16 100644 --- a/polyp/scache.h +++ b/polyp/scache.h @@ -45,6 +45,8 @@ int pa_scache_add_item(struct pa_core *c, const char *name, struct pa_sample_spe int pa_scache_add_file(struct pa_core *c, const char *name, const char *filename, uint32_t *index); int pa_scache_add_file_lazy(struct pa_core *c, const char *name, const char *filename, uint32_t *index); +int pa_scache_add_directory_lazy(struct pa_core *c, const char *pathname); + int pa_scache_remove_item(struct pa_core *c, const char *name); int pa_scache_play_item(struct pa_core *c, const char *name, struct pa_sink *sink, uint32_t volume); void pa_scache_free(struct pa_core *c); diff --git a/polyp/sink.c b/polyp/sink.c index c17af4ae..29aef6fb 100644 --- a/polyp/sink.c +++ b/polyp/sink.c @@ -225,7 +225,7 @@ int pa_sink_render(struct pa_sink*s, size_t length, struct pa_memchunk *result) volume = pa_volume_multiply(s->volume, info[0].volume); if (volume != PA_VOLUME_NORM) { - pa_memchunk_make_writable(result, s->core->memblock_stat); + pa_memchunk_make_writable(result, s->core->memblock_stat, 0); pa_volume_memchunk(result, &s->sample_spec, volume); } } else { diff --git a/polyp/socket-client.c b/polyp/socket-client.c index b77d2aeb..aea38586 100644 --- a/polyp/socket-client.c +++ b/polyp/socket-client.c @@ -338,16 +338,24 @@ struct pa_socket_client* pa_socket_client_new_string(struct pa_mainloop_api *m, memset(&hints, 0, sizeof(hints)); hints.ai_family = kind == KIND_TCP4 ? AF_INET : (kind == KIND_TCP6 ? AF_INET6 : AF_UNSPEC); - if (getaddrinfo(h, NULL, &hints, &res) < 0 || !res) + if (getaddrinfo(h, NULL, &hints, &res) < 0 || !res || !res->ai_addr) return NULL; - if (res->ai_addr->sa_family == AF_INET) + if (res->ai_family == AF_INET) { + if (res->ai_addrlen != sizeof(struct sockaddr_in)) + return NULL; + assert(res->ai_addr->sa_family == res->ai_family); + ((struct sockaddr_in*) res->ai_addr)->sin_port = htons(port); - else if (res->ai_addr->sa_family == AF_INET6) + } else if (res->ai_family == AF_INET6) { + if (res->ai_addrlen != sizeof(struct sockaddr_in6)) + return NULL; + assert(res->ai_addr->sa_family == res->ai_family); + ((struct sockaddr_in6*) res->ai_addr)->sin6_port = htons(port); - else + } else return NULL; - + c = pa_socket_client_new_sockaddr(m, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); return c; @@ -360,6 +368,9 @@ struct pa_socket_client* pa_socket_client_new_string(struct pa_mainloop_api *m, } +/* Return non-zero when the target sockaddr is considered + local. "local" means UNIX socket or TCP socket on localhost. Other + local IP addresses are not considered local. */ int pa_socket_client_is_local(struct pa_socket_client *c) { assert(c); return c->local; diff --git a/polyp/socket-util.c b/polyp/socket-util.c index 1800710f..495ee1b0 100644 --- a/polyp/socket-util.c +++ b/polyp/socket-util.c @@ -202,36 +202,3 @@ int pa_unix_socket_remove_stale(const char *fn) { return 0; } - -struct sockaddr *pa_resolve_server(const char *server, size_t *len, uint16_t nport) { - struct sockaddr *sa; - struct addrinfo hints, *result = NULL; - char *port, host[256], tmp[16]; - assert(server && len); - - snprintf(host, sizeof(host), "%s", server); - host[strcspn(host, ":")] = 0; - - if ((port = strrchr(server, ':'))) - port++; - - if (!port) - snprintf(port = tmp, sizeof(tmp), "%u", nport); - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = 0; - - if (getaddrinfo(host, port, &hints, &result) != 0) - return NULL; - assert(result); - - sa = pa_xmalloc(*len = result->ai_addrlen); - memcpy(sa, result->ai_addr, *len); - - freeaddrinfo(result); - - return sa; -} - diff --git a/polyp/socket-util.h b/polyp/socket-util.h index 6c8ffe3b..ae16fb16 100644 --- a/polyp/socket-util.h +++ b/polyp/socket-util.h @@ -35,6 +35,4 @@ int pa_socket_set_rcvbuf(int fd, size_t l); int pa_unix_socket_is_stale(const char *fn); int pa_unix_socket_remove_stale(const char *fn); -struct sockaddr *pa_resolve_server(const char *server, size_t *len, uint16_t port); - #endif diff --git a/polyp/strbuf.c b/polyp/strbuf.c index 8876ba13..a6521651 100644 --- a/polyp/strbuf.c +++ b/polyp/strbuf.c @@ -33,6 +33,7 @@ #include "strbuf.h" +/* Some magic for zero-length arrays */ #ifdef __STDC_VERSION__ #if __STDC_VERSION__ >= 199901L #ifndef STDC99 @@ -41,6 +42,7 @@ #endif #endif +/* A chunk of the linked list that makes up the string */ struct chunk { struct chunk *next; size_t length; @@ -74,6 +76,8 @@ void pa_strbuf_free(struct pa_strbuf *sb) { pa_xfree(sb); } +/* Make a C string from the string buffer. The caller has to free + * string with pa_xfree(). */ char *pa_strbuf_tostring(struct pa_strbuf *sb) { char *t, *e; struct chunk *c; @@ -83,15 +87,18 @@ char *pa_strbuf_tostring(struct pa_strbuf *sb) { e = t; for (c = sb->head; c; c = c->next) { + assert((size_t) (e-t) <= sb->length); memcpy(e, c->text, c->length); e += c->length; } + /* Trailing NUL */ *e = 0; return t; } +/* Combination of pa_strbuf_free() and pa_strbuf_tostring() */ char *pa_strbuf_tostring_free(struct pa_strbuf *sb) { char *t; assert(sb); @@ -100,11 +107,30 @@ char *pa_strbuf_tostring_free(struct pa_strbuf *sb) { return t; } +/* Append a string to the string buffer */ void pa_strbuf_puts(struct pa_strbuf *sb, const char *t) { assert(sb && t); pa_strbuf_putsn(sb, t, strlen(t)); -} +} + +/* Append a new chunk to the linked list */ +static void append(struct pa_strbuf *sb, struct chunk *c) { + assert(sb && c); + + if (sb->tail) { + assert(sb->head); + sb->tail->next = c; + } else { + assert(!sb->head); + sb->head = c; + } + + sb->tail = c; + sb->length += c->length; + c->next = NULL; +} +/* Append up to l bytes of a string to the string buffer */ void pa_strbuf_putsn(struct pa_strbuf *sb, const char *t, size_t l) { struct chunk *c; assert(sb && t); @@ -113,33 +139,23 @@ void pa_strbuf_putsn(struct pa_strbuf *sb, const char *t, size_t l) { return; c = pa_xmalloc(sizeof(struct chunk)+l); - - c->next = NULL; c->length = l; memcpy(c->text, t, l); - if (sb->tail) { - assert(sb->head); - sb->tail->next = c; - } else { - assert(!sb->head); - sb->head = c; - } - - sb->tail = c; - sb->length += l; + append(sb, c); } +/* Append a printf() style formatted string to the string buffer. */ /* The following is based on an example from the GNU libc documentation */ - int pa_strbuf_printf(struct pa_strbuf *sb, const char *format, ...) { - int r, size = 100; + int size = 100; struct chunk *c = NULL; assert(sb); for(;;) { va_list ap; + int r; c = pa_xrealloc(c, sizeof(struct chunk)+size); @@ -149,19 +165,7 @@ int pa_strbuf_printf(struct pa_strbuf *sb, const char *format, ...) { if (r > -1 && r < size) { c->length = r; - c->next = NULL; - - if (sb->tail) { - assert(sb->head); - sb->tail->next = c; - } else { - assert(!sb->head); - sb->head = c; - } - - sb->tail = c; - sb->length += r; - + append(sb, c); return r; } |