diff options
| -rw-r--r-- | configure.ac | 5 | ||||
| -rw-r--r-- | doc/todo | 5 | ||||
| -rw-r--r-- | polyp/Makefile.am | 21 | ||||
| -rw-r--r-- | polyp/cmdline.c | 38 | ||||
| -rwxr-xr-x | polyp/esdcompat.sh.in | 2 | ||||
| -rw-r--r-- | polyp/main.c | 8 | ||||
| -rw-r--r-- | polyp/module-native-protocol-fd.c | 78 | ||||
| -rw-r--r-- | polyp/pacat.c | 2 | ||||
| -rwxr-xr-x | polyp/polypaudio.pa | 2 | ||||
| -rw-r--r-- | polyp/polyplib-context.c | 125 | ||||
| -rw-r--r-- | polyp/polyplib-context.h | 9 | ||||
| -rw-r--r-- | polyp/polyplib-internal.h | 1 | ||||
| -rw-r--r-- | polyp/protocol-native.c | 36 | ||||
| -rw-r--r-- | polyp/protocol-native.h | 2 | 
14 files changed, 292 insertions, 42 deletions
diff --git a/configure.ac b/configure.ac index 2c470435..7aa6c5d4 100644 --- a/configure.ac +++ b/configure.ac @@ -115,8 +115,5 @@ AM_CONDITIONAL([USE_LYNX], [test "x$lynx" = xyes])  AM_CONDITIONAL(BUILD_LIBPOLYPCORE, false) -ESDCOMPAT_BINARY=$(bindir)/polypaudio -AC_SUBST(ESDCOMPAT_BINARY) - -AC_CONFIG_FILES([Makefile polyp/Makefile polyplib.pc polyplib-simple.pc polyplib-mainloop.pc polyplib-error.pc polyplib-glib-mainloop.pc polyplib-glib12-mainloop.pc doc/Makefile doc/README.html doc/cli.html doc/daemon.html doc/modules.html doxygen/Makefile doxygen/doxygen.conf polyp/polyplib-version.h polyp/esdcompat.sh]) +AC_CONFIG_FILES([Makefile polyp/Makefile polyplib.pc polyplib-simple.pc polyplib-mainloop.pc polyplib-error.pc polyplib-glib-mainloop.pc polyplib-glib12-mainloop.pc doc/Makefile doc/README.html doc/cli.html doc/daemon.html doc/modules.html doxygen/Makefile doxygen/doxygen.conf polyp/polyplib-version.h])  AC_OUTPUT @@ -9,11 +9,14 @@     kill client/...     autoload management  - more complete pactl -- daemon autostart  - cleanup tagstruct and modargs (add s32, pa_volume_t, pa_usec_t)  - remove all gcc warnings  - add total sample cache size to stat  - make fragments settings runtime configurable +- CLOEXEC +- logging +- automatic termination of daemon if unused +- add sample directory  ** later ***  - xmlrpc/http diff --git a/polyp/Makefile.am b/polyp/Makefile.am index 7d6d9bb3..923a3522 100644 --- a/polyp/Makefile.am +++ b/polyp/Makefile.am @@ -21,14 +21,16 @@ polypincludedir=$(includedir)/polyp  polypconfdir=$(sysconfdir)/polyp  AM_CFLAGS=-D_GNU_SOURCE -I$(top_srcdir) $(PTHREAD_CFLAGS) -#AM_CFLAGS+= -DPA_DLSEARCHDIR=\"$(pkglibdir)\"  -AM_CFLAGS+= -DPA_DEFAULT_CONFIG_FILE=\"$(polypconfdir)/polypaudio.pa\" +#AM_CFLAGS+= -DDLSEARCHDIR=\"$(pkglibdir)\"  +AM_CFLAGS+="-DDEFAULT_CONFIG_FILE=\"$(polypconfdir)/polypaudio.pa\"" +AM_CFLAGS+="-DPOLYPAUDIO_BINARY=\"$(bindir)/polypaudio\"" +  AM_LDADD=$(PTHREAD_LIBS) -lm  AM_LIBADD=$(PTHREAD_LIBS) -lm - -EXTRA_DIST = polypaudio.pa depmod.py +EXTRA_DIST = polypaudio.pa depmod.py esdcompat.sh.in  bin_PROGRAMS = polypaudio pacat pactl +bin_SCRIPTS = esdcompat.sh  noinst_PROGRAMS = mainloop-test mainloop-test-glib mainloop-test-glib12 pacat-simple parec-simple   polypconf_DATA=polypaudio.pa  @@ -85,6 +87,7 @@ pkglib_LTLIBRARIES=libiochannel.la \  		module-esound-protocol-unix.la \  		module-native-protocol-tcp.la \  		module-native-protocol-unix.la \ +		module-native-protocol-fd.la \  		module-sine.la  if !X_DISPLAY_MISSING @@ -244,6 +247,11 @@ module_native_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_NATIVE  module_native_protocol_unix_la_LDFLAGS = -module -avoid-version  module_native_protocol_unix_la_LIBADD = $(AM_LIBADD) libprotocol-native.la libsocket-server.la libsocket-util.la +module_native_protocol_fd_la_SOURCES = module-native-protocol-fd.c +module_native_protocol_fd_la_CFLAGS = $(AM_CFLAGS) +module_native_protocol_fd_la_LDFLAGS = -module -avoid-version +module_native_protocol_fd_la_LIBADD = $(AM_LIBADD) libprotocol-native.la libsocket-server.la libsocket-util.la +  module_esound_protocol_tcp_la_SOURCES = module-protocol-stub.c  module_esound_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS)  module_esound_protocol_tcp_la_LDFLAGS = -module -avoid-version @@ -452,3 +460,8 @@ endif  suid: polypaudio  	chown root:root $<  	chmod u+s $< + +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' < $< > $@ diff --git a/polyp/cmdline.c b/polyp/cmdline.c index 6538b930..c07e7bdc 100644 --- a/polyp/cmdline.c +++ b/polyp/cmdline.c @@ -28,14 +28,36 @@  #include <stdlib.h>  #include <stdio.h>  #include <getopt.h> +#include <sys/stat.h>  #include "cmdline.h"  #include "util.h"  #include "strbuf.h"  #include "xmalloc.h" +#define ENV_CONFIG_FILE "POLYP_CONFIG" + +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++; @@ -48,16 +70,18 @@ void pa_cmdline_help(const char *argv0) {             "  -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 ("PA_DEFAULT_CONFIG_FILE")\n" +           "  -n         Don't load configuration file (%s)\n"             "  -D         Daemonize after loading the modules\n"             "  -f         Dont quit when the startup fails\n"             "  -v         Verbose startup\n"             "  -h         Show this help\n" -           "  -V         Show version\n", e); +           "  -V         Show version\n", e, cfg); + +    pa_xfree(cfg);  }  struct pa_cmdline* pa_cmdline_parse(int argc, char * const argv []) { -    char c; +    char c, *cfg;      struct pa_cmdline *cmdline = NULL;      struct pa_strbuf *buf = NULL;      int no_default_config_file = 0; @@ -111,9 +135,11 @@ struct pa_cmdline* pa_cmdline_parse(int argc, char * const argv []) {          }      } -    if (!no_default_config_file) -        pa_strbuf_puts(buf, ".include "PA_DEFAULT_CONFIG_FILE"\n"); -     +    if (!no_default_config_file) { +        cfg = config_file(); +        pa_strbuf_printf(buf, ".include %s\n", cfg); +        pa_xfree(cfg); +    }      cmdline->cli_commands = pa_strbuf_tostring_free(buf);      return cmdline; diff --git a/polyp/esdcompat.sh.in b/polyp/esdcompat.sh.in index 1033930e..88ff447f 100755 --- a/polyp/esdcompat.sh.in +++ b/polyp/esdcompat.sh.in @@ -80,4 +80,4 @@ EOF      shift  done -exec "@ESDCOMPAT_BINARY@" -r +exec "@POLYPAUDIO_BINARY@" -r diff --git a/polyp/main.c b/polyp/main.c index e41a106f..87265da6 100644 --- a/polyp/main.c +++ b/polyp/main.c @@ -141,12 +141,15 @@ int main(int argc, char *argv[]) {          setsid();          setpgrp(); +         +        close(0); +        close(1);      }      r = lt_dlinit();      assert(r == 0); -#ifdef PA_DLSEARCHDIR -    lt_dladdsearchdir(PA_DLSEARCHDIR); +#ifdef DLSEARCHDIR +    lt_dladdsearchdir(DLSEARCHDIR);  #endif      mainloop = pa_mainloop_new(); @@ -155,6 +158,7 @@ int main(int argc, char *argv[]) {      r = pa_signal_init(pa_mainloop_get_api(mainloop));      assert(r == 0);      pa_signal_new(SIGINT, exit_signal_callback, NULL); +    pa_signal_new(SIGTERM, exit_signal_callback, NULL);      signal(SIGPIPE, SIG_IGN);      c = pa_core_new(pa_mainloop_get_api(mainloop)); diff --git a/polyp/module-native-protocol-fd.c b/polyp/module-native-protocol-fd.c new file mode 100644 index 00000000..58d09ffe --- /dev/null +++ b/polyp/module-native-protocol-fd.c @@ -0,0 +1,78 @@ +/* $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 <stdio.h> +#include <assert.h> +#include <unistd.h> + +#include "module.h" +#include "iochannel.h" +#include "modargs.h" +#include "protocol-native.h" + +static const char* const valid_modargs[] = { +    "fd", +    "public", +    "cookie", +    NULL, +}; + +int pa_module_init(struct pa_core *c, struct pa_module*m) { +    struct pa_iochannel *io; +    struct pa_modargs *ma; +    int fd, r = -1; +    assert(c && m); + +    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { +        fprintf(stderr, __FILE__": failed to parse module arguments.\n"); +        goto finish; +    } + +    if (pa_modargs_get_value_u32(ma, "fd", &fd) < 0) { +        fprintf(stderr, __FILE__": invalid file descriptor.\n"); +        goto finish; +    } +     +    io = pa_iochannel_new(c->mainloop, fd, fd); + +    if (!(m->userdata = pa_protocol_native_new_iochannel(c, io, m, ma))) { +        pa_iochannel_free(io); +        goto finish; +    } + +    r = 0; + +finish: +    if (ma) +        pa_modargs_free(ma); +     +    return r; +} + +void pa_module_done(struct pa_core *c, struct pa_module*m) { +    assert(c && m); + +    pa_protocol_native_free(m->userdata); +} diff --git a/polyp/pacat.c b/polyp/pacat.c index b251cc35..fd6e90e1 100644 --- a/polyp/pacat.c +++ b/polyp/pacat.c @@ -346,7 +346,7 @@ int main(int argc, char *argv[]) {      pa_context_set_state_callback(context, context_state_callback, NULL);      /* Connect the context */ -    pa_context_connect(context, NULL); +    pa_context_connect_spawn(context, NULL);      /* Run the main loop */      if (pa_mainloop_run(m, &ret) < 0) { diff --git a/polyp/polypaudio.pa b/polyp/polypaudio.pa index 0c69893a..715e23b6 100755 --- a/polyp/polypaudio.pa +++ b/polyp/polypaudio.pa @@ -43,7 +43,7 @@ load module-native-protocol-unix  load module-esound-protocol-unix  # Load the CLI module -load module-cli +#load module-cli  # Make some devices default  sink_default output diff --git a/polyp/polyplib-context.c b/polyp/polyplib-context.c index 2ead4004..fb6eadf4 100644 --- a/polyp/polyplib-context.c +++ b/polyp/polyplib-context.c @@ -30,6 +30,10 @@  #include <sys/types.h>  #include <sys/socket.h>  #include <netdb.h> +#include <unistd.h> +#include <sys/stat.h> +#include <errno.h> +#include <sys/wait.h>  #include "polyplib-internal.h"  #include "polyplib-context.h" @@ -234,9 +238,11 @@ static void setup_complete_callback(struct pa_pdispatch *pd, uint32_t command, u      pa_context_ref(c);      if (command != PA_COMMAND_REPLY) { +                  if (pa_context_handle_error(c, command, t) < 0)              pa_context_fail(c, PA_ERROR_PROTOCOL); +        pa_context_fail(c, c->error);          goto finish;      } @@ -267,22 +273,13 @@ finish:      pa_context_unref(c);  } -static void on_connection(struct pa_socket_client *client, struct pa_iochannel*io, void *userdata) { -    struct pa_context *c = userdata; +static void setup_context(struct pa_context *c, struct pa_iochannel *io) {      struct pa_tagstruct *t;      uint32_t tag; -    assert(client && c && c->state == PA_CONTEXT_CONNECTING); +    assert(c && io);      pa_context_ref(c); -    pa_socket_client_unref(client); -    c->client = NULL; - -    if (!io) { -        pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED); -        goto finish; -    } -      assert(!c->pstream);      c->pstream = pa_pstream_new(c->mainloop, io, c->memblock_stat);      assert(c->pstream); @@ -295,6 +292,11 @@ static void on_connection(struct pa_socket_client *client, struct pa_iochannel*i      c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX);      assert(c->pdispatch); +    if (pa_authkey_load_from_home(PA_NATIVE_COOKIE_FILE, c->auth_cookie, sizeof(c->auth_cookie)) < 0) { +        pa_context_fail(c, PA_ERROR_AUTHKEY); +        goto finish; +    } +      t = pa_tagstruct_new(NULL, 0);      assert(t);      pa_tagstruct_putu32(t, PA_COMMAND_AUTH); @@ -306,6 +308,27 @@ static void on_connection(struct pa_socket_client *client, struct pa_iochannel*i      pa_context_set_state(c, PA_CONTEXT_AUTHORIZING);  finish: +     +    pa_context_unref(c); +} + +static void on_connection(struct pa_socket_client *client, struct pa_iochannel*io, void *userdata) { +    struct pa_context *c = userdata; +    assert(client && c && c->state == PA_CONTEXT_CONNECTING); + +    pa_context_ref(c); +     +    pa_socket_client_unref(client); +    c->client = NULL; + +    if (!io) { +        pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED); +        goto finish; +    } + +    setup_context(c, io); + +finish:      pa_context_unref(c);  } @@ -343,11 +366,6 @@ int pa_context_connect(struct pa_context *c, const char *server) {      pa_context_ref(c); -    if (pa_authkey_load_from_home(PA_NATIVE_COOKIE_FILE, c->auth_cookie, sizeof(c->auth_cookie)) < 0) { -        pa_context_fail(c, PA_ERROR_AUTHKEY); -        goto finish; -    } -      if (!server)          if (!(server = getenv(ENV_DEFAULT_SERVER)))              server = DEFAULT_SERVER; @@ -539,3 +557,78 @@ struct pa_operation* pa_context_send_simple_command(struct pa_context *c, uint32  const char* pa_get_library_version(void) {      return PACKAGE_VERSION;  } + +static int is_running(void) { +    struct stat st; +     +    if (DEFAULT_SERVER[0] != '/') +        return 1; + +    if (stat(DEFAULT_SERVER, &st) < 0) +        return 0; + +    return 1; +} + +int pa_context_connect_spawn(struct pa_context *c, void (*atfork)(void)) { +    pid_t pid; +    int status; +    int fds[2] = { -1, -1} ; +    struct pa_iochannel *io; +     +    if (getenv(ENV_DEFAULT_SERVER) || is_running()) +        return pa_context_connect(c, NULL); + +    if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { +        fprintf(stderr, __FILE__": socketpair() failed: %s\n", strerror(errno)); +        pa_context_fail(c, PA_ERROR_INTERNAL); +        goto fail; +    } + +    if ((pid = fork()) < 0) { +        fprintf(stderr, __FILE__": fork() failed: %s\n", strerror(errno)); +        pa_context_fail(c, PA_ERROR_INTERNAL); +        goto fail; +    } else if (!pid) { +        char t[64]; +        char *p; +        /* Child */ + +        close(fds[0]); +         +        if (atfork) +            atfork(); + +        if (!(p = getenv(ENV_DEFAULT_BINARY))) +            p = POLYPAUDIO_BINARY; +         +        snprintf(t, sizeof(t), "-Lmodule-native-protocol-fd fd=%i", fds[1]); +        execl(p, p, "-r", "-D", t, NULL); +         +        exit(1); +    }  + +    /* Parent */ +    if (waitpid(pid, &status, 0) < 0) { +        fprintf(stderr, __FILE__": waitpid() failed: %s\n", strerror(errno)); +        pa_context_fail(c, PA_ERROR_INTERNAL); +        goto fail; +    } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { +        pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED); +        goto fail; +    } + +    close(fds[1]); +     +    io = pa_iochannel_new(c->mainloop, fds[0], fds[0]); +    setup_context(c, io); +    return 0; + +fail: +    if (fds[0] != -1) +        close(fds[0]); +    if (fds[1] != -1) +        close(fds[1]); + +    return -1; +} diff --git a/polyp/polyplib-context.h b/polyp/polyplib-context.h index 9614ce69..c7d20703 100644 --- a/polyp/polyplib-context.h +++ b/polyp/polyplib-context.h @@ -1,3 +1,4 @@ +  #ifndef foopolyplibcontexthfoo  #define foopolyplibcontexthfoo @@ -78,6 +79,14 @@ return synchronously on error. Use pa_context_set_state_callback() to  be notified when the connection is established */  int pa_context_connect(struct pa_context *c, const char *server); +/** Connect the context to a server. If the default server is local + * but not accessible, spawn a new daemon. If atfork is not NULL it is + * run after the fork() in the child process. It may be used to close + * file descriptors or to do any other cleanups. Make sure that + * SIGCHLD is handled when calling this function. The function will + * waitpid() on the daemon's PID. \since 0.4 */ +int pa_context_connect_spawn(struct pa_context *c, void (*atfork)(void)); +  /** Terminate the context connection immediately */  void pa_context_disconnect(struct pa_context *c); diff --git a/polyp/polyplib-internal.h b/polyp/polyplib-internal.h index 813bb04e..de63b1ba 100644 --- a/polyp/polyplib-internal.h +++ b/polyp/polyplib-internal.h @@ -47,6 +47,7 @@  #define ENV_DEFAULT_SINK "POLYP_SINK"  #define ENV_DEFAULT_SOURCE "POLYP_SOURCE"  #define ENV_DEFAULT_SERVER "POLYP_SERVER" +#define ENV_DEFAULT_BINARY "POLYP_BINARY"  struct pa_context {      int ref; diff --git a/polyp/protocol-native.c b/polyp/protocol-native.c index 9c6996be..9dddf9a1 100644 --- a/polyp/protocol-native.c +++ b/polyp/protocol-native.c @@ -1466,7 +1466,7 @@ static void client_kill_cb(struct pa_client *c) {  static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) {      struct pa_protocol_native *p = userdata;      struct connection *c; -    assert(s && io && p); +    assert(io && p);      c = pa_xmalloc(sizeof(struct connection));      c->authorized = p->public; @@ -1501,10 +1501,10 @@ static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, vo  /*** module entry points ***/ -struct pa_protocol_native* pa_protocol_native_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) { +static struct pa_protocol_native* protocol_new_internal(struct pa_core *c, struct pa_module *m, struct pa_modargs *ma) {      struct pa_protocol_native *p;      uint32_t public; -    assert(core && server && ma); +    assert(c && ma);      if (pa_modargs_get_value_u32(ma, "public", &public) < 0) {          fprintf(stderr, __FILE__": public= expects numeric argument.\n"); @@ -1520,11 +1520,21 @@ struct pa_protocol_native* pa_protocol_native_new(struct pa_core *core, struct p      p->module = m;      p->public = public; -    p->server = server; -    p->core = core; +    p->server = NULL; +    p->core = c;      p->connections = pa_idxset_new(NULL, NULL);      assert(p->connections); +    return p; +} + +struct pa_protocol_native* pa_protocol_native_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) { +    struct pa_protocol_native *p; + +    if (!(p = protocol_new_internal(core, m, ma))) +        return NULL; +     +    p->server = server;      pa_socket_server_set_callback(p->server, on_connection, p);      return p; @@ -1537,6 +1547,20 @@ void pa_protocol_native_free(struct pa_protocol_native *p) {      while ((c = pa_idxset_first(p->connections, NULL)))          connection_free(c);      pa_idxset_free(p->connections, NULL, NULL); -    pa_socket_server_unref(p->server); + +    if (p->server) +        pa_socket_server_unref(p->server); +          pa_xfree(p);  } + +struct pa_protocol_native* pa_protocol_native_new_iochannel(struct pa_core*core, struct pa_iochannel *io, struct pa_module *m, struct pa_modargs *ma) { +    struct pa_protocol_native *p; + +    if (!(p = protocol_new_internal(core, m, ma))) +        return NULL; + +    on_connection(NULL, io, p); +     +    return p; +} diff --git a/polyp/protocol-native.h b/polyp/protocol-native.h index 3d9fdde1..edc6acbc 100644 --- a/polyp/protocol-native.h +++ b/polyp/protocol-native.h @@ -32,4 +32,6 @@ struct pa_protocol_native;  struct pa_protocol_native* pa_protocol_native_new(struct pa_core*core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma);  void pa_protocol_native_free(struct pa_protocol_native *n); +struct pa_protocol_native* pa_protocol_native_new_iochannel(struct pa_core*core, struct pa_iochannel *io, struct pa_module *m, struct pa_modargs *ma); +  #endif  | 
