153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci    This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci    Copyright 2006 Lennart Poettering
553a5a1b3Sopenharmony_ci    Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
653a5a1b3Sopenharmony_ci
753a5a1b3Sopenharmony_ci    PulseAudio is free software; you can redistribute it and/or modify
853a5a1b3Sopenharmony_ci    it under the terms of the GNU Lesser General Public License as
953a5a1b3Sopenharmony_ci    published by the Free Software Foundation; either version 2.1 of the
1053a5a1b3Sopenharmony_ci    License, or (at your option) any later version.
1153a5a1b3Sopenharmony_ci
1253a5a1b3Sopenharmony_ci    PulseAudio is distributed in the hope that it will be useful, but
1353a5a1b3Sopenharmony_ci    WITHOUT ANY WARRANTY; without even the implied warranty of
1453a5a1b3Sopenharmony_ci    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1553a5a1b3Sopenharmony_ci    Lesser General Public License for more details.
1653a5a1b3Sopenharmony_ci
1753a5a1b3Sopenharmony_ci    You should have received a copy of the GNU Lesser General Public
1853a5a1b3Sopenharmony_ci    License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1953a5a1b3Sopenharmony_ci***/
2053a5a1b3Sopenharmony_ci
2153a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2253a5a1b3Sopenharmony_ci#include <config.h>
2353a5a1b3Sopenharmony_ci#endif
2453a5a1b3Sopenharmony_ci
2553a5a1b3Sopenharmony_ci#ifndef LOG_TAG
2653a5a1b3Sopenharmony_ci#define LOG_TAG "Shm"
2753a5a1b3Sopenharmony_ci#endif
2853a5a1b3Sopenharmony_ci
2953a5a1b3Sopenharmony_ci#include <stdlib.h>
3053a5a1b3Sopenharmony_ci#include <unistd.h>
3153a5a1b3Sopenharmony_ci#include <fcntl.h>
3253a5a1b3Sopenharmony_ci#include <stdio.h>
3353a5a1b3Sopenharmony_ci#include <errno.h>
3453a5a1b3Sopenharmony_ci#include <string.h>
3553a5a1b3Sopenharmony_ci#include <sys/stat.h>
3653a5a1b3Sopenharmony_ci#include <sys/types.h>
3753a5a1b3Sopenharmony_ci#include <dirent.h>
3853a5a1b3Sopenharmony_ci#include <signal.h>
3953a5a1b3Sopenharmony_ci
4053a5a1b3Sopenharmony_ci#ifdef HAVE_SYS_MMAN_H
4153a5a1b3Sopenharmony_ci#include <sys/mman.h>
4253a5a1b3Sopenharmony_ci#endif
4353a5a1b3Sopenharmony_ci
4453a5a1b3Sopenharmony_ci/* This is deprecated on glibc but is still used by FreeBSD */
4553a5a1b3Sopenharmony_ci#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
4653a5a1b3Sopenharmony_ci# define MAP_ANONYMOUS MAP_ANON
4753a5a1b3Sopenharmony_ci#endif
4853a5a1b3Sopenharmony_ci
4953a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
5053a5a1b3Sopenharmony_ci#include <pulse/gccmacro.h>
5153a5a1b3Sopenharmony_ci
5253a5a1b3Sopenharmony_ci#include <pulsecore/memfd-wrappers.h>
5353a5a1b3Sopenharmony_ci#include <pulsecore/core-error.h>
5453a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
5553a5a1b3Sopenharmony_ci#include <pulsecore/random.h>
5653a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
5753a5a1b3Sopenharmony_ci#include <pulsecore/macro.h>
5853a5a1b3Sopenharmony_ci#include <pulsecore/atomic.h>
5953a5a1b3Sopenharmony_ci#include <pulsecore/mem.h>
6053a5a1b3Sopenharmony_ci
6153a5a1b3Sopenharmony_ci#include "shm.h"
6253a5a1b3Sopenharmony_ci
6353a5a1b3Sopenharmony_ci#if defined(__linux__) && !defined(MADV_REMOVE)
6453a5a1b3Sopenharmony_ci#define MADV_REMOVE 9
6553a5a1b3Sopenharmony_ci#endif
6653a5a1b3Sopenharmony_ci
6753a5a1b3Sopenharmony_ci/* 1 GiB at max */
6853a5a1b3Sopenharmony_ci#define MAX_SHM_SIZE (PA_ALIGN(1024*1024*1024))
6953a5a1b3Sopenharmony_ci
7053a5a1b3Sopenharmony_ci#ifdef __linux__
7153a5a1b3Sopenharmony_ci/* On Linux we know that the shared memory blocks are files in
7253a5a1b3Sopenharmony_ci * /dev/shm. We can use that information to list all blocks and
7353a5a1b3Sopenharmony_ci * cleanup unused ones */
7453a5a1b3Sopenharmony_ci#define SHM_PATH "/dev/shm/"
7553a5a1b3Sopenharmony_ci#define SHM_ID_LEN 10
7653a5a1b3Sopenharmony_ci#elif defined(__sun)
7753a5a1b3Sopenharmony_ci#define SHM_PATH "/tmp"
7853a5a1b3Sopenharmony_ci#define SHM_ID_LEN 15
7953a5a1b3Sopenharmony_ci#else
8053a5a1b3Sopenharmony_ci#undef SHM_PATH
8153a5a1b3Sopenharmony_ci#undef SHM_ID_LEN
8253a5a1b3Sopenharmony_ci#endif
8353a5a1b3Sopenharmony_ci
8453a5a1b3Sopenharmony_ci#define SHM_MARKER ((int) 0xbeefcafe)
8553a5a1b3Sopenharmony_ci
8653a5a1b3Sopenharmony_ci/* We now put this SHM marker at the end of each segment. It's
8753a5a1b3Sopenharmony_ci * optional, to not require a reboot when upgrading, though. Note that
8853a5a1b3Sopenharmony_ci * on multiarch systems 32bit and 64bit processes might access this
8953a5a1b3Sopenharmony_ci * region simultaneously. The header fields need to be independent
9053a5a1b3Sopenharmony_ci * from the process' word with */
9153a5a1b3Sopenharmony_cistruct shm_marker {
9253a5a1b3Sopenharmony_ci    pa_atomic_t marker; /* 0xbeefcafe */
9353a5a1b3Sopenharmony_ci    pa_atomic_t pid;
9453a5a1b3Sopenharmony_ci    uint64_t _reserved1;
9553a5a1b3Sopenharmony_ci    uint64_t _reserved2;
9653a5a1b3Sopenharmony_ci    uint64_t _reserved3;
9753a5a1b3Sopenharmony_ci    uint64_t _reserved4;
9853a5a1b3Sopenharmony_ci};
9953a5a1b3Sopenharmony_ci
10053a5a1b3Sopenharmony_ci// Ensure struct is appropriately packed
10153a5a1b3Sopenharmony_cistatic_assert(sizeof(struct shm_marker) == 8 * 5, "`struct shm_marker` is not tightly packed");
10253a5a1b3Sopenharmony_ci
10353a5a1b3Sopenharmony_cistatic inline size_t shm_marker_size(pa_mem_type_t type) {
10453a5a1b3Sopenharmony_ci    if (type == PA_MEM_TYPE_SHARED_POSIX)
10553a5a1b3Sopenharmony_ci        return PA_ALIGN(sizeof(struct shm_marker));
10653a5a1b3Sopenharmony_ci
10753a5a1b3Sopenharmony_ci    return 0;
10853a5a1b3Sopenharmony_ci}
10953a5a1b3Sopenharmony_ci
11053a5a1b3Sopenharmony_ci#ifdef HAVE_SHM_OPEN
11153a5a1b3Sopenharmony_cistatic char *segment_name(char *fn, size_t l, unsigned id) {
11253a5a1b3Sopenharmony_ci    pa_snprintf(fn, l, "/pulse-shm-%u", id);
11353a5a1b3Sopenharmony_ci    return fn;
11453a5a1b3Sopenharmony_ci}
11553a5a1b3Sopenharmony_ci#endif
11653a5a1b3Sopenharmony_ci
11753a5a1b3Sopenharmony_cistatic int privatemem_create(pa_shm *m, size_t size) {
11853a5a1b3Sopenharmony_ci    pa_assert(m);
11953a5a1b3Sopenharmony_ci    pa_assert(size > 0);
12053a5a1b3Sopenharmony_ci
12153a5a1b3Sopenharmony_ci    m->type = PA_MEM_TYPE_PRIVATE;
12253a5a1b3Sopenharmony_ci    m->id = 0;
12353a5a1b3Sopenharmony_ci    m->size = size;
12453a5a1b3Sopenharmony_ci    m->do_unlink = false;
12553a5a1b3Sopenharmony_ci    m->fd = -1;
12653a5a1b3Sopenharmony_ci
12753a5a1b3Sopenharmony_ci#ifdef MAP_ANONYMOUS
12853a5a1b3Sopenharmony_ci    if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, (off_t) 0)) == MAP_FAILED) {
12953a5a1b3Sopenharmony_ci        pa_log_error("mmap() failed: %s", pa_cstrerror(errno));
13053a5a1b3Sopenharmony_ci        return -1;
13153a5a1b3Sopenharmony_ci    }
13253a5a1b3Sopenharmony_ci#elif defined(HAVE_POSIX_MEMALIGN)
13353a5a1b3Sopenharmony_ci    {
13453a5a1b3Sopenharmony_ci        int r;
13553a5a1b3Sopenharmony_ci
13653a5a1b3Sopenharmony_ci        if ((r = posix_memalign(&m->ptr, pa_page_size(), size)) < 0) {
13753a5a1b3Sopenharmony_ci            pa_log_error("posix_memalign() failed: %s", pa_cstrerror(r));
13853a5a1b3Sopenharmony_ci            return r;
13953a5a1b3Sopenharmony_ci        }
14053a5a1b3Sopenharmony_ci    }
14153a5a1b3Sopenharmony_ci#else
14253a5a1b3Sopenharmony_ci    m->ptr = pa_xmalloc(m->size);
14353a5a1b3Sopenharmony_ci#endif
14453a5a1b3Sopenharmony_ci
14553a5a1b3Sopenharmony_ci    return 0;
14653a5a1b3Sopenharmony_ci}
14753a5a1b3Sopenharmony_ci
14853a5a1b3Sopenharmony_cistatic int sharedmem_create(pa_shm *m, pa_mem_type_t type, size_t size, mode_t mode) {
14953a5a1b3Sopenharmony_ci#if defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD)
15053a5a1b3Sopenharmony_ci    char fn[32];
15153a5a1b3Sopenharmony_ci    int fd = -1;
15253a5a1b3Sopenharmony_ci    struct shm_marker *marker;
15353a5a1b3Sopenharmony_ci    bool do_unlink = false;
15453a5a1b3Sopenharmony_ci
15553a5a1b3Sopenharmony_ci    /* Each time we create a new SHM area, let's first drop all stale
15653a5a1b3Sopenharmony_ci     * ones */
15753a5a1b3Sopenharmony_ci    pa_shm_cleanup();
15853a5a1b3Sopenharmony_ci
15953a5a1b3Sopenharmony_ci    pa_random(&m->id, sizeof(m->id));
16053a5a1b3Sopenharmony_ci
16153a5a1b3Sopenharmony_ci    switch (type) {
16253a5a1b3Sopenharmony_ci#ifdef HAVE_SHM_OPEN
16353a5a1b3Sopenharmony_ci    case PA_MEM_TYPE_SHARED_POSIX:
16453a5a1b3Sopenharmony_ci        segment_name(fn, sizeof(fn), m->id);
16553a5a1b3Sopenharmony_ci        fd = shm_open(fn, O_RDWR|O_CREAT|O_EXCL, mode);
16653a5a1b3Sopenharmony_ci        do_unlink = true;
16753a5a1b3Sopenharmony_ci        break;
16853a5a1b3Sopenharmony_ci#endif
16953a5a1b3Sopenharmony_ci#ifdef HAVE_MEMFD
17053a5a1b3Sopenharmony_ci    case PA_MEM_TYPE_SHARED_MEMFD:
17153a5a1b3Sopenharmony_ci        fd = memfd_create("pulseaudio", MFD_ALLOW_SEALING);
17253a5a1b3Sopenharmony_ci        break;
17353a5a1b3Sopenharmony_ci#endif
17453a5a1b3Sopenharmony_ci    default:
17553a5a1b3Sopenharmony_ci        goto fail;
17653a5a1b3Sopenharmony_ci    }
17753a5a1b3Sopenharmony_ci
17853a5a1b3Sopenharmony_ci    if (fd < 0) {
17953a5a1b3Sopenharmony_ci        pa_log_error("%s open() failed: %s", pa_mem_type_to_string(type), pa_cstrerror(errno));
18053a5a1b3Sopenharmony_ci        goto fail;
18153a5a1b3Sopenharmony_ci    }
18253a5a1b3Sopenharmony_ci
18353a5a1b3Sopenharmony_ci    m->type = type;
18453a5a1b3Sopenharmony_ci    m->size = size + shm_marker_size(type);
18553a5a1b3Sopenharmony_ci    m->do_unlink = do_unlink;
18653a5a1b3Sopenharmony_ci
18753a5a1b3Sopenharmony_ci    if (ftruncate(fd, (off_t) m->size) < 0) {
18853a5a1b3Sopenharmony_ci        pa_log_error("ftruncate() failed: %s", pa_cstrerror(errno));
18953a5a1b3Sopenharmony_ci        goto fail;
19053a5a1b3Sopenharmony_ci    }
19153a5a1b3Sopenharmony_ci
19253a5a1b3Sopenharmony_ci#ifndef MAP_NORESERVE
19353a5a1b3Sopenharmony_ci#define MAP_NORESERVE 0
19453a5a1b3Sopenharmony_ci#endif
19553a5a1b3Sopenharmony_ci
19653a5a1b3Sopenharmony_ci    if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_NORESERVE, fd, (off_t) 0)) == MAP_FAILED) {
19753a5a1b3Sopenharmony_ci        pa_log_error("mmap() failed: %s", pa_cstrerror(errno));
19853a5a1b3Sopenharmony_ci        goto fail;
19953a5a1b3Sopenharmony_ci    }
20053a5a1b3Sopenharmony_ci
20153a5a1b3Sopenharmony_ci    if (type == PA_MEM_TYPE_SHARED_POSIX) {
20253a5a1b3Sopenharmony_ci        /* We store our PID at the end of the shm block, so that we
20353a5a1b3Sopenharmony_ci         * can check for dead shm segments later */
20453a5a1b3Sopenharmony_ci        marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - shm_marker_size(type));
20553a5a1b3Sopenharmony_ci        pa_atomic_store(&marker->pid, (int) getpid());
20653a5a1b3Sopenharmony_ci        pa_atomic_store(&marker->marker, SHM_MARKER);
20753a5a1b3Sopenharmony_ci    }
20853a5a1b3Sopenharmony_ci
20953a5a1b3Sopenharmony_ci    /* For memfds, we keep the fd open until we pass it
21053a5a1b3Sopenharmony_ci     * to the other PA endpoint over unix domain socket. */
21153a5a1b3Sopenharmony_ci    if (type != PA_MEM_TYPE_SHARED_MEMFD) {
21253a5a1b3Sopenharmony_ci        pa_assert_se(pa_close(fd) == 0);
21353a5a1b3Sopenharmony_ci        m->fd = -1;
21453a5a1b3Sopenharmony_ci    }
21553a5a1b3Sopenharmony_ci#ifdef HAVE_MEMFD
21653a5a1b3Sopenharmony_ci    else
21753a5a1b3Sopenharmony_ci        m->fd = fd;
21853a5a1b3Sopenharmony_ci#endif
21953a5a1b3Sopenharmony_ci
22053a5a1b3Sopenharmony_ci    return 0;
22153a5a1b3Sopenharmony_ci
22253a5a1b3Sopenharmony_cifail:
22353a5a1b3Sopenharmony_ci    if (fd >= 0) {
22453a5a1b3Sopenharmony_ci#ifdef HAVE_SHM_OPEN
22553a5a1b3Sopenharmony_ci        if (type == PA_MEM_TYPE_SHARED_POSIX)
22653a5a1b3Sopenharmony_ci            shm_unlink(fn);
22753a5a1b3Sopenharmony_ci#endif
22853a5a1b3Sopenharmony_ci        pa_close(fd);
22953a5a1b3Sopenharmony_ci    }
23053a5a1b3Sopenharmony_ci#endif /* defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD) */
23153a5a1b3Sopenharmony_ci
23253a5a1b3Sopenharmony_ci    return -1;
23353a5a1b3Sopenharmony_ci}
23453a5a1b3Sopenharmony_ci
23553a5a1b3Sopenharmony_ciint pa_shm_create_rw(pa_shm *m, pa_mem_type_t type, size_t size, mode_t mode) {
23653a5a1b3Sopenharmony_ci    pa_assert(m);
23753a5a1b3Sopenharmony_ci    pa_assert(size > 0);
23853a5a1b3Sopenharmony_ci    pa_assert(size <= MAX_SHM_SIZE);
23953a5a1b3Sopenharmony_ci    pa_assert(!(mode & ~0777));
24053a5a1b3Sopenharmony_ci    pa_assert(mode >= 0600);
24153a5a1b3Sopenharmony_ci
24253a5a1b3Sopenharmony_ci    /* Round up to make it page aligned */
24353a5a1b3Sopenharmony_ci    size = PA_PAGE_ALIGN(size);
24453a5a1b3Sopenharmony_ci
24553a5a1b3Sopenharmony_ci    if (type == PA_MEM_TYPE_PRIVATE)
24653a5a1b3Sopenharmony_ci        return privatemem_create(m, size);
24753a5a1b3Sopenharmony_ci
24853a5a1b3Sopenharmony_ci    return sharedmem_create(m, type, size, mode);
24953a5a1b3Sopenharmony_ci}
25053a5a1b3Sopenharmony_ci
25153a5a1b3Sopenharmony_cistatic void privatemem_free(pa_shm *m) {
25253a5a1b3Sopenharmony_ci    pa_assert(m);
25353a5a1b3Sopenharmony_ci    pa_assert(m->ptr);
25453a5a1b3Sopenharmony_ci    pa_assert(m->size > 0);
25553a5a1b3Sopenharmony_ci
25653a5a1b3Sopenharmony_ci#ifdef MAP_ANONYMOUS
25753a5a1b3Sopenharmony_ci    if (munmap(m->ptr, m->size) < 0)
25853a5a1b3Sopenharmony_ci        pa_log_error("munmap() failed: %s", pa_cstrerror(errno));
25953a5a1b3Sopenharmony_ci#elif defined(HAVE_POSIX_MEMALIGN)
26053a5a1b3Sopenharmony_ci    free(m->ptr);
26153a5a1b3Sopenharmony_ci#else
26253a5a1b3Sopenharmony_ci    pa_xfree(m->ptr);
26353a5a1b3Sopenharmony_ci#endif
26453a5a1b3Sopenharmony_ci}
26553a5a1b3Sopenharmony_ci
26653a5a1b3Sopenharmony_civoid pa_shm_free(pa_shm *m) {
26753a5a1b3Sopenharmony_ci    pa_assert(m);
26853a5a1b3Sopenharmony_ci    pa_assert(m->ptr);
26953a5a1b3Sopenharmony_ci    pa_assert(m->size > 0);
27053a5a1b3Sopenharmony_ci
27153a5a1b3Sopenharmony_ci#ifdef MAP_FAILED
27253a5a1b3Sopenharmony_ci    pa_assert(m->ptr != MAP_FAILED);
27353a5a1b3Sopenharmony_ci#endif
27453a5a1b3Sopenharmony_ci
27553a5a1b3Sopenharmony_ci    if (m->type == PA_MEM_TYPE_PRIVATE) {
27653a5a1b3Sopenharmony_ci        privatemem_free(m);
27753a5a1b3Sopenharmony_ci        goto finish;
27853a5a1b3Sopenharmony_ci    }
27953a5a1b3Sopenharmony_ci
28053a5a1b3Sopenharmony_ci    pa_log_info("mem type: %d", m->type);
28153a5a1b3Sopenharmony_ci
28253a5a1b3Sopenharmony_ci#if defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD)
28353a5a1b3Sopenharmony_ci    if (munmap(m->ptr, PA_PAGE_ALIGN(m->size)) < 0)
28453a5a1b3Sopenharmony_ci        pa_log_error("munmap() failed: %s", pa_cstrerror(errno));
28553a5a1b3Sopenharmony_ci
28653a5a1b3Sopenharmony_ci#ifdef HAVE_SHM_OPEN
28753a5a1b3Sopenharmony_ci    if (m->type == PA_MEM_TYPE_SHARED_POSIX && m->do_unlink) {
28853a5a1b3Sopenharmony_ci        char fn[32];
28953a5a1b3Sopenharmony_ci
29053a5a1b3Sopenharmony_ci        segment_name(fn, sizeof(fn), m->id);
29153a5a1b3Sopenharmony_ci        if (shm_unlink(fn) < 0)
29253a5a1b3Sopenharmony_ci            pa_log_error(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
29353a5a1b3Sopenharmony_ci    }
29453a5a1b3Sopenharmony_ci#endif
29553a5a1b3Sopenharmony_ci#ifdef HAVE_MEMFD
29653a5a1b3Sopenharmony_ci    if (m->type == PA_MEM_TYPE_SHARED_MEMFD && m->fd != -1)
29753a5a1b3Sopenharmony_ci        pa_assert_se(pa_close(m->fd) == 0);
29853a5a1b3Sopenharmony_ci#endif
29953a5a1b3Sopenharmony_ci
30053a5a1b3Sopenharmony_ci#else
30153a5a1b3Sopenharmony_ci    /* We shouldn't be here without shm or memfd support */
30253a5a1b3Sopenharmony_ci    pa_log_error("remove pa_assert_not_reached call");
30353a5a1b3Sopenharmony_ci#endif
30453a5a1b3Sopenharmony_ci
30553a5a1b3Sopenharmony_cifinish:
30653a5a1b3Sopenharmony_ci    pa_zero(*m);
30753a5a1b3Sopenharmony_ci}
30853a5a1b3Sopenharmony_ci
30953a5a1b3Sopenharmony_civoid pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
31053a5a1b3Sopenharmony_ci    void *ptr;
31153a5a1b3Sopenharmony_ci    size_t o;
31253a5a1b3Sopenharmony_ci    const size_t page_size = pa_page_size();
31353a5a1b3Sopenharmony_ci
31453a5a1b3Sopenharmony_ci    pa_assert(m);
31553a5a1b3Sopenharmony_ci    pa_assert(m->ptr);
31653a5a1b3Sopenharmony_ci    pa_assert(m->size > 0);
31753a5a1b3Sopenharmony_ci    pa_assert(offset+size <= m->size);
31853a5a1b3Sopenharmony_ci
31953a5a1b3Sopenharmony_ci#ifdef MAP_FAILED
32053a5a1b3Sopenharmony_ci    pa_assert(m->ptr != MAP_FAILED);
32153a5a1b3Sopenharmony_ci#endif
32253a5a1b3Sopenharmony_ci
32353a5a1b3Sopenharmony_ci    /* You're welcome to implement this as NOOP on systems that don't
32453a5a1b3Sopenharmony_ci     * support it */
32553a5a1b3Sopenharmony_ci
32653a5a1b3Sopenharmony_ci    /* Align the pointer up to multiples of the page size */
32753a5a1b3Sopenharmony_ci    ptr = (uint8_t*) m->ptr + offset;
32853a5a1b3Sopenharmony_ci    o = (size_t) ((uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr));
32953a5a1b3Sopenharmony_ci
33053a5a1b3Sopenharmony_ci    if (o > 0) {
33153a5a1b3Sopenharmony_ci        size_t delta = page_size - o;
33253a5a1b3Sopenharmony_ci        ptr = (uint8_t*) ptr + delta;
33353a5a1b3Sopenharmony_ci        size -= delta;
33453a5a1b3Sopenharmony_ci    }
33553a5a1b3Sopenharmony_ci
33653a5a1b3Sopenharmony_ci    /* Align the size down to multiples of page size */
33753a5a1b3Sopenharmony_ci    size = (size / page_size) * page_size;
33853a5a1b3Sopenharmony_ci
33953a5a1b3Sopenharmony_ci#ifdef MADV_REMOVE
34053a5a1b3Sopenharmony_ci    if (madvise(ptr, size, MADV_REMOVE) >= 0)
34153a5a1b3Sopenharmony_ci        return;
34253a5a1b3Sopenharmony_ci#endif
34353a5a1b3Sopenharmony_ci
34453a5a1b3Sopenharmony_ci#ifdef MADV_FREE
34553a5a1b3Sopenharmony_ci    if (madvise(ptr, size, MADV_FREE) >= 0)
34653a5a1b3Sopenharmony_ci        return;
34753a5a1b3Sopenharmony_ci#endif
34853a5a1b3Sopenharmony_ci
34953a5a1b3Sopenharmony_ci#ifdef MADV_DONTNEED
35053a5a1b3Sopenharmony_ci    madvise(ptr, size, MADV_DONTNEED);
35153a5a1b3Sopenharmony_ci#elif defined(POSIX_MADV_DONTNEED)
35253a5a1b3Sopenharmony_ci    posix_madvise(ptr, size, POSIX_MADV_DONTNEED);
35353a5a1b3Sopenharmony_ci#endif
35453a5a1b3Sopenharmony_ci}
35553a5a1b3Sopenharmony_ci
35653a5a1b3Sopenharmony_cistatic int shm_attach(pa_shm *m, pa_mem_type_t type, unsigned id, int memfd_fd, bool writable, bool for_cleanup) {
35753a5a1b3Sopenharmony_ci#if defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD)
35853a5a1b3Sopenharmony_ci    char fn[32];
35953a5a1b3Sopenharmony_ci    int fd = -1;
36053a5a1b3Sopenharmony_ci    int prot;
36153a5a1b3Sopenharmony_ci    struct stat st;
36253a5a1b3Sopenharmony_ci
36353a5a1b3Sopenharmony_ci    pa_assert(m);
36453a5a1b3Sopenharmony_ci
36553a5a1b3Sopenharmony_ci    switch (type) {
36653a5a1b3Sopenharmony_ci#ifdef HAVE_SHM_OPEN
36753a5a1b3Sopenharmony_ci    case PA_MEM_TYPE_SHARED_POSIX:
36853a5a1b3Sopenharmony_ci        pa_assert(memfd_fd == -1);
36953a5a1b3Sopenharmony_ci        segment_name(fn, sizeof(fn), id);
37053a5a1b3Sopenharmony_ci        if ((fd = shm_open(fn, writable ? O_RDWR : O_RDONLY, 0)) < 0) {
37153a5a1b3Sopenharmony_ci            if ((errno != EACCES && errno != ENOENT) || !for_cleanup)
37253a5a1b3Sopenharmony_ci                pa_log_error("shm_open() failed: %s", pa_cstrerror(errno));
37353a5a1b3Sopenharmony_ci            goto fail;
37453a5a1b3Sopenharmony_ci        }
37553a5a1b3Sopenharmony_ci        break;
37653a5a1b3Sopenharmony_ci#endif
37753a5a1b3Sopenharmony_ci#ifdef HAVE_MEMFD
37853a5a1b3Sopenharmony_ci    case PA_MEM_TYPE_SHARED_MEMFD:
37953a5a1b3Sopenharmony_ci        pa_assert(memfd_fd != -1);
38053a5a1b3Sopenharmony_ci        fd = memfd_fd;
38153a5a1b3Sopenharmony_ci        break;
38253a5a1b3Sopenharmony_ci#endif
38353a5a1b3Sopenharmony_ci    default:
38453a5a1b3Sopenharmony_ci        goto fail;
38553a5a1b3Sopenharmony_ci    }
38653a5a1b3Sopenharmony_ci
38753a5a1b3Sopenharmony_ci    if (fstat(fd, &st) < 0) {
38853a5a1b3Sopenharmony_ci        pa_log_error("fstat() failed: %s", pa_cstrerror(errno));
38953a5a1b3Sopenharmony_ci        goto fail;
39053a5a1b3Sopenharmony_ci    }
39153a5a1b3Sopenharmony_ci
39253a5a1b3Sopenharmony_ci    if (st.st_size <= 0 ||
39353a5a1b3Sopenharmony_ci        st.st_size > (off_t) MAX_SHM_SIZE + (off_t) shm_marker_size(type) ||
39453a5a1b3Sopenharmony_ci        PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) {
39553a5a1b3Sopenharmony_ci        pa_log_error("Invalid shared memory segment size");
39653a5a1b3Sopenharmony_ci        goto fail;
39753a5a1b3Sopenharmony_ci    }
39853a5a1b3Sopenharmony_ci
39953a5a1b3Sopenharmony_ci    prot = writable ? PROT_READ | PROT_WRITE : PROT_READ;
40053a5a1b3Sopenharmony_ci    if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(st.st_size), prot, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
40153a5a1b3Sopenharmony_ci        pa_log_error("mmap() failed: %s", pa_cstrerror(errno));
40253a5a1b3Sopenharmony_ci        goto fail;
40353a5a1b3Sopenharmony_ci    }
40453a5a1b3Sopenharmony_ci
40553a5a1b3Sopenharmony_ci    /* In case of attaching to memfd areas, _the caller_ maintains
40653a5a1b3Sopenharmony_ci     * ownership of the passed fd and has the sole responsibility
40753a5a1b3Sopenharmony_ci     * of closing it down.. For other types, we're the code path
40853a5a1b3Sopenharmony_ci     * which created the fd in the first place and we're thus the
40953a5a1b3Sopenharmony_ci     * ones responsible for closing it down */
41053a5a1b3Sopenharmony_ci    if (type != PA_MEM_TYPE_SHARED_MEMFD)
41153a5a1b3Sopenharmony_ci        pa_assert_se(pa_close(fd) == 0);
41253a5a1b3Sopenharmony_ci
41353a5a1b3Sopenharmony_ci    pa_log_info("shm_attach set mem type %d", type);
41453a5a1b3Sopenharmony_ci    m->type = type;
41553a5a1b3Sopenharmony_ci    m->id = id;
41653a5a1b3Sopenharmony_ci    m->size = (size_t) st.st_size;
41753a5a1b3Sopenharmony_ci    m->do_unlink = false;
41853a5a1b3Sopenharmony_ci    m->fd = -1;
41953a5a1b3Sopenharmony_ci
42053a5a1b3Sopenharmony_ci    return 0;
42153a5a1b3Sopenharmony_ci
42253a5a1b3Sopenharmony_cifail:
42353a5a1b3Sopenharmony_ci    /* In case of memfds, caller maintains fd ownership */
42453a5a1b3Sopenharmony_ci    if (fd >= 0 && type != PA_MEM_TYPE_SHARED_MEMFD)
42553a5a1b3Sopenharmony_ci        pa_close(fd);
42653a5a1b3Sopenharmony_ci
42753a5a1b3Sopenharmony_ci#endif /* defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD) */
42853a5a1b3Sopenharmony_ci
42953a5a1b3Sopenharmony_ci    return -1;
43053a5a1b3Sopenharmony_ci}
43153a5a1b3Sopenharmony_ci
43253a5a1b3Sopenharmony_ci/* Caller owns passed @memfd_fd and must close it down when appropriate. */
43353a5a1b3Sopenharmony_ciint pa_shm_attach(pa_shm *m, pa_mem_type_t type, unsigned id, int memfd_fd, bool writable) {
43453a5a1b3Sopenharmony_ci    return shm_attach(m, type, id, memfd_fd, writable, false);
43553a5a1b3Sopenharmony_ci}
43653a5a1b3Sopenharmony_ci
43753a5a1b3Sopenharmony_ciint pa_shm_cleanup(void) {
43853a5a1b3Sopenharmony_ci    pa_log_info("start pa_shm_cleanup");
43953a5a1b3Sopenharmony_ci#ifdef HAVE_SHM_OPEN
44053a5a1b3Sopenharmony_ci#ifdef SHM_PATH
44153a5a1b3Sopenharmony_ci    DIR *d;
44253a5a1b3Sopenharmony_ci    struct dirent *de;
44353a5a1b3Sopenharmony_ci
44453a5a1b3Sopenharmony_ci    if (!(d = opendir(SHM_PATH))) {
44553a5a1b3Sopenharmony_ci        pa_log_warn("Failed to read "SHM_PATH": %s", pa_cstrerror(errno));
44653a5a1b3Sopenharmony_ci        return -1;
44753a5a1b3Sopenharmony_ci    }
44853a5a1b3Sopenharmony_ci
44953a5a1b3Sopenharmony_ci    while ((de = readdir(d))) {
45053a5a1b3Sopenharmony_ci        pa_shm seg;
45153a5a1b3Sopenharmony_ci        unsigned id;
45253a5a1b3Sopenharmony_ci        pid_t pid;
45353a5a1b3Sopenharmony_ci        char fn[128];
45453a5a1b3Sopenharmony_ci        struct shm_marker *m;
45553a5a1b3Sopenharmony_ci
45653a5a1b3Sopenharmony_ci#if defined(__sun)
45753a5a1b3Sopenharmony_ci        if (strncmp(de->d_name, ".SHMDpulse-shm-", SHM_ID_LEN))
45853a5a1b3Sopenharmony_ci#else
45953a5a1b3Sopenharmony_ci        if (strncmp(de->d_name, "pulse-shm-", SHM_ID_LEN))
46053a5a1b3Sopenharmony_ci#endif
46153a5a1b3Sopenharmony_ci            continue;
46253a5a1b3Sopenharmony_ci
46353a5a1b3Sopenharmony_ci        if (pa_atou(de->d_name + SHM_ID_LEN, &id) < 0)
46453a5a1b3Sopenharmony_ci            continue;
46553a5a1b3Sopenharmony_ci
46653a5a1b3Sopenharmony_ci        if (shm_attach(&seg, PA_MEM_TYPE_SHARED_POSIX, id, -1, false, true) < 0)
46753a5a1b3Sopenharmony_ci            continue;
46853a5a1b3Sopenharmony_ci
46953a5a1b3Sopenharmony_ci        if (seg.size < shm_marker_size(seg.type)) {
47053a5a1b3Sopenharmony_ci            pa_shm_free(&seg);
47153a5a1b3Sopenharmony_ci            continue;
47253a5a1b3Sopenharmony_ci        }
47353a5a1b3Sopenharmony_ci
47453a5a1b3Sopenharmony_ci        m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - shm_marker_size(seg.type));
47553a5a1b3Sopenharmony_ci
47653a5a1b3Sopenharmony_ci        if (pa_atomic_load(&m->marker) != SHM_MARKER) {
47753a5a1b3Sopenharmony_ci            pa_shm_free(&seg);
47853a5a1b3Sopenharmony_ci            continue;
47953a5a1b3Sopenharmony_ci        }
48053a5a1b3Sopenharmony_ci
48153a5a1b3Sopenharmony_ci        if (!(pid = (pid_t) pa_atomic_load(&m->pid))) {
48253a5a1b3Sopenharmony_ci            pa_shm_free(&seg);
48353a5a1b3Sopenharmony_ci            continue;
48453a5a1b3Sopenharmony_ci        }
48553a5a1b3Sopenharmony_ci
48653a5a1b3Sopenharmony_ci        if (kill(pid, 0) == 0 || errno != ESRCH) {
48753a5a1b3Sopenharmony_ci            pa_shm_free(&seg);
48853a5a1b3Sopenharmony_ci            continue;
48953a5a1b3Sopenharmony_ci        }
49053a5a1b3Sopenharmony_ci
49153a5a1b3Sopenharmony_ci        pa_shm_free(&seg);
49253a5a1b3Sopenharmony_ci
49353a5a1b3Sopenharmony_ci        /* Ok, the owner of this shms segment is dead, so, let's remove the segment */
49453a5a1b3Sopenharmony_ci        segment_name(fn, sizeof(fn), id);
49553a5a1b3Sopenharmony_ci
49653a5a1b3Sopenharmony_ci        if (shm_unlink(fn) < 0 && errno != EACCES && errno != ENOENT)
49753a5a1b3Sopenharmony_ci            pa_log_warn("Failed to remove SHM segment %s: %s", fn, pa_cstrerror(errno));
49853a5a1b3Sopenharmony_ci    }
49953a5a1b3Sopenharmony_ci
50053a5a1b3Sopenharmony_ci    closedir(d);
50153a5a1b3Sopenharmony_ci#endif /* SHM_PATH */
50253a5a1b3Sopenharmony_ci#endif /* HAVE_SHM_OPEN */
50353a5a1b3Sopenharmony_ci
50453a5a1b3Sopenharmony_ci    return 0;
50553a5a1b3Sopenharmony_ci}
506