[ABRT PATCH] applet: update seen list when X Session dies

Jakub Filak jfilak at redhat.com
Mon Aug 12 11:55:56 UTC 2013


Closes rhbz#886094

Signed-off-by: Jakub Filak <jfilak at redhat.com>
---
 configure.ac           |   2 +
 src/applet/Makefile.am |   4 ++
 src/applet/applet.c    | 192 +++++++++++++++++++++++++++++++++++++++++++------
 3 files changed, 177 insertions(+), 21 deletions(-)

diff --git a/configure.ac b/configure.ac
index 70fafe1..97e6b76 100644
--- a/configure.ac
+++ b/configure.ac
@@ -92,6 +92,8 @@ PYTHON_LIBS=`python-config --libs 2> /dev/null`
 AC_SUBST(PYTHON_CFLAGS)
 AC_SUBST(PYTHON_LIBS)
 
+PKG_CHECK_MODULES([XICE], [ice])
+PKG_CHECK_MODULES([XSMP], [sm])
 PKG_CHECK_MODULES([GTK], [gtk+-3.0])
 PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.21])
 PKG_CHECK_MODULES([DBUS], [dbus-1])
diff --git a/src/applet/Makefile.am b/src/applet/Makefile.am
index 2684ca5..ec50fcd 100644
--- a/src/applet/Makefile.am
+++ b/src/applet/Makefile.am
@@ -9,6 +9,8 @@ abrt_applet_CPPFLAGS = \
     -DBIN_DIR=\"$(bindir)\" \
     -DLIBEXEC_DIR=\"$(libexecdir)\" \
     -DICON_DIR=\"${datadir}/abrt/icons/hicolor/48x48/status\" \
+    $(XICE_CFLAGS) \
+    $(XSMP_CFLAGS) \
     $(GTK_CFLAGS) \
     $(DBUS_CFLAGS) \
     $(LIBREPORT_GTK_CFLAGS) \
@@ -23,6 +25,8 @@ abrt_applet_LDADD = \
     -labrt_dbus \
     -lglib-2.0 \
     -lgthread-2.0 \
+    $(XICE_LIBS) \
+    $(XSMP_LIBS) \
     $(DBUS_LIBS) \
     $(LIBNOTIFY_LIBS) \
     $(GTK_LIBS)
diff --git a/src/applet/applet.c b/src/applet/applet.c
index 91c8256..738c14e 100644
--- a/src/applet/applet.c
+++ b/src/applet/applet.c
@@ -19,6 +19,8 @@
 #if HAVE_LOCALE_H
 # include <locale.h>
 #endif
+#include <X11/Xlib.h>
+#include <X11/SM/SMlib.h>
 #include <gtk/gtk.h>
 #include <libnotify/notify.h>
 #include <dbus/dbus-shared.h>
@@ -1261,6 +1263,169 @@ static gboolean handle_sigterm_pipe(GIOChannel *gio, GIOCondition condition, gpo
     return FALSE; /* Pointless (loop is done and signal handler was reset); "please remove this event" */
 }
 
