From 348738751c50c7d3c4c9ed22801a0c2cb917b790 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 1 Sep 2004 21:12:27 +0000 Subject: daemon auto spawn git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@173 fefdeb5f-60dc-0310-8127-8f9354f1896f --- configure.ac | 5 +- doc/todo | 5 +- polyp/Makefile.am | 21 +++++-- polyp/cmdline.c | 38 ++++++++++-- polyp/esdcompat.sh.in | 2 +- polyp/main.c | 8 ++- polyp/module-native-protocol-fd.c | 78 ++++++++++++++++++++++++ polyp/pacat.c | 2 +- polyp/polypaudio.pa | 2 +- polyp/polyplib-context.c | 125 +++++++++++++++++++++++++++++++++----- polyp/polyplib-context.h | 9 +++ polyp/polyplib-internal.h | 1 + polyp/protocol-native.c | 36 +++++++++-- polyp/protocol-native.h | 2 + 14 files changed, 292 insertions(+), 42 deletions(-) create mode 100644 polyp/module-native-protocol-fd.c 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 diff --git a/doc/todo b/doc/todo index e9c82562..19afa7fa 100644 --- a/doc/todo +++ b/doc/todo @@ -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 #include #include +#include #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 +#endif + +#include +#include +#include + +#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 #include #include +#include +#include +#include +#include #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); @@ -305,6 +307,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 -- cgit