]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
Add desktopEvents plugins to open-vm-tools.
authorVMware, Inc <>
Mon, 26 Jul 2010 19:18:45 +0000 (12:18 -0700)
committerMarcelo Vanzin <mvanzin@vmware.com>
Mon, 26 Jul 2010 19:18:45 +0000 (12:18 -0700)
Signed-off-by: Marcelo Vanzin <mvanzin@vmware.com>
open-vm-tools/configure.ac
open-vm-tools/services/plugins/Makefile.am
open-vm-tools/services/plugins/desktopEvents/Makefile.am [new file with mode: 0644]
open-vm-tools/services/plugins/desktopEvents/deFeatures.h [new file with mode: 0644]
open-vm-tools/services/plugins/desktopEvents/desktopEvents.c [new file with mode: 0644]
open-vm-tools/services/plugins/desktopEvents/desktopEventsInt.h [new file with mode: 0644]
open-vm-tools/services/plugins/desktopEvents/reload.c [new file with mode: 0644]
open-vm-tools/services/plugins/desktopEvents/x11Lock.c [new file with mode: 0644]
open-vm-tools/services/plugins/desktopEvents/xioError.c [new file with mode: 0644]

index d7dd098bb40cfb6fcdf4c50c34c55596d9123f24..b4c3ea991d0d71738c6ce67d0c4307d216600d4a 100644 (file)
@@ -1269,6 +1269,7 @@ AC_CONFIG_FILES([                      \
    services/Makefile                   \
    services/vmtoolsd/Makefile          \
    services/plugins/Makefile           \
+   services/plugins/desktopEvents/Makefile \
    services/plugins/guestInfo/Makefile \
    services/plugins/guestInfo/getlib/Makefile \
    services/plugins/hgfsServer/Makefile \
index af57da989c26e57e0f5efc63672c1f243ce95e66..b029576e15147b68d02c5408b8ac5996ab1ce15f 100644 (file)
@@ -16,6 +16,9 @@
 ################################################################################
 
 SUBDIRS =
+if HAVE_X11
+   SUBDIRS += desktopEvents
+endif
 SUBDIRS += guestInfo
 SUBDIRS += hgfsServer
 SUBDIRS += powerOps
diff --git a/open-vm-tools/services/plugins/desktopEvents/Makefile.am b/open-vm-tools/services/plugins/desktopEvents/Makefile.am
new file mode 100644 (file)
index 0000000..581bee9
--- /dev/null
@@ -0,0 +1,38 @@
+################################################################################
+### Copyright 2010 VMware, Inc.  All rights reserved.
+###
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of version 2 of the GNU General Public License as
+### published by the Free Software Foundation.
+###
+### 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
+### GNU General Public License for more details.
+###
+### You should have received a copy of the GNU 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
+################################################################################
+
+plugindir = @VMUSR_PLUGIN_INSTALLDIR@
+plugin_LTLIBRARIES = libdesktopEvents.la
+
+libdesktopEvents_la_CPPFLAGS =
+libdesktopEvents_la_CPPFLAGS += @GTK_CPPFLAGS@
+libdesktopEvents_la_CPPFLAGS += @PLUGIN_CPPFLAGS@
+
+libdesktopEvents_la_LDFLAGS =
+libdesktopEvents_la_LDFLAGS += @PLUGIN_LDFLAGS@
+
+libdesktopEvents_la_LIBADD =
+libdesktopEvents_la_LIBADD += @COMMON_XLIBS@
+libdesktopEvents_la_LIBADD += @GTK_LIBS@
+libdesktopEvents_la_LIBADD += @VMTOOLS_LIBS@
+
+libdesktopEvents_la_SOURCES =
+libdesktopEvents_la_SOURCES += desktopEvents.c
+libdesktopEvents_la_SOURCES += reload.c
+libdesktopEvents_la_SOURCES += x11Lock.c
+libdesktopEvents_la_SOURCES += xioError.c
+
diff --git a/open-vm-tools/services/plugins/desktopEvents/deFeatures.h b/open-vm-tools/services/plugins/desktopEvents/deFeatures.h
new file mode 100644 (file)
index 0000000..1eb8a23
--- /dev/null
@@ -0,0 +1,62 @@
+/*********************************************************
+ * Copyright (C) 2010 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.
+ *
+ *********************************************************/
+
+#ifndef _DEFEATURES_H_
+#define _DEFEATURES_H_
+
+/**
+ * @file deFeatures.h
+ *
+ * X11-specific featurs of the plugin. Don't include this file directly -
+ * include desktopEventsInt.h instead.
+ */
+
+#define VMUSER_TITLE    "vmware-user"
+
+void
+Reload_Do(void);
+
+gboolean
+Reload_Init(ToolsAppCtx *ctx,
+            ToolsPluginData *pdata);
+
+void
+Reload_Shutdown(ToolsAppCtx *ctx);
+
+gboolean
+X11Lock_Init(ToolsAppCtx *ctx,
+             ToolsPluginData *pdata);
+
+gboolean
+XIOError_Init(ToolsAppCtx *ctx,
+              ToolsPluginData *pdata);
+
+void
+XIOError_Shutdown(ToolsAppCtx *ctx);
+
+
+#if defined(DE_MAIN)
+static DesktopEventFuncs gFeatures[] = {
+   { X11Lock_Init,         NULL,                      FALSE },
+   { Reload_Init,          Reload_Shutdown,           FALSE },
+   { 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
new file mode 100644 (file)
index 0000000..2338101
--- /dev/null
@@ -0,0 +1,133 @@
+/*********************************************************
+ * Copyright (C) 2010 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 desktopEvents.c
+ *
+ * Entry point for the desktop events plugin. Initializes all the individual
+ * features of the plugin.
+ */
+
+/*
+ * DE_MAIN enables the definition of the list of features of this plugin
+ * (gFeatures) defined in the platform-specific "deFeatures.h" files.
+ */
+#define DE_MAIN
+#include "vmware.h"
+#include "desktopEventsInt.h"
+
+/*
+ ******************************************************************************
+ * DesktopEventsShutdown --                                             */ /**
+ *
+ * Description of DesktopEventsShutdown.
+ *
+ * Calls the shutdown function of the available features.
+ *
+ * @param[in]  ctx      The application context.
+ * @param[in]  plugin   Unused.
+ *
+ * @return TRUE.
+ *
+ ******************************************************************************
+ */
+
+static gboolean
+DesktopEventsShutdown(ToolsAppCtx *ctx,
+                      ToolsPluginData *plugin)
+{
+   size_t i;
+
+   for (i = 0; i < ARRAYSIZE(gFeatures); i++) {
+      DesktopEventFuncs *f = &gFeatures[i];
+      if (f->initialized && f->shutdownFn != NULL) {
+         f->shutdownFn(ctx);
+      }
+   }
+
+   return TRUE;
+}
+
+
+/*
+ ******************************************************************************
+ * ToolsOnLoad --                                                       */ /**
+ *
+ * Returns the registration data for the plugin.
+ *
+ * @param[in]  ctx   The application context.
+ *
+ * @return The registration data.
+ *
+ ******************************************************************************
+ */
+
+TOOLS_MODULE_EXPORT ToolsPluginData *
+ToolsOnLoad(ToolsAppCtx *ctx)
+{
+   static ToolsPluginData regData = {
+      "desktopEvents",
+      NULL,
+      NULL
+   };
+
+   size_t i;
+
+#if defined(_WIN32)
+   g_return_val_if_fail(gPluginHandle != NULL, NULL);
+#endif
+
+   regData.regs = g_array_new(FALSE, TRUE, sizeof (ToolsAppReg));
+   for (i = 0; i < ARRAYSIZE(gFeatures); i++) {
+      DesktopEventFuncs *f = &gFeatures[i];
+      if (!f->initFn(ctx, &regData)) {
+         break;
+      }
+      f->initialized = TRUE;
+   }
+
+   /*
+    * Register the shutdown callback and return if all features were
+    * initialized successfully.
+    */
+   if (i == ARRAYSIZE(gFeatures)) {
+      ToolsPluginSignalCb sigs[] = {
+         { TOOLS_CORE_SIG_SHUTDOWN, DesktopEventsShutdown, NULL }
+      };
+      ToolsAppReg regs[] = {
+         { TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) },
+      };
+      g_array_append_vals(regData.regs, regs, ARRAYSIZE(regs));
+      return &regData;
+   }
+
+   /* Failed to initialize something, clean up and unload. */
+   DesktopEventsShutdown(ctx, &regData);
+
+   /* Cleanup regData to make sure memory is freed. */
+   for (i = 0; i < regData.regs->len; i++) {
+      ToolsAppReg *reg = &g_array_index(regData.regs, ToolsAppReg, i);
+      if (reg->data != NULL) {
+         g_array_free(reg->data, TRUE);
+      }
+   }
+   g_array_free(regData.regs, TRUE);
+
+   return NULL;
+}
+
diff --git a/open-vm-tools/services/plugins/desktopEvents/desktopEventsInt.h b/open-vm-tools/services/plugins/desktopEvents/desktopEventsInt.h
new file mode 100644 (file)
index 0000000..453a07a
--- /dev/null
@@ -0,0 +1,45 @@
+/*********************************************************
+ * Copyright (C) 2009 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.
+ *
+ *********************************************************/
+
+#ifndef _DESKTOPEVENTSINT_H_
+#define _DESKTOPEVENTSINT_H_
+
+/**
+ * @file destopEventsInt.h
+ *
+ * Internal plugin definitions.
+ */
+
+#define VMW_TEXT_DOMAIN "desktopEvents"
+#define G_LOG_DOMAIN    VMW_TEXT_DOMAIN
+#include "vmware/tools/plugin.h"
+
+typedef struct DesktopEventFuncs {
+   gboolean (*initFn)(ToolsAppCtx *, ToolsPluginData *);
+   void     (*shutdownFn)(ToolsAppCtx *ctx);
+   gboolean initialized;
+} DesktopEventFuncs;
+
+/*
+ * This platform-specific file defines the list of features available
+ * for the current platform being built.
+ */
+#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
new file mode 100644 (file)
index 0000000..e293c49
--- /dev/null
@@ -0,0 +1,125 @@
+/*********************************************************
+ * Copyright (C) 2010 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 reload.c
+ *
+ * Code to respond to SIGUSR2 and restart the vmtoolsd instance.
+ */
+
+#include "desktopEventsInt.h"
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static GSource *gReloadSrc;
+
+
+/*
+ ******************************************************************************
+ * ReloadSelf --                                                        */ /**
+ *
+ * Signal handler for SIGUSR2. Stops the RPC channel and reloads the vmusr
+ * instance.
+ *
+ * @param[in] info    Unused.
+ * @param[in] data    The application context.
+ *
+ * @return FALSE.
+ *
+ ******************************************************************************
+ */
+
+static gboolean
+ReloadSelf(const siginfo_t *info,
+           gpointer data)
+{
+   ToolsAppCtx *ctx = data;
+   if (ctx->rpc != NULL) {
+      RpcChannel_Stop(ctx->rpc);
+   }
+   Reload_Do();
+   return FALSE;
+}
+
+
+/*
+ ******************************************************************************
+ * Reload_Do --                                                         */ /**
+ *
+ * Re-launch vmware-user by attempting to execute VMUSER_TITLE ('vmware-user'),
+ * relying on the user's search path.
+ *
+ * On success, vmware-user is relaunched in our stead.  On failure, we exit with
+ * EXIT_FAILURE.
+ *
+ ******************************************************************************
+ */
+
+void
+Reload_Do(void)
+{
+   g_debug("Reloading the vmusr instance.");
+   execlp(VMUSER_TITLE, VMUSER_TITLE, NULL);
+   _exit(EXIT_FAILURE);
+}
+
+
+/*
+ ******************************************************************************
+ * Reload_Init --                                                       */ /**
+ *
+ * Registers a signal handler for SIGUSR2 that reloads the container.
+ *
+ * @param[in] ctx       Application context.
+ * @param[in] pdata     Registration data.
+ *
+ * @return TRUE.
+ *
+ ******************************************************************************
+ */
+
+gboolean
+Reload_Init(ToolsAppCtx *ctx,
+            ToolsPluginData *pdata)
+{
+   gReloadSrc = VMTools_NewSignalSource(SIGUSR2);
+   VMTOOLSAPP_ATTACH_SOURCE(ctx, gReloadSrc, ReloadSelf, ctx, NULL);
+   return TRUE;
+}
+
+
+/*
+ ******************************************************************************
+ * Reload_Shutdown --                                                   */ /**
+ *
+ * Unregisters the SIGUSR2 signal handler.
+ *
+ * @param[in] ctx   Application context.
+ *
+ ******************************************************************************
+ */
+
+void
+Reload_Shutdown(ToolsAppCtx *ctx)
+{
+   g_source_destroy(gReloadSrc);
+   g_source_unref(gReloadSrc);
+   gReloadSrc = NULL;
+}
+
diff --git a/open-vm-tools/services/plugins/desktopEvents/x11Lock.c b/open-vm-tools/services/plugins/desktopEvents/x11Lock.c
new file mode 100644 (file)
index 0000000..24fb123
--- /dev/null
@@ -0,0 +1,378 @@
+/*********************************************************
+ * Copyright (C) 2010 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 x11lock.c
+ *
+ * Sets up an X11 lock atom and check it to avoid multiple running instances
+ * of vmusr.
+ */
+
+#include "desktopEventsInt.h"
+#include "vmware.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+#include <X11/Xlib.h>
+
+#define LOCK_ATOM_NAME  "vmware-user-lock"
+
+
+/*
+ ******************************************************************************
+ * InitGroupLeader --                                                   */ /**
+ *
+ * This routine sets a few properties related to our main window created
+ * by {gdk,gtk}_init.  Specifically this routine sets the window title,
+ * sets the override_redirect X11 property, and reparents it to the root
+ * window,
+ *
+ * In addition, this routine will return Xlib handles for the following
+ * objects:
+ *   - Main or group leader window
+ *   - Display's root window
+ *
+ * As a result of this function:
+ *   - groupLeader will have a title of VMUSER_TITLE.
+ *   - groupLeader, if not already directly parented by the root, will be.
+ *
+ *   - dpy will point to our default display (ex: $DISPLAY).
+ *   - groupLeader will point to the window created by gtk_init().
+ *   - rootWindow will point to the root window on $DISPLAY.
+ *
+ * @param[out] groupLeader    Group leader window.
+ * @param[out] rootWindow     Root window.
+ *
+ * @return TRUE on success, FALSE on failure.
+ *
+ ******************************************************************************
+ */
+
+static gboolean
+InitGroupLeader(Window *groupLeader,
+                Window *rootWindow)
+{
+   Window myGroupLeader;
+   Window myRootWindow;
+   XSetWindowAttributes attr;
+   GdkDisplay *gdkDisplay;
+   GdkWindow *gdkLeader;
+
+   attr.override_redirect = True;
+
+   ASSERT(groupLeader);
+   ASSERT(rootWindow);
+
+   gdkDisplay = gdk_display_get_default();
+   gdkLeader = gdk_display_get_default_group(gdkDisplay);
+   myGroupLeader = GDK_WINDOW_XWINDOW(gdkLeader);
+   myRootWindow = GDK_ROOT_WINDOW();
+
+   ASSERT(myGroupLeader);
+   ASSERT(myRootWindow);
+
+   /* XXX: With g_set_prgname() being called, this can probably go away. */
+   XStoreName(GDK_DISPLAY(), myGroupLeader, VMUSER_TITLE);
+
+   /*
+    * Sanity check:  Set the override redirect property on our group leader
+    * window (not default), then re-parent it to the root window (default).
+    * This makes sure that (a) a window manager can't re-parent our window,
+    * and (b) that we remain a top-level window.
+    */
+   XChangeWindowAttributes(GDK_DISPLAY(), myGroupLeader, CWOverrideRedirect,
+                           &attr);
+   XReparentWindow(GDK_DISPLAY(), myGroupLeader, myRootWindow, 10, 10);
+   XSync(GDK_DISPLAY(), FALSE);
+
+   *groupLeader = myGroupLeader;
+   *rootWindow = myRootWindow;
+
+   return TRUE;
+}
+
+
+/*
+ ******************************************************************************
+ * QueryX11Lock --                                                      */ /**
+ *
+ * This is just a wrapper around XGetWindowProperty which queries the
+ * window described by <dpy,w> for the property described by lockAtom.
+ *
+ * @param[in] dpy          X11 display to query
+ * @param[in] w            Window to query
+ * @param[in] lockAtom     Atom used for locking
+ *
+ * @return TRUE if property defined by parameters exists; FALSE otherwise.
+ *
+ ******************************************************************************
+ */
+
+static gboolean
+QueryX11Lock(Display *dpy,
+             Window w,
+             Atom lockAtom)
+{
+   Atom ptype;                  // returned property type
+   int pfmt;                    // returned property format
+   unsigned long np;            // returned # of properties
+   unsigned long remaining;     // amount of data remaining in property
+   unsigned char *data = NULL;
+
+   if (XGetWindowProperty(dpy, w, lockAtom, 0, 1, False, lockAtom,
+                          &ptype, &pfmt, &np, &remaining, &data) != Success) {
+      g_warning("%s: Unable to query window %lx for property %s\n", __func__, w,
+                LOCK_ATOM_NAME);
+      return FALSE;
+   }
+
+   /*
+    * Xlib is wacky.  If the following test is true, then our property
+    * didn't exist for the window in question.  As a result, `data' is
+    * unset, so don't worry about the lack of XFree(data) here.
+    */
+   if (ptype == None) {
+      return FALSE;
+   }
+
+   /*
+    * We care only about the existence of the property, not its value.
+    */
+   XFree(data);
+
+   return TRUE;
+}
+
+
+/*
+ ******************************************************************************
+ * AcquireDisplayLock --                                                */ /**
+ *
+ * This function "locks" the display against being "claimed" by another
+ * instance of vmware-user.  It will succeed if we're the first/only
+ * instance of vmware-user, and fail otherwise.
+ *
+ * NB: This routine must be called -after- gtk_init().
+ *
+ * Vmware-user enjoys per-display exclusivity using the following algorithm:
+ *
+ *   1.  Grab X server.  (I.e., get exclusive access.)
+ *   2.  Search for top-level X windows meeting the following criteria:
+ *       a.  named "vmware-user"
+ *       b.  has the property "vmware-user-lock" set.
+ *   3a. If any such windows described above found, then another vmware-user
+ *       process is attached to this display, so we consider the display
+ *       locked.
+ *   3b. Else we're the only one.  Set the "vmware-user-lock" property on
+ *       our top-level window.
+ *   4.  Ungrab the X server.
+ *
+ * The first time this routine is ever called during the lifetime of an X
+ * session, a new X11 Atom, "vmware-user-lock" is created for the lifetime
+ * of the X server.
+ *
+ * The "vmware-user-lock" property may be set on this process's group leader
+ * window.
+ *
+ * @return TRUE if "lock" acquired (i.e., we're the first/only vmware-user
+ *         process); otherwise FALSE.
+ *
+ ******************************************************************************
+ */
+
+static gboolean
+AcquireDisplayLock(void)
+{
+   Display *defaultDisplay;     // Current default X11 display.
+   Window rootWindow;           // Root window of defaultDisplay; used as root node
+                                // passed to XQueryTree().
+   Window groupLeader;          // Our instance's window group leader.  This is
+                                // implicitly created by gtk_init().
+
+   Window *children = NULL;     // Array of windows returned by XQueryTree().
+   unsigned int nchildren;      // Length of children.
+
+   Window dummy1, dummy2;       // Throwaway window IDs for XQueryTree().
+   Atom lockAtom;               // Refers to the "vmware-user-lock" X11 Atom.
+
+   unsigned int index;
+   Bool alreadyLocked = FALSE;  // Set to TRUE if we discover lock is held.
+   Bool retval = FALSE;
+
+   defaultDisplay = GDK_DISPLAY();
+
+   /*
+    * Reset some of our main window's settings & fetch Xlib handles for
+    * the GDK group leader and root windows.
+    */
+   if (InitGroupLeader(&groupLeader, &rootWindow) == FALSE) {
+      g_warning("%s: unable to initialize main window.\n", __func__);
+      return FALSE;
+   }
+
+   /*
+    * Look up the lock atom, creating it if it doesn't already exist.
+    */
+   lockAtom = XInternAtom(defaultDisplay, LOCK_ATOM_NAME, False);
+   if (lockAtom == None) {
+      g_warning("%s: unable to create X11 atom: " LOCK_ATOM_NAME "\n", __func__);
+      return FALSE;
+   }
+
+   /*
+    * Okay, so at this point the following is done:
+    *
+    *   1.  Our top-level / group leader window is a child of the display's
+    *       root window.
+    *   2.  The window manager can't get its hands on said window.
+    *   3.  We have a handle on the X11 atom which will be used to identify
+    *       the X11 property used as our lock.
+    */
+
+   g_debug("%s: Grabbing X server.\n", __func__);
+
+   /*
+    * Neither of these can fail, or at least not in the sense that they'd
+    * return an error.  Instead we'd likely see an X11 I/O error, tearing
+    * the connection down.
+    *
+    * XSync simply blocks until the XGrabServer request is acknowledged
+    * by the server.  It makes sure that we don't continue issuing requests,
+    * such as XQueryTree, until the server grants our "grab".
+    */
+   XGrabServer(defaultDisplay);
+   XSync(defaultDisplay, False);
+
+   /*
+    * WARNING:  At this point, we have grabbed the X server.  Consider the
+    * UI to be completely frozen.  Under -no- circumstances should we return
+    * without ungrabbing the server first.
+    */
+
+   if (XQueryTree(defaultDisplay, rootWindow, &dummy1, &dummy2, &children,
+                  &nchildren) == 0) {
+      g_warning("%s: XQueryTree failed\n", __func__);
+      goto out;
+   }
+
+   /*
+    * Iterate over array of top-level windows.  Search for those named
+    * vmware-user and with the property "vmware-user-lock" set.
+    *
+    * If any such windows are found, then another process has already
+    * claimed this X session.
+    */
+   for (index = 0; (index < nchildren) && !alreadyLocked; index++) {
+      char *name = NULL;
+
+      /* Skip unless window is named vmware-user. */
+      if ((XFetchName(defaultDisplay, children[index], &name) == 0) ||
+          (name == NULL) ||
+          strcmp(name, VMUSER_TITLE)) {
+         XFree(name);
+         continue;
+      }
+
+      /*
+       * Query the window for the "vmware-user-lock" property.
+       */
+      alreadyLocked = QueryX11Lock(defaultDisplay, children[index], lockAtom);
+      XFree(name);
+   }
+
+   /*
+    * Yay.  Lock isn't held, so go ahead and acquire it.
+    */
+   if (!alreadyLocked) {
+      unsigned char dummy[] = "1";
+      g_debug("%s: Setting property " LOCK_ATOM_NAME "\n", __func__);
+      /*
+       * NB: Current Xlib always returns one.  This may generate a -fatal- IO
+       * error, though.
+       */
+      XChangeProperty(defaultDisplay, groupLeader, lockAtom, lockAtom, 8,
+                      PropModeReplace, dummy, sizeof dummy);
+      retval = TRUE;
+   }
+
+out:
+   XUngrabServer(defaultDisplay);
+   XSync(defaultDisplay, False);
+   XFree(children);
+
+   return retval;
+}
+
+
+/*
+ ******************************************************************************
+ * X11Lock_Init --                                                      */ /**
+ *
+ * Initializes GTK, and sets up a lock atom to make sure only one vmusr
+ * instance is running.
+ *
+ * On error, this function will request that the application's main loop stop
+ * running.
+ *
+ * @param[in] ctx       Application context.
+ * @param[in] pdata     Registration data.
+ *
+ * @return TRUE on success, FALSE if another vmusr instance owns the display or
+ *         not running in the right container (vmusr).
+ *
+ ******************************************************************************
+ */
+
+gboolean
+X11Lock_Init(ToolsAppCtx *ctx,
+             ToolsPluginData *pdata)
+{
+   int argc = 0;
+   char *argv[] = { NULL, NULL };
+
+   if (strcmp(ctx->name, VMTOOLS_USER_SERVICE) != 0) {
+      VMTOOLSAPP_ERROR(ctx, EXIT_FAILURE);
+      return FALSE;
+   }
+
+   /*
+    * We depend on the window title when performing (primitive) vmware-user
+    * session detection, and unfortunately for us, GTK has a nasty habit of
+    * retitling toplevel windows.  That said, we can control GTK's default
+    * title by setting Glib's application or program name.
+    *
+    * XXX Consider using g_set_application_name("VMware User Agent") or
+    * similar.
+    */
+   g_set_prgname(VMUSER_TITLE);
+   argv[0] = VMUSER_TITLE;
+
+   /* XXX: is calling gtk_init() multiple times safe? */
+   gtk_init(&argc, (char ***) &argv);
+
+   if (!AcquireDisplayLock()) {
+      g_warning("Another instance of vmware-user already running. Exiting.\n");
+      VMTOOLSAPP_ERROR(ctx, EXIT_FAILURE);
+      return FALSE;
+   }
+
+   return TRUE;
+}
+
diff --git a/open-vm-tools/services/plugins/desktopEvents/xioError.c b/open-vm-tools/services/plugins/desktopEvents/xioError.c
new file mode 100644 (file)
index 0000000..aa80c74
--- /dev/null
@@ -0,0 +1,142 @@
+/*********************************************************
+ * Copyright (C) 2010 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 xioError.c
+ *
+ * Handles responding to X11 I/O errors.
+ */
+
+#include "desktopEventsInt.h"
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+
+
+static int gParentPid;
+static ToolsAppCtx *gCtx;
+static XIOErrorHandler gOrigHandler;
+
+/*
+ ******************************************************************************
+ * DEXIOErrorHandler --                                                 */ /**
+ *
+ * Handler for all X I/O errors. Xlib documentation says we should not
+ * return when handling I/O errors.
+ *
+ * @param[in] dpy    Unused.
+ *
+ * @return 1 (but doesn't really return).
+ *
+ ******************************************************************************
+ */
+
+static int
+DEXIOErrorHandler(Display *dpy)
+{
+   pid_t my_pid = getpid();
+
+   /*
+    * ProcMgr_ExecAsync() needs to fork off a child to handle watching the
+    * process being run.  When it dies, it will come through here, so we don't
+    * want to let it shut down the RPC channel.
+    */
+   if (my_pid == gParentPid) {
+      g_debug("%s", __func__);
+
+      /*
+       * XXX: the really correct thing to do here would be to properly stop all
+       * plugins so that capabilities are unset and all other "clean shutdown"
+       * tasks are performed. Unfortunately two things currently prevent that:
+       *
+       * . we can't rely on g_main_loop_quit() because we can't return from this
+       *   function (well, we can, but Xlib will exit() before vmtoolsd is able
+       *   to clean up things), so the main loop will never regain control off
+       *   the app.
+       *
+       * . we can't access the internal vmtoolsd functions that cleanly shuts
+       *   down plugins.
+       *
+       * So, right now, let's stick with just stopping the RPC channel so that
+       * the host is notified the application is gone. This may cause temporary
+       * issues with clients that only look at capabilities and not at the
+       * status of vmusr.
+       */
+      if (gCtx->rpc != NULL) {
+         RpcChannel_Stop(gCtx->rpc);
+      }
+      Reload_Do();
+      exit(EXIT_FAILURE);
+   } else {
+      /*
+       * _exit is used here so that any atexit() registered routines don't
+       * interfere with any resources shared with the parent.
+       */
+      g_debug("%s hit from forked() child", __func__);
+      _exit(EXIT_FAILURE);
+   }
+
+   return 1;
+}
+
+
+/*
+ ******************************************************************************
+ * XIOError_Init --                                                     */ /**
+ *
+ * Sets up an X11 I/O error callback that stops the daemon.
+ *
+ * @param[in] ctx       Application context.
+ * @param[in] pdata     Registration data.
+ *
+ * @return TRUE.
+ *
+ ******************************************************************************
+ */
+
+gboolean
+XIOError_Init(ToolsAppCtx *ctx,
+              ToolsPluginData *pdata)
+{
+   gCtx = ctx;
+   gParentPid = getpid();
+   gOrigHandler = XSetIOErrorHandler(DEXIOErrorHandler);
+   return TRUE;
+}
+
+
+/*
+ ******************************************************************************
+ * XIOError_Shutdown --                                                 */ /**
+ *
+ * Shutdown function, restores the original X I/O error handler.
+ *
+ * @param[in] ctx   Application context.
+ *
+ ******************************************************************************
+ */
+
+void
+XIOError_Shutdown(ToolsAppCtx *ctx)
+{
+   XSetIOErrorHandler(gOrigHandler);
+   gCtx = NULL;
+   gOrigHandler = NULL;
+}
+