From 8c110d904ddf30ce35c9a0c18449436af18a5095 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 15 Sep 2004 13:03:25 +0000 Subject: correct autospawning git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@202 fefdeb5f-60dc-0310-8127-8f9354f1896f --- polyp/Makefile.am | 3 +- polyp/conf.c | 30 ++++++- polyp/core.c | 3 +- polyp/main.c | 14 +--- polyp/pacat.c | 2 +- polyp/pactl.c | 2 +- polyp/polyplib-context.c | 199 ++++++++++++++++++++++++---------------------- polyp/polyplib-context.h | 21 ++--- polyp/polyplib-def.h | 19 +++++ polyp/polyplib-internal.h | 2 + polyp/polyplib-simple.c | 2 +- polyp/util.c | 22 ++++- polyp/util.h | 4 +- 13 files changed, 187 insertions(+), 136 deletions(-) diff --git a/polyp/Makefile.am b/polyp/Makefile.am index 9c672a3b..fc3fb18d 100644 --- a/polyp/Makefile.am +++ b/polyp/Makefile.am @@ -25,7 +25,8 @@ modlibdir=$(libdir)/polypaudio-@PA_MAJORMINOR@ AM_CFLAGS=-D_GNU_SOURCE -I$(top_srcdir) $(PTHREAD_CFLAGS) AM_CFLAGS+=-DDLSEARCHPATH=\"$(modlibdir)\" AM_CFLAGS+=-DDEFAULT_SCRIPT_FILE=\"$(polypconfdir)/default.pa\" -AM_CFLAGS+=-DDEFAULT_CONFIG_FILE=\"$(polypconfdir)/config\" +AM_CFLAGS+=-DDEFAULT_CONFIG_FILE=\"$(polypconfdir)/default.conf\" +AM_CFLAGS+=-DAUTOSPAWN_CONFIG_FILE=\"$(polypconfdir)/autospawn.conf\" AM_CFLAGS+=-DPOLYPAUDIO_BINARY=\"$(bindir)/polypaudio\" AM_LDADD=$(PTHREAD_LIBS) -lm diff --git a/polyp/conf.c b/polyp/conf.c index 3a894a9b..b74a5ede 100644 --- a/polyp/conf.c +++ b/polyp/conf.c @@ -53,17 +53,23 @@ static const struct pa_conf default_conf = { #define ENV_SCRIPT_FILE "POLYP_SCRIPT" #define ENV_CONFIG_FILE "POLYP_CONFIG" +#define ENV_AUTOSPAWNED "POLYP_AUTOSPAWNED" #ifndef DEFAULT_SCRIPT_FILE #define DEFAULT_SCRIPT_FILE "/etc/polypaudio/default.pa" #endif #ifndef DEFAULT_CONFIG_FILE -#define DEFAULT_CONFIG_FILE "/etc/polypaudio/config" +#define DEFAULT_CONFIG_FILE "/etc/polypaudio/default.conf" +#endif + +#ifndef AUTOSPAWN_CONFIG_FILE +#define AUTOSPAWN_CONFIG_FILE "/etc/polypaudio/autospawn.conf" #endif #define DEFAULT_SCRIPT_FILE_LOCAL ".polypaudio.pa" #define DEFAULT_CONFIG_FILE_LOCAL ".polypaudio.conf" +#define AUTOSPAWN_CONFIG_FILE_LOCAL ".polypaudio-autospawn.conf" char* default_file(const char *envvar, const char *global, const char *local) { char *p, *h; @@ -85,10 +91,26 @@ char* default_file(const char *envvar, const char *global, const char *local) { return pa_xstrdup(global); } +char *default_config_file(void) { + char *b; + int autospawned = 0; + + if ((b = getenv(ENV_AUTOSPAWNED))) + autospawned = pa_parse_boolean(b) > 0; + + return default_file(ENV_CONFIG_FILE, + autospawned ? AUTOSPAWN_CONFIG_FILE : DEFAULT_CONFIG_FILE, + autospawned ? AUTOSPAWN_CONFIG_FILE_LOCAL : DEFAULT_CONFIG_FILE_LOCAL); + +} + +char *default_script_file(void) { + return default_file(ENV_SCRIPT_FILE, DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_LOCAL); +} struct pa_conf* pa_conf_new(void) { struct pa_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf)); - c->default_script_file = default_file(ENV_SCRIPT_FILE, DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_LOCAL); + c->default_script_file = default_script_file(); return c; } @@ -223,7 +245,7 @@ int pa_conf_load(struct pa_conf *c, const char *filename) { assert(c); if (!filename) - filename = def = default_file(ENV_CONFIG_FILE, DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_LOCAL); + filename = def = default_config_file(); if (!(f = fopen(filename, "r"))) { if (errno != ENOENT) @@ -259,7 +281,7 @@ 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); + d = default_config_file(); pa_strbuf_printf(s, "### Default configuration file: %s ###\n", d); pa_strbuf_printf(s, "verbose = %i\n", !!c->verbose); diff --git a/polyp/core.c b/polyp/core.c index 80abe9fe..58035087 100644 --- a/polyp/core.c +++ b/polyp/core.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "core.h" #include "module.h" @@ -79,7 +80,7 @@ struct pa_core* pa_core_new(struct pa_mainloop_api *m) { c->module_idle_time = 20; c->scache_idle_time = 20; - pa_check_for_sigpipe(); + pa_check_signal_is_blocked(SIGPIPE); return c; } diff --git a/polyp/main.c b/polyp/main.c index 04bcceef..e44fc013 100644 --- a/polyp/main.c +++ b/polyp/main.c @@ -58,20 +58,8 @@ static void drop_root(void) { } } -static const char* signal_name(int s) { - switch(s) { - case SIGINT: return "SIGINT"; - case SIGTERM: return "SIGTERM"; - case SIGUSR1: return "SIGUSR1"; - case SIGUSR2: return "SIGUSR2"; - case SIGXCPU: return "SIGXCPU"; - case SIGPIPE: return "SIGPIPE"; - default: return "UNKNOWN SIGNAL"; - } -} - static void signal_callback(struct pa_mainloop_api*m, struct pa_signal_event *e, int sig, void *userdata) { - pa_log(__FILE__": Got signal %s.\n", signal_name(sig)); + pa_log(__FILE__": Got signal %s.\n", pa_strsignal(sig)); switch (sig) { case SIGUSR1: diff --git a/polyp/pacat.c b/polyp/pacat.c index b499f71a..f4597714 100644 --- a/polyp/pacat.c +++ b/polyp/pacat.c @@ -360,7 +360,7 @@ int main(int argc, char *argv[]) { pa_context_set_state_callback(context, context_state_callback, NULL); /* Connect the context */ - pa_context_connect_spawn(context, NULL, NULL, NULL); + pa_context_connect(context, NULL, 1, NULL); /* Run the main loop */ if (pa_mainloop_run(m, &ret) < 0) { diff --git a/polyp/pactl.c b/polyp/pactl.c index dfa11b70..c93fd235 100644 --- a/polyp/pactl.c +++ b/polyp/pactl.c @@ -292,7 +292,7 @@ int main(int argc, char *argv[]) { } pa_context_set_state_callback(context, context_state_callback, NULL); - pa_context_connect(context, NULL); + pa_context_connect(context, NULL, 1, NULL); if (pa_mainloop_run(m, &ret) < 0) { fprintf(stderr, "pa_mainloop_run() failed.\n"); diff --git a/polyp/polyplib-context.c b/polyp/polyplib-context.c index 63b42eb3..7fef6b12 100644 --- a/polyp/polyplib-context.c +++ b/polyp/polyplib-context.c @@ -85,7 +85,7 @@ struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char * c->memblock_stat = pa_memblock_stat_new(); - pa_check_for_sigpipe(); + pa_check_signal_is_blocked(SIGPIPE); return c; } @@ -365,15 +365,116 @@ static struct sockaddr *resolve_server(const char *server, size_t *len) { return sa; } -int pa_context_connect(struct pa_context *c, const char *server) { +static int is_running(void) { + struct stat st; + + if (DEFAULT_SERVER[0] != '/') + return 1; + + if (stat(DEFAULT_SERVER, &st) < 0) + return 0; + + return 1; +} + +static int context_connect_spawn(struct pa_context *c, const struct pa_spawn_api *api) { + pid_t pid; + int status, r; + int fds[2] = { -1, -1} ; + struct pa_iochannel *io; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { + pa_log(__FILE__": socketpair() failed: %s\n", strerror(errno)); + pa_context_fail(c, PA_ERROR_INTERNAL); + goto fail; + } + + if (api && api->prefork) + api->prefork(); + + if ((pid = fork()) < 0) { + pa_log(__FILE__": fork() failed: %s\n", strerror(errno)); + pa_context_fail(c, PA_ERROR_INTERNAL); + + if (api && api->postfork) + api->postfork(); + + goto fail; + } else if (!pid) { + char t[128]; + char *p; + /* Child */ + + close(fds[0]); + + if (api && api->atfork) + api->atfork(); + + if (!(p = getenv(ENV_DEFAULT_BINARY))) + p = POLYPAUDIO_BINARY; + + snprintf(t, sizeof(t), "%s=1", ENV_AUTOSPAWNED); + putenv(t); + + snprintf(t, sizeof(t), "-Lmodule-native-protocol-fd fd=%i", fds[1]); + execl(p, p, t, NULL); + + exit(1); + } + + /* Parent */ + + r = waitpid(pid, &status, 0); + + if (api && api->postfork) + api->postfork(); + + if (r < 0) { + pa_log(__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; +} + + + +int pa_context_connect(struct pa_context *c, const char *server, int spawn, const struct pa_spawn_api *api) { int r = -1; assert(c && c->ref >= 1 && c->state == PA_CONTEXT_UNCONNECTED); - pa_context_ref(c); - if (!server) - if (!(server = getenv(ENV_DEFAULT_SERVER))) + if (!(server = getenv(ENV_DEFAULT_SERVER))) { + if (spawn && !is_running()) { + char *b; + + if ((b = getenv(ENV_DISABLE_AUTOSPAWN))) + if (pa_parse_boolean(b) > 1) + return -1; + + return context_connect_spawn(c, api); + } + server = DEFAULT_SERVER; + } + + pa_context_ref(c); assert(!c->client); @@ -562,94 +663,6 @@ 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), void (*prefork)(void), void (*postfork)(void)) { - pid_t pid; - int status, r; - 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) { - pa_log(__FILE__": socketpair() failed: %s\n", strerror(errno)); - pa_context_fail(c, PA_ERROR_INTERNAL); - goto fail; - } - - if (prefork) - prefork(); - - if ((pid = fork()) < 0) { - pa_log(__FILE__": fork() failed: %s\n", strerror(errno)); - pa_context_fail(c, PA_ERROR_INTERNAL); - - if (postfork) - postfork(); - - 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", "-lsyslog", "-X 5", t, NULL); - - exit(1); - } - - /* Parent */ - - r = waitpid(pid, &status, 0); - - if (postfork) - postfork(); - - if (r < 0) { - pa_log(__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; -} - struct pa_operation* pa_context_set_default_sink(struct pa_context *c, const char *name, void(*cb)(struct pa_context*c, int success, void *userdata), void *userdata) { struct pa_tagstruct *t; struct pa_operation *o; diff --git a/polyp/polyplib-context.h b/polyp/polyplib-context.h index 4b199751..84e47b64 100644 --- a/polyp/polyplib-context.h +++ b/polyp/polyplib-context.h @@ -76,22 +76,11 @@ enum pa_context_state pa_context_get_state(struct pa_context *c); /** Connect the context to the specified server. If server is NULL, connect to the default server. This routine may but will not always 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. (It is not safe to - * close all file descriptors unconditionally, since a UNIX socket is - * passed to the new process.) if prefork is not NULL it is run just - * before forking in the parent process. Use this to block SIGCHLD - * handling if required. If postfork is not NULL it is run just after - * forking in the parent process. Use this to unblock SIGCHLD if - * required. The function will waitpid() on the daemon's PID, but - * will not block or ignore SIGCHLD signals, since this cannot be done - * in a thread compatible way. \since 0.4 */ -int pa_context_connect_spawn(struct pa_context *c, void (*atfork)(void), void (*prefork)(void), void (*postfork)(void)); +be notified when the connection is established. If spawn is non-zero +and no specific server is specified or accessible a new daemon is +spawned. If api is non-NULL, the functions specified in the structure +are used when forking a new child process. */ +int pa_context_connect(struct pa_context *c, const char *server, int spawn, const struct pa_spawn_api *api); /** Terminate the context connection immediately */ void pa_context_disconnect(struct pa_context *c); diff --git a/polyp/polyplib-def.h b/polyp/polyplib-def.h index 4a49a1f8..2aa33338 100644 --- a/polyp/polyplib-def.h +++ b/polyp/polyplib-def.h @@ -156,6 +156,25 @@ struct pa_latency_info { struct timeval timestamp; /**< The time when this latency info was current */ }; +/** A structure for the spawn api. This may be used to integrate auto + * spawned daemons into your application. For more information see + * pa_context_connect(). When spawning a new child process the + * waitpid() is used on the child's PID. The spawn routine will not + * block or ignore SIGCHLD signals, since this cannot be done in a + * thread compatible way. You might have to do this in + * prefork/postfork. \since 0.4 */ +struct pa_spawn_api { + void (*prefork)(void); /**< Is called just before the fork in the parent process. May be NULL. */ + void (*postfork)(void); /**< Is called immediately after the fork in the parent process. May be NULL.*/ + void (*atfork)(void); /**< Is called immediately after the + * fork in the child process. May be + * NULL. It is not safe to close all + * file descriptors in this function + * unconditionally, since a UNIX socket + * (created using socketpair()) is + * passed to the new process. */ +}; + PA_C_DECL_END #endif diff --git a/polyp/polyplib-internal.h b/polyp/polyplib-internal.h index de63b1ba..98fd7924 100644 --- a/polyp/polyplib-internal.h +++ b/polyp/polyplib-internal.h @@ -48,6 +48,8 @@ #define ENV_DEFAULT_SOURCE "POLYP_SOURCE" #define ENV_DEFAULT_SERVER "POLYP_SERVER" #define ENV_DEFAULT_BINARY "POLYP_BINARY" +#define ENV_DISABLE_AUTOSPAWN "POLYP_NOAUTOSPAWN" +#define ENV_AUTOSPAWNED "POLYP_AUTOSPAWNED" struct pa_context { int ref; diff --git a/polyp/polyplib-simple.c b/polyp/polyplib-simple.c index 1ac08869..36e6e757 100644 --- a/polyp/polyplib-simple.c +++ b/polyp/polyplib-simple.c @@ -130,7 +130,7 @@ struct pa_simple* pa_simple_new( if (!(p->context = pa_context_new(pa_mainloop_get_api(p->mainloop), name))) goto fail; - pa_context_connect(p->context, server); + pa_context_connect(p->context, server, 1, NULL); /* Wait until the context is ready */ while (pa_context_get_state(p->context) != PA_CONTEXT_READY) { diff --git a/polyp/util.c b/polyp/util.c index 3ab6d51a..bb71bbf9 100644 --- a/polyp/util.c +++ b/polyp/util.c @@ -115,7 +115,7 @@ ssize_t pa_loop_write(int fd, const void*data, size_t size) { return ret; } -void pa_check_for_sigpipe(void) { +void pa_check_signal_is_blocked(int sig) { struct sigaction sa; sigset_t set; @@ -130,10 +130,10 @@ void pa_check_for_sigpipe(void) { } #endif - if (sigismember(&set, SIGPIPE)) + if (sigismember(&set, sig)) return; - if (sigaction(SIGPIPE, NULL, &sa) < 0) { + if (sigaction(sig, NULL, &sa) < 0) { pa_log(__FILE__": sigaction() failed: %s\n", strerror(errno)); return; } @@ -141,7 +141,7 @@ void pa_check_for_sigpipe(void) { if (sa.sa_handler != SIG_DFL) return; - pa_log(__FILE__": WARNING: SIGPIPE is not trapped. This might cause malfunction!\n"); + pa_log(__FILE__": WARNING: %s is not trapped. This might cause malfunction!\n", pa_strsignal(sig)); } /* The following is based on an example from the GNU libc documentation */ @@ -389,3 +389,17 @@ char *pa_split(const char *c, const char *delimiter, const char**state) { return pa_xstrndup(current, l); } + +const char *pa_strsignal(int sig) { + switch(sig) { + case SIGINT: return "SIGINT"; + case SIGTERM: return "SIGTERM"; + case SIGUSR1: return "SIGUSR1"; + case SIGUSR2: return "SIGUSR2"; + case SIGXCPU: return "SIGXCPU"; + case SIGPIPE: return "SIGPIPE"; + case SIGCHLD: return "SIGCHLD"; + default: return "UNKNOWN SIGNAL"; + } +} + diff --git a/polyp/util.h b/polyp/util.h index eae98e6e..f5cda200 100644 --- a/polyp/util.h +++ b/polyp/util.h @@ -36,7 +36,7 @@ int pa_make_secure_dir(const char* dir); ssize_t pa_loop_read(int fd, void*data, size_t size); ssize_t pa_loop_write(int fd, const void*data, size_t size); -void pa_check_for_sigpipe(void); +void pa_check_signal_is_blocked(int sig); char *pa_sprintf_malloc(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2); char *pa_vsprintf_malloc(const char *format, va_list ap); @@ -61,4 +61,6 @@ int pa_parse_boolean(const char *s); char *pa_split(const char *c, const char*delimiters, const char **state); +const char *pa_strsignal(int sig); + #endif -- cgit