]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
Un*x session management integration via desktopEvents
authorVMware, Inc <>
Tue, 29 Mar 2011 20:07:51 +0000 (13:07 -0700)
committerMarcelo Vanzin <mvanzin@vmware.com>
Tue, 29 Mar 2011 20:07:51 +0000 (13:07 -0700)
So we have this guest agent, vmusr, which runs in the context of graphical
desktop sessions.  It's intended to run for the duration of the session,
which is normally fine, but it has a nasty habit of overstaying its
welcome.  Our gracious host, the session manager, politely asks its guests
to leave, but vmusr just doesn't wanna listen.

Rather than stick around until the X server terminates, this change
introduces integration with XSM-compliant[1] session managers such
that, in response to a heads-up from the manager, we can exit cleanly.

libSM and libICE are used to get the job done.

libICE clients use their own connections, and they're managed via a
custom GSource.  For now, this desktopEvents feature is the only libICE
client, so rather than go overboard and deal with ICE in a separate
provider plugin, all ICE-GLib connection management is handled here.

Additionally, I changed up the Desktop Events features' mini API to
keep a GHashTable as the plugin's private data.  Each mini feature may
add its own private data to that hash using the feature's name as a
key.

1.  http://cgit.freedesktop.org/xorg/lib/libSM/tree/doc/SMlib.xml

Signed-off-by: Marcelo Vanzin <mvanzin@vmware.com>
open-vm-tools/configure.ac
open-vm-tools/services/plugins/desktopEvents/Makefile.am
open-vm-tools/services/plugins/desktopEvents/deFeatures.h
open-vm-tools/services/plugins/desktopEvents/desktopEvents.c
open-vm-tools/services/plugins/desktopEvents/desktopEventsInt.h
open-vm-tools/services/plugins/desktopEvents/reload.c
open-vm-tools/services/plugins/desktopEvents/sessionMgr.c [new file with mode: 0644]
open-vm-tools/services/plugins/desktopEvents/xioError.c

index ef6cfc31b3405ffb2911d5de621250af1c71a9bd..0801b2d91ca39bf408cec864d68bc796fb1a59d5 100644 (file)
@@ -500,6 +500,21 @@ else
         [libXtst not found. Please configure without X11 (using --without-x) or install the libXtst devel package(s).])],
       [$COMMON_XLIBS])
 
+   AC_CHECK_LIB(
+      [SM],
+      [SmcOpenConnection],
+      [XSM_LIBS="-lSM -lICE" && have_xsm_lib="yes"],
+      []
+      [-lICE])
+
+   AC_CHECK_HEADERS([X11/SM/SMlib.h X11/ICE/ICElib.h],
+                    [have_xsm_header="yes"],
+                    [],
+                    [])
+   if test "$have_xsm_lib" = "yes" -a "$have_xsm_header" = "yes"; then
+      have_xsm="yes"
+   fi
+
    # If we're building with support for Unity, we'll need a few additional
    # libraries.
    if test "$enable_unity" != "no"; then
@@ -1070,6 +1085,7 @@ AM_CONDITIONAL(THIRTY_TWO_BIT_USERSPACE, test "$userSpaceBitness" = "32")
 AM_CONDITIONAL(HAVE_X11, test "$have_x" = "yes")
 AM_CONDITIONAL(HAVE_ICU, test "$with_icu" = "yes")
 AM_CONDITIONAL(WITH_KERNEL_MODULES, test "$with_kernel_modules" = "yes")
+AM_CONDITIONAL(HAVE_XSM, test "$have_xsm" = "yes")
 AM_CONDITIONAL(ENABLE_UNITY, test "$enable_unity" = "yes")
 AM_CONDITIONAL(ENABLE_TESTS, test "$have_cunit" = "yes")
 AM_CONDITIONAL(WITH_ROOT_PRIVILEGES, test "$with_root_privileges" = "yes")
