• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the 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 THE
19  * 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 DEALINGS
22  * IN THE SOFTWARE.
23  *
24  * This role for wrapping dbus fds in a wsi + role is unusual in that the
25  * wsi it creates and binds to the role do not have control over the related fd
26  * lifecycle.  In fact dbus doesn't inform us directly about the lifecycle of
27  * the fds it wants to be managed by the lws event loop.
28  *
29  * What it does tell us is when it wants to wait on POLLOUT and / or POLLIN,
30  * and since it should stop any watchers before close, we take the approach to
31  * create a lightweight "shadow" wsi for any fd from dbus that has a POLLIN or
32  * POLLOUT wait active.  When the dbus fd asks to have no wait active, we
33  * destroy the wsi, since this is indistinguishable from dbus close path
34  * behaviour.  If it actually stays alive and later asks to wait again, well no
35  * worries we create a new shadow wsi until it looks like it is closing again.
36  */
37 
38 #include <private-lib-core.h>
39 
40 #include <libwebsockets/lws-dbus.h>
41 
42 /*
43  * retreives existing or creates new shadow wsi for fd owned by dbus stuff.
44  *
45  * Requires vhost lock
46  */
47 
48 static struct lws *
__lws_shadow_wsi(struct lws_dbus_ctx * ctx,DBusWatch * w,int fd,int create_ok)49 __lws_shadow_wsi(struct lws_dbus_ctx *ctx, DBusWatch *w, int fd, int create_ok)
50 {
51 	struct lws *wsi;
52 
53 	if (fd < 0 || fd >= (int)ctx->vh->context->fd_limit_per_thread) {
54 		lwsl_err("%s: fd %d vs fds_count %d\n", __func__, fd,
55 				(int)ctx->vh->context->fd_limit_per_thread);
56 		assert(0);
57 
58 		return NULL;
59 	}
60 
61 	wsi = wsi_from_fd(ctx->vh->context, fd);
62 	if (wsi) {
63 		assert(wsi->opaque_parent_data == ctx);
64 
65 		return wsi;
66 	}
67 
68 	if (!create_ok)
69 		return NULL;
70 
71 	wsi = lws_zalloc(sizeof(*wsi), "shadow wsi");
72 	if (wsi == NULL) {
73 		lwsl_err("Out of mem\n");
74 		return NULL;
75 	}
76 
77 	lwsl_info("%s: creating shadow wsi\n", __func__);
78 
79 	wsi->context = ctx->vh->context;
80 	wsi->desc.sockfd = fd;
81 	lws_role_transition(wsi, 0, LRS_ESTABLISHED, &role_ops_dbus);
82 	wsi->protocol = ctx->vh->protocols;
83 	wsi->tsi = ctx->tsi;
84 	wsi->shadow = 1;
85 	wsi->opaque_parent_data = ctx;
86 	ctx->w[0] = w;
87 
88 	lws_vhost_bind_wsi(ctx->vh, wsi);
89 	if (__insert_wsi_socket_into_fds(ctx->vh->context, wsi)) {
90 		lwsl_err("inserting wsi socket into fds failed\n");
91 		lws_vhost_unbind_wsi(wsi);
92 		lws_free(wsi);
93 		return NULL;
94 	}
95 
96 	ctx->vh->context->count_wsi_allocated++;
97 
98 	return wsi;
99 }
100 
101 /*
102  * Requires vhost lock
103  */
104 
105 static int
__lws_shadow_wsi_destroy(struct lws_dbus_ctx * ctx,struct lws * wsi)106 __lws_shadow_wsi_destroy(struct lws_dbus_ctx *ctx, struct lws *wsi)
107 {
108 	lwsl_info("%s: destroying shadow wsi\n", __func__);
109 
110 	if (__remove_wsi_socket_from_fds(wsi)) {
111 		lwsl_err("%s: unable to remove %d from fds\n", __func__,
112 				wsi->desc.sockfd);
113 
114 		return 1;
115 	}
116 
117 	ctx->vh->context->count_wsi_allocated--;
118 	lws_vhost_unbind_wsi(wsi);
119 
120 	lws_free(wsi);
121 
122 	return 0;
123 }
124 
125 
126 static void
handle_dispatch_status(DBusConnection * c,DBusDispatchStatus s,void * data)127 handle_dispatch_status(DBusConnection *c, DBusDispatchStatus s, void *data)
128 {
129 	lwsl_info("%s: new dbus dispatch status: %d\n", __func__, s);
130 }
131 
132 /*
133  * These are complicated by the fact libdbus can have two separate DBusWatch
134  * objects for the same fd, to control watching POLLIN and POLLOUT individually.
135  *
136  * However we will actually watch using poll(), where the unit is the fd, and
137  * it has a unified events field with just POLLIN / POLLOUT flags.
138  *
139  * So we have to be prepared for one or two watchers coming in any order.
140  */
141 
142 static dbus_bool_t
lws_dbus_add_watch(DBusWatch * w,void * data)143 lws_dbus_add_watch(DBusWatch *w, void *data)
144 {
145 	struct lws_dbus_ctx *ctx = (struct lws_dbus_ctx *)data;
146 	struct lws_context_per_thread *pt = &ctx->vh->context->pt[ctx->tsi];
147 	unsigned int flags = 0, lws_flags = 0;
148 	struct lws *wsi;
149 	int n;
150 
151 	lws_pt_lock(pt, __func__);
152 
153 	wsi = __lws_shadow_wsi(ctx, w, dbus_watch_get_unix_fd(w), 1);
154 	if (!wsi) {
155 		lws_pt_unlock(pt);
156 		lwsl_err("%s: unable to get wsi\n", __func__);
157 
158 		return FALSE;
159 	}
160 
161 	for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
162 		if (w == ctx->w[n])
163 			break;
164 
165 	if (n == (int)LWS_ARRAY_SIZE(ctx->w))
166 		for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
167 			if (!ctx->w[n]) {
168 				ctx->w[n] = w;
169 				break;
170 			}
171 
172 	for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
173 		if (ctx->w[n])
174 			flags |= dbus_watch_get_flags(ctx->w[n]);
175 
176 	if (flags & DBUS_WATCH_READABLE)
177 		lws_flags |= LWS_POLLIN;
178 	if (flags & DBUS_WATCH_WRITABLE)
179 		lws_flags |= LWS_POLLOUT;
180 
181 	lwsl_info("%s: w %p, fd %d, data %p, flags %d\n", __func__, w,
182 		  dbus_watch_get_unix_fd(w), data, lws_flags);
183 
184 	__lws_change_pollfd(wsi, 0, lws_flags);
185 
186 	lws_pt_unlock(pt);
187 
188 	return TRUE;
189 }
190 
191 static int
check_destroy_shadow_wsi(struct lws_dbus_ctx * ctx,struct lws * wsi)192 check_destroy_shadow_wsi(struct lws_dbus_ctx *ctx, struct lws *wsi)
193 {
194 	int n;
195 
196 	if (!wsi)
197 		return 0;
198 
199 	for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
200 		if (ctx->w[n])
201 			return 0;
202 
203 	__lws_shadow_wsi_destroy(ctx, wsi);
204 
205 	if (!ctx->conn || !ctx->hup || ctx->timeouts)
206 		return 0;
207 
208 	if (dbus_connection_get_dispatch_status(ctx->conn) ==
209 						     DBUS_DISPATCH_DATA_REMAINS)
210 		return 0;
211 
212 	if (ctx->cb_closing)
213 		ctx->cb_closing(ctx);
214 
215 	return 1;
216 }
217 
218 static void
lws_dbus_remove_watch(DBusWatch * w,void * data)219 lws_dbus_remove_watch(DBusWatch *w, void *data)
220 {
221 	struct lws_dbus_ctx *ctx = (struct lws_dbus_ctx *)data;
222 	struct lws_context_per_thread *pt = &ctx->vh->context->pt[ctx->tsi];
223 	unsigned int flags = 0, lws_flags = 0;
224 	struct lws *wsi;
225 	int n;
226 
227 	lws_pt_lock(pt, __func__);
228 
229 	wsi = __lws_shadow_wsi(ctx, w, dbus_watch_get_unix_fd(w), 0);
230 	if (!wsi)
231 		goto bail;
232 
233 	for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
234 		if (w == ctx->w[n]) {
235 			ctx->w[n] = NULL;
236 			break;
237 		}
238 
239 	for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
240 		if (ctx->w[n])
241 			flags |= dbus_watch_get_flags(ctx->w[n]);
242 
243 	if ((~flags) & DBUS_WATCH_READABLE)
244 		lws_flags |= LWS_POLLIN;
245 	if ((~flags) & DBUS_WATCH_WRITABLE)
246 		lws_flags |= LWS_POLLOUT;
247 
248 	lwsl_info("%s: w %p, fd %d, data %p, clearing lws flags %d\n",
249 		  __func__, w, dbus_watch_get_unix_fd(w), data, lws_flags);
250 
251 	__lws_change_pollfd(wsi, lws_flags, 0);
252 
253 bail:
254 	lws_pt_unlock(pt);
255 }
256 
257 static void
lws_dbus_toggle_watch(DBusWatch * w,void * data)258 lws_dbus_toggle_watch(DBusWatch *w, void *data)
259 {
260 	if (dbus_watch_get_enabled(w))
261 		lws_dbus_add_watch(w, data);
262 	else
263 		lws_dbus_remove_watch(w, data);
264 }
265 
266 
267 static dbus_bool_t
lws_dbus_add_timeout(DBusTimeout * t,void * data)268 lws_dbus_add_timeout(DBusTimeout *t, void *data)
269 {
270 	struct lws_dbus_ctx *ctx = (struct lws_dbus_ctx *)data;
271 	struct lws_context_per_thread *pt = &ctx->vh->context->pt[ctx->tsi];
272 	int ms = dbus_timeout_get_interval(t);
273 	struct lws_role_dbus_timer *dbt;
274 	time_t ti = time(NULL);
275 
276 	if (!dbus_timeout_get_enabled(t))
277 		return TRUE;
278 
279 	if (ms < 1000)
280 		ms = 1000;
281 
282 	dbt = lws_malloc(sizeof(*dbt), "dbus timer");
283 	if (!dbt)
284 		return FALSE;
285 
286 	lwsl_info("%s: adding timeout %dms\n", __func__,
287 			dbus_timeout_get_interval(t));
288 
289 	dbt->data = t;
290 	dbt->fire = ti + (ms < 1000);
291 	dbt->timer_list.prev = NULL;
292 	dbt->timer_list.next = NULL;
293 	dbt->timer_list.owner = NULL;
294 	lws_dll2_add_head(&dbt->timer_list, &pt->dbus.timer_list_owner);
295 
296 	ctx->timeouts++;
297 
298 	return TRUE;
299 }
300 
301 static void
lws_dbus_remove_timeout(DBusTimeout * t,void * data)302 lws_dbus_remove_timeout(DBusTimeout *t, void *data)
303 {
304 	struct lws_dbus_ctx *ctx = (struct lws_dbus_ctx *)data;
305 	struct lws_context_per_thread *pt = &ctx->vh->context->pt[ctx->tsi];
306 
307 	lwsl_info("%s: t %p, data %p\n", __func__, t, data);
308 
309 	lws_start_foreach_dll_safe(struct lws_dll2 *, rdt, nx,
310 				lws_dll2_get_head(&pt->dbus.timer_list_owner)) {
311 		struct lws_role_dbus_timer *r = lws_container_of(rdt,
312 					struct lws_role_dbus_timer, timer_list);
313 		if (t == r->data) {
314 			lws_dll2_remove(rdt);
315 			lws_free(rdt);
316 			ctx->timeouts--;
317 			break;
318 		}
319 	} lws_end_foreach_dll_safe(rdt, nx);
320 }
321 
322 static void
lws_dbus_toggle_timeout(DBusTimeout * t,void * data)323 lws_dbus_toggle_timeout(DBusTimeout *t, void *data)
324 {
325 	if (dbus_timeout_get_enabled(t))
326 		lws_dbus_add_timeout(t, data);
327 	else
328 		lws_dbus_remove_timeout(t, data);
329 }
330 
331 /*
332  * This sets up a connection along the same lines as
333  * dbus_connection_setup_with_g_main(), but for using the lws event loop.
334  */
335 
336 int
lws_dbus_connection_setup(struct lws_dbus_ctx * ctx,DBusConnection * conn,lws_dbus_closing_t cb_closing)337 lws_dbus_connection_setup(struct lws_dbus_ctx *ctx, DBusConnection *conn,
338 			  lws_dbus_closing_t cb_closing)
339 {
340 	int n;
341 
342 	ctx->conn = conn;
343 	ctx->cb_closing = cb_closing;
344 	ctx->hup = 0;
345 	ctx->timeouts = 0;
346 	for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
347 		ctx->w[n] = NULL;
348 
349 	if (!dbus_connection_set_watch_functions(conn, lws_dbus_add_watch,
350 						 lws_dbus_remove_watch,
351 						 lws_dbus_toggle_watch,
352 						 ctx, NULL)) {
353 		lwsl_err("%s: dbus_connection_set_watch_functions fail\n",
354 			 __func__);
355 		return 1;
356 	}
357 
358 	if (!dbus_connection_set_timeout_functions(conn,
359 						   lws_dbus_add_timeout,
360 						   lws_dbus_remove_timeout,
361 						   lws_dbus_toggle_timeout,
362 						   ctx, NULL)) {
363 		lwsl_err("%s: dbus_connection_set_timeout_functions fail\n",
364 			 __func__);
365 		return 1;
366 	}
367 
368 	dbus_connection_set_dispatch_status_function(conn,
369 						     handle_dispatch_status,
370 						     ctx, NULL);
371 
372 	return 0;
373 }
374 
375 /*
376  * This wraps dbus_server_listen(), additionally taking care of the event loop
377  * -related setups.
378  */
379 
380 DBusServer *
lws_dbus_server_listen(struct lws_dbus_ctx * ctx,const char * ads,DBusError * e,DBusNewConnectionFunction new_conn)381 lws_dbus_server_listen(struct lws_dbus_ctx *ctx, const char *ads, DBusError *e,
382 		       DBusNewConnectionFunction new_conn)
383 {
384 	ctx->cb_closing = NULL;
385 	ctx->hup = 0;
386 	ctx->timeouts = 0;
387 
388 	ctx->dbs = dbus_server_listen(ads, e);
389 	if (!ctx->dbs)
390 		return NULL;
391 
392 	dbus_server_set_new_connection_function(ctx->dbs, new_conn, ctx, NULL);
393 
394 	if (!dbus_server_set_watch_functions(ctx->dbs, lws_dbus_add_watch,
395 					     lws_dbus_remove_watch,
396 					     lws_dbus_toggle_watch,
397 					     ctx, NULL)) {
398 		lwsl_err("%s: dbus_connection_set_watch_functions fail\n",
399 			 __func__);
400 		goto bail;
401 	}
402 
403 	if (!dbus_server_set_timeout_functions(ctx->dbs, lws_dbus_add_timeout,
404 					       lws_dbus_remove_timeout,
405 					       lws_dbus_toggle_timeout,
406 					       ctx, NULL)) {
407 		lwsl_err("%s: dbus_connection_set_timeout_functions fail\n",
408 			 __func__);
409 		goto bail;
410 	}
411 
412 	return ctx->dbs;
413 
414 bail:
415 	dbus_server_disconnect(ctx->dbs);
416 	dbus_server_unref(ctx->dbs);
417 
418 	return NULL;
419 }
420 
421 
422 /*
423  * There shouldn't be a race here with watcher removal and poll wait, because
424  * everything including the dbus activity is serialized in one event loop.
425  *
426  * If it removes the watcher and we remove the wsi and fd entry before this,
427  * actually we can no longer map the fd to this invalidated wsi pointer to call
428  * this.
429  */
430 
431 static int
rops_handle_POLLIN_dbus(struct lws_context_per_thread * pt,struct lws * wsi,struct lws_pollfd * pollfd)432 rops_handle_POLLIN_dbus(struct lws_context_per_thread *pt, struct lws *wsi,
433 			struct lws_pollfd *pollfd)
434 {
435 	struct lws_dbus_ctx *ctx =
436 			(struct lws_dbus_ctx *)wsi->opaque_parent_data;
437 	unsigned int flags = 0;
438 	int n;
439 
440 	if (pollfd->revents & LWS_POLLIN)
441 		flags |= DBUS_WATCH_READABLE;
442 	if (pollfd->revents & LWS_POLLOUT)
443 		flags |= DBUS_WATCH_WRITABLE;
444 
445 	if (pollfd->revents & (LWS_POLLHUP))
446 		ctx->hup = 1;
447 
448 	/*
449 	 * POLLIN + POLLOUT gets us called here on the corresponding shadow
450 	 * wsi.  wsi->opaque_parent_data is the watcher handle bound to the wsi
451 	 */
452 
453 	for (n = 0; n < (int)LWS_ARRAY_SIZE(ctx->w); n++)
454 		if (ctx->w[n] && !dbus_watch_handle(ctx->w[n], flags))
455 			lwsl_err("%s: dbus_watch_handle failed\n", __func__);
456 
457 	if (ctx->conn) {
458 		lwsl_info("%s: conn: flags %d\n", __func__, flags);
459 
460 		while (dbus_connection_get_dispatch_status(ctx->conn) ==
461 						DBUS_DISPATCH_DATA_REMAINS)
462 			dbus_connection_dispatch(ctx->conn);
463 
464 		handle_dispatch_status(NULL, DBUS_DISPATCH_DATA_REMAINS, NULL);
465 
466 		check_destroy_shadow_wsi(ctx, wsi);
467 	} else
468 		if (ctx->dbs)
469 			/* ??? */
470 			lwsl_debug("%s: dbs: %d\n", __func__, flags);
471 
472 	return LWS_HPI_RET_HANDLED;
473 }
474 
475 static void
lws_dbus_sul_cb(lws_sorted_usec_list_t * sul)476 lws_dbus_sul_cb(lws_sorted_usec_list_t *sul)
477 {
478 	struct lws_context_per_thread *pt = lws_container_of(sul,
479 				struct lws_context_per_thread, dbus.sul);
480 
481 	lws_start_foreach_dll_safe(struct lws_dll2 *, rdt, nx,
482 			 lws_dll2_get_head(&pt->dbus.timer_list_owner)) {
483 		struct lws_role_dbus_timer *r = lws_container_of(rdt,
484 					struct lws_role_dbus_timer, timer_list);
485 
486 		if (time(NULL) > r->fire) {
487 			lwsl_notice("%s: firing timer\n", __func__);
488 			dbus_timeout_handle(r->data);
489 			lws_dll2_remove(rdt);
490 			lws_free(rdt);
491 		}
492 	} lws_end_foreach_dll_safe(rdt, nx);
493 
494 	__lws_sul_insert(&pt->pt_sul_owner, &pt->dbus.sul,
495 			 3 * LWS_US_PER_SEC);
496 }
497 
498 static int
rops_pt_init_destroy_dbus(struct lws_context * context,const struct lws_context_creation_info * info,struct lws_context_per_thread * pt,int destroy)499 rops_pt_init_destroy_dbus(struct lws_context *context,
500 		    const struct lws_context_creation_info *info,
501 		    struct lws_context_per_thread *pt, int destroy)
502 {
503 	if (!destroy) {
504 
505 		pt->dbus.sul.cb = lws_dbus_sul_cb;
506 
507 		__lws_sul_insert(&pt->pt_sul_owner, &pt->dbus.sul,
508 				 3 * LWS_US_PER_SEC);
509 	} else
510 		lws_dll2_remove(&pt->dbus.sul.list);
511 
512 	return 0;
513 }
514 
515 const struct lws_role_ops role_ops_dbus = {
516 	/* role name */			"dbus",
517 	/* alpn id */			NULL,
518 	/* check_upgrades */		NULL,
519 	/* pt_init_destroy */		rops_pt_init_destroy_dbus,
520 	/* init_vhost */		NULL,
521 	/* destroy_vhost */		NULL,
522 	/* service_flag_pending */	NULL,
523 	/* handle_POLLIN */		rops_handle_POLLIN_dbus,
524 	/* handle_POLLOUT */		NULL,
525 	/* perform_user_POLLOUT */	NULL,
526 	/* callback_on_writable */	NULL,
527 	/* tx_credit */			NULL,
528 	/* write_role_protocol */	NULL,
529 	/* encapsulation_parent */	NULL,
530 	/* alpn_negotiated */		NULL,
531 	/* close_via_role_protocol */	NULL,
532 	/* close_role */		NULL,
533 	/* close_kill_connection */	NULL,
534 	/* destroy_role */		NULL,
535 	/* adoption_bind */		NULL,
536 	/* client_bind */		NULL,
537 	/* issue_keepalive */		NULL,
538 	/* adoption_cb clnt, srv */	{ 0, 0 },
539 	/* rx_cb clnt, srv */		{ 0, 0 },
540 	/* writeable cb clnt, srv */	{ 0, 0 },
541 	/* close cb clnt, srv */	{ 0, 0 },
542 	/* protocol_bind_cb c,s */	{ 0, 0 },
543 	/* protocol_unbind_cb c,s */	{ 0, 0 },
544 	/* file_handle */		0,
545 };
546