Merge commit 'origin/master-tx'
[profile/ivi/pulseaudio-panda.git] / 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 }