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