correct autospawning

git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@202 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
Lennart Poettering 2004-09-15 13:03:25 +00:00
parent 935826f4f3
commit 8c110d904d
13 changed files with 187 additions and 136 deletions

View file

@ -25,7 +25,8 @@ modlibdir=$(libdir)/polypaudio-@PA_MAJORMINOR@
AM_CFLAGS=-D_GNU_SOURCE -I$(top_srcdir) $(PTHREAD_CFLAGS) AM_CFLAGS=-D_GNU_SOURCE -I$(top_srcdir) $(PTHREAD_CFLAGS)
AM_CFLAGS+=-DDLSEARCHPATH=\"$(modlibdir)\" AM_CFLAGS+=-DDLSEARCHPATH=\"$(modlibdir)\"
AM_CFLAGS+=-DDEFAULT_SCRIPT_FILE=\"$(polypconfdir)/default.pa\" 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_CFLAGS+=-DPOLYPAUDIO_BINARY=\"$(bindir)/polypaudio\"
AM_LDADD=$(PTHREAD_LIBS) -lm AM_LDADD=$(PTHREAD_LIBS) -lm

View file

@ -53,17 +53,23 @@ static const struct pa_conf default_conf = {
#define ENV_SCRIPT_FILE "POLYP_SCRIPT" #define ENV_SCRIPT_FILE "POLYP_SCRIPT"
#define ENV_CONFIG_FILE "POLYP_CONFIG" #define ENV_CONFIG_FILE "POLYP_CONFIG"
#define ENV_AUTOSPAWNED "POLYP_AUTOSPAWNED"
#ifndef DEFAULT_SCRIPT_FILE #ifndef DEFAULT_SCRIPT_FILE
#define DEFAULT_SCRIPT_FILE "/etc/polypaudio/default.pa" #define DEFAULT_SCRIPT_FILE "/etc/polypaudio/default.pa"
#endif #endif
#ifndef DEFAULT_CONFIG_FILE #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 #endif
#define DEFAULT_SCRIPT_FILE_LOCAL ".polypaudio.pa" #define DEFAULT_SCRIPT_FILE_LOCAL ".polypaudio.pa"
#define DEFAULT_CONFIG_FILE_LOCAL ".polypaudio.conf" #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* default_file(const char *envvar, const char *global, const char *local) {
char *p, *h; char *p, *h;
@ -85,10 +91,26 @@ char* default_file(const char *envvar, const char *global, const char *local) {
return pa_xstrdup(global); 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* pa_conf_new(void) {
struct pa_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf)); 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; return c;
} }
@ -223,7 +245,7 @@ int pa_conf_load(struct pa_conf *c, const char *filename) {
assert(c); assert(c);
if (!filename) 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 (!(f = fopen(filename, "r"))) {
if (errno != ENOENT) if (errno != ENOENT)
@ -259,7 +281,7 @@ char *pa_conf_dump(struct pa_conf *c) {
struct pa_strbuf *s = pa_strbuf_new(); struct pa_strbuf *s = pa_strbuf_new();
char *d; 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, "### Default configuration file: %s ###\n", d);
pa_strbuf_printf(s, "verbose = %i\n", !!c->verbose); pa_strbuf_printf(s, "verbose = %i\n", !!c->verbose);

View file

@ -26,6 +26,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <signal.h>
#include "core.h" #include "core.h"
#include "module.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->module_idle_time = 20;
c->scache_idle_time = 20; c->scache_idle_time = 20;
pa_check_for_sigpipe(); pa_check_signal_is_blocked(SIGPIPE);
return c; return c;
} }

View file

