pulse-server: alsa safely make native socket

Only make the native socket when it's not there or when it appears
dead when we can't seem to connect to it.

- When the socket is not found, we take it.
- When an existing pulseaudio is running we won't unlink and take
  over the socket because we can connect to it
- When a crashed pulseaudio leaves a stale socket, we fail to
  connect and then we unlink and take over.
This commit is contained in:
Wim Taymans 2020-11-12 12:17:12 +01:00
parent 1d001974d7
commit 6e2ab9973b

View file

@ -4390,9 +4390,20 @@ get_server_name(struct pw_context *context)
return name; return name;
} }
static int check_connect(struct server *server, int fd)
{
int res;
socklen_t size;
size = offsetof(struct sockaddr_un, sun_path) + strlen(server->addr.sun_path);
if ((res = connect(fd, (struct sockaddr *)&server->addr, size)) < 0)
return -errno;
return 0;
}
static int make_local_socket(struct server *server, char *name) static int make_local_socket(struct server *server, char *name)
{ {
struct impl *impl = server->impl;
char runtime_dir[PATH_MAX]; char runtime_dir[PATH_MAX];
socklen_t size; socklen_t size;
int name_size, fd, res; int name_size, fd, res;
@ -4403,8 +4414,7 @@ static int make_local_socket(struct server *server, char *name)
server->addr.sun_family = AF_LOCAL; server->addr.sun_family = AF_LOCAL;
name_size = snprintf(server->addr.sun_path, sizeof(server->addr.sun_path), name_size = snprintf(server->addr.sun_path, sizeof(server->addr.sun_path),
"%s/%s-%s", runtime_dir, name, "%s/%s", runtime_dir, name) + 1;
get_server_name(impl->context)) + 1;
if (name_size > (int) sizeof(server->addr.sun_path)) { if (name_size > (int) sizeof(server->addr.sun_path)) {
pw_log_error(NAME" %p: %s/%s too long", pw_log_error(NAME" %p: %s/%s too long",
@ -4412,6 +4422,8 @@ static int make_local_socket(struct server *server, char *name)
res = -ENAMETOOLONG; res = -ENAMETOOLONG;
goto error; goto error;
} }
size = offsetof(struct sockaddr_un, sun_path) + strlen(server->addr.sun_path);
if ((fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0) { if ((fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0) {
res = -errno; res = -errno;
goto error; goto error;
@ -4419,15 +4431,24 @@ static int make_local_socket(struct server *server, char *name)
if (stat(server->addr.sun_path, &socket_stat) < 0) { if (stat(server->addr.sun_path, &socket_stat) < 0) {
if (errno != ENOENT) { if (errno != ENOENT) {
res = -errno; res = -errno;
pw_log_error("server %p: stat %s failed with error: %m", pw_log_error(NAME" %p: stat %s failed with error: %m",
server, server->addr.sun_path); server, server->addr.sun_path);
goto error_close; goto error_close;
} }
} else if (socket_stat.st_mode & S_IWUSR || socket_stat.st_mode & S_IWGRP) { } else if (socket_stat.st_mode & S_IWUSR || socket_stat.st_mode & S_IWGRP) {
unlink(server->addr.sun_path); /* socket is there, check if we can connect */
if ((res = check_connect(server, fd)) < 0) {
/* we can't connect, probably stale, remove it */
pw_log_warn(NAME" %p: unlink stale socket %s: %s", server,
server->addr.sun_path, spa_strerror(res));
unlink(server->addr.sun_path);
} else {
/* we could connect so it's probably in use */
pw_log_info(NAME" %p: socket %s is in use", server,
server->addr.sun_path);
goto error_close;
}
} }
size = offsetof(struct sockaddr_un, sun_path) + strlen(server->addr.sun_path);
if (bind(fd, (struct sockaddr *) &server->addr, size) < 0) { if (bind(fd, (struct sockaddr *) &server->addr, size) < 0) {
res = -errno; res = -errno;
pw_log_error(NAME" %p: bind() to %s failed with error: %m", server, pw_log_error(NAME" %p: bind() to %s failed with error: %m", server,
@ -4584,12 +4605,24 @@ struct pw_protocol_pulse *pw_protocol_pulse_new(struct pw_context *context,
struct impl *impl; struct impl *impl;
const char *str; const char *str;
int i, n_addr; int i, n_addr;
char **addr; char **addr, *free_str = NULL;
impl = calloc(1, sizeof(struct impl) + user_data_size); impl = calloc(1, sizeof(struct impl) + user_data_size);
if (impl == NULL) if (impl == NULL)
return NULL; return NULL;
str = NULL;
if (props != NULL)
str = pw_properties_get(props, "server.address");
if (str == NULL) {
str = free_str = spa_aprintf("%s,%s-%s",
PW_PROTOCOL_PULSE_DEFAULT_SERVER,
PW_PROTOCOL_PULSE_DEFAULT_SERVER,
get_server_name(context));
}
if (str == NULL)
goto error_free;
debug_messages = pw_debug_is_category_enabled("connection"); debug_messages = pw_debug_is_category_enabled("connection");
impl->context = context; impl->context = context;
@ -4602,22 +4635,23 @@ struct pw_protocol_pulse *pw_protocol_pulse_new(struct pw_context *context,
pw_context_add_listener(context, &impl->context_listener, pw_context_add_listener(context, &impl->context_listener,
&context_events, impl); &context_events, impl);
str = NULL;
if (props != NULL)
str = pw_properties_get(props, "server.address");
if (str == NULL)
str = PW_PROTOCOL_PULSE_DEFAULT_SERVER;
addr = pw_split_strv(str, ",", INT_MAX, &n_addr); addr = pw_split_strv(str, ",", INT_MAX, &n_addr);
for (i = 0; i < n_addr; i++) { for (i = 0; i < n_addr; i++) {
if (addr[i] == NULL)
continue;
if (create_server(impl, addr[i]) == NULL) { if (create_server(impl, addr[i]) == NULL) {
pw_log_warn(NAME" %p: can't create server for %s: %m", pw_log_warn(NAME" %p: can't create server for %s: %m",
impl, addr[i]); impl, addr[i]);
} }
} }
pw_free_strv(addr); pw_free_strv(addr);
free(free_str);
return (struct pw_protocol_pulse*)impl; return (struct pw_protocol_pulse*)impl;
error_free:
free(impl);
return NULL;
} }
void *pw_protocol_pulse_get_user_data(struct pw_protocol_pulse *pulse) void *pw_protocol_pulse_get_user_data(struct pw_protocol_pulse *pulse)