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