]> code.delx.au - pulseaudio/blob - src/pulsecore/shm.c
Merge branch 'master' into dbus-work
[pulseaudio] / src / pulsecore / shm.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
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.
11
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.
16
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 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
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>
37
38 #ifdef HAVE_SYS_MMAN_H
39 #include <sys/mman.h>
40 #endif
41
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
46
47 #include <pulse/xmalloc.h>
48 #include <pulse/gccmacro.h>
49
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>
56
57 #include "shm.h"
58
59 #if defined(__linux__) && !defined(MADV_REMOVE)
60 #define MADV_REMOVE 9
61 #endif
62
63 #define MAX_SHM_SIZE (PA_ALIGN(1024*1024*64))
64
65 #ifdef __linux__
66 /* On Linux we know that the shared memory blocks are files in
67 * /dev/shm. We can use that information to list all blocks and
68 * cleanup unused ones */
69 #define SHM_PATH "/dev/shm/"
70 #else
71 #undef SHM_PATH
72 #endif
73
74 #define SHM_MARKER ((int) 0xbeefcafe)
75
76 /* We now put this SHM marker at the end of each segment. It's
77 * optional, to not require a reboot when upgrading, though. Note that
78 * on multiarch systems 32bit and 64bit processes might access this
79 * region simultaneously. The header fields need to be independant
80 * from the process' word with */
81 struct shm_marker {
82 pa_atomic_t marker; /* 0xbeefcafe */
83 pa_atomic_t pid;
84 uint64_t _reserved1;
85 uint64_t _reserved2;
86 uint64_t _reserved3;
87 uint64_t _reserved4;
88 } PA_GCC_PACKED;
89
90 #define SHM_MARKER_SIZE PA_ALIGN(sizeof(struct shm_marker))
91
92 static char *segment_name(char *fn, size_t l, unsigned id) {
93 pa_snprintf(fn, l, "/pulse-shm-%u", id);
94 return fn;
95 }
96
97 int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode) {
98 char fn[32];
99 int fd = -1;
100
101 pa_assert(m);
102 pa_assert(size > 0);
103 pa_assert(size <= MAX_SHM_SIZE);
104 pa_assert(mode >= 0600);
105
106 /* Each time we create a new SHM area, let's first drop all stale
107 * ones */
108 pa_shm_cleanup();
109
110 /* Round up to make it page aligned */
111 size = PA_PAGE_ALIGN(size);
112
113 if (!shared) {
114 m->id = 0;
115 m->size = size;
116
117 #ifdef MAP_ANONYMOUS
118 if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, (off_t) 0)) == MAP_FAILED) {
119 pa_log("mmap() failed: %s", pa_cstrerror(errno));
120 goto fail;
121 }
122 #elif defined(HAVE_POSIX_MEMALIGN)
123 {
124 int r;
125
126 if ((r = posix_memalign(&m->ptr, PA_PAGE_SIZE, size)) < 0) {
127 pa_log("posix_memalign() failed: %s", pa_cstrerror(r));
128 goto fail;
129 }
130 }
131 #else
132 m->ptr = pa_xmalloc(m->size);
133 #endif
134
135 m->do_unlink = FALSE;
136
137 } else {
138 #ifdef HAVE_SHM_OPEN
139 struct shm_marker *marker;
140
141 pa_random(&m->id, sizeof(m->id));
142 segment_name(fn, sizeof(fn), m->id);
143
144 if ((fd = shm_open(fn, O_RDWR|O_CREAT|O_EXCL, mode & 0444)) < 0) {
145 pa_log("shm_open() failed: %s", pa_cstrerror(errno));
146 goto fail;
147 }
148
149 m->size = size + SHM_MARKER_SIZE;
150
151 if (ftruncate(fd, (off_t) m->size) < 0) {
152 pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
153 goto fail;
154 }
155
156 if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
157 pa_log("mmap() failed: %s", pa_cstrerror(errno));
158 goto fail;
159 }
160
161 /* We store our PID at the end of the shm block, so that we
162 * can check for dead shm segments later */
163 marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - SHM_MARKER_SIZE);
164 pa_atomic_store(&marker->pid, (int) getpid());
165 pa_atomic_store(&marker->marker, SHM_MARKER);
166
167 pa_assert_se(pa_close(fd) == 0);
168 m->do_unlink = TRUE;
169 #else
170 return -1;
171 #endif
172 }
173
174 m->shared = shared;
175
176 return 0;
177
178 fail:
179
180 #ifdef HAVE_SHM_OPEN
181 if (fd >= 0) {
182 shm_unlink(fn);
183 pa_close(fd);
184 }
185 #endif
186
187 return -1;
188 }
189
190 void pa_shm_free(pa_shm *m) {
191 pa_assert(m);
192 pa_assert(m->ptr);
193 pa_assert(m->size > 0);
194
195 #ifdef MAP_FAILED
196 pa_assert(m->ptr != MAP_FAILED);
197 #endif
198
199 if (!m->shared) {
200 #ifdef MAP_ANONYMOUS
201 if (munmap(m->ptr, m->size) < 0)
202 pa_log("munmap() failed: %s", pa_cstrerror(errno));
203 #elif defined(HAVE_POSIX_MEMALIGN)
204 free(m->ptr);
205 #else
206 pa_xfree(m->ptr);
207 #endif
208 } else {
209 #ifdef HAVE_SHM_OPEN
210 if (munmap(m->ptr, PA_PAGE_ALIGN(m->size)) < 0)
211 pa_log("munmap() failed: %s", pa_cstrerror(errno));
212
213 if (m->do_unlink) {
214 char fn[32];
215
216 segment_name(fn, sizeof(fn), m->id);
217
218 if (shm_unlink(fn) < 0)
219 pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
220 }
221 #else
222 /* We shouldn't be here without shm support */
223 pa_assert_not_reached();
224 #endif
225 }
226
227 pa_zero(*m);
228 }
229
230 void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
231 void *ptr;
232 size_t o;
233
234 pa_assert(m);
235 pa_assert(m->ptr);
236 pa_assert(m->size > 0);
237 pa_assert(offset+size <= m->size);
238
239 #ifdef MAP_FAILED
240 pa_assert(m->ptr != MAP_FAILED);
241 #endif
242
243 /* You're welcome to implement this as NOOP on systems that don't
244 * support it */
245
246 /* Align the pointer up to multiples of the page size */
247 ptr = (uint8_t*) m->ptr + offset;
248 o = (size_t) ((uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr));
249
250 if (o > 0) {
251 size_t delta = PA_PAGE_SIZE - o;
252 ptr = (uint8_t*) ptr + delta;
253 size -= delta;
254 }
255
256 /* Align the size down to multiples of page size */
257 size = (size / PA_PAGE_SIZE) * PA_PAGE_SIZE;
258
259 #ifdef MADV_REMOVE
260 if (madvise(ptr, size, MADV_REMOVE) >= 0)
261 return;
262 #endif
263
264 #ifdef MADV_FREE
265 if (madvise(ptr, size, MADV_FREE) >= 0)
266 return;
267 #endif
268
269 #ifdef MADV_DONTNEED
270 madvise(ptr, size, MADV_DONTNEED);
271 #elif defined(POSIX_MADV_DONTNEED)
272 posix_madvise(ptr, size, POSIX_MADV_DONTNEED);
273 #endif
274 }
275
276 #ifdef HAVE_SHM_OPEN
277
278 int pa_shm_attach_ro(pa_shm *m, unsigned id) {
279 char fn[32];
280 int fd = -1;
281 struct stat st;
282
283 pa_assert(m);
284
285 segment_name(fn, sizeof(fn), m->id = id);
286
287 if ((fd = shm_open(fn, O_RDONLY, 0)) < 0) {
288 if (errno != EACCES)
289 pa_log("shm_open() failed: %s", pa_cstrerror(errno));
290 goto fail;
291 }
292
293 if (fstat(fd, &st) < 0) {
294 pa_log("fstat() failed: %s", pa_cstrerror(errno));
295 goto fail;
296 }
297
298 if (st.st_size <= 0 ||
299 st.st_size > (off_t) (MAX_SHM_SIZE+SHM_MARKER_SIZE) ||
300 PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) {
301 pa_log("Invalid shared memory segment size");
302 goto fail;
303 }
304
305 m->size = (size_t) st.st_size;
306
307 if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
308 pa_log("mmap() failed: %s", pa_cstrerror(errno));
309 goto fail;
310 }
311
312 m->do_unlink = FALSE;
313 m->shared = TRUE;
314
315 pa_assert_se(pa_close(fd) == 0);
316
317 return 0;
318
319 fail:
320 if (fd >= 0)
321 pa_close(fd);
322
323 return -1;
324 }
325
326 #else /* HAVE_SHM_OPEN */
327
328 int pa_shm_attach_ro(pa_shm *m, unsigned id) {
329 return -1;
330 }
331
332 #endif /* HAVE_SHM_OPEN */
333
334 int pa_shm_cleanup(void) {
335
336 #ifdef HAVE_SHM_OPEN
337 #ifdef SHM_PATH
338 DIR *d;
339 struct dirent *de;
340
341 if (!(d = opendir(SHM_PATH))) {
342 pa_log_warn("Failed to read "SHM_PATH": %s", pa_cstrerror(errno));
343 return -1;
344 }
345
346 while ((de = readdir(d))) {
347 pa_shm seg;
348 unsigned id;
349 pid_t pid;
350 char fn[128];
351 struct shm_marker *m;
352
353 if (strncmp(de->d_name, "pulse-shm-", 10))
354 continue;
355
356 if (pa_atou(de->d_name + 10, &id) < 0)
357 continue;
358
359 if (pa_shm_attach_ro(&seg, id) < 0)
360 continue;
361
362 if (seg.size < SHM_MARKER_SIZE) {
363 pa_shm_free(&seg);
364 continue;
365 }
366
367 m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - SHM_MARKER_SIZE);
368
369 if (pa_atomic_load(&m->marker) != SHM_MARKER) {
370 pa_shm_free(&seg);
371 continue;
372 }
373
374 if (!(pid = (pid_t) pa_atomic_load(&m->pid))) {
375 pa_shm_free(&seg);
376 continue;
377 }
378
379 if (kill(pid, 0) == 0 || errno != ESRCH) {
380 pa_shm_free(&seg);
381 continue;
382 }
383
384 pa_shm_free(&seg);
385
386 /* Ok, the owner of this shms segment is dead, so, let's remove the segment */
387 segment_name(fn, sizeof(fn), id);
388
389 if (shm_unlink(fn) < 0 && errno != EACCES && errno != ENOENT)
390 pa_log_warn("Failed to remove SHM segment %s: %s\n", fn, pa_cstrerror(errno));
391 }
392
393 closedir(d);
394 #endif /* SHM_PATH */
395 #endif /* HAVE_SHM_OPEN */
396
397 return 0;
398 }