*** 0.7 ****
- per-channel volume
-- unix socket directories include user name
- add sample directory
- make mcalign merge chunks
- option to use default fragment size on alsa drivers
- make most buffer sizes dependant on the sample type
- X11: support for the X11 synchronization extension
-- X11: save auth info in root window
- pass meta info for hearing impaired
- limit all resources
- check getaddrinfo results
parec-simple \
cpulimit-test \
cpulimit-test2 \
- voltest
+ voltest \
+ strlist-test
polypconf_DATA=default.pa daemon.conf client.conf
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 \
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 \
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
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
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
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
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
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
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
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
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;
#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=<TCP port number> loopback=<listen on loopback device only?>"
+#elif defined(USE_TCP6_SOCKETS)
+#define SOCKET_DESCRIPTION "(TCP/IPv6 sockets)"
+#define SOCKET_USAGE "port=<TCP port number> loopback=<listen on loopback device only?>"
#else
#define SOCKET_DESCRIPTION "(UNIX sockets)"
#define SOCKET_USAGE "socket=<path to UNIX socket>"
#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
#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
#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
#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
static const char* const valid_modargs[] = {
MODULE_ARGUMENTS
-#ifdef USE_TCP_SOCKETS
+#if defined(USE_TCP_SOCKETS) || defined(USE_TCP6_SOCKETS)
"port",
"loopback",
#else
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;
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
#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")
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");
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)))
#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
#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 },
[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);
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);
static void context_free(struct pa_context *c) {
assert(c);
+ unlock_autospawn_lock_file(c);
+
while (c->operations)
pa_operation_cancel(c->operations);
if (c->conf)
pa_client_conf_free(c->conf);
+
+ pa_strlist_free(c->server_list);
pa_xfree(c->name);
pa_xfree(c);
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} ;
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) {
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 */
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));
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);
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);
#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)
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;
};
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);
+}
/* 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);
#include "log.h"
#include "autoload.h"
#include "authkey-prop.h"
+#include "strlist.h"
+#include "props.h"
struct connection;
struct pa_protocol_native;
}
struct pa_protocol_native* pa_protocol_native_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) {
+ char t[256];
struct pa_protocol_native *p;
if (!(p = protocol_new_internal(core, m, ma)))
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;
}
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);
}
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <netdb.h>
#include "socket-client.h"
#include "socket-util.h"
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) {
c->defer_event = NULL;
c->callback = NULL;
c->userdata = NULL;
+ c->local = 0;
return c;
}
if (error != 0) {
/* pa_log(__FILE__": connect(): %s\n", strerror(error)); */
+ errno = error;
goto finish;
}
}
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) {
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);
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;
+}
#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
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) {
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);
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);
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;
+ }
+}
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
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)
#endif
ret = -1;
#endif
-*/
#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || \
defined(IPPROTO_IP))
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;
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
--- /dev/null
+#include <stdio.h>
+
+#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);
+}
--- /dev/null
+/* $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 <config.h>
+#endif
+
+#include <string.h>
+#include <assert.h>
+
+#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;
+}
--- /dev/null
+#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
#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;
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) {
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;
}
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);
}
const char *current = *state ? *state : c;
size_t l;
- if (!*current)
+ if (!*current || *c == 0)
return NULL;
current += strspn(current, WHITESPACE);
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;
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;
+}
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);
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