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