x11: gracefully handle ICE connection error
authorIgor V. Kovalenko <igor.v.kovalenko@gmail.com>
Wed, 23 Dec 2020 07:31:18 +0000 (10:31 +0300)
committerPulseAudio Marge Bot <pulseaudio-maintainers@lists.freedesktop.org>
Mon, 4 Jan 2021 15:59:22 +0000 (15:59 +0000)
Implementation is largely inspired by GNOME/mutter ICE session handling.

Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/441>

src/modules/x11/module-x11-xsmp.c

index eb18b43..1ff6cc7 100644 (file)
@@ -55,21 +55,50 @@ struct userdata {
     pa_module *module;
     pa_client *client;
     SmcConn connection;
-    pa_x11_wrapper *x11;
+
+    pa_x11_wrapper *x11_wrapper;
+    pa_x11_client *x11_client;
 };
 
+static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(w);
+    pa_assert(u);
+    pa_assert(u->x11_wrapper == w);
+
+    pa_log_debug("X11 client kill callback called");
+
+    if (u->connection) {
+        SmcCloseConnection(u->connection, 0, NULL);
+        u->connection = NULL;
+    }
+
+    if (u->x11_client) {
+        pa_x11_client_free(u->x11_client);
+        u->x11_client = NULL;
+    }
+
+    if (u->x11_wrapper) {
+        pa_x11_wrapper_unref(u->x11_wrapper);
+        u->x11_wrapper = NULL;
+    }
+
+    pa_module_unload_request(u->module, true);
+}
+
 static void die_cb(SmcConn connection, SmPointer client_data) {
     struct userdata *u = client_data;
     pa_assert(u);
 
     pa_log_debug("Got die message from XSMP.");
 
-    pa_x11_wrapper_kill_deferred(u->x11);
-
-    pa_x11_wrapper_unref(u->x11);
-    u->x11 = NULL;
+    if (u->connection) {
+        SmcCloseConnection(u->connection, 0, NULL);
+        u->connection = NULL;
+    }
 
-    pa_module_unload_request(u->module, true);
+    pa_x11_wrapper_kill_deferred(u->x11_wrapper);
 }
 
 static void save_complete_cb(SmcConn connection, SmPointer client_data) {
@@ -87,6 +116,7 @@ static void ice_io_cb(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_fla
     IceConn connection = userdata;
 
     if (IceProcessMessages(connection, NULL, NULL) == IceProcessMessagesIOError) {
+        pa_log_debug("IceProcessMessages: I/O error, closing ICE connection");
         IceSetShutdownNegotiation(connection, False);
         IceCloseConnection(connection);
     }
@@ -106,6 +136,17 @@ static void new_ice_connection(IceConn connection, IcePointer client_data, Bool
         c->mainloop->io_free(*watch_data);
 }
 
+static IceIOErrorHandler ice_installed_handler;
+
+/* We call any handler installed before (or after) module is loaded but
+   avoid calling the default libICE handler which does an exit() */
+
+static void ice_io_error_handler(IceConn iceConn) {
+    pa_log_warn("ICE I/O error handler called");
+    if (ice_installed_handler)
+      (*ice_installed_handler) (iceConn);
+}
+
 int pa__init(pa_module*m) {
 
     pa_modargs *ma = NULL;
@@ -123,17 +164,27 @@ int pa__init(pa_module*m) {
     if (ice_in_use) {
         pa_log("module-x11-xsmp may not be loaded twice.");
         return -1;
-    }
+    } else {
+        IceIOErrorHandler default_handler;
+
+        ice_installed_handler = IceSetIOErrorHandler (NULL);
+        default_handler = IceSetIOErrorHandler (ice_io_error_handler);
 
-    IceAddConnectionWatch(new_ice_connection, m->core);
-    ice_in_use = true;
+        if (ice_installed_handler == default_handler)
+            ice_installed_handler = NULL;
+
+        IceSetIOErrorHandler(ice_io_error_handler);
+
+        IceAddConnectionWatch(new_ice_connection, m->core);
+        ice_in_use = true;
+    }
 
     m->userdata = u = pa_xnew(struct userdata, 1);
     u->core = m->core;
     u->module = m;
     u->client = NULL;
     u->connection = NULL;
-    u->x11 = NULL;
+    u->x11_wrapper = NULL;
 
     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
         pa_log("Failed to parse module arguments");
@@ -147,9 +198,11 @@ int pa__init(pa_module*m) {
         }
     }
 
-    if (!(u->x11 = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))
+    if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL))))
         goto fail;
 
+    u->x11_client = pa_x11_client_new(u->x11_wrapper, NULL, x11_kill_cb, u);
+
     e = pa_modargs_get_value(ma, "session_manager", NULL);
 
     if (!e && !getenv("SESSION_MANAGER")) {
@@ -253,8 +306,11 @@ void pa__done(pa_module*m) {
         if (u->client)
             pa_client_free(u->client);
 
-        if (u->x11)
-            pa_x11_wrapper_unref(u->x11);
+        if (u->x11_client)
+            pa_x11_client_free(u->x11_client);
+
+        if (u->x11_wrapper)
+            pa_x11_wrapper_unref(u->x11_wrapper);
 
         pa_xfree(u);
     }