From c005bd466651d8720aef3198d73bc0a3d459f953 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 11 Nov 2004 21:18:33 +0000 Subject: add username to runtime directory name in /tmp/ rework autospawning code and x11 credential publishing add support for IPv6 reenable LOWDELAY for tcp sockets git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@280 fefdeb5f-60dc-0310-8127-8f9354f1896f --- polyp/Makefile.am | 52 +++++++++-- polyp/client-conf-x11.c | 5 +- polyp/module-protocol-stub.c | 51 ++++++++--- polyp/module-x11-publish.c | 14 ++- polyp/native-common.h | 3 +- polyp/polyplib-context.c | 212 +++++++++++++++++++++++++++---------------- polyp/polyplib-internal.h | 6 ++ polyp/props.c | 7 ++ polyp/props.h | 3 + polyp/protocol-native.c | 28 +++++- polyp/socket-client.c | 191 +++++++++++++++++++++++++++++--------- polyp/socket-client.h | 6 +- polyp/socket-server.c | 145 ++++++++++++++++++++++++++++- polyp/socket-server.h | 3 + polyp/socket-util.c | 39 +------- polyp/socket-util.h | 3 - polyp/strlist-test.c | 39 ++++++++ polyp/strlist.c | 137 ++++++++++++++++++++++++++++ polyp/strlist.h | 47 ++++++++++ polyp/util.c | 83 +++++++++++++---- polyp/util.h | 5 + 21 files changed, 870 insertions(+), 209 deletions(-) create mode 100644 polyp/strlist-test.c create mode 100644 polyp/strlist.c create mode 100644 polyp/strlist.h (limited to 'polyp') diff --git a/polyp/Makefile.am b/polyp/Makefile.am index cc9705c2..c4f9e2d2 100644 --- a/polyp/Makefile.am +++ b/polyp/Makefile.am @@ -39,7 +39,8 @@ noinst_PROGRAMS = \ parec-simple \ cpulimit-test \ cpulimit-test2 \ - voltest + voltest \ + strlist-test polypconf_DATA=default.pa daemon.conf client.conf @@ -85,21 +86,26 @@ modlib_LTLIBRARIES= \ libpdispatch.la \ libauthkey.la \ libauthkey-prop.la \ + libstrlist.la \ libprotocol-simple.la \ libprotocol-esound.la \ libprotocol-native.la \ module-cli.la \ module-cli-protocol-tcp.la \ + module-cli-protocol-tcp6.la \ module-cli-protocol-unix.la \ module-pipe-sink.la \ module-pipe-source.la \ module-oss.la \ module-oss-mmap.la \ module-simple-protocol-tcp.la \ + module-simple-protocol-tcp6.la \ module-simple-protocol-unix.la \ module-esound-protocol-tcp.la \ + module-esound-protocol-tcp6.la \ module-esound-protocol-unix.la \ module-native-protocol-tcp.la \ + module-native-protocol-tcp6.la \ module-native-protocol-unix.la \ module-native-protocol-fd.la \ module-sine.la \ @@ -114,16 +120,20 @@ modlib_LTLIBRARIES= \ SYMDEF_FILES= \ module-cli-symdef.h \ module-cli-protocol-tcp-symdef.h \ + module-cli-protocol-tcp6-symdef.h \ module-cli-protocol-unix-symdef.h \ module-pipe-sink-symdef.h \ module-pipe-source-symdef.h \ module-oss-symdef.h \ module-oss-mmap-symdef.h \ module-simple-protocol-tcp-symdef.h \ + module-simple-protocol-tcp6-symdef.h \ module-simple-protocol-unix-symdef.h \ module-esound-protocol-tcp-symdef.h \ + module-esound-protocol-tcp6-symdef.h \ module-esound-protocol-unix-symdef.h \ module-native-protocol-tcp-symdef.h \ + module-native-protocol-tcp6-symdef.h \ module-native-protocol-unix-symdef.h \ module-native-protocol-fd-symdef.h \ module-sine-symdef.h \ @@ -244,13 +254,17 @@ libcli_la_SOURCES = cli.c cli.h libcli_la_LDFLAGS = -avoid-version libcli_la_LIBADD = $(AM_LIBADD) libiochannel.la libioline.la +libstrlist_la_SOURCES = strlist.c strlist.h +libstrlist_la_LDFLAGS = -avoid-version +libstrlist_la_LIBADD = $(AM_LIBADD) + libprotocol_cli_la_SOURCES = protocol-cli.c protocol-cli.h libprotocol_cli_la_LDFLAGS = -avoid-version libprotocol_cli_la_LIBADD = $(AM_LIBADD) libsocket-server.la libiochannel.la libcli.la libprotocol_native_la_SOURCES = protocol-native.c protocol-native.h native-common.h libprotocol_native_la_LDFLAGS = -avoid-version -libprotocol_native_la_LIBADD = $(AM_LIBADD) libsocket-server.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauthkey-prop.la +libprotocol_native_la_LIBADD = $(AM_LIBADD) libsocket-server.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauthkey-prop.la libstrlist.la libtagstruct_la_SOURCES = tagstruct.c tagstruct.h libtagstruct_la_LDFLAGS = -avoid-version @@ -273,6 +287,11 @@ module_simple_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_SIMPLE $ module_simple_protocol_tcp_la_LDFLAGS = -module -avoid-version module_simple_protocol_tcp_la_LIBADD = $(AM_LIBADD) libprotocol-simple.la libsocket-server.la +module_simple_protocol_tcp6_la_SOURCES = module-protocol-stub.c +module_simple_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS) +module_simple_protocol_tcp6_la_LDFLAGS = -module -avoid-version +module_simple_protocol_tcp6_la_LIBADD = $(AM_LIBADD) libprotocol-simple.la libsocket-server.la + module_simple_protocol_unix_la_SOURCES = module-protocol-stub.c module_simple_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS) module_simple_protocol_unix_la_LDFLAGS = -module -avoid-version @@ -283,6 +302,11 @@ module_cli_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CF module_cli_protocol_tcp_la_LDFLAGS = -module -avoid-version module_cli_protocol_tcp_la_LIBADD = $(AM_LIBADD) libprotocol-cli.la libsocket-server.la +module_cli_protocol_tcp6_la_SOURCES = module-protocol-stub.c +module_cli_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS) +module_cli_protocol_tcp6_la_LDFLAGS = -module -avoid-version +module_cli_protocol_tcp6_la_LIBADD = $(AM_LIBADD) libprotocol-cli.la libsocket-server.la + module_cli_protocol_unix_la_SOURCES = module-protocol-stub.c module_cli_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS) module_cli_protocol_unix_la_LDFLAGS = -module -avoid-version @@ -293,6 +317,11 @@ module_native_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_NATIVE $ module_native_protocol_tcp_la_LDFLAGS = -module -avoid-version module_native_protocol_tcp_la_LIBADD = $(AM_LIBADD) libprotocol-native.la libsocket-server.la +module_native_protocol_tcp6_la_SOURCES = module-protocol-stub.c +module_native_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS) +module_native_protocol_tcp6_la_LDFLAGS = -module -avoid-version +module_native_protocol_tcp6_la_LIBADD = $(AM_LIBADD) libprotocol-native.la libsocket-server.la + module_native_protocol_unix_la_SOURCES = module-protocol-stub.c module_native_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS) module_native_protocol_unix_la_LDFLAGS = -module -avoid-version @@ -308,6 +337,11 @@ module_esound_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_ESOUND $ module_esound_protocol_tcp_la_LDFLAGS = -module -avoid-version module_esound_protocol_tcp_la_LIBADD = $(AM_LIBADD) libprotocol-esound.la libsocket-server.la +module_esound_protocol_tcp6_la_SOURCES = module-protocol-stub.c +module_esound_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS) +module_esound_protocol_tcp6_la_LDFLAGS = -module -avoid-version +module_esound_protocol_tcp6_la_LIBADD = $(AM_LIBADD) libprotocol-esound.la libsocket-server.la + module_esound_protocol_unix_la_SOURCES = module-protocol-stub.c module_esound_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS) module_esound_protocol_unix_la_LDFLAGS = -module -avoid-version @@ -399,7 +433,9 @@ libpolyp_@PA_MAJORMINOR@_la_SOURCES = polyplib.h \ log.c log.h \ gcc-printf.h \ client-conf.c client-conf.h \ - conf-parser.c conf-parser.h + conf-parser.c conf-parser.h \ + strlist.c strlist.h \ + strbuf.c strbuf.h libpolyp_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) libpolyp_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0 @@ -449,11 +485,15 @@ voltest_SOURCES = voltest.c sample.c voltest_CFLAGS = $(AM_CFLAGS) voltest_LDADD = $(AM_LDADD) -cpulimit_test_SOURCES = cpulimit-test.c cpulimit.c util.c log.c +strlist_test_SOURCES = strlist-test.c strlist.c strlist.h strbuf.c strbuf.h util.c util.h xmalloc.c xmalloc.h log.c log.h +strlist_test_CFLAGS = $(AM_CFLAGS) +strlist_test_LDADD = $(AM_LDADD) + +cpulimit_test_SOURCES = cpulimit-test.c cpulimit.c util.c log.c cpulimit.h util.h log.h cpulimit_test_CFLAGS = $(AM_CFLAGS) cpulimit_test_LDADD = $(AM_LDADD) libpolyp-mainloop-@PA_MAJORMINOR@.la -cpulimit_test2_SOURCES = cpulimit-test.c cpulimit.c util.c log.c +cpulimit_test2_SOURCES = cpulimit-test.c cpulimit.c util.c log.c cpulimit.h util.h log.h cpulimit_test2_CFLAGS = $(AM_CFLAGS) -DTEST2 cpulimit_test2_LDADD = $(AM_LDADD) libpolyp-mainloop-@PA_MAJORMINOR@.la @@ -487,7 +527,7 @@ module_x11_bell_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA module_x11_publish_la_SOURCES = module-x11-publish.c module_x11_publish_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS) module_x11_publish_la_LDFLAGS = -module -avoid-version -module_x11_publish_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libx11wrap.la libauthkey.la libauthkey-prop.la libx11prop.la +module_x11_publish_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libx11wrap.la libauthkey.la libauthkey-prop.la libx11prop.la libstrlist.la bin_PROGRAMS+= \ pax11publish diff --git a/polyp/client-conf-x11.c b/polyp/client-conf-x11.c index 54c3b06a..f667bd78 100644 --- a/polyp/client-conf-x11.c +++ b/polyp/client-conf-x11.c @@ -39,7 +39,10 @@ int pa_client_conf_from_x11(struct pa_client_conf *c, const char *dname) { Display *d = NULL; int ret = -1; char t[1024]; - + + if (!dname && !getenv("DISPLAY")) + goto finish; + if (!(d = XOpenDisplay(dname))) { pa_log(__FILE__": XOpenDisplay() failed\n"); goto finish; diff --git a/polyp/module-protocol-stub.c b/polyp/module-protocol-stub.c index 6ff7d774..6a0f88a2 100644 --- a/polyp/module-protocol-stub.c +++ b/polyp/module-protocol-stub.c @@ -38,10 +38,14 @@ #include "modargs.h" #include "log.h" #include "native-common.h" +#include "util.h" #ifdef USE_TCP_SOCKETS #define SOCKET_DESCRIPTION "(TCP sockets)" #define SOCKET_USAGE "port= loopback=" +#elif defined(USE_TCP6_SOCKETS) +#define SOCKET_DESCRIPTION "(TCP/IPv6 sockets)" +#define SOCKET_USAGE "port= loopback=" #else #define SOCKET_DESCRIPTION "(UNIX sockets)" #define SOCKET_USAGE "socket=" @@ -53,10 +57,12 @@ #define protocol_free pa_protocol_simple_free #define TCPWRAP_SERVICE "polypaudio-simple" #define IPV4_PORT 4711 - #define UNIX_SOCKET "/tmp/polypaudio/simple" + #define UNIX_SOCKET "simple" #define MODULE_ARGUMENTS "rate", "format", "channels", "sink", "source", "playback", "record", - #ifdef USE_TCP_SOCKETS + #if defined(USE_TCP_SOCKETS) #include "module-simple-protocol-tcp-symdef.h" + #elif defined(USE_TCP6_SOCKETS) + #include "module-simple-protocol-tcp6-symdef.h" #else #include "module-simple-protocol-unix-symdef.h" #endif @@ -68,10 +74,12 @@ #define protocol_free pa_protocol_cli_free #define TCPWRAP_SERVICE "polypaudio-cli" #define IPV4_PORT 4712 - #define UNIX_SOCKET "/tmp/polypaudio/cli" + #define UNIX_SOCKET "cli" #define MODULE_ARGUMENTS #ifdef USE_TCP_SOCKETS #include "module-cli-protocol-tcp-symdef.h" + #elif defined(USE_TCP6_SOCKETS) + #include "module-cli-protocol-tcp6-symdef.h" #else #include "module-cli-protocol-unix-symdef.h" #endif @@ -83,10 +91,12 @@ #define protocol_free pa_protocol_native_free #define TCPWRAP_SERVICE "polypaudio-native" #define IPV4_PORT PA_NATIVE_DEFAULT_PORT - #define UNIX_SOCKET PA_NATIVE_DEFAULT_SERVER_UNIX + #define UNIX_SOCKET PA_NATIVE_DEFAULT_UNIX_SOCKET #define MODULE_ARGUMENTS "public", "cookie", #ifdef USE_TCP_SOCKETS #include "module-native-protocol-tcp-symdef.h" + #elif defined(USE_TCP6_SOCKETS) + #include "module-native-protocol-tcp6-symdef.h" #else #include "module-native-protocol-unix-symdef.h" #endif @@ -103,6 +113,8 @@ #define MODULE_ARGUMENTS "sink", "source", "public", "cookie", #ifdef USE_TCP_SOCKETS #include "module-esound-protocol-tcp-symdef.h" + #elif defined(USE_TCP6_SOCKETS) + #include "module-esound-protocol-tcp6-symdef.h" #else #include "module-esound-protocol-unix-symdef.h" #endif @@ -117,7 +129,7 @@ PA_MODULE_VERSION(PACKAGE_VERSION) static const char* const valid_modargs[] = { MODULE_ARGUMENTS -#ifdef USE_TCP_SOCKETS +#if defined(USE_TCP_SOCKETS) || defined(USE_TCP6_SOCKETS) "port", "loopback", #else @@ -128,7 +140,7 @@ static const char* const valid_modargs[] = { static struct pa_socket_server *create_socket_server(struct pa_core *c, struct pa_modargs *ma) { struct pa_socket_server *s; -#ifdef USE_TCP_SOCKETS +#if defined(USE_TCP_SOCKETS) || defined(USE_TCP6_SOCKETS) int loopback = 1; uint32_t port = IPV4_PORT; @@ -141,30 +153,39 @@ static struct pa_socket_server *create_socket_server(struct pa_core *c, struct p pa_log(__FILE__": port= expects a numerical argument between 1 and 65535.\n"); return NULL; } - + +#ifdef USE_TCP6_SOCKETS + if (!(s = pa_socket_server_new_ipv6(c->mainloop, loopback ? (uint8_t*) &in6addr_loopback : (uint8_t*) &in6addr_any, port))) + return NULL; +#else if (!(s = pa_socket_server_new_ipv4(c->mainloop, loopback ? INADDR_LOOPBACK : INADDR_ANY, port, TCPWRAP_SERVICE))) return NULL; +#endif + #else int r; - const char *p; + const char *v; + char tmp[PATH_MAX]; + + v = pa_modargs_get_value(ma, "socket", UNIX_SOCKET); + assert(v); - p = pa_modargs_get_value(ma, "socket", UNIX_SOCKET); - assert(p); + pa_runtime_path(v, tmp, sizeof(tmp)); - if (pa_unix_socket_make_secure_dir(p) < 0) { + if (pa_make_secure_parent_dir(tmp) < 0) { pa_log(__FILE__": Failed to create secure socket directory.\n"); return NULL; } - if ((r = pa_unix_socket_remove_stale(p)) < 0) { - pa_log(__FILE__": Failed to remove stale UNIX socket '%s': %s\n", p, strerror(errno)); + if ((r = pa_unix_socket_remove_stale(tmp)) < 0) { + pa_log(__FILE__": Failed to remove stale UNIX socket '%s': %s\n", tmp, strerror(errno)); return NULL; } if (r) - pa_log(__FILE__": Removed stale UNIX socket '%s'.", p); + pa_log(__FILE__": Removed stale UNIX socket '%s'.", tmp); - if (!(s = pa_socket_server_new_unix(c->mainloop, p))) + if (!(s = pa_socket_server_new_unix(c->mainloop, tmp))) return NULL; #endif diff --git a/polyp/module-x11-publish.c b/polyp/module-x11-publish.c index 6e100153..60284bd6 100644 --- a/polyp/module-x11-publish.c +++ b/polyp/module-x11-publish.c @@ -46,6 +46,8 @@ #include "authkey-prop.h" #include "authkey.h" #include "x11prop.h" +#include "strlist.h" +#include "props.h" PA_MODULE_AUTHOR("Lennart Poettering") PA_MODULE_DESCRIPTION("X11 Credential Publisher") @@ -101,6 +103,8 @@ int pa__init(struct pa_core *c, struct pa_module*m) { char hn[256], un[128]; char hx[PA_NATIVE_COOKIE_LENGTH*2+1]; const char *t; + char *s; + struct pa_strlist *l; if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log(__FILE__": failed to parse module arguments\n"); @@ -120,15 +124,17 @@ int pa__init(struct pa_core *c, struct pa_module*m) { u->display = pa_x11_wrapper_get_display(u->x11_wrapper); - if (!pa_get_fqdn(hn, sizeof(hn))) + if (!(l = pa_property_get(c, PA_NATIVE_SERVER_PROPERTY_NAME))) goto fail; + + s = pa_strlist_tostring(l); + pa_x11_set_prop(u->display, "POLYP_SERVER", s); + pa_xfree(s); - if (!pa_get_user_name(un, sizeof(un))) + if (!pa_get_fqdn(hn, sizeof(hn)) || !pa_get_user_name(un, sizeof(un))) goto fail; u->id = pa_sprintf_malloc("%s@%s/%u", un, hn, (unsigned) getpid()); - - pa_x11_set_prop(u->display, "POLYP_SERVER", hn); pa_x11_set_prop(u->display, "POLYP_ID", u->id); if ((t = pa_modargs_get_value(ma, "source", NULL))) diff --git a/polyp/native-common.h b/polyp/native-common.h index 597bb56b..28a471d0 100644 --- a/polyp/native-common.h +++ b/polyp/native-common.h @@ -99,8 +99,9 @@ enum { #define PA_NATIVE_DEFAULT_PORT 4713 #define PA_NATIVE_COOKIE_PROPERTY_NAME "protocol-native-cookie" +#define PA_NATIVE_SERVER_PROPERTY_NAME "protocol-native-server" -#define PA_NATIVE_DEFAULT_SERVER_UNIX "/tmp/polypaudio/native" +#define PA_NATIVE_DEFAULT_UNIX_SOCKET "native" PA_C_DECL_END diff --git a/polyp/polyplib-context.c b/polyp/polyplib-context.c index 36512a8a..a4770313 100644 --- a/polyp/polyplib-context.c +++ b/polyp/polyplib-context.c @@ -54,7 +54,9 @@ #include "client-conf-x11.h" #endif -#define AUTOSPAWN_LOCK "/tmp/polypaudio/autospawn.lock" +#define AUTOSPAWN_LOCK "autospawn.lock" + + static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = { [PA_COMMAND_REQUEST] = { pa_command_request }, @@ -63,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); @@ -93,6 +105,10 @@ 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); @@ -109,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); @@ -133,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); @@ -337,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); +static void on_connection(struct pa_socket_client *client, struct pa_iochannel*io, void *userdata); -finish: - pa_context_unref(c); -} - -static int default_server_is_running(void) { - struct stat st; - - if (PA_NATIVE_DEFAULT_SERVER_UNIX[0] != '/') - return 1; - - if (stat(PA_NATIVE_DEFAULT_SERVER_UNIX, &st) < 0) - return 0; - - 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} ; @@ -382,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) { @@ -402,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 */ @@ -430,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)); @@ -453,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); @@ -465,71 +465,131 @@ 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 = PA_NATIVE_DEFAULT_SERVER_UNIX; + 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) { + pa_log("failure: %s\n", strerror(errno)); + + /* 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); - if (!(sa = pa_resolve_server(server, &sa_len, PA_NATIVE_DEFAULT_PORT))) { +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; + + + 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); diff --git a/polyp/polyplib-internal.h b/polyp/polyplib-internal.h index 4e6553a8..589388f8 100644 --- a/polyp/polyplib-internal.h +++ b/polyp/polyplib-internal.h @@ -34,6 +34,7 @@ #include "llist.h" #include "native-common.h" #include "client-conf.h" +#include "strlist.h" #define DEFAULT_TLENGTH (44100*2*2/2) //(10240*8) #define DEFAULT_MAXLENGTH ((DEFAULT_TLENGTH*3)/2) @@ -70,6 +71,11 @@ struct pa_context { struct pa_memblock_stat *memblock_stat; int local; + int do_autospawn; + int autospawn_lock_fd; + struct pa_spawn_api spawn_api; + + struct pa_strlist *server_list; struct pa_client_conf *conf; }; diff --git a/polyp/props.c b/polyp/props.c index 014059ec..596133bc 100644 --- a/polyp/props.c +++ b/polyp/props.c @@ -110,3 +110,10 @@ void pa_property_dump(struct pa_core *c, struct pa_strbuf *s) { while ((p = pa_hashmap_iterate(c->properties, &state, NULL))) pa_strbuf_printf(s, "[%s] -> [%p]\n", p->name, p->data); } + +int pa_property_replace(struct pa_core *c, const char *name, void *data) { + assert(c && name); + + pa_property_remove(c, name); + return pa_property_set(c, name, data); +} diff --git a/polyp/props.h b/polyp/props.h index f19e9260..954d2540 100644 --- a/polyp/props.h +++ b/polyp/props.h @@ -43,6 +43,9 @@ int pa_property_set(struct pa_core *c, const char *name, void *data); /* Remove the specified property. Return non-zero on failure */ int pa_property_remove(struct pa_core *c, const char *name); +/* A combination of pa_property_remove() and pa_property_set() */ +int pa_property_replace(struct pa_core *c, const char *name, void *data); + /* Free all memory used by the property system */ void pa_property_cleanup(struct pa_core *c); diff --git a/polyp/protocol-native.c b/polyp/protocol-native.c index fade2a2f..0102e0ca 100644 --- a/polyp/protocol-native.c +++ b/polyp/protocol-native.c @@ -47,6 +47,8 @@ #include "log.h" #include "autoload.h" #include "authkey-prop.h" +#include "strlist.h" +#include "props.h" struct connection; struct pa_protocol_native; @@ -2064,6 +2066,7 @@ static struct pa_protocol_native* protocol_new_internal(struct pa_core *c, struc } 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) { + char t[256]; struct pa_protocol_native *p; if (!(p = protocol_new_internal(core, m, ma))) @@ -2071,6 +2074,13 @@ struct pa_protocol_native* pa_protocol_native_new(struct pa_core *core, struct p p->server = server; pa_socket_server_set_callback(p->server, on_connection, p); + + if (pa_socket_server_get_address(p->server, t, sizeof(t))) { + struct pa_strlist *l; + l = pa_property_get(core, PA_NATIVE_SERVER_PROPERTY_NAME); + l = pa_strlist_prepend(l, t); + pa_property_replace(core, PA_NATIVE_SERVER_PROPERTY_NAME, l); + } return p; } @@ -2083,12 +2093,26 @@ void pa_protocol_native_free(struct pa_protocol_native *p) { connection_free(c); pa_idxset_free(p->connections, NULL, NULL); - if (p->server) + if (p->server) { + char t[256]; + + if (pa_socket_server_get_address(p->server, t, sizeof(t))) { + struct pa_strlist *l; + l = pa_property_get(p->core, PA_NATIVE_SERVER_PROPERTY_NAME); + l = pa_strlist_remove(l, t); + + if (l) + pa_property_replace(p->core, PA_NATIVE_SERVER_PROPERTY_NAME, l); + else + pa_property_remove(p->core, PA_NATIVE_SERVER_PROPERTY_NAME); + } + pa_socket_server_unref(p->server); + } if (p->auth_cookie_in_property) pa_authkey_prop_unref(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME); - + pa_xfree(p); } diff --git a/polyp/socket-client.c b/polyp/socket-client.c index fbc259ff..9b80d809 100644 --- a/polyp/socket-client.c +++ b/polyp/socket-client.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "socket-client.h" #include "socket-util.h" @@ -47,6 +48,7 @@ struct pa_socket_client { struct pa_defer_event *defer_event; void (*callback)(struct pa_socket_client*c, struct pa_iochannel *io, void *userdata); void *userdata; + int local; }; static struct pa_socket_client*pa_socket_client_new(struct pa_mainloop_api *m) { @@ -61,6 +63,7 @@ static struct pa_socket_client*pa_socket_client_new(struct pa_mainloop_api *m) { c->defer_event = NULL; c->callback = NULL; c->userdata = NULL; + c->local = 0; return c; } @@ -85,6 +88,7 @@ static void do_call(struct pa_socket_client *c) { if (error != 0) { /* pa_log(__FILE__": connect(): %s\n", strerror(error)); */ + errno = error; goto finish; } @@ -141,63 +145,27 @@ static int do_connect(struct pa_socket_client *c, const struct sockaddr *sa, soc } struct pa_socket_client* pa_socket_client_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port) { - struct pa_socket_client *c; struct sockaddr_in sa; - assert(m && address && port); - - c = pa_socket_client_new(m); - assert(c); - - if ((c->fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { - pa_log(__FILE__": socket(): %s\n", strerror(errno)); - goto fail; - } - - pa_fd_set_cloexec(c->fd, 1); - pa_socket_tcp_low_delay(c->fd); + assert(m && port > 0); + memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(port); sa.sin_addr.s_addr = htonl(address); - if (do_connect(c, (struct sockaddr*) &sa, sizeof(sa)) < 0) - goto fail; - - return c; - -fail: - pa_socket_client_unref(c); - return NULL; + return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa)); } struct pa_socket_client* pa_socket_client_new_unix(struct pa_mainloop_api *m, const char *filename) { - struct pa_socket_client *c; struct sockaddr_un sa; assert(m && filename); - c = pa_socket_client_new(m); - assert(c); - - if ((c->fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { - pa_log(__FILE__": socket(): %s\n", strerror(errno)); - goto fail; - } - - pa_fd_set_cloexec(c->fd, 1); - pa_socket_low_delay(c->fd); - + memset(&sa, 0, sizeof(sa)); sa.sun_family = AF_LOCAL; strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1); sa.sun_path[sizeof(sa.sun_path) - 1] = 0; - - if (do_connect(c, (struct sockaddr*) &sa, sizeof(sa)) < 0) - goto fail; - - return c; -fail: - pa_socket_client_unref(c); - return NULL; + return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa)); } struct pa_socket_client* pa_socket_client_new_sockaddr(struct pa_mainloop_api *m, const struct sockaddr *sa, size_t salen) { @@ -206,13 +174,30 @@ struct pa_socket_client* pa_socket_client_new_sockaddr(struct pa_mainloop_api *m c = pa_socket_client_new(m); assert(c); + switch (sa->sa_family) { + case AF_UNIX: + c->local = 1; + break; + + case AF_INET: + c->local = ((const struct sockaddr_in*) sa)->sin_addr.s_addr == INADDR_LOOPBACK; + break; + + case AF_INET6: + c->local = memcmp(&((const struct sockaddr_in6*) sa)->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr)) == 0; + break; + + default: + c->local = 0; + } + if ((c->fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) { pa_log(__FILE__": socket(): %s\n", strerror(errno)); goto fail; } pa_fd_set_cloexec(c->fd, 1); - if (sa->sa_family == AF_INET) + if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6) pa_socket_tcp_low_delay(c->fd); else pa_socket_low_delay(c->fd); @@ -257,3 +242,125 @@ void pa_socket_client_set_callback(struct pa_socket_client *c, void (*on_connect c->callback = on_connection; c->userdata = userdata; } + +struct pa_socket_client* pa_socket_client_new_ipv6(struct pa_mainloop_api *m, uint8_t address[16], uint16_t port) { + struct sockaddr_in6 sa; + + memset(&sa, 0, sizeof(sa)); + sa.sin6_family = AF_INET6; + sa.sin6_port = htons(port); + memcpy(&sa.sin6_addr, address, sizeof(sa.sin6_addr)); + + return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa)); +} + +/* Parse addresses in one of the following forms: + * HOSTNAME + * HOSTNAME:PORT + * [HOSTNAME] + * [HOSTNAME]:PORT + * + * Return a newly allocated string of the hostname and fill in *port if specified */ + +static char *parse_address(const char *s, uint16_t *port) { + assert(s && port); + if (*s == '[') { + char *e; + if (!(e = strchr(s+1, ']'))) + return NULL; + + if (e[1] == ':') + *port = atoi(e+2); + else if (e[1] != 0) + return NULL; + + return pa_xstrndup(s+1, e-s-1); + } else { + char *e; + + if (!(e = strrchr(s, ':'))) + return pa_xstrdup(s); + + *port = atoi(e+1); + return pa_xstrndup(s, e-s); + } +} + +struct pa_socket_client* pa_socket_client_new_string(struct pa_mainloop_api *m, const char*name, uint16_t default_port) { + const char *p; + struct pa_socket_client *c = NULL; + enum { KIND_UNIX, KIND_TCP_AUTO, KIND_TCP4, KIND_TCP6 } kind = KIND_TCP_AUTO; + assert(m && name); + + if (*name == '{') { + char hn[256], *pfx; + /* The URL starts with a host specification for detecting local connections */ + + if (!pa_get_host_name(hn, sizeof(hn))) + return NULL; + + pfx = pa_sprintf_malloc("{%s}", hn); + if (!pa_startswith(name, pfx)) + /* Not local */ + return NULL; + + p = name + strlen(pfx); + } else + p = name; + + if (*p == '/') + kind = KIND_UNIX; + else if (pa_startswith(p, "unix:")) { + kind = KIND_UNIX; + p += sizeof("unix:")-1; + } else if (pa_startswith(p, "tcp:") || pa_startswith(p, "tcp4:")) { + kind = KIND_TCP4; + p += sizeof("tcp:")-1; + } else if (pa_startswith(p, "tcp6:")) { + kind = KIND_TCP6; + p += sizeof("tcp6:")-1; + } + + switch (kind) { + case KIND_UNIX: + return pa_socket_client_new_unix(m, p); + + case KIND_TCP_AUTO: /* Fallthrough */ + case KIND_TCP4: + case KIND_TCP6: { + uint16_t port = default_port; + char *h; + struct addrinfo hints, *res; + + if (!(h = parse_address(p, &port))) + return NULL; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = kind == KIND_TCP4 ? AF_INET : (kind == KIND_TCP6 ? AF_INET6 : AF_UNSPEC); + + if (getaddrinfo(h, NULL, &hints, &res) < 0 || !res) + return NULL; + + if (res->ai_addr->sa_family == AF_INET) + ((struct sockaddr_in*) res->ai_addr)->sin_port = htons(port); + else if (res->ai_addr->sa_family == AF_INET6) + ((struct sockaddr_in6*) res->ai_addr)->sin6_port = htons(port); + else + return NULL; + + c = pa_socket_client_new_sockaddr(m, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + return c; + } + } + + /* Should never be reached */ + assert(0); + return NULL; + +} + +int pa_socket_client_is_local(struct pa_socket_client *c) { + assert(c); + return c->local; +} diff --git a/polyp/socket-client.h b/polyp/socket-client.h index 1957355c..262fbac3 100644 --- a/polyp/socket-client.h +++ b/polyp/socket-client.h @@ -28,17 +28,19 @@ #include "mainloop-api.h" #include "iochannel.h" -/* It is safe to destroy the calling socket_client object from the callback */ - struct pa_socket_client; struct pa_socket_client* pa_socket_client_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port); +struct pa_socket_client* pa_socket_client_new_ipv6(struct pa_mainloop_api *m, uint8_t address[16], uint16_t port); struct pa_socket_client* pa_socket_client_new_unix(struct pa_mainloop_api *m, const char *filename); struct pa_socket_client* pa_socket_client_new_sockaddr(struct pa_mainloop_api *m, const struct sockaddr *sa, size_t salen); +struct pa_socket_client* pa_socket_client_new_string(struct pa_mainloop_api *m, const char *a, uint16_t default_port); void pa_socket_client_unref(struct pa_socket_client *c); struct pa_socket_client* pa_socket_client_ref(struct pa_socket_client *c); void pa_socket_client_set_callback(struct pa_socket_client *c, void (*on_connection)(struct pa_socket_client *c, struct pa_iochannel*io, void *userdata), void *userdata); +int pa_socket_client_is_local(struct pa_socket_client *c); + #endif diff --git a/polyp/socket-server.c b/polyp/socket-server.c index c170bf6e..a37b3e34 100644 --- a/polyp/socket-server.c +++ b/polyp/socket-server.c @@ -56,7 +56,7 @@ struct pa_socket_server { struct pa_io_event *io_event; struct pa_mainloop_api *mainloop; - enum { SOCKET_SERVER_GENERIC, SOCKET_SERVER_IPV4, SOCKET_SERVER_UNIX } type; + enum { SOCKET_SERVER_GENERIC, SOCKET_SERVER_IPV4, SOCKET_SERVER_UNIX, SOCKET_SERVER_IPV6 } type; }; static void callback(struct pa_mainloop_api *mainloop, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata) { @@ -202,6 +202,7 @@ struct pa_socket_server* pa_socket_server_new_ipv4(struct pa_mainloop_api *m, ui pa_socket_tcp_low_delay(fd); + memset(&sa, sizeof(sa), 0); sa.sin_family = AF_INET; sa.sin_port = htons(port); sa.sin_addr.s_addr = htonl(address); @@ -230,6 +231,53 @@ fail: return NULL; } +struct pa_socket_server* pa_socket_server_new_ipv6(struct pa_mainloop_api *m, uint8_t address[16], uint16_t port) { + struct pa_socket_server *ss; + int fd = -1; + struct sockaddr_in6 sa; + int on = 1; + + assert(m && port); + + if ((fd = socket(PF_INET6, SOCK_STREAM, 0)) < 0) { + pa_log(__FILE__": socket(): %s\n", strerror(errno)); + goto fail; + } + + pa_fd_set_cloexec(fd, 1); + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) + pa_log(__FILE__": setsockopt(): %s\n", strerror(errno)); + + pa_socket_tcp_low_delay(fd); + + memset(&sa, sizeof(sa), 0); + sa.sin6_family = AF_INET6; + sa.sin6_port = htons(port); + memcpy(sa.sin6_addr.s6_addr, address, 16); + + if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { + pa_log(__FILE__": bind(): %s\n", strerror(errno)); + goto fail; + } + + if (listen(fd, 5) < 0) { + pa_log(__FILE__": listen(): %s\n", strerror(errno)); + goto fail; + } + + if ((ss = pa_socket_server_new(m, fd))) + ss->type = SOCKET_SERVER_IPV6; + + return ss; + +fail: + if (fd >= 0) + close(fd); + + return NULL; +} + static void socket_server_free(struct pa_socket_server*s) { assert(s); close(s->fd); @@ -258,3 +306,98 @@ void pa_socket_server_set_callback(struct pa_socket_server*s, void (*on_connecti s->on_connection = on_connection; s->userdata = userdata; } + + +char *pa_socket_server_get_address(struct pa_socket_server *s, char *c, size_t l) { + assert(s && c && l > 0); + + switch (s->type) { + case SOCKET_SERVER_IPV6: { + struct sockaddr_in6 sa; + socklen_t l = sizeof(sa); + + if (getsockname(s->fd, (struct sockaddr*) &sa, &l) < 0) { + pa_log(__FILE__": getsockname() failed: %s\n", strerror(errno)); + return NULL; + } + + if (memcmp(&in6addr_any, &sa.sin6_addr, sizeof(in6addr_any)) == 0) { + char fqdn[256]; + if (!pa_get_fqdn(fqdn, sizeof(fqdn))) + return NULL; + + snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port)); + + } else if (memcmp(&in6addr_loopback, &sa.sin6_addr, sizeof(in6addr_loopback)) == 0) { + char hn[256]; + if (!pa_get_host_name(hn, sizeof(hn))) + return NULL; + + snprintf(c, l, "{%s}tcp6:localhost:%u", hn, (unsigned) ntohs(sa.sin6_port)); + } else { + char ip[INET6_ADDRSTRLEN]; + + if (!inet_ntop(AF_INET6, &sa.sin6_addr, ip, sizeof(ip))) { + pa_log(__FILE__": inet_ntop() failed: %s\n", strerror(errno)); + return NULL; + } + + snprintf(c, l, "tcp6:[%s]:%u", ip, (unsigned) ntohs(sa.sin6_port)); + } + + return c; + } + + case SOCKET_SERVER_IPV4: { + struct sockaddr_in sa; + socklen_t l = sizeof(sa); + + if (getsockname(s->fd, &sa, &l) < 0) { + pa_log(__FILE__": getsockname() failed: %s\n", strerror(errno)); + return NULL; + } + + if (sa.sin_addr.s_addr == INADDR_ANY) { + char fqdn[256]; + if (!pa_get_fqdn(fqdn, sizeof(fqdn))) + return NULL; + + snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port)); + } else if (sa.sin_addr.s_addr == INADDR_LOOPBACK) { + char hn[256]; + if (!pa_get_host_name(hn, sizeof(hn))) + return NULL; + + snprintf(c, l, "{%s}tcp:localhost:%u", hn, (unsigned) ntohs(sa.sin_port)); + } else { + char ip[INET_ADDRSTRLEN]; + + if (!inet_ntop(AF_INET, &sa.sin_addr, ip, sizeof(ip))) { + pa_log(__FILE__": inet_ntop() failed: %s\n", strerror(errno)); + return NULL; + } + + snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port)); + + } + + return c; + } + + case SOCKET_SERVER_UNIX: { + char hn[256]; + + if (!s->filename) + return NULL; + + if (!pa_get_host_name(hn, sizeof(hn))) + return NULL; + + snprintf(c, l, "{%s}unix:%s", hn, s->filename); + return c; + } + + default: + return NULL; + } +} diff --git a/polyp/socket-server.h b/polyp/socket-server.h index f5877e55..cc5524d1 100644 --- a/polyp/socket-server.h +++ b/polyp/socket-server.h @@ -33,10 +33,13 @@ struct pa_socket_server; struct pa_socket_server* pa_socket_server_new(struct pa_mainloop_api *m, int fd); struct pa_socket_server* pa_socket_server_new_unix(struct pa_mainloop_api *m, const char *filename); struct pa_socket_server* pa_socket_server_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port, const char *tcpwrap_service); +struct pa_socket_server* pa_socket_server_new_ipv6(struct pa_mainloop_api *m, uint8_t address[16], uint16_t port); void pa_socket_server_unref(struct pa_socket_server*s); struct pa_socket_server* pa_socket_server_ref(struct pa_socket_server *s); void pa_socket_server_set_callback(struct pa_socket_server*s, void (*on_connection)(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata), void *userdata); +char *pa_socket_server_get_address(struct pa_socket_server *s, char *c, size_t l); + #endif diff --git a/polyp/socket-util.c b/polyp/socket-util.c index 96ea2c60..e0540179 100644 --- a/polyp/socket-util.c +++ b/polyp/socket-util.c @@ -114,7 +114,7 @@ int pa_socket_tcp_low_delay(int fd) { ret = pa_socket_low_delay(fd); on = 1; -/* + #if defined(SOL_TCP) || defined(IPPROTO_TCP) #if defined(SOL_TCP) if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) @@ -123,7 +123,6 @@ int pa_socket_tcp_low_delay(int fd) { #endif ret = -1; #endif -*/ #if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || \ defined(IPPROTO_IP)) @@ -204,42 +203,6 @@ int pa_unix_socket_remove_stale(const char *fn) { return 0; } -int pa_unix_socket_make_secure_dir(const char *fn) { - int ret = -1; - char *slash, *dir = pa_xstrdup(fn); - - if (!(slash = strrchr(dir, '/'))) - goto finish; - *slash = 0; - - if (pa_make_secure_dir(dir) < 0) - goto finish; - - ret = 0; - -finish: - pa_xfree(dir); - return ret; -} - -int pa_unix_socket_remove_secure_dir(const char *fn) { - int ret = -1; - char *slash, *dir = pa_xstrdup(fn); - - if (!(slash = strrchr(dir, '/'))) - goto finish; - *slash = 0; - - if (rmdir(dir) < 0) - goto finish; - - ret = 0; - -finish: - pa_xfree(dir); - return ret; -} - struct sockaddr *pa_resolve_server(const char *server, size_t *len, uint16_t nport) { struct sockaddr *sa; struct addrinfo hints, *result = NULL; diff --git a/polyp/socket-util.h b/polyp/socket-util.h index 46fcfdc9..aa2de8a5 100644 --- a/polyp/socket-util.h +++ b/polyp/socket-util.h @@ -35,9 +35,6 @@ int pa_socket_set_rcvbuf(int fd, size_t l); int pa_unix_socket_is_stale(const char *fn); int pa_unix_socket_remove_stale(const char *fn); -int pa_unix_socket_make_secure_dir(const char *fn); -int pa_unix_socket_remove_secure_dir(const char *fn); - struct sockaddr *pa_resolve_server(const char *server, size_t *len, uint16_t port); #endif diff --git a/polyp/strlist-test.c b/polyp/strlist-test.c new file mode 100644 index 00000000..01dcdf12 --- /dev/null +++ b/polyp/strlist-test.c @@ -0,0 +1,39 @@ +#include + +#include "strlist.h" +#include "xmalloc.h" + +int main(int argc, char* argv[]) { + char *t, *u; + struct pa_strlist *l = NULL; + + l = pa_strlist_prepend(l, "e"); + l = pa_strlist_prepend(l, "d"); + l = pa_strlist_prepend(l, "c"); + l = pa_strlist_prepend(l, "b"); + l = pa_strlist_prepend(l, "a"); + + t = pa_strlist_tostring(l); + pa_strlist_free(l); + + fprintf(stderr, "1: %s\n", t); + + l = pa_strlist_parse(t); + pa_xfree(t); + + t = pa_strlist_tostring(l); + fprintf(stderr, "2: %s\n", t); + pa_xfree(t); + + l = pa_strlist_pop(l, &u); + fprintf(stderr, "3: %s\n", u); + pa_xfree(u); + + l = pa_strlist_remove(l, "c"); + + t = pa_strlist_tostring(l); + fprintf(stderr, "4: %s\n", t); + pa_xfree(t); + + pa_strlist_free(l); +} diff --git a/polyp/strlist.c b/polyp/strlist.c new file mode 100644 index 00000000..df7278d2 --- /dev/null +++ b/polyp/strlist.c @@ -0,0 +1,137 @@ +/* $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 "strlist.h" +#include "xmalloc.h" +#include "strbuf.h" +#include "util.h" + +struct pa_strlist { + struct pa_strlist *next; + char *str; +}; + +struct pa_strlist* pa_strlist_prepend(struct pa_strlist *l, const char *s) { + struct pa_strlist *n; + assert(s); + n = pa_xmalloc(sizeof(struct pa_strlist)); + n->str = pa_xstrdup(s); + n->next = l; + return n; +} + +char *pa_strlist_tostring(struct pa_strlist *l) { + int first = 1; + struct pa_strbuf *b; + + b = pa_strbuf_new(); + for (; l; l = l->next) { + if (!first) + pa_strbuf_puts(b, " "); + first = 0; + pa_strbuf_puts(b, l->str); + } + + return pa_strbuf_tostring_free(b); +} + +struct pa_strlist* pa_strlist_remove(struct pa_strlist *l, const char *s) { + struct pa_strlist *ret = l, *prev = NULL; + assert(l && s); + + while (l) { + if (!strcmp(l->str, s)) { + struct pa_strlist *n = l->next; + + if (!prev) { + assert(ret == l); + ret = n; + } else + prev->next = n; + + pa_xfree(l->str); + pa_xfree(l); + + l = n; + + } else { + prev = l; + l = l->next; + } + } + + return ret; +} + +void pa_strlist_free(struct pa_strlist *l) { + while (l) { + struct pa_strlist *c = l; + l = l->next; + + pa_xfree(c->str); + pa_xfree(c); + } +} + +struct pa_strlist* pa_strlist_pop(struct pa_strlist *l, char **s) { + struct pa_strlist *r; + assert(s); + + if (!l) { + *s = NULL; + return NULL; + } + + *s = l->str; + r = l->next; + pa_xfree(l); + return r; +} + +struct pa_strlist* pa_strlist_parse(const char *s) { + struct pa_strlist *head = NULL, *p = NULL; + const char *state = NULL; + char *r; + + while ((r = pa_split_spaces(s, &state))) { + struct pa_strlist *n; + + n = pa_xmalloc(sizeof(struct pa_strlist)); + n->str = r; + n->next = NULL; + + if (p) + p->next = n; + else + head = n; + + p = n; + } + + return head; +} diff --git a/polyp/strlist.h b/polyp/strlist.h new file mode 100644 index 00000000..2a209dba --- /dev/null +++ b/polyp/strlist.h @@ -0,0 +1,47 @@ +#ifndef foostrlisthfoo +#define foostrlisthfoo + +/* $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. +***/ + +struct pa_strlist; + +/* Add the specified server string to the list, return the new linked list head */ +struct pa_strlist* pa_strlist_prepend(struct pa_strlist *l, const char *s); + +/* Remove the specified string from the list, return the new linked list head */ +struct pa_strlist* pa_strlist_remove(struct pa_strlist *l, const char *s); + +/* Make a whitespace separated string of all server stringes. Returned memory has to be freed with pa_xfree() */ +char *pa_strlist_tostring(struct pa_strlist *l); + +/* Free the entire list */ +void pa_strlist_free(struct pa_strlist *l); + +/* Return the next entry in the list in *string and remove it from + * the list. Returns the new list head. The memory *string points to + * has to be freed with pa_xfree() */ +struct pa_strlist* pa_strlist_pop(struct pa_strlist *l, char **s); + +/* Parse a whitespace separated server list */ +struct pa_strlist* pa_strlist_parse(const char *s); + +#endif diff --git a/polyp/util.c b/polyp/util.c index ad91a307..4db92cf4 100644 --- a/polyp/util.c +++ b/polyp/util.c @@ -51,6 +51,8 @@ #include "xmalloc.h" #include "log.h" +#define PA_RUNTIME_PATH_PREFIX "/tmp/polypaudio-" + /** Make a file descriptor nonblock. Doesn't do any error checking */ void pa_make_nonblock_fd(int fd) { int v; @@ -83,6 +85,26 @@ fail: return -1; } +/* Creates a the parent directory of the specified path securely */ +int pa_make_secure_parent_dir(const char *fn) { + int ret = -1; + char *slash, *dir = pa_xstrdup(fn); + + if (!(slash = strrchr(dir, '/'))) + goto finish; + *slash = 0; + + if (pa_make_secure_dir(dir) < 0) + goto finish; + + ret = 0; + +finish: + pa_xfree(dir); + return ret; +} + + /** Calls read() in a loop. Makes sure that as much as 'size' bytes, * unless EOF is reached or an error occured */ ssize_t pa_loop_read(int fd, void*data, size_t size) { @@ -225,31 +247,32 @@ char *pa_get_user_name(char *s, size_t l) { char *p; assert(s && l > 0); - if (!(p = getenv("USER"))) - if (!(p = getenv("LOGNAME"))) - if (!(p = getenv("USERNAME"))) { - + if (!(p = getenv("USER")) && !(p = getenv("LOGNAME")) && !(p = getenv("USERNAME"))) { + #ifdef HAVE_GETPWUID_R - if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) { + if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) { #else - /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) - * that do not support getpwuid_r. */ - if ((r = getpwuid(getuid())) == NULL) { + /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) + * that do not support getpwuid_r. */ + if ((r = getpwuid(getuid())) == NULL) { #endif - snprintf(s, l, "%lu", (unsigned long) getuid()); - return s; - } - - p = r->pw_name; + snprintf(s, l, "%lu", (unsigned long) getuid()); + return s; } + + p = r->pw_name; + } return pa_strlcpy(s, p, l); -} + } /* Return the current hostname in the specified buffer. */ char *pa_get_host_name(char *s, size_t l) { assert(s && l > 0); - gethostname(s, l); + if (gethostname(s, l) < 0) { + pa_log(__FILE__": gethostname(): %s\n", strerror(errno)); + return NULL; + } s[l-1] = 0; return s; } @@ -264,8 +287,10 @@ char *pa_get_home_dir(char *s, size_t l) { if ((e = getenv("HOME"))) return pa_strlcpy(s, e, l); - if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) + if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) { + pa_log(__FILE__": getpwuid_r() failed\n"); return NULL; + } return pa_strlcpy(s, r->pw_dir, l); } @@ -479,7 +504,7 @@ char *pa_split_spaces(const char *c, const char **state) { const char *current = *state ? *state : c; size_t l; - if (!*current) + if (!*current || *c == 0) return NULL; current += strspn(current, WHITESPACE); @@ -768,13 +793,13 @@ size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) { return (size_t) -1; d[j] |= (uint8_t) b; - j++; } return j; } +/* Return the fully qualified domain name in *s */ char *pa_get_fqdn(char *s, size_t l) { char hn[256]; struct addrinfo *a, hints; @@ -793,3 +818,25 @@ char *pa_get_fqdn(char *s, size_t l) { freeaddrinfo(a); return s; } + +/* Returns nonzero when *s starts with *pfx */ +int pa_startswith(const char *s, const char *pfx) { + size_t l; + assert(s && pfx); + l = strlen(pfx); + + return strlen(s) >= l && strncmp(s, pfx, l) == 0; +} + +/* if fn is null return the polypaudio run time path in s (/tmp/polypaudio) + * if fn is non-null and starts with / return fn in s + * otherwise append fn to the run time path and return it in s */ +char *pa_runtime_path(const char *fn, char *s, size_t l) { + char u[256]; + + if (fn && *fn == '/') + return pa_strlcpy(s, fn, l); + + snprintf(s, l, PA_RUNTIME_PATH_PREFIX"%s%s%s", pa_get_user_name(u, sizeof(u)), fn ? "/" : "", fn ? fn : ""); + return s; +} diff --git a/polyp/util.h b/polyp/util.h index 3f09a9a7..e9d938ce 100644 --- a/polyp/util.h +++ b/polyp/util.h @@ -33,6 +33,7 @@ void pa_make_nonblock_fd(int fd); int pa_make_secure_dir(const char* dir); +int pa_make_secure_parent_dir(const char *fn); 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); @@ -85,4 +86,8 @@ FILE *pa_open_config_file(const char *env, const char *global, const char *local char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength); size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength); +int pa_startswith(const char *s, const char *pfx); + +char *pa_runtime_path(const char *fn, char *s, size_t l); + #endif -- cgit