X-Git-Url: https://code.delx.au/pulseaudio/blobdiff_plain/ee452b0f06c82dd870de82fb3e6bdd4e14d29f3c..fa499dad06ba6558111cdef64c18f2401e803cff:/polyp/polyplib-context.c diff --git a/polyp/polyplib-context.c b/polyp/polyplib-context.c index cf1a8e60..b15dd8e7 100644 --- a/polyp/polyplib-context.c +++ b/polyp/polyplib-context.c @@ -4,7 +4,7 @@ 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 + 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. @@ -13,7 +13,7 @@ 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 + 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. @@ -34,6 +34,7 @@ #include #include #include +#include #include "polyplib-internal.h" #include "polyplib-context.h" @@ -43,15 +44,19 @@ #include "dynarray.h" #include "socket-client.h" #include "pstream-util.h" -#include "authkey.h" #include "util.h" #include "xmalloc.h" #include "log.h" #include "client-conf.h" #include "socket-util.h" -#define DEFAULT_SERVER "/tmp/polypaudio/native" -#define AUTOSPAWN_LOCK "/tmp/polypaudio/autospawn.lock" +#ifdef HAVE_X11 +#include "client-conf-x11.h" +#endif + +#define AUTOSPAWN_LOCK "autospawn.lock" + + static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = { [PA_COMMAND_REQUEST] = { pa_command_request }, @@ -60,6 +65,16 @@ static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = { [PA_COMMAND_SUBSCRIBE_EVENT] = { pa_command_subscribe_event }, }; +static void unlock_autospawn_lock_file(struct pa_context *c) { + assert(c); + + if (c->autospawn_lock_fd >= 0) { + 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) { struct pa_context *c; assert(mainloop && name); @@ -90,11 +105,18 @@ 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->autospawn_lock_fd = -1; + memset(&c->spawn_api, 0, sizeof(c->spawn_api)); + c->do_autospawn = 0; pa_check_signal_is_blocked(SIGPIPE); c->conf = pa_client_conf_new(); pa_client_conf_load(c->conf, NULL); +#ifdef HAVE_X11 + pa_client_conf_from_x11(c->conf, NULL); +#endif pa_client_conf_env(c->conf); return c; @@ -103,6 +125,8 @@ struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char * static void context_free(struct pa_context *c) { assert(c); + unlock_autospawn_lock_file(c); + while (c->operations) pa_operation_cancel(c->operations); @@ -127,6 +151,8 @@ static void context_free(struct pa_context *c) { if (c->conf) pa_client_conf_free(c->conf); + + pa_strlist_free(c->server_list); pa_xfree(c->name); pa_xfree(c); @@ -311,7 +337,7 @@ static void setup_context(struct pa_context *c, struct pa_iochannel *io) { 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) { + if (!c->conf->cookie_valid) { pa_context_fail(c, PA_ERROR_AUTHKEY); goto finish; } @@ -320,7 +346,7 @@ static void setup_context(struct pa_context *c, struct pa_iochannel *io) { assert(t); pa_tagstruct_putu32(t, PA_COMMAND_AUTH); pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_put_arbitrary(t, c->auth_cookie, sizeof(c->auth_cookie)); + pa_tagstruct_put_arbitrary(t, c->conf->cookie, sizeof(c->conf->cookie)); pa_pstream_send_tagstruct(c->pstream, t); pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c); @@ -331,38 +357,9 @@ 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); -} - -static int default_server_is_running(void) { - struct stat st; - - if (DEFAULT_SERVER[0] != '/') - return 1; - - if (stat(DEFAULT_SERVER, &st) < 0) - return 0; +static void on_connection(struct pa_socket_client *client, struct pa_iochannel*io, void *userdata); - return 1; -} -static int context_connect_spawn(struct pa_context *c, const struct pa_spawn_api *api) { +static int context_connect_spawn(struct pa_context *c) { pid_t pid; int status, r; int fds[2] = { -1, -1} ; @@ -376,15 +373,20 @@ static int context_connect_spawn(struct pa_context *c, const struct pa_spawn_api goto fail; } - if (api && api->prefork) - api->prefork(); + pa_fd_set_cloexec(fds[0], 1); + + pa_socket_low_delay(fds[0]); + pa_socket_low_delay(fds[1]); + + if (c->spawn_api.prefork) + c->spawn_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(); + if (c->spawn_api.postfork) + c->spawn_api.postfork(); goto fail; } else if (!pid) { @@ -396,10 +398,11 @@ static int context_connect_spawn(struct pa_context *c, const struct pa_spawn_api char *argv[MAX_ARGS+1]; int n; + /* Not required, since fds[0] has CLOEXEC enabled anyway */ close(fds[0]); - if (api && api->atfork) - api->atfork(); + if (c->spawn_api.atfork) + c->spawn_api.atfork(); /* Setup argv */ @@ -424,14 +427,15 @@ static int context_connect_spawn(struct pa_context *c, const struct pa_spawn_api execv(argv[0], argv); _exit(1); +#undef MAX_ARGS } /* Parent */ r = waitpid(pid, &status, 0); - if (api && api->postfork) - api->postfork(); + if (c->spawn_api.postfork) + c->spawn_api.postfork(); if (r < 0) { pa_log(__FILE__": waitpid() failed: %s\n", strerror(errno)); @@ -447,7 +451,9 @@ static int context_connect_spawn(struct pa_context *c, const struct pa_spawn_api c->local = 1; io = pa_iochannel_new(c->mainloop, fds[0], fds[0]); + setup_context(c, io); + unlock_autospawn_lock_file(c); pa_context_unref(c); @@ -459,71 +465,129 @@ fail: if (fds[1] != -1) close(fds[1]); + unlock_autospawn_lock_file(c); + pa_context_unref(c); return -1; } -int pa_context_connect(struct pa_context *c, const char *server, int spawn, const struct pa_spawn_api *api) { +static int try_next_connection(struct pa_context *c) { + char *u = NULL; int r = -1; - assert(c && c->ref >= 1 && c->state == PA_CONTEXT_UNCONNECTED); - - if (!server) - server = c->conf->default_server; + assert(c && !c->client); - if (!server && spawn && c->conf->autospawn) { - int lock_fd = pa_lock_lockfile(AUTOSPAWN_LOCK); + for (;;) { + if (u) + pa_xfree(u); + u = NULL; + + c->server_list = pa_strlist_pop(c->server_list, &u); - if (!default_server_is_running()) { - int r = context_connect_spawn(c, api); + if (!u) { - if (lock_fd >= 0) - pa_unlock_lockfile(lock_fd); - return r; + if (c->do_autospawn) { + r = context_connect_spawn(c); + goto finish; + } + + pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED); + goto finish; } - - if (lock_fd >= 0) - pa_unlock_lockfile(lock_fd); + +/* pa_log(__FILE__": Trying to connect to %s...\n", u); */ + + if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT))) + continue; + + c->local = pa_socket_client_is_local(c->client); + pa_socket_client_set_callback(c->client, on_connection, c); + break; } + + r = 0; + +finish: + if (u) + pa_xfree(u); - if (!server) - server = DEFAULT_SERVER; + return r; +} - pa_context_ref(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); - assert(!c->client); + pa_context_ref(c); - if (*server == '/') { - if (!(c->client = pa_socket_client_new_unix(c->mainloop, server))) { - pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED); + pa_socket_client_unref(client); + c->client = NULL; + + if (!io) { + /* Try the item in the list */ + if (errno == ECONNREFUSED || errno == ETIMEDOUT || errno == EHOSTUNREACH) { + try_next_connection(c); goto finish; } - c->local = 1; - } else { - struct sockaddr* sa; - size_t sa_len; + pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED); + goto finish; + } + + unlock_autospawn_lock_file(c); + setup_context(c, io); + +finish: + pa_context_unref(c); +} + +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); + + if (!server) + server = c->conf->default_server; + - if (!(sa = pa_resolve_server(server, &sa_len, PA_NATIVE_DEFAULT_PORT))) { + pa_context_ref(c); + + assert(!c->server_list); + + if (server) { + if (!(c->server_list = pa_strlist_parse(server))) { pa_context_fail(c, PA_ERROR_INVALIDSERVER); goto finish; } + } else { + char *d; + char ufn[PATH_MAX]; + + /* Prepend in reverse order */ + + if ((d = getenv("DISPLAY"))) + c->server_list = pa_strlist_prepend(c->server_list, d); + + c->server_list = pa_strlist_prepend(c->server_list, "tcp6:localhost"); + c->server_list = pa_strlist_prepend(c->server_list, "localhost"); + c->server_list = pa_strlist_prepend(c->server_list, pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET, ufn, sizeof(ufn))); - c->client = pa_socket_client_new_sockaddr(c->mainloop, sa, sa_len); - pa_xfree(sa); + /* Wrap the connection attempts in a single transaction for sane autospwan locking */ + if (spawn && c->conf->autospawn) { + char lf[PATH_MAX]; - if (!c->client) { - pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED); - goto finish; + pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf)); + assert(c->autospawn_lock_fd <= 0); + c->autospawn_lock_fd = pa_lock_lockfile(lf); + + if (api) + c->spawn_api = *api; + c->do_autospawn = 1; } - c->local = 0; } - pa_socket_client_set_callback(c->client, on_connection, c); pa_context_set_state(c, PA_CONTEXT_CONNECTING); - - r = 0; + r = try_next_connection(c); finish: pa_context_unref(c);