]> code.delx.au - pulseaudio/blob - src/pulsecore/shm.c
444d4010e13a476f30f8839f2927c28e0ce7b4b8
[pulseaudio] / 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 <assert.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <stdio.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <sys/stat.h>
37
38 #ifdef HAVE_SYS_MMAN_H
39 #include <sys/mman.h>
40 #endif
41
42 #include <pulsecore/core-error.h>
43 #include <pulsecore/log.h>
44 #include <pulsecore/random.h>
45 #include <pulse/xmalloc.h>
46
47 #include "shm.h"
48
49 #if defined(__linux__) && !defined(MADV_REMOVE)
50 #define MADV_REMOVE 9
51 #endif
52
53 #define MAX_SHM_SIZE (1024*1024*20)
54
55 static char *segment_name(char *fn, size_t l, unsigned id) {
56 snprintf(fn, l, "/pulse-shm-%u", id);
57 return fn;
58 }
59
60 int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) {
61 char fn[32];
62 int fd = -1;
63
64 assert(m);
65 assert(size > 0);
66 assert(size < MAX_SHM_SIZE);
67 assert(mode >= 0600);
68
69 if (!shared) {
70 m->id = 0;
71 m->size = size;
72
73 #ifdef MAP_ANONYMOUS
74 if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
75 pa_log("mmap() failed: %s", pa_cstrerror(errno));
76 goto fail;
77 }
78 #elif defined(HAVE_POSIX_MEMALIGN)
79 {
80 int r;
81
82 if ((r = posix_memalign(&m->ptr, sysconf(_SC_PAGESIZE), size)) < 0) {
83 pa_log("posix_memalign() failed: %s", pa_cstrerror(r));
84 goto fail;
85 }
86 }
87 #else
88 m->ptr = pa_xmalloc(m->size);
89 #endif
90
91 m->do_unlink = 0;
92
93 } else {
94 #ifdef HAVE_SHM_OPEN
95 pa_random(&m->id, sizeof(m->id));
96 segment_name(fn, sizeof(fn), m->id);
97
98 if ((fd = shm_open(fn, O_RDWR|O_CREAT|O_EXCL, mode & 0444)) < 0) {
99 pa_log("shm_open() failed: %s", pa_cstrerror(errno));
100 goto fail;
101 }
102
103 if (ftruncate(fd, m->size = size) < 0) {
104 pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
105 goto fail;
106 }
107
108 if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
109 pa_log("mmap() failed: %s", pa_cstrerror(errno));
110 goto fail;
111 }
112
113 close(fd);
114 m->do_unlink = 1;
115 #else
116 return -1;
117 #endif
118 }
119
120 m->shared = shared;
121
122 return 0;
123
124 fail:
125
126 #ifdef HAVE_SHM_OPEN
127 if (fd >= 0) {
128 shm_unlink(fn);
129 close(fd);
130 }
131 #endif
132
133 return -1;
134 }
135
136 void pa_shm_free(pa_shm *m) {
137 assert(m);
138 assert(m->ptr);
139 assert(m->size > 0);
140
141 #ifdef MAP_FAILED
142 assert(m->ptr != MAP_FAILED);
143 #endif
144
145 if (!m->shared) {
146 #ifdef MAP_ANONYMOUS
147 if (munmap(m->ptr, m->size) < 0)
148 pa_log("munmap() failed: %s", pa_cstrerror(errno));
149 #elif defined(HAVE_POSIX_MEMALIGN)
150 free(m->ptr);
151 #else
152 pa_xfree(m->ptr);
153 #endif
154 } else {
155 #ifdef HAVE_SHM_OPEN
156 if (munmap(m->ptr, m->size) < 0)
157 pa_log("munmap() failed: %s", pa_cstrerror(errno));
158
159 if (m->do_unlink) {
160 char fn[32];
161
162 segment_name(fn, sizeof(fn), m->id);
163
164 if (shm_unlink(fn) < 0)
165 pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
166 }
167 #else
168 /* We shouldn't be here without shm support */
169 assert(0);
170 #endif
171 }
172
173 memset(m, 0, sizeof(*m));
174 }
175
176 void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
177 void *ptr;
178
179 assert(m);
180 assert(m->ptr);
181 assert(m->size > 0);
182 assert(offset+size <= m->size);
183
184 #ifdef MAP_FAILED
185 assert(m->ptr != MAP_FAILED);
186 #endif
187
188 /* You're welcome to implement this as NOOP on systems that don't
189 * support it */
190
191 ptr = (uint8_t*) m->ptr + offset;
192
193 #ifdef __linux__
194 {
195 /* On Linux ptr must be page aligned */
196 long psz = sysconf(_SC_PAGESIZE);
197 unsigned o;
198
199 o = ((unsigned long) ptr) - ((((unsigned long) ptr)/psz) * psz);
200
201 if (o > 0) {
202 ptr = (uint8_t*) ptr + (psz - o);
203 size -= psz - o;
204 }
205 }
206 #endif
207
208 #ifdef MADV_REMOVE
209 if (madvise(ptr, size, MADV_REMOVE) >= 0)
210 return;
211 #endif
212
213 #ifdef MADV_FREE
214 if (madvise(ptr, size, MADV_FREE) >= 0)
215 return;
216 #endif
217
218 #ifdef MADV_DONTNEED
219 madvise(ptr, size, MADV_DONTNEED);
220 #endif
221 }
222
223 #ifdef HAVE_SHM_OPEN
224
225 int pa_shm_attach_ro(pa_shm *m, unsigned id) {
226 char fn[32];
227 int fd = -1;
228 struct stat st;
229
230 assert(m);
231
232 segment_name(fn, sizeof(fn), m->id = id);
233
234 if ((fd = shm_open(fn, O_RDONLY, 0)) < 0) {
235 pa_log("shm_open() failed: %s", pa_cstrerror(errno));
236 goto fail;
237 }
238
239 if (fstat(fd, &st) < 0) {
240 pa_log("fstat() failed: %s", pa_cstrerror(errno));
241 goto fail;
242 }
243
244 if (st.st_size <= 0 || st.st_size > MAX_SHM_SIZE) {
245 pa_log("Invalid shared memory segment size");
246 goto fail;
247 }
248
249 m->size = st.st_size;
250
251 if ((m->ptr = mmap(NULL, m->size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
252 pa_log("mmap() failed: %s", pa_cstrerror(errno));
253 goto fail;
254 }
255
256 m->do_unlink = 0;
257 m->shared = 1;
258
259 close(fd);
260
261 return 0;
262
263 fail:
264 if (fd >= 0)
265 close(fd);
266
267 return -1;
268 }
269
270 #else /* HAVE_SHM_OPEN */
271
272 int pa_shm_attach_ro(pa_shm *m, unsigned id) {
273 return -1;
274 }
275
276 #endif /* HAVE_SHM_OPEN */