build-sys: Use ax_check_flag macros from autoconf archive
[pulseaudio-mirror.git] / src / pulsecore / shm.c
blob442b698e224cea88b65a33dc30f427cff8fa43b9
1 /***
2 This file is part of PulseAudio.
4 Copyright 2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as
9 published by the Free Software Foundation; either version 2.1 of the
10 License, or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <dirent.h>
36 #include <signal.h>
38 #ifdef HAVE_SYS_MMAN_H
39 #include <sys/mman.h>
40 #endif
42 /* This is deprecated on glibc but is still used by FreeBSD */
43 #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
44 # define MAP_ANONYMOUS MAP_ANON
45 #endif
47 #include <pulse/xmalloc.h>
48 #include <pulse/gccmacro.h>
50 #include <pulsecore/core-error.h>
51 #include <pulsecore/log.h>
52 #include <pulsecore/random.h>
53 #include <pulsecore/core-util.h>
54 #include <pulsecore/macro.h>
55 #include <pulsecore/atomic.h>
57 #include "shm.h"
59 #if defined(__linux__) && !defined(MADV_REMOVE)
60 #define MADV_REMOVE 9
61 #endif
63 /* 1 GiB at max */
64 #define MAX_SHM_SIZE (PA_ALIGN(1024*1024*1024))
66 #ifdef __linux__
67 /* On Linux we know that the shared memory blocks are files in
68 * /dev/shm. We can use that information to list all blocks and
69 * cleanup unused ones */
70 #define SHM_PATH "/dev/shm/"
71 #else
72 #undef SHM_PATH
73 #endif
75 #define SHM_MARKER ((int) 0xbeefcafe)
77 /* We now put this SHM marker at the end of each segment. It's
78 * optional, to not require a reboot when upgrading, though. Note that
79 * on multiarch systems 32bit and 64bit processes might access this
80 * region simultaneously. The header fields need to be independant
81 * from the process' word with */
82 struct shm_marker {
83 pa_atomic_t marker; /* 0xbeefcafe */
84 pa_atomic_t pid;
85 uint64_t _reserved1;
86 uint64_t _reserved2;
87 uint64_t _reserved3;
88 uint64_t _reserved4;
89 } PA_GCC_PACKED;
91 #define SHM_MARKER_SIZE PA_ALIGN(sizeof(struct shm_marker))
93 #ifdef HAVE_SHM_OPEN
94 static char *segment_name(char *fn, size_t l, unsigned id) {
95 pa_snprintf(fn, l, "/pulse-shm-%u", id);
96 return fn;
98 #endif
100 int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode) {
101 #ifdef HAVE_SHM_OPEN
102 char fn[32];
103 int fd = -1;
104 #endif
106 pa_assert(m);
107 pa_assert(size > 0);
108 pa_assert(size <= MAX_SHM_SIZE);
109 pa_assert(mode >= 0600);
111 /* Each time we create a new SHM area, let's first drop all stale
112 * ones */
113 pa_shm_cleanup();
115 /* Round up to make it page aligned */
116 size = PA_PAGE_ALIGN(size);
118 if (!shared) {
119 m->id = 0;
120 m->size = size;
122 #ifdef MAP_ANONYMOUS
123 if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, (off_t) 0)) == MAP_FAILED) {
124 pa_log("mmap() failed: %s", pa_cstrerror(errno));
125 goto fail;
127 #elif defined(HAVE_POSIX_MEMALIGN)
129 int r;
131 if ((r = posix_memalign(&m->ptr, PA_PAGE_SIZE, size)) < 0) {
132 pa_log("posix_memalign() failed: %s", pa_cstrerror(r));
133 goto fail;
136 #else
137 m->ptr = pa_xmalloc(m->size);
138 #endif
140 m->do_unlink = FALSE;
142 } else {
143 #ifdef HAVE_SHM_OPEN
144 struct shm_marker *marker;
146 pa_random(&m->id, sizeof(m->id));
147 segment_name(fn, sizeof(fn), m->id);
149 if ((fd = shm_open(fn, O_RDWR|O_CREAT|O_EXCL, mode & 0444)) < 0) {
150 pa_log("shm_open() failed: %s", pa_cstrerror(errno));
151 goto fail;
154 m->size = size + SHM_MARKER_SIZE;
156 if (ftruncate(fd, (off_t) m->size) < 0) {
157 pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
158 goto fail;
161 #ifndef MAP_NORESERVE
162 #define MAP_NORESERVE 0
163 #endif
165 if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_NORESERVE, fd, (off_t) 0)) == MAP_FAILED) {
166 pa_log("mmap() failed: %s", pa_cstrerror(errno));
167 goto fail;
170 /* We store our PID at the end of the shm block, so that we
171 * can check for dead shm segments later */
172 marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - SHM_MARKER_SIZE);
173 pa_atomic_store(&marker->pid, (int) getpid());
174 pa_atomic_store(&marker->marker, SHM_MARKER);
176 pa_assert_se(pa_close(fd) == 0);
177 m->do_unlink = TRUE;
178 #else
179 return -1;
180 #endif
183 m->shared = shared;
185 return 0;
187 fail:
189 #ifdef HAVE_SHM_OPEN
190 if (fd >= 0) {
191 shm_unlink(fn);
192 pa_close(fd);
194 #endif
196 return -1;
199 void pa_shm_free(pa_shm *m) {
200 pa_assert(m);
201 pa_assert(m->ptr);
202 pa_assert(m->size > 0);
204 #ifdef MAP_FAILED
205 pa_assert(m->ptr != MAP_FAILED);
206 #endif
208 if (!m->shared) {
209 #ifdef MAP_ANONYMOUS
210 if (munmap(m->ptr, m->size) < 0)
211 pa_log("munmap() failed: %s", pa_cstrerror(errno));
212 #elif defined(HAVE_POSIX_MEMALIGN)
213 free(m->ptr);
214 #else
215 pa_xfree(m->ptr);
216 #endif
217 } else {
218 #ifdef HAVE_SHM_OPEN
219 if (munmap(m->ptr, PA_PAGE_ALIGN(m->size)) < 0)
220 pa_log("munmap() failed: %s", pa_cstrerror(errno));
222 if (m->do_unlink) {
223 char fn[32];
225 segment_name(fn, sizeof(fn), m->id);
227 if (shm_unlink(fn) < 0)
228 pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
230 #else
231 /* We shouldn't be here without shm support */
232 pa_assert_not_reached();
233 #endif
236 pa_zero(*m);
239 void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
240 void *ptr;
241 size_t o;
243 pa_assert(m);
244 pa_assert(m->ptr);
245 pa_assert(m->size > 0);
246 pa_assert(offset+size <= m->size);
248 #ifdef MAP_FAILED
249 pa_assert(m->ptr != MAP_FAILED);
250 #endif
252 /* You're welcome to implement this as NOOP on systems that don't
253 * support it */
255 /* Align the pointer up to multiples of the page size */
256 ptr = (uint8_t*) m->ptr + offset;
257 o = (size_t) ((uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr));
259 if (o > 0) {
260 size_t delta = PA_PAGE_SIZE - o;
261 ptr = (uint8_t*) ptr + delta;
262 size -= delta;
265 /* Align the size down to multiples of page size */
266 size = (size / PA_PAGE_SIZE) * PA_PAGE_SIZE;
268 #ifdef MADV_REMOVE
269 if (madvise(ptr, size, MADV_REMOVE) >= 0)
270 return;
271 #endif
273 #ifdef MADV_FREE
274 if (madvise(ptr, size, MADV_FREE) >= 0)
275 return;
276 #endif
278 #ifdef MADV_DONTNEED
279 madvise(ptr, size, MADV_DONTNEED);
280 #elif defined(POSIX_MADV_DONTNEED)
281 posix_madvise(ptr, size, POSIX_MADV_DONTNEED);
282 #endif
285 #ifdef HAVE_SHM_OPEN
287 int pa_shm_attach_ro(pa_shm *m, unsigned id) {
288 char fn[32];
289 int fd = -1;
290 struct stat st;
292 pa_assert(m);
294 segment_name(fn, sizeof(fn), m->id = id);
296 if ((fd = shm_open(fn, O_RDONLY, 0)) < 0) {
297 if (errno != EACCES && errno != ENOENT)
298 pa_log("shm_open() failed: %s", pa_cstrerror(errno));
299 goto fail;
302 if (fstat(fd, &st) < 0) {
303 pa_log("fstat() failed: %s", pa_cstrerror(errno));
304 goto fail;
307 if (st.st_size <= 0 ||
308 st.st_size > (off_t) (MAX_SHM_SIZE+SHM_MARKER_SIZE) ||
309 PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) {
310 pa_log("Invalid shared memory segment size");
311 goto fail;
314 m->size = (size_t) st.st_size;
316 if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
317 pa_log("mmap() failed: %s", pa_cstrerror(errno));
318 goto fail;
321 m->do_unlink = FALSE;
322 m->shared = TRUE;
324 pa_assert_se(pa_close(fd) == 0);
326 return 0;
328 fail:
329 if (fd >= 0)
330 pa_close(fd);
332 return -1;
335 #else /* HAVE_SHM_OPEN */
337 int pa_shm_attach_ro(pa_shm *m, unsigned id) {
338 return -1;
341 #endif /* HAVE_SHM_OPEN */
343 int pa_shm_cleanup(void) {
345 #ifdef HAVE_SHM_OPEN
346 #ifdef SHM_PATH
347 DIR *d;
348 struct dirent *de;
350 if (!(d = opendir(SHM_PATH))) {
351 pa_log_warn("Failed to read "SHM_PATH": %s", pa_cstrerror(errno));
352 return -1;
355 while ((de = readdir(d))) {
356 pa_shm seg;
357 unsigned id;
358 pid_t pid;
359 char fn[128];
360 struct shm_marker *m;
362 if (strncmp(de->d_name, "pulse-shm-", 10))
363 continue;
365 if (pa_atou(de->d_name + 10, &id) < 0)
366 continue;
368 if (pa_shm_attach_ro(&seg, id) < 0)
369 continue;
371 if (seg.size < SHM_MARKER_SIZE) {
372 pa_shm_free(&seg);
373 continue;
376 m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - SHM_MARKER_SIZE);
378 if (pa_atomic_load(&m->marker) != SHM_MARKER) {
379 pa_shm_free(&seg);
380 continue;
383 if (!(pid = (pid_t) pa_atomic_load(&m->pid))) {
384 pa_shm_free(&seg);
385 continue;
388 if (kill(pid, 0) == 0 || errno != ESRCH) {
389 pa_shm_free(&seg);
390 continue;
393 pa_shm_free(&seg);
395 /* Ok, the owner of this shms segment is dead, so, let's remove the segment */
396 segment_name(fn, sizeof(fn), id);
398 if (shm_unlink(fn) < 0 && errno != EACCES && errno != ENOENT)
399 pa_log_warn("Failed to remove SHM segment %s: %s\n", fn, pa_cstrerror(errno));
402 closedir(d);
403 #endif /* SHM_PATH */
404 #endif /* HAVE_SHM_OPEN */
406 return 0;