From: VMware, Inc <> Date: Tue, 29 Mar 2011 20:07:51 +0000 (-0700) Subject: Un*x session management integration via desktopEvents X-Git-Tag: 2011.03.28-387002~50 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=cfa19829a5c5e72bd72c34b1aeb19d99c479d524;p=thirdparty%2Fopen-vm-tools.git Un*x session management integration via desktopEvents 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 --- diff --git a/open-vm-tools/configure.ac b/open-vm-tools/configure.ac index ef6cfc31b..0801b2d91 100644 --- a/open-vm-tools/configure.ac +++ b/open-vm-tools/configure.ac @@ -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]) diff --git a/open-vm-tools/services/plugins/desktopEvents/Makefile.am b/open-vm-tools/services/plugins/desktopEvents/Makefile.am index 581bee954..85d1673ad 100644 --- a/open-vm-tools/services/plugins/desktopEvents/Makefile.am +++ b/open-vm-tools/services/plugins/desktopEvents/Makefile.am @@ -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 diff --git a/open-vm-tools/services/plugins/desktopEvents/deFeatures.h b/open-vm-tools/services/plugins/desktopEvents/deFeatures.h index 1eb8a238c..9b858672b 100644 --- a/open-vm-tools/services/plugins/desktopEvents/deFeatures.h +++ b/open-vm-tools/services/plugins/desktopEvents/deFeatures.h @@ -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_ */ diff --git a/open-vm-tools/services/plugins/desktopEvents/desktopEvents.c b/open-vm-tools/services/plugins/desktopEvents/desktopEvents.c index 233810115..9ff73715f 100644 --- a/open-vm-tools/services/plugins/desktopEvents/desktopEvents.c +++ b/open-vm-tools/services/plugins/desktopEvents/desktopEvents.c @@ -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, ®Data)) { @@ -107,7 +119,7 @@ ToolsOnLoad(ToolsAppCtx *ctx) */ if (i == ARRAYSIZE(gFeatures)) { ToolsPluginSignalCb sigs[] = { - { TOOLS_CORE_SIG_SHUTDOWN, DesktopEventsShutdown, NULL } + { TOOLS_CORE_SIG_SHUTDOWN, DesktopEventsShutdown, ®Data } }; 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, ®Data); + DesktopEventsShutdown(NULL, ctx, ®Data); /* 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; } - diff --git a/open-vm-tools/services/plugins/desktopEvents/desktopEventsInt.h b/open-vm-tools/services/plugins/desktopEvents/desktopEventsInt.h index 453a07ae6..45e9fead3 100644 --- a/open-vm-tools/services/plugins/desktopEvents/desktopEventsInt.h +++ b/open-vm-tools/services/plugins/desktopEvents/desktopEventsInt.h @@ -31,10 +31,19 @@ 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_ */ - diff --git a/open-vm-tools/services/plugins/desktopEvents/reload.c b/open-vm-tools/services/plugins/desktopEvents/reload.c index e293c4901..67da62bdb 100644 --- a/open-vm-tools/services/plugins/desktopEvents/reload.c +++ b/open-vm-tools/services/plugins/desktopEvents/reload.c @@ -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 index 000000000..b58e7aec0 --- /dev/null +++ b/open-vm-tools/services/plugins/desktopEvents/sessionMgr.c @@ -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 +#include +#include +#include +#include +#include +#include + + +/* + * 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. + ****************************************************************************** + */ diff --git a/open-vm-tools/services/plugins/desktopEvents/xioError.c b/open-vm-tools/services/plugins/desktopEvents/xioError.c index aa80c74bb..2b36dec9f 100644 --- a/open-vm-tools/services/plugins/desktopEvents/xioError.c +++ b/open-vm-tools/services/plugins/desktopEvents/xioError.c @@ -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;