@@ -1081,6 +1097,10 @@ AM_CONDITIONAL(HAVE_GTKMM, test "$have_x" = "yes" -a "$with_gtkmm" = "yes")
 AM_CONDITIONAL(HAVE_PAM, test "$with_pam" = "yes")
 AM_CONDITIONAL(USE_SLASH_PROC, test "os" = "linux" -a "$have_glib_2_14" = "yes")
 
+if test "$have_xsm" != "yes"; then
+AC_DEFINE([NO_XSM], 1, [])
+fi
+
 ### Feature-specific flags / actions
 # Combine where possible
 
@@ -1141,6 +1161,7 @@ AC_SUBST([MODULES_OS])
 AC_SUBST([MODULES_DIR])
 AC_SUBST([MODULES])
 AC_SUBST([COMMON_XLIBS])
+AC_SUBST([XSM_LIBS])
 AC_SUBST([PAM_PREFIX])
 AC_SUBST([PLUGIN_CPPFLAGS])
 AC_SUBST([PLUGIN_LDFLAGS])
index 581bee954a9dd904508aa353c12b0e7f021579f3..85d1673adf55418931567fde6038544d9d3dba90 100644 (file)
@@ -36,3 +36,7 @@ libdesktopEvents_la_SOURCES += reload.c
 libdesktopEvents_la_SOURCES += x11Lock.c
 libdesktopEvents_la_SOURCES += xioError.c
 
+if HAVE_XSM
+libdesktopEvents_la_LIBADD += @XSM_LIBS@
+libdesktopEvents_la_SOURCES += sessionMgr.c
+endif
index 1eb8a238c93eae2d4585e1a207d8d484080751f7..9b858672b509a77e2294239a8247da5c198b921b 100644 (file)
@@ -36,7 +36,18 @@ Reload_Init(ToolsAppCtx *ctx,
             ToolsPluginData *pdata);
 
 void
-Reload_Shutdown(ToolsAppCtx *ctx);
+Reload_Shutdown(ToolsAppCtx *ctx,
+                ToolsPluginData *pdata);
+
+#ifndef NO_XSM
+gboolean
+SessionMgr_Init(ToolsAppCtx *ctx,
+                ToolsPluginData *pdata);
+
+void
+SessionMgr_Shutdown(ToolsAppCtx *ctx,
+                    ToolsPluginData *pdata);
+#endif
 
 gboolean
 X11Lock_Init(ToolsAppCtx *ctx,
@@ -47,16 +58,21 @@ XIOError_Init(ToolsAppCtx *ctx,
               ToolsPluginData *pdata);
 
 void
-XIOError_Shutdown(ToolsAppCtx *ctx);
+XIOError_Shutdown(ToolsAppCtx *ctx,
+                  ToolsPluginData *pdata);
 
 
 #if defined(DE_MAIN)
 static DesktopEventFuncs gFeatures[] = {
    { X11Lock_Init,         NULL,                      FALSE },
    { Reload_Init,          Reload_Shutdown,           FALSE },
+#ifndef NO_XSM
+   { SessionMgr_Init,      SessionMgr_Shutdown,       FALSE },
+#endif
    { XIOError_Init,        XIOError_Shutdown,         FALSE },
 };
 #endif
 
+
 #endif /* _DEFEATURES_H_ */
 
index 233810115033b0428a376a10887f44cb1afb38e2..9ff73715f19dd1af0f0bf16818de4810516db86e 100644 (file)
@@ -39,8 +39,9 @@
  *
  * Calls the shutdown function of the available features.
  *
+ * @param[in]  obj      Unused.
  * @param[in]  ctx      The application context.
- * @param[in]  plugin   Unused.
+ * @param[in]  plugin   Plugin data.
  *
  * @return TRUE.
  *
@@ -48,7 +49,8 @@
  */
 
 static gboolean
-DesktopEventsShutdown(ToolsAppCtx *ctx,
+DesktopEventsShutdown(gpointer serviceObj,
+                      ToolsAppCtx *ctx,
                       ToolsPluginData *plugin)
 {
    size_t i;
@@ -56,10 +58,16 @@ DesktopEventsShutdown(ToolsAppCtx *ctx,
    for (i = 0; i < ARRAYSIZE(gFeatures); i++) {
       DesktopEventFuncs *f = &gFeatures[i];
       if (f->initialized && f->shutdownFn != NULL) {
-         f->shutdownFn(ctx);
+         f->shutdownFn(ctx, plugin);
       }
    }
 
+   if (plugin->_private) {
+      g_hash_table_remove(plugin->_private, DE_PRIVATE_CTX);
+      g_hash_table_unref(plugin->_private);
+      plugin->_private = NULL;
+   }
+
    return TRUE;
 }
 
@@ -83,6 +91,7 @@ ToolsOnLoad(ToolsAppCtx *ctx)
    static ToolsPluginData regData = {
       "desktopEvents",
       NULL,
+      NULL,
       NULL
    };
 
@@ -93,6 +102,9 @@ ToolsOnLoad(ToolsAppCtx *ctx)
 #endif
 
    regData.regs = g_array_new(FALSE, TRUE, sizeof (ToolsAppReg));
+   regData._private = g_hash_table_new(g_str_hash, g_str_equal);
+   g_hash_table_insert(regData._private, DE_PRIVATE_CTX, ctx);
+
    for (i = 0; i < ARRAYSIZE(gFeatures); i++) {
       DesktopEventFuncs *f = &gFeatures[i];
       if (!f->initFn(ctx, &regData)) {
@@ -107,7 +119,7 @@ ToolsOnLoad(ToolsAppCtx *ctx)
     */
    if (i == ARRAYSIZE(gFeatures)) {
       ToolsPluginSignalCb sigs[] = {
-         { TOOLS_CORE_SIG_SHUTDOWN, DesktopEventsShutdown, NULL }
+         { TOOLS_CORE_SIG_SHUTDOWN, DesktopEventsShutdown, &regData }
       };
       ToolsAppReg regs[] = {
          { TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) },
@@ -117,7 +129,7 @@ ToolsOnLoad(ToolsAppCtx *ctx)
    }
 
    /* Failed to initialize something, clean up and unload. */
-   DesktopEventsShutdown(ctx, &regData);
+   DesktopEventsShutdown(NULL, ctx, &regData);
 
    /* Cleanup regData to make sure memory is freed. */
    for (i = 0; i < regData.regs->len; i++) {
@@ -130,4 +142,3 @@ ToolsOnLoad(ToolsAppCtx *ctx)
 
    return NULL;
 }
-
index 453a07ae6e74deef85ce0c36f0d7a90d7b6a4466..45e9fead3147839d16262663295301fec7dac97c 100644 (file)
 
 typedef struct DesktopEventFuncs {
    gboolean (*initFn)(ToolsAppCtx *, ToolsPluginData *);
-   void     (*shutdownFn)(ToolsAppCtx *ctx);
+   void     (*shutdownFn)(ToolsAppCtx *ctx, ToolsPluginData *);
    gboolean initialized;
 } DesktopEventFuncs;
 
+
+/*
+ * This plugin's private data field is a GHashTable*.  Each sub-feature may use
+ * that table to keep its own private state.  The following key is reserved and
+ * points to the hosting application's ToolsAppCtx.
+ */
+#define DE_PRIVATE_CTX  "ctx"
+
+
 /*
  * This platform-specific file defines the list of features available
  * for the current platform being built.
@@ -42,4 +51,3 @@ typedef struct DesktopEventFuncs {
 #include "deFeatures.h"
 
 #endif /* _DESKTOPEVENTSINT_H_ */
-
index e293c4901cc20de0d8825b0ad20a1e5d1bd3fe70..67da62bdb609ce4c82a9d4c24d67c5ac821673a5 100644 (file)
@@ -111,12 +111,14 @@ Reload_Init(ToolsAppCtx *ctx,
  * Unregisters the SIGUSR2 signal handler.
  *
  * @param[in] ctx   Application context.
+ * @param[in] pdata Plugin data (unused).
  *
  ******************************************************************************
  */
 
 void
-Reload_Shutdown(ToolsAppCtx *ctx)
+Reload_Shutdown(ToolsAppCtx *ctx,
+                ToolsPluginData *pdata)
 {
    g_source_destroy(gReloadSrc);
    g_source_unref(gReloadSrc);
diff --git a/open-vm-tools/services/plugins/desktopEvents/sessionMgr.c b/open-vm-tools/services/plugins/desktopEvents/sessionMgr.c
new file mode 100644 (file)
index 0000000..b58e7ae
--- /dev/null
@@ -0,0 +1,459 @@
+/*********************************************************
+ * Copyright (C) 2011 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
+ *
+ *********************************************************/
+
+/**
+ * @file sessionMgr.c
+ *
+ * Support for X session management using the, well, X Session Management
+ * Library (libSM) with a little help from the Inter-Client Exchange Library
+ * (libICE).  This allows vmusr to receive X session lifecycle events and clean
+ * up appropriately upon session termination.  For now, cleanup upon shutdown
+ * is the only activity we're interested in.
+ *
+ * A custom event source is used to bind libICE connections with GLib's main
+ * loop and dispatch messages.  As this is the first (and hopefully only)
+ * libICE client, all ICE interaction is handled here (as opposed to in a
+ * provider application).
+ *
+ * This should work with any session manager implementing XSMP, covering all
+ * of our supported desktop environments.
+ *
+ * @todo Scrutinize libICE error handling.  I/O errors should be handled here,
+ *       but errors handled by libICE's default may exit().
+ */
+
+
+/* Include first.  Sets G_LOG_DOMAIN. */
+#include "desktopEventsInt.h"
+#include "vmware.h"
+
+#include <errno.h>
+#include <glib.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <X11/SM/SMlib.h>
+#include <X11/ICE/ICElib.h>
+
+
+/*
+ * Identifier used for SessionMgr private data in DesktopEvents private hash
+ * table.
+ */
+#define DE_FEATURE_KEY  "sessionMgr"
+
+
+/*
+ * libICE integration declarations.
+ */
+
+
+typedef struct {
+   GSource *iceSource;
+   IceConn  iceCnx;
+} ICEWatchCtx;
+
+static gboolean ICEDispatch(GIOChannel *chn, GIOCondition cond, gpointer cbData);
+static void ICEIOErrorHandler(IceConn iceCnx);
+static void ICEWatch(IceConn iceCnx, IcePointer clientData, Bool opening,
+                     IcePointer *watchData);
+
+
+/*
+ * libSM callbacks.
+ */
+
+
+static void SMDieCb(SmcConn smcCnx, SmPointer cbData);
+static void SMSaveYourselfCb(SmcConn smcCnx, SmPointer cbData, int saveType,
+                             Bool shutdown, int interactStyle, Bool fast);
+static void SMSaveCompleteCb(SmcConn smcCnx, SmPointer cbData);
+static void SMShutdownCancelledCb(SmcConn smcCnx, SmPointer cbData);
+
+
+/*
+ ******************************************************************************
+ * SessionMgr_Init --                                                    */ /**
+ *
+ * Register custom ICE connection source and sign up with the session manager.
+ *
+ * @param[in] ctx   Application context.
+ * @param[in] pdata Plugin data.
+ *
+ * @retval TRUE Always "succeeds".  Session management is optional.
+ *
+ ******************************************************************************
+ */
+
+gboolean
+SessionMgr_Init(ToolsAppCtx *ctx,
+                ToolsPluginData *pdata)
+{
+   SmcCallbacks smCallbacks;
+   unsigned long cbMask =
+        SmcSaveYourselfProcMask
+      | SmcDieProcMask
+      | SmcSaveCompleteProcMask
+      | SmcShutdownCancelledProcMask;
+   SmcConn smcCnx;
+   char errorBuf[128];
+   char *clientID = NULL;
+
+   IceSetIOErrorHandler(ICEIOErrorHandler);
+   IceAddConnectionWatch(ICEWatch, pdata);
+
+   memset(&smCallbacks, 0, sizeof smCallbacks);
+   smCallbacks.save_yourself.callback = &SMSaveYourselfCb;
+   smCallbacks.save_complete.callback = &SMSaveCompleteCb;
+   smCallbacks.shutdown_cancelled.callback = &SMShutdownCancelledCb;
+   smCallbacks.die.callback = &SMDieCb;
+   smCallbacks.die.client_data = pdata;
+
+   smcCnx =
+      SmcOpenConnection(NULL, NULL, SmProtoMajor, SmProtoMinor, cbMask,
+                        &smCallbacks, NULL, &clientID, sizeof errorBuf, errorBuf);
+   if (smcCnx != NULL) {
+      g_hash_table_insert(pdata->_private, DE_FEATURE_KEY, smcCnx);
+      g_debug("Registered with session manager as %s\n", clientID);
+      free(clientID);
+   } else {
+      g_message("Failed to register with session manager.\n");
+      g_message("SmcOpenConnection: %s\n", errorBuf);
+      IceRemoveConnectionWatch(ICEWatch, pdata);
+   }
+
+   return TRUE;
+}
+
+
+/*
+ ******************************************************************************
+ * SessionMgr_Shutdown --                                                */ /**
+ *
+ * Unregisters signal callbacks and disappears from the message bus.
+ *
+ * @param[in] ctx   Application context.
+ * @param[in] pdata Plugin data.
+ *
+ ******************************************************************************
+ */
+
+void
+SessionMgr_Shutdown(ToolsAppCtx *ctx,
+                    ToolsPluginData *pdata)
+{
+   GHashTable *desktopData = pdata->_private;
+   SmcConn smcCnx = g_hash_table_lookup(desktopData, DE_FEATURE_KEY);
+   if (smcCnx != NULL) {
+      SmcCloseConnection(smcCnx, 0, NULL);
+      IceRemoveConnectionWatch(ICEWatch, pdata);
+      g_hash_table_remove(desktopData, DE_FEATURE_KEY);
+   }
+}
+
+
+/*
+ ******************************************************************************
+ * BEGIN libICE stuff.
+ */
+
+
+/*
+ * A note on source reference counting:
+ *
+ * There are two entities that maintain references on ICEWatchCtx's GSource.
+ *    - GLib's GMainContext, and
+ *    - libICE's IceConn (via ICEWatch's watchData).
+ *
+ * A source's initial reference created in ICEWatch may be considered as
+ * transferred to libICE until ICEWatch is again called upon connection close.
+ * The second reference comes from attaching the source to the GLib main loop.
+ */
+
+
+/*
+ ******************************************************************************
+ * ICEIOErrorHandler --                                                  */ /**
+ *
+ * Handler for libICE I/O errors.
+ *
+ * Does nothing but replaces libICE's default handler which would've caused us
+ * to exit.
+ *
+ * @param[in]  iceCnx    Opaque ICE connection descriptor.
+ *
+ ******************************************************************************
+ */
+
+static void
+ICEIOErrorHandler(IceConn iceCnx)
+{
+   g_message("%s: %s\n", __func__, strerror(errno));
+}
+
+
+/*
+ ******************************************************************************
+ * ICEDispatch --                                                        */ /**
+ *
+ * GSource dispatch routine.  Calls IceProcessMessages on the ICE connection.
+ *
+ * @param[in]  chn      GIOChannel event source
+ * @param[in]  cond     condition satisfied (ignored)
+ * @param[in]  cbData   (ICEWatchCtx*) Channel context.
+ *
+ * @retval TRUE  Underlying iceCnx is still valid, so do not kill this source.
+ * @retval FALSE Underlying iceCnx was destroyed, so remove this source.
+ *
+ ******************************************************************************
+ */
+
+static gboolean
+ICEDispatch(GIOChannel *chn,
+            GIOCondition cond,
+            gpointer cbData)
+{
+   IceProcessMessagesStatus status;
+   ICEWatchCtx *watchCtx = cbData;
+   ASSERT(watchCtx);
+
+   /*
+    * We ignore the error conditions here and let IceProcessMessages return
+    * an IceProcessMessagesIOError, resulting in a single, shared error code
+    * path.
+    */
+
+   status = IceProcessMessages(watchCtx->iceCnx, NULL, NULL);
+   switch (status) {
+   case IceProcessMessagesSuccess:
+      return TRUE;
+   case IceProcessMessagesIOError:
+      IceCloseConnection(watchCtx->iceCnx);    // Let ICEWatch kill the source.
+      return TRUE;
+   case IceProcessMessagesConnectionClosed:
+      /*
+       * iceCnx was invalidated, so we won't see another call to ICEWatch,
+       * so we'll return FALSE and let GLib destroy the source for us.
+       */
+      watchCtx->iceCnx = NULL;
+      g_source_unref(watchCtx->iceSource);
+      return FALSE;
+   }
+
+   NOT_REACHED();
+}
+
+
+/*
+ ******************************************************************************
+ * ICEWatch --                                                           */ /**
+ *
+ * libICE connection watching callback.
+ *
+ * Creates or removes GLib event sources upon ICE connection creation/
+ * destruction.
+ *
+ * From ICElib.xml:
+ *    "If opening is True the client should set the *watch_data pointer to
+ *    any data it may need to save until the connection is closed and the
+ *    watch procedure is invoked again with opening set to False."
+ *
+ * @param[in]  iceCnx    Opaque ICE connection descriptor.
+ * @parma[in]  cbData    (ToolsPluginData*) plugin data
+ * @param[in]  opening   True if creating a connection, False if closing.
+ * @param[in]  watchData See above.  New source will be stored here.
+ *
+ ******************************************************************************
+ */
+
+static void
+ICEWatch(IceConn iceCnx,
+         IcePointer cbData,
+         Bool opening,
+         IcePointer *watchData)
+{
+   ToolsPluginData *pdata = cbData;
+   ToolsAppCtx *ctx;
+
+   ctx = g_hash_table_lookup(pdata->_private, DE_PRIVATE_CTX);
+   ASSERT(ctx);
+
+   if (opening) {
+      GIOChannel *iceChannel;
+      GSource *iceSource;
+      ICEWatchCtx *watchCtx;
+      GError *error = NULL;
+
+      iceChannel = g_io_channel_unix_new(IceConnectionNumber(iceCnx));
+      if (g_io_channel_set_encoding(iceChannel, NULL, &error) != G_IO_STATUS_NORMAL) {
+         g_warning("%s: g_io_channel_set_encoding: %s\n", __func__, error->message);
+         g_clear_error(&error);
+         g_io_channel_unref(iceChannel);
+         return;
+      }
+      g_io_channel_set_buffered(iceChannel, FALSE);
+
+      iceSource = g_io_create_watch(iceChannel, G_IO_IN|G_IO_HUP|G_IO_ERR);
+      g_io_channel_unref(iceChannel);   // Ownership transferred to iceSource.
+
+      watchCtx = g_new(ICEWatchCtx, 1);
+      watchCtx->iceSource = iceSource;
+      watchCtx->iceCnx = iceCnx;
+      *watchData = watchCtx;
+
+      VMTOOLSAPP_ATTACH_SOURCE(ctx, iceSource, &ICEDispatch, watchCtx, NULL);
+   } else {
+      ICEWatchCtx *watchCtx = *watchData;
+      if (watchCtx) {
+         watchCtx->iceCnx = NULL;
+         if (watchCtx->iceSource) {
+            g_source_destroy(watchCtx->iceSource);
+            g_source_unref(watchCtx->iceSource);
+         }
+         g_free(watchCtx);
+         *watchData = NULL;
+      }
+   }
+}
+
+
+/*
+ * END libICE stuff.
+ ******************************************************************************
+ */
+
+
+/*
+ ******************************************************************************
+ * BEGIN libSM stuff.
+ */
+
+
+/*
+ ******************************************************************************
+ * SMDieCb --                                                            */ /**
+ *
+ * Callback for a XSM "Die" event.
+ *
+ * Instructs the main loop to quit, then acknowledges the session manager's
+ * request.
+ *
+ * @param[in]  smcCnx   Opaque XSM connection object.
+ * @param[in]  cbData   (ToolsPluginData*) Plugin data.
+ *
+ ******************************************************************************
+ */
+
+static void
+SMDieCb(SmcConn smcCnx,
+        SmPointer cbData)
+{
+   ToolsPluginData *pdata = cbData;
+   ToolsAppCtx *ctx = g_hash_table_lookup(pdata->_private, DE_PRIVATE_CTX);
+   ASSERT(ctx);
+
+   g_message("Session manager says our time is up.  Exiting.\n");
+   g_main_loop_quit(ctx->mainLoop);
+   SmcCloseConnection(smcCnx, 0, NULL);
+}
+
+
+/*
+ ******************************************************************************
+ * SMSaveYourselfCb --                                                   */ /**
+ *
+ * Callback for XSM "SaveYourself" event.
+ *
+ * This event is sent to all XSM clients either to checkpoint a session
+ * or in advance of a (cancellable) session  shutdown event.  If we needed
+ * time to persist state, now would be the time to do it.  Since we don't,
+ * however, this is nearly a no-op -- we only acknowledge the manager.
+ *
+ * @param[in]  smcCnx   Opaque XSM connection object.
+ * @param[in]  cbData   (ToolsPluginData*) Plugin data.
+ * @param[in]  saveType Refer to SMlib.xml.
+ * @param[in]  shutdown Checkpoint or shutdown?
+ * @param[in]  interactStyle May interact with user?
+ * @param[in]  fast     Shutdown as quickly as possible.
+ *
+ * @todo Consider whether it'd make sense to unregister capabilities and pause
+ *       other plugins if shutdown == True until either we receive a 'die' or
+ *       'shutdown cancelled' event.
+ *
+ ******************************************************************************
+ */
+
+static void
+SMSaveYourselfCb(SmcConn smcCnx,
+                 SmPointer cbData,
+                 int saveType,
+                 Bool shutdown,
+                 int interactStyle,
+                 Bool fast)
+{
+   SmcSaveYourselfDone(smcCnx, True);
+}
+
+
+/*
+ ******************************************************************************
+ * SMSaveCompleteCb --                                                   */ /**
+ *
+ * Callback for XSM "SaveComplete" event.
+ *
+ * State has been checkpointed.  Application may resume normal operations.
+ * Total no-op.
+ *
+ * @param[in]  smcCnx   Opaque XSM connection object.
+ * @param[in]  cbData   (ToolsPluginData*) Plugin data.
+ *
+ ******************************************************************************
+ */
+
+static void
+SMSaveCompleteCb(SmcConn smcCnx,
+                 SmPointer cbData)
+{
+}
+
+
+/*
+ ******************************************************************************
+ * SMShutdownCancelledCb --                                              */ /**
+ *
+ * Callback for XSM "ShutdownCancelled" event.
+ *
+ * User cancelled shutdown.  May resume normal operations.  Again, total no-op.
+ *
+ * @param[in]  smcCnx   Opaque XSM connection object.
+ * @param[in]  cbData   (ToolsPluginData*) Plugin data.
+ *
+ ******************************************************************************
+ */
+
+static void
+SMShutdownCancelledCb(SmcConn smcCnx,
+                      SmPointer cbData)
+{
+}
+
+
+/*
+ * END libSM stuff.
+ ******************************************************************************
+ */
index aa80c74bb1697fcf33fc4ee53a3b3d6e2fc3d946..2b36dec9f69bb64678bd0bde661f7c66138387de 100644 (file)
@@ -128,12 +128,14 @@ XIOError_Init(ToolsAppCtx *ctx,
  * Shutdown function, restores the original X I/O error handler.
  *
  * @param[in] ctx   Application context.
+ * @param[in] pdata Plugin data (unused).
  *
  ******************************************************************************
  */
 
 void
-XIOError_Shutdown(ToolsAppCtx *ctx)
+XIOError_Shutdown(ToolsAppCtx *ctx,
+                  ToolsPluginData *pdata)
 {
    XSetIOErrorHandler(gOrigHandler);
    gCtx = NULL;