+/*
+ * XSMP client
+ */
+static void
+cb_smc_save_yourself(SmcConn smc_conn, SmPointer client_data, gint save_type,
+                          gboolean shutdown, gint interact_style, gboolean fast)
+{
+    /* http://lesstif.sourceforge.net/doc/super-ux/g1ae04e/chap12.html#12.4.1.1 */
+    /* Since we do not save any state, we can declare that we succeeded (True) */
+    SmcSaveYourselfDone(smc_conn, True);
+}
+
+static void
+cb_smc_die(SmcConn smc_conn, SmPointer client_data)
+{
+    /* The session manager sends a Die message to a client when it wants it to
+     * die. The client should respond by calling SmcCloseConnection.
+     *
+     * http://lesstif.sourceforge.net/doc/super-ux/g1ae04e/chap12.html#12.4.1.2
+     *
+     * The reasons will most likely be NULL if resignation is expected by the
+     * client.
+     *
+     * http://lesstif.sourceforge.net/doc/super-ux/g1ae04e/chap12.html#12.4.2
+     */
+    SmcCloseConnection(smc_conn, 0 /* The number of reason messages */,
+                       NULL /* The reasons for closing the connection */);
+
+    gtk_main_quit();
+}
+
+static void
+cb_smc_save_complete(SmcConn smc_conn, SmPointer client_data)
+{
+    /* No action needed */
+}
+
+static void
+cb_smc_shutdown_cancelled(SmcConn smc_conn, SmPointer client_data)
+{
+    /* Since we do not save any state, we can declare that we succeeded (True) */
+    SmcSaveYourselfDone(smc_conn, True);
+}
+
+static gboolean
+cb_ice_process_messages(GIOChannel *gio, GIOCondition condition, gpointer user_data)
+{
+    /* May return an error but we don't care, it isn't our problem */
+    IceConn ice_conn = (IceConn)user_data;
+    IceProcessMessages(ice_conn, NULL /* Indicates if a reply is being waited for */,
+                       NULL /* If set to True on return, a reply is ready */);
+
+    return TRUE; /* do not remove this source */
+}
+
+/*
+ * We do not need to save any special state, we just want to be notified about
+ * session death.
+ */
+static void
+xsmp_client_connect(void)
+{
+    SmcCallbacks callbacks;
+    memset(&callbacks, 0, sizeof(callbacks));
+    callbacks.die.callback = cb_smc_die;
+    callbacks.save_yourself.callback = cb_smc_save_yourself;
+    callbacks.save_complete.callback = cb_smc_save_complete;
+    callbacks.shutdown_cancelled.callback = cb_smc_shutdown_cancelled;
+    char error_buf[256];
+    char *client_id;
+
+    SmcConn conn = SmcOpenConnection(NULL /* SESSION_MANAGER env variable */,
+            NULL /* share ICE connection */, SmProtoMajor, SmProtoMinor,
+            SmcSaveYourselfProcMask | SmcDieProcMask |
+            SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask,
+            &callbacks, NULL /* previous client id */,
+            &client_id, sizeof(error_buf), error_buf);
+
+    if (!conn)
+    {
+        log("Failed to open connection to session manager: %s", error_buf);
+        return;
+    }
+
+    free(client_id);
+
+    /* These 4 properties are required, however we do not need a special
+     * handling neither for restart nor for clone, because we just need to
+     * update seen problems list at exit.
+     *
+     * http://www.x.org/releases/X11R7.7/doc/libSM/xsmp.html#Predefined_Properties
+     */
+    SmProp prop_program, prop_user, prop_restart_cmd, prop_clone_cmd;
+    SmProp *prop_list[4];
+    SmPropValue val_program, val_user;
+    prop_program.name = (char *)SmProgram;
+    prop_program.type = (char *)SmARRAY8;
+    val_program.value = (char *)"abrt-applet";
+    val_program.length = (int)strlen(val_program.value);
+    prop_program.num_vals = 1;
+    prop_program.vals = &val_program;
+    prop_list[0] = &prop_program;
+
+    prop_user.name = (char *)SmUserID;
+    prop_user.type = (char *)SmARRAY8;
+
+    char userid_string[256];
+    uid_t uid = getuid();
+    struct passwd * pwd;
+    if ((pwd = getpwuid(uid)) != NULL)
+        snprintf(userid_string, sizeof(userid_string), "%s", pwd->pw_name);
+    else
+        snprintf(userid_string, sizeof(userid_string), "%d", uid);
+
+    val_user.value = (char *)userid_string;
+    val_user.length = (int) strlen(val_user.value);
+    prop_user.num_vals = 1;
+    prop_user.vals = &val_user;
+    prop_list[1] = &prop_user;
+
+    prop_restart_cmd.name = (char *)SmRestartCommand;
+    prop_restart_cmd.type = (char *)SmARRAY8;
+    prop_restart_cmd.num_vals = 1;
+    /* Use Program property */
+    prop_restart_cmd.vals = &val_program;
+    prop_list[2] = &prop_restart_cmd;
+
+    prop_clone_cmd.name = (char *)SmCloneCommand;
+    prop_clone_cmd.type = (char *)SmARRAY8;
+    prop_clone_cmd.num_vals = 1;
+    /* Use Program property */
+    prop_clone_cmd.vals = &val_program;
+    prop_list[3] = &prop_clone_cmd;
+
+    SmcSetProperties(conn, 4 /* number of properties */, prop_list);
+
+    /* The X Session Management Protocol is layered on top of the Inter-Client
+     * Exchange (ICE) Protocol.
+     *
+     * http://lesstif.sourceforge.net/doc/super-ux/g1ae04e/chap12.html#12.2
+     */
+    IceConn ice_conn = SmcGetIceConnection(conn);
+    gint fd = IceConnectionNumber(ice_conn);
+
+    /* Make sure we don't pass on these file descriptors to an
+     * exec'd child process.
+     */
+    fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
+
+    /* When a client detects that there is data to read on an ICE connection,
+     * it should call the IceProcessMessages function.
+     *
+     * http://lesstif.sourceforge.net/doc/super-ux/g1ae04e/chap12.html#12.2
+     */
+    GIOChannel *channel = g_io_channel_unix_new(fd);
+    g_io_add_watch(channel, G_IO_ERR | G_IO_HUP | G_IO_IN,
+                   cb_ice_process_messages, (gpointer)ice_conn);
+    g_io_channel_unref(channel);
+}
+/*
+ * XSMP client finito
+ */
+
 int main(int argc, char** argv)
 {
     /* I18n */
@@ -1473,10 +1638,14 @@ next:
                 handle_sigterm_pipe,
                 NULL);
 
+    /* Register a handler quiting from gtk main loop on X Session death.
+     */
+    xsmp_client_connect();
+
     /* Enter main loop
      *
-     * Returns on SIGTERM signal or on menu button Quit click.
-     * Please, see the comment below.
+     * Returns on SIGTERM signal, on menu button Quit click or on X Session
+     * death.
      */
     gtk_main();
 
@@ -1484,25 +1653,6 @@ next:
 
     ignored_problems_free(g_ignore_set);
 
-    /* GTK3 doesn't return from main loop in case of termination due to desktop
-     * session end. (GTK handles XSMP (X Session Management Protocol) messages
-     * on its own and exits on session end.)
-     *
-     * https://bugs.freedesktop.org/show_bug.cgi?id=32839
-     * https://bugzilla.gnome.org/show_bug.cgi?id=639770
-     *
-     * !!! None of the following commands will be executed. !!!
-     *
-     * The commands should be pushed into some callback used by deprecated
-     * gtk_quit_add() or proposed g_flush_all()
-     *
-     * Or we can use proposed GtkSMClient (currently available in libegg)
-     * https://live.gnome.org/SessionManagement/EggSMClient
-     *
-     * Or DBus GnomeSession manager (this won't work for other desktops)
-     * http://people.gnome.org/~mccann/gnome-session/docs/gnome-session.html
-     */
-
 #if (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 31)
     gdk_threads_leave();
 #endif
-- 
1.8.3.1



More information about the Crash-catcher mailing list