• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2016 The Chromium OS Authors. All rights reserved.
2  * Use of this source code is governed by a BSD-style license that can be
3  * found in the LICENSE file.
4  */
5 
6 #include <sys/cdefs.h>
7 #include <sys/mman.h>
8 #ifdef __BIONIC__
9 #include <cutils/ashmem.h>
10 #else
11 #include <sys/shm.h>
12 #endif
13 #include <errno.h>
14 #include <syslog.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <unistd.h>
18 
19 #include "cras_shm.h"
20 
cras_shm_info_init(const char * stream_name,uint32_t length,struct cras_shm_info * info_out)21 int cras_shm_info_init(const char *stream_name, uint32_t length,
22 		       struct cras_shm_info *info_out)
23 {
24 	struct cras_shm_info info;
25 
26 	if (!info_out)
27 		return -EINVAL;
28 
29 	strncpy(info.name, stream_name, sizeof(info.name) - 1);
30 	info.name[sizeof(info.name) - 1] = '\0';
31 	info.length = length;
32 	info.fd = cras_shm_open_rw(info.name, info.length);
33 	if (info.fd < 0)
34 		return info.fd;
35 
36 	*info_out = info;
37 
38 	return 0;
39 }
40 
cras_shm_info_init_with_fd(int fd,size_t length,struct cras_shm_info * info_out)41 int cras_shm_info_init_with_fd(int fd, size_t length,
42 			       struct cras_shm_info *info_out)
43 {
44 	struct cras_shm_info info;
45 
46 	if (!info_out)
47 		return -EINVAL;
48 
49 	info.name[0] = '\0';
50 	info.length = length;
51 	info.fd = dup(fd);
52 	if (info.fd < 0)
53 		return info.fd;
54 
55 	*info_out = info;
56 
57 	return 0;
58 }
59 
60 /* Move the resources from the cras_shm_info 'from' into the cras_shm_info 'to'.
61  * The owner of 'to' will be responsible for cleaning up those resources with
62  * cras_shm_info_cleanup.
63  */
cras_shm_info_move(struct cras_shm_info * from,struct cras_shm_info * to)64 static int cras_shm_info_move(struct cras_shm_info *from,
65 			      struct cras_shm_info *to)
66 {
67 	if (!from || !to)
68 		return -EINVAL;
69 
70 	*to = *from;
71 	from->fd = -1;
72 	from->name[0] = '\0';
73 	return 0;
74 }
75 
cras_shm_info_cleanup(struct cras_shm_info * info)76 void cras_shm_info_cleanup(struct cras_shm_info *info)
77 {
78 	if (!info)
79 		return;
80 
81 	if (info->name[0] != '\0')
82 		cras_shm_close_unlink(info->name, info->fd);
83 	else
84 		close(info->fd);
85 
86 	info->fd = -1;
87 	info->name[0] = '\0';
88 }
89 
cras_audio_shm_create(struct cras_shm_info * header_info,struct cras_shm_info * samples_info,int samples_prot,struct cras_audio_shm ** shm_out)90 int cras_audio_shm_create(struct cras_shm_info *header_info,
91 			  struct cras_shm_info *samples_info, int samples_prot,
92 			  struct cras_audio_shm **shm_out)
93 {
94 	struct cras_audio_shm *shm;
95 	int ret;
96 
97 	if (!header_info || !samples_info || !shm_out) {
98 		ret = -EINVAL;
99 		goto cleanup_info;
100 	}
101 
102 	if (samples_prot != PROT_READ && samples_prot != PROT_WRITE) {
103 		ret = -EINVAL;
104 		syslog(LOG_ERR,
105 		       "cras_shm: samples must be mapped read or write only");
106 		goto cleanup_info;
107 	}
108 
109 	shm = calloc(1, sizeof(*shm));
110 	if (!shm) {
111 		ret = -ENOMEM;
112 		goto cleanup_info;
113 	}
114 
115 	/* Move the cras_shm_info params into the new cras_audio_shm object.
116 	 * The parameters are cleared, and the owner of cras_audio_shm is now
117 	 * responsible for closing the fds and unlinking any associated shm
118 	 * files using cras_audio_shm_destroy.
119 	 *
120 	 * The source pointers are updated to point to the moved structs so that
121 	 * they will be properly cleaned up in the error case.
122 	 */
123 	ret = cras_shm_info_move(header_info, &shm->header_info);
124 	if (ret)
125 		goto free_shm;
126 	header_info = &shm->header_info;
127 
128 	ret = cras_shm_info_move(samples_info, &shm->samples_info);
129 	if (ret)
130 		goto free_shm;
131 	samples_info = &shm->samples_info;
132 
133 	shm->header = mmap(NULL, header_info->length, PROT_READ | PROT_WRITE,
134 			   MAP_SHARED, header_info->fd, 0);
135 	if (shm->header == (struct cras_audio_shm_header *)-1) {
136 		ret = errno;
137 		syslog(LOG_ERR, "cras_shm: mmap failed to map shm for header.");
138 		goto free_shm;
139 	}
140 
141 	shm->samples = mmap(NULL, samples_info->length, samples_prot,
142 			    MAP_SHARED, samples_info->fd, 0);
143 	if (shm->samples == (uint8_t *)-1) {
144 		ret = errno;
145 		syslog(LOG_ERR,
146 		       "cras_shm: mmap failed to map shm for samples.");
147 		goto unmap_header;
148 	}
149 
150 	cras_shm_set_volume_scaler(shm, 1.0);
151 
152 	*shm_out = shm;
153 	return 0;
154 
155 unmap_header:
156 	munmap(shm->header, shm->header_info.length);
157 free_shm:
158 	free(shm);
159 cleanup_info:
160 	cras_shm_info_cleanup(samples_info);
161 	cras_shm_info_cleanup(header_info);
162 	return ret;
163 }
164 
cras_audio_shm_destroy(struct cras_audio_shm * shm)165 void cras_audio_shm_destroy(struct cras_audio_shm *shm)
166 {
167 	if (!shm)
168 		return;
169 
170 	munmap(shm->samples, shm->samples_info.length);
171 	cras_shm_info_cleanup(&shm->samples_info);
172 	munmap(shm->header, shm->header_info.length);
173 	cras_shm_info_cleanup(&shm->header_info);
174 	free(shm);
175 }
176 
177 /* Set the correct SELinux label for SHM fds. */
cras_shm_restorecon(int fd)178 static void cras_shm_restorecon(int fd)
179 {
180 #ifdef CRAS_SELINUX
181 	char fd_proc_path[64];
182 
183 	if (snprintf(fd_proc_path, sizeof(fd_proc_path), "/proc/self/fd/%d",
184 		     fd) < 0) {
185 		syslog(LOG_WARNING,
186 		       "Couldn't construct proc symlink path of fd: %d", fd);
187 		return;
188 	}
189 
190 	/* Get the actual file-path for this fd. */
191 	char *path = realpath(fd_proc_path, NULL);
192 	if (path == NULL) {
193 		syslog(LOG_WARNING, "Couldn't run realpath() for %s: %s",
194 		       fd_proc_path, strerror(errno));
195 		return;
196 	}
197 
198 	if (cras_selinux_restorecon(path) < 0) {
199 		syslog(LOG_WARNING, "Restorecon on %s failed: %s", fd_proc_path,
200 		       strerror(errno));
201 	}
202 
203 	free(path);
204 #endif
205 }
206 
207 #ifdef __BIONIC__
208 
cras_shm_open_rw(const char * name,size_t size)209 int cras_shm_open_rw(const char *name, size_t size)
210 {
211 	int fd;
212 
213 	/* Eliminate the / in the shm_name. */
214 	if (name[0] == '/')
215 		name++;
216 	fd = ashmem_create_region(name, size);
217 	if (fd < 0) {
218 		fd = -errno;
219 		syslog(LOG_ERR, "failed to ashmem_create_region %s: %s\n", name,
220 		       strerror(-fd));
221 	}
222 	return fd;
223 }
224 
cras_shm_reopen_ro(const char * name,int fd)225 int cras_shm_reopen_ro(const char *name, int fd)
226 {
227 	/* After mmaping the ashmem read/write, change it's protection
228 	   bits to disallow further write access. */
229 	if (ashmem_set_prot_region(fd, PROT_READ) != 0) {
230 		fd = -errno;
231 		syslog(LOG_ERR, "failed to ashmem_set_prot_region %s: %s\n",
232 		       name, strerror(-fd));
233 	}
234 	return fd;
235 }
236 
cras_shm_close_unlink(const char * name,int fd)237 void cras_shm_close_unlink(const char *name, int fd)
238 {
239 	close(fd);
240 }
241 
242 #else
243 
cras_shm_open_rw(const char * name,size_t size)244 int cras_shm_open_rw(const char *name, size_t size)
245 {
246 	int fd;
247 	int rc;
248 
249 	fd = shm_open(name, O_CREAT | O_EXCL | O_RDWR, 0600);
250 	if (fd < 0) {
251 		fd = -errno;
252 		syslog(LOG_ERR, "failed to shm_open %s: %s\n", name,
253 		       strerror(-fd));
254 		return fd;
255 	}
256 	rc = ftruncate(fd, size);
257 	if (rc) {
258 		rc = -errno;
259 		syslog(LOG_ERR, "failed to set size of shm %s: %s\n", name,
260 		       strerror(-rc));
261 		return rc;
262 	}
263 
264 	cras_shm_restorecon(fd);
265 
266 	return fd;
267 }
268 
cras_shm_reopen_ro(const char * name,int fd)269 int cras_shm_reopen_ro(const char *name, int fd)
270 {
271 	/* Open a read-only copy to dup and pass to clients. */
272 	fd = shm_open(name, O_RDONLY, 0);
273 	if (fd < 0) {
274 		fd = -errno;
275 		syslog(LOG_ERR,
276 		       "Failed to re-open shared memory '%s' read-only: %s",
277 		       name, strerror(-fd));
278 	}
279 	return fd;
280 }
281 
cras_shm_close_unlink(const char * name,int fd)282 void cras_shm_close_unlink(const char *name, int fd)
283 {
284 	shm_unlink(name);
285 	close(fd);
286 }
287 
288 #endif
289 
cras_shm_setup(const char * name,size_t mmap_size,int * rw_fd_out,int * ro_fd_out)290 void *cras_shm_setup(const char *name, size_t mmap_size, int *rw_fd_out,
291 		     int *ro_fd_out)
292 {
293 	int rw_shm_fd = cras_shm_open_rw(name, mmap_size);
294 	if (rw_shm_fd < 0)
295 		return NULL;
296 
297 	/* mmap shm. */
298 	void *exp_state = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,
299 			       MAP_SHARED, rw_shm_fd, 0);
300 	if (exp_state == (void *)-1)
301 		return NULL;
302 
303 	/* Open a read-only copy to dup and pass to clients. */
304 	int ro_shm_fd = cras_shm_reopen_ro(name, rw_shm_fd);
305 	if (ro_shm_fd < 0)
306 		return NULL;
307 
308 	*rw_fd_out = rw_shm_fd;
309 	*ro_fd_out = ro_shm_fd;
310 
311 	return exp_state;
312 }
313