fe556daac8fce929c844039f405cc7a73b619bc9
[platform/upstream/pulseaudio.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 */
73 struct shm_marker {
74     pa_atomic_t marker; /* 0xbeefcafe */
75     pa_atomic_t pid;
76     uint64_t _reserved1;
77     uint64_t _reserved2;
78     uint64_t _reserved3;
79     uint64_t _reserved4;
80 } PA_GCC_PACKED;
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, PA_PAGE_ALIGN(m->size), PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, (off_t) 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, (off_t) m->size) < 0) {
142             pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
143             goto fail;
144         }
145
146         if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, (off_t) 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 = (size_t) ((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 = (size_t) st.st_size;
293
294     if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ, MAP_SHARED, fd, (off_t) 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 }