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 \
################################################################################
SUBDIRS =
+if HAVE_X11
+ SUBDIRS += desktopEvents
+endif
SUBDIRS += guestInfo
SUBDIRS += hgfsServer
SUBDIRS += powerOps
--- /dev/null
+################################################################################
+### 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
+
--- /dev/null
+/*********************************************************
+ * 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_ */
+
--- /dev/null
+/*********************************************************
+ * 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, ®Data)) {
+ 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 ®Data;
+ }
+
+ /* Failed to initialize something, clean up and unload. */
+ DesktopEventsShutdown(ctx, ®Data);
+
+ /* 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;
+}
+
--- /dev/null
+/*********************************************************
+ * 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_ */
+
--- /dev/null
+/*********************************************************
+ * 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;
+}
+
--- /dev/null
+/*********************************************************
+ * 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;
+}
+
--- /dev/null
+/*********************************************************
+ * 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;
+}
+