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