@ -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) { 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) { switch (sig) {
case SIGUSR1: case SIGUSR1:

View file

@ -360,7 +360,7 @@ int main(int argc, char *argv[]) {
pa_context_set_state_callback(context, context_state_callback, NULL); pa_context_set_state_callback(context, context_state_callback, NULL);
/* Connect the context */ /* Connect the context */
pa_context_connect_spawn(context, NULL, NULL, NULL); pa_context_connect(context, NULL, 1, NULL);
/* Run the main loop */ /* Run the main loop */
if (pa_mainloop_run(m, &ret) < 0) { if (pa_mainloop_run(m, &ret) < 0) {

View file

@ -292,7 +292,7 @@ int main(int argc, char *argv[]) {
} }
pa_context_set_state_callback(context, context_state_callback, NULL); 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) { if (pa_mainloop_run(m, &ret) < 0) {
fprintf(stderr, "pa_mainloop_run() failed.\n"); fprintf(stderr, "pa_mainloop_run() failed.\n");

View file

@ -85,7 +85,7 @@ struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char *
c->memblock_stat = pa_memblock_stat_new(); c->memblock_stat = pa_memblock_stat_new();
pa_check_for_sigpipe(); pa_check_signal_is_blocked(SIGPIPE);
return c; return c;
} }
@ -365,15 +365,116 @@ static struct sockaddr *resolve_server(const char *server, size_t *len) {
return sa; 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; int r = -1;
assert(c && c->ref >= 1 && c->state == PA_CONTEXT_UNCONNECTED); assert(c && c->ref >= 1 && c->state == PA_CONTEXT_UNCONNECTED);
pa_context_ref(c);
if (!server) 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; server = DEFAULT_SERVER;
}
pa_context_ref(c);
assert(!c->client); assert(!c->client);
@ -562,94 +663,6 @@ const char* pa_get_library_version(void) {
return PACKAGE_VERSION; 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_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_tagstruct *t;
struct pa_operation *o; struct pa_operation *o;

View file

@ -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 the context to the specified server. If server is NULL,
connect to the default server. This routine may but will not always connect to the default server. This routine may but will not always
return synchronously on error. Use pa_context_set_state_callback() to return synchronously on error. Use pa_context_set_state_callback() to
be notified when the connection is established */ be notified when the connection is established. If spawn is non-zero
int pa_context_connect(struct pa_context *c, const char *server); and no specific server is specified or accessible a new daemon is
spawned. If api is non-NULL, the functions specified in the structure
/** Connect the context to a server. If the default server is local are used when forking a new child process. */
* but not accessible, spawn a new daemon. If atfork is not NULL it is int pa_context_connect(struct pa_context *c, const char *server, int spawn, const struct pa_spawn_api *api);
* 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));
/** Terminate the context connection immediately */ /** Terminate the context connection immediately */
void pa_context_disconnect(struct pa_context *c); void pa_context_disconnect(struct pa_context *c);

View file

@ -156,6 +156,25 @@ struct pa_latency_info {
struct timeval timestamp; /**< The time when this latency info was current */ 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 PA_C_DECL_END
#endif #endif

View file

@ -48,6 +48,8 @@
#define ENV_DEFAULT_SOURCE "POLYP_SOURCE" #define ENV_DEFAULT_SOURCE "POLYP_SOURCE"
#define ENV_DEFAULT_SERVER "POLYP_SERVER" #define ENV_DEFAULT_SERVER "POLYP_SERVER"
#define ENV_DEFAULT_BINARY "POLYP_BINARY" #define ENV_DEFAULT_BINARY "POLYP_BINARY"
#define ENV_DISABLE_AUTOSPAWN "POLYP_NOAUTOSPAWN"
#define ENV_AUTOSPAWNED "POLYP_AUTOSPAWNED"
struct pa_context { struct pa_context {
int ref; int ref;

View file

@ -130,7 +130,7 @@ struct pa_simple* pa_simple_new(
if (!(p->context = pa_context_new(pa_mainloop_get_api(p->mainloop), name))) if (!(p->context = pa_context_new(pa_mainloop_get_api(p->mainloop), name)))
goto fail; goto fail;
pa_context_connect(p->context, server); pa_context_connect(p->context, server, 1, NULL);
/* Wait until the context is ready */ /* Wait until the context is ready */
while (pa_context_get_state(p->context) != PA_CONTEXT_READY) { while (pa_context_get_state(p->context) != PA_CONTEXT_READY) {

View file

@ -115,7 +115,7 @@ ssize_t pa_loop_write(int fd, const void*data, size_t size) {
return ret; return ret;
} }
void pa_check_for_sigpipe(void) { void pa_check_signal_is_blocked(int sig) {
struct sigaction sa; struct sigaction sa;
sigset_t set; sigset_t set;
@ -130,10 +130,10 @@ void pa_check_for_sigpipe(void) {
} }
#endif #endif
if (sigismember(&set, SIGPIPE)) if (sigismember(&set, sig))
return; return;
if (sigaction(SIGPIPE, NULL, &sa) < 0) { if (sigaction(sig, NULL, &sa) < 0) {
pa_log(__FILE__": sigaction() failed: %s\n", strerror(errno)); pa_log(__FILE__": sigaction() failed: %s\n", strerror(errno));
return; return;
} }
@ -141,7 +141,7 @@ void pa_check_for_sigpipe(void) {
if (sa.sa_handler != SIG_DFL) if (sa.sa_handler != SIG_DFL)
return; 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 */ /* 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); 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";
}
}

View file

@ -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_read(int fd, void*data, size_t size);
ssize_t pa_loop_write(int fd, const 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_sprintf_malloc(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
char *pa_vsprintf_malloc(const char *format, va_list ap); 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); char *pa_split(const char *c, const char*delimiters, const char **state);
const char *pa_strsignal(int sig);
#endif #endif