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 23static struct lws_dbus_ctx *dbus_ctx; 24static struct lws_context *context; 25static 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 34static DBusHandlerResult 35client_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 54static void 55destroy_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 75static void 76cb_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 86static struct lws_dbus_ctx * 87create_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 133fail: 134 dbus_error_free(&err); 135 136 free(ctx); 137 138 return NULL; 139} 140 141 142void 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 152static void 153pending_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 176bail1: 177 dbus_message_unref(msg); 178bail: 179 dbus_pending_call_unref(pending); 180} 181 182static int 183remote_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 213bail: 214 dbus_message_unref(msg); 215 216 return ret; 217} 218 219int 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 264bail2: 265 destroy_dbus_client_conn(dbus_ctx); 266 267bail1: 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 276bail: 277 lwsl_err("%s: failed to start\n", __func__); 278 lws_context_destroy(context); 279 280 return 1; 281} 282