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