diff --git a/src/modules/meson.build b/src/modules/meson.build index b871b938a..c6852f0ab 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -141,6 +141,7 @@ pipewire_module_protocol_pulse_sources = [ 'module-protocol-pulse/pulse-server.c', 'module-protocol-pulse/sample.c', 'module-protocol-pulse/sample-play.c', + 'module-protocol-pulse/utils.c', 'module-protocol-pulse/volume.c', 'module-protocol-pulse/modules/module-combine-sink.c', 'module-protocol-pulse/modules/module-echo-cancel.c', diff --git a/src/modules/module-protocol-pulse/pulse-server.c b/src/modules/module-protocol-pulse/pulse-server.c index 5c6cf9fb1..a476af271 100644 --- a/src/modules/module-protocol-pulse/pulse-server.c +++ b/src/modules/module-protocol-pulse/pulse-server.c @@ -44,15 +44,6 @@ #include #include #include -#if HAVE_SYS_VFS_H -#include -#endif -#if HAVE_SYS_MOUNT_H -#include -#endif -#if HAVE_PWD_H -#include -#endif #ifdef HAVE_SYSTEMD #include @@ -89,6 +80,7 @@ #include "pending-sample.h" #include "sample.h" #include "sample-play.h" +#include "utils.h" #include "volume.h" #define DEFAULT_MIN_REQ "256/48000" @@ -5673,76 +5665,6 @@ error: goto done; } -static int check_flatpak(struct client *client, int pid) -{ - char root_path[2048]; - int root_fd, info_fd, res; - struct stat stat_buf; - - sprintf(root_path, "/proc/%u/root", pid); - root_fd = openat(AT_FDCWD, root_path, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY); - if (root_fd == -1) { - res = -errno; - if (res == -EACCES) { - struct statfs buf; - /* Access to the root dir isn't allowed. This can happen if the root is on a fuse - * filesystem, such as in a toolbox container. We will never have a fuse rootfs - * in the flatpak case, so in that case its safe to ignore this and - * continue to detect other types of apps. */ - if (statfs(root_path, &buf) == 0 && - buf.f_type == 0x65735546) /* FUSE_SUPER_MAGIC */ - return 0; - } - /* Not able to open the root dir shouldn't happen. Probably the app died and - * we're failing due to /proc/$pid not existing. In that case fail instead - * of treating this as privileged. */ - pw_log_info("failed to open \"%s\": %s", root_path, spa_strerror(res)); - return res; - } - info_fd = openat(root_fd, ".flatpak-info", O_RDONLY | O_CLOEXEC | O_NOCTTY); - close(root_fd); - if (info_fd == -1) { - if (errno == ENOENT) { - pw_log_debug("no .flatpak-info, client on the host"); - /* No file => on the host */ - return 0; - } - res = -errno; - pw_log_error("error opening .flatpak-info: %m"); - return res; - } - if (fstat(info_fd, &stat_buf) != 0 || !S_ISREG(stat_buf.st_mode)) { - /* Some weird fd => failure, assume sandboxed */ - pw_log_error("error fstat .flatpak-info: %m"); - } - close(info_fd); - return 1; -} - -static int get_client_pid(struct client *client, int client_fd) -{ - socklen_t len; -#if defined(__linux__) - struct ucred ucred; - len = sizeof(ucred); - if (getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) { - pw_log_warn(NAME": client %p: no peercred: %m", client); - } else - return ucred.pid; -#elif defined(__FreeBSD__) - struct xucred xucred; - len = sizeof(xucred); - if (getsockopt(client_fd, 0, LOCAL_PEERCRED, &xucred, &len) < 0) { - pw_log_warn(NAME": client %p: no peercred: %m", client); - } else { -#if __FreeBSD__ >= 13 - return xucred.cr_pid; -#endif - } -#endif - return 0; -} - static void on_connect(void *data, int fd, uint32_t mask) { @@ -5841,48 +5763,6 @@ error: return; } -static int -get_runtime_dir(char *buf, size_t buflen, const char *dir) -{ - const char *runtime_dir; - struct stat stat_buf; - int res, size; - - runtime_dir = getenv("PULSE_RUNTIME_PATH"); - if (runtime_dir == NULL) - runtime_dir = getenv("XDG_RUNTIME_DIR"); - if (runtime_dir == NULL) - runtime_dir = getenv("HOME"); - if (runtime_dir == NULL) { - struct passwd pwd, *result = NULL; - char buffer[4096]; - if (getpwuid_r(getuid(), &pwd, buffer, sizeof(buffer), &result) == 0) - runtime_dir = result ? result->pw_dir : NULL; - } - size = snprintf(buf, buflen, "%s/%s", runtime_dir, dir) + 1; - if (size > (int) buflen) { - pw_log_error(NAME": path %s/%s too long", runtime_dir, dir); - return -ENAMETOOLONG; - } - if (stat(buf, &stat_buf) < 0) { - res = -errno; - if (res != -ENOENT) { - pw_log_error(NAME": stat() %s failed: %m", buf); - return res; - } - if (mkdir(buf, 0700) < 0) { - res = -errno; - pw_log_error(NAME": mkdir() %s failed: %m", buf); - return res; - } - pw_log_info(NAME": created %s", buf); - } else if ((stat_buf.st_mode & S_IFMT) != S_IFDIR) { - pw_log_error(NAME": %s is not a directory", buf); - return -ENOTDIR; - } - return 0; -} - void server_free(struct server *server) { struct impl *impl = server->impl; @@ -5904,21 +5784,6 @@ void server_free(struct server *server) free(server); } -static const char * -get_server_name(struct pw_context *context) -{ - const char *name = NULL; - const struct pw_properties *props = pw_context_get_properties(context); - - if (props) - name = pw_properties_get(props, PW_KEY_REMOTE_NAME); - if (name == NULL || name[0] == '\0') - name = getenv("PIPEWIRE_REMOTE"); - if (name == NULL || name[0] == '\0') - name = PW_DEFAULT_REMOTE; - return name; -} - static int parse_unix_address(const char *address, struct pw_array *addrs) { struct sockaddr_un addr = {0}, *s; @@ -6463,33 +6328,6 @@ int create_and_start_servers(struct impl *impl, const char *addresses, struct pw #undef UPDATE_ERR } -static int create_pid_file(void) { - char pid_file[PATH_MAX]; - FILE *f; - int res; - - if ((res = get_runtime_dir(pid_file, sizeof(pid_file), "pulse")) < 0) - return res; - - if (strlen(pid_file) > PATH_MAX - 5) { - pw_log_error(NAME": path too long: %s/pid", pid_file); - return -ENAMETOOLONG; - } - - strcat(pid_file, "/pid"); - - if ((f = fopen(pid_file, "w")) == NULL) { - res = -errno; - pw_log_error(NAME": failed to open pid file: %m"); - return res; - } - - fprintf(f, "%lu\n", (unsigned long) getpid()); - fclose(f); - - return 0; -} - static int impl_free_sample(void *item, void *data) { struct sample *s = item; diff --git a/src/modules/module-protocol-pulse/utils.c b/src/modules/module-protocol-pulse/utils.c new file mode 100644 index 000000000..72c5e9010 --- /dev/null +++ b/src/modules/module-protocol-pulse/utils.c @@ -0,0 +1,204 @@ +/* PipeWire + * + * Copyright © 2020 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#define NAME "pulse-server" + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_VFS_H +#include +#endif +#ifdef HAVE_PWD_H +#include +#endif + +#include +#include +#include +#include + +#include "utils.h" + +int get_runtime_dir(char *buf, size_t buflen, const char *dir) +{ + const char *runtime_dir; + struct stat stat_buf; + int res, size; + + runtime_dir = getenv("PULSE_RUNTIME_PATH"); + if (runtime_dir == NULL) + runtime_dir = getenv("XDG_RUNTIME_DIR"); + if (runtime_dir == NULL) + runtime_dir = getenv("HOME"); + if (runtime_dir == NULL) { + struct passwd pwd, *result = NULL; + char buffer[4096]; + if (getpwuid_r(getuid(), &pwd, buffer, sizeof(buffer), &result) == 0) + runtime_dir = result ? result->pw_dir : NULL; + } + size = snprintf(buf, buflen, "%s/%s", runtime_dir, dir) + 1; + if (size > (int) buflen) { + pw_log_error(NAME": path %s/%s too long", runtime_dir, dir); + return -ENAMETOOLONG; + } + if (stat(buf, &stat_buf) < 0) { + res = -errno; + if (res != -ENOENT) { + pw_log_error(NAME": stat() %s failed: %m", buf); + return res; + } + if (mkdir(buf, 0700) < 0) { + res = -errno; + pw_log_error(NAME": mkdir() %s failed: %m", buf); + return res; + } + pw_log_info(NAME": created %s", buf); + } else if ((stat_buf.st_mode & S_IFMT) != S_IFDIR) { + pw_log_error(NAME": %s is not a directory", buf); + return -ENOTDIR; + } + return 0; +} + +int check_flatpak(struct client *client, int pid) +{ + char root_path[2048]; + int root_fd, info_fd, res; + struct stat stat_buf; + + sprintf(root_path, "/proc/%u/root", pid); + root_fd = openat(AT_FDCWD, root_path, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY); + if (root_fd == -1) { + res = -errno; + if (res == -EACCES) { + struct statfs buf; + /* Access to the root dir isn't allowed. This can happen if the root is on a fuse + * filesystem, such as in a toolbox container. We will never have a fuse rootfs + * in the flatpak case, so in that case its safe to ignore this and + * continue to detect other types of apps. */ + if (statfs(root_path, &buf) == 0 && + buf.f_type == 0x65735546) /* FUSE_SUPER_MAGIC */ + return 0; + } + /* Not able to open the root dir shouldn't happen. Probably the app died and + * we're failing due to /proc/$pid not existing. In that case fail instead + * of treating this as privileged. */ + pw_log_info("failed to open \"%s\": %s", root_path, spa_strerror(res)); + return res; + } + info_fd = openat(root_fd, ".flatpak-info", O_RDONLY | O_CLOEXEC | O_NOCTTY); + close(root_fd); + if (info_fd == -1) { + if (errno == ENOENT) { + pw_log_debug("no .flatpak-info, client on the host"); + /* No file => on the host */ + return 0; + } + res = -errno; + pw_log_error("error opening .flatpak-info: %m"); + return res; + } + if (fstat(info_fd, &stat_buf) != 0 || !S_ISREG(stat_buf.st_mode)) { + /* Some weird fd => failure, assume sandboxed */ + pw_log_error("error fstat .flatpak-info: %m"); + } + close(info_fd); + return 1; +} + +int get_client_pid(struct client *client, int client_fd) +{ + socklen_t len; +#if defined(__linux__) + struct ucred ucred; + len = sizeof(ucred); + if (getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) { + pw_log_warn("client %p: no peercred: %m", client); + } else + return ucred.pid; +#elif defined(__FreeBSD__) + struct xucred xucred; + len = sizeof(xucred); + if (getsockopt(client_fd, 0, LOCAL_PEERCRED, &xucred, &len) < 0) { + pw_log_warn("client %p: no peercred: %m", client); + } else { +#if __FreeBSD__ >= 13 + return xucred.cr_pid; +#endif + } +#endif + return 0; +} + +const char *get_server_name(struct pw_context *context) +{ + const char *name = NULL; + const struct pw_properties *props = pw_context_get_properties(context); + + if (props) + name = pw_properties_get(props, PW_KEY_REMOTE_NAME); + if (name == NULL || name[0] == '\0') + name = getenv("PIPEWIRE_REMOTE"); + if (name == NULL || name[0] == '\0') + name = PW_DEFAULT_REMOTE; + return name; +} + +int create_pid_file(void) { + char pid_file[PATH_MAX]; + FILE *f; + int res; + + if ((res = get_runtime_dir(pid_file, sizeof(pid_file), "pulse")) < 0) + return res; + + if (strlen(pid_file) > PATH_MAX - 5) { + pw_log_error(NAME": path too long: %s/pid", pid_file); + return -ENAMETOOLONG; + } + + strcat(pid_file, "/pid"); + + if ((f = fopen(pid_file, "w")) == NULL) { + res = -errno; + pw_log_error(NAME": failed to open pid file: %m"); + return res; + } + + fprintf(f, "%lu\n", (unsigned long) getpid()); + fclose(f); + + return 0; +} diff --git a/src/modules/module-protocol-pulse/utils.h b/src/modules/module-protocol-pulse/utils.h new file mode 100644 index 000000000..77a59d282 --- /dev/null +++ b/src/modules/module-protocol-pulse/utils.h @@ -0,0 +1,39 @@ +/* PipeWire + * + * Copyright © 2020 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef PULSE_SERVER_UTILS_H +#define PULSE_SERVER_UTILS_H + +#include + +struct client; +struct pw_context; + +int get_runtime_dir(char *buf, size_t buflen, const char *dir); +int check_flatpak(struct client *client, int pid); +int get_client_pid(struct client *client, int client_fd); +const char *get_server_name(struct pw_context *context); +int create_pid_file(void); + +#endif /* PULSE_SERVER_UTILS_H */