1 /* PipeWire
2  *
3  * Copyright © 2018 Wim Taymans
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include <assert.h>
26 
27 #include <pipewire/log.h>
28 #include <pipewire/proxy.h>
29 #include <pipewire/core.h>
30 #include <pipewire/private.h>
31 #include <pipewire/type.h>
32 
33 #include <spa/debug/types.h>
34 
35 PW_LOG_TOPIC_EXTERN(log_proxy);
36 #define PW_LOG_TOPIC_DEFAULT log_proxy
37 
38 /** \cond */
39 struct proxy {
40 	struct pw_proxy this;
41 };
42 /** \endcond */
43 
pw_proxy_init(struct pw_proxy * proxy,const char * type,uint32_t version)44 int pw_proxy_init(struct pw_proxy *proxy, const char *type, uint32_t version)
45 {
46 	int res;
47 
48 	proxy->refcount = 1;
49 	proxy->type = type;
50 	proxy->version = version;
51 	proxy->bound_id = SPA_ID_INVALID;
52 
53 	proxy->id = pw_map_insert_new(&proxy->core->objects, proxy);
54 	if (proxy->id == SPA_ID_INVALID) {
55 		res = -errno;
56 		pw_log_error("%p: can't allocate new id: %m", proxy);
57 		goto error;
58 	}
59 
60 	spa_hook_list_init(&proxy->listener_list);
61 	spa_hook_list_init(&proxy->object_listener_list);
62 
63 	if ((res = pw_proxy_install_marshal(proxy, false)) < 0) {
64 		pw_log_error("%p: no marshal for type %s/%d: %s", proxy,
65 				type, version, spa_strerror(res));
66 		goto error_clean;
67 	}
68 	proxy->in_map = true;
69 	return 0;
70 
71 error_clean:
72 	pw_map_remove(&proxy->core->objects, proxy->id);
73 error:
74 	return res;
75 }
76 
77 /** Create a proxy object with a given id and type
78  *
79  * \param factory another proxy object that serves as a factory
80  * \param type Type of the proxy object
81  * \param version Interface version
82  * \param user_data_size size of user_data
83  * \return A newly allocated proxy object or NULL on failure
84  *
85  * This function creates a new proxy object with the supplied id and type. The
86  * proxy object will have an id assigned from the client id space.
87  *
88  * \sa pw_core
89  */
90 SPA_EXPORT
pw_proxy_new(struct pw_proxy * factory,const char * type,uint32_t version,size_t user_data_size)91 struct pw_proxy *pw_proxy_new(struct pw_proxy *factory,
92 			      const char *type, uint32_t version,
93 			      size_t user_data_size)
94 {
95 	struct proxy *impl;
96 	struct pw_proxy *this;
97 	int res;
98 
99 	impl = calloc(1, sizeof(struct proxy) + user_data_size);
100 	if (impl == NULL)
101 		return NULL;
102 
103 	this = &impl->this;
104 	this->core = factory->core;
105 
106 	if ((res = pw_proxy_init(this, type, version)) < 0)
107 		goto error_init;
108 
109 	if (user_data_size > 0)
110 		this->user_data = SPA_PTROFF(impl, sizeof(struct proxy), void);
111 
112 	pw_log_debug("%p: new %u type %s/%d core-proxy:%p, marshal:%p",
113 			this, this->id, type, version, this->core, this->marshal);
114 	return this;
115 
116 error_init:
117 	free(impl);
118 	errno = -res;
119 	return NULL;
120 }
121 
122 SPA_EXPORT
pw_proxy_install_marshal(struct pw_proxy * this,bool implementor)123 int pw_proxy_install_marshal(struct pw_proxy *this, bool implementor)
124 {
125 	struct pw_core *core = this->core;
126 	const struct pw_protocol_marshal *marshal;
127 
128 	if (core == NULL)
129 		return -EIO;
130 
131 	marshal = pw_protocol_get_marshal(core->conn->protocol,
132 			this->type, this->version,
133 			implementor ? PW_PROTOCOL_MARSHAL_FLAG_IMPL : 0);
134 	if (marshal == NULL)
135 		return -EPROTO;
136 
137 	this->marshal = marshal;
138 	this->type = marshal->type;
139 
140 	this->impl = SPA_INTERFACE_INIT(
141 			this->type,
142 			this->marshal->version,
143 			this->marshal->client_marshal, this);
144 	return 0;
145 }
146 
147 SPA_EXPORT
pw_proxy_get_user_data(struct pw_proxy * proxy)148 void *pw_proxy_get_user_data(struct pw_proxy *proxy)
149 {
150 	return proxy->user_data;
151 }
152 
153 SPA_EXPORT
pw_proxy_get_id(struct pw_proxy * proxy)154 uint32_t pw_proxy_get_id(struct pw_proxy *proxy)
155 {
156 	return proxy->id;
157 }
158 
159 SPA_EXPORT
pw_proxy_set_bound_id(struct pw_proxy * proxy,uint32_t global_id)160 int pw_proxy_set_bound_id(struct pw_proxy *proxy, uint32_t global_id)
161 {
162 	proxy->bound_id = global_id;
163 	pw_log_debug("%p: id:%d bound:%d", proxy, proxy->id, global_id);
164 	pw_proxy_emit_bound(proxy, global_id);
165 	return 0;
166 }
167 
168 SPA_EXPORT
pw_proxy_get_bound_id(struct pw_proxy * proxy)169 uint32_t pw_proxy_get_bound_id(struct pw_proxy *proxy)
170 {
171 	return proxy->bound_id;
172 }
173 
174 SPA_EXPORT
pw_proxy_get_type(struct pw_proxy * proxy,uint32_t * version)175 const char *pw_proxy_get_type(struct pw_proxy *proxy, uint32_t *version)
176 {
177 	if (version)
178 		*version = proxy->version;
179 	return proxy->type;
180 }
181 
182 SPA_EXPORT
pw_proxy_get_core(struct pw_proxy * proxy)183 struct pw_core *pw_proxy_get_core(struct pw_proxy *proxy)
184 {
185 	return proxy->core;
186 }
187 
188 SPA_EXPORT
pw_proxy_get_protocol(struct pw_proxy * proxy)189 struct pw_protocol *pw_proxy_get_protocol(struct pw_proxy *proxy)
190 {
191 	if (proxy->core == NULL || proxy->core->conn == NULL)
192 		return NULL;
193 	return proxy->core->conn->protocol;
194 }
195 
196 SPA_EXPORT
pw_proxy_add_listener(struct pw_proxy * proxy,struct spa_hook * listener,const struct pw_proxy_events * events,void * data)197 void pw_proxy_add_listener(struct pw_proxy *proxy,
198 			   struct spa_hook *listener,
199 			   const struct pw_proxy_events *events,
200 			   void *data)
201 {
202 	spa_hook_list_append(&proxy->listener_list, listener, events, data);
203 }
204 
205 SPA_EXPORT
pw_proxy_add_object_listener(struct pw_proxy * proxy,struct spa_hook * listener,const void * funcs,void * data)206 void pw_proxy_add_object_listener(struct pw_proxy *proxy,
207 				 struct spa_hook *listener,
208 				 const void *funcs,
209 				 void *data)
210 {
211 	spa_hook_list_append(&proxy->object_listener_list, listener, funcs, data);
212 }
213 
remove_from_map(struct pw_proxy * proxy)214 static inline void remove_from_map(struct pw_proxy *proxy)
215 {
216 	if (proxy->in_map) {
217 		if (proxy->core)
218 			pw_map_remove(&proxy->core->objects, proxy->id);
219 		proxy->in_map = false;
220 	}
221 }
222 
223 /** Destroy a proxy object
224  *
225  * \param proxy Proxy object to destroy
226  *
227  * \note This is normally called by \ref pw_core when the server
228  *       decides to destroy the server side object
229  */
230 SPA_EXPORT
pw_proxy_destroy(struct pw_proxy * proxy)231 void pw_proxy_destroy(struct pw_proxy *proxy)
232 {
233 	pw_log_debug("%p: destroy id:%u removed:%u zombie:%u ref:%d", proxy,
234 			proxy->id, proxy->removed, proxy->zombie, proxy->refcount);
235 
236 	assert(!proxy->destroyed);
237 	proxy->destroyed = true;
238 
239 	if (!proxy->removed) {
240 		/* if the server did not remove this proxy, schedule a
241 		 * destroy if we can */
242 		if (proxy->core && !proxy->core->removed) {
243 			pw_core_destroy(proxy->core, proxy);
244 			proxy->refcount++;
245 		} else {
246 			proxy->removed = true;
247 		}
248 	}
249 	if (proxy->removed)
250 		remove_from_map(proxy);
251 
252 	if (!proxy->zombie) {
253 		/* mark zombie and emit destroyed. No more
254 		 * events will be emitted on zombie objects */
255 		proxy->zombie = true;
256 		pw_proxy_emit_destroy(proxy);
257 	}
258 
259 	spa_hook_list_clean(&proxy->listener_list);
260 	spa_hook_list_clean(&proxy->object_listener_list);
261 
262 	pw_proxy_unref(proxy);
263 }
264 
265 /** called when cleaning up or when the server removed the resource. Can
266  * be called multiple times */
pw_proxy_remove(struct pw_proxy * proxy)267 void pw_proxy_remove(struct pw_proxy *proxy)
268 {
269 	assert(proxy->refcount > 0);
270 
271 	pw_log_debug("%p: remove id:%u removed:%u destroyed:%u zombie:%u ref:%d", proxy,
272 			proxy->id, proxy->removed, proxy->destroyed, proxy->zombie,
273 			proxy->refcount);
274 
275 	if (!proxy->destroyed)
276 		proxy->refcount++;
277 
278 	if (!proxy->removed) {
279 		/* mark removed and emit the removed signal only once and
280 		 * only when not already destroyed */
281 		proxy->removed = true;
282 		if (!proxy->destroyed)
283 			pw_proxy_emit_removed(proxy);
284 	}
285 	if (proxy->destroyed)
286 		remove_from_map(proxy);
287 
288 	pw_proxy_unref(proxy);
289 }
290 
291 SPA_EXPORT
pw_proxy_unref(struct pw_proxy * proxy)292 void pw_proxy_unref(struct pw_proxy *proxy)
293 {
294 	assert(proxy->refcount > 0);
295 	if (--proxy->refcount > 0)
296 		return;
297 
298 	pw_log_debug("%p: free %u", proxy, proxy->id);
299 	/** client must explicitly destroy all proxies */
300 	assert(proxy->destroyed);
301 	free(proxy);
302 }
303 
304 SPA_EXPORT
pw_proxy_ref(struct pw_proxy * proxy)305 void pw_proxy_ref(struct pw_proxy *proxy)
306 {
307 	assert(proxy->refcount > 0);
308 	proxy->refcount++;
309 }
310 
311 SPA_EXPORT
pw_proxy_sync(struct pw_proxy * proxy,int seq)312 int pw_proxy_sync(struct pw_proxy *proxy, int seq)
313 {
314 	int res = -EIO;
315 	struct pw_core *core = proxy->core;
316 
317 	if (core && !core->removed) {
318 		res = pw_core_sync(core, proxy->id, seq);
319 		pw_log_debug("%p: %u seq:%d sync %u", proxy, proxy->id, seq, res);
320 	}
321 	return res;
322 }
323 
324 SPA_EXPORT
pw_proxy_errorf(struct pw_proxy * proxy,int res,const char * error,...)325 int pw_proxy_errorf(struct pw_proxy *proxy, int res, const char *error, ...)
326 {
327 	va_list ap;
328 	int r = -EIO;
329 	struct pw_core *core = proxy->core;
330 
331 	va_start(ap, error);
332 	if (core && !core->removed)
333 		r = pw_core_errorv(core, proxy->id,
334 				core->recv_seq, res, error, ap);
335 	va_end(ap);
336 	return r;
337 }
338 
339 SPA_EXPORT
pw_proxy_error(struct pw_proxy * proxy,int res,const char * error)340 int pw_proxy_error(struct pw_proxy *proxy, int res, const char *error)
341 {
342 	int r = -EIO;
343 	struct pw_core *core = proxy->core;
344 
345 	if (core && !core->removed)
346 		r = pw_core_error(core, proxy->id,
347 				core->recv_seq, res, error);
348 	return r;
349 }
350 
351 SPA_EXPORT
pw_proxy_get_object_listeners(struct pw_proxy * proxy)352 struct spa_hook_list *pw_proxy_get_object_listeners(struct pw_proxy *proxy)
353 {
354 	return &proxy->object_listener_list;
355 }
356 
357 SPA_EXPORT
pw_proxy_get_marshal(struct pw_proxy * proxy)358 const struct pw_protocol_marshal *pw_proxy_get_marshal(struct pw_proxy *proxy)
359 {
360 	return proxy->marshal;
361 }
362