xref: /aosp_15_r20/external/wayland/src/wayland-shm.c (revision 84e872a0dc482bffdb63672969dd03a827d67c73)
1*84e872a0SLloyd Pique /*
2*84e872a0SLloyd Pique  * Copyright © 2008 Kristian Høgsberg
3*84e872a0SLloyd Pique  *
4*84e872a0SLloyd Pique  * Permission is hereby granted, free of charge, to any person obtaining
5*84e872a0SLloyd Pique  * a copy of this software and associated documentation files (the
6*84e872a0SLloyd Pique  * "Software"), to deal in the Software without restriction, including
7*84e872a0SLloyd Pique  * without limitation the rights to use, copy, modify, merge, publish,
8*84e872a0SLloyd Pique  * distribute, sublicense, and/or sell copies of the Software, and to
9*84e872a0SLloyd Pique  * permit persons to whom the Software is furnished to do so, subject to
10*84e872a0SLloyd Pique  * the following conditions:
11*84e872a0SLloyd Pique  *
12*84e872a0SLloyd Pique  * The above copyright notice and this permission notice (including the
13*84e872a0SLloyd Pique  * next paragraph) shall be included in all copies or substantial
14*84e872a0SLloyd Pique  * portions of the Software.
15*84e872a0SLloyd Pique  *
16*84e872a0SLloyd Pique  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17*84e872a0SLloyd Pique  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18*84e872a0SLloyd Pique  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19*84e872a0SLloyd Pique  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20*84e872a0SLloyd Pique  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21*84e872a0SLloyd Pique  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22*84e872a0SLloyd Pique  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23*84e872a0SLloyd Pique  * SOFTWARE.
24*84e872a0SLloyd Pique  *
25*84e872a0SLloyd Pique  * Authors:
26*84e872a0SLloyd Pique  *    Kristian Høgsberg <[email protected]>
27*84e872a0SLloyd Pique  *    Benjamin Franzke <[email protected]>
28*84e872a0SLloyd Pique  *
29*84e872a0SLloyd Pique  */
30*84e872a0SLloyd Pique 
31*84e872a0SLloyd Pique #define _GNU_SOURCE
32*84e872a0SLloyd Pique 
33*84e872a0SLloyd Pique #include "config.h"
34*84e872a0SLloyd Pique 
35*84e872a0SLloyd Pique #include <stdbool.h>
36*84e872a0SLloyd Pique #include <stdio.h>
37*84e872a0SLloyd Pique #include <stdlib.h>
38*84e872a0SLloyd Pique #include <stdint.h>
39*84e872a0SLloyd Pique #include <string.h>
40*84e872a0SLloyd Pique #include <sys/mman.h>
41*84e872a0SLloyd Pique #include <sys/stat.h>
42*84e872a0SLloyd Pique #include <unistd.h>
43*84e872a0SLloyd Pique #include <assert.h>
44*84e872a0SLloyd Pique #include <signal.h>
45*84e872a0SLloyd Pique #include <pthread.h>
46*84e872a0SLloyd Pique #include <errno.h>
47*84e872a0SLloyd Pique #include <fcntl.h>
48*84e872a0SLloyd Pique 
49*84e872a0SLloyd Pique #include "wayland-os.h"
50*84e872a0SLloyd Pique #include "wayland-util.h"
51*84e872a0SLloyd Pique #include "wayland-private.h"
52*84e872a0SLloyd Pique #include "wayland-server.h"
53*84e872a0SLloyd Pique 
54*84e872a0SLloyd Pique /* This once_t is used to synchronize installing the SIGBUS handler
55*84e872a0SLloyd Pique  * and creating the TLS key. This will be done in the first call
56*84e872a0SLloyd Pique  * wl_shm_buffer_begin_access which can happen from any thread */
57*84e872a0SLloyd Pique static pthread_once_t wl_shm_sigbus_once = PTHREAD_ONCE_INIT;
58*84e872a0SLloyd Pique static pthread_key_t wl_shm_sigbus_data_key;
59*84e872a0SLloyd Pique static struct sigaction wl_shm_old_sigbus_action;
60*84e872a0SLloyd Pique 
61*84e872a0SLloyd Pique struct wl_shm_pool {
62*84e872a0SLloyd Pique 	struct wl_resource *resource;
63*84e872a0SLloyd Pique 	int internal_refcount;
64*84e872a0SLloyd Pique 	int external_refcount;
65*84e872a0SLloyd Pique 	char *data;
66*84e872a0SLloyd Pique 	ssize_t size;
67*84e872a0SLloyd Pique 	ssize_t new_size;
68*84e872a0SLloyd Pique #ifndef MREMAP_MAYMOVE
69*84e872a0SLloyd Pique 	/* The following three fields are needed for mremap() emulation. */
70*84e872a0SLloyd Pique 	int mmap_fd;
71*84e872a0SLloyd Pique 	int mmap_flags;
72*84e872a0SLloyd Pique 	int mmap_prot;
73*84e872a0SLloyd Pique #endif
74*84e872a0SLloyd Pique 	bool sigbus_is_impossible;
75*84e872a0SLloyd Pique };
76*84e872a0SLloyd Pique 
77*84e872a0SLloyd Pique /** \class wl_shm_buffer
78*84e872a0SLloyd Pique  *
79*84e872a0SLloyd Pique  * \brief A SHM buffer
80*84e872a0SLloyd Pique  *
81*84e872a0SLloyd Pique  * wl_shm_buffer provides a helper for accessing the contents of a wl_buffer
82*84e872a0SLloyd Pique  * resource created via the wl_shm interface.
83*84e872a0SLloyd Pique  *
84*84e872a0SLloyd Pique  * A wl_shm_buffer becomes invalid as soon as its #wl_resource is destroyed.
85*84e872a0SLloyd Pique  */
86*84e872a0SLloyd Pique struct wl_shm_buffer {
87*84e872a0SLloyd Pique 	struct wl_resource *resource;
88*84e872a0SLloyd Pique 	int32_t width, height;
89*84e872a0SLloyd Pique 	int32_t stride;
90*84e872a0SLloyd Pique 	uint32_t format;
91*84e872a0SLloyd Pique 	int offset;
92*84e872a0SLloyd Pique 	struct wl_shm_pool *pool;
93*84e872a0SLloyd Pique };
94*84e872a0SLloyd Pique 
95*84e872a0SLloyd Pique struct wl_shm_sigbus_data {
96*84e872a0SLloyd Pique 	struct wl_shm_pool *current_pool;
97*84e872a0SLloyd Pique 	int access_count;
98*84e872a0SLloyd Pique 	int fallback_mapping_used;
99*84e872a0SLloyd Pique };
100*84e872a0SLloyd Pique 
101*84e872a0SLloyd Pique static void *
shm_pool_grow_mapping(struct wl_shm_pool * pool)102*84e872a0SLloyd Pique shm_pool_grow_mapping(struct wl_shm_pool *pool)
103*84e872a0SLloyd Pique {
104*84e872a0SLloyd Pique 	void *data;
105*84e872a0SLloyd Pique 
106*84e872a0SLloyd Pique #ifdef MREMAP_MAYMOVE
107*84e872a0SLloyd Pique 	data = mremap(pool->data, pool->size, pool->new_size, MREMAP_MAYMOVE);
108*84e872a0SLloyd Pique #else
109*84e872a0SLloyd Pique 	data = wl_os_mremap_maymove(pool->mmap_fd, pool->data, &pool->size,
110*84e872a0SLloyd Pique 				    pool->new_size, pool->mmap_prot,
111*84e872a0SLloyd Pique 				    pool->mmap_flags);
112*84e872a0SLloyd Pique 	if (pool->size != 0 && pool->resource != NULL) {
113*84e872a0SLloyd Pique 		wl_resource_post_error(pool->resource,
114*84e872a0SLloyd Pique 				       WL_SHM_ERROR_INVALID_FD,
115*84e872a0SLloyd Pique 				       "leaked old mapping");
116*84e872a0SLloyd Pique 	}
117*84e872a0SLloyd Pique #endif
118*84e872a0SLloyd Pique 	return data;
119*84e872a0SLloyd Pique }
120*84e872a0SLloyd Pique 
121*84e872a0SLloyd Pique static void
shm_pool_finish_resize(struct wl_shm_pool * pool)122*84e872a0SLloyd Pique shm_pool_finish_resize(struct wl_shm_pool *pool)
123*84e872a0SLloyd Pique {
124*84e872a0SLloyd Pique 	void *data;
125*84e872a0SLloyd Pique 
126*84e872a0SLloyd Pique 	if (pool->size == pool->new_size)
127*84e872a0SLloyd Pique 		return;
128*84e872a0SLloyd Pique 
129*84e872a0SLloyd Pique 	data = shm_pool_grow_mapping(pool);
130*84e872a0SLloyd Pique 	if (data == MAP_FAILED) {
131*84e872a0SLloyd Pique 		if (pool->resource != NULL)
132*84e872a0SLloyd Pique 			wl_resource_post_error(pool->resource,
133*84e872a0SLloyd Pique 					       WL_SHM_ERROR_INVALID_FD,
134*84e872a0SLloyd Pique 					       "failed mremap");
135*84e872a0SLloyd Pique 		return;
136*84e872a0SLloyd Pique 	}
137*84e872a0SLloyd Pique 
138*84e872a0SLloyd Pique 	pool->data = data;
139*84e872a0SLloyd Pique 	pool->size = pool->new_size;
140*84e872a0SLloyd Pique }
141*84e872a0SLloyd Pique 
142*84e872a0SLloyd Pique static void
shm_pool_unref(struct wl_shm_pool * pool,bool external)143*84e872a0SLloyd Pique shm_pool_unref(struct wl_shm_pool *pool, bool external)
144*84e872a0SLloyd Pique {
145*84e872a0SLloyd Pique 	if (external) {
146*84e872a0SLloyd Pique 		pool->external_refcount--;
147*84e872a0SLloyd Pique 		assert(pool->external_refcount >= 0);
148*84e872a0SLloyd Pique 		if (pool->external_refcount == 0)
149*84e872a0SLloyd Pique 			shm_pool_finish_resize(pool);
150*84e872a0SLloyd Pique 	} else {
151*84e872a0SLloyd Pique 		pool->internal_refcount--;
152*84e872a0SLloyd Pique 		assert(pool->internal_refcount >= 0);
153*84e872a0SLloyd Pique 	}
154*84e872a0SLloyd Pique 
155*84e872a0SLloyd Pique 	if (pool->internal_refcount + pool->external_refcount > 0)
156*84e872a0SLloyd Pique 		return;
157*84e872a0SLloyd Pique 
158*84e872a0SLloyd Pique 	munmap(pool->data, pool->size);
159*84e872a0SLloyd Pique #ifndef MREMAP_MAYMOVE
160*84e872a0SLloyd Pique 	close(pool->mmap_fd);
161*84e872a0SLloyd Pique #endif
162*84e872a0SLloyd Pique 	free(pool);
163*84e872a0SLloyd Pique }
164*84e872a0SLloyd Pique 
165*84e872a0SLloyd Pique static void
destroy_buffer(struct wl_resource * resource)166*84e872a0SLloyd Pique destroy_buffer(struct wl_resource *resource)
167*84e872a0SLloyd Pique {
168*84e872a0SLloyd Pique 	struct wl_shm_buffer *buffer = wl_resource_get_user_data(resource);
169*84e872a0SLloyd Pique 
170*84e872a0SLloyd Pique 	shm_pool_unref(buffer->pool, false);
171*84e872a0SLloyd Pique 	free(buffer);
172*84e872a0SLloyd Pique }
173*84e872a0SLloyd Pique 
174*84e872a0SLloyd Pique static void
shm_buffer_destroy(struct wl_client * client,struct wl_resource * resource)175*84e872a0SLloyd Pique shm_buffer_destroy(struct wl_client *client, struct wl_resource *resource)
176*84e872a0SLloyd Pique {
177*84e872a0SLloyd Pique 	wl_resource_destroy(resource);
178*84e872a0SLloyd Pique }
179*84e872a0SLloyd Pique 
180*84e872a0SLloyd Pique static const struct wl_buffer_interface shm_buffer_interface = {
181*84e872a0SLloyd Pique 	shm_buffer_destroy
182*84e872a0SLloyd Pique };
183*84e872a0SLloyd Pique 
184*84e872a0SLloyd Pique static bool
format_is_supported(struct wl_client * client,uint32_t format)185*84e872a0SLloyd Pique format_is_supported(struct wl_client *client, uint32_t format)
186*84e872a0SLloyd Pique {
187*84e872a0SLloyd Pique 	struct wl_display *display = wl_client_get_display(client);
188*84e872a0SLloyd Pique 	struct wl_array *formats;
189*84e872a0SLloyd Pique 	uint32_t *p;
190*84e872a0SLloyd Pique 
191*84e872a0SLloyd Pique 	switch (format) {
192*84e872a0SLloyd Pique 	case WL_SHM_FORMAT_ARGB8888:
193*84e872a0SLloyd Pique 	case WL_SHM_FORMAT_XRGB8888:
194*84e872a0SLloyd Pique 		return true;
195*84e872a0SLloyd Pique 	default:
196*84e872a0SLloyd Pique 		formats = wl_display_get_additional_shm_formats(display);
197*84e872a0SLloyd Pique 		wl_array_for_each(p, formats)
198*84e872a0SLloyd Pique 			if (*p == format)
199*84e872a0SLloyd Pique 				return true;
200*84e872a0SLloyd Pique 	}
201*84e872a0SLloyd Pique 
202*84e872a0SLloyd Pique 	return false;
203*84e872a0SLloyd Pique }
204*84e872a0SLloyd Pique 
205*84e872a0SLloyd Pique static void
shm_pool_create_buffer(struct wl_client * client,struct wl_resource * resource,uint32_t id,int32_t offset,int32_t width,int32_t height,int32_t stride,uint32_t format)206*84e872a0SLloyd Pique shm_pool_create_buffer(struct wl_client *client, struct wl_resource *resource,
207*84e872a0SLloyd Pique 		       uint32_t id, int32_t offset,
208*84e872a0SLloyd Pique 		       int32_t width, int32_t height,
209*84e872a0SLloyd Pique 		       int32_t stride, uint32_t format)
210*84e872a0SLloyd Pique {
211*84e872a0SLloyd Pique 	struct wl_shm_pool *pool = wl_resource_get_user_data(resource);
212*84e872a0SLloyd Pique 	struct wl_shm_buffer *buffer;
213*84e872a0SLloyd Pique 
214*84e872a0SLloyd Pique 	if (!format_is_supported(client, format)) {
215*84e872a0SLloyd Pique 		wl_resource_post_error(resource,
216*84e872a0SLloyd Pique 				       WL_SHM_ERROR_INVALID_FORMAT,
217*84e872a0SLloyd Pique 				       "invalid format 0x%x", format);
218*84e872a0SLloyd Pique 		return;
219*84e872a0SLloyd Pique 	}
220*84e872a0SLloyd Pique 
221*84e872a0SLloyd Pique 	if (offset < 0 || width <= 0 || height <= 0 || stride < width ||
222*84e872a0SLloyd Pique 	    INT32_MAX / stride < height ||
223*84e872a0SLloyd Pique 	    offset > pool->size - stride * height) {
224*84e872a0SLloyd Pique 		wl_resource_post_error(resource,
225*84e872a0SLloyd Pique 				       WL_SHM_ERROR_INVALID_STRIDE,
226*84e872a0SLloyd Pique 				       "invalid width, height or stride (%dx%d, %u)",
227*84e872a0SLloyd Pique 				       width, height, stride);
228*84e872a0SLloyd Pique 		return;
229*84e872a0SLloyd Pique 	}
230*84e872a0SLloyd Pique 
231*84e872a0SLloyd Pique 	buffer = zalloc(sizeof *buffer);
232*84e872a0SLloyd Pique 	if (buffer == NULL) {
233*84e872a0SLloyd Pique 		wl_client_post_no_memory(client);
234*84e872a0SLloyd Pique 		return;
235*84e872a0SLloyd Pique 	}
236*84e872a0SLloyd Pique 
237*84e872a0SLloyd Pique 	buffer->width = width;
238*84e872a0SLloyd Pique 	buffer->height = height;
239*84e872a0SLloyd Pique 	buffer->format = format;
240*84e872a0SLloyd Pique 	buffer->stride = stride;
241*84e872a0SLloyd Pique 	buffer->offset = offset;
242*84e872a0SLloyd Pique 	buffer->pool = pool;
243*84e872a0SLloyd Pique 	pool->internal_refcount++;
244*84e872a0SLloyd Pique 
245*84e872a0SLloyd Pique 	buffer->resource =
246*84e872a0SLloyd Pique 		wl_resource_create(client, &wl_buffer_interface, 1, id);
247*84e872a0SLloyd Pique 	if (buffer->resource == NULL) {
248*84e872a0SLloyd Pique 		wl_client_post_no_memory(client);
249*84e872a0SLloyd Pique 		shm_pool_unref(pool, false);
250*84e872a0SLloyd Pique 		free(buffer);
251*84e872a0SLloyd Pique 		return;
252*84e872a0SLloyd Pique 	}
253*84e872a0SLloyd Pique 
254*84e872a0SLloyd Pique 	wl_resource_set_implementation(buffer->resource,
255*84e872a0SLloyd Pique 				       &shm_buffer_interface,
256*84e872a0SLloyd Pique 				       buffer, destroy_buffer);
257*84e872a0SLloyd Pique }
258*84e872a0SLloyd Pique 
259*84e872a0SLloyd Pique static void
destroy_pool(struct wl_resource * resource)260*84e872a0SLloyd Pique destroy_pool(struct wl_resource *resource)
261*84e872a0SLloyd Pique {
262*84e872a0SLloyd Pique 	struct wl_shm_pool *pool = wl_resource_get_user_data(resource);
263*84e872a0SLloyd Pique 
264*84e872a0SLloyd Pique 	pool->resource = NULL;
265*84e872a0SLloyd Pique 	shm_pool_unref(pool, false);
266*84e872a0SLloyd Pique }
267*84e872a0SLloyd Pique 
268*84e872a0SLloyd Pique static void
shm_pool_destroy(struct wl_client * client,struct wl_resource * resource)269*84e872a0SLloyd Pique shm_pool_destroy(struct wl_client *client, struct wl_resource *resource)
270*84e872a0SLloyd Pique {
271*84e872a0SLloyd Pique 	wl_resource_destroy(resource);
272*84e872a0SLloyd Pique }
273*84e872a0SLloyd Pique 
274*84e872a0SLloyd Pique static void
shm_pool_resize(struct wl_client * client,struct wl_resource * resource,int32_t size)275*84e872a0SLloyd Pique shm_pool_resize(struct wl_client *client, struct wl_resource *resource,
276*84e872a0SLloyd Pique 		int32_t size)
277*84e872a0SLloyd Pique {
278*84e872a0SLloyd Pique 	struct wl_shm_pool *pool = wl_resource_get_user_data(resource);
279*84e872a0SLloyd Pique 
280*84e872a0SLloyd Pique 	if (size < pool->size) {
281*84e872a0SLloyd Pique 		wl_resource_post_error(resource,
282*84e872a0SLloyd Pique 				       WL_SHM_ERROR_INVALID_FD,
283*84e872a0SLloyd Pique 				       "shrinking pool invalid");
284*84e872a0SLloyd Pique 		return;
285*84e872a0SLloyd Pique 	}
286*84e872a0SLloyd Pique 
287*84e872a0SLloyd Pique 	pool->new_size = size;
288*84e872a0SLloyd Pique 
289*84e872a0SLloyd Pique 	/* If the compositor has taken references on this pool it
290*84e872a0SLloyd Pique 	 * may be caching pointers into it. In that case we
291*84e872a0SLloyd Pique 	 * defer the resize (which may move the entire mapping)
292*84e872a0SLloyd Pique 	 * until the compositor finishes dereferencing the pool.
293*84e872a0SLloyd Pique 	 */
294*84e872a0SLloyd Pique 	if (pool->external_refcount == 0)
295*84e872a0SLloyd Pique 		shm_pool_finish_resize(pool);
296*84e872a0SLloyd Pique }
297*84e872a0SLloyd Pique 
298*84e872a0SLloyd Pique static const struct wl_shm_pool_interface shm_pool_interface = {
299*84e872a0SLloyd Pique 	shm_pool_create_buffer,
300*84e872a0SLloyd Pique 	shm_pool_destroy,
301*84e872a0SLloyd Pique 	shm_pool_resize
302*84e872a0SLloyd Pique };
303*84e872a0SLloyd Pique 
304*84e872a0SLloyd Pique static void
shm_create_pool(struct wl_client * client,struct wl_resource * resource,uint32_t id,int fd,int32_t size)305*84e872a0SLloyd Pique shm_create_pool(struct wl_client *client, struct wl_resource *resource,
306*84e872a0SLloyd Pique 		uint32_t id, int fd, int32_t size)
307*84e872a0SLloyd Pique {
308*84e872a0SLloyd Pique 	struct wl_shm_pool *pool;
309*84e872a0SLloyd Pique 	struct stat statbuf;
310*84e872a0SLloyd Pique 	int seals;
311*84e872a0SLloyd Pique 	int prot;
312*84e872a0SLloyd Pique 	int flags;
313*84e872a0SLloyd Pique 
314*84e872a0SLloyd Pique 	if (size <= 0) {
315*84e872a0SLloyd Pique 		wl_resource_post_error(resource,
316*84e872a0SLloyd Pique 				       WL_SHM_ERROR_INVALID_STRIDE,
317*84e872a0SLloyd Pique 				       "invalid size (%d)", size);
318*84e872a0SLloyd Pique 		goto err_close;
319*84e872a0SLloyd Pique 	}
320*84e872a0SLloyd Pique 
321*84e872a0SLloyd Pique 	pool = zalloc(sizeof *pool);
322*84e872a0SLloyd Pique 	if (pool == NULL) {
323*84e872a0SLloyd Pique 		wl_client_post_no_memory(client);
324*84e872a0SLloyd Pique 		goto err_close;
325*84e872a0SLloyd Pique 	}
326*84e872a0SLloyd Pique 
327*84e872a0SLloyd Pique #ifdef HAVE_MEMFD_CREATE
328*84e872a0SLloyd Pique 	seals = fcntl(fd, F_GET_SEALS);
329*84e872a0SLloyd Pique 	if (seals == -1)
330*84e872a0SLloyd Pique 		seals = 0;
331*84e872a0SLloyd Pique 
332*84e872a0SLloyd Pique 	if ((seals & F_SEAL_SHRINK) && fstat(fd, &statbuf) >= 0)
333*84e872a0SLloyd Pique 		pool->sigbus_is_impossible = statbuf.st_size >= size;
334*84e872a0SLloyd Pique 	else
335*84e872a0SLloyd Pique 		pool->sigbus_is_impossible = false;
336*84e872a0SLloyd Pique #else
337*84e872a0SLloyd Pique 	pool->sigbus_is_impossible = false;
338*84e872a0SLloyd Pique #endif
339*84e872a0SLloyd Pique 
340*84e872a0SLloyd Pique 	pool->internal_refcount = 1;
341*84e872a0SLloyd Pique 	pool->external_refcount = 0;
342*84e872a0SLloyd Pique 	pool->size = size;
343*84e872a0SLloyd Pique 	pool->new_size = size;
344*84e872a0SLloyd Pique 	prot = PROT_READ | PROT_WRITE;
345*84e872a0SLloyd Pique 	flags = MAP_SHARED;
346*84e872a0SLloyd Pique 	pool->data = mmap(NULL, size, prot, flags, fd, 0);
347*84e872a0SLloyd Pique 	if (pool->data == MAP_FAILED) {
348*84e872a0SLloyd Pique 		wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_FD,
349*84e872a0SLloyd Pique 				       "failed mmap fd %d: %s", fd,
350*84e872a0SLloyd Pique 				       strerror(errno));
351*84e872a0SLloyd Pique 		goto err_free;
352*84e872a0SLloyd Pique 	}
353*84e872a0SLloyd Pique #ifndef MREMAP_MAYMOVE
354*84e872a0SLloyd Pique 	/* We may need to keep the fd, prot and flags to emulate mremap(). */
355*84e872a0SLloyd Pique 	pool->mmap_fd = fd;
356*84e872a0SLloyd Pique 	pool->mmap_prot = prot;
357*84e872a0SLloyd Pique 	pool->mmap_flags = flags;
358*84e872a0SLloyd Pique #else
359*84e872a0SLloyd Pique 	close(fd);
360*84e872a0SLloyd Pique #endif
361*84e872a0SLloyd Pique 	pool->resource =
362*84e872a0SLloyd Pique 		wl_resource_create(client, &wl_shm_pool_interface, 1, id);
363*84e872a0SLloyd Pique 	if (!pool->resource) {
364*84e872a0SLloyd Pique 		wl_client_post_no_memory(client);
365*84e872a0SLloyd Pique 		munmap(pool->data, pool->size);
366*84e872a0SLloyd Pique 		free(pool);
367*84e872a0SLloyd Pique 		return;
368*84e872a0SLloyd Pique 	}
369*84e872a0SLloyd Pique 
370*84e872a0SLloyd Pique 	wl_resource_set_implementation(pool->resource,
371*84e872a0SLloyd Pique 				       &shm_pool_interface,
372*84e872a0SLloyd Pique 				       pool, destroy_pool);
373*84e872a0SLloyd Pique 
374*84e872a0SLloyd Pique 	return;
375*84e872a0SLloyd Pique 
376*84e872a0SLloyd Pique err_free:
377*84e872a0SLloyd Pique 	free(pool);
378*84e872a0SLloyd Pique err_close:
379*84e872a0SLloyd Pique 	close(fd);
380*84e872a0SLloyd Pique }
381*84e872a0SLloyd Pique 
382*84e872a0SLloyd Pique static const struct wl_shm_interface shm_interface = {
383*84e872a0SLloyd Pique 	shm_create_pool
384*84e872a0SLloyd Pique };
385*84e872a0SLloyd Pique 
386*84e872a0SLloyd Pique static void
bind_shm(struct wl_client * client,void * data,uint32_t version,uint32_t id)387*84e872a0SLloyd Pique bind_shm(struct wl_client *client,
388*84e872a0SLloyd Pique 	 void *data, uint32_t version, uint32_t id)
389*84e872a0SLloyd Pique {
390*84e872a0SLloyd Pique 	struct wl_resource *resource;
391*84e872a0SLloyd Pique 	struct wl_display *display = wl_client_get_display(client);
392*84e872a0SLloyd Pique 	struct wl_array *additional_formats;
393*84e872a0SLloyd Pique 	uint32_t *p;
394*84e872a0SLloyd Pique 
395*84e872a0SLloyd Pique 	resource = wl_resource_create(client, &wl_shm_interface, 1, id);
396*84e872a0SLloyd Pique 	if (!resource) {
397*84e872a0SLloyd Pique 		wl_client_post_no_memory(client);
398*84e872a0SLloyd Pique 		return;
399*84e872a0SLloyd Pique 	}
400*84e872a0SLloyd Pique 
401*84e872a0SLloyd Pique 	wl_resource_set_implementation(resource, &shm_interface, data, NULL);
402*84e872a0SLloyd Pique 
403*84e872a0SLloyd Pique 	wl_shm_send_format(resource, WL_SHM_FORMAT_ARGB8888);
404*84e872a0SLloyd Pique 	wl_shm_send_format(resource, WL_SHM_FORMAT_XRGB8888);
405*84e872a0SLloyd Pique 
406*84e872a0SLloyd Pique 	additional_formats = wl_display_get_additional_shm_formats(display);
407*84e872a0SLloyd Pique 	wl_array_for_each(p, additional_formats)
408*84e872a0SLloyd Pique 		wl_shm_send_format(resource, *p);
409*84e872a0SLloyd Pique }
410*84e872a0SLloyd Pique 
411*84e872a0SLloyd Pique WL_EXPORT int
wl_display_init_shm(struct wl_display * display)412*84e872a0SLloyd Pique wl_display_init_shm(struct wl_display *display)
413*84e872a0SLloyd Pique {
414*84e872a0SLloyd Pique 	if (!wl_global_create(display, &wl_shm_interface, 1, NULL, bind_shm))
415*84e872a0SLloyd Pique 		return -1;
416*84e872a0SLloyd Pique 
417*84e872a0SLloyd Pique 	return 0;
418*84e872a0SLloyd Pique }
419*84e872a0SLloyd Pique 
420*84e872a0SLloyd Pique WL_EXPORT struct wl_shm_buffer *
wl_shm_buffer_get(struct wl_resource * resource)421*84e872a0SLloyd Pique wl_shm_buffer_get(struct wl_resource *resource)
422*84e872a0SLloyd Pique {
423*84e872a0SLloyd Pique 	if (resource == NULL)
424*84e872a0SLloyd Pique 		return NULL;
425*84e872a0SLloyd Pique 
426*84e872a0SLloyd Pique 	if (wl_resource_instance_of(resource, &wl_buffer_interface,
427*84e872a0SLloyd Pique 				    &shm_buffer_interface))
428*84e872a0SLloyd Pique 		return wl_resource_get_user_data(resource);
429*84e872a0SLloyd Pique 	else
430*84e872a0SLloyd Pique 		return NULL;
431*84e872a0SLloyd Pique }
432*84e872a0SLloyd Pique 
433*84e872a0SLloyd Pique WL_EXPORT int32_t
wl_shm_buffer_get_stride(struct wl_shm_buffer * buffer)434*84e872a0SLloyd Pique wl_shm_buffer_get_stride(struct wl_shm_buffer *buffer)
435*84e872a0SLloyd Pique {
436*84e872a0SLloyd Pique 	return buffer->stride;
437*84e872a0SLloyd Pique }
438*84e872a0SLloyd Pique 
439*84e872a0SLloyd Pique 
440*84e872a0SLloyd Pique /** Get a pointer to the memory for the SHM buffer
441*84e872a0SLloyd Pique  *
442*84e872a0SLloyd Pique  * \param buffer The buffer object
443*84e872a0SLloyd Pique  *
444*84e872a0SLloyd Pique  * Returns a pointer which can be used to read the data contained in
445*84e872a0SLloyd Pique  * the given SHM buffer.
446*84e872a0SLloyd Pique  *
447*84e872a0SLloyd Pique  * As this buffer is memory-mapped, reading from it may generate
448*84e872a0SLloyd Pique  * SIGBUS signals. This can happen if the client claims that the
449*84e872a0SLloyd Pique  * buffer is larger than it is or if something truncates the
450*84e872a0SLloyd Pique  * underlying file. To prevent this signal from causing the compositor
451*84e872a0SLloyd Pique  * to crash you should call wl_shm_buffer_begin_access and
452*84e872a0SLloyd Pique  * wl_shm_buffer_end_access around code that reads from the memory.
453*84e872a0SLloyd Pique  *
454*84e872a0SLloyd Pique  * \memberof wl_shm_buffer
455*84e872a0SLloyd Pique  */
456*84e872a0SLloyd Pique WL_EXPORT void *
wl_shm_buffer_get_data(struct wl_shm_buffer * buffer)457*84e872a0SLloyd Pique wl_shm_buffer_get_data(struct wl_shm_buffer *buffer)
458*84e872a0SLloyd Pique {
459*84e872a0SLloyd Pique 	if (buffer->pool->external_refcount &&
460*84e872a0SLloyd Pique 	    (buffer->pool->size != buffer->pool->new_size))
461*84e872a0SLloyd Pique 		wl_log("Buffer address requested when its parent pool "
462*84e872a0SLloyd Pique 		       "has an external reference and a deferred resize "
463*84e872a0SLloyd Pique 		       "pending.\n");
464*84e872a0SLloyd Pique 	return buffer->pool->data + buffer->offset;
465*84e872a0SLloyd Pique }
466*84e872a0SLloyd Pique 
467*84e872a0SLloyd Pique WL_EXPORT uint32_t
wl_shm_buffer_get_format(struct wl_shm_buffer * buffer)468*84e872a0SLloyd Pique wl_shm_buffer_get_format(struct wl_shm_buffer *buffer)
469*84e872a0SLloyd Pique {
470*84e872a0SLloyd Pique 	return buffer->format;
471*84e872a0SLloyd Pique }
472*84e872a0SLloyd Pique 
473*84e872a0SLloyd Pique WL_EXPORT int32_t
wl_shm_buffer_get_width(struct wl_shm_buffer * buffer)474*84e872a0SLloyd Pique wl_shm_buffer_get_width(struct wl_shm_buffer *buffer)
475*84e872a0SLloyd Pique {
476*84e872a0SLloyd Pique 	return buffer->width;
477*84e872a0SLloyd Pique }
478*84e872a0SLloyd Pique 
479*84e872a0SLloyd Pique WL_EXPORT int32_t
wl_shm_buffer_get_height(struct wl_shm_buffer * buffer)480*84e872a0SLloyd Pique wl_shm_buffer_get_height(struct wl_shm_buffer *buffer)
481*84e872a0SLloyd Pique {
482*84e872a0SLloyd Pique 	return buffer->height;
483*84e872a0SLloyd Pique }
484*84e872a0SLloyd Pique 
485*84e872a0SLloyd Pique /** Get a reference to a shm_buffer's shm_pool
486*84e872a0SLloyd Pique  *
487*84e872a0SLloyd Pique  * \param buffer The buffer object
488*84e872a0SLloyd Pique  *
489*84e872a0SLloyd Pique  * Returns a pointer to a buffer's shm_pool and increases the
490*84e872a0SLloyd Pique  * shm_pool refcount.
491*84e872a0SLloyd Pique  *
492*84e872a0SLloyd Pique  * The compositor must remember to call wl_shm_pool_unref when
493*84e872a0SLloyd Pique  * it no longer needs the reference to ensure proper destruction
494*84e872a0SLloyd Pique  * of the pool.
495*84e872a0SLloyd Pique  *
496*84e872a0SLloyd Pique  * \memberof wl_shm_buffer
497*84e872a0SLloyd Pique  * \sa wl_shm_pool_unref
498*84e872a0SLloyd Pique  */
499*84e872a0SLloyd Pique WL_EXPORT struct wl_shm_pool *
wl_shm_buffer_ref_pool(struct wl_shm_buffer * buffer)500*84e872a0SLloyd Pique wl_shm_buffer_ref_pool(struct wl_shm_buffer *buffer)
501*84e872a0SLloyd Pique {
502*84e872a0SLloyd Pique 	assert(buffer->pool->internal_refcount +
503*84e872a0SLloyd Pique 	       buffer->pool->external_refcount);
504*84e872a0SLloyd Pique 
505*84e872a0SLloyd Pique 	buffer->pool->external_refcount++;
506*84e872a0SLloyd Pique 	return buffer->pool;
507*84e872a0SLloyd Pique }
508*84e872a0SLloyd Pique 
509*84e872a0SLloyd Pique /** Unreference a shm_pool
510*84e872a0SLloyd Pique  *
511*84e872a0SLloyd Pique  * \param pool The pool object
512*84e872a0SLloyd Pique  *
513*84e872a0SLloyd Pique  * Drops a reference to a wl_shm_pool object.
514*84e872a0SLloyd Pique  *
515*84e872a0SLloyd Pique  * This is only necessary if the compositor has explicitly
516*84e872a0SLloyd Pique  * taken a reference with wl_shm_buffer_ref_pool(), otherwise
517*84e872a0SLloyd Pique  * the pool will be automatically destroyed when appropriate.
518*84e872a0SLloyd Pique  *
519*84e872a0SLloyd Pique  * \memberof wl_shm_pool
520*84e872a0SLloyd Pique  * \sa wl_shm_buffer_ref_pool
521*84e872a0SLloyd Pique  */
522*84e872a0SLloyd Pique WL_EXPORT void
wl_shm_pool_unref(struct wl_shm_pool * pool)523*84e872a0SLloyd Pique wl_shm_pool_unref(struct wl_shm_pool *pool)
524*84e872a0SLloyd Pique {
525*84e872a0SLloyd Pique 	shm_pool_unref(pool, true);
526*84e872a0SLloyd Pique }
527*84e872a0SLloyd Pique 
528*84e872a0SLloyd Pique static void
reraise_sigbus(void)529*84e872a0SLloyd Pique reraise_sigbus(void)
530*84e872a0SLloyd Pique {
531*84e872a0SLloyd Pique 	/* If SIGBUS is raised for some other reason than accessing
532*84e872a0SLloyd Pique 	 * the pool then we'll uninstall the signal handler so we can
533*84e872a0SLloyd Pique 	 * reraise it. This would presumably kill the process */
534*84e872a0SLloyd Pique 	sigaction(SIGBUS, &wl_shm_old_sigbus_action, NULL);
535*84e872a0SLloyd Pique 	raise(SIGBUS);
536*84e872a0SLloyd Pique }
537*84e872a0SLloyd Pique 
538*84e872a0SLloyd Pique static void
sigbus_handler(int signum,siginfo_t * info,void * context)539*84e872a0SLloyd Pique sigbus_handler(int signum, siginfo_t *info, void *context)
540*84e872a0SLloyd Pique {
541*84e872a0SLloyd Pique 	struct wl_shm_sigbus_data *sigbus_data =
542*84e872a0SLloyd Pique 		pthread_getspecific(wl_shm_sigbus_data_key);
543*84e872a0SLloyd Pique 	struct wl_shm_pool *pool;
544*84e872a0SLloyd Pique 
545*84e872a0SLloyd Pique 	if (sigbus_data == NULL) {
546*84e872a0SLloyd Pique 		reraise_sigbus();
547*84e872a0SLloyd Pique 		return;
548*84e872a0SLloyd Pique 	}
549*84e872a0SLloyd Pique 
550*84e872a0SLloyd Pique 	pool = sigbus_data->current_pool;
551*84e872a0SLloyd Pique 
552*84e872a0SLloyd Pique 	/* If the offending address is outside the mapped space for
553*84e872a0SLloyd Pique 	 * the pool then the error is a real problem so we'll reraise
554*84e872a0SLloyd Pique 	 * the signal */
555*84e872a0SLloyd Pique 	if (pool == NULL ||
556*84e872a0SLloyd Pique 	    (char *) info->si_addr < pool->data ||
557*84e872a0SLloyd Pique 	    (char *) info->si_addr >= pool->data + pool->size) {
558*84e872a0SLloyd Pique 		reraise_sigbus();
559*84e872a0SLloyd Pique 		return;
560*84e872a0SLloyd Pique 	}
561*84e872a0SLloyd Pique 
562*84e872a0SLloyd Pique 	sigbus_data->fallback_mapping_used = 1;
563*84e872a0SLloyd Pique 
564*84e872a0SLloyd Pique 	/* This should replace the previous mapping */
565*84e872a0SLloyd Pique 	if (mmap(pool->data, pool->size, PROT_READ | PROT_WRITE,
566*84e872a0SLloyd Pique 		 MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, 0, 0) == MAP_FAILED) {
567*84e872a0SLloyd Pique 		reraise_sigbus();
568*84e872a0SLloyd Pique 		return;
569*84e872a0SLloyd Pique 	}
570*84e872a0SLloyd Pique }
571*84e872a0SLloyd Pique 
572*84e872a0SLloyd Pique static void
destroy_sigbus_data(void * data)573*84e872a0SLloyd Pique destroy_sigbus_data(void *data)
574*84e872a0SLloyd Pique {
575*84e872a0SLloyd Pique 	struct wl_shm_sigbus_data *sigbus_data = data;
576*84e872a0SLloyd Pique 
577*84e872a0SLloyd Pique 	free(sigbus_data);
578*84e872a0SLloyd Pique }
579*84e872a0SLloyd Pique 
580*84e872a0SLloyd Pique static void
init_sigbus_data_key(void)581*84e872a0SLloyd Pique init_sigbus_data_key(void)
582*84e872a0SLloyd Pique {
583*84e872a0SLloyd Pique 	struct sigaction new_action = {
584*84e872a0SLloyd Pique 		.sa_sigaction = sigbus_handler,
585*84e872a0SLloyd Pique 		.sa_flags = SA_SIGINFO | SA_NODEFER
586*84e872a0SLloyd Pique 	};
587*84e872a0SLloyd Pique 
588*84e872a0SLloyd Pique 	sigemptyset(&new_action.sa_mask);
589*84e872a0SLloyd Pique 
590*84e872a0SLloyd Pique 	sigaction(SIGBUS, &new_action, &wl_shm_old_sigbus_action);
591*84e872a0SLloyd Pique 
592*84e872a0SLloyd Pique 	pthread_key_create(&wl_shm_sigbus_data_key, destroy_sigbus_data);
593*84e872a0SLloyd Pique }
594*84e872a0SLloyd Pique 
595*84e872a0SLloyd Pique /** Mark that the given SHM buffer is about to be accessed
596*84e872a0SLloyd Pique  *
597*84e872a0SLloyd Pique  * \param buffer The SHM buffer
598*84e872a0SLloyd Pique  *
599*84e872a0SLloyd Pique  * An SHM buffer is a memory-mapped file given by the client.
600*84e872a0SLloyd Pique  * According to POSIX, reading from a memory-mapped region that
601*84e872a0SLloyd Pique  * extends off the end of the file will cause a SIGBUS signal to be
602*84e872a0SLloyd Pique  * generated. Normally this would cause the compositor to terminate.
603*84e872a0SLloyd Pique  * In order to make the compositor robust against clients that change
604*84e872a0SLloyd Pique  * the size of the underlying file or lie about its size, you should
605*84e872a0SLloyd Pique  * protect access to the buffer by calling this function before
606*84e872a0SLloyd Pique  * reading from the memory and call wl_shm_buffer_end_access
607*84e872a0SLloyd Pique  * afterwards. This will install a signal handler for SIGBUS which
608*84e872a0SLloyd Pique  * will prevent the compositor from crashing.
609*84e872a0SLloyd Pique  *
610*84e872a0SLloyd Pique  * After calling this function the signal handler will remain
611*84e872a0SLloyd Pique  * installed for the lifetime of the compositor process. Note that
612*84e872a0SLloyd Pique  * this function will not work properly if the compositor is also
613*84e872a0SLloyd Pique  * installing its own handler for SIGBUS.
614*84e872a0SLloyd Pique  *
615*84e872a0SLloyd Pique  * If a SIGBUS signal is received for an address within the range of
616*84e872a0SLloyd Pique  * the SHM pool of the given buffer then the client will be sent an
617*84e872a0SLloyd Pique  * error event when wl_shm_buffer_end_access is called. If the signal
618*84e872a0SLloyd Pique  * is for an address outside that range then the signal handler will
619*84e872a0SLloyd Pique  * reraise the signal which would will likely cause the compositor to
620*84e872a0SLloyd Pique  * terminate.
621*84e872a0SLloyd Pique  *
622*84e872a0SLloyd Pique  * It is safe to nest calls to these functions as long as the nested
623*84e872a0SLloyd Pique  * calls are all accessing the same buffer. The number of calls to
624*84e872a0SLloyd Pique  * wl_shm_buffer_end_access must match the number of calls to
625*84e872a0SLloyd Pique  * wl_shm_buffer_begin_access. These functions are thread-safe and it
626*84e872a0SLloyd Pique  * is allowed to simultaneously access different buffers or the same
627*84e872a0SLloyd Pique  * buffer from multiple threads.
628*84e872a0SLloyd Pique  *
629*84e872a0SLloyd Pique  * \memberof wl_shm_buffer
630*84e872a0SLloyd Pique  */
631*84e872a0SLloyd Pique WL_EXPORT void
wl_shm_buffer_begin_access(struct wl_shm_buffer * buffer)632*84e872a0SLloyd Pique wl_shm_buffer_begin_access(struct wl_shm_buffer *buffer)
633*84e872a0SLloyd Pique {
634*84e872a0SLloyd Pique 	struct wl_shm_pool *pool = buffer->pool;
635*84e872a0SLloyd Pique 	struct wl_shm_sigbus_data *sigbus_data;
636*84e872a0SLloyd Pique 
637*84e872a0SLloyd Pique 	if (pool->sigbus_is_impossible)
638*84e872a0SLloyd Pique 		return;
639*84e872a0SLloyd Pique 
640*84e872a0SLloyd Pique 	pthread_once(&wl_shm_sigbus_once, init_sigbus_data_key);
641*84e872a0SLloyd Pique 
642*84e872a0SLloyd Pique 	sigbus_data = pthread_getspecific(wl_shm_sigbus_data_key);
643*84e872a0SLloyd Pique 	if (sigbus_data == NULL) {
644*84e872a0SLloyd Pique 		sigbus_data = zalloc(sizeof *sigbus_data);
645*84e872a0SLloyd Pique 		if (sigbus_data == NULL)
646*84e872a0SLloyd Pique 			return;
647*84e872a0SLloyd Pique 
648*84e872a0SLloyd Pique 		pthread_setspecific(wl_shm_sigbus_data_key, sigbus_data);
649*84e872a0SLloyd Pique 	}
650*84e872a0SLloyd Pique 
651*84e872a0SLloyd Pique 	assert(sigbus_data->current_pool == NULL ||
652*84e872a0SLloyd Pique 	       sigbus_data->current_pool == pool);
653*84e872a0SLloyd Pique 
654*84e872a0SLloyd Pique 	sigbus_data->current_pool = pool;
655*84e872a0SLloyd Pique 	sigbus_data->access_count++;
656*84e872a0SLloyd Pique }
657*84e872a0SLloyd Pique 
658*84e872a0SLloyd Pique /** Ends the access to a buffer started by wl_shm_buffer_begin_access
659*84e872a0SLloyd Pique  *
660*84e872a0SLloyd Pique  * \param buffer The SHM buffer
661*84e872a0SLloyd Pique  *
662*84e872a0SLloyd Pique  * This should be called after wl_shm_buffer_begin_access once the
663*84e872a0SLloyd Pique  * buffer is no longer being accessed. If a SIGBUS signal was
664*84e872a0SLloyd Pique  * generated in-between these two calls then the resource for the
665*84e872a0SLloyd Pique  * given buffer will be sent an error.
666*84e872a0SLloyd Pique  *
667*84e872a0SLloyd Pique  * \memberof wl_shm_buffer
668*84e872a0SLloyd Pique  */
669*84e872a0SLloyd Pique WL_EXPORT void
wl_shm_buffer_end_access(struct wl_shm_buffer * buffer)670*84e872a0SLloyd Pique wl_shm_buffer_end_access(struct wl_shm_buffer *buffer)
671*84e872a0SLloyd Pique {
672*84e872a0SLloyd Pique 	struct wl_shm_pool *pool = buffer->pool;
673*84e872a0SLloyd Pique 	struct wl_shm_sigbus_data *sigbus_data;
674*84e872a0SLloyd Pique 
675*84e872a0SLloyd Pique 	if (pool->sigbus_is_impossible)
676*84e872a0SLloyd Pique 		return;
677*84e872a0SLloyd Pique 
678*84e872a0SLloyd Pique 	sigbus_data = pthread_getspecific(wl_shm_sigbus_data_key);
679*84e872a0SLloyd Pique 	assert(sigbus_data && sigbus_data->access_count >= 1);
680*84e872a0SLloyd Pique 
681*84e872a0SLloyd Pique 	if (--sigbus_data->access_count == 0) {
682*84e872a0SLloyd Pique 		if (sigbus_data->fallback_mapping_used) {
683*84e872a0SLloyd Pique 			wl_resource_post_error(buffer->resource,
684*84e872a0SLloyd Pique 					       WL_SHM_ERROR_INVALID_FD,
685*84e872a0SLloyd Pique 					       "error accessing SHM buffer");
686*84e872a0SLloyd Pique 			sigbus_data->fallback_mapping_used = 0;
687*84e872a0SLloyd Pique 		}
688*84e872a0SLloyd Pique 
689*84e872a0SLloyd Pique 		sigbus_data->current_pool = NULL;
690*84e872a0SLloyd Pique 	}
691*84e872a0SLloyd Pique }
692*84e872a0SLloyd Pique 
693*84e872a0SLloyd Pique /** \cond */ /* Deprecated functions below. */
694*84e872a0SLloyd Pique 
695*84e872a0SLloyd Pique WL_EXPORT struct wl_shm_buffer *
wl_shm_buffer_create(struct wl_client * client,uint32_t id,int32_t width,int32_t height,int32_t stride,uint32_t format)696*84e872a0SLloyd Pique wl_shm_buffer_create(struct wl_client *client,
697*84e872a0SLloyd Pique 		     uint32_t id, int32_t width, int32_t height,
698*84e872a0SLloyd Pique 		     int32_t stride, uint32_t format)
699*84e872a0SLloyd Pique {
700*84e872a0SLloyd Pique 	return NULL;
701*84e872a0SLloyd Pique }
702*84e872a0SLloyd Pique 
703*84e872a0SLloyd Pique /** \endcond */
704*84e872a0SLloyd Pique 
705*84e872a0SLloyd Pique /* Functions at the end of this file are deprecated.  Instead of adding new
706*84e872a0SLloyd Pique  * code here, add it before the comment above that states:
707*84e872a0SLloyd Pique  * Deprecated functions below.
708*84e872a0SLloyd Pique  */
709