From: VMware, Inc <> Date: Mon, 26 Jul 2010 19:18:45 +0000 (-0700) Subject: Add desktopEvents plugins to open-vm-tools. X-Git-Tag: 2010.07.25-280253~16 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c9fcdc3125c63c48a040b99be5b5fd4efe0bd642;p=thirdparty%2Fopen-vm-tools.git Add desktopEvents plugins to open-vm-tools. Signed-off-by: Marcelo Vanzin --- diff --git a/open-vm-tools/configure.ac b/open-vm-tools/configure.ac index d7dd098bb..b4c3ea991 100644 --- a/open-vm-tools/configure.ac +++ b/open-vm-tools/configure.ac @@ -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 \ diff --git a/open-vm-tools/services/plugins/Makefile.am b/open-vm-tools/services/plugins/Makefile.am index af57da989..b029576e1 100644 --- a/open-vm-tools/services/plugins/Makefile.am +++ b/open-vm-tools/services/plugins/Makefile.am @@ -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 index 000000000..581bee954 --- /dev/null +++ b/open-vm-tools/services/plugins/desktopEvents/Makefile.am @@ -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 index 000000000..1eb8a238c --- /dev/null +++ b/open-vm-tools/services/plugins/desktopEvents/deFeatures.h @@ -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 index 000000000..233810115 --- /dev/null +++ b/open-vm-tools/services/plugins/desktopEvents/desktopEvents.c @@ -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, ®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; +} + diff --git a/open-vm-tools/services/plugins/desktopEvents/desktopEventsInt.h b/open-vm-tools/services/plugins/desktopEvents/desktopEventsInt.h new file mode 100644 index 000000000..453a07ae6 --- /dev/null +++ b/open-vm-tools/services/plugins/desktopEvents/desktopEventsInt.h @@ -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 index 000000000..e293c4901 --- /dev/null +++ b/open-vm-tools/services/plugins/desktopEvents/reload.c @@ -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 +#include +#include + +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 index 000000000..24fb12351 --- /dev/null +++ b/open-vm-tools/services/plugins/desktopEvents/x11Lock.c @@ -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 +#include +#include +#include +#include + +#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 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 index 000000000..aa80c74bb --- /dev/null +++ b/open-vm-tools/services/plugins/desktopEvents/xioError.c @@ -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 +#include +#include +#include + + +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; +} +