merge glitch-free branch back into trunk
[profile/ivi/pulseaudio.git] / src / pulsecore / shm.c
1 /* $Id$ */
2
3 /***
4     This file is part of PulseAudio.
5
6     Copyright 2006 Lennart Poettering
7     Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
8
9     PulseAudio is free software; you can redistribute it and/or modify
10     it under the terms of the GNU Lesser General Public License as
11     published by the Free Software Foundation; either version 2.1 of the
12     License, or (at your option) any later version.
13
14     PulseAudio is distributed in the hope that it will be useful, but
15     WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17     Lesser General Public License for more details.
18
19     You should have received a copy of the GNU Lesser General Public
20     License along with PulseAudio; if not, write to the Free Software
21     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22     USA.
23 ***/
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <stdio.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <dirent.h>
38 #include <signal.h>
39
40 #ifdef HAVE_SYS_MMAN_H
41 #include <sys/mman.h>
42 #endif
43
44 #include <pulse/xmalloc.h>
45 #include <pulse/gccmacro.h>
46
47 #include <pulsecore/core-error.h>
48 #include <pulsecore/log.h>
49 #include <pulsecore/random.h>
50 #include <pulsecore/core-util.h>
51 #include <pulsecore/macro.h>
52 #include <pulsecore/atomic.h>
53
54 #include "shm.h"
55
56 #if defined(__linux__) && !defined(MADV_REMOVE)
57 #define MADV_REMOVE 9
58 #endif
59
60 #define MAX_SHM_SIZE (PA_ALIGN(1024*1024*64))
61
62 #ifdef __linux__
63 /* On Linux we know that the shared memory blocks are files in
64  * /dev/shm. We can use that information to list all blocks and
65  * cleanup unused ones */
66 #define SHM_PATH "/dev/shm/"
67 #else
68 #undef SHM_PATH
69 #endif
70
71 #define SHM_MARKER ((int) 0xbeefcafe)
72
73 /* We now put this SHM marker at the end of each segment. It's
74  * optional, to not require a reboot when upgrading, though */
75 struct shm_marker PA_GCC_PACKED {
76     pa_atomic_t marker; /* 0xbeefcafe */
77     pa_atomic_t pid;
78     uint64_t *_reserverd1;
79     uint64_t *_reserverd2;
80     uint64_t *_reserverd3;
81     uint64_t *_reserverd4;
82 };
83
84 static char *segment_name(char *fn, size_t l, unsigned id) {
85     pa_snprintf(fn, l, "/pulse-shm-%u", id);
86     return fn;
87 }
88
89 int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode) {
90     char fn[32];
91     int fd = -1;
92
93     pa_assert(m);
94     pa_assert(size > 0);
95     pa_assert(size <= MAX_SHM_SIZE);
96     pa_assert(mode >= 0600);
97
98     /* Each time we create a new SHM area, let's first drop all stale
99      * ones */
100     pa_shm_cleanup();
101
102     /* Round up to make it aligned */
103     size = PA_ALIGN(size);
104
105     if (!shared) {
106         m->id = 0;
107         m->size = size;
108
109 #ifdef MAP_ANONYMOUS
110         if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
111             pa_log("mmap() failed: %s", pa_cstrerror(errno));
112             goto fail;
113         }
114 #elif defined(HAVE_POSIX_MEMALIGN)
115         {
116             int r;
117
118             if ((r = posix_memalign(&m->ptr, PA_PAGE_SIZE, size)) < 0) {
119                 pa_log("posix_memalign() failed: %s", pa_cstrerror(r));
120                 goto fail;
121             }
122         }
123 #else
124         m->ptr = pa_xmalloc(m->size);
125 #endif
126
127         m->do_unlink = FALSE;
128
129     } else {
130 #ifdef HAVE_SHM_OPEN
131         struct shm_marker *marker;
132
133         pa_random(&m->id, sizeof(m->id));
134         segment_name(fn, sizeof(fn), m->id);
135
136         if ((fd = shm_open(fn, O_RDWR|O_CREAT|O_EXCL, mode & 0444)) < 0) {
137             pa_log("shm_open() failed: %s", pa_cstrerror(errno));
138             goto fail;
139         }
140
141         m->size = size + PA_ALIGN(sizeof(struct shm_marker));
142
143         if (ftruncate(fd, m->size) < 0) {
144             pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
145             goto fail;
146         }
147
148         if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
149             pa_log("mmap() failed: %s", pa_cstrerror(errno));
150             goto fail;
151         }
152
153         /* We store our PID at the end of the shm block, so that we
154          * can check for dead shm segments later */
155         marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - PA_ALIGN(sizeof(struct shm_marker)));
156         pa_atomic_store(&marker->pid, (int) getpid());
157         pa_atomic_store(&marker->marker, SHM_MARKER);
158
159         pa_assert_se(close(fd) == 0);
160         m->do_unlink = TRUE;
161 #else
162         return -1;
163 #endif
164     }
165
166     m->shared = shared;
167
168     return 0;
169
170 fail:
171
172 #ifdef HAVE_SHM_OPEN
173     if (fd >= 0) {
174         shm_unlink(fn);
175         pa_close(fd);
176     }
177 #endif
178
179     return -1;
180 }
181
182 void pa_shm_free(pa_shm *m) {
183     pa_assert(m);
184     pa_assert(m->ptr);
185     pa_assert(m->size > 0);
186
187 #ifdef MAP_FAILED
188     pa_assert(m->ptr != MAP_FAILED);
189 #endif
190
191     if (!m->shared) {
192 #ifdef MAP_ANONYMOUS
193         if (munmap(m->ptr, m->size) < 0)
194             pa_log("munmap() failed: %s", pa_cstrerror(errno));
195 #elif defined(HAVE_POSIX_MEMALIGN)
196         free(m->ptr);
197 #else
198         pa_xfree(m->ptr);
199 #endif
200     } else {
201 #ifdef HAVE_SHM_OPEN
202         if (munmap(m->ptr, m->size) < 0)
203             pa_log("munmap() failed: %s", pa_cstrerror(errno));
204
205         if (m->do_unlink) {
206             char fn[32];
207
208             segment_name(fn, sizeof(fn), m->id);
209
210             if (shm_unlink(fn) < 0)
211                 pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
212         }
213 #else
214         /* We shouldn't be here without shm support */
215         pa_assert_not_reached();
216 #endif
217     }
218
219     memset(m, 0, sizeof(*m));
220 }
221
222 void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
223     void *ptr;
224     size_t o, ps;
225
226     pa_assert(m);
227     pa_assert(m->ptr);
228     pa_assert(m->size > 0);
229     pa_assert(offset+size <= m->size);
230
231 #ifdef MAP_FAILED
232     pa_assert(m->ptr != MAP_FAILED);
233 #endif
234
235     /* You're welcome to implement this as NOOP on systems that don't
236      * support it */
237
238     /* Align this to multiples of the page size */
239     ptr = (uint8_t*) m->ptr + offset;
240     o = (uint8_t*) ptr - (uint8_t*) PA_PAGE_ALIGN_PTR(ptr);
241
242     if (o > 0) {
243         ps = PA_PAGE_SIZE;
244         ptr = (uint8_t*) ptr + (ps - o);
245         size -= ps - o;
246     }
247
248 #ifdef MADV_REMOVE
249     if (madvise(ptr, size, MADV_REMOVE) >= 0)
250         return;
251 #endif
252
253 #ifdef MADV_FREE
254     if (madvise(ptr, size, MADV_FREE) >= 0)
255         return;
256 #endif
257
258 #ifdef MADV_DONTNEED
259     pa_assert_se(madvise(ptr, size, MADV_DONTNEED) == 0);
260 #elif defined(POSIX_MADV_DONTNEED)
261     pa_assert_se(posix_madvise(ptr, size, POSIX_MADV_DONTNEED) == 0);
262 #endif
263 }
264
265 #ifdef HAVE_SHM_OPEN
266
267 int pa_shm_attach_ro(pa_shm *m, unsigned id) {
268     char fn[32];
269     int fd = -1;
270     struct stat st;
271
272     pa_assert(m);
273
274     segment_name(fn, sizeof(fn), m->id = id);
275
276     if ((fd = shm_open(fn, O_RDONLY, 0)) < 0) {
277         if (errno != EACCES)
278             pa_log("shm_open() failed: %s", pa_cstrerror(errno));
279         goto fail;
280     }
281
282     if (fstat(fd, &st) < 0) {
283         pa_log("fstat() failed: %s", pa_cstrerror(errno));
284         goto fail;
285     }
286
287     if (st.st_size <= 0 ||
288         st.st_size > (off_t) (MAX_SHM_SIZE+PA_ALIGN(sizeof(struct shm_marker))) ||
289         PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) {
290         pa_log("Invalid shared memory segment size");
291         goto fail;
292     }
293
294     m->size = st.st_size;
295
296     if ((m->ptr = mmap(NULL, m->size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
297         pa_log("mmap() failed: %s", pa_cstrerror(errno));
298         goto fail;
299     }
300
301     m->do_unlink = 0;
302     m->shared = 1;
303
304     pa_assert_se(pa_close(fd) == 0);
305
306     return 0;
307
308 fail:
309     if (fd >= 0)
310         pa_close(fd);
311
312     return -1;
313 }
314
315 #else /* HAVE_SHM_OPEN */
316
317 int pa_shm_attach_ro(pa_shm *m, unsigned id) {
318     return -1;
319 }
320
321 #endif /* HAVE_SHM_OPEN */
322
323 int pa_shm_cleanup(void) {
324
325 #ifdef HAVE_SHM_OPEN
326 #ifdef SHM_PATH
327     DIR *d;
328     struct dirent *de;
329
330     if (!(d = opendir(SHM_PATH))) {
331         pa_log_warn("Failed to read "SHM_PATH": %s", pa_cstrerror(errno));
332         return -1;
333     }
334
335     while ((de = readdir(d))) {
336         pa_shm seg;
337         unsigned id;
338         pid_t pid;
339         char fn[128];
340         struct shm_marker *m;
341
342         if (strncmp(de->d_name, "pulse-shm-", 10))
343             continue;
344
345         if (pa_atou(de->d_name + 10, &id) < 0)
346             continue;
347
348         if (pa_shm_attach_ro(&seg, id) < 0)
349             continue;
350
351         if (seg.size < PA_ALIGN(sizeof(struct shm_marker))) {
352             pa_shm_free(&seg);
353             continue;
354         }
355
356         m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - PA_ALIGN(sizeof(struct shm_marker)));
357
358         if (pa_atomic_load(&m->marker) != SHM_MARKER) {
359             pa_shm_free(&seg);
360             continue;
361         }
362
363         if (!(pid = (pid_t) pa_atomic_load(&m->pid))) {
364             pa_shm_free(&seg);
365             continue;
366         }
367
368         if (kill(pid, 0) == 0 || errno != ESRCH) {
369             pa_shm_free(&seg);
370             continue;
371         }
372
373         pa_shm_free(&seg);
374
375         /* Ok, the owner of this shms segment is dead, so, let's remove the segment */
376         segment_name(fn, sizeof(fn), id);
377
378         if (shm_unlink(fn) < 0 && errno != EACCES && errno != ENOENT)
379             pa_log_warn("Failed to remove SHM segment %s: %s\n", fn, pa_cstrerror(errno));
380     }
381
382     closedir(d);
383 #endif /* SHM_PATH */
384 #endif /* HAVE_SHM_OPEN */
385
386     return 0;
387 }