diff options
Diffstat (limited to 'xbmc/platform/posix/utils/SharedMemory.cpp')
-rw-r--r-- | xbmc/platform/posix/utils/SharedMemory.cpp | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/xbmc/platform/posix/utils/SharedMemory.cpp b/xbmc/platform/posix/utils/SharedMemory.cpp new file mode 100644 index 0000000..6c1318e --- /dev/null +++ b/xbmc/platform/posix/utils/SharedMemory.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2017-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "SharedMemory.h" + +#include <fcntl.h> +#include <sys/mman.h> +#include <unistd.h> + +#if defined(HAVE_LINUX_MEMFD) +#include <linux/memfd.h> +#include <sys/syscall.h> +#endif + +#include <cerrno> +#include <cstdlib> +#include <system_error> + +#include "utils/log.h" + +using namespace KODI::UTILS::POSIX; + +CSharedMemory::CSharedMemory(std::size_t size) +: m_size{size}, m_fd{Open()}, m_mmap(nullptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0) +{ +} + +CFileHandle CSharedMemory::Open() +{ + CFileHandle fd; + try + { + fd = OpenMemfd(); + } + catch (std::system_error const& error) + { + if (error.code() == std::errc::function_not_supported) + { + CLog::Log(LOGDEBUG, "Kernel does not support memfd, falling back to plain shm"); + fd = OpenShm(); + } + else + { + throw; + } + } + + if (ftruncate(fd, m_size) < 0) + { + throw std::system_error(errno, std::generic_category(), "ftruncate"); + } + + return fd; +} + +CFileHandle CSharedMemory::OpenMemfd() +{ +#if defined(SYS_memfd_create) && defined(HAVE_LINUX_MEMFD) + // This is specific to Linux >= 3.17, but preferred over shm_create if available + // because it is race-free + int fd = syscall(SYS_memfd_create, "kodi", MFD_CLOEXEC); + if (fd < 0) + { + throw std::system_error(errno, std::generic_category(), "memfd_create"); + } + + return CFileHandle(fd); +#else + throw std::system_error(std::make_error_code(std::errc::function_not_supported), "memfd_create"); +#endif +} + +CFileHandle CSharedMemory::OpenShm() +{ + char* xdgRuntimeDir = std::getenv("XDG_RUNTIME_DIR"); + if (!xdgRuntimeDir) + { + throw std::runtime_error("XDG_RUNTIME_DIR environment variable must be set"); + } + + std::string tmpFilename(xdgRuntimeDir); + tmpFilename.append("/kodi-shared-XXXXXX"); + + int rawFd; +#if defined(HAVE_MKOSTEMP) + // Opening the file with O_CLOEXEC is preferred since it avoids races where + // other threads might exec() before setting the CLOEXEC flag + rawFd = mkostemp(&tmpFilename[0], O_CLOEXEC); +#else + rawFd = mkstemp(&tmpFilename[0]); +#endif + + if (rawFd < 0) + { + throw std::system_error(errno, std::generic_category(), "mkstemp"); + } + CFileHandle fd(rawFd); + + int flags = fcntl(fd, F_GETFD); + if (flags < 0) + { + throw std::system_error(errno, std::generic_category(), "fcntl F_GETFD"); + } + // Set FD_CLOEXEC if unset + if (!(flags & FD_CLOEXEC) && fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) + { + throw std::system_error(errno, std::generic_category(), "fcntl F_SETFD FD_CLOEXEC"); + } + + unlink(tmpFilename.c_str()); + + return fd; +}
\ No newline at end of file |