• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ws protocol handler plugin for dbus ws proxy
3  *
4  * Written in 2010-2019 by Andy Green <andy@warmcat.com>
5  *
6  * This file is made available under the Creative Commons CC0 1.0
7  * Universal Public Domain Dedication.
8  *
9  * This proxies outgoing ws client connections on DBUS.  So a DBUS client can
10  * reach out and get remote WS payloads in both directions.
11  *
12  * DEVELOPER NOTE
13  *
14  * Two worlds, dbus and ws, collide in this file.
15  *
16  * There main thing keeping it sane is both worlds are running in the same
17  * thread and on the same event loop.  Although things may happen completely
18  * asynchronously in both worlds, the logical reaction to those events are
19  * serialized in a single event loop doing one thing at a time.
20  *
21  * So while you are servicing an event in the ws world, you can be certain the
22  * logical state of any related dbus thing cannot change underneath you, until
23  * you return back to the event loop, and vice versa.  So other-world objects
24  * can't be freed, other-world handles can't close etc while you are servicing
25  * in your world.
26  *
27  * Since all bets are off what happens next, and in which world, after you
28  * return back to the event loop though, an additional rule is needed: worlds
29  * must not allocate in objects owned by the other world.  They must generate
30  * their own objects in their world and use those for allocations and state.
31  *
32  * For example in the dbus-world there is a struct lws_dbus_ctx_wsproxy with
33  * various state, but he is subject to deletion by events in dbus-world.  If
34  * the ws-world stored things there, they are subject to going out of scope
35  * at the whim of the dbus connection without the ws world hearing about it and
36  * cleanly deallocaing them.  So the ws world must keep his own pss that remains
37  * in scope until the ws link closes for allocations from ws-world.
38  *
39  * In this application there's a point of contact between the worlds, a ring
40  * buffer allocated in ws world when the ws connection is established, and
41  * deallocated when the ws connection is closed.  The DBUS world needs to put
42  * things in this ringbuffer.  But the way lws_ring works, when the message
43  * allocated in DBUS world is queued on the ringbuffer, the ringbuffer itself
44  * takes responsibility for deallocation.  So there is no problem.
45  */
46 
47 #if !defined (LWS_PLUGIN_STATIC)
48 #define LWS_DLL
49 #define LWS_INTERNAL
50 #include <libwebsockets.h>
51 #include <libwebsockets/lws-dbus.h>
52 #endif
53 
54 #include <string.h>
55 #include <assert.h>
56 #include <signal.h>
57 
58 /*
59  * dbus accepted connections create these larger context structs that start
60  * with the lws dbus context
61  */
62 
63 struct vhd_dbus_proxy;
64 
65 struct msg {
66 	void *payload; /* is malloc'd */
67 	size_t len;
68 	char binary;
69 	char first;
70 	char final;
71 };
72 
73 struct pss_dbus_proxy {
74 	struct lws_ring *ring_out;
75 	uint32_t ring_out_tail;
76 };
77 
78 struct lws_dbus_ctx_wsproxy {
79 	struct lws_dbus_ctx ctx;
80 
81 	struct lws *cwsi;
82 	struct vhd_dbus_proxy *vhd;
83 	struct pss_dbus_proxy *pss;
84 };
85 
86 struct vhd_dbus_proxy {
87 	struct lws_context *context;
88 	struct lws_vhost *vhost;
89 
90 	/*
91 	 * Because the listener ctx is composed in the vhd, we can always get a
92 	 * pointer to the outer vhd from a pointer to ctx_listener inside.
93 	 */
94 	struct lws_dbus_ctx ctx_listener;
95 	struct lws_dbus_ctx_wsproxy dctx;
96 
97 	const char *dbus_listen_ads;
98 };
99 
100 #define THIS_INTERFACE	"org.libwebsockets.wsclientproxy"
101 #define THIS_OBJECT	"/org/libwebsockets/wsclientproxy"
102 #define THIS_BUSNAME	"org.libwebsockets.wsclientproxy"
103 static const char *version = "0.1";
104 
105 static const char *server_introspection_xml =
106 	DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
107 	"<node>\n"
108 	"  <interface name='" DBUS_INTERFACE_INTROSPECTABLE "'>\n"
109 	"    <method name='Introspect'>\n"
110 	"      <arg name='data' type='s' direction='out' />\n"
111 	"    </method>\n"
112 	"  </interface>\n"
113 
114 	"  <interface name='" DBUS_INTERFACE_PROPERTIES "'>\n"
115 	"    <method name='Get'>\n"
116 	"      <arg name='interface' type='s' direction='in' />\n"
117 	"      <arg name='property'  type='s' direction='in' />\n"
118 	"      <arg name='value'     type='s' direction='out' />\n"
119 	"    </method>\n"
120 	"    <method name='GetAll'>\n"
121 	"      <arg name='interface'  type='s'     direction='in'/>\n"
122 	"      <arg name='properties' type='a{sv}' direction='out'/>\n"
123 	"    </method>\n"
124 	"  </interface>\n"
125 
126 	"  <interface name='"THIS_INTERFACE"'>\n"
127 	"    <property name='Version' type='s' access='read' />\n"
128 	"    <method name='Connect' >\n"
129 	"      <arg name='url' type='s' direction='in' />\n"
130 	"      <arg name='subprotocol' type='s' direction='in' />\n"
131 	"    </method>\n"
132 	"    <method name='Send'>\n"
133 	"      <arg name='payload' type='s' direction='in' />\n"
134 	"    </method>\n"
135 	"    <signal name='Receive'>\n"
136 	"    </signal>"
137 	"    <signal name='Status'>\n"
138 	"    </signal>"
139 	"  </interface>\n"
140 
141 	"</node>\n";
142 
143 static void
destroy_message(void * _msg)144 destroy_message(void *_msg)
145 {
146 	struct msg *msg = _msg;
147 
148 	free(msg->payload);
149 	msg->payload = NULL;
150 	msg->len = 0;
151 }
152 
153 /*
154  * DBUS WORLD
155  */
156 
157 static DBusHandlerResult
dmh_introspect(DBusConnection * c,DBusMessage * m,DBusMessage ** reply,void * d)158 dmh_introspect(DBusConnection *c, DBusMessage *m, DBusMessage **reply, void *d)
159 {
160 	dbus_message_append_args(*reply,
161 				 DBUS_TYPE_STRING, &server_introspection_xml,
162 				 DBUS_TYPE_INVALID);
163 
164 	return DBUS_HANDLER_RESULT_HANDLED;
165 }
166 
167 static DBusHandlerResult
dmh_get(DBusConnection * c,DBusMessage * m,DBusMessage ** reply,void * d)168 dmh_get(DBusConnection *c, DBusMessage *m, DBusMessage **reply, void *d)
169 {
170 	const char *interface, *property;
171 	DBusError err;
172 
173 	dbus_error_init(&err);
174 
175 	if (!dbus_message_get_args(m, &err, DBUS_TYPE_STRING, &interface,
176 					    DBUS_TYPE_STRING, &property,
177 					    DBUS_TYPE_INVALID)) {
178 		dbus_message_unref(*reply);
179 		*reply = dbus_message_new_error(m, err.name, err.message);
180 		dbus_error_free(&err);
181 
182 		return DBUS_HANDLER_RESULT_HANDLED;
183 	}
184 
185 	if (strcmp(property, "Version")) /* Unknown property */
186 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
187 
188 	dbus_message_append_args(*reply, DBUS_TYPE_STRING, &version,
189 					 DBUS_TYPE_INVALID);
190 
191 	return DBUS_HANDLER_RESULT_HANDLED;
192 }
193 
194 static DBusHandlerResult
dmh_getall(DBusConnection * c,DBusMessage * m,DBusMessage ** reply,void * d)195 dmh_getall(DBusConnection *c, DBusMessage *m, DBusMessage **reply, void *d)
196 {
197 	DBusMessageIter arr, di, iter, va;
198 	const char *property = "Version";
199 
200 	dbus_message_iter_init_append(*reply, &iter);
201 	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &arr);
202 
203 	/* Append all properties name/value pairs */
204 	dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY, NULL, &di);
205 	dbus_message_iter_append_basic(&di, DBUS_TYPE_STRING, &property);
206 	dbus_message_iter_open_container(&di, DBUS_TYPE_VARIANT, "s", &va);
207 	dbus_message_iter_append_basic(&va, DBUS_TYPE_STRING, &version);
208 	dbus_message_iter_close_container(&di, &va);
209 	dbus_message_iter_close_container(&arr, &di);
210 
211 	dbus_message_iter_close_container(&iter, &arr);
212 
213 	return DBUS_HANDLER_RESULT_HANDLED;
214 }
215 
216 static DBusHandlerResult
dmh_connect(DBusConnection * c,DBusMessage * m,DBusMessage ** reply,void * d)217 dmh_connect(DBusConnection *c, DBusMessage *m, DBusMessage **reply, void *d)
218 {
219 	struct lws_dbus_ctx_wsproxy *wspctx = (struct lws_dbus_ctx_wsproxy *)d;
220 	const char *prot = "", *ads = "", *path = "", *baduri = "Bad Uri",
221 		   *connecting = "Connecting", *failed = "Failed", **pp;
222 	struct lws_client_connect_info i;
223 	char host[128], uri_copy[512];
224 	const char *uri, *subprotocol;
225 	DBusError err;
226 	int port = 0;
227 
228 	dbus_error_init(&err);
229 
230 	if (!dbus_message_get_args(m, &err, DBUS_TYPE_STRING, &uri,
231 			 	 	    DBUS_TYPE_STRING, &subprotocol,
232 					    DBUS_TYPE_INVALID)) {
233 		dbus_message_unref(*reply);
234 		*reply = dbus_message_new_error(m, err.name, err.message);
235 		dbus_error_free(&err);
236 
237 		return DBUS_HANDLER_RESULT_HANDLED;
238 	}
239 
240 	strncpy(uri_copy, uri, sizeof(uri_copy) - 1);
241 	uri_copy[sizeof(uri_copy) - 1] = '\0';
242 
243 	if (lws_parse_uri(uri_copy, &prot, &ads, &port, &path)) {
244 		pp = &baduri;
245 		goto send_reply;
246 	}
247 
248 	lws_snprintf(host, sizeof(host), "%s:%u", ads, port);
249 
250 	memset(&i, 0, sizeof(i));
251 
252 	assert(wspctx);
253 	assert(wspctx->vhd);
254 
255 	i.context = wspctx->vhd->context;
256 	i.port = port;
257 	i.address = ads;
258 	i.path = path;
259 	i.host = host;
260 	i.origin = host;
261 	i.ssl_connection = !strcmp(prot, "https") || !strcmp(prot, "wss");
262 	i.vhost = wspctx->ctx.vh;
263 	i.protocol = subprotocol;
264 	i.local_protocol_name = "lws-minimal-dbus-wsproxy";
265 	i.pwsi = &wspctx->cwsi;
266 
267 	lwsl_user("%s: connecting to %s://%s:%d%s\n", __func__, prot,
268 			i.address, i.port, i.path);
269 
270 	if (!lws_client_connect_via_info(&i)) {
271 		lwsl_notice("%s: client connect failed\n", __func__);
272 		pp = &failed;
273 		goto send_reply;
274 	}
275 
276 	lws_set_opaque_parent_data(wspctx->cwsi, wspctx);
277 	lwsl_notice("%s: client connecting...\n", __func__);
278 	pp = &connecting;
279 
280 send_reply:
281 	dbus_message_append_args(*reply, DBUS_TYPE_STRING, pp,
282 					 DBUS_TYPE_INVALID);
283 
284 	return DBUS_HANDLER_RESULT_HANDLED;
285 }
286 
287 static int
issue_dbus_signal(struct lws * wsi,const char * signame,const char * string)288 issue_dbus_signal(struct lws *wsi, const char *signame, const char *string)
289 {
290 	struct lws_dbus_ctx_wsproxy *wspctx =
291 			lws_get_opaque_parent_data(wsi);
292 	DBusMessage *m;
293 
294 	if (!wspctx)
295 		return 1;
296 
297 	m = dbus_message_new_signal(THIS_OBJECT, THIS_INTERFACE, signame);
298 	if (!m) {
299 		lwsl_err("%s: new signal failed\n", __func__);
300 		return 1;
301 	}
302 
303 	dbus_message_append_args(m, DBUS_TYPE_STRING, &string,
304 				    DBUS_TYPE_INVALID);
305 
306 	if (!dbus_connection_send(wspctx->ctx.conn, m, NULL))
307 		lwsl_err("%s: unable to send\n", __func__);
308 
309 	dbus_message_unref(m);
310 
311 	return 0;
312 }
313 
314 static DBusHandlerResult
dmh_send(DBusConnection * c,DBusMessage * m,DBusMessage ** reply,void * d)315 dmh_send(DBusConnection *c, DBusMessage *m, DBusMessage **reply, void *d)
316 {
317 	struct lws_dbus_ctx_wsproxy *wspctx = (struct lws_dbus_ctx_wsproxy *)d;
318 	const char *payload;
319 	struct msg amsg;
320 	DBusError err;
321 
322 	dbus_error_init(&err);
323 
324 	if (!wspctx->cwsi || !wspctx->pss) {
325 		dbus_message_unref(*reply);
326 		*reply = dbus_message_new_error(m, "Send Fail", "No ws conn");
327 
328 		return DBUS_HANDLER_RESULT_HANDLED;
329 	}
330 
331 	if (!dbus_message_get_args(m, &err, DBUS_TYPE_STRING, &payload,
332 					    DBUS_TYPE_INVALID)) {
333 		dbus_message_unref(*reply);
334 		*reply = dbus_message_new_error(m, err.name, err.message);
335 		dbus_error_free(&err);
336 
337 		return DBUS_HANDLER_RESULT_HANDLED;
338 	}
339 
340 	/*
341 	 * we allocate on the ringbuffer in ws world, but responsibility for
342 	 * freeing it is understood by lws_ring.
343 	 */
344 
345 	amsg.len = strlen(payload);
346 	/* notice we over-allocate by LWS_PRE */
347 	amsg.payload = malloc(LWS_PRE + amsg.len);
348 	if (!amsg.payload) {
349 		lwsl_user("OOM: dropping\n");
350 		dbus_message_unref(*reply);
351 		*reply = dbus_message_new_error(m, "Send Fail", "OOM");
352 
353 		return DBUS_HANDLER_RESULT_HANDLED;
354 	}
355 	amsg.binary = 0;
356 	amsg.first = 1;
357 	amsg.final = 1;
358 
359 	memcpy((char *)amsg.payload + LWS_PRE, payload, amsg.len);
360 	if (!lws_ring_insert(wspctx->pss->ring_out, &amsg, 1)) {
361 		destroy_message(&amsg);
362 		lwsl_user("Ring Full!\n");
363 		dbus_message_unref(*reply);
364 		*reply = dbus_message_new_error(m, "Send Fail", "Ring full");
365 
366 		return DBUS_HANDLER_RESULT_HANDLED;
367 	}
368 	if (wspctx->cwsi)
369 		lws_callback_on_writable(wspctx->cwsi);
370 
371 	return DBUS_HANDLER_RESULT_HANDLED;
372 }
373 
374 struct lws_dbus_methods {
375 	const char *inter;
376 	const char *call;
377 	lws_dbus_message_handler handler;
378 } meths[] = {
379 	{ DBUS_INTERFACE_INTROSPECTABLE, "Introspect",	dmh_introspect	},
380 	{ DBUS_INTERFACE_PROPERTIES,	 "Get",		dmh_get		},
381 	{ DBUS_INTERFACE_PROPERTIES,	 "GetAll",	dmh_getall	},
382 	{ THIS_INTERFACE,		 "Connect",	dmh_connect	},
383 	{ THIS_INTERFACE,		 "Send",	dmh_send	},
384 };
385 
386 static DBusHandlerResult
server_message_handler(DBusConnection * conn,DBusMessage * message,void * data)387 server_message_handler(DBusConnection *conn, DBusMessage *message, void *data)
388 {
389 	struct lws_dbus_methods *mp = meths;
390         DBusMessage *reply = NULL;
391 	DBusHandlerResult result;
392 	size_t n;
393 
394 	assert(data);
395 
396 	lwsl_info("%s: Got D-Bus request: %s.%s on %s\n", __func__,
397 		dbus_message_get_interface(message),
398 		dbus_message_get_member(message),
399 		dbus_message_get_path(message));
400 
401 	for (n = 0; n < LWS_ARRAY_SIZE(meths); n++) {
402 		if (dbus_message_is_method_call(message, mp->inter, mp->call)) {
403 			reply = dbus_message_new_method_return(message);
404 			if (!reply)
405 				return DBUS_HANDLER_RESULT_NEED_MEMORY;
406 
407 			result = mp->handler(conn, message, &reply, data);
408 
409 			if (result == DBUS_HANDLER_RESULT_HANDLED &&
410 			    !dbus_connection_send(conn, reply, NULL))
411 				result = DBUS_HANDLER_RESULT_NEED_MEMORY;
412 
413 			dbus_message_unref(reply);
414 
415 			return result;
416 		}
417 
418 		mp++;
419 	}
420 
421 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
422 }
423 
424 static const DBusObjectPathVTable vtable = {
425 	.message_function = server_message_handler
426 };
427 
428 static void
destroy_dbus_server_conn(struct lws_dbus_ctx_wsproxy * wsctx)429 destroy_dbus_server_conn(struct lws_dbus_ctx_wsproxy *wsctx)
430 {
431 	if (!wsctx->ctx.conn)
432 		return;
433 
434 	lwsl_notice("%s\n", __func__);
435 
436 	dbus_connection_unregister_object_path(wsctx->ctx.conn, THIS_OBJECT);
437 	lws_dll2_remove(&wsctx->ctx.next);
438 	dbus_connection_unref(wsctx->ctx.conn);
439 }
440 
441 /*
442  * This is the client dbus side going away.  We need to stop the associated
443  * client ws part and make sure it can't dereference us now we are gone.
444  */
445 
446 static void
cb_closing(struct lws_dbus_ctx * ctx)447 cb_closing(struct lws_dbus_ctx *ctx)
448 {
449 	struct lws_dbus_ctx_wsproxy *wspctx =
450 			(struct lws_dbus_ctx_wsproxy *)ctx;
451 	lwsl_err("%s: closing\n", __func__);
452 
453 	/*
454 	 * We have to take care that the associated proxy wsi knows our
455 	 * dbus ctx is going out of scope after we return from here.
456 	 *
457 	 * We do it by setting its pointer to our dbus ctx to NULL.
458 	 */
459 
460 	if (wspctx->cwsi) {
461 		lws_set_opaque_parent_data(wspctx->cwsi, NULL);
462 		lws_set_timeout(wspctx->cwsi,
463 				PENDING_TIMEOUT_KILLED_BY_PROXY_CLIENT_CLOSE,
464 				LWS_TO_KILL_ASYNC);
465 	}
466 
467 	destroy_dbus_server_conn(wspctx);
468 
469 	free(wspctx);
470 }
471 
472 static void
new_conn(DBusServer * server,DBusConnection * conn,void * d)473 new_conn(DBusServer *server, DBusConnection *conn, void *d)
474 {
475 	struct lws_dbus_ctx_wsproxy *conn_wspctx, /* the new conn context */
476 				    /* the listener context */
477 				    *wspctx = (struct lws_dbus_ctx_wsproxy *)d;
478 	struct vhd_dbus_proxy *vhd = lws_container_of(d,
479 					struct vhd_dbus_proxy, ctx_listener);
480 
481 	assert(vhd->vhost == wspctx->ctx.vh);
482 
483 	lwsl_notice("%s\n", __func__);
484 
485 	conn_wspctx = malloc(sizeof(*conn_wspctx));
486 	if (!conn_wspctx)
487 		return;
488 
489 	memset(conn_wspctx, 0, sizeof(*conn_wspctx));
490 
491 	conn_wspctx->ctx.tsi = wspctx->ctx.tsi;
492 	conn_wspctx->ctx.vh = wspctx->ctx.vh;
493 	conn_wspctx->ctx.conn = conn;
494 	conn_wspctx->vhd = vhd; /* let accepted connections also know the vhd */
495 
496 	assert(conn_wspctx->vhd);
497 
498 	if (lws_dbus_connection_setup(&conn_wspctx->ctx, conn, cb_closing)) {
499 		lwsl_err("%s: connection bind to lws failed\n", __func__);
500 		goto bail;
501 	}
502 
503 	if (!dbus_connection_register_object_path(conn, THIS_OBJECT, &vtable,
504 						  conn_wspctx)) {
505 		lwsl_err("%s: Failed to register object path\n", __func__);
506 		goto bail;
507 	}
508 
509 	lws_dll2_add_head(&conn_wspctx->ctx.next, &wspctx->ctx.owner);
510 
511 	/* we take on responsibility for explicit close / unref with this... */
512 	dbus_connection_ref(conn);
513 
514 	return;
515 
516 bail:
517 	free(conn_wspctx);
518 }
519 
520 static int
create_dbus_listener(struct vhd_dbus_proxy * vhd,int tsi)521 create_dbus_listener(struct vhd_dbus_proxy *vhd, int tsi)
522 {
523 	DBusError e;
524 
525         dbus_error_init(&e);
526 #if 0
527         vhd->dctx.ctx.tsi = tsi;
528         vhd->dctx.ctx.vh = vhd->vhost;
529         vhd->dctx.ctx.next.prev = NULL;
530         vhd->dctx.ctx.next.next = NULL;
531         vhd->dctx.vhd = vhd;
532         vhd->dctx.cwsi = NULL;
533 
534 	/* connect to the SYSTEM bus */
535 
536 	vhd->dctx.ctx.conn = dbus_bus_get(DBUS_BUS_SYSTEM, &e);
537 	if (!vhd->dctx.ctx.conn) {
538 		lwsl_notice("%s: Failed to get a session DBus connection: '%s'"
539 			    ", continuing with daemon listener only\n",
540 			 __func__, e.message);
541 		dbus_error_free(&e);
542 		dbus_error_init(&e);
543 		goto daemon;
544 	}
545 
546 	/*
547 	 * by default dbus will call exit() when this connection closes...
548 	 * we have to shut down other things cleanly, so disable that
549 	 */
550 	dbus_connection_set_exit_on_disconnect(vhd->dctx.ctx.conn, 0);
551 
552 	if (dbus_bus_request_name(vhd->dctx.ctx.conn, THIS_BUSNAME,
553 				  DBUS_NAME_FLAG_REPLACE_EXISTING, &e) !=
554 					DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
555 		lwsl_notice("%s: Failed to request name on bus: '%s',"
556 			 " continuing with daemon listener only\n",
557 			 __func__, e.message);
558 		dbus_connection_unref(vhd->dctx.ctx.conn);
559 		vhd->dctx.ctx.conn = NULL;
560 		dbus_error_free(&e);
561 		dbus_error_init(&e);
562 		goto daemon;
563 	}
564 
565 	if (!dbus_connection_register_object_path(vhd->dctx.ctx.conn,
566 						  THIS_OBJECT, &vtable,
567 						  &vhd->dctx)) {
568 		lwsl_err("%s: Failed to register object path\n", __func__);
569 		goto fail;
570 	}
571 
572 	/*
573 	 * This is the part that binds the connection to lws watcher and
574 	 * timeout handling provided by lws
575 	 */
576 
577 	if (lws_dbus_connection_setup(&vhd->dctx.ctx, vhd->dctx.ctx.conn,
578 				      cb_closing)) {
579 		lwsl_err("%s: connection bind to lws failed\n", __func__);
580 		goto fail;
581 	}
582 
583 daemon:
584 #endif
585         vhd->ctx_listener.vh = vhd->vhost;
586         vhd->ctx_listener.tsi = tsi;
587 
588 	if (!lws_dbus_server_listen(&vhd->ctx_listener, vhd->dbus_listen_ads,
589 				    &e, new_conn)) {
590 		lwsl_err("%s: failed\n", __func__);
591 		dbus_error_free(&e);
592 
593 		return 1;
594 	}
595 
596 	lwsl_notice("%s: created DBUS listener on %s\n", __func__,
597 			vhd->dbus_listen_ads);
598 
599 	return 0;
600 #if 0
601 fail:
602 	dbus_error_free(&e);
603 
604 	return 1;
605 #endif
606 }
607 
608 static void
destroy_dbus_server_listener(struct vhd_dbus_proxy * vhd)609 destroy_dbus_server_listener(struct vhd_dbus_proxy *vhd)
610 {
611 	dbus_server_disconnect(vhd->ctx_listener.dbs);
612 
613 	lws_start_foreach_dll_safe(struct lws_dll2 *, rdt, nx,
614 			vhd->ctx_listener.owner.head) {
615 		struct lws_dbus_ctx *r = lws_container_of(rdt,
616 						struct lws_dbus_ctx, next);
617 
618 		dbus_connection_close(r->conn);
619 		dbus_connection_unref(r->conn);
620 		free(r);
621 	} lws_end_foreach_dll_safe(rdt, nx);
622 
623 	if (vhd->dctx.ctx.conn)
624 		dbus_connection_unref(vhd->dctx.ctx.conn);
625 	dbus_server_unref(vhd->ctx_listener.dbs);
626 }
627 
628 /*
629  * WS WORLD
630  */
631 
632 static int
callback_minimal_dbus_wsproxy(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)633 callback_minimal_dbus_wsproxy(struct lws *wsi, enum lws_callback_reasons reason,
634 			      void *user, void *in, size_t len)
635 {
636 	struct pss_dbus_proxy *pss = (struct pss_dbus_proxy *)user;
637 	struct vhd_dbus_proxy *vhd = (struct vhd_dbus_proxy *)
638 			lws_protocol_vh_priv_get(lws_get_vhost(wsi),
639 						 lws_get_protocol(wsi));
640 	struct lws_dbus_ctx_wsproxy *wspctx;
641 	const struct msg *pmsg;
642 	int flags, m;
643 
644 	switch (reason) {
645 
646 	case LWS_CALLBACK_PROTOCOL_INIT:
647 		vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
648 					lws_get_protocol(wsi), sizeof(*vhd));
649 		if (!vhd)
650 			return -1;
651 
652 		vhd->context = lws_get_context(wsi);
653 		vhd->vhost = lws_get_vhost(wsi);
654 
655 		if (lws_pvo_get_str(in, "ads", &vhd->dbus_listen_ads)) {
656 			lwsl_err("%s: pvo 'ads' must be set\n", __func__);
657 			return -1;
658 		}
659 
660 		if (create_dbus_listener(vhd, 0)) {
661 			lwsl_err("%s: create_dbus_listener failed\n", __func__);
662 			return -1;
663 		}
664 		break;
665 
666 	case LWS_CALLBACK_PROTOCOL_DESTROY:
667 		destroy_dbus_server_listener(vhd);
668 		/* this is required for valgrind-cleanliness */
669 		dbus_shutdown();
670 		break;
671 
672 	case LWS_CALLBACK_CLIENT_ESTABLISHED:
673 		lwsl_user("LWS_CALLBACK_CLIENT_ESTABLISHED\n");
674 
675 		/*
676 		 * create the send ringbuffer now the ws connection is
677 		 * established.
678 		 */
679 
680 		wspctx = lws_get_opaque_parent_data(wsi);
681 		if (!wspctx)
682 			break;
683 
684 		wspctx->pss = pss;
685 		pss->ring_out_tail = 0;
686 		pss->ring_out = lws_ring_create(sizeof(struct msg), 8,
687 						   destroy_message);
688 		if (!pss->ring_out) {
689 			lwsl_err("OOM\n");
690 			return -1;
691 		}
692 
693 		issue_dbus_signal(wsi, "Status",
694 				  "ws client connection established");
695 		break;
696 
697 	case LWS_CALLBACK_CLIENT_WRITEABLE:
698 		lwsl_user("LWS_CALLBACK_CLIENT_WRITEABLE:\n");
699 
700 		pmsg = lws_ring_get_element(pss->ring_out, &pss->ring_out_tail);
701 		if (!pmsg) {
702 			lwsl_user(" (nothing in ring)\n");
703 			break;
704 		}
705 
706 		flags = lws_write_ws_flags(
707 			    pmsg->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT,
708 			    pmsg->first, pmsg->final);
709 
710 		/* notice we allowed for LWS_PRE in the payload already */
711 		m = lws_write(wsi, ((unsigned char *)pmsg->payload) + LWS_PRE,
712 			      pmsg->len, flags);
713 		if (m < (int)pmsg->len) {
714 			lwsl_err("ERROR %d writing to ws socket\n", m);
715 			return -1;
716 		}
717 
718 		lwsl_user(" wrote %d: flags: 0x%x first: %d final %d\n",
719 				m, flags, pmsg->first, pmsg->final);
720 
721 		lws_ring_consume_single_tail(pss->ring_out,
722 					     &pss->ring_out_tail, 1);
723 
724 		/* more to do for us? */
725 		if (lws_ring_get_element(pss->ring_out, &pss->ring_out_tail))
726 			/* come back as soon as we can write more */
727 			lws_callback_on_writable(wsi);
728 
729 		break;
730 
731 	case LWS_CALLBACK_CLIENT_RECEIVE:
732 
733 		lwsl_user("LWS_CALLBACK_CLIENT_RECEIVE: %4d "
734 			  "(rpp %5d, first %d, last %d, bin %d)\n",
735 			  (int)len, (int)lws_remaining_packet_payload(wsi),
736 			  lws_is_first_fragment(wsi),
737 			  lws_is_final_fragment(wsi),
738 			  lws_frame_is_binary(wsi));
739 
740 		{
741 			char strbuf[256];
742 			size_t l = len;
743 
744 			if (l > sizeof(strbuf) - 1u)
745 				l = sizeof(strbuf) - 1u;
746 
747 			memcpy(strbuf, in, l);
748 			strbuf[l] = '\0';
749 
750 			issue_dbus_signal(wsi, "Receive", strbuf);
751 		}
752 		break;
753 
754 	case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
755 		lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
756 			 in ? (char *)in : "(null)");
757 		issue_dbus_signal(wsi, "Status", "ws client connection error");
758 		break;
759 
760 	case LWS_CALLBACK_CLIENT_CLOSED:
761 		lwsl_err("LWS_CALLBACK_CLIENT_CLOSED ()\n");
762 		issue_dbus_signal(wsi, "Status", "ws client connection closed");
763 
764 		/* destroy any ringbuffer and pending messages */
765 
766 		lws_ring_destroy(pss->ring_out);
767 
768 		wspctx = lws_get_opaque_parent_data(wsi);
769 		if (!wspctx)
770 			break;
771 
772 		/*
773 		 * the wspctx cannot refer to its child wsi any longer, it is
774 		 * about to go out of scope.
775 		 */
776 
777 		wspctx->cwsi = NULL;
778 		wspctx->pss = NULL;
779 		break;
780 
781 	default:
782 		break;
783 	}
784 
785 	return 0;
786 }
787 
788 #define LWS_PLUGIN_PROTOCOL_MINIMAL_DBUS_WSPROXY \
789 	{ \
790 		"lws-minimal-dbus-wsproxy", \
791 		callback_minimal_dbus_wsproxy, \
792 		sizeof(struct pss_dbus_proxy), \
793 		1024, \
794 		0, NULL, 0 \
795 	}
796