1d4afb5ceSopenharmony_ci/*
2d4afb5ceSopenharmony_ci * lws-minimal-dbus-client
3d4afb5ceSopenharmony_ci *
4d4afb5ceSopenharmony_ci * Written in 2010-2019 by Andy Green <andy@warmcat.com>
5d4afb5ceSopenharmony_ci *
6d4afb5ceSopenharmony_ci * This file is made available under the Creative Commons CC0 1.0
7d4afb5ceSopenharmony_ci * Universal Public Domain Dedication.
8d4afb5ceSopenharmony_ci *
9d4afb5ceSopenharmony_ci * This demonstrates a minimal session dbus server that uses the lws event loop,
10d4afb5ceSopenharmony_ci * making it possible to integrate it with other lws features.
11d4afb5ceSopenharmony_ci */
12d4afb5ceSopenharmony_ci
13d4afb5ceSopenharmony_ci#include <stdbool.h>
14d4afb5ceSopenharmony_ci#include <string.h>
15d4afb5ceSopenharmony_ci#include <stdio.h>
16d4afb5ceSopenharmony_ci#include <stdlib.h>
17d4afb5ceSopenharmony_ci#include <unistd.h>
18d4afb5ceSopenharmony_ci#include <signal.h>
19d4afb5ceSopenharmony_ci
20d4afb5ceSopenharmony_ci#include <libwebsockets.h>
21d4afb5ceSopenharmony_ci#include <libwebsockets/lws-dbus.h>
22d4afb5ceSopenharmony_ci
23d4afb5ceSopenharmony_cistatic struct lws_dbus_ctx *dbus_ctx;
24d4afb5ceSopenharmony_cistatic struct lws_context *context;
25d4afb5ceSopenharmony_cistatic int interrupted;
26d4afb5ceSopenharmony_ci
27d4afb5ceSopenharmony_ci#define THIS_INTERFACE	 "org.libwebsockets.test"
28d4afb5ceSopenharmony_ci#define THIS_OBJECT	 "/org/libwebsockets/test"
29d4afb5ceSopenharmony_ci#define THIS_BUSNAME	 "org.libwebsockets.test"
30d4afb5ceSopenharmony_ci
31d4afb5ceSopenharmony_ci#define THIS_LISTEN_PATH "unix:abstract=org.libwebsockets.test"
32d4afb5ceSopenharmony_ci
33d4afb5ceSopenharmony_ci
34d4afb5ceSopenharmony_cistatic DBusHandlerResult
35d4afb5ceSopenharmony_ciclient_message_handler(DBusConnection *conn, DBusMessage *message, void *data)
36d4afb5ceSopenharmony_ci{
37d4afb5ceSopenharmony_ci	const char *str;
38d4afb5ceSopenharmony_ci
39d4afb5ceSopenharmony_ci	lwsl_info("%s: Got D-Bus request: %s.%s on %s\n", __func__,
40d4afb5ceSopenharmony_ci		  dbus_message_get_interface(message),
41d4afb5ceSopenharmony_ci		  dbus_message_get_member(message),
42d4afb5ceSopenharmony_ci		  dbus_message_get_path(message));
43d4afb5ceSopenharmony_ci
44d4afb5ceSopenharmony_ci	if (!dbus_message_get_args(message, NULL,
45d4afb5ceSopenharmony_ci				   DBUS_TYPE_STRING, &str,
46d4afb5ceSopenharmony_ci				   DBUS_TYPE_INVALID))
47d4afb5ceSopenharmony_ci		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
48d4afb5ceSopenharmony_ci
49d4afb5ceSopenharmony_ci	lwsl_notice("%s: '%s'\n", __func__, str);
50d4afb5ceSopenharmony_ci
51d4afb5ceSopenharmony_ci	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
52d4afb5ceSopenharmony_ci}
53d4afb5ceSopenharmony_ci
54d4afb5ceSopenharmony_cistatic void
55d4afb5ceSopenharmony_cidestroy_dbus_client_conn(struct lws_dbus_ctx *ctx)
56d4afb5ceSopenharmony_ci{
57d4afb5ceSopenharmony_ci	if (!ctx || !ctx->conn)
58d4afb5ceSopenharmony_ci		return;
59d4afb5ceSopenharmony_ci
60d4afb5ceSopenharmony_ci	lwsl_notice("%s\n", __func__);
61d4afb5ceSopenharmony_ci
62d4afb5ceSopenharmony_ci	dbus_connection_remove_filter(ctx->conn, client_message_handler, ctx);
63d4afb5ceSopenharmony_ci	dbus_connection_close(ctx->conn);
64d4afb5ceSopenharmony_ci	dbus_connection_unref(ctx->conn);
65d4afb5ceSopenharmony_ci
66d4afb5ceSopenharmony_ci	free(ctx);
67d4afb5ceSopenharmony_ci}
68d4afb5ceSopenharmony_ci
69d4afb5ceSopenharmony_ci/*
70d4afb5ceSopenharmony_ci * This callback is coming when lws has noticed the fd took a POLLHUP.  The
71d4afb5ceSopenharmony_ci * ctx has effectively gone out of scope before this, and the connection can
72d4afb5ceSopenharmony_ci * be cleaned up and the ctx freed.
73d4afb5ceSopenharmony_ci */
74d4afb5ceSopenharmony_ci
75d4afb5ceSopenharmony_cistatic void
76d4afb5ceSopenharmony_cicb_closing(struct lws_dbus_ctx *ctx)
77d4afb5ceSopenharmony_ci{
78d4afb5ceSopenharmony_ci	lwsl_err("%s: closing\n", __func__);
79d4afb5ceSopenharmony_ci
80d4afb5ceSopenharmony_ci	if (ctx == dbus_ctx)
81d4afb5ceSopenharmony_ci		dbus_ctx = NULL;
82d4afb5ceSopenharmony_ci
83d4afb5ceSopenharmony_ci	destroy_dbus_client_conn(ctx);
84d4afb5ceSopenharmony_ci}
85d4afb5ceSopenharmony_ci
86d4afb5ceSopenharmony_cistatic struct lws_dbus_ctx *
87d4afb5ceSopenharmony_cicreate_dbus_client_conn(struct lws_vhost *vh, int tsi, const char *ads)
88d4afb5ceSopenharmony_ci{
89d4afb5ceSopenharmony_ci	struct lws_dbus_ctx *ctx;
90d4afb5ceSopenharmony_ci	DBusError err;
91d4afb5ceSopenharmony_ci
92d4afb5ceSopenharmony_ci	ctx = malloc(sizeof(*ctx));
93d4afb5ceSopenharmony_ci	if (!ctx)
94d4afb5ceSopenharmony_ci		return NULL;
95d4afb5ceSopenharmony_ci
96d4afb5ceSopenharmony_ci	memset(ctx, 0, sizeof(*ctx));
97d4afb5ceSopenharmony_ci
98d4afb5ceSopenharmony_ci	ctx->vh = vh;
99d4afb5ceSopenharmony_ci	ctx->tsi = tsi;
100d4afb5ceSopenharmony_ci
101d4afb5ceSopenharmony_ci        dbus_error_init(&err);
102d4afb5ceSopenharmony_ci
103d4afb5ceSopenharmony_ci	/* connect to the daemon bus */
104d4afb5ceSopenharmony_ci	ctx->conn = dbus_connection_open_private(ads, &err);
105d4afb5ceSopenharmony_ci	if (!ctx->conn) {
106d4afb5ceSopenharmony_ci		lwsl_err("%s: Failed to connect: %s\n",
107d4afb5ceSopenharmony_ci			 __func__, err.message);
108d4afb5ceSopenharmony_ci		goto fail;
109d4afb5ceSopenharmony_ci	}
110d4afb5ceSopenharmony_ci
111d4afb5ceSopenharmony_ci	dbus_connection_set_exit_on_disconnect(ctx->conn, 0);
112d4afb5ceSopenharmony_ci
113d4afb5ceSopenharmony_ci	if (!dbus_connection_add_filter(ctx->conn, client_message_handler,
114d4afb5ceSopenharmony_ci					ctx, NULL)) {
115d4afb5ceSopenharmony_ci		lwsl_err("%s: Failed to add filter\n", __func__);
116d4afb5ceSopenharmony_ci		goto fail;
117d4afb5ceSopenharmony_ci	}
118d4afb5ceSopenharmony_ci
119d4afb5ceSopenharmony_ci	/*
120d4afb5ceSopenharmony_ci	 * This is the part that binds the connection to lws watcher and
121d4afb5ceSopenharmony_ci	 * timeout handling provided by lws
122d4afb5ceSopenharmony_ci	 */
123d4afb5ceSopenharmony_ci
124d4afb5ceSopenharmony_ci	if (lws_dbus_connection_setup(ctx, ctx->conn, cb_closing)) {
125d4afb5ceSopenharmony_ci		lwsl_err("%s: connection bind to lws failed\n", __func__);
126d4afb5ceSopenharmony_ci		goto fail;
127d4afb5ceSopenharmony_ci	}
128d4afb5ceSopenharmony_ci
129d4afb5ceSopenharmony_ci	lwsl_notice("%s: created OK\n", __func__);
130d4afb5ceSopenharmony_ci
131d4afb5ceSopenharmony_ci	return ctx;
132d4afb5ceSopenharmony_ci
133d4afb5ceSopenharmony_cifail:
134d4afb5ceSopenharmony_ci	dbus_error_free(&err);
135d4afb5ceSopenharmony_ci
136d4afb5ceSopenharmony_ci	free(ctx);
137d4afb5ceSopenharmony_ci
138d4afb5ceSopenharmony_ci	return NULL;
139d4afb5ceSopenharmony_ci}
140d4afb5ceSopenharmony_ci
141d4afb5ceSopenharmony_ci
142d4afb5ceSopenharmony_civoid sigint_handler(int sig)
143d4afb5ceSopenharmony_ci{
144d4afb5ceSopenharmony_ci	interrupted = 1;
145d4afb5ceSopenharmony_ci}
146d4afb5ceSopenharmony_ci
147d4afb5ceSopenharmony_ci/*
148d4afb5ceSopenharmony_ci * This gets called if we timed out waiting for the server reply, or the
149d4afb5ceSopenharmony_ci * reply arrived.
150d4afb5ceSopenharmony_ci */
151d4afb5ceSopenharmony_ci
152d4afb5ceSopenharmony_cistatic void
153d4afb5ceSopenharmony_cipending_call_notify(DBusPendingCall *pending, void *data)
154d4afb5ceSopenharmony_ci{
155d4afb5ceSopenharmony_ci	// struct lws_dbus_ctx *ctx = (struct lws_dbus_ctx *)data;
156d4afb5ceSopenharmony_ci	const char *payload;
157d4afb5ceSopenharmony_ci	DBusMessage *msg;
158d4afb5ceSopenharmony_ci
159d4afb5ceSopenharmony_ci	if (!dbus_pending_call_get_completed(pending)) {
160d4afb5ceSopenharmony_ci		lwsl_err("%s: timed out waiting for reply\n", __func__);
161d4afb5ceSopenharmony_ci
162d4afb5ceSopenharmony_ci		goto bail;
163d4afb5ceSopenharmony_ci	}
164d4afb5ceSopenharmony_ci
165d4afb5ceSopenharmony_ci	msg = dbus_pending_call_steal_reply(pending);
166d4afb5ceSopenharmony_ci	if (!msg)
167d4afb5ceSopenharmony_ci		goto bail;
168d4afb5ceSopenharmony_ci
169d4afb5ceSopenharmony_ci	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &payload,
170d4afb5ceSopenharmony_ci				   DBUS_TYPE_INVALID)) {
171d4afb5ceSopenharmony_ci		goto bail1;
172d4afb5ceSopenharmony_ci	}
173d4afb5ceSopenharmony_ci
174d4afb5ceSopenharmony_ci	lwsl_user("%s: received '%s'\n", __func__, payload);
175d4afb5ceSopenharmony_ci
176d4afb5ceSopenharmony_cibail1:
177d4afb5ceSopenharmony_ci	dbus_message_unref(msg);
178d4afb5ceSopenharmony_cibail:
179d4afb5ceSopenharmony_ci	dbus_pending_call_unref(pending);
180d4afb5ceSopenharmony_ci}
181d4afb5ceSopenharmony_ci
182d4afb5ceSopenharmony_cistatic int
183d4afb5ceSopenharmony_ciremote_method_call(struct lws_dbus_ctx *ctx)
184d4afb5ceSopenharmony_ci{
185d4afb5ceSopenharmony_ci	DBusMessage *msg;
186d4afb5ceSopenharmony_ci	const char *payload = "Hello!";
187d4afb5ceSopenharmony_ci	int ret = 1;
188d4afb5ceSopenharmony_ci
189d4afb5ceSopenharmony_ci	msg = dbus_message_new_method_call(
190d4afb5ceSopenharmony_ci			/* dest */	  THIS_BUSNAME,
191d4afb5ceSopenharmony_ci			/* object-path */ THIS_OBJECT,
192d4afb5ceSopenharmony_ci			/* interface */   THIS_INTERFACE,
193d4afb5ceSopenharmony_ci			/* method */	  "Echo");
194d4afb5ceSopenharmony_ci	if (!msg)
195d4afb5ceSopenharmony_ci		return 1;
196d4afb5ceSopenharmony_ci
197d4afb5ceSopenharmony_ci	if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &payload,
198d4afb5ceSopenharmony_ci				      DBUS_TYPE_INVALID))
199d4afb5ceSopenharmony_ci		goto bail;
200d4afb5ceSopenharmony_ci
201d4afb5ceSopenharmony_ci	if (!dbus_connection_send_with_reply(ctx->conn, msg,
202d4afb5ceSopenharmony_ci					     &ctx->pc,
203d4afb5ceSopenharmony_ci					     DBUS_TIMEOUT_USE_DEFAULT)) {
204d4afb5ceSopenharmony_ci		lwsl_err("%s: unable to send\n", __func__);
205d4afb5ceSopenharmony_ci
206d4afb5ceSopenharmony_ci		goto bail;
207d4afb5ceSopenharmony_ci	}
208d4afb5ceSopenharmony_ci
209d4afb5ceSopenharmony_ci	dbus_pending_call_set_notify(ctx->pc, pending_call_notify, ctx, NULL);
210d4afb5ceSopenharmony_ci
211d4afb5ceSopenharmony_ci	ret = 0;
212d4afb5ceSopenharmony_ci
213d4afb5ceSopenharmony_cibail:
214d4afb5ceSopenharmony_ci	dbus_message_unref(msg);
215d4afb5ceSopenharmony_ci
216d4afb5ceSopenharmony_ci	return ret;
217d4afb5ceSopenharmony_ci}
218d4afb5ceSopenharmony_ci
219d4afb5ceSopenharmony_ciint main(int argc, const char **argv)
220d4afb5ceSopenharmony_ci{
221d4afb5ceSopenharmony_ci	struct lws_vhost *vh;
222d4afb5ceSopenharmony_ci	struct lws_context_creation_info info;
223d4afb5ceSopenharmony_ci	const char *p;
224d4afb5ceSopenharmony_ci	int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
225d4afb5ceSopenharmony_ci			/* for LLL_ verbosity above NOTICE to be built into lws,
226d4afb5ceSopenharmony_ci			 * lws must have been configured and built with
227d4afb5ceSopenharmony_ci			 * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
228d4afb5ceSopenharmony_ci			/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
229d4afb5ceSopenharmony_ci			/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
230d4afb5ceSopenharmony_ci			/* | LLL_DEBUG */ /* | LLL_THREAD */;
231d4afb5ceSopenharmony_ci
232d4afb5ceSopenharmony_ci	signal(SIGINT, sigint_handler);
233d4afb5ceSopenharmony_ci
234d4afb5ceSopenharmony_ci	if ((p = lws_cmdline_option(argc, argv, "-d")))
235d4afb5ceSopenharmony_ci		logs = atoi(p);
236d4afb5ceSopenharmony_ci
237d4afb5ceSopenharmony_ci	lws_set_log_level(logs, NULL);
238d4afb5ceSopenharmony_ci	lwsl_user("LWS minimal DBUS client\n");
239d4afb5ceSopenharmony_ci
240d4afb5ceSopenharmony_ci	memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
241d4afb5ceSopenharmony_ci	info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
242d4afb5ceSopenharmony_ci	context = lws_create_context(&info);
243d4afb5ceSopenharmony_ci	if (!context) {
244d4afb5ceSopenharmony_ci		lwsl_err("lws init failed\n");
245d4afb5ceSopenharmony_ci		return 1;
246d4afb5ceSopenharmony_ci	}
247d4afb5ceSopenharmony_ci
248d4afb5ceSopenharmony_ci	vh = lws_create_vhost(context, &info);
249d4afb5ceSopenharmony_ci	if (!vh)
250d4afb5ceSopenharmony_ci		goto bail;
251d4afb5ceSopenharmony_ci
252d4afb5ceSopenharmony_ci	dbus_ctx = create_dbus_client_conn(vh, 0, THIS_LISTEN_PATH);
253d4afb5ceSopenharmony_ci	if (!dbus_ctx)
254d4afb5ceSopenharmony_ci		goto bail1;
255d4afb5ceSopenharmony_ci
256d4afb5ceSopenharmony_ci	if (remote_method_call(dbus_ctx))
257d4afb5ceSopenharmony_ci		goto bail2;
258d4afb5ceSopenharmony_ci
259d4afb5ceSopenharmony_ci	/* lws event loop (default poll one) */
260d4afb5ceSopenharmony_ci
261d4afb5ceSopenharmony_ci	while (n >= 0 && !interrupted)
262d4afb5ceSopenharmony_ci		n = lws_service(context, 0);
263d4afb5ceSopenharmony_ci
264d4afb5ceSopenharmony_cibail2:
265d4afb5ceSopenharmony_ci	destroy_dbus_client_conn(dbus_ctx);
266d4afb5ceSopenharmony_ci
267d4afb5ceSopenharmony_cibail1:
268d4afb5ceSopenharmony_ci	/* this is required for valgrind-cleanliness */
269d4afb5ceSopenharmony_ci	dbus_shutdown();
270d4afb5ceSopenharmony_ci	lws_context_destroy(context);
271d4afb5ceSopenharmony_ci
272d4afb5ceSopenharmony_ci	lwsl_notice("Exiting cleanly\n");
273d4afb5ceSopenharmony_ci
274d4afb5ceSopenharmony_ci	return 0;
275d4afb5ceSopenharmony_ci
276d4afb5ceSopenharmony_cibail:
277d4afb5ceSopenharmony_ci	lwsl_err("%s: failed to start\n", __func__);
278d4afb5ceSopenharmony_ci	lws_context_destroy(context);
279d4afb5ceSopenharmony_ci
280d4afb5ceSopenharmony_ci	return 1;
281d4afb5ceSopenharmony_ci}
282