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