pulse-server: support server config

Extend the server.address property so that you can also specify
an object per server. Add support for configuring some aspects of the
server such as max-clients and backlog.

Most importantly, the pipewire client.access can be configured per
server.

See #1960
This commit is contained in:
Wim Taymans 2022-01-04 14:26:08 +01:00
parent 611591d0fc
commit 22625fb658
3 changed files with 104 additions and 73 deletions

View file

@ -361,6 +361,7 @@ on_connect(void *data, int fd, uint32_t mask)
socklen_t length;
int client_fd, val;
struct client *client = NULL;
const char *client_access = NULL;
pid_t pid;
length = sizeof(name);
@ -410,6 +411,9 @@ on_connect(void *data, int fd, uint32_t mask)
if (client->routes == NULL)
goto error;
if (server->client_access[0] != '\0')
client_access = server->client_access;
if (server->addr.ss_family == AF_UNIX) {
#ifdef SO_PRIORITY
val = 6;
@ -418,9 +422,10 @@ on_connect(void *data, int fd, uint32_t mask)
#endif
pid = get_client_pid(client, client_fd);
if (pid != 0 && check_flatpak(client, pid) == 1)
pw_properties_set(client->props, PW_KEY_CLIENT_ACCESS, "flatpak");
client_access = "flatpak";
}
else if (server->addr.ss_family == AF_INET || server->addr.ss_family == AF_INET6) {
val = 1;
if (setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) < 0)
pw_log_warn("setsockopt(TCP_NODELAY) failed: %m");
@ -430,9 +435,10 @@ on_connect(void *data, int fd, uint32_t mask)
if (setsockopt(client_fd, IPPROTO_IP, IP_TOS, &val, sizeof(val)) < 0)
pw_log_warn("setsockopt(IP_TOS) failed: %m");
}
pw_properties_set(client->props, PW_KEY_CLIENT_ACCESS, "restricted");
if (client_access == NULL)
client_access = "restricted";
}
pw_properties_set(client->props, PW_KEY_CLIENT_ACCESS, client_access);
return;
@ -442,9 +448,9 @@ error:
client_free(client);
}
static int parse_unix_address(const char *address, struct pw_array *addrs)
static int parse_unix_address(const char *address, struct sockaddr_storage *addrs, int len)
{
struct sockaddr_un addr = {0}, *s;
struct sockaddr_un addr = {0};
int res;
if (address[0] != '/') {
@ -469,15 +475,13 @@ static int parse_unix_address(const char *address, struct pw_array *addrs)
return -ENAMETOOLONG;
}
s = pw_array_add(addrs, sizeof(struct sockaddr_storage));
if (s == NULL)
return -ENOMEM;
if (len < 1)
return -ENOSPC;
addr.sun_family = AF_UNIX;
*s = addr;
return 0;
memcpy(&addrs[0], &addr, sizeof(addr));
return 1;
}
#ifndef SUN_LEN
@ -581,7 +585,7 @@ static int start_unix_server(struct server *server, const struct sockaddr_storag
goto error_close;
}
if (listen(fd, LISTEN_BACKLOG) < 0) {
if (listen(fd, server->listen_backlog) < 0) {
res = -errno;
pw_log_warn("server %p: listen() on '%s' failed: %m",
server, addr_un->sun_path);
@ -745,49 +749,43 @@ static int get_ip_address_length(const struct sockaddr_storage *addr)
}
}
static int parse_ip_address(const char *address, struct pw_array *addrs)
static int parse_ip_address(const char *address, struct sockaddr_storage *addrs, int len)
{
char ip[FORMATTED_IP_ADDR_STRLEN];
struct sockaddr_storage addr, *s;
struct sockaddr_storage addr;
int res;
res = parse_ipv6_address(address, (struct sockaddr_in6 *) &addr);
if (res == 0) {
s = pw_array_add(addrs, sizeof(*s));
if (s == NULL)
return -ENOMEM;
*s = addr;
return 0;
if (len < 1)
return -ENOSPC;
addrs[0] = addr;
return 1;
}
res = parse_ipv4_address(address, (struct sockaddr_in *) &addr);
if (res == 0) {
s = pw_array_add(addrs, sizeof(*s));
if (s == NULL)
return -ENOMEM;
*s = addr;
return 0;
if (len < 1)
return -ENOSPC;
addrs[0] = addr;
return 1;
}
res = parse_port(address);
if (res < 0)
return res;
s = pw_array_add(addrs, sizeof(*s) * 2);
if (s == NULL)
return -ENOMEM;
if (len < 2)
return -ENOSPC;
snprintf(ip, sizeof(ip), "[::]:%d", res);
spa_assert_se(parse_ipv6_address(ip, (struct sockaddr_in6 *) &addr) == 0);
*s++ = addr;
addrs[0] = addr;
snprintf(ip, sizeof(ip), "0.0.0.0:%d", res);
spa_assert_se(parse_ipv4_address(ip, (struct sockaddr_in *) &addr) == 0);
*s++ = addr;
return 0;
addrs[1] = addr;
return 2;
}
static int start_ip_server(struct server *server, const struct sockaddr_storage *addr)
@ -822,7 +820,7 @@ static int start_ip_server(struct server *server, const struct sockaddr_storage
goto error_close;
}
if (listen(fd, LISTEN_BACKLOG) < 0) {
if (listen(fd, server->listen_backlog) < 0) {
res = -errno;
pw_log_warn("server %p: listen() failed: %m", server);
goto error_close;
@ -888,13 +886,13 @@ static int server_start(struct server *server, const struct sockaddr_storage *ad
return res;
}
static int parse_address(const char *address, struct pw_array *addrs)
static int parse_address(const char *address, struct sockaddr_storage *addrs, int len)
{
if (spa_strstartswith(address, "tcp:"))
return parse_ip_address(address + strlen("tcp:"), addrs);
return parse_ip_address(address + strlen("tcp:"), addrs, len);
if (spa_strstartswith(address, "unix:"))
return parse_unix_address(address + strlen("unix:"), addrs);
return parse_unix_address(address + strlen("unix:"), addrs, len);
return -EAFNOSUPPORT;
}
@ -927,11 +925,9 @@ static int format_socket_address(const struct sockaddr_storage *addr, char *buff
int servers_create_and_start(struct impl *impl, const char *addresses, struct pw_array *servers)
{
struct pw_array addrs = PW_ARRAY_INIT(sizeof(struct sockaddr_storage));
const struct sockaddr_storage *addr;
char addr_str[FORMATTED_SOCKET_ADDR_STRLEN];
int res, count = 0, err = 0; /* store the first error to return when no servers could be created */
struct spa_json it[2];
int len, res, count = 0, err = 0; /* store the first error to return when no servers could be created */
const char *v;
struct spa_json it[3];
/* update `err` if it hasn't been set to an errno */
#define UPDATE_ERR(e) do { if (err == 0) err = (e); } while (false)
@ -939,52 +935,77 @@ int servers_create_and_start(struct impl *impl, const char *addresses, struct pw
/* collect addresses into an array of `struct sockaddr_storage` */
spa_json_init(&it[0], addresses, strlen(addresses));
/* [ <server-spec> ... ] */
if (spa_json_enter_array(&it[0], &it[1]) < 0)
return -EINVAL;
while (spa_json_get_string(&it[1], addr_str, sizeof(addr_str)) > 0) {
res = parse_address(addr_str, &addrs);
if (res < 0) {
/* a server-spec is either an address or an object */
while ((len = spa_json_next(&it[1], &v)) > 0) {
char addr_str[FORMATTED_SOCKET_ADDR_STRLEN] = { 0 };
char key[128], client_access[64] = { 0 };
struct sockaddr_storage addr[2];
int i, max_clients = MAX_CLIENTS, listen_backlog = LISTEN_BACKLOG, n_addr;
if (spa_json_is_object(v, len)) {
spa_json_enter(&it[1], &it[2]);
while (spa_json_get_string(&it[2], key, sizeof(key)) > 0) {
if ((len = spa_json_next(&it[2], &v)) <= 0)
break;
if (spa_streq(key, "address")) {
spa_json_parse_stringn(v, len, addr_str, sizeof(addr_str));
} else if (spa_streq(key, "max-clients")) {
spa_json_parse_int(v, len, &max_clients);
} else if (spa_streq(key, "listen-backlog")) {
spa_json_parse_int(v, len, &listen_backlog);
} else if (spa_streq(key, "client.access")) {
spa_json_parse_stringn(v, len, client_access, sizeof(client_access));
}
}
} else {
spa_json_parse_stringn(v, len, addr_str, sizeof(addr_str));
}
n_addr = parse_address(addr_str, addr, 2);
if (n_addr < 0) {
pw_log_warn("pulse-server %p: failed to parse address '%s': %s",
impl, addr_str, spa_strerror(res));
UPDATE_ERR(res);
}
}
/* try to create sockets for each address in the list */
pw_array_for_each (addr, &addrs) {
struct server * const server = server_new(impl);
if (server == NULL) {
UPDATE_ERR(-errno);
impl, addr_str, spa_strerror(n_addr));
UPDATE_ERR(n_addr);
continue;
}
res = server_start(server, addr);
if (res < 0) {
spa_assert_se(format_socket_address(addr, addr_str, sizeof(addr_str)) >= 0);
pw_log_warn("pulse-server %p: failed to start server on '%s': %s",
impl, addr_str, spa_strerror(res));
/* try to create sockets for each address in the list */
for (i = 0; i < n_addr; i++) {
struct server * const server = server_new(impl);
if (server == NULL) {
UPDATE_ERR(-errno);
continue;
}
UPDATE_ERR(res);
server_free(server);
server->max_clients = max_clients;
server->listen_backlog = listen_backlog;
memcpy(server->client_access, client_access, sizeof(client_access));
continue;
res = server_start(server, addr);
if (res < 0) {
spa_assert_se(format_socket_address(addr, addr_str, sizeof(addr_str)) >= 0);
pw_log_warn("pulse-server %p: failed to start server on '%s': %s",
impl, addr_str, spa_strerror(res));
UPDATE_ERR(res);
server_free(server);
continue;
}
if (servers != NULL)
pw_array_add_ptr(servers, server);
count += 1;
}
if (servers != NULL)
pw_array_add_ptr(servers, server);
count += 1;
}
pw_array_clear(&addrs);
if (count == 0) {
UPDATE_ERR(-EINVAL);
return err;
}
return count;
#undef UPDATE_ERR