From: Marcelo Vanzin Date: Wed, 18 Feb 2009 22:16:55 +0000 (-0800) Subject: 2009.02.18-148847 X-Git-Tag: 2009.02.18-148847 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0cca30d49e85cd837819011ae06e09739b23a092;p=thirdparty%2Fopen-vm-tools.git 2009.02.18-148847 --- diff --git a/open-vm-tools/ChangeLog b/open-vm-tools/ChangeLog index 27847cde4..60bd98279 100644 --- a/open-vm-tools/ChangeLog +++ b/open-vm-tools/ChangeLog @@ -1,74 +1,206 @@ -2009-01-21 Marcelo Vanzin - * Resync with internal trunk (2009.01.19) +2009-02-18 Marcelo Vanzin + * Resync with internal trunk (2009.01.13) + + * configure.ac, m4/vmtools.m4: clean up a lot of the library detection code. + + * configure.ac: remove support for gtk+ 1.2 (code already depended on it in + any case); enforce need for glib 2.6.0 or later due to new code being + added; add detection for gtkmm; check for C++ compiler when it's needed; + reorder the include path to avoid clashing with system headers in some + situations. + + * guestd/foundryToolsDaemon.*, vmware-user/foundryToolsDaemon.*, + guestd/Makefile.am, vmware-user/Makefile.am: moved shared source files to + a new place to avoid duplication. + + * hgfsmounter/hgfsmounter.c: add support for Solaris. + + * lib/appUtil/appUtilX11.c: fix loading of icons when the name has a period. + + * lib/dnd/dndClipboard.c, lib/dnd/dndInt.h, lib/dnd/dndMsg.c, + lib/dnd/Makefile.am, lib/dndGuest/*, lib/include/copyPasteBase.h, + lib/include/copyPaste.hh, lib/include/copyPasteRpc.hh, + lib/include/copyPasteRpcV3.hh, lib/include/dndBase.h, + lib/include/dndClipboard.h, lib/include/dndFileContentsUtil.h, + lib/include/dndFileList.hh, lib/include/dnd.h, lib/include/dnd.hh, + lib/include/dndInt.h, lib/include/dndMsg.h, lib/include/dndRpc.hh, + lib/include/dndRpcV3.hh, lib/include/dndTransportGuestRpc.hh, + lib/include/dndTransport.hh, lib/include/libExport.hh, + vmware-user/copyPaste.cpp, vmware-user/copyPasteUI.{cpp,h}, + vmware-user/copyPasteV3.h, vmware-user/copyPasteWrapper.{cpp,h}, + vmware-user/dnd.cpp, vmware-user/Makefile.am, + vmware-user/vmware-user.{c,cpp}, vmware-user/vmwareuserInt.h, + vmware-user/stringxx/string.{cc,hh}, vmware-user/stringxx/ubstr_t.hh: add + support for new version of the DnD protocol. + + * lib/guestInfo/guestInfoPerfMonLinux.c: fix problem with overwriting flags + after GuestInfoMonitorReadMeminfo() was called. + + * lib/guestRpc/guestlibV3.x, lib/include/vmGuestLib.h, + libguestlib/vmGuestLib.c: add new host stats. + + * lib/guestRpc/Makefile.am: fix a few compatibility issues with non-GNU + versions of make. - * configure.ac: detect the presence of FUSE libraries. + * lib/hgfsServer/hgfsServer.c, lib/hgfsServer/hgfsServerInt.h, + lib/hgfsServer/hgfsServerLinux.c, */hgfsProto.h, + modules/freebsd/vmhgfs/vnops.c, modules/freebsd/vmhgfs/vnopscommon.{c,h}: + don't trust the local VFS layer, check the HGFS server to see if an + operation would succeed. + + * lib/include/rpcChannel.h, lib/rpcChannel/*: add new Guest RPC channel + abstraction library used by the new "core services" code. + + * lib/include/vmrpcdbg.h, tests/*: add test code from the "core services" + project. + + * lib/include/vmtoolsApp.h, lib/include/vmtoolsd_version.h, + services/vmtoolsd/*: add "vmtoolsd", the new service "shell" used in the + "core services" project. + + * lib/unity/unityPlatformX11Window.c: don't send initial "region" updates for + shaped windows. This works around an issue where resizing a shaped window + would not work as expected in Unity mode. + + * lib/wiper/wiperPosix.c: fix major number detection for disks on newer Linux + kernels. + + * libvmtools/Makefile.am: link more libraries needed by vmtoolsd and the new + plugins. + + * modules/linux/pvscsi/pvscsi.c, modules/linux/pvscsi/pvscsi_version.h: use + PCI-specific memory allocation functions and update driver version. - * configure.ac, Makefile.am: compile kernel modules for Solaris, and - vmblock-fuse module if FUSE is available. + * modules/linux/vmci/vmciKernelIf.c: disable queue pair support in the host + version of the driver on older Linux kernels. This doesn't affect the + guest driver. + + * modules/linux/vmci/vmci_queue_pair.h: implement MSG_PEEK support on + Linux driver. + + * modules/linux/vmhgfs/bdhandler.c, modules/linux/vmhgfs/compat_sched.h, + modules/linux/vmmemctl/os.c: fix issue with HGFS module interfering with + suspend / hibernation on newer Linux kernels (bug #2523263). + + * modules/linux/vmhgfs/compat_cred.h, modules/linux/vmhgfs/file.c, + modules/linux/vmhgfs/filesystem.c, modules/linux/vmhgfs/inode.c: changes + for compatibility with newer Linux kernels, where it's not possible to + change fsuid/capabilities directly anymore. + + * modules/linux/vmhgfs/compat_pagemap.h, modules/linux/vmhgfs/page.c: + fix warning, and compatibility changes for newer Linux kernels + (2.6.28.1 and newer; bug #2530616). + + * modules/linux/vmhgfs/inode.c: fix creation of symlinks (bug #2531303). + + * modules/linux/vmxnet/vmxnet.c: use PCI-specific memory allocation + functions. - * lib/ghIntegration/ghIntegrationX11.c: retrieved localized application - names. + * modules/linux/vmxnet/vmxnet.c, modules/linux/vmxnet3/vmxnet3.c: add option + to disable LRO. - * lib/guestInfo/guestInfo.c, lib/guestInfo/guestInfoPosix.c, - lib/include/hostinfo.h, lib/misc/hostinfo_misc.c, lib/misc/hostinfoPosic.c, - lib/vixTools/vixTools.c: refactoring to move code shared with other VMware - products to a common library. + * modules/linux/vsock/af_inet.{c,h}, modules/linux/vsock/util.{c,h}: add + MSG_PEEK support; remove ifdefs that were disabling support for queue + pairs on the host kernel module; fix compilation with certain versions + of gcc (bug #2531283). + + * modules/solaris/vmhgfs/*: move backdoor handling code to the HGFS driver; + this makes the user-level code to handle the driver (currently in + vmware-guestd) obsolete. + + * modules/solaris/vmxnet/*: add vmxnet driver for Solaris, under the CDDL. + + * services/plugins/*: add all currently available "core services" plugins. + The current set of plugins provide the functionality available in + vmware-guestd; there are a few plugins that replace functionality from + vmware-user, but not all features are ported yet (and vmtoolsd - with + vmware-user plugins - and vmware-user cannot run at the same time). + + * toolbox/toolboxAbout.c: fix the copyright character. + + * toolbox/toolbox-cmd.c: reword a few messages, fix typos. + + * toolbox/toolboxCmdInt.h, toolbox/toolboxcmd-record.c, + toolbox/toolbox-gtk.c, toolbox/toolboxRecord.c: remove the "record" + functionality from tools (due to internal request). + + * toolbox/toolboxInt.c, toolbox/toolboxScripts.c, toolbox/toolbox-scripts.c: + changes to use the new config file format used by the "core services" code. + + * */Makefile.am: make sure 'rm' and 'mv' are being used instead of $(RM) and + $(MV) (which are not defined by automake; bug #2492040). + +2009-01-21 Marcelo Vanzin + * Resync with internal trunk (2009.01.19) + + * configure.ac: detect the presence of FUSE libraries. + + * configure.ac, Makefile.am: compile kernel modules for Solaris, and + vmblock-fuse module if FUSE is available. + + * lib/ghIntegration/ghIntegrationX11.c: retrieved localized application + names. + + * lib/guestInfo/guestInfo.c, lib/guestInfo/guestInfoPosix.c, + lib/include/hostinfo.h, lib/misc/hostinfo_misc.c, lib/misc/hostinfoPosic.c, + lib/vixTools/vixTools.c: refactoring to move code shared with other VMware + products to a common library. - * lib/guestRpc/guestlibV3.x, lib/guestRpc/Makefile.am, - lib/include/vmGuestLib.h, libguestlib/*: add new iteration of the - "guestlib" protocol. This is a more extensible solution than the current - protocol, and should make it easier to add new information when needed. + * lib/guestRpc/guestlibV3.x, lib/guestRpc/Makefile.am, + lib/include/vmGuestLib.h, libguestlib/*: add new iteration of the + "guestlib" protocol. This is a more extensible solution than the current + protocol, and should make it easier to add new information when needed. - * lib/include/dynarray.h, lib/include/dynbuf.h, lib/misc/dynarray.c, - lib/misc/dynbuf.c: make some DynArray/DynBuf functions inline for speed. + * lib/include/dynarray.h, lib/include/dynbuf.h, lib/misc/dynarray.c, + lib/misc/dynbuf.c: make some DynArray/DynBuf functions inline for speed. - * lib/include/unityCommon.h: more documentation about the Unity protocol. + * lib/include/unityCommon.h: more documentation about the Unity protocol. - * lib/unity/unityPlatformX11.c: fix Unity<->guest desktop ID mappings. + * lib/unity/unityPlatformX11.c: fix Unity<->guest desktop ID mappings. - * libvmtools/vmtoolsLog.c: change the way log domains are configured; now - sub-domains inherit the default handler for the app if no handler is - specified. + * libvmtools/vmtoolsLog.c: change the way log domains are configured; now + sub-domains inherit the default handler for the app if no handler is + specified. - * modules/freebsd/vmhgfs/state.c, modules/freebsd/vmhgfs/state.h, - modules/freebsd/vmhgfs/vnopscommon.{c,h}: implement support for mmap on - the Mac OS driver, which allows running executables from an HGFS - share. The FreeBSD module still does not support this functionality. + * modules/freebsd/vmhgfs/state.c, modules/freebsd/vmhgfs/state.h, + modules/freebsd/vmhgfs/vnopscommon.{c,h}: implement support for mmap on + the Mac OS driver, which allows running executables from an HGFS + share. The FreeBSD module still does not support this functionality. - * modules/freebsd/vmhgfs/transport.{c,h}: refactoring for sharing structure - definitions. + * modules/freebsd/vmhgfs/transport.{c,h}: refactoring for sharing structure + definitions. - * modules/linux/pvscsi/pvscsi.c, modules/linux/vmblock/linux/module.c, - modules/linux/vmci/vmci_drv.c, modules/linux/hgfs/module.c, - modules/linux/vmmemctl/os.c, modules/linux/vmsync/sync.c, - modules/linux/vmxnet/vmxnet.c, modules/linux/vmxnet3/vmxnet3.c: add support - for Novell's proprietary module info tag ("supported"). + * modules/linux/pvscsi/pvscsi.c, modules/linux/vmblock/linux/module.c, + modules/linux/vmci/vmci_drv.c, modules/linux/hgfs/module.c, + modules/linux/vmmemctl/os.c, modules/linux/vmsync/sync.c, + modules/linux/vmxnet/vmxnet.c, modules/linux/vmxnet3/vmxnet3.c: add support + for Novell's proprietary module info tag ("supported"). - * modules/linux/vmci/vmciKernelIf.c: add support for VMCI queue pairs on - the host. This does not affect the driver when it runs inside virtual - machines. + * modules/linux/vmci/vmciKernelIf.c: add support for VMCI queue pairs on + the host. This does not affect the driver when it runs inside virtual + machines. - * modules/linux/vmci/*.h, modules/linux/vsock/*.h: some changes in the common - code to support Mac OS X, and also queue pairs on the Solaris VMCI module. + * modules/linux/vmci/*.h, modules/linux/vsock/*.h: some changes in the common + code to support Mac OS X, and also queue pairs on the Solaris VMCI module. - * modules/linux/vsock/af_vsock.{c,h}: add functions for registering with - the vsock driver from within the kernel. + * modules/linux/vsock/af_vsock.{c,h}: add functions for registering with + the vsock driver from within the kernel. - * modules/linux/*/Makefile: add $(LINUXINCLUDE) to the compiler flags; this - allows compiling the modules against recent kernels which removed that - from $(KBUILD_CPPFLAGS). + * modules/linux/*/Makefile: add $(LINUXINCLUDE) to the compiler flags; this + allows compiling the modules against recent kernels which removed that + from $(KBUILD_CPPFLAGS). - * modules/Makefile.am: add support for compiling Solaris kernel modules. + * modules/Makefile.am: add support for compiling Solaris kernel modules. - * modules/solaris/vmhgfs/*: initial release of the Solaris HGFS driver in - open-vm-tools. Driver is licensed under the CDDL 1.0. + * modules/solaris/vmhgfs/*: initial release of the Solaris HGFS driver in + open-vm-tools. Driver is licensed under the CDDL 1.0. - * vmblock-fuse/*: add the user-level implementation of the vmblock driver, - which is build on top of FUSE, to open-vm-tools. vmware-user hasn't yet - been modified to use this version of vmblock. + * vmblock-fuse/*: add the user-level implementation of the vmblock driver, + which is build on top of FUSE, to open-vm-tools. vmware-user hasn't yet + been modified to use this version of vmblock. - * vmware-user/copyPaseV3.h: new header introduced during development of the - next version of copy paste / DnD for X11 platforms. + * vmware-user/copyPaseV3.h: new header introduced during development of the + next version of copy paste / DnD for X11 platforms. 2008-11-23 Marcelo Vanzin diff --git a/open-vm-tools/Makefile.am b/open-vm-tools/Makefile.am index 4563b0d1e..dc90b4968 100644 --- a/open-vm-tools/Makefile.am +++ b/open-vm-tools/Makefile.am @@ -39,15 +39,15 @@ if BUILD_HGFSMOUNTER endif SUBDIRS += checkvm SUBDIRS += libguestlib -if ENABLE_CORE_SERVICES - SUBDIRS += libvmtools -endif +SUBDIRS += libvmtools +SUBDIRS += scripts +SUBDIRS += services +SUBDIRS += toolbox if HAVE_FUSE SUBDIRS += vmblock-fuse endif SUBDIRS += xferlogs -SUBDIRS += scripts -SUBDIRS += toolbox +SUBDIRS += tests if WITH_KERNEL_MODULES SUBDIRS += modules endif diff --git a/open-vm-tools/NEWS b/open-vm-tools/NEWS index b92a81a51..822016ee8 100644 --- a/open-vm-tools/NEWS +++ b/open-vm-tools/NEWS @@ -1,15 +1,33 @@ +open-vm-tools 2009.02.18 changes: + * open-vm-tools now depend on glib 2.6.0 as a minimum requirement. + + * Added vmxnet module for Solaris, and reworked the HGFS module so it + works without help from vmware-guestd. + + * Added implementation of new DnD protocol, which adds a dependency on + a C++ compiler and the gtkmm library to vmware-user. + + * The code from the "core services" has been added to open-vm-tools, + including a few tests. vmtoolsd + plugins are now capable of replacing + vmware-guestd (vmware-user still needs to have some features ported + over), but this code is still not to be considered as stable as the + legacy services, so vmware-guestd is still included. + + * A few fixes for compatibility with non-GNU toolchains, newer Linux + kernels and old gcc compilers. + open-vm-tools 2009.01.21 changes: - * First open source release of the HGFS module for Solaris, under the CDDL. - Other modules are expected to be added in the upcoming releases. + * First open source release of the HGFS module for Solaris, under the CDDL. + Other modules are expected to be added in the upcoming releases. - * Added an implementation of vmblock on top of FUSE; vmware-user still - doesn't use this module even if it is available, though. + * Added an implementation of vmblock on top of FUSE; vmware-user still + doesn't use this module even if it is available, though. - * Linux modules now add the "supported" tag used by Novell in their - SLES 10 SP2 release when loading modules. + * Linux modules now add the "supported" tag used by Novell in their + SLES 10 SP2 release when loading modules. - * Fix compilation of modules in newer Linux kernels which don't include - $(LINUXINCLUDE) in the compiler flags anymore. + * Fix compilation of modules in newer Linux kernels which don't include + $(LINUXINCLUDE) in the compiler flags anymore. open-vm-tools 2008.12.23 changes: * Lots of makefile cleanup with the switch to using libtool archives. diff --git a/open-vm-tools/configure.ac b/open-vm-tools/configure.ac index 8572cc7e4..c4e841b07 100644 --- a/open-vm-tools/configure.ac +++ b/open-vm-tools/configure.ac @@ -21,18 +21,14 @@ ################################################################################ # In addition to the usual environment variables and command line # arguments that a configure script usually takes (CFLAGS, LDFLAGS, -# etc.), this script also accepts: -# $CUSTOM_FUSE_CPPFLAGS - compile-time flags to find FUSE headers. -# $CUSTOM_FUSE_LIBS - link-time flags to find FUSE libraries. -# $CUSTOM_GLIB2_CPPFLAGS - compile-time flags to find Glib headers. -# $CUSTOM_GLIB2_LIBS - link-time flags to find Glib libraries. -# $CUSTOM_GTK_CPPFLAGS - compile-time flags to find Gtk headers. -# $CUSTOM_GTK_LIBS - link-time flags to find and link the Gtk libraries. -# $CUSTOM_PROCPS_LIBS - link-time flags to find and link the procps library. -# $CUSTOM_URIPARSER_CPPFLAGS - compile-time flags to find the uriparser headers -# $CUSTOM_URIPARSER_LIBS - link-time flags to find and link the uriparser library -# $CUSTOM_ZLIB_CPPFLAGS - compile-time flags to find the zlib headers -# $CUSTOM_ZLIB_LIBS - link-time flags to find and link the zlib library +# etc.), this script also accepts variables of the form: +# +# CUSTOM_LIB_CPPFLAGS: compile-time flags for LIB +# CUSTOM_LIB_LIBS: link-time flags for LIB +# RPCGENFLAGS: extra flags to pass to rpcgen +# +# The following libraries are currently tested: DNET, FUSE, GLIB2, GMODULE, +# GOBJECT, GTHREAD, GTK, GTKMM, LIBPNG, PROCPS, URIPARSER, ZLIB ################################################################################ ### @@ -41,7 +37,7 @@ AC_INIT( [open-vm-tools], - [2009.01.21-142982], + [2009.02.18-148847], [open-vm-tools-devel@lists.sourceforge.net]) # In order to make this configure script auto-detect situations where @@ -166,13 +162,19 @@ AC_ARG_WITH( gtk2, AS_HELP_STRING( [--without-gtk2], - [compiles without Gtk 2.0 (falls back on Gtk 1.2)]), + [compiles without Gtk 2.0]), [with_gtk2="$withval"], [with_gtk2="yes"]) -# If we're not going to use Gtk 2.0, disable Unity. It may be possible to -# get Unity working with Gtk 1.2 (it used to in the past), but that will -# require a great deal of work. +AC_ARG_WITH( + gtkmm, + AS_HELP_STRING( + [--without-gtkmm], + [compiles without Gtkmm, sigc++, and related libs]), + [with_gtkmm="$withval"], + [with_gtkmm="yes"]) + +# If we're not going to use Gtk 2.0, disable Unity. if test "$with_gtk2" != "yes"; then enable_unity="no" fi @@ -241,79 +243,85 @@ fi AC_PATH_XTRA +# +# Check for libintl.h. When configuring using "--without-x", /usr/local/include +# may not be added to the include path, so code that use glib's i18n functions +# would fail to compile because it can't find libintl.h. +# +AC_CHECK_HEADER([libintl.h], + [], + [have_libintl=no], + []) +if test "$have_libintl" = "no"; then + unset ac_cv_header_libintl_h + CPPFLAGS="$CPPFLAGS -I/usr/local/include" + AC_CHECK_HEADER([libintl.h], + [], + [AC_MSG_ERROR([libintl.h not found. Make sure you have the gettext headers installed.])], + []) +fi + # # Check for glib 2.6.0 or greater. -# XXX: glib < 2.6 should become an error once the code that needs it is the default. # -if test "$HAVE_PKG_CONFIG" = "yes"; then - AC_MSG_CHECKING([for glib >= 2.6.0 (via pkg-config)]) - if pkg-config --exists 'glib-2.0 >= 2.6.0'; then - GLIB2_CPPFLAGS="`pkg-config --cflags glib-2.0`" - GLIB2_LIBS="`pkg-config --libs glib-2.0`" - have_glib_26="yes" - AC_MSG_RESULT([yes]) - else - have_glib_26="no" - AC_MSG_RESULT([no]) - fi -else - # g_key_file_new (and related) functions were added in glib 2.6. That's the - # minimum version supported by the Core Services code. - AC_CHECK_LIB( - [glib-2.0], - [g_key_file_new], - [have_glib_26="yes"], - [AC_MSG_WARN([glib 2 is older than 2.6.0, some features will be disabled.])], - [$CUSTOM_GLIB2_LIBS]) - if test "$have_glib_26" = "yes"; then - GLIB2_CPPFLAGS="$CUSTOM_GLIB2_CPPFLAGS" - GLIB2_LIBS="$CUSTOM_GLIB2_LIBS" - fi +AC_VMW_CHECK_LIB([glib-2.0], + [GLIB2], + [glib-2.0], + [], + [2.6.0], + [glib.h], + [g_key_file_new], + [], + [AC_MSG_ERROR([glib >= 2.6.0 is required.])]) +AC_VMW_CHECK_LIB([gmodule-2.0], + [GMODULE], + [gmodule-2.0], + [], + [2.6.0], + [], + [], + [], + [AC_MSG_ERROR([gmodule >= 2.6.0 is required.])]) +AC_VMW_CHECK_LIB([gobject-2.0], + [GOBJECT], + [gobject-2.0], + [], + [2.6.0], + [glib-object.h], + [], + [], + [AC_MSG_ERROR([gobject >= 2.6.0 is required.])]) +AC_VMW_CHECK_LIB([gthread-2.0], + [GTHREAD], + [gthread-2.0], + [], + [2.6.0], + [], + [], + [], + [AC_MSG_ERROR([glib >= 2.6.0 is required.])]) +AC_CHECK_PROG([have_genmarshal], + [glib-genmarshal], + [yes], + [no]) + +if test "$have_genmarshal" != "yes"; then + AC_MSG_ERROR([glib-genmarshal is required; make sure it's available in your path.]) fi # # Check for fuse. # -if test "$HAVE_PKG_CONFIG" = "yes"; then - AC_MSG_CHECKING([for fuse (via pkg-config)]) - if pkg-config --exists 'fuse'; then - FUSE_CPPFLAGS="`pkg-config --cflags fuse`" - FUSE_LIBS="`pkg-config --libs fuse`" - have_fuse="yes" - AC_MSG_RESULT([yes]) - else - have_fuse="no" - AC_MSG_RESULT([no]) - fi -else - ORIGINAL_LDFLAGS="$LDFLAGS" - LDFLAGS="$CUSTOM_FUSE_LIBS $LDFLAGS" - AC_CHECK_LIB( - [fuse], - [fuse_main], - [have_fuse="yes"; - if test -n "$CUSTOM_FUSE_LIBS"; then - FUSE_LIBS="$CUSTOM_FUSE_LIBS" - else - FUSE_LIBS="-lfuse" - fi], - [have_fuse="no"; AC_MSG_WARN([Fuse is missing, vmblock-fuse will be disabled.])]) - LDFLAGS="$ORIGINAL_LDFLAGS" - - if test "$have_fuse" = "yes"; then - ORIGINAL_CPPFLAGS="$CPPFLAGS" - CPPFLAGS="$CUSTOM_FUSE_CPPFLAGS $CPPFLAGS" - AC_CHECK_HEADER( - [fuse.h], - [if test -n "$CUSTOM_FUSE_CPPFLAGS"; then - FUSE_CPPFLAGS="$CUSTOM_FUSE_CPPFLAGS" - else - FUSE_CPPFLAGS="-I/usr/include" - fi], - [have_fuse="no"; AC_MSG_WARN([Can't locate fuse headers, vmblock-fuse will be disabled.])]) - CPPFLAGS="$ORIGINAL_CPPFLAGS" - fi -fi +AC_VMW_CHECK_LIB([fuse], + [FUSE], + [fuse], + [], + [], + [fuse.h], + [fuse_main], + [have_fuse=yes], + [have_fuse=no; + AC_MSG_WARN([Fuse is missing, vmblock-fuse will be disabled.])]) # If the user explicitly disables X11, then don't try to detect the X-related libraries if test "$have_x" = "disabled"; then @@ -404,196 +412,63 @@ else # Note that we look for uriFreeQueryListA because it's a relatively # new symbol that our code needs (it isn't present in the uriparser # that shipped with Ubuntu Hardy). - ORIGINAL_LDFLAGS="$LDFLAGS" - LDFLAGS="$CUSTOM_URIPARSER_LIBS $LDFLAGS" - AC_CHECK_LIB( - [uriparser], - [uriFreeQueryListA], - [if test -n "$CUSTOM_URIPARSER_LIBS"; then - URIPARSER_LIBS="$CUSTOM_URIPARSER_LIBS" - else - URIPARSER_LIBS="-luriparser" - fi], - [AC_MSG_ERROR( - [uriparser library not found or is too old. Please configure without Unity (using --disable-unity) or install the liburiparser devel package.])]) - LDFLAGS="$ORIGINAL_LDFLAGS" - - if test -n "$CUSTOM_URIPARSER_CPPFLAGS"; then - URIPARSER_CPPFLAGS="$CUSTOM_URIPARSER_CPPFLAGS" - else - # XXX: No pkg-config and no uriparser-config means we need to - # hard-code a sensible default. - URIPARSER_CPPFLAGS="-I/usr/include/uriparser" - fi - ORIGINAL_CPPFLAGS="$CPPFLAGS" - CPPFLAGS="$URIPARSER_CPPFLAGS $CPPFLAGS" - AC_CHECK_HEADER( - [Uri.h], - [], - [AC_MSG_ERROR( - [uriparser headers not found. Please configure without Unity (using --disable-unity) or install the liburiparser devel package])]) - CPPFLAGS="$ORIGINAL_CPPFLAGS" - - # Check for libpng using pkg-config, or barring that, using - # libpng-config. - if test "$HAVE_PKG_CONFIG" = "yes"; then - AC_MSG_CHECKING([for libpng (via pkg-config)]) - if pkg-config --exists libpng12; then - LIBPNG_CPPFLAGS="`pkg-config --cflags libpng12`" - LIBPNG_LIBS="`pkg-config --libs libpng12`" - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - fi - else - AC_PATH_PROG( - [LIBPNG_CONFIG], - [libpng-config], - [no]) - if test "$LIBPNG_CONFIG" != "no"; then - LIBPNG_CPPFLAGS="`$LIBPNG_CONFIG --cflags`" - LIBPNG_LIBS="`$LIBPNG_CONFIG --libs`" - fi - fi - if test -z "$LIBPNG_CPPFLAGS"; then - AC_MSG_ERROR( - [libpng not found. Please configure without Unity (using --disable-unity) or install the libpng devel package]) + if test -z "$CUSTOM_URIPARSER_CPPFLAGS"; then + # XXX: No pkg-config and no uriparser-config means we need to + # hard-code a sensible default. + CUSTOM_URIPARSER_CPPFLAGS="-I/usr/include/uriparser" fi + AC_VMW_CHECK_LIB([uriparser], + [URIPARSER], + [], + [], + [], + [Uri.h], + [uriFreeQueryListA], + [], + [AC_MSG_ERROR([uriparser library not found or is too old. Please configure without Unity (using --disable-unity) or install the liburiparser devel package.])]) + + # Check for libpng. + AC_VMW_CHECK_LIB([png12], + [LIBPNG], + [libpng12], + [libpng-config], + [], + [png.h], + [png_access_version_number], + [], + [AC_MSG_ERROR([libpng not found. Please configure without Unity (using --disable-unity) or install the libpng devel package])]) # Check for the zlib library and headers. Like uriparser, there # isn't a pkg-config or zlib-config file. - ORIGINAL_LDFLAGS="$LDFLAGS" - LDFLAGS="$CUSTOM_ZLIB_LIBS $LDFLAGS" - AC_CHECK_LIB( - [z], - [zlibVersion], - [if test -n "$CUSTOM_ZLIB_LIBS"; then - ZLIB_LIBS="$CUSTOM_ZLIB_LIBS" - else - ZLIB_LIBS="-lz" - fi], - [AC_MSG_ERROR( - [zlib library not found. Please configure without Unity (using --disable-unity) or install the zlib devel package.])]) - LDFLAGS="$ORIGINAL_LDFLAGS" - - if test -n "$CUSTOM_ZLIB_CPPFLAGS"; then - ZLIB_CPPFLAGS="$CUSTOM_ZLIB_CPPFLAGS" - else - # XXX: No pkg-config and no zlib-config means we need to - # hard-code a sensible default. - ZLIB_CPPFLAGS="-I/usr/include" + if test -z "$CUSTOM_ZLIB_CPPFLAGS"; then + # XXX: No pkg-config and no zlib-config means we need to + # hard-code a sensible default. + CUSTOM_ZLIB_CPPFLAGS="-I/usr/include" fi - ORIGINAL_CPPFLAGS="$CPPFLAGS" - CPPFLAGS="$ZLIB_CPPFLAGS $CPPFLAGS" - AC_CHECK_HEADER( - [zlib.h], - [], - [AC_MSG_ERROR( - [zlib headers not found. Please configure without Unity (using --disable-unity) or install the zlib devel package])]) - CPPFLAGS="$ORIGINAL_CPPFLAGS" + AC_VMW_CHECK_LIB([z], + [ZLIB], + [], + [], + [], + [zlib.h], + [zlibVersion], + [], + [AC_MSG_ERROR([zlib library not found. Please configure without Unity (using --disable-unity) or install the zlib devel package.])]) fi # End of library checks for Unity. - if test -n "$CUSTOM_GTK_CPPFLAGS" -o -n "$CUSTOM_GTK_LIBS"; then - # Check to see if we have some version of GTK. gtk_accel_group_add - # is a random function that is present in Gtk 1.2 but not Gtk - # 2.0. Similarly, gtk_tree_view_new is a random function in Gtk 2.0 - # which is not present in Gtk 1.2. - AC_CHECK_LIB( - [gtk], - [gtk_accel_group_add], - [HAVE_GTK1="yes"],, - [$CUSTOM_GTK_LIBS]) - if test "$with_gtk2" != "no"; then - AC_CHECK_LIB( - [gtk-x11-2.0], - [gtk_tree_view_new], - [HAVE_GTK2="yes"],, - [$CUSTOM_GTK_LIBS]) - fi - fi - - # Sometimes pkg-config can find stuff that AC_CHECK_LIB can't, so we use that as well. - if test "$HAVE_PKG_CONFIG" = "yes"; then - if test "$with_gtk2" != "no"; then - AC_MSG_CHECKING([for Gtk+ 2.0 (via pkg-config)]) - if pkg-config --exists gtk+-2.0; then - HAVE_GTK2="yes" - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - fi - fi - - AC_MSG_CHECKING([for Gtk+ 1.x (via pkg-config)]) - # The latter half of the test is because pkg-config's fallback - # to gtk-config is broken on systems such as Ubuntu that use - # bash-completion for unknown commands. (On Ubuntu, typing - # 'gtk-config' on a system that doesn't have libgtk1.2-dev - # installed outputs hints about using apt-get, in addition to the - # regular bash "command not found" message.) - if pkg-config --exists gtk+ \ - && test -n "`pkg-config --libs gtk+ 2>/dev/null | grep -v '^ *$'`"; then - HAVE_GTK1="yes" - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - fi - fi - - if test "$HAVE_GTK1" = "yes" -o "$HAVE_GTK2" = "yes"; then - # This bit is to fix builds on OpenSolaris B71, where - # 'pkg-config --cflags gtk+-2.0' gives an error. - if test "$HAVE_GTK2" = "yes" -a "$HAVE_PKG_CONFIG" = "yes" \ - && ! pkg-config --exists gtk+-2.0 \ - && test -z "$CUSTOM_GTK_CPPFLAGS" -a -z "$CUSTOM_GTK_LIBS"; then - HAVE_GTK2="no" - fi - - if test "$HAVE_GTK2" = "yes"; then - # Gtk 2.0 - GTK_CPPFLAGS="$GTK_CPPFLAGS -DGTK2" - gtkPackageName="gtk+-2.0" - elif test "$HAVE_GTK1" = "yes"; then - # Gtk 1.2 - gtkPackageName="gtk+" - - # Unity requires Gtk 2.0. - if test "$enable_unity" != "no"; then - AC_MSG_ERROR([The Unity feature requires Gtk+ 2.0. Please install the Gtk+ 2.0 devel packages or configure without Unity (using --disable-unity)]) - fi - else - # We found Gtk+ 2.0 that turned out to have a non-working - # pkg-config setup, AND Gtk 1.2 wasn't installed. - AC_MSG_ERROR([The Gtk+ library installed on your system is not fully functional. Please contact your OS vendor.]) - fi - - # Prefer flags specified, in the environment, by the user. - if test -n "$CUSTOM_GTK_CPPFLAGS" -o -n "$CUSTOM_GTK_LIBS"; then - GTK_CPPFLAGS="$GTK_CPPFLAGS $CUSTOM_GTK_CPPFLAGS" - GTK_LIBS="$GTK_LIBS $CUSTOM_GTK_LIBS" - # Use pkg-config if available and there are no user-specified flags. - elif test "$HAVE_PKG_CONFIG" = "yes"; then - GTK_CPPFLAGS="$GTK_CPPFLAGS $(pkg-config --cflags $gtkPackageName)" - GTK_LIBS="$GTK_LIBS $(pkg-config --libs $gtkPackageName)" - # Otherwise, we have a problem. - else - AC_MSG_ERROR([pkg-config not found. Get pkg-config or set CUSTOM_GTK_CPPFLAGS and CUSTOM_GTK_LIBS to specify gtk header file and library locations.]) - fi - - # Check for a sufficiently new installation of Gtk 2.0. This symbol is, at - # the time of writing, used in vmware-user. We must perform this check - # after GTK_LIBS has been set up. Note: we don't actually use the - # resultant HAVE_NEW_GTK2 variable; it's just set so that the default - # action, which will append gdk-x11-2.0 to LIBS, isn't taken. - if test "$HAVE_GTK2" == "yes"; then - AC_CHECK_LIB( - [gdk-x11-2.0], - [gdk_display_get_default_group], - [HAVE_NEW_GTK2="yes"], - [AC_MSG_ERROR( - [Gtk 2.0 installation may be too old. Please upgrade your Gtk 2.0 packages or configure without Gtk 2.0 (using --without-gtk2)])] - [$GTK_LIBS]) - fi + # Check whether we have gtk+ 2.0. + if test "$with_gtk2" != "no"; then + # gdk_display_get_default_group (added in gtk+ 2.4.0) is function currently + # needed by vmware-user. + AC_VMW_CHECK_LIB([gtk-x11-2.0], + [GTK], + [gtk+-2.0], + [], + [2.4.0], + [gtk/gtk.h], + [gdk_display_get_default_group], + [GTK_CPPFLAGS="$GTK_CPPFLAGS -DGTK2"], + [AC_MSG_ERROR([Gtk+ 2.0 library not found or too old. Please configure without Gtk+ support (using --without-gtk2) or install the Gtk+ 2.0 devel package.])]) # Unity requires libgdk_pixbuf_xlib, which most systems don't seem to # provide via "pkg-config --libs gtk+-2.0". @@ -602,20 +477,33 @@ else [gdk_pixbuf_xlib-2.0], [gdk_pixbuf_xlib_init], [GTK_LIBS="$GTK_LIBS -lgdk_pixbuf_xlib-2.0"], - [AC_MSG_ERROR( - [gdk_pixbuf_xlib-2.0 not found. Make sure your Gtk 2.0 installation is sane, or configure without Unity (using --disable-unity).])], + [AC_MSG_ERROR([gdk_pixbuf_xlib-2.0 not found. Make sure your Gtk 2.0 installation is sane, or configure without Unity (using --disable-unity).])], [$GTK_LIBS]) fi - else - AC_MSG_ERROR([libgtk not found. Please install the gtk devel package(s).]) + fi + + # + # Check for gtkmm 2.4.0 or greater. + # + + if test "$with_gtkmm" != "no"; then + CUSTOM_GTKMM_CPPFLAGS="$CUSTOM_GTKMM_CPPFLAGS $GTK_CPPFLAGS" + AC_VMW_CHECK_LIBXX([gtkmm-2.4], + [GTKMM], + [gtkmm-2.4], + [], + [2.4.0], + [gtkmm.h], + [], + [GTKMM_CPPFLAGS="$GTKMM_CPPFLAGS -DHAVE_GTKMM"], + [AC_MSG_ERROR([gtkmm library not found. Please install the libgtkmm devel package(s), or re-configure using --without-gtkmm.])]) fi fi # End of checks for X libraries AC_CHECK_LIB( [crypt], [crypt], - [HAVE_CRYPT="yes" - GUESTD_LDADD="$GUESTD_LDADD -lcrypt"], + [HAVE_CRYPT="yes"], [AC_MSG_ERROR( [libcrypt not found. Please install the libc/libcrypt devel package(s).])]) @@ -642,8 +530,7 @@ AC_CHECK_FUNCS([fcvt]) AC_CHECK_LIB( [pthread], [pthread_mutex_init], - [GUESTD_LDADD="$GUESTD_LDADD -lpthread" - VMWARE_USER_LDADD="$VMWARE_USER_LDADD -lpthread"], + [HAVE_PTHREAD=yes], [AC_MSG_ERROR( [libpthread not found. Please install the libc/libpthread devel package(s).])]) @@ -665,22 +552,21 @@ else fi if test "$with_procps" = "yes"; then - ORIGINAL_LDFLAGS="$LDFLAGS" - LDFLAGS="$CUSTOM_PROCPS_LIBS $LDFLAGS" - AC_CHECK_LIB( - [proc-3.2.7], - [getstat], - [if test -n "$CUSTOM_PROCPS_LIBS"; then - PROCPS_LIBS="$CUSTOM_PROCPS_LIBS" - else - PROCPS_LIBS="-lproc-3.2.7" - fi], - [AC_MSG_ERROR( - [libproc not found. Please configure without procps (using --without-procps) or install procps - http://procps.sourceforge.net])]) - LDFLAGS="$ORIGINAL_LDFLAGS" - - GUESTD_LDADD="$GUESTD_LDADD $PROCPS_LIBS" - VMWARE_USER_LDADD="$VMWARE_USER_LDADD $PROCPS_LIBS" + # XXX: Force CUSTOM_PROCPS_LIBS to have something so that AC_VMW_CHECK_LIB + # properly performs the library check. + if test -z "$CUSTOM_PROCPS_LIBS"; then + CUSTOM_PROCPS_LIBS="-L/lib" + fi + AC_VMW_CHECK_LIB([proc-3.2.7], + [PROCPS], + [], + [], + [], + [], + [getstat], + [GUESTD_LDADD="$GUESTD_LDADD $PROCPS_LIBS"; + VMWARE_USER_LDADD="$VMWARE_USER_LDADD $PROCPS_LIBS"], + [AC_MSG_ERROR([libproc not found. Please configure without procps (using --without-procps) or install procps - http://procps.sourceforge.net])]) fi if test "$with_procps" != "yes"; then @@ -694,24 +580,38 @@ AC_ARG_WITH([dnet], [with_dnet=yes]) if test "$with_dnet" = "yes"; then + have_dnet=0 # On Debian, dnet is installed via the libdumbnet package. We need to # detect this so that our source files include dumbnet.h instead of # dnet.h, which is part of a different package altogether. - AC_CHECK_PROGS([DNET_CONFIG], - [dumbnet-config dnet-config], - [no]) - - if test "$DNET_CONFIG" != no; then - DNET_CPPFLAGS="`$DNET_CONFIG --cflags`" - DNET_LIBS="`$DNET_CONFIG --libs`" - - GUESTD_LDADD="$GUESTD_LDADD $DNET_LIBS" - VMWARE_USER_LDADD="$VMWARE_USER_LDADD $DNET_LIBS" - if test "$DNET_CONFIG" == dumbnet-config; then - AC_DEFINE([DNET_IS_DUMBNET], 1, [Define to 1 if substituting Debian's libdumbnet for libdnet.]) - fi - else + AC_VMW_CHECK_LIB([dumbnet], + [DNET], + [], + [dumbnet-config], + [], + [dumbnet.h], + [intf_open], + [have_dnet=1; + AC_DEFINE([DNET_IS_DUMBNET], 1, [Define to 1 if substituting Debian's libdumbnet for libdnet.])], + []) + + if test $have_dnet -eq 0; then + AC_VMW_CHECK_LIB([dnet], + [DNET], + [], + [dnet-config], + [], + [dnet.h], + [intf_open], + [have_dnet=1], + []) + fi + + if test $have_dnet -eq 1; then + GUESTD_LDADD="$GUESTD_LDADD $DNET_LIBS" + VMWARE_USER_LDADD="$VMWARE_USER_LDADD $DNET_LIBS" + else AC_MSG_ERROR( [dnet-config was not found on your PATH. Please configure without dnet (using --without-dnet) or install dnet - http://libdnet.sourceforge.net]) fi @@ -727,67 +627,34 @@ AC_ARG_WITH([icu], [], [with_icu=yes]) -if test "$with_icu" = "yes"; then +if test "$have_x" = "yes" -o "$with_icu" = "yes"; then + AC_CHECK_TOOL([have_cxx], [$CXX], [no]) + if test "$have_cxx" = "no"; then + AC_MSG_ERROR([C++ compiler not found. Make sure you have a C++ compiler installed or configure without X11 (using --without-x) and without ICU (using --without-icu).]) + fi +fi - AC_PATH_PROG([ICU_CONFIG], - [icu-config], - [no]) - - if test "$ICU_CONFIG" != no; then - ICU_CPPFLAGS="`$ICU_CONFIG --cppflags` -DUSE_ICU" - ICU_LIBS="`$ICU_CONFIG --ldflags`" - - # The icu components are built with C++, so we need to ensure - # that the C++ linker actually exists, and use it when linking - # icu into our applications and when performing configuration - # tests against icu. - # - # XXX: Getting automake to choose between the C linker and the - # C++ linker depending on HAVE_ICU was a royal pain in the ass. - # The classic way to do this is to define an optional source - # file for a program with an extension of .cxx, using - # nodist_EXTRA_fooprogram_SOURCES. This causes automake's - # linker detection algorithm to see a C++ source file and - # automatically set up the C++ linker and link line for us. - # Unfortunately, said linker detection doesn't obey - # conditionals, which means that it'd always pick the C++ - # linker, regardless of the value of HAVE_ICU. Instead, we are - # forced to manually set the correct linker in fooprogram_LINK. - # However, since none of our programs actually contain C++ - # code, automake doesn't make the CXXLINK variable (which - # contains the linker as well as all link flags) available to - # us, so we must hard-code the entire link line into - # fooprogram_LINK. Not exactly a futureproof solution... - # - # Additional references on this problem: - # http://sources.redhat.com/ml/automake/1999-10/msg00101.html - # http://lists.gnu.org/archive/html/bug-automake/2008-04/msg00010.html - # http://www.gnu.org/software/automake/manual/automake.html#Libtool-Convenience-Libraries - # http://www.gnu.org/software/automake/manual/automake.html#C_002b_002b-Support - AC_CHECK_TOOL( - [HAVE_CXX], - [$CXX]) - if test -z "$HAVE_CXX"; then - AC_MSG_ERROR( - [No C++ compiler was found on your PATH. Please configure without ICU (using --without-icu) or install a C++ compiler.]) - fi - AC_LANG_PUSH([C++]) - AC_CHECK_LIB([icuuc], - [ucasemap_utf8ToTitle_3_8], - [have_ICU_38="yes"],, - [$ICU_LIBS]) - AC_LANG_POP([C++]) - - if test "$have_ICU_38" = "yes"; then - ICU_CPPFLAGS="$ICU_CPPFLAGS -DHAVE_ICU_38" - fi - - # Easier to give all modules the ICU defines/includes... - CPPFLAGS="$CPPFLAGS $ICU_CPPFLAGS" - else - AC_MSG_ERROR( - [icu-config was not found on your PATH. Please configure without ICU (using --without-icu) or install ICU - http://www.icu-project.org]) - fi +if test "$with_icu" = "yes"; then + AC_VMW_CHECK_LIBXX([icuuc], + [ICU], + [], + [icu-config], + [], + [unicode/utf.h], + [], + [ICU_CPPFLAGS="$ICU_CPPFLAGS -DUSE_ICU"], + [AC_MSG_ERROR([ICU library not found. Please configure without ICU (using --without-icu) or install ICU - http://www.icu-project.org])]) + # Check whether we have ICU 3.8. + AC_LANG_PUSH([C++]) + AC_CHECK_LIB([icuuc], + [ucasemap_utf8ToTitle_3_8], + [ICU_CPPFLAGS="$ICU_CPPFLAGS -DHAVE_ICU_38"], + [], + [$ICU_LIBS]) + AC_LANG_POP([C++]) + + # Easier to give all modules the ICU defines/includes... + CPPFLAGS="$CPPFLAGS $ICU_CPPFLAGS" fi AC_PATH_PROG( @@ -941,11 +808,22 @@ done CPPFLAGS="$CPPFLAGS -DVMX86_TOOLS" CPPFLAGS="$CPPFLAGS -DNO_CORE_ICU" +# -fvisibility is used by "core service" plugins, but not required. +ORIGINAL_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -fvisibility=hidden" +AC_MSG_CHECKING([for GCC flag -fvisibility]) +AC_TRY_COMPILE([], + [return 0;], + [PLUGIN_CPPFLAGS="-fvisibility=hidden -DGCC_EXPLICIT_EXPORT"; + AC_MSG_RESULT(yes)], + [AC_MSG_RESULT(no)]) +CFLAGS="$ORIGINAL_CFLAGS" + BUILDDIR="`pwd`" INCLUDE_DIR="`cd $srcdir; pwd`/lib/include" BLD_INCLUDE_DIR="$BUILDDIR/lib/include" -CPPFLAGS="$CPPFLAGS -I$INCLUDE_DIR -I$BLD_INCLUDE_DIR" +CPPFLAGS="-I$INCLUDE_DIR -I$BLD_INCLUDE_DIR $CPPFLAGS" ### OS/arch-specific flags / actions @@ -1017,6 +895,11 @@ if test "$os" = "solaris"; then CPPFLAGS="$CPPFLAGS -DSOL10" MODULES="$MODULES vmhgfs" fi + + # We build vmxnet on Solaris 9 and 10 + if test "$osVersion" -lt 511000 -a "$osVersion" -ge 509000; then + MODULES="$MODULES vmxnet" + fi fi AM_CONDITIONAL(BUILD_HGFSMOUNTER, test "$buildHgfsmounter" = "yes") @@ -1030,9 +913,9 @@ AM_CONDITIONAL(WITH_KERNEL_MODULES, test "$with_kernel_modules" = "yes") AM_CONDITIONAL(PAMD, test -d /etc/pam.d) AM_CONDITIONAL(ENABLE_UNITY, test "$enable_unity" = "yes") AM_CONDITIONAL(WITH_ROOT_PRIVILEGES, test "$with_root_privileges" = "yes") -AM_CONDITIONAL(ENABLE_CORE_SERVICES, test "$have_glib_26" = "yes") AM_CONDITIONAL(HAVE_FUSE, test "$have_fuse" = "yes") AM_CONDITIONAL(HAVE_GNU_LD, test "$with_gnu_ld" = "yes") +AM_CONDITIONAL(HAVE_GTKMM, test "$have_x" = "yes" -a "$with_gtkmm" = "yes") ### Feature-specific flags / actions # Combine where possible @@ -1065,17 +948,49 @@ fi if test "$HAVE_CRYPT" = "yes"; then LIB_AUTH_CPPFLAGS="$LIB_AUTH_CPPFLAGS -DGLIBC_VERSION_22" + GUESTD_LDADD="$GUESTD_LDADD -lcrypt" + VIX_LIBADD="$VIX_LIBADD -lcrypt" fi if test "$HAVE_WCHAR_H" = "yes"; then LIB_STRING_CPPFLAGS="$LIB_STRING_CPPFLAGS -DGLIBC_VERSION_22" fi +if test "$HAVE_PTHREAD" = "yes"; then + GUESTD_LDADD="$GUESTD_LDADD -lpthread" + VIX_LIBADD="$VIX_LIBADD -lpthread" + VMWARE_USER_LDADD="$VMWARE_USER_LDADD -lpthread" +fi + ### Core Services definitions. -VMTOOLS_LIBS="$BUILDDIR/libvmtools/libvmtools.la" +VMTOOLS_LIBS="$BUILDDIR/libvmtools/libvmtools.la $GLIB2_LIBS" VMTOOLS_CPPFLAGS="-DVMTOOLS_USE_GLIB $GLIB2_CPPFLAGS" +PLUGIN_CPPFLAGS="$VMTOOLS_CPPFLAGS $PLUGIN_CPPFLAGS" + +# For plugins, we'd like to build shared libraries but not install them in the +# usual shared library destination. Problem is, "noinst_LTLIBRARIES" does not +# create shared libraries by itself: the "rpath" flag seems to be needed to tell +# libtool it should create one. All I've found as far as documentation goes is +# this: +# +# http://lists.gnu.org/archive/html/libtool/2007-07/msg00065.html +# +# The other options are common options we use when building shared libraries. +PLUGIN_LDFLAGS="-rpath `pwd` -Wl,-z,defs -Wl,-lc" + +# In Solaris, the XDR-related functions are not in libc like in Linux and +# FreeBSD, so binaries need to be linked to some extra libraries. +XDR_LIBS= +if test "$os" = "solaris"; then + XDR_LIBS="-lnsl -lrpcsvc" +fi + +# Installation directories for core services plugins. +VMSVC_PLUGIN_INSTALLDIR=$libdir/open-vm-tools/plugins/vmsvc +VMUSR_PLUGIN_INSTALLDIR=$libdir/open-vm-tools/plugins/vmusr + ### General substs AC_SUBST([TARGET_OS]) @@ -1084,26 +999,15 @@ AC_SUBST([MODULES_OS]) AC_SUBST([MODULES_DIR]) AC_SUBST([MODULES]) AC_SUBST([COMMON_XLIBS]) -AC_SUBST([GLIB2_CPPFLAGS]) -AC_SUBST([GLIB2_LIBS]) -AC_SUBST([GTK_CPPFLAGS]) -AC_SUBST([GTK_LIBS]) -AC_SUBST([DNET_CPPFLAGS]) -AC_SUBST([DNET_LIBS]) -AC_SUBST([PROCPS_LIBS]) -AC_SUBST([ICU_CPPFLAGS]) -AC_SUBST([ICU_LIBS]) AC_SUBST([PAM_PREFIX]) -AC_SUBST([URIPARSER_CPPFLAGS]) -AC_SUBST([URIPARSER_LIBS]) -AC_SUBST([LIBPNG_CPPFLAGS]) -AC_SUBST([LIBPNG_LIBS]) +AC_SUBST([PLUGIN_CPPFLAGS]) +AC_SUBST([PLUGIN_LDFLAGS]) AC_SUBST([VMTOOLS_CPPFLAGS]) AC_SUBST([VMTOOLS_LIBS]) -AC_SUBST([ZLIB_CPPFLAGS]) -AC_SUBST([ZLIB_LIBS]) -AC_SUBST([FUSE_CPPFLAGS]) -AC_SUBST([FUSE_LIBS]) +AC_SUBST([RPCGENFLAGS]) +AC_SUBST([XDR_LIBS]) +AC_SUBST([VMSVC_PLUGIN_INSTALLDIR]) +AC_SUBST([VMUSR_PLUGIN_INSTALLDIR]) ### Lib substs @@ -1122,7 +1026,7 @@ AC_SUBST([LIBVMTOOLS_LIBADD]) ### Program substs AC_SUBST([GUESTD_LDADD]) - +AC_SUBST([VIX_LIBADD]) AC_SUBST([VMWARE_USER_CPPFLAGS]) AC_SUBST([VMWARE_USER_LDADD]) @@ -1138,6 +1042,7 @@ AC_CONFIG_FILES([ \ lib/conf/Makefile \ lib/dict/Makefile \ lib/dnd/Makefile \ + lib/dndGuest/Makefile \ lib/dynxdr/Makefile \ lib/err/Makefile \ lib/eventManager/Makefile \ @@ -1166,6 +1071,7 @@ AC_CONFIG_FILES([ \ lib/raster/Makefile \ lib/region/Makefile \ lib/resolution/Makefile \ + lib/rpcChannel/Makefile \ lib/rpcIn/Makefile \ lib/rpcOut/Makefile \ lib/rpcVmx/Makefile \ @@ -1188,6 +1094,17 @@ AC_CONFIG_FILES([ \ lib/vmSignal/Makefile \ lib/wiper/Makefile \ guestd/Makefile \ + services/Makefile \ + services/vmtoolsd/Makefile \ + services/plugins/Makefile \ + services/plugins/guestInfo/Makefile \ + services/plugins/hgfsServer/Makefile \ + services/plugins/powerOps/Makefile \ + services/plugins/resolutionSet/Makefile \ + services/plugins/timeSync/Makefile \ + services/plugins/vix/Makefile \ + services/plugins/vixUser/Makefile \ + services/plugins/vmbackup/Makefile \ vmware-user/Makefile \ vmware-user-suid-wrapper/Makefile \ toolbox/Makefile \ @@ -1199,6 +1116,10 @@ AC_CONFIG_FILES([ \ xferlogs/Makefile \ modules/Makefile \ vmblock-fuse/Makefile \ + tests/Makefile \ + tests/vmrpcdbg/Makefile \ + tests/testDebug/Makefile \ + tests/testPlugin/Makefile \ scripts/Makefile \ ]) diff --git a/open-vm-tools/guestd/Makefile.am b/open-vm-tools/guestd/Makefile.am index 5cd078037..23e7f4f1a 100644 --- a/open-vm-tools/guestd/Makefile.am +++ b/open-vm-tools/guestd/Makefile.am @@ -19,6 +19,7 @@ sbin_PROGRAMS = vmware-guestd pamdir = $(PAM_PREFIX)/pam.d AM_CFLAGS = -I$(top_builddir)/include +AM_CFLAGS += -I$(top_srcdir)/services/plugins/vix vmware_guestd_LDADD = vmware_guestd_LDADD += ../lib/backdoor/libBackdoor.la @@ -79,7 +80,7 @@ vmware_guestd_SOURCES = vmware_guestd_SOURCES += debugStdio.c vmware_guestd_SOURCES += foreignVMToolsDaemon.c vmware_guestd_SOURCES += foreignVMToolsNetworking.c -vmware_guestd_SOURCES += foundryToolsDaemon.c +vmware_guestd_SOURCES += $(top_srcdir)/services/plugins/vix/foundryToolsDaemon.c vmware_guestd_SOURCES += main.c vmware_guestd_SOURCES += toolsDaemon.c @@ -107,4 +108,4 @@ install-exec-hook: echo 'disable-tools-version = "true"' > $(DESTDIR)/etc/vmware-tools/tools.conf uninstall-hook: - $(RM) -rf $(DESTDIR)/etc/vmware-tools + rm -rf $(DESTDIR)/etc/vmware-tools diff --git a/open-vm-tools/guestd/toolsDaemon.c b/open-vm-tools/guestd/toolsDaemon.c index d12f51f51..90d658d38 100644 --- a/open-vm-tools/guestd/toolsDaemon.c +++ b/open-vm-tools/guestd/toolsDaemon.c @@ -56,6 +56,7 @@ extern "C" { #include "guestApp.h" #include "rpcout.h" #include "rpcin.h" +#include "hostinfo.h" #include "strutil.h" #include "str.h" #include "msg.h" @@ -840,7 +841,7 @@ ToolsDaemonStateChangeDone(Bool status, // IN status = TRUE; #ifdef _WIN32 if (data->stateChgInProgress == GUESTOS_STATECHANGE_REBOOT || data->stateChgInProgress == GUESTOS_STATECHANGE_HALT) { - if (System_GetOSType() >= OS_VISTA) { + if (Hostinfo_GetOSType() >= OS_VISTA) { DISABLE_RES_CAPS(); } } @@ -1056,7 +1057,7 @@ ToolsDaemonTcloCapReg(char const **result, // OUT * current UIs will treat as unsupported. */ if (!RpcOut_sendOne(NULL, NULL, "tools.capability.display_topology_set %s", - System_GetOSType() >= OS_WINXP ? "2" : "0")) { + Hostinfo_GetOSType() >= OS_WINXP ? "2" : "0")) { Debug("ToolsDaemonTcloCapReg: Unable to register display topology set " "capability\n"); } @@ -1175,7 +1176,7 @@ ToolsDaemonTcloCapReg(char const **result, // OUT #if defined(WIN32) HgfsUsability_RegisterServiceCaps(); - if (System_GetOSType() >= OS_VISTA) { + if (Hostinfo_GetOSType() >= OS_VISTA) { ServiceHelpers_SendResolutionCaps(); } #endif diff --git a/open-vm-tools/hgfsmounter/Makefile.am b/open-vm-tools/hgfsmounter/Makefile.am index 91cad0cb5..932287126 100644 --- a/open-vm-tools/hgfsmounter/Makefile.am +++ b/open-vm-tools/hgfsmounter/Makefile.am @@ -30,7 +30,7 @@ mount_vmhgfs_SOURCES += stub-user-util.c if FREEBSD install-exec-hook: - $(MV) $(DESTDIR)$(sbindir)/mount.vmhgfs \ + mv $(DESTDIR)$(sbindir)/mount.vmhgfs \ $(DESTDIR)$(sbindir)/mount_vmhgfs if WITH_ROOT_PRIVILEGES chmod u+s $(DESTDIR)$(sbindir)/mount_vmhgfs @@ -39,7 +39,7 @@ endif WITH_ROOT_PRIVILEGES -$(LN_S) $(DESTDIR)$(sbindir)/mount_vmhgfs \ $(DESTDIR)/sbin/mount_vmhgfs &> /dev/null uninstall-hook: - -$(RM) -f $(DESTDIR)$(sbindir)/mount_vmhgfs &> /dev/null + rm -f $(DESTDIR)$(sbindir)/mount_vmhgfs else install-exec-hook: if WITH_ROOT_PRIVILEGES @@ -49,5 +49,5 @@ endif WITH_ROOT_PRIVILEGES -$(LN_S) $(DESTDIR)$(sbindir)/mount.vmhgfs \ $(DESTDIR)/sbin/mount.vmhgfs &> /dev/null uninstall-hook: - -$(RM) -f $(DESTDIR)/sbin/mount.vmhgfs &> /dev/null + rm -f $(DESTDIR)/sbin/mount.vmhgfs endif !FREEBSD diff --git a/open-vm-tools/hgfsmounter/hgfsmounter.c b/open-vm-tools/hgfsmounter/hgfsmounter.c index bbf2c8b78..9a23b9dab 100644 --- a/open-vm-tools/hgfsmounter/hgfsmounter.c +++ b/open-vm-tools/hgfsmounter/hgfsmounter.c @@ -39,6 +39,7 @@ #if defined(__FreeBSD__) || defined(__APPLE__) # include # include + # define MS_MANDLOCK 0 # define MS_RDONLY MNT_RDONLY # define MS_SYNCHRONOUS MNT_SYNCHRONOUS @@ -71,11 +72,9 @@ * 10.4 one. Once this is built against the newer library, this should be changed. */ # define MS_NOATIME 0 +# endif #endif -#endif - - #include #include #include @@ -85,7 +84,7 @@ #include #include -#ifdef MOUNTED // defined in mntent.h +#ifdef MOUNTED /* defined in mntent.h */ /* * If VM_HAVE_MTAB is set, hgfsmounter will update /etc/mtab. This is not necessary on * systems such as FreeBSD (and possibly Solaris) that don't have a mtab or the mntent.h @@ -126,30 +125,23 @@ #define LOG(format, ...) (beVerbose ? printf(format, ##__VA_ARGS__) : 0) #endif +typedef struct MountOptions { + char *opt; /* Option name */ + int flag; /* Corresponding flag */ + Bool set; /* Whether the flag should be set or reset */ + char *helpMsg; /* Help message for the option */ + char *logMsg; /* Log message to emit when option was detected */ + /* Special handler for more complex options */ + Bool (*handler)(const char *opt, + HgfsMountInfo *mountInfo, int *flags); +} MountOptions; + static char *thisProgram; static char *thisProgramBase; static char *shareName; static char *mountPoint; static Bool beVerbose = FALSE; -static void PrintVersion(void); -static void PrintUsage(void); -static size_t GetPathMax(const char *path); -static Bool ParseShareName(const char *shareName, - const char **shareNameHost, - const char **shareNameDir); -static Bool ParseUid(const char *uidString, - uid_t *uid); -static Bool ParseGid(const char *gidString, - gid_t *gid); -static Bool ParseOptions(const char *optionString, - HgfsMountInfo *mountInfo, - int *flags); -#ifdef VM_HAVE_MTAB -static void UpdateMtab(HgfsMountInfo *mountInfo, - int flags); -#endif - /* *----------------------------------------------------------------------------- * @@ -174,64 +166,6 @@ PrintVersion(void) } -/* - *----------------------------------------------------------------------------- - * - * PrintUsage -- - * - * Displays usage for the HGFS mounting utility, and exits with failure. - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -static void -PrintUsage(void) -{ - printf("Usage: %s [-o ]\n", thisProgramBase); - printf("Mount the HGFS share, specified by name, to a local directory.\n"); - printf("Share name must be in host:dir format.\n\nOptions:\n"); - printf(" uid= mount owner (by uid or username)\n"); - printf(" gid= mount group (by gid or groupname)\n"); - printf(" fmask= file umask (in octal)\n"); - printf(" dmask= directory umask (in octal)\n"); - printf(" ro mount read-only\n"); - printf(" rw mount read-write (default)\n"); - printf(" nosuid ignore suid/sgid bits\n"); - printf(" suid allow suid/sgid bits (default)\n"); -#ifdef MS_NODEV - printf(" nodev prevent device node access\n"); - printf(" dev allow device node access (default)\n"); -#endif - printf(" noexec prevent program execution\n"); - printf(" exec allow program execution (default)\n"); - printf(" sync file writes are synchronous\n"); - printf(" async file writes are asynchronous (default)\n"); - printf(" mand allow mandatory locks\n"); - printf(" nomand prevent mandatory locks (default)\n"); - printf(" noatime do not update access times\n"); - printf(" atime update access times (default)\n"); - printf(" nodiratime do not update directory access times\n"); - printf(" adirtime update directory access times (default)\n"); - printf(" ttl= time before file attributes must be\n"); - printf(" revalidated (in seconds). Improves\n"); - printf(" performance but decreases coherency.\n"); - printf(" Defaults to 1 if not set.\n"); - printf("\n"); - printf("This command is intended to be run from within /bin/mount by\n"); - printf("passing the option '-t %s'. For example:\n", HGFS_NAME); - printf(" mount -t %s .host:/ /mnt/hgfs/\n", HGFS_NAME); - printf(" mount -t %s .host:/foo /mnt/foo\n", HGFS_NAME); - printf(" mount -t %s .host:/foo/bar /var/lib/bar\n", HGFS_NAME); - exit(EXIT_FAILURE); -} - - /* *----------------------------------------------------------------------------- * @@ -307,7 +241,8 @@ ParseShareName(const char *shareName, // IN: Share name to validate const char **shareNameHost, // OUT: Share name, host component const char **shareNameDir) // OUT: Share name, dir component { - const char *colon, *dir; + const char *colon; + const char *dir; /* 1) Must be colon separated into host and dir. */ colon = strchr(shareName, ':'); @@ -349,27 +284,40 @@ ParseShareName(const char *shareName, // IN: Share name to validate * ParseUid * * A helper function to process a string containing either a user name - * or a uid and return the resulting uid. + * or a uid and set up mountInfo accordingly. * * Results: - * TRUE on success, uid contains the user ID. - * FALSE on failure, uid is undefined. + * TRUE on success, mountInfo modified appropriately, FALSE on failure. * * Side effects: * None. * *----------------------------------------------------------------------------- */ - +#ifndef sun static Bool -ParseUid(const char *uidString, // IN: String with uid - uid_t *uid) // OUT: Converted uid +ParseUid(const char *option, // IN: option string along with value + HgfsMountInfo *mountInfo, // OUT: mount data + int *flags) // OUT: mount flags { Bool success = FALSE; - uid_t myUid; + unsigned int idx = 0; + char *optString; + char *uidString; + uid_t myUid = 0; + + ASSERT(option); + ASSERT(mountInfo); + ASSERT(flags); - ASSERT(uidString); - ASSERT(uid); + /* Figure where value starts, we don't need result, just index. */ + optString = StrUtil_GetNextToken(&idx, option, "="); + ASSERT(optString); + uidString = StrUtil_GetNextToken(&idx, option, "="); + if (!uidString) { + LOG("Error getting the value for uid\n"); + goto out; + } /* * The uid can be a direct value or a username which we must first @@ -379,7 +327,6 @@ ParseUid(const char *uidString, // IN: String with uid errno = 0; myUid = strtoul(uidString, NULL, 10); if (errno == 0) { - *uid = myUid; success = TRUE; } else { printf("Bad UID value \"%s\"\n", uidString); @@ -390,14 +337,24 @@ ParseUid(const char *uidString, // IN: String with uid if (!(pw = getpwnam(uidString))) { printf("Bad user name \"%s\"\n", uidString); } else { - *uid = pw->pw_uid; + myUid = pw->pw_uid; success = TRUE; } endpwent(); } + + if (success) { + mountInfo->uid = myUid; + mountInfo->uidSet = TRUE; + LOG("Setting mount owner to %"FMTUID"\n", myUid); + } + + free(uidString); +out: + free(optString); return success; } - +#endif /* *----------------------------------------------------------------------------- @@ -405,27 +362,40 @@ ParseUid(const char *uidString, // IN: String with uid * ParseGid * * A helper function to process a string containing either a group name - * or a gid and return the resulting gid. + * or a gid and set up mountInfo accordingly. * * Results: - * TRUE on success, gid contains the group ID. - * FALSE on failure, gid is undefined. + * TRUE on success, mountInfo modified appropriately, FALSE on failure. * * Side effects: * None. * *----------------------------------------------------------------------------- */ - +#ifndef sun static Bool -ParseGid(const char *gidString, // IN: String with gid - gid_t *gid) // OUT: Converted gid +ParseGid(const char *option, // IN: option string along with value + HgfsMountInfo *mountInfo, // OUT: mount data + int *flags) // OUT: mount flags { Bool success = FALSE; - gid_t myGid; + unsigned int idx = 0; + char *optString; + char *gidString; + gid_t myGid = 0; - ASSERT(gidString); - ASSERT(gid); + ASSERT(option); + ASSERT(mountInfo); + ASSERT(flags); + + /* Figure where value starts, we don't need result, just index. */ + optString = StrUtil_GetNextToken(&idx, option, "="); + ASSERT(optString); + gidString = StrUtil_GetNextToken(&idx, option, "="); + if (!gidString) { + LOG("Error getting the value for gid\n"); + goto out; + } /* * The gid can be a direct value or a group name which we must first @@ -435,7 +405,6 @@ ParseGid(const char *gidString, // IN: String with gid errno = 0; myGid = strtoul(gidString, NULL, 10); if (errno == 0) { - *gid = myGid; success = TRUE; } else { printf("Bad GID value \"%s\"\n", gidString); @@ -446,15 +415,258 @@ ParseGid(const char *gidString, // IN: String with gid if (!(gr = getgrnam(gidString))) { printf("Bad group name \"%s\"\n", gidString); } else { - *gid = gr->gr_gid; + myGid = gr->gr_gid; success = TRUE; } endpwent(); } + + if (success) { + mountInfo->gid = myGid; + mountInfo->gidSet = TRUE; + LOG("Setting mount group to %"FMTUID"\n", myGid); + } + + free(gidString); +out: + free(optString); + return success; +} +#endif + +/* + *----------------------------------------------------------------------------- + * + * ParseMask + * + * A helper function to parse a string containing File/Directory + * mask value. + * + * Results: + * TRUE on success, along with the mask, FALSE on failure. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ +#ifndef sun +static Bool +ParseMask(const char *option, // IN: option string along with value + unsigned short *pmask) // OUT: parsed mask +{ + Bool success = FALSE; + unsigned int idx = 0; + char *optString; + char *maskString; + unsigned short mask; + + optString = StrUtil_GetNextToken(&idx, option, "="); + ASSERT(optString); + maskString = StrUtil_GetNextToken(&idx, option, "="); + if (!maskString) { + LOG("Error getting the value for %s\n", optString); + goto out; + } + + /* + * The way to check for an overflow in strtol(3), according to its + * man page. + */ + errno = 0; + mask = strtol(maskString, NULL, 8); + if (errno == 0) { + *pmask = mask; + success = TRUE; + } else { + LOG("Error, overflow in %s\n", optString); + } + + free(maskString); +out: + free(optString); return success; } +#endif +/* + *----------------------------------------------------------------------------- + * + * ParseFmask + * + * A helper function to process a string containing File mask value. + * + * Results: + * TRUE on success, mountInfo modified appropriately, FALSE on failure. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ +#ifndef sun +static Bool +ParseFmask(const char *option, // IN: option string along with value + HgfsMountInfo *mountInfo, // OUT: mount data + int *flags) // OUT: mount flags +{ + ASSERT(option); + ASSERT(mountInfo); + + if (ParseMask(option, &mountInfo->fmask)) { + LOG("Setting mount fmask to %o\n", mountInfo->fmask); + return TRUE; + } + + return FALSE; +} +#endif + + +/* + *----------------------------------------------------------------------------- + * + * ParseDmask + * + * A helper function to process a string containing dmask value. + * + * Results: + * TRUE on success, mountInfo modified appropriately, FALSE on failure. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ +#ifndef sun +static Bool +ParseDmask(const char *option, // IN: option string along with value + HgfsMountInfo *mountInfo, // OUT: mount data + int *flags) // OUT: mount flags +{ + ASSERT(option); + ASSERT(mountInfo); + + if (ParseMask(option, &mountInfo->fmask)) { + LOG("Setting mount dmask to %o\n", mountInfo->dmask); + return TRUE; + } + + return FALSE; +} +#endif + + +/* + *----------------------------------------------------------------------------- + * + * ParseTtl + * + * A helper function to process a string containing TTL value. + * + * Results: + * TRUE on success, mountInfo modified appropriately, FALSE on failure. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ +#ifndef sun +static Bool +ParseTtl(const char *option, // IN: option string along with value + HgfsMountInfo *mountInfo, // OUT: mount data + int *flags) // OUT: mount flags +{ + Bool success = FALSE; + unsigned int idx = 0; + char *optString; + int32 ttl; + + ASSERT(option); + ASSERT(mountInfo); + ASSERT(flags); + + /* Figure where value starts, we don't need result, just index. */ + optString = StrUtil_GetNextToken(&idx, option, "="); + ASSERT(optString); + + if (StrUtil_GetNextIntToken(&ttl, &idx, option, "=") && ttl > 0) { + LOG("Setting maximum attribute TTL to %u\n", ttl); + mountInfo->ttl = ttl; + success = TRUE; + } else { + LOG("Error getting the value for ttl\n"); + } + + free(optString); + return success; +} +#endif + +static MountOptions mountOptions[] = { + { "ro", MS_RDONLY, TRUE, "mount read-only", + "Setting mount read-only", NULL }, + { "rw", MS_RDONLY, FALSE, "mount read-write", + "Setting mount read-write", NULL }, + { "nosuid", MS_NOSUID, TRUE, "ignore suid/sgid bits", + "Setting mount option for allowing suid/sgid bits off", NULL }, + { "suid", MS_NOSUID, FALSE, "allow suid/sgid bits (default)", + "Setting mount option for allowing suid/sgid bits on", NULL }, + +#ifndef sun /* Solaris does not have any of these options */ + + { "uid=", 0, TRUE, "mount owner (by uid or username)", + NULL, ParseUid }, + { "gid=", 0, TRUE, "mount group (by gid or groupname)", + NULL, ParseGid }, + { "fmask=", 0, TRUE, "file umask (in octal)", + NULL, ParseFmask }, + { "dmask=", 0, TRUE, "directory umask (in octal)", + NULL, ParseDmask }, +#ifdef MS_NODEV + { "nodev", MS_NODEV, TRUE, "prevent device node access", + "Setting mount option for accessing device nodes off", NULL }, + { "dev", MS_NODEV, FALSE, "allow device node access (default)", + "Setting mount option for accessing device nodes on", NULL }, +#endif + { "noexec", MS_NOEXEC, TRUE, "prevent program execution", + "Setting mount option for program execution off", NULL }, + { "exec", MS_NOEXEC, FALSE, "allow program execution (default)", + "Setting mount option for program execution on", NULL }, + { "sync", MS_SYNCHRONOUS, TRUE, "file writes are synchronous", + "Setting mount synchronous writes", NULL }, + { "async", MS_SYNCHRONOUS, FALSE, "file writes are asynchronous (default)", + "Setting mount synchronous writes", NULL }, + { "mand", MS_MANDLOCK, TRUE, "allow mandatory locks", + "Setting mount option for allow mandatory locks on", NULL }, + { "nomand", MS_MANDLOCK, FALSE, "prevent mandatory locks (default)", + "Setting mount option for allow mandatory locks off", NULL }, + { "noatime", MS_NOATIME, TRUE, "do not update access times", + "Setting mount option for updating access times off", NULL }, + { "atime", MS_NOATIME, FALSE, "update access times (default)", + "Setting mount option for updating access times on", NULL }, + { "nodiratime", MS_NOATIME, TRUE, "do not update directory access times", + "Setting mount option for updating directory access times off", NULL }, + { "diratime", MS_NOATIME, FALSE, "update access directory times (default)", + "Setting mount option for updating directory access times on", NULL }, + { "ttl=", 0, TRUE, + "time before file attributes must be\n" + "revalidated (in seconds). Improves\n" + "performance but decreases coherency.\n" + "Defaults to 1 if not set.\n", + NULL, ParseTtl }, + { "bind", MS_BIND, TRUE, "perform bind mount", + "Setting mount type to bind", NULL }, + { "bind", MS_MOVE, TRUE, "move an existig mount point", + "Setting mount type to move", NULL }, +#endif + + { "remount", MS_REMOUNT, TRUE, "remount already mounted filesystem", + "Setting mount type to remount", NULL } +}; + /* *----------------------------------------------------------------------------- * @@ -480,9 +692,10 @@ ParseOptions(const char *optionString, // IN: Option string to parse int *flags) // OUT: Flags we might modify { unsigned int commaIndex = 0; - Bool valid, success = FALSE; + Bool success = FALSE; char *key = NULL; char *keyVal = NULL; + int i; ASSERT(optionString); ASSERT(mountInfo); @@ -502,177 +715,103 @@ ParseOptions(const char *optionString, // IN: Option string to parse goto out; } - /* - * Here are all our recognized option keys. Some have corresponding - * values, others don't. - */ - if (strcmp(key, "fmask") == 0) { - char *fmaskString = StrUtil_GetNextToken(&equalsIndex, keyVal, "="); - if (fmaskString != NULL) { - unsigned short fmask; - - /* - * The way to check for an overflow in strtol(3), according to its - * man page. - */ - errno = 0; - fmask = strtol(fmaskString, NULL, 8); - free(fmaskString); - if (errno == 0) { - LOG("Setting mount fmask to %o\n", fmask); - mountInfo->fmask = fmask; - } else { - LOG("Error, overflow in fmask\n"); - goto out; - } - } else { - LOG("Error getting the value for fmask\n"); - goto out; - } - } else if (strcmp(key, "dmask") == 0) { - char *dmaskString = StrUtil_GetNextToken(&equalsIndex, keyVal, "="); - if (dmaskString != NULL) { - unsigned short dmask; - - /* - * The way to check for an overflow in strtol(3), according to its - * man page. - */ - errno = 0; - dmask = strtol(dmaskString, NULL, 8); - free(dmaskString); - if (errno == 0) { - LOG("Setting mount dmask to %o\n", dmask); - mountInfo->dmask = dmask; - } else { - LOG("Error, overflow in dmask\n"); - goto out; - } - } else { - LOG("Error getting the value for dmask\n"); + for (i = 0; i < ARRAYSIZE(mountOptions); i++) { + Bool match = FALSE; + unsigned int idx = 0; + char *optName = StrUtil_GetNextToken(&idx, mountOptions[i].opt, "="); + + if (!optName) { + printf("Failed to parse option name, out of memory?\n"); goto out; } - } else if (strcmp(key, "uid") == 0) { - char *uidString = StrUtil_GetNextToken(&equalsIndex, keyVal, "="); - if (uidString != NULL) { - uid_t uid = 0; - - valid = ParseUid(uidString, &uid); - free(uidString); - if (valid) { - mountInfo->uid = uid; - mountInfo->uidSet = TRUE; - LOG("Setting mount owner to %u\n", uid); + + match = strcmp(key, optName) == 0; + free(optName); + + if (match) { + if (mountOptions[i].handler) { + if (!mountOptions[i].handler(keyVal, mountInfo, flags)) { + goto out; + } } else { - goto out; + if (mountOptions[i].set) { + *flags |= mountOptions[i].flag; + } else { + *flags &= ~mountOptions[i].flag; + } + LOG("%s\n", mountOptions[i].logMsg); } - } else { - LOG("Error getting the value for uid\n"); - goto out; + break; } - } else if (strcmp(key, "gid") == 0) { - char *gidString = StrUtil_GetNextToken(&equalsIndex, keyVal, "="); - if (gidString != NULL) { - gid_t gid = 0; - - valid = ParseGid(gidString, &gid); - free(gidString); - if (valid) { - mountInfo->gid = gid; - mountInfo->gidSet = TRUE; - LOG("Setting mount group to %u\n", gid); - } else { - goto out; - } - } else { - LOG("Error getting the value for gid\n"); - goto out; - } - } else if (strcmp(key, "ttl") == 0) { - int32 ttl; + } - if (StrUtil_GetNextIntToken(&ttl, &equalsIndex, keyVal, "=") && ttl > 0) { - mountInfo->ttl = ttl; - LOG("Setting maximum attribute TTL to %u\n", ttl); - } else { - LOG("Error getting the value for ttl\n"); - goto out; - } - } else if (strcmp(key, "rw") == 0) { // can we write to the mount? - *flags &= ~MS_RDONLY; - LOG("Setting mount read-write\n"); - } else if (strcmp(key, "ro") == 0) { - *flags |= MS_RDONLY; - LOG("Setting mount read-only\n"); - } else if (strcmp(key, "nosuid") == 0) { // allow suid and sgid bits? - *flags |= MS_NOSUID; - LOG("Setting mount option for allowing suid/sgid bits off\n"); - } else if (strcmp(key, "suid") == 0) { - *flags &= ~MS_NOSUID; - LOG("Setting mount option for allowing suid/sgid bits on\n"); -#ifdef MS_NODEV - } else if (strcmp(key, "nodev") == 0) { // allow access to device nodes? - *flags |= MS_NODEV; - LOG("Setting mount option for accessing device nodes off\n"); - } else if (strcmp(key, "dev") == 0) { - *flags &= ~MS_NODEV; - LOG("Setting mount option for accessing device nodes on\n"); -#endif - } else if (strcmp(key, "noexec") == 0) { // allow program execution? - *flags |= MS_NOEXEC; - LOG("Setting mount option for program execution off\n"); - } else if (strcmp(key, "exec") == 0) { - *flags &= ~MS_NOEXEC; - LOG("Setting mount option for program execution on\n"); - } else if (strcmp(key, "sync") == 0) { // meaningless at the moment - *flags |= MS_SYNCHRONOUS; - LOG("Setting mount synchronous writes\n"); - } else if (strcmp(key, "async") == 0) { - *flags &= ~MS_SYNCHRONOUS; - LOG("Setting mount asynchronous writes\n"); - } else if (strcmp(key, "mand") == 0) { // are mandatory locks allowed? - *flags |= MS_MANDLOCK; - LOG("Setting mount option for allow mandatory locks on\n"); - } else if (strcmp(key, "nomand") == 0) { - *flags &= ~MS_MANDLOCK; - LOG("Setting mount option for allow mandatory locks off\n"); - } else if (strcmp(key, "noatime") == 0) { // don't update access times? - *flags |= MS_NOATIME; - LOG("Setting mount option for updating access times off\n"); - } else if (strcmp(key, "atime") == 0) { - *flags &= ~MS_NOATIME; - LOG("Setting mount option for updating access times on\n"); - } else if (strcmp(key, "nodiratime") == 0) { // don't update dir atimes? - *flags |= MS_NODIRATIME; - LOG("Setting mount option for updating directory access times off\n"); - } else if (strcmp(key, "diratime") == 0) { - *flags &= ~MS_NODIRATIME; - LOG("Setting mount option for updating directory access times on\n"); - } else if (strcmp(key, "bind") == 0) { // bind mount? - *flags |= MS_BIND; - LOG("Setting mount type to bind\n"); - } else if (strcmp(key, "move") == 0) { // move an existing mount? - *flags |= MS_MOVE; - LOG("Setting mount type to move\n"); - } else if (strcmp(key, "remount") == 0) { // remount? - *flags |= MS_REMOUNT; - LOG("Setting mount type to remount\n"); - } else { + if (i == ARRAYSIZE(mountOptions)) { LOG("Skipping unrecognized option \"%s\"\n", key); } + free(key); free(keyVal); key = NULL; keyVal = NULL; } + success = TRUE; - out: +out: free(key); free(keyVal); return success; } +/* + *----------------------------------------------------------------------------- + * + * PrintUsage -- + * + * Displays usage for the HGFS mounting utility, and exits with failure. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static void +PrintUsage(void) +{ + int i; + + printf("Usage: %s [-o ]\n", thisProgramBase); + printf("Mount the HGFS share, specified by name, to a local directory.\n"); + printf("Share name must be in host:dir format.\n\nOptions:\n"); + + for (i = 0; i < ARRAYSIZE(mountOptions); i++) { + Bool printOptName = TRUE; + unsigned int tokidx = 0; + char *msg; + + while ((msg = StrUtil_GetNextToken(&tokidx, + mountOptions[i].helpMsg, + "\n")) != NULL) { + printf(" %-15s %s\n", + printOptName ? mountOptions[i].opt : "", msg); + free(msg); + printOptName = FALSE; + } + } + + printf("\n"); + printf("This command is intended to be run from within /bin/mount by\n"); + printf("passing the option '-t %s'. For example:\n", HGFS_NAME); + printf(" mount -t %s .host:/ /mnt/hgfs/\n", HGFS_NAME); + printf(" mount -t %s .host:/foo /mnt/foo\n", HGFS_NAME); + printf(" mount -t %s .host:/foo/bar /var/lib/bar\n", HGFS_NAME); + exit(EXIT_FAILURE); +} + #ifdef VM_HAVE_MTAB /* @@ -923,6 +1062,8 @@ main(int argc, // IN mountInfo.magicNumber = HGFS_SUPER_MAGIC; mountInfo.version = HGFS_PROTOCOL_VERSION; + +#ifndef sun mountInfo.fmask = 0; mountInfo.dmask = 0; mountInfo.uidSet = FALSE; @@ -930,6 +1071,7 @@ main(int argc, // IN mountInfo.ttl = HGFS_DEFAULT_TTL; mountInfo.shareNameHost = shareNameHost; mountInfo.shareNameDir = shareNameDir; +#endif /* * This'll write the rest of the options into HgfsMountInfo and possibly @@ -988,6 +1130,9 @@ main(int argc, // IN } #elif defined(__APPLE__) mntRes = mount(HGFS_NAME, mountPoint, flags, &mountInfo); +#elif defined(sun) + mntRes = mount(mountPoint, mountPoint, MS_DATA | flags, HGFS_NAME, + &mountInfo, sizeof mountInfo); #endif if (mntRes) { perror("Error: cannot mount filesystem"); diff --git a/open-vm-tools/lib/Makefile.am b/open-vm-tools/lib/Makefile.am index 5a06cdbc1..81bcdc48e 100644 --- a/open-vm-tools/lib/Makefile.am +++ b/open-vm-tools/lib/Makefile.am @@ -26,6 +26,9 @@ SUBDIRS += conf SUBDIRS += dict SUBDIRS += deployPkg SUBDIRS += dnd +if HAVE_GTKMM + SUBDIRS += dndGuest +endif SUBDIRS += dynxdr SUBDIRS += err SUBDIRS += eventManager @@ -61,6 +64,7 @@ endif if HAVE_X11 SUBDIRS += resolution endif +SUBDIRS += rpcChannel SUBDIRS += rpcIn SUBDIRS += rpcOut SUBDIRS += rpcVmx diff --git a/open-vm-tools/lib/appUtil/appUtilX11.c b/open-vm-tools/lib/appUtil/appUtilX11.c index 6dbc534df..b7139194d 100644 --- a/open-vm-tools/lib/appUtil/appUtilX11.c +++ b/open-vm-tools/lib/appUtil/appUtilX11.c @@ -152,40 +152,63 @@ AppUtilCollectNamedIcons(GPtrArray *pixbufs, // IN/OUT */ if (iconName[0] != '/') { char *ctmp2; - char *ctmpext; int i; + static const char *iconExtensions[] = { + "", // MUST be first entry. + ".png", + ".xpm", + ".gif", + ".svg", + }; + myIconName = NULL; - ctmpext = strrchr(iconName, '.'); + ctmp2 = g_alloca(PATH_MAX); for (i = 0; !myIconName && i < ARRAYSIZE(extraIconPaths); i++) { + int j; + if (!extraIconPaths[i]) { continue; } - if (ctmpext) { - g_snprintf(ctmp2, PATH_MAX, "%s/%s", extraIconPaths[i], iconName); - if (g_file_test(ctmp2, G_FILE_TEST_EXISTS)) { - myIconName = ctmp2; - foundItInFile = TRUE; + /* + * Per Desktop Entry Specification and Icon Theme Specification, I don't + * believe that the iconName, unless it's an absolute path, should include + * the file extension. + * + * However, empirical evidence shows that -many- applications ignore that + * and may specify an icon of, for example, "foo.png". We'll also handle + * those special cases here. + */ + for (j = 0; !myIconName && j < ARRAYSIZE(iconExtensions); j++) { + g_snprintf(ctmp2, PATH_MAX, "%s/%s%s", extraIconPaths[i], iconName, + iconExtensions[j]); + if (!g_file_test(ctmp2, G_FILE_TEST_EXISTS)) { + continue; } - } else { - static const char *iconExtensions[] = { - ".png", - ".xpm", - ".gif", - ".svg", - }; - int j; - - for (j = 0; j < ARRAYSIZE(iconExtensions); j++) { - g_snprintf(ctmp2, PATH_MAX, "%s/%s%s", - extraIconPaths[i], - iconName, iconExtensions[j]); - if (g_file_test(ctmp2, G_FILE_TEST_EXISTS)) { - myIconName = ctmp2; - foundItInFile = TRUE; - break; + + if (j != 0) { + /* + * Case 1: We located an icon by appending an image extension to + * IconName. Success! + */ + myIconName = ctmp2; // Will break "i" loop. + foundItInFile = TRUE; + break; + } else { + /* + * Case 2: We found an icon without appending an extension. Verify + * that the filename contains a valid image extension. + */ + int k; + char *ctmpext = strrchr(ctmp2, '.'); + for (k = 1; ctmpext && k < ARRAYSIZE(iconExtensions); k++) { + if (strcmp(ctmpext, iconExtensions[k]) == 0) { + myIconName = ctmp2; // Will break "i" loop. + foundItInFile = TRUE; + break; + } } } } diff --git a/open-vm-tools/lib/dnd/Makefile.am b/open-vm-tools/lib/dnd/Makefile.am index d8fab87db..2661ca0d2 100644 --- a/open-vm-tools/lib/dnd/Makefile.am +++ b/open-vm-tools/lib/dnd/Makefile.am @@ -18,5 +18,7 @@ noinst_LTLIBRARIES = libDnd.la libDnd_la_SOURCES = +libDnd_la_SOURCES += dndClipboard.c libDnd_la_SOURCES += dndCommon.c libDnd_la_SOURCES += dndLinux.c +libDnd_la_SOURCES += dndMsg.c diff --git a/open-vm-tools/lib/dnd/dndClipboard.c b/open-vm-tools/lib/dnd/dndClipboard.c new file mode 100644 index 000000000..08e0703b8 --- /dev/null +++ b/open-vm-tools/lib/dnd/dndClipboard.c @@ -0,0 +1,638 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + + +/* + * dndClipboard.c + * + * Implements dndClipboard.h + */ + +#include +#include + +#include "vm_assert.h" + +#include "dndClipboard.h" +#include "dndInt.h" + + +#define CPFormatToIndex(x) ((unsigned int)(x) - 1) + +/* + *---------------------------------------------------------------------------- + * + * CPClipItemInit -- + * + * CPClipboardItem constructor. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +static INLINE void +CPClipItemInit(CPClipItem *item) // IN/OUT: the clipboard item +{ + ASSERT(item); + + item->buf = NULL; + item->size = 0; + item->exists = FALSE; +} + + +/* + *---------------------------------------------------------------------------- + * + * CPClipItemDestroy -- + * + * CPCilpboardItem destructor. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +static INLINE void +CPClipItemDestroy(CPClipItem *item) // IN/OUT: the clipboard item +{ + ASSERT(item); + + free(item->buf); + item->buf = NULL; + item->size = 0; + item->exists = FALSE; +} + + +/* + *---------------------------------------------------------------------------- + * + * CPClipItemCopy -- + * + * Copy clipboard item from src to dest. + * + * Results: + * TRUE on success, FALSE on failure. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +Bool +CPClipItemCopy(CPClipItem *dest, // IN: dest clipboard item + const CPClipItem *src) // IN: source clipboard item +{ + ASSERT(dest); + ASSERT(src); + + if (src->buf) { + void *tmp = dest->buf; + dest->buf = realloc(dest->buf, src->size); + if (!dest->buf) { + dest->buf = tmp; + return FALSE; + } + memcpy(dest->buf, src->buf, src->size); + } + + dest->size = src->size; + dest->exists = src->exists; + + return TRUE; +} + + +/* + *---------------------------------------------------------------------------- + * + * CPClipboard_Init -- + * + * Constructor for CPClipboard. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +void +CPClipboard_Init(CPClipboard *clip) // IN/OUT: the clipboard +{ + unsigned int i; + + ASSERT(clip); + + clip->changed = TRUE; + for (i = CPFORMAT_MIN; i < CPFORMAT_MAX; ++i) { + CPClipItemInit(&clip->items[CPFormatToIndex(i)]); + } +} + + +/* + *---------------------------------------------------------------------------- + * + * CPClipboard_Destroy -- + * + * Destroys a clipboard. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +void +CPClipboard_Destroy(CPClipboard *clip) // IN/OUT: the clipboard +{ + unsigned int i; + + ASSERT(clip); + + for (i = CPFORMAT_MIN; i < CPFORMAT_MAX; ++i) { + CPClipItemDestroy(&clip->items[CPFormatToIndex(i)]); + } +} + + +/* + *---------------------------------------------------------------------------- + * + * CPClipboard_Clear -- + * + * Clears the items in CPClipboard. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +void +CPClipboard_Clear(CPClipboard *clip) // IN/OUT: the clipboard +{ + unsigned int i; + + ASSERT(clip); + + clip->changed = TRUE; + for (i = CPFORMAT_MIN; i < CPFORMAT_MAX; ++i) { + CPClipboard_ClearItem(clip, i); + } +} + + +/* + *---------------------------------------------------------------------------- + * + * CPClipboard_SetItem -- + * + * Makes a copy of the item adds it to the clipboard. If something + * already exists for the format it is overwritten. To set a promised + * type, pass in NULL for buffer and 0 for the size. + * + * Results: + * TRUE on success, FALSE on failure. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +Bool +CPClipboard_SetItem(CPClipboard *clip, // IN/OUT: the clipboard + const DND_CPFORMAT fmt, // IN: the item format + const void *clipitem, // IN: the item + const size_t size) // IN: the item size +{ + CPClipItem *item; + void *newBuf = NULL; + + ASSERT(clip); + + if (!(CPFORMAT_UNKNOWN < fmt && fmt < CPFORMAT_MAX)) { + return FALSE; + } + + if (!CPClipboard_ClearItem(clip, fmt)) { + return FALSE; + } + + item = &clip->items[CPFormatToIndex(fmt)]; + + if (clipitem) { + newBuf = malloc(size); + if (!newBuf) { + return FALSE; + } + memcpy(newBuf, clipitem, size); + } + + item->buf = newBuf; + item->size = size; + item->exists = TRUE; + + return TRUE; +} + + +/* + *---------------------------------------------------------------------------- + * + * CPClipboard_ClearItem -- + * + * Clears the item in the clipboard. + * + * Results: + * TRUE on success, FALSE on failure. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +Bool +CPClipboard_ClearItem(CPClipboard *clip, // IN: the clipboard + DND_CPFORMAT fmt) // IN: item to be cleared +{ + CPClipItem *item; + + ASSERT(clip); + + if (!(CPFORMAT_UNKNOWN < fmt && fmt < CPFORMAT_MAX)) { + return FALSE; + } + + item = &clip->items[CPFormatToIndex(fmt)]; + + free(item->buf); + item->buf = NULL; + item->size = 0; + item->exists = FALSE; + + return TRUE; +} + + +/* + *---------------------------------------------------------------------------- + * + * CPClipboard_GetItem -- + * + * Get the clipboard item of format fmt from the clipboard. The clipboard + * maintains ownership of the data. If the item is promised, the buffer + * will contain NULL and the size will be 0. + * + * Results: + * TRUE if item exists, FALSE otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +Bool +CPClipboard_GetItem(const CPClipboard *clip, // IN: the clipboard + DND_CPFORMAT fmt, // IN: the format + void **buf, // OUT + size_t *size) // OUT +{ + ASSERT(clip); + ASSERT(size); + ASSERT(buf); + + if(!(CPFORMAT_UNKNOWN < fmt && fmt < CPFORMAT_MAX)) { + return FALSE; + } + + if (clip->items[CPFormatToIndex(fmt)].exists) { + *buf = clip->items[CPFormatToIndex(fmt)].buf; + *size = clip->items[CPFormatToIndex(fmt)].size; + return TRUE; + } else { + ASSERT(!clip->items[CPFormatToIndex(fmt)].size); + return FALSE; + } +} + + +/* + *---------------------------------------------------------------------------- + * + * CPClipboard_ItemExists -- + * + * Check if the clipboard item of format fmt exists or not. + * + * Results: + * TRUE if item exists, FALSE otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +Bool +CPClipboard_ItemExists(const CPClipboard *clip, // IN: the clipboard + DND_CPFORMAT fmt) // IN: the format +{ + ASSERT(clip); + + if(!(CPFORMAT_UNKNOWN < fmt && fmt < CPFORMAT_MAX)) { + return FALSE; + } + + return (clip->items[CPFormatToIndex(fmt)].exists && + clip->items[CPFormatToIndex(fmt)].size > 0); +} + + +/* + *---------------------------------------------------------------------------- + * + * CPClipboard_IsEmpty -- + * + * Check if the clipboard item of format fmt exists or not. + * + * Results: + * TRUE if item exists, FALSE otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +Bool +CPClipboard_IsEmpty(const CPClipboard *clip) // IN: the clipboard +{ + unsigned int i; + + ASSERT(clip); + + for (i = CPFORMAT_MIN; i < CPFORMAT_MAX; ++i) { + if (clip->items[CPFormatToIndex(i)].exists && + clip->items[CPFormatToIndex(i)].size > 0) { + return FALSE; + } + } + return TRUE; +} + + +/* + *---------------------------------------------------------------------------- + * + * CPClipboard_SetChanged -- + * + * Set clip->changed. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +void +CPClipboard_SetChanged(CPClipboard *clip, // IN/OUT: the clipboard + Bool changed) // IN +{ + clip->changed = changed; +} + + +/* + *---------------------------------------------------------------------------- + * + * CPClipboard_Changed -- + * + * Return clip->changed. + * + * Results: + * Return clip->changed. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +Bool +CPClipboard_Changed(const CPClipboard *clip) // IN: the clipboard +{ + return clip->changed; +} + + +/* + *---------------------------------------------------------------------------- + * + * CPClipboard_Copy -- + * + * Copies the clipboard contents from src to dest. Assumes that dest has + * been initialized and contains no data. + * + * Results: + * TRUE on success, FALSE on failure. On failure the caller should + * destroy the object to ensure memory is cleaned up. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +Bool +CPClipboard_Copy(CPClipboard *dest, // IN: the desination clipboard + const CPClipboard *src) // IN: the source clipboard +{ + unsigned int i; + + ASSERT(dest); + ASSERT(src); + + for (i = CPFORMAT_MIN; i < CPFORMAT_MAX; ++i) { + if (!CPClipItemCopy(&dest->items[CPFormatToIndex(i)], + &src->items[CPFormatToIndex(i)])) { + return FALSE; + } + } + dest->changed = src->changed; + + return TRUE; +} + + +/* + *---------------------------------------------------------------------------- + * + * CPClipboard_Serialize -- + * + * Serialize the contents of the CPClipboard out to the provided dynbuf. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +Bool +CPClipboard_Serialize(const CPClipboard *clip, // IN + DynBuf *buf) // OUT: the output buffer +{ + DND_CPFORMAT fmt; + uint32 maxFmt = CPFORMAT_MAX; + + ASSERT(clip); + ASSERT(buf); + + /* First append number of formats in clip. */ + if (!DynBuf_Append(buf, &maxFmt, sizeof maxFmt)) { + return FALSE; + } + + /* Append format data one by one. */ + for (fmt = CPFORMAT_MIN; fmt < CPFORMAT_MAX; ++fmt) { + CPClipItem *item = (CPClipItem *)&(clip->items[CPFormatToIndex(fmt)]); + if (!DynBuf_Append(buf, &item->exists, sizeof item->exists) || + !DynBuf_Append(buf, &item->size, sizeof item->size)) { + return FALSE; + } + if (item->exists && (item->size > 0) && + !DynBuf_Append(buf, item->buf, item->size)) { + return FALSE; + } + } + + if (!DynBuf_Append(buf, &clip->changed, sizeof clip->changed)) { + return FALSE; + } + + return TRUE; +} + + +/* + *---------------------------------------------------------------------------- + * + * CPClipboard_Unserialize -- + * + * Unserialize the arguments of the CPClipboard provided by the buffer. + * On failure the clip will be destroyed. + * + * Results: + * TRUE if success, FALSE otherwise + * + * Side effects: + * The clip passed in should be empty, otherwise will cause memory leakage. + * On success, arguments found in buf are unserialized into clip. + * + *---------------------------------------------------------------------------- + */ + +Bool +CPClipboard_Unserialize(CPClipboard *clip, // OUT: the clipboard + void *buf, // IN: input buffer + size_t len) // IN: buffer length +{ + DND_CPFORMAT fmt; + BufRead r; + uint32 maxFmt; + + ASSERT(clip); + ASSERT(buf); + + CPClipboard_Init(clip); + + r.pos = buf; + r.unreadLen = len; + + /* First get number of formats in the buf. */ + if (!DnDReadBuffer(&r, &maxFmt, sizeof maxFmt)) { + goto error; + } + + /* This version only supports number of formats up to CPFORMAT_MAX. */ + maxFmt = MIN(CPFORMAT_MAX, maxFmt); + + for (fmt = CPFORMAT_MIN; fmt < maxFmt; ++fmt) { + Bool exists; + uint32 size; + + if (!DnDReadBuffer(&r, &exists, sizeof exists) || + !DnDReadBuffer(&r, &size, sizeof size)) { + goto error; + } + + if (exists && size) { + if (size > r.unreadLen) { + goto error; + } + + if (!CPClipboard_SetItem(clip, fmt, r.pos, size)) { + goto error; + } + if (!DnDSlideBuffer(&r, size)) { + goto error; + } + } + } + + /* It is possible that clip->changed is missing in some beta products. */ + if (r.unreadLen == sizeof clip->changed && + !DnDReadBuffer(&r, &clip->changed, sizeof clip->changed)) { + goto error; + } + + return TRUE; + +error: + CPClipboard_Destroy(clip); + return FALSE; +} diff --git a/open-vm-tools/lib/dnd/dndMsg.c b/open-vm-tools/lib/dnd/dndMsg.c new file mode 100644 index 000000000..c2971053e --- /dev/null +++ b/open-vm-tools/lib/dnd/dndMsg.c @@ -0,0 +1,499 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + +/* + * dndMsg.c -- + * + * DnDMsg represents an rpc message which is sent across the + * wire. Any args that it holds will be written out exactly as stored. + * + * To protect itself there are many checks to ensure the data which is + * serialized and unserialized is sane. Defines and asserts are used to + * ensure the message stays under these limits when serializing out and + * checks are enforced to ensure that the data to be unserialized remains + * under these limits. + */ + +#include +#include +#include "vm_assert.h" + +#include "dndMsg.h" +#include "dndInt.h" + +#define LOGLEVEL_MODULE dnd +#include "loglevel_user.h" + + +/* + *---------------------------------------------------------------------------- + * + * DnDMsg_Init -- + * + * DnDMsg constructor. + * + * Results: + * None. + * + * Side effects: + * None + * + *---------------------------------------------------------------------------- + */ + +void +DnDMsg_Init(DnDMsg *msg) // IN/OUT: the message +{ + ASSERT(msg); + + msg->ver = 3; + msg->cmd = 0; + msg->nargs = 0; + DynBufArray_Init(&msg->args, 0); + msg->expectedArgsSz = 0; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDMsg_Destroy -- + * + * Destroys a message by clearing any of the data that is contained in it. + * + * Results: + * None + * + * Side effects: + * Frees the arguments' memory. + * + *---------------------------------------------------------------------------- + */ + +void +DnDMsg_Destroy(DnDMsg *msg) // IN/OUT: the message +{ + uint32 i; + uint32 count; + + ASSERT(msg); + + count = DynArray_Count(&msg->args); + + msg->ver = 0; + msg->cmd = 0; + msg->nargs = 0; + msg->expectedArgsSz = 0; + + for (i = 0; i < count; ++i) { + DynBuf *b = DynArray_AddressOf(&msg->args, i); + DynBuf_Destroy(b); + } + DynArray_SetCount(&msg->args, 0); + DynBufArray_Destroy(&msg->args); +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDMsg_Cmd -- + * + * Gets the dnd/copy paste command from the header. + * + * Results: + * An uint32 representing the command. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +uint32 +DnDMsg_GetCmd(DnDMsg *msg) // IN/OUT: the message +{ + ASSERT(msg); + + return msg->cmd; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDMsg_SetCmd -- + * + * Sets the command for the message. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +void +DnDMsg_SetCmd(DnDMsg *msg, // IN/OUT: the message + uint32 cmd) // IN: the command +{ + ASSERT(msg); + ASSERT((DND_INVALID < cmd && cmd < DND_MAX) || + (CP_INVALID < cmd && cmd < CP_MAX)); + + msg->cmd = cmd; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDMsg_NumArgs -- + * + * Determines the number of arguments currently in the DnDMsg. + * + * Results: + * The number of arguments. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +uint32 +DnDMsg_NumArgs(DnDMsg *msg) // IN/OUT: the message +{ + ASSERT(msg); + + return DynBufArray_Count(&msg->args); +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDMsg_GetArg -- + * + * Gets an argument stored in DnDMsg. + * + * Results: + * Null if the argument is out of bounds, otherwise a pointer to a dynbuf + * containing the argument. + * This dynbuf is still + * managed by the DnDMsg and should NOT be destroyed. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +DynBuf * +DnDMsg_GetArg(DnDMsg *msg, // IN/OUT: the message + uint32 idx) // IN: the argument to return +{ + ASSERT(msg); + ASSERT(0 <= idx && idx < DynBufArray_Count(&msg->args)); + + return DynArray_AddressOf(&msg->args, idx); +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDMsg_AppendArg -- + * + * Adds the data to the end of the argument list in the message. It will + * create a copy of the data to be mananged by DnDMsg until the message is + * destroyed. + * + * Results: + * TRUE on success, FALSE on failure. + * + * Side effects: + * Increases the internal arg size counter. + * + *---------------------------------------------------------------------------- + */ + +Bool +DnDMsg_AppendArg(DnDMsg *msg, // IN/OUT: the message + void *buf, // IN: the input buffer + size_t len) // IN: the length of the input buffer +{ + DynBuf clonebuf; + + ASSERT(msg); + ASSERT(buf); + + if (DynBufArray_Count(&msg->args) >= DNDMSG_MAX_ARGS) { + return FALSE; + } + + DynBuf_Init(&clonebuf); + if (!DynBuf_Append(&clonebuf, buf, len)) { + goto err; + } + + /* The dynbufarray now owns the clonebuf data. */ + if (!DynBufArray_Push(&msg->args, clonebuf)) { + goto err; + } + return TRUE; + +err: + DynBuf_Destroy(&clonebuf); + return FALSE; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDMsg_Serialize -- + * + * Serialize the contents of the DnDMsg out to the provided dynbuf. It + * will ASSERT if any invariants are broken. + * + * Results: + * TRUE on success. + * FALSE on failure. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +Bool +DnDMsg_Serialize(DnDMsg *msg, // IN/OUT: the message + DynBuf* buf) // OUT: the output buffer +{ + DynBuf *curArg; + uint32 nargs; + uint32 i; + uint32 serializeArgsSz = 0; + + ASSERT(msg); + ASSERT(buf); + ASSERT((DND_INVALID < msg->cmd && msg->cmd < DND_MAX) || + (CP_INVALID < msg->cmd && msg->cmd < CP_MAX)); + + nargs = DynBufArray_Count(&msg->args); + + for (i = 0; i < nargs; ++i) { + DynBuf *b = DynArray_AddressOf(&msg->args, i); + serializeArgsSz += sizeof(uint32) + DynBuf_GetSize(b); + } + + if (DynBuf_Append(buf, &msg->ver, sizeof msg->ver) && + DynBuf_Append(buf, &msg->cmd, sizeof msg->cmd) && + DynBuf_Append(buf, &nargs, sizeof nargs) && + DynBuf_Append(buf, &serializeArgsSz, sizeof serializeArgsSz)) { + int i; + uint32 curArgsSz; + + for (i = 0; i < nargs; i++) { + curArg = DynBufArray_AddressOf(&msg->args, i); + curArgsSz = DynBuf_GetSize(curArg); + + if (!DynBuf_Append(buf, &curArgsSz, sizeof curArgsSz) || + !DynBuf_Append(buf, DynBuf_Get(curArg), curArgsSz)) { + return FALSE; + } + } + } else { + return FALSE; + } + + return TRUE; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDMsg_UnserializeHeader -- + * + * Read the header from the buffer into a DnDMsg. Any contents in the + * DnDMsg will be destroyed. This allows you to retrieve header + * information. These functions are specified in the dndMsg.h. Most + * notably, you can retrieve the size of the arguments so that you can + * pass a properly sized buffer to DnDMsg_UnserializeArgs. + * + * This is the one of the two places that nargs is set. The other is + * implicitly set by DnDMsg_AppendArg with the push and only ever + * realized through the DnDMsg_Serialize function. expectedArgsSz, + * curArgSz follows the same idea. + * + * Results: + * DNDMSG_SUCCESS on success. + * DNDMSG_INPUT_TOO_SMALL when provided buffer is too small. + * DNDMSG_INPUT_ERR when the provided buffer is inconsistant. + * DNDMSG_NOMEM when we run out of memory. + * DNDMSG_ERR on any other error. + * + * Side effects: + * On success the msg's header will be filled. On failure the msg will be + * destroyed. + * + *---------------------------------------------------------------------------- + */ + +DnDMsgErr +DnDMsg_UnserializeHeader(DnDMsg *msg, // IN/OUT: the message + void *buf, // IN: the input buffer + size_t len) // IN: the buffer length +{ + BufRead r; + + ASSERT(msg); + ASSERT(buf); + + r.pos = buf; + r.unreadLen = len; + + if (len < DNDMSG_HEADERSIZE_V3) { + return DNDMSG_INPUT_TOO_SMALL; + } + + /* Read buffer into msg. */ + if (DnDReadBuffer(&r, &msg->ver, sizeof msg->ver) && + DnDReadBuffer(&r, &msg->cmd, sizeof msg->cmd) && + DnDReadBuffer(&r, &msg->nargs, sizeof msg->nargs) && + DnDReadBuffer(&r, &msg->expectedArgsSz, sizeof msg->expectedArgsSz)) { + /* Sanity checks. */ + if (msg->expectedArgsSz < DNDMSG_MAX_ARGSZ && + (msg->cmd < DND_MAX || msg->cmd < CP_MAX) && + 0 < msg->cmd && + msg->ver >= 3 && + msg->nargs < DNDMSG_MAX_ARGS) { + return DNDMSG_SUCCESS; + } else { + return DNDMSG_INPUT_ERR; + } + } else { + return DNDMSG_INPUT_TOO_SMALL; + } +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDMsg_UnserializeArgs -- + * + * Unserialize the arguments of the message provided by the buffer. + * Each argument is a uint32 of the size followed by the buffer. On + * failure the message will revert to the state which was passed into the + * function. + * + * Results: + * DNDMSG_SUCCESS on success. + * DNDMSG_INPUT_TOO_SMALL when provided buffer is too small. + * DNDMSG_INPUT_ERR when the provided buffer is inconsistant. + * DNDMSG_NOMEM when we run out of memory. + * DNDMSG_ERR on any other error. + * + * Side effects: + * On success, arguments found in buf are unserialized into msg. + * + *---------------------------------------------------------------------------- + */ + +DnDMsgErr +DnDMsg_UnserializeArgs(DnDMsg *msg, // IN/OUT: the message + void *buf, // IN: input buffer + size_t len) // IN: buffer length +{ + uint32 i; + uint32 count; + BufRead r; + uint32 readArgsSz = 0; + + void *data = NULL; + DnDMsgErr ret = DNDMSG_SUCCESS; + + ASSERT(msg); + ASSERT(DynBufArray_Count(&msg->args) == 0); + ASSERT(buf); + + r.pos = buf; + r.unreadLen = len; + + if (len < msg->expectedArgsSz) { + return DNDMSG_INPUT_TOO_SMALL; + } + + for (i = 0; i < msg->nargs; ++i) { + uint32 argSz; + if (!DnDReadBuffer(&r, &argSz, sizeof argSz)) { + ret = DNDMSG_INPUT_TOO_SMALL; + goto err; + } + + if (argSz > DNDMSG_MAX_ARGSZ || + readArgsSz + sizeof (uint32) + argSz > msg->expectedArgsSz) { + ret = DNDMSG_INPUT_ERR; + goto err; + } + + data = malloc(argSz); + if (!data) { + ret = DNDMSG_NOMEM; + goto err; + } + + if (!DnDReadBuffer(&r, data, argSz)) { + ret = DNDMSG_ERR; + goto err; + } + + if (!DnDMsg_AppendArg(msg, data, argSz)) { + ret = DNDMSG_NOMEM; + goto err; + } + readArgsSz += argSz + sizeof (uint32); + free(data); + } + + ASSERT(ret == DNDMSG_SUCCESS); + return ret; + +err: + if (data) { + free(data); + } + + count = DynBufArray_Count(&msg->args); + for (i = 0; i < count; ++i) { + DynBuf *b = DynArray_AddressOf(&msg->args, i); + DynBuf_Destroy(b); + } + /* + * DnDMsg_AppendArg relies on DynBufArray_Push, hence the count needs to be + * reset. + */ + DynBufArray_SetCount(&msg->args, 0); + + return ret; +} diff --git a/open-vm-tools/lib/dndGuest/Makefile.am b/open-vm-tools/lib/dndGuest/Makefile.am new file mode 100644 index 000000000..edbbad999 --- /dev/null +++ b/open-vm-tools/lib/dndGuest/Makefile.am @@ -0,0 +1,35 @@ +################################################################################ +### Copyright 2009 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 +################################################################################ + +noinst_LTLIBRARIES = libDndGuest.la + +AM_CFLAGS = @GTK_CPPFLAGS@ +AM_CFLAGS += @GTKMM_CPPFLAGS@ +AM_CFLAGS += @VMWARE_USER_CPPFLAGS@ +AM_CFLAGS += -I$(top_builddir)/include + +AM_CPPFLAGS = @GTK_CPPFLAGS@ +AM_CPPFLAGS += @GTKMM_CPPFLAGS@ +AM_CPPFLAGS += @VMWARE_USER_CPPFLAGS@ +AM_CPPFLAGS += -I$(top_builddir)/include + +libDndGuest_la_SOURCES = +libDndGuest_la_SOURCES += copyPaste.cc +libDndGuest_la_SOURCES += copyPasteRpcV3.cc +libDndGuest_la_SOURCES += dnd.cc +libDndGuest_la_SOURCES += dndFileList.cc +libDndGuest_la_SOURCES += dndTransportGuestRpc.cc diff --git a/open-vm-tools/lib/dndGuest/copyPaste.cc b/open-vm-tools/lib/dndGuest/copyPaste.cc new file mode 100644 index 000000000..dcec0ff55 --- /dev/null +++ b/open-vm-tools/lib/dndGuest/copyPaste.cc @@ -0,0 +1,391 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + +/* + * copyPaste.cc -- + * + * Implementation of common layer CopyPaste object for guest. + */ + +#include "copyPaste.hh" +#include "copyPasteRpcV3.hh" +#include "dndFileList.hh" + +extern "C" { + #include "util.h" + #include "debug.h" + #include "file.h" + #include "str.h" + #include "cpNameUtil.h" + #include "hostinfo.h" + #include "dndClipboard.h" +} + +/* + *--------------------------------------------------------------------- + * + * CopyPaste::CopyPaste -- + * + * Constructor for CopyPaste. + * + * Results: + * None. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------- + */ + +CopyPaste::CopyPaste(void) // IN + : mRpc(NULL), + mVmxCopyPasteVersion(0) +{ + mState = CPSTATE_INVALID; +} + + +/* + *--------------------------------------------------------------------- + * + * CopyPaste::~CopyPaste -- + * + * Destructor. + * + * Results: + * None. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------- + */ + +CopyPaste::~CopyPaste(void) +{ + delete mRpc; +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPaste::VmxCopyPasteVersionChanged -- + * + * Vmx CopyPaste version changed so should create corresponding Rpc and + * register callbacks for signals if version changed. + * + * Results: + * None + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +CopyPaste::VmxCopyPasteVersionChanged(struct RpcIn *rpcIn, // IN + uint32 version) // IN +{ + /* Do nothing if version is not changed. */ + if (mVmxCopyPasteVersion == version) { + return; + } + + mVmxCopyPasteVersion = version; + Debug("%s: Version: %d", __FUNCTION__, version); + + delete mRpc; + mRpc = NULL; + + mState = CPSTATE_INVALID; + Hostinfo_GetTimeOfDay(&mStateChangeTime); + + switch (version) { + case 1: + case 2: + /* Here should create CopyPasteRpcV2 for version 1 & 2. */ + break; + case 3: + mRpc = new CopyPasteRpcV3(rpcIn); + break; + default: + Debug("%s: got unsupported guest CopyPaste version %u.\n", + __FUNCTION__, version); + return; + } + + if (mRpc) { + mRpc->ghGetClipboardChanged.connect( + sigc::mem_fun(this, &CopyPaste::OnGetLocalClipboard)); + mRpc->hgSetClipboardChanged.connect( + sigc::mem_fun(this, &CopyPaste::OnGetRemoteClipboardDone)); + mRpc->hgFileCopyDoneChanged.connect( + sigc::mem_fun(this, &CopyPaste::OnHGFileCopyDone)); + mState = CPSTATE_READY; + } +} + + +/* + *---------------------------------------------------------------------- + * + * CopyPaste::OnGetRemoteClipboardDone -- + * + * Got clipboard data from host. Put it into local buffer and + * inform UI to process the clipboard data. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +CopyPaste::OnGetRemoteClipboardDone(const CPClipboard *clip) // IN: new clipboard +{ + newClipboard.emit(clip); + mState = CPSTATE_READY; + Hostinfo_GetTimeOfDay(&mStateChangeTime); +} + + +/* + *---------------------------------------------------------------------- + * + * CopyPaste::OnGetLocalClipboard -- + * + * Host is asking for guest clipboard data. Try to get it from local + * UI and send to host with GHGetClipboardDone. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +CopyPaste::OnGetLocalClipboard(void) +{ + CPClipboard clip; + bool ready; + + CPClipboard_Init(&clip); + ready = localGetClipboard.emit(&clip); + /* + * if caller decided to handle this asyncronously, it is responsible for + * calling SetRemoteClipboard() when it has completed. Otherwise, call it + * now. + */ + if (ready) { + SetRemoteClipboard(&clip); + } + CPClipboard_Destroy(&clip); +} + + +/* + *---------------------------------------------------------------------- + * + * CopyPaste::OnHGFileCopyDone -- + * + * Host finished file copy. Inform UI to remove the block. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +CopyPaste::OnHGFileCopyDone(bool success) +{ + if (!success && !mStagingDir.empty()) { + /* Delete all files if host canceled the file transfer. */ + DnD_DeleteStagingFiles(mStagingDir.c_str(), FALSE); + mStagingDir.clear(); + } + localGetFilesDoneChanged.emit(success); +} + + +/* + *---------------------------------------------------------------------------- + * + * CopyPaste::SetRemoteClipboard -- + * + * Sets the internal clipboard. Also synchronizes remote clipboard with + * the guest clipboard. + * + * Results: + * True if success, false otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +bool +CopyPaste::SetRemoteClipboard(const CPClipboard* clip) // IN: new clipboard +{ + if (!mRpc) { + Debug("%s: no valid rpc, guest version is %u.\n", + __FUNCTION__, mVmxCopyPasteVersion); + return false; + } + + if (mState == CPSTATE_INVALID) { + Debug("%s: Invalid state.", __FUNCTION__); + return false; + } + + if (!IsCopyPasteAllowed()) { + Debug("%s: CopyPasteAllowed() returned false.", __FUNCTION__); + return false; + } + + /* Send clipboard data to remote side. */ + mRpc->GHGetClipboardDone(clip); + + return true; +} + + +/* + *---------------------------------------------------------------------------- + * + * CopyPaste::GetFiles -- + * + * Gets the files that are referenced on the clipboard. + * + * Results: + * The destination directory if we can start file copy, otherwise empty + * string. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +std::string +CopyPaste::GetFiles(std::string dir) // IN: suggested destination directory + // in local format +{ + std::string destDir; + char cpName[FILE_MAXPATH]; + int32 cpNameSize; + + if (mState == CPSTATE_INVALID) { + Debug("%s: Invalid state.", __FUNCTION__); + return ""; + } + + /* Setup staging directory. */ + destDir = SetupDestDir(dir); + if (destDir.empty()) { + return ""; + } + + /* XXX Here should first convert encoding to precomposed UTF8. */ + + /* Convert staging name to CP format. */ + cpNameSize = CPNameUtil_ConvertToRoot(destDir.c_str(), + sizeof cpName, + cpName); + if (cpNameSize < 0) { + Debug("%s: Error, could not convert to CPName.\n", __FUNCTION__); + return ""; + } + + if (!mRpc->HGStartFileCopy(cpName, cpNameSize)) { + return ""; + } + + mStagingDir = destDir; + + return destDir; +} + + +/* + *---------------------------------------------------------------------------- + * + * CopyPaste::SetupDestDir -- + * + * Creates a directory for file transfer. If the destination + * dir is provided, we will attempt to copy files to the directory. + * + * Input directory will be in local format. + * + * Results: + * The destination directory on success, an empty string on failure. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +std::string +CopyPaste::SetupDestDir(const std::string &destDir) // IN: +{ + static const std::string failDir = ""; + + if (!destDir.empty() && File_Exists(destDir.c_str())) { + mStagingDir = destDir; + const char *lastSep = Str_Strrchr(mStagingDir.c_str(), DIRSEPC); + if (lastSep && lastSep[1] != '\0') { + mStagingDir += DIRSEPS; + } + + return mStagingDir; + } else { + char *newDir; + + newDir = DnD_CreateStagingDirectory(); + if (newDir != NULL) { + mStagingDir = newDir; + + char *lastSep = Str_Strrchr(newDir, DIRSEPC); + if (lastSep && lastSep[1] != '\0') { + mStagingDir += DIRSEPS; + } + free(newDir); + Debug("%s: destdir: %s", __FUNCTION__, mStagingDir.c_str()); + + return mStagingDir; + } else { + Debug("%s: destdir not created", __FUNCTION__); + return failDir; + } + } +} + + diff --git a/open-vm-tools/lib/dndGuest/copyPasteRpcV3.cc b/open-vm-tools/lib/dndGuest/copyPasteRpcV3.cc new file mode 100644 index 000000000..d413aa47c --- /dev/null +++ b/open-vm-tools/lib/dndGuest/copyPasteRpcV3.cc @@ -0,0 +1,266 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + +/* + * CopyPasteRpcV3.cc -- + * + * Implementation of the CopyPasteRpcV3 interface. + */ + + +#include "copyPasteRpcV3.hh" +#include "dndTransportGuestRpc.hh" + +extern "C" { + #include "dndMsg.h" + #include "debug.h" + #include "dndClipboard.h" +} + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteRpcV3 -- + * + * Constructor of CopyPasteRpcV3 class. + * + * Results: + * None + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +CopyPasteRpcV3::CopyPasteRpcV3(struct RpcIn *rpcIn) // IN +{ + mTransport = new DnDTransportGuestRpc(rpcIn, "copypaste.transport"); + mTransport->recvMsgChanged.connect(sigc::mem_fun(this, &CopyPasteRpcV3::OnRecvMsg)); +} + + +/* + *---------------------------------------------------------------------- + * + * CopyPasteRpcV3::~CopyPasteRpcV3 -- + * + * Destructor of CopyPasteRpcV3. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +CopyPasteRpcV3::~CopyPasteRpcV3(void) +{ + delete mTransport; +} + + +/* + *---------------------------------------------------------------------- + * + * CopyPasteRpcV3::GHGetClipboardDone -- + * + * Serialize all parameters and pass to transport layer for + * CP_GH_GET_CLIPBOARD_DONE signal. + * + * Results: + * TRUE if success, FALSE otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +bool +CopyPasteRpcV3::GHGetClipboardDone(const CPClipboard* clip) // IN +{ + DnDMsg msg; + DynBuf buf; + bool ret = FALSE; + + DnDMsg_Init(&msg); + DynBuf_Init(&buf); + + /* Serialize clip and output into buf. */ + if (!CPClipboard_Serialize(clip, &buf)) { + Debug("%s: CPClipboard_Serialize failed.\n", __FUNCTION__); + goto exit; + } + + /* Construct msg with both cmd CP_GH_GET_CLIPBOARD_DONE and buf. */ + DnDMsg_SetCmd(&msg, CP_GH_GET_CLIPBOARD_DONE); + if (!DnDMsg_AppendArg(&msg, DynBuf_Get(&buf), DynBuf_GetSize(&buf))) { + Debug("%s: DnDMsg_AppendData failed.\n", __FUNCTION__); + goto exit; + } + + /* Reset buf. */ + DynBuf_Destroy(&buf); + DynBuf_Init(&buf); + + /* Serialize msg and output to buf. */ + if (!DnDMsg_Serialize(&msg, &buf)) { + Debug("%s: DnDMsg_Serialize failed.\n", __FUNCTION__); + goto exit; + } + + ret = mTransport->SendMsg((uint8 *)DynBuf_Get(&buf), DynBuf_GetSize(&buf)); + +exit: + DynBuf_Destroy(&buf); + DnDMsg_Destroy(&msg); + return ret; +} + + +/* + *---------------------------------------------------------------------- + * + * CopyPasteRpcV3::HGStartFileCopy -- + * + * Serialize all parameters and pass to transport layer for + * CP_HG_START_FILE_COPY signal. + * + * Results: + * TRUE if success, FALSE otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +bool +CopyPasteRpcV3::HGStartFileCopy(const char *stagingDirCP, // IN + size_t sz) // IN +{ + DnDMsg msg; + DynBuf buf; + bool ret = FALSE; + + DnDMsg_Init(&msg); + DynBuf_Init(&buf); + + /* Construct msg with both cmd CP_HG_START_FILE_COPY and stagingDirCP. */ + DnDMsg_SetCmd(&msg, CP_HG_START_FILE_COPY); + if (!DnDMsg_AppendArg(&msg, (void *)stagingDirCP, sz)) { + Debug("%s: DnDMsg_AppendData failed.\n", __FUNCTION__); + goto exit; + } + + /* Serialize msg and output to buf. */ + if (!DnDMsg_Serialize(&msg, &buf)) { + Debug("%s: DnDMsg_Serialize failed.\n", __FUNCTION__); + goto exit; + } + + ret = mTransport->SendMsg((uint8 *)DynBuf_Get(&buf), DynBuf_GetSize(&buf)); + +exit: + DynBuf_Destroy(&buf); + DnDMsg_Destroy(&msg); + return ret; +} + + +/* + *---------------------------------------------------------------------- + * + * CopyPasteRpcV3::OnRecvMsg -- + * + * Received a new message from transport layer. Unserialize and + * translate it and emit corresponding signal. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +CopyPasteRpcV3::OnRecvMsg(const uint8 *data, // IN + size_t dataSize) // IN +{ + DnDMsg msg; + DnDMsgErr ret; + DynBuf *buf = NULL; + + DnDMsg_Init(&msg); + + ret = DnDMsg_UnserializeHeader(&msg, (void *)data, dataSize); + if (DNDMSG_SUCCESS != ret) { + Debug("%s: DnDMsg_UnserializeHeader failed with %d\n", + __FUNCTION__, ret); + goto exit; + } + + ret = DnDMsg_UnserializeArgs(&msg, + (void *)&data[DNDMSG_HEADERSIZE_V3], + dataSize - DNDMSG_HEADERSIZE_V3); + if (DNDMSG_SUCCESS != ret) { + Debug("%s: DnDMsg_UnserializeArgs failed with %d\n", + __FUNCTION__, ret); + goto exit; + } + + /* Translate command and emit signal. */ + switch (DnDMsg_GetCmd(&msg)) { + case CP_GH_GET_CLIPBOARD: + ghGetClipboardChanged.emit(); + break; + case CP_HG_SET_CLIPBOARD: + { + CPClipboard clip; + + /* Unserialize clipboard data for the command. */ + buf = DnDMsg_GetArg(&msg, 0); + if (!CPClipboard_Unserialize(&clip, DynBuf_Get(buf), DynBuf_GetSize(buf))) { + Debug("%s: CPClipboard_Unserialize failed.\n", __FUNCTION__); + goto exit; + } + hgSetClipboardChanged.emit(&clip); + break; + } + case CP_HG_FILE_COPY_DONE: + { + bool success = false; + buf = DnDMsg_GetArg(&msg, 0); + if (sizeof success == DynBuf_GetSize(buf)) { + memcpy(&success, DynBuf_Get(buf), DynBuf_GetSize(buf)); + } + hgFileCopyDoneChanged.emit(success); + break; + } + default: + Debug("%s: got unsupported new command %d.\n", + __FUNCTION__, DnDMsg_GetCmd(&msg)); + } +exit: + DnDMsg_Destroy(&msg); +} diff --git a/open-vm-tools/lib/dndGuest/dnd.cc b/open-vm-tools/lib/dndGuest/dnd.cc new file mode 100644 index 000000000..afb8fda1a --- /dev/null +++ b/open-vm-tools/lib/dndGuest/dnd.cc @@ -0,0 +1,727 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + +/* + * dnd.cc -- + * + * Implementation of common layer DnD object. + */ + +#include "dnd.hh" +#include "dndRpcV3.hh" + +extern "C" { + #include "util.h" + #include "debug.h" + #include "file.h" + #include "cpNameUtil.h" + #include "str.h" + #include "eventManager.h" +} + +#define UNGRAB_TIMEOUT 50 // 0.5 s + + +/* + *--------------------------------------------------------------------- + * + * DnDUngrabTimeout -- + * + * UngrabTimeout callback for GH DnD. + * + * Results: + * Always TRUE. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------- + */ + +static Bool +DnDUngrabTimeout(void *clientData) // IN +{ + ASSERT(clientData); + DnD *dnd = (DnD *)clientData; + dnd->UngrabTimeout(); + return TRUE; +} + + +/* + *--------------------------------------------------------------------- + * + * DnD::DnD -- + * + * Constructor for DnD. + * + * Results: + * None. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------- + */ + +DnD::DnD(DblLnkLst_Links *eventQueue) // IN + : mRpc(NULL), + mVmxDnDVersion(0), + mDnDAllowed(false), + mStagingDir(""), + mUngrabTimeout(NULL), + mEventQueue(eventQueue) +{ + mState = DNDSTATE_INVALID; + mFeedback = DROP_UNKNOWN; + CPClipboard_Init(&mClipboard); +} + + +/* + *--------------------------------------------------------------------- + * + * DnD::~DnD -- + * + * Destructor. + * + * Results: + * None. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------- + */ + +DnD::~DnD(void) +{ + delete mRpc; + CPClipboard_Destroy(&mClipboard); +} + + +/* + *----------------------------------------------------------------------------- + * + * DnD::VmxDnDVersionChanged -- + * + * Guest DnD version changed so should create corresponding Rpc and + * register callbacks for signals if version changed. + * + * Results: + * None + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +DnD::VmxDnDVersionChanged(struct RpcIn *rpcIn, // IN + uint32 version) // IN +{ + /* Do nothing if version is not changed. */ + if (mVmxDnDVersion == version) { + return; + } + + mVmxDnDVersion = version; + + delete mRpc; + mRpc = NULL; + mState = DNDSTATE_INVALID; + Debug("%s: state changed to INVALID\n", __FUNCTION__); + + switch (version) { + case 1: + case 2: + /* Here should create DnDRpcV2 for version 1 & 2. */ + break; + case 3: + mRpc = new DnDRpcV3(rpcIn); + break; + default: + Debug("%s: got unsupported vmx DnD version %u.\n", __FUNCTION__, version); + return; + } + + if (mRpc) { + mRpc->ghUpdateUnityDetWndChanged.connect( + sigc::mem_fun(this, &DnD::OnGHUpdateUnityDetWnd)); + mRpc->ghQueryPendingDragChanged.connect( + sigc::mem_fun(this, &DnD::OnGHQueryPendingDrag)); + mRpc->ghCancelChanged.connect( + sigc::mem_fun(this, &DnD::OnGHCancel)); + mRpc->hgDragEnterChanged.connect( + sigc::mem_fun(this, &DnD::OnHGDragEnter)); + mRpc->hgDragStartChanged.connect( + sigc::mem_fun(this, &DnD::OnHGDragStart)); + mRpc->hgCancelChanged.connect( + sigc::mem_fun(this, &DnD::OnHGCancel)); + mRpc->hgDropChanged.connect( + sigc::mem_fun(this, &DnD::OnHGDrop)); + mRpc->hgFileCopyDoneChanged.connect( + sigc::mem_fun(this, &DnD::OnHGFileCopyDone)); + mState = DNDSTATE_READY; + Debug("%s: state changed to READY\n", __FUNCTION__); + } +} + + +/* + *---------------------------------------------------------------------- + * + * DnD::OnHGDragEnter -- + * + * This callback is trigged when host got dragEnter signal and + * related DnD data. Put data into local buffer, show detection + * window and inform host UI to simulate first mouse click in the + * guest. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +DnD::OnHGDragEnter(const CPClipboard *clip) // IN +{ + if (mState != DNDSTATE_READY && + mState != DNDSTATE_ENTERING) { + /* Reset DnD for any wrong state. */ + Debug("%s: Bad state: %d\n", __FUNCTION__, mState); + ResetDnD(); + return; + } + + CPClipboard_Clear(&mClipboard); + CPClipboard_Copy(&mClipboard, clip); + + /* Show detection window in (0, 0). */ + updateDetWndChanged.emit(true, 0, 0); + + /* + * Ask host to simulate mouse click inside the detection window. + * Host should simulate all guest dragging for HG DnD. + */ + mRpc->HGDragEnterDone(DRAG_DET_WINDOW_WIDTH / 2, + DRAG_DET_WINDOW_WIDTH / 2); + mState = DNDSTATE_ENTERING; + Debug("%s: state changed to ENTERING\n", __FUNCTION__); +} + + +/* + *---------------------------------------------------------------------- + * + * DnD::OnHGDragStart -- + * + * Host just simulated the first mouse click inside the detection + * window. Now guest can initialize HG DnD. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +DnD::OnHGDragStart(void) // IN +{ + if (mState != DNDSTATE_ENTERING) { + /* Reset DnD for any wrong state. */ + Debug("%s: Bad state: %d\n", __FUNCTION__, mState); + ResetDnD(); + return; + } + + /* Setup staging directory. */ + mStagingDir = SetupDestDir(""); + if (mStagingDir.empty()) { + return; + } + + dragStartChanged.emit(&mClipboard, mStagingDir); +} + + +/* + *---------------------------------------------------------------------- + * + * DnD::HGDragStartDone -- + * + * Tell host we are done with DnD initialization. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +DnD::HGDragStartDone(void) +{ + if (mState != DNDSTATE_ENTERING) { + /* Reset DnD for any wrong state. */ + Debug("%s: Bad state: %d\n", __FUNCTION__, mState); + ResetDnD(); + return; + } + + mRpc->HGDragStartDone(); + mState = DNDSTATE_DRAGGING_INSIDE; + Debug("%s: state changed to DRAGGING\n", __FUNCTION__); +} + + +/* + *---------------------------------------------------------------------- + * + * DnD::SetFeedback -- + * + * Got feedback from local UI. Notify host to update. For HG DnD. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +DnD::SetFeedback(DND_DROPEFFECT effect) // IN +{ + if (mState == DNDSTATE_INVALID) { + /* Wrong state. */ + Debug("%s: Bad state: %d\n", __FUNCTION__, mState); + return; + } + + ASSERT(mRpc); + Debug("%s: feedback is %d\n", __FUNCTION__, effect); + mRpc->HGUpdateFeedback(effect); +} + + +/* + *---------------------------------------------------------------------- + * + * DnD::OnHGCancel -- + * + * Host just cancelled the HG DnD. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +DnD::OnHGCancel(void) +{ + sourceCancelChanged.emit(); + /* Hide detection window. */ + updateDetWndChanged.emit(false, 0, 0); + mState = DNDSTATE_READY; + Debug("%s: state changed to READY\n", __FUNCTION__); +} + + +/* + *---------------------------------------------------------------------- + * + * DnD::OnHGDrop -- + * + * Host just drop the data but not copy file yet. Provide staging + * directory and add block. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +DnD::OnHGDrop(void) +{ + char cpName[FILE_MAXPATH]; + int32 cpNameSize; + + if (mState != DNDSTATE_DRAGGING_INSIDE) { + /* Reset DnD for any wrong state. */ + Debug("%s: Bad state: %d\n", __FUNCTION__, mState); + ResetDnD(); + return; + } + + if (CPClipboard_ItemExists(&mClipboard, CPFORMAT_FILELIST)) { + /* Convert staging name to CP format. */ + cpNameSize = CPNameUtil_ConvertToRoot(mStagingDir.c_str(), + sizeof cpName, + cpName); + if (cpNameSize < 0) { + Debug("%s: Error, could not convert to CPName.\n", __FUNCTION__); + return; + } + + mRpc->HGDropDone(cpName, cpNameSize); + sourceDropChanged.emit(); + } else { + /* For non-file formats, the DnD is done. Hide detection window. */ + updateDetWndChanged.emit(false, 0, 0); + } + mState = DNDSTATE_READY; + Debug("%s: state changed to READY\n", __FUNCTION__); +} + + +/* + *---------------------------------------------------------------------- + * + * DnD::OnHGFileCopyDone -- + * + * Host just finished file copy for HG DnD. Remove the block from + * staging directory. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +DnD::OnHGFileCopyDone(bool success, // IN + std::vector stagingDir) // IN +{ + if (!success && !mStagingDir.empty()) { + /* Delete all files if host canceled the file transfer. */ + DnD_DeleteStagingFiles(mStagingDir.c_str(), FALSE); + mStagingDir.clear(); + } + fileCopyDoneChanged.emit(success, stagingDir); +} + + +/* + *---------------------------------------------------------------------------- + * + * DnD::SetupDestDir -- + * + * Creates a directory for file transfer. If the destination + * dir is provided, we will attempt to copy files to the directory. + * + * Input directory will be in local format. + * + * Results: + * The destination directory on success, an empty string on failure. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +std::string +DnD::SetupDestDir(const std::string &destDir) // IN +{ + static const std::string failDir = ""; + + if (!destDir.empty() && File_Exists(destDir.c_str())) { + mStagingDir = destDir; + const char *lastSep = Str_Strrchr(mStagingDir.c_str(), DIRSEPC); + if (lastSep && lastSep[1] != '\0') { + mStagingDir += DIRSEPS; + } + + return mStagingDir; + } else { + char *newDir; + + newDir = DnD_CreateStagingDirectory(); + if (newDir != NULL) { + mStagingDir = newDir; + + char *lastSep = Str_Strrchr(newDir, DIRSEPC); + if (lastSep && lastSep[1] != '\0') { + mStagingDir += DIRSEPS; + } + free(newDir); + Debug("%s: destdir: %s", __FUNCTION__, mStagingDir.c_str()); + + return mStagingDir; + } else { + Debug("%s: destdir not created", __FUNCTION__); + return failDir; + } + } +} + + +/* + *---------------------------------------------------------------------- + * + * DnD::OnGHUpdateUnityDetWnd -- + * + * This callback is trigged when users clicks into any Unity window + * or just release the mouse button. Either show the full-screen + * detection window right after the Unity window, or hide the + * detection window. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +DnD::OnGHUpdateUnityDetWnd(bool bShow, // IN + uint32 unityWndId) // IN +{ + if (bShow && mState != DNDSTATE_READY) { + /* + * Reset DnD for any wrong state. Only do this when host asked to + * show the window. + */ + Debug("%s: Bad state: %d\n", __FUNCTION__, mState); + ResetDnD(); + return; + } + + if (bShow) { + /* + * When show full screen window, also show the small top-most + * window in (1, 1). After detected a GH DnD, the full screen + * window will be hidden to avoid blocking other windows. So use + * this window to accept drop in cancel case. + */ + updateDetWndChanged.emit(bShow, 1, 1); + } + + /* Show/hide the full screent detection window. */ + updateUnityDetWndChanged.emit(bShow, unityWndId); + Debug("%s: updating Unity detection window, bShow %d, id %u\n", + __FUNCTION__, bShow, unityWndId); +} + + +/* + *---------------------------------------------------------------------- + * + * DnD::OnGHQueryPendingDrag -- + * + * This callback is trigged when host got initial GH DnD signal. + * UI will check if there is any pending GH dragging. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +DnD::OnGHQueryPendingDrag(int32 x, // IN + int32 y) // IN +{ + if (mState != DNDSTATE_READY) { + /* Reset DnD for any wrong state. */ + Debug("%s: Bad state: %d\n", __FUNCTION__, mState); + ResetDnD(); + return; + } + + /* Show detection window to detect pending GH DnD. */ + updateDetWndChanged.emit(true, x, y); + mState = DNDSTATE_QUERY_EXITING; + Debug("%s: state changed to QUERY_EXITING\n", __FUNCTION__); + + /* + * Add event to fire and hide our window if a DnD is not pending. Note that + * this is here in case the drag isn't picked up by our drag detection window + * for some reason. + */ + if (NULL == mUngrabTimeout) { + mUngrabTimeout = EventManager_Add(mEventQueue, UNGRAB_TIMEOUT, + DnDUngrabTimeout, this); + } +} + + +/* + *---------------------------------------------------------------------- + * + * DnD::UngrabTimeout -- + * + * Can not detect pending GH DnD within UNGRAB_TIMEOUT, cancel it. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +DnD::UngrabTimeout(void) +{ + if (mState != DNDSTATE_QUERY_EXITING) { + /* Reset DnD for any wrong state. */ + Debug("%s: Bad state: %d\n", __FUNCTION__, mState); + ResetDnD(); + return; + } + + ASSERT(mRpc); + mRpc->GHUngrabTimeout(); + + mUngrabTimeout = NULL; + OnGHCancel(); +} + + +/* + *---------------------------------------------------------------------- + * + * DnD::DragEnter -- + * + * This is reponse to OnGHQueryPendingDrag if there is a pending + * GH dragging. + * + * If guest can not detect any pending GH dragging, UngrabTimeout + * will notify host to cancel GH DnD. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +DnD::DragEnter(const CPClipboard *clip) +{ + /* + * In Unity mode, there is no QueryPendingDrag signal, so may get called + * with state READY. + */ + if (mState != DNDSTATE_QUERY_EXITING && mState != DNDSTATE_READY) { + /* Reset DnD for any wrong state. */ + Debug("%s: Bad state: %d\n", __FUNCTION__, mState); + ResetDnD(); + return; + } + + /* Remove untriggered ungrab timer. */ + if (mUngrabTimeout) { + EventManager_Remove(mUngrabTimeout); + mUngrabTimeout = NULL; + } + + ASSERT(mRpc); + mRpc->GHDragEnter(clip); + mState = DNDSTATE_DRAGGING_OUTSIDE; + Debug("%s: state changed to DRAGGING_OUTSIDE\n", __FUNCTION__); +} + + +/* + *---------------------------------------------------------------------- + * + * DnD::OnGHCancel -- + * + * Host cancelled current GH DnD. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +DnD::OnGHCancel(void) +{ + /* Hide detection window. */ + updateDetWndChanged.emit(false, 0, 0); + /* Remove the timer. */ + if (mUngrabTimeout) { + EventManager_Remove(mUngrabTimeout); + mUngrabTimeout = NULL; + } + mState = DNDSTATE_READY; + Debug("%s: state changed to READY\n", __FUNCTION__); +} + + +/* + *---------------------------------------------------------------------- + * + * DnD::ResetDnD -- + * + * Cancel both HG and GH DnD. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +DnD::ResetDnD(void) +{ + OnHGCancel(); + OnGHCancel(); +} + diff --git a/open-vm-tools/lib/dndGuest/dndFileList.cc b/open-vm-tools/lib/dndGuest/dndFileList.cc new file mode 100644 index 000000000..bc0d8ceec --- /dev/null +++ b/open-vm-tools/lib/dndGuest/dndFileList.cc @@ -0,0 +1,566 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + +/* + * dndFileList.cc + * + * Handles filelists for CPClipboard. + * Relative paths are read/write to allow placing data on the clipboard, + * but full paths are write only. Full path parsing depends on g->h/h->g + * as well as dnd/fcp versions. Given that the host ui never needs to + * parse it, full paths are only stored in binary format for consumption + * by the vmx. + * + * Local relative paths are expected to be encoded in Normalized UTF8 in + * local format. + * + * XXX This file is almost identical to bora/apps/lib/cui/dndFileList.cc + * but right now we can not find a way to share the file. + */ + +#include "dndFileList.hh" + +extern "C" { + #include "dndClipboard.h" + #include "vm_assert.h" + #include "file.h" + #include "cpNameUtil.h" +} + +/* + *---------------------------------------------------------------------------- + * + * DnDFileList -- + * + * Constructor. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +DnDFileList::DnDFileList() +{ + mFileSize = 0; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDFileList::SetFileSize -- + * + * Sets the expected size of the files. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +void +DnDFileList::SetFileSize(uint64 fsize) // IN: The size of the files. +{ + mFileSize = fsize; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDFileList::GetFileSize -- + * + * Gets the size of the files if known. + * + * Results: + * 0 if the size of the files is not known, otherwise the expected size of + * the files. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +uint64 +DnDFileList::GetFileSize() const +{ + return mFileSize; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDFileList::AddFile -- + * + * Add the full path and the relative path to the file list. Both strings + * should be normalized UTF8. + * Will not add file if the file list was created from a clipboard buffer. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +void +DnDFileList::AddFile(const std::string fullPath, // IN: filename + const std::string relPath) // IN: filename +{ + ASSERT(mFullPathsBinary.empty()); + + if (!mFullPathsBinary.empty()) { + return; + } + + mRelPaths.push_back(relPath); + mFullPaths.push_back(fullPath); +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDFileList::AddFileUri -- + * + * Add the full uri UTF8 path the file list. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +void +DnDFileList::AddFileUri(const std::string uriPath) // IN +{ + mUriPaths.push_back(uriPath); +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDFileList::AddFiles -- + * + * Add the full path list and the relative path list. Both lists should be + * normalized UTF8. + * + * Will not add lists if the file list was created from a clipboard buffer. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +void +DnDFileList::AddFiles(const std::vector fullPathList, // IN: filenames + const std::vector relPathList) // IN: filenames +{ + ASSERT(mFullPathsBinary.empty()); + + if (!mFullPathsBinary.empty()) { + return; + } + + mRelPaths = relPathList; + mFullPaths = fullPathList; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDFileList::SetRelPathsStr -- + * + * Set the relative paths based on the serialized input string. Paths are + * in normalized utf-8 with local path separators. Exposed for dnd/cp + * version 2. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +void +DnDFileList::SetRelPathsStr(const std::string inpath) // IN: +{ + std::string::size_type mPos = 0; + std::string curFile; + std::string path; + const char* cpath; + + if (inpath.empty()) { + return; + } + + if (inpath[inpath.size()-1] != '\0') { + path = inpath + '\0'; + } else { + path = inpath; + } + + cpath = path.c_str(); + mRelPaths.clear(); + curFile = cpath; + mPos = path.find('\0', mPos); + + while (mPos != std::string::npos) { + mPos++; + mRelPaths.push_back(curFile); + curFile = cpath + mPos; + mPos = path.find('\0', mPos); + } +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDFileList::GetRelPaths -- + * + * Gets a vector containing the reletive paths of the files. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +std::vector +DnDFileList::GetRelPaths() const +{ + return mRelPaths; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDFileList::GetFullPathsStr -- + * + * Gets the serialized version of the full paths. If the file list was + * created from a CPClipboard, use the serialized input instead. + * + * Local paths are serialized inot NUL separated pathes. + * CPName paths are converted from local to CPName and serialized into + * uint32,cpname pairs. + * + * Results: + * An std::string containing the serialized full paths. Empty string on + * error. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +std::string +DnDFileList::GetFullPathsStr(bool local) // IN: Use local format + const +{ + std::string stringList; + std::vector::const_iterator i; + + if (mFullPathsBinary.empty() && !mFullPaths.empty()) { + for (i = mFullPaths.begin(); i != mFullPaths.end(); ++i) { + if (local) { + stringList.append(i->c_str()); + stringList.push_back('\0'); + } else { + char outPath[FILE_MAXPATH + 100]; + int32 outPathLen; + + outPathLen = CPNameUtil_ConvertToRoot(i->c_str(), + sizeof outPath, + outPath); + if (outPathLen < 0) { + continue; + } + + stringList.append(reinterpret_cast(&outPathLen), + sizeof outPathLen); + stringList.append(outPath, outPathLen); + } + } + + return stringList; + } else if (!mFullPathsBinary.empty() && mFullPaths.empty()) { + return mFullPathsBinary; + } else { + return ""; + } +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDFileList::GetRelPathsStr -- + * + * Gets the serialized version of the relative paths. Primarily used for + * dnd/cp v2. + * + * Results: + * An std::string contatin the serialized full paths. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +std::string +DnDFileList::GetRelPathsStr() + const +{ + std::string stringList; + std::vector::const_iterator i; + + for (i = mRelPaths.begin(); i != mRelPaths.end(); ++i) { + stringList.append(i->c_str()); + stringList.push_back('\0'); + } + return stringList; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDFileList::GetUriPathsStr -- + * + * Gets the serialized version of the uri paths. + * + * Results: + * An std::string contatin the serialized uri paths. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +std::string +DnDFileList::GetUriPathsStr(void) + const +{ + std::string stringList; + std::vector::const_iterator i; + + for (i = mUriPaths.begin(); i != mUriPaths.end(); i++) { + stringList.append(i->c_str()); + stringList.push_back('\0'); + } + return stringList; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDFileList::FromCPClipboard -- + * + * Loads the filelist from a buffer, typically from CPClipboard. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +bool +DnDFileList::FromCPClipboard(const void *buf, // IN: Source buffer + size_t len) // IN: Buffer length +{ + const CPFileList *flist; + std::string relPaths; + + ASSERT(buf); + ASSERT(len); + if (!buf || !len) { + return false; + } + + flist = reinterpret_cast(buf); + relPaths.assign( + reinterpret_cast(flist->filelists), flist->relPathsLen); + + mRelPaths.clear(); + mFullPaths.clear(); + + mFileSize = flist->fileSize; + + SetRelPathsStr(relPaths); + mFullPathsBinary.assign( + reinterpret_cast(flist->filelists + flist->relPathsLen), + flist->fulPathsLen); + + return true; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDFileList::ToCPClipboard -- + * + * Serializes contents for CPClipboard in eithr CPFormat or local format. + * + * Results: + * false on error, true on success. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +bool +DnDFileList::ToCPClipboard(DynBuf *out, // OUT: Initialized output buffer + bool local) // IN: Use local format + const +{ + std::string strListRel; + std::string strListFul; + CPFileList header; + + strListRel = GetRelPathsStr(); + strListFul = GetFullPathsStr(local); + + if (!out) { + return false; + } + + /* Check if the size is too big. */ + if (strListRel.size() > MAX_UINT32 || + strListFul.size() > MAX_UINT32) { + return false; + } + + header.fileSize = mFileSize; + header.relPathsLen = strListRel.size(); + header.fulPathsLen = strListFul.size(); + + DynBuf_Append(out, &header, CPFILELIST_HEADER_SIZE); + DynBuf_Append(out, strListRel.c_str(), header.relPathsLen); + DynBuf_Append(out, strListFul.c_str(), header.fulPathsLen); + + return true; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDFileList::ToCPClipboard -- + * + * Serializes uri paths for CPClipboard in URI Format. + * + * Results: + * false on error, true on success. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +bool +DnDFileList::ToUriClipboard(DynBuf *out) // OUT: Initialized output buffer + const +{ + std::string strListUri; + UriFileList header; + + if (!out) { + return false; + } + + strListUri = GetUriPathsStr(); + + /* Check if the size is too big. */ + if (strListUri.size() > MAX_UINT32) { + return false; + } + + header.fileSize = mFileSize; + header.uriPathsLen = strListUri.size(); + + DynBuf_Append(out, &header, URI_FILELIST_HEADER_SIZE); + DynBuf_Append(out, strListUri.c_str(), header.uriPathsLen); + + return true; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDFileList::Clear -- + * + * Clears the contents of the filelist. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +void +DnDFileList::Clear() +{ + mRelPaths.clear(); + mFullPaths.clear(); + mUriPaths.clear(); + mFullPathsBinary.clear(); + mFileSize = 0; +} + diff --git a/open-vm-tools/lib/dndGuest/dndTransportGuestRpc.cc b/open-vm-tools/lib/dndGuest/dndTransportGuestRpc.cc new file mode 100644 index 000000000..5bdaa2f33 --- /dev/null +++ b/open-vm-tools/lib/dndGuest/dndTransportGuestRpc.cc @@ -0,0 +1,385 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + +/* + * dndTransportGuestRpc.cc -- + * + * GuestRpc implementation of the dndTransport interface. + * + * A multiple packet transport protocol is implemented in this layer to + * support big buffer transfer. With the support, message size limit is + * improved from 64K to 4M (theoratically can be 4G). + * + * There are 3 different types of packet. If a message is smaller than + * 64K, it will be sent with type DND_TRANSPORT_PACKET_TYPE_SINGLE. If + * it is bigger than 64K, initial 64K will be first sent with + * DND_TRANSPORT_PACKET_TYPE_PAYLOAD. After received the packet, the + * receiver should reply with DND_TRANSPORT_PACKET_TYPE_REQUEST, then + * next 64K will be sent. + * + * This is a temporary solution before VICF is available. To simplify + * the implementation, there are some limitations: + * 1. Any time for any transport class, there is only one pending + * big buffer for each direction. Any following big size message will + * be dropped before the pending transfer is done.Small message (<64K) + * can be sent at any time. + * 2. Caller can not cancel any pending big buffer sending and + * receiving. + * 3. Pending big buffer will be dropped if there is any error. + */ + + +#include + +#include "dndTransportGuestRpc.hh" + +extern "C" { + #include "util.h" + #include "rpcout.h" + #include "rpcin.h" + #include "debug.h" + #include "str.h" + #include "hostinfo.h" + #include "dndMsg.h" +} + +/* + *----------------------------------------------------------------------------- + * + * RecvMsgCB -- + * + * Reads msg from VMX. + * + * Results: + * TRUE if success, FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static Bool +RecvMsgCB(char const **result, // OUT + size_t *resultLen, // OUT + const char *name, // IN + const char *args, // IN + size_t argsSize, // IN: Size of args + void *clientData) // IN +{ + DnDTransportGuestRpc *transport = (DnDTransportGuestRpc *)clientData; + ASSERT(transport); + + /* '- 1' is to ignore empty space between command and args. */ + if ((argsSize - 1) <= 0) { + Debug("%s: invalid argsSize\n", __FUNCTION__); + return RpcIn_SetRetVals(result, resultLen, "invalid arg size", FALSE); + } + transport->RecvMsg((DnDTransportPacketHeader *)(args + 1), argsSize - 1); + return RpcIn_SetRetVals(result, resultLen, "", TRUE); +} + + +/* + *----------------------------------------------------------------------------- + * + * DnDTransportGuestRpc::DnDTransportGuestRpc -- + * + * Constructor of DnDTransportGuestRpc class. + * + * Results: + * None + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +DnDTransportGuestRpc::DnDTransportGuestRpc(struct RpcIn *rpcIn, // IN + const char *rpcCmd) // IN + : mRpcIn(rpcIn) +{ + ASSERT(rpcIn); + ASSERT(rpcCmd); + + RpcIn_RegisterCallback(rpcIn, rpcCmd, RecvMsgCB, this); + mRpcCmd = Util_SafeStrdup(rpcCmd); + + mSendBuf.buffer = NULL; + mRecvBuf.buffer = NULL; + DnD_TransportBufReset(&mSendBuf); + DnD_TransportBufReset(&mRecvBuf); + mSeqNum= 0; +} + + +/* + *---------------------------------------------------------------------- + * + * DnDTransportGuestRpc::~DnDTransportGuestRpc -- + * + * Destructor of DnDTransportGuestRpc. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +DnDTransportGuestRpc::~DnDTransportGuestRpc(void) +{ + RpcIn_UnregisterCallback(mRpcIn, mRpcCmd); + free(mRpcCmd); + free(mSendBuf.buffer); + free(mRecvBuf.buffer); +} + + +/* + *----------------------------------------------------------------------------- + * + * DnDTransportGuestRpc::SendMsg -- + * + * Sends msg to the VMX. + * + * Results: + * TRUE if success, FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +bool +DnDTransportGuestRpc::SendMsg(uint8 *msg, // IN + size_t length) // IN +{ + DnDTransportPacketHeader *packet = NULL; + size_t packetSize; + bool ret = FALSE; + + if (length > DNDMSG_MAX_ARGSZ) { + Debug("%s: message is too big, quit.\n", __FUNCTION__); + return FALSE; + } + + Debug("%s: got message, size %"FMTSZ"u\n", __FUNCTION__, length); + + if (length <= DND_MAX_TRANSPORT_PACKET_PAYLOAD_SIZE) { + /* + * It is a small size message, so it is not needed to buffer it in the transport + * layer. Just put message into a packet and send it. + */ + packetSize = DnD_TransportMsgToPacket(msg, length, mSeqNum, &packet); + } else { + /* + * It is a big size message. First buffer it in the transport layer and send + * it with multiple packets. + */ + if (mSendBuf.buffer) { + /* + * There is a pending big size message. If there is no update time more than + * DND_MAX_TRANSPORT_LATENCY_TIME, remove old message and send new message. + * Otherwise ignore this message. + * + * XXX This transport implementation is just a temporary solution before VICF + * is available. So any time there is only one pending big buffer. Any other + * big message during this time will be dropped. Current pending buffer will + * also be dropped if there is any error. Later on VICF should provide a + * better transportation. + */ + VmTimeType curTime; + + Hostinfo_GetTimeOfDay(&curTime); + + if ((curTime - mSendBuf.lastUpdateTime) < DND_MAX_TRANSPORT_LATENCY_TIME) { + Debug("%s: got a big buffer, but there is already a pending one, quitting\n", + __FUNCTION__); + return FALSE; + } + } + DnD_TransportBufInit(&mSendBuf, msg, length, mSeqNum); + packetSize = DnD_TransportBufGetPacket(&mSendBuf, &packet); + } + + mSeqNum++; + if (packetSize) { + ret = SendPacket((uint8 *)packet, packetSize); + } + free(packet); + return ret; +} + + +/* + *----------------------------------------------------------------------------- + * + * DnDTransportGuestRpc::SendPacket -- + * + * Sends packet to the VMX. + * + * Results: + * TRUE if success, FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +bool +DnDTransportGuestRpc::SendPacket(uint8 *packet, // IN + size_t packetSize) // IN +{ + char *rpc = NULL; + size_t rpcSize = 0; + size_t nrWritten = 0; + bool ret = FALSE; + + if (packetSize == 0 || packetSize > DND_MAX_TRANSPORT_PACKET_SIZE) { + Debug("%s: invalid packet\n", __FUNCTION__); + return FALSE; + } + + rpcSize = strlen(mRpcCmd) + 1 + packetSize; + rpc = (char *)Util_SafeMalloc(rpcSize); + nrWritten = Str_Sprintf(rpc, rpcSize, "%s ", mRpcCmd); + ASSERT(nrWritten + packetSize <= rpcSize); + memcpy(rpc + nrWritten, packet, packetSize); + + ret = (TRUE == RpcOut_SendOneRaw(rpc, rpcSize, NULL, NULL)); + + if (!ret) { + Debug("%s: failed to send msg to host\n", __FUNCTION__); + } + + free(rpc); + return ret; +} + + +/* + *----------------------------------------------------------------------------- + * + * DnDTransportGuestRpc::RecvMsg -- + * + * Receives packet from VMX. + * + * Results: + * None + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +DnDTransportGuestRpc::RecvMsg(DnDTransportPacketHeader *packet, // IN + size_t packetSize) // IN +{ + if (packetSize <= 0 || + packetSize != (packet->payloadSize + DND_TRANSPORT_PACKET_HEADER_SIZE) || + packetSize > DND_MAX_TRANSPORT_PACKET_SIZE) { + Debug("%s: Received invalid data.\n", __FUNCTION__); + return; + } + + Debug("%s: received data, size %"FMTSZ"u.\n", __FUNCTION__, packetSize); + + switch (packet->type) { + case DND_TRANSPORT_PACKET_TYPE_SINGLE: + if (packet->payloadSize != packet->totalSize) { + Debug("%s: received invalid packet.\n", __FUNCTION__); + return; + } + recvMsgChanged.emit(packet->payload, + packet->payloadSize); + break; + case DND_TRANSPORT_PACKET_TYPE_REQUEST: + { + DnDTransportPacketHeader *replyPacket = NULL; + size_t replyPacketSize; + + /* Validate received packet. */ + if (packet->payloadSize || + packet->seqNum != mSendBuf.seqNum || + packet->offset != mSendBuf.offset) { + Debug("%s: received packet does not match local buffer.\n", __FUNCTION__); + return; + } + + replyPacketSize = DnD_TransportBufGetPacket(&mSendBuf, &replyPacket); + + if (!replyPacketSize) { + /* + * Not needed to reset mSendBuf because DnD_TransportBufGetPacket already + * did that. + */ + Debug("%s: DnD_TransportBufGetPacket failed.\n", __FUNCTION__); + return; + } + + if (!SendPacket((uint8 *)replyPacket, replyPacketSize) || + mSendBuf.offset == mSendBuf.totalSize) { + /* Reset mSendBuf if whole buffer is sent or there is any error. */ + DnD_TransportBufReset(&mSendBuf); + } + + free(replyPacket); + + break; + } + case DND_TRANSPORT_PACKET_TYPE_PAYLOAD: + if (!DnD_TransportBufAppendPacket(&mRecvBuf, packet, packetSize)) { + Debug("%s: DnD_TransportBufAppendPacket failed.\n", __FUNCTION__); + return; + } + + if (mRecvBuf.offset == mRecvBuf.totalSize) { + /* Received all packets for the messge. */ + recvMsgChanged.emit(mRecvBuf.buffer, mRecvBuf.totalSize); + DnD_TransportBufReset(&mRecvBuf); + } else { + /* Send request for next packet. */ + DnDTransportPacketHeader *replyPacket = NULL; + size_t replyPacketSize; + + replyPacketSize = DnD_TransportReqPacket(&mRecvBuf, &replyPacket); + + if (!replyPacketSize) { + Debug("%s: DnD_TransportReqPacket failed.\n", __FUNCTION__); + return; + } + + if (!SendPacket((uint8 *)replyPacket, replyPacketSize)) { + DnD_TransportBufReset(&mRecvBuf); + } + free(replyPacket); + } + break; + default: + Debug("%s: unknown packet.\n", __FUNCTION__); + break; + } +} diff --git a/open-vm-tools/lib/dynxdr/dynxdr.c b/open-vm-tools/lib/dynxdr/dynxdr.c index d3b24bdd9..792eb4861 100644 --- a/open-vm-tools/lib/dynxdr/dynxdr.c +++ b/open-vm-tools/lib/dynxdr/dynxdr.c @@ -69,6 +69,15 @@ typedef struct DynXdrData { # define DYNXDR_SIZE_T u_int #endif +/* + * Mac OS 64-bit defines the parameter to "x_putlong" as an int. + */ +#if defined __APPLE__ && defined __LP64__ +# define DYNXDR_LONG int +#else +# define DYNXDR_LONG long +#endif + /* *----------------------------------------------------------------------------- @@ -177,11 +186,16 @@ DynXdrPutInt32(XDR *xdrs, // IN/OUT */ static bool_t -DynXdrPutLong(XDR *xdrs, // IN/OUT - DYNXDR_CONST long *lp) // IN +DynXdrPutLong(XDR *xdrs, // IN/OUT + DYNXDR_CONST DYNXDR_LONG *lp) // IN { - int32 out = htonl((int32)*lp); + int32 out; DynXdrData *priv = (DynXdrData *) xdrs->x_private; + +#ifdef __APPLE__ + ASSERT_ON_COMPILE(sizeof *lp <= sizeof (int32)); +#endif + out = htonl((int32)*lp); return DynBuf_Append(&priv->data, &out, sizeof out); } diff --git a/open-vm-tools/lib/file/fileIOPosix.c b/open-vm-tools/lib/file/fileIOPosix.c index 65475b8e7..b603ac24d 100644 --- a/open-vm-tools/lib/file/fileIOPosix.c +++ b/open-vm-tools/lib/file/fileIOPosix.c @@ -49,7 +49,7 @@ #include "su.h" #if defined(__APPLE__) -#include +#include "sysSocket.h" // Don't move this: it fixes a system header. #include #include #include @@ -1196,8 +1196,8 @@ FileIO_Truncate(FileIODescriptor *file, // IN * Close a file * * Results: - * On success: 0 - * On failure: non-zero + * TRUE: an error occured + * FALSE: no error occured * * Side effects: * None @@ -1205,14 +1205,14 @@ FileIO_Truncate(FileIODescriptor *file, // IN *---------------------------------------------------------------------- */ -int +Bool FileIO_Close(FileIODescriptor *file) // IN { - int retval; + int err; ASSERT(file); - retval = close(file->posix); + err = (close(file->posix) == -1) ? errno : 0; FileIO_StatsExit(file); @@ -1221,7 +1221,11 @@ FileIO_Close(FileIODescriptor *file) // IN FileIO_Cleanup(file); FileIO_Invalidate(file); - return retval; + if (err) { + errno = err; + } + + return err != 0; } diff --git a/open-vm-tools/lib/file/fileLockPrimitive.c b/open-vm-tools/lib/file/fileLockPrimitive.c index 9fa7730da..09e3c49d0 100644 --- a/open-vm-tools/lib/file/fileLockPrimitive.c +++ b/open-vm-tools/lib/file/fileLockPrimitive.c @@ -1180,13 +1180,11 @@ MakeDirectory(ConstUnicode pathName) // IN: #if !defined(_WIN32) mode_t save; - ASSERT(pathName); - save = umask(0); -#else - ASSERT(pathName); #endif + ASSERT(pathName); + err = FileCreateDirectoryRobust(pathName); #if !defined(_WIN32) @@ -1253,9 +1251,9 @@ CreateEntryDirectory(const char *machineID, // IN: if (fileData.fileType == FILE_TYPE_REGULAR) { /* - * It's a file. Assume this is an (active?) old style lock - * and err on the safe side - don't remove it (and - * automatically upgrade to a new style lock). + * It's a file. Assume this is an (active?) old style lock and + * err on the safe side - don't remove it (and automatically + * upgrade to a new style lock). */ Log(LGPFX" %s: '%s' exists; an old style lock file?\n", diff --git a/open-vm-tools/lib/file/filePosix.c b/open-vm-tools/lib/file/filePosix.c index 5745fc5ee..891b40b5d 100644 --- a/open-vm-tools/lib/file/filePosix.c +++ b/open-vm-tools/lib/file/filePosix.c @@ -1318,13 +1318,13 @@ end: * Returns a string which uniquely identifies the underlying filesystem * for a given path. * - * 'path' can be relative (including empty) or absolute, and any number of - * non-existing components at the end of 'path' are simply ignored. + * 'path' can be relative (including empty) or absolute, and any number + * of non-existing components at the end of 'path' are simply ignored. * * XXX: On Posix systems, we choose the underlying device's name as the - * unique ID. I make no claim that this is 100% unique so if you need - * this functionality to be 100% perfect, I suggest you think about - * it more deeply than I did. -meccleston + * unique ID. I make no claim that this is 100% unique so if you + * need this functionality to be 100% perfect, I suggest you think + * about it more deeply than I did. -meccleston * * Results: * On success: Allocated and NUL-terminated filesystem ID. @@ -2090,8 +2090,8 @@ File_SupportsFileSize(ConstUnicode pathName, // IN: * We acquire the full path name for testing in * FilePosixCreateTestFileSize(). This is also done in the event that * a user tries to create a virtual disk in the directory that they want - * a vmdk created in (setting filePath only to the disk name, not the entire - * path. + * a vmdk created in (setting filePath only to the disk name, not the + * entire path. */ fullPath = File_FullPath(pathName); @@ -2101,9 +2101,9 @@ File_SupportsFileSize(ConstUnicode pathName, // IN: } /* - * This function expects a filename. If given one, truncate the name to point - * to the parent directory so we can get accurate results from FileIsVMFS. - * If handed a directory directly, no truncation is necessary. + * This function expects a filename. If given one, truncate the name to + * point to the parent directory so we can get accurate results from + * FileIsVMFS. If handed a directory directly, no truncation is necessary. */ if (File_IsDirectory(pathName)) { @@ -2724,10 +2724,11 @@ File_MakeCfgFileExecutable(ConstUnicode pathName) * * File_GetSizeAlternate -- * - * An alternate way to determine the filesize. Useful for finding problems - * with files on remote fileservers, such as described in bug 19036. - * However, in Linux we do not have an alternate way, yet, to determine the - * problem, so we call back into the regular getSize function. + * An alternate way to determine the filesize. Useful for finding + * problems with files on remote fileservers, such as described in bug + * 19036. However, in Linux we do not have an alternate way, yet, to + * determine the problem, so we call back into the regular getSize + * function. * * Results: * Size of file or -1. diff --git a/open-vm-tools/lib/foundryMsg/foundryMsg.c b/open-vm-tools/lib/foundryMsg/foundryMsg.c index 0dc1783e9..928b1bdcf 100644 --- a/open-vm-tools/lib/foundryMsg/foundryMsg.c +++ b/open-vm-tools/lib/foundryMsg/foundryMsg.c @@ -451,7 +451,6 @@ VixMsg_AllocResponseMsg(const VixCommandRequestHeader *requestHeader, // IN VixCommandResponseHeader *responseHeader; size_t totalMessageSize; - ASSERT(responseBodyLength >= 0); ASSERT((NULL != responseBody) || (0 == responseBodyLength)); /* @@ -518,9 +517,9 @@ VixMsg_InitResponseMsg(VixCommandResponseHeader *responseHeader, // IN size_t responseBodyLength; ASSERT(NULL != responseHeader); + ASSERT(totalMessageSize >= sizeof(VixCommandResponseHeader)); responseBodyLength = totalMessageSize - sizeof(VixCommandResponseHeader); - ASSERT(responseBodyLength >= 0); /* * Fill in the response header. diff --git a/open-vm-tools/lib/foundryMsg/foundryPropertyListCommon.c b/open-vm-tools/lib/foundryMsg/foundryPropertyListCommon.c index 16a55e935..7774f5f82 100644 --- a/open-vm-tools/lib/foundryMsg/foundryPropertyListCommon.c +++ b/open-vm-tools/lib/foundryMsg/foundryPropertyListCommon.c @@ -332,8 +332,12 @@ VixPropertyList_Serialize(VixPropertyListImpl *propList, // IN abort: if (VIX_OK != err) { free(serializeBuffer); - *resultBuffer = NULL; - *resultSize = 0; + if (NULL != resultBuffer) { + *resultBuffer = NULL; + } + if (NULL != resultSize) { + *resultSize = 0; + } } return err; diff --git a/open-vm-tools/lib/foundryMsg/foundryThreads.c b/open-vm-tools/lib/foundryMsg/foundryThreads.c index 678762045..fcc600084 100644 --- a/open-vm-tools/lib/foundryMsg/foundryThreads.c +++ b/open-vm-tools/lib/foundryMsg/foundryThreads.c @@ -68,6 +68,8 @@ FoundryThreads_StartThread(FoundryThreadProc proc, // IN { VixError err = VIX_OK; FoundryWorkerThread *threadState = NULL; + static const char *createThreadFailureMsg = + "%s: thread creation failed with error %d.\n"; ASSERT(proc); @@ -86,6 +88,7 @@ FoundryThreads_StartThread(FoundryThreadProc proc, // IN 0, &(threadState->threadId)); if (NULL == threadState->threadHandle) { + Log(createThreadFailureMsg, __FUNCTION__, GetLastError()); err = VIX_E_OUT_OF_MEMORY; goto abort; } @@ -103,6 +106,7 @@ FoundryThreads_StartThread(FoundryThreadProc proc, // IN FoundryThreadWrapperProc, threadState); if (0 != result) { + Log(createThreadFailureMsg, __FUNCTION__, result); err = VIX_E_OUT_OF_MEMORY; goto abort; } @@ -130,6 +134,7 @@ abort: * None. * * Side effects: + * May block while the given thread stops. * *----------------------------------------------------------------------------- */ @@ -137,33 +142,35 @@ abort: void FoundryThreads_StopThread(FoundryWorkerThread *threadState) // IN { +#ifdef _WIN32 + DWORD waitResult; +#endif + + if (NULL == threadState) { + ASSERT(0); + return; + } + /* - * Stop the poll thread. + * Stop the thread. */ threadState->stopThread = TRUE; - if (NULL != threadState) { -#ifdef _WIN32 - DWORD waitResult; - - ASSERT(threadState->threadId != GetCurrentThreadId()); - - waitResult = WaitForSingleObject(threadState->threadHandle, 30000); - if (WAIT_OBJECT_0 != waitResult) { - TerminateThread(threadState->threadHandle, 0x1); - } + ASSERT(!FoundryThreads_IsCurrentThread(threadState)); - CloseHandle(threadState->threadHandle); - threadState->threadHandle = NULL; +#ifdef _WIN32 + waitResult = WaitForSingleObject(threadState->threadHandle, 30000); + if (WAIT_OBJECT_0 != waitResult) { + TerminateThread(threadState->threadHandle, 0x1); + } #else - pthread_join(threadState->threadInfo, NULL); - threadState->threadInfo = 0; + pthread_join(threadState->threadInfo, NULL); #endif - } - free(threadState); + FoundryThreads_Free(threadState); } + /* *----------------------------------------------------------------------------- * @@ -175,6 +182,7 @@ FoundryThreads_StopThread(FoundryWorkerThread *threadState) // IN * None. * * Side effects: + * None. * *----------------------------------------------------------------------------- */ @@ -189,11 +197,12 @@ FoundryThreads_Free(FoundryWorkerThread *threadState) // IN #else threadState->threadInfo = 0; #endif - } - free(threadState); + free(threadState); + } } + /* *----------------------------------------------------------------------------- * @@ -217,21 +226,10 @@ FoundryThreads_IsCurrentThread(struct FoundryWorkerThread *threadState) // I return FALSE; } #ifdef _WIN32 - { - Util_ThreadID id = GetCurrentThreadId(); - if (id == threadState->threadId) { - return TRUE; - } - } + return (GetCurrentThreadId() == threadState->threadId); #else - { - pthread_t id = pthread_self(); - if (pthread_equal(id, threadState->threadInfo)) { - return TRUE; - } - } + return pthread_equal(pthread_self(), threadState->threadInfo); #endif - return FALSE; } @@ -263,10 +261,11 @@ FoundryThreadWrapperProc(void *threadParameter) // IN threadState = (FoundryWorkerThread *) threadParameter; if (NULL == threadState) { + ASSERT(0); goto abort; } - - if (NULL != threadState) { + + if (NULL != threadState->threadProc) { (*(threadState->threadProc))(threadState); } diff --git a/open-vm-tools/lib/guestApp/guestApp.c b/open-vm-tools/lib/guestApp/guestApp.c index 5542cd824..82de72322 100644 --- a/open-vm-tools/lib/guestApp/guestApp.c +++ b/open-vm-tools/lib/guestApp/guestApp.c @@ -47,8 +47,6 @@ extern "C" { #include "dictll.h" #include "msg.h" #include "file.h" -#include "dynbuf.h" -#include "vmstdio.h" #include "codeset.h" #include "productState.h" #include "posix.h" @@ -1153,86 +1151,6 @@ GuestApp_GetLogPath(void) } -/* - *---------------------------------------------------------------------- - * - * GuestApp_GetCmdOutput -- - * - * Run a cmd & get its cmd line output - * - * Results: - * An allocated string or NULL if an error occurred. - * - * Side effects: - * The cmd is run. - * - *---------------------------------------------------------------------- - */ - -char * -GuestApp_GetCmdOutput(const char *cmd) // IN -{ -#ifdef N_PLAT_NLM - Debug("Trying to execute command \"%s\" and catch its output... No way on NetWare...\n", cmd); - return NULL; -#else - DynBuf db; - FILE *stream; - char *out = NULL; - - DynBuf_Init(&db); - - stream = Posix_Popen(cmd, "r"); - if (stream == NULL) { - Debug("Unable to get output of command \"%s\"\n", cmd); - - return NULL; - } - - for (;;) { - char *line = NULL; - size_t size; - - switch (StdIO_ReadNextLine(stream, &line, 0, &size)) { - case StdIO_Error: - goto close; - break; - - case StdIO_EOF: - break; - - case StdIO_Success: - break; - - default: - ASSERT_NOT_IMPLEMENTED(FALSE); - } - - if (line == NULL) { - break; - } - - DynBuf_Append(&db, line, size); - free(line); - } - - if (DynBuf_Get(&db)) { - out = (char *)DynBuf_AllocGet(&db); - } - close: - DynBuf_Destroy(&db); - -#ifndef _WIN32 - pclose(stream); -#else - _pclose(stream); -#endif - - return out; -#endif -} - - /* *---------------------------------------------------------------------- * diff --git a/open-vm-tools/lib/guestInfo/Makefile.am b/open-vm-tools/lib/guestInfo/Makefile.am index 033c1be4a..8188bacf8 100644 --- a/open-vm-tools/lib/guestInfo/Makefile.am +++ b/open-vm-tools/lib/guestInfo/Makefile.am @@ -23,13 +23,11 @@ libGuestInfo_la_SOURCES += guestInfoServer.c libGuestInfo_la_SOURCES += guestInfoPosix.c libGuestInfo_la_SOURCES += guestInfoPerfMonLinux.c -if ENABLE_CORE_SERVICES noinst_LTLIBRARIES += libGuestInfoCS.la libGuestInfoCS_la_SOURCES = libGuestInfoCS_la_SOURCES += guestInfo.c libGuestInfoCS_la_SOURCES += guestInfoPosix.c -endif AM_CFLAGS = $(DNET_CPPFLAGS) diff --git a/open-vm-tools/lib/guestInfo/guestInfo.c b/open-vm-tools/lib/guestInfo/guestInfo.c index 7f28c68ee..7d1c16277 100644 --- a/open-vm-tools/lib/guestInfo/guestInfo.c +++ b/open-vm-tools/lib/guestInfo/guestInfo.c @@ -158,48 +158,6 @@ GuestInfo_GetDiskInfo(PGuestDiskInfo di) // IN/OUT } -/* - *----------------------------------------------------------------------------- - * - * GuestInfoGetOSName -- - * - * Return OS version information. - * - * osFullName format: - * Linux/POSIX: - * - * An example of such string would be: - * Linux 2.4.18-3 Red Hat Linux release 7.3 (Valhalla) - * - * Win32: - * (BUILD ) - * An example of such string would be: - * Windows XP Professional Service Pack 2 (Build 2600) - * - * osName contains an os name in the same format that is used - * in .vmx file. - * - * Results: - * Returns TRUE on success and FALSE on failure. - * Returns the guest's full OS name (osNameFull) - * Returns the guest's OS name in the same format as .vmx file (osName) - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -GuestInfo_GetOSName(unsigned int outBufFullLen, // IN - unsigned int outBufLen, // IN - char *osNameFull, // OUT - char *osName) // OUT -{ - return GuestInfoGetOSName(outBufFullLen, outBufLen, osNameFull, osName); -} - - /** * Add a NIC into the given list. The macAddress of the new GuestNic is * initialized with the given address. diff --git a/open-vm-tools/lib/guestInfo/guestInfoInt.h b/open-vm-tools/lib/guestInfo/guestInfoInt.h index 4da543aa9..de39df738 100644 --- a/open-vm-tools/lib/guestInfo/guestInfoInt.h +++ b/open-vm-tools/lib/guestInfo/guestInfoInt.h @@ -35,8 +35,6 @@ Bool GuestInfoGetFqdn(int outBufLen, char fqdn[]); Bool GuestInfoGetNicInfo(GuestNicList *nicInfo); void GuestInfoMemset(void * mem, int value, unsigned int size); Bool GuestInfoGetDiskInfo(PGuestDiskInfo di); -Bool GuestInfoGetOSName(unsigned int outBufFullLen, unsigned int outBufLen, - char *osNameFull, char *osName); Bool GuestInfo_PerfMon(struct GuestMemInfo *vmStats); GuestNic *GuestInfoAddNicEntry(GuestNicList *nicInfo, diff --git a/open-vm-tools/lib/guestInfo/guestInfoPerfMonLinux.c b/open-vm-tools/lib/guestInfo/guestInfoPerfMonLinux.c index c35f12c54..838b61048 100644 --- a/open-vm-tools/lib/guestInfo/guestInfoPerfMonLinux.c +++ b/open-vm-tools/lib/guestInfo/guestInfoPerfMonLinux.c @@ -65,9 +65,10 @@ GuestInfo_PerfMon(GuestMemInfo *vmStats) // OUT: filled vmstats { #ifndef NO_PROCPS ASSERT(vmStats); + vmStats->flags = 0; GuestInfoMonitorGetStat(vmStats); if (GuestInfoMonitorReadMeminfo(vmStats)) { - vmStats->flags = LINUX_MEMINFO_FLAGS; + vmStats->flags |= LINUX_MEMINFO_FLAGS; return TRUE; } #endif diff --git a/open-vm-tools/lib/guestInfo/guestInfoPosix.c b/open-vm-tools/lib/guestInfo/guestInfoPosix.c index 43ddcd813..a02a4c40d 100644 --- a/open-vm-tools/lib/guestInfo/guestInfoPosix.c +++ b/open-vm-tools/lib/guestInfo/guestInfoPosix.c @@ -63,72 +63,6 @@ #include "guestApp.h" #include "guestInfo.h" -#define DISTRO_BUF_SIZE 255 - -typedef struct lsb_distro_info { - char *name; - char *scanstring; -} LSBDistroInfo; - - -LSBDistroInfo lsbFields[] = { - {"DISTRIB_ID=", "DISTRIB_ID=%s"}, - {"DISTRIB_RELEASE=", "DISTRIB_RELEASE=%s"}, - {"DISTRIB_CODENAME=", "DISTRIB_CODENAME=%s"}, - {"DISTRIB_DESCRIPTION=", "DISTRIB_DESCRIPTION=%s"}, - {NULL, NULL}, -}; - - -typedef struct distro_info { - char *name; - char *filename; -} DistroInfo; - - -DistroInfo distroArray[] = { - {"RedHat", "/etc/redhat-release"}, - {"RedHat", "/etc/redhat_version"}, - {"Sun", "/etc/sun-release"}, - {"SuSE", "/etc/SuSE-release"}, - {"SuSE", "/etc/novell-release"}, - {"SuSE", "/etc/sles-release"}, - {"Debian", "/etc/debian_version"}, - {"Debian", "/etc/debian_release"}, - {"Mandrake", "/etc/mandrake-release"}, - {"Mandriva", "/etc/mandriva-release"}, - {"Mandrake", "/etc/mandrakelinux-release"}, - {"TurboLinux", "/etc/turbolinux-release"}, - {"Fedora Core", "/etc/fedora-release"}, - {"Gentoo", "/etc/gentoo-release"}, - {"Novell", "/etc/nld-release"}, - {"Ubuntu", "/etc/lsb-release"}, - {"Annvix", "/etc/annvix-release"}, - {"Arch", "/etc/arch-release"}, - {"Arklinux", "/etc/arklinux-release"}, - {"Aurox", "/etc/aurox-release"}, - {"BlackCat", "/etc/blackcat-release"}, - {"Cobalt", "/etc/cobalt-release"}, - {"Conectiva", "/etc/conectiva-release"}, - {"Immunix", "/etc/immunix-release"}, - {"Knoppix", "/etc/knoppix_version"}, - {"Linux-From-Scratch", "/etc/lfs-release"}, - {"Linux-PPC", "/etc/linuxppc-release"}, - {"MkLinux", "/etc/mklinux-release"}, - {"PLD", "/etc/pld-release"}, - {"Slackware", "/etc/slackware-version"}, - {"Slackware", "/etc/slackware-release"}, - {"SMEServer", "/etc/e-smith-release"}, - {"Solaris", "/etc/release"}, - {"Tiny Sofa", "/etc/tinysofa-release"}, - {"UltraPenguin", "/etc/ultrapenguin-release"}, - {"UnitedLinux", "/etc/UnitedLinux-release"}, - {"VALinux", "/etc/va-release"}, - {"Yellow Dog", "/etc/yellowdog-release"}, - {NULL, NULL}, -}; - - /* *----------------------------------------------------------------------------- @@ -319,456 +253,3 @@ GuestInfoGetNicInfo(GuestNicList *nicInfo) // OUT return FALSE; #endif } - -/* - *----------------------------------------------------------------------------- - * - * GetShortName -- - * - * Returns distro information based on .vmx format (distroShort). - * - * Return value: - * Overwrited the short name if we recognise the OS. - * Otherwise leave the short name as it is. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -void -GetShortName(char *distro, // IN: full distro name - char *distroShort, // OUT: short distro name (based on .vmx file) - int distroShortSize) // IN: size of the buffer allocated for short distro name - -{ - char *distroLower = NULL; /* Lower case distro name */ - - distroLower = calloc(strlen(distro) + 1, sizeof *distroLower); - - if (distroLower == NULL) { - Debug("GetShortName: could not allocate memory\n"); - return; - } - - Str_Strcpy(distroLower, distro, distroShortSize); - distroLower = Str_ToLower(distroLower); - - if (strstr(distroLower, "red hat")) { - if (strstr(distroLower, "enterprise")) { - - /* - * Looking for "release x" here instead of "x" as there could be - * build version which can be misleading. - * For example Red Hat Enterprise Linux ES release 4 (Nahant Update 3) - */ - - int release = 0; - char *releaseStart = strstr(distroLower, "release"); - - if (releaseStart) { - sscanf(releaseStart, "release %d", &release); - if (release > 0) { - snprintf(distroShort, distroShortSize, STR_OS_RED_HAT_EN"%d", release); - } - } - - if (release <= 0) { - Debug("GetShortName: could not read Red Hat Enterprise release version\n"); - Str_Strcpy(distroShort, STR_OS_RED_HAT_EN, distroShortSize); - } - - } else { - Str_Strcpy(distroShort, STR_OS_RED_HAT, distroShortSize); - } - } else if (strstr(distroLower, "suse")) { - if (strstr(distroLower, "enterprise")) { - Str_Strcpy(distroShort, STR_OS_SUSE_EN, distroShortSize); - } else if (strstr(distroLower, "sun")) { - Str_Strcpy(distroShort, STR_OS_SUN_DESK, distroShortSize); - } else if (strstr(distroLower, "novell")) { - Str_Strcpy(distroShort, STR_OS_NOVELL, distroShortSize); - } else { - Str_Strcpy(distroShort, STR_OS_SUSE, distroShortSize); - } - } else if (strstr(distroLower, "mandrake")) { - Str_Strcpy(distroShort, STR_OS_MANDRAKE, distroShortSize); - } else if (strstr(distroLower, "turbolinux")) { - Str_Strcpy(distroShort, STR_OS_TURBO, distroShortSize); - } else if (strstr(distroLower, "sun")) { - Str_Strcpy(distroShort, STR_OS_SUN_DESK, distroShortSize); - } else if (strstr(distroLower, "annvix")) { - Str_Strcpy(distroShort, STR_OS_ANNVIX, distroShortSize); - } else if (strstr(distroLower, "arch")) { - Str_Strcpy(distroShort, STR_OS_ARCH, distroShortSize); - } else if (strstr(distroLower, "arklinux")) { - Str_Strcpy(distroShort, STR_OS_ARKLINUX, distroShortSize); - } else if (strstr(distroLower, "aurox")) { - Str_Strcpy(distroShort, STR_OS_AUROX, distroShortSize); - } else if (strstr(distroLower, "black cat")) { - Str_Strcpy(distroShort, STR_OS_BLACKCAT, distroShortSize); - } else if (strstr(distroLower, "cobalt")) { - Str_Strcpy(distroShort, STR_OS_COBALT, distroShortSize); - } else if (strstr(distroLower, "conectiva")) { - Str_Strcpy(distroShort, STR_OS_CONECTIVA, distroShortSize); - } else if (strstr(distroLower, "debian")) { - Str_Strcpy(distroShort, STR_OS_DEBIAN, distroShortSize); - } else if (strstr(distroLower, "fedora")) { - Str_Strcpy(distroShort, STR_OS_FEDORA, distroShortSize); - } else if (strstr(distroLower, "gentoo")) { - Str_Strcpy(distroShort, STR_OS_GENTOO, distroShortSize); - } else if (strstr(distroLower, "immunix")) { - Str_Strcpy(distroShort, STR_OS_IMMUNIX, distroShortSize); - } else if (strstr(distroLower, "linux-from-scratch")) { - Str_Strcpy(distroShort, STR_OS_LINUX_FROM_SCRATCH, distroShortSize); - } else if (strstr(distroLower, "linux-ppc")) { - Str_Strcpy(distroShort, STR_OS_LINUX_PPC, distroShortSize); - } else if (strstr(distroLower, "mandriva")) { - Str_Strcpy(distroShort, STR_OS_MANDRIVA, distroShortSize); - } else if (strstr(distroLower, "mklinux")) { - Str_Strcpy(distroShort, STR_OS_MKLINUX, distroShortSize); - } else if (strstr(distroLower, "pld")) { - Str_Strcpy(distroShort, STR_OS_PLD, distroShortSize); - } else if (strstr(distroLower, "slackware")) { - Str_Strcpy(distroShort, STR_OS_SLACKWARE, distroShortSize); - } else if (strstr(distroLower, "sme server")) { - Str_Strcpy(distroShort, STR_OS_SMESERVER, distroShortSize); - } else if (strstr(distroLower, "tiny sofa")) { - Str_Strcpy(distroShort, STR_OS_TINYSOFA, distroShortSize); - } else if (strstr(distroLower, "ubuntu")) { - Str_Strcpy(distroShort, STR_OS_UBUNTU, distroShortSize); - } else if (strstr(distroLower, "ultra penguin")) { - Str_Strcpy(distroShort, STR_OS_ULTRAPENGUIN, distroShortSize); - } else if (strstr(distroLower, "united linux")) { - Str_Strcpy(distroShort, STR_OS_UNITEDLINUX, distroShortSize); - } else if (strstr(distroLower, "va linux")) { - Str_Strcpy(distroShort, STR_OS_VALINUX, distroShortSize); - } else if (strstr(distroLower, "yellow dog")) { - Str_Strcpy(distroShort, STR_OS_YELLOW_DOG, distroShortSize); - } - - free(distroLower); - -} - - -/* - *----------------------------------------------------------------------------- - * - * ReadDistroFile -- - * - * Look for a distro version file /etc/xxx-release. - * Once found, read the file in and figure out which distribution. - * - * Return value: - * Returns TRUE on success and FALSE on failure. - * Returns distro information verbatium from /etc/xxx-release (distro). - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -static Bool -ReadDistroFile(char *filename, // IN: distro version file name - int distroSize, // IN: size of the buffer allocated for OS distro name - char *distro) // OUT: full distro name -{ - int fd; - int buf_sz; - struct stat st; - Bool ret = FALSE; - char *distroOrig = NULL; - char distroPart[DISTRO_BUF_SIZE]; - char *tmpDistroPos = NULL; - int i = 0; - - if ((fd = open(filename, O_RDONLY)) < 0) { - Debug("ReadDistroFile: could not open file%s: %d\n", filename, errno); - return FALSE; - } - - if (fstat(fd, &st)) { - Debug("ReadDistroFile: could not stat the file %s: %d\n", filename, errno); - goto out; - } - - buf_sz = st.st_size; - if (buf_sz >= distroSize) { - Debug("ReadDistroFile: input buffer too small\n"); - goto out; - } - distroOrig = calloc(distroSize, sizeof *distroOrig); - - if (distroOrig == NULL) { - Debug("ReadDistroFile: could not allocate memory\n"); - close(fd); - return FALSE; - } - - if (read(fd, distroOrig, buf_sz) != buf_sz) { - Debug("ReadDistroFile: could not read file %s: %d\n", filename, errno); - goto out; - } - - distroOrig[buf_sz] = '\0'; - - /* - * For the case where we do have a release file in the LSB format, - * but there is no LSB module, let's parse the LSB file for possible fields. - */ - - distro[0] = '\0'; - - for (i = 0; lsbFields[i].name != NULL; i++) { - - tmpDistroPos = strstr(distroOrig, lsbFields[i].name); - if (tmpDistroPos) { - sscanf(tmpDistroPos, lsbFields[i].scanstring, distroPart); - if (distroPart[0] == '"') { - char *tmpMakeNull = NULL; - - tmpDistroPos += strlen(lsbFields[i].name) + 1; - tmpMakeNull = strchr(tmpDistroPos + 1 , '"'); - if (tmpMakeNull) { - *tmpMakeNull = '\0'; - Str_Strcat(distro, tmpDistroPos, distroSize); - *tmpMakeNull = '"' ; - } - } else { - Str_Strcat(distro, distroPart, distroSize); - } - Str_Strcat(distro, " ", distroSize); - } - } - - if (distro[0] == '\0') { - /* Copy original string. What we got wasn't LSB compliant. */ - Str_Strcpy(distro, distroOrig, distroSize); - } - - ret = TRUE; - -out: - close(fd); - free(distroOrig); - - return ret; -} - - -/* - *----------------------------------------------------------------------------- - * - * GuestInfoGetOSName -- - * - * Return OS version information. First retrieve OS information using - * uname, then look in /etc/xxx-release file to get the distro info. - * osFullName will be: - * - * An example of such string would be: - * Linux 2.4.18-3 Red Hat Linux release 7.3 (Valhalla) - * - * osName contains an os name in the same format that is used - * in .vmx file. - * - * Return value: - * Returns TRUE on success and FALSE on failure. - * Returns the guest's full OS name (osNameFull) - * Returns the guest's OS name in the same format as .vmx file (osName) - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -GuestInfoGetOSName(unsigned int outBufFullLen, // IN: length of osNameFull buffer - unsigned int outBufLen, // IN: length of osName buffer - char *osNameFull, // OUT: Full OS name - char *osName) // OUT: OS name (.vmx file format) -{ - struct utsname buf; - unsigned int lastCharPos; - const char *lsbCmd = "lsb_release -sd 2>/dev/null"; - char *lsbOutput = NULL; - - /* - * Use uname to get complete OS information. - */ - - if (uname(&buf) < 0) { - Debug("GuestInfoGetOSName: uname failed %d\n", errno); - return FALSE; - } - - - if (strlen(buf.sysname) + strlen(buf.release) + 3 > outBufFullLen) { - Debug("GuestInfoGetOSName: Error: buffer too small\n"); - return FALSE; - } - - Str_Strcpy(osName, STR_OS_EMPTY, outBufLen); - Str_Strcpy(osNameFull, buf.sysname, outBufFullLen); - Str_Strcat(osNameFull, STR_OS_EMPTY, outBufFullLen); - Str_Strcat(osNameFull, buf.release, outBufFullLen); - - /* - * Check to see if this is Linux - * If yes, determine the distro by looking for /etc/xxx file. - */ - - if (strstr(osNameFull, "Linux")) { - char distro[DISTRO_BUF_SIZE]; - char distroShort[DISTRO_BUF_SIZE]; - int i = 0; - int distroSize = sizeof distro; - - /* - * Write default distro string depending on - * the kernel version. - * If later we find more detailed information - * this will get overwritten. - */ - - if (strstr(buf.release, "2.4")) { - Str_Strcpy(distro, STR_OS_OTHER_24_FULL, distroSize); - Str_Strcpy(distroShort, STR_OS_OTHER_24, distroSize); - } else if (strstr(buf.release, "2.6")) { - Str_Strcpy(distro, STR_OS_OTHER_26_FULL, distroSize); - Str_Strcpy(distroShort, STR_OS_OTHER_26, distroSize); - } else { - Str_Strcpy(distro, STR_OS_OTHER_FULL, distroSize); - Str_Strcpy(distroShort, STR_OS_OTHER, distroSize); - } - - /* - * Try to get OS detailed information - * from the lsb_release command. - */ - lsbOutput = GuestApp_GetCmdOutput(lsbCmd); - - if (!lsbOutput) { - /* - * Try to get more detailed information - * from the version file. - */ - - for (i = 0; distroArray[i].filename != NULL; i++) { - if (ReadDistroFile(distroArray[i].filename, distroSize, distro)) { - break; - } - } - - /* - * If we failed to read every distro file, exit now, before calling - * strlen on the distro buffer (which wasn't set). - */ - if (distroArray[i].filename == NULL) { - Debug("GuestInfoGetOSName: Error: no distro file found\n"); - return FALSE; - } - } else { - char *lsbStart = lsbOutput; - char *quoteEnd = NULL; - - if (lsbStart[0] == '"') { - lsbStart++; - quoteEnd = strchr(lsbStart, '"'); - if (quoteEnd) { - *quoteEnd = '\0'; - } - } - Str_Strcpy(distro, lsbStart, distroSize); - free(lsbOutput); - } - - GetShortName(distro, distroShort, distroSize); - - if (strlen(distro) + strlen(osNameFull) + 2 > outBufFullLen) { - Debug("GuestInfoGetOSName: Error: buffer too small\n"); - return FALSE; - } - - Str_Strcat(osNameFull, " ", outBufFullLen); - Str_Strcat(osNameFull, distro, outBufFullLen); - - if (strlen(distroShort) + 1 > outBufLen) { - Debug("GuestInfoGetOSName: Error: buffer too small\n"); - return FALSE; - } - - Str_Strcpy(osName, distroShort, outBufLen); - } else if (strstr(osNameFull, "FreeBSD")) { - size_t nameLen = sizeof STR_OS_FREEBSD - 1; - size_t releaseLen = 0; - char *dashPtr; - - /* - * FreeBSD releases report their version as "x.y-RELEASE". We'll be naive - * look for the first dash, and use everything before it as the version - * number. - */ - dashPtr = Str_Strchr(buf.release, '-'); - if (dashPtr != NULL) { - releaseLen = dashPtr - buf.release; - } - - if (nameLen + releaseLen + 1 > outBufLen) { - Debug("GuestInfoGetOSName: Error: buffer too small\n"); - return FALSE; - } - - Str_Strcpy(osName, STR_OS_FREEBSD, outBufLen); - if (releaseLen != 0) { - Str_Strncat(osName, outBufLen, buf.release, releaseLen); - } - } else if (strstr(osNameFull, "SunOS")) { - size_t nameLen = sizeof STR_OS_SOLARIS - 1; - size_t releaseLen = 0; - char *periodPtr; - - /* - * Solaris releases report their version as "x.y". For our supported - * releases it seems that x is always "5", and is ignored in favor of - * y for the version number. We'll be naive and look for the first - * period, and use the entire string after that as the version number. - */ - periodPtr = Str_Strchr(buf.release, '.'); - if (periodPtr != NULL) { - releaseLen = (buf.release + strlen(buf.release)) - periodPtr; - } - - if (nameLen + releaseLen + 1 > outBufLen) { - Debug("GuestInfoGetOSName: Error: buffer too small\n"); - return FALSE; - } - - Str_Strcpy(osName, STR_OS_SOLARIS, outBufLen); - if (releaseLen != 0) { - Str_Strcat(osName, periodPtr + 1, outBufLen); - } - } - - if (Hostinfo_GetSystemBitness() == 64) { - if (strlen(osName) + sizeof STR_OS_64BIT_SUFFIX > outBufLen) { - Debug("GuestInfoGetOSName: Error: buffer too small\n"); - return FALSE; - } - Str_Strcat(osName, STR_OS_64BIT_SUFFIX, outBufLen); - } - - /* - * Before returning, truncate - * the \n character at the end of the full name. - */ - lastCharPos = strlen(osNameFull) - 1; - if (osNameFull[lastCharPos] == '\n') { - osNameFull[lastCharPos] = '\0'; - } - - return TRUE; -} diff --git a/open-vm-tools/lib/guestInfo/guestInfoServer.c b/open-vm-tools/lib/guestInfo/guestInfoServer.c index 076fca4ba..3bb8064b6 100644 --- a/open-vm-tools/lib/guestInfo/guestInfoServer.c +++ b/open-vm-tools/lib/guestInfo/guestInfoServer.c @@ -41,6 +41,7 @@ #include "util.h" #include "rpcout.h" #include "rpcvmx.h" +#include "hostinfo.h" #include "guestInfo.h" #include "guestApp.h" #include "guestInfoServer.h" @@ -253,7 +254,8 @@ GuestInfoGather(void *clientData) // IN: unused } /* Gather all the relevant guest information. */ - if (!GuestInfoGetOSName(sizeof osNameFull, sizeof osName, osNameFull, osName)) { + if (!Hostinfo_GetOSName(sizeof osNameFull, sizeof osName, osNameFull, + osName)) { Debug("Failed to get OS info.\n"); } else { if (!GuestInfoUpdateVmdb(INFO_OS_NAME_FULL, osNameFull)) { diff --git a/open-vm-tools/lib/guestRpc/Makefile.am b/open-vm-tools/lib/guestRpc/Makefile.am index 27cde3ca3..7fc685fb1 100644 --- a/open-vm-tools/lib/guestRpc/Makefile.am +++ b/open-vm-tools/lib/guestRpc/Makefile.am @@ -60,67 +60,67 @@ clean-local: # to copy the sources to the build dir before compiling them. ghiGetBinaryHandlers.h: ghiGetBinaryHandlers.x - if [ $(top_builddir) != $(top_srcdir) ]; then \ - cp $(top_srcdir)/lib/guestRpc/ghiGetBinaryHandlers.x $(builddir); \ + if [ $(top_builddir) != $(top_srcdir) ]; then \ + cp -f $(top_srcdir)/lib/guestRpc/ghiGetBinaryHandlers.x $(builddir); \ fi - $(RPCGEN) -h -o $@ ghiGetBinaryHandlers.x + $(RPCGEN) $(RPCGENFLAGS) -h -o $@ $(builddir)/ghiGetBinaryHandlers.x $(SED) 's,rpc/rpc\.h,vmxrpc.h,' $@ > $@.tmp mv $@.tmp $@ $(MKDIR_P) $(top_builddir)/lib/include/guestrpc cp $@ $(top_builddir)/lib/include/guestrpc ghiGetBinaryHandlers_xdr.c: ghiGetBinaryHandlers.x ghiGetBinaryHandlers.h - $(RPCGEN) -c -o $@ ghiGetBinaryHandlers.x + $(RPCGEN) $(RPCGENFLAGS) -c -o $@ $(builddir)/ghiGetBinaryHandlers.x ghiProtocolHandler.h: ghiProtocolHandler.x - if [ $(top_builddir) != $(top_srcdir) ]; then \ - cp $(top_srcdir)/lib/guestRpc/ghiProtocolHandler.x $(builddir); \ + if [ $(top_builddir) != $(top_srcdir) ]; then \ + cp -f $(top_srcdir)/lib/guestRpc/ghiProtocolHandler.x $(builddir); \ fi - $(RPCGEN) -h -o $@ ghiProtocolHandler.x + $(RPCGEN) $(RPCGENFLAGS) -h -o $@ $(builddir)/ghiProtocolHandler.x $(SED) 's,rpc/rpc\.h,vmxrpc.h,' $@ > $@.tmp mv $@.tmp $@ $(MKDIR_P) $(top_builddir)/lib/include/guestrpc cp $@ $(top_builddir)/lib/include/guestrpc ghiProtocolHandler_xdr.c: ghiProtocolHandler.x ghiProtocolHandler.h - $(RPCGEN) -c -o $@ ghiProtocolHandler.x + $(RPCGEN) $(RPCGENFLAGS) -c -o $@ $(builddir)/ghiProtocolHandler.x nicinfo.h: nicinfo.x - if [ $(top_builddir) != $(top_srcdir) ]; then \ - cp $(top_srcdir)/lib/guestRpc/nicinfo.x $(builddir); \ + if [ $(top_builddir) != $(top_srcdir) ]; then \ + cp -f $(top_srcdir)/lib/guestRpc/nicinfo.x $(builddir); \ fi - $(RPCGEN) -h -o $@ $< + $(RPCGEN) $(RPCGENFLAGS) -h -o $@ $(builddir)/nicinfo.x $(SED) 's,rpc/rpc\.h,vmxrpc.h,' $@ > $@.tmp mv $@.tmp $@ $(MKDIR_P) $(top_builddir)/lib/include/guestrpc cp $@ $(top_builddir)/lib/include/guestrpc +nicinfo_xdr.c: nicinfo.x nicinfo.h + $(RPCGEN) $(RPCGENFLAGS) -c -o $@ $(builddir)/nicinfo.x + guestlibV3.h: guestlibV3.x - if [ $(top_builddir) != $(top_srcdir) ]; then \ - cp $(top_srcdir)/lib/guestRpc/guestlibV3.x $(builddir); \ + if [ $(top_builddir) != $(top_srcdir) ]; then \ + cp -f $(top_srcdir)/lib/guestRpc/guestlibV3.x $(builddir); \ fi - $(RPCGEN) -h -o $@ $< + $(RPCGEN) $(RPCGENFLAGS) -h -o $@ $(builddir)/guestlibV3.x $(SED) 's,rpc/rpc\.h,vmxrpc.h,' $@ > $@.tmp mv $@.tmp $@ $(MKDIR_P) $(top_builddir)/lib/include/guestrpc cp $@ $(top_builddir)/lib/include/guestrpc -nicinfo_xdr.c: nicinfo.x nicinfo.h - $(RPCGEN) -c -o $@ nicinfo.x - guestlibV3_xdr.c: guestlibV3.x guestlibV3.h - $(RPCGEN) -c -o $@ guestlibV3.x + $(RPCGEN) $(RPCGENFLAGS) -c -o $@ $(builddir)/guestlibV3.x unityActive.h: unityActive.x - if [ $(top_builddir) != $(top_srcdir) ]; then \ - cp $(top_srcdir)/lib/guestRpc/unityActive.x $(builddir); \ + if [ $(top_builddir) != $(top_srcdir) ]; then \ + cp -f $(top_srcdir)/lib/guestRpc/unityActive.x $(builddir); \ fi - $(RPCGEN) -h -o $@ unityActive.x + $(RPCGEN) $(RPCGENFLAGS) -h -o $@ $(builddir)/unityActive.x $(SED) 's,rpc/rpc\.h,vmxrpc.h,' $@ > $@.tmp mv $@.tmp $@ $(MKDIR_P) $(top_builddir)/lib/include/guestrpc cp $@ $(top_builddir)/lib/include/guestrpc unityActive_xdr.c: unityActive.x unityActive.h - $(RPCGEN) -c -o $@ unityActive.x - GetBinaryHandlers.h: ghiGetBinaryHandlers.x + $(RPCGEN) $(RPCGENFLAGS) -c -o $@ $(builddir)/unityActive.x + diff --git a/open-vm-tools/lib/guestRpc/guestlibV3.x b/open-vm-tools/lib/guestRpc/guestlibV3.x index dbf5c6ff1..07bfd7c4e 100644 --- a/open-vm-tools/lib/guestRpc/guestlibV3.x +++ b/open-vm-tools/lib/guestRpc/guestlibV3.x @@ -66,10 +66,12 @@ enum GuestLibV3TypeIds { GUESTLIB_ELAPSED_MS = 17, GUESTLIB_RESOURCE_POOL_PATH = 18, + GUESTLIB_CPU_STOLEN_MS = 19, + GUESTLIB_MEM_TARGET_SIZE_MB = 20, /*------ Add any new statistics above this line. ------- */ /*------ Bump this when adding to this list. -------*/ - GUESTLIB_MAX_STATISTIC_ID = 19 + GUESTLIB_MAX_STATISTIC_ID = 21 }; union GuestLibV3Stat switch (GuestLibV3TypeIds d) { @@ -113,5 +115,10 @@ union GuestLibV3Stat switch (GuestLibV3TypeIds d) { case GUESTLIB_RESOURCE_POOL_PATH: struct GuestLibV3String resourcePoolPath; + + case GUESTLIB_CPU_STOLEN_MS: + struct GuestLibV3StatUint64 cpuStolenMs; + case GUESTLIB_MEM_TARGET_SIZE_MB: + struct GuestLibV3StatUint64 memTargetSizeMB; }; diff --git a/open-vm-tools/lib/hgfsServer/hgfsServer.c b/open-vm-tools/lib/hgfsServer/hgfsServer.c index d76220d3d..f79fff01a 100644 --- a/open-vm-tools/lib/hgfsServer/hgfsServer.c +++ b/open-vm-tools/lib/hgfsServer/hgfsServer.c @@ -4404,6 +4404,7 @@ HgfsPackGetattrReply(HgfsFileAttrInfo *attr, // IN: attr stucture reply->attr.groupId = attr->groupId; reply->attr.hostFileId = attr->hostFileId; reply->attr.volumeId = attr->volumeId; + reply->attr.effectivePerms = attr->effectivePerms; reply->reserved = 0; *packetSize = HGFS_REP_PAYLOAD_SIZE_V3(reply) + utf8TargetNameLen; break; diff --git a/open-vm-tools/lib/hgfsServer/hgfsServerInt.h b/open-vm-tools/lib/hgfsServer/hgfsServerInt.h index 3a9036e7b..7dd21f4aa 100644 --- a/open-vm-tools/lib/hgfsServer/hgfsServerInt.h +++ b/open-vm-tools/lib/hgfsServer/hgfsServerInt.h @@ -283,6 +283,7 @@ typedef struct HgfsFileAttrInfo { uint32 groupId; /* group identifier, ignored by Windows */ uint64 hostFileId; /* File Id of the file on host: inode_t on Linux */ uint32 volumeId; /* Volume Id of the volune on which the file resides */ + uint32 effectivePerms; /* Permissions in effect for the current user */ } HgfsFileAttrInfo; typedef struct HgfsCreateDirInfo { diff --git a/open-vm-tools/lib/hgfsServer/hgfsServerLinux.c b/open-vm-tools/lib/hgfsServer/hgfsServerLinux.c index 0213463d9..2ecb15a62 100644 --- a/open-vm-tools/lib/hgfsServer/hgfsServerLinux.c +++ b/open-vm-tools/lib/hgfsServer/hgfsServerLinux.c @@ -333,7 +333,9 @@ static HgfsInternalStatus HgfsSetattrFromName(char *cpName, static Bool HgfsIsShareRoot(char const *cpName, size_t cpNameSize); static HgfsInternalStatus HgfsGetHiddenXAttr(char const *fileName, Bool *attribute); static HgfsInternalStatus HgfsSetHiddenXAttr(char const *fileName, Bool value); - +static HgfsInternalStatus HgfsEffectivePermissions(char *fileName, + Bool readOnlyShare, + uint32 *permissions); #ifdef HGFS_OPLOCKS /* *----------------------------------------------------------------------------- @@ -1196,6 +1198,8 @@ HgfsGetattrResolveAlias(char const *fileName, // IN: Input filename Boolean wasAliased; OSStatus osStatus; + ASSERT_ON_COMPILE(sizeof osStatus == sizeof (int32)); + /* * Create and resolve an FSRef of the desired path. We pass FALSE to * resolveAliasChains because aliases to aliases should behave as @@ -1205,7 +1209,7 @@ HgfsGetattrResolveAlias(char const *fileName, // IN: Input filename osStatus = FSPathMakeRef(fileName, &fileRef, NULL); if (osStatus != noErr) { LOG(4, ("HgfsGetattrResolveAlias: could not create file reference: " - "error %lu\n", osStatus)); + "error %d\n", (int32)osStatus)); goto exit; } /* @@ -1225,7 +1229,7 @@ HgfsGetattrResolveAlias(char const *fileName, // IN: Input filename &wasAliased, kResolveAliasFileNoUI); if (osStatus != noErr) { LOG(4, ("HgfsGetattrResolveAlias: could not resolve reference: error " - "%lu\n", osStatus)); + "%d\n", (int32)osStatus)); goto exit; } @@ -1776,6 +1780,42 @@ HgfsServerCaseConversionRequired() } +/* + *----------------------------------------------------------------------------- + * + * HgfsEffectivePermissions -- + * + * Get permissions that are in efffect for the current user. + * + * Results: + * Zero on success. + * Non-zero on failure. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static HgfsInternalStatus +HgfsEffectivePermissions(char *fileName, // IN: Input filename + Bool readOnlyShare, // IN: Share name + uint32 *permissions) // OUT: Effective permissions +{ + *permissions = 0; + if (access(fileName, R_OK) == 0) { + *permissions |= HGFS_PERM_READ; + } + if (access(fileName, X_OK) == 0) { + *permissions |= HGFS_PERM_EXEC; + } + if (!readOnlyShare && (access(fileName, W_OK) == 0)) { + *permissions |= HGFS_PERM_WRITE; + } + return 0; +} + + /* *----------------------------------------------------------------------------- * @@ -1786,6 +1826,12 @@ HgfsServerCaseConversionRequired() * readlink to get it. If not a symlink, the targetName argument is * untouched. Does necessary translation between Unix file stats and the * HgfsFileAttrInfo formats. + * NOTE: The function is different from HgfsGetAttrFromId: this function returns + * effectve permissions while HgfsGetAttrFromId does not. + * The reason for this asymmetry is that effective permissions are needed + * to get a new handle. If the file is already opened then + * getting effective permissions does not have any value. However getting + * effective permissions would hurt perfomance and should be avoided. * * Results: * Zero on success. @@ -1954,6 +2000,22 @@ HgfsGetattrFromName(char *fileName, // IN/OUT: Input filenam */ HgfsGetHiddenAttr(fileName, attr); + /* Get effective permissions if we can */ + if (!(S_ISLNK(stats.st_mode))) { + HgfsOpenMode shareMode; + uint32 permissions; + HgfsNameStatus nameStatus; + nameStatus = HgfsServerPolicy_GetShareMode(shareName, strlen(shareName), + &shareMode); + if (nameStatus == HGFS_NAME_STATUS_COMPLETE && + HgfsEffectivePermissions(fileName, + shareMode == HGFS_OPEN_MODE_READ_ONLY, + &permissions) == 0) { + attr->mask |= HGFS_ATTR_VALID_EFFECTIVE_PERMS; + attr->effectivePerms = permissions; + } + } + exit: free(myTargetName); return status; @@ -3677,15 +3739,18 @@ HgfsServerSearchRead(char const *packetIn, // IN: incoming packet status = HgfsGetattrFromName(fullName, configOptions, search.utf8ShareName, &attr, NULL); - free(fullName); - if (status != 0) { - LOG(4, ("HgfsServerSearchRead: stat FAILED, removing\n")); - free(HgfsGetSearchResult(hgfsSearchHandle, requestedOffset, TRUE)); - free(dent); - continue; + HgfsOp savedOp = attr.requestType; + LOG(4, ("HgfsServerSearchRead: stat FAILED %s (%d)\n", + fullName, status)); + memset(&attr, 0, sizeof attr); + attr.requestType = savedOp; + attr.type = HGFS_FILE_TYPE_REGULAR; + attr.mask = 0; } + free(fullName); + #if defined(__APPLE__) /* * HGFS clients receive names in unicode normal form C, diff --git a/open-vm-tools/lib/include/backdoor_def.h b/open-vm-tools/lib/include/backdoor_def.h index ac09bf5e9..7bb991ad4 100644 --- a/open-vm-tools/lib/include/backdoor_def.h +++ b/open-vm-tools/lib/include/backdoor_def.h @@ -118,7 +118,8 @@ #define BDOOR_CMD_ACPI_HOTPLUG_MEMORY 57 #define BDOOR_CMD_ACPI_HOTPLUG_CBRET 58 #define BDOOR_CMD_GET_HOST_VIDEO_MODES 59 -#define BDOOR_CMD_MAX 60 +#define BDOOR_CMD_ACPI_HOTPLUG_CPU 60 +#define BDOOR_CMD_MAX 61 /* * IMPORTANT NOTE: When modifying the behavior of an existing backdoor command, diff --git a/open-vm-tools/lib/include/buildNumber.h b/open-vm-tools/lib/include/buildNumber.h index 343be2ca8..7161abd07 100644 --- a/open-vm-tools/lib/include/buildNumber.h +++ b/open-vm-tools/lib/include/buildNumber.h @@ -1,12 +1,12 @@ #define BUILD_NUMBER \ - "build-142982" + "build-148847" #define BUILD_NUMBER_NUMERIC \ - 142982 + 148847 #define BUILD_NUMBER_NUMERIC_STRING \ - "142982" + "148847" #define PRODUCT_BUILD_NUMBER \ - "product-build-444" + "product-build-472" #define PRODUCT_BUILD_NUMBER_NUMERIC \ - 444 + 472 #define PRODUCT_BUILD_NUMBER_NUMERIC_STRING \ - "444" + "472" diff --git a/open-vm-tools/lib/include/copyPaste.hh b/open-vm-tools/lib/include/copyPaste.hh new file mode 100644 index 000000000..721c894cc --- /dev/null +++ b/open-vm-tools/lib/include/copyPaste.hh @@ -0,0 +1,75 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + +/* + * copyPaste.hh -- + * + * CopyPaste common layer object for guest. + */ + +#ifndef COPY_PASTE_HH +#define COPY_PASTE_HH + +#include +#include +#include "copyPasteBase.h" +#include "copyPasteRpc.hh" + +class CopyPaste + : public CopyPasteBase, + public sigc::trackable +{ + public: + CopyPaste(void); + virtual ~CopyPaste(void); + + /* Common CopyPaste layer API exposed to UI (all platforms). */ + /* Local UI as CopyPaste source. */ + virtual bool SetRemoteClipboard(const CPClipboard *clip); + + /* Local UI as CopyPaste target. */ + virtual bool GetRemoteClipboard(void) {return false;} + virtual std::string GetFiles(std::string dir = ""); + + virtual bool IsCopyPasteAllowed(void) { return mCopyPasteAllowed; } + + void VmxCopyPasteVersionChanged(struct RpcIn *rpcIn, + uint32 version); + void SetCopyPasteAllowed(bool isCopyPasteAllowed) + { mCopyPasteAllowed = isCopyPasteAllowed; } + + private: + /* Callbacks from rpc. */ + void OnGetLocalClipboard(void); + void OnGetRemoteClipboardDone(const CPClipboard *clip); + void OnHGFileCopyDone(bool success); + + std::string SetupDestDir(const std::string &destDir); + + bool mCanCopyPaste; + bool mCopyPasteAllowed; + + CopyPasteRpc *mRpc; + uint32 mVmxCopyPasteVersion; + VmTimeType mStateChangeTime; + + std::string mStagingDir; +}; + +#endif // COPY_PASTE_HH + diff --git a/open-vm-tools/lib/include/copyPasteBase.h b/open-vm-tools/lib/include/copyPasteBase.h new file mode 100644 index 000000000..ccd8b4b82 --- /dev/null +++ b/open-vm-tools/lib/include/copyPasteBase.h @@ -0,0 +1,82 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + +/* + * copyPasteBase.h -- + * + * Base object for CopyPaste. This is the common interface between UI and + * cui CopyPaste protocol layer. Both host side and guest side, and also + * different platforms, share this same interface. + */ + +#ifndef COPYPASTE_BASE_H +#define COPYPASTE_BASE_H + +#include + +#ifdef VMX86_TOOLS + /* + * LIB_EXPORT definition is not needed on guest. + */ +# define LIB_EXPORT +#else + /* + * The interface class on host side for Windows is in dll which needs + * LIB_EXPORT definition. + */ +# include "libExport.hh" +#endif + +extern "C" { + #include "vm_basic_types.h" + #include "dnd.h" +} + +class LIB_EXPORT CopyPasteBase +{ + public: + enum COPYPASTE_STATE { + CPSTATE_INVALID = 0, + CPSTATE_READY, + CPSTATE_REQUESTING_CLIPBOARD, + }; + + virtual ~CopyPasteBase(void) {}; + + /* sigc signals for local UI callbacks. */ + /* Local UI as CopyPaste source. */ + sigc::signal newClipboard; + /* Local UI as CopyPaste target. */ + sigc::signal localGetClipboard; + sigc::signal localGetFilesDoneChanged; + + /* cui CopyPaste protocol layer API exposed to UI (all platforms). */ + /* Local UI as CopyPaste source. */ + virtual bool SetRemoteClipboard(const CPClipboard *clip) = 0; + + /* Local UI as CopyPaste target. */ + virtual bool GetRemoteClipboard(void) = 0; + + virtual bool IsCopyPasteAllowed(void) = 0; + + protected: + COPYPASTE_STATE mState; +}; + +#endif // COPYPASTE_BASE_H + diff --git a/open-vm-tools/lib/include/copyPasteRpc.hh b/open-vm-tools/lib/include/copyPasteRpc.hh new file mode 100644 index 000000000..0b33d509a --- /dev/null +++ b/open-vm-tools/lib/include/copyPasteRpc.hh @@ -0,0 +1,50 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + +/* + * copyPasteRpc.hh -- + * + * Rpc layer object for CopyPaste. + */ + +#ifndef COPY_PASTE_RPC_HH +#define COPY_PASTE_RPC_HH + +#include +#include "vm_basic_types.h" + +extern "C" { + #include "dnd.h" +} + +class CopyPasteRpc +{ + public: + virtual ~CopyPasteRpc(void) {}; + + /* sigc signals for callback. */ + sigc::signal ghGetClipboardChanged; + sigc::signal hgSetClipboardChanged; + sigc::signal hgFileCopyDoneChanged; + + /* CopyPaste Rpc functions. */ + virtual bool GHGetClipboardDone(const CPClipboard* clip) = 0; + virtual bool HGStartFileCopy(const char *stagingDirCP, size_t sz) = 0; +}; + +#endif // COPY_PASTE_RPC_HH diff --git a/open-vm-tools/lib/include/copyPasteRpcV3.hh b/open-vm-tools/lib/include/copyPasteRpcV3.hh new file mode 100644 index 000000000..19d8904f5 --- /dev/null +++ b/open-vm-tools/lib/include/copyPasteRpcV3.hh @@ -0,0 +1,49 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + +/* + * copyPasteRpcV3.hh -- + * + * Rpc layer object for CopyPaste. + */ + +#ifndef COPY_PASTE_RPC_V3_HH +#define COPY_PASTE_RPC_V3_HH + +#include +#include "copyPasteRpc.hh" +#include "dndTransport.hh" + +class CopyPasteRpcV3 + : public CopyPasteRpc, + public sigc::trackable +{ + public: + CopyPasteRpcV3(struct RpcIn *rpcIn); + virtual ~CopyPasteRpcV3(void); + + /* CopyPaste Rpc functions. */ + virtual bool GHGetClipboardDone(const CPClipboard* clip); + virtual bool HGStartFileCopy(const char *stagingDirCP, size_t sz); + + private: + void OnRecvMsg(const uint8 *data, size_t dataSize); + DnDTransport* mTransport; +}; + +#endif // COPY_PASTE_RPC_V3_HH diff --git a/open-vm-tools/lib/include/dnd.h b/open-vm-tools/lib/include/dnd.h index cec0cabd1..161f70010 100644 --- a/open-vm-tools/lib/include/dnd.h +++ b/open-vm-tools/lib/include/dnd.h @@ -77,6 +77,12 @@ #define FCP_COPY_DELAY 1000000 // 1 second #define TARGET_NAME_TIMESTAMP "TIMESTAMP" +#define TARGET_NAME_STRING "STRING" +#define TARGET_NAME_TEXT_PLAIN "text/plain" +#define TARGET_NAME_UTF8_STRING "UTF8_STRING" +#define TARGET_NAME_COMPOUND_TEXT "COMPOUND_TEXT" +#define TARGET_NAME_APPLICATION_RTF "application/rtf" +#define TARGET_NAME_TEXT_RICHTEXT "text/richtext" #define DRAG_TARGET_NAME_URI_LIST "text/uri-list" #define DRAG_LEAVE_TIMEOUT 500 diff --git a/open-vm-tools/lib/include/dnd.hh b/open-vm-tools/lib/include/dnd.hh new file mode 100644 index 000000000..816a35ad1 --- /dev/null +++ b/open-vm-tools/lib/include/dnd.hh @@ -0,0 +1,94 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + +/* + * dnd.hh -- + * + * DnD protocol object. + */ + +#ifndef DND_HH +#define DND_HH + +#include +#include +#include "dndBase.h" +#include "dndRpc.hh" + +struct Event; +struct DblLnkLst_Links; + +class DnD + : public DnDBase, + public sigc::trackable +{ + public: + DnD(DblLnkLst_Links *eventQueue); + virtual ~DnD(void); + + /* Common DnD layer API exposed to UI (all platforms). */ + /* Local UI as DnD source. */ + virtual DND_DROPEFFECT GetFeedback(void) { return DROP_UNKNOWN; } + virtual void SourceCancel(void) {} + virtual void SourceDrop(DND_DROPEFFECT feedback) {} + + /* Local UI as DnD target. */ + virtual void DragEnter(const CPClipboard *clip); + virtual void SetMouse(int32 x, int32 y, bool down) {} + virtual void SetFeedback(DND_DROPEFFECT feedback); + virtual void DragLeave(int32 x, int32 y) {} + virtual void TargetCancel(void) {} + virtual void TargetDrop(const CPClipboard *clip, + int32 x, + int32 y) {} + + virtual bool IsDnDAllowed(void) { return mDnDAllowed; } + + void VmxDnDVersionChanged(struct RpcIn *rpcIn, + uint32 version); + + void SetDnDAllowed(bool isDnDAllowed) + { mDnDAllowed = isDnDAllowed; } + void UngrabTimeout(void); + void ResetDnD(void); + void HGDragStartDone(void); + + private: + /* Callbacks from rpc. */ + void OnGHUpdateUnityDetWnd(bool bShow, uint32 unityWndId); + void OnGHQueryPendingDrag(int x, int y); + void OnGHCancel(void); + void OnHGDragEnter(const CPClipboard *clip); + void OnHGDragStart(void); + void OnHGCancel(void); + void OnHGDrop(void); + void OnHGFileCopyDone(bool cancel, + std::vector stagingDir); + + std::string SetupDestDir(const std::string &destDir); + + DnDRpc *mRpc; + uint32 mVmxDnDVersion; + bool mDnDAllowed; + std::string mStagingDir; + Event *mUngrabTimeout; + DblLnkLst_Links *mEventQueue; +}; + +#endif // DND_HH + diff --git a/open-vm-tools/lib/include/dndBase.h b/open-vm-tools/lib/include/dndBase.h new file mode 100644 index 000000000..2c2b64960 --- /dev/null +++ b/open-vm-tools/lib/include/dndBase.h @@ -0,0 +1,121 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + +/* + * dndBase.h -- + * + * Base object for DnD. This is the common interface between UI and + * cui DnD protocol layer. Both host side and guest side, and also + * different platforms, share this same interface. + */ + +#ifndef DND_BASE_H +#define DND_BASE_H + +#include +#include + +#ifdef VMX86_TOOLS + /* + * LIB_EXPORT definition is not needed on guest. + */ +# define LIB_EXPORT +#else + /* + * The interface class on host side for Windows is in dll which needs + * LIB_EXPORT definition. + */ +# include "libExport.hh" +#endif + +extern "C" { +#include "dnd.h" +#include "unityCommon.h" +} + +class LIB_EXPORT DnDBase +{ + public: + virtual ~DnDBase(void) {}; + + /* sigc signals for local UI callbacks. */ + /* Local UI as DnD source. */ + sigc::signal dragExitChanged; + sigc::signal dndUngrabChanged; /* X11's notion of ungrab. */ + sigc::signal > fileCopyDoneChanged; + sigc::signal sourceDropChanged; + sigc::signal sourceCancelChanged; + sigc::signal dragStartChanged; + + /* Local UI as DnD target. */ + sigc::signal updateFeedbackChanged; + sigc::signal updateDetWndChanged; + sigc::signal updateUnityDetWndChanged; + + sigc::signal reset; + sigc::signal disable; + sigc::signal enable; + /* cui DnD protocol layer API exposed to UI (all platforms). */ + + /* Local UI as DnD source. */ + virtual void UpdateUnityDetWnd(bool bShow, uint32 unityWndId) {}; + virtual void DragLeave(int32 x, int32 y) = 0; + virtual void SourceCancel(void) = 0; + virtual void SourceDrop(DND_DROPEFFECT feedback) = 0; + + /* Local UI as DnD target. */ + virtual void DragEnter(const CPClipboard* clip) = 0; + /* Host only. */ + virtual void SetMouse(int32 x, int32 y, bool down = true) = 0; + /* Guest only. */ + virtual void SetFeedback(DND_DROPEFFECT feedback) = 0; + virtual void TargetCancel(void) = 0; + virtual void TargetDrop(const CPClipboard *clip, + int32 x, + int32 y) = 0; + + const CPClipboard* GetClipboard(void) { return &mClipboard; } + + virtual bool IsDnDAllowed(void) = 0; + + protected: + /* + * For details about state machine definition, please refer to + * https://wiki.eng.vmware.com/ToolsDnDV3FlowChart + */ + enum DND_STATE { + DNDSTATE_INVALID = 0, + DNDSTATE_READY, + /* H->G */ + DNDSTATE_ENTERING, + DNDSTATE_STARTING, + DNDSTATE_DRAGGING_INSIDE, + /* G->H */ + DNDSTATE_QUERY_EXITING, + DNDSTATE_UNGRABING, // ask mks to ungrab, v2 only + DNDSTATE_DRAGGING_OUTSIDE, + DNDSTATE_DRAGGING_PRIVATE, + }; + + DND_STATE mState; + DND_DROPEFFECT mFeedback; + CPClipboard mClipboard; +}; + +#endif // DND_BASE_H + diff --git a/open-vm-tools/lib/include/dndClipboard.h b/open-vm-tools/lib/include/dndClipboard.h new file mode 100644 index 000000000..d7098bb42 --- /dev/null +++ b/open-vm-tools/lib/include/dndClipboard.h @@ -0,0 +1,112 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + + +/* + * dndClipboard.h + * + * This file maintains an interface for the clipboard object. The object + * may contain several representations of the same object. For + * example, we could have a plain text filename as well as the file's + * contents on the clipboard at the same time. + * + * The purpose of this structure is to store cross platform clipboard + * data for further processing. The UI is responsible for converting local + * clipboard data to a crossplatform format and inserting it into the + * cross platform clipboard. + */ + +#ifndef _DND_CLIPBOARD_H_ +#define _DND_CLIPBOARD_H_ + +#include "vm_basic_types.h" + +#include "dnd.h" +#include "dynbuf.h" + +/* + * Make sure each clipboard item is at most 64kb - 100 b. This limitation is + * copied from the current text copy paste limit. + */ +#define CPCLIPITEM_MAX_SIZE_V1 ((1 << 16) - 100) +#define CPCLIPITEM_MAX_SIZE_V2 ((1 << 16) - 100) +#define CPCLIPITEM_MAX_SIZE_V3 (DNDMSG_MAX_ARGSZ - 100) + +/* Cross platform formats */ +typedef +#include "vmware_pack_begin.h" +struct CPFileList { + uint64 fileSize; + uint32 relPathsLen; + uint32 fulPathsLen; + uint8 filelists[1]; +} +#include "vmware_pack_end.h" +CPFileList; + +#define CPFILELIST_HEADER_SIZE (1* sizeof(uint64) + 2 * sizeof(uint32)) + +typedef +#include "vmware_pack_begin.h" +struct UriFileList { + uint64 fileSize; + uint32 uriPathsLen; + uint8 filelists[1]; +} +#include "vmware_pack_end.h" +UriFileList; + +#define URI_FILELIST_HEADER_SIZE (1* sizeof(uint64) + 1 * sizeof(uint32)) + +/* Types which can be stored on the clipboard. */ +/* + * XXX + * This is currently in dnd.h, but should be moved over to dndClipboard.h + * Ling's vmx code reorg should handle this. + */ +/* +typedef enum +{ + CPFORMAT_UNKNOWN = 0 + CPFORMAT_TEXT, + CPFORMAT_FILELIST, + CPFORMAT_MAX, +} DND_CPFORMAT; +*/ + +#define CPFORMAT_MIN CPFORMAT_TEXT + +/* CPClipboard */ +void CPClipboard_Init(CPClipboard *clip); +void CPClipboard_Destroy(CPClipboard *clip); + +void CPClipboard_Clear(CPClipboard *clip); +Bool CPClipboard_SetItem(CPClipboard *clip, const DND_CPFORMAT fmt, const void *buf, + const size_t size); +void CPClipboard_SetChanged(CPClipboard *clip, Bool changed); +Bool CPClipboard_Changed(const CPClipboard *clip); +Bool CPClipboard_ClearItem(CPClipboard *clip, DND_CPFORMAT fmt); +Bool CPClipboard_GetItem(const CPClipboard *clip, DND_CPFORMAT fmt, + void **buf, size_t *size); +Bool CPClipboard_ItemExists(const CPClipboard *clip, DND_CPFORMAT fmt); +Bool CPClipboard_IsEmpty(const CPClipboard *clip); +Bool CPClipboard_Copy(CPClipboard *dest, const CPClipboard *src); +Bool CPClipboard_Serialize(const CPClipboard *clip, DynBuf *buf); +Bool CPClipboard_Unserialize(CPClipboard *clip, void *buf, size_t len); + +#endif // _DND_CLIPBOARD_H_ diff --git a/open-vm-tools/lib/include/dndFileContentsUtil.h b/open-vm-tools/lib/include/dndFileContentsUtil.h new file mode 100644 index 000000000..9ec6b7fb0 --- /dev/null +++ b/open-vm-tools/lib/include/dndFileContentsUtil.h @@ -0,0 +1,68 @@ +/********************************************************* + * Copyright (C) 2008 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. + * + *********************************************************/ + + +/* + * dndFileContentsUtil.h + * + * Helper functions for format conversion for file contents. + */ + +#ifndef _DND_FILE_CONTENTS_UTIL_H_ +#define _DND_FILE_CONTENTS_UTIL_H_ + +#include "guestrpc/cpFileContents.h" + +#define CP_FILE_VALID_NONE 0 +#define CP_FILE_VALID_TYPE (1 << 0) +#define CP_FILE_VALID_SIZE (1 << 1) +#define CP_FILE_VALID_CREATE_TIME (1 << 2) +#define CP_FILE_VALID_ACCESS_TIME (1 << 3) +#define CP_FILE_VALID_WRITE_TIME (1 << 4) +#define CP_FILE_VALID_CHANGE_TIME (1 << 5) +#define CP_FILE_VALID_PERMS (1 << 6) +#define CP_FILE_VALID_ATTR (1 << 7) +#define CP_FILE_VALID_NAME (1 << 8) +#define CP_FILE_VALID_CONTENT (1 << 9) + +#define CP_FILE_ATTR_NONE 0 +#define CP_FILE_ATTR_HIDDEN (1 << 0) +#define CP_FILE_ATTR_SYSTEM (1 << 1) +#define CP_FILE_ATTR_ARCHIVE (1 << 2) +#define CP_FILE_ATTR_HIDDEN_FORCED (1 << 3) +#define CP_FILE_ATTR_READONLY (1 << 4) + +#define CP_FILE_TYPE_REGULAR 1 +#define CP_FILE_TYPE_DIRECTORY 2 +#define CP_FILE_TYPE_SYMLINK 3 + +#ifdef _WIN32 + +#include + +FILEGROUPDESCRIPTORA *DnD_GroupDescWToGroupDescA(FILEGROUPDESCRIPTORW *groupDescW); +Bool DnD_FileDescAToCPFileContents(FILEDESCRIPTORA *fileDescA, + CPFileItem* cpItem); +Bool DnD_CPFileContentsToFileDescA(CPFileItem* cpItem, + FILEDESCRIPTORA *fileDescA, + Bool processName); +Bool DnD_CPFileContentsToFileDescW(CPFileItem* cpItem, + FILEDESCRIPTORW *fileDescW); +#endif // _WIN32 + +#endif // _DND_FILE_CONTENTS_UTIL_H_ diff --git a/open-vm-tools/lib/include/dndFileList.hh b/open-vm-tools/lib/include/dndFileList.hh new file mode 100644 index 000000000..e43035026 --- /dev/null +++ b/open-vm-tools/lib/include/dndFileList.hh @@ -0,0 +1,76 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + +/* + * dndFileList.hh + * + * Translates filelists to different formats. + */ + +#ifndef DND_FILELIST_HH +#define DND_FILELIST_HH + +#include +#include + +extern "C" { +#include "vm_basic_types.h" +#include "dynbuf.h" +} + +class DnDFileList { + public: + DnDFileList(); + ~DnDFileList() {}; + + void SetFileSize(uint64 fsize); + uint64 GetFileSize() const; + void AddFile(const std::string fullPath, + const std::string relPath); + void AddFileUri(const std::string uriPath); + void AddFiles(const std::vector fullPathList, + const std::vector relPathList); + + /* Copy paste/dndV2 V2 rpc */ + void SetRelPathsStr(const std::string inpath); + + /* DnDFileTransfer & V2 RPC */ + std::string GetRelPathsStr() const; + std::string GetFullPathsStr(bool local) const; + std::string GetUriPathsStr() const; + + /* UI Local clipboard */ + std::vector GetRelPaths() const; + + /* CPClipboard */ + bool ToCPClipboard(DynBuf *out, bool local) const; + bool ToUriClipboard(DynBuf *out) const; + bool FromCPClipboard(const void *buf, size_t len); + + void Clear(); + + private: + + std::vector mRelPaths; + std::vector mFullPaths; + std::vector mUriPaths; + std::string mFullPathsBinary; + uint64 mFileSize; +}; + +#endif // DND_FILELIST_HH diff --git a/open-vm-tools/lib/dnd/dndInt.h b/open-vm-tools/lib/include/dndInt.h similarity index 100% rename from open-vm-tools/lib/dnd/dndInt.h rename to open-vm-tools/lib/include/dndInt.h diff --git a/open-vm-tools/lib/include/dndMsg.h b/open-vm-tools/lib/include/dndMsg.h new file mode 100644 index 000000000..5b72cde6f --- /dev/null +++ b/open-vm-tools/lib/include/dndMsg.h @@ -0,0 +1,148 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + +/* + * dndMsg.h -- + * + * DnDMsg represents an rpc message which is sent across the + * wire. Any args that it holds will be written out exactly as stored. + */ + +#ifndef _DNDMSG_H_ +#define _DNDMSG_H_ + +#define INCLUDE_ALLOW_USERLEVEL + +#include "includeCheck.h" +#include "vm_basic_types.h" + +#include "dynbuf.h" +#include "dynarray.h" +#include "dnd.h" + +/* Various return types serialization/unserialization functions can return. */ + +typedef enum { + DNDMSG_SUCCESS = 0, + DNDMSG_ERR, + DNDMSG_NOMEM, + DNDMSG_INPUT_TOO_SMALL, /* Input buffer needs to be bigger. */ + DNDMSG_INPUT_ERR, /* Serialize/unserialized failed sanity checks. */ +} DnDMsgErr; + +/* + * DnD Commands. + */ + +typedef enum { + DND_INVALID = 0, + + /* + * We need to send mouse packets for old protocols because old guest tools + * manipulate the mouse directly, but in DnD Version 3+, the host controls the + * mouse pointer via foundry. + */ + /* DnD Ver 1/2 Commands */ + DND_HG_SEND_MOUSE_PACKET, + + /* DnD version 3 commands */ + // GHDnD (h->g) + DND_GH_QUERY_PENDING_DRAG, + DND_GH_CANCEL, + DND_GH_COPY_DONE, + // GHDnD (g->h) + DND_GH_DRAG_ENTER, + DND_GH_NOT_PENDING, + + // HGDnD (h->g) + DND_HG_DRAG_ENTER, + DND_HG_DRAG_START, + DND_HG_CANCEL, + DND_HG_DROP, + DND_HG_FILE_COPY_DONE, + // HGDnD (g->h) + DND_HG_DRAG_ENTER_DONE, + DND_HG_DRAG_READY, + DND_HG_UPDATE_FEEDBACK, + DND_HG_DROP_DONE, + DND_HG_START_FILE_COPY, + + // Add future commands here. + DND_GH_UPDATE_UNITY_DET_WND, + DND_MAX, +} DnDCommand; + +/* + * Copy/Paste commands. + */ + +typedef enum { + CP_INVALID = 0, + + /* DnD version 3 commands. */ + // GHCopyPaste (h->g) + CP_GH_GET_CLIPBOARD, + // GHCopyPaste (g->h) + CP_GH_GET_CLIPBOARD_DONE, // FORMAT DATA(property list?) + + // HGCopyPaste (h->g) + CP_HG_SET_CLIPBOARD, + CP_HG_FILE_COPY_DONE, + // HGCopyPaste(g->h) + CP_HG_START_FILE_COPY, + + // Add future commands here. + CP_MAX, +} CopyPasteCommand; + +/* + * Opaque data structure. + * Members are listed in order of unserialization. + */ + +typedef struct DnDMsg { + /* Header */ + uint8 ver; // Must be first across all versions. + uint32 cmd; + uint32 nargs; + /* The expected size of the buffer needed to unserialize the arguments. */ + uint32 expectedArgsSz; + + /* Body */ + DynBufArray args; +} DnDMsg; + +#define DnDMsg_Success(x) ((x) == DNDMSG_SUCCESS) + +void DnDMsg_Init(DnDMsg *msg); +void DnDMsg_Destroy(DnDMsg *msg); + +/* Header information. */ +uint32 DnDMsg_GetCmd(DnDMsg *msg); +void DnDMsg_SetCmd(DnDMsg *msg, uint32 cmd); +uint32 DnDMsg_NumArgs(DnDMsg *msg); + +DynBuf *DnDMsg_GetArg(DnDMsg *msg, uint32 arg); + +Bool DnDMsg_AppendArg(DnDMsg *msg, void *buf, size_t len); +Bool DnDMsg_Serialize(DnDMsg *msg, DynBuf *buf); + +DnDMsgErr DnDMsg_UnserializeHeader(DnDMsg *msg, void *buf, size_t len); +DnDMsgErr DnDMsg_UnserializeArgs(DnDMsg *msg, void *buf, size_t len); + +#endif /* _DNDMSG_H_ */ diff --git a/open-vm-tools/lib/include/dndRpc.hh b/open-vm-tools/lib/include/dndRpc.hh new file mode 100644 index 000000000..76f2cc7e0 --- /dev/null +++ b/open-vm-tools/lib/include/dndRpc.hh @@ -0,0 +1,62 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + +/* + * dndRpc.hh -- + * + * Rpc layer object for DnD. + */ + +#ifndef DND_RPC_HH +#define DND_RPC_HH + +#include +#include +#include "vm_basic_types.h" + +extern "C" { + #include "dndClipboard.h" +} + +class DnDRpc +{ + public: + virtual ~DnDRpc(void) {}; + + /* sigc signals for callback. */ + sigc::signal ghUpdateUnityDetWndChanged; + sigc::signal ghQueryPendingDragChanged; + sigc::signal ghCancelChanged; + sigc::signal hgDragEnterChanged; + sigc::signal hgDragStartChanged; + sigc::signal hgCancelChanged; + sigc::signal hgDropChanged; + sigc::signal > hgFileCopyDoneChanged; + + /* GH DnD. */ + virtual bool GHDragEnter(const CPClipboard *clip) = 0; + virtual bool GHUngrabTimeout(void) = 0; + + /* HG DnD. */ + virtual bool HGDragEnterDone(int32 x, int32 y) = 0; + virtual bool HGDragStartDone(void) = 0; + virtual bool HGUpdateFeedback(DND_DROPEFFECT feedback) = 0; + virtual bool HGDropDone(const char *stagingDirCP, size_t sz) = 0; +}; + +#endif // DND_RPC_HH diff --git a/open-vm-tools/lib/include/dndRpcV3.hh b/open-vm-tools/lib/include/dndRpcV3.hh new file mode 100644 index 000000000..34e12fce9 --- /dev/null +++ b/open-vm-tools/lib/include/dndRpcV3.hh @@ -0,0 +1,64 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + +/* + * dndRpcV3.hh -- + * + * Rpc layer object for DnD version 3. + */ + +#ifndef DND_RPC_V3_HH +#define DND_RPC_V3_HH + +#include +#include "dndRpc.hh" +#include "dndTransport.hh" + +extern "C" { + #include "dnd.h" + #include "dndMsg.h" +} + +class DnDRpcV3 + : public DnDRpc, + public sigc::trackable +{ + public: + DnDRpcV3(struct RpcIn *rpcIn); + virtual ~DnDRpcV3(void); + + /* DnD functions. */ + /* GH DnD. */ + virtual bool GHDragEnter(const CPClipboard *clip); + virtual bool GHUngrabTimeout(void); + + /* HG DnD. */ + virtual bool HGDragEnterDone(int32 x, int32 y); + virtual bool HGDragStartDone(void); + virtual bool HGUpdateFeedback(DND_DROPEFFECT feedback); + virtual bool HGDropDone(const char *stagingDirCP, size_t sz); + + private: + void OnRecvMsg(const uint8 *data, size_t dataSize); + bool SendSingleCmd(DnDCommand cmd); + bool SendCmdWithClip(DnDCommand cmd, const CPClipboard *clip); + + DnDTransport *mTransport; +}; + +#endif // DND_RPC_V3_HH diff --git a/open-vm-tools/lib/include/dndTransport.hh b/open-vm-tools/lib/include/dndTransport.hh new file mode 100644 index 000000000..5061fa35f --- /dev/null +++ b/open-vm-tools/lib/include/dndTransport.hh @@ -0,0 +1,45 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + +/* + * dndTransport.hh -- + * + * DnDTransport provides a data transportation interface for both dnd and copyPaste. + */ + +#ifndef DND_TRANSPORT_HH +#define DND_TRANSPORT_HH + +#include +#include + +#include "vm_basic_types.h" + +class DnDTransport +{ +public: + virtual ~DnDTransport() {}; + + /* sigc signals for RecvMsg. */ + sigc::signal recvMsgChanged; + + virtual bool SendMsg(uint8 *msg, + size_t length) = 0; +}; + +#endif // DND_TRANSPORT_HH diff --git a/open-vm-tools/lib/include/dndTransportGuestRpc.hh b/open-vm-tools/lib/include/dndTransportGuestRpc.hh new file mode 100644 index 000000000..cb26f55ad --- /dev/null +++ b/open-vm-tools/lib/include/dndTransportGuestRpc.hh @@ -0,0 +1,62 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + +/* + * dndTransportGuestRpc.hh -- + * + * GuestRpc implementation of the dndTransport interface. + */ + + +#ifndef DND_TRANSPORT_GUEST_RPC_HH +#define DND_TRANSPORT_GUEST_RPC_HH + +#include +#include "dndTransport.hh" + +extern "C" { + #include "dnd.h" +} + +class DnDTransportGuestRpc + : public DnDTransport, + public sigc::trackable +{ +public: + DnDTransportGuestRpc(struct RpcIn *rpcIn, + const char *rpcCmd); + virtual ~DnDTransportGuestRpc(void); + + virtual bool SendMsg(uint8 *msg, + size_t length); + void RecvMsg(DnDTransportPacketHeader *packet, + size_t packetSize); + +private: + char *mRpcCmd; + struct RpcIn *mRpcIn; + + DnDTransportBuffer mSendBuf; + DnDTransportBuffer mRecvBuf; + uint32 mSeqNum; + + bool SendPacket(uint8 *packet, + size_t packetSize); +}; + +#endif // DND_TRANSPORT_GUEST_RPC_HH diff --git a/open-vm-tools/lib/include/fileIO.h b/open-vm-tools/lib/include/fileIO.h index eb94bde52..4998d886c 100644 --- a/open-vm-tools/lib/include/fileIO.h +++ b/open-vm-tools/lib/include/fileIO.h @@ -329,7 +329,7 @@ Bool FileIO_SetAllocSize(const FileIODescriptor *fd, uint64 size); int64 FileIO_GetSizeByPath(ConstUnicode pathName); -int FileIO_Close(FileIODescriptor *file); +Bool FileIO_Close(FileIODescriptor *file); uint32 FileIO_GetFlags(FileIODescriptor *file); diff --git a/open-vm-tools/lib/include/guestApp.h b/open-vm-tools/lib/include/guestApp.h index 141d8c10d..423a63dc0 100644 --- a/open-vm-tools/lib/include/guestApp.h +++ b/open-vm-tools/lib/include/guestApp.h @@ -116,9 +116,6 @@ GuestApp_GetConfPath(void); char * GuestApp_GetLogPath(void); -char * -GuestApp_GetCmdOutput(const char *cmd); // IN - Bool GuestApp_IsHgfsCapable(void); diff --git a/open-vm-tools/lib/include/guest_os.h b/open-vm-tools/lib/include/guest_os.h index 6496b1977..e87ad9910 100644 --- a/open-vm-tools/lib/include/guest_os.h +++ b/open-vm-tools/lib/include/guest_os.h @@ -72,13 +72,15 @@ typedef enum GuestOSType { GUEST_OS_SOLARIS10 = GUEST_OS_BASE + 35, GUEST_OS_SOLARIS10_64 = GUEST_OS_BASE + 36, GUEST_OS_VMKERNEL = GUEST_OS_BASE + 37, - GUEST_OS_DARWIN = GUEST_OS_BASE + 38, - GUEST_OS_DARWIN_64 = GUEST_OS_BASE + 39, - GUEST_OS_OPENSERVER5 = GUEST_OS_BASE + 40, - GUEST_OS_OPENSERVER6 = GUEST_OS_BASE + 41, - GUEST_OS_UNIXWARE7 = GUEST_OS_BASE + 42, - GUEST_OS_DEBIAN45 = GUEST_OS_BASE + 43, - GUEST_OS_DEBIAN45_64 = GUEST_OS_BASE + 44, + GUEST_OS_DARWIN9 = GUEST_OS_BASE + 38, // Mac OS 10.5 + GUEST_OS_DARWIN9_64 = GUEST_OS_BASE + 39, + GUEST_OS_DARWIN10 = GUEST_OS_BASE + 40, // Mac OS 10.6 + GUEST_OS_DARWIN10_64 = GUEST_OS_BASE + 41, + GUEST_OS_OPENSERVER5 = GUEST_OS_BASE + 42, + GUEST_OS_OPENSERVER6 = GUEST_OS_BASE + 43, + GUEST_OS_UNIXWARE7 = GUEST_OS_BASE + 44, + GUEST_OS_DEBIAN45 = GUEST_OS_BASE + 45, + GUEST_OS_DEBIAN45_64 = GUEST_OS_BASE + 46, } GuestOSType; @@ -131,10 +133,12 @@ typedef enum GuestOSFamilyType { BS(OTHERLINUX) | ALL26XLINUX32) #define ALLLINUX64 (BS(OTHERLINUX_64) | BS(OTHER24XLINUX_64) | ALL26XLINUX64) #define ALLLINUX (ALLLINUX32 | ALLLINUX64) -#define ALLDARWIN (BS(DARWIN) | BS(DARWIN_64)) +#define ALLDARWIN32 (BS(DARWIN9) | BS(DARWIN10)) +#define ALLDARWIN64 (BS(DARWIN9_64) | BS(DARWIN10_64)) +#define ALLDARWIN (ALLDARWIN32 | ALLDARWIN64) #define ALL64 (ALLWIN64 | ALLLINUX64 | \ BS(SOLARIS10_64) | BS(FREEBSD_64) | \ - BS(DARWIN_64) | BS(OTHER_64)) + ALLDARWIN64 | BS(OTHER_64)) /* @@ -267,6 +271,14 @@ typedef enum GuestOSFamilyType { #define STR_OS_WIN_2008_ENTERPRISE_CORE_FULL "Windows Server 2008 Enterprise Edition (core installation)" #define STR_OS_WIN_2008_ENTERPRISE_ITANIUM "winServer2008EnterpriseItanium-32" #define STR_OS_WIN_2008_ENTERPRISE_ITANIUM_FULL "Windows Server 2008 Enterprise Edition for Itanium-based Systems" +#define STR_OS_WIN_2008_MEDIUM_MANAGEMENT "winServer2008MediumManagement-32" +#define STR_OS_WIN_2008_MEDIUM_MANAGEMENT_FULL "Windows Essential Business Server Management Server" +#define STR_OS_WIN_2008_MEDIUM_MESSAGING "winServer2008MediumMessaging-32" +#define STR_OS_WIN_2008_MEDIUM_MESSAGING_FULL "Windows Essential Business Server Messaging Server" +#define STR_OS_WIN_2008_MEDIUM_SECURITY "winServer2008MediumSecurity-32" +#define STR_OS_WIN_2008_MEDIUM_SECURITY_FULL "Windows Essential Business Server Security Server" +#define STR_OS_WIN_2008_SERVER_FOR_SMALLBUSINESS "winServer2008ForSmallBusiness-32" +#define STR_OS_WIN_2008_SERVER_FOR_SMALLBUSINESS_FULL "Windows Server 2008 for Windows Essential Server Solutions" #define STR_OS_WIN_2008_SMALL_BUSINESS "winServer2008SmallBusiness-32" #define STR_OS_WIN_2008_SMALL_BUSINESS_FULL "Windows Server 2008 Small Business Server" #define STR_OS_WIN_2008_SMALL_BUSINESS_PREMIUM "winServer2008SmallBusinessPremium-32" @@ -275,8 +287,18 @@ typedef enum GuestOSFamilyType { #define STR_OS_WIN_2008_STANDARD_FULL "Windows Server 2008 Standard Edition" #define STR_OS_WIN_2008_STANDARD_CORE "winServer2008StandardCore-32" #define STR_OS_WIN_2008_STANDARD_CORE_FULL "Windows Server 2008 Standard Edition (core installation)" +#define STR_OS_WIN_2008_STORAGE_ENTERPRISE "winServer2008StorageEnterprise-32" +#define STR_OS_WIN_2008_STORAGE_ENTERPRISE_FULL "Windows Server 2008 Storage Server Enterprise" +#define STR_OS_WIN_2008_STORAGE_EXPRESS "winServer2008StorageExpress-32" +#define STR_OS_WIN_2008_STORAGE_EXPRESS_FULL "Windows Server 2008 Storage Server Express" +#define STR_OS_WIN_2008_STORAGE_STANDARD "winServer2008StorageStandard-32" +#define STR_OS_WIN_2008_STORAGE_STANDARD_FULL "Windows Server 2008 Storage Server Standard" +#define STR_OS_WIN_2008_STORAGE_WORKGROUP "winServer2008StorageWorkgroup-32" +#define STR_OS_WIN_2008_STORAGE_WORKGROUP_FULL "Windows Server 2008 Storage Server Workgroup" #define STR_OS_WIN_2008_WEB_SERVER "winServer2008Web-32" #define STR_OS_WIN_2008_WEB_SERVER_FULL "Windows Server 2008 Web Server Edition" + +/* Windows 64-bit */ #define STR_OS_WIN_VISTA_ULTIMATE_X64 "winVistaUltimate-64" #define STR_OS_WIN_VISTA_HOME_PREMIUM_X64 "winVistaHomePremium-64" #define STR_OS_WIN_VISTA_HOME_BASIC_X64 "winVistaHomeBasic-64" @@ -289,12 +311,24 @@ typedef enum GuestOSFamilyType { #define STR_OS_WIN_2008_ENTERPRISE_X64 "winServer2008Enterprise-64" #define STR_OS_WIN_2008_ENTERPRISE_CORE_X64 "winServer2008EnterpriseCore-64" #define STR_OS_WIN_2008_ENTERPRISE_ITANIUM_X64 "winServer2008EnterpriseItanium-64" +#define STR_OS_WIN_2008_MEDIUM_MANAGEMENT_X64 "winServer2008MediumManagement-64" +#define STR_OS_WIN_2008_MEDIUM_MESSAGING_X64 "winServer2008MediumMessaging-64" +#define STR_OS_WIN_2008_MEDIUM_SECURITY_X64 "winServer2008MediumSecurity-64" +#define STR_OS_WIN_2008_SERVER_FOR_SMALLBUSINESS_X64 "winServer2008ForSmallBusiness-64" #define STR_OS_WIN_2008_SMALL_BUSINESS_X64 "winServer2008SmallBusiness-64" #define STR_OS_WIN_2008_SMALL_BUSINESS_PREMIUM_X64 "winServer2008SmallBusinessPremium-64" #define STR_OS_WIN_2008_STANDARD_X64 "winServer2008Standard-64" #define STR_OS_WIN_2008_STANDARD_CORE_X64 "winServer2008StandardCore-64" +#define STR_OS_WIN_2008_STORAGE_ENTERPRISE_X64 "winServer2008StorageEnterprise-64" +#define STR_OS_WIN_2008_STORAGE_EXPRESS_X64 "winServer2008StorageExpress-64" +#define STR_OS_WIN_2008_STORAGE_STANDARD_X64 "winServer2008StorageStandard-64" +#define STR_OS_WIN_2008_STORAGE_WORKGROUP_X64 "winServer2008StorageWorkgroup-64" #define STR_OS_WIN_2008_WEB_SERVER_X64 "winServer2008Web-64" +/* XXX */ +#define STR_OS_HYPERV "winHyperV" +#define STR_OS_HYPERV_FULL "Hyper-V Server" + /* Modifiers for Windows Vista and Windows Server 2008 */ #define STR_OS_WIN_32_BIT_EXTENSION ", 32-bit" #define STR_OS_WIN_64_BIT_EXTENSION ", 64-bit" diff --git a/open-vm-tools/lib/include/hgfsProto.h b/open-vm-tools/lib/include/hgfsProto.h index 4c0537007..5dccac0de 100644 --- a/open-vm-tools/lib/include/hgfsProto.h +++ b/open-vm-tools/lib/include/hgfsProto.h @@ -178,6 +178,55 @@ typedef uint8 HgfsPermissions; #define HGFS_PERM_WRITE 2 #define HGFS_PERM_EXEC 1 +/* + * Access mode bits. + * + * Different operating systems have different set of file access mode. + * Here are constants that are rich enough to describe all access modes in an OS + * independent way. + */ + +typedef uint32 HgfsAccessMode; +/* + * Generic access rights control coarse grain access for the file. + * A particular generic rigth can be expanded into different set of specific rights + * on different OS. + */ + +/* + * HGFS_MODE_GENERIC_READ means ability to read file data and read various file + * attributes and properties. + */ +#define HGFS_MODE_GENERIC_READ (1 << 0) +/* + * HGFS_MODE_GENERIC_WRITE means ability to write file data and updaate various file + * attributes and properties. + */ +#define HGFS_MODE_GENERIC_WRITE (1 << 1) +/* + * HGFS_MODE_GENERIC_EXECUE means ability to execute file. For network redirectors + * ability to execute usualy implies ability to read data; for local file systems + * HGFS_MODE_GENERIC_EXECUTE does not imply ability to read data. + */ +#define HGFS_MODE_GENERIC_EXECUTE (1 << 2) + +/* Specific rights define fine grain access modes. */ +#define HGFS_MODE_READ_DATA (1 << 3) // Ability to read file data +#define HGFS_MODE_WRITE_DATA (1 << 4) // Ability to writge file data +#define HGFS_MODE_APPEND_DATA (1 << 5) // Appending data to the end of file +#define HGFS_MODE_DELETE (1 << 6) // Ability to delete the file +#define HGFS_MODE_TRAVERSE_DIRECTORY (1 << 7) // Ability to access files in a directory +#define HGFS_MODE_LIST_DIRECTORY (1 << 8) // Ability to list file names +#define HGFS_MODE_ADD_SUBDIRECTORY (1 << 9) // Ability to create a new subdirectory +#define HGFS_MODE_ADD_FILE (1 << 10) // Ability to create a new file +#define HGFS_MODE_DELETE_CHILD (1 << 11) // Ability to delete file/subdirectory +#define HGFS_MODE_READ_ATTRIBUTES (1 << 12) // Ability to read attributes +#define HGFS_MODE_WRITE_ATTRIBUTES (1 << 13) // Ability to write attributes +#define HGFS_MODE_READ_EXTATTRIBUTES (1 << 14) // Ability to read extended attributes +#define HGFS_MODE_WRITE_EXTATTRIBUTES (1 << 15) // Ability to write extended attributes +#define HGFS_MODE_READ_SECURITY (1 << 16) // Ability to read permissions/ACLs/owner +#define HGFS_MODE_WRITE_SECURITY (1 << 17) // Ability to change permissions/ACLs +#define HGFS_MODE_TAKE_OWNERSHIP (1 << 18) // Ability to change file owner/group /* * Server-side locking (oplocks and leases). @@ -356,6 +405,15 @@ typedef uint64 HgfsAttrValid; * only to determine if the ID is valid. */ #define HGFS_ATTR_VALID_NON_STATIC_FILEID (1 << 16) +/* + * File permissions that are in effect for the user which runs HGFS server. + * Client needs to know effective permissions in order to implement access(2). + * Client can't derive it from group/owner/other permissions because of two resaons: + * 1. It does not know user/group id of the user which runs HGFS server + * 2. Effective permissions account for additional restrictions that may be imposed + * by host file system, for example by ACL. + */ +#define HGFS_ATTR_VALID_EFFECTIVE_PERMS (1 << 17) /* @@ -400,7 +458,7 @@ struct HgfsAttrV2 { uint32 groupId; /* group identifier, ignored by Windows */ uint64 hostFileId; /* File Id of the file on host: inode_t on Linux */ uint32 volumeId; /* volume identifier, non-zero is valid. */ - uint32 reserved1; /* Reserved for future use */ + uint32 effectivePerms; /* Permissions in effect for the user on the host. */ uint64 reserved2; /* Reserved for future use */ } #include "vmware_pack_end.h" diff --git a/open-vm-tools/lib/include/hostinfo.h b/open-vm-tools/lib/include/hostinfo.h index 11bc7a5ac..f641683f4 100644 --- a/open-vm-tools/lib/include/hostinfo.h +++ b/open-vm-tools/lib/include/hostinfo.h @@ -62,6 +62,12 @@ extern VmTimeType Hostinfo_SystemTimerUS(void); extern int Hostinfo_OSVersion(int i); extern int Hostinfo_GetSystemBitness(void); extern const char *Hostinfo_OSVersionString(void); + +extern Bool Hostinfo_GetOSName(uint32 outBufFullLen, + uint32 outBufLen, + char *osNameFull, + char *osName); + extern Bool Hostinfo_OSIsSMP(void); #if defined(_WIN32) extern Bool Hostinfo_OSIsWinNT(void); @@ -117,6 +123,44 @@ extern Bool Hostinfo_HTDisabled(void); #endif #if defined(_WIN32) +typedef enum { + OS_WIN95 = 1, + OS_WIN98 = 2, + OS_WINME = 3, + OS_WINNT = 4, + OS_WIN2K = 5, + OS_WINXP = 6, + OS_WIN2K3 = 7, + OS_VISTA = 8, + OS_UNKNOWN = 99999 // last, highest value +} OS_TYPE; + +typedef enum { + OS_DETAIL_WIN95 = 1, + OS_DETAIL_WIN98 = 2, + OS_DETAIL_WINME = 3, + OS_DETAIL_WINNT = 4, + OS_DETAIL_WIN2K = 5, + OS_DETAIL_WIN2K_PRO = 6, + OS_DETAIL_WIN2K_SERV = 7, + OS_DETAIL_WIN2K_ADV_SERV = 8, + OS_DETAIL_WINXP = 9, + OS_DETAIL_WINXP_HOME = 10, + OS_DETAIL_WINXP_PRO = 11, + OS_DETAIL_WINXP_X64_PRO = 12, + OS_DETAIL_WIN2K3 = 13, + OS_DETAIL_WIN2K3_WEB = 14, + OS_DETAIL_WIN2K3_ST = 15, + OS_DETAIL_WIN2K3_EN = 16, + OS_DETAIL_WIN2K3_BUS = 17, + OS_DETAIL_VISTA = 18, + OS_DETAIL_UNKNOWN = 99999 // last, highest value +} OS_DETAIL_TYPE; + +/* generic names (to protect the future) but Windows specific for now */ +OS_TYPE Hostinfo_GetOSType(void); +OS_DETAIL_TYPE Hostinfo_GetOSDetailType(void); + Bool Hostinfo_GetPCFrequency(uint64 *pcHz); Bool Hostinfo_GetMhzOfProcessor(int32 processorNumber, uint32 *currentMhz, uint32 *maxMhz); diff --git a/open-vm-tools/lib/include/libExport.hh b/open-vm-tools/lib/include/libExport.hh new file mode 100644 index 000000000..d7bb01ef7 --- /dev/null +++ b/open-vm-tools/lib/include/libExport.hh @@ -0,0 +1,53 @@ +/********************************************************* + * Copyright (C) 2004 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. + * + *********************************************************/ + +/* + * libExport.hh -- + * + * Declares proper decorations for dll-export interface + * for all modules in libs. + */ + +#ifndef LIB_EXPORT_HH +#define LIB_EXPORT_HH + +#ifdef _WIN32 + #ifdef LIB_EXPORT_SOURCE + #define LIB_EXPORT __declspec(dllexport) + #else + #define LIB_EXPORT __declspec(dllimport) + #endif + + #ifdef LIB_EXPORT_WUI_SOURCE + #define LIB_EXPORT_WUI __declspec(dllexport) + #else + #define LIB_EXPORT_WUI __declspec(dllimport) + #endif + + #ifdef VMSTRING_EXPORT_SOURCE + #define VMSTRING_EXPORT __declspec(dllexport) + #else + #define VMSTRING_EXPORT __declspec(dllimport) + #endif +#else + #define LIB_EXPORT + #define LIB_EXPORT_WUI + #define VMSTRING_EXPORT +#endif // WIN32 + +#endif // LIB_EXPORT_HH diff --git a/open-vm-tools/lib/include/log.h b/open-vm-tools/lib/include/log.h index 43efb4f74..e8e81b1db 100644 --- a/open-vm-tools/lib/include/log.h +++ b/open-vm-tools/lib/include/log.h @@ -29,8 +29,6 @@ typedef void (LogBasicFunc)(const char *fmt, va_list args); -struct LogState; - typedef struct { const char *fileName; // File name, if known diff --git a/open-vm-tools/lib/include/resolution.h b/open-vm-tools/lib/include/resolution.h index 7d391b349..809c53968 100644 --- a/open-vm-tools/lib/include/resolution.h +++ b/open-vm-tools/lib/include/resolution.h @@ -67,7 +67,7 @@ # undef Bool typedef Display * InitHandle; -#elif defined(__APPLE__) +#elif defined(__APPLE__) || defined(RESOLUTION_WIN32) typedef void * InitHandle; #else # error Unknown display backend diff --git a/open-vm-tools/lib/include/rpcChannel.h b/open-vm-tools/lib/include/rpcChannel.h new file mode 100644 index 000000000..7ff672fc6 --- /dev/null +++ b/open-vm-tools/lib/include/rpcChannel.h @@ -0,0 +1,175 @@ +/********************************************************* + * Copyright (C) 2008 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 _RPCCHANNEL_H_ +#define _RPCCHANNEL_H_ + +/** + * @file rpcChannel.h + * + * Defines the interface between applications and the underlying GuestRPC + * channel. The goal is to have an abstraction so applications can run over + * the backdoor, VMCI sockets or TCP/IP sockets by just picking up the + * desired channel at runtime, without the need to modify the code. + * + * For this reason, the behavior of all channels is modeled after the RpcIn + * channel currently used in Tools, so the socket-based channels won't + * provide much better functionality than what the backdoor provides (aside + * from being interrupt-based rather than poll-based). + * + * @addtogroup vmtools_guestrpc + * @{ + */ + +#if !defined(VMTOOLS_USE_GLIB) +# error "This library needs to be compiled with VMTOOLS_USE_GLIB." +#endif + +#include +#include "vm_basic_types.h" +#include "rpcin.h" + +struct RpcChannel; + +/** Defines the registration data for a GuestRPC application. */ +typedef struct RpcChannelCallback { + /** String identifying the RPC message. */ + const char *name; + /** Function to call when data arrives. */ + RpcIn_Callback callback; + /** Data to provide to callback function. */ + gpointer clientData; + /** If not NULL, the input data will be deserialized using this function. */ + gpointer xdrIn; + /** + * If not NULL, the output data will be serialized using this function. The + * output data should be stored in the @a result field of the RpcInData + * structure, and should have been allocated with glib's @a g_malloc() if + * @a freeResult is TRUE. + */ + gpointer xdrOut; + /** + * If xdrIn is not NULL, this should be the amount of memory to allocate + * for deserializing the input data. + */ + size_t xdrInSize; +} RpcChannelCallback; + + +typedef Bool (*RpcChannelStartFn)(struct RpcChannel *); +typedef Bool (*RpcChannelStopFn)(struct RpcChannel *); +typedef Bool (*RpcChannelShutdownFn)(struct RpcChannel *); +typedef Bool (*RpcChannelSendFn)(struct RpcChannel *, + char *data, + size_t dataLen, + char **result, + size_t *resultLen); + + +/** + * Signature for the callback function called after a channel reset. + * + * @param[in] chan The RPC channel. + * @param[in] success Whether reset was successful. + * @param[in] data Client data. + */ +typedef void (*RpcChannelResetCb)(struct RpcChannel *chan, + gboolean success, + gpointer data); + + +/** Defines the interface between the application and the RPC channel. */ +typedef struct RpcChannel { + RpcChannelStartFn start; + RpcChannelStopFn stop; + RpcChannelSendFn send; + /* Private section: don't use the fields below directly. */ + RpcChannelShutdownFn shutdown; + gchar *appName; + GHashTable *rpcs; + GMainContext *mainCtx; + GSource *resetCheck; + gpointer appCtx; + RpcChannelCallback resetReg; + RpcChannelResetCb resetCb; + gpointer resetData; + gboolean rpcError; + guint rpcErrorCount; + gpointer _private; +} RpcChannel; + + +/** + * Wrapper for the send function of an RPC channel struct. + * + * @param[in] chan The RPC channel instance. + * @param[in] data Data to send. + * @param[in] dataLen Number of bytes to send. + * @param[out] result Response from other side. + * @param[out] resultLen Number of bytes in response. + * + * @return The status from the remote end (TRUE if call was successful). + */ + +static INLINE Bool +RpcChannel_Send(RpcChannel *chan, + char *data, + size_t dataLen, + char **result, + size_t *resultLen) +{ + return chan->send(chan, data, dataLen, result, resultLen); +} + +Bool +RpcChannel_BuildXdrCommand(const char *cmd, + void *xdrProc, + void *xdrData, + char **result, + size_t *resultLen); + +gboolean +RpcChannel_Destroy(RpcChannel *chan); + +Bool +RpcChannel_Dispatch(RpcInData *data); + +void +RpcChannel_Setup(RpcChannel *chan, + const gchar *appName, + GMainContext *mainCtx, + gpointer appCtx, + RpcChannelResetCb resetCb, + gpointer resetData); + +void +RpcChannel_RegisterCallback(RpcChannel *chan, + RpcChannelCallback *rpc); + +void +RpcChannel_UnregisterCallback(RpcChannel *chan, + RpcChannelCallback *rpc); + + +RpcChannel * +RpcChannel_NewBackdoorChannel(GMainContext *mainCtx); + +/** @} */ + +#endif + diff --git a/open-vm-tools/lib/include/system.h b/open-vm-tools/lib/include/system.h index 2c68daeb3..5e8ed6048 100644 --- a/open-vm-tools/lib/include/system.h +++ b/open-vm-tools/lib/include/system.h @@ -43,44 +43,10 @@ Bool System_IsACPI(void); void System_Shutdown(Bool reboot); Bool System_IsUserAdmin(void); -char *System_GetEnv(Bool global, char *valueName); -int System_SetEnv(Bool global, char *valueName, char *value); +char *System_GetEnv(Bool global, const char *valueName); +int System_SetEnv(Bool global, const char *valueName, const char *value); #ifdef _WIN32 -typedef enum { - OS_WIN95 = 1, - OS_WIN98 = 2, - OS_WINME = 3, - OS_WINNT = 4, - OS_WIN2K = 5, - OS_WINXP = 6, - OS_WIN2K3 = 7, - OS_VISTA = 8, - OS_UNKNOWN = 9 -} OS_TYPE; - -typedef enum { - OS_DETAIL_WIN95 = 1, - OS_DETAIL_WIN98 = 2, - OS_DETAIL_WINME = 3, - OS_DETAIL_WINNT = 4, - OS_DETAIL_WIN2K = 5, - OS_DETAIL_WIN2K_PRO = 6, - OS_DETAIL_WIN2K_SERV = 7, - OS_DETAIL_WIN2K_ADV_SERV = 8, - OS_DETAIL_WINXP = 9, - OS_DETAIL_WINXP_HOME = 10, - OS_DETAIL_WINXP_PRO = 11, - OS_DETAIL_WINXP_X64_PRO = 12, - OS_DETAIL_WIN2K3 = 13, - OS_DETAIL_WIN2K3_WEB = 14, - OS_DETAIL_WIN2K3_ST = 15, - OS_DETAIL_WIN2K3_EN = 16, - OS_DETAIL_WIN2K3_BUS = 17, - OS_DETAIL_VISTA = 18, - OS_DETAIL_UNKNOWN = 19 -} OS_DETAIL_TYPE; - typedef void (*DesktopSwitchNotifyCB)(void *); typedef struct { DesktopSwitchNotifyCB cb; // callback to invoke. @@ -96,8 +62,6 @@ typedef struct { #define VM_SERVICE_STATE_UNKNOWN 0xffffffff BOOL System_SetProcessPrivilege(LPCTSTR lpszPrivilege, Bool bEnablePrivilege); -OS_TYPE System_GetOSType(void); -OS_DETAIL_TYPE System_GetOSDetailType(void); int32 System_GetSPVersion(void); Bool System_IsLoginScreenActive(void); Bool System_IsScreenSaverActive(void); diff --git a/open-vm-tools/lib/include/timeutil.h b/open-vm-tools/lib/include/timeutil.h index f2bad872b..6ae892ff0 100644 --- a/open-vm-tools/lib/include/timeutil.h +++ b/open-vm-tools/lib/include/timeutil.h @@ -119,6 +119,6 @@ EXTERN Bool TimeUtil_UTCTimeToSystemTime(const __time64_t utcTime, // IN SYSTEMTIME *systemTime); // OUT #endif -EXTERN int TimeUtil_GetLocalWindowsTimeZoneIndex(void); +EXTERN int TimeUtil_GetLocalWindowsTimeZoneIndexAndName(char **ptzName); #endif // _TIMEUTIL_H_ diff --git a/open-vm-tools/lib/include/vix.h b/open-vm-tools/lib/include/vix.h index 2cb40240b..999c5d844 100644 --- a/open-vm-tools/lib/include/vix.h +++ b/open-vm-tools/lib/include/vix.h @@ -161,6 +161,7 @@ enum { VIX_E_INTERACTIVE_SESSION_USER_MISMATCH = 3035, VIX_E_UNABLE_TO_REPLAY_VM = 3039, VIX_E_CANNOT_POWER_ON_VM = 3041, + VIX_E_NO_DISPLAY_SERVER = 3043, /* VM Errors */ VIX_E_VM_NOT_FOUND = 4000, @@ -301,6 +302,7 @@ enum { VIX_E_TOOLS_INSTALL_SIG_CHECK_FAILED = 21008, VIX_E_TOOLS_INSTALL_ERROR = 21009, VIX_E_TOOLS_INSTALL_ALREADY_UP_TO_DATE = 21010, + VIX_E_TOOLS_INSTALL_IN_PROGRESS = 21011, /* Wrapper Errors */ VIX_E_WRAPPER_WORKSTATION_NOT_INSTALLED = 22001, diff --git a/open-vm-tools/lib/include/vixOpenSource.h b/open-vm-tools/lib/include/vixOpenSource.h index e3850fb68..012c56138 100644 --- a/open-vm-tools/lib/include/vixOpenSource.h +++ b/open-vm-tools/lib/include/vixOpenSource.h @@ -396,8 +396,10 @@ enum { #ifndef VIX_HIDE_FROM_JAVA extern int vixDebugGlobalSpewLevel; -extern char *VixAllocDebugString(char *fmt, ...); -extern void VixDebugInit(int level, Bool panicOnVixAssert); +extern int vixApiTraceGlobalSpewLevel; +extern char *VixAllocDebugString(char *fmt, ...) PRINTF_DECL(1,2); +extern void VixDebugInit(int debugLevel, int apiTraceLevel, + Bool panicOnVixAssert); extern const char *VixDebug_GetFileBaseName(const char *path); extern void VixAssert(const char *cond, const char *file, int lineNum); @@ -406,6 +408,7 @@ extern void VixAssert(const char *cond, const char *file, int lineNum); */ #define VIX_DEBUG_PREFERENCE_NAME "vix.debugLevel" #define VIX_ASSERT_PREFERENCE_NAME "vix.doAssert" +#define VIX_API_TRACE_PREFERENCE_NAME "vix.apiTraceLevel" /* * Assertions. Normally we'd just use ASSERT(), but we've hit many cases @@ -424,6 +427,7 @@ extern void VixAssert(const char *cond, const char *file, int lineNum); #endif #define DEFAULT_VIX_LOG_LEVEL 0 +#define DEFAULT_VIX_API_TRACE_LEVEL 0 #define VIX_DEBUG_LEVEL(logLevel, s) if (logLevel <= vixDebugGlobalSpewLevel) \ { char *debugString = VixAllocDebugString s; \ @@ -431,7 +435,7 @@ extern void VixAssert(const char *cond, const char *file, int lineNum); VixDebug_GetFileBaseName(__FILE__), __LINE__, debugString); \ free(debugString); } -#define VIX_DEBUG(s) if (0 != vixDebugGlobalSpewLevel) \ +#define VIX_DEBUG(s) if (0 != vixDebugGlobalSpewLevel) \ { char *debugString = VixAllocDebugString s; \ Log("Vix: [%lu %s:%d]: %s", (unsigned long)Util_GetCurrentThreadId(), \ VixDebug_GetFileBaseName(__FILE__), __LINE__, debugString); \ @@ -442,6 +446,14 @@ extern void VixAssert(const char *cond, const char *file, int lineNum); VixDebug_GetFileBaseName(__FILE__), __LINE__, debugString); \ free(debugString); } +#define VIX_API_TRACE_ON() (vixApiTraceGlobalSpewLevel > 0) + +#define VIX_API_LOG(s) if (VIX_API_TRACE_ON()) \ + { char *debugString = VixAllocDebugString s; \ + Log("VixApiLog: %lu %s %s\n", (unsigned long) Util_GetCurrentThreadId(),\ + __FUNCTION__, debugString); \ + free(debugString); } + #endif // VIX_HIDE_FROM_JAVA #ifdef __cplusplus diff --git a/open-vm-tools/lib/include/vmGuestLib.h b/open-vm-tools/lib/include/vmGuestLib.h index 809ed0e64..6766ed53b 100644 --- a/open-vm-tools/lib/include/vmGuestLib.h +++ b/open-vm-tools/lib/include/vmGuestLib.h @@ -366,6 +366,20 @@ VMGuestLibError VMGuestLib_GetResourcePoolPath(VMGuestLibHandle handle, // IN size_t *bufferSize, // IN/OUT char *pathBuffer); // OUT +/* + * CPU stolen time. The time (in ms) that the VM was runnable but not scheduled + * to run. + */ + +VMGuestLibError VMGuestLib_GetCpuStolenMs(VMGuestLibHandle handle, // IN + uint64 *cpuStolenMs); // OUT +/* + * Memory Target Size. + */ + +VMGuestLibError VMGuestLib_GetMemTargetSizeMB(VMGuestLibHandle handle, // IN + uint64 *memTargetSizeMB); // OUT + #ifdef __cplusplus } diff --git a/open-vm-tools/lib/include/vm_assert.h b/open-vm-tools/lib/include/vm_assert.h index 970bc700f..c98e39d54 100644 --- a/open-vm-tools/lib/include/vm_assert.h +++ b/open-vm-tools/lib/include/vm_assert.h @@ -83,10 +83,26 @@ EXTERN void WarningThrottled(uint32 *count, const char *fmt, ...) */ #ifndef ASSERT_IFNOT - #ifdef __cplusplus - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : (void)0) + /* + * PR 271512: When compiling with gcc, catch assignments inside an ASSERT. + * + * 'UNLIKELY' is defined with __builtin_expect, which does not warn when passed an + * assignment (gcc bug 36050). + * To get around this, we put 'cond' in an 'if' statement and make sure it never gets + * executed by putting that inside of 'if (0)'. + * We use gcc's statement expression syntax to make ASSERT an expression because some + * code uses it that way. + * + * Since statement expression syntax is a gcc extension and since it's not clear if + * this is a problem with other compilers, the ASSERT definition was not changed for + * them. Using a bare 'cond' with the ternary operator may provide a solution. + */ + #ifdef __GNUC__ + #define ASSERT_IFNOT(cond, panic) \ + ({if (UNLIKELY(!(cond))) { panic; if (0) { if (cond) { ; } } } (void)0;}) #else - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : 0) + #define ASSERT_IFNOT(cond, panic) \ + (UNLIKELY(!(cond)) ? (panic) : (void)0) #endif #endif diff --git a/open-vm-tools/lib/include/vm_atomic.h b/open-vm-tools/lib/include/vm_atomic.h index 763604b86..77260c79e 100644 --- a/open-vm-tools/lib/include/vm_atomic.h +++ b/open-vm-tools/lib/include/vm_atomic.h @@ -196,10 +196,29 @@ static INLINE void AtomicEpilogue(void) { #ifdef ATOMIC_USE_FENCE +#ifdef VMM + /* The monitor conditionally patches out the lfence when not needed.*/ + /* Construct a MonitorPatchTextEntry in the .patchtext section. */ + asm volatile ("1:\n\t" + "lfence\n\t" + "2:\n\t" + ".pushsection .patchtext\n\t" +#ifdef VMM32 + ".long 1b\n\t" + ".long 0\n\t" + ".long 2b\n\t" + ".long 0\n\t" +#else + ".quad 1b\n\t" + ".quad 2b\n\t" +#endif + ".popsection\n\t" ::: "memory"); +#else if (UNLIKELY(AtomicUseFence)) { asm volatile ("lfence" ::: "memory"); } #endif +#endif } diff --git a/open-vm-tools/lib/include/vm_basic_asm.h b/open-vm-tools/lib/include/vm_basic_asm.h index 3032179f8..9d43452c1 100644 --- a/open-vm-tools/lib/include/vm_basic_asm.h +++ b/open-vm-tools/lib/include/vm_basic_asm.h @@ -853,4 +853,21 @@ RDTSC(void) #error No compiler defined for RDTSC #endif /* __GNUC__ */ +/* + *----------------------------------------------------------------------------- + * + * DEBUGBREAK -- + * + * Does an int3 for MSVC / GCC. This is a macro to make sure int3 is + * always inlined. + * + *----------------------------------------------------------------------------- + */ + +#ifdef _MSC_VER +#define DEBUGBREAK() __debugbreak() +#else +#define DEBUGBREAK() __asm__ (" int $3 ") +#endif + #endif diff --git a/open-vm-tools/lib/include/vm_basic_types.h b/open-vm-tools/lib/include/vm_basic_types.h index c4b9bbda0..b1503b5d4 100644 --- a/open-vm-tools/lib/include/vm_basic_types.h +++ b/open-vm-tools/lib/include/vm_basic_types.h @@ -338,6 +338,9 @@ typedef int64 VmTimeVirtualClock; /* Virtual Clock kept in CPU cycles */ #ifdef _MSC_VER #define CONST64(c) c##I64 #define CONST64U(c) c##uI64 +#elif defined __APPLE__ +#define CONST64(c) c##LL +#define CONST64U(c) c##uLL #elif __GNUC__ #ifdef VM_X86_64 #define CONST64(c) c##L @@ -489,10 +492,11 @@ typedef uint64 MPN64; */ typedef VA32 UserVA32; typedef VA64 UserVA64; -typedef UserVA32 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA64 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA32 UserVA32Const; /* Userspace ptr to data that we may only read. */ typedef UserVA64 UserVA64Const; /* Used by 64-bit syscalls until conversion is finished. */ #ifdef VMKERNEL -typedef UserVA32 UserVA; +typedef UserVA64 UserVA; #else typedef void * UserVA; #endif diff --git a/open-vm-tools/lib/include/vm_version.h b/open-vm-tools/lib/include/vm_version.h index eaefc1caa..8b1a01621 100644 --- a/open-vm-tools/lib/include/vm_version.h +++ b/open-vm-tools/lib/include/vm_version.h @@ -282,7 +282,7 @@ /* * The current Tools version, derived from vm_tools_version.h. Do not modify this. */ -#define TOOLS_VERSION "2009.01.21" +#define TOOLS_VERSION "2009.02.18" #ifdef VMX86_VPX #define VIM_API_TYPE "VirtualCenter" diff --git a/open-vm-tools/lib/include/vmrpcdbg.h b/open-vm-tools/lib/include/vmrpcdbg.h new file mode 100644 index 000000000..b906f9a81 --- /dev/null +++ b/open-vm-tools/lib/include/vmrpcdbg.h @@ -0,0 +1,183 @@ +/********************************************************* + * Copyright (C) 2008 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 _VMRPCDBG_H_ +#define _VMRPCDBG_H_ + +/** + * @file vmrpcdbg.h + * + * Defines the public API for the "GuestRPC Debug Channel" implementation, and + * the interface between the debug library and debug plugins. + * + * @addtogroup vmtools_debug + * @{ + */ + +#include "vmtoolsApp.h" +#include "util.h" + +struct RpcDebugPlugin; + +/** + * Signature for the plugin's "receive" function, to validate the data + * applications send using RpcChannel_Send. + */ +typedef gboolean (*RpcDebugRecvFn)(char *data, + size_t dataLen, + char **result, + size_t *resultLen); + +/** Defines a mapping between a message and a "receive" function. */ +typedef struct RpcDebugRecvMapping { + gchar *name; + RpcDebugRecvFn recvFn; + /** + * If not NULL, should be a xdrproc_t function for deserializing the data + * in the received message. + */ + gpointer xdrProc; + /** If xdrProc is provided, should be the size of the structure to allocate. */ + size_t xdrSize; +} RpcDebugRecvMapping; + + +/** + * Signature for validation functions. Validation functions are called after + * an application has processed an "incoming" RPC, so that the plugin can + * validate the response. + */ +typedef gboolean (*RpcDebugValidateFn)(RpcInData *data, + Bool ret); + +/** Defines a mapping between a message and a "validate" function. */ +typedef struct RpcDebugMsgMapping { + gchar *message; + size_t messageLen; + RpcDebugValidateFn validateFn; + gboolean freeMsg; +} RpcDebugMsgMapping; + +/** Defines a (NULL-terminated) list of message / validator mappings. */ +typedef struct RpcDebugMsgList { + RpcDebugMsgMapping *mappings; + size_t index; +} RpcDebugMsgList; + + +/** + * Signature for the plugin's "send" function, which provides the data + * to be sent when the service tries to read from the RPC Channel. + * + * The function should return FALSE if the service should finish the + * test (any data provided when this function returns FALSE is ignored). + */ +typedef gboolean (*RpcDebugSendFn)(RpcDebugMsgMapping *rpcdata); + +/** Signature for the plugin's "shutdown" function. */ +typedef void (*RpcDebugShutdownFn)(ToolsAppCtx *ctx, + struct RpcDebugPlugin *plugin); + +/** + * Registration data for debug plugins, should be returned by the plugin's + * entry point function. + */ +typedef struct RpcDebugPlugin { + /** Maps "incoming" RPCs to specific receive functions. NULL-terminated. */ + RpcDebugRecvMapping *recvFns; + /** + * Default receive function for when no mapping matches the incoming command. + * May be NULL. + */ + RpcDebugRecvFn dfltRecvFn; + /** Send function. */ + RpcDebugSendFn sendFn; + /** Shutdown function. */ + RpcDebugShutdownFn shutdownFn; +} RpcDebugPlugin; + + +/** + * Signature for the plugin's entry point. The function works in a similar + * way to the "ToolsOnLoad" function for regular plugins. + */ +typedef RpcDebugPlugin *(*RpcDebugOnLoadFn)(ToolsAppCtx *ctx); + +struct RpcDebugLibData; + +/** + * Describes the external interface of the library. An instance of this struct + * is returned by RpcDebug_Initialize() and can be used by applications using + * the library to use the debugging functionality. + */ +typedef struct RpcDebugLibData { + RpcChannel * (*newDebugChannel) (ToolsAppCtx *, struct RpcDebugLibData *); + void (*shutdown) (ToolsAppCtx *, struct RpcDebugLibData *); + RpcDebugPlugin *debugPlugin; +} RpcDebugLibData; + +/** Signature of the library's initialization function. */ +typedef RpcDebugLibData *(* RpcDebugInitializeFn)(ToolsAppCtx *, gchar *); + + +/** + * Helper macro to set @a result / @a resultLen when responding to an RPC. + * + * @param[in] resultStr The string to set. + * @param[out] result Where to store the result. + * @param[out] resultLen Where to store the length. + */ + +#define RPCDEBUG_SET_RESULT(resultStr, result, resultLen) do { \ + char *__resultStr = (resultStr); \ + char **__result = (result); \ + size_t *__resultLen = (resultLen); \ + if (__result != NULL) { \ + *__result = Util_SafeStrdup(__resultStr); \ + } \ + if (__resultLen != NULL) { \ + *__resultLen = strlen(__resultStr); \ + } \ +} while (0) + +void +RpcDebug_DecRef(ToolsAppCtx *ctx); + +void +RpcDebug_IncRef(void); + +RpcDebugLibData * +RpcDebug_Initialize(ToolsAppCtx *ctx, + gchar *dbgPlugin); + +RpcChannel * +RpcDebug_NewDebugChannel(ToolsAppCtx *ctx, + RpcDebugLibData *data); + +gboolean +RpcDebug_SendNext(RpcDebugMsgMapping *rpcdata, + RpcDebugMsgList *list); + +void +RpcDebug_Shutdown(ToolsAppCtx *ctx, + RpcDebugLibData *data); + +/** @} */ + +#endif /* _VMRPCDBG_H_ */ + diff --git a/open-vm-tools/lib/include/vmtoolsApp.h b/open-vm-tools/lib/include/vmtoolsApp.h new file mode 100644 index 000000000..ddc193fbf --- /dev/null +++ b/open-vm-tools/lib/include/vmtoolsApp.h @@ -0,0 +1,317 @@ +/********************************************************* + * Copyright (C) 2008 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 _VMTOOLSAPP_H_ +#define _VMTOOLSAPP_H_ + +/** + * @file vmtoolsApp.h + * + * Defines the interface between the core tools services and the plugins + * that are dynamically loaded into the service. + * + * @addtogroup vmtools_plugins + * @{ + */ + +#include +#if defined(G_PLATFORM_WIN32) +# include +# include +#endif +#include "guestCaps.h" +#include "rpcChannel.h" + +/** + * Error reporting macro. Call this if the app encounters an error + * that requires the service to quit. The service's main loop will stop + * as soon as it regains control of the application. + * + * @param[in] ctx The application context. + * @param[in] err Error code. Must not be 0. + */ +#define VMTOOLSAPP_ERROR(ctx, err) do { \ + g_assert((err) != 0); \ + (ctx)->errorCode = (err); \ + g_main_loop_quit((ctx)->mainLoop); \ +} while (0) + + +/** + * Attaches the given event source to the app context's main loop. + * + * @param[in] ctx The application context. + * @param[in] src Source to attach. + * @param[in] cb Callback to call when event is "ready". + * @param[in] data Data to provide to the callback. + * @param[in] destroy Destruction notification callback. + */ +#define VMTOOLSAPP_ATTACH_SOURCE(ctx, src, cb, data, destroy) do { \ + GSource *__src = (src); \ + g_source_set_callback(__src, (GSourceFunc) (cb), (data), (destroy)); \ + g_source_attach(__src, g_main_loop_get_context((ctx)->mainLoop)); \ +} while (0) + + +/** + * Signal sent when registering or unregistering capabilities. + * + * @param[in] src The source object. + * @param[in] ctx ToolsAppCtx *: the application context. + * @param[in] set gboolean: TRUE if setting capabilities, FALSE if unsetting them. + * @param[in] data Client data. + * + * @return A GArray instance with the capabilities to be set or unset. The + * elements should be of type ToolsAppCapability. + */ +#define TOOLS_CORE_SIG_CAPABILITIES "tcs_capabilities" + +/** + * Signal sent when a successful RpcChannel reset occurs. + * + * @param[in] src The source object. + * @param[in] ctx ToolsAppCtx *: The application context. + * @param[in] data Client data. + */ +#define TOOLS_CORE_SIG_RESET "tcs_reset" + +/** + * Signal sent when a "set option" RPC message arrives. + * + * @param[in] src The source object. + * @param[in] ctx ToolsAppCtx *: The application context. + * @param[in] option gchar *: Option being set. + * @param[in] value gchar *: Option value. + * @param[in] data Client data. + * + * @return A gboolean saying whether the option was recognized and the value + * was valid. + */ +#define TOOLS_CORE_SIG_SET_OPTION "tcs_set_option" + +/** + * Signal sent when shutting down the service. + * + * @param[in] src The source object. + * @param[in] ctx ToolsAppCtx *: The application context. + * @param[in] data Client data. + */ +#define TOOLS_CORE_SIG_SHUTDOWN "tcs_shutdown" + +#if defined(G_PLATFORM_WIN32) +/** + * Signal sent when there's a change in the state of a user's session. + * + * @param[in] src The source object. + * @param[in] ctx ToolsAppCtx *: The application context. + * @param[in] code DWORD: Session state change code. + * @param[in] id DWORD: Session ID. + * @param[in] data Client data. + */ +#define TOOLS_CORE_SIG_SESSION_CHANGE "tcs_session_change" +#endif + + +/** + * Defines the context of a tools application. This data is provided by the + * core services to applications when they're loaded. + */ +typedef struct ToolsAppCtx { + /** Name of the application. */ + const gchar *name; + /** Whether we're running under a VMware hypervisor. */ + gboolean isVMware; + /** Error code to return from the main loop. */ + int errorCode; + /** The main loop instance for the service. */ + GMainLoop *mainLoop; + /** The RPC channel used to communicate with the VMX. */ + RpcChannel *rpc; + /** Service configuration from the config file. */ + GKeyFile *config; +#if defined(G_PLATFORM_WIN32) + /** Whether COM is initialized. */ + gboolean comInitialized; +#else + /** The FD to access the VMware blocking fs. -1 if no FD available. */ + int blockFD; +#endif + /** + * A GObject instance shared among all plugins. The object itself doesn't + * provide any functionality; but the service emits a few signals on this + * object (see the signal name declarations in this header), and plugins can + * register and emit their own signals using this object. + */ + gpointer serviceObj; +} ToolsAppCtx; + +#if defined(G_PLATFORM_WIN32) +/** + * Initializes COM if it hasn't been initialized yet. + * + * @param[in] ctx The application context. + * + * @return TRUE if COM is initialized when the function returns. + */ +static INLINE gboolean +ToolsCore_InitializeCOM(ToolsAppCtx *ctx) +{ + if (!ctx->comInitialized) { + HRESULT ret = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + ctx->comInitialized = SUCCEEDED(ret); + if (!ctx->comInitialized) { + g_log(ctx->name, G_LOG_LEVEL_WARNING, + "COM initialization failed(0x%x)\n", ret); + } + } + return ctx->comInitialized; +} +#endif + + +/* Capabilities. */ + +/** Identifies the type of a Tools capability. */ +typedef enum { + TOOLS_CAP_OLD = 0, + TOOLS_CAP_OLD_NOVAL = 1, + TOOLS_CAP_NEW = 2 +} ToolsCapabilityType; + +/** + * Information about a capability supported by the application. This structure + * supports both old-style capabilities (which have a separate RPC message for + * each capability) and new-style capabilities (as defined in guestCaps.h). + * + * The service will register all capabilities with non-zero values when the + * service is started (or the host asks for the service to register its + * capabilities). + */ +typedef struct ToolsAppCapability { + /** Identifies the type of the capability. */ + ToolsCapabilityType type; + /** + * For old-style, the capability name. The RPC message for setting the + * capability will be "tools.capability.[name]". Ignored for TOOLS_CAP_NEW. + */ + gchar *name; + /** + * The capability entry in the enum defined in guestCaps.h. + * Used only for TOOLS_CAP_NEW. + */ + GuestCapabilities index; + /** The capability value. 0 means disabled. Ignored for TOOLS_CAP_OLD_NOVAL. */ + guint value; +} ToolsAppCapability; + + +/* Application registration. */ + +/** Type of the application feature being registered. */ +typedef enum { + /** + * Denotes a list of GuestRPC registrations (type RpcChannelCallback). + */ + TOOLS_APP_GUESTRPC = 1, + /** + * Denotes a list of signals the application is interested in (type + * ToolsPluginSignalCb). + */ + TOOLS_APP_SIGNALS = 2 +} ToolsAppType; + + +/** + * Defines a "transport-specific" registration. The array contains data specific + * to a "transport" implementation. + * + * When the service is shutting down, if the @a data field is not NULL, the + * array instance will be freed, including its backing element array. + * See the documentation for g_array_free(). This will happen only after any + * plugin's shutdown callback is called, so plugins have a chance of performing + * custom clean up of this data. + */ +typedef struct ToolsAppReg { + ToolsAppType type; + GArray *data; +} ToolsAppReg; + + +/** + * Defines a struct for mapping callbacks to signals. Normally it would suffice + * to use g_signal_connect() directly to register interest in signals; but to + * allow dynamic registration of signals by plugins, using this struct allows + * registration to be delayed until all plugins have been loaded and have had + * the chance to register their own signals. The daemon code then can go + * through the plugins' registration data and connect all desired signals. + */ + +typedef struct ToolsPluginSignalCb { + const gchar *signame; + gpointer callback; + gpointer clientData; +} ToolsPluginSignalCb; + + +/** + * The registration data for an application. This gives the service information + * about all functionality exported by the application, and any events that the + * application may be interested in. + * + * All fields are optional. When the plugin is shut down, if the @a regs field + * is not NULL, it (and its element array) are freed with g_array_free(). + */ +typedef struct ToolsPluginData { + /** Name of the application. */ + char *name; + /** List of features provided by the app. */ + GArray *regs; + /** Private plugin data. */ + gpointer _private; +} ToolsPluginData; + +/** + * Definition for tagging functions to be exported in the plugin binary. Use this + * to tag the plugin entry point function, and any other functions that the plugin + * needs to export. + */ +#if defined(G_PLATFORM_WIN32) +# define TOOLS_MODULE_EXPORT __declspec(dllexport) +#elif defined(GCC_EXPLICIT_EXPORT) +# define TOOLS_MODULE_EXPORT __attribute__((visibility("default"))) +#else +# define TOOLS_MODULE_EXPORT +#endif + +/** + * Signature for the plugin entry point function. The function should be called + * @a ToolsOnLoad, and be exported in the plugin binary (e.g., by tagging it + * with TOOLS_MODULE_EXPORT). + * + * If the plugin wants to stay loaded, it always should return the registration + * data, even if all it contains is the (mandatory) plugin name. Plugins which + * return NULL will be unloaded before the service is started, so they shouldn't + * modify the service state (for example, by adding callbacks to the service's + * main loop). + */ +typedef ToolsPluginData *(*ToolsPluginOnLoad)(ToolsAppCtx *ctx); + +/** @} */ + +#endif /* _VMTOOLSAPP_H_ */ + diff --git a/open-vm-tools/lib/include/vmtoolsd_version.h b/open-vm-tools/lib/include/vmtoolsd_version.h new file mode 100644 index 000000000..0c6c587a9 --- /dev/null +++ b/open-vm-tools/lib/include/vmtoolsd_version.h @@ -0,0 +1,27 @@ +/********************************************************* + * Copyright (C) 2008 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 _VMTOOLSD_VERSION_H_ +#define _VMTOOLSD_VERSION_H_ + +#include "vm_tools_version.h" +#define VMTOOLSD_VERSION_COMMAS TOOLS_VERSION_EXT_CURRENT_CSV +#define VMTOOLSD_VERSION_STRING TOOLS_VERSION_EXT_CURRENT_STR + +#endif /* _VMTOOLS_VERSION_H_ */ + diff --git a/open-vm-tools/lib/include/x86cpuid.h b/open-vm-tools/lib/include/x86cpuid.h index ba534c9a4..38c2058ac 100644 --- a/open-vm-tools/lib/include/x86cpuid.h +++ b/open-vm-tools/lib/include/x86cpuid.h @@ -440,7 +440,7 @@ FLAGDEF( 87, EDX, AMD, 4, 1, TM, NA, IGNORE, 0, FALSE) FLAGDEF( 87, EDX, AMD, 5, 1, STC, NA, IGNORE, 0, FALSE) \ FLAGDEF( 87, EDX, AMD, 6, 1, 100MHZSTEPS, NA, IGNORE, 0, FALSE) \ FLAGDEF( 87, EDX, AMD, 7, 1, HWPSTATE, NA, IGNORE, 0, FALSE) \ -FLAGDEF( 87, EDX, AMD, 8, 1, TSC_INVARIANT, NA, IGNORE, 0, FALSE) \ +FLAGDEF( 87, EDX, COMMON, 8, 1, TSC_INVARIANT, NA, IGNORE, 0, FALSE) \ FIELDDEFA(88, EAX, COMMON, 0, 8, PHYSBITS, NA, IGNORE, 0, FALSE, PHYS_BITS) \ FIELDDEFA(88, EAX, COMMON, 8, 8, VIRTBITS, NA, IGNORE, 0, FALSE, VIRT_BITS) \ FIELDDEFA(88, ECX, AMD, 0, 8, CORE_COUNT, NA, IGNORE, 0, FALSE, AMD_CORE_COUNT) \ @@ -644,6 +644,9 @@ FIELD_FUNC(MWAIT_C4_SUBSTATE, CPUID_INTEL_ID5EDX_MWAIT_C4_SUBSTATE) #define CPUID_MODEL_PIII_08 8 #define CPUID_MODEL_PIII_0A 10 +/* AMD model information */ +#define CPUID_MODEL_BARCELONA_02 0x02 // Barcelona (both Opteron & Phenom kind) + /* *---------------------------------------------------------------------- * @@ -815,6 +818,17 @@ CPUID_FAMILY_IS_K8STAR(uint32 _eax) CPUID_FAMILY_IS_K8MOBILE(_eax); } +/* + * AMD Barcelona (of either Opteron or Phenom kind). + */ +static INLINE Bool +CPUID_MODEL_IS_BARCELONA(uint32 v) // IN: %eax from CPUID with %eax=1. +{ + /* Assumes the CPU manufacturer is AMD. */ + return CPUID_FAMILY_IS_K8L(v) && + CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_BARCELONA_02; +} + #define CPUID_TYPE_PRIMARY 0 #define CPUID_TYPE_OVERDRIVE 1 diff --git a/open-vm-tools/lib/misc/hostinfoPosix.c b/open-vm-tools/lib/misc/hostinfoPosix.c index cd26d0aa1..fc8931fb3 100644 --- a/open-vm-tools/lib/misc/hostinfoPosix.c +++ b/open-vm-tools/lib/misc/hostinfoPosix.c @@ -39,6 +39,10 @@ #include "hostinfo.h" #include "safetime.h" #include "str.h" +#include "guest_os.h" +#include "dynbuf.h" +#include "vmstdio.h" +#include "posix.h" #define SYSINFO_STRING_32 "i386" #define SYSINFO_STRING_64 "amd64" @@ -54,6 +58,71 @@ static Bool hostinfoOSVersionInitialized; static int hostinfoOSVersion[3]; static char hostinfoOSVersionString[SYS_NMLN]; +#define DISTRO_BUF_SIZE 255 + +typedef struct lsb_distro_info { + char *name; + char *scanstring; +} LSBDistroInfo; + + +static LSBDistroInfo lsbFields[] = { + {"DISTRIB_ID=", "DISTRIB_ID=%s"}, + {"DISTRIB_RELEASE=", "DISTRIB_RELEASE=%s"}, + {"DISTRIB_CODENAME=", "DISTRIB_CODENAME=%s"}, + {"DISTRIB_DESCRIPTION=", "DISTRIB_DESCRIPTION=%s"}, + {NULL, NULL}, +}; + + +typedef struct distro_info { + char *name; + char *filename; +} DistroInfo; + + +static DistroInfo distroArray[] = { + {"RedHat", "/etc/redhat-release"}, + {"RedHat", "/etc/redhat_version"}, + {"Sun", "/etc/sun-release"}, + {"SuSE", "/etc/SuSE-release"}, + {"SuSE", "/etc/novell-release"}, + {"SuSE", "/etc/sles-release"}, + {"Debian", "/etc/debian_version"}, + {"Debian", "/etc/debian_release"}, + {"Mandrake", "/etc/mandrake-release"}, + {"Mandriva", "/etc/mandriva-release"}, + {"Mandrake", "/etc/mandrakelinux-release"}, + {"TurboLinux", "/etc/turbolinux-release"}, + {"Fedora Core", "/etc/fedora-release"}, + {"Gentoo", "/etc/gentoo-release"}, + {"Novell", "/etc/nld-release"}, + {"Ubuntu", "/etc/lsb-release"}, + {"Annvix", "/etc/annvix-release"}, + {"Arch", "/etc/arch-release"}, + {"Arklinux", "/etc/arklinux-release"}, + {"Aurox", "/etc/aurox-release"}, + {"BlackCat", "/etc/blackcat-release"}, + {"Cobalt", "/etc/cobalt-release"}, + {"Conectiva", "/etc/conectiva-release"}, + {"Immunix", "/etc/immunix-release"}, + {"Knoppix", "/etc/knoppix_version"}, + {"Linux-From-Scratch", "/etc/lfs-release"}, + {"Linux-PPC", "/etc/linuxppc-release"}, + {"MkLinux", "/etc/mklinux-release"}, + {"PLD", "/etc/pld-release"}, + {"Slackware", "/etc/slackware-version"}, + {"Slackware", "/etc/slackware-release"}, + {"SMEServer", "/etc/e-smith-release"}, + {"Solaris", "/etc/release"}, + {"Tiny Sofa", "/etc/tinysofa-release"}, + {"UltraPenguin", "/etc/ultrapenguin-release"}, + {"UnitedLinux", "/etc/UnitedLinux-release"}, + {"VALinux", "/etc/va-release"}, + {"Yellow Dog", "/etc/yellowdog-release"}, + {NULL, NULL}, +}; + /* *---------------------------------------------------------------------- @@ -256,3 +325,531 @@ Hostinfo_GetSystemBitness(void) return -1; #endif } + + +/* + *----------------------------------------------------------------------------- + * + * HostinfoGetOSShortName -- + * + * Returns distro information based on .vmx format (distroShort). + * + * Return value: + * Overwrited the short name if we recognise the OS. + * Otherwise leave the short name as it is. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static void +HostinfoGetOSShortName(char *distro, // IN: full distro name + char *distroShort, // OUT: short distro name + int distroShortSize) // IN: size of short distro name + +{ + char *distroLower = NULL; /* Lower case distro name */ + + distroLower = calloc(strlen(distro) + 1, sizeof *distroLower); + + if (distroLower == NULL) { + Warning("%s: could not allocate memory\n", __FUNCTION__); + return; + } + + Str_Strcpy(distroLower, distro, distroShortSize); + distroLower = Str_ToLower(distroLower); + + if (strstr(distroLower, "red hat")) { + if (strstr(distroLower, "enterprise")) { + + /* + * Looking for "release x" here instead of "x" as there could be + * build version which can be misleading. For example Red Hat + * Enterprise Linux ES release 4 (Nahant Update 3) + */ + + int release = 0; + char *releaseStart = strstr(distroLower, "release"); + + if (releaseStart) { + sscanf(releaseStart, "release %d", &release); + if (release > 0) { + snprintf(distroShort, distroShortSize, STR_OS_RED_HAT_EN"%d", release); + } + } + + if (release <= 0) { + Warning("%s: could not read Red Hat Enterprise release version\n", + __FUNCTION__); + Str_Strcpy(distroShort, STR_OS_RED_HAT_EN, distroShortSize); + } + + } else { + Str_Strcpy(distroShort, STR_OS_RED_HAT, distroShortSize); + } + } else if (strstr(distroLower, "suse")) { + if (strstr(distroLower, "enterprise")) { + Str_Strcpy(distroShort, STR_OS_SUSE_EN, distroShortSize); + } else if (strstr(distroLower, "sun")) { + Str_Strcpy(distroShort, STR_OS_SUN_DESK, distroShortSize); + } else if (strstr(distroLower, "novell")) { + Str_Strcpy(distroShort, STR_OS_NOVELL, distroShortSize); + } else { + Str_Strcpy(distroShort, STR_OS_SUSE, distroShortSize); + } + } else if (strstr(distroLower, "mandrake")) { + Str_Strcpy(distroShort, STR_OS_MANDRAKE, distroShortSize); + } else if (strstr(distroLower, "turbolinux")) { + Str_Strcpy(distroShort, STR_OS_TURBO, distroShortSize); + } else if (strstr(distroLower, "sun")) { + Str_Strcpy(distroShort, STR_OS_SUN_DESK, distroShortSize); + } else if (strstr(distroLower, "annvix")) { + Str_Strcpy(distroShort, STR_OS_ANNVIX, distroShortSize); + } else if (strstr(distroLower, "arch")) { + Str_Strcpy(distroShort, STR_OS_ARCH, distroShortSize); + } else if (strstr(distroLower, "arklinux")) { + Str_Strcpy(distroShort, STR_OS_ARKLINUX, distroShortSize); + } else if (strstr(distroLower, "aurox")) { + Str_Strcpy(distroShort, STR_OS_AUROX, distroShortSize); + } else if (strstr(distroLower, "black cat")) { + Str_Strcpy(distroShort, STR_OS_BLACKCAT, distroShortSize); + } else if (strstr(distroLower, "cobalt")) { + Str_Strcpy(distroShort, STR_OS_COBALT, distroShortSize); + } else if (strstr(distroLower, "conectiva")) { + Str_Strcpy(distroShort, STR_OS_CONECTIVA, distroShortSize); + } else if (strstr(distroLower, "debian")) { + Str_Strcpy(distroShort, STR_OS_DEBIAN, distroShortSize); + } else if (strstr(distroLower, "fedora")) { + Str_Strcpy(distroShort, STR_OS_FEDORA, distroShortSize); + } else if (strstr(distroLower, "gentoo")) { + Str_Strcpy(distroShort, STR_OS_GENTOO, distroShortSize); + } else if (strstr(distroLower, "immunix")) { + Str_Strcpy(distroShort, STR_OS_IMMUNIX, distroShortSize); + } else if (strstr(distroLower, "linux-from-scratch")) { + Str_Strcpy(distroShort, STR_OS_LINUX_FROM_SCRATCH, distroShortSize); + } else if (strstr(distroLower, "linux-ppc")) { + Str_Strcpy(distroShort, STR_OS_LINUX_PPC, distroShortSize); + } else if (strstr(distroLower, "mandriva")) { + Str_Strcpy(distroShort, STR_OS_MANDRIVA, distroShortSize); + } else if (strstr(distroLower, "mklinux")) { + Str_Strcpy(distroShort, STR_OS_MKLINUX, distroShortSize); + } else if (strstr(distroLower, "pld")) { + Str_Strcpy(distroShort, STR_OS_PLD, distroShortSize); + } else if (strstr(distroLower, "slackware")) { + Str_Strcpy(distroShort, STR_OS_SLACKWARE, distroShortSize); + } else if (strstr(distroLower, "sme server")) { + Str_Strcpy(distroShort, STR_OS_SMESERVER, distroShortSize); + } else if (strstr(distroLower, "tiny sofa")) { + Str_Strcpy(distroShort, STR_OS_TINYSOFA, distroShortSize); + } else if (strstr(distroLower, "ubuntu")) { + Str_Strcpy(distroShort, STR_OS_UBUNTU, distroShortSize); + } else if (strstr(distroLower, "ultra penguin")) { + Str_Strcpy(distroShort, STR_OS_ULTRAPENGUIN, distroShortSize); + } else if (strstr(distroLower, "united linux")) { + Str_Strcpy(distroShort, STR_OS_UNITEDLINUX, distroShortSize); + } else if (strstr(distroLower, "va linux")) { + Str_Strcpy(distroShort, STR_OS_VALINUX, distroShortSize); + } else if (strstr(distroLower, "yellow dog")) { + Str_Strcpy(distroShort, STR_OS_YELLOW_DOG, distroShortSize); + } + + free(distroLower); +} + + +/* + *----------------------------------------------------------------------------- + * + * HostinfoReadDistroFile -- + * + * Look for a distro version file /etc/xxx-release. + * Once found, read the file in and figure out which distribution. + * + * Return value: + * Returns TRUE on success and FALSE on failure. + * Returns distro information verbatium from /etc/xxx-release (distro). + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static Bool +HostinfoReadDistroFile(char *filename, // IN: distro version file name + int distroSize, // IN: size of OS distro name buffer + char *distro) // OUT: full distro name +{ + int fd; + int buf_sz; + struct stat st; + Bool ret = FALSE; + char *distroOrig = NULL; + char distroPart[DISTRO_BUF_SIZE]; + char *tmpDistroPos = NULL; + int i = 0; + + if ((fd = open(filename, O_RDONLY)) < 0) { + Warning("%s: could not open file%s: %d\n", __FUNCTION__, filename, errno); + return FALSE; + } + + if (fstat(fd, &st)) { + Warning("%s: could not stat the file %s: %d\n", __FUNCTION__, filename, + errno); + goto out; + } + + buf_sz = st.st_size; + if (buf_sz >= distroSize) { + Warning("%s: input buffer too small\n", __FUNCTION__); + goto out; + } + distroOrig = calloc(distroSize, sizeof *distroOrig); + + if (distroOrig == NULL) { + Warning("%s: could not allocate memory\n", __FUNCTION__); + close(fd); + return FALSE; + } + + if (read(fd, distroOrig, buf_sz) != buf_sz) { + Warning("%s: could not read file %s: %d\n", __FUNCTION__, filename, errno); + goto out; + } + + distroOrig[buf_sz] = '\0'; + + /* + * For the case where we do have a release file in the LSB format, + * but there is no LSB module, let's parse the LSB file for possible fields. + */ + + distro[0] = '\0'; + + for (i = 0; lsbFields[i].name != NULL; i++) { + tmpDistroPos = strstr(distroOrig, lsbFields[i].name); + if (tmpDistroPos) { + sscanf(tmpDistroPos, lsbFields[i].scanstring, distroPart); + if (distroPart[0] == '"') { + char *tmpMakeNull = NULL; + + tmpDistroPos += strlen(lsbFields[i].name) + 1; + tmpMakeNull = strchr(tmpDistroPos + 1 , '"'); + if (tmpMakeNull) { + *tmpMakeNull = '\0'; + Str_Strcat(distro, tmpDistroPos, distroSize); + *tmpMakeNull = '"' ; + } + } else { + Str_Strcat(distro, distroPart, distroSize); + } + Str_Strcat(distro, " ", distroSize); + } + } + + if (distro[0] == '\0') { + /* Copy original string. What we got wasn't LSB compliant. */ + Str_Strcpy(distro, distroOrig, distroSize); + } + + ret = TRUE; + +out: + close(fd); + free(distroOrig); + + return ret; +} + + +/* + *---------------------------------------------------------------------- + * + * HostinfoGetCmdOutput -- + * + * Run a cmd & get its cmd line output + * + * Results: + * An allocated string or NULL if an error occurred. + * + * Side effects: + * The cmd is run. + * + *---------------------------------------------------------------------- + */ + +static char * +HostinfoGetCmdOutput(const char *cmd) // IN: +{ +#if defined(N_PLAT_NLM) + Warning("Trying to execute command \"%s\" and catch its output... No way on NetWare...\n", cmd); + return NULL; +#else + DynBuf db; + FILE *stream; + char *out = NULL; + + DynBuf_Init(&db); + + stream = Posix_Popen(cmd, "r"); + if (stream == NULL) { + Warning("Unable to get output of command \"%s\"\n", cmd); + + return NULL; + } + + for (;;) { + char *line = NULL; + size_t size; + + switch (StdIO_ReadNextLine(stream, &line, 0, &size)) { + case StdIO_Error: + goto closeIt; + break; + + case StdIO_EOF: + break; + + case StdIO_Success: + break; + + default: + ASSERT_NOT_IMPLEMENTED(FALSE); + } + + if (line == NULL) { + break; + } + + DynBuf_Append(&db, line, size); + free(line); + } + + if (DynBuf_Get(&db)) { + out = (char *) DynBuf_AllocGet(&db); + } + closeIt: + DynBuf_Destroy(&db); + + pclose(stream); + + return out; +#endif +} + + +/* + *----------------------------------------------------------------------------- + * + * Hostinfo_GetOSName -- + * + * Return OS version information. First retrieve OS information using + * uname, then look in /etc/xxx-release file to get the distro info. + * osFullName will be: + * + * An example of such string would be: + * Linux 2.4.18-3 Red Hat Linux release 7.3 (Valhalla) + * + * osName contains an os name in the same format that is used + * in .vmx file. + * + * Return value: + * Returns TRUE on success and FALSE on failure. + * Returns the guest's full OS name (osNameFull) + * Returns the guest's OS name in the same format as .vmx file (osName) + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +Hostinfo_GetOSName(uint32 outBufFullLen, // IN: length of osNameFull buffer + uint32 outBufLen, // IN: length of osName buffer + char *osNameFull, // OUT: Full OS name + char *osName) // OUT: OS name (.vmx file format) +{ + struct utsname buf; + unsigned int lastCharPos; + const char *lsbCmd = "lsb_release -sd 2>/dev/null"; + char *lsbOutput = NULL; + + /* + * Use uname to get complete OS information. + */ + + if (uname(&buf) < 0) { + Warning("%s: uname failed %d\n", __FUNCTION__, errno); + return FALSE; + } + + + if (strlen(buf.sysname) + strlen(buf.release) + 3 > outBufFullLen) { + Warning("%s: Error: buffer too small\n", __FUNCTION__); + return FALSE; + } + + Str_Strcpy(osName, STR_OS_EMPTY, outBufLen); + Str_Strcpy(osNameFull, buf.sysname, outBufFullLen); + Str_Strcat(osNameFull, STR_OS_EMPTY, outBufFullLen); + Str_Strcat(osNameFull, buf.release, outBufFullLen); + + /* + * Check to see if this is Linux + * If yes, determine the distro by looking for /etc/xxx file. + */ + + if (strstr(osNameFull, "Linux")) { + char distro[DISTRO_BUF_SIZE]; + char distroShort[DISTRO_BUF_SIZE]; + int i = 0; + int distroSize = sizeof distro; + + /* + * Write default distro string depending on the kernel version. If + * later we find more detailed information this will get overwritten. + */ + + if (strstr(buf.release, "2.4")) { + Str_Strcpy(distro, STR_OS_OTHER_24_FULL, distroSize); + Str_Strcpy(distroShort, STR_OS_OTHER_24, distroSize); + } else if (strstr(buf.release, "2.6")) { + Str_Strcpy(distro, STR_OS_OTHER_26_FULL, distroSize); + Str_Strcpy(distroShort, STR_OS_OTHER_26, distroSize); + } else { + Str_Strcpy(distro, STR_OS_OTHER_FULL, distroSize); + Str_Strcpy(distroShort, STR_OS_OTHER, distroSize); + } + + /* + * Try to get OS detailed information + * from the lsb_release command. + */ + + lsbOutput = HostinfoGetCmdOutput(lsbCmd); + + if (!lsbOutput) { + /* + * Try to get more detailed information from the version file. + */ + + for (i = 0; distroArray[i].filename != NULL; i++) { + if (HostinfoReadDistroFile(distroArray[i].filename, distroSize, + distro)) { + break; + } + } + + /* + * If we failed to read every distro file, exit now, before calling + * strlen on the distro buffer (which wasn't set). + */ + + if (distroArray[i].filename == NULL) { + Warning("%s: Error: no distro file found\n", __FUNCTION__); + return FALSE; + } + } else { + char *lsbStart = lsbOutput; + char *quoteEnd = NULL; + + if (lsbStart[0] == '"') { + lsbStart++; + quoteEnd = strchr(lsbStart, '"'); + if (quoteEnd) { + *quoteEnd = '\0'; + } + } + Str_Strcpy(distro, lsbStart, distroSize); + free(lsbOutput); + } + + HostinfoGetOSShortName(distro, distroShort, distroSize); + + if (strlen(distro) + strlen(osNameFull) + 2 > outBufFullLen) { + Warning("%s: Error: buffer too small\n", __FUNCTION__); + return FALSE; + } + + Str_Strcat(osNameFull, " ", outBufFullLen); + Str_Strcat(osNameFull, distro, outBufFullLen); + + if (strlen(distroShort) + 1 > outBufLen) { + Warning("%s: Error: buffer too small\n", __FUNCTION__); + return FALSE; + } + + Str_Strcpy(osName, distroShort, outBufLen); + } else if (strstr(osNameFull, "FreeBSD")) { + size_t nameLen = sizeof STR_OS_FREEBSD - 1; + size_t releaseLen = 0; + char *dashPtr; + + /* + * FreeBSD releases report their version as "x.y-RELEASE". We'll be naive + * look for the first dash, and use everything before it as the version + * number. + */ + + dashPtr = Str_Strchr(buf.release, '-'); + if (dashPtr != NULL) { + releaseLen = dashPtr - buf.release; + } + + if (nameLen + releaseLen + 1 > outBufLen) { + Warning("GuestInfoGetOSName: Error: buffer too small\n"); + return FALSE; + } + + Str_Strcpy(osName, STR_OS_FREEBSD, outBufLen); + } else if (strstr(osNameFull, "SunOS")) { + size_t nameLen = sizeof STR_OS_SOLARIS - 1; + size_t releaseLen = 0; + char *periodPtr; + + /* + * Solaris releases report their version as "x.y". For our supported + * releases it seems that x is always "5", and is ignored in favor of + * y for the version number. We'll be naive and look for the first + * period, and use the entire string after that as the version number. + */ + + periodPtr = Str_Strchr(buf.release, '.'); + if (periodPtr != NULL) { + releaseLen = (buf.release + strlen(buf.release)) - periodPtr; + } + + if (nameLen + releaseLen + 1 > outBufLen) { + Warning("GuestInfoGetOSName: Error: buffer too small\n"); + return FALSE; + } + + Str_Strcpy(osName, STR_OS_SOLARIS, outBufLen); + } + + if (Hostinfo_GetSystemBitness() == 64) { + if (strlen(osName) + sizeof STR_OS_64BIT_SUFFIX > outBufLen) { + Warning("%s: Error: buffer too small\n", __FUNCTION__); + return FALSE; + } + Str_Strcat(osName, STR_OS_64BIT_SUFFIX, outBufLen); + } + + /* + * Before returning, truncate + * the \n character at the end of the full name. + */ + + lastCharPos = strlen(osNameFull) - 1; + if (osNameFull[lastCharPos] == '\n') { + osNameFull[lastCharPos] = '\0'; + } + + return TRUE; +} + diff --git a/open-vm-tools/lib/misc/idLinux.c b/open-vm-tools/lib/misc/idLinux.c index 70d13448a..badc1bbe2 100644 --- a/open-vm-tools/lib/misc/idLinux.c +++ b/open-vm-tools/lib/misc/idLinux.c @@ -412,8 +412,9 @@ IdAuthCreate(void) if (ret == errAuthorizationSuccess) { return auth; } else { - Warning("%s: AuthorizationCreate failed, error %ld.\n", - __FUNCTION__, ret); + ASSERT_ON_COMPILE(sizeof ret == sizeof (int32)); + Warning("%s: AuthorizationCreate failed, error %d.\n", + __FUNCTION__, (int32)ret); return NULL; } } diff --git a/open-vm-tools/lib/misc/timeutil.c b/open-vm-tools/lib/misc/timeutil.c index d1b965ef5..79cba7afb 100644 --- a/open-vm-tools/lib/misc/timeutil.c +++ b/open-vm-tools/lib/misc/timeutil.c @@ -24,6 +24,7 @@ #include "safetime.h" +#include "unicode.h" #if defined(N_PLAT_NLM) # include @@ -78,10 +79,10 @@ static Bool TimeUtilIsValidDate(unsigned int year, unsigned int month, unsigned /* - * Function to guess Windows TZ Index by using time offset in + * Function to guess Windows TZ Index and Name by using time offset in * a lookup table */ -static int TimeUtilFindIndexByUTCOffset(int utcToStdOffsetMins); +static int TimeUtilFindIndexAndNameByUTCOffset(int utcStdOffMins, const char **ptzName); #if defined(_WIN32) /* @@ -953,9 +954,9 @@ TimeUtil_UTCTimeToSystemTime(const __time64_t utcTime, // IN /* *---------------------------------------------------------------------- * - * TimeUtil_GetLocalWindowsTimeZoneIndex -- + * TimeUtil_GetLocalWindowsTimeZoneIndexAndName -- * - * Gets Windows TZ index for local time zone. + * Gets Windows TZ Index and Name for local time zone. * * Results: * -1 if there is any error, else the Windows Time Zone ID of the @@ -968,71 +969,78 @@ TimeUtil_UTCTimeToSystemTime(const __time64_t utcTime, // IN *---------------------------------------------------------------------- */ int -TimeUtil_GetLocalWindowsTimeZoneIndex(void) +TimeUtil_GetLocalWindowsTimeZoneIndexAndName(char **ptzName) // OUT: returning TZ Name { int utcStdOffMins = 0; int winTimeZoneIndex = (-1); + const char *tzNameByUTCOffset = NULL; + + *ptzName = NULL; #if defined(_WIN32) - TIME_ZONE_INFORMATION tz; - char name[256] = { 0 }; - size_t nameLen = 0, nc = 0; - if (GetTimeZoneInformation(&tz) == TIME_ZONE_ID_INVALID) { - return (-1); - } + { + TIME_ZONE_INFORMATION tz; + if (GetTimeZoneInformation(&tz) == TIME_ZONE_ID_INVALID) { + return (-1); + } - /* 'Bias' = diff between UTC and local standard time */ - utcStdOffMins = 0-tz.Bias; // already in minutes + /* 'Bias' = diff between UTC and local standard time */ + utcStdOffMins = 0 - tz.Bias; // already in minutes - /* Get TZ name */ - nameLen = wcslen(tz.StandardName); - if (wcstombs(name, tz.StandardName, 255) <= 0) { - return (-1); + /* Find Windows TZ index */ + *ptzName = Unicode_AllocWithUTF16(tz.StandardName); + winTimeZoneIndex = Win32TimeUtilLookupZoneIndex(*ptzName); + if (winTimeZoneIndex < 0) { + Unicode_Free(*ptzName); + *ptzName = NULL; + } } - /* Convert name to Windows TZ index */ - winTimeZoneIndex = Win32TimeUtilLookupZoneIndex(name); - #else // NOT _WIN32 - /* - * Use localtime_r() to get offset between our local - * time and UTC. This varies by platform. Also, the structure - * fields are named "*gmt*" but the man pages claim offsets are - * to UTC, not GMT. - */ - - time_t now = time(NULL); - struct tm tim; - localtime_r(&now, &tim); - - #if defined SOL9 || defined SOL10 || defined N_PLAT_NLM - /* - * Offset is to standard (no need for DST adjustment). - * Negative is east of prime meridian. - */ - utcStdOffMins = 0 - timezone/60; - #else + { /* - * FreeBSD, Apple, Linux only: - * Offset is to local (need to adjust for DST). - * Negative is west of prime meridian. + * Use localtime_r() to get offset between our local + * time and UTC. This varies by platform. Also, the structure + * fields are named "*gmt*" but the man pages claim offsets are + * to UTC, not GMT. */ - utcStdOffMins = tim.tm_gmtoff/60; - if (tim.tm_isdst) { - utcStdOffMins -= 60; - } - #endif - /* can't figure this out directly for non-Win32 */ - winTimeZoneIndex = (-1); + time_t now = time(NULL); + struct tm tim; + localtime_r(&now, &tim); + + #if defined SOL9 || defined SOL10 || defined N_PLAT_NLM + /* + * Offset is to standard (no need for DST adjustment). + * Negative is east of prime meridian. + */ + utcStdOffMins = 0 - timezone/60; + #else + /* + * FreeBSD, Apple, Linux only: + * Offset is to local (need to adjust for DST). + * Negative is west of prime meridian. + */ + utcStdOffMins = tim.tm_gmtoff/60; + if (tim.tm_isdst) { + utcStdOffMins -= 60; + } + #endif + + /* can't figure this out directly for non-Win32 */ + winTimeZoneIndex = (-1); + } #endif /* If we don't have it yet, look up windowsCode. */ if (winTimeZoneIndex < 0) { - winTimeZoneIndex = TimeUtilFindIndexByUTCOffset(utcStdOffMins); + winTimeZoneIndex = TimeUtilFindIndexAndNameByUTCOffset(utcStdOffMins, &tzNameByUTCOffset); + if (winTimeZoneIndex >= 0) { + *ptzName = Unicode_AllocWithUTF8(tzNameByUTCOffset); + } } return winTimeZoneIndex; @@ -1234,15 +1242,11 @@ TimeUtilLoadDate(TimeUtil_Date *d, // IN/OUT /* *---------------------------------------------------------------------- * - * TimeUtilFindIndexByUTCOffset -- + * TimeUtilFindIndexAndNameByUTCOffset -- * * Private function. Scans a table for a given UTC-to-Standard * offset and returns the Windows TZ Index of the first match - * found. - * - * Parameters: - * utcStdOffMins Offset to look for (in minutes) - * <0 is west of PM, >0 is east of PM. + * found together with its Windows TZ Name. * * Results: * Returns Windows TZ Index (>=0) if found, else -1. @@ -1252,96 +1256,110 @@ TimeUtilLoadDate(TimeUtil_Date *d, // IN/OUT * *---------------------------------------------------------------------- */ -static int TimeUtilFindIndexByUTCOffset(int utcStdOffMins) +static int +TimeUtilFindIndexAndNameByUTCOffset(int utcStdOffMins, // IN: offset (in minutes) + const char **ptzName) // OUT: returning TZ Name { static struct _tzinfo { int winTzIndex; + char winTzName[256]; int utcStdOffMins; } TABLE[] = { - { 0, /* "Dateline Standard Time",*/ -720 }, // -12 - { 1, /* "Samoa Standard Time",*/ -660 }, // -11 - { 2, /* "Hawaiian Standard Time",*/ -600 }, // -10 - { 3, /* "Alaskan Standard Time",*/ -540 }, // -9 - { 4, /* "Pacific Standard Time",*/ -480 }, // -8 - { 10, /* "Mountain Standard Time",*/ -420 }, // -7 - { 13, /* "Mexico Standard Time 2",*/ -420 }, // -7 - { 15, /* "U.S. Mountain Standard Time",*/ -420 }, // -7 - { 20, /* "Central Standard Time",*/ -360 }, // -6 - { 25, /* "Canada Central Standard Time",*/ -360 }, // -6 - { 30, /* "Mexico Standard Time",*/ -360 }, // -6 - { 33, /* "Central America Standard Time",*/ -360 }, // -6 - { 35, /* "Eastern Standard Time",*/ -300 }, // -5 - { 40, /* "U.S. Eastern Standard Time",*/ -300 }, // -5 - { 45, /* "S.A. Pacific Standard Time",*/ -300 }, // -5 - { 50, /* "Atlantic Standard Time",*/ -240 }, // -4 - { 55, /* "S.A. Western Standard Time",*/ -240 }, // -4 - { 56, /* "Pacific S.A. Standard Time",*/ -240 }, // -4 - { 60, /* "Newfoundland Standard Time",*/ -210 }, // -3.5 - { 65, /* "E. South America Standard Time",*/-180 }, // -3 - { 70, /* "S.A. Eastern Standard Time",*/ -180 }, // -3 - { 73, /* "Greenland Standard Time",*/ -180 }, // -3 - { 75, /* "Mid-Atlantic Standard Time",*/ -120 }, // -2 - { 80, /* "Azores Standard Time",*/ -60 }, // -1 - { 83, /* "Cape Verde Standard Time",*/ -60 }, // -1 - { 85, /* "GMT Standard Time",*/ 0 }, // 0 - { 90, /* "Greenwich Standard Time",*/ 0 }, // 0 - { 95, /* "Central Europe Standard Time",*/ 60 }, // +1 - { 100, /* "Central European Standard Time",*/ 60 }, // +1 - { 105, /* "Romance Standard Time",*/ 60 }, // +1 - { 110, /* "W. Europe Standard Time",*/ 60 }, // +1 - { 113, /* "W. Central Africa Standard Time",*/ 60 }, // +1 - { 115, /* "E. Europe Standard Time",*/ 120 }, // +2 - { 120, /* "Egypt Standard Time",*/ 120 }, // +2 - { 125, /* "FLE Standard Time",*/ 120 }, // +2 - { 130, /* "GTB Standard Time",*/ 120 }, // +2 - { 135, /* "Israel Standard Time",*/ 120 }, // +2 - { 140, /* "South Africa Standard Time",*/ 120 }, // +2 - { 145, /* "Russian Standard Time",*/ 180 }, // +3 - { 150, /* "Arab Standard Time",*/ 180 }, // +3 - { 155, /* "E. Africa Standard Time",*/ 180 }, // +3 - { 158, /* "Arabic Standard Time",*/ 180 }, // +3 - { 160, /* "Iran Standard Time",*/ 210 }, // +3.5 - { 165, /* "Arabian Standard Time",*/ 240 }, // +4 - { 170, /* "Caucasus Standard Time",*/ 240 }, // +4 - { 175, /* "Afghanistan Standard Time",*/ 270 }, // +4.5 - { 180, /* "Ekaterinburg Standard Time",*/ 300 }, // +5 - { 185, /* "West Asia Standard Time",*/ 300 }, // +5 - { 190, /* "India Standard Time",*/ 330 }, // +5.5 - { 193, /* "Nepal Standard Time",*/ 345 }, // +5.75 - { 195, /* "Central Asia Standard Time",*/ 360 }, // +6 - { 200, /* "Sri Lanka Standard Time",*/ 360 }, // +6 - { 201, /* "N. Central Asia Standard Time",*/ 360 }, // +6 - { 203, /* "Myanmar Standard Time",*/ 390 }, // +6.5 - { 205, /* "S.E. Asia Standard Time",*/ 420 }, // +7 - { 207, /* "North Asia Standard Time",*/ 420 }, // +7 - { 210, /* "China Standard Time",*/ 480 }, // +8 - { 215, /* "Singapore Standard Time",*/ 480 }, // +8 - { 220, /* "Taipei Standard Time",*/ 480 }, // +8 - { 225, /* "W. Australia Standard Time",*/ 480 }, // +8 - { 227, /* "North Asia East Standard Time",*/ 480 }, // +8 - { 230, /* "Korea Standard Time",*/ 540 }, // +9 - { 235, /* "Tokyo Standard Time",*/ 540 }, // +9 - { 240, /* "Yakutsk Standard Time",*/ 540 }, // +9 - { 245, /* "A.U.S. Central Standard Time",*/ 570 }, // +9.5 - { 250, /* "Cen. Australia Standard Time",*/ 570 }, // +9.5 - { 255, /* "A.U.S. Eastern Standard Time",*/ 600 }, // +10 - { 260, /* "E. Australia Standard Time",*/ 600 }, // +10 - { 265, /* "Tasmania Standard Time",*/ 600 }, // +10 - { 270, /* "Vladivostok Standard Time",*/ 600 }, // +10 - { 275, /* "West Pacific Standard Time",*/ 600 }, // +10 - { 280, /* "Central Pacific Standard Time",*/ 660 }, // +11 - { 285, /* "Fiji Islands Standard Time",*/ 720 }, // +12 - { 290, /* "New Zealand Standard Time",*/ 720 }, // +12 - { 300, /* "Tonga Standard Time",*/ 780 }};// +13 - - int tableSize = sizeof(TABLE) / sizeof(TABLE[0]); - int look; + + /* + * These values are from Microsoft's TimeZone documentation: + * + * http://technet.microsoft.com/en-us/library/cc749073.aspx + */ + + { 0, "Dateline Standard Time", -720 }, // -12 + { 1, "Samoa Standard Time", -660 }, // -11 + { 2, "Hawaiian Standard Time", -600 }, // -10 + { 3, "Alaskan Standard Time", -540 }, // -9 + { 4, "Pacific Standard Time", -480 }, // -8 + { 10, "Mountain Standard Time", -420 }, // -7 + { 13, "Mountain Standard Time (Mexico)", -420 }, // -7 + { 15, "US Mountain Standard Time", -420 }, // -7 + { 20, "Central Standard Time", -360 }, // -6 + { 25, "Canada Central Standard Time", -360 }, // -6 + { 30, "Central Standard Time (Mexico)", -360 }, // -6 + { 33, "Central America Standard Time", -360 }, // -6 + { 35, "Eastern Standard Time", -300 }, // -5 + { 40, "US Eastern Standard Time", -300 }, // -5 + { 45, "SA Pacific Standard Time", -300 }, // -5 + { 50, "Atlantic Standard Time", -240 }, // -4 + { 55, "SA Western Standard Time", -240 }, // -4 + { 56, "Pacific SA Standard Time", -240 }, // -4 + { 60, "Newfoundland Standard Time", -210 }, // -3.5 + { 65, "E. South America Standard Time", -180 }, // -3 + { 70, "SA Eastern Standard Time", -180 }, // -3 + { 73, "Greenland Standard Time", -180 }, // -3 + { 75, "Mid-Atlantic Standard Time", -120 }, // -2 + { 80, "Azores Standard Time", -60 }, // -1 + { 83, "Cape Verde Standard Time", -60 }, // -1 + { 85, "GMT Standard Time", 0 }, // 0 + { 90, "Greenwich Standard Time", 0 }, // 0 + { 110, "W. Europe Standard Time", 60 }, // +1 + { 95, "Central Europe Standard Time", 60 }, // +1 + { 100, "Central European Standard Time", 60 }, // +1 + { 105, "Romance Standard Time", 60 }, // +1 + { 113, "W. Central Africa Standard Time", 60 }, // +1 + { 115, "E. Europe Standard Time", 120 }, // +2 + { 120, "Egypt Standard Time", 120 }, // +2 + { 125, "FLE Standard Time", 120 }, // +2 + { 130, "GTB Standard Time", 120 }, // +2 + { 135, "Israel Standard Time", 120 }, // +2 + { 140, "South Africa Standard Time", 120 }, // +2 + { 145, "Russian Standard Time", 180 }, // +3 + { 150, "Arab Standard Time", 180 }, // +3 + { 155, "E. Africa Standard Time", 180 }, // +3 + { 158, "Arabic Standard Time", 180 }, // +3 + { 160, "Iran Standard Time", 210 }, // +3.5 + { 165, "Arabian Standard Time", 240 }, // +4 + { 170, "Caucasus Standard Time", 240 }, // +4 + { 175, "Afghanistan Standard Time", 270 }, // +4.5 + { 180, "Ekaterinburg Standard Time", 300 }, // +5 + { 185, "West Asia Standard Time", 300 }, // +5 + { 190, "India Standard Time", 330 }, // +5.5 + { 193, "Nepal Standard Time", 345 }, // +5.75 + { 195, "Central Asia Standard Time", 360 }, // +6 + { 200, "Sri Lanka Standard Time", 360 }, // +6 + { 201, "N. Central Asia Standard Time", 360 }, // +6 + { 203, "Myanmar Standard Time", 390 }, // +6.5 + { 205, "SE Asia Standard Time", 420 }, // +7 + { 207, "North Asia Standard Time", 420 }, // +7 + { 210, "China Standard Time", 480 }, // +8 + { 215, "Singapore Standard Time", 480 }, // +8 + { 220, "Taipei Standard Time", 480 }, // +8 + { 225, "W. Australia Standard Time", 480 }, // +8 + { 227, "North Asia East Standard Time", 480 }, // +8 + { 230, "Korea Standard Time", 540 }, // +9 + { 235, "Tokyo Standard Time", 540 }, // +9 + { 240, "Yakutsk Standard Time", 540 }, // +9 + { 245, "AUS Central Standard Time", 570 }, // +9.5 + { 250, "Cen. Australia Standard Time", 570 }, // +9.5 + { 255, "AUS Eastern Standard Time", 600 }, // +10 + { 260, "E. Australia Standard Time", 600 }, // +10 + { 265, "Tasmania Standard Time", 600 }, // +10 + { 270, "Vladivostok Standard Time", 600 }, // +10 + { 275, "West Pacific Standard Time", 600 }, // +10 + { 280, "Central Pacific Standard Time", 660 }, // +11 + { 285, "Fiji Standard Time", 720 }, // +12 + { 290, "New Zealand Standard Time", 720 }, // +12 + { 300, "Tonga Standard Time", 780 }};// +13 + + size_t tableSize = ARRAYSIZE(TABLE); + size_t look; int tzIndex = (-1); + *ptzName = NULL; + /* XXX Finds the first match, not necessariy the right match! */ - for (look = 0; look < tableSize && tzIndex < 0; look++) { + for (look = 0; look < tableSize; look++) { if (TABLE[look].utcStdOffMins == utcStdOffMins) { tzIndex = TABLE[look].winTzIndex; + *ptzName = TABLE[look].winTzName; + break; } } diff --git a/open-vm-tools/lib/misc/util_misc.c b/open-vm-tools/lib/misc/util_misc.c index d3fd0d98b..55f6c94cc 100644 --- a/open-vm-tools/lib/misc/util_misc.c +++ b/open-vm-tools/lib/misc/util_misc.c @@ -315,9 +315,11 @@ Bool Util_IsAbsolutePath(const char *path) // IN: path to check { #if defined(__linux__) || defined(__APPLE__) + // path[0] is valid even for the empty string. return path && path[0] == DIRSEPC; #elif defined(_WIN32) - if (!path) { + // if the length is 2, path[2] will be valid because of the null terminator. + if (!path || strlen(path) < 2) { return FALSE; } diff --git a/open-vm-tools/lib/rpcChannel/Makefile.am b/open-vm-tools/lib/rpcChannel/Makefile.am new file mode 100644 index 000000000..96e956a9a --- /dev/null +++ b/open-vm-tools/lib/rpcChannel/Makefile.am @@ -0,0 +1,25 @@ +################################################################################ +### Copyright 2009 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 +################################################################################ + +noinst_LTLIBRARIES = libRpcChannel.la + +libRpcChannel_la_SOURCES = +libRpcChannel_la_SOURCES += bdoorChannel.c +libRpcChannel_la_SOURCES += rpcChannel.c + +libRpcChannel_la_CPPFLAGS = +libRpcChannel_la_CPPFLAGS += @VMTOOLS_CPPFLAGS@ diff --git a/open-vm-tools/lib/rpcChannel/bdoorChannel.c b/open-vm-tools/lib/rpcChannel/bdoorChannel.c new file mode 100644 index 000000000..24879bc2c --- /dev/null +++ b/open-vm-tools/lib/rpcChannel/bdoorChannel.c @@ -0,0 +1,246 @@ +/********************************************************* + * Copyright (C) 2008 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 bdoorChannel.c + * + * Implements a backdoor-based RPC channel. This is based on the + * RpcIn / RpcOut libraries. + */ + +#include "vm_assert.h" +#include "rpcChannelInt.h" +#include "rpcout.h" +#include "util.h" + +/** Max amount of time (in .01s) that the RpcIn loop will sleep for. */ +#define RPCIN_MAX_DELAY 10 + +typedef struct BackdoorChannel { + RpcIn *in; + RpcOut *out; + gboolean inStarted; + gboolean outStarted; +} BackdoorChannel; + + +/** + * Stops a channel, optionally destroying the channel. It's safe to call + * this function more than once. + * + * @param[in] chan The RPC channel instance. + * @param[in] destroy Whether to destroy the channels. + * + * @return TRUE on success. + */ + +static Bool +RpcInStopChannel(RpcChannel *chan, + gboolean destroy) +{ + gboolean ret = TRUE; + BackdoorChannel *bdoor = chan->_private; + + ASSERT(chan->appName != NULL); + if (bdoor->out != NULL) { + if (!bdoor->outStarted || RpcOut_stop(bdoor->out)) { + if (destroy) { + RpcOut_Destruct(bdoor->out); + bdoor->out = NULL; + } + bdoor->outStarted = FALSE; + } else { + ret = FALSE; + } + } else { + ASSERT(!bdoor->outStarted); + } + + if (bdoor->in != NULL) { + if (!bdoor->inStarted || RpcIn_stop(bdoor->in)) { + if (destroy) { + RpcIn_Destruct(bdoor->in); + bdoor->in = NULL; + } + bdoor->inStarted = FALSE; + } else { + ret = FALSE; + } + } else { + ASSERT(!bdoor->inStarted); + } + + return ret; +} + + +/** + * Starts the RpcIn loop and the RpcOut channel. + * + * @param[in] chan The RPC channel instance. + * + * @return TRUE on success. + */ + +static Bool +RpcInStart(RpcChannel *chan) +{ + gboolean ret; + BackdoorChannel *bdoor = chan->_private; + + ASSERT(chan->appName != NULL); + ASSERT(!bdoor->inStarted); + ASSERT(!bdoor->outStarted); + + ret = RpcIn_start(bdoor->in, RPCIN_MAX_DELAY, RpcChannel_Error, chan); + if (ret) { + ret = RpcOut_start(bdoor->out); + if (!ret) { + RpcIn_stop(bdoor->in); + } + } + bdoor->inStarted = TRUE; + bdoor->outStarted = TRUE; + return ret; +} + + +/** + * Shuts down the RpcIn channel. Due to the "split brain" nature of the backdoor, + * if this function fails, it's possible that while the "out" channel was shut + * down the "in" one wasn't, for example, although that's unlikely. + * + * @param[in] chan The RPC channel instance. + * + * @return TRUE on success. + */ + +static Bool +RpcInShutdown(RpcChannel *chan) +{ + return RpcInStopChannel(chan, TRUE); +} + + +/** + * Stops the channel from receiving messages and releases the channel, but + * don't clean up the internal channel state. This allows the channel to be + * restarted later if needed. The "out" channel is also stopped, so sending + * a message on the channel while it's stopped will fail. + * + * @param[in] chan The RPC channel instance. + * + * @return TRUE on success. + */ + +static Bool +RpcInStop(RpcChannel *chan) +{ + return RpcInStopChannel(chan, FALSE); +} + + +/** + * Sends the data using the RpcOut library. + * + * @param[in] chan The RPC channel instance. + * @param[in] data Data to send. + * @param[in] dataLen Number of bytes to send. + * @param[out] result Response from other side. + * @param[out] resultLen Number of bytes in response. + * + * @return The status from the remote end (TRUE if call was successful). + */ + +static Bool +RpcInSend(RpcChannel *chan, + char *data, + size_t dataLen, + char **result, + size_t *resultLen) +{ + gboolean ret; + const char *reply; + size_t replyLen; + BackdoorChannel *bdoor = chan->_private; + + ASSERT(chan->appName != NULL); + + if (!bdoor->outStarted) { + return FALSE; + } + + ret = RpcOut_send(bdoor->out, data, dataLen, &reply, &replyLen); + + /* + * A lot of this logic is just replicated from rpcout.c:RpcOut_SendOneRaw(). + * Look there for comments about a few details. + */ + if (result != NULL) { + if (reply != NULL) { + *result = Util_SafeMalloc(replyLen + 1); + memcpy(*result, reply, replyLen); + (*result)[replyLen] = '\0'; + } else { + *result = NULL; + } + } + + if (resultLen != NULL) { + *resultLen = replyLen; + } + + return ret; +} + + +/** + * Creates a new RpcIn channel. + * + * @param[in] mainCtx The app's main context. + * + * @return A new channel instance (never NULL). + */ + +RpcChannel * +RpcChannel_NewBackdoorChannel(GMainContext *mainCtx) +{ + RpcChannel *ret; + BackdoorChannel *bdoor; + + ret = g_malloc0(sizeof *ret); + + bdoor = g_malloc(sizeof *bdoor); + bdoor->in = RpcIn_Construct(mainCtx, RpcChannel_Dispatch, ret); + ASSERT(bdoor->in != NULL); + + bdoor->out = RpcOut_Construct(); + ASSERT(bdoor->out != NULL); + + bdoor->inStarted = FALSE; + bdoor->outStarted = FALSE; + + ret->start = RpcInStart; + ret->stop = RpcInStop; + ret->send = RpcInSend; + ret->shutdown = RpcInShutdown; + ret->_private = bdoor; + + return ret; +} + diff --git a/open-vm-tools/lib/rpcChannel/rpcChannel.c b/open-vm-tools/lib/rpcChannel/rpcChannel.c new file mode 100644 index 000000000..9cedd0f17 --- /dev/null +++ b/open-vm-tools/lib/rpcChannel/rpcChannel.c @@ -0,0 +1,511 @@ +/********************************************************* + * Copyright (C) 2008 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 rpcChannel.c + * + * Common functions to all RPC channel implementations. + */ + +#include +#include "dynxdr.h" +#include "rpcChannelInt.h" +#include "str.h" +#include "strutil.h" +#include "vmxrpc.h" +#include "xdrutil.h" + +/** Max number of times to attempt a channel restart. */ +#define RPCIN_MAX_RESTARTS 60 + +static Bool +RpcChannelPing(RpcInData *data); + +static RpcChannelCallback gRpcHandlers[] = { + { "ping", RpcChannelPing, NULL, NULL, NULL, 0 } +}; + + +/** + * Handler for a "ping" message. Does nothing. + * + * @param[in] data The RPC data. + * + * @return TRUE. + */ + +static Bool +RpcChannelPing(RpcInData *data) +{ + return RPCIN_SETRETVALS(data, "", TRUE); +} + + +/** + * Callback for restarting the RPC channel. + * + * @param[in] _chan The RPC channel + * + * @return FALSE + */ + +static gboolean +RpcChannelRestart(gpointer _chan) +{ + RpcChannel *chan = _chan; + + if (!chan->stop(chan) || !chan->start(chan)) { + g_warning("Channel reset failed [%d]\n", chan->rpcErrorCount); + if (chan->resetCb != NULL) { + chan->resetCb(chan, FALSE, chan->resetData); + } + } else { + chan->rpcError = FALSE; + } + + return FALSE; +} + + +/** + * Checks and potentially resets the RPC channel. This code is based on the + * toolsDaemon.c function "ToolsDaemon_CheckReset". + * + * @param[in] _chan The RPC channel. + * + * @return FALSE. The reset callback will schedule a new check when it's called. + */ + +static gboolean +RpcChannelCheckReset(gpointer _chan) +{ + static int channelTimeoutAttempts = RPCIN_MAX_RESTARTS; + RpcChannel *chan = _chan; + + /* Check the channel state. */ + if (chan->rpcError) { + GSource *src; + + if (++(chan->rpcErrorCount) > channelTimeoutAttempts) { + g_warning("Failed to reset channel after %u attempts\n", + chan->rpcErrorCount - 1); + if (chan->resetCb != NULL) { + chan->resetCb(chan, FALSE, chan->resetData); + } + goto exit; + } + + /* Schedule the channel restart for 1 sec in the future. */ + g_debug("Resetting channel [%u]\n", chan->rpcErrorCount); + src = g_timeout_source_new(1000); + g_source_set_callback(src, RpcChannelRestart, chan, NULL); + g_source_attach(src, chan->mainCtx); + g_source_unref(src); + goto exit; + } + + /* Reset was successful. */ + g_debug("Channel was reset successfully.\n"); + chan->rpcErrorCount = 0; + + if (chan->resetCb != NULL) { + chan->resetCb(chan, TRUE, chan->resetData); + } + +exit: + g_source_unref(chan->resetCheck); + chan->resetCheck = NULL; + return FALSE; +} + + +/** + * Handles an RPC reset. Calls the reset callback of all loaded plugins. + * + * @param[in] data The RPC data. + * + * @return TRUE. + */ + +static Bool +RpcChannelReset(RpcInData *data) +{ + gchar *msg; + RpcChannel *chan = data->clientData; + + g_assert(chan->resetCheck == NULL); + chan->resetCheck = g_idle_source_new(); + g_source_set_callback(chan->resetCheck, RpcChannelCheckReset, chan, NULL); + g_source_attach(chan->resetCheck, chan->mainCtx); + + msg = Str_Asprintf(NULL, "ATR %s", chan->appName); + ASSERT_MEM_ALLOC(msg); + data->freeResult = TRUE; + return RPCIN_SETRETVALS(data, msg, TRUE); +} + + +/** + * A wrapper for standard RPC callback functions which provides automatic + * XDR serialization / deserialization if requested by the application. + * + * @param[in] data RpcIn data. + * @param[in] rpc The RPC registration data. + * + * @return Whether the RPC was handled successfully. + */ + +static Bool +RpcChannelXdrWrapper(RpcInData *data, + RpcChannelCallback *rpc) +{ + Bool ret; + RpcInData copy; + void *xdrData = NULL; + + if (rpc->xdrIn != NULL) { + xdrData = malloc(rpc->xdrInSize); + if (xdrData == NULL) { + ret = RPCIN_SETRETVALS(data, "Out of memory.", FALSE); + goto exit; + } + + memset(xdrData, 0, rpc->xdrInSize); + if (!XdrUtil_Deserialize(data->args + 1, data->argsSize, rpc->xdrIn, xdrData)) { + ret = RPCIN_SETRETVALS(data, "XDR deserialization failed.", FALSE); + free(xdrData); + goto exit; + } + + copy.name = data->name; + copy.args = xdrData; + copy.argsSize = rpc->xdrInSize; + copy.result = data->result; + copy.resultLen = data->resultLen; + copy.freeResult = data->freeResult; + copy.appCtx = data->appCtx; + copy.clientData = rpc->clientData; + } else { + memcpy(©, data, sizeof copy); + } + + ret = rpc->callback(©); + + if (rpc->xdrIn != NULL) { + VMX_XDR_FREE(rpc->xdrIn, xdrData); + free(xdrData); + copy.args = NULL; + data->result = copy.result; + data->resultLen = copy.resultLen; + data->freeResult = copy.freeResult; + } + + if (rpc->xdrOut != NULL && copy.result != NULL) { + XDR xdrs; + xdrproc_t xdrProc = rpc->xdrOut; + + if (DynXdr_Create(&xdrs) == NULL) { + ret = RPCIN_SETRETVALS(data, "Out of memory.", FALSE); + goto exit; + } + + if (!xdrProc(&xdrs, copy.result)) { + ret = RPCIN_SETRETVALS(data, "XDR serialization failed.", FALSE); + DynXdr_Destroy(&xdrs, TRUE); + goto exit; + } + + if (copy.freeResult) { + VMX_XDR_FREE(rpc->xdrOut, copy.result); + } + data->result = DynXdr_Get(&xdrs); + data->resultLen = XDR_GETPOS(&xdrs); + data->freeResult = TRUE; + DynXdr_Destroy(&xdrs, FALSE); + } + +exit: + if (copy.freeResult && copy.result != NULL) { + g_free(copy.result); + } + return ret; +} + + +/** + * Builds an "rpcout" command to send a XDR struct. + * + * @param[in] cmd The command name. + * @param[in] xdrProc Function to use for serializing the XDR struct. + * @param[in] xdrData The XDR struct to serialize. + * @param[out] result Where to store the serialized data. + * @param[out] resultLen Where to store the serialized data length. + * + * @return Whether successfully built the command. + */ + +Bool +RpcChannel_BuildXdrCommand(const char *cmd, + void *xdrProc, + void *xdrData, + char **result, + size_t *resultLen) +{ + Bool ret = FALSE; + xdrproc_t proc = xdrProc; + XDR xdrs; + + if (DynXdr_Create(&xdrs) == NULL) { + return FALSE; + } + + if (!DynXdr_AppendRaw(&xdrs, cmd, strlen(cmd))) { + goto exit; + } + + if (!DynXdr_AppendRaw(&xdrs, " ", 1)) { + goto exit; + } + + if (!proc(&xdrs, xdrData)) { + goto exit; + } + + *result = DynXdr_Get(&xdrs); + *resultLen = xdr_getpos(&xdrs); + + ret = TRUE; + +exit: + DynXdr_Destroy(&xdrs, !ret); + return ret; +} + + +/** + * Dispatches the given RPC to the registered handler. This mimics the behavior + * of the RpcIn library (but is not tied to that particular implementation of + * an RPC channel). + * + * @param[in,out] data The RPC data. + * + * @return Whether the RPC was handled successfully. + */ + +Bool +RpcChannel_Dispatch(RpcInData *data) +{ + char *name = NULL; + unsigned int index = 0; + size_t nameLen; + Bool status; + RpcChannelCallback *rpc = NULL; + RpcChannel *chan = data->clientData; + + name = StrUtil_GetNextToken(&index, data->args, " "); + if (name == NULL) { + status = RPCIN_SETRETVALS(data, "Bad command", FALSE); + goto exit; + } + + if (chan->rpcs != NULL) { + rpc = g_hash_table_lookup(chan->rpcs, name); + } + + if (rpc == NULL) { + status = RPCIN_SETRETVALS(data, "Unknown Command", FALSE); + goto exit; + } + + /* Adjust the RPC arguments. */ + nameLen = strlen(name); + data->name = name; + data->args = data->args + nameLen; + data->argsSize -= nameLen; + data->appCtx = chan->appCtx; + data->clientData = rpc->clientData; + + if (rpc->xdrIn != NULL || rpc->xdrOut != NULL) { + status = RpcChannelXdrWrapper(data, rpc); + } else { + status = rpc->callback(data); + } + + g_assert(data->result != NULL); + +exit: + data->name = NULL; + free(name); + return status; +} + + +/** + * Shuts down an RPC channel and release any held resources. + * + * @param[in] chan The RPC channel. + * + * @return Whether the channel was shut down successfully. + */ + +gboolean +RpcChannel_Destroy(RpcChannel *chan) +{ + size_t i; + + if (chan->shutdown != NULL && !chan->shutdown(chan)) { + return FALSE; + } + + RpcChannel_UnregisterCallback(chan, &chan->resetReg); + for (i = 0; i < ARRAYSIZE(gRpcHandlers); i++) { + RpcChannel_UnregisterCallback(chan, &gRpcHandlers[i]); + } + + if (chan->rpcs != NULL) { + g_hash_table_destroy(chan->rpcs); + chan->rpcs = NULL; + } + + chan->resetCb = NULL; + chan->resetData = NULL; + chan->appCtx = NULL; + + g_free(chan->appName); + chan->appName = NULL; + + g_main_context_unref(chan->mainCtx); + chan->mainCtx = NULL; + + if (chan->resetCheck != NULL) { + g_source_destroy(chan->resetCheck); + chan->resetCheck = NULL; + } + + g_free(chan); + return TRUE; +} + + +/** + * Error handling function for the RPC channel. Enqueues the "check reset" + * function for running later, if it's not yet enqueued. + * + * @param[in] _chan The RPC channel. + * @param[in] status Error description. + */ + +void +RpcChannel_Error(void *_chan, + char const *status) +{ + RpcChannel *chan = _chan; + chan->rpcError = TRUE; + g_warning("Error in the RPC receive loop: %s.\n", status); + + if (chan->resetCheck == NULL) { + chan->resetCheck = g_idle_source_new(); + g_source_set_callback(chan->resetCheck, RpcChannelCheckReset, chan, NULL); + g_source_attach(chan->resetCheck, chan->mainCtx); + } +} + + +/** + * Initializes the RPC channel for use. This function must be called before + * starting the channel. + * + * @param[in] chan The RPC channel. + * @param[in] appName TCLO application name. + * @param[in] mainCtx Application event context. + * @param[in] appCtx Application context. + * @param[in] resetCb Callback for when a reset occurs. + * @param[in] resetData Client data for the reset callback. + */ + +void +RpcChannel_Setup(RpcChannel *chan, + const gchar *appName, + GMainContext *mainCtx, + gpointer appCtx, + RpcChannelResetCb resetCb, + gpointer resetData) +{ + size_t i; + + chan->appName = g_strdup(appName); + chan->appCtx = appCtx; + chan->mainCtx = g_main_context_ref(mainCtx); + chan->resetCb = resetCb; + chan->resetData = resetData; + + chan->resetReg.name = "reset"; + chan->resetReg.callback = RpcChannelReset; + chan->resetReg.clientData = chan; + + /* Register the callbacks handled by the rpcChannel library. */ + RpcChannel_RegisterCallback(chan, &chan->resetReg); + + for (i = 0; i < ARRAYSIZE(gRpcHandlers); i++) { + RpcChannel_RegisterCallback(chan, &gRpcHandlers[i]); + } +} + + +/** + * Registers a new RPC handler in the given RPC channel. This function is + * not thread-safe. + * + * @param[in] chan The channel instance. + * @param[in] rpc Info about the RPC being registered. + */ + +void +RpcChannel_RegisterCallback(RpcChannel *chan, + RpcChannelCallback *rpc) +{ + g_assert(rpc->name != NULL && strlen(rpc->name) > 0); + g_assert(rpc->callback); + g_assert(rpc->xdrIn == NULL || rpc->xdrInSize > 0); + if (chan->rpcs == NULL) { + chan->rpcs = g_hash_table_new(g_str_hash, g_str_equal); + } + if (g_hash_table_lookup(chan->rpcs, rpc->name) != NULL) { + g_error("Trying to overwrite existing RPC registration for %s!\n", rpc->name); + } + g_hash_table_insert(chan->rpcs, (gpointer) rpc->name, rpc); +} + + +/** + * Unregisters a new RPC handler from the given RPC channel. This function is + * not thread-safe. + * + * @param[in] chan The channel instance. + * @param[in] rpc Info about the RPC being unregistered. + */ + +void +RpcChannel_UnregisterCallback(RpcChannel *chan, + RpcChannelCallback *rpc) +{ + if (chan->rpcs != NULL) { + g_hash_table_remove(chan->rpcs, rpc->name); + } +} + diff --git a/open-vm-tools/lib/rpcChannel/rpcChannelInt.h b/open-vm-tools/lib/rpcChannel/rpcChannelInt.h new file mode 100644 index 000000000..f58f0465b --- /dev/null +++ b/open-vm-tools/lib/rpcChannel/rpcChannelInt.h @@ -0,0 +1,35 @@ +/********************************************************* + * Copyright (C) 2008 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 _RPCCHANNELINT_H_ +#define _RPCCHANNELINT_H_ + +/** + * @file rpcChannelInt.h + * + * Internal definitions for the RPC channel library. + */ + +#include "rpcChannel.h" + +void +RpcChannel_Error(void *_state, + char const *status); + +#endif /* _RPCCHANNELINT_H_ */ + diff --git a/open-vm-tools/lib/stubs/Makefile.am b/open-vm-tools/lib/stubs/Makefile.am index 2965aa9a1..c7c7ccaaf 100644 --- a/open-vm-tools/lib/stubs/Makefile.am +++ b/open-vm-tools/lib/stubs/Makefile.am @@ -24,12 +24,10 @@ libStubs_la_SOURCES += stub-warning.c libStubs_la_SOURCES += stub-user-msg.c libStubs_la_SOURCES += stub-user-panic.c -if ENABLE_CORE_SERVICES noinst_LTLIBRARIES += libStubsCS.la libStubsCS_la_SOURCES = libStubsCS_la_SOURCES += stub-config.c libStubsCS_la_SOURCES += stub-user-msg.c libStubsCS_la_SOURCES += stub-user-panic.c -endif diff --git a/open-vm-tools/lib/system/systemLinux.c b/open-vm-tools/lib/system/systemLinux.c index d57d478f9..8c5327e7b 100644 --- a/open-vm-tools/lib/system/systemLinux.c +++ b/open-vm-tools/lib/system/systemLinux.c @@ -658,8 +658,8 @@ System_IsUserAdmin(void) */ char * -System_GetEnv(Bool global, // IN - char *valueName) // IN: UTF-8 +System_GetEnv(Bool global, // IN + const char *valueName) // IN: UTF-8 { char *result; @@ -698,8 +698,8 @@ System_GetEnv(Bool global, // IN int System_SetEnv(Bool global, // IN - char *valueName, // IN: UTF-8 - char *value) // IN: UTF-8 + const char *valueName, // IN: UTF-8 + const char *value) // IN: UTF-8 { #if defined(sun) return(-1); diff --git a/open-vm-tools/lib/unity/unityPlatformX11Window.c b/open-vm-tools/lib/unity/unityPlatformX11Window.c index 46f8e7333..839f3f570 100644 --- a/open-vm-tools/lib/unity/unityPlatformX11Window.c +++ b/open-vm-tools/lib/unity/unityPlatformX11Window.c @@ -452,6 +452,15 @@ UPWindowSetWindows(UnityPlatform *up, // IN XSelectInput(up->display, upw->clientWindow, 0); HashTable_Delete(up->allWindows, GUINT_TO_POINTER(upw->clientWindow)); } +#if defined(VM_HAVE_X11_SHAPE_EXT) + if (up->shapeEventBase) { + XShapeSelectInput(up->display, upw->toplevelWindow, 0); + + if (upw->clientWindow) { + XShapeSelectInput(up->display, upw->clientWindow, 0); + } + } +#endif /* * Okay, now we may have two UnityPlatformWindows running around, one each for @@ -490,6 +499,10 @@ UPWindowSetWindows(UnityPlatform *up, // IN #if defined(VM_HAVE_X11_SHAPE_EXT) if (up->shapeEventBase) { XShapeSelectInput(up->display, toplevelWindow, ShapeNotifyMask); + + if (upw->clientWindow) { + XShapeSelectInput(up->display, upw->clientWindow, ShapeNotifyMask); + } } #endif @@ -1711,7 +1724,6 @@ UnityPlatformArgvToWindowPaths(UnityPlatform *up, // IN int numQueryArgs; int i; int err; - char *ctmp = NULL; char **argv; char *windowQueryString = NULL; char *execQueryString = NULL; @@ -1736,6 +1748,7 @@ UnityPlatformArgvToWindowPaths(UnityPlatform *up, // IN } if (argv[0][0] != '/') { + char *ctmp = NULL; if ((ctmp = AppUtil_CanonicalizeAppName(argv[0], cwd))) { char **newArgv; newArgv = alloca(argc * sizeof argv[0]); @@ -2532,6 +2545,31 @@ UPWindowUpdateShape(UnityPlatform *up, // IN XFree(rects); rects = NULL; rectCount = 0; + /* + * The X Shape Extension operates on unshaped windows by using default bounding + * and clipping regions. I.e., XShapeGetRectangles will return a single rectangle + * for an unshaped window. Without the following test, we'd end up informing the + * Unity client that this window can be described by a single rectangle region, + * and it'd expect further region updates when the window's geometry changes. + * + * This wouldn't be a problem, except we don't receive XShapeEvents on these + * windows when they're resized. To work around this, we make sure that the UWT + * knows that unshaped windows are exactly that. + */ + { + int bShaped; + int cShaped; + int dummy; + + XShapeQueryExtents(up->display, upw->toplevelWindow, + &bShaped, &dummy, &dummy, &dummy, &dummy, + &cShaped, &dummy, &dummy, &dummy, &dummy); + if (!bShaped && !cShaped) { + UnityWindowTracker_ChangeWindowRegion(up->tracker, upw->toplevelWindow, 0); + return; + } + } + UnityPlatformResetErrorCount(up); /* diff --git a/open-vm-tools/lib/vixTools/vixTools.c b/open-vm-tools/lib/vixTools/vixTools.c index 87df639f9..8507f590a 100644 --- a/open-vm-tools/lib/vixTools/vixTools.c +++ b/open-vm-tools/lib/vixTools/vixTools.c @@ -811,7 +811,8 @@ VixTools_GetToolsPropertiesImpl(GuestApp_Dict **confDictRef, // IN #else osFamily = GUEST_OS_FAMILY_LINUX; #endif - if (!(GuestInfo_GetOSName(sizeof osNameFull, sizeof osName, osNameFull, osName))) { + if (!(Hostinfo_GetOSName(sizeof osNameFull, sizeof osName, osNameFull, + osName))) { osNameFull[0] = 0; osName[0] = 0; } diff --git a/open-vm-tools/lib/vmCheck/vmcheck.c b/open-vm-tools/lib/vmCheck/vmcheck.c index d35afa828..2b61e4415 100644 --- a/open-vm-tools/lib/vmCheck/vmcheck.c +++ b/open-vm-tools/lib/vmCheck/vmcheck.c @@ -206,7 +206,7 @@ VmCheck_IsVirtualWorld(void) if (version != VERSION_MAGIC) { Debug("The version of this program is incompatible with your %s.\n" "For information on updating your VMware Tools please see\n" - "http://www.vmware.com/support/reference/common/info_tools.html.\n" + "http://www.vmware.com/info?id=99\n" "\n", PRODUCT_LINE_NAME); return FALSE; } diff --git a/open-vm-tools/lib/wiper/wiperPosix.c b/open-vm-tools/lib/wiper/wiperPosix.c index bd23c2821..eb7ae956b 100644 --- a/open-vm-tools/lib/wiper/wiperPosix.c +++ b/open-vm-tools/lib/wiper/wiperPosix.c @@ -180,16 +180,17 @@ static INLINE Bool WiperIsDiskDevice(MNTINFO *mnt, // IN: file system being considered struct stat *s) // IN: stat(2) info of fs source { - int major; + int majorN; - major = (s->st_rdev >> 8) & 0xFF; - if (! ( (major == 3) /* First MFM, RLL and IDE hard disk/CD-ROM + majorN = major(s->st_rdev); + if (! ( (majorN == 3) /* First MFM, RLL and IDE hard disk/CD-ROM interface. Inside a VM, this is simply the First IDE hard disk/CD-ROM interface because we don't support others */ - || (major == 22) /* Second IDE hard disk/CD-ROM interface */ - || (major == 8) /* SCSI disk devices */ - || (major == 43) /* Network block device */)) { + || (majorN == 22) /* Second IDE hard disk/CD-ROM interface */ + || (majorN == 8) /* SCSI disk devices */ + || (majorN == 43) /* Network block device */ + || (majorN == 259) /* Disks in 2.6.27 */)) { return FALSE; } diff --git a/open-vm-tools/libguestlib/vmGuestLib.c b/open-vm-tools/libguestlib/vmGuestLib.c index d9cbf1c4f..0481b2584 100644 --- a/open-vm-tools/libguestlib/vmGuestLib.c +++ b/open-vm-tools/libguestlib/vmGuestLib.c @@ -1252,3 +1252,61 @@ VMGuestLib_GetResourcePoolPath(VMGuestLibHandle handle, // IN return error; } + + +/* + *----------------------------------------------------------------------------- + * + * VMGuestLib_GetCpuStolenMs -- + * + * Retrieve CPU stolen. + * + * Results: + * VMGuestLibError + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +VMGuestLibError +VMGuestLib_GetCpuStolenMs(VMGuestLibHandle handle, // IN + uint64 *cpuStolenMs) // OUT +{ + VMGuestLibError error = VMGUESTLIB_ERROR_OTHER; + VMGUESTLIB_GETSTAT_V3(handle, error, + cpuStolenMs, cpuStolenMs, + GUESTLIB_CPU_STOLEN_MS); + return error; +} + + +/* + *----------------------------------------------------------------------------- + * + * VMGuestLib_GetMemTargetSizeMB -- + * + * Retrieve Memory Target Size. + * + * Results: + * VMGuestLibError + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +VMGuestLibError +VMGuestLib_GetMemTargetSizeMB(VMGuestLibHandle handle, // IN + uint64 *memTargetSizeMB) // OUT +{ + VMGuestLibError error = VMGUESTLIB_ERROR_OTHER; + VMGUESTLIB_GETSTAT_V3(handle, error, + memTargetSizeMB, memTargetSizeMB, + GUESTLIB_MEM_TARGET_SIZE_MB); + return error; +} + + diff --git a/open-vm-tools/libvmtools/Makefile.am b/open-vm-tools/libvmtools/Makefile.am index b665b1f42..9e5cafe0f 100644 --- a/open-vm-tools/libvmtools/Makefile.am +++ b/open-vm-tools/libvmtools/Makefile.am @@ -26,13 +26,19 @@ libvmtools_la_LIBADD += ../lib/err/libErr.la libvmtools_la_LIBADD += ../lib/file/libFile.la libvmtools_la_LIBADD += ../lib/guestApp/libGuestApp.la libvmtools_la_LIBADD += ../lib/guestInfo/libGuestInfoCS.la +libvmtools_la_LIBADD += ../lib/guestRpc/libGuestRpc.la libvmtools_la_LIBADD += ../lib/hgfs/libHgfs.la libvmtools_la_LIBADD += ../lib/message/libMessage.la libvmtools_la_LIBADD += ../lib/misc/libMisc.la +libvmtools_la_LIBADD += ../lib/netUtil/libNetUtil.la libvmtools_la_LIBADD += ../lib/panic/libPanic.la +libvmtools_la_LIBADD += ../lib/procMgr/libProcMgr.la +libvmtools_la_LIBADD += ../lib/rpcChannel/libRpcChannel.la libvmtools_la_LIBADD += ../lib/rpcOut/libRpcOut.la +libvmtools_la_LIBADD += ../lib/rpcVmx/libRpcVmx.la libvmtools_la_LIBADD += ../lib/string/libString.la libvmtools_la_LIBADD += ../lib/sync/libSync.la +libvmtools_la_LIBADD += ../lib/syncDriver/libSyncDriver.la libvmtools_la_LIBADD += ../lib/system/libSystem.la libvmtools_la_LIBADD += ../lib/stubs/libStubsCS.la libvmtools_la_LIBADD += ../lib/unicode/libUnicode.la diff --git a/open-vm-tools/libvmtools/vmtoolsConfig.c b/open-vm-tools/libvmtools/vmtoolsConfig.c index 1711a2f87..dfe26d063 100644 --- a/open-vm-tools/libvmtools/vmtoolsConfig.c +++ b/open-vm-tools/libvmtools/vmtoolsConfig.c @@ -124,6 +124,7 @@ VMToolsConfigUpgrade(GuestApp_Dict *old, { CONFNAME_REBOOT, "vmsvc", CONFNAME_REBOOT, CFG_STRING, NULL }, /* HGFS options. */ { CONFNAME_MOUNT_POINT, "hgfs", CONFNAME_MOUNT_POINT, CFG_STRING, NULL }, +#else /* Tray options. */ { CONFNAME_SHOW_WIRELESS_ICON, "vmtray", CONFNAME_SHOW_WIRELESS_ICON, CFG_BOOLEAN, NULL }, #endif diff --git a/open-vm-tools/libvmtools/vmtoolsLog.c b/open-vm-tools/libvmtools/vmtoolsLog.c index b4bbcb25d..40dd1748a 100644 --- a/open-vm-tools/libvmtools/vmtoolsLog.c +++ b/open-vm-tools/libvmtools/vmtoolsLog.c @@ -213,13 +213,16 @@ VMToolsLogFormat(const gchar *message, * we detect whether the original message already had a new line, and * remove it, to avoid having two newlines when printing our log messages. */ - if (msgCurr[len - 2] == '\n') { + if (msgCurr != NULL && msgCurr[len - 2] == '\n') { msgCurr[len - 1] = '\0'; } } - free(msg); - return msgCurr; + if (msgCurr != NULL) { + free(msg); + return msgCurr; + } + return msg; } diff --git a/open-vm-tools/m4/COPYING b/open-vm-tools/m4/COPYING new file mode 100644 index 000000000..09f465ab7 --- /dev/null +++ b/open-vm-tools/m4/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/open-vm-tools/m4/vmtools.m4 b/open-vm-tools/m4/vmtools.m4 new file mode 100644 index 000000000..bff29c2b6 --- /dev/null +++ b/open-vm-tools/m4/vmtools.m4 @@ -0,0 +1,210 @@ +################################################################################ +### Copyright 2009 VMware, Inc. All rights reserved. +### +### VMware-specific macros for use with autoconf. +### +### +### 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 +################################################################################ + +# +# AC_VMW_CHECK_LIB(library, lvar, pkgname, lconfig, version, header, function, +# [action-if-found], +# [action-if-not-found]) +# +# Checks for the existence of a library using three different methods, in the +# following order: +# +# - user defined CUSTOM_(LIB)_CPPFLAGS and CUSTOM_(LIB)_LIBS variables. +# - pkg-config +# - the library's custom "config" +# +# If a library is successfully detected, the (LIB)_CPPFLAGS and (LIB)_LIBS +# variables are set to contain the appropriate flags, and these variables are +# exported using AC_SUBST. The parameters to the macro are: +# +# library ($1): the library name; this is used for testing whether we can link +# to the library (with AC_CHECK_LIB), and also to modify the user +# provided CUSTOM_(LIB)_LIBS variable to make sure the library is +# available in the linker flags. +# lvar ($2): root name of the variables holding the libraries CPPFLAGS / LIBS; +# e.g., FOO means that FOO_CPPFLAGS will be set, and CUSTOM_FOO_CPPFLAGS +# used with AC_CHECK_HEADER. +# pkgname ($3): pkg-config name of the library. +# lconfig ($4): library's custom "config" program for figuring out compiler and +# linker flags (optional). +# version ($5): minimum version of the library when using pkg-config (optional). +# header ($6): header file (when not using pkg-config; see AC_CHECK_HEADER; optional). +# function ($7): function (when not using pkg-config; see AC_CHECK_LIB; optional). +# action-if-found ($8): what to execute when successfully found the library. +# action-if-not-found ($9): what to execute when the library was not found. +# +AC_DEFUN([AC_VMW_CHECK_LIB],[ + AC_REQUIRE([AC_CHECK_LIB]) dnl + AC_REQUIRE([AC_CHECK_HEADER]) dnl + + if test -z "$1"; then + AC_MSG_ERROR(['library' parameter is required.']) + fi + if test -z "$2"; then + AC_MSG_ERROR(['lvar' parameter is required.']) + fi + + ac_vmw_have_lib=0 + ac_vmw_have_lib_func=0 + ac_vmw_have_lib_header=0 + + # + # First, try any user-defined CUSTOM_* flags. + # + if test -n "${CUSTOM_$2_CPPFLAGS}" || test -n "${CUSTOM_$2_LIBS}"; then + CUSTOM_$2_LIBS="${CUSTOM_$2_LIBS} -l$1" + if test -n "$6"; then + ORIGINAL_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="${CUSTOM_$2_CPPFLAGS} $CPPFLAGS" + + AC_CHECK_HEADER([$6], + [ac_vmw_have_lib_header=1]) + + CPPFLAGS="$ORIGINAL_CPPFLAGS" + else + ac_vmw_have_lib_header=1 + fi + + # Check a specific function in the library if requested. + # If it hasn't, just pick a random function from libc, just to make + # sure the linker can find the library being tested. + if test $ac_vmw_have_lib_header -eq 1; then + if test -n "$7"; then + ac_vmw_function=$7 + else + ac_vmw_function=strlen + fi + AC_CHECK_LIB( + [$1], + [$ac_vmw_function], + [ac_vmw_have_lib_func=1], + [], + [${CUSTOM_$2_LIBS}]) + fi + + if test $ac_vmw_have_lib_func -eq 1 && test $ac_vmw_have_lib_header -eq 1; then + $2_CPPFLAGS="${CUSTOM_$2_CPPFLAGS}" + $2_LIBS="${CUSTOM_$2_LIBS}" + ac_vmw_have_lib=1 + fi + fi + + # If that didn't work, try with pkg-config. + if test $ac_vmw_have_lib -eq 0 && test "$HAVE_PKG_CONFIG" = "yes" && test -n "$3"; then + if test -n "$5"; then + AC_MSG_CHECKING([for $3 >= $5 (via pkg-config)]) + if pkg-config --exists '$3 >= $5'; then + ac_vmw_have_lib=1 + fi + else + AC_MSG_CHECKING([for $3 (via pkg-config)]) + if pkg-config --exists '$3'; then + ac_vmw_have_lib=1 + fi + fi + + if test $ac_vmw_have_lib -eq 1; then + # Sometimes pkg-config might fail; for example, "pkg-config gtk+-2.0 --cflags" + # fails on OpenSolaris B71. So be pessimistic. + ac_vmw_cppflags="`pkg-config --cflags $3`" + ac_vmw_ret1=$? + ac_vmw_libs="`pkg-config --libs $3`" + ac_vmw_ret2=$? + if test $ac_vmw_ret1 -eq 0 && test $ac_vmw_ret2 -eq 0; then + AC_MSG_RESULT([yes]) + $2_CPPFLAGS="$ac_vmw_cppflags" + $2_LIBS="$ac_vmw_libs" + else + AC_MSG_RESULT([no]) + fi + else + AC_MSG_RESULT([no]) + fi + fi + + # If we still haven't found the lib, try with the library's custom "config" script. + # Before checking, flush the AC_PATH_PROG cached variable. + unset ac_cv_path_ac_vmw_lib_cfg + unset ac_vmw_lib_cfg + if test $ac_vmw_have_lib -eq 0 && test -n "$4"; then + AC_PATH_PROG([ac_vmw_lib_cfg], [$4], [no]) + if test "$ac_vmw_lib_cfg" != "no"; then + # XXX: icu-config does not follow the "--cflags" and "--libs" convention, + # so single it out here to avoid having to replicate all the rest of the + # logic elsewhere. + if test `basename "$ac_vmw_lib_cfg"` = "icu-config"; then + $2_CPPFLAGS="`$ac_vmw_lib_cfg --cppflags`" + $2_LIBS="`$ac_vmw_lib_cfg --ldflags`" + else + $2_CPPFLAGS="`$ac_vmw_lib_cfg --cflags`" + $2_LIBS="`$ac_vmw_lib_cfg --libs`" + fi + ac_vmw_have_lib=1 + fi + fi + + # Finish by executing the user provided action. The call to "true" is needed + # because the actions are optional, and we need something inside the block. + if test $ac_vmw_have_lib -eq 1; then + AC_SUBST([$2_CPPFLAGS]) + AC_SUBST([$2_LIBS]) + true + $8 + else + true + $9 + fi +]) + + +# +# AC_VMW_CHECK_LIBXX(library, lvar, pkgname, lconfig, version, header, function, +# [action-if-found], +# [action-if-not-found]) +# +# Similar to AC_VMW_CHECK_LIB, but for C++ libraries. +# +# XXX: Getting automake to choose between the C linker and the C++ linker +# depending on whether we're linking any C++ library was a royal pain in the +# ass. The classic way to do this is to define an optional source file for a +# program with an extension of .cxx, using nodist_EXTRA_fooprogram_SOURCES. This +# causes automake's linker detection algorithm to see a C++ source file and +# automatically set up the C++ linker and link line for us. Unfortunately, said +# linker detection doesn't obey conditionals, which means that it'd always pick +# the C++ linker, regardless of whether it's linking to a C++ library or not. +# Instead, we are forced to manually set the correct linker in fooprogram_LINK. +# However, since none of our programs actually contain C++ code, automake +# doesn't make the CXXLINK variable (which contains the linker as well as all +# link flags) available to us, so we must hard-code the entire link line into +# fooprogram_LINK. Not exactly a futureproof solution... +# +# Additional references on this problem: +# http://sources.redhat.com/ml/automake/1999-10/msg00101.html +# http://lists.gnu.org/archive/html/bug-automake/2008-04/msg00010.html +# http://www.gnu.org/software/automake/manual/automake.html#Libtool-Convenience-Libraries +# http://www.gnu.org/software/automake/manual/automake.html#C_002b_002b-Support +# +AC_DEFUN([AC_VMW_CHECK_LIBXX],[ + AC_REQUIRE([AC_VMW_CHECK_LIB]) + AC_LANG_PUSH([C++]) + AC_VMW_CHECK_LIB([$1], [$2], [$3], [$4], [$5], [$6], [$7], [$8], [$9]) + AC_LANG_POP([C++]) +]) + diff --git a/open-vm-tools/modules/Makefile.am b/open-vm-tools/modules/Makefile.am index 41e16582b..ab996be72 100644 --- a/open-vm-tools/modules/Makefile.am +++ b/open-vm-tools/modules/Makefile.am @@ -6,7 +6,8 @@ modules: $(MODULES) modulesrc = $(top_srcdir)/modules $(MODULES): - $(MAKE) VM_UNAME=$(KERNEL_RELEASE) $(EXTRA_ARGS) -C "$(modulesrc)/$(MODULES_OS)/$@" + $(MAKE) VM_UNAME=$(KERNEL_RELEASE) MV=mv RM=rm $(EXTRA_ARGS) \ + -C "$(modulesrc)/$(MODULES_OS)/$@" if LINUX export pvscsidir := $(MODULES_DIR)/drivers/scsi @@ -23,14 +24,6 @@ export DEPMOD := depmod -a endif WITH_ROOT_PRIVILEGES endif LINUX -if FREEBSD -export vmblockdir := $(MODULES_DIR) -export vmhgfsdir := $(MODULES_DIR) -export vmmemctldir := $(MODULES_DIR) -export vmsyncdir := $(MODULES_DIR) -export vmxnetdir := $(MODULES_DIR) -endif FREEBSD - if SOLARIS export vmhgfsdir := $(MODULES_DIR) # Solaris does not have Kbuild-like system so we need to supply @@ -48,19 +41,27 @@ endif clean-local: for MOD in $(MODULES); do \ if [ -d "$(modulesrc)/$(MODULES_OS)/$$MOD" ]; then \ - $(MAKE) VM_UNAME=$(KERNEL_RELEASE) \ + $(MAKE) VM_UNAME=$(KERNEL_RELEASE) MV=mv RM=rm \ -C "$(modulesrc)/$(MODULES_OS)/$$MOD" clean || exit 1; \ fi \ done - $(RM) -f $(modulesrc)/$(MODULES_OS)/*.o $(modulesrc)/$(MODULES_OS)/*.ko + rm -f $(modulesrc)/$(MODULES_OS)/*.o $(modulesrc)/$(MODULES_OS)/*.ko install-exec-hook: if SOLARIS for MOD in $(MODULES); do \ - $(MAKE) VM_UNAME=$(KERNEL_RELEASE) \ + $(MAKE) VM_UNAME=$(KERNEL_RELEASE) MV=mv RM=rm \ -C "$(modulesrc)/$(MODULES_OS)/$$MOD" install || exit 1; \ done -else +endif +if FREEBSD + for MOD in $(MODULES); do \ + $(INSTALL) -d $(DESTDIR)$(MODULES_DIR); \ + $(INSTALL) -m644 $(modulesrc)/$(MODULES_OS)/$$MOD.ko \ + $(DESTDIR)$(MODULES_DIR); \ + done +endif +if LINUX for MOD in $(MODULES); do \ $(INSTALL) -d $(DESTDIR)`eval echo '$$'$${MOD}dir`; \ $(INSTALL) -m644 $(modulesrc)/$(MODULES_OS)/$$MOD/$$MOD.ko \ @@ -71,6 +72,6 @@ endif uninstall-hook: for MOD in $(MODULES); do \ - $(RM) -f $(DESTDIR)`eval echo '$$'$${MOD}dir`/$$MOD.ko &> /dev/null;\ + rm -f $(DESTDIR)`eval echo '$$'$${MOD}dir`/$$MOD.ko &> /dev/null;\ done eval "$$DEPMOD" diff --git a/open-vm-tools/modules/freebsd/vmblock/vm_assert.h b/open-vm-tools/modules/freebsd/vmblock/vm_assert.h index 48519d2c2..7fdaf824f 100644 --- a/open-vm-tools/modules/freebsd/vmblock/vm_assert.h +++ b/open-vm-tools/modules/freebsd/vmblock/vm_assert.h @@ -95,10 +95,26 @@ EXTERN void WarningThrottled(uint32 *count, const char *fmt, ...) */ #ifndef ASSERT_IFNOT - #ifdef __cplusplus - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : (void)0) + /* + * PR 271512: When compiling with gcc, catch assignments inside an ASSERT. + * + * 'UNLIKELY' is defined with __builtin_expect, which does not warn when passed an + * assignment (gcc bug 36050). + * To get around this, we put 'cond' in an 'if' statement and make sure it never gets + * executed by putting that inside of 'if (0)'. + * We use gcc's statement expression syntax to make ASSERT an expression because some + * code uses it that way. + * + * Since statement expression syntax is a gcc extension and since it's not clear if + * this is a problem with other compilers, the ASSERT definition was not changed for + * them. Using a bare 'cond' with the ternary operator may provide a solution. + */ + #ifdef __GNUC__ + #define ASSERT_IFNOT(cond, panic) \ + ({if (UNLIKELY(!(cond))) { panic; if (0) { if (cond) { ; } } } (void)0;}) #else - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : 0) + #define ASSERT_IFNOT(cond, panic) \ + (UNLIKELY(!(cond)) ? (panic) : (void)0) #endif #endif diff --git a/open-vm-tools/modules/freebsd/vmblock/vm_basic_types.h b/open-vm-tools/modules/freebsd/vmblock/vm_basic_types.h index 4c8255f70..54a3b4477 100644 --- a/open-vm-tools/modules/freebsd/vmblock/vm_basic_types.h +++ b/open-vm-tools/modules/freebsd/vmblock/vm_basic_types.h @@ -350,6 +350,9 @@ typedef int64 VmTimeVirtualClock; /* Virtual Clock kept in CPU cycles */ #ifdef _MSC_VER #define CONST64(c) c##I64 #define CONST64U(c) c##uI64 +#elif defined __APPLE__ +#define CONST64(c) c##LL +#define CONST64U(c) c##uLL #elif __GNUC__ #ifdef VM_X86_64 #define CONST64(c) c##L @@ -501,10 +504,11 @@ typedef uint64 MPN64; */ typedef VA32 UserVA32; typedef VA64 UserVA64; -typedef UserVA32 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA64 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA32 UserVA32Const; /* Userspace ptr to data that we may only read. */ typedef UserVA64 UserVA64Const; /* Used by 64-bit syscalls until conversion is finished. */ #ifdef VMKERNEL -typedef UserVA32 UserVA; +typedef UserVA64 UserVA; #else typedef void * UserVA; #endif diff --git a/open-vm-tools/modules/freebsd/vmhgfs/backdoor_def.h b/open-vm-tools/modules/freebsd/vmhgfs/backdoor_def.h index b902e6eb2..84a44f063 100644 --- a/open-vm-tools/modules/freebsd/vmhgfs/backdoor_def.h +++ b/open-vm-tools/modules/freebsd/vmhgfs/backdoor_def.h @@ -118,7 +118,8 @@ #define BDOOR_CMD_ACPI_HOTPLUG_MEMORY 57 #define BDOOR_CMD_ACPI_HOTPLUG_CBRET 58 #define BDOOR_CMD_GET_HOST_VIDEO_MODES 59 -#define BDOOR_CMD_MAX 60 +#define BDOOR_CMD_ACPI_HOTPLUG_CPU 60 +#define BDOOR_CMD_MAX 61 /* * IMPORTANT NOTE: When modifying the behavior of an existing backdoor command, diff --git a/open-vm-tools/modules/freebsd/vmhgfs/hgfsProto.h b/open-vm-tools/modules/freebsd/vmhgfs/hgfsProto.h index b3ce70382..925a287c5 100644 --- a/open-vm-tools/modules/freebsd/vmhgfs/hgfsProto.h +++ b/open-vm-tools/modules/freebsd/vmhgfs/hgfsProto.h @@ -178,6 +178,55 @@ typedef uint8 HgfsPermissions; #define HGFS_PERM_WRITE 2 #define HGFS_PERM_EXEC 1 +/* + * Access mode bits. + * + * Different operating systems have different set of file access mode. + * Here are constants that are rich enough to describe all access modes in an OS + * independent way. + */ + +typedef uint32 HgfsAccessMode; +/* + * Generic access rights control coarse grain access for the file. + * A particular generic rigth can be expanded into different set of specific rights + * on different OS. + */ + +/* + * HGFS_MODE_GENERIC_READ means ability to read file data and read various file + * attributes and properties. + */ +#define HGFS_MODE_GENERIC_READ (1 << 0) +/* + * HGFS_MODE_GENERIC_WRITE means ability to write file data and updaate various file + * attributes and properties. + */ +#define HGFS_MODE_GENERIC_WRITE (1 << 1) +/* + * HGFS_MODE_GENERIC_EXECUE means ability to execute file. For network redirectors + * ability to execute usualy implies ability to read data; for local file systems + * HGFS_MODE_GENERIC_EXECUTE does not imply ability to read data. + */ +#define HGFS_MODE_GENERIC_EXECUTE (1 << 2) + +/* Specific rights define fine grain access modes. */ +#define HGFS_MODE_READ_DATA (1 << 3) // Ability to read file data +#define HGFS_MODE_WRITE_DATA (1 << 4) // Ability to writge file data +#define HGFS_MODE_APPEND_DATA (1 << 5) // Appending data to the end of file +#define HGFS_MODE_DELETE (1 << 6) // Ability to delete the file +#define HGFS_MODE_TRAVERSE_DIRECTORY (1 << 7) // Ability to access files in a directory +#define HGFS_MODE_LIST_DIRECTORY (1 << 8) // Ability to list file names +#define HGFS_MODE_ADD_SUBDIRECTORY (1 << 9) // Ability to create a new subdirectory +#define HGFS_MODE_ADD_FILE (1 << 10) // Ability to create a new file +#define HGFS_MODE_DELETE_CHILD (1 << 11) // Ability to delete file/subdirectory +#define HGFS_MODE_READ_ATTRIBUTES (1 << 12) // Ability to read attributes +#define HGFS_MODE_WRITE_ATTRIBUTES (1 << 13) // Ability to write attributes +#define HGFS_MODE_READ_EXTATTRIBUTES (1 << 14) // Ability to read extended attributes +#define HGFS_MODE_WRITE_EXTATTRIBUTES (1 << 15) // Ability to write extended attributes +#define HGFS_MODE_READ_SECURITY (1 << 16) // Ability to read permissions/ACLs/owner +#define HGFS_MODE_WRITE_SECURITY (1 << 17) // Ability to change permissions/ACLs +#define HGFS_MODE_TAKE_OWNERSHIP (1 << 18) // Ability to change file owner/group /* * Server-side locking (oplocks and leases). @@ -356,6 +405,15 @@ typedef uint64 HgfsAttrValid; * only to determine if the ID is valid. */ #define HGFS_ATTR_VALID_NON_STATIC_FILEID (1 << 16) +/* + * File permissions that are in effect for the user which runs HGFS server. + * Client needs to know effective permissions in order to implement access(2). + * Client can't derive it from group/owner/other permissions because of two resaons: + * 1. It does not know user/group id of the user which runs HGFS server + * 2. Effective permissions account for additional restrictions that may be imposed + * by host file system, for example by ACL. + */ +#define HGFS_ATTR_VALID_EFFECTIVE_PERMS (1 << 17) /* @@ -400,7 +458,7 @@ struct HgfsAttrV2 { uint32 groupId; /* group identifier, ignored by Windows */ uint64 hostFileId; /* File Id of the file on host: inode_t on Linux */ uint32 volumeId; /* volume identifier, non-zero is valid. */ - uint32 reserved1; /* Reserved for future use */ + uint32 effectivePerms; /* Permissions in effect for the user on the host. */ uint64 reserved2; /* Reserved for future use */ } #include "vmware_pack_end.h" diff --git a/open-vm-tools/modules/freebsd/vmhgfs/kernelStubs.h b/open-vm-tools/modules/freebsd/vmhgfs/kernelStubs.h index 6c443265f..3757177fa 100644 --- a/open-vm-tools/modules/freebsd/vmhgfs/kernelStubs.h +++ b/open-vm-tools/modules/freebsd/vmhgfs/kernelStubs.h @@ -61,13 +61,17 @@ # endif # include # include +# elif defined(sun) +# include "vm_basic_types.h" +# include +# include #endif /* * Function Prototypes */ -#if defined(linux) || defined(__APPLE__) /* if (linux) || (__APPLE__) { */ +#if defined(linux) || defined(__APPLE__) || defined (sun) # ifdef linux /* if (linux) { */ char *strdup(const char *source); diff --git a/open-vm-tools/modules/freebsd/vmhgfs/os.c b/open-vm-tools/modules/freebsd/vmhgfs/os.c index 7aee00aea..476f5d4d0 100644 --- a/open-vm-tools/modules/freebsd/vmhgfs/os.c +++ b/open-vm-tools/modules/freebsd/vmhgfs/os.c @@ -27,12 +27,15 @@ # error "This os.c file can only be compiled for the FreeBSD kernel." #endif +#include #include #include #include // for struct mtx #include #include // for uma_zone_t #include // for kthread_create() +#include +#include // for vnode_pager_setsize #include "vm_basic_types.h" #include "os.h" @@ -861,3 +864,56 @@ os_path_to_utf8_precomposed(char const *bufIn, // IN NOT_IMPLEMENTED(); return OS_ERR; } + + +/* + *---------------------------------------------------------------------------- + * + * os_SetSize -- + * + * Notifies memory management system that file size has been changed. + * + * Results: + * None. + * + * Side effects: + * None. + *---------------------------------------------------------------------------- + */ + +void +os_SetSize(struct vnode* vp, // IN: vnode which size has changed + off_t newSize) // IN: new file size +{ + vnode_pager_setsize(vp, newSize); +} + + +/* + *---------------------------------------------------------------------------- + * + * os_FlushRange -- + * + * Flushes dirty pages associated with the file. + * + * Results: + * Always retun 0 (success) for now since it is NOOP. + * + * Side effects: + * None. + *---------------------------------------------------------------------------- + */ + +int +os_FlushRange(struct vnode *vp, // IN: vnode which data needs flushing + off_t start, // IN: starting offset in the file to flush + uint32_t length) // IN: length of data to flush +{ + /* + * XXX: NOOP for now. This routine is needed to maintain coherence + * between memory mapped data and data for read/write operations. + * Will need to implement when adding support for memory mapped files to HGFS + * for FreeBsd. + */ + return 0; +} diff --git a/open-vm-tools/modules/freebsd/vmhgfs/os.h b/open-vm-tools/modules/freebsd/vmhgfs/os.h index 6f7e8feba..151a9d371 100644 --- a/open-vm-tools/modules/freebsd/vmhgfs/os.h +++ b/open-vm-tools/modules/freebsd/vmhgfs/os.h @@ -20,8 +20,10 @@ /* * os.h -- * - * Implementation OS X / FreeBSD independent memory allocation and + * Wrappers for OS specific functions that are different between OS X and FreeBsd. + * 1. Implementation OS X / FreeBSD independent memory allocation and * thread synchronization routines. + * 2. Interaction with memory manager/pager. */ #ifndef _OS_H_ @@ -126,5 +128,8 @@ extern int os_path_to_utf8_precomposed(const char *bufIn, uint32 bufInSize, char uint32 bufOutSize); extern Bool os_utf8_conversion_needed(void); +// Memory manager/pager functions +void os_SetSize(struct vnode *vp, off_t newSize); +int os_FlushRange(struct vnode *vp, off_t start, uint32_t length); #endif // ifndef _OS_H_ diff --git a/open-vm-tools/modules/freebsd/vmhgfs/state.c b/open-vm-tools/modules/freebsd/vmhgfs/state.c index 64ae67271..f5034000a 100644 --- a/open-vm-tools/modules/freebsd/vmhgfs/state.c +++ b/open-vm-tools/modules/freebsd/vmhgfs/state.c @@ -82,12 +82,13 @@ static int HgfsVnodeGetInt(struct vnode **vpp, HgfsFileHashTable *htp, Bool rootVnode, Bool createFile, - int permissions); + int permissions, + off_t fileSize); /* Allocation/initialization/free of open file state */ static HgfsFile *HgfsAllocFile(const char *fileName, HgfsFileType fileType, struct vnode *dvp, HgfsFileHashTable *htp, - int permissions); + int permissions, off_t fileSize); /* Acquiring/releasing file state */ static HgfsFile *HgfsInsertFile(const char *fileName, @@ -95,7 +96,7 @@ static HgfsFile *HgfsInsertFile(const char *fileName, HgfsFileHashTable *htp); static void HgfsReleaseFile(HgfsFile *fp, HgfsFileHashTable *htp); static int HgfsInitFile(HgfsFile *fp, struct vnode *dvp, const char *fileName, - HgfsFileType fileType, int permissions); + HgfsFileType fileType, int permissions, off_t fileSize); static void HgfsFreeFile(HgfsFile *fp); /* Adding/finding/removing file state from hash table */ @@ -145,10 +146,11 @@ HgfsVnodeGet(struct vnode **vpp, // OUT: Filled with address of created v HgfsFileType fileType, // IN: Type of file HgfsFileHashTable *htp, // IN: File hash table Bool createFile, // IN: Creating a new file or open existing? - int permissions) // IN: Permissions for the created file + int permissions, // IN: Permissions for the created file + off_t fileSize) // IN: File size if the vnode is VREG { return HgfsVnodeGetInt(vpp, dvp, sip, vfsp, fileName, fileType, htp, FALSE, - createFile, permissions); + createFile, permissions, fileSize); } @@ -178,7 +180,8 @@ HgfsVnodeGetRoot(struct vnode **vpp, // OUT: Filled with address of created HgfsFileType fileType, // IN: Type of file HgfsFileHashTable *htp) // IN: File hash table { - return HgfsVnodeGetInt(vpp, NULL, sip, vfsp, fileName, fileType, htp, TRUE, FALSE, 0); + return HgfsVnodeGetInt(vpp, NULL, sip, vfsp, fileName, fileType, htp, TRUE, + FALSE, 0, 0); } @@ -430,7 +433,7 @@ HgfsFileHashTableIsEmpty(HgfsSuperInfo *sip, // IN: Superinfo * Results: * Returns 0 if the handle is set and had been referenced, * EACCES if the handle is set but has an incompatible open mode, - * ENOENT if no hadnle is set for the vnode + * ENOENT if no handle is set for the vnode * * Side effects: * None. @@ -440,10 +443,11 @@ HgfsFileHashTableIsEmpty(HgfsSuperInfo *sip, // IN: Superinfo int HgfsCheckAndReferenceHandle(struct vnode *vp, // IN: Vnode to check handle of + Bool mmap, // IN: True if it is mmap call int requestedOpenMode) // IN: Requested open mode { HgfsFile *fp; - Bool ret; + int ret; ASSERT(vp); @@ -453,9 +457,29 @@ HgfsCheckAndReferenceHandle(struct vnode *vp, // IN: Vnode to check handle ret = fp->handleRefCount ? 0 : ENOENT; if (ret == 0) { if (HgfsIsModeCompatible(requestedOpenMode, fp->mode)) { - fp->handleRefCount++; + DEBUG(VM_DEBUG_LOG, "Handle exists, %d %d %d\n", + mmap, fp->mmapped, fp->handleRefCount); + /* + * Do nothing for subsequent mmap requests since OS invokes mnomap only once + * for multiple mmap calls. + */ + if (!mmap || !fp->mmapped) { + if (fp->implicitlyOpened) { + /* + * Consider it a regular open from now on. + * Handle already had been referenced, no need to increment + * count. + */ + fp->implicitlyOpened = FALSE; + DEBUG(VM_DEBUG_LOG, "Clear implicitly opened flag\n"); + } else { + fp->handleRefCount++; + } + fp->mmapped = fp->mmapped || mmap; + } } else { ret = EACCES; + DEBUG(VM_DEBUG_LOG, "Incompatible modes: %d %d\n", requestedOpenMode, fp->mode); } } @@ -485,7 +509,8 @@ HgfsCheckAndReferenceHandle(struct vnode *vp, // IN: Vnode to check handle void HgfsSetOpenFileHandle(struct vnode *vp, // IN: Vnode to set handle for HgfsHandle handle, // IN: Value of handle - HgfsMode openMode) // IN: Mode assosiated with the handle + HgfsMode openMode, // IN: Mode assosiated with the handle + Bool implicit) // IN: TRUE if not in VNOP_OPEN context { HgfsFile *fp; @@ -497,6 +522,7 @@ HgfsSetOpenFileHandle(struct vnode *vp, // IN: Vnode to set handle for fp->handle = handle; fp->mode = openMode; fp->handleRefCount = 1; + fp->implicitlyOpened = implicit; DEBUG(VM_DEBUG_STATE, "HgfsSetOpenFileHandle: set handle for %s to %d\n", HGFS_VP_TO_FILENAME(vp), fp->handle); @@ -560,6 +586,7 @@ HgfsGetOpenFileHandle(struct vnode *vp, // IN: Vnode to get handle for * Returns new handle reference count. * When the returned value is 0 returns the file handle which need to be closed * on the host. + * Returns special value -1 if the handle had not been open. * * Side effects: * None. @@ -568,10 +595,11 @@ HgfsGetOpenFileHandle(struct vnode *vp, // IN: Vnode to get handle for */ int -HgfsReleaseOpenFileHandle(struct vnode *vp, // IN - HgfsHandle *handleToClose) // OUT +HgfsReleaseOpenFileHandle(struct vnode *vp, // IN: correspondent vnode + Bool mnomap, // IN: True if called from mnomap + HgfsHandle *handleToClose) // OUT: Host handle to close { - int ret; + int ret = -1; HgfsFile *fp; ASSERT(vp); @@ -582,21 +610,25 @@ HgfsReleaseOpenFileHandle(struct vnode *vp, // IN os_rw_lock_lock_exclusive(fp->handleLock); /* Make sure the reference count is not going negative! */ - ASSERT(fp->handleRefCount > 0); + ASSERT(fp->handleRefCount >= 0); - --fp->handleRefCount; - ret = fp->handleRefCount; + if (fp->handleRefCount > 0) { + --fp->handleRefCount; + ret = fp->handleRefCount; + if (mnomap) { + fp->mmapped = FALSE; + } - /* If the reference count has gone to zero, clear the handle. */ - if (ret == 0) { - DEBUG(VM_DEBUG_LOG, "closing directory handle\n"); - *handleToClose = fp->handle; - fp->handle = 0; - } else { - DEBUG(VM_DEBUG_LOG, "ReleaseOpenFileHandle with a refcount of: %d\n", - fp->handleRefCount); + /* If the reference count has gone to zero, clear the handle. */ + if (ret == 0) { + DEBUG(VM_DEBUG_LOG, "closing directory handle\n"); + *handleToClose = fp->handle; + fp->handle = 0; + } else { + DEBUG(VM_DEBUG_LOG, "ReleaseOpenFileHandle with a refcount of: %d\n", + fp->handleRefCount); + } } - os_rw_lock_unlock_exclusive(fp->handleLock); return ret; @@ -638,6 +670,7 @@ HgfsLookupExistingVnode(const char* fileName, /* First verify if a vnode for the filename is already allocated. */ existingFp = HgfsFindFile(fileName, htp); if (existingFp != NULL) { + DEBUG(VM_DEBUG_LOG, "Found existing vnode for %s\n", fileName); if (failIfExist) { err = EEXIST; } else { @@ -694,7 +727,8 @@ HgfsVnodeGetInt(struct vnode **vpp, // OUT: Filled with address of creat HgfsFileHashTable *htp, // IN: File hash Bool rootVnode, // IN: Is this a root vnode? Bool fileCreate, // IN: Is it a new file creation? - int permissions) // IN: Permissions for new files + int permissions, // IN: Permissions for new files + off_t fileSize) // IN: Size of the file { struct vnode *vp; int ret; @@ -762,7 +796,7 @@ HgfsVnodeGetInt(struct vnode **vpp, // OUT: Filled with address of creat } /* We now allocate our private open file structure. */ - fp = (void *)HgfsAllocFile(fileName, fileType, dvp, htp, permissions); + fp = (void *)HgfsAllocFile(fileName, fileType, dvp, htp, permissions, fileSize); if (fp == NULL) { ret = ENOMEM; goto destroyOut; @@ -821,7 +855,8 @@ HgfsVnodeGetInt(struct vnode **vpp, // OUT HgfsFileHashTable *htp, // IN Bool rootVnode, // IN Bool fileCreate, // IN - int permissions) // IN + int permissions, // IN + off_t fileSize) // IN { struct vnode *vp; struct vnode_fsparam params; @@ -842,13 +877,13 @@ HgfsVnodeGetInt(struct vnode **vpp, // OUT } params.vnfs_mp = vfsp; - params.vnfs_str = NULL; - params.vnfs_dvp = NULL; + params.vnfs_str = "hgfs"; + params.vnfs_dvp = dvp; params.vnfs_fsnode = NULL; params.vnfs_vops = HgfsVnodeOps; params.vnfs_marksystem = FALSE; params.vnfs_rdev = 0; - params.vnfs_filesize = 0; + params.vnfs_filesize = fileSize; params.vnfs_cnp = NULL; /* Do not let OS X cache vnodes for us. */ params.vnfs_flags = VNFS_NOCACHE | VNFS_CANTCACHE; @@ -884,7 +919,7 @@ HgfsVnodeGetInt(struct vnode **vpp, // OUT goto out; } - fp = HgfsAllocFile(fileName, fileType, dvp, htp, permissions); + fp = HgfsAllocFile(fileName, fileType, dvp, htp, permissions, fileSize); params.vnfs_fsnode = (void *)fp; if (params.vnfs_fsnode == NULL) { @@ -958,7 +993,8 @@ HgfsAllocFile(const char *fileName, // IN: Name of file HgfsFileType fileType, // IN: Type of file struct vnode *dvp, // IN: Parent directory vnode HgfsFileHashTable *htp, // IN: Hash table - int permissions) // IN: permissions for creating new files + int permissions, // IN: permissions for creating new files + off_t fileSize) // IN: file size { HgfsFile *fp; fp = os_malloc(sizeof *fp, M_ZERO | M_WAITOK); @@ -966,7 +1002,7 @@ HgfsAllocFile(const char *fileName, // IN: Name of file if (fp != NULL) { DEBUG(VM_DEBUG_INFO, "HgfsGetFile: allocated HgfsFile for %s.\n", fileName); - if (HgfsInitFile(fp, dvp, fileName, fileType, permissions) != 0) { + if (HgfsInitFile(fp, dvp, fileName, fileType, permissions, fileSize) != 0) { DEBUG(VM_DEBUG_FAIL, "Failed to initialize HgfsFile"); os_free(fp, sizeof(*fp)); fp = NULL; @@ -1001,7 +1037,7 @@ HgfsAllocFile(const char *fileName, // IN: Name of file static HgfsFile * HgfsInsertFile(const char *fileName, // IN: Filename to get file for HgfsFile *fp, // IN: HgfsFile object to insert - HgfsFileHashTable *htp) // IN: Hash table to look in + HgfsFileHashTable *htp) // IN: Hash table to look in { HgfsFile *existingFp = NULL; @@ -1015,21 +1051,21 @@ HgfsInsertFile(const char *fileName, // IN: Filename to get file for os_mutex_lock(htp->mutex); existingFp = HgfsFindFile(fileName, htp); - if (existingFp) { // HgfsFile with this name alrady exists + if (existingFp) { // HgfsFile with this name already exists int ret = vnode_get(existingFp->vnodep); if (ret != 0) { /* * It is not clear why vnode_get may fail while there is HgfsFile in * our hash table. Most likely it will never happen. * However if this ever occur the safest approach is to remove - * the HgsfFle structure from the hash table but do dont free it. + * the HgfsFile structure from the hash table but do dont free it. * It should be freed later on when the vnode is recycled. */ DblLnkLst_Unlink1(&existingFp->listNode); existingFp = NULL; } } - if (existingFp) { + if (existingFp == NULL) { HgfsAddFile(fp, htp); } @@ -1100,7 +1136,8 @@ HgfsInitFile(HgfsFile *fp, // IN: File to initialize struct vnode *dvp, // IN: Paretn directory vnode const char *fileName, // IN: Name of file HgfsFileType fileType, // IN: Type of file - int permissions) // IN: Permissions for new files + int permissions, // IN: Permissions for new files + off_t fileSize) // IN: File size { int len; @@ -1137,6 +1174,9 @@ HgfsInitFile(HgfsFile *fp, // IN: File to initialize fp->handleRefCount = 0; fp->handle = 0; + fp->implicitlyOpened = FALSE; + fp->mmapped = FALSE; + fp->fileSize = fileSize; fp->handleLock = os_rw_lock_alloc_init("hgfs_rw_handle_lock"); if (!fp->handleLock) { @@ -1205,7 +1245,7 @@ destroyOut: */ void -HgfsFreeFile(HgfsFile *fp) +HgfsFreeFile(HgfsFile *fp) // IN: HgfsFile structure to free { ASSERT(fp); os_rw_lock_free(fp->handleLock); @@ -1220,6 +1260,33 @@ HgfsFreeFile(HgfsFile *fp) os_free(fp, sizeof *fp); } + +/* + *---------------------------------------------------------------------------- + * + * HgfsMarkFileMmapped -- + * + * Sets/Clears mmap flag in HgfsFile structure + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +void +HgfsMarkFileMmapped(struct vnode *vp, // vnode which state is being changed + Bool mmapped) // New mapping state +{ + HgfsFile *fp; + ASSERT(vp); + fp = HGFS_VP_TO_FP(vp); + fp->mmapped = mmapped; +} + /* Adding/finding/removing file state from hash table */ @@ -1504,3 +1571,34 @@ HgfsIsModeCompatible(HgfsMode requestedMode, // IN: Requested open mode requestedMode == existingMode); } + +/* + *---------------------------------------------------------------------------- + * + * HgfsSetFileSize -- + * + * Notifies virtual memory system that file size has changed. + * Required for memory mapped files to work properly. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +void +HgfsSetFileSize(struct vnode *vp, // IN: vnode which file size has changed + off_t newSize) // IN: new value for file size +{ + HgfsFile *fp; + + ASSERT(vp); + fp = HGFS_VP_TO_FP(vp); + if (fp->fileSize != newSize) { + fp->fileSize = newSize; + os_SetSize(vp, newSize); + } +} diff --git a/open-vm-tools/modules/freebsd/vmhgfs/state.h b/open-vm-tools/modules/freebsd/vmhgfs/state.h index f897437ca..f0caa6aae 100644 --- a/open-vm-tools/modules/freebsd/vmhgfs/state.h +++ b/open-vm-tools/modules/freebsd/vmhgfs/state.h @@ -74,6 +74,12 @@ #define HGFS_VP_TO_PERMISSIONS(vp) \ HGFS_VP_TO_FP(vp)->permissions +#define HGFS_VP_TO_MMAPPED(vp) \ + HGFS_VP_TO_FP(vp)->mmapped + +#define HGFS_VP_TO_FILESIZE(vp) \ + HGFS_VP_TO_FP(vp)->fileSize + /* * Types @@ -146,6 +152,20 @@ typedef struct HgfsFile { OS_RWLOCK_T *rwFileLock; #endif + /* TRUE indicates that memory mapping has been established for the file. */ + Bool mmapped; + /* + * TRUE if the handle has an additional reference which is the + * result of reading file before opening which occurs on MAC OS. + */ + Bool implicitlyOpened; + /* + * File size. HGFS must tell memory management system when file size is changed. + * It implies that HGFS has to know if a write request writes data beyond EOF thus + * it has to maintain local copy of file size that is kept in sync with the size + * reported to memory manager/pager. + */ + off_t fileSize; } HgfsFile; /* The hash table for file state. */ @@ -159,7 +179,8 @@ struct HgfsSuperInfo; int HgfsVnodeGet(struct vnode **vpp, struct vnode *dp, struct HgfsSuperInfo *sip, struct mount *vfsp, const char *fileName, HgfsFileType fileType, - HgfsFileHashTable *htp, Bool createFile, int permissions); + HgfsFileHashTable *htp, Bool createFile, int permissions, + off_t fileSize); int HgfsVnodeGetRoot(struct vnode **vpp, struct HgfsSuperInfo *sip, struct mount *vfsp, const char *fileName, HgfsFileType fileType, HgfsFileHashTable *htp); int HgfsReleaseVnodeContext(struct vnode *vp, HgfsFileHashTable *htp); @@ -170,10 +191,12 @@ void HgfsDestroyFileHashTable(HgfsFileHashTable *htp); Bool HgfsFileHashTableIsEmpty(struct HgfsSuperInfo *sip, HgfsFileHashTable *htp); /* Handle get/set/clear functions */ -void HgfsSetOpenFileHandle(struct vnode *vp, HgfsHandle handle, HgfsMode openMode); +void HgfsSetOpenFileHandle(struct vnode *vp, HgfsHandle handle, + HgfsMode openMode, Bool implicit); int HgfsGetOpenFileHandle(struct vnode *vp, HgfsHandle *outHandle); -int HgfsReleaseOpenFileHandle(struct vnode *vp, HgfsHandle *handleToClose); -int HgfsCheckAndReferenceHandle(struct vnode *vp, int requestedOpenMode); +int HgfsReleaseOpenFileHandle(struct vnode *vp, Bool mnomap, HgfsHandle *handleToClose); +int HgfsCheckAndReferenceHandle(struct vnode *vp, Bool mmap, int requestedOpenMode); int HgfsHandleIncrementRefCount(struct vnode *vp); +void HgfsSetFileSize(struct vnode *vp, off_t newSize); #endif /* _STATE_H_ */ diff --git a/open-vm-tools/modules/freebsd/vmhgfs/vm_assert.h b/open-vm-tools/modules/freebsd/vmhgfs/vm_assert.h index 2e70ae1fe..d6bb8f7db 100644 --- a/open-vm-tools/modules/freebsd/vmhgfs/vm_assert.h +++ b/open-vm-tools/modules/freebsd/vmhgfs/vm_assert.h @@ -83,10 +83,26 @@ EXTERN void WarningThrottled(uint32 *count, const char *fmt, ...) */ #ifndef ASSERT_IFNOT - #ifdef __cplusplus - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : (void)0) + /* + * PR 271512: When compiling with gcc, catch assignments inside an ASSERT. + * + * 'UNLIKELY' is defined with __builtin_expect, which does not warn when passed an + * assignment (gcc bug 36050). + * To get around this, we put 'cond' in an 'if' statement and make sure it never gets + * executed by putting that inside of 'if (0)'. + * We use gcc's statement expression syntax to make ASSERT an expression because some + * code uses it that way. + * + * Since statement expression syntax is a gcc extension and since it's not clear if + * this is a problem with other compilers, the ASSERT definition was not changed for + * them. Using a bare 'cond' with the ternary operator may provide a solution. + */ + #ifdef __GNUC__ + #define ASSERT_IFNOT(cond, panic) \ + ({if (UNLIKELY(!(cond))) { panic; if (0) { if (cond) { ; } } } (void)0;}) #else - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : 0) + #define ASSERT_IFNOT(cond, panic) \ + (UNLIKELY(!(cond)) ? (panic) : (void)0) #endif #endif diff --git a/open-vm-tools/modules/freebsd/vmhgfs/vm_basic_asm.h b/open-vm-tools/modules/freebsd/vmhgfs/vm_basic_asm.h index 5bc4d6b71..0180e3919 100644 --- a/open-vm-tools/modules/freebsd/vmhgfs/vm_basic_asm.h +++ b/open-vm-tools/modules/freebsd/vmhgfs/vm_basic_asm.h @@ -853,4 +853,21 @@ RDTSC(void) #error No compiler defined for RDTSC #endif /* __GNUC__ */ +/* + *----------------------------------------------------------------------------- + * + * DEBUGBREAK -- + * + * Does an int3 for MSVC / GCC. This is a macro to make sure int3 is + * always inlined. + * + *----------------------------------------------------------------------------- + */ + +#ifdef _MSC_VER +#define DEBUGBREAK() __debugbreak() +#else +#define DEBUGBREAK() __asm__ (" int $3 ") +#endif + #endif diff --git a/open-vm-tools/modules/freebsd/vmhgfs/vm_basic_types.h b/open-vm-tools/modules/freebsd/vmhgfs/vm_basic_types.h index 53ee91746..638e9ce6c 100644 --- a/open-vm-tools/modules/freebsd/vmhgfs/vm_basic_types.h +++ b/open-vm-tools/modules/freebsd/vmhgfs/vm_basic_types.h @@ -338,6 +338,9 @@ typedef int64 VmTimeVirtualClock; /* Virtual Clock kept in CPU cycles */ #ifdef _MSC_VER #define CONST64(c) c##I64 #define CONST64U(c) c##uI64 +#elif defined __APPLE__ +#define CONST64(c) c##LL +#define CONST64U(c) c##uLL #elif __GNUC__ #ifdef VM_X86_64 #define CONST64(c) c##L @@ -489,10 +492,11 @@ typedef uint64 MPN64; */ typedef VA32 UserVA32; typedef VA64 UserVA64; -typedef UserVA32 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA64 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA32 UserVA32Const; /* Userspace ptr to data that we may only read. */ typedef UserVA64 UserVA64Const; /* Used by 64-bit syscalls until conversion is finished. */ #ifdef VMKERNEL -typedef UserVA32 UserVA; +typedef UserVA64 UserVA; #else typedef void * UserVA; #endif diff --git a/open-vm-tools/modules/freebsd/vmhgfs/vnops.c b/open-vm-tools/modules/freebsd/vmhgfs/vnops.c index a958b58ae..15f3c1650 100644 --- a/open-vm-tools/modules/freebsd/vmhgfs/vnops.c +++ b/open-vm-tools/modules/freebsd/vmhgfs/vnops.c @@ -216,7 +216,7 @@ struct vop_open_args { struct vnode *vp = ap->a_vp; int mode = ap->a_mode; - return HgfsOpenInt(vp, mode); + return HgfsOpenInt(vp, mode, FALSE); } @@ -326,8 +326,30 @@ struct vop_access_args { { struct vnode *vp = ap->a_vp; int mode = ap->a_mode; + HgfsAccessMode accessMode = 0; + Bool isDir = vp->v_type == VDIR; + if (mode & VREAD) { + accessMode |= isDir ? HGFS_MODE_LIST_DIRECTORY : HGFS_MODE_READ_DATA; + accessMode |= HGFS_MODE_READ_ATTRIBUTES; + } + if (mode & VWRITE) { + if (isDir) { + accessMode |= HGFS_MODE_ADD_FILE | HGFS_MODE_ADD_SUBDIRECTORY | + HGFS_MODE_DELETE | HGFS_MODE_DELETE_CHILD | + HGFS_MODE_WRITE_ATTRIBUTES; + } else { + accessMode |= HGFS_MODE_WRITE_DATA | HGFS_MODE_ADD_SUBDIRECTORY | + HGFS_MODE_DELETE | HGFS_MODE_WRITE_ATTRIBUTES; + } + } + if (mode & VAPPEND) { + accessMode |= isDir ? HGFS_MODE_ADD_SUBDIRECTORY : HGFS_MODE_APPEND_DATA; + } + if (mode & VEXEC) { + accessMode |= isDir ? HGFS_MODE_TRAVERSE_DIRECTORY : HGFS_MODE_GENERIC_EXECUTE; + } - return HgfsAccessInt(vp, mode); + return HgfsAccessInt(vp, accessMode); } @@ -433,7 +455,7 @@ struct vop_read_args { struct vnode *vp = ap->a_vp; struct uio *uiop = ap->a_uio; - return HgfsReadInt(vp, uiop); + return HgfsReadInt(vp, uiop, FALSE); } @@ -469,7 +491,7 @@ struct vop_write_args { struct uio *uiop = ap->a_uio; int ioflag = ap->a_ioflag; - return HgfsWriteInt(vp, uiop, ioflag); + return HgfsWriteInt(vp, uiop, ioflag, FALSE); } diff --git a/open-vm-tools/modules/freebsd/vmhgfs/vnopscommon.c b/open-vm-tools/modules/freebsd/vmhgfs/vnopscommon.c index 072359a3f..b48699e53 100644 --- a/open-vm-tools/modules/freebsd/vmhgfs/vnopscommon.c +++ b/open-vm-tools/modules/freebsd/vmhgfs/vnopscommon.c @@ -39,7 +39,7 @@ int HgfsGetNextDirEntry(HgfsSuperInfo *sip, HgfsHandle handle, HgfsFileType *type, Bool *done); int HgfsDirOpen(HgfsSuperInfo *sip, struct vnode *vp); int HgfsFileOpen(HgfsSuperInfo *sip, struct vnode *vp, - int flag, int permissions); + int flag, int permissions, Bool implicit); int HgfsDirClose(HgfsSuperInfo *sip, struct vnode *vp); int HgfsFileClose(HgfsSuperInfo *sip, struct vnode *vp, int flag); int HgfsDoRead(HgfsSuperInfo *sip, HgfsHandle handle, uint64_t offset, @@ -474,11 +474,9 @@ HgfsGetattrInt(struct vnode *vp, // IN : vnode of the file * * HgfsSetattrInt -- * - * Maps the Solaris attributes to Hgfs attributes (by calling + * Maps the MAC OS/FreeBsd attributes to Hgfs attributes (by calling * HgfsSetattrCopy()) and sends a set attribute request to the Hgfs server. * - * "Sets the attributes for the supplied vnode." (Solaris Internals, p537) - * * Results: * Returns 0 on success and a non-zero error code on error. * @@ -490,7 +488,7 @@ HgfsGetattrInt(struct vnode *vp, // IN : vnode of the file int HgfsSetattrInt(struct vnode *vp, // IN : vnode of the file - HgfsVnodeAttr *vap) // IN : attributes container + HgfsVnodeAttr *vap) // IN : attributes container { HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp); HgfsKReqHandle req; @@ -581,6 +579,10 @@ HgfsSetattrInt(struct vnode *vp, // IN : vnode of the file DEBUG(VM_DEBUG_FAIL, "Error encountered with ret = %d\n", ret); } goto destroyOut; + } else { + if (HGFS_VATTR_SIZE_IS_ACTIVE(vap, HGFS_VA_DATA_SIZE)) { + HgfsSetFileSize(vp, vap->HGFS_VA_DATA_SIZE); + } } destroyOut: @@ -644,8 +646,6 @@ HgfsRmdirInt(struct vnode *dvp, // IN: parent directory * Composes the full pathname of this file and sends a DELETE_FILE request * by calling HgfsDelete(). * - * "Removes the file for the supplied vnode." (Solaris Internals, p537) - * * Results: * Returns 0 on success or a non-zero error code on error. * @@ -672,6 +672,9 @@ int HgfsRemoveInt(struct vnode *vp) // IN: Vnode to delete goto out; } + os_FlushRange(vp, 0, HGFS_VP_TO_FILESIZE(vp)); + os_SetSize(vp, 0); + /* We can now send the delete request. */ ret = HgfsDelete(sip, HGFS_VP_TO_FILENAME(vp), HGFS_OP_DELETE_FILE_V3); @@ -763,7 +766,8 @@ HgfsCloseInt(struct vnode *vp, // IN: Vnode to close. int HgfsOpenInt(struct vnode *vp, // IN: Vnode to open. - int mode) // IN: Mode of vnode being opened. + int mode, // IN: Mode of vnode being opened. + Bool implicit) // IN: TRUE if called outside of VNOP_OPEN. { HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp); DEBUG(VM_DEBUG_LOG, "Enter %s.\n", HGFS_VP_TO_FILENAME(vp)); @@ -782,7 +786,7 @@ HgfsOpenInt(struct vnode *vp, // IN: Vnode to open. * are ignored by HgfsFileOpen. */ DEBUG(VM_DEBUG_COMM, "opening a file with flag %x\n", mode); - return HgfsFileOpen(sip, vp, mode, HGFS_VP_TO_PERMISSIONS(vp)); + return HgfsFileOpen(sip, vp, mode, HGFS_VP_TO_PERMISSIONS(vp), implicit); } default: @@ -863,7 +867,7 @@ HgfsLookupInt(struct vnode *dvp, // IN : directory vnode *vpp = dvp; return 0; } - + /* * Get pointer to the superinfo. If the device is not attached, * hgfsInstance will not be valid and we immediately return an error. @@ -936,7 +940,8 @@ HgfsLookupInt(struct vnode *dvp, // IN : directory vnode attrV2.type, // Type of file &sip->fileHashTable, // File hash table FALSE, // Not a new file creation - 0); // No permissions - not a new file + 0, // No permissions - not a new file + attrV2.size); // File size if (ret) { DEBUG(VM_DEBUG_FAIL, "couldn't create vnode for \"%s\".\n", path); @@ -1020,9 +1025,23 @@ int HgfsCreateInt(struct vnode *dvp, // IN : Directory vnode if (ret >= 0) { /* Create the vnode for this file. */ ret = HgfsVnodeGet(vpp, dvp, sip, HGFS_VP_TO_MP(dvp), fullname, - HGFS_FILE_TYPE_REGULAR, &sip->fileHashTable, FALSE, mode); + HGFS_FILE_TYPE_REGULAR, &sip->fileHashTable, TRUE, + mode, 0); /* HgfsVnodeGet() guarantees this. */ ASSERT(ret != 0 || *vpp); + /* + * NOTE: This is a temporary workaround. + * This condition may occur because we look up vnodes by file name in the + * vnode cache. + * There is a race condition when file is already deleted but still referenced - + * thus vnode still exist. If a new file with the same name is created I + * can neither use the vnode of the deleted file nor insert a new vnode with + * the same name - thus I fail the request. This behavior is not correct and will + * be fixed after further restructuring if the source code. + */ + if (ret == EEXIST) { + ret = EIO; + } } else { DEBUG(VM_DEBUG_FAIL, "couldn't create full path name.\n"); ret = ENAMETOOLONG; @@ -1063,7 +1082,8 @@ int HgfsCreateInt(struct vnode *dvp, // IN : Directory vnode int HgfsReadInt(struct vnode *vp, // IN : Vnode to read from - struct uio *uiop) // IN/OUT: Buffer to write data into. + struct uio *uiop, // IN/OUT: Buffer to write data into. + Bool pagingIo) // IN: True if the read is a result of a page fault { HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp); HgfsHandle handle; @@ -1073,9 +1093,9 @@ HgfsReadInt(struct vnode *vp, // IN : Vnode to read from DEBUG(VM_DEBUG_ENTRY, "entry %s.\n", HGFS_VP_TO_FILENAME(vp)); /* We can't read from directories, that's what readdir() is for. */ - if (HGFS_VP_TO_VTYPE(vp) == VDIR) { - DEBUG(VM_DEBUG_FAIL, "cannot read directories.\n"); - return EISDIR; + if (HGFS_VP_TO_VTYPE(vp) != VREG) { + DEBUG(VM_DEBUG_FAIL, "Can only read regular files.\n"); + return (HGFS_VP_TO_VTYPE(vp) == VDIR) ? EISDIR : EPERM; } /* off_t is a signed quantity */ @@ -1094,9 +1114,18 @@ HgfsReadInt(struct vnode *vp, // IN : Vnode to read from */ ret = HgfsGetOpenFileHandle(vp, &handle); if (ret) { - DEBUG(VM_DEBUG_FAIL, "could not get handle.\n"); + DEBUG(VM_DEBUG_FAIL, "could not get handle.\n"); + return EINVAL; + } + + /* Flush mmaped data to maintain data coherence between mmap and read. */ + if (!pagingIo) { + ret = os_FlushRange(vp, offset, HGFS_UIOP_TO_RESID(uiop)); + if (ret) { + DEBUG(VM_DEBUG_FAIL, "could not flush data.\n"); return EINVAL; } + } /* * Here we loop around HgfsDoRead with requests less than or equal to @@ -1119,7 +1148,8 @@ HgfsReadInt(struct vnode *vp, // IN : Vnode to read from handle, HGFS_VP_TO_FILENAME(vp)); /* Request at most HGFS_IO_MAX bytes */ - size = (HGFS_UIOP_TO_RESID(uiop) > HGFS_IO_MAX) ? HGFS_IO_MAX : HGFS_UIOP_TO_RESID(uiop); + size = (HGFS_UIOP_TO_RESID(uiop) > HGFS_IO_MAX) ? HGFS_IO_MAX : + HGFS_UIOP_TO_RESID(uiop); /* Send one read request. */ ret = HgfsDoRead(sip, handle, offset, size, uiop); @@ -1158,10 +1188,6 @@ HgfsReadInt(struct vnode *vp, // IN : Vnode to read from * We call HgfsDoWrite() once with requests less than or equal to * HGFS_IO_MAX bytes until the user's write request has completed. * - * "Writes the range supplied for the given vnode. The write system call - * typically maps the requested range of a file into kernel memory and then - * uses vop_putpage() to do the real work." (Solaris Internals, p538) - * * Results: * Returns 0 on success and error code on error. * @@ -1173,13 +1199,15 @@ HgfsReadInt(struct vnode *vp, // IN : Vnode to read from int HgfsWriteInt(struct vnode *vp, // IN : the vnode of the file - struct uio *uiop, // IN/OUT: location of data to be written - int ioflag) // IN : hints & other directives + struct uio *uiop, // IN/OUT: location of data to be written + int ioflag, // IN : hints & other directives + Bool pagingIo) // IN: True if the write is originated by memory manager { HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp); HgfsHandle handle; uint64_t offset; int ret; + int error = 0; DEBUG(VM_DEBUG_ENTRY, "entry. (vp=%p)\n", vp); @@ -1207,6 +1235,11 @@ HgfsWriteInt(struct vnode *vp, // IN : the vnode of the file return EINVAL; } + /* Flush mmaped data to maintain data coherence between mmap and read. */ + if (!pagingIo && (ioflag & IO_APPEND) == 0) { + ret = os_FlushRange(vp, offset, HGFS_UIOP_TO_RESID(uiop)); + } + /* * We loop around calls to HgfsDoWrite() until either (1) we have written all * of our data or (2) an error has occurred. HGFS_UIOP_TO_RESID(uiop) is decremented @@ -1233,7 +1266,8 @@ HgfsWriteInt(struct vnode *vp, // IN : the vnode of the file * function header of HgfsDoWrite() for a more complete explanation. */ DEBUG(VM_DEBUG_INFO, "HgfsDoWrite failed, returning %d\n", -ret); - return -ret; + error = -ret; + break; } /* Increment the offest by the amount already written. */ @@ -1241,10 +1275,20 @@ HgfsWriteInt(struct vnode *vp, // IN : the vnode of the file } while (HGFS_UIOP_TO_RESID(uiop)); - /* We have completed the user's write request, so return success. */ + /* Need to notify memory manager if written data extended the file. */ + if (!pagingIo && (offset > HGFS_VP_TO_FILESIZE(vp))) { + if ((ioflag & IO_APPEND) == 0) { + os_SetSize(vp, offset); + } else { + off_t oldSize = HGFS_VP_TO_FILESIZE(vp); + off_t writtenData = offset - HGFS_UIOP_TO_OFFSET(uiop); + os_SetSize(vp, oldSize + writtenData); + } + } + /* We have completed the user's write request, so return. */ DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(vp)); - return 0; + return error; } @@ -1379,7 +1423,8 @@ HgfsMkdirInt(struct vnode *dvp, // IN : directory vnode } ret = HgfsVnodeGet(vpp, dvp, sip, HGFS_VP_TO_MP(dvp), fullName, - HGFS_FILE_TYPE_DIRECTORY, &sip->fileHashTable, TRUE, mode); + HGFS_FILE_TYPE_DIRECTORY, &sip->fileHashTable, TRUE, + mode, 0); if (ret) { ret = EIO; goto destroyOut; @@ -1440,7 +1485,7 @@ HgfsDirOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer * There is no different open modes for directories thus the handle is compatible. */ os_rw_lock_lock_exclusive(fp->handleLock); - ret = HgfsCheckAndReferenceHandle(vp, 0); + ret = HgfsCheckAndReferenceHandle(vp, FALSE, 0); if (ret == ENOENT) { // Handle is not set, need to get one from the host if (HGFS_IS_ROOT_VNODE(sip, vp)) { @@ -1457,7 +1502,7 @@ HgfsDirOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer * We successfully received a reply, so we need to save the handle in * this file's HgfsOpenFile and return success. */ - HgfsSetOpenFileHandle(vp, handle, 0); + HgfsSetOpenFileHandle(vp, handle, HGFS_OPEN_MODE_READ_ONLY, FALSE); } } os_rw_lock_unlock_exclusive(fp->handleLock); @@ -1491,7 +1536,8 @@ int HgfsFileOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer struct vnode *vp, // IN: Vnode of file to open int flag, // IN: Flags of open - int permissions) // IN: Permissions of open (only when creating) + int permissions, // IN: Permissions of open (only when creating) + Bool implicit) // IN: TRUE if called outside of HgfsOpenInt { char *fullPath = NULL; uint32 fullPathLen; @@ -1549,7 +1595,7 @@ HgfsFileOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer * If it is true then add reference to vnode and grant the access, otherwise * deny the access. */ - ret = HgfsCheckAndReferenceHandle(vp, openMode); + ret = HgfsCheckAndReferenceHandle(vp, FALSE, openMode); if (ret == ENOENT) { // Handle is not set, need to get one from the host int requestedMode = HGFS_OPEN_MODE_READ_WRITE; @@ -1569,7 +1615,7 @@ HgfsFileOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer * Try exact open mode now. */ DEBUG(VM_DEBUG_FAIL, "RW mode failed, re-submitting original mode = %d.\n", - ret); + openMode); requestedMode = openMode; ret = HgfsSendOpenRequest(sip, requestedMode, openFlags, permissions, fullPath, fullPathLen, &handle); @@ -1581,14 +1627,14 @@ HgfsFileOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer * this file's HgfsOpenFile and return success. */ if (ret == 0) { - HgfsSetOpenFileHandle(vp, handle, requestedMode); + HgfsSetOpenFileHandle(vp, handle, requestedMode, implicit); } } os_rw_lock_unlock_exclusive(fp->handleLock); out: - DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(vp)); + DEBUG(VM_DEBUG_LOG, "Exit (%d) %s.\n", ret, HGFS_VP_TO_FILENAME(vp)); return ret; } @@ -1627,7 +1673,7 @@ HgfsDirClose(HgfsSuperInfo *sip, // IN: Superinfo pointer * Check to see if we should close the file handle on the host ( which happen when * the reference count of the current handle become 0. */ - if (HgfsReleaseOpenFileHandle(vp, &handleToClose) == 0) { + if (HgfsReleaseOpenFileHandle(vp, FALSE, &handleToClose) == 0) { ret = HgfsCloseServerDirHandle(sip, handleToClose); } DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(vp)); @@ -1670,7 +1716,7 @@ HgfsFileClose(HgfsSuperInfo *sip, // IN: Superinfo pointer * Check to see if we should close the file handle on the host ( which happen when * the reference count of the current handle become 0. */ - if (HgfsReleaseOpenFileHandle(vp, &handleToClose) == 0) { + if (HgfsReleaseOpenFileHandle(vp, FALSE, &handleToClose) == 0) { ret = HgfsCloseServerFileHandle(sip, handleToClose); } DEBUG(VM_DEBUG_LOG, "Exit %s.\n", HGFS_VP_TO_FILENAME(vp)); @@ -2421,7 +2467,7 @@ HgfsSymlinkInt(struct vnode *dvp, // IN : directory vnode ret = HgfsGetStatus(req, repSize); if (ret == 0) { ret = HgfsVnodeGet(vpp, dvp, sip, HGFS_VP_TO_MP(dvp), fullName, - HGFS_FILE_TYPE_SYMLINK, &sip->fileHashTable, TRUE, 0); + HGFS_FILE_TYPE_SYMLINK, &sip->fileHashTable, TRUE, 0, 0); if (ret) { ret = EIO; } @@ -2552,6 +2598,7 @@ HgfsDoGetattrInt(const char *path, // IN : Path to get attributes for return ret; } + /* *---------------------------------------------------------------------------- * @@ -2698,12 +2745,12 @@ out: /* *---------------------------------------------------------------------------- * - * HgfsAccessInt -- + * IsModeCompatible -- * - * Check to ensure the user has the specified type of access to the file. + * Checks if the requested mode is compatible with permissions. * * Results: - * Returns 0 if access is allowed and a non-zero error code otherwise. + * Returns TRUE if the mode is compatible, FALSE otherwise. * * Side effects: * None. @@ -2711,95 +2758,164 @@ out: *---------------------------------------------------------------------------- */ -int -HgfsAccessInt(struct vnode *vp, // IN: Vnode to check access for - int mode) // IN: Access mdoe requested. +static Bool +IsModeCompatible(HgfsAccessMode mode, // IN: Requested open mode + uint32 permissions) // IN: Effective user permissions { - HgfsVnodeAttr va; - int ret = 0; + if ((permissions & HGFS_PERM_READ) == 0) { + if ((mode & (HGFS_MODE_GENERIC_READ | + HGFS_MODE_READ_DATA | + HGFS_MODE_LIST_DIRECTORY | + HGFS_MODE_READ_ATTRIBUTES | + HGFS_MODE_READ_EXTATTRIBUTES | + HGFS_MODE_READ_SECURITY)) != 0) { + return FALSE; + } + } - DEBUG(VM_DEBUG_LOG, "Enter %s.\n", HGFS_VP_TO_FILENAME(vp)); + if ((permissions & HGFS_PERM_WRITE) == 0) { + if ((mode & (HGFS_MODE_GENERIC_WRITE | + HGFS_MODE_WRITE_DATA | + HGFS_MODE_APPEND_DATA | + HGFS_MODE_DELETE | + HGFS_MODE_ADD_SUBDIRECTORY | + HGFS_MODE_DELETE_CHILD | + HGFS_MODE_WRITE_ATTRIBUTES | + HGFS_MODE_WRITE_EXTATTRIBUTES | + HGFS_MODE_WRITE_SECURITY | + HGFS_MODE_TAKE_OWNERSHIP | + HGFS_MODE_ADD_FILE)) != 0) { + return FALSE; + } + } - /* Get the attributes for this file from the Hgfs server. */ - ret = HgfsGetattrInt(vp, &va); - if (ret) { - return ret; + if ((permissions & HGFS_PERM_EXEC) == 0) { + if ((mode & (HGFS_MODE_GENERIC_EXECUTE | + HGFS_MODE_TRAVERSE_DIRECTORY)) != 0) { + return FALSE; + } } + return TRUE; +} - DEBUG(VM_DEBUG_INFO, "vp's mode: %o\n", va.va_mode); - if ((mode & S_ISUID) && !(va.va_mode & S_ISUID)) { - DEBUG(VM_DEBUG_FAIL, "Special: user id not set on execution (%s).\n", - HGFS_VP_TO_FILENAME(vp)); - return EPERM; - } +/* + *---------------------------------------------------------------------------- + * + * HgfsAccessInt -- + * + * Check to ensure the user has the specified type of access to the file. + * + * Results: + * Returns 0 if access is allowed and a non-zero error code otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ - if ((mode & S_ISGID) && !(va.va_mode & S_ISGID)) { - DEBUG(VM_DEBUG_FAIL, "Special: group id not set on execution(%s).\n", - HGFS_VP_TO_FILENAME(vp)); - return EPERM; - } +int +HgfsAccessInt(struct vnode *vp, // IN: Vnode to check access for + HgfsAccessMode mode) // IN: Access mode requested. +{ + int ret = 0; + HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp); + HgfsAttrV2 hgfsAttrV2; - if ((mode & S_ISVTX) && !(va.va_mode & S_ISVTX)) { - DEBUG(VM_DEBUG_FAIL, "Special: sticky bit not set (%s).\n", - HGFS_VP_TO_FILENAME(vp)); - return EPERM; - } + DEBUG(VM_DEBUG_FAIL, "HgfsAccessInt is called\n"); - if ((mode & S_IRUSR) && !(va.va_mode & S_IRUSR)) { - DEBUG(VM_DEBUG_FAIL, "Owner: read access not allowed (%s).\n", - HGFS_VP_TO_FILENAME(vp)); - return EPERM; + ret = HgfsDoGetattrByName(HGFS_VP_TO_FILENAME(vp), sip, &hgfsAttrV2); + if (ret == 0) { + uint32 effectivePermissions; + if (hgfsAttrV2.mask & HGFS_ATTR_VALID_EFFECTIVE_PERMS) { + effectivePermissions = hgfsAttrV2.effectivePerms; + } else { + /* + * If the server did not return actual effective permissions then + * need to calculate ourselves. However we should avoid unnecessary denial of + * access so perform optimistic permissions calculation. + * It is safe since host enforces necessary restrictions regardless of + * the client's decisions. + */ + effectivePermissions = + hgfsAttrV2.ownerPerms | hgfsAttrV2.groupPerms | hgfsAttrV2.otherPerms; + } + if (!IsModeCompatible(mode, effectivePermissions)) { + ret = EPERM; + DEBUG(VM_DEBUG_FAIL, "HgfsAccessInt denied access: %s (%d, %d)\n", + HGFS_VP_TO_FILENAME(vp), mode, effectivePermissions); + } + } else { + DEBUG(VM_DEBUG_FAIL, "HgfsAccessInt failed getting attrib: %s (%d)\n", + HGFS_VP_TO_FILENAME(vp), ret); } + return ret; +} - if ((mode & S_IWUSR) && !(va.va_mode & S_IWUSR)) { - DEBUG(VM_DEBUG_FAIL, "Owner: write access not allowed (%s).\n", - HGFS_VP_TO_FILENAME(vp)); - return EPERM; - } - if ((mode & S_IXUSR) && !(va.va_mode & S_IXUSR)) { - DEBUG(VM_DEBUG_FAIL, "Owner: execute access not allowed (%s).\n", - HGFS_VP_TO_FILENAME(vp)); - return EPERM; - } +/* + *---------------------------------------------------------------------------- + * + * HgfsMmapInt -- + * + * HgfsMmapInt is invoked invoked from HgfsVnopMmap to verify parameters + * and mark vnode as mmapped if necessary. + * + * Results: + * Zero on success or non-zero error code otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ - if ((mode & S_IRGRP) && !(va.va_mode & S_IRGRP)) { - DEBUG(VM_DEBUG_FAIL, "Group: read access not allowed (%s).\n", - HGFS_VP_TO_FILENAME(vp)); - return EPERM; - } +int +HgfsMmapInt(struct vnode *vp, + int accessMode) +{ + ASSERT(vp); - if ((mode & S_IWGRP) && !(va.va_mode & S_IWGRP)) { - DEBUG(VM_DEBUG_FAIL, "Group: write access not allowed (%s).\n", - HGFS_VP_TO_FILENAME(vp)); - return EPERM; - } + DEBUG(VM_DEBUG_ENTRY, "mmapping \"%s\"\n", HGFS_VP_TO_FILENAME(vp)); - if ((mode & S_IXGRP) && !(va.va_mode & S_IXGRP)) { - DEBUG(VM_DEBUG_FAIL, "Group: execute access not allowed (%s).\n", - HGFS_VP_TO_FILENAME(vp)); - return EPERM; - } + return HgfsCheckAndReferenceHandle(vp, TRUE, accessMode); +} - if ((mode & S_IROTH) && !(va.va_mode & S_IROTH)) { - DEBUG(VM_DEBUG_FAIL, "Other: read access not allowed (%s).\n", - HGFS_VP_TO_FILENAME(vp)); - return EPERM; - } - if ((mode & S_IWOTH) && !(va.va_mode & S_IWOTH)) { - DEBUG(VM_DEBUG_FAIL, "Other: write access not allowed (%s).\n", - HGFS_VP_TO_FILENAME(vp)); - return EPERM; - } +/* + *---------------------------------------------------------------------------- + * + * HgfsMnomapInt -- + * + * HgfsMnomapInt is invoked invoked from HgfsVnopNomap to tear down memory + * mapping and dereference file handle. + * + * Results: + * Zero on success or non-zero error code otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ - if ((mode & S_IXOTH) && !(va.va_mode & S_IXOTH)) { - DEBUG(VM_DEBUG_FAIL, "Other: execute access not allowed (%s).\n", - HGFS_VP_TO_FILENAME(vp)); - return EPERM; - } +int +HgfsMnomapInt(struct vnode *vp) +{ + int error = 0; + HgfsHandle handleToClose; + HgfsSuperInfo *sip = HGFS_VP_TO_SIP(vp); - return 0; + ASSERT(vp); + DEBUG(VM_DEBUG_ENTRY, "unmmapping \"%s\"\n", HGFS_VP_TO_FILENAME(vp)); + /* + * Check to see if we should close the file handle on the host, which happen when + * the reference count of the current handle become 0. + */ + if (HgfsReleaseOpenFileHandle(vp, TRUE, &handleToClose) == 0) { + error = HgfsCloseServerFileHandle(sip, handleToClose); + } + return error; } diff --git a/open-vm-tools/modules/freebsd/vmhgfs/vnopscommon.h b/open-vm-tools/modules/freebsd/vmhgfs/vnopscommon.h index 9ed9472fa..303992d71 100644 --- a/open-vm-tools/modules/freebsd/vmhgfs/vnopscommon.h +++ b/open-vm-tools/modules/freebsd/vmhgfs/vnopscommon.h @@ -69,23 +69,26 @@ int HgfsRmdirInt(struct vnode *dvp, struct vnode *vp, struct componentname *cnp); int HgfsRemoveInt(struct vnode *vp); int HgfsCloseInt(struct vnode *vp, int mode); -int HgfsOpenInt(struct vnode *vp, int fflag); +int HgfsOpenInt(struct vnode *vp, int fflag, Bool implicit); int HgfsLookupInt(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp); int HgfsCreateInt(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, int mode); -int HgfsReadInt(struct vnode *vp, struct uio *uiop); +int HgfsReadInt(struct vnode *vp, struct uio *uiop, Bool pagingIo); int HgfsReadlinkInt(struct vnode *vp, struct uio *uiop); -int HgfsWriteInt(struct vnode *vp, struct uio *uiop, int ioflag); +int HgfsWriteInt(struct vnode *vp, struct uio *uiop, + int ioflag, Bool pagingIo); int HgfsMkdirInt(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, int mode); int HgfsRenameInt(struct vnode *fvp, struct vnode *tdvp, struct vnode *tvp, struct componentname *tcnp); -int HgfsAccessInt(struct vnode *vp, int mode); +int HgfsAccessInt(struct vnode *vp, HgfsAccessMode mode); int HgfsSymlinkInt(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, char *targetName); +int HgfsMmapInt(struct vnode *vp, int accessMode); +int HgfsMnomapInt(struct vnode *vp); #endif // _HGFS_VNOPS_COMMON_H_ diff --git a/open-vm-tools/modules/freebsd/vmhgfs/x86cpuid.h b/open-vm-tools/modules/freebsd/vmhgfs/x86cpuid.h index 0a2f3c6be..337fd8cd9 100644 --- a/open-vm-tools/modules/freebsd/vmhgfs/x86cpuid.h +++ b/open-vm-tools/modules/freebsd/vmhgfs/x86cpuid.h @@ -440,7 +440,7 @@ FLAGDEF( 87, EDX, AMD, 4, 1, TM, NA, IGNORE, 0, FALSE) FLAGDEF( 87, EDX, AMD, 5, 1, STC, NA, IGNORE, 0, FALSE) \ FLAGDEF( 87, EDX, AMD, 6, 1, 100MHZSTEPS, NA, IGNORE, 0, FALSE) \ FLAGDEF( 87, EDX, AMD, 7, 1, HWPSTATE, NA, IGNORE, 0, FALSE) \ -FLAGDEF( 87, EDX, AMD, 8, 1, TSC_INVARIANT, NA, IGNORE, 0, FALSE) \ +FLAGDEF( 87, EDX, COMMON, 8, 1, TSC_INVARIANT, NA, IGNORE, 0, FALSE) \ FIELDDEFA(88, EAX, COMMON, 0, 8, PHYSBITS, NA, IGNORE, 0, FALSE, PHYS_BITS) \ FIELDDEFA(88, EAX, COMMON, 8, 8, VIRTBITS, NA, IGNORE, 0, FALSE, VIRT_BITS) \ FIELDDEFA(88, ECX, AMD, 0, 8, CORE_COUNT, NA, IGNORE, 0, FALSE, AMD_CORE_COUNT) \ @@ -644,6 +644,9 @@ FIELD_FUNC(MWAIT_C4_SUBSTATE, CPUID_INTEL_ID5EDX_MWAIT_C4_SUBSTATE) #define CPUID_MODEL_PIII_08 8 #define CPUID_MODEL_PIII_0A 10 +/* AMD model information */ +#define CPUID_MODEL_BARCELONA_02 0x02 // Barcelona (both Opteron & Phenom kind) + /* *---------------------------------------------------------------------- * @@ -815,6 +818,17 @@ CPUID_FAMILY_IS_K8STAR(uint32 _eax) CPUID_FAMILY_IS_K8MOBILE(_eax); } +/* + * AMD Barcelona (of either Opteron or Phenom kind). + */ +static INLINE Bool +CPUID_MODEL_IS_BARCELONA(uint32 v) // IN: %eax from CPUID with %eax=1. +{ + /* Assumes the CPU manufacturer is AMD. */ + return CPUID_FAMILY_IS_K8L(v) && + CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_BARCELONA_02; +} + #define CPUID_TYPE_PRIMARY 0 #define CPUID_TYPE_OVERDRIVE 1 diff --git a/open-vm-tools/modules/freebsd/vmmemctl/vm_assert.h b/open-vm-tools/modules/freebsd/vmmemctl/vm_assert.h index 2e70ae1fe..d6bb8f7db 100644 --- a/open-vm-tools/modules/freebsd/vmmemctl/vm_assert.h +++ b/open-vm-tools/modules/freebsd/vmmemctl/vm_assert.h @@ -83,10 +83,26 @@ EXTERN void WarningThrottled(uint32 *count, const char *fmt, ...) */ #ifndef ASSERT_IFNOT - #ifdef __cplusplus - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : (void)0) + /* + * PR 271512: When compiling with gcc, catch assignments inside an ASSERT. + * + * 'UNLIKELY' is defined with __builtin_expect, which does not warn when passed an + * assignment (gcc bug 36050). + * To get around this, we put 'cond' in an 'if' statement and make sure it never gets + * executed by putting that inside of 'if (0)'. + * We use gcc's statement expression syntax to make ASSERT an expression because some + * code uses it that way. + * + * Since statement expression syntax is a gcc extension and since it's not clear if + * this is a problem with other compilers, the ASSERT definition was not changed for + * them. Using a bare 'cond' with the ternary operator may provide a solution. + */ + #ifdef __GNUC__ + #define ASSERT_IFNOT(cond, panic) \ + ({if (UNLIKELY(!(cond))) { panic; if (0) { if (cond) { ; } } } (void)0;}) #else - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : 0) + #define ASSERT_IFNOT(cond, panic) \ + (UNLIKELY(!(cond)) ? (panic) : (void)0) #endif #endif diff --git a/open-vm-tools/modules/freebsd/vmmemctl/vm_basic_types.h b/open-vm-tools/modules/freebsd/vmmemctl/vm_basic_types.h index 53ee91746..638e9ce6c 100644 --- a/open-vm-tools/modules/freebsd/vmmemctl/vm_basic_types.h +++ b/open-vm-tools/modules/freebsd/vmmemctl/vm_basic_types.h @@ -338,6 +338,9 @@ typedef int64 VmTimeVirtualClock; /* Virtual Clock kept in CPU cycles */ #ifdef _MSC_VER #define CONST64(c) c##I64 #define CONST64U(c) c##uI64 +#elif defined __APPLE__ +#define CONST64(c) c##LL +#define CONST64U(c) c##uLL #elif __GNUC__ #ifdef VM_X86_64 #define CONST64(c) c##L @@ -489,10 +492,11 @@ typedef uint64 MPN64; */ typedef VA32 UserVA32; typedef VA64 UserVA64; -typedef UserVA32 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA64 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA32 UserVA32Const; /* Userspace ptr to data that we may only read. */ typedef UserVA64 UserVA64Const; /* Used by 64-bit syscalls until conversion is finished. */ #ifdef VMKERNEL -typedef UserVA32 UserVA; +typedef UserVA64 UserVA; #else typedef void * UserVA; #endif diff --git a/open-vm-tools/modules/freebsd/vmxnet/vm_basic_types.h b/open-vm-tools/modules/freebsd/vmxnet/vm_basic_types.h index 53ee91746..638e9ce6c 100644 --- a/open-vm-tools/modules/freebsd/vmxnet/vm_basic_types.h +++ b/open-vm-tools/modules/freebsd/vmxnet/vm_basic_types.h @@ -338,6 +338,9 @@ typedef int64 VmTimeVirtualClock; /* Virtual Clock kept in CPU cycles */ #ifdef _MSC_VER #define CONST64(c) c##I64 #define CONST64U(c) c##uI64 +#elif defined __APPLE__ +#define CONST64(c) c##LL +#define CONST64U(c) c##uLL #elif __GNUC__ #ifdef VM_X86_64 #define CONST64(c) c##L @@ -489,10 +492,11 @@ typedef uint64 MPN64; */ typedef VA32 UserVA32; typedef VA64 UserVA64; -typedef UserVA32 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA64 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA32 UserVA32Const; /* Userspace ptr to data that we may only read. */ typedef UserVA64 UserVA64Const; /* Used by 64-bit syscalls until conversion is finished. */ #ifdef VMKERNEL -typedef UserVA32 UserVA; +typedef UserVA64 UserVA; #else typedef void * UserVA; #endif diff --git a/open-vm-tools/modules/linux/dkms.conf b/open-vm-tools/modules/linux/dkms.conf index eaf6fbe59..b8dac81e5 100644 --- a/open-vm-tools/modules/linux/dkms.conf +++ b/open-vm-tools/modules/linux/dkms.conf @@ -1,5 +1,5 @@ PACKAGE_NAME=open-vm-tools -PACKAGE_VERSION=2009.01.21 +PACKAGE_VERSION=2009.02.18 MAKE[0]="make -C pvscsi VM_UNAME=\$kernelver; \ make -C vmblock VM_UNAME=\$kernelver; \ make -C vmhgfs VM_UNAME=\$kernelver; \ diff --git a/open-vm-tools/modules/linux/pvscsi/pvscsi.c b/open-vm-tools/modules/linux/pvscsi/pvscsi.c index e026d0daf..8f880b5a0 100644 --- a/open-vm-tools/modules/linux/pvscsi/pvscsi.c +++ b/open-vm-tools/modules/linux/pvscsi/pvscsi.c @@ -61,7 +61,6 @@ #define PVSCSI_LINUX_DRIVER_DESC "VMware PVSCSI driver" -/* Module definitions */ MODULE_DESCRIPTION(PVSCSI_LINUX_DRIVER_DESC); MODULE_AUTHOR("VMware, Inc."); MODULE_LICENSE("GPL v2"); @@ -188,6 +187,9 @@ struct pvscsi_ctx { struct scsi_cmnd *cmd; struct PVSCSISGList *sgl; struct list_head list; + dma_addr_t dataPA; + dma_addr_t sensePA; + dma_addr_t sglPA; }; /* Private per-adapter struct */ @@ -201,15 +203,19 @@ struct pvscsi_adapter { char log; spinlock_t hw_lock; + RingReqDesc *req_ring; unsigned req_pages; unsigned req_depth; + dma_addr_t reqRingPA; RingCmpDesc *cmp_ring; unsigned cmp_pages; unsigned cmp_depth; + dma_addr_t cmpRingPA; RingsState *ring_state; + dma_addr_t ringStatePA; struct pci_dev *dev; struct Scsi_Host *host; @@ -267,12 +273,12 @@ static void __exit pvscsi_exit(void) static inline void pvscsi_free_sgls(struct pvscsi_adapter *adapter) { - unsigned i, max; struct pvscsi_ctx *ctx = adapter->cmd_map; + unsigned i; - max = adapter->req_depth; - for (i = 0; i < max; ++i, ++ctx) - kfree(ctx->sgl); + for (i = 0; i < adapter->req_depth; ++i, ++ctx) + pci_free_consistent(adapter->dev, PAGE_SIZE, ctx->sgl, + ctx->sglPA); } static inline int pvscsi_setup_msix(struct pvscsi_adapter *adapter) @@ -296,12 +302,18 @@ static inline int pvscsi_setup_msix(struct pvscsi_adapter *adapter) #endif } -static inline void pvscsi_shutdown_msi(struct pvscsi_adapter *adapter) +static inline void pvscsi_shutdown_intr(struct pvscsi_adapter *adapter) { + if (adapter->irq) { + free_irq(adapter->irq, adapter); + adapter->irq = 0; + } #ifdef CONFIG_PCI_MSI - if (adapter->use_msi) + if (adapter->use_msi) { pci_disable_msi(adapter->dev); + adapter->use_msi = 0; + } if (adapter->use_msix) { #if 0 @@ -312,16 +324,14 @@ static inline void pvscsi_shutdown_msi(struct pvscsi_adapter *adapter) free_irq(adapter->irq_vectors[i], adapter); #endif pci_disable_msix(adapter->dev); + adapter->use_msix = 0; } #endif } static void pvscsi_release_resources(struct pvscsi_adapter *adapter) { - if (adapter->irq) - free_irq(adapter->irq, adapter); - - pvscsi_shutdown_msi(adapter); + pvscsi_shutdown_intr(adapter); if (adapter->iomap) iounmap((void *)adapter->iomap); @@ -333,26 +343,35 @@ static void pvscsi_release_resources(struct pvscsi_adapter *adapter) kfree(adapter->cmd_map); } - kfree(adapter->ring_state); + if (adapter->ring_state) + pci_free_consistent(adapter->dev, PAGE_SIZE, + adapter->ring_state, adapter->ringStatePA); if (adapter->req_ring) - vfree(adapter->req_ring); + pci_free_consistent(adapter->dev, + adapter->req_pages * PAGE_SIZE, + adapter->req_ring, adapter->reqRingPA); if (adapter->cmp_ring) - vfree(adapter->cmp_ring); + pci_free_consistent(adapter->dev, + adapter->cmp_pages * PAGE_SIZE, + adapter->cmp_ring, adapter->cmpRingPA); } static int __devinit pvscsi_allocate_rings(struct pvscsi_adapter *adapter) { - adapter->ring_state = kmalloc(PAGE_SIZE, GFP_KERNEL); + adapter->ring_state = pci_alloc_consistent(adapter->dev, PAGE_SIZE, + &adapter->ringStatePA); if (!adapter->ring_state) return -ENOMEM; - adapter->req_pages = MIN(PVSCSI_MAX_NUM_PAGES_REQ_RING, - pvscsi_ring_pages); - adapter->req_depth = adapter->req_pages * + adapter->req_pages = MIN(PVSCSI_MAX_NUM_PAGES_REQ_RING, + pvscsi_ring_pages); + adapter->req_depth = adapter->req_pages * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE; - adapter->req_ring = vmalloc(adapter->req_pages * PAGE_SIZE); + adapter->req_ring = pci_alloc_consistent(adapter->dev, + adapter->req_pages * PAGE_SIZE, + &adapter->reqRingPA); if (!adapter->req_ring) return -ENOMEM; @@ -360,10 +379,15 @@ static int __devinit pvscsi_allocate_rings(struct pvscsi_adapter *adapter) pvscsi_ring_pages); adapter->cmp_depth = adapter->cmp_pages * PVSCSI_MAX_NUM_CMP_ENTRIES_PER_PAGE; - adapter->cmp_ring = vmalloc(adapter->cmp_pages * PAGE_SIZE); + adapter->cmp_ring = pci_alloc_consistent(adapter->dev, + adapter->cmp_pages * PAGE_SIZE, + &adapter->cmpRingPA); if (!adapter->cmp_ring) return -ENOMEM; + BUG_ON(adapter->ringStatePA & ~PAGE_MASK); + BUG_ON(adapter->reqRingPA & ~PAGE_MASK); + BUG_ON(adapter->cmpRingPA & ~PAGE_MASK); return 0; } @@ -383,20 +407,21 @@ static int __devinit pvscsi_allocate_rings(struct pvscsi_adapter *adapter) static int __devinit pvscsi_allocate_sg(struct pvscsi_adapter *adapter) { struct pvscsi_ctx *ctx; - unsigned max; int i; ctx = adapter->cmd_map; - max = adapter->req_depth; ASSERT_ON_COMPILE(sizeof(struct PVSCSISGList) <= PAGE_SIZE); - for (i = 0; i < max; ++i, ++ctx) { - ctx->sgl = kmalloc(PAGE_SIZE, GFP_KERNEL); - BUG_ON((long)ctx->sgl & ~PAGE_MASK); + for (i = 0; i < adapter->req_depth; ++i, ++ctx) { + ctx->sgl = pci_alloc_consistent(adapter->dev, PAGE_SIZE, + &ctx->sglPA); + BUG_ON(ctx->sglPA & ~PAGE_MASK); if (!ctx->sgl) { for (; i >= 0; --i, --ctx) { - kfree(ctx->sgl); + pci_free_consistent(adapter->dev, PAGE_SIZE, + ctx->sgl, ctx->sglPA); ctx->sgl = NULL; + ctx->sglPA = 0; } return -ENOMEM; } @@ -425,14 +450,16 @@ pvscsi_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_dev_func = pdev->devfn; pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev); - if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) || - pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK)) { - if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) && - pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)) { - printk(KERN_ERR "pvscsi: unable to set usable DMA mask\n"); - goto out_disable_device; - } - } + if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) == 0 && + pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK) == 0) { + printk(KERN_INFO "pvscsi: using 64bit dma\n"); + } else if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) == 0 && + pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK) == 0) { + printk(KERN_INFO "pvscsi: using 32bit dma\n"); + } else { + printk(KERN_ERR "pvscsi: failed to set DMA mask\n"); + goto out_disable_device; + } printk(KERN_NOTICE "pvscsi: found VMware PVSCSI rev %d on " "bus %d:slot %d:func %d\n", rev, pci_bus, @@ -515,8 +542,7 @@ pvscsi_probe(struct pci_dev *pdev, const struct pci_device_id *id) printk(KERN_ERR "pvscsi: failed to allocate memory.\n"); goto out_reset_adapter; } - memset(adapter->cmd_map, 0, adapter->req_depth * - sizeof(struct pvscsi_ctx)); + memset(adapter->cmd_map, 0, adapter->req_depth * sizeof(struct pvscsi_ctx)); INIT_LIST_HEAD(&adapter->cmd_pool); for (i = 0; i < adapter->req_depth; i++) { @@ -607,7 +633,7 @@ pvscsi_find_context(const struct pvscsi_adapter *adapter, struct scsi_cmnd *cmd) } static struct pvscsi_ctx * -pvscsi_allocate_context(struct pvscsi_adapter *adapter, struct scsi_cmnd *cmd) +pvscsi_acquire_context(struct pvscsi_adapter *adapter, struct scsi_cmnd *cmd) { struct pvscsi_ctx *ctx; @@ -621,16 +647,11 @@ pvscsi_allocate_context(struct pvscsi_adapter *adapter, struct scsi_cmnd *cmd) return ctx; } -static inline struct scsi_cmnd * -pvscsi_free_context(struct pvscsi_adapter *adapter, struct pvscsi_ctx *ctx) +static inline void +pvscsi_release_context(struct pvscsi_adapter *adapter, struct pvscsi_ctx *ctx) { - struct scsi_cmnd *cmd; - - cmd = ctx->cmd; ctx->cmd = NULL; list_add(&ctx->list, &adapter->cmd_pool); - - return cmd; } /* @@ -658,10 +679,10 @@ static int pvscsi_queue(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) spin_lock_irqsave(&adapter->hw_lock, flags); - ctx = pvscsi_allocate_context(adapter, cmd); + ctx = pvscsi_acquire_context(adapter, cmd); if (!ctx || pvscsi_queue_ring(adapter, ctx, cmd) != 0) { if (ctx) - pvscsi_free_context(adapter, ctx); + pvscsi_release_context(adapter, ctx); spin_unlock_irqrestore(&adapter->hw_lock, flags); return SCSI_MLQUEUE_HOST_BUSY; } @@ -714,14 +735,29 @@ out: } static inline void -pvscsi_free_sg(const struct pvscsi_adapter *adapter, struct scsi_cmnd *cmd) +pvscsi_unmap_buffers(const struct pvscsi_adapter *adapter, struct pvscsi_ctx *ctx) { - unsigned count = scsi_sg_count(cmd); + struct scsi_cmnd *cmd; + unsigned bufflen; - if (count) { - struct scatterlist *sg = scsi_sglist(cmd); - pci_unmap_sg(adapter->dev, sg, count, cmd->sc_data_direction); - } + cmd = ctx->cmd; + bufflen = scsi_bufflen(cmd); + + if (bufflen != 0) { + unsigned count = scsi_sg_count(cmd); + + if (count != 0) { + pci_unmap_sg(adapter->dev, scsi_sglist(cmd), count, + cmd->sc_data_direction); + } else { + pci_unmap_single(adapter->dev, ctx->dataPA, bufflen, + cmd->sc_data_direction); + } + } + if (cmd->sense_buffer) { + pci_unmap_single(adapter->dev, ctx->sensePA, SCSI_SENSE_BUFFERSIZE, + PCI_DMA_FROMDEVICE); + } } /* @@ -739,8 +775,8 @@ static inline void pvscsi_reset_all(struct pvscsi_adapter *adapter) struct scsi_cmnd *cmd = ctx->cmd; if (cmd) { printk(KERN_ERR "pvscsi: forced reset on cmd %p\n", cmd); - pvscsi_free_sg(adapter, cmd); - pvscsi_free_context(adapter, ctx); + pvscsi_unmap_buffers(adapter, ctx); + pvscsi_release_context(adapter, ctx); cmd->result = (DID_RESET << 16); cmd->scsi_done(cmd); } @@ -859,21 +895,12 @@ static irqreturn_t pvscsi_isr COMPAT_IRQ_HANDLER_ARGS(irq, devp) return IRQ_RETVAL(handled); } -/* Shutdown an entire device, syncinc all outstanding I/O */ +/* Shutdown an entire device, sync'ing all outstanding I/O */ static void __pvscsi_shutdown(struct pvscsi_adapter *adapter) { pvscsi_write_intr_mask(adapter, 0); - if (adapter->irq) { - free_irq(adapter->irq, adapter); - adapter->irq = 0; - } -#ifdef CONFIG_PCI_MSI - if (adapter->use_msi) { - pci_disable_msi(adapter->dev); - adapter->use_msi = 0; - } -#endif + pvscsi_shutdown_intr(adapter); pvscsi_process_request_ring(adapter); pvscsi_process_completion_ring(adapter); @@ -914,27 +941,23 @@ static void pvscsi_remove(struct pci_dev *pdev) * **************************************************************/ -static inline struct PVSCSISGList * +static void pvscsi_create_sg(struct pvscsi_ctx *ctx, struct scatterlist *sg, unsigned count) { unsigned i; - struct PVSCSISGList *sgl; struct PVSCSISGElement *sge; - sgl = ctx->sgl; - BUG_ON(count > PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT); - sge = &sgl->sge[0]; + sge = &ctx->sgl->sge[0]; for (i = 0; i < count; i++, sg++) { - sge[i].addr = sg_dma_address(sg); + sge[i].addr = sg_dma_address(sg); sge[i].length = sg_dma_len(sg); - sge[i].flags = 0; + sge[i].flags = 0; } - - return sgl; } + /* * Map all data buffers for a command into PCI space and * setup the scatter/gather list if needed. @@ -951,21 +974,24 @@ pvscsi_map_buffers(struct pvscsi_adapter *adapter, struct pvscsi_ctx *ctx, if (bufflen == 0) return; - count = scsi_sg_count(cmd); + count = scsi_sg_count(cmd); if (count != 0) { struct scatterlist *sg = scsi_sglist(cmd); int segs = pci_map_sg(adapter->dev, sg, count, - cmd->sc_data_direction); + cmd->sc_data_direction); if (segs > 1) { - struct PVSCSISGList *sgl; + pvscsi_create_sg(ctx, sg, segs); e->flags |= PVSCSI_FLAG_CMD_WITH_SG_LIST; - sgl = pvscsi_create_sg(ctx, sg, segs); - e->dataAddr = __pa(sgl); - } else + e->dataAddr = ctx->sglPA; + } else { e->dataAddr = sg_dma_address(sg); + } } else { - e->dataAddr = __pa(scsi_request_buffer(cmd)); + ctx->dataPA = pci_map_single(adapter->dev, + scsi_request_buffer(cmd), bufflen, + cmd->sc_data_direction); + e->dataAddr = ctx->dataPA; } } @@ -1000,19 +1026,22 @@ pvscsi_queue_ring(struct pvscsi_adapter *adapter, struct pvscsi_ctx *ctx, { struct scsi_device *sdev; sdev = cmd->device; - e->bus = sdev->channel; + e->bus = sdev->channel; e->target = sdev->id; memset(e->lun, 0, sizeof(e->lun)); e->lun[1] = sdev->lun; } if (cmd->sense_buffer) { + ctx->sensePA = pci_map_single(adapter->dev, cmd->sense_buffer, + SCSI_SENSE_BUFFERSIZE, + PCI_DMA_FROMDEVICE); + e->senseAddr = ctx->sensePA; e->senseLen = SCSI_SENSE_BUFFERSIZE; - e->senseAddr = __pa(cmd->sense_buffer); } else { - e->senseLen = 0; + e->senseLen = 0; e->senseAddr = 0; } - e->cdbLen = cmd->cmd_len; + e->cdbLen = cmd->cmd_len; e->vcpuHint = smp_processor_id(); memcpy(e->cdb, cmd->cmnd, e->cdbLen); @@ -1060,7 +1089,9 @@ pvscsi_complete_request(struct pvscsi_adapter *adapter, const RingCmpDesc *e) u32 sdstat = e->scsiStatus; ctx = pvscsi_get_context(adapter, e->context); - cmd = pvscsi_free_context(adapter, ctx); + cmd = ctx->cmd; + pvscsi_unmap_buffers(adapter, ctx); + pvscsi_release_context(adapter, ctx); cmd->result = 0; if (sdstat != SAM_STAT_GOOD && @@ -1146,8 +1177,6 @@ pvscsi_complete_request(struct pvscsi_adapter *adapter, const RingCmpDesc *e) LOG(3, "cmd=%p %x ctx=%p result=0x%x status=0x%x,%x\n", cmd, cmd->cmnd[0], ctx, cmd->result, btstat, sdstat); - pvscsi_free_sg(adapter, cmd); - cmd->scsi_done(cmd); } @@ -1275,19 +1304,21 @@ static void ll_device_reset(const struct pvscsi_adapter *adapter, u32 target) static void pvscsi_setup_rings(struct pvscsi_adapter *adapter) { struct CmdDescSetupRings cmd; - unsigned i, pages; - void *base; + dma_addr_t base; + unsigned i; memset(&cmd, 0, sizeof(cmd)); - cmd.ringsStatePPN = __pa(adapter->ring_state) >> PAGE_SHIFT; + cmd.ringsStatePPN = adapter->ringStatePA >> PAGE_SHIFT; + cmd.reqRingNumPages = adapter->req_pages; + cmd.cmpRingNumPages = adapter->cmp_pages; - cmd.reqRingNumPages = pages = adapter->req_pages; - for (i = 0, base = adapter->req_ring; i < pages; i++, base += PAGE_SIZE) - cmd.reqRingPPNs[i] = page_to_pfn(vmalloc_to_page(base)); + for (i = 0, base = adapter->reqRingPA; i < adapter->req_pages; + i++, base += PAGE_SIZE) + cmd.reqRingPPNs[i] = base >> PAGE_SHIFT; - cmd.cmpRingNumPages = pages = adapter->cmp_pages; - for (i = 0, base = adapter->cmp_ring; i < pages; i++, base += PAGE_SIZE) - cmd.cmpRingPPNs[i] = page_to_pfn(vmalloc_to_page(base)); + for (i = 0, base = adapter->cmpRingPA; i < adapter->cmp_pages; + i++, base += PAGE_SIZE) + cmd.cmpRingPPNs[i] = base >> PAGE_SHIFT; memset(adapter->ring_state, 0, PAGE_SIZE); memset(adapter->req_ring, 0, adapter->req_pages * PAGE_SIZE); diff --git a/open-vm-tools/modules/linux/pvscsi/pvscsi_version.h b/open-vm-tools/modules/linux/pvscsi/pvscsi_version.h index ee3edde1a..5949d6609 100644 --- a/open-vm-tools/modules/linux/pvscsi/pvscsi_version.h +++ b/open-vm-tools/modules/linux/pvscsi/pvscsi_version.h @@ -25,8 +25,8 @@ #ifndef _PVSCSI_VERSION_H_ #define _PVSCSI_VERSION_H_ -#define PVSCSI_DRIVER_VERSION 0.0.0.6 -#define PVSCSI_DRIVER_VERSION_COMMAS 0,0,0,6 -#define PVSCSI_DRIVER_VERSION_STRING "0.0.0.6" +#define PVSCSI_DRIVER_VERSION 0.0.0.7 +#define PVSCSI_DRIVER_VERSION_COMMAS 0,0,0,7 +#define PVSCSI_DRIVER_VERSION_STRING "0.0.0.7" #endif /* _PVSCSI_VERSION_H_ */ diff --git a/open-vm-tools/modules/linux/pvscsi/vm_assert.h b/open-vm-tools/modules/linux/pvscsi/vm_assert.h index 2e70ae1fe..d6bb8f7db 100644 --- a/open-vm-tools/modules/linux/pvscsi/vm_assert.h +++ b/open-vm-tools/modules/linux/pvscsi/vm_assert.h @@ -83,10 +83,26 @@ EXTERN void WarningThrottled(uint32 *count, const char *fmt, ...) */ #ifndef ASSERT_IFNOT - #ifdef __cplusplus - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : (void)0) + /* + * PR 271512: When compiling with gcc, catch assignments inside an ASSERT. + * + * 'UNLIKELY' is defined with __builtin_expect, which does not warn when passed an + * assignment (gcc bug 36050). + * To get around this, we put 'cond' in an 'if' statement and make sure it never gets + * executed by putting that inside of 'if (0)'. + * We use gcc's statement expression syntax to make ASSERT an expression because some + * code uses it that way. + * + * Since statement expression syntax is a gcc extension and since it's not clear if + * this is a problem with other compilers, the ASSERT definition was not changed for + * them. Using a bare 'cond' with the ternary operator may provide a solution. + */ + #ifdef __GNUC__ + #define ASSERT_IFNOT(cond, panic) \ + ({if (UNLIKELY(!(cond))) { panic; if (0) { if (cond) { ; } } } (void)0;}) #else - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : 0) + #define ASSERT_IFNOT(cond, panic) \ + (UNLIKELY(!(cond)) ? (panic) : (void)0) #endif #endif diff --git a/open-vm-tools/modules/linux/pvscsi/vm_basic_types.h b/open-vm-tools/modules/linux/pvscsi/vm_basic_types.h index 53ee91746..638e9ce6c 100644 --- a/open-vm-tools/modules/linux/pvscsi/vm_basic_types.h +++ b/open-vm-tools/modules/linux/pvscsi/vm_basic_types.h @@ -338,6 +338,9 @@ typedef int64 VmTimeVirtualClock; /* Virtual Clock kept in CPU cycles */ #ifdef _MSC_VER #define CONST64(c) c##I64 #define CONST64U(c) c##uI64 +#elif defined __APPLE__ +#define CONST64(c) c##LL +#define CONST64U(c) c##uLL #elif __GNUC__ #ifdef VM_X86_64 #define CONST64(c) c##L @@ -489,10 +492,11 @@ typedef uint64 MPN64; */ typedef VA32 UserVA32; typedef VA64 UserVA64; -typedef UserVA32 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA64 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA32 UserVA32Const; /* Userspace ptr to data that we may only read. */ typedef UserVA64 UserVA64Const; /* Used by 64-bit syscalls until conversion is finished. */ #ifdef VMKERNEL -typedef UserVA32 UserVA; +typedef UserVA64 UserVA; #else typedef void * UserVA; #endif diff --git a/open-vm-tools/modules/linux/vmblock/include/compat_sched.h b/open-vm-tools/modules/linux/vmblock/include/compat_sched.h index efb9fc44d..a9696ebcc 100644 --- a/open-vm-tools/modules/linux/vmblock/include/compat_sched.h +++ b/open-vm-tools/modules/linux/vmblock/include/compat_sched.h @@ -271,6 +271,20 @@ static inline int compat_try_to_freeze(void) { return 0; } #define compat_set_freezable() do {} while (0) #endif +/* + * Around 2.6.27 kernel stopped sending signals to kernel + * threads being frozen, instead threads have to check + * freezing() or use wait_event_freezable(). Unfortunately + * wait_event_freezable() completely hides the fact that + * thread was frozen from calling code and sometimes we do + * want to know that. + */ +#ifdef PF_FREEZER_NOSIG +#define compat_wait_check_freezing() freezing(current) +#else +#define compat_wait_check_freezing() (0) +#endif + /* * Since 2.6.27-rc2 kill_proc() is gone... Replacement (GPL-only!) * API is available since 2.6.19. Use them from 2.6.27-rc1 up. diff --git a/open-vm-tools/modules/linux/vmblock/include/vm_assert.h b/open-vm-tools/modules/linux/vmblock/include/vm_assert.h index 2e70ae1fe..d6bb8f7db 100644 --- a/open-vm-tools/modules/linux/vmblock/include/vm_assert.h +++ b/open-vm-tools/modules/linux/vmblock/include/vm_assert.h @@ -83,10 +83,26 @@ EXTERN void WarningThrottled(uint32 *count, const char *fmt, ...) */ #ifndef ASSERT_IFNOT - #ifdef __cplusplus - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : (void)0) + /* + * PR 271512: When compiling with gcc, catch assignments inside an ASSERT. + * + * 'UNLIKELY' is defined with __builtin_expect, which does not warn when passed an + * assignment (gcc bug 36050). + * To get around this, we put 'cond' in an 'if' statement and make sure it never gets + * executed by putting that inside of 'if (0)'. + * We use gcc's statement expression syntax to make ASSERT an expression because some + * code uses it that way. + * + * Since statement expression syntax is a gcc extension and since it's not clear if + * this is a problem with other compilers, the ASSERT definition was not changed for + * them. Using a bare 'cond' with the ternary operator may provide a solution. + */ + #ifdef __GNUC__ + #define ASSERT_IFNOT(cond, panic) \ + ({if (UNLIKELY(!(cond))) { panic; if (0) { if (cond) { ; } } } (void)0;}) #else - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : 0) + #define ASSERT_IFNOT(cond, panic) \ + (UNLIKELY(!(cond)) ? (panic) : (void)0) #endif #endif diff --git a/open-vm-tools/modules/linux/vmblock/include/vm_basic_types.h b/open-vm-tools/modules/linux/vmblock/include/vm_basic_types.h index 53ee91746..638e9ce6c 100644 --- a/open-vm-tools/modules/linux/vmblock/include/vm_basic_types.h +++ b/open-vm-tools/modules/linux/vmblock/include/vm_basic_types.h @@ -338,6 +338,9 @@ typedef int64 VmTimeVirtualClock; /* Virtual Clock kept in CPU cycles */ #ifdef _MSC_VER #define CONST64(c) c##I64 #define CONST64U(c) c##uI64 +#elif defined __APPLE__ +#define CONST64(c) c##LL +#define CONST64U(c) c##uLL #elif __GNUC__ #ifdef VM_X86_64 #define CONST64(c) c##L @@ -489,10 +492,11 @@ typedef uint64 MPN64; */ typedef VA32 UserVA32; typedef VA64 UserVA64; -typedef UserVA32 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA64 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA32 UserVA32Const; /* Userspace ptr to data that we may only read. */ typedef UserVA64 UserVA64Const; /* Used by 64-bit syscalls until conversion is finished. */ #ifdef VMKERNEL -typedef UserVA32 UserVA; +typedef UserVA64 UserVA; #else typedef void * UserVA; #endif diff --git a/open-vm-tools/modules/linux/vmci/compat_sched.h b/open-vm-tools/modules/linux/vmci/compat_sched.h index efb9fc44d..a9696ebcc 100644 --- a/open-vm-tools/modules/linux/vmci/compat_sched.h +++ b/open-vm-tools/modules/linux/vmci/compat_sched.h @@ -271,6 +271,20 @@ static inline int compat_try_to_freeze(void) { return 0; } #define compat_set_freezable() do {} while (0) #endif +/* + * Around 2.6.27 kernel stopped sending signals to kernel + * threads being frozen, instead threads have to check + * freezing() or use wait_event_freezable(). Unfortunately + * wait_event_freezable() completely hides the fact that + * thread was frozen from calling code and sometimes we do + * want to know that. + */ +#ifdef PF_FREEZER_NOSIG +#define compat_wait_check_freezing() freezing(current) +#else +#define compat_wait_check_freezing() (0) +#endif + /* * Since 2.6.27-rc2 kill_proc() is gone... Replacement (GPL-only!) * API is available since 2.6.19. Use them from 2.6.27-rc1 up. diff --git a/open-vm-tools/modules/linux/vmci/kernelStubs.h b/open-vm-tools/modules/linux/vmci/kernelStubs.h index 6c443265f..3757177fa 100644 --- a/open-vm-tools/modules/linux/vmci/kernelStubs.h +++ b/open-vm-tools/modules/linux/vmci/kernelStubs.h @@ -61,13 +61,17 @@ # endif # include # include +# elif defined(sun) +# include "vm_basic_types.h" +# include +# include #endif /* * Function Prototypes */ -#if defined(linux) || defined(__APPLE__) /* if (linux) || (__APPLE__) { */ +#if defined(linux) || defined(__APPLE__) || defined (sun) # ifdef linux /* if (linux) { */ char *strdup(const char *source); diff --git a/open-vm-tools/modules/linux/vmci/vm_assert.h b/open-vm-tools/modules/linux/vmci/vm_assert.h index 2e70ae1fe..d6bb8f7db 100644 --- a/open-vm-tools/modules/linux/vmci/vm_assert.h +++ b/open-vm-tools/modules/linux/vmci/vm_assert.h @@ -83,10 +83,26 @@ EXTERN void WarningThrottled(uint32 *count, const char *fmt, ...) */ #ifndef ASSERT_IFNOT - #ifdef __cplusplus - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : (void)0) + /* + * PR 271512: When compiling with gcc, catch assignments inside an ASSERT. + * + * 'UNLIKELY' is defined with __builtin_expect, which does not warn when passed an + * assignment (gcc bug 36050). + * To get around this, we put 'cond' in an 'if' statement and make sure it never gets + * executed by putting that inside of 'if (0)'. + * We use gcc's statement expression syntax to make ASSERT an expression because some + * code uses it that way. + * + * Since statement expression syntax is a gcc extension and since it's not clear if + * this is a problem with other compilers, the ASSERT definition was not changed for + * them. Using a bare 'cond' with the ternary operator may provide a solution. + */ + #ifdef __GNUC__ + #define ASSERT_IFNOT(cond, panic) \ + ({if (UNLIKELY(!(cond))) { panic; if (0) { if (cond) { ; } } } (void)0;}) #else - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : 0) + #define ASSERT_IFNOT(cond, panic) \ + (UNLIKELY(!(cond)) ? (panic) : (void)0) #endif #endif diff --git a/open-vm-tools/modules/linux/vmci/vm_atomic.h b/open-vm-tools/modules/linux/vmci/vm_atomic.h index abfa5fccf..37c8174c7 100644 --- a/open-vm-tools/modules/linux/vmci/vm_atomic.h +++ b/open-vm-tools/modules/linux/vmci/vm_atomic.h @@ -196,10 +196,29 @@ static INLINE void AtomicEpilogue(void) { #ifdef ATOMIC_USE_FENCE +#ifdef VMM + /* The monitor conditionally patches out the lfence when not needed.*/ + /* Construct a MonitorPatchTextEntry in the .patchtext section. */ + asm volatile ("1:\n\t" + "lfence\n\t" + "2:\n\t" + ".pushsection .patchtext\n\t" +#ifdef VMM32 + ".long 1b\n\t" + ".long 0\n\t" + ".long 2b\n\t" + ".long 0\n\t" +#else + ".quad 1b\n\t" + ".quad 2b\n\t" +#endif + ".popsection\n\t" ::: "memory"); +#else if (UNLIKELY(AtomicUseFence)) { asm volatile ("lfence" ::: "memory"); } #endif +#endif } diff --git a/open-vm-tools/modules/linux/vmci/vm_basic_types.h b/open-vm-tools/modules/linux/vmci/vm_basic_types.h index 53ee91746..638e9ce6c 100644 --- a/open-vm-tools/modules/linux/vmci/vm_basic_types.h +++ b/open-vm-tools/modules/linux/vmci/vm_basic_types.h @@ -338,6 +338,9 @@ typedef int64 VmTimeVirtualClock; /* Virtual Clock kept in CPU cycles */ #ifdef _MSC_VER #define CONST64(c) c##I64 #define CONST64U(c) c##uI64 +#elif defined __APPLE__ +#define CONST64(c) c##LL +#define CONST64U(c) c##uLL #elif __GNUC__ #ifdef VM_X86_64 #define CONST64(c) c##L @@ -489,10 +492,11 @@ typedef uint64 MPN64; */ typedef VA32 UserVA32; typedef VA64 UserVA64; -typedef UserVA32 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA64 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA32 UserVA32Const; /* Userspace ptr to data that we may only read. */ typedef UserVA64 UserVA64Const; /* Used by 64-bit syscalls until conversion is finished. */ #ifdef VMKERNEL -typedef UserVA32 UserVA; +typedef UserVA64 UserVA; #else typedef void * UserVA; #endif diff --git a/open-vm-tools/modules/linux/vmci/vmciKernelIf.c b/open-vm-tools/modules/linux/vmci/vmciKernelIf.c index 11fa12913..db7a551cb 100644 --- a/open-vm-tools/modules/linux/vmci/vmciKernelIf.c +++ b/open-vm-tools/modules/linux/vmci/vmciKernelIf.c @@ -1425,10 +1425,18 @@ VMCIHost_FinishAttach(PageStoreAttachInfo *attach, // IN consumeQ->queueHeaderPtr = kmap(attach->consumePages[0]); consumeQ->page = &attach->consumePages[1]; #else + /* + * Host queue pair support for earlier kernels temporarily + * disabled. See bug 365496. + */ + + ASSERT_NOT_IMPLEMENTED(FALSE); +#if 0 produceQ->queueHeaderPtr = kmap(attach->produceIoBuf->maplist[0]); produceQ->page = &attach->produceIoBuf->maplist[1]; consumeQ->queueHeaderPtr = kmap(attach->consumeIoBuf->maplist[0]); consumeQ->page = &attach->consumeIoBuf->maplist[1]; +#endif #endif return VMCI_SUCCESS; @@ -1461,9 +1469,17 @@ VMCIHost_DetachMappings(PageStoreAttachInfo *attach) // IN kunmap(attach->producePages[0]); kunmap(attach->consumePages[0]); #else + /* + * Host queue pair support for earlier kernels temporarily + * disabled. See bug 365496. + */ + + ASSERT_NOT_IMPLEMENTED(FALSE); +#if 0 kunmap(attach->produceIoBuf->maplist[0]); kunmap(attach->consumeIoBuf->maplist[0]); #endif +#endif } @@ -1487,10 +1503,10 @@ VMCIHost_DetachMappings(PageStoreAttachInfo *attach) // IN int VMCIHost_GetUserMemory(PageStoreAttachInfo *attach) // IN/OUT { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) int retval; int err = VMCI_SUCCESS; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) attach->producePages = VMCI_AllocKernelMem(attach->numProducePages * sizeof attach->producePages[0], @@ -1567,7 +1583,13 @@ errorDealloc: return err; #else + /* + * Host queue pair support for earlier kernels temporarily + * disabled. See bug 365496. + */ + ASSERT_NOT_IMPLEMENTED(FALSE); +#if 0 attach->produceIoBuf = VMCI_AllocKernelMem(sizeof *attach->produceIoBuf, VMCI_MEMORY_NORMAL); if (attach->produceIoBuf == NULL) { @@ -1611,7 +1633,9 @@ out: } } return err; - +#else + return FALSE; +#endif #endif } @@ -1663,6 +1687,13 @@ VMCIHost_ReleaseUserMemory(PageStoreAttachInfo *attach) // IN attach->numConsumePages * sizeof attach->consumePages[0]); #else + /* + * Host queue pair support for earlier kernels temporarily + * disabled. See bug 365496. + */ + + ASSERT_NOT_IMPLEMENTED(FALSE); +#if 0 mark_dirty_kiobuf(attach->produceIoBuf, attach->numProducePages * PAGE_SIZE); unmap_kiobuf(attach->produceIoBuf); @@ -1676,6 +1707,7 @@ VMCIHost_ReleaseUserMemory(PageStoreAttachInfo *attach) // IN VMCI_FreeKernelMem(attach->consumeIoBuf, sizeof *attach->consumeIoBuf); #endif +#endif } #endif diff --git a/open-vm-tools/modules/linux/vmci/vmci_queue_pair.h b/open-vm-tools/modules/linux/vmci/vmci_queue_pair.h index 05884f5c8..8d9e5c51f 100644 --- a/open-vm-tools/modules/linux/vmci/vmci_queue_pair.h +++ b/open-vm-tools/modules/linux/vmci/vmci_queue_pair.h @@ -844,7 +844,7 @@ VMCIQueue_EnqueueV(VMCIQueue *produceQueue, // IN: * * Results: * VMCI_ERROR_QUEUEPAIR_NODATA if no data was available to dequeue. - * VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue + * VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue * (as defined by the queue size). * Otherwise the number of bytes dequeued is returned. * @@ -860,7 +860,8 @@ __VMCIQueue_Dequeue(VMCIQueue *produceQueue, // IN: const uint64 consumeQSize, // IN: void *buf, // IN: size_t bufSize, // IN: - VMCIMemcpyFromQueueFunc memcpyFromQueue) // IN: + VMCIMemcpyFromQueueFunc memcpyFromQueue, // IN: + Bool updateConsumer) // IN: { const int64 bufReady = VMCIQueue_BufReady(consumeQueue, produceQueue, consumeQSize); @@ -884,7 +885,11 @@ __VMCIQueue_Dequeue(VMCIQueue *produceQueue, // IN: memcpyFromQueue(buf, 0, consumeQueue, head, tmp); memcpyFromQueue(buf, tmp, consumeQueue, 0, written - tmp); } - AddPointer(&VMCIQueue_GetHeader(produceQueue)->consumerHead, written, consumeQSize); + + if (updateConsumer) { + AddPointer(&VMCIQueue_GetHeader(produceQueue)->consumerHead, written, + consumeQSize); + } return written; } @@ -918,7 +923,7 @@ VMCIQueue_Dequeue(VMCIQueue *produceQueue, // IN: size_t bufSize) // IN: { return __VMCIQueue_Dequeue(produceQueue, consumeQueue, consumeQSize, - buf, bufSize, VMCIMemcpyFromQueue); + buf, bufSize, VMCIMemcpyFromQueue, TRUE); } @@ -952,7 +957,75 @@ VMCIQueue_DequeueV(VMCIQueue *produceQueue, // IN: size_t iovSize) // IN: { return __VMCIQueue_Dequeue(produceQueue, consumeQueue, consumeQSize, - (void *)iov, iovSize, VMCIMemcpyFromQueueV); + (void *)iov, iovSize, VMCIMemcpyFromQueueV, TRUE); +} +#endif + + +/* + *----------------------------------------------------------------------------- + * + * VMCIQueue_Peek -- + * + * Reads data (if available) from the given consume queue. Copies data + * to the provided user buffer but does not update the consumer counter + * of the queue. + * + * Results: + * VMCI_ERROR_QUEUEPAIR_NODATA if no data was available to dequeue. + * VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue + * (as defined by the queue size). + * Otherwise the number of bytes copied to user buffer is returned. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static INLINE ssize_t +VMCIQueue_Peek(VMCIQueue *produceQueue, // IN: + const VMCIQueue *consumeQueue, // IN: + const uint64 consumeQSize, // IN: + void *buf, // IN: + size_t bufSize) // IN: +{ + return __VMCIQueue_Dequeue(produceQueue, consumeQueue, consumeQSize, + buf, bufSize, VMCIMemcpyFromQueue, FALSE); +} + + +#if defined(SOLARIS) || (defined(__linux__) && defined(__KERNEL__)) +/* + *----------------------------------------------------------------------------- + * + * VMCIQueue_PeekV -- + * + * Reads data (if available) from the given consume queue. Copies data + * to the provided user iovec but does not update the consumer counter + * of the queue. + * + * Results: + * VMCI_ERROR_QUEUEPAIR_NODATA if no data was available to dequeue. + * VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue + * (as defined by the queue size). + * Otherwise the number of bytes copied to user iovec is returned. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static INLINE ssize_t +VMCIQueue_PeekV(VMCIQueue *produceQueue, // IN: + const VMCIQueue *consumeQueue, // IN: + const uint64 consumeQSize, // IN: + struct iovec *iov, // IN: + size_t iovSize) // IN: +{ + return __VMCIQueue_Dequeue(produceQueue, consumeQueue, consumeQSize, + (void *)iov, iovSize, VMCIMemcpyFromQueueV, FALSE); } #endif diff --git a/open-vm-tools/modules/linux/vmhgfs/backdoor_def.h b/open-vm-tools/modules/linux/vmhgfs/backdoor_def.h index b902e6eb2..84a44f063 100644 --- a/open-vm-tools/modules/linux/vmhgfs/backdoor_def.h +++ b/open-vm-tools/modules/linux/vmhgfs/backdoor_def.h @@ -118,7 +118,8 @@ #define BDOOR_CMD_ACPI_HOTPLUG_MEMORY 57 #define BDOOR_CMD_ACPI_HOTPLUG_CBRET 58 #define BDOOR_CMD_GET_HOST_VIDEO_MODES 59 -#define BDOOR_CMD_MAX 60 +#define BDOOR_CMD_ACPI_HOTPLUG_CPU 60 +#define BDOOR_CMD_MAX 61 /* * IMPORTANT NOTE: When modifying the behavior of an existing backdoor command, diff --git a/open-vm-tools/modules/linux/vmhgfs/bdhandler.c b/open-vm-tools/modules/linux/vmhgfs/bdhandler.c index 4c469235f..6239b829f 100644 --- a/open-vm-tools/modules/linux/vmhgfs/bdhandler.c +++ b/open-vm-tools/modules/linux/vmhgfs/bdhandler.c @@ -278,6 +278,7 @@ HgfsBdHandler(void *data) // Ignored wait_event_interruptible(hgfsReqThreadWait, test_bit(HGFS_REQ_THREAD_SEND, &hgfsReqThreadFlags) || + compat_wait_check_freezing() || compat_kthread_should_stop()); /* diff --git a/open-vm-tools/modules/linux/vmhgfs/compat_cred.h b/open-vm-tools/modules/linux/vmhgfs/compat_cred.h new file mode 100644 index 000000000..e3bab071a --- /dev/null +++ b/open-vm-tools/modules/linux/vmhgfs/compat_cred.h @@ -0,0 +1,39 @@ +/********************************************************* + * Copyright (C) 2002 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2 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 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 + * + *********************************************************/ + +#ifndef __COMPAT_CRED_H__ +# define __COMPAT_CRED_H__ + + +/* + * Include linux/cred.h via linux/sched.h - it is not nice, but + * as cpp does not have #ifexist... + */ +#include + +#if !defined(current_fsuid) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) +#define current_uid() (current->uid) +#define current_euid() (current->euid) +#define current_fsuid() (current->fsuid) +#define current_gid() (current->gid) +#define current_egid() (current->egid) +#define current_fsgid() (current->fsgid) +#endif + + +#endif /* __COMPAT_CRED_H__ */ diff --git a/open-vm-tools/modules/linux/vmhgfs/compat_pagemap.h b/open-vm-tools/modules/linux/vmhgfs/compat_pagemap.h new file mode 100644 index 000000000..8b37c7d7a --- /dev/null +++ b/open-vm-tools/modules/linux/vmhgfs/compat_pagemap.h @@ -0,0 +1,37 @@ +/********************************************************* + * 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 General Public License as published by the + * Free Software Foundation version 2 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 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 + * + *********************************************************/ + +#ifndef __COMPAT_PAGEMAP_H__ +# define __COMPAT_PAGEMAP_H__ + + +#include + +/* + * AOP_FLAG_NOFS was defined in the same changeset that + * grab_cache_page_write_begin() was introduced. + */ +#ifdef AOP_FLAG_NOFS +#define compat_grab_cache_page_write_begin(mapping, index, flags) \ + grab_cache_page_write_begin((mapping), (index), (flags)) +#else +#define compat_grab_cache_page_write_begin(mapping, index, flags) \ + __grab_cache_page((mapping), (index)); +#endif + +#endif /* __COMPAT_PAGEMAP_H__ */ diff --git a/open-vm-tools/modules/linux/vmhgfs/compat_sched.h b/open-vm-tools/modules/linux/vmhgfs/compat_sched.h index efb9fc44d..a9696ebcc 100644 --- a/open-vm-tools/modules/linux/vmhgfs/compat_sched.h +++ b/open-vm-tools/modules/linux/vmhgfs/compat_sched.h @@ -271,6 +271,20 @@ static inline int compat_try_to_freeze(void) { return 0; } #define compat_set_freezable() do {} while (0) #endif +/* + * Around 2.6.27 kernel stopped sending signals to kernel + * threads being frozen, instead threads have to check + * freezing() or use wait_event_freezable(). Unfortunately + * wait_event_freezable() completely hides the fact that + * thread was frozen from calling code and sometimes we do + * want to know that. + */ +#ifdef PF_FREEZER_NOSIG +#define compat_wait_check_freezing() freezing(current) +#else +#define compat_wait_check_freezing() (0) +#endif + /* * Since 2.6.27-rc2 kill_proc() is gone... Replacement (GPL-only!) * API is available since 2.6.19. Use them from 2.6.27-rc1 up. diff --git a/open-vm-tools/modules/linux/vmhgfs/file.c b/open-vm-tools/modules/linux/vmhgfs/file.c index d55e32013..ece9780d2 100644 --- a/open-vm-tools/modules/linux/vmhgfs/file.c +++ b/open-vm-tools/modules/linux/vmhgfs/file.c @@ -28,6 +28,7 @@ #include #include #include +#include "compat_cred.h" #include "compat_fs.h" #include "compat_kernel.h" #include "compat_slab.h" @@ -634,7 +635,7 @@ HgfsOpen(struct inode *inode, // IN: Inode of the file to open iparent = dparent->d_inode; HgfsSetUidGid(iparent, file->f_dentry, - current->fsuid, current->fsgid); + current_fsuid(), current_fsgid()); dput(dparent); } diff --git a/open-vm-tools/modules/linux/vmhgfs/filesystem.c b/open-vm-tools/modules/linux/vmhgfs/filesystem.c index e4103dd19..4bcba9fe5 100644 --- a/open-vm-tools/modules/linux/vmhgfs/filesystem.c +++ b/open-vm-tools/modules/linux/vmhgfs/filesystem.c @@ -32,6 +32,7 @@ #include #include #include "compat_completion.h" +#include "compat_cred.h" #include "compat_dcache.h" #include "compat_fs.h" #include "compat_kernel.h" @@ -256,13 +257,13 @@ HgfsInitSuperInfo(HgfsMountInfo *mountInfo) // IN: Passed down from the user if (si->uidSet) { si->uid = mountInfo->uid; } else { - si->uid = current->uid; + si->uid = current_uid(); } si->gidSet = mountInfo->gidSet; if (si->gidSet) { si->gid = mountInfo->gid; } else { - si->gid = current->gid; + si->gid = current_gid(); } si->fmask = mountInfo->fmask; si->dmask = mountInfo->dmask; diff --git a/open-vm-tools/modules/linux/vmhgfs/hgfsProto.h b/open-vm-tools/modules/linux/vmhgfs/hgfsProto.h index b3ce70382..925a287c5 100644 --- a/open-vm-tools/modules/linux/vmhgfs/hgfsProto.h +++ b/open-vm-tools/modules/linux/vmhgfs/hgfsProto.h @@ -178,6 +178,55 @@ typedef uint8 HgfsPermissions; #define HGFS_PERM_WRITE 2 #define HGFS_PERM_EXEC 1 +/* + * Access mode bits. + * + * Different operating systems have different set of file access mode. + * Here are constants that are rich enough to describe all access modes in an OS + * independent way. + */ + +typedef uint32 HgfsAccessMode; +/* + * Generic access rights control coarse grain access for the file. + * A particular generic rigth can be expanded into different set of specific rights + * on different OS. + */ + +/* + * HGFS_MODE_GENERIC_READ means ability to read file data and read various file + * attributes and properties. + */ +#define HGFS_MODE_GENERIC_READ (1 << 0) +/* + * HGFS_MODE_GENERIC_WRITE means ability to write file data and updaate various file + * attributes and properties. + */ +#define HGFS_MODE_GENERIC_WRITE (1 << 1) +/* + * HGFS_MODE_GENERIC_EXECUE means ability to execute file. For network redirectors + * ability to execute usualy implies ability to read data; for local file systems + * HGFS_MODE_GENERIC_EXECUTE does not imply ability to read data. + */ +#define HGFS_MODE_GENERIC_EXECUTE (1 << 2) + +/* Specific rights define fine grain access modes. */ +#define HGFS_MODE_READ_DATA (1 << 3) // Ability to read file data +#define HGFS_MODE_WRITE_DATA (1 << 4) // Ability to writge file data +#define HGFS_MODE_APPEND_DATA (1 << 5) // Appending data to the end of file +#define HGFS_MODE_DELETE (1 << 6) // Ability to delete the file +#define HGFS_MODE_TRAVERSE_DIRECTORY (1 << 7) // Ability to access files in a directory +#define HGFS_MODE_LIST_DIRECTORY (1 << 8) // Ability to list file names +#define HGFS_MODE_ADD_SUBDIRECTORY (1 << 9) // Ability to create a new subdirectory +#define HGFS_MODE_ADD_FILE (1 << 10) // Ability to create a new file +#define HGFS_MODE_DELETE_CHILD (1 << 11) // Ability to delete file/subdirectory +#define HGFS_MODE_READ_ATTRIBUTES (1 << 12) // Ability to read attributes +#define HGFS_MODE_WRITE_ATTRIBUTES (1 << 13) // Ability to write attributes +#define HGFS_MODE_READ_EXTATTRIBUTES (1 << 14) // Ability to read extended attributes +#define HGFS_MODE_WRITE_EXTATTRIBUTES (1 << 15) // Ability to write extended attributes +#define HGFS_MODE_READ_SECURITY (1 << 16) // Ability to read permissions/ACLs/owner +#define HGFS_MODE_WRITE_SECURITY (1 << 17) // Ability to change permissions/ACLs +#define HGFS_MODE_TAKE_OWNERSHIP (1 << 18) // Ability to change file owner/group /* * Server-side locking (oplocks and leases). @@ -356,6 +405,15 @@ typedef uint64 HgfsAttrValid; * only to determine if the ID is valid. */ #define HGFS_ATTR_VALID_NON_STATIC_FILEID (1 << 16) +/* + * File permissions that are in effect for the user which runs HGFS server. + * Client needs to know effective permissions in order to implement access(2). + * Client can't derive it from group/owner/other permissions because of two resaons: + * 1. It does not know user/group id of the user which runs HGFS server + * 2. Effective permissions account for additional restrictions that may be imposed + * by host file system, for example by ACL. + */ +#define HGFS_ATTR_VALID_EFFECTIVE_PERMS (1 << 17) /* @@ -400,7 +458,7 @@ struct HgfsAttrV2 { uint32 groupId; /* group identifier, ignored by Windows */ uint64 hostFileId; /* File Id of the file on host: inode_t on Linux */ uint32 volumeId; /* volume identifier, non-zero is valid. */ - uint32 reserved1; /* Reserved for future use */ + uint32 effectivePerms; /* Permissions in effect for the user on the host. */ uint64 reserved2; /* Reserved for future use */ } #include "vmware_pack_end.h" diff --git a/open-vm-tools/modules/linux/vmhgfs/inode.c b/open-vm-tools/modules/linux/vmhgfs/inode.c index 86b869f47..e94b032fa 100644 --- a/open-vm-tools/modules/linux/vmhgfs/inode.c +++ b/open-vm-tools/modules/linux/vmhgfs/inode.c @@ -28,6 +28,7 @@ #include #include +#include "compat_cred.h" #include "compat_fs.h" #include "compat_highmem.h" #include "compat_kernel.h" @@ -1194,7 +1195,7 @@ HgfsMkdir(struct inode *dir, // IN: Inode of parent directory * a Linux machine and as root, but we might as well give it * a go. */ - HgfsSetUidGid(dir, dentry, current->fsuid, current->fsgid); + HgfsSetUidGid(dir, dentry, current_fsuid(), current_fsgid()); } /* @@ -1629,8 +1630,8 @@ HgfsPackSymlinkCreateRequest(struct dentry *dentry, // IN: File pointer for th /* Convert target name to CPName-lite format. */ CPNameLite_ConvertTo(targetName, targetNameBytes - 1, '/'); - *targetNameLength = result; - req->payloadSize += result; + *targetNameLength = targetNameBytes - 1; + req->payloadSize += targetNameBytes - 1; return 0; } diff --git a/open-vm-tools/modules/linux/vmhgfs/kernelStubs.h b/open-vm-tools/modules/linux/vmhgfs/kernelStubs.h index 6c443265f..3757177fa 100644 --- a/open-vm-tools/modules/linux/vmhgfs/kernelStubs.h +++ b/open-vm-tools/modules/linux/vmhgfs/kernelStubs.h @@ -61,13 +61,17 @@ # endif # include # include +# elif defined(sun) +# include "vm_basic_types.h" +# include +# include #endif /* * Function Prototypes */ -#if defined(linux) || defined(__APPLE__) /* if (linux) || (__APPLE__) { */ +#if defined(linux) || defined(__APPLE__) || defined (sun) # ifdef linux /* if (linux) { */ char *strdup(const char *source); diff --git a/open-vm-tools/modules/linux/vmhgfs/page.c b/open-vm-tools/modules/linux/vmhgfs/page.c index 4fc9b5352..81f2d8c49 100644 --- a/open-vm-tools/modules/linux/vmhgfs/page.c +++ b/open-vm-tools/modules/linux/vmhgfs/page.c @@ -31,6 +31,7 @@ #include "compat_page-flags.h" #include "compat_fs.h" #include "compat_kernel.h" +#include "compat_pagemap.h" #ifdef HGFS_ENABLE_WRITEBACK #include #endif @@ -758,10 +759,16 @@ HgfsDoWriteBegin(struct page *page, // IN: Page to be written unsigned pageFrom, // IN: Starting page offset unsigned pageTo) // IN: Ending page offset { +#ifndef HGFS_ENABLE_WRITEBACK ASSERT(page); -#ifdef HGFS_ENABLE_WRITEBACK - loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT; - loff_t currentFileSize = compat_i_size_read(page->mapping->host); +#else + loff_t offset; + loff_t currentFileSize; + + ASSERT(page); + + offset = (loff_t)page->index << PAGE_CACHE_SHIFT; + currentFileSize = compat_i_size_read(page->mapping->host); /* * If we are doing a partial write into a new page (beyond end of @@ -864,7 +871,7 @@ HgfsWriteBegin(struct file *file, // IN: File to be written unsigned pageTo = pos + len; struct page *page; - page = __grab_cache_page(mapping, index); + page = compat_grab_cache_page_write_begin(mapping, index, flags); if (page == NULL) { return -ENOMEM; } diff --git a/open-vm-tools/modules/linux/vmhgfs/vm_assert.h b/open-vm-tools/modules/linux/vmhgfs/vm_assert.h index 2e70ae1fe..d6bb8f7db 100644 --- a/open-vm-tools/modules/linux/vmhgfs/vm_assert.h +++ b/open-vm-tools/modules/linux/vmhgfs/vm_assert.h @@ -83,10 +83,26 @@ EXTERN void WarningThrottled(uint32 *count, const char *fmt, ...) */ #ifndef ASSERT_IFNOT - #ifdef __cplusplus - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : (void)0) + /* + * PR 271512: When compiling with gcc, catch assignments inside an ASSERT. + * + * 'UNLIKELY' is defined with __builtin_expect, which does not warn when passed an + * assignment (gcc bug 36050). + * To get around this, we put 'cond' in an 'if' statement and make sure it never gets + * executed by putting that inside of 'if (0)'. + * We use gcc's statement expression syntax to make ASSERT an expression because some + * code uses it that way. + * + * Since statement expression syntax is a gcc extension and since it's not clear if + * this is a problem with other compilers, the ASSERT definition was not changed for + * them. Using a bare 'cond' with the ternary operator may provide a solution. + */ + #ifdef __GNUC__ + #define ASSERT_IFNOT(cond, panic) \ + ({if (UNLIKELY(!(cond))) { panic; if (0) { if (cond) { ; } } } (void)0;}) #else - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : 0) + #define ASSERT_IFNOT(cond, panic) \ + (UNLIKELY(!(cond)) ? (panic) : (void)0) #endif #endif diff --git a/open-vm-tools/modules/linux/vmhgfs/vm_basic_asm.h b/open-vm-tools/modules/linux/vmhgfs/vm_basic_asm.h index 5bc4d6b71..0180e3919 100644 --- a/open-vm-tools/modules/linux/vmhgfs/vm_basic_asm.h +++ b/open-vm-tools/modules/linux/vmhgfs/vm_basic_asm.h @@ -853,4 +853,21 @@ RDTSC(void) #error No compiler defined for RDTSC #endif /* __GNUC__ */ +/* + *----------------------------------------------------------------------------- + * + * DEBUGBREAK -- + * + * Does an int3 for MSVC / GCC. This is a macro to make sure int3 is + * always inlined. + * + *----------------------------------------------------------------------------- + */ + +#ifdef _MSC_VER +#define DEBUGBREAK() __debugbreak() +#else +#define DEBUGBREAK() __asm__ (" int $3 ") +#endif + #endif diff --git a/open-vm-tools/modules/linux/vmhgfs/vm_basic_types.h b/open-vm-tools/modules/linux/vmhgfs/vm_basic_types.h index 53ee91746..638e9ce6c 100644 --- a/open-vm-tools/modules/linux/vmhgfs/vm_basic_types.h +++ b/open-vm-tools/modules/linux/vmhgfs/vm_basic_types.h @@ -338,6 +338,9 @@ typedef int64 VmTimeVirtualClock; /* Virtual Clock kept in CPU cycles */ #ifdef _MSC_VER #define CONST64(c) c##I64 #define CONST64U(c) c##uI64 +#elif defined __APPLE__ +#define CONST64(c) c##LL +#define CONST64U(c) c##uLL #elif __GNUC__ #ifdef VM_X86_64 #define CONST64(c) c##L @@ -489,10 +492,11 @@ typedef uint64 MPN64; */ typedef VA32 UserVA32; typedef VA64 UserVA64; -typedef UserVA32 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA64 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA32 UserVA32Const; /* Userspace ptr to data that we may only read. */ typedef UserVA64 UserVA64Const; /* Used by 64-bit syscalls until conversion is finished. */ #ifdef VMKERNEL -typedef UserVA32 UserVA; +typedef UserVA64 UserVA; #else typedef void * UserVA; #endif diff --git a/open-vm-tools/modules/linux/vmhgfs/x86cpuid.h b/open-vm-tools/modules/linux/vmhgfs/x86cpuid.h index 0a2f3c6be..337fd8cd9 100644 --- a/open-vm-tools/modules/linux/vmhgfs/x86cpuid.h +++ b/open-vm-tools/modules/linux/vmhgfs/x86cpuid.h @@ -440,7 +440,7 @@ FLAGDEF( 87, EDX, AMD, 4, 1, TM, NA, IGNORE, 0, FALSE) FLAGDEF( 87, EDX, AMD, 5, 1, STC, NA, IGNORE, 0, FALSE) \ FLAGDEF( 87, EDX, AMD, 6, 1, 100MHZSTEPS, NA, IGNORE, 0, FALSE) \ FLAGDEF( 87, EDX, AMD, 7, 1, HWPSTATE, NA, IGNORE, 0, FALSE) \ -FLAGDEF( 87, EDX, AMD, 8, 1, TSC_INVARIANT, NA, IGNORE, 0, FALSE) \ +FLAGDEF( 87, EDX, COMMON, 8, 1, TSC_INVARIANT, NA, IGNORE, 0, FALSE) \ FIELDDEFA(88, EAX, COMMON, 0, 8, PHYSBITS, NA, IGNORE, 0, FALSE, PHYS_BITS) \ FIELDDEFA(88, EAX, COMMON, 8, 8, VIRTBITS, NA, IGNORE, 0, FALSE, VIRT_BITS) \ FIELDDEFA(88, ECX, AMD, 0, 8, CORE_COUNT, NA, IGNORE, 0, FALSE, AMD_CORE_COUNT) \ @@ -644,6 +644,9 @@ FIELD_FUNC(MWAIT_C4_SUBSTATE, CPUID_INTEL_ID5EDX_MWAIT_C4_SUBSTATE) #define CPUID_MODEL_PIII_08 8 #define CPUID_MODEL_PIII_0A 10 +/* AMD model information */ +#define CPUID_MODEL_BARCELONA_02 0x02 // Barcelona (both Opteron & Phenom kind) + /* *---------------------------------------------------------------------- * @@ -815,6 +818,17 @@ CPUID_FAMILY_IS_K8STAR(uint32 _eax) CPUID_FAMILY_IS_K8MOBILE(_eax); } +/* + * AMD Barcelona (of either Opteron or Phenom kind). + */ +static INLINE Bool +CPUID_MODEL_IS_BARCELONA(uint32 v) // IN: %eax from CPUID with %eax=1. +{ + /* Assumes the CPU manufacturer is AMD. */ + return CPUID_FAMILY_IS_K8L(v) && + CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_BARCELONA_02; +} + #define CPUID_TYPE_PRIMARY 0 #define CPUID_TYPE_OVERDRIVE 1 diff --git a/open-vm-tools/modules/linux/vmmemctl/backdoor_def.h b/open-vm-tools/modules/linux/vmmemctl/backdoor_def.h index b902e6eb2..84a44f063 100644 --- a/open-vm-tools/modules/linux/vmmemctl/backdoor_def.h +++ b/open-vm-tools/modules/linux/vmmemctl/backdoor_def.h @@ -118,7 +118,8 @@ #define BDOOR_CMD_ACPI_HOTPLUG_MEMORY 57 #define BDOOR_CMD_ACPI_HOTPLUG_CBRET 58 #define BDOOR_CMD_GET_HOST_VIDEO_MODES 59 -#define BDOOR_CMD_MAX 60 +#define BDOOR_CMD_ACPI_HOTPLUG_CPU 60 +#define BDOOR_CMD_MAX 61 /* * IMPORTANT NOTE: When modifying the behavior of an existing backdoor command, diff --git a/open-vm-tools/modules/linux/vmmemctl/compat_sched.h b/open-vm-tools/modules/linux/vmmemctl/compat_sched.h index efb9fc44d..a9696ebcc 100644 --- a/open-vm-tools/modules/linux/vmmemctl/compat_sched.h +++ b/open-vm-tools/modules/linux/vmmemctl/compat_sched.h @@ -271,6 +271,20 @@ static inline int compat_try_to_freeze(void) { return 0; } #define compat_set_freezable() do {} while (0) #endif +/* + * Around 2.6.27 kernel stopped sending signals to kernel + * threads being frozen, instead threads have to check + * freezing() or use wait_event_freezable(). Unfortunately + * wait_event_freezable() completely hides the fact that + * thread was frozen from calling code and sometimes we do + * want to know that. + */ +#ifdef PF_FREEZER_NOSIG +#define compat_wait_check_freezing() freezing(current) +#else +#define compat_wait_check_freezing() (0) +#endif + /* * Since 2.6.27-rc2 kill_proc() is gone... Replacement (GPL-only!) * API is available since 2.6.19. Use them from 2.6.27-rc1 up. diff --git a/open-vm-tools/modules/linux/vmmemctl/os.c b/open-vm-tools/modules/linux/vmmemctl/os.c index bf88e3928..aab656777 100644 --- a/open-vm-tools/modules/linux/vmmemctl/os.c +++ b/open-vm-tools/modules/linux/vmmemctl/os.c @@ -417,7 +417,9 @@ static int os_timer_thread_loop(void *data) /* main loop */ while (1) { /* sleep for specified period */ - wait_event_interruptible_timeout(t->delay, compat_kthread_should_stop(), + wait_event_interruptible_timeout(t->delay, + compat_wait_check_freezing() || + compat_kthread_should_stop(), t->period); compat_try_to_freeze(); if (compat_kthread_should_stop()) { diff --git a/open-vm-tools/modules/linux/vmmemctl/vm_assert.h b/open-vm-tools/modules/linux/vmmemctl/vm_assert.h index 2e70ae1fe..d6bb8f7db 100644 --- a/open-vm-tools/modules/linux/vmmemctl/vm_assert.h +++ b/open-vm-tools/modules/linux/vmmemctl/vm_assert.h @@ -83,10 +83,26 @@ EXTERN void WarningThrottled(uint32 *count, const char *fmt, ...) */ #ifndef ASSERT_IFNOT - #ifdef __cplusplus - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : (void)0) + /* + * PR 271512: When compiling with gcc, catch assignments inside an ASSERT. + * + * 'UNLIKELY' is defined with __builtin_expect, which does not warn when passed an + * assignment (gcc bug 36050). + * To get around this, we put 'cond' in an 'if' statement and make sure it never gets + * executed by putting that inside of 'if (0)'. + * We use gcc's statement expression syntax to make ASSERT an expression because some + * code uses it that way. + * + * Since statement expression syntax is a gcc extension and since it's not clear if + * this is a problem with other compilers, the ASSERT definition was not changed for + * them. Using a bare 'cond' with the ternary operator may provide a solution. + */ + #ifdef __GNUC__ + #define ASSERT_IFNOT(cond, panic) \ + ({if (UNLIKELY(!(cond))) { panic; if (0) { if (cond) { ; } } } (void)0;}) #else - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : 0) + #define ASSERT_IFNOT(cond, panic) \ + (UNLIKELY(!(cond)) ? (panic) : (void)0) #endif #endif diff --git a/open-vm-tools/modules/linux/vmmemctl/vm_basic_types.h b/open-vm-tools/modules/linux/vmmemctl/vm_basic_types.h index 53ee91746..638e9ce6c 100644 --- a/open-vm-tools/modules/linux/vmmemctl/vm_basic_types.h +++ b/open-vm-tools/modules/linux/vmmemctl/vm_basic_types.h @@ -338,6 +338,9 @@ typedef int64 VmTimeVirtualClock; /* Virtual Clock kept in CPU cycles */ #ifdef _MSC_VER #define CONST64(c) c##I64 #define CONST64U(c) c##uI64 +#elif defined __APPLE__ +#define CONST64(c) c##LL +#define CONST64U(c) c##uLL #elif __GNUC__ #ifdef VM_X86_64 #define CONST64(c) c##L @@ -489,10 +492,11 @@ typedef uint64 MPN64; */ typedef VA32 UserVA32; typedef VA64 UserVA64; -typedef UserVA32 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA64 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA32 UserVA32Const; /* Userspace ptr to data that we may only read. */ typedef UserVA64 UserVA64Const; /* Used by 64-bit syscalls until conversion is finished. */ #ifdef VMKERNEL -typedef UserVA32 UserVA; +typedef UserVA64 UserVA; #else typedef void * UserVA; #endif diff --git a/open-vm-tools/modules/linux/vmxnet/compat_pci_mapping.h b/open-vm-tools/modules/linux/vmxnet/compat_pci_mapping.h new file mode 100644 index 000000000..fd9129992 --- /dev/null +++ b/open-vm-tools/modules/linux/vmxnet/compat_pci_mapping.h @@ -0,0 +1,84 @@ +/********************************************************* + * Copyright (C) 2008 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2 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 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 + * + *********************************************************/ + +#ifndef __COMPAT_PCI_MAPPING_H__ +#define __COMPAT_PCI_MAPPING_H__ + +#include +#include +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,41) +typedef u32 dma_addr_t; + +static __inline__ int +get_order(unsigned long size) +{ + int order; + + size = (size - 1) >> (PAGE_SHIFT - 1); + order = -1; + do { + size >>= 1; + order++; + } while (size); + return order; +} + +static inline void * +compat_pci_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle) +{ + void *ptr = (void *)__get_free_pages(GFP_ATOMIC, get_order(size)); + if (ptr) { + memset(ptr, 0, size); + *dma_handle = virt_to_phys(ptr); + } + return ptr; +} + +static inline void +compat_pci_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr, + dma_addr_t dma_handle) +{ + free_pages((unsigned long)vaddr, get_order(size)); +} + +static inline dma_addr_t +compat_pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction) +{ + return virt_to_phys(ptr); +} + +static inline void +compat_pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, + size_t size, int direction) +{ +} + +#else +#define compat_pci_alloc_consistent(hwdev, size, dma_handle) \ + pci_alloc_consistent(hwdev, size, dma_handle) +#define compat_pci_free_consistent(hwdev, size, vaddr, dma_handle) \ + pci_free_consistent(hwdev, size, vaddr, dma_handle) +#define compat_pci_map_single(hwdev, ptr, size, direction) \ + pci_map_single(hwdev, ptr, size, direction) +#define compat_pci_unmap_single(hwdev, dma_addr, size, direction) \ + pci_unmap_single(hwdev, dma_addr, size, direction) +#endif + +#endif diff --git a/open-vm-tools/modules/linux/vmxnet/vm_basic_types.h b/open-vm-tools/modules/linux/vmxnet/vm_basic_types.h index 53ee91746..638e9ce6c 100644 --- a/open-vm-tools/modules/linux/vmxnet/vm_basic_types.h +++ b/open-vm-tools/modules/linux/vmxnet/vm_basic_types.h @@ -338,6 +338,9 @@ typedef int64 VmTimeVirtualClock; /* Virtual Clock kept in CPU cycles */ #ifdef _MSC_VER #define CONST64(c) c##I64 #define CONST64U(c) c##uI64 +#elif defined __APPLE__ +#define CONST64(c) c##LL +#define CONST64U(c) c##uLL #elif __GNUC__ #ifdef VM_X86_64 #define CONST64(c) c##L @@ -489,10 +492,11 @@ typedef uint64 MPN64; */ typedef VA32 UserVA32; typedef VA64 UserVA64; -typedef UserVA32 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA64 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA32 UserVA32Const; /* Userspace ptr to data that we may only read. */ typedef UserVA64 UserVA64Const; /* Used by 64-bit syscalls until conversion is finished. */ #ifdef VMKERNEL -typedef UserVA32 UserVA; +typedef UserVA64 UserVA; #else typedef void * UserVA; #endif diff --git a/open-vm-tools/modules/linux/vmxnet/vmxnet.c b/open-vm-tools/modules/linux/vmxnet/vmxnet.c index 9917c1e9d..3c6d95eda 100644 --- a/open-vm-tools/modules/linux/vmxnet/vmxnet.c +++ b/open-vm-tools/modules/linux/vmxnet/vmxnet.c @@ -29,6 +29,7 @@ #include "compat_slab.h" #include "compat_spinlock.h" #include "compat_pci.h" +#include "compat_pci_mapping.h" #include "compat_init.h" #include "compat_timer.h" #include @@ -97,10 +98,24 @@ static int debug = -1; #undef VMXNET_DO_ZERO_COPY #endif -#if defined(MAX_SKB_FRAGS) && ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,18) ) && ( LINUX_VERSION_CODE != KERNEL_VERSION(2, 6, 0) ) +#if defined(MAX_SKB_FRAGS) && \ + ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,18) ) && \ + ( LINUX_VERSION_CODE != KERNEL_VERSION(2, 6, 0) ) #define VMXNET_DO_ZERO_COPY #endif +#define VMXNET_GET_LO_ADDR(dma) ((uint32)(dma)) +#define VMXNET_GET_HI_ADDR(dma) ((uint16)(((uint64)(dma)) >> 32)) +#define VMXNET_GET_DMA_ADDR(sge) ((dma_addr_t)((((uint64)(sge).addrHi) << 32) | \ + (sge).addrLow)) + +#define VMXNET_FILL_SG(sg, dma, size)\ +do{\ + (sg).addrLow = VMXNET_GET_LO_ADDR(dma);\ + (sg).addrHi = VMXNET_GET_HI_ADDR(dma);\ + (sg).length = size;\ +} while (0) + #ifdef VMXNET_DO_ZERO_COPY #include #include @@ -128,17 +143,6 @@ static int debug = -1; #define PKT_OF_IPV4(skb) PKT_OF_PROTO(skb, ETH_TYPE_IP) -#define VMXNET_GET_LO_ADDR(dma) ((uint32)(dma)) -#define VMXNET_GET_HI_ADDR(dma) ((uint16)(((uint64)(dma)) >> 32)) -#define VMXNET_GET_DMA_ADDR(sge) ((dma_addr_t)((((uint64)(sge).addrHi) << 32) | (sge).addrLow)) - -#define VMXNET_FILL_SG(sg, dma, size)\ -do{\ - (sg).addrLow = VMXNET_GET_LO_ADDR(dma);\ - (sg).addrHi = VMXNET_GET_HI_ADDR(dma);\ - (sg).length = size;\ -} while (0) - #if defined(NETIF_F_TSO) #define VMXNET_DO_TSO @@ -151,6 +155,15 @@ do{\ #endif // VMXNET_DO_ZERO_COPY +#ifdef VMXNET_DO_TSO +static int disable_lro = 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 9) + module_param(disable_lro, int, 0); +#else + MODULE_PARM(disable_lro, "i"); +#endif +#endif + #ifdef VMXNET_DEBUG #define VMXNET_LOG(msg...) printk(KERN_ERR msg) #else @@ -710,7 +723,6 @@ vmxnet_probe_device(struct pci_dev *pdev, // IN: vmxnet PCI device Bool morphed = FALSE; Bool enhanced = FALSE; int i; - unsigned int driverDataSize; i = compat_pci_enable_device(pdev); if (i) { @@ -904,7 +916,8 @@ vmxnet_probe_device(struct pci_dev *pdev, // IN: vmxnet PCI device } if ((lp->capabilities & VMNET_CAP_LPD) && - (lp->features & VMXNET_FEATURE_LPD)) { + (lp->features & VMXNET_FEATURE_LPD) && + !disable_lro) { lp->lpd = TRUE; printk(" lpd"); } @@ -958,37 +971,28 @@ vmxnet_probe_device(struct pci_dev *pdev, // IN: vmxnet PCI device numTxBuffers = defNumTxBuffers; } - driverDataSize = - sizeof(Vmxnet2_DriverData) + - (numRxBuffers + numRxBuffers2) * sizeof(Vmxnet2_RxRingEntry) + - numTxBuffers * sizeof(Vmxnet2_TxRingEntry); - VMXNET_LOG("vmxnet: numRxBuffers=((%d+%d)*%d) numTxBuffers=(%d*%d) driverDataSize=%d\n", + lp->ddSize = sizeof(Vmxnet2_DriverData) + + (numRxBuffers + numRxBuffers2) * sizeof(Vmxnet2_RxRingEntry) + + numTxBuffers * sizeof(Vmxnet2_TxRingEntry); + VMXNET_LOG("vmxnet: numRxBuffers=((%d+%d)*%d) numTxBuffers=(%d*%d) ddSize=%d\n", numRxBuffers, numRxBuffers2, (uint32)sizeof(Vmxnet2_RxRingEntry), numTxBuffers, (uint32)sizeof(Vmxnet2_TxRingEntry), - driverDataSize); - lp->ddAllocated = kmalloc(driverDataSize + 15, GFP_DMA | GFP_KERNEL); - - if (!lp->ddAllocated) { + (int)lp->ddSize); + lp->dd = kmalloc(lp->ddSize, GFP_KERNEL | GFP_DMA); + if (!lp->dd) { printk(KERN_ERR "Unable to allocate memory for driver data\n"); goto free_dev; } - if ((uintptr_t)virt_to_bus(lp->ddAllocated) > SHARED_MEM_MAX) { - printk(KERN_ERR - "Unable to initialize driver data, address outside of shared area (0x%p)\n", - (void*)virt_to_bus(lp->ddAllocated)); - goto free_dev_dd; - } - - /* Align on paragraph boundary */ - lp->dd = (Vmxnet2_DriverData*)(((unsigned long)lp->ddAllocated + 15) & ~15UL); - memset(lp->dd, 0, driverDataSize); + lp->ddPA = compat_pci_map_single(lp->pdev, lp->dd, lp->ddSize, + PCI_DMA_BIDIRECTIONAL); + memset(lp->dd, 0, lp->ddSize); spin_lock_init(&lp->txLock); lp->numRxBuffers = numRxBuffers; lp->numRxBuffers2 = numRxBuffers2; lp->numTxBuffers = numTxBuffers; /* So that the vmkernel can check it is compatible */ lp->dd->magic = VMXNET2_MAGIC; - lp->dd->length = driverDataSize; + lp->dd->length = lp->ddSize; lp->name = VMXNET_CHIP_NAME; /* @@ -1020,16 +1024,13 @@ vmxnet_probe_device(struct pci_dev *pdev, // IN: vmxnet PCI device lp->dd->txBufferPhysLength = 0; if (lp->partialHeaderCopyEnabled) { - unsigned int txBufferSize; - - txBufferSize = numTxBuffers * TX_PKT_HEADER_SIZE; - lp->txBufferStartRaw = kmalloc(txBufferSize + PAGE_SIZE, - GFP_DMA | GFP_KERNEL); - if (lp->txBufferStartRaw) { - lp->txBufferStart = (char*)((unsigned long)(lp->txBufferStartRaw + PAGE_SIZE - 1) & - (unsigned long)~(PAGE_SIZE - 1)); - lp->dd->txBufferPhysStart = virt_to_phys(lp->txBufferStart); - lp->dd->txBufferPhysLength = txBufferSize; + lp->txBufferSize = numTxBuffers * TX_PKT_HEADER_SIZE; + lp->txBufferStart = kmalloc(lp->txBufferSize, GFP_KERNEL | GFP_DMA); + if (lp->txBufferStart) { + lp->txBufferPA = compat_pci_map_single(lp->pdev, lp->txBufferStart, + lp->txBufferSize, PCI_DMA_TODEVICE); + lp->dd->txBufferPhysStart = lp->txBufferPA; + lp->dd->txBufferPhysLength = lp->txBufferSize; lp->dd->txPktMaxSize = TX_PKT_HEADER_SIZE; } else { lp->partialHeaderCopyEnabled = FALSE; @@ -1088,8 +1089,16 @@ vmxnet_probe_device(struct pci_dev *pdev, // IN: vmxnet PCI device pci_set_drvdata(pdev, dev); return 0; -free_dev_dd:; - kfree(lp->ddAllocated); +free_dev_dd: +#ifdef VMXNET_DO_ZERO_COPY + if (lp->partialHeaderCopyEnabled && lp->txBufferStart) { + compat_pci_unmap_single(pdev, lp->txBufferPA, lp->txBufferSize, + PCI_DMA_TODEVICE); + kfree(lp->txBufferStart); + } +#endif + compat_pci_unmap_single(lp->pdev, lp->ddPA, lp->ddSize, PCI_DMA_BIDIRECTIONAL); + kfree(lp->dd); free_dev:; compat_free_netdev(dev); morph_back:; @@ -1166,12 +1175,15 @@ vmxnet_remove_device(struct pci_dev* pdev) } #ifdef VMXNET_DO_ZERO_COPY - if (lp->partialHeaderCopyEnabled){ - kfree(lp->txBufferStartRaw); + if (lp->partialHeaderCopyEnabled && lp->txBufferStart) { + compat_pci_unmap_single(pdev, lp->txBufferPA, lp->txBufferSize, + PCI_DMA_TODEVICE); + kfree(lp->txBufferStart); } #endif - kfree(lp->ddAllocated); + compat_pci_unmap_single(lp->pdev, lp->ddPA, lp->ddSize, PCI_DMA_BIDIRECTIONAL); + kfree(lp->dd); compat_free_netdev(dev); compat_pci_disable_device(pdev); } @@ -1221,7 +1233,7 @@ vmxnet_init_ring(struct net_device *dev) (uint64)offset, dd->length); for (i = 0; i < lp->numRxBuffers; i++) { - lp->rxSkbuff[i] = dev_alloc_skb(PKT_BUF_SZ); + lp->rxSkbuff[i] = dev_alloc_skb(PKT_BUF_SZ + COMPAT_NET_IP_ALIGN); if (lp->rxSkbuff[i] == NULL) { unsigned int j; @@ -1233,7 +1245,10 @@ vmxnet_init_ring(struct net_device *dev) return -ENOMEM; } - lp->rxRing[i].paddr = virt_to_bus(compat_skb_tail_pointer(lp->rxSkbuff[i])); + skb_reserve(lp->rxSkbuff[i], COMPAT_NET_IP_ALIGN); + + lp->rxRing[i].paddr = compat_pci_map_single(lp->pdev, lp->rxSkbuff[i]->data, + PKT_BUF_SZ, PCI_DMA_FROMDEVICE); lp->rxRing[i].bufferLength = PKT_BUF_SZ; lp->rxRing[i].actualLength = 0; lp->rxRing[i].ownership = VMXNET2_OWNERSHIP_NIC; @@ -1322,7 +1337,7 @@ vmxnet_open(struct net_device *dev) { struct Vmxnet_Private *lp = (Vmxnet_Private *)dev->priv; unsigned int ioaddr = dev->base_addr; - u32 paddr; + uint32 ddPA; if (dev->irq == 0 || request_irq(dev->irq, &vmxnet_interrupt, COMPAT_IRQF_SHARED, lp->name, (void *)dev)) { @@ -1330,18 +1345,16 @@ vmxnet_open(struct net_device *dev) } if (vmxnet_debug > 1) { - printk(KERN_DEBUG "%s: vmxnet_open() irq %d lp %#x.\n", - dev->name, dev->irq, - (u32) virt_to_bus(lp)); + printk(KERN_DEBUG "%s: vmxnet_open() irq %d lp %p.\n", + dev->name, dev->irq, lp); } if (vmxnet_init_ring(dev)) { return -ENOMEM; } - paddr = virt_to_bus(lp->dd); - - outl(paddr, ioaddr + VMXNET_INIT_ADDR); + ddPA = VMXNET_GET_LO_ADDR(lp->ddPA); // lp->dd was allocated out of ZONE_DMA32 + outl(ddPA, ioaddr + VMXNET_INIT_ADDR); outl(lp->dd->length, ioaddr + VMXNET_INIT_LENGTH); #ifdef VMXNET_DO_ZERO_COPY @@ -1588,12 +1601,16 @@ check_tx_queue(struct net_device *dev) break; } #ifdef VMXNET_DO_ZERO_COPY - if (lp->zeroCopyTx){ + if (lp->zeroCopyTx) { VMXNET_LOG("unmap txRing[%u]\n", dd->txDriverCur); vmxnet_unmap_buf(skb, &lp->txBufInfo[dd->txDriverCur], xre, lp->pdev); - } + } else #endif - + { + compat_pci_unmap_single(lp->pdev, xre->sg.sg[0].addrLow, + xre->sg.sg[0].length, + PCI_DMA_TODEVICE); + } if (lp->txBufInfo[dd->txDriverCur].eop) { compat_dev_kfree_skb_irq(skb, FREE_WRITE); } @@ -1716,7 +1733,8 @@ vmxnet_tx(struct sk_buff *skb, struct net_device *dev) return VMXNET_DEFER_TRANSMIT; } } - if (UNLIKELY(*(uint16*)(skb->data + ETH_FRAME_TYPE_LOCATION) == ETH_TYPE_VLAN_TAG)) { + if (UNLIKELY(*(uint16*)(skb->data + ETH_FRAME_TYPE_LOCATION) == + ETH_TYPE_VLAN_TAG)) { headerSize += VLAN_TAG_LENGTH; if (UNLIKELY(skb_headlen(skb) < headerSize)) { @@ -1780,7 +1798,7 @@ vmxnet_tx(struct sk_buff *skb, struct net_device *dev) return VMXNET_DEFER_TRANSMIT; } - xre->sg.sg[0].addrLow = (uint32)dd->txBufferPhysStart + pos; + xre->sg.sg[0].addrLow = dd->txBufferPhysStart + pos; xre->sg.sg[0].addrHi = 0; xre->sg.sg[0].length = headerSize; vmxnet_map_pkt(skb, headerSize, lp, 1); @@ -1804,6 +1822,7 @@ vmxnet_tx(struct sk_buff *skb, struct net_device *dev) #endif { struct Vmxnet2_TxBuf *tb; + dma_addr_t dmaAddr; spin_lock_irqsave(&lp->txLock, flags); @@ -1818,9 +1837,8 @@ vmxnet_tx(struct sk_buff *skb, struct net_device *dev) lp->numTxPending ++; - xre->sg.sg[0].addrLow = virt_to_bus(skb->data); - xre->sg.sg[0].addrHi = 0; - xre->sg.sg[0].length = skb->len; + dmaAddr = compat_pci_map_single(lp->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); + VMXNET_FILL_SG(xre->sg.sg[0], dmaAddr, skb->len); xre->sg.length = 1; xre->flags = 0; @@ -2068,7 +2086,7 @@ vmxnet_rx(struct net_device *dev) skb = lp->rxSkbuff[dd->rxDriverNext]; /* refill the rx ring */ - newSkb = dev_alloc_skb(PKT_BUF_SZ); + newSkb = dev_alloc_skb(PKT_BUF_SZ + COMPAT_NET_IP_ALIGN); if (UNLIKELY(newSkb == NULL)) { printk(KERN_DEBUG "%s: Memory squeeze, dropping packet.\n", dev->name); #ifdef VMXNET_DO_ZERO_COPY @@ -2079,12 +2097,16 @@ vmxnet_rx(struct net_device *dev) lp->stats.rx_errors++; goto next_pkt; } + skb_reserve(newSkb, COMPAT_NET_IP_ALIGN); + + compat_pci_unmap_single(lp->pdev, rre->paddr, PKT_BUF_SZ, PCI_DMA_FROMDEVICE); + skb_put(skb, rre->actualLength); lp->rxSkbuff[dd->rxDriverNext] = newSkb; - rre->paddr = virt_to_bus(newSkb->data); + rre->paddr = compat_pci_map_single(lp->pdev, newSkb->data, + PKT_BUF_SZ, PCI_DMA_FROMDEVICE); rre->bufferLength = PKT_BUF_SZ; - skb_put(skb, rre->actualLength); #ifdef VMXNET_DO_ZERO_COPY if (rre->flags & VMXNET2_RX_WITH_FRAG) { @@ -2299,6 +2321,8 @@ vmxnet_close(struct net_device *dev) for (i = 0; i < lp->numRxBuffers; i++) { if (lp->rxSkbuff[i] != NULL) { + compat_pci_unmap_single(lp->pdev, lp->rxRing[i].paddr, + PKT_BUF_SZ, PCI_DMA_FROMDEVICE); compat_dev_kfree_skb(lp->rxSkbuff[i], FREE_WRITE); lp->rxSkbuff[i] = NULL; } @@ -2307,7 +2331,8 @@ vmxnet_close(struct net_device *dev) if (lp->jumboFrame || lp->lpd) { for (i = 0; i < lp->numRxBuffers2; i++) { if (lp->rxPages[i] != NULL) { - pci_unmap_page(lp->pdev, lp->rxRing2[i].paddr, PAGE_SIZE, PCI_DMA_FROMDEVICE); + pci_unmap_page(lp->pdev, lp->rxRing2[i].paddr, PAGE_SIZE, + PCI_DMA_FROMDEVICE); put_page(lp->rxPages[i]); lp->rxPages[i] = NULL; } diff --git a/open-vm-tools/modules/linux/vmxnet/vmxnetInt.h b/open-vm-tools/modules/linux/vmxnet/vmxnetInt.h index b56785dd0..e0e8c8820 100644 --- a/open-vm-tools/modules/linux/vmxnet/vmxnetInt.h +++ b/open-vm-tools/modules/linux/vmxnet/vmxnetInt.h @@ -30,9 +30,6 @@ #define VMXNET_MIN_MTU (ETH_MIN_FRAME_LEN - 14) #define VMXNET_MAX_MTU (16 * 1024 - 18) -/* Largest address able to be shared between the driver and the device */ -#define SHARED_MEM_MAX 0xFFFFFFFF - typedef enum Vmxnet_TxStatus { VMXNET_CALL_TRANSMIT, VMXNET_DEFER_TRANSMIT, @@ -71,6 +68,8 @@ struct Vmxnet2_TxBuf { */ typedef struct Vmxnet_Private { Vmxnet2_DriverData *dd; + dma_addr_t ddPA; + size_t ddSize; const char *name; struct net_device_stats stats; struct sk_buff *rxSkbuff[ENHANCED_VMXNET2_MAX_NUM_RX_BUFFERS]; @@ -100,9 +99,9 @@ typedef struct Vmxnet_Private { Bool lpd; Bool morphed; // Indicates whether adapter is morphed - void *ddAllocated; - char *txBufferStartRaw; + size_t txBufferSize; char *txBufferStart; + dma_addr_t txBufferPA; struct pci_dev *pdev; struct timer_list linkCheckTimer; } Vmxnet_Private; diff --git a/open-vm-tools/modules/linux/vmxnet/vmxnet_version.h b/open-vm-tools/modules/linux/vmxnet/vmxnet_version.h index d39dfb150..e238993d4 100644 --- a/open-vm-tools/modules/linux/vmxnet/vmxnet_version.h +++ b/open-vm-tools/modules/linux/vmxnet/vmxnet_version.h @@ -25,8 +25,8 @@ #ifndef _VMXNET_VERSION_H_ #define _VMXNET_VERSION_H_ -#define VMXNET_DRIVER_VERSION 2.0.1.1 -#define VMXNET_DRIVER_VERSION_COMMAS 2,0,1,1 -#define VMXNET_DRIVER_VERSION_STRING "2.0.1.1" +#define VMXNET_DRIVER_VERSION 2.0.2.1 +#define VMXNET_DRIVER_VERSION_COMMAS 2,0,2,1 +#define VMXNET_DRIVER_VERSION_STRING "2.0.2.1" #endif /* _VMXNET_VERSION_H_ */ diff --git a/open-vm-tools/modules/linux/vmxnet3/vm_basic_types.h b/open-vm-tools/modules/linux/vmxnet3/vm_basic_types.h index 53ee91746..638e9ce6c 100644 --- a/open-vm-tools/modules/linux/vmxnet3/vm_basic_types.h +++ b/open-vm-tools/modules/linux/vmxnet3/vm_basic_types.h @@ -338,6 +338,9 @@ typedef int64 VmTimeVirtualClock; /* Virtual Clock kept in CPU cycles */ #ifdef _MSC_VER #define CONST64(c) c##I64 #define CONST64U(c) c##uI64 +#elif defined __APPLE__ +#define CONST64(c) c##LL +#define CONST64U(c) c##uLL #elif __GNUC__ #ifdef VM_X86_64 #define CONST64(c) c##L @@ -489,10 +492,11 @@ typedef uint64 MPN64; */ typedef VA32 UserVA32; typedef VA64 UserVA64; -typedef UserVA32 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA64 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA32 UserVA32Const; /* Userspace ptr to data that we may only read. */ typedef UserVA64 UserVA64Const; /* Used by 64-bit syscalls until conversion is finished. */ #ifdef VMKERNEL -typedef UserVA32 UserVA; +typedef UserVA64 UserVA; #else typedef void * UserVA; #endif diff --git a/open-vm-tools/modules/linux/vmxnet3/vmxnet3.c b/open-vm-tools/modules/linux/vmxnet3/vmxnet3.c index 52e2d0e8e..bbc9631ad 100644 --- a/open-vm-tools/modules/linux/vmxnet3/vmxnet3.c +++ b/open-vm-tools/modules/linux/vmxnet3/vmxnet3.c @@ -30,9 +30,9 @@ #endif #include "compat_module.h" -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 9) +//#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 9) #include -#endif +//#endif #include "compat_slab.h" #include "compat_spinlock.h" @@ -155,6 +155,7 @@ static struct pci_driver vmxnet3_driver = { #endif }; +static int disable_lro = 0; /* *---------------------------------------------------------------------------- @@ -3101,8 +3102,10 @@ vmxnet3_declare_features(struct vmxnet3_adapter *adapter, Bool dma64) printk(" tsoIPv6"); #endif - adapter->lro = TRUE; - printk(" lro"); + if (!disable_lro) { + adapter->lro = TRUE; + printk(" lro"); + } if (dma64) { netdev->features |= NETIF_F_HIGHDMA; @@ -4558,5 +4561,5 @@ MODULE_VERSION(VMXNET3_DRIVER_VERSION_STRING); * by default (i.e., neither mkinitrd nor modprobe will accept it). */ MODULE_INFO(supported, "external"); - +module_param(disable_lro, int, 0); diff --git a/open-vm-tools/modules/linux/vmxnet3/vmxnet3_version.h b/open-vm-tools/modules/linux/vmxnet3/vmxnet3_version.h index 0ee39d621..fb4334e62 100644 --- a/open-vm-tools/modules/linux/vmxnet3/vmxnet3_version.h +++ b/open-vm-tools/modules/linux/vmxnet3/vmxnet3_version.h @@ -25,10 +25,11 @@ #ifndef _VMXNET3_VERSION_H_ #define _VMXNET3_VERSION_H_ -#define VMXNET3_DRIVER_VERSION 0.0.0.31 -#define VMXNET3_DRIVER_VERSION_COMMAS 0,0,0,31 -#define VMXNET3_DRIVER_VERSION_STRING "0.0.0.31" +#define VMXNET3_DRIVER_VERSION 1.0.0.32 +#define VMXNET3_DRIVER_VERSION_COMMAS 1,0,0,32 +#define VMXNET3_DRIVER_VERSION_STRING "1.0.0.32" -#define VMXNET3_DRIVER_VERSION_NUM 31 +/* a 32-bit int, each byte encode a verion number in VMXNET3_DRIVER_VERSION */ +#define VMXNET3_DRIVER_VERSION_NUM 0x01000020 #endif /* _VMXNET3_VERSION_H_ */ diff --git a/open-vm-tools/modules/linux/vsock/include/compat_sched.h b/open-vm-tools/modules/linux/vsock/include/compat_sched.h index efb9fc44d..a9696ebcc 100644 --- a/open-vm-tools/modules/linux/vsock/include/compat_sched.h +++ b/open-vm-tools/modules/linux/vsock/include/compat_sched.h @@ -271,6 +271,20 @@ static inline int compat_try_to_freeze(void) { return 0; } #define compat_set_freezable() do {} while (0) #endif +/* + * Around 2.6.27 kernel stopped sending signals to kernel + * threads being frozen, instead threads have to check + * freezing() or use wait_event_freezable(). Unfortunately + * wait_event_freezable() completely hides the fact that + * thread was frozen from calling code and sometimes we do + * want to know that. + */ +#ifdef PF_FREEZER_NOSIG +#define compat_wait_check_freezing() freezing(current) +#else +#define compat_wait_check_freezing() (0) +#endif + /* * Since 2.6.27-rc2 kill_proc() is gone... Replacement (GPL-only!) * API is available since 2.6.19. Use them from 2.6.27-rc1 up. diff --git a/open-vm-tools/modules/linux/vsock/include/vm_assert.h b/open-vm-tools/modules/linux/vsock/include/vm_assert.h index 2e70ae1fe..d6bb8f7db 100644 --- a/open-vm-tools/modules/linux/vsock/include/vm_assert.h +++ b/open-vm-tools/modules/linux/vsock/include/vm_assert.h @@ -83,10 +83,26 @@ EXTERN void WarningThrottled(uint32 *count, const char *fmt, ...) */ #ifndef ASSERT_IFNOT - #ifdef __cplusplus - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : (void)0) + /* + * PR 271512: When compiling with gcc, catch assignments inside an ASSERT. + * + * 'UNLIKELY' is defined with __builtin_expect, which does not warn when passed an + * assignment (gcc bug 36050). + * To get around this, we put 'cond' in an 'if' statement and make sure it never gets + * executed by putting that inside of 'if (0)'. + * We use gcc's statement expression syntax to make ASSERT an expression because some + * code uses it that way. + * + * Since statement expression syntax is a gcc extension and since it's not clear if + * this is a problem with other compilers, the ASSERT definition was not changed for + * them. Using a bare 'cond' with the ternary operator may provide a solution. + */ + #ifdef __GNUC__ + #define ASSERT_IFNOT(cond, panic) \ + ({if (UNLIKELY(!(cond))) { panic; if (0) { if (cond) { ; } } } (void)0;}) #else - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : 0) + #define ASSERT_IFNOT(cond, panic) \ + (UNLIKELY(!(cond)) ? (panic) : (void)0) #endif #endif diff --git a/open-vm-tools/modules/linux/vsock/include/vm_atomic.h b/open-vm-tools/modules/linux/vsock/include/vm_atomic.h index abfa5fccf..37c8174c7 100644 --- a/open-vm-tools/modules/linux/vsock/include/vm_atomic.h +++ b/open-vm-tools/modules/linux/vsock/include/vm_atomic.h @@ -196,10 +196,29 @@ static INLINE void AtomicEpilogue(void) { #ifdef ATOMIC_USE_FENCE +#ifdef VMM + /* The monitor conditionally patches out the lfence when not needed.*/ + /* Construct a MonitorPatchTextEntry in the .patchtext section. */ + asm volatile ("1:\n\t" + "lfence\n\t" + "2:\n\t" + ".pushsection .patchtext\n\t" +#ifdef VMM32 + ".long 1b\n\t" + ".long 0\n\t" + ".long 2b\n\t" + ".long 0\n\t" +#else + ".quad 1b\n\t" + ".quad 2b\n\t" +#endif + ".popsection\n\t" ::: "memory"); +#else if (UNLIKELY(AtomicUseFence)) { asm volatile ("lfence" ::: "memory"); } #endif +#endif } diff --git a/open-vm-tools/modules/linux/vsock/include/vm_basic_asm.h b/open-vm-tools/modules/linux/vsock/include/vm_basic_asm.h index 5bc4d6b71..0180e3919 100644 --- a/open-vm-tools/modules/linux/vsock/include/vm_basic_asm.h +++ b/open-vm-tools/modules/linux/vsock/include/vm_basic_asm.h @@ -853,4 +853,21 @@ RDTSC(void) #error No compiler defined for RDTSC #endif /* __GNUC__ */ +/* + *----------------------------------------------------------------------------- + * + * DEBUGBREAK -- + * + * Does an int3 for MSVC / GCC. This is a macro to make sure int3 is + * always inlined. + * + *----------------------------------------------------------------------------- + */ + +#ifdef _MSC_VER +#define DEBUGBREAK() __debugbreak() +#else +#define DEBUGBREAK() __asm__ (" int $3 ") +#endif + #endif diff --git a/open-vm-tools/modules/linux/vsock/include/vm_basic_types.h b/open-vm-tools/modules/linux/vsock/include/vm_basic_types.h index 53ee91746..638e9ce6c 100644 --- a/open-vm-tools/modules/linux/vsock/include/vm_basic_types.h +++ b/open-vm-tools/modules/linux/vsock/include/vm_basic_types.h @@ -338,6 +338,9 @@ typedef int64 VmTimeVirtualClock; /* Virtual Clock kept in CPU cycles */ #ifdef _MSC_VER #define CONST64(c) c##I64 #define CONST64U(c) c##uI64 +#elif defined __APPLE__ +#define CONST64(c) c##LL +#define CONST64U(c) c##uLL #elif __GNUC__ #ifdef VM_X86_64 #define CONST64(c) c##L @@ -489,10 +492,11 @@ typedef uint64 MPN64; */ typedef VA32 UserVA32; typedef VA64 UserVA64; -typedef UserVA32 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA64 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA32 UserVA32Const; /* Userspace ptr to data that we may only read. */ typedef UserVA64 UserVA64Const; /* Used by 64-bit syscalls until conversion is finished. */ #ifdef VMKERNEL -typedef UserVA32 UserVA; +typedef UserVA64 UserVA; #else typedef void * UserVA; #endif diff --git a/open-vm-tools/modules/linux/vsock/include/vmciHostKernelAPI.h b/open-vm-tools/modules/linux/vsock/include/vmciHostKernelAPI.h index e8e25a913..2a39e59b2 100644 --- a/open-vm-tools/modules/linux/vsock/include/vmciHostKernelAPI.h +++ b/open-vm-tools/modules/linux/vsock/include/vmciHostKernelAPI.h @@ -42,16 +42,15 @@ #define VMCI_MINOR_VERSION(v) ((v) & 0xffff) #include "vmci_defs.h" -#ifdef VMKERNEL +#if defined(VMKERNEL) #include "vm_atomic.h" #include "return_status.h" #include "util_copy_dist.h" #endif -#include "vmci_defs.h" #include "vmci_call_defs.h" - -#ifdef VMKERNEL +#if (defined(__linux__) || defined(_WIN32)) && !defined(VMKERNEL) +#include "vmci_queue_pair.h" #endif @@ -76,15 +75,19 @@ int VMCIDatagram_Send(VMCIDatagram *msg); typedef void (*VMCI_EventCB)(VMCIId subID, VMCI_EventData *ed, void *clientData); -int VMCIEvent_Subscribe(VMCI_Event event, VMCI_EventCB callback, +int VMCIEvent_Subscribe(VMCI_Event event, VMCI_EventCB callback, void *callbackData, VMCIId *subID); int VMCIEvent_Unsubscribe(VMCIId subID); -/* - * Since QP API for non-VMKernel hosts is defined in vmci_queue_pair.h, here - * declarations are included only for VMKERNEL +/* + * Queue pair operations for manipulating the content of a queue + * pair are defined in vmci_queue_pair.h for non-vmkernel hosts. + * Here we define the API for allocating and detaching from queue + * pairs for all hosts, and content manipulation functions for + * VMKERNEL. */ -#ifdef VMKERNEL + +#if defined(VMKERNEL) /* * For guest kernels, the VMCIQueue is directly mapped onto the @@ -100,15 +103,10 @@ typedef struct VMCIQueue { struct QueuePairEntry *entry; // Endpoint state for the queue pair } VMCIQueue; - /* VMCI Queuepair API */ void VMCIQueue_Init(const VMCIHandle handle, VMCIQueue *queue); void VMCIQueue_GetPointers(const VMCIQueue *produceQ, const VMCIQueue *consumeQ, uint64 *producerTail, uint64 *consumerHead); -int VMCIQueuePair_Alloc(VMCIHandle *handle, VMCIQueue **produceQ, - uint64 produceSize, VMCIQueue **consumeQ, - uint64 consumeSize, VMCIId peer, uint32 flags); -int VMCIQueuePair_Detach(VMCIHandle handle); int64 VMCIQueue_FreeSpace(const VMCIQueue *produceQueue, const VMCIQueue *consumeQueue, const uint64 produceQSize); @@ -137,5 +135,12 @@ ssize_t VMCIQueue_DequeueV(VMCIQueue *produceQueue, const VMCIQueue *consumeQueu size_t bufSize, Util_BufferType bufType); #endif /* VMKERNEL */ +#if defined(VMKERNEL) || defined(__linux__) || defined(_WIN32) +int VMCIQueuePair_Alloc(VMCIHandle *handle, VMCIQueue **produceQ, + uint64 produceSize, VMCIQueue **consumeQ, + uint64 consumeSize, VMCIId peer, uint32 flags); +int VMCIQueuePair_Detach(VMCIHandle handle); +#endif + #endif /* !__VMCI_HOSTKERNELAPI_H__ */ diff --git a/open-vm-tools/modules/linux/vsock/include/vmci_queue_pair.h b/open-vm-tools/modules/linux/vsock/include/vmci_queue_pair.h index 05884f5c8..8d9e5c51f 100644 --- a/open-vm-tools/modules/linux/vsock/include/vmci_queue_pair.h +++ b/open-vm-tools/modules/linux/vsock/include/vmci_queue_pair.h @@ -844,7 +844,7 @@ VMCIQueue_EnqueueV(VMCIQueue *produceQueue, // IN: * * Results: * VMCI_ERROR_QUEUEPAIR_NODATA if no data was available to dequeue. - * VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue + * VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue * (as defined by the queue size). * Otherwise the number of bytes dequeued is returned. * @@ -860,7 +860,8 @@ __VMCIQueue_Dequeue(VMCIQueue *produceQueue, // IN: const uint64 consumeQSize, // IN: void *buf, // IN: size_t bufSize, // IN: - VMCIMemcpyFromQueueFunc memcpyFromQueue) // IN: + VMCIMemcpyFromQueueFunc memcpyFromQueue, // IN: + Bool updateConsumer) // IN: { const int64 bufReady = VMCIQueue_BufReady(consumeQueue, produceQueue, consumeQSize); @@ -884,7 +885,11 @@ __VMCIQueue_Dequeue(VMCIQueue *produceQueue, // IN: memcpyFromQueue(buf, 0, consumeQueue, head, tmp); memcpyFromQueue(buf, tmp, consumeQueue, 0, written - tmp); } - AddPointer(&VMCIQueue_GetHeader(produceQueue)->consumerHead, written, consumeQSize); + + if (updateConsumer) { + AddPointer(&VMCIQueue_GetHeader(produceQueue)->consumerHead, written, + consumeQSize); + } return written; } @@ -918,7 +923,7 @@ VMCIQueue_Dequeue(VMCIQueue *produceQueue, // IN: size_t bufSize) // IN: { return __VMCIQueue_Dequeue(produceQueue, consumeQueue, consumeQSize, - buf, bufSize, VMCIMemcpyFromQueue); + buf, bufSize, VMCIMemcpyFromQueue, TRUE); } @@ -952,7 +957,75 @@ VMCIQueue_DequeueV(VMCIQueue *produceQueue, // IN: size_t iovSize) // IN: { return __VMCIQueue_Dequeue(produceQueue, consumeQueue, consumeQSize, - (void *)iov, iovSize, VMCIMemcpyFromQueueV); + (void *)iov, iovSize, VMCIMemcpyFromQueueV, TRUE); +} +#endif + + +/* + *----------------------------------------------------------------------------- + * + * VMCIQueue_Peek -- + * + * Reads data (if available) from the given consume queue. Copies data + * to the provided user buffer but does not update the consumer counter + * of the queue. + * + * Results: + * VMCI_ERROR_QUEUEPAIR_NODATA if no data was available to dequeue. + * VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue + * (as defined by the queue size). + * Otherwise the number of bytes copied to user buffer is returned. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static INLINE ssize_t +VMCIQueue_Peek(VMCIQueue *produceQueue, // IN: + const VMCIQueue *consumeQueue, // IN: + const uint64 consumeQSize, // IN: + void *buf, // IN: + size_t bufSize) // IN: +{ + return __VMCIQueue_Dequeue(produceQueue, consumeQueue, consumeQSize, + buf, bufSize, VMCIMemcpyFromQueue, FALSE); +} + + +#if defined(SOLARIS) || (defined(__linux__) && defined(__KERNEL__)) +/* + *----------------------------------------------------------------------------- + * + * VMCIQueue_PeekV -- + * + * Reads data (if available) from the given consume queue. Copies data + * to the provided user iovec but does not update the consumer counter + * of the queue. + * + * Results: + * VMCI_ERROR_QUEUEPAIR_NODATA if no data was available to dequeue. + * VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue + * (as defined by the queue size). + * Otherwise the number of bytes copied to user iovec is returned. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static INLINE ssize_t +VMCIQueue_PeekV(VMCIQueue *produceQueue, // IN: + const VMCIQueue *consumeQueue, // IN: + const uint64 consumeQSize, // IN: + struct iovec *iov, // IN: + size_t iovSize) // IN: +{ + return __VMCIQueue_Dequeue(produceQueue, consumeQueue, consumeQSize, + (void *)iov, iovSize, VMCIMemcpyFromQueueV, FALSE); } #endif diff --git a/open-vm-tools/modules/linux/vsock/include/x86cpuid.h b/open-vm-tools/modules/linux/vsock/include/x86cpuid.h index 0a2f3c6be..337fd8cd9 100644 --- a/open-vm-tools/modules/linux/vsock/include/x86cpuid.h +++ b/open-vm-tools/modules/linux/vsock/include/x86cpuid.h @@ -440,7 +440,7 @@ FLAGDEF( 87, EDX, AMD, 4, 1, TM, NA, IGNORE, 0, FALSE) FLAGDEF( 87, EDX, AMD, 5, 1, STC, NA, IGNORE, 0, FALSE) \ FLAGDEF( 87, EDX, AMD, 6, 1, 100MHZSTEPS, NA, IGNORE, 0, FALSE) \ FLAGDEF( 87, EDX, AMD, 7, 1, HWPSTATE, NA, IGNORE, 0, FALSE) \ -FLAGDEF( 87, EDX, AMD, 8, 1, TSC_INVARIANT, NA, IGNORE, 0, FALSE) \ +FLAGDEF( 87, EDX, COMMON, 8, 1, TSC_INVARIANT, NA, IGNORE, 0, FALSE) \ FIELDDEFA(88, EAX, COMMON, 0, 8, PHYSBITS, NA, IGNORE, 0, FALSE, PHYS_BITS) \ FIELDDEFA(88, EAX, COMMON, 8, 8, VIRTBITS, NA, IGNORE, 0, FALSE, VIRT_BITS) \ FIELDDEFA(88, ECX, AMD, 0, 8, CORE_COUNT, NA, IGNORE, 0, FALSE, AMD_CORE_COUNT) \ @@ -644,6 +644,9 @@ FIELD_FUNC(MWAIT_C4_SUBSTATE, CPUID_INTEL_ID5EDX_MWAIT_C4_SUBSTATE) #define CPUID_MODEL_PIII_08 8 #define CPUID_MODEL_PIII_0A 10 +/* AMD model information */ +#define CPUID_MODEL_BARCELONA_02 0x02 // Barcelona (both Opteron & Phenom kind) + /* *---------------------------------------------------------------------- * @@ -815,6 +818,17 @@ CPUID_FAMILY_IS_K8STAR(uint32 _eax) CPUID_FAMILY_IS_K8MOBILE(_eax); } +/* + * AMD Barcelona (of either Opteron or Phenom kind). + */ +static INLINE Bool +CPUID_MODEL_IS_BARCELONA(uint32 v) // IN: %eax from CPUID with %eax=1. +{ + /* Assumes the CPU manufacturer is AMD. */ + return CPUID_FAMILY_IS_K8L(v) && + CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_BARCELONA_02; +} + #define CPUID_TYPE_PRIMARY 0 #define CPUID_TYPE_OVERDRIVE 1 diff --git a/open-vm-tools/modules/linux/vsock/linux/af_vsock.c b/open-vm-tools/modules/linux/vsock/linux/af_vsock.c index 1facc055a..4d0ee72e6 100644 --- a/open-vm-tools/modules/linux/vsock/linux/af_vsock.c +++ b/open-vm-tools/modules/linux/vsock/linux/af_vsock.c @@ -128,7 +128,7 @@ sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); #include "compat_workqueue.h" #include "compat_list.h" #if defined(HAVE_COMPAT_IOCTL) || defined(HAVE_UNLOCKED_IOCTL) -# include "compat_semaphore.h" +# include "compat_semaphore.h" #endif #include "vmware.h" @@ -138,14 +138,7 @@ sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); #include "vsockPacket.h" #include "vsockVmci.h" -#include "vmci_defs.h" -#include "vmci_call_defs.h" #include "vmci_iocontrols.h" -#ifdef VMX86_TOOLS -# include "vmciGuestKernelAPI.h" -#else -# include "vmciHostKernelAPI.h" -#endif #include "af_vsock.h" #include "util.h" @@ -170,7 +163,6 @@ int VSockVmci_GetAFValue(void); /* Internal functions. */ static int VSockVmciGetAFValue(void); static int VSockVmciRecvDgramCB(void *data, VMCIDatagram *dg); -#ifdef VMX86_TOOLS static int VSockVmciRecvStreamCB(void *data, VMCIDatagram *dg); static void VSockVmciPeerAttachCB(VMCIId subId, VMCI_EventData *ed, void *clientData); @@ -194,7 +186,6 @@ static int VSockVmciRecvConnectingClient(struct sock *sk, VSockPacket *pkt); static int VSockVmciRecvConnectingClientNegotiate(struct sock *sk, VSockPacket *pkt); static int VSockVmciRecvConnected(struct sock *sk, VSockPacket *pkt); -#endif static int __VSockVmciBind(struct sock *sk, struct sockaddr_vm *addr); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) static struct sock *__VSockVmciCreate(struct socket *sock, unsigned int priority, @@ -207,7 +198,7 @@ static struct sock *__VSockVmciCreate(struct net *net, struct socket *sock, gfp_t priority, unsigned short type); #endif -static inline void VSockVmciTestUnregister(void); +static void VSockVmciTestUnregister(void); static int VSockVmciRegisterAddressFamily(void); static void VSockVmciUnregisterAddressFamily(void); @@ -220,38 +211,30 @@ static int VSockVmciBind(struct socket *sock, struct sockaddr *addr, int addrLen); static int VSockVmciDgramConnect(struct socket *sock, struct sockaddr *addr, int addrLen, int flags); -#ifdef VMX86_TOOLS static int VSockVmciStreamConnect(struct socket *sock, struct sockaddr *addr, int addrLen, int flags); static int VSockVmciAccept(struct socket *sock, struct socket *newsock, int flags); -#endif static int VSockVmciGetname(struct socket *sock, struct sockaddr *addr, int *addrLen, int peer); static unsigned int VSockVmciPoll(struct file *file, struct socket *sock, poll_table *wait); -#ifdef VMX86_TOOLS static int VSockVmciListen(struct socket *sock, int backlog); -#endif static int VSockVmciShutdown(struct socket *sock, int mode); -#ifdef VMX86_TOOLS static int VSockVmciStreamSetsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen); static int VSockVmciStreamGetsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user * optlen); -#endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 43) static int VSockVmciDgramSendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm); static int VSockVmciDgramRecvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm); -# ifdef VMX86_TOOLS static int VSockVmciStreamSendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm); static int VSockVmciStreamRecvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm); -# endif #elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 65) static int VSockVmciDgramSendmsg(struct kiocb *kiocb, struct socket *sock, struct msghdr *msg, int len, @@ -259,36 +242,30 @@ static int VSockVmciDgramSendmsg(struct kiocb *kiocb, struct socket *sock, static int VSockVmciDgramRecvmsg(struct kiocb *kiocb, struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm); -# ifdef VMX86_TOOLS static int VSockVmciStreamSendmsg(struct kiocb *kiocb, struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm); static int VSockVmciStreamRecvmsg(struct kiocb *kiocb, struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm); -# endif #elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 2) static int VSockVmciDgramSendmsg(struct kiocb *kiocb, struct socket *sock, struct msghdr *msg, int len); static int VSockVmciDgramRecvmsg(struct kiocb *kiocb, struct socket *sock, struct msghdr *msg, int len, int flags); -# ifdef VMX86_TOOLS static int VSockVmciStreamSendmsg(struct kiocb *kiocb, struct socket *sock, struct msghdr *msg, int len); static int VSockVmciStreamRecvmsg(struct kiocb *kiocb, struct socket *sock, struct msghdr *msg, int len, int flags); -# endif #else static int VSockVmciDgramSendmsg(struct kiocb *kiocb, struct socket *sock, struct msghdr *msg, size_t len); static int VSockVmciDgramRecvmsg(struct kiocb *kiocb, struct socket *sock, struct msghdr *msg, size_t len, int flags); -# ifdef VMX86_TOOLS static int VSockVmciStreamSendmsg(struct kiocb *kiocb, struct socket *sock, struct msghdr *msg, size_t len); static int VSockVmciStreamRecvmsg(struct kiocb *kiocb, struct socket *sock, struct msghdr *msg, size_t len, int flags); -# endif #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) @@ -374,7 +351,6 @@ static struct proto_ops vsockVmciDgramOps = { #endif }; -#ifdef VMX86_TOOLS static struct proto_ops vsockVmciStreamOps = { .family = VSOCK_INVALID_FAMILY, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 69) @@ -399,7 +375,6 @@ static struct proto_ops vsockVmciStreamOps = { .sendpage = sock_no_sendpage, #endif }; -#endif static struct file_operations vsockVmciDeviceOps = { #ifdef HAVE_UNLOCKED_IOCTL @@ -431,14 +406,14 @@ static int devOpenCount = 0; static int vsockVmciSocketCount = 0; static int vsockVmciKernClientCount = 0; #ifdef VMX86_TOOLS -static VMCIHandle vmciStreamHandle = { VMCI_INVALID_ID, VMCI_INVALID_ID }; static Bool vmciDevicePresent = FALSE; -static VMCIId qpResumedSubId = VMCI_INVALID_ID; #endif +static VMCIHandle vmciStreamHandle = { VMCI_INVALID_ID, VMCI_INVALID_ID }; +static VMCIId qpResumedSubId = VMCI_INVALID_ID; /* Comment this out to compare with old protocol. */ #define VSOCK_OPTIMIZATION_WAITING_NOTIFY 1 -#if defined(VMX86_TOOLS) && defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) /* Comment this out to remove flow control for "new" protocol */ # define VSOCK_OPTIMIZATION_FLOW_CONTROL 1 #endif @@ -711,7 +686,7 @@ VSockVmci_GetAFValue(void) *---------------------------------------------------------------------------- */ -static inline void +static void VSockVmciTestUnregister(void) { if (devOpenCount <= 0 && vsockVmciSocketCount <= 0 && @@ -740,17 +715,17 @@ VSockVmciTestUnregister(void) *---------------------------------------------------------------------------- */ -static inline void +static void VSockVmciUpdateQueueBucketCount(VMCIQueue *mainQueue, // IN VMCIQueue *otherQueue, // IN - uint64 mainQueueSize, // IN + uint64 mainQueueSize, // IN uint64 queueLevel[]) // IN { uint64 bucket = 0; uint32 remainder = 0; uint64 dataReady = VMCIQueue_BufReady(mainQueue, - otherQueue, - mainQueueSize); + otherQueue, + mainQueueSize); /* * We can't do 64 / 64 = 64 bit divides on linux because it requires a libgcc * which is not linked into the kernel module. Since this code is only used by @@ -763,8 +738,6 @@ VSockVmciUpdateQueueBucketCount(VMCIQueue *mainQueue, // IN } #endif - -#ifdef VMX86_TOOLS /* *---------------------------------------------------------------------------- * @@ -784,7 +757,7 @@ VSockVmciUpdateQueueBucketCount(VMCIQueue *mainQueue, // IN static Bool VSockVmciNotifyWaitingWrite(VSockVmciSock *vsk) // IN { -#ifdef VSOCK_OPTIMIZATION_WAITING_NOTIFY +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) Bool retval; uint64 notifyLimit; @@ -868,7 +841,7 @@ VSockVmciNotifyWaitingWrite(VSockVmciSock *vsk) // IN static Bool VSockVmciNotifyWaitingRead(VSockVmciSock *vsk) // IN { -#ifdef VSOCK_OPTIMIZATION_WAITING_NOTIFY +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) if (!vsk->peerWaitingRead) { return FALSE; } @@ -910,7 +883,7 @@ VSockVmciHandleWrote(struct sock *sk, // IN struct sockaddr_vm *dst, // IN: unused struct sockaddr_vm *src) // IN: unused { -#ifdef VSOCK_OPTIMIZATION_WAITING_NOTIFY +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) VSockVmciSock *vsk; vsk = vsock_sk(sk); @@ -944,7 +917,7 @@ VSockVmciHandleRead(struct sock *sk, // IN struct sockaddr_vm *dst, // IN: unused struct sockaddr_vm *src) // IN: unused { -#ifdef VSOCK_OPTIMIZATION_WAITING_NOTIFY +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) VSockVmciSock *vsk; vsk = vsock_sk(sk); @@ -979,7 +952,7 @@ VSockVmciHandleWaitingWrite(struct sock *sk, // IN struct sockaddr_vm *dst, // IN struct sockaddr_vm *src) // IN { -#ifdef VSOCK_OPTIMIZATION_WAITING_NOTIFY +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) VSockVmciSock *vsk; vsk = vsock_sk(sk); @@ -1029,7 +1002,7 @@ VSockVmciHandleWaitingRead(struct sock *sk, // IN struct sockaddr_vm *dst, // IN struct sockaddr_vm *src) // IN { -#ifdef VSOCK_OPTIMIZATION_WAITING_NOTIFY +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) VSockVmciSock *vsk; vsk = vsock_sk(sk); @@ -1053,7 +1026,6 @@ VSockVmciHandleWaitingRead(struct sock *sk, // IN } #endif } -#endif /* @@ -1112,7 +1084,6 @@ VSockVmciRecvDgramCB(void *data, // IN } -#ifdef VMX86_TOOLS /* *---------------------------------------------------------------------------- * @@ -1358,7 +1329,7 @@ out: *---------------------------------------------------------------------------- */ -static INLINE void +static void VSockVmciHandleDetach(struct sock *sk) // IN { VSockVmciSock *vsk; @@ -1370,12 +1341,10 @@ VSockVmciHandleDetach(struct sock *sk) // IN ASSERT(vsk->produceQ); ASSERT(vsk->consumeQ); -#ifdef VMX86_TOOLS if (sk->compat_sk_type == SOCK_STREAM && - sk->compat_sk_state == SS_CONNECTED) { - compat_sock_set_done(sk); + sk->compat_sk_state == SS_CONNECTED) { + compat_sock_set_done(sk); } -#endif /* On a detach the peer will not be sending or receiving anymore. */ vsk->peerShutdown = SHUTDOWN_MASK; @@ -2412,7 +2381,6 @@ VSockVmciSendControlPkt(struct sock *sk, // IN return err; } -#endif /* @@ -2562,7 +2530,6 @@ out: } -#ifdef VMX86_TOOLS /* *---------------------------------------------------------------------------- * @@ -2586,7 +2553,7 @@ static Bool VSockVmciSendWaitingWrite(struct sock *sk, // IN uint64 roomNeeded) // IN { -#ifdef VSOCK_OPTIMIZATION_WAITING_NOTIFY +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) VSockVmciSock *vsk; VSockWaitingInfo waitingInfo; uint64 tail; @@ -2645,7 +2612,7 @@ static Bool VSockVmciSendWaitingRead(struct sock *sk, // IN uint64 roomNeeded) // IN { -#ifdef VSOCK_OPTIMIZATION_WAITING_NOTIFY +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) VSockVmciSock *vsk; VSockWaitingInfo waitingInfo; uint64 tail; @@ -2748,7 +2715,6 @@ VSockVmciSendReadNotification(struct sock *sk) // IN } return err; } -#endif // VMX86_TOOLS /* @@ -2868,7 +2834,6 @@ __VSockVmciCreate(struct net *net, // IN: Network namespace INIT_LIST_HEAD(&vsk->boundTable); INIT_LIST_HEAD(&vsk->connectedTable); vsk->dgHandle = VMCI_INVALID_HANDLE; -#ifdef VMX86_TOOLS vsk->qpHandle = VMCI_INVALID_HANDLE; vsk->produceQ = vsk->consumeQ = NULL; vsk->produceQGeneration = vsk->consumeQGeneration = 0; @@ -2889,7 +2854,6 @@ __VSockVmciCreate(struct net *net, // IN: Network namespace vsk->rejected = FALSE; vsk->attachSubId = vsk->detachSubId = VMCI_INVALID_ID; vsk->peerShutdown = 0; -#endif if (sock) { VSockVmciInsertBound(vsockUnboundSockets, sk); @@ -2948,12 +2912,10 @@ __VSockVmciRelease(struct sock *sk) // IN } /* Clean up any sockets that never were accepted. */ -#ifdef VMX86_TOOLS while ((pending = VSockVmciDequeueAccept(sk)) != NULL) { __VSockVmciRelease(pending); sock_put(pending); } -#endif release_sock(sk); sock_put(sk); @@ -2989,7 +2951,6 @@ VSockVmciSkDestruct(struct sock *sk) // IN vsk = vsock_sk(sk); -#ifdef VMX86_TOOLS if (vsk->attachSubId != VMCI_INVALID_ID) { VMCIEvent_Unsubscribe(vsk->attachSubId); vsk->attachSubId = VMCI_INVALID_ID; @@ -3006,7 +2967,6 @@ VSockVmciSkDestruct(struct sock *sk) // IN vsk->produceQ = vsk->consumeQ = NULL; vsk->produceSize = vsk->consumeSize = 0; } -#endif /* * Each list entry holds a reference on the socket, so we should not even be @@ -3015,10 +2975,8 @@ VSockVmciSkDestruct(struct sock *sk) // IN */ ASSERT(!VSockVmciInBoundTable(sk)); ASSERT(!VSockVmciInConnectedTable(sk)); -#ifdef VMX86_TOOLS ASSERT(!VSockVmciIsPending(sk)); ASSERT(!VSockVmciInAcceptQueue(sk)); -#endif /* * When clearing these addresses, there's no need to set the family and @@ -3107,12 +3065,10 @@ VSockVmciQueueRcvSkb(struct sock *sk, // IN *---------------------------------------------------------------------------- */ -static INLINE int +static int VSockVmciRegisterProto(void) { - int err; - - err = 0; + int err = 0; /* * Before 2.6.9, each address family created their own slab (by calling @@ -3162,7 +3118,7 @@ VSockVmciRegisterProto(void) *---------------------------------------------------------------------------- */ -static INLINE void +static void VSockVmciUnregisterProto(void) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 5) @@ -3183,11 +3139,11 @@ VSockVmciUnregisterProto(void) } for (index = 0; index < ARRAYSIZE(consumeQueueLevel); index++) { - consumeQueueLevel[index] = 0; + consumeQueueLevel[index] = 0; } for (index = 0; index < ARRAYSIZE(produceQueueLevel); index++) { - produceQueueLevel[index] = 0; + produceQueueLevel[index] = 0; } } @@ -3230,6 +3186,7 @@ VSockVmciRegisterAddressFamily(void) Log("Could not register VMCI Sockets because VMCI device is not present.\n"); return -1; } +#endif /* * Create the datagram handle that we will use to send and receive all @@ -3254,7 +3211,6 @@ VSockVmciRegisterAddressFamily(void) qpResumedSubId = VMCI_INVALID_ID; goto error; } -#endif /* * Linux will not allocate an address family to code that is not part of the @@ -3275,9 +3231,7 @@ VSockVmciRegisterAddressFamily(void) vsockVmciFamilyOps.family = VSOCK_INVALID_FAMILY; } else { vsockVmciDgramOps.family = i; -#ifdef VMX86_TOOLS vsockVmciStreamOps.family = i; -#endif break; } } @@ -3289,13 +3243,11 @@ VSockVmciRegisterAddressFamily(void) return vsockVmciFamilyOps.family; error: -#ifdef VMX86_TOOLS if (qpResumedSubId != VMCI_INVALID_ID) { VMCIEvent_Unsubscribe(qpResumedSubId); qpResumedSubId = VMCI_INVALID_ID; } VMCIDatagram_DestroyHnd(vmciStreamHandle); -#endif return err; } @@ -3326,6 +3278,7 @@ VSockVmciUnregisterAddressFamily(void) /* Nothing was registered. */ return; } +#endif if (!VMCI_HANDLE_INVALID(vmciStreamHandle)) { if (VMCIDatagram_DestroyHnd(vmciStreamHandle) != VMCI_SUCCESS) { @@ -3337,16 +3290,13 @@ VSockVmciUnregisterAddressFamily(void) VMCIEvent_Unsubscribe(qpResumedSubId); qpResumedSubId = VMCI_INVALID_ID; } -#endif if (vsockVmciFamilyOps.family != VSOCK_INVALID_FAMILY) { sock_unregister(vsockVmciFamilyOps.family); } vsockVmciDgramOps.family = vsockVmciFamilyOps.family = VSOCK_INVALID_FAMILY; -#ifdef VMX86_TOOLS vsockVmciStreamOps.family = vsockVmciFamilyOps.family; -#endif } @@ -3486,7 +3436,6 @@ out: } -#ifdef VMX86_TOOLS /* *---------------------------------------------------------------------------- * @@ -3740,7 +3689,6 @@ out: release_sock(listener); return err; } -#endif /* @@ -3864,7 +3812,6 @@ VSockVmciPoll(struct file *file, // IN if (!(sk->compat_sk_shutdown & SEND_SHUTDOWN)) { mask |= POLLOUT | POLLWRNORM | POLLWRBAND; } -#ifdef VMX86_TOOLS } else if (sock->type == SOCK_STREAM) { VSockVmciSock *vsk; @@ -3935,14 +3882,12 @@ VSockVmciPoll(struct file *file, // IN } release_sock(sk); -#endif } return mask; } -#ifdef VMX86_TOOLS /* *---------------------------------------------------------------------------- * @@ -3997,7 +3942,6 @@ out: release_sock(sk); return err; } -#endif /* @@ -4051,12 +3995,10 @@ VSockVmciShutdown(struct socket *sock, // IN release_sock(sk); } -#ifdef VMX86_TOOLS if (sk->compat_sk_type == SOCK_STREAM && mode) { compat_sock_reset_done(sk); VSOCK_SEND_SHUTDOWN(sk, mode); } -#endif return 0; } @@ -4201,7 +4143,6 @@ out: return err; } -#ifdef VMX86_TOOLS /* *---------------------------------------------------------------------------- * @@ -4290,9 +4231,7 @@ out: return err; } -#endif -#ifdef VMX86_TOOLS /* *---------------------------------------------------------------------------- * @@ -4364,10 +4303,8 @@ VSockVmciStreamGetsockopt(struct socket *sock, // IN } return 0; } -#endif -#ifdef VMX86_TOOLS /* *---------------------------------------------------------------------------- * @@ -4416,7 +4353,7 @@ VSockVmciStreamSendmsg(struct kiocb *kiocb, // UNUSED ssize_t totalWritten; long timeout; int err; -#if defined(VMX86_TOOLS) && defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) uint64 produceTail; uint64 consumeHead; #endif @@ -4528,7 +4465,7 @@ VSockVmciStreamSendmsg(struct kiocb *kiocb, // UNUSED * size. It is the caller's responsibility to check how many bytes we were * able to send. */ -#if defined(VMX86_TOOLS) && defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) VMCIQueue_GetPointers(vsk->produceQ, vsk->consumeQ, &produceTail, &consumeHead); #endif @@ -4541,7 +4478,7 @@ VSockVmciStreamSendmsg(struct kiocb *kiocb, // UNUSED goto outWait; } -#if defined(VMX86_TOOLS) && defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) /* * Detect a wrap-around to maintain queue generation. Note that this is * safe since we hold the socket lock across the two queue pair @@ -4575,7 +4512,7 @@ VSockVmciStreamSendmsg(struct kiocb *kiocb, // UNUSED Warning("unable to send wrote notification to peer for socket %p.\n", sk); goto outWait; } else { -#if defined(VMX86_TOOLS) && defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) vsk->peerWaitingRead = FALSE; #endif } @@ -4593,7 +4530,6 @@ out: release_sock(sk); return err; } -#endif /* @@ -4711,7 +4647,6 @@ out: } -#ifdef VMX86_TOOLS /* *---------------------------------------------------------------------------- * @@ -4768,7 +4703,7 @@ VSockVmciStreamRecvmsg(struct kiocb *kiocb, // UNUSED ssize_t copied; Bool sentRead; unsigned int retries; -#if defined(VMX86_TOOLS) && defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) uint64 consumeHead; uint64 produceTail; #ifdef VSOCK_OPTIMIZATION_FLOW_CONTROL @@ -4856,7 +4791,7 @@ VSockVmciStreamRecvmsg(struct kiocb *kiocb, // UNUSED !(vsk->peerShutdown & SEND_SHUTDOWN)) { if (ready < 0) { - /* + /* * Invalid queue pair content. XXX This should be changed to * a connection reset in a later change. */ @@ -4926,49 +4861,61 @@ VSockVmciStreamRecvmsg(struct kiocb *kiocb, // UNUSED * Now consume up to len bytes from the queue. Note that since we have the * socket locked we should copy at least ready bytes. */ -#if defined(VMX86_TOOLS) && defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) VMCIQueue_GetPointers(vsk->consumeQ, vsk->produceQ, &produceTail, &consumeHead); #endif - copied = VMCIQueue_DequeueV(vsk->produceQ, vsk->consumeQ, + if (flags & MSG_PEEK) { + copied = VMCIQueue_PeekV(vsk->produceQ, vsk->consumeQ, vsk->consumeSize, msg->msg_iov, len); + } else { + copied = VMCIQueue_DequeueV(vsk->produceQ, vsk->consumeQ, + vsk->consumeSize, msg->msg_iov, len); + } + if (copied < 0) { err = -ENOMEM; goto outWait; } -#if defined(VMX86_TOOLS) && defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) - /* - * Detect a wrap-around to maintain queue generation. Note that this is - * safe since we hold the socket lock across the two queue pair - * operations. - */ - if (copied >= vsk->consumeSize - consumeHead) { - vsk->consumeQGeneration++; - } -#endif - ASSERT(copied >= target); /* - * If the other side has shutdown for sending and there is nothing more to - * read, then set our socket's RCV_SHUTDOWN flag and modify the socket - * state. + * We only do these additional bookkeeping/notification steps if we actually + * copied something out of the queue pair instead of just peeking ahead. */ - if (vsk->peerShutdown & SEND_SHUTDOWN) { - if (VMCIQueue_BufReady(vsk->consumeQ, - vsk->produceQ, vsk->consumeSize) <= 0) { - sk->compat_sk_shutdown |= RCV_SHUTDOWN; - sk->compat_sk_state = SS_UNCONNECTED; - compat_sock_set_done(sk); - sk->compat_sk_state_change(sk); + if (!(flags & MSG_PEEK)) { +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) + /* + * Detect a wrap-around to maintain queue generation. Note that this is + * safe since we hold the socket lock across the two queue pair + * operations. + */ + if (copied >= vsk->consumeSize - consumeHead) { + vsk->consumeQGeneration++; } - } +#endif - err = VSockVmciSendReadNotification(sk); - if (err < 0) { - goto outWait; + /* + * If the other side has shutdown for sending and there is nothing more to + * read, then set our socket's RCV_SHUTDOWN flag and modify the socket + * state. + */ + if (vsk->peerShutdown & SEND_SHUTDOWN) { + if (VMCIQueue_BufReady(vsk->consumeQ, + vsk->produceQ, vsk->consumeSize) <= 0) { + sk->compat_sk_shutdown |= RCV_SHUTDOWN; + sk->compat_sk_state = SS_UNCONNECTED; + compat_sock_set_done(sk); + sk->compat_sk_state_change(sk); + } + } + + err = VSockVmciSendReadNotification(sk); + if (err < 0) { + goto outWait; + } } ASSERT(copied <= INT_MAX); @@ -4980,7 +4927,6 @@ out: release_sock(sk); return err; } -#endif /* @@ -5026,15 +4972,9 @@ VSockVmciCreate(struct net *net, // IN case SOCK_DGRAM: sock->ops = &vsockVmciDgramOps; break; -# ifdef VMX86_TOOLS - /* - * Queue pairs are /currently/ only supported within guests, so stream - * sockets are only supported within guests. - */ case SOCK_STREAM: sock->ops = &vsockVmciStreamOps; break; -# endif default: return -ESOCKTNOSUPPORT; } diff --git a/open-vm-tools/modules/linux/vsock/linux/af_vsock.h b/open-vm-tools/modules/linux/vsock/linux/af_vsock.h index a38de6820..ea2ed5cf8 100644 --- a/open-vm-tools/modules/linux/vsock/linux/af_vsock.h +++ b/open-vm-tools/modules/linux/vsock/linux/af_vsock.h @@ -30,6 +30,12 @@ #include "vsockPacket.h" #include "compat_workqueue.h" +#if defined(VMX86_TOOLS) +# include "vmciGuestKernelAPI.h" +#else +# include "vmciHostKernelAPI.h" +#endif + #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 5) # define vsock_sk(__sk) ((VSockVmciSock *)(__sk)->user_data) # define sk_vsock(__vsk) ((__vsk)->sk) @@ -51,7 +57,6 @@ typedef struct VSockVmciSock { struct list_head boundTable; struct list_head connectedTable; VMCIHandle dgHandle; /* For SOCK_DGRAM only. */ -#ifdef VMX86_TOOLS /* Rest are SOCK_STREAM only. */ VMCIHandle qpHandle; VMCIQueue *produceQ; @@ -90,7 +95,6 @@ typedef struct VSockVmciSock { Bool rejected; compat_delayed_work dwork; uint32 peerShutdown; -#endif } VSockVmciSock; #endif /* __AF_VSOCK_H__ */ diff --git a/open-vm-tools/modules/linux/vsock/linux/util.c b/open-vm-tools/modules/linux/vsock/linux/util.c index 7c355c20d..53f2edb8e 100644 --- a/open-vm-tools/modules/linux/vsock/linux/util.c +++ b/open-vm-tools/modules/linux/vsock/linux/util.c @@ -475,7 +475,6 @@ __VSockVmciInConnectedTable(struct sock *sk) // IN } -#ifdef VMX86_TOOLS /* *---------------------------------------------------------------------------- * @@ -854,4 +853,3 @@ VSockVmciIsPending(struct sock *sk) // IN: socket vsk = vsock_sk(sk); return !list_empty(&vsk->pendingLinks); } -#endif diff --git a/open-vm-tools/modules/linux/vsock/linux/util.h b/open-vm-tools/modules/linux/vsock/linux/util.h index 6fc42d5cf..fbf909169 100644 --- a/open-vm-tools/modules/linux/vsock/linux/util.h +++ b/open-vm-tools/modules/linux/vsock/linux/util.h @@ -86,7 +86,6 @@ struct sock *__VSockVmciFindConnectedSocket(struct sockaddr_vm *src, Bool __VSockVmciInBoundTable(struct sock *sk); Bool __VSockVmciInConnectedTable(struct sock *sk); -#ifdef VMX86_TOOLS struct sock *VSockVmciGetPending(struct sock *listener, VSockPacket *pkt); void VSockVmciReleasePending(struct sock *pending); void VSockVmciAddPending(struct sock *listener, struct sock *pending); @@ -97,7 +96,6 @@ void VSockVmciRemoveAccept(struct sock *listener, struct sock *connected); Bool VSockVmciInAcceptQueue(struct sock *sk); Bool VSockVmciIsAcceptQueueEmpty(struct sock *sk); Bool VSockVmciIsPending(struct sock *sk); -#endif static INLINE void VSockVmciInsertBound(struct list_head *list, struct sock *sk); static INLINE void VSockVmciInsertConnected(struct list_head *list, struct sock *sk); diff --git a/open-vm-tools/modules/linux/vsock/linux/vsockSocketWrapper.h b/open-vm-tools/modules/linux/vsock/linux/vsockSocketWrapper.h index 86bb3a6da..5b85beae7 100644 --- a/open-vm-tools/modules/linux/vsock/linux/vsockSocketWrapper.h +++ b/open-vm-tools/modules/linux/vsock/linux/vsockSocketWrapper.h @@ -98,6 +98,7 @@ # define EHOSTUNREACH WSAEHOSTUNREACH # define __ELOCALSHUTDOWN ESHUTDOWN # define __EPEERSHUTDOWN ECONNABORTED +# define __ECONNINPROGRESS EWOULDBLOCK #else #if defined(VMKERNEL) # define EINTR VMK_WAIT_INTERRUPTED @@ -140,6 +141,7 @@ # define EPIPE VMK_BROKEN_PIPE # define __ELOCALSHUTDOWN EPIPE # define __EPEERSHUTDOWN EPIPE +# define __ECONNINPROGRESS EINPROGRESS #endif // VMKERNEL #endif // _WIN32 diff --git a/open-vm-tools/modules/solaris/vmhgfs/Makefile b/open-vm-tools/modules/solaris/vmhgfs/Makefile index bf48b948c..58254a750 100644 --- a/open-vm-tools/modules/solaris/vmhgfs/Makefile +++ b/open-vm-tools/modules/solaris/vmhgfs/Makefile @@ -45,37 +45,52 @@ CFLAGS += -Wall -Werror CFLAGS += $(GLOBAL_DEFS) CFLAGS += $(CC_WARNINGS) CFLAGS += -DVMX86_TOOLS -CFLAGS += -D_KERNEL ifdef VMX86_DEVEL CFLAGS += -DVMX86_DEVEL endif -ifdef VMX86_DEBUG # For assertions +ifdef VMX86_DEBUG # For assertions CFLAGS += -DVMX86_DEBUG endif -ifdef VMX86_LOG # For debug logging +ifdef VMX86_LOG # For debug logging CFLAGS += -DVM_DEBUGGING_ON endif ## ## Objects needed to build the HGFS kernel module ## -SOLHGFS_OBJS := debug.o -SOLHGFS_OBJS += device.o -SOLHGFS_OBJS += filesystem.o -SOLHGFS_OBJS += hgfsState.o -SOLHGFS_OBJS += module.o -SOLHGFS_OBJS += request.o -SOLHGFS_OBJS += vnode.o -SOLHGFS_OBJS += cpName.o -SOLHGFS_OBJS += cpNameLinux.o -SOLHGFS_OBJS += dbllnklst.o -SOLHGFS_OBJS += hgfsEscape.o -SOLHGFS_OBJS += hgfsUtil.o -SOLHGFS_OBJS += sha1.o - -SOLHGFS_32_OBJS := $(addprefix i386/, $(SOLHGFS_OBJS)) -SOLHGFS_64_OBJS := $(addprefix amd64/, $(SOLHGFS_OBJS)) +SOLHGFS_KRN_OBJS := debug.o +SOLHGFS_KRN_OBJS += filesystem.o +SOLHGFS_KRN_OBJS += hgfsBdGlue.o +SOLHGFS_KRN_OBJS += hgfsState.o +SOLHGFS_KRN_OBJS += kernelStubsSolaris.o +SOLHGFS_KRN_OBJS += module.o +SOLHGFS_KRN_OBJS += request.o +SOLHGFS_KRN_OBJS += vnode.o + +SOLHGFS_32_KRN_OBJS := $(addprefix i386/, $(SOLHGFS_KRN_OBJS)) +SOLHGFS_64_KRN_OBJS := $(addprefix amd64/, $(SOLHGFS_KRN_OBJS)) + +SOLHGFS_LIB_OBJS := backdoor.o +SOLHGFS_LIB_OBJS += cpName.o +SOLHGFS_LIB_OBJS += cpNameLinux.o +SOLHGFS_LIB_OBJS += dbllnklst.o +SOLHGFS_LIB_OBJS += hgfsBd.o +SOLHGFS_LIB_OBJS += hgfsEscape.o +SOLHGFS_LIB_OBJS += hgfsUtil.o +SOLHGFS_LIB_OBJS += message.o +SOLHGFS_LIB_OBJS += messageBackdoor.o +SOLHGFS_LIB_OBJS += rpcout.o +SOLHGFS_LIB_OBJS += sha1.o + +SOLHGFS_32_LIB_OBJS := $(addprefix i386/, $(SOLHGFS_LIB_OBJS)) +SOLHGFS_32_LIB_OBJS += i386/backdoorGcc32.o + +SOLHGFS_64_LIB_OBJS := $(addprefix amd64/, $(SOLHGFS_LIB_OBJS)) +SOLHGFS_64_LIB_OBJS += amd64/backdoorGcc64.o + +SOLHGFS_32_OBJS := $(SOLHGFS_32_KRN_OBJS) $(SOLHGFS_32_LIB_OBJS) +SOLHGFS_64_OBJS := $(SOLHGFS_64_KRN_OBJS) $(SOLHGFS_64_LIB_OBJS) MODULE_32 := i386/$(MODULE) MODULE_64 := amd64/$(MODULE) @@ -112,7 +127,11 @@ $(MODULE_32): $(SOLHGFS_32_OBJS) @echo "Linking $(MODULE_32)" $(LD) $(LDFLAGS_32) -r -o $(MODULE_32) $(SOLHGFS_32_OBJS) -$(SOLHGFS_32_OBJS): i386/%.o: %.c +$(SOLHGFS_32_KRN_OBJS): i386/%.o: %.c + @echo "Compiling $( +# define BACKDOOR_LOG(args) Debug args +# define BACKDOOR_LOG_PROTO_STRUCT(x) BackdoorPrintProtoStruct((x)) +# define BACKDOOR_LOG_HB_PROTO_STRUCT(x) BackdoorPrintHbProtoStruct((x)) + + +/* + *---------------------------------------------------------------------------- + * + * BackdoorPrintProtoStruct -- + * BackdoorPrintHbProtoStruct -- + * + * Print the contents of the specified backdoor protocol structure via + * printf. + * + * Results: + * None. + * + * Side effects: + * Output to stdout. + * + *---------------------------------------------------------------------------- + */ + +void +BackdoorPrintProtoStruct(Backdoor_proto *myBp) +{ + Debug("magic 0x%08x, command %d, size %"FMTSZ"u, port %d\n", + myBp->in.ax.word, myBp->in.cx.halfs.low, + myBp->in.size, myBp->in.dx.halfs.low); + +#ifndef VM_X86_64 + Debug("ax %#x, " + "bx %#x, " + "cx %#x, " + "dx %#x, " + "si %#x, " + "di %#x\n", + myBp->out.ax.word, + myBp->out.bx.word, + myBp->out.cx.word, + myBp->out.dx.word, + myBp->out.si.word, + myBp->out.di.word); +#else + Debug("ax %#"FMT64"x, " + "bx %#"FMT64"x, " + "cx %#"FMT64"x, " + "dx %#"FMT64"x, " + "si %#"FMT64"x, " + "di %#"FMT64"x\n", + myBp->out.ax.quad, + myBp->out.bx.quad, + myBp->out.cx.quad, + myBp->out.dx.quad, + myBp->out.si.quad, + myBp->out.di.quad); +#endif +} + + +void +BackdoorPrintHbProtoStruct(Backdoor_proto_hb *myBp) +{ + Debug("magic 0x%08x, command %d, size %"FMTSZ"u, port %d, " + "srcAddr %"FMTSZ"u, dstAddr %"FMTSZ"u\n", + myBp->in.ax.word, myBp->in.bx.halfs.low, myBp->in.size, + myBp->in.dx.halfs.low, myBp->in.srcAddr, myBp->in.dstAddr); + +#ifndef VM_X86_64 + Debug("ax %#x, " + "bx %#x, " + "cx %#x, " + "dx %#x, " + "si %#x, " + "di %#x, " + "bp %#x\n", + myBp->out.ax.word, + myBp->out.bx.word, + myBp->out.cx.word, + myBp->out.dx.word, + myBp->out.si.word, + myBp->out.di.word, + myBp->out.bp.word); +#else + Debug("ax %#"FMT64"x, " + "bx %#"FMT64"x, " + "cx %#"FMT64"x, " + "dx %#"FMT64"x, " + "si %#"FMT64"x, " + "di %#"FMT64"x, " + "bp %#"FMT64"x\n", + myBp->out.ax.quad, + myBp->out.bx.quad, + myBp->out.cx.quad, + myBp->out.dx.quad, + myBp->out.si.quad, + myBp->out.di.quad, + myBp->out.bp.quad); +#endif +} + +#else +# define BACKDOOR_LOG(args) +# define BACKDOOR_LOG_PROTO_STRUCT(x) +# define BACKDOOR_LOG_HB_PROTO_STRUCT(x) +#endif + + +/* + *----------------------------------------------------------------------------- + * + * Backdoor -- + * + * Send a low-bandwidth basic request (16 bytes) to vmware, and return its + * reply (24 bytes). + * + * Result: + * None + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +Backdoor(Backdoor_proto *myBp) // IN/OUT +{ + ASSERT(myBp); + + myBp->in.ax.word = BDOOR_MAGIC; + myBp->in.dx.halfs.low = BDOOR_PORT; + + BACKDOOR_LOG(("Backdoor: before ")); + BACKDOOR_LOG_PROTO_STRUCT(myBp); + + Backdoor_InOut(myBp); + + BACKDOOR_LOG(("Backdoor: after ")); + BACKDOOR_LOG_PROTO_STRUCT(myBp); +} + + +/* + *----------------------------------------------------------------------------- + * + * Backdoor_HbOut -- + * + * Send a high-bandwidth basic request to vmware, and return its + * reply. + * + * Result: + * The host-side response is returned via the IN/OUT parameter. + * + * Side-effects: + * Pokes the high-bandwidth backdoor. + * + *----------------------------------------------------------------------------- + */ + +void +Backdoor_HbOut(Backdoor_proto_hb *myBp) // IN/OUT +{ + ASSERT(myBp); + + myBp->in.ax.word = BDOOR_MAGIC; + myBp->in.dx.halfs.low = BDOORHB_PORT; + + BACKDOOR_LOG(("Backdoor_HbOut: before ")); + BACKDOOR_LOG_HB_PROTO_STRUCT(myBp); + + BackdoorHbOut(myBp); + + BACKDOOR_LOG(("Backdoor_HbOut: after ")); + BACKDOOR_LOG_HB_PROTO_STRUCT(myBp); +} + + +/* + *----------------------------------------------------------------------------- + * + * Backdoor_HbIn -- + * + * Send a basic request to vmware, and return its high-bandwidth + * reply + * + * Result: + * Host-side response returned via the IN/OUT parameter. + * + * Side-effects: + * Pokes the high-bandwidth backdoor. + * + *----------------------------------------------------------------------------- + */ + +void +Backdoor_HbIn(Backdoor_proto_hb *myBp) // IN/OUT +{ + ASSERT(myBp); + + myBp->in.ax.word = BDOOR_MAGIC; + myBp->in.dx.halfs.low = BDOORHB_PORT; + + BACKDOOR_LOG(("Backdoor_HbIn: before ")); + BACKDOOR_LOG_HB_PROTO_STRUCT(myBp); + + BackdoorHbIn(myBp); + + BACKDOOR_LOG(("Backdoor_HbIn: after ")); + BACKDOOR_LOG_HB_PROTO_STRUCT(myBp); +} + +#ifdef __cplusplus +} +#endif diff --git a/open-vm-tools/modules/solaris/vmhgfs/backdoor.h b/open-vm-tools/modules/solaris/vmhgfs/backdoor.h new file mode 100644 index 000000000..ad7b42229 --- /dev/null +++ b/open-vm-tools/modules/solaris/vmhgfs/backdoor.h @@ -0,0 +1,44 @@ +/********************************************************* + * Copyright (C) 1999 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * backdoor.h -- + * + * First layer of the internal communication channel between guest + * applications and vmware + */ + +#ifndef _BACKDOOR_H_ +#define _BACKDOOR_H_ + +#include "vm_basic_types.h" +#include "vm_assert.h" + +#include "backdoor_types.h" + +void +Backdoor(Backdoor_proto *bp); // IN/OUT + +void +Backdoor_InOut(Backdoor_proto *bp); // IN/OUT + +void +Backdoor_HbOut(Backdoor_proto_hb *bp); // IN/OUT + +void +Backdoor_HbIn(Backdoor_proto_hb *bp); // IN/OUT + +#endif /* _BACKDOOR_H_ */ diff --git a/open-vm-tools/modules/solaris/vmhgfs/backdoorGcc32.c b/open-vm-tools/modules/solaris/vmhgfs/backdoorGcc32.c new file mode 100644 index 000000000..98e06e3bd --- /dev/null +++ b/open-vm-tools/modules/solaris/vmhgfs/backdoorGcc32.c @@ -0,0 +1,219 @@ +/********************************************************* + * Copyright (C) 2005 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * backdoorGcc32.c -- + * + * Implements the real work for guest-side backdoor for GCC, 32-bit + * target (supports inline ASM, GAS syntax). The asm sections are marked + * volatile since vmware can change the registers content without the + * compiler knowing it. + * + * XXX + * I tried to write this more cleanly, but: + * - There is no way to specify an "ebp" constraint + * - "ebp" is ignored when specified as cloberred register + * - gas barfs when there is more than 10 operands + * - gas 2.7.2.3, depending on the order of the operands, can + * mis-assemble without any warning + * --hpreg + * + * Note that the problems with gas noted above might longer be relevant + * now that we've upgraded most of our compiler versions. + * --rrdharan + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "backdoor.h" +#include "backdoorInt.h" + +/* + *---------------------------------------------------------------------------- + * + * Backdoor_InOut -- + * + * Send a low-bandwidth basic request (16 bytes) to vmware, and return its + * reply (24 bytes). + * + * Results: + * Host-side response returned in bp IN/OUT parameter. + * + * Side effects: + * Pokes the backdoor. + * + *---------------------------------------------------------------------------- + */ + +void +Backdoor_InOut(Backdoor_proto *myBp) // IN/OUT +{ + uint32 dummy; + + __asm__ __volatile__( +#ifdef __PIC__ + "pushl %%ebx" "\n\t" +#endif + "pushl %%eax" "\n\t" + "movl 20(%%eax), %%edi" "\n\t" + "movl 16(%%eax), %%esi" "\n\t" + "movl 12(%%eax), %%edx" "\n\t" + "movl 8(%%eax), %%ecx" "\n\t" + "movl 4(%%eax), %%ebx" "\n\t" + "movl (%%eax), %%eax" "\n\t" + "inl %%dx, %%eax" "\n\t" + "xchgl %%eax, (%%esp)" "\n\t" + "movl %%edi, 20(%%eax)" "\n\t" + "movl %%esi, 16(%%eax)" "\n\t" + "movl %%edx, 12(%%eax)" "\n\t" + "movl %%ecx, 8(%%eax)" "\n\t" + "movl %%ebx, 4(%%eax)" "\n\t" + "popl (%%eax)" "\n\t" +#ifdef __PIC__ + "popl %%ebx" "\n\t" +#endif + : "=a" (dummy) + : "0" (myBp) + /* + * vmware can modify the whole VM state without the compiler knowing + * it. So far it does not modify EFLAGS. --hpreg + */ + : +#ifndef __PIC__ + "ebx", +#endif + "ecx", "edx", "esi", "edi", "memory" + ); +} + + +/* + *----------------------------------------------------------------------------- + * + * BackdoorHbIn -- + * BackdoorHbOut -- + * + * Send a high-bandwidth basic request to vmware, and return its + * reply. + * + * Results: + * Host-side response returned in bp IN/OUT parameter. + * + * Side-effects: + * Pokes the high-bandwidth backdoor port. + * + *----------------------------------------------------------------------------- + */ + +void +BackdoorHbIn(Backdoor_proto_hb *myBp) // IN/OUT +{ + uint32 dummy; + + __asm__ __volatile__( +#ifdef __PIC__ + "pushl %%ebx" "\n\t" +#endif + "pushl %%ebp" "\n\t" + + "pushl %%eax" "\n\t" + "movl 24(%%eax), %%ebp" "\n\t" + "movl 20(%%eax), %%edi" "\n\t" + "movl 16(%%eax), %%esi" "\n\t" + "movl 12(%%eax), %%edx" "\n\t" + "movl 8(%%eax), %%ecx" "\n\t" + "movl 4(%%eax), %%ebx" "\n\t" + "movl (%%eax), %%eax" "\n\t" + "cld" "\n\t" + "rep; insb" "\n\t" + "xchgl %%eax, (%%esp)" "\n\t" + "movl %%ebp, 24(%%eax)" "\n\t" + "movl %%edi, 20(%%eax)" "\n\t" + "movl %%esi, 16(%%eax)" "\n\t" + "movl %%edx, 12(%%eax)" "\n\t" + "movl %%ecx, 8(%%eax)" "\n\t" + "movl %%ebx, 4(%%eax)" "\n\t" + "popl (%%eax)" "\n\t" + + "popl %%ebp" "\n\t" +#ifdef __PIC__ + "popl %%ebx" "\n\t" +#endif + : "=a" (dummy) + : "0" (myBp) + /* + * vmware can modify the whole VM state without the compiler knowing + * it. --hpreg + */ + : +#ifndef __PIC__ + "ebx", +#endif + "ecx", "edx", "esi", "edi", "memory", "cc" + ); +} + + +void +BackdoorHbOut(Backdoor_proto_hb *myBp) // IN/OUT +{ + uint32 dummy; + + __asm__ __volatile__( +#ifdef __PIC__ + "pushl %%ebx" "\n\t" +#endif + "pushl %%ebp" "\n\t" + + "pushl %%eax" "\n\t" + "movl 24(%%eax), %%ebp" "\n\t" + "movl 20(%%eax), %%edi" "\n\t" + "movl 16(%%eax), %%esi" "\n\t" + "movl 12(%%eax), %%edx" "\n\t" + "movl 8(%%eax), %%ecx" "\n\t" + "movl 4(%%eax), %%ebx" "\n\t" + "movl (%%eax), %%eax" "\n\t" + "cld" "\n\t" + "rep; outsb" "\n\t" + "xchgl %%eax, (%%esp)" "\n\t" + "movl %%ebp, 24(%%eax)" "\n\t" + "movl %%edi, 20(%%eax)" "\n\t" + "movl %%esi, 16(%%eax)" "\n\t" + "movl %%edx, 12(%%eax)" "\n\t" + "movl %%ecx, 8(%%eax)" "\n\t" + "movl %%ebx, 4(%%eax)" "\n\t" + "popl (%%eax)" "\n\t" + + "popl %%ebp" "\n\t" +#ifdef __PIC__ + "popl %%ebx" "\n\t" +#endif + : "=a" (dummy) + : "0" (myBp) + : +#ifndef __PIC__ + "ebx", +#endif + "ecx", "edx", "esi", "edi", "memory", "cc" + ); +} + +#ifdef __cplusplus +} +#endif + diff --git a/open-vm-tools/modules/solaris/vmhgfs/backdoorGcc64.c b/open-vm-tools/modules/solaris/vmhgfs/backdoorGcc64.c new file mode 100644 index 000000000..6549b32ee --- /dev/null +++ b/open-vm-tools/modules/solaris/vmhgfs/backdoorGcc64.c @@ -0,0 +1,183 @@ +/********************************************************* + * Copyright (C) 2005 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * backdoorGcc64.c -- + * + * Implements the real work for guest-side backdoor for GCC, 64-bit + * target (supports inline ASM, GAS syntax). The asm sections are marked + * volatile since vmware can change the registers content without the + * compiler knowing it. + * + * See backdoorGCC32.c (from which this code was mostly copied) for + * details on why the ASM is written this way. Also note that it might be + * possible to write the asm blocks using the symbolic operand specifiers + * in such a way that the same asm would generate correct code for both + * 32-bit and 64-bit targets, but I'm too lazy to figure it all out. + * --rrdharan + */ +#ifdef __cplusplus +extern "C" { +#endif + +#include "backdoor.h" +#include "backdoorInt.h" + + +/* + *---------------------------------------------------------------------------- + * + * Backdoor_InOut -- + * + * Send a low-bandwidth basic request (16 bytes) to vmware, and return its + * reply (24 bytes). + * + * Results: + * Host-side response returned in bp IN/OUT parameter. + * + * Side effects: + * Pokes the backdoor. + * + *---------------------------------------------------------------------------- + */ + +void +Backdoor_InOut(Backdoor_proto *myBp) // IN/OUT +{ + uint64 dummy; + + __asm__ __volatile__( + "pushq %%rax" "\n\t" + "movq 40(%%rax), %%rdi" "\n\t" + "movq 32(%%rax), %%rsi" "\n\t" + "movq 24(%%rax), %%rdx" "\n\t" + "movq 16(%%rax), %%rcx" "\n\t" + "movq 8(%%rax), %%rbx" "\n\t" + "movq (%%rax), %%rax" "\n\t" + "inl %%dx, %%eax" "\n\t" /* NB: There is no inq instruction */ + "xchgq %%rax, (%%rsp)" "\n\t" + "movq %%rdi, 40(%%rax)" "\n\t" + "movq %%rsi, 32(%%rax)" "\n\t" + "movq %%rdx, 24(%%rax)" "\n\t" + "movq %%rcx, 16(%%rax)" "\n\t" + "movq %%rbx, 8(%%rax)" "\n\t" + "popq (%%rax)" + : "=a" (dummy) + : "0" (myBp) + /* + * vmware can modify the whole VM state without the compiler knowing + * it. So far it does not modify EFLAGS. --hpreg + */ + : "rbx", "rcx", "rdx", "rsi", "rdi", "memory" + ); +} + + +/* + *----------------------------------------------------------------------------- + * + * BackdoorHbIn -- + * BackdoorHbOut -- + * + * Send a high-bandwidth basic request to vmware, and return its + * reply. + * + * Results: + * Host-side response returned in bp IN/OUT parameter. + * + * Side-effects: + * Pokes the high-bandwidth backdoor port. + * + *----------------------------------------------------------------------------- + */ + +void +BackdoorHbIn(Backdoor_proto_hb *myBp) // IN/OUT +{ + uint32 dummy; + + __asm__ __volatile__( + "pushq %%rbp" "\n\t" + + "pushq %%rax" "\n\t" + "movq 48(%%rax), %%rbp" "\n\t" + "movq 40(%%rax), %%rdi" "\n\t" + "movq 32(%%rax), %%rsi" "\n\t" + "movq 24(%%rax), %%rdx" "\n\t" + "movq 16(%%rax), %%rcx" "\n\t" + "movq 8(%%rax), %%rbx" "\n\t" + "movq (%%rax), %%rax" "\n\t" + "cld" "\n\t" + "rep; insb" "\n\t" + "xchgq %%rax, (%%rsp)" "\n\t" + "movq %%rbp, 48(%%rax)" "\n\t" + "movq %%rdi, 40(%%rax)" "\n\t" + "movq %%rsi, 32(%%rax)" "\n\t" + "movq %%rdx, 24(%%rax)" "\n\t" + "movq %%rcx, 16(%%rax)" "\n\t" + "movq %%rbx, 8(%%rax)" "\n\t" + "popq (%%rax)" "\n\t" + + "popq %%rbp" + : "=a" (dummy) + : "0" (myBp) + /* + * vmware can modify the whole VM state without the compiler knowing + * it. --hpreg + */ + : "rbx", "rcx", "rdx", "rsi", "rdi", "memory", "cc" + ); +} + + +void +BackdoorHbOut(Backdoor_proto_hb *myBp) // IN/OUT +{ + uint64 dummy; + + __asm__ __volatile__( + "pushq %%rbp" "\n\t" + + "pushq %%rax" "\n\t" + "movq 48(%%rax), %%rbp" "\n\t" + "movq 40(%%rax), %%rdi" "\n\t" + "movq 32(%%rax), %%rsi" "\n\t" + "movq 24(%%rax), %%rdx" "\n\t" + "movq 16(%%rax), %%rcx" "\n\t" + "movq 8(%%rax), %%rbx" "\n\t" + "movq (%%rax), %%rax" "\n\t" + "cld" "\n\t" + "rep; outsb" "\n\t" + "xchgq %%rax, (%%rsp)" "\n\t" + "movq %%rbp, 48(%%rax)" "\n\t" + "movq %%rdi, 40(%%rax)" "\n\t" + "movq %%rsi, 32(%%rax)" "\n\t" + "movq %%rdx, 24(%%rax)" "\n\t" + "movq %%rcx, 16(%%rax)" "\n\t" + "movq %%rbx, 8(%%rax)" "\n\t" + "popq (%%rax)" "\n\t" + + "popq %%rbp" + : "=a" (dummy) + : "0" (myBp) + : "rbx", "rcx", "rdx", "rsi", "rdi", "memory", "cc" + ); +} + + +#ifdef __cplusplus +} +#endif diff --git a/open-vm-tools/modules/solaris/vmhgfs/backdoorInt.h b/open-vm-tools/modules/solaris/vmhgfs/backdoorInt.h new file mode 100644 index 000000000..07222b427 --- /dev/null +++ b/open-vm-tools/modules/solaris/vmhgfs/backdoorInt.h @@ -0,0 +1,24 @@ +/********************************************************* + * Copyright (C) 2005 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * backdoorInt.h -- + * + * Internal function prototypes for the real backdoor work. + */ + +void BackdoorHbIn(Backdoor_proto_hb *bp); +void BackdoorHbOut(Backdoor_proto_hb *bp); diff --git a/open-vm-tools/modules/solaris/vmhgfs/backdoor_def.h b/open-vm-tools/modules/solaris/vmhgfs/backdoor_def.h new file mode 100644 index 000000000..7f66e0d77 --- /dev/null +++ b/open-vm-tools/modules/solaris/vmhgfs/backdoor_def.h @@ -0,0 +1,176 @@ +/********************************************************* + * Copyright (C) 1998 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * backdoor_def.h -- + * + * This contains backdoor defines that can be included from + * an assembly language file. + */ + + + +#ifndef _BACKDOOR_DEF_H_ +#define _BACKDOOR_DEF_H_ + +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_USERLEVEL +#define INCLUDE_ALLOW_VMMEXT +#define INCLUDE_ALLOW_VMCORE +#define INCLUDE_ALLOW_VMKERNEL +#include "includeCheck.h" + +/* + * If you want to add a new low-level backdoor call for a guest userland + * application, please consider using the GuestRpc mechanism instead. --hpreg + */ + +#define BDOOR_MAGIC 0x564D5868 + +/* Low-bandwidth backdoor port. --hpreg */ + +#define BDOOR_PORT 0x5658 + +#define BDOOR_CMD_GETMHZ 1 +/* + * BDOOR_CMD_APMFUNCTION is used by: + * + * o The FrobOS code, which instead should either program the virtual chipset + * (like the new BIOS code does, matthias offered to implement that), or not + * use any VM-specific code (which requires that we correctly implement + * "power off on CLI HLT" for SMP VMs, boris offered to implement that) + * + * o The old BIOS code, which will soon be jettisoned + * + * --hpreg + */ +#define BDOOR_CMD_APMFUNCTION 2 +#define BDOOR_CMD_GETDISKGEO 3 +#define BDOOR_CMD_GETPTRLOCATION 4 +#define BDOOR_CMD_SETPTRLOCATION 5 +#define BDOOR_CMD_GETSELLENGTH 6 +#define BDOOR_CMD_GETNEXTPIECE 7 +#define BDOOR_CMD_SETSELLENGTH 8 +#define BDOOR_CMD_SETNEXTPIECE 9 +#define BDOOR_CMD_GETVERSION 10 +#define BDOOR_CMD_GETDEVICELISTELEMENT 11 +#define BDOOR_CMD_TOGGLEDEVICE 12 +#define BDOOR_CMD_GETGUIOPTIONS 13 +#define BDOOR_CMD_SETGUIOPTIONS 14 +#define BDOOR_CMD_GETSCREENSIZE 15 +#define BDOOR_CMD_MONITOR_CONTROL 16 +#define BDOOR_CMD_GETHWVERSION 17 +#define BDOOR_CMD_OSNOTFOUND 18 +#define BDOOR_CMD_GETUUID 19 +#define BDOOR_CMD_GETMEMSIZE 20 +#define BDOOR_CMD_HOSTCOPY 21 /* Devel only */ +#define BDOOR_CMD_SERVICE_VM 22 /* prototype only */ +#define BDOOR_CMD_GETTIME 23 /* Deprecated. Use GETTIMEFULL. */ +#define BDOOR_CMD_STOPCATCHUP 24 +#define BDOOR_CMD_PUTCHR 25 /* Devel only */ +#define BDOOR_CMD_ENABLE_MSG 26 /* Devel only */ +#define BDOOR_CMD_GOTO_TCL 27 /* Devel only */ +#define BDOOR_CMD_INITPCIOPROM 28 +#define BDOOR_CMD_INT13 29 +#define BDOOR_CMD_MESSAGE 30 +#define BDOOR_CMD_RSVD0 31 +#define BDOOR_CMD_RSVD1 32 +#define BDOOR_CMD_RSVD2 33 +#define BDOOR_CMD_ISACPIDISABLED 34 +#define BDOOR_CMD_TOE 35 /* Not in use */ +#define BDOOR_CMD_ISMOUSEABSOLUTE 36 +#define BDOOR_CMD_PATCH_SMBIOS_STRUCTS 37 +#define BDOOR_CMD_MAPMEM 38 /* Devel only */ +#define BDOOR_CMD_ABSPOINTER_DATA 39 +#define BDOOR_CMD_ABSPOINTER_STATUS 40 +#define BDOOR_CMD_ABSPOINTER_COMMAND 41 +#define BDOOR_CMD_TIMER_SPONGE 42 +#define BDOOR_CMD_PATCH_ACPI_TABLES 43 +/* Catch-all to allow synchronous tests */ +#define BDOOR_CMD_DEVEL_FAKEHARDWARE 44 /* Debug only - needed in beta */ +#define BDOOR_CMD_GETHZ 45 +#define BDOOR_CMD_GETTIMEFULL 46 +#define BDOOR_CMD_STATELOGGER 47 +#define BDOOR_CMD_CHECKFORCEBIOSSETUP 48 +#define BDOOR_CMD_LAZYTIMEREMULATION 49 +#define BDOOR_CMD_BIOSBBS 50 +#define BDOOR_CMD_VASSERT 51 +#define BDOOR_CMD_ISGOSDARWIN 52 +#define BDOOR_CMD_DEBUGEVENT 53 +#define BDOOR_CMD_OSNOTMACOSXSERVER 54 +#define BDOOR_CMD_GETTIMEFULL_WITH_LAG 55 +#define BDOOR_CMD_ACPI_HOTPLUG_DEVICE 56 +#define BDOOR_CMD_ACPI_HOTPLUG_MEMORY 57 +#define BDOOR_CMD_ACPI_HOTPLUG_CBRET 58 +#define BDOOR_CMD_GET_HOST_VIDEO_MODES 59 +#define BDOOR_CMD_ACPI_HOTPLUG_CPU 60 +#define BDOOR_CMD_MAX 61 + +/* + * IMPORTANT NOTE: When modifying the behavior of an existing backdoor command, + * you must adhere to the semantics expected by the oldest Tools who use that + * command. Specifically, do not alter the way in which the command modifies + * the registers. Otherwise backwards compatibility will suffer. + */ + +/* High-bandwidth backdoor port. --hpreg */ + +#define BDOORHB_PORT 0x5659 + +#define BDOORHB_CMD_MESSAGE 0 +#define BDOORHB_CMD_VASSERT 1 +#define BDOORHB_CMD_MAX 2 + +/* + * There is another backdoor which allows access to certain TSC-related + * values using otherwise illegal PMC indices when the pseudo_perfctr + * control flag is set. + */ + +#define BDOOR_PMC_HW_TSC 0x10000 +#define BDOOR_PMC_REAL_NS 0x10001 +#define BDOOR_PMC_APPARENT_NS 0x10002 + +#define IS_BDOOR_PMC(index) (((index) | 3) == 0x10003) +#define BDOOR_CMD(ecx) ((ecx) & 0xffff) + + +#ifdef VMM +/* + *---------------------------------------------------------------------- + * + * Backdoor_CmdRequiresFullyValidVCPU -- + * + * A few backdoor commands require the full VCPU to be valid + * (including GDTR, IDTR, TR and LDTR). The rest get read/write + * access to GPRs and read access to Segment registers (selectors). + * + * Result: + * True iff VECX contains a command that require the full VCPU to + * be valid. + * + *---------------------------------------------------------------------- + */ +static INLINE Bool +Backdoor_CmdRequiresFullyValidVCPU(unsigned cmd) +{ + return cmd == BDOOR_CMD_RSVD0 || + cmd == BDOOR_CMD_RSVD1 || + cmd == BDOOR_CMD_RSVD2; +} +#endif + +#endif diff --git a/open-vm-tools/modules/solaris/vmhgfs/backdoor_types.h b/open-vm-tools/modules/solaris/vmhgfs/backdoor_types.h new file mode 100644 index 000000000..d53cef0cf --- /dev/null +++ b/open-vm-tools/modules/solaris/vmhgfs/backdoor_types.h @@ -0,0 +1,116 @@ +/********************************************************* + * Copyright (C) 1999 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * backdoor_types.h -- + * + * Type definitions for backdoor interaction code. + */ + +#ifndef _BACKDOOR_TYPES_H_ +#define _BACKDOOR_TYPES_H_ + +#ifndef VM_I386 +#error The backdoor protocol is only supported on x86 architectures. +#endif + +/* + * These #defines are intended for defining register structs as part of + * existing named unions. If the union should encapsulate the register + * (and nothing else), use DECLARE_REG_NAMED_STRUCT defined below. + */ + +#define DECLARE_REG32_STRUCT \ + struct { \ + uint16 low; \ + uint16 high; \ + } halfs; \ + uint32 word + +#define DECLARE_REG64_STRUCT \ + DECLARE_REG32_STRUCT; \ + struct { \ + uint32 low; \ + uint32 high; \ + } words; \ + uint64 quad + +#ifndef VM_X86_64 +#define DECLARE_REG_STRUCT DECLARE_REG32_STRUCT +#else +#define DECLARE_REG_STRUCT DECLARE_REG64_STRUCT +#endif + +#define DECLARE_REG_NAMED_STRUCT(_r) \ + union { DECLARE_REG_STRUCT; } _r + +/* + * Some of the registers are expressed by semantic name, because if they were + * expressed as register structs declared above, we could only address them + * by fixed size (half-word, word, quad, etc.) instead of by varying size + * (size_t, uintptr_t). + * + * To be cleaner, these registers are expressed ONLY by semantic name, + * rather than by a union of the semantic name and a register struct. + */ +typedef union { + struct { + DECLARE_REG_NAMED_STRUCT(ax); + size_t size; /* Register bx. */ + DECLARE_REG_NAMED_STRUCT(cx); + DECLARE_REG_NAMED_STRUCT(dx); + DECLARE_REG_NAMED_STRUCT(si); + DECLARE_REG_NAMED_STRUCT(di); + } in; + struct { + DECLARE_REG_NAMED_STRUCT(ax); + DECLARE_REG_NAMED_STRUCT(bx); + DECLARE_REG_NAMED_STRUCT(cx); + DECLARE_REG_NAMED_STRUCT(dx); + DECLARE_REG_NAMED_STRUCT(si); + DECLARE_REG_NAMED_STRUCT(di); + } out; +} Backdoor_proto; + +typedef union { + struct { + DECLARE_REG_NAMED_STRUCT(ax); + DECLARE_REG_NAMED_STRUCT(bx); + size_t size; /* Register cx. */ + DECLARE_REG_NAMED_STRUCT(dx); + uintptr_t srcAddr; /* Register si. */ + uintptr_t dstAddr; /* Register di. */ + DECLARE_REG_NAMED_STRUCT(bp); + } in; + struct { + DECLARE_REG_NAMED_STRUCT(ax); + DECLARE_REG_NAMED_STRUCT(bx); + DECLARE_REG_NAMED_STRUCT(cx); + DECLARE_REG_NAMED_STRUCT(dx); + DECLARE_REG_NAMED_STRUCT(si); + DECLARE_REG_NAMED_STRUCT(di); + DECLARE_REG_NAMED_STRUCT(bp); + } out; +} Backdoor_proto_hb; + +MY_ASSERTS(BACKDOOR_STRUCT_SIZES, + ASSERT_ON_COMPILE(sizeof(Backdoor_proto) == 6 * sizeof(uintptr_t)); + ASSERT_ON_COMPILE(sizeof(Backdoor_proto_hb) == 7 * sizeof(uintptr_t)); +) + +#undef DECLARE_REG_STRUCT + +#endif /* _BACKDOOR_TYPES_H_ */ diff --git a/open-vm-tools/modules/solaris/vmhgfs/debug.c b/open-vm-tools/modules/solaris/vmhgfs/debug.c index 0e6be2446..b82476fdd 100644 --- a/open-vm-tools/modules/solaris/vmhgfs/debug.c +++ b/open-vm-tools/modules/solaris/vmhgfs/debug.c @@ -145,7 +145,8 @@ HgfsDebugPrintVfs(char *str, struct vfs *vfsp) */ INLINE void -HgfsDebugPrintVnode(uint32 level, char *str, struct vnode *vnodep) +HgfsDebugPrintVnode(uint32 level, char *str, + struct vnode *vnodep, Bool printFileName) { ASSERT(str); ASSERT(vnodep); @@ -172,7 +173,7 @@ HgfsDebugPrintVnode(uint32 level, char *str, struct vnode *vnodep) DEBUG(level, " v_locality : %p\n", vnodep->v_locality); DEBUG(level, " v_nbllock : %p\n", &vnodep->v_nbllock); - if (HGFS_VP_TO_OFP(vnodep) && HGFS_VP_TO_FP(vnodep)) { + if (printFileName && HGFS_VP_TO_OFP(vnodep) && HGFS_VP_TO_FP(vnodep)) { DEBUG(level, " filename : %s\n", HGFS_VP_TO_FILENAME(vnodep)); } } @@ -409,11 +410,6 @@ HgfsDebugPrintReqPool(const char *str) } -/* - * Gotta have Panic. Stolen from vmnix. - * (Then stolen from hgfs/driver/linux/main.c and tweaked for Solaris) - */ - /* * There is a problem in Solaris 9's header files when using the va_start * and va_end macros, so we manually do what the preprocessor would have @@ -426,58 +422,62 @@ HgfsDebugPrintReqPool(const char *str) * argument. */ #ifdef SOL9 -# define compat_va_list char * # define compat_va_start(arg, fmt) arg = ((char *)__builtin_next_arg(fmt)) # define compat_va_end(arg) #else -# define compat_va_list va_list # define compat_va_start(arg, fmt) va_start(arg, fmt) # define compat_va_end(arg) va_end(arg) #endif -void -Panic(const char *fmt, ...) // IN +static void +vLog(const char *fmt, + va_list args) { - compat_va_list args; +#ifdef VM_DEBUG_LEV char buffer[1024]; - volatile char *p = NULL; - compat_va_start(args, fmt); - vsprintf(buffer, fmt, args); - compat_va_end(args); + /* + * We check this here to avoid unnecessarily manipulating buffer if we + * aren't even going to print the log. + */ + if (VM_DEBUG_LOG & VM_DEBUG_LEV) { + vsprintf(buffer, fmt, args); + cmn_err(HGFS_DEBUG, "%s", buffer); + } +#endif +} + + +/* + * For compatibility with existing code. + */ - cmn_err(HGFS_ERROR, "%s", buffer); +void +Log(const char *fmt, ...) // IN: format string, etc +{ + va_list args; - /* force segfault */ - buffer[0] = *p; - while (1) ; // avoid compiler warning + compat_va_start(args, fmt); + vLog(fmt, args); + compat_va_end(args); } /* * For compatibility with existing code. */ + void -Log(const char *fmt, ...) +Debug(const char *fmt, ...) // IN: format string, etc. { -#ifdef VM_DEBUG_LEV - compat_va_list args; - char buffer[1024]; + va_list args; - /* - * We check this here to avoid unnecessarily manipulating buffer if we - * aren't even going to print the log. - */ - if (VM_DEBUG_LOG & VM_DEBUG_LEV) { - compat_va_start(args, fmt); - vsprintf(buffer, fmt, args); - compat_va_end(args); - - cmn_err(HGFS_DEBUG, "%s", buffer); - } -#endif + compat_va_start(args, fmt); + vLog(fmt, args); + compat_va_end(args); } + #undef compat_va_list #undef compat_va_start #undef compat_va_end diff --git a/open-vm-tools/modules/solaris/vmhgfs/debug.h b/open-vm-tools/modules/solaris/vmhgfs/debug.h index 9811fd0dd..657eff0ff 100644 --- a/open-vm-tools/modules/solaris/vmhgfs/debug.h +++ b/open-vm-tools/modules/solaris/vmhgfs/debug.h @@ -25,12 +25,15 @@ #ifndef __DEBUG_H_ #define __DEBUG_H_ +#ifdef _KERNEL + #include #include #include "hgfsSolaris.h" #include "filesystem.h" +#endif /* * Debugging @@ -71,7 +74,6 @@ #define VM_DEBUG_LEV (VM_DEBUG_ALWAYS | VM_DEBUG_FAIL) #endif - #ifdef VM_DEBUG_LEV #define DEBUG(type, args...) \ ((type & VM_DEBUG_LEV) ? (cmn_err(HGFS_DEBUG, args)) : 0) @@ -83,18 +85,24 @@ /* * Prototypes */ + +#ifdef _KERNEL + INLINE void HgfsDebugPrintVfssw(char *str, struct vfssw *vfsswp); INLINE void HgfsDebugPrintVfs(char *str, struct vfs *vfsp); -INLINE void HgfsDebugPrintVnode(uint32 level, char *str, struct vnode *vnodep); +INLINE void HgfsDebugPrintVnode(uint32 level, char *str, + struct vnode *vnodep, Bool printFileName); INLINE void HgfsDebugPrintCred(char *str, struct cred *credp); INLINE void HgfsDebugPrintMounta(char *str, struct mounta *mntp); INLINE void HgfsDebugPrintVattr(const struct vattr *vap); void HgfsDebugPrintReqList(DblLnkLst_Links *listAnchor); void HgfsDebugPrintReq(const char *str, HgfsReq *req); void HgfsDebugPrintReqPool(const char *str); -void Panic(const char *fmt, ...); -void Log(const char *fmt, ...); +#endif + +void Log(const char *fmt, ...); +void Debug(const char *fmt, ...); #endif /* __DEBUG_H_ */ diff --git a/open-vm-tools/modules/solaris/vmhgfs/device.c b/open-vm-tools/modules/solaris/vmhgfs/device.c deleted file mode 100644 index 3948f68c5..000000000 --- a/open-vm-tools/modules/solaris/vmhgfs/device.c +++ /dev/null @@ -1,1111 +0,0 @@ -/********************************************************* - * Copyright (C) 2004 VMware, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common - * Development and Distribution License (the "License") version 1.0 - * and no later version. You may not use this file except in - * compliance with the License. - * - * You can obtain a copy of the License at - * http://www.opensource.org/licenses/cddl1.php - * - * See the License for the specific language governing permissions - * and limitations under the License. - * - *********************************************************/ - - -/* - * device.c -- - * - * HGFS device driver for Solaris. - * - * Implements a device driver that creates a device in /dev that can be - * interacted with through standard file IO syscalls. Implements open(), - * close(), read(), write(), and poll(). - * - * read() provides requests from the filesystem to the caller and is - * synchronized with the HgfsSubmitRequest() function in the filesystem. - * write() receives replies to those requests and provides them to the - * filesystem by waking up processes waiting in HgfsSubmitRequest(). - * - */ - -#include "hgfsSolaris.h" -#include "device.h" -#include "request.h" -#include "filesystem.h" - -/* - * Local variables - */ -static Bool hgfsAttached; /* Flag to tell whether device is attached */ - -/* - * Function Prototypes - */ -static int HgfsSendRequestToUser(HgfsReq *req, uio_t *uiop); -static INLINE int HgfsGetReplyHeaderFromUser(HgfsReplyHeader *reply, uio_t *uiop); -static int HgfsGetReplyPacketFromUser(HgfsReq *req, ssize_t offset, uio_t *uiop); - -/* Local utility functions */ -static INLINE HgfsSuperInfo *HgfsDevToSuperInfo(dev_t dev); -static INLINE HgfsSuperInfo *HgfsDevinfoToSuperInfo(dev_info_t *dip); -static INLINE HgfsReq *HgfsReplyToRequest(HgfsReplyHeader *reply); - -/* - * Driver "configuration" functions - */ - - -/* - *---------------------------------------------------------------------------- - * - * HgfsDevGetinfo -- - * - * The entry point for getinfo(9E). Carries out commands requested by the - * kernel that either return the device information structure or the - * instance number. - * - * Results: - * Returns DDI_SUCCESS on success and DDI_FAILURE on failure. - * When infocmd is DDI_INFO_DEVT2DEVINFO, a pointer to the device's - * information structure is returned in result. - * When infocmd is DDI_INFO_DEVT2INSTANCE, the device's instance number is - * returned in result. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsDevGetinfo(dev_info_t *dip, // IN: Device info for this device - ddi_info_cmd_t infocmd, // IN: Command to carry out - void *arg, // IN: Device number (as dev_t) for this device - void **result) // OUT: Result of command -{ - minor_t instance; /* getminor() returns minor_t */ - HgfsSuperInfo *sip; - - DEBUG(VM_DEBUG_DEVENTRY, "HgfsDevGetinfo().\n"); - - ASSERT(arg); - ASSERT(result); - - /* These indicate a kernel error */ - if ((!arg) || (!result)) { - cmn_err(HGFS_ERROR, "NULL input from Kernel in HgfsDevGetinfo().\n"); - return EINVAL; - } - - - switch (infocmd) { - - /* This wants a dev_info_t from the dev_t: get it from the superinfo */ - case DDI_INFO_DEVT2DEVINFO: - sip = HgfsDevToSuperInfo((dev_t)arg); - if (!sip) { - goto error; - } - - *result = sip->dip; - DEBUG(VM_DEBUG_DEVDONE, "HgfsDevGetinfo() done.\n"); - return DDI_SUCCESS; - - /* - * This wants the instance number, which is the minor number since we set - * it to be that in HgfsDevAttach()'s call to ddi_create_minor_node(). - */ - case DDI_INFO_DEVT2INSTANCE: - instance = getminor((dev_t)arg); - *result = (void *)((uintptr_t)instance); - DEBUG(VM_DEBUG_DEVDONE, "HgfsDevGetinfo() done.\n"); - return DDI_SUCCESS; - - /* Unrecognized command */ - default: - goto error; - } - -error: - *result = NULL; - DEBUG(VM_DEBUG_FAIL, "HgfsDevGetinfo() done (FAIL).\n"); - return DDI_FAILURE; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsDevAttach -- - * - * The entry point for attach(9E) that is invoked when the driver is loaded - * into the kernel. This allocates and initializes a super info structure - * and creates the device entry ('minor node') in the file system. - * XXX: do we need to handle suspend and resume commands also? - * - * Results: - * Returns zero on success, and an appropriate error code on failure. - * - * Side effects: - * The device entry appears in the filesystem (/devices). - * The hgfsAttaches flag is set. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsDevAttach(dev_info_t *dip, // IN: Device info for this device - ddi_attach_cmd_t cmd) // IN: Command to carry out -{ - int instance; /* ddi_get_instance() returns int */ - HgfsSuperInfo *sip; - - DEBUG(VM_DEBUG_DEVENTRY, "HgfsDevAttach().\n"); - - ASSERT(dip); - - /* This indicates a kernel error */ - if (!dip) { - cmn_err(HGFS_ERROR, "NULL input from Kernel in HgfsDevAttach().\n"); - return EINVAL; - } - - if (hgfsAttached) { - cmn_err(HGFS_ERROR, "Device already attached.\n"); - return EIO; - } - - switch (cmd) { - - /* Attach this device */ - case DDI_ATTACH: - /* hgfsInstance is used by the filesystem side to get superinfo pointer */ - instance = hgfsInstance = ddi_get_instance(dip); - - /* - * Here we allocate state for this instance of the driver (see comment - * in _init() for more information on Solaris' DDI Soft State interface). - * - * Once we have the pointer to the state we just allocated, we'll - * initialize its fields. In particular: - * - a pointer to the dev_info_t is stored because HgfsGetInfo() will - * need it. - * - clear the flag indicating whether the device is open. - * - the request list, along with its mutex and condition variable, are - * initialized. - */ - if (ddi_soft_state_zalloc(superInfoHead, instance) != DDI_SUCCESS) { - cmn_err(HGFS_ERROR, - "could not zalloc state for this instance (%d).\n", instance); - goto error; - } - - sip = (HgfsSuperInfo *)ddi_get_soft_state(superInfoHead, instance); - if (!sip) { - goto error_free; - } - - sip->dip = dip; - sip->devOpen = 0; - - HgfsInitRequestList(sip); - - /* - * Create the minor node (the /devices entry) - */ - if (ddi_create_minor_node(dip, /* device info ptr */ - HGFS_DEV_NAME, /* name of device */ - S_IFCHR, /* type of device (character) */ - instance, /* minor number */ - DDI_PSEUDO, /* pseudo device b/c not backed - by physical device */ - 0 /* flag: not a clone device */ - ) != DDI_SUCCESS) { - - cmn_err(HGFS_ERROR, "could not create minor node (/devices entry).\n"); - goto error_free; - } - - /* Report presence of device to system log (syslog/dmesg) */ - ddi_report_dev(dip); - - /* - * We only want one instance of this driver to ever be running, so set a - * flag stating the device is attached - */ - hgfsAttached = TRUE; - - DEBUG(VM_DEBUG_DEVDONE, "HgfsDevAttach() done.\n"); - return DDI_SUCCESS; - - /* Unsupported commands: DDI_PM_RESUME and DDI_RESUME */ - default: - goto error; - } - -error_free: - ddi_soft_state_free(sip, instance); -error: - DEBUG(VM_DEBUG_FAIL, "HgfsDevAttach() done (FAIL).\n"); - return DDI_FAILURE; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsDevDetach -- - * - * The detach(9E) entry point that is invoked when the driver is - * unloaded from the kernel. This cleans up all state allocated in - * HgfsDevAttach() and removes the device entry ('minor node'). - * - * Results: - * Returns zero on success, and an appropriate error code on failure. - * - * Side effects: - * The super info structure for this driver is deallocated. - * The device entry (/devices) is no longer valid. - * The hgfsAttached flag is cleared. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsDevDetach(dev_info_t *dip, // IN: Device info for this device - ddi_detach_cmd_t cmd) // IN: Command to carry out -{ - int instance; - HgfsSuperInfo *sip; - - DEBUG(VM_DEBUG_DEVENTRY, "HgfsDevDetach().\n"); - - ASSERT(dip); - - /* This indicates a kernel error */ - if (!dip) { - cmn_err(HGFS_ERROR, "NULL input from Kernel in HgfsDevDetach().\n"); - return EINVAL; - } - - switch(cmd) { - - /* Detach this device */ - case DDI_DETACH: - /* - * Clean up the state allocated for this instance of the driver: - * - Get a pointer to the super info structure, - * - Remove the device entry (minor node) from the system, - * - Free the super info structure. - */ - instance = ddi_get_instance(dip); - sip = (HgfsSuperInfo *)ddi_get_soft_state(superInfoHead, instance); - if (!sip) { - cmn_err(HGFS_ERROR, "could not find HgfsSuperInfo on detach.\n"); - goto error; - } - - ddi_remove_minor_node(dip, NULL); /* NULL means remove all for this dev_info_t */ - ddi_soft_state_free(superInfoHead, instance); - - /* Indicate driver is no longer attached */ - hgfsAttached = 0; - - DEBUG(VM_DEBUG_DEVDONE, "HgfsDevDetach() done.\n"); - return DDI_SUCCESS; - - /* Unsupported commands: DDI_PM_SUSPEND and DDI_SUSPEND */ - default: - goto error; - } - -error: - DEBUG(VM_DEBUG_FAIL, "HgfsDevDetach() done (FAIL).\n"); - return DDI_FAILURE; -} - - - -/* - * Device functions - */ - - -/* - *---------------------------------------------------------------------------- - * - * HgfsDevOpen -- - * The entry point for open(9E) that is invoked when a user process calls - * open(2) on the device. This checks that the specified device and open - * type are valid. - * - * Results: - * Returns zero on success, and an appropriate error code on failure. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsDevOpen(dev_t *devp, // IN: Pointer to device number - int flag, // IN: Indicates open mode - // (FREAD, FWRITE, FEXCL, FNDELAY) - int otyp, // IN: Type of open (OTYP_CHR, OTYP_BLK, OTYP_LYR) - cred_t *credp) // IN: Pointer to credentials of caller -{ - HgfsSuperInfo *sip; - - DEBUG(VM_DEBUG_DEVENTRY, "HgfsDevOpen().\n"); - - ASSERT(devp); - ASSERT(credp); - - /* These indicate a kernel error */ - if ((!devp) || (!credp)) { - cmn_err(HGFS_ERROR, "NULL input from Kernel in HgfsDevOpen().\n"); - return EINVAL; - } - - /* - * Here we do a few checks to ensure that this open is valid. First, our - * device is a character device so we ensure that the type of open - * indicates this. Next we ensure that the caller is root. Finally, we - * make sure that the device number specified is valid by attempting to - * retrieve its state that was allocated in HgfsDevDetach(). - */ - - if (otyp != OTYP_CHR) { - DEBUG(VM_DEBUG_FAIL, "HgfsDevOpen() done (FAIL).\n"); - return EINVAL; - } - - if (!HgfsSuser(credp)) { - DEBUG(VM_DEBUG_FAIL, "HgfsDevOpen() done (FAIL).\n"); - return EINVAL; - } - - sip = HgfsDevToSuperInfo(*devp); - if (!sip) { - DEBUG(VM_DEBUG_FAIL, "HgfsDevOpen() done (FAIL).\n"); - return ENXIO; - } - - /* Make sure device isn't opened more than once */ - if (sip->devOpen) { - return ENXIO; - } - - sip->devOpen = TRUE; - - DEBUG(VM_DEBUG_DEVDONE, "HgfsDevOpen() done.\n"); - return 0; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsDevClose -- - * The entry point for close(9E), which is invoked when a user process - * calls close(2) on the device. This ensures that the specified device - * number is valid (by trying to find its state structure). - * - * Results: - * Returns zero on success, and an appropriate error code on failure. - * - * Side effects: - * All pending requests are cancelled. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsDevClose(dev_t dev, // IN: Device number - int flag, // IN: Mode opened with (see HgfsOpen() comment) - int otyp, // IN: Type of open (see HgfsOpen() comment) - cred_t *credp) // IN: Pointer to credentials of caller -{ - HgfsSuperInfo *sip; - - DEBUG(VM_DEBUG_DEVENTRY, "HgfsDevClose().\n"); - - ASSERT(credp); - - /* This indicates a kernel error */ - if (!credp) { - cmn_err(HGFS_ERROR, "NULL input from Kernel in HgfsDevClose().\n"); - return EINVAL; - } - - /* - * We only allow root to open and close the device. - */ - if (!HgfsSuser(credp)) { - return EINVAL; - } - - - /* Just make sure this instance is valid, state is freed in HgfsDevDetach() */ - sip = HgfsDevToSuperInfo(dev); - if (!sip) { - DEBUG(VM_DEBUG_FAIL, "HgfsDevClose() done (FAIL).\n"); - return ENXIO; - } - - /* - * This will signify to the filesystem that the device half is no longer - * present. - */ - sip->devOpen = 0; - - /* - * Each submitted request must be told an error has occurred. - */ - HgfsCancelAllRequests(sip); - - - DEBUG(VM_DEBUG_DEVDONE, "HgfsDevClose() done.\n"); - return 0; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsDevRead -- - * - * Entry point for read(9E) that is invoked when a user process calls - * read(2) on the device. - * - * This checks the pending request list for any outstanding requests. If - * the list is empty, this blocks on a condition variable that is signaled - * each time a request is enqueued. The request is removed from the list - * and copied to the user's buffer. - * - * Results: - * Returns zero on success, an appropriate error code on failure. - * - * Side effects: - * Blocks if the list is empty. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsDevRead(dev_t dev, // IN: Device number - uio_t *uiop, // IN: Ptr to structure defining read request - cred_t *credp) // IN: Ptr to caller's credentials -{ - int ret; - HgfsSuperInfo *sip; - HgfsReq *nextReq = NULL; - - DEBUG(VM_DEBUG_DEVENTRY, "HgfsDevRead().\n"); - - ASSERT(uiop); - ASSERT(credp); - - /* These indicate a kernel error */ - if ((!uiop) || (!credp)) { - cmn_err(HGFS_ERROR, "NULL input from Kernel in HgfsDevRead().\n"); - return EINVAL; - } - - sip = HgfsDevToSuperInfo(dev); - if (!sip) { - DEBUG(VM_DEBUG_FAIL, "HgfsDevRead() done (FAIL).\n"); - return ENXIO; - } - - /* - * We need to check the request list to see if there is anything to read. - * If not, wait on the list's condition variable so we will be - * cv_signal()'ed when a request is placed onto the request list (by the - * filesystem half). Once a request is there we can remove it from - * the list and copy it to user space. - */ - mutex_enter(&sip->reqMutex); - - for (;;) { - /* - * The cv_wait_sig(9F) man page says the cv_wait*() functions can return - * prematurely in certain situations, typically when job control or - * debugging is used, so it is required that we loop around cv_wait_sig(). - */ - while (HgfsListIsEmpty(&sip->reqList)) { - DEBUG(VM_DEBUG_COMM, "HgfsDevRead: blocking ...\n"); - if (cv_wait_sig(&sip->reqCondVar, &sip->reqMutex) == 0) { - /* - * We received a system signal (e.g., SIGKILL) while waiting for the - * cv_signal(). Release mutex and return appropriate error code. - */ - mutex_exit(&sip->reqMutex); - DEBUG(VM_DEBUG_FAIL, - "cv_wait_sig() interrupted by signal in HgfsDevRead().\n"); - return EINTR; - } - } - - /* - * We hold the lock so the next request is guaranteed to be there. If - * the next request is still in the submitted state then we process it; - * otherwise the request must be in the abandoned state and we destroy it - * and wait for the next one. - */ - nextReq = HgfsDequeueRequest(sip); - ASSERT(nextReq); - if (HgfsReqGetState(nextReq) == HGFS_REQ_SUBMITTED) { - break; - } - - ASSERT(HgfsReqGetState(nextReq) == HGFS_REQ_ABANDONED); - HgfsDestroyReq(sip, nextReq); - } - - DEBUG(VM_DEBUG_LIST, "HgfsDevRead received request for ID %d", nextReq->id); - mutex_exit(&sip->reqMutex); - - /* - * Filesystem ensures that requests are small enough to fit in a packet sent - * through the backdoor. - */ - ret = HgfsSendRequestToUser(nextReq, uiop); - /* - * If we couldn't copy the request to guestd, set the request's state to - * error and wake up the client. The filesystem will have to clean up. - */ - if (ret) { - DEBUG(VM_DEBUG_FAIL, "HgfsDevRead: could not copy request to user (FAIL).\n"); - HgfsReqSetState(nextReq, HGFS_REQ_ERROR); - HgfsWakeWaitingClient(sip, nextReq); - } - - DEBUG(VM_DEBUG_INFO, "resid=%ld\n", uiop->uio_resid); - DEBUG(VM_DEBUG_DEVDONE, "HgfsDevRead() done.\n"); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsDevWrite -- - * - * Entry point for write(9E), which is invoked when a user process calls - * write(2) on the device. - * - * This copies in a reply header, finds the associated request, then copies - * the reply packet into the request packet. It handles cleaning up - * requests that have been abandoned. - * - * Results: - * Returns zero on success, an appropriate error code on failure. - * - * Side effects: - * The state of the request corresponding to this reply is modified. - * The process waiting for this reply is woken up (if it is still waiting). - * - *---------------------------------------------------------------------------- - */ - -int -HgfsDevWrite(dev_t dev, // IN: Device number - uio_t *uiop, // IN: Ptr to structure defining write request - cred_t *credp) // IN: Ptr to caller's credentials -{ - int ret; - uint32_t writeSize; - HgfsSuperInfo *sip; - HgfsReq *request; - HgfsReplyHeader reply; - - DEBUG(VM_DEBUG_DEVENTRY, "HgfsDevWrite().\n"); - - ASSERT(uiop); - ASSERT(credp); - - /* These indicate a kernel error */ - if (!uiop || !credp) { - cmn_err(HGFS_ERROR, "NULL input from kernel in HgfsDevWrite().\n"); - return EINVAL; - } - - sip = HgfsDevToSuperInfo(dev); - if (!sip) { - DEBUG(VM_DEBUG_FAIL, "HgfsDevWrite(): couldn't get superinfo (FAIL)\n"); - return ENXIO; - } - - /* We need at least the reply structure to do anything useful. */ - if (uiop->uio_resid < sizeof reply) { - DEBUG(VM_DEBUG_FAIL, "HgfsDevWrite(): too little data written (FAIL).\n"); - return ERANGE; - } - - /* - * If this fails there is a problem with guestd or the filesystem isn't - * splitting requests properly. This also allows writeSize to be a uint32 - * despite uiop->uio_resid being a ssize_t (which can be a long). - */ - if (uiop->uio_resid > HGFS_PACKET_MAX) { - return EINVAL; - } - - /* Needed to set packet size upon successful receipt of packet. */ - writeSize = uiop->uio_resid; - - /* - * Copy in just the reply header that will tell us what request this is - * for and what the status of the reply is. - */ - ret = HgfsGetReplyHeaderFromUser(&reply, uiop); - if (ret) { - DEBUG(VM_DEBUG_FAIL, - "HgfsDevWrite(): couldn't copy header from user (FAIL).\n"); - return EIO; - } - - DEBUG(VM_DEBUG_COMM, "HgfsDevWrite: Reply for %d with status %d.\n", - reply.id, reply.status); - - /* - * Here we need to determine which request this reply is for, then determine - * if that requester is still waiting. If so we copy the contents of the - * reply into the request's packet and wake up the client; otherwise we - * destroy the abandoned request and return an error. - */ - request = HgfsReplyToRequest(&reply); - if (!request) { - DEBUG(VM_DEBUG_FAIL, "HgfsDevWrite(): invalid id in reply (FAIL).\n"); - return EINVAL; - } - - /* - * Acquiring this mutex makes the checking of whether the request has been - * ABANDONED and setting the request to COMPLETED or ERROR atomic. It is - * also acquired in HgfsSubmitRequest(). - */ - mutex_enter(&sip->reqMutex); - - if (HgfsReqGetState(request) == HGFS_REQ_ABANDONED) { - /* The requesting process is gone so we don't need to wake it up */ - HgfsDestroyReq(sip, request); - mutex_exit(&sip->reqMutex); - DEBUG(VM_DEBUG_FAIL, "HgfsDevWrite(): request was abandoned (FAIL).\n"); - return EINTR; /* XXX: Does guestd need to know this? */ - } - - /* - * Now that we know which request this reply is for, zero out its packet and - * copy in the reply header. - */ - bzero(request->packet, sizeof request->packet); - memcpy(request->packet, &reply, sizeof reply); - - /* Copy rest of reply into request packet after the header */ - ret = HgfsGetReplyPacketFromUser(request, sizeof reply, uiop); - if (ret < 0) { - /* - * If there was an error while copying the reply packet, we set the state - * to error and return an error code (in case guestd cares). - */ - HgfsReqSetState(request, HGFS_REQ_ERROR); - ret = EIO; - } else { - /* ... otherwise we set the packet size and set the state to completed. */ - request->packetSize = writeSize - uiop->uio_resid; - HgfsReqSetState(request, HGFS_REQ_COMPLETED); - ret = 0; - } - - /* - * Wake up the client waiting on the request (Note we don't call - * HgfsWakeWaitingClient() since we already hold the lock.) - */ - cv_signal(&request->condVar); - mutex_exit(&sip->reqMutex); - - DEBUG(VM_DEBUG_DEVDONE, "HgfsDevWrite() done.\n"); - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsDevChpoll -- - * - * Entry point for chpoll(9E), which is invoked (potentially more than - * once) when either poll(2) or select(3C) is called by a user process. - * This examines the state of the device and returns information indicating - * which operations are ready to be performed. - * - * Results: - * Returns 0 on success, or an error code if an error occurs. - * If any of the events polled have occurred, this is specified by filling - * in reventsp with the appropriate bits. - * If none of the events polled have occurred, reventsp is cleared. phpp - * is also set to point to a pollhead structure if no other file - * descriptors polled have had events. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -int -HgfsDevChpoll(dev_t dev, // IN: Device number - short int events, // IN: Events that could occur - // (see man -s 9E chpoll for all - // events) - int anyyet, // IN: Flag (set if other fds have events pending) - short int *reventsp, // OUT: Ptr to bitmask of events satisfied - struct pollhead **phpp) // OUT: Ptr to a pollhead structure -{ - short revents; - HgfsSuperInfo *sip; - - DEBUG(VM_DEBUG_DEVENTRY, "HgfsDevChPoll(). (events=%x)\n", events); - - ASSERT(reventsp); - ASSERT(phpp); - - /* These indicate a kernel error */ - if ((!reventsp) || (!phpp)) { - cmn_err(HGFS_ERROR, "NULL input from Kernel in HgfsDevChpoll().\n"); - return EINVAL; - } - - sip = HgfsDevToSuperInfo(dev); - if (!sip) { - DEBUG(VM_DEBUG_FAIL, "HgfsDevChPoll() done (FAIL).\n"); - return ENXIO; - } - - /* - * This comment is for reference, can remove in future: - * - * Algorithm to implement (chpoll(9E) and p195 DDK docs): - * - * if (events are satisfied now) { - * *reventsp = (mask of satisfied events); - * } else { - * *reventsp = 0; - * if (!anyyet) { - * *phpp = &(local pollhead structure); - * } - * } - * return 0; - * - * The pollhead structure should not be referenced by the driver. - */ - - revents = 0; - /* Clear flag indicating the need to call pollwakeup() on write() */ - sip->pollwakeupOnWrite = FALSE; - - /* - * Caller asked to read: if the request list is not empty then they can - * read without blocking. - */ - if (events & HGFS_POLL_READ) { - mutex_enter(&sip->reqMutex); - - if ( !HgfsListIsEmpty(&sip->reqList) ) { - /* Set just those bits the caller asked for */ - revents |= (events & HGFS_POLL_READ); - } - DEBUG(VM_DEBUG_CHPOLL, "HgfsChpoll(): HGFS_POLL_READ, revents=%d", revents); - - mutex_exit(&sip->reqMutex); - } - - /* - * Caller asked to write: they can always write. - */ - if (events & HGFS_POLL_WRITE) { - revents |= (events & HGFS_POLL_WRITE); - DEBUG(VM_DEBUG_CHPOLL, "HgfsChpoll(): HGFS_POLL_WRITE, revent=%d\n", revents); - } - - /* - * No events have occurred - */ - if (revents == 0) { - DEBUG(VM_DEBUG_CHPOLL, "HgfsChpoll(): no events\n"); - *reventsp = 0; - if (anyyet == 0) { - /* - * No other file descriptors have had events, so we set the pollhead - * structure and a flag indicating the need to call pollwakeup() on - * a successful completion of a write(). - */ - *phpp = &sip->hgfsPollhead; - DEBUG(VM_DEBUG_CHPOLL, "HgfsChpoll(): setting pollwakeupOnWrite.\n"); - sip->pollwakeupOnWrite = TRUE; - } - } - - *reventsp = revents; - - DEBUG(VM_DEBUG_DEVDONE, "HgfsDevChPoll() done. (revents=%x)\n", revents); - return 0; -} - - -/* - * Functions to copy to and from user space. - */ - - -/* - *---------------------------------------------------------------------------- - * - * HgfsSendRequestToUser -- - * - * Sends a requst to the user. - * - * Results: - * Returns zero on success, and non-zero on error. - * - * Side effects: - * The uio_resid field of uiop will be decremented by the number of bytes - * read. - * - *---------------------------------------------------------------------------- - */ - -static int -HgfsSendRequestToUser(HgfsReq *req, // IN: Request to send - uio_t *uiop) // IN: User's read request -{ - int ret; - - DEBUG(VM_DEBUG_DEVENTRY, "HgfsSendRequestToUser().\n"); - - ASSERT(req); - ASSERT(uiop); - - /* - * If the buffer provided by guestd is not large enough to hold the largest - * packet, then either the filesystem or guestd is doing something wrong. - * (uio_resid is the number of bytes we can write to the user's buffer.) - */ - ASSERT(req->packetSize <= HGFS_PACKET_MAX); - ASSERT(uiop->uio_resid >= req->packetSize); - - DEBUG(VM_DEBUG_INFO, "HgfsSendRequestToUser: uiomove(%p, %d, UIO_READ, %p)\n", - req->packet, req->packetSize, uiop); - - /* - * uiomove(9F) will handle copying of data from kernel buffer to user buffer - * for us. It understands the format of the uio_t that describes the - * user's read request and verifies user address is valid. It returns zero - * on success and an error code otherwise. - */ - ret = uiomove(req->packet, // Kernel src address - req->packetSize, // Number of bytes to transfer - UIO_READ, // rwflag: READ - uiop); // Pointer to user's read request - - DEBUG(VM_DEBUG_DEVDONE, - "HgfsSendRequestToUser() sent %d bytes in request (ret=%d).\n", - req->packetSize, ret); - - return ret; -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsGetReplyHeaderFromUser -- - * - * Copies the reply header from the user into the provided reply - * - * Results: - * Returns zero on success, non-zero on error. - * - * Side effects: - * uiop's uio_resid is decremented by the number of bytes that were copied. - * - *---------------------------------------------------------------------------- - */ - -static INLINE int -HgfsGetReplyHeaderFromUser(HgfsReplyHeader *header, // OUT: Header to copy into - uio_t *uiop) // IN: User's write request -{ - ASSERT(header); - ASSERT(uiop); - - DEBUG(VM_DEBUG_COMM, - "HgfsGetReplyHeaderFromUser(): writing %"FMTSZ"u bytes" - " into request packet's header.\n", sizeof *header); - - /* Only copy in the header, uio_resid decremented for us by uiomove() */ - return uiomove(header, sizeof *header, UIO_WRITE, uiop); -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsGetReplyPacketFromUser -- - * - * Copies the rest of the user's reply into the offset of the request's - * packet. - * - * Results: - * Returns zero on success, non-zero on error. - * - * Side effects: - * None. - * - * - *---------------------------------------------------------------------------- - */ - -static int -HgfsGetReplyPacketFromUser(HgfsReq *req, // IN/OUT: Request to copy into - ssize_t offset, // IN: Offset in packet to - // start copying at - uio_t *uiop) // IN: User's write request -{ - ASSERT(req); - ASSERT(uiop); - - - /* - * If guestd has more to write than the max room left in the packet then it - * is doing something wrong. - */ - ASSERT(uiop->uio_resid <= HGFS_PACKET_MAX - offset); - - DEBUG(VM_DEBUG_COMM, - "HgfsGetReplyPacketFromUser(): writing %ld bytes into request packet.\n", - uiop->uio_resid); - - /* Write the reply into the specified buffer */ - return uiomove(req->packet + offset, uiop->uio_resid, UIO_WRITE, uiop); -} - - - -/* - * Utility functions - */ - - -/* - *---------------------------------------------------------------------------- - * - * HgfsDevToSuperInfo -- - * - * Gets a pointer to the super info structure from a device number (dev_t). - * - * Results: - * Returns a pointer to the super info struct, or NULL if it cannot be - * found. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static INLINE HgfsSuperInfo * -HgfsDevToSuperInfo(dev_t dev) // IN: Device number -{ - minor_t instance; - - DEBUG(VM_DEBUG_DEVENTRY, "HgfsDevToSuperInfo().\n"); - - instance = getminor(dev); - - DEBUG(VM_DEBUG_RARE, - "HgfsDevToSuperInfo: getting ptr to instance %d's state.\n", instance); - - return (HgfsSuperInfo *)ddi_get_soft_state(superInfoHead, (int)instance); -} - - -/* - *---------------------------------------------------------------------------- - * - * HgfsDevinfoToSuperInfo -- - * - * Gets a pointer to the super info from a device information struct. - * XXX: currently unused since HgfsDevDetach needs value of instance - * itself. - * - * Results: - * Returns a pointer to the super info structure, or NULL if it cannot be - * found. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static INLINE HgfsSuperInfo * -HgfsDevinfoToSuperInfo(dev_info_t *dip) // IN: Device information structure -{ - int instance; - - ASSERT(dip); - - instance = ddi_get_instance(dip); - - DEBUG(VM_DEBUG_RARE, - "HgfsDevinfoToSuperInfo: getting ptr to instance %d's state.\n", instance); - - return (HgfsSuperInfo *)ddi_get_soft_state(superInfoHead, instance); -} - - - -/* - *---------------------------------------------------------------------------- - * - * HgfsReplyToRequest -- - * - * Determines the request that corresponds with the provided reply. - * - * Results: - * Returns a pointer to the request, or NULL if it cannot be located. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -static INLINE HgfsReq * -HgfsReplyToRequest(HgfsReplyHeader *reply) // IN: Reply to find request of -{ - ASSERT(reply); - - if (reply->id > HGFS_MAX_OUTSTANDING_REQS) { - return NULL; - } - - /* - * When this function is called, the request should only be in the - * SUBMITTED, ABANDONED, or ERROR states. - */ - ASSERT((HgfsReqGetState(&requestPool[reply->id]) == HGFS_REQ_SUBMITTED) || - (HgfsReqGetState(&requestPool[reply->id]) == HGFS_REQ_ABANDONED) || - (HgfsReqGetState(&requestPool[reply->id]) == HGFS_REQ_ERROR)); - - DEBUG(VM_DEBUG_INFO, "HgfsRequestToReply: reply for request id %d\n", reply->id); - - return &requestPool[reply->id]; -} diff --git a/open-vm-tools/modules/solaris/vmhgfs/device.h b/open-vm-tools/modules/solaris/vmhgfs/device.h deleted file mode 100644 index f9403f067..000000000 --- a/open-vm-tools/modules/solaris/vmhgfs/device.h +++ /dev/null @@ -1,46 +0,0 @@ -/********************************************************* - * Copyright (C) 2004 VMware, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of the Common - * Development and Distribution License (the "License") version 1.0 - * and no later version. You may not use this file except in - * compliance with the License. - * - * You can obtain a copy of the License at - * http://www.opensource.org/licenses/cddl1.php - * - * See the License for the specific language governing permissions - * and limitations under the License. - * - *********************************************************/ - - -/* - * device.h -- - * - * Definitions and includes for device driver code. - * - */ - - -#ifndef __DEVICE_H_ -#define __DEVICE_H_ - -#include /* OTYP_CHR */ -#include /* S_IFCHR */ -#include /* Device Driver Interface */ -#include /* Sun DDI defines (DDI_PSEUDO) */ -#include /* mutex and condition variables */ - -#include "debug.h" - -/* - * Macros - */ - -/* Flags for chpoll(): we don't distinguish between these priorities of data */ -#define HGFS_POLL_READ (POLLIN | POLLRDNORM | POLLRDBAND) -#define HGFS_POLL_WRITE (POLLOUT | POLLWRNORM | POLLWRBAND) - - -#endif /* __DEVICE_H_ */ diff --git a/open-vm-tools/modules/solaris/vmhgfs/filesystem.c b/open-vm-tools/modules/solaris/vmhgfs/filesystem.c index c8d38de14..d10e3d9ba 100644 --- a/open-vm-tools/modules/solaris/vmhgfs/filesystem.c +++ b/open-vm-tools/modules/solaris/vmhgfs/filesystem.c @@ -24,6 +24,7 @@ */ #include /* MAXNAMELEN */ +#include /* FKIOCTL */ #include "hgfsSolaris.h" #include "hgfsState.h" @@ -55,6 +56,15 @@ void HgfsFreevfs(struct vfs *vfsp); #endif #endif +#if HGFS_VFS_VERSION > 2 +static vfsops_t *hgfsVfsOpsP; +#endif + +/* + * Fileystem type number given to us upon initialization. + */ +static int hgfsType; + #if HGFS_VFS_VERSION == 2 /* @@ -129,7 +139,7 @@ HgfsInit(int fstype, // IN: Filesystem type int ret; /* Construct the VFS operations array to give to vfs_setfsops() */ - fs_operation_def_t vfsOpsArr[] = { + static fs_operation_def_t vfsOpsArr[] = { HGFS_VOP(VFSNAME_MOUNT, vfs_mount, HgfsMount), HGFS_VOP(VFSNAME_UNMOUNT, vfs_unmount, HgfsUnmount), HGFS_VOP(VFSNAME_ROOT, vfs_root, HgfsRoot), @@ -154,16 +164,23 @@ HgfsInit(int fstype, // IN: Filesystem type DEBUG(VM_DEBUG_ENTRY, "HgfsInit: fstype=%d, name=\"%s\"\n", fstype, name); - /* Set our filesystem type. */ - hgfsType = fstype; - /* Assign VFS operations to our filesystem. */ - ret = vfs_setfsops(hgfsType, vfsOpsArr, NULL); + ret = vfs_setfsops(fstype, vfsOpsArr, &hgfsVfsOpsP); if (ret) { DEBUG(VM_DEBUG_FAIL, "HgfsInit: vfs_setfsops returned %d\n", ret); return ret; } + ret = HgfsMakeVnodeOps(); + if (ret) { + DEBUG(VM_DEBUG_FAIL, "HgfsInit: could not register HGFS Vnode Ops.\n"); + vfs_freevfsops_by_type(fstype); + ret = EIO; + return ret; + } + + /* Set our filesystem type. */ + hgfsType = fstype; DEBUG(VM_DEBUG_DONE, "HgfsInit: done. (fstype=%d)\n", hgfsType); return 0; @@ -171,6 +188,33 @@ HgfsInit(int fstype, // IN: Filesystem type #endif +/* + *---------------------------------------------------------------------------- + * + * HgfsFreeVfsOps -- + * + * Free VFS Ops created when we initialized the filesystem. + * + * Results: + * None. + * + * Side effects: + * Resets hgfsVfsOpsP back to NULL. + * + *---------------------------------------------------------------------------- + */ + + +void +HgfsFreeVfsOps(void) +{ +#if HGFS_VFS_VERSION > 2 + if (hgfsVfsOpsP) { + vfs_freevfsops_by_type(hgfsType); + } +#endif +} + /* * VFS Entry Points @@ -212,73 +256,48 @@ HgfsMount(struct vfs *vfsp, // IN: Filesystem to mount DEBUG(VM_DEBUG_ENTRY, "HgfsMount().\n"); //HgfsDebugPrintVfs("HgfsMount", vfsp); - //HgfsDebugPrintVnode(VM_DEBUG_STRUCT, "HgfsMount", vnodep); + //HgfsDebugPrintVnode(VM_DEBUG_STRUCT, "HgfsMount", vnodep, FALSE); //HgfsDebugPrintMounta("HgfsMount", mntp); //HgfsDebugPrintCred("HgfsMount", credp); - /* - * Initial sanity checks to ensure that: - * - device side has allocated our superinfo struct (see HgfsDevAttach()) - * - the device has been sucecssfully opened - * - caller is root - * - data provided by caller is the expected size - * - * Note that this is the one place in the filesystem that we call - * ddi_get_soft_state() directly to get the superinfo pointer. All other - * places use the HgfsGetSuperInfo() wrapper which only returns the - * superinfo if the filesystem is in fact mounted. - */ - sip = (HgfsSuperInfo *)ddi_get_soft_state(superInfoHead, hgfsInstance); - if (!sip) { - DEBUG(VM_DEBUG_FAIL, "HgfsMount: superinfo has not been allocated/set\n"); - return ENXIO; - } - - if (!sip->devOpen) { - DEBUG(VM_DEBUG_FAIL, "HgfsMount: device has not been opened\n"); - return ENXIO; - } - if (!HgfsSuser(credp)) { return EPERM; } if (mntp->datalen != sizeof *mountData) { + DEBUG(VM_DEBUG_FAIL, "HgfsMount: bad data size (%lu vs %lu).\n", + (unsigned long) mntp->datalen, + (unsigned long) sizeof *mountData); return EINVAL; } - /* - * If mount data is in kernel space, just cast pointer; otherwise we need - * to safely copy it from user space. - */ - if (mntp->flags & MS_SYSSPACE) { - mountData = (HgfsMountData *)mntp->dataptr; - DEBUG(VM_DEBUG_LOAD, "HgfsMount(): mount data was in kernel space.\n"); - } else { - mountData = (HgfsMountData *)kmem_zalloc(sizeof *mountData, HGFS_ALLOC_FLAG); -# if HGFS_ALLOC_FLAG != KM_SLEEP - /* This check is not needed if HGFS_ALLOC_FLAG == KM_SLEEP */ - if (!mountData) { - return ENOMEM; - } -# endif - - if (ddi_copyin(mntp->dataptr, mountData, - sizeof *mountData, HGFS_COPYIN_FLAGS) == -1) { - DEBUG(VM_DEBUG_FAIL, "HgfsMount: couldn't copy in mount data.\n"); - ret = EFAULT; - goto out; - } + mountData = kmem_zalloc(sizeof *mountData, HGFS_ALLOC_FLAG); + if (!mountData) { + return ENOMEM; + } + + if (ddi_copyin(mntp->dataptr, mountData, sizeof *mountData, + mntp->flags & MS_SYSSPACE ? FKIOCTL : 0) == -1) { + DEBUG(VM_DEBUG_FAIL, "HgfsMount: couldn't copy mount data.\n"); + ret = EFAULT; + goto out; } /* - * Make sure mount data matches what guestd will send us. + * Make sure mount data matches what mount program will send us. */ if (mountData->magic != HGFS_MAGIC) { - DEBUG(VM_DEBUG_FAIL, "HgfsMount: received invalid magic value: %x\n", - mountData->magic); - ret = EINVAL; - goto out; + DEBUG(VM_DEBUG_FAIL, "HgfsMount: received invalid magic value: %x\n", + mountData->magic); + ret = EINVAL; + goto out; + } + + /* We support only one instance of hgfs, at least for now */ + if (HgfsGetSuperInfo()) { + DEBUG(VM_DEBUG_FAIL, "HgfsMount: HGFS is already mounted somewhere\n"); + ret = EBUSY; + goto out; } /* @@ -286,9 +305,9 @@ HgfsMount(struct vfs *vfsp, // IN: Filesystem to mount * construct the filesystem id. */ if ((dev = getudev()) == -1) { - DEBUG(VM_DEBUG_FAIL, "HgfsMount(): getudev() failed.\n"); - ret = ENXIO; - goto out; + DEBUG(VM_DEBUG_FAIL, "HgfsMount(): getudev() failed.\n"); + ret = ENXIO; + goto out; } DEBUG(VM_DEBUG_LOAD, "HgfsMount: dev=%lu\n", dev); @@ -334,10 +353,14 @@ HgfsMount(struct vfs *vfsp, // IN: Filesystem to mount vnodep->v_vfsmountedhere = vfsp; # endif - /* We need to initialize the File hash table before getting our first vnode. */ - ret = HgfsInitFileHashTable(&sip->fileHashTable); - if (ret) { - DEBUG(VM_DEBUG_FAIL, "HgfsMount: couldn't initialize hash table.\n"); + HgfsInitSuperInfo(vfsp); + + sip = HgfsGetSuperInfo(); + vfsp->vfs_data = (caddr_t)sip; + + if (!sip->transportInit()) { + DEBUG(VM_DEBUG_FAIL, "HgfsMount: failed to start transport.\n"); + HgfsClearSuperInfo(); ret = EIO; goto out; } @@ -357,6 +380,8 @@ HgfsMount(struct vfs *vfsp, // IN: Filesystem to mount &sip->fileHashTable); // File hash table if (ret) { DEBUG(VM_DEBUG_FAIL, "HgfsMount: couldn't get root vnode.\n"); + sip->transportCleanup(); + HgfsClearSuperInfo(); ret = EIO; goto out; } @@ -366,25 +391,14 @@ HgfsMount(struct vfs *vfsp, // IN: Filesystem to mount sip->rootVnode->v_flag |= VROOT; mutex_exit(&sip->rootVnode->v_lock); - /* - * Fill in value(s) for the HGFS superinfo structure and link it to this - * filesystem's VFS structure. - */ - sip->vfsp = vfsp; - - vfsp->vfs_data = (caddr_t)sip; - /* XXX do this? */ VN_HOLD(vnodep); - ret = 0; /* Return success */ - DEBUG(VM_DEBUG_DONE, "HgfsMount() done.\n"); + ret = 0; /* Return success */ out: - if (!(mntp->flags & MS_SYSSPACE)) { - kmem_free(mountData, sizeof *mountData); - } + kmem_free(mountData, sizeof *mountData); return ret; } @@ -411,15 +425,15 @@ HgfsUnmount(struct vfs *vfsp, // IN: This filesystem struct cred *credp) // IN: Credentials of caller { HgfsSuperInfo *sip; + int ret; + + DEBUG(VM_DEBUG_ENTRY, "HgfsUnmount().\n"); if (!vfsp || !credp) { cmn_err(HGFS_ERROR, "HgfsUnmount: NULL input from Kernel.\n"); return EINVAL; } - DEBUG(VM_DEBUG_ENTRY, "HgfsUnmount().\n"); - HgfsDebugPrintVnode(VM_DEBUG_STRUCT, "HgfsUnmount", vfsp->vfs_vnodecovered); - /* * Initial check to ensure caller is root. */ @@ -436,51 +450,60 @@ HgfsUnmount(struct vfs *vfsp, // IN: This filesystem DEBUG(VM_DEBUG_ALWAYS, "HgfsUnmount: vfsp != sip->vfsp.\n"); } + HgfsDebugPrintVnode(VM_DEBUG_STRUCT, "HgfsUnmount", + vfsp->vfs_vnodecovered, FALSE); + + /* Take the request lock to prevent submitting new requests */ + mutex_enter(&sip->reqMutex); + /* * Make sure there are no active files (besides the root vnode which we * release at the end of the function). */ HgfsDebugPrintFileHashTable(&sip->fileHashTable, VM_DEBUG_STATE); - if (!HgfsFileHashTableIsEmpty(sip, &sip->fileHashTable)) { - /* - * If the force flag was specified, we'll cancel all requests and perform - * the unmount. Otherwise, we'll just return EBUSY. - */ - if (mflag & MS_FORCE) { - HgfsCancelAllRequests(sip); - } else { - DEBUG(VM_DEBUG_FAIL, "HgfsUnmount: there are still active files.\n"); - return EBUSY; - } - } - /* Send an unclean unmount warning to syslog. */ - if (!sip->devOpen) { - cmn_err(HGFS_ERROR, "HgfsUnmount: unclean unmount; device was closed.\n"); + if (!HgfsFileHashTableIsEmpty(sip, &sip->fileHashTable) && + !(mflag & MS_FORCE)) { + DEBUG(VM_DEBUG_FAIL, "HgfsUnmount: there are still active files.\n"); + ret = EBUSY; + goto out; } + HgfsCancelAllRequests(sip); + /* * Set unmounted flag in vfs structure. */ vfsp->vfs_flag |= VFS_UNMOUNTED; + /* + * Close transport channel, we should not be gettign any more requests. + */ + sip->transportCleanup(); + /* * Clean up fields in vnode structure of mount point and release hold on * vnodes for mount. */ # if HGFS_VFS_VERSION == 2 - (vfsp->vfs_vnodecovered)->v_vfsmountedhere = NULL; + vfsp->vfs_vnodecovered->v_vfsmountedhere = NULL; # endif VN_RELE(vfsp->vfs_vnodecovered); - VN_RELE( HGFS_ROOT_VNODE(sip) ); + VN_RELE(HGFS_ROOT_VNODE(sip)); /* * Signify to the device half that the filesystem has been unmounted. */ - sip->vfsp = NULL; HGFS_ROOT_VNODE(sip) = NULL; + HgfsClearSuperInfo(); - return 0; + ret = 0; + +out: + mutex_exit(&sip->reqMutex); + + DEBUG(VM_DEBUG_DONE, "HgfsUnmount() done.\n"); + return ret; } diff --git a/open-vm-tools/modules/solaris/vmhgfs/filesystem.h b/open-vm-tools/modules/solaris/vmhgfs/filesystem.h index 704903a0d..69900ea19 100644 --- a/open-vm-tools/modules/solaris/vmhgfs/filesystem.h +++ b/open-vm-tools/modules/solaris/vmhgfs/filesystem.h @@ -60,7 +60,7 @@ #define HGFS_FSTYPE HGFS_FS_NAME /* - * Struct passed from user (guestd) to kernel (fs module) + * Struct passed from mount program to kernel (fs module) */ typedef struct HgfsMountData { uint32_t magic; @@ -104,6 +104,7 @@ typedef struct HgfsMountData { /* To abstract Solaris 9 and 10 differences in suser() calls */ int HgfsSuser(struct cred *cr); +void HgfsFreeVfsOps(void); /* * Extern variables diff --git a/open-vm-tools/modules/solaris/vmhgfs/guest_msg_def.h b/open-vm-tools/modules/solaris/vmhgfs/guest_msg_def.h new file mode 100644 index 000000000..a90e1ad4f --- /dev/null +++ b/open-vm-tools/modules/solaris/vmhgfs/guest_msg_def.h @@ -0,0 +1,90 @@ +/********************************************************* + * Copyright (C) 1998 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * guest_msg_def.h -- + * + * Second layer of the internal communication channel between guest + * applications and vmware + * + */ + +#ifndef _GUEST_MSG_DEF_H_ +#define _GUEST_MSG_DEF_H_ + +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_USERLEVEL +#define INCLUDE_ALLOW_VMMEXT +#include "includeCheck.h" + + +/* Basic request types */ +typedef enum { + MESSAGE_TYPE_OPEN, + MESSAGE_TYPE_SENDSIZE, + MESSAGE_TYPE_SENDPAYLOAD, + MESSAGE_TYPE_RECVSIZE, + MESSAGE_TYPE_RECVPAYLOAD, + MESSAGE_TYPE_RECVSTATUS, + MESSAGE_TYPE_CLOSE, +} MessageType; + + +/* Reply statuses */ +/* The basic request succeeded */ +#define MESSAGE_STATUS_SUCCESS 0x0001 +/* vmware has a message available for its party */ +#define MESSAGE_STATUS_DORECV 0x0002 +/* The channel has been closed */ +#define MESSAGE_STATUS_CLOSED 0x0004 +/* vmware removed the message before the party fetched it */ +#define MESSAGE_STATUS_UNSENT 0x0008 +/* A checkpoint occurred */ +#define MESSAGE_STATUS_CPT 0x0010 +/* An underlying device is powering off */ +#define MESSAGE_STATUS_POWEROFF 0x0020 +/* vmware has detected a timeout on the channel */ +#define MESSAGE_STATUS_TIMEOUT 0x0040 +/* vmware supports high-bandwidth for sending and receiving the payload */ +#define MESSAGE_STATUS_HB 0x0080 + +/* + * This mask defines the status bits that the guest is allowed to set; + * we use this to mask out all other bits when receiving the status + * from the guest. Otherwise, the guest can manipulate VMX state by + * setting status bits that are only supposed to be changed by the + * VMX. See bug 45385. + */ +#define MESSAGE_STATUS_GUEST_MASK MESSAGE_STATUS_SUCCESS + +/* + * Max number of channels. + * Unfortunately this has to be public because the monitor part + * of the backdoor needs it for its trivial-case optimization. [greg] + */ +#define GUESTMSG_MAX_CHANNEL 8 + +/* Flags to open a channel. --hpreg */ +#define GUESTMSG_FLAG_COOKIE 0x80000000 +#define GUESTMSG_FLAG_ALL GUESTMSG_FLAG_COOKIE + +/* + * Maximum size of incoming message. This is to prevent denial of host service + * attacks from guest applications. + */ +#define GUESTMSG_MAX_IN_SIZE (64 * 1024) + +#endif /* _GUEST_MSG_DEF_H_ */ diff --git a/open-vm-tools/modules/solaris/vmhgfs/hgfsBd.c b/open-vm-tools/modules/solaris/vmhgfs/hgfsBd.c new file mode 100644 index 000000000..284704ac7 --- /dev/null +++ b/open-vm-tools/modules/solaris/vmhgfs/hgfsBd.c @@ -0,0 +1,410 @@ +/********************************************************* + * Copyright (C) 1998 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * hgfsBd.c -- + * + * Backdoor calls used by hgfs pserver. [bac] + */ + +#if defined(__KERNEL__) || defined(_KERNEL) || defined(KERNEL) +# include "kernelStubs.h" +#else +# include +# include +# include +# include +# include "str.h" // for Str_Strcpy +# include "debug.h" +#endif + +#include "vm_assert.h" +#include "rpcout.h" +#include "hgfs.h" // for common HGFS definitions +#include "hgfsBd.h" + + +/* + *----------------------------------------------------------------------------- + * + * HgfsBdGetBufInt -- + * + * Allocates a buffer to send a hgfs request in. This can be either a + * HGFS_PACKET_MAX or HGFS_LARGE_PACKET_MAX size buffer depending on the + * external funciton called. + * + * Results: + * Pointer to a buffer that has the correct backdoor command prefix for + * sending hgfs requests over the backdoor. + * NULL on failure (not enough memory). + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static char * +HgfsBdGetBufInt(size_t bufSize) +{ + /* + * Allocate a buffer that is large enough for an HGFS packet and the + * synchronous HGFS command, write the command, and return a pointer that + * points into the buffer, after the command. + */ + size_t len = bufSize + HGFS_SYNC_REQREP_CLIENT_CMD_LEN; + char *buf = (char*) calloc(sizeof(char), len); + + if (!buf) { + Debug("HgfsBd_GetBuf: Failed to allocate a bd buffer\n"); + return NULL; + } + + Str_Strcpy(buf, HGFS_SYNC_REQREP_CLIENT_CMD, len); + + return buf + HGFS_SYNC_REQREP_CLIENT_CMD_LEN; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsBd_GetBuf -- + * + * Get a buffer of size HGFS_PACKET_MAX to send hgfs requests in. + * + * Results: + * See HgfsBdGetBufInt. + * + * Side effects: + * Allocates memory that must be freed with a call to HgfsBd_PutBuf. + * + *----------------------------------------------------------------------------- + */ + +char * +HgfsBd_GetBuf(void) +{ + return HgfsBdGetBufInt(HGFS_PACKET_MAX); +} + +/* + *----------------------------------------------------------------------------- + * + * HgfsBd_GetLargeBuf -- + * + * Get a buffer of size HGFS_LARGE_PACKET_MAX to send hgfs requests in. + * + * Results: + * See HgfsBdGetBufInt. + * + * Side effects: + * Allocates memory that must be freed with a call to HgfsBd_PutBuf. + * + *----------------------------------------------------------------------------- + */ + +char * +HgfsBd_GetLargeBuf(void) +{ + return HgfsBdGetBufInt(HGFS_LARGE_PACKET_MAX); +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsBd_PutBuf -- + * + * Release a buffer obtained with HgfsBd_GetBuf. + * + * Results: + * None + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +HgfsBd_PutBuf(char *buf) // IN +{ + ASSERT(buf); + + free(buf - HGFS_SYNC_REQREP_CLIENT_CMD_LEN); +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsBd_GetChannel -- + * + * Allocate a new RpcOut channel, and try to open the connection. + * + * Results: + * Pointer to the allocated, opened channel on success. + * NULL on failure (not enough memory, or failed to open the connection). + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +RpcOut * +HgfsBd_GetChannel(void) +{ + RpcOut *out = RpcOut_Construct(); + Bool status; + + if (!out) { + Debug("HgfsBd_GetChannel: Failed to allocate an RpcOut\n"); + return NULL; + } + + status = RpcOut_start(out); + if (status == FALSE) { + RpcOut_Destruct(out); + return NULL; + } + + return out; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsBd_CloseChannel -- + * + * Close the channel and free the RpcOut object. + * + * Results: + * TRUE if closing the channel succeeded, FALSE if it failed. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsBd_CloseChannel(RpcOut *out) // IN: Channel to close and free +{ + Bool success; + + ASSERT(out); + + success = RpcOut_stop(out); + if (success == TRUE) { + RpcOut_Destruct(out); + } + + return success; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsBd_Dispatch -- + * + * Get a reply to an hgfs request. We call RpcOut_Sent, which + * returns a buffer with the reply in it, and we pass this back to + * the caller. + * + * Results: + * On success, returns zero. On failure, returns a negative error. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +int +HgfsBd_Dispatch(RpcOut *out, // IN: Channel to send on + char *packetIn, // IN: Buf containing request packet + size_t *packetSize, // IN/OUT: Size of packet in/out + char const **packetOut) // OUT: Buf containing reply packet +{ + Bool success; + char const *reply; + size_t replyLen; + + ASSERT(out); + ASSERT(packetIn); + ASSERT(packetSize); + ASSERT(packetOut); + + success = RpcOut_send(out, packetIn - HGFS_CLIENT_CMD_LEN, + *packetSize + HGFS_CLIENT_CMD_LEN, + &reply, &replyLen); + if (success == FALSE) { + Debug("HgfsBd_Dispatch: RpcOut_send returned failure\n"); + return -1; + } + + ASSERT(replyLen <= HGFS_LARGE_PACKET_MAX); + *packetOut = reply; + *packetSize = replyLen; + + return 0; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsBd_Enabled -- + * + * Test to see if hgfs is enabled on the host. + * + * Results: + * TRUE if hgfs is enabled. + * FALSE if hgfs is disabled. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsBd_Enabled(RpcOut *out, // IN: RPCI Channel + char *requestPacket) // IN: Buffer (obtained from HgfsBd_GetBuf) +{ + char const *replyPacket; // Buffer returned by HgfsBd_Dispatch + size_t packetSize; + int error; + + /* + * Send a bogus (empty) request to the VMX. If hgfs is disabled on + * the host side then the request will fail (because the RPCI call + * itself will fail). If hgfs is enabled, we will get a packet back + * (it will be an error packet because our request was malformed, + * but we just discard it anyway). + */ + packetSize = 0; + error = HgfsBd_Dispatch(out, + requestPacket, + &packetSize, + &replyPacket); + if (error < 0) { + return FALSE; + } + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsBd_OpenBackdoor -- + * + * Check if the HGFS channel is open, and, if not, open it. This is a + * one-stop convenience wrapper around HgfsBd_Enabled, HgfsBd_GetBuf, and + * HgfsBd_GetChannel. + * + * Results: + * TRUE if the backdoor is now open, regardless of its previous state. + * FALSE if the backdoor could not be opened. + * + * Side effects: + * May open a channel to the host. + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsBd_OpenBackdoor(RpcOut **out) // IN/OUT: RPCI Channel +{ + char *packetBuffer = NULL; + Bool success = FALSE; + + ASSERT(out); + + /* Short-circuit: backdoor is already open. */ + if (*out != NULL) { + return TRUE; + } + + /* Open the channel. */ + *out = HgfsBd_GetChannel(); + if (*out == NULL) { + return FALSE; + } + + /* Allocate a buffer for use in pinging the HGFS server. */ + packetBuffer = HgfsBd_GetBuf(); + if (packetBuffer == NULL) { + goto out; + } + + /* Ping the HGFS server. */ + if (!HgfsBd_Enabled(*out, packetBuffer)) { + goto out; + } + success = TRUE; + + out: + if (packetBuffer != NULL) { + HgfsBd_PutBuf(packetBuffer); + } + if (!success && *out != NULL) { + HgfsBd_CloseChannel(*out); + *out = NULL; + } + return success; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsBd_CloseBackdoor -- + * + * Closes the backdoor channel, if it's open. + * + * Results: + * TRUE if the channel is now closed, regardless of its previous state. + * FALSE if we could not close the channel. + * + * Side effects: + * May close the channel to the host. + * + *----------------------------------------------------------------------------- + */ + +Bool +HgfsBd_CloseBackdoor(RpcOut **out) // IN/OUT: RPCI Channel +{ + Bool success = TRUE; + + ASSERT(out); + + if (*out != NULL) { + if (!HgfsBd_CloseChannel(*out)) { + success = FALSE; + } + *out = NULL; + } + + return success; +} diff --git a/open-vm-tools/modules/solaris/vmhgfs/hgfsBd.h b/open-vm-tools/modules/solaris/vmhgfs/hgfsBd.h new file mode 100644 index 000000000..0439dc565 --- /dev/null +++ b/open-vm-tools/modules/solaris/vmhgfs/hgfsBd.h @@ -0,0 +1,50 @@ +/********************************************************* + * Copyright (C) 1998 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +#ifndef _HGFS_BD_H_ +# define _HGFS_BD_H_ + +/* + * hgfsBd.h -- + * + * Backdoor calls used by hgfs clients. + */ + +#include "rpcout.h" + +char *HgfsBd_GetBuf(void); + +char *HgfsBd_GetLargeBuf(void); + +void HgfsBd_PutBuf(char *); + +RpcOut *HgfsBd_GetChannel(void); + +Bool HgfsBd_CloseChannel(RpcOut *out); + +int HgfsBd_Dispatch(RpcOut *out, + char *packetIn, + size_t *packetSize, + char const **packetOut); + +Bool HgfsBd_Enabled(RpcOut *out, + char *requestPacket); + +Bool HgfsBd_OpenBackdoor(RpcOut **out); + +Bool HgfsBd_CloseBackdoor(RpcOut **out); + +#endif // _HGFS_BD_H_ diff --git a/open-vm-tools/modules/solaris/vmhgfs/hgfsBdGlue.c b/open-vm-tools/modules/solaris/vmhgfs/hgfsBdGlue.c new file mode 100644 index 000000000..8d302abde --- /dev/null +++ b/open-vm-tools/modules/solaris/vmhgfs/hgfsBdGlue.c @@ -0,0 +1,190 @@ +/********************************************************* + * Copyright (C) 2009 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * hgfsBdGlue.c -- + * + * Glue to communicate directly with backdoor code instead of offloading + * it to guestd + */ + +#include "hgfsSolaris.h" +#include "request.h" +#include "hgfsBdGlue.h" +#include "hgfsBd.h" +#include "vm_assert.h" +#include "debug.h" + +static RpcOut *hgfsRpcOut; +static void *packetBuffer; + +/* + * Public function implementations. + */ + +/* + *----------------------------------------------------------------------------- + * + * HgfsBackdoorSendRequest -- + * + * Send one request through backdoor and wait for the result. + * + * Results: + * 0 on success, standard UNIX error code upon failure. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +int +HgfsBackdoorSendRequest(HgfsReq *req) // IN/OUT: Request to be sent +{ + char const *replyPacket; + size_t packetSize; + + ASSERT(req->state == HGFS_REQ_SUBMITTED); + + ASSERT(req->packetSize <= HGFS_PACKET_MAX); + bcopy(req->packet, packetBuffer, req->packetSize); + packetSize = req->packetSize; + + DEBUG(VM_DEBUG_COMM, + "HgfsBackdoorSendRequest: Sending packet over backdoor\n"); + + /* + * We should attempt to reopen the backdoor channel with every request, + * because the HGFS server in the host can be enabled or disabled at any + * time. + */ + if (!HgfsBd_OpenBackdoor(&hgfsRpcOut)) { + + DEBUG(VM_DEBUG_COMM, + "HgfsBackdoorSendRequest: HGFS is disabled in the host\n"); + + req->state = HGFS_REQ_ERROR; + return ENOSYS; + + } else if (HgfsBd_Dispatch(hgfsRpcOut, packetBuffer, + &packetSize, &replyPacket) == 0) { + + DEBUG(VM_DEBUG_COMM, + "HgfsBackdoorSendRequest: backdoor reply received\n"); + + /* Request was sent successfully. Copy the reply and return to the client. */ + ASSERT(packetSize <= HGFS_PACKET_MAX); + + bcopy(replyPacket, req->packet, packetSize); + req->packetSize = packetSize; + req->state = HGFS_REQ_COMPLETED; + + } else { + + DEBUG(VM_DEBUG_COMM, + "HgfsBackdoorSendRequest: backdoor error\n"); + + /* Pass the error into the request. */ + req->state = HGFS_REQ_ERROR; + + /* + * If the channel was previously open, make sure it's dead and gone + * now. We do this because subsequent requests deserve a chance to + * reopen it. + */ + HgfsBd_CloseBackdoor(&hgfsRpcOut); + } + + return 0; +} + + +/* + *----------------------------------------------------------------------------- + * + * HgfsBackdoorCancelRequest -- + * + * Cancel request stub. Since backdoor is synchronous transport this + * function should never be called in practice. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +void +HgfsBackdoorCancelRequest(HgfsReq *req) // IN: Request to be cancelled +{ + DEBUG(VM_DEBUG_COMM, "HgfsBackdoorCancelRequest: %p\n", req); +} + + +/* + *---------------------------------------------------------------------- + * + * HgfsBackdoorInit -- + * + * This function initializes backdoor transport by allocating transfer + * buffer. + * + * Results: + * TRUE if buffer was allocated succsessfully, FALSE otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Bool +HgfsBackdoorInit(void) +{ + packetBuffer = HgfsBd_GetBuf(); + + return packetBuffer != NULL; +} + + +/* + *---------------------------------------------------------------------- + * + * HgfsBackdoorCleanup -- + * + * This function closes backdoor channel. It is supposed to be + * called when we unmount the filesystem. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +HgfsBackdoorCleanup(void) +{ + DEBUG(VM_DEBUG_COMM, "HgfsBackdoorCleanup: Closing backdoor\n"); + + HgfsBd_CloseBackdoor(&hgfsRpcOut); + HgfsBd_PutBuf(packetBuffer); +} + diff --git a/open-vm-tools/modules/solaris/vmhgfs/hgfsBdGlue.h b/open-vm-tools/modules/solaris/vmhgfs/hgfsBdGlue.h new file mode 100644 index 000000000..b39bc504f --- /dev/null +++ b/open-vm-tools/modules/solaris/vmhgfs/hgfsBdGlue.h @@ -0,0 +1,31 @@ +/********************************************************* + * Copyright (C) 2009 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * bdhandler.h -- + * + * Glue for backdoor library. + */ + +#ifndef _HGFS_BD_GLUE_H_ +#define _HGFS_BD_GLUE_H_ + +int HgfsBackdoorSendRequest(HgfsReq *req); +void HgfsBackdoorCancelRequest(HgfsReq *req); +Bool HgfsBackdoorInit(void); +void HgfsBackdoorCleanup(void); + +#endif // _HGFS_DRIVER_BDHANDLER_H_ diff --git a/open-vm-tools/modules/solaris/vmhgfs/hgfsProto.h b/open-vm-tools/modules/solaris/vmhgfs/hgfsProto.h index 637306aba..04b665b3e 100644 --- a/open-vm-tools/modules/solaris/vmhgfs/hgfsProto.h +++ b/open-vm-tools/modules/solaris/vmhgfs/hgfsProto.h @@ -176,6 +176,55 @@ typedef uint8 HgfsPermissions; #define HGFS_PERM_WRITE 2 #define HGFS_PERM_EXEC 1 +/* + * Access mode bits. + * + * Different operating systems have different set of file access mode. + * Here are constants that are rich enough to describe all access modes in an OS + * independent way. + */ + +typedef uint32 HgfsAccessMode; +/* + * Generic access rights control coarse grain access for the file. + * A particular generic rigth can be expanded into different set of specific rights + * on different OS. + */ + +/* + * HGFS_MODE_GENERIC_READ means ability to read file data and read various file + * attributes and properties. + */ +#define HGFS_MODE_GENERIC_READ (1 << 0) +/* + * HGFS_MODE_GENERIC_WRITE means ability to write file data and updaate various file + * attributes and properties. + */ +#define HGFS_MODE_GENERIC_WRITE (1 << 1) +/* + * HGFS_MODE_GENERIC_EXECUE means ability to execute file. For network redirectors + * ability to execute usualy implies ability to read data; for local file systems + * HGFS_MODE_GENERIC_EXECUTE does not imply ability to read data. + */ +#define HGFS_MODE_GENERIC_EXECUTE (1 << 2) + +/* Specific rights define fine grain access modes. */ +#define HGFS_MODE_READ_DATA (1 << 3) // Ability to read file data +#define HGFS_MODE_WRITE_DATA (1 << 4) // Ability to writge file data +#define HGFS_MODE_APPEND_DATA (1 << 5) // Appending data to the end of file +#define HGFS_MODE_DELETE (1 << 6) // Ability to delete the file +#define HGFS_MODE_TRAVERSE_DIRECTORY (1 << 7) // Ability to access files in a directory +#define HGFS_MODE_LIST_DIRECTORY (1 << 8) // Ability to list file names +#define HGFS_MODE_ADD_SUBDIRECTORY (1 << 9) // Ability to create a new subdirectory +#define HGFS_MODE_ADD_FILE (1 << 10) // Ability to create a new file +#define HGFS_MODE_DELETE_CHILD (1 << 11) // Ability to delete file/subdirectory +#define HGFS_MODE_READ_ATTRIBUTES (1 << 12) // Ability to read attributes +#define HGFS_MODE_WRITE_ATTRIBUTES (1 << 13) // Ability to write attributes +#define HGFS_MODE_READ_EXTATTRIBUTES (1 << 14) // Ability to read extended attributes +#define HGFS_MODE_WRITE_EXTATTRIBUTES (1 << 15) // Ability to write extended attributes +#define HGFS_MODE_READ_SECURITY (1 << 16) // Ability to read permissions/ACLs/owner +#define HGFS_MODE_WRITE_SECURITY (1 << 17) // Ability to change permissions/ACLs +#define HGFS_MODE_TAKE_OWNERSHIP (1 << 18) // Ability to change file owner/group /* * Server-side locking (oplocks and leases). @@ -354,6 +403,15 @@ typedef uint64 HgfsAttrValid; * only to determine if the ID is valid. */ #define HGFS_ATTR_VALID_NON_STATIC_FILEID (1 << 16) +/* + * File permissions that are in effect for the user which runs HGFS server. + * Client needs to know effective permissions in order to implement access(2). + * Client can't derive it from group/owner/other permissions because of two resaons: + * 1. It does not know user/group id of the user which runs HGFS server + * 2. Effective permissions account for additional restrictions that may be imposed + * by host file system, for example by ACL. + */ +#define HGFS_ATTR_VALID_EFFECTIVE_PERMS (1 << 17) /* @@ -398,7 +456,7 @@ struct HgfsAttrV2 { uint32 groupId; /* group identifier, ignored by Windows */ uint64 hostFileId; /* File Id of the file on host: inode_t on Linux */ uint32 volumeId; /* volume identifier, non-zero is valid. */ - uint32 reserved1; /* Reserved for future use */ + uint32 effectivePerms; /* Permissions in effect for the user on the host. */ uint64 reserved2; /* Reserved for future use */ } #include "vmware_pack_end.h" diff --git a/open-vm-tools/modules/solaris/vmhgfs/hgfsSolaris.h b/open-vm-tools/modules/solaris/vmhgfs/hgfsSolaris.h index c32d57918..5a1f8871b 100644 --- a/open-vm-tools/modules/solaris/vmhgfs/hgfsSolaris.h +++ b/open-vm-tools/modules/solaris/vmhgfs/hgfsSolaris.h @@ -84,9 +84,6 @@ /* Determines size of request pool */ #define HGFS_MAX_OUTSTANDING_REQS 4 -/* For ddi_soft_state_init() call in _init() */ -#define HGFS_EXPECTED_INSTANCES 23 - /* HGFS cmn_err() levels */ #define HGFS_ERROR (CE_WARN) @@ -96,22 +93,12 @@ #define HGFS_ERR_NODEV (-51) #define HGFS_ERR_INVAL (-52) -/* This is what shows up after the ':' in the /devices entry */ -#define HGFS_DEV_NAME "vmware-hgfs" - /* * Don't change this to KM_NOSLEEP without first making sure that we handle * the possibility of kmem_zalloc() failing: KM_SLEEP guarantees it won't fail */ #define HGFS_ALLOC_FLAG (KM_SLEEP) -/* - * hgfsInstance is uninitialized when device is not open and hgfsType is - * uninitialized before the file system initialization routine is called. - */ -#define HGFS_INSTANCE_UNINITIALIZED (-1) -#define HGFS_TYPE_UNINITIALIZED (-1) - /* Accessing root inode and vnode */ #define HGFS_ROOT_VNODE(sip) (sip->rootVnode) @@ -132,39 +119,6 @@ typedef HgfsRequest HgfsRequestHeader; */ #ifdef _KERNEL -/* - * The global state structure for the entire module. This is allocated in - * HgfsDevAttach() and deallocated in HgfsDevDetach(). - * - * Note that reqMutex and reqFreeList are also used for synchronization between - * the filesystem and driver. See docs/synchronization.txt for details. - */ -typedef struct HgfsSuperInfo { - dev_info_t *dip; /* Device information pointer */ - Bool devOpen; /* Flag indicating whether device is open */ - /* Poll */ - struct pollhead hgfsPollhead; /* Needed for chpoll() implementation */ - Bool pollwakeupOnWrite; /* Flag which indicates need to call - pollwakeup() on write() -- no mutex - since only modified in chpoll() */ - /* Request list */ - DblLnkLst_Links reqList; /* Anchor for pending request list */ - kmutex_t reqMutex; /* For protection of request list */ - kcondvar_t reqCondVar; /* For waiting on request list */ - /* Free request list */ - DblLnkLst_Links reqFreeList; /* Anchor for free request list */ - kmutex_t reqFreeMutex; /* For protection of reqFreeList */ - kcondvar_t reqFreeCondVar; /* For waiting on free request list */ - /* For filesystem */ - struct vfs *vfsp; /* Our filesystem structure */ - struct vnode *rootVnode; /* Root vnode of the filesystem */ - HgfsFileHashTable fileHashTable; /* File hash table */ -# if HGFS_VFS_VERSION > 2 - /* We keep a pointer to the vnodeops the Kernel created for us in Sol 10 */ - struct vnodeops *vnodeOps; /* Operations on files */ -# endif -} HgfsSuperInfo; - /* * Each request will traverse through this set of states. See * docs/request-lifecycle.txt for an explanation of these and the API for @@ -191,30 +145,45 @@ typedef struct HgfsReq { signal presence of reply. Used with the reqMutex in HgfsSuperInfo. */ HgfsReqState state; /* Indicates state of request */ - kmutex_t stateLock; /* Protects state: only used in - HgfsReq{S,G}etState() */ - uint32_t id; /* The unique identifier of this request */ - uint32_t packetSize; /* Total size of packet */ char packet[HGFS_PACKET_MAX]; /* Contains both requests and replies */ } HgfsReq; /* - * Global Variables + * The global state structure for the entire module. This is allocated in + * HgfsDevAttach() and deallocated in HgfsDevDetach(). + * + * Note that reqMutex and reqFreeList are also used for synchronization between + * the filesystem and driver. See docs/synchronization.txt for details. */ +typedef struct HgfsSuperInfo { + kmutex_t reqMutex; /* Serializes sending of requests */ + + /* Free request list */ + DblLnkLst_Links reqFreeList; /* Anchor for free request list */ + kmutex_t reqFreeMutex; /* For protection of reqFreeList */ + kcondvar_t reqFreeCondVar; /* For waiting on free request list */ + + /* For filesystem */ + struct vfs *vfsp; /* Our filesystem structure */ + struct vnode *rootVnode; /* Root vnode of the filesystem */ + HgfsFileHashTable fileHashTable; /* File hash table */ + + int (*sendRequest)(HgfsReq *req); /* Current transport's sent method */ + void (*cancelRequest)(HgfsReq *req); /* Current transport's cancel method */ + Bool (*transportInit)(void); + void (*transportCleanup)(void); +} HgfsSuperInfo; -/* Pool of request structures */ -HgfsReq requestPool[HGFS_MAX_OUTSTANDING_REQS]; /* - * Fileystem type number. - * - * This needs to be stored here rather than in the Superinfo because the soft - * state is not guaranteed to have been allocated when HgfsInit() is called. + * Global Variables */ -int hgfsType; + +/* Pool of request structures */ +HgfsReq requestPool[HGFS_MAX_OUTSTANDING_REQS]; /* * Used to access shared state of driver and filesystem. superInfoHead is diff --git a/open-vm-tools/modules/solaris/vmhgfs/hgfsState.c b/open-vm-tools/modules/solaris/vmhgfs/hgfsState.c index 9b0080951..56a004713 100644 --- a/open-vm-tools/modules/solaris/vmhgfs/hgfsState.c +++ b/open-vm-tools/modules/solaris/vmhgfs/hgfsState.c @@ -170,7 +170,7 @@ HgfsVnodeGet(struct vnode **vpp, // OUT: Filled with address of created v * Now set the vnode operations. This is handled differently on Solaris * 9 and 10, and we call HgfsSetVnodeOps() to take care of this for us. */ - if (HgfsSetVnodeOps(sip, vp) != 0) { + if (HgfsSetVnodeOps(vp) != 0) { goto vnode_error; } @@ -483,7 +483,7 @@ HgfsNodeIdGet(HgfsFileHashTable *htp, // IN: File hash table * Initializes the hash table used to track per-file state. * * Results: - * Returns 0 on success and a non-zero error code on failure. + * None. * * Side effects: * None. @@ -491,7 +491,7 @@ HgfsNodeIdGet(HgfsFileHashTable *htp, // IN: File hash table *---------------------------------------------------------------------------- */ -int +void HgfsInitFileHashTable(HgfsFileHashTable *htp) // IN: Hash table to initialize { int i; @@ -503,8 +503,6 @@ HgfsInitFileHashTable(HgfsFileHashTable *htp) // IN: Hash table to initialize for (i = 0; i < ARRAYSIZE(htp->hashTable); i++) { DblLnkLst_Init(&htp->hashTable[i]); } - - return 0; } diff --git a/open-vm-tools/modules/solaris/vmhgfs/hgfsState.h b/open-vm-tools/modules/solaris/vmhgfs/hgfsState.h index 337e7e43d..bfaa44233 100644 --- a/open-vm-tools/modules/solaris/vmhgfs/hgfsState.h +++ b/open-vm-tools/modules/solaris/vmhgfs/hgfsState.h @@ -172,7 +172,7 @@ int HgfsFileNameToVnode(const char *fileName, struct vnode **vpp, HgfsFileHashTable *htp); void HgfsNodeIdGet(HgfsFileHashTable *ht, const char *fileName, uint32_t fileNameLength, ino64_t *outNodeId); -int HgfsInitFileHashTable(HgfsFileHashTable *htp); +void HgfsInitFileHashTable(HgfsFileHashTable *htp); Bool HgfsFileHashTableIsEmpty(struct HgfsSuperInfo *sip, HgfsFileHashTable *htp); /* Handle get/set/clear functions */ diff --git a/open-vm-tools/modules/solaris/vmhgfs/kernelStubs.h b/open-vm-tools/modules/solaris/vmhgfs/kernelStubs.h new file mode 100644 index 000000000..20cbb3e34 --- /dev/null +++ b/open-vm-tools/modules/solaris/vmhgfs/kernelStubs.h @@ -0,0 +1,148 @@ +/********************************************************* + * Copyright (C) 2006 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * kernelStubs.h + * + * KernelStubs implements some userspace library functions in terms + * of kernel functions to allow library userspace code to be used in a + * kernel. + */ + +#ifndef __KERNELSTUBS_H__ +#define __KERNELSTUBS_H__ + +#ifdef linux +# ifndef __KERNEL__ +# error "__KERNEL__ is not defined" +# endif +# include "driver-config.h" // Must be included before any other header files +# include "vm_basic_types.h" +# include +# include +#elif defined(_WIN32) +# include "vm_basic_types.h" +# include /* kernel memory APIs */ +# include /* for _vsnprintf, vsprintf */ +# include /* for va_start stuff */ +# include /* for min macro. */ +# include "vm_assert.h" /* Our assert macros */ +#elif defined(__FreeBSD__) +# include "vm_basic_types.h" +# ifndef _KERNEL +# error "_KERNEL is not defined" +# endif +# include +# include +# include +# include +# include +# include +# include "vm_assert.h" +#elif defined(__APPLE__) +# include "vm_basic_types.h" +# ifndef KERNEL +# error "KERNEL is not defined" +# endif +# include +# include +# elif defined(sun) +# include "vm_basic_types.h" +# include +# include +#endif + +/* + * Function Prototypes + */ + +#if defined(linux) || defined(__APPLE__) || defined (sun) + +# ifdef linux /* if (linux) { */ +char *strdup(const char *source); +# endif + +/* Shared between Linux and Apple kernel stubs. */ +void *malloc(size_t size); +void free(void *mem); +void *calloc(size_t num, size_t len); +void *realloc(void *ptr, size_t newSize); + +#elif defined(_WIN32) /* } else if (_WIN32) { */ + +#if (_WIN32_WINNT == 0x0400) +/* The following declarations are missing on NT4. */ +typedef unsigned int UINT_PTR; +typedef unsigned int SIZE_T; + +/* No free with tag availaible on NT4 kernel! */ +#define KRNL_STUBS_FREE(P,T) ExFreePool((P)) + +#else /* _WIN32_WINNT */ +#define KRNL_STUBS_FREE(P,T) ExFreePoolWithTag((P),(T)) +/* Win 2K and later useful kernel function, documented but not declared! */ +NTKERNELAPI VOID ExFreePoolWithTag(IN PVOID P, IN ULONG Tag); +#endif /* _WIN32_WINNT */ + +#elif defined(__FreeBSD__) /* } else if (FreeBSD) { */ + +/* Kernel memory on FreeBSD is tagged for statistics and sanity checking. */ +MALLOC_DECLARE(M_VMWARE_TEMP); + +/* + * On FreeBSD, the general memory allocator for both userland and the kernel is named + * malloc, but the kernel malloc() takes more arguments. The following alias & macros + * work around this, to provide the standard malloc() API for userspace code that is + * being used in the kernel. + */ + +# undef malloc + +static INLINE void * +__compat_malloc(unsigned long size, struct malloc_type *type, int flags) { + return malloc(size, type, flags); +} + +# define malloc(size) __compat_malloc(size, M_VMWARE_TEMP, M_NOWAIT) +# define calloc(count, size) __compat_malloc((count) * (size), \ + M_VMWARE_TEMP, M_NOWAIT|M_ZERO) +# define realloc(buf, size) realloc(buf, size, M_VMWARE_TEMP, M_NOWAIT) +# define free(buf) free(buf, M_VMWARE_TEMP) +# define strchr(s,c) index(s,c) +# define strrchr(s,c) rindex(s,c) + +#endif /* } */ + +/* + * Stub functions we provide. + */ + +void Panic(const char *fmt, ...); + +char *Str_Strcpy(char *buf, const char *src, size_t maxSize); +int Str_Vsnprintf(char *str, size_t size, const char *format, + va_list arguments); +char *Str_Vasprintf(size_t *length, const char *format, + va_list arguments); +char *Str_Asprintf(size_t *length, const char *Format, ...); + +/* + * Functions the driver must implement for the stubs. + */ +EXTERN void Debug(const char *fmt, ...); + + +#endif /* __KERNELSTUBS_H__ */ diff --git a/open-vm-tools/modules/solaris/vmhgfs/kernelStubsSolaris.c b/open-vm-tools/modules/solaris/vmhgfs/kernelStubsSolaris.c new file mode 100644 index 000000000..75ae9b9f1 --- /dev/null +++ b/open-vm-tools/modules/solaris/vmhgfs/kernelStubsSolaris.c @@ -0,0 +1,380 @@ +/********************************************************* + * Copyright (C) 2009 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * kernelStubsSolaris.c + * + * This file contains implementations of common userspace functions in terms + * that the Solaris kernel can understand. + */ + +#include "kernelStubs.h" +#include +#include +#include + +#ifdef SOL9 +# define compat_va_start(arg, fmt) arg = ((char *)__builtin_next_arg(fmt)) +# define compat_va_end(arg) +# define compat_va_copy(arg1, arg2) va_copy(arg1, arg2) +#else +# define compat_va_start(arg, fmt) va_start(arg, fmt) +# define compat_va_end(arg) va_end(arg) +# define compat_va_copy(arg1, arg2) va_copy(arg1, arg2) +#endif + +/* + *----------------------------------------------------------------------------- + * + * Panic -- + * + * Prints the debug message and stops the system. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +void +Panic(const char *fmt, ...) // IN +{ + va_list args; + char *result; + + compat_va_start(args, fmt); + result = Str_Vasprintf(NULL, fmt, args); + compat_va_end(args); + + cmn_err(CE_PANIC, "%s", + result ? result : "Unable to format PANIC message"); +} + +/* + *---------------------------------------------------------------------- + * + * Str_Strcpy -- + * + * Wrapper for strcpy that checks for buffer overruns. + * + * Results: + * Same as strcpy. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +char * +Str_Strcpy(char *buf, // OUT + const char *src, // IN + size_t maxSize) // IN +{ + size_t len; + + len = strlen(src); + if (len >= maxSize) { + Panic("%s:%d Buffer too small %p\n", __FILE__, __LINE__, buf); + } + bcopy(src, buf, len + 1); + return buf; +} + + +/* + *---------------------------------------------------------------------- + * + * Str_Vsnprintf -- + * + * Compatability wrapper b/w different libc versions + * + * Results: + * int - number of bytes written (not including NULL terminator), + * -1 on overflow (insufficient space for NULL terminator + * is considered overflow). + * + * NB: on overflow the buffer WILL be null terminated. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +Str_Vsnprintf(char *str, // OUT + size_t size, // IN + const char *format, // IN + va_list arguments) // IN +{ + int retval = vsnprintf(str, size, format, arguments); + + /* + * Linux glibc 2.0.x returns -1 and null terminates (which we shouldn't + * be linking against), but glibc 2.1.x follows c99 and returns + * characters that would have been written. + */ + if (retval >= size) { + return -1; + } + return retval; +} + + +/* + *----------------------------------------------------------------------------- + * + * Str_Vasprintf -- + * + * Allocate and format a string, using the GNU libc way to specify the + * format (i.e. optionally allow the use of positional parameters). + * + * Results: + * The allocated string on success (if 'length' is not NULL, *length + * is set to the length of the allocated string), NULL on failure. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +char * +Str_Vasprintf(size_t *length, // OUT + const char *format, // IN + va_list arguments) // IN +{ + /* + * Simple implementation of Str_Vasprintf when userlevel libraries are not + * available (e.g. for use in drivers). We just fallback to vsnprintf, + * doubling if we didn't have enough space. + */ + unsigned int bufSize; + char *buf; + int retval; + + bufSize = strlen(format); + buf = NULL; + + do { + /* + * Initial allocation of strlen(format) * 2. Should this be tunable? + * XXX Yes, this could overflow and spin forever when you get near 2GB + * allocations. I don't care. --rrdharan + */ + va_list args2; + + bufSize *= 2; + buf = realloc(buf, bufSize); + + if (!buf) { + return NULL; + } + + compat_va_copy(args2, arguments); + retval = Str_Vsnprintf(buf, bufSize, format, args2); + compat_va_end(args2); + } while (retval == -1); + + if (length) { + *length = retval; + } + + /* + * Try to trim the buffer here to save memory? + */ + return buf; +} + + +/* + *----------------------------------------------------------------------------- + * + * Str_Asprintf -- + * + * Same as Str_Vasprintf(), but parameters are passed inline --hpreg + * + * Results: + * Same as Str_Vasprintf() + * + * Side effects: + * Same as Str_Vasprintf() + * + *----------------------------------------------------------------------------- + */ + +char * +Str_Asprintf(size_t *length, // OUT + const char *format, // IN + ...) // IN +{ + va_list arguments; + char *result; + + compat_va_start(arguments, format); + result = Str_Vasprintf(length, format, arguments); + compat_va_end(arguments); + + return result; +} + + +/* + *---------------------------------------------------------------------------- + * + * malloc -- + * + * Allocate memory using kmalloc. There is no realloc + * equivalent, so we roll our own by padding each allocation with + * 4 (or 8 for 64 bit guests) extra bytes to store the block length. + * + * Results: + * Pointer to driver heap memory, offset by 4 (or 8) + * bytes from the real block pointer. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +void * +malloc(size_t size) // IN +{ + size_t *ptr = kmem_alloc(size + sizeof(size), KM_SLEEP); + + if (ptr) { + *ptr++ = size; + } + return ptr; +} + + +/* + *--------------------------------------------------------------------------- + * + * free -- + * + * Free memory allocated by a previous call to malloc, calloc or realloc. + * + * Results: + * None. + * + * Side effects: + * Calls kmem_free to free the real (base) pointer. + * + *--------------------------------------------------------------------------- + */ + +void +free(void *mem) // IN +{ + size_t *dataPtr = mem; + + if (mem) { + --dataPtr; + kmem_free(dataPtr, *dataPtr + sizeof(*dataPtr)); + } +} + + +/* + *---------------------------------------------------------------------------- + * + * calloc -- + * + * Malloc and zero. + * + * Results: + * Pointer to driver heap memory (see malloc, above). + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +void * +calloc(size_t num, // IN + size_t len) // IN +{ + size_t size; + void *ptr; + + size = num * len; + ptr = malloc(size); + if (ptr) { + memset(ptr, 0, size); + } + return ptr; +} + + +/* + *---------------------------------------------------------------------------- + * + * realloc -- + * + * Since the driver heap has no realloc equivalent, we have to roll our + * own. Fortunately, we can retrieve the block size of every block we + * hand out since we stashed it at allocation time (see malloc above). + * + * Results: + * Pointer to memory block valid for 'newSize' bytes, or NULL if + * allocation failed. + * + * Side effects: + * Could copy memory around. + * + *---------------------------------------------------------------------------- + */ + +void * +realloc(void *ptr, // IN + size_t newSize) // IN +{ + void *newPtr; + size_t *dataPtr; + size_t length, lenUsed; + + dataPtr = (size_t *)ptr; + length = ptr ? dataPtr[-1] : 0; + if (newSize == 0) { + if (ptr) { + free(ptr); + newPtr = NULL; + } else { + newPtr = malloc(newSize); + } + } else if (newSize == length) { + newPtr = ptr; + } else if ((newPtr = malloc(newSize))) { + if (length < newSize) { + lenUsed = length; + } else { + lenUsed = newSize; + } + memcpy(newPtr, ptr, lenUsed); + free(ptr); + } + return newPtr; +} + + diff --git a/open-vm-tools/modules/solaris/vmhgfs/message.c b/open-vm-tools/modules/solaris/vmhgfs/message.c new file mode 100644 index 000000000..7a7e3ab54 --- /dev/null +++ b/open-vm-tools/modules/solaris/vmhgfs/message.c @@ -0,0 +1,296 @@ +/********************************************************* + * Copyright (C) 1999 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * message.c -- + * + * Second layer of the internal communication channel between guest + * applications and vmware + * + * Build a generic messaging system between guest applications and vmware. + * + * The protocol is not completely symmetrical, because: + * . basic requests can only be sent by guest applications (when vmware + * wants to post a message to a guest application, the message will be + * really fetched only when the guest application will poll for new + * available messages) + * . several guest applications can talk to vmware, while the contrary is + * not true + * + * Operations that are not atomic (in terms of number of backdoor calls) + * can be aborted by vmware if a checkpoint/restore occurs in the middle of + * such an operation. This layer takes care of retrying those operations. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__KERNEL__) || defined(_KERNEL) || defined(KERNEL) +# include "kernelStubs.h" +#else +# include +# include +# include "debug.h" +#endif + +#include "backdoor_def.h" +#include "guest_msg_def.h" +#include "backdoor.h" +#include "message.h" + +static MessageOpenProcType externalOpenProc = NULL; +static MessageGetReadEventProcType externalGetReadEventProc = NULL; +static MessageSendProcType externalSendProc = NULL; +static MessageReceiveProcType externalReceiveProc = NULL; +static MessageCloseProcType externalCloseProc = NULL; + +/* + * Currently, the default implementation is to use the backdoor. Soon, + * this will not be the default, as we will explicitly set it when we + * decide to use the backdoor. + */ +EXTERN Message_Channel *MessageBackdoor_Open(uint32 proto); + +EXTERN Bool MessageBackdoor_GetReadEvent(Message_Channel *chan, + int64 *event); + +EXTERN Bool MessageBackdoor_Send(Message_Channel *chan, + const unsigned char *buf, + size_t bufSize); + +EXTERN Bool MessageBackdoor_Receive(Message_Channel *chan, + unsigned char **buf, + size_t *bufSize); + +EXTERN Bool MessageBackdoor_Close(Message_Channel *chan); + + + +/* + *----------------------------------------------------------------------------- + * + * Message_SetTransport -- + * + * This tells the message layer to use an alternate transport + * for messages. By default, we use the backdoor, so this function + * overrides that default at runtime and switches everything over to + * an alternate transport. + * + * Result: + * None + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +Message_SetTransport(MessageOpenProcType openProc, // IN + MessageGetReadEventProcType getReadEeventProc, // IN + MessageSendProcType sendProc, // IN + MessageReceiveProcType receiveProc, // IN + MessageCloseProcType closeProc) // IN +{ + externalOpenProc = openProc; + externalGetReadEventProc = getReadEeventProc; + externalSendProc = sendProc; + externalReceiveProc = receiveProc; + externalCloseProc = closeProc; +} + + +/* + *----------------------------------------------------------------------------- + * + * Message_Open -- + * + * Open a communication channel + * + * Result: + * An allocated Message_Channel on success + * NULL on failure + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Message_Channel * +Message_Open(uint32 proto) // IN +{ + /* + * If there is an alterate backdoor implementation, then call that. + */ + if (NULL != externalOpenProc) { + return((*externalOpenProc)(proto)); + } + + /* + * Otherwise, we default to the backdoor. + */ + return(MessageBackdoor_Open(proto)); +} + + +/* + *----------------------------------------------------------------------------- + * + * Message_GetReadEvent -- + * + * This allows higher levels of the IPC stack to use an event to detect + * when a message has arrived. This allows an asynchronous, event-based-model + * rather than continually calling Message_Receive in a busy loop. This may + * only be supported by some transports. The backdoor does not, so the IPC + * code will still have to poll in those cases. + * + * Result: + * Bool - whether this feature is supported by this transport. + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +Message_GetReadEvent(Message_Channel *chan, // IN + int64 *event) // OUT +{ + /* + * If there is an alterate backdoor implementation, then call that. + */ + if (NULL != externalGetReadEventProc) { + return((*externalGetReadEventProc)(chan, event)); + } + + /* + * Otherwise, we default to the backdoor. + */ + return(MessageBackdoor_GetReadEvent(chan, event)); +} + + +/* + *----------------------------------------------------------------------------- + * + * Message_Send -- + * + * Send a message over a communication channel + * + * Result: + * TRUE on success + * FALSE on failure (the message is discarded by vmware) + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +Message_Send(Message_Channel *chan, // IN/OUT + const unsigned char *buf, // IN + size_t bufSize) // IN +{ + /* + * If there is an alterate backdoor implementation, then call that. + */ + if (NULL != externalSendProc) { + return((*externalSendProc)(chan, buf, bufSize)); + } + + /* + * Otherwise, we default to the backdoor. + */ + return(MessageBackdoor_Send(chan, buf, bufSize)); +} + + +/* + *----------------------------------------------------------------------------- + * + * Message_Receive -- + * + * If vmware has posted a message for this channel, retrieve it + * + * Result: + * TRUE on success (bufSize is 0 if there is no message) + * FALSE on failure + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +Message_Receive(Message_Channel *chan, // IN/OUT + unsigned char **buf, // OUT + size_t *bufSize) // OUT +{ + /* + * If there is an alterate backdoor implementation, then call that. + */ + if (NULL != externalReceiveProc) { + return((*externalReceiveProc)(chan, buf, bufSize)); + } + + /* + * Otherwise, we default to the backdoor. + */ + return(MessageBackdoor_Receive(chan, buf, bufSize)); +} + + +/* + *----------------------------------------------------------------------------- + * + * Message_Close -- + * + * Close a communication channel + * + * Result: + * TRUE on success, the channel is destroyed + * FALSE on failure + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +Message_Close(Message_Channel *chan) // IN/OUT +{ + /* + * If there is an alterate backdoor implementation, then call that. + */ + if (NULL != externalCloseProc) { + return((*externalCloseProc)(chan)); + } + + /* + * Otherwise, we default to the backdoor. + */ + return(MessageBackdoor_Close(chan)); +} + +#ifdef __cplusplus +} +#endif diff --git a/open-vm-tools/modules/solaris/vmhgfs/message.h b/open-vm-tools/modules/solaris/vmhgfs/message.h new file mode 100644 index 000000000..92c6b2a50 --- /dev/null +++ b/open-vm-tools/modules/solaris/vmhgfs/message.h @@ -0,0 +1,104 @@ +/********************************************************* + * Copyright (C) 1999 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * message.h -- + * + * Second layer of the internal communication channel between guest + * applications and vmware + */ + +#ifndef __MESSAGE_H__ +# define __MESSAGE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "vm_basic_types.h" + + +typedef struct Message_Channel Message_Channel; + + +/* + * These functions must be implemented by any external Message + * transport implementation. Some examples include crossTalk, + * a network socket, or a Microsoft Hypervisor backdoor. + * + * These external functions mirror the same corresponding Message_* + * functions below. + */ +typedef Message_Channel *(*MessageOpenProcType)(uint32 proto); + +typedef Bool (*MessageGetReadEventProcType)(Message_Channel *chan, + int64 *readEvent); + +typedef Bool (*MessageSendProcType)(Message_Channel *chan, + const unsigned char *buf, + size_t bufSize); +typedef Bool (*MessageReceiveProcType)(Message_Channel *chan, + unsigned char **buf, + size_t *bufSize); +typedef Bool (*MessageCloseProcType)(Message_Channel *chan); + + +/* + * This tells the message layer to use an alternate transport + * for messages. By default, we use the backdoor, so this function + * overrides that default at runtime and switches everything over to + * an alternate transport. + */ +void Message_SetTransport(MessageOpenProcType openProc, + MessageGetReadEventProcType getReadEeventProc, + MessageSendProcType sendProc, + MessageReceiveProcType receiveProc, + MessageCloseProcType closeProc); + +void MessageStub_RegisterTransport(void); + +Message_Channel * +Message_Open(uint32 proto); // IN + +/* + * This allows higher levels of the IPC stack to use an event to detect + * when a message has arrived. This allows an interrupt-model rather than + * continually calling Message_Receive in a busy loop. This may only be supported + * by some transports. The backdoor does not, so the IPC code will still + * have to poll in those cases. + */ +Bool +Message_GetReadEvent(Message_Channel *chan, // IN + int64 *event); // OUT + +Bool +Message_Send(Message_Channel *chan, // IN/OUT + const unsigned char *buf, // IN + size_t bufSize); // IN + +Bool +Message_Receive(Message_Channel *chan, // IN/OUT + unsigned char **buf, // OUT + size_t *bufSize); // OUT + +Bool +Message_Close(Message_Channel *chan); // IN/OUT + +#ifdef __cplusplus +} +#endif + +#endif /* __MESSAGE_H__ */ diff --git a/open-vm-tools/modules/solaris/vmhgfs/messageBackdoor.c b/open-vm-tools/modules/solaris/vmhgfs/messageBackdoor.c new file mode 100644 index 000000000..9c72011b3 --- /dev/null +++ b/open-vm-tools/modules/solaris/vmhgfs/messageBackdoor.c @@ -0,0 +1,626 @@ +/********************************************************* + * Copyright (C) 1999 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * messageBackdoor.c -- + * + * Second layer of the internal communication channel between guest + * applications and vmware + * + * Build a generic messaging system between guest applications and vmware. + * + * The protocol is not completely symmetrical, because: + * . basic requests can only be sent by guest applications (when vmware + * wants to post a message to a guest application, the message will be + * really fetched only when the guest application will poll for new + * available messages) + * . several guest applications can talk to vmware, while the contrary is + * not true + * + * Operations that are not atomic (in terms of number of backdoor calls) + * can be aborted by vmware if a checkpoint/restore occurs in the middle of + * such an operation. This layer takes care of retrying those operations. + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +#if defined(__KERNEL__) || defined(_KERNEL) || defined(KERNEL) +# include "kernelStubs.h" +#else +# include +# include +# include "debug.h" +#endif + +#ifdef MESSAGE_DEBUG +# define MESSAGE_LOG(args) Debug args +#else /* MESSAGE_DEBUG */ +# define MESSAGE_LOG(args) +#endif /* MESSAGE_DEBUG */ + + +#include "backdoor_def.h" +#include "guest_msg_def.h" +#include "backdoor.h" +#include "message.h" + + +/* The channel object */ +struct Message_Channel { + /* Identifier */ + uint16 id; + + /* Reception buffer */ + /* Data */ + unsigned char *in; + /* Allocated size */ + size_t inAlloc; + + /* The cookie */ + uint32 cookieHigh; + uint32 cookieLow; +}; + + +/* + *----------------------------------------------------------------------------- + * + * MessageBackdoor_Open -- + * + * Open a communication channel + * + * Result: + * An allocated Message_Channel on success + * NULL on failure + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Message_Channel * +MessageBackdoor_Open(uint32 proto) // IN +{ + Message_Channel *chan; + uint32 flags; + Backdoor_proto bp; + + chan = (Message_Channel *)malloc(sizeof(*chan)); + if (chan == NULL) { + MESSAGE_LOG(("Message: Not enough memory\n")); + goto error_quit; + } + + flags = GUESTMSG_FLAG_COOKIE; +retry: + /* IN: Type */ + bp.in.cx.halfs.high = MESSAGE_TYPE_OPEN; + /* IN: Magic number of the protocol and flags */ + bp.in.size = proto | flags; + + bp.in.cx.halfs.low = BDOOR_CMD_MESSAGE; + Backdoor(&bp); + + /* OUT: Status */ + if ((bp.in.cx.halfs.high & MESSAGE_STATUS_SUCCESS) == 0) { + if (flags) { + /* Cookies not supported. Fall back to no cookie. --hpreg */ + flags = 0; + goto retry; + } + + MESSAGE_LOG(("Message: Unable to open a communication channel\n")); + goto error_quit; + } + + /* OUT: Id and cookie */ + chan->id = bp.in.dx.halfs.high; + chan->cookieHigh = bp.out.si.word; + chan->cookieLow = bp.out.di.word; + + /* Initialize the channel */ + chan->in = NULL; + chan->inAlloc = 0; + + MESSAGE_LOG(("Message: Communication channel %u opened\n", chan->id)); + return chan; + +error_quit: + free(chan); + chan = NULL; + return NULL; +} + + +/* + *----------------------------------------------------------------------------- + * + * MessageBackdoor_GetReadEvent -- + * + * This allows higher levels of the IPC stack to use an event to detect + * when a message has arrived. This allows an interrupt-model rather than + * continually calling Message_Receive in a busy loop. This may only be supported + * by some transports. The backdoor does not, so the IPC code will still + * have to poll in those cases. + * + * Result: + * Bool - whether this feature is supported by this transport. + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +MessageBackdoor_GetReadEvent(Message_Channel *chan, // IN + int64 *event) // OUT +{ + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * MessageBackdoor_Send -- + * + * Send a message over a communication channel + * + * Result: + * TRUE on success + * FALSE on failure (the message is discarded by vmware) + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +MessageBackdoor_Send(Message_Channel *chan, // IN/OUT + const unsigned char *buf, // IN + size_t bufSize) // IN +{ + const unsigned char *myBuf; + size_t myBufSize; + Backdoor_proto bp; + +retry: + myBuf = buf; + myBufSize = bufSize; + + /* + * Send the size. + */ + + /* IN: Type */ + bp.in.cx.halfs.high = MESSAGE_TYPE_SENDSIZE; + /* IN: Id and cookie */ + bp.in.dx.halfs.high = chan->id; + bp.in.si.word = chan->cookieHigh; + bp.in.di.word = chan->cookieLow; + /* IN: Size */ + bp.in.size = myBufSize; + + bp.in.cx.halfs.low = BDOOR_CMD_MESSAGE; + Backdoor(&bp); + + /* OUT: Status */ + if ((bp.in.cx.halfs.high & MESSAGE_STATUS_SUCCESS) == 0) { + MESSAGE_LOG(("Message: Unable to send a message over the communication " + "channel %u\n", chan->id)); + return FALSE; + } + + if (bp.in.cx.halfs.high & MESSAGE_STATUS_HB) { + /* + * High-bandwidth backdoor port supported. Send the message in one + * backdoor operation. --hpreg + */ + + if (myBufSize) { + Backdoor_proto_hb bphb; + + bphb.in.bx.halfs.low = BDOORHB_CMD_MESSAGE; + bphb.in.bx.halfs.high = MESSAGE_STATUS_SUCCESS; + bphb.in.dx.halfs.high = chan->id; + bphb.in.bp.word = chan->cookieHigh; + bphb.in.dstAddr = chan->cookieLow; + bphb.in.size = myBufSize; + bphb.in.srcAddr = (uintptr_t) myBuf; + Backdoor_HbOut(&bphb); + if ((bphb.in.bx.halfs.high & MESSAGE_STATUS_SUCCESS) == 0) { + if ((bphb.in.bx.halfs.high & MESSAGE_STATUS_CPT) != 0) { + /* A checkpoint occurred. Retry the operation. --hpreg */ + goto retry; + } + + MESSAGE_LOG(("Message: Unable to send a message over the " + "communication channel %u\n", chan->id)); + return FALSE; + } + } + } else { + /* + * High-bandwidth backdoor port not supported. Send the message, 4 bytes + * at a time. --hpreg + */ + + for (;;) { + if (myBufSize == 0) { + /* We are done */ + break; + } + + /* IN: Type */ + bp.in.cx.halfs.high = MESSAGE_TYPE_SENDPAYLOAD; + /* IN: Id and cookie */ + bp.in.dx.halfs.high = chan->id; + bp.in.si.word = chan->cookieHigh; + bp.in.di.word = chan->cookieLow; + /* IN: Piece of message */ + /* + * Beware in case we are not allowed to read extra bytes beyond the + * end of the buffer. + */ + switch (myBufSize) { + case 1: + bp.in.size = myBuf[0]; + myBufSize -= 1; + break; + case 2: + bp.in.size = myBuf[0] | myBuf[1] << 8; + myBufSize -= 2; + break; + case 3: + bp.in.size = myBuf[0] | myBuf[1] << 8 | myBuf[2] << 16; + myBufSize -= 3; + break; + default: + bp.in.size = *(const uint32 *)myBuf; + myBufSize -= 4; + break; + } + + bp.in.cx.halfs.low = BDOOR_CMD_MESSAGE; + Backdoor(&bp); + + /* OUT: Status */ + if ((bp.in.cx.halfs.high & MESSAGE_STATUS_SUCCESS) == 0) { + if ((bp.in.cx.halfs.high & MESSAGE_STATUS_CPT) != 0) { + /* A checkpoint occurred. Retry the operation. --hpreg */ + goto retry; + } + + MESSAGE_LOG(("Message: Unable to send a message over the " + "communication channel %u\n", chan->id)); + return FALSE; + } + + myBuf += 4; + } + } + + MESSAGE_LOG(("Message: Sent a message over the communication channel %u\n", + chan->id)); + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * MessageBackdoor_Receive -- + * + * If vmware has posted a message for this channel, retrieve it + * + * Result: + * TRUE on success (bufSize is 0 if there is no message) + * FALSE on failure + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +MessageBackdoor_Receive(Message_Channel *chan, // IN/OUT + unsigned char **buf, // OUT + size_t *bufSize) // OUT +{ + Backdoor_proto bp; + size_t myBufSize; + unsigned char *myBuf; + +retry: + /* + * Is there a message waiting for our retrieval? + */ + + /* IN: Type */ + bp.in.cx.halfs.high = MESSAGE_TYPE_RECVSIZE; + /* IN: Id and cookie */ + bp.in.dx.halfs.high = chan->id; + bp.in.si.word = chan->cookieHigh; + bp.in.di.word = chan->cookieLow; + + bp.in.cx.halfs.low = BDOOR_CMD_MESSAGE; + Backdoor(&bp); + + /* OUT: Status */ + if ((bp.in.cx.halfs.high & MESSAGE_STATUS_SUCCESS) == 0) { + MESSAGE_LOG(("Message: Unable to poll for messages over the " + "communication channel %u\n", chan->id)); + return FALSE; + } + + if ((bp.in.cx.halfs.high & MESSAGE_STATUS_DORECV) == 0) { + /* No message to retrieve */ + *bufSize = 0; + return TRUE; + } + + /* + * Receive the size. + */ + + /* OUT: Type */ + if (bp.in.dx.halfs.high != MESSAGE_TYPE_SENDSIZE) { + MESSAGE_LOG(("Message: Protocol error. Expected a " + "MESSAGE_TYPE_SENDSIZE request from vmware\n")); + return FALSE; + } + + /* OUT: Size */ + myBufSize = bp.out.bx.word; + + /* + * Allocate an extra byte for a trailing NUL character. The code that will + * deal with this message may not know about binary strings, and may expect + * a C string instead. --hpreg + */ + if (myBufSize + 1 > chan->inAlloc) { + myBuf = (unsigned char *)realloc(chan->in, myBufSize + 1); + if (myBuf == NULL) { + MESSAGE_LOG(("Message: Not enough memory to receive a message over " + "the communication channel %u\n", chan->id)); + goto error_quit; + } + + chan->in = myBuf; + chan->inAlloc = myBufSize + 1; + } + *bufSize = myBufSize; + myBuf = *buf = chan->in; + + if (bp.in.cx.halfs.high & MESSAGE_STATUS_HB) { + /* + * High-bandwidth backdoor port supported. Receive the message in one + * backdoor operation. --hpreg + */ + + if (myBufSize) { + Backdoor_proto_hb bphb; + + bphb.in.bx.halfs.low = BDOORHB_CMD_MESSAGE; + bphb.in.bx.halfs.high = MESSAGE_STATUS_SUCCESS; + bphb.in.dx.halfs.high = chan->id; + bphb.in.srcAddr = chan->cookieHigh; + bphb.in.bp.word = chan->cookieLow; + bphb.in.size = myBufSize; + bphb.in.dstAddr = (uintptr_t) myBuf; + Backdoor_HbIn(&bphb); + if ((bphb.in.bx.halfs.high & MESSAGE_STATUS_SUCCESS) == 0) { + if ((bphb.in.bx.halfs.high & MESSAGE_STATUS_CPT) != 0) { + /* A checkpoint occurred. Retry the operation. --hpreg */ + goto retry; + } + + MESSAGE_LOG(("Message: Unable to receive a message over the " + "communication channel %u\n", chan->id)); + goto error_quit; + } + } + } else { + /* + * High-bandwidth backdoor port not supported. Receive the message, 4 + * bytes at a time. --hpreg + */ + + for (;;) { + if (myBufSize == 0) { + /* We are done */ + break; + } + + /* IN: Type */ + bp.in.cx.halfs.high = MESSAGE_TYPE_RECVPAYLOAD; + /* IN: Id and cookie */ + bp.in.dx.halfs.high = chan->id; + bp.in.si.word = chan->cookieHigh; + bp.in.di.word = chan->cookieLow; + /* IN: Status for the previous request (that succeeded) */ + bp.in.size = MESSAGE_STATUS_SUCCESS; + + bp.in.cx.halfs.low = BDOOR_CMD_MESSAGE; + Backdoor(&bp); + + /* OUT: Status */ + if ((bp.in.cx.halfs.high & MESSAGE_STATUS_SUCCESS) == 0) { + if ((bp.in.cx.halfs.high & MESSAGE_STATUS_CPT) != 0) { + /* A checkpoint occurred. Retry the operation. --hpreg */ + goto retry; + } + + MESSAGE_LOG(("Message: Unable to receive a message over the " + "communication channel %u\n", chan->id)); + goto error_quit; + } + + /* OUT: Type */ + if (bp.in.dx.halfs.high != MESSAGE_TYPE_SENDPAYLOAD) { + MESSAGE_LOG(("Message: Protocol error. Expected a " + "MESSAGE_TYPE_SENDPAYLOAD from vmware\n")); + goto error_quit; + } + + /* OUT: Piece of message */ + /* + * Beware in case we are not allowed to write extra bytes beyond the + * end of the buffer. --hpreg + */ + switch (myBufSize) { + case 1: + myBuf[0] = bp.out.bx.word & 0xff; + myBufSize -= 1; + break; + case 2: + myBuf[0] = bp.out.bx.word & 0xff; + myBuf[1] = (bp.out.bx.word >> 8) & 0xff; + myBufSize -= 2; + break; + case 3: + myBuf[0] = bp.out.bx.word & 0xff; + myBuf[1] = (bp.out.bx.word >> 8) & 0xff; + myBuf[2] = (bp.out.bx.word >> 16) & 0xff; + myBufSize -= 3; + break; + default: + *(uint32 *)myBuf = bp.out.bx.word; + myBufSize -= 4; + break; + } + + myBuf += 4; + } + } + + /* Write a trailing NUL just after the message. --hpreg */ + chan->in[*bufSize] = '\0'; + + /* IN: Type */ + bp.in.cx.halfs.high = MESSAGE_TYPE_RECVSTATUS; + /* IN: Id and cookie */ + bp.in.dx.halfs.high = chan->id; + bp.in.si.word = chan->cookieHigh; + bp.in.di.word = chan->cookieLow; + /* IN: Status for the previous request (that succeeded) */ + bp.in.size = MESSAGE_STATUS_SUCCESS; + + bp.in.cx.halfs.low = BDOOR_CMD_MESSAGE; + Backdoor(&bp); + + /* OUT: Status */ + if ((bp.in.cx.halfs.high & MESSAGE_STATUS_SUCCESS) == 0) { + if ((bp.in.cx.halfs.high & MESSAGE_STATUS_CPT) != 0) { + /* A checkpoint occurred. Retry the operation. --hpreg */ + goto retry; + } + + MESSAGE_LOG(("Message: Unable to receive a message over the " + "communication channel %u\n", chan->id)); + goto error_quit; + } + + return TRUE; + +error_quit: + /* IN: Type */ + if (myBufSize == 0) { + bp.in.cx.halfs.high = MESSAGE_TYPE_RECVSTATUS; + } else { + bp.in.cx.halfs.high = MESSAGE_TYPE_RECVPAYLOAD; + } + /* IN: Id and cookie */ + bp.in.dx.halfs.high = chan->id; + bp.in.si.word = chan->cookieHigh; + bp.in.di.word = chan->cookieLow; + /* IN: Status for the previous request (that failed) */ + bp.in.size = 0; + + bp.in.cx.halfs.low = BDOOR_CMD_MESSAGE; + Backdoor(&bp); + + /* OUT: Status */ + if ((bp.in.cx.halfs.high & MESSAGE_STATUS_SUCCESS) == 0) { + MESSAGE_LOG(("Message: Unable to signal an error of reception over the " + "communication channel %u\n", chan->id)); + return FALSE; + } + + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * MessageBackdoor_Close -- + * + * Close a communication channel + * + * Result: + * TRUE on success, the channel is destroyed + * FALSE on failure + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +MessageBackdoor_Close(Message_Channel *chan) // IN/OUT +{ + Backdoor_proto bp; + Bool ret = TRUE; + + /* IN: Type */ + bp.in.cx.halfs.high = MESSAGE_TYPE_CLOSE; + /* IN: Id and cookie */ + bp.in.dx.halfs.high = chan->id; + bp.in.si.word = chan->cookieHigh; + bp.in.di.word = chan->cookieLow; + + bp.in.cx.halfs.low = BDOOR_CMD_MESSAGE; + Backdoor(&bp); + + /* OUT: Status */ + if ((bp.in.cx.halfs.high & MESSAGE_STATUS_SUCCESS) == 0) { + MESSAGE_LOG(("Message: Unable to close the communication channel %u\n", + chan->id)); + ret = FALSE; + } else { + MESSAGE_LOG(("Message: Communication channel %u closed\n", chan->id)); + } + + free(chan->in); + chan->in = NULL; + + free(chan); + return ret; +} + +#ifdef __cplusplus +} +#endif diff --git a/open-vm-tools/modules/solaris/vmhgfs/module.c b/open-vm-tools/modules/solaris/vmhgfs/module.c index 04dbd4bd2..62cef6b66 100644 --- a/open-vm-tools/modules/solaris/vmhgfs/module.c +++ b/open-vm-tools/modules/solaris/vmhgfs/module.c @@ -26,19 +26,6 @@ * For reference: Solaris Structures * ================================= * - * Driver module struct hierarchy: - * ------------------------------- - * - * (Solaris 9 & 10) - * - * modlinkage --> modldrv --> dev_ops --> cb_ops - * - * - modlinkage & modldrv: module loading/unloading - * - dev_ops: operations for driver configuration (attach(), detach(), etc) - * - cb_ops: operations on device, entry points to character device (open(), - * close(), etc) - * - * * VFS module struct hierarchy: * ---------------------------- * @@ -81,24 +68,12 @@ * * (Build 58 contains the same structure named vfsdev_v3) * - * - * Combination: - * ------------ - * - * In this file we are placing both modldrv and modlfs inside one modlinkage - * structure. This is achieved by placing pointers to both of these structures - * in modlinkage's ml_linkage arary of pointers to module structures. This is - * done so the two modules can have shared state and to prevent dependency - * issues between them. This looks like: - * - * modlinkage --> modldrv --> as above - * \ - * -> modlfs --> as above - * */ #include "hgfsSolaris.h" #include "module.h" +#include "vnode.h" +#include "filesystem.h" #include "debug.h" @@ -108,63 +83,6 @@ #define HGFS_VFSSW_FLAGS 0 -/* - * Structures - */ - -/* - * Device driver structures - */ - -/* Structure defining operations on device */ -static struct cb_ops HgfsDevCBOps = { - HgfsDevOpen, /* open(9E) */ - HgfsDevClose, /* close(9E) */ - nodev, /* strategy(9E) */ - nodev, /* print(9E) */ - nodev, /* dump(9E) */ - HgfsDevRead, /* read(9E) */ - HgfsDevWrite, /* write(9E) */ - nodev, /* ioctl(9E) */ - nodev, /* devmap(9E) */ - nodev, /* mmap(9E) */ - nodev, /* segmap(9E) */ - HgfsDevChpoll, /* chpoll(9E) */ - ddi_prop_op, /* prop_op(9E) */ - NULL, /* streamtab(9S) */ - (D_NEW | D_MP), /* cb_flag: D_NEW because this is a new-style - driver and D_MP is required */ - CB_REV, /* cb_rev */ - nodev, /* aread(9E) */ - nodev, /* awrite(9E) */ -}; - - -/* Structure defining operations for driver */ -static struct dev_ops HgfsDevOps = { - DEVO_REV, /* devo_rev */ - 0, /* devo_refcnt */ - HgfsDevGetinfo, /* getinfo(9E) */ - nulldev, /* identify(9E) */ - nulldev, /* probe(9E) */ - HgfsDevAttach, /* attach(9E) */ - HgfsDevDetach, /* detach(9E) */ - nodev, /* devo_reset */ - &HgfsDevCBOps, /* devo_cb_ops (points to struct cb_ops) */ - NULL, /* devo_bus_ops */ - NULL /* power(9E) */ -}; - - -/* Driver module linkage structure */ -static struct modldrv HgfsDevModldrv = { - &mod_driverops, /* drv_modops (don't change) */ - "HGFS Device Interface", /* drv_linkinfo */ - &HgfsDevOps /* drv_dev_ops (points to struct dev_ops) */ -}; - - - /* * Filesystem structures */ @@ -234,17 +152,11 @@ static struct modlfs HgfsModlfs = { /* - * Modlinkage shared by device driver and filesystem. + * Modlinkage containing filesystem. */ static struct modlinkage HgfsModlinkage = { MODREV_1, /* Module revision: must be MODREV_1 */ - /* - * We put pointers to /both/ modules in the following NULL terminated array - * of pointers (ml_linkage[]). This is what causes both modules to be - * loaded into the kernel when the single driver is added. - */ { - &HgfsDevModldrv, /* Driver module structure */ &HgfsModlfs, /* FS module structure */ NULL, /* NULL terminator */ } @@ -281,56 +193,14 @@ _init(void) DEBUG(VM_DEBUG_ENTRY, "_init() for HGFS.\n"); - /* - * Initialize hgfsInstance so filesystem knows device has not been attached - * yet, and hgfsType so we know whether to free the vfsops in _fini(). - */ - hgfsInstance = HGFS_INSTANCE_UNINITIALIZED; - hgfsType = HGFS_TYPE_UNINITIALIZED; - - /* - * For reference: - * - * Solaris provides an interface to simplify allocation and deallocation of - * per-driver-instance state through the DDI Soft State functions. - * - * - ddi_soft_state_init() takes a double pointer and uses it as a handle - * to access the various per-instance state structures. This handle will - * be passed to the other DDI Soft State functions to indicate which - * state list to operate on. The second argument is the amount of memory - * to allocate for each state structure, and the last is the expected number - * of instances of the driver (number of state structures). - * - * - ddi_soft_state_zalloc() and ddi_soft_state_free() allocate and free a - * soft state structure respectively. The first argument is the - * handle pointer (see above) and the second is the item number - * associated with this instance. - * - * - ddi_get_soft_state_free() takes a handle and item and returns a - * pointer to the state structure that was allocated with - * ddi_soft_state_zalloc() for that item number. - * - * See man ddi_soft_state_init for more information. - * - */ - ret = ddi_soft_state_init(&superInfoHead, - sizeof (HgfsSuperInfo), HGFS_EXPECTED_INSTANCES); - if (ret) { - goto error; - } - ret = mod_install(&HgfsModlinkage); if (ret) { - goto error; + cmn_err(HGFS_ERROR, "could not install HGFS module.\n"); + return ret; } DEBUG(VM_DEBUG_DONE, "_init() done.\n"); return 0; - -error: - cmn_err(HGFS_ERROR, "could not install HGFS module.\n"); - ddi_soft_state_fini(&superInfoHead); - return ret; } @@ -353,21 +223,17 @@ error: int _fini(void) { - HgfsSuperInfo *sip; int error; DEBUG(VM_DEBUG_ENTRY, "_fini() for HGFS.\n"); - sip = ddi_get_soft_state(superInfoHead, hgfsInstance); - /* - * Make sure the device is closed and the fs is not mounted if we have - * a valid superinfo pointer. + * Make sure that the fs is not mounted. */ - if (sip && (sip->devOpen || sip->vfsp)) { - DEBUG(VM_DEBUG_FAIL, "Cannot unload module because either device" - "is open or file system is mounted\n"); - return -1; + if (HgfsGetSuperInfo()) { + DEBUG(VM_DEBUG_FAIL, + "Cannot unload module because file system is mounted\n"); + return EBUSY; } error = mod_remove(&HgfsModlinkage); @@ -376,15 +242,8 @@ _fini(void) return error; } -#if HGFS_VFS_VERSION > 2 - if (hgfsType != HGFS_TYPE_UNINITIALIZED) { - vfs_freevfsops_by_type(hgfsType); - } - if (sip && sip->vnodeOps) { - vn_freevnodeops(sip->vnodeOps); - } -#endif - ddi_soft_state_fini(&superInfoHead); /* void return */ + HgfsFreeVnodeOps(); + HgfsFreeVfsOps(); DEBUG(VM_DEBUG_DONE, "_fini() done.\n"); return 0; diff --git a/open-vm-tools/modules/solaris/vmhgfs/module.h b/open-vm-tools/modules/solaris/vmhgfs/module.h index 9d498af27..1dab9d6d8 100644 --- a/open-vm-tools/modules/solaris/vmhgfs/module.h +++ b/open-vm-tools/modules/solaris/vmhgfs/module.h @@ -25,28 +25,6 @@ #include "filesystem.h" -/* - * Device driver EXTERNs - */ - -/* Functions for the cb_ops structure (see driver.c) */ -EXTERN int HgfsDevOpen(dev_t *devp, int flag, int otyp, cred_t *credp); -EXTERN int HgfsDevClose(dev_t dev, int flag, int otyp, cred_t *credp); -EXTERN int HgfsDevRead(dev_t dev, uio_t *uiop, cred_t *credp); -EXTERN int HgfsDevWrite(dev_t dev, uio_t *uiop, cred_t *credp); -EXTERN int HgfsDevChpoll(dev_t dev, short int events, int anyyet, - short int *reventsp, struct pollhead **phpp); - -/* Functions for the dev_ops structure (see driver.c) */ -EXTERN int HgfsDevGetinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, - void *arg, void **result); -EXTERN int HgfsDevAttach(dev_info_t *dip, ddi_attach_cmd_t cmd); -EXTERN int HgfsDevDetach(dev_info_t *dip, ddi_detach_cmd_t cmd); - -/* Struct needed in struct modldrv */ -EXTERN struct mod_ops mod_driverops; - - /* * Filesystem EXTERNs */ diff --git a/open-vm-tools/modules/solaris/vmhgfs/request.c b/open-vm-tools/modules/solaris/vmhgfs/request.c index 58c5d1f06..644a437de 100644 --- a/open-vm-tools/modules/solaris/vmhgfs/request.c +++ b/open-vm-tools/modules/solaris/vmhgfs/request.c @@ -56,22 +56,8 @@ HgfsInitRequestList(HgfsSuperInfo *sip) // IN: Pointer to superinfo structure DEBUG(VM_DEBUG_REQUEST, "HgfsInitRequestList().\n"); ASSERT(sip); - /* - * We ASSERT() this because this function must be called before the device is - * opened to ensure the request lists are ready to be used by the - * filesystem. (The filesystem won't mount unless the device is open.) - * By ASSERT()ing this condition we also obviate the need to acquire the - * mutex when adding requests to reqFreeList in the for loop below, since we - * are guaranteed (in practice) to have exclusive access. - */ - ASSERT(!sip->devOpen); - /* None of these fail */ - - /* Initialize pending request list */ - DblLnkLst_Init(&sip->reqList); mutex_init(&sip->reqMutex, NULL, MUTEX_DRIVER, NULL); - cv_init(&sip->reqCondVar, NULL, CV_DRIVER, NULL); /* Initialize free request list */ DblLnkLst_Init(&sip->reqFreeList); @@ -88,7 +74,6 @@ HgfsInitRequestList(HgfsSuperInfo *sip) // IN: Pointer to superinfo structure for (i = 0; i < ARRAYSIZE(requestPool); i++) { requestPool[i].id = i; requestPool[i].state = HGFS_REQ_UNUSED; - mutex_init(&requestPool[i].stateLock, NULL, MUTEX_DRIVER, NULL); DblLnkLst_Init(&requestPool[i].listNode); DblLnkLst_LinkLast(&sip->reqFreeList, &requestPool[i].listNode); @@ -104,16 +89,14 @@ HgfsInitRequestList(HgfsSuperInfo *sip) // IN: Pointer to superinfo structure * * HgfsCancelAllRequests -- * - * Cancels all pending requests by removing them from the request list and - * waking up the clients waiting for replies. Also cancels requests that - * have already been sent to guestd (and are not on the pending list) and - * are either abandoned or still have clients waiting for them. + * Cancels all pending (SUBMITTED) requests by signalling the transport + * code that they should be aborted. * * Results: * None. * * Side effects: - * The pending request list is empty and all requests are in the free list. + * Threads waiting on requests are woken up with error conditions. * *---------------------------------------------------------------------------- */ @@ -122,71 +105,23 @@ void HgfsCancelAllRequests(HgfsSuperInfo *sip) // IN: Superinfo containing // request list { - DblLnkLst_Links *currNode, *nextNode; - HgfsReq *currRequest; int i; DEBUG(VM_DEBUG_REQUEST, "HgfsCancelAllRequests().\n"); ASSERT(sip); + ASSERT(mutex_owned(&sip->reqMutex)); - /* Traverse list to make sure we cancel all pending requests on the list. */ - mutex_enter(&sip->reqMutex); - - DEBUG(VM_DEBUG_REQUEST, "HgfsCancelAllRequests(): traversing pending request list.\n"); - - for (currNode = HGFS_REQ_LIST_HEAD_NODE(sip); - currNode != &sip->reqList; - currNode = nextNode) { - - /* Get the next element while we still have a pointer to it. */ - nextNode = currNode->next; - - DblLnkLst_Unlink1(currNode); - currRequest = DblLnkLst_Container(currNode, HgfsReq, listNode); - - if (HgfsReqGetState(currRequest) == HGFS_REQ_ABANDONED) { - /* - * If the client is no longer waiting, clean up for it by destroying - * the request. - */ - HgfsDestroyReq(sip, currRequest); - } else { - /* - * ... otherwise indicate an error and wakeup the client. Note that - * we can't call HgfsWakeWaitingClient() because we already hold the - * reqMutex lock. - * - * Also, make sure that all non-ABANDONED requests are SUBMITTED. - */ - ASSERT(HgfsReqGetState(currRequest) == HGFS_REQ_SUBMITTED); - - HgfsReqSetState(currRequest, HGFS_REQ_ERROR); - cv_signal(&currRequest->condVar); - } - } - - DEBUG(VM_DEBUG_REQUEST, "HgfsCancelAllRequests(): traversing request pool.\n"); - - /* - * Now look for abandoned and submitted requests that are in the pool but - * not on the pending list (because they were already sent to guestd). - */ for (i = 0; i < ARRAYSIZE(requestPool); i++) { /* - * As above, abandoned requests are cleaned up and we wake up the clients - * for submitted requests. + * Signal that all submitted requests need to be cancelled. + * We expect that transport implementation wakes up processes + * waiting on requests */ - if (HgfsReqGetState(&requestPool[i]) == HGFS_REQ_ABANDONED) { - HgfsDestroyReq(sip, &requestPool[i]); - } else if (HgfsReqGetState(&requestPool[i]) == HGFS_REQ_SUBMITTED) { - HgfsReqSetState(&requestPool[i], HGFS_REQ_ERROR); - cv_signal(&requestPool[i].condVar); - } + if (requestPool[i].state == HGFS_REQ_SUBMITTED) + sip->cancelRequest(&requestPool[i]); } - mutex_exit(&sip->reqMutex); - DEBUG(VM_DEBUG_REQUEST, "HgfsCancelAllRequests() done.\n"); } @@ -275,22 +210,22 @@ HgfsGetNewReq(HgfsSuperInfo *sip) // IN: Superinfo containing free list HgfsDebugPrintReq("HgfsGetNewReq", newReq); /* Failure of these indicates error in program's logic */ - ASSERT(newReq && (HgfsReqGetState(newReq) == HGFS_REQ_UNUSED)); + ASSERT(newReq && newReq->state == HGFS_REQ_UNUSED); /* Take request off the free list and indicate it has been ALLOCATED */ DblLnkLst_Unlink1(&newReq->listNode); - HgfsReqSetState(newReq, HGFS_REQ_ALLOCATED); + newReq->state = HGFS_REQ_ALLOCATED; /* Clear packet of request before allocating to clients. */ bzero(newReq->packet, sizeof newReq->packet); + DEBUG(VM_DEBUG_LIST, "Dequeued from free list: %s", newReq->packet); + HgfsDebugPrintReqList(&sip->reqFreeList); + out: mutex_exit(&sip->reqFreeMutex); - DEBUG(VM_DEBUG_LIST, "Dequeued from free list: %s", newReq->packet); - HgfsDebugPrintReqList(&sip->reqFreeList); DEBUG(VM_DEBUG_REQUEST, "HgfsGetNewReq() done.\n"); - return newReq; } @@ -312,24 +247,24 @@ out: *---------------------------------------------------------------------------- */ -INLINE void +void HgfsDestroyReq(HgfsSuperInfo *sip, // IN: Superinfo containing free list HgfsReq *oldReq) // IN: Request to destroy { DEBUG(VM_DEBUG_ENTRY, "HgfsDestroyReq().\n"); /* XXX This should go away later, just for testing */ - if (HgfsReqGetState(oldReq) != HGFS_REQ_COMPLETED) { + if (oldReq->state != HGFS_REQ_COMPLETED) { DEBUG(VM_DEBUG_ALWAYS, "HgfsDestroyReq() (oldReq state=%d).\n", - HgfsReqGetState(oldReq)); + oldReq->state); } ASSERT(sip); ASSERT(oldReq); /* Failure of this check indicates an error in program logic */ - ASSERT((HgfsReqGetState(oldReq) == HGFS_REQ_COMPLETED) || - (HgfsReqGetState(oldReq) == HGFS_REQ_ABANDONED) || - (HgfsReqGetState(oldReq) == HGFS_REQ_ERROR)); + ASSERT(oldReq->state == HGFS_REQ_COMPLETED || + oldReq->state == HGFS_REQ_ABANDONED || + oldReq->state == HGFS_REQ_ERROR); /* * To make the request available for other clients we change its state to @@ -337,7 +272,7 @@ HgfsDestroyReq(HgfsSuperInfo *sip, // IN: Superinfo containing free list */ mutex_enter(&sip->reqFreeMutex); - HgfsReqSetState(oldReq, HGFS_REQ_UNUSED); + oldReq->state = HGFS_REQ_UNUSED; DblLnkLst_LinkLast(&sip->reqFreeList, &oldReq->listNode); /* Wake up clients waiting for a request structure */ cv_signal(&sip->reqFreeCondVar); @@ -353,9 +288,10 @@ HgfsDestroyReq(HgfsSuperInfo *sip, // IN: Superinfo containing free list /* *---------------------------------------------------------------------------- * - * HgfsEnqueueRequest -- + * HgfsSendRequest -- * - * Adds the provided request to the end of the request list. + * Sends request for execution. The exact details depend on transport used + * to communicate with the host. * * Note: this assumes it is called with the list lock held because often * callers will need this function to be atomic with other operations. @@ -370,84 +306,24 @@ HgfsDestroyReq(HgfsSuperInfo *sip, // IN: Superinfo containing free list *---------------------------------------------------------------------------- */ -INLINE void -HgfsEnqueueRequest(HgfsSuperInfo *sip, // IN: Superinfo containing request list - HgfsReq *newReq) // IN: Request to enqueue +int +HgfsSendRequest(HgfsSuperInfo *sip, // IN: Superinfo sructure with methods + HgfsReq *req) // IN/OUT: Request to be sent { - DEBUG(VM_DEBUG_REQUEST, "HgfsEnqueueRequest().\n"); + int ret; ASSERT(sip); - ASSERT(newReq); + ASSERT(req); /* Failure of this check indicates error in program logic */ - ASSERT(HgfsReqGetState(newReq) == HGFS_REQ_ALLOCATED); - - /* - * This simply changes the state and places the request on the pending - * request list. Signaling the (potentially sleeping) device is handled by - * the caller (HgfsSubmitRequest()). - */ - HgfsReqSetState(newReq, HGFS_REQ_SUBMITTED); - DblLnkLst_LinkLast(&sip->reqList, &newReq->listNode); - - DEBUG(VM_DEBUG_LIST, "Enqueued on pending list: %s", newReq->packet); - HgfsDebugPrintReqList(&sip->reqList); - DEBUG(VM_DEBUG_REQUEST, "HgfsEnqueueRequest() done.\n"); -} + ASSERT(req->state == HGFS_REQ_ALLOCATED); - -/* - *---------------------------------------------------------------------------- - * - * HgfsDequeueRequest -- - * - * Removes the next request from the list. - * - * Note: this assumes it is called with the list lock held because often - * callers will need this function to be atomic with other operations. - * - * Results: - * Returns a pointer to the request removed from the list, or NULL if the - * list is empty. - * - * Side effects: - * Request is removed from the pending request list. - * - * - *---------------------------------------------------------------------------- - */ - -INLINE HgfsReq * -HgfsDequeueRequest(HgfsSuperInfo *sip) // IN: Superinfo containing request list -{ - HgfsReq *nextReq; - - DEBUG(VM_DEBUG_REQUEST, "HgfsDequeueRequest().\n"); - - ASSERT(sip); - - if (HgfsListIsEmpty(&sip->reqList)) { - return NULL; + req->state = HGFS_REQ_SUBMITTED; + ret = sip->sendRequest(req); + if (ret) { + req->state = HGFS_REQ_ERROR; } - nextReq = HGFS_REQ_LIST_HEAD(sip); - - /* - * nextReq should never be NULL since we are called with the lock held and - * we just checked if the list was empty. The request's state should only - * be SUBMITTED or ABANDONED. (Errors occurring while copying the request - * to guestd happen after removal from this list.) - */ - ASSERT(nextReq); - ASSERT(HgfsReqGetState(nextReq) == HGFS_REQ_SUBMITTED || - HgfsReqGetState(nextReq) == HGFS_REQ_ABANDONED); - - DblLnkLst_Unlink1(&nextReq->listNode); - - DEBUG(VM_DEBUG_LIST, "Dequeued from pending list: %s", nextReq->packet); - HgfsDebugPrintReqList(&sip->reqList); - DEBUG(VM_DEBUG_REQUEST, "HgfsDequeueRequest() done.\n"); - - return nextReq; + return ret; } @@ -489,77 +365,3 @@ HgfsWakeWaitingClient(HgfsSuperInfo *sip, // IN: Superinfo with request mu // DEBUG(VM_DEBUG_REQUEST, "HgfsWakeWaitingClient() done.\n"); } - -/* - * These two state functions will help prevent forgetting to lock. - */ - - -/* - *---------------------------------------------------------------------------- - * HgfsSetState -- - * - * Sets the state of the request. - * - * Results: - * Returns void. - * - * Side effects: - * The state of the request is modified. - * - *---------------------------------------------------------------------------- - */ - -INLINE void -HgfsReqSetState(HgfsReq *req, // IN: Request to alter state of - HgfsReqState state) // IN: State to set request to -{ -// DEBUG(VM_DEBUG_REQUEST, "HgfsReqSetState().\n"); - - ASSERT(req); - - mutex_enter(&req->stateLock); - - req->state = state; - - mutex_exit(&req->stateLock); - -// DEBUG(VM_DEBUG_REQUEST, "HgfsReqSetState() done.\n"); -} - - - -/* - *---------------------------------------------------------------------------- - * - * HgfsReqGetState -- - * - * Retrieves state of provided request. - * - * Results: - * Returns state of request. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------------- - */ - -INLINE HgfsReqState -HgfsReqGetState(HgfsReq *req) // IN: Request to retrieve state of -{ - HgfsReqState state; - - DEBUG(VM_DEBUG_REQUEST, "HgfsReqGetState().\n"); - - ASSERT(req); - - mutex_enter(&req->stateLock); - - state = req->state; - - mutex_exit(&req->stateLock); - - DEBUG(VM_DEBUG_REQUEST, "HgfsReqGetState() done.\n"); - return state; -} diff --git a/open-vm-tools/modules/solaris/vmhgfs/request.h b/open-vm-tools/modules/solaris/vmhgfs/request.h index d19766523..6d081bc7b 100644 --- a/open-vm-tools/modules/solaris/vmhgfs/request.h +++ b/open-vm-tools/modules/solaris/vmhgfs/request.h @@ -57,11 +57,8 @@ void HgfsInitRequestList(HgfsSuperInfo *sip); void HgfsCancelAllRequests(HgfsSuperInfo *sip); INLINE Bool HgfsListIsEmpty(DblLnkLst_Links *listAnchor); HgfsReq *HgfsGetNewReq(HgfsSuperInfo *sip); -INLINE void HgfsDestroyReq(HgfsSuperInfo *sip, HgfsReq *oldReq); -INLINE void HgfsEnqueueRequest(HgfsSuperInfo *sip, HgfsReq *newReq); -INLINE HgfsReq *HgfsDequeueRequest(HgfsSuperInfo *sip); +void HgfsDestroyReq(HgfsSuperInfo *sip, HgfsReq *oldReq); +int HgfsSendRequest(HgfsSuperInfo *sip, HgfsReq *req); INLINE void HgfsWakeWaitingClient(HgfsSuperInfo *sip, HgfsReq *req); -INLINE void HgfsReqSetState(HgfsReq *req, HgfsReqState state); -INLINE HgfsReqState HgfsReqGetState(HgfsReq *req); #endif /* __REQUEST_H_ */ diff --git a/open-vm-tools/modules/solaris/vmhgfs/rpcout.c b/open-vm-tools/modules/solaris/vmhgfs/rpcout.c new file mode 100644 index 000000000..0c2561cab --- /dev/null +++ b/open-vm-tools/modules/solaris/vmhgfs/rpcout.c @@ -0,0 +1,464 @@ +/********************************************************* + * Copyright (C) 2004 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * rpcout.c -- + * + * Remote Procedure Call between VMware and guest applications + * C implementation. + * + * This module contains implements the out (guest=>host) direction only. + * The in and out modules are separate since some applications (e.g. + * drivers that want to do RPC-based logging) only want/need/can have the + * out direction. + */ + + +#if defined(__KERNEL__) || defined(_KERNEL) || defined(KERNEL) +# include "kernelStubs.h" +#else +# include +# include +# include +# include +# include "str.h" +# include "debug.h" +#endif + +#include "vmware.h" +#include "rpcout.h" +#include "message.h" + + +/* + * The RpcOut object + */ + +struct RpcOut { + Message_Channel *channel; +}; + + +/* + *----------------------------------------------------------------------------- + * + * RpcOut_Construct -- + * + * Constructor for the RpcOut object + * + * Results: + * New RpcOut object. + * + * Side effects: + * Allocates memory. + * + *----------------------------------------------------------------------------- + */ + +RpcOut * +RpcOut_Construct(void) +{ + return (RpcOut *)calloc(1, sizeof(RpcOut)); +} + + +/* + *----------------------------------------------------------------------------- + * + * RpcOut_Destruct -- + * + * Destructor for the RpcOut object. + * + * Results: + * None. + * + * Side effects: + * Frees RpcOut object memory. + * + *----------------------------------------------------------------------------- + */ + +void +RpcOut_Destruct(RpcOut *out) // IN +{ + ASSERT(out); + ASSERT(out->channel == NULL); + + free(out); +} + + +/* + *----------------------------------------------------------------------------- + * + * RpcOut_start -- + * + * Open the channel + * + * Result: + * TRUE on success + * FALSE on failure + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +RpcOut_start(RpcOut *out) // IN +{ + ASSERT(out); + ASSERT(out->channel == NULL); + out->channel = Message_Open(RPCI_PROTOCOL_NUM); + if (out->channel == NULL) { + Debug("RpcOut: couldn't open channel with RPCI protocol\n"); + return FALSE; + } + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * RpcOut_send -- + * + * Make VMware synchroneously execute a TCLO command + * + * Unlike the other send varieties, RpcOut_send requires that the + * caller pass non-NULL reply and repLen arguments. + * + * Result + * TRUE on success. 'reply' contains the result of the rpc + * FALSE on error. 'reply' will contain a description of the error + * + * In both cases, the caller should not free the reply. + * + * Side-effects + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +RpcOut_send(RpcOut *out, // IN + char const *request, // IN + size_t reqLen, // IN + char const **reply, // OUT + size_t *repLen) // OUT +{ + unsigned char *myReply; + size_t myRepLen; + Bool success; + + ASSERT(out); + + ASSERT(out->channel); + if (Message_Send(out->channel, (const unsigned char *)request, reqLen) == FALSE) { + *reply = "RpcOut: Unable to send the RPCI command"; + *repLen = strlen(*reply); + + return FALSE; + } + + if (Message_Receive(out->channel, &myReply, &myRepLen) == FALSE) { + *reply = "RpcOut: Unable to receive the result of the RPCI command"; + *repLen = strlen(*reply); + + return FALSE; + } + + if (myRepLen < 2 + || ( (success = strncmp((const char *)myReply, "1 ", 2) == 0) == FALSE + && strncmp((const char *)myReply, "0 ", 2))) { + *reply = "RpcOut: Invalid format for the result of the RPCI command"; + *repLen = strlen(*reply); + + return FALSE; + } + + *reply = ((const char *)myReply) + 2; + *repLen = myRepLen - 2; + + return success; +} + + +/* + *----------------------------------------------------------------------------- + * + * RpcOut_stop -- + * + * Close the channel + * + * Result + * TRUE on success + * FALSE on failure + * + * Side-effects + * Frees the result of the last command. + * + *----------------------------------------------------------------------------- + */ + +Bool +RpcOut_stop(RpcOut *out) // IN +{ + Bool status; + + ASSERT(out); + + status = TRUE; + + if (out->channel) { + /* Try to close the channel */ + if (Message_Close(out->channel) == FALSE) { + Debug("RpcOut: couldn't close channel\n"); + status = FALSE; + } + + out->channel = NULL; + } + + return status; +} + + +/* + *----------------------------------------------------------------------------- + * + * RpcOut_sendOne -- + * + * Make VMware execute a RPCI command + * + * VMware closes a channel when it detects that there has been no activity + * on it for a while. Because we do not know how often this program will + * make VMware execute a RPCI, we open/close one channel per RPCI command + * + * Return value: + * TRUE on success. '*reply' contains an allocated result of the rpc + * FALSE on error. '*reply' contains an allocated description of the error + * or NULL. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +RpcOut_sendOne(char **reply, // OUT: Result + size_t *repLen, // OUT: Length of the result + char const *reqFmt, // IN: RPCI command + ...) // Unspecified +{ + va_list args; + Bool status; + char *request; + size_t reqLen = 0; + + status = FALSE; + + /* Format the request string */ + va_start(args, reqFmt); + request = Str_Vasprintf(&reqLen, reqFmt, args); + va_end(args); + + /* + * If Str_Vasprintf failed, write NULL into the reply if the caller wanted + * a reply back. + */ + if (request == NULL) { + if (reply) { + *reply = NULL; + } + return FALSE; + } + + /* + * If the command doesn't contain a space, add one to the end to maintain + * compatibility with old VMXs. + * + * For a long time, the GuestRpc logic in the VMX was wired to expect a + * trailing space in every command, even commands without arguments. That is + * no longer true, but we must continue to add a trailing space because we + * don't know whether we're talking to an old or new VMX. + */ + if (strchr(request, ' ') == NULL) { + char *tmp; + + tmp = Str_Asprintf(NULL, "%s ", request); + free(request); + request = tmp; + + /* + * If Str_Asprintf failed, write NULL into reply if the caller wanted + * a reply back. + */ + if (request == NULL) { + if (reply != NULL) { + *reply = NULL; + } + return FALSE; + } + } + + status = RpcOut_SendOneRaw(request, reqLen, reply, repLen); + + free(request); + + return status; +} + + +/* + *----------------------------------------------------------------------------- + * + * RpcOut_SendOneRaw -- + * + * Make VMware execute a RPCI command + * + * VMware closes a channel when it detects that there has been no activity + * on it for a while. Because we do not know how often this program will + * make VMware execute a RPCI, we open/close one channel per RPCI command. + * + * This function sends a message over the backdoor without using + * any of the Str_ functions on the request buffer; Str_Asprintf() in + * particular uses FormatMessage on Win32, which corrupts some UTF-8 + * strings. Using this function directly instead of using RpcOut_SendOne() + * avoids these problems. + * + * If this is not an issue, you can use RpcOut_sendOne(), which has + * varargs. + * + * Note: It is the caller's responsibility to ensure that the RPCI command + * followed by a space appear at the start of the request buffer. See + * the command in RpcOut_sendOne for details. + * + * Return value: + * TRUE on success. '*reply' contains an allocated result of the rpc + * FALSE on error. '*reply' contains an allocated description of the + * error or NULL. + * + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +RpcOut_SendOneRaw(void *request, // IN: RPCI command + size_t reqLen, // IN: Size of request buffer + char **reply, // OUT: Result + size_t *repLen) // OUT: Length of the result +{ + Bool status; + RpcOut *out = NULL; + char const *myReply; + size_t myRepLen; + + status = FALSE; + + Debug("Rpci: Sending request='%s'\n", (char *)request); + out = RpcOut_Construct(); + if (out == NULL) { + myReply = "RpcOut: Unable to create the RpcOut object"; + myRepLen = strlen(myReply); + + goto sent; + } else if (RpcOut_start(out) == FALSE) { + myReply = "RpcOut: Unable to open the communication channel"; + myRepLen = strlen(myReply); + + goto sent; + } else if (RpcOut_send(out, request, reqLen, &myReply, &myRepLen) + == FALSE) { + /* We already have the description of the error */ + goto sent; + } + + status = TRUE; + +sent: + Debug("Rpci: Sent request='%s', reply='%s', len=%"FMTSZ"u, status=%d\n", + (char *)request, myReply, myRepLen, status); + + if (reply != NULL) { + /* + * If we got a non-NULL reply, make a copy of it, because the reply + * we got back is inside the channel buffer, which will get destroyed + * at the end of this function. + */ + if (myReply != NULL) { + /* + * We previously used strdup to duplicate myReply, but that + * breaks if you are sending binary (not string) data over the + * backdoor. Don't assume the data is a string. + * + * myRepLen is strlen(myReply), so we need an extra byte to + * cover the NUL terminator. + */ + *reply = malloc(myRepLen + 1); + if (*reply != NULL) { + memcpy(*reply, myReply, myRepLen); + /* + * The message layer already writes a trailing NUL but we might + * change that someday, so do it again here. + */ + (*reply)[myRepLen] = 0; + } + } else { + /* + * Our reply was NULL, so just pass the NULL back up to the caller. + */ + *reply = NULL; + } + + /* + * Only set the length if the caller wanted it and if we got a good + * reply. + */ + if (repLen != NULL && *reply != NULL) { + *repLen = myRepLen; + } + } + + if (out) { + if (RpcOut_stop(out) == FALSE) { + /* + * We couldn't stop the channel. Free anything we allocated, give our + * client a reply of NULL, and return FALSE. + */ + + if (reply != NULL) { + free(*reply); + *reply = NULL; + } + Debug("Rpci: unable to close the communication channel\n"); + status = FALSE; + } + + RpcOut_Destruct(out); + out = NULL; + } + + return status; +} + + diff --git a/open-vm-tools/modules/solaris/vmhgfs/rpcout.h b/open-vm-tools/modules/solaris/vmhgfs/rpcout.h new file mode 100644 index 000000000..8b026c420 --- /dev/null +++ b/open-vm-tools/modules/solaris/vmhgfs/rpcout.h @@ -0,0 +1,57 @@ +/********************************************************* + * Copyright (C) 2007 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * rpcout.h -- + * + * Remote Procedure Call between VMware and guest applications + * C declarations + */ + + +#ifndef __RPCOUT_H__ +# define __RPCOUT_H__ + +#include "vm_basic_types.h" + +#define RPCI_PROTOCOL_NUM 0x49435052 /* 'RPCI' ;-) */ + +typedef struct RpcOut RpcOut; + +RpcOut *RpcOut_Construct(void); +void RpcOut_Destruct(RpcOut *out); +Bool RpcOut_start(RpcOut *out); +Bool RpcOut_send(RpcOut *out, char const *request, size_t reqLen, + char const **reply, size_t *repLen); +Bool RpcOut_stop(RpcOut *out); + + +/* + * This is the only method needed to send a message to vmware for + * 99% of uses. I'm leaving the others defined here so people know + * they can be exported again if the need arises. [greg] + */ +Bool RpcOut_sendOne(char **reply, size_t *repLen, char const *reqFmt, ...); + +/* + * A new version of the above function that works with UTF-8 strings + * and other data that would be corrupted by Win32's FormatMessage + * function (which is used by RpcOut_sendOne()). + */ + +Bool RpcOut_SendOneRaw(void *request, size_t reqLen, char **reply, size_t *repLen); + +#endif /* __RPCOUT_H__ */ diff --git a/open-vm-tools/modules/solaris/vmhgfs/str.h b/open-vm-tools/modules/solaris/vmhgfs/str.h new file mode 100644 index 000000000..b79c313d1 --- /dev/null +++ b/open-vm-tools/modules/solaris/vmhgfs/str.h @@ -0,0 +1,202 @@ +/********************************************************* + * Copyright (C) 1998 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * str.h -- + * + * string wrapping functions + */ + +#ifndef _STR_H_ +#define _STR_H_ + +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_USERLEVEL +#define INCLUDE_ALLOW_VMCORE +#include "includeCheck.h" + +#if defined(GLIBC_VERSION_22) +#include +#elif defined(_WIN32) +#include +#elif __APPLE__ +#include +#endif +#include + +#include "vm_basic_types.h" + + +/* + * These platforms use bsd_vsnprintf(). + * This does not mean it has bsd_vsnwprintf(). + */ +#if defined _WIN32 && !defined STR_NO_WIN32_LIBS || \ + defined __linux__ && !defined N_PLAT_NLM || defined __APPLE__ +#define HAS_BSD_PRINTF 1 +#endif + +/* + * And these platforms/setups use bsd_vsnwprintf() + */ +#if (defined _WIN32 && !defined STR_NO_WIN32_LIBS) || \ + (defined __GNUC__ && (__GNUC__ < 2 \ + || (__GNUC__ == 2 \ + && __GNUC_MINOR__ < 96))) +#define HAS_BSD_WPRINTF 1 +#endif + +/* + * ASCII/UTF-8 versions + * + * NOTE: All size_t arguments and integer returns values are in bytes. + * + * NOTE: Str_Asprintf/Str_Vasprintf return NULL on failure, while + * Str_SafeAsprintf/Str_SafeVasprintf ASSERT_NOT_IMPLEMENTED. + * + * NOTE: "%s" refers to strings of "char" units, while "%S" refers to + * strings of "wchar_t" units, regardless of platform. + */ + +EXTERN int Str_Sprintf(char *buf, size_t max, + const char *fmt, ...) PRINTF_DECL(3, 4); +EXTERN int Str_Snprintf(char *buf, size_t len, + const char *fmt, ...) PRINTF_DECL(3, 4); +EXTERN int Str_Vsnprintf(char *buf, size_t len, + const char *fmt, va_list args); +EXTERN size_t Str_Strlen(const char *src, size_t maxLen); +EXTERN char *Str_Strnstr(const char *src, const char *sub, size_t n); +EXTERN char *Str_Strcpy(char *dst, const char *src, size_t maxLen); +EXTERN char *Str_Strcat(char *dst, const char *src, size_t maxLen); +EXTERN char *Str_Strncat(char *buf, size_t bufSize, const char *src, size_t n); + +EXTERN char *Str_Asprintf(size_t *length, + const char *format, ...) PRINTF_DECL(2, 3); +EXTERN char *Str_Vasprintf(size_t *length, const char *format, + va_list arguments); +EXTERN char *Str_SafeAsprintf(size_t *length, + const char *format, ...) PRINTF_DECL(2, 3); +EXTERN char *Str_SafeVasprintf(size_t *length, const char *format, + va_list arguments); + +#if defined(_WIN32) || defined(GLIBC_VERSION_22) // { + +/* + * wchar_t versions + * + * NOTE: All size_t arguments and integer return values are in + * wchar_ts, not bytes. + * + * NOTE: Str_Aswprintf/Str_Vaswprintf return NULL on failure, while + * Str_SafeAswprintf/Str_SafeVaswprintf ASSERT_NOT_IMPLEMENTED. + * + * NOTE: "%s" refers to strings of "char" units, while "%S" refers to + * strings of "wchar_t" units, regardless of platform. + */ + +EXTERN int Str_Swprintf(wchar_t *buf, size_t max, + const wchar_t *fmt, ...); +EXTERN int Str_Snwprintf(wchar_t *buf, size_t len, + const wchar_t *fmt, ...); +EXTERN int Str_Vsnwprintf(wchar_t *buf, size_t len, + const wchar_t *fmt, va_list args); +EXTERN wchar_t *Str_Wcscpy(wchar_t *dst, const wchar_t *src, size_t maxLen); +EXTERN wchar_t *Str_Wcscat(wchar_t *dst, const wchar_t *src, size_t maxLen); +EXTERN wchar_t *Str_Wcsncat(wchar_t *buf, size_t bufSize, const wchar_t *src, + size_t n); + +EXTERN wchar_t *Str_Aswprintf(size_t *length, + const wchar_t *format, ...); +EXTERN wchar_t *Str_Vaswprintf(size_t *length, const wchar_t *format, + va_list arguments); +EXTERN wchar_t *Str_SafeAswprintf(size_t *length, + const wchar_t *format, ...); +EXTERN wchar_t *Str_SafeVaswprintf(size_t *length, const wchar_t *format, + va_list arguments); + +unsigned char *Str_Mbscpy(char *buf, const char *src, + size_t maxSize); +unsigned char *Str_Mbscat(char *buf, const char *src, + size_t maxSize); + +/* + * These are handly for Windows programmers. They are like + * the _tcs functions, but with Str_Strcpy-style bounds checking. + * + * We don't have Str_Mbsncat() because it has some odd semantic + * ambiguity (whether to truncate in the middle of a multibyte + * sequence) that I want to stay away from. -- edward + */ + +#ifdef _WIN32 +#ifdef UNICODE + #define Str_Tcscpy(s1, s2, n) Str_Wcscpy(s1, s2, n) + #define Str_Tcscat(s1, s2, n) Str_Wcscat(s1, s2, n) +#else + #define Str_Tcscpy(s1, s2, n) Str_Mbscpy(s1, s2, n) + #define Str_Tcscat(s1, s2, n) Str_Mbscat(s1, s2, n) +#endif +#endif + +#endif // } defined(_WIN32) || defined(GLIBC_VERSION_22) + + +/* + * Wrappers for standard string functions + * + * These are either for Windows-Posix compatibility, + * or just gratuitous wrapping for consistency. + */ + +#define Str_Strcmp(s1, s2) strcmp(s1, s2) +#define Str_Strncmp(s1, s2, n) strncmp(s1, s2, n) + +#define Str_Strchr(s, c) strchr(s, c) +#define Str_Strrchr(s, c) strrchr(s, c) +#define Str_Strspn(s1, s2) strspn(s1, s2) +#define Str_Strcspn(s1, s2) strcspn(s1, s2) + +#if defined(_WIN32) + #define Str_Strcasecmp(s1, s2) _stricmp(s1, s2) + #define Str_Strncasecmp(s1, s2, n) _strnicmp(s1, s2, n) + #define Str_ToUpper(s) _strupr(s) + #define Str_ToLower(s) _strlwr(s) +#elif defined(N_PLAT_NLM) + #define Str_Strcasecmp(s1, s2) stricmp(s1, s2) + #define Str_Strncasecmp(s1, s2, n) strnicmp(s1, s2, n) + char *Str_ToUpper(char *string); + char *Str_ToLower(char *string); +#else + #define Str_Strcasecmp(s1, s2) strcasecmp(s1, s2) + #define Str_Strncasecmp(s1, s2, n) strncasecmp(s1, s2, n) + char *Str_ToUpper(char *string); + char *Str_ToLower(char *string); +#endif + +#ifdef _WIN32 + #define Str_Tcscmp(s1, s2) _tcscmp(s1, s2) + #define Str_Tcsncmp(s1, s2, n) _tcsncmp(s1, s2, n) + #define Str_Tcsicmp(s1, s2) _tcsicmp(s1, s2) + #define Str_Tcsnicmp(s1, s2, n) _tcsnicmp(s1, s2, n) + #define Str_Tcschr(s, c) _tcschr(s, c) + #define Str_Tcsrchr(s, c) _tcsrchr(s, c) + #define Str_Tcsspn(s1, s2) _tcsspn(s1, s2) + #define Str_Tcscspn(s1, s2) _tcscspn(s1, s2) + #define Str_Tcsupr(s) _tcsupr(s) + #define Str_Tcslwr(s) _tcslwr(s) +#endif + +#endif /* _STR_H_ */ diff --git a/open-vm-tools/modules/solaris/vmhgfs/vm_assert.h b/open-vm-tools/modules/solaris/vmhgfs/vm_assert.h index 80b5490bd..f03920614 100644 --- a/open-vm-tools/modules/solaris/vmhgfs/vm_assert.h +++ b/open-vm-tools/modules/solaris/vmhgfs/vm_assert.h @@ -81,10 +81,26 @@ EXTERN void WarningThrottled(uint32 *count, const char *fmt, ...) */ #ifndef ASSERT_IFNOT - #ifdef __cplusplus - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : (void)0) + /* + * PR 271512: When compiling with gcc, catch assignments inside an ASSERT. + * + * 'UNLIKELY' is defined with __builtin_expect, which does not warn when passed an + * assignment (gcc bug 36050). + * To get around this, we put 'cond' in an 'if' statement and make sure it never gets + * executed by putting that inside of 'if (0)'. + * We use gcc's statement expression syntax to make ASSERT an expression because some + * code uses it that way. + * + * Since statement expression syntax is a gcc extension and since it's not clear if + * this is a problem with other compilers, the ASSERT definition was not changed for + * them. Using a bare 'cond' with the ternary operator may provide a solution. + */ + #ifdef __GNUC__ + #define ASSERT_IFNOT(cond, panic) \ + ({if (UNLIKELY(!(cond))) { panic; if (0) { if (cond) { ; } } } (void)0;}) #else - #define ASSERT_IFNOT(cond, panic) (UNLIKELY(!(cond)) ? (panic) : 0) + #define ASSERT_IFNOT(cond, panic) \ + (UNLIKELY(!(cond)) ? (panic) : (void)0) #endif #endif diff --git a/open-vm-tools/modules/solaris/vmhgfs/vm_basic_asm.h b/open-vm-tools/modules/solaris/vmhgfs/vm_basic_asm.h index 7d83e29a3..483b830f8 100644 --- a/open-vm-tools/modules/solaris/vmhgfs/vm_basic_asm.h +++ b/open-vm-tools/modules/solaris/vmhgfs/vm_basic_asm.h @@ -851,4 +851,21 @@ RDTSC(void) #error No compiler defined for RDTSC #endif /* __GNUC__ */ +/* + *----------------------------------------------------------------------------- + * + * DEBUGBREAK -- + * + * Does an int3 for MSVC / GCC. This is a macro to make sure int3 is + * always inlined. + * + *----------------------------------------------------------------------------- + */ + +#ifdef _MSC_VER +#define DEBUGBREAK() __debugbreak() +#else +#define DEBUGBREAK() __asm__ (" int $3 ") +#endif + #endif diff --git a/open-vm-tools/modules/solaris/vmhgfs/vm_basic_types.h b/open-vm-tools/modules/solaris/vmhgfs/vm_basic_types.h index 05feb1219..dc0ad6108 100644 --- a/open-vm-tools/modules/solaris/vmhgfs/vm_basic_types.h +++ b/open-vm-tools/modules/solaris/vmhgfs/vm_basic_types.h @@ -336,6 +336,9 @@ typedef int64 VmTimeVirtualClock; /* Virtual Clock kept in CPU cycles */ #ifdef _MSC_VER #define CONST64(c) c##I64 #define CONST64U(c) c##uI64 +#elif defined __APPLE__ +#define CONST64(c) c##LL +#define CONST64U(c) c##uLL #elif __GNUC__ #ifdef VM_X86_64 #define CONST64(c) c##L @@ -487,10 +490,11 @@ typedef uint64 MPN64; */ typedef VA32 UserVA32; typedef VA64 UserVA64; -typedef UserVA32 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA64 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA32 UserVA32Const; /* Userspace ptr to data that we may only read. */ typedef UserVA64 UserVA64Const; /* Used by 64-bit syscalls until conversion is finished. */ #ifdef VMKERNEL -typedef UserVA32 UserVA; +typedef UserVA64 UserVA; #else typedef void * UserVA; #endif diff --git a/open-vm-tools/modules/solaris/vmhgfs/vnode.c b/open-vm-tools/modules/solaris/vmhgfs/vnode.c index 25f02f6fa..982a26394 100644 --- a/open-vm-tools/modules/solaris/vmhgfs/vnode.c +++ b/open-vm-tools/modules/solaris/vmhgfs/vnode.c @@ -38,6 +38,7 @@ #include "hgfsSolaris.h" #include "hgfsState.h" +#include "hgfsBdGlue.h" #include "vnode.h" #include "filesystem.h" #include "request.h" @@ -90,10 +91,6 @@ static int HgfsDoWrite(HgfsSuperInfo *sip, HgfsHandle handle, int ioflag, uint64_t offset, uint32_t size, uio_t *uiop); static int HgfsDelete(HgfsSuperInfo *sip, char *filename, HgfsOp op); -/* Local utility functions */ -#if HGFS_VFS_VERSION > 2 -static int HgfsMakeVnodeOps(HgfsSuperInfo *sip); -#endif static int HgfsSubmitRequest(HgfsSuperInfo *sip, HgfsReq *req); static int HgfsValidateReply(HgfsReq *req, uint32_t minSize); static void HgfsAttrToSolaris(struct vnode *vp, const HgfsAttr *hgfsAttr, @@ -328,7 +325,7 @@ static int HgfsVnevent(struct vnode *vp, vnevent_t event, vnode_t *dvp, #if HGFS_VFS_VERSION == 2 /* vnode Operations Structure */ -struct vnodeops HgfsVnodeOps = { +struct vnodeops hgfsVnodeOps = { HgfsOpen, /* vop_open() */ HgfsClose, /* vop_close() */ HgfsRead, /* vop_read() */ @@ -374,8 +371,15 @@ struct vnodeops HgfsVnodeOps = { HgfsShrlock /* vop_shrlock() */ }; +#else + +/* Will be set up during HGFS initialization (see HgfsInit) */ +static struct vnodeops *hgfsVnodeOpsP; + #endif +static HgfsSuperInfo hgfsSuperInfo; + /* * Vnode Entry Points @@ -546,7 +550,7 @@ HgfsClose(struct vnode *vp, // IN: Vnode of file that is being closed if ( !HGFS_KNOW_FILENAME(vp) ) { DEBUG(VM_DEBUG_FAIL, "HgfsClose: we don't know the filename of:\n"); - HgfsDebugPrintVnode(VM_DEBUG_STRUCT, "HgfsClose", vp); + HgfsDebugPrintVnode(VM_DEBUG_STRUCT, "HgfsClose", vp, TRUE); return EINVAL; } @@ -957,8 +961,8 @@ HgfsGetattr(struct vnode *vp, // IN: Vnode of file to get attributes for DEBUG(VM_DEBUG_FAIL, "HgfsGetattr: CPName_ConvertTo failed.\n"); ret = ENAMETOOLONG; /* We need to set the request's state to error before destroying. */ - HgfsReqSetState(req, HGFS_REQ_ERROR); - goto destroy_out; + req->state = HGFS_REQ_ERROR; + goto out; } request->fileName.length = ret; @@ -972,7 +976,6 @@ HgfsGetattr(struct vnode *vp, // IN: Vnode of file to get attributes for */ ret = HgfsSubmitRequest(sip, req); if (ret) { - /* HgfsSubmitRequest destroys the request if necessary */ goto out; } @@ -981,7 +984,7 @@ HgfsGetattr(struct vnode *vp, // IN: Vnode of file to get attributes for if (HgfsValidateReply(req, sizeof reply->header) != 0) { DEBUG(VM_DEBUG_FAIL, "HgfsGetattr: reply not valid.\n"); ret = EPROTO; - goto destroy_out; + goto out; } switch (reply->header.status) { @@ -1014,9 +1017,8 @@ HgfsGetattr(struct vnode *vp, // IN: Vnode of file to get attributes for break; } -destroy_out: - HgfsDestroyReq(sip, req); out: + HgfsDestroyReq(sip, req); return ret; } @@ -1095,8 +1097,8 @@ HgfsSetattr(struct vnode *vp, // IN: Vnode of file to get attributes for DEBUG(VM_DEBUG_DONE, "HgfsSetattr: don't need to update attributes.\n"); ret = 0; /* We need to set the request state to completed before destroying. */ - HgfsReqSetState(req, HGFS_REQ_COMPLETED); - goto destroy_out; + req->state = HGFS_REQ_COMPLETED; + goto out; } /* Convert the filename to cross platform and escape its buffer. */ @@ -1105,8 +1107,8 @@ HgfsSetattr(struct vnode *vp, // IN: Vnode of file to get attributes for DEBUG(VM_DEBUG_FAIL, "HgfsSetattr: CPName_ConvertTo failed.\n"); ret = ENAMETOOLONG; /* We need to set the request state to error before destroying. */ - HgfsReqSetState(req, HGFS_REQ_ERROR); - goto destroy_out; + req->state = HGFS_REQ_ERROR; + goto out; } request->fileName.length = ret; @@ -1116,7 +1118,6 @@ HgfsSetattr(struct vnode *vp, // IN: Vnode of file to get attributes for ret = HgfsSubmitRequest(sip, req); if (ret) { - /* HgfsSubmitRequest() destroys the request if necessary. */ goto out; } @@ -1125,7 +1126,7 @@ HgfsSetattr(struct vnode *vp, // IN: Vnode of file to get attributes for if (HgfsValidateReply(req, sizeof *reply) != 0) { DEBUG(VM_DEBUG_FAIL, "HgfsSetattr: invalid reply received.\n"); ret = EPROTO; - goto destroy_out; + goto out; } switch(reply->header.status) { @@ -1137,26 +1138,25 @@ HgfsSetattr(struct vnode *vp, // IN: Vnode of file to get attributes for case HGFS_STATUS_INVALID_NAME: DEBUG(VM_DEBUG_FAIL, "HgfsSetattr: no such file or directory.\n"); ret = ENOENT; - goto destroy_out; + goto out; case HGFS_STATUS_OPERATION_NOT_PERMITTED: DEBUG(VM_DEBUG_FAIL, "HgfsSetattr: operation not permitted.\n"); ret = EPERM; - goto destroy_out; + goto out; default: DEBUG(VM_DEBUG_FAIL, "HgfsSetattr: default error.\n"); ret = EPROTO; - goto destroy_out; + goto out; } /* Success */ ret = 0; DEBUG(VM_DEBUG_DONE, "HgfsSetattr: done.\n"); -destroy_out: - HgfsDestroyReq(sip, req); out: + HgfsDestroyReq(sip, req); return ret; } @@ -1340,8 +1340,7 @@ HgfsLookup(struct vnode *dvp, // IN: Directory to look in path, // Destination buffer sizeof path); // Size of dest buffer if (ret < 0) { - ret = EINVAL; - goto out; + return EINVAL; } DEBUG(VM_DEBUG_LOAD, "HgfsLookup: full path is \"%s\"\n", path); @@ -1388,7 +1387,7 @@ HgfsLookup(struct vnode *dvp, // IN: Directory to look in * per-open state for it. */ DEBUG(VM_DEBUG_DONE, "HgfsLookup: created per-open state from filename.\n"); - goto out; + return 0; } #endif @@ -1411,8 +1410,8 @@ HgfsLookup(struct vnode *dvp, // IN: Directory to look in DEBUG(VM_DEBUG_FAIL, "HgfsLookup: CPName_ConvertTo failed.\n"); ret = ENAMETOOLONG; /* We need to set the request state to error before destroying. */ - HgfsReqSetState(req, HGFS_REQ_ERROR); - goto destroy_out; + req->state = HGFS_REQ_ERROR; + goto out; } request->fileName.length = ret; @@ -1425,8 +1424,7 @@ HgfsLookup(struct vnode *dvp, // IN: Directory to look in DEBUG(VM_DEBUG_COMM, " fileName.name: \"%s\"\n", request->fileName.name); /* - * Submit the request and wait for the reply. HgfsSubmitRequest handles - * destroying the request on both error and interrupt cases. + * Submit the request and wait for the reply. */ ret = HgfsSubmitRequest(sip, req); if (ret) { @@ -1441,7 +1439,7 @@ HgfsLookup(struct vnode *dvp, // IN: Directory to look in DEBUG(VM_DEBUG_FAIL, "HgfsLookup(): invalid reply received for ID %d " "with status %d.\n", reply->header.id, reply->header.status); ret = EPROTO; - goto destroy_out; + goto out; } DEBUG(VM_DEBUG_COMM, "HgfsLookup: received reply for ID %d\n", reply->header.id); @@ -1457,7 +1455,7 @@ HgfsLookup(struct vnode *dvp, // IN: Directory to look in DEBUG(VM_DEBUG_COMM, "HgfsLookup: invalid packet size received for \"%s\".\n", nm); ret = EIO; - goto destroy_out; + goto out; } /* Success */ break; @@ -1465,7 +1463,7 @@ HgfsLookup(struct vnode *dvp, // IN: Directory to look in case HGFS_STATUS_OPERATION_NOT_PERMITTED: DEBUG(VM_DEBUG_LOG, "HgfsLookup: operation not permitted on \"%s\".\n", nm); ret = EACCES; - goto destroy_out; + goto out; case HGFS_STATUS_NO_SUCH_FILE_OR_DIR: case HGFS_STATUS_INVALID_NAME: @@ -1476,12 +1474,12 @@ HgfsLookup(struct vnode *dvp, // IN: Directory to look in */ DEBUG(VM_DEBUG_LOG, "HgfsLookup: \"%s\" does not exist.\n", nm); ret = ENOENT; - goto destroy_out; + goto out; default: DEBUG(VM_DEBUG_LOG, "HgfsLookup: error on \"%s\".\n", nm); ret = EIO; - goto destroy_out; + goto out; } /* @@ -1500,7 +1498,7 @@ HgfsLookup(struct vnode *dvp, // IN: Directory to look in if (ret) { DEBUG(VM_DEBUG_FAIL, "HgfsLookup: couldn't create vnode for \"%s\".\n", path); ret = EIO; - goto destroy_out; + goto out; } /* HgfsVnodeGet guarantees this. */ ASSERT(*vpp); @@ -1509,9 +1507,8 @@ HgfsLookup(struct vnode *dvp, // IN: Directory to look in ret = 0; /* Return success */ -destroy_out: - HgfsDestroyReq(sip, req); out: + HgfsDestroyReq(sip, req); return ret; } @@ -1856,8 +1853,8 @@ HgfsRename(struct vnode *sdvp, // IN: Source directory that contains file to "HgfsRename: couldn't convert source to cross platform name.\n"); ret = ENAMETOOLONG; /* We need to set the request state to error before destroying. */ - HgfsReqSetState(req, HGFS_REQ_ERROR); - goto destroy_out; + req->state = HGFS_REQ_ERROR; + goto out; } request->oldName.length = ret; @@ -1877,8 +1874,8 @@ HgfsRename(struct vnode *sdvp, // IN: Source directory that contains file to "HgfsRename: couldn't convert destination to cross platform name.\n"); ret = ENAMETOOLONG; /* We need to set the request state to error before destroying. */ - HgfsReqSetState(req, HGFS_REQ_ERROR); - goto destroy_out; + req->state = HGFS_REQ_ERROR; + goto out; } newNameP->length = ret; @@ -1888,7 +1885,6 @@ HgfsRename(struct vnode *sdvp, // IN: Source directory that contains file to ret = HgfsSubmitRequest(sip, req); if (ret) { - /* HgfsSubmitRequest() destroys the request if necessary. */ goto out; } @@ -1898,7 +1894,7 @@ HgfsRename(struct vnode *sdvp, // IN: Source directory that contains file to if (HgfsValidateReply(req, sizeof *reply) != 0) { DEBUG(VM_DEBUG_FAIL, "HgfsRename: invalid reply received.\n"); ret = EPROTO; - goto destroy_out; + goto out; } /* Return appropriate value. */ @@ -1910,28 +1906,28 @@ HgfsRename(struct vnode *sdvp, // IN: Source directory that contains file to case HGFS_STATUS_OPERATION_NOT_PERMITTED: DEBUG(VM_DEBUG_FAIL, "HgfsRename: operation not permitted.\n"); ret = EACCES; - goto destroy_out; + goto out; case HGFS_STATUS_NOT_DIRECTORY: DEBUG(VM_DEBUG_FAIL, "HgfsRename: not a directory.\n"); ret = ENOTDIR; - goto destroy_out; + goto out; case HGFS_STATUS_DIR_NOT_EMPTY: DEBUG(VM_DEBUG_FAIL, "HgfsRename: directory not empty.\n"); ret = EEXIST; - goto destroy_out; + goto out; case HGFS_STATUS_NO_SUCH_FILE_OR_DIR: case HGFS_STATUS_INVALID_NAME: DEBUG(VM_DEBUG_FAIL, "HgfsRename: no such file or directory.\n"); ret = ENOENT; - goto destroy_out; + goto out; default: DEBUG(VM_DEBUG_FAIL, "HgfsRename: default error.\n"); ret = EPROTO; - goto destroy_out; + goto out; } @@ -1939,9 +1935,8 @@ HgfsRename(struct vnode *sdvp, // IN: Source directory that contains file to ret = 0; DEBUG(VM_DEBUG_DONE, "HgfsRename: done.\n"); -destroy_out: - HgfsDestroyReq(sip, req); out: + HgfsDestroyReq(sip, req); return ret; } @@ -2045,8 +2040,8 @@ HgfsMkdir(struct vnode *dvp, // IN: Vnode of directory to create directory DEBUG(VM_DEBUG_FAIL, "HgfsMkdir: cross-platform name is too long.\n"); ret = ENAMETOOLONG; /* We need to set the request state to error before destroying. */ - HgfsReqSetState(req, HGFS_REQ_ERROR); - goto destroy_out; + req->state = HGFS_REQ_ERROR; + goto out; } request->fileName.length = ret; @@ -2054,10 +2049,9 @@ HgfsMkdir(struct vnode *dvp, // IN: Vnode of directory to create directory /* Set the size of this request. */ req->packetSize = sizeof *request + request->fileName.length; - /* Send the request to guestd. */ + /* Send the request. */ ret = HgfsSubmitRequest(sip, req); if (ret) { - /* Request is destroyed in HgfsSubmitRequest() if necessary. */ goto out; } @@ -2066,7 +2060,7 @@ HgfsMkdir(struct vnode *dvp, // IN: Vnode of directory to create directory if (HgfsValidateReply(req, sizeof *reply) != 0) { DEBUG(VM_DEBUG_FAIL, "HgfsMkdir: invalid reply received.\n"); ret = EPROTO; - goto destroy_out; + goto out; } switch (reply->header.status) { @@ -2077,16 +2071,16 @@ HgfsMkdir(struct vnode *dvp, // IN: Vnode of directory to create directory case HGFS_STATUS_OPERATION_NOT_PERMITTED: DEBUG(VM_DEBUG_FAIL, "HgfsMkdir: operation not permitted.\n"); ret = EACCES; - goto destroy_out; + goto out; case HGFS_STATUS_FILE_EXISTS: DEBUG(VM_DEBUG_FAIL, "HgfsMkdir: directory already exists.\n"); ret = EEXIST; - goto destroy_out; + goto out; default: ret = EPROTO; - goto destroy_out; + goto out; } /* We now create the vnode for the new directory. */ @@ -2094,15 +2088,14 @@ HgfsMkdir(struct vnode *dvp, // IN: Vnode of directory to create directory HGFS_FILE_TYPE_DIRECTORY, &sip->fileHashTable); if (ret) { ret = EIO; - goto destroy_out; + goto out; } ASSERT(*vpp); /* HgfsIget guarantees this. */ ret = 0; -destroy_out: - HgfsDestroyReq(sip, req); out: + HgfsDestroyReq(sip, req); return ret; } @@ -3697,8 +3690,8 @@ HgfsDirOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer if (ret < 0) { ret = ENAMETOOLONG; /* We need to set the request state to error before destroying. */ - HgfsReqSetState(req, HGFS_REQ_ERROR); - goto destroy_out; + req->state = HGFS_REQ_ERROR; + goto out; } request->dirName.length = ret; @@ -3708,7 +3701,6 @@ HgfsDirOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer /* Submit the request to the Hgfs server */ ret = HgfsSubmitRequest(sip, req); if (ret) { - /* HgfsSubmitRequest destroys the request if necessary */ goto out; } @@ -3719,7 +3711,7 @@ HgfsDirOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer if (HgfsValidateReply(req, sizeof reply->header) != 0) { DEBUG(VM_DEBUG_FAIL, "HgfsDirOpen(): invalid reply received.\n"); ret = EPROTO; - goto destroy_out; + goto out; } DEBUG(VM_DEBUG_COMM, "HgfsDirOpen: received reply for ID %d\n", reply->header.id); @@ -3731,7 +3723,7 @@ HgfsDirOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer if (req->packetSize != sizeof *reply) { DEBUG(VM_DEBUG_FAIL, "HgfsDirOpen: incorrect packet size.\n"); ret = EIO; - goto destroy_out; + goto out; } /* Success handled after switch. */ break; @@ -3740,13 +3732,13 @@ HgfsDirOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer DEBUG(VM_DEBUG_FAIL, "HgfsDirOpen: operation not permitted (%s).\n", HGFS_VP_TO_FILENAME(vp)); ret = EACCES; - goto destroy_out; + goto out; default: DEBUG(VM_DEBUG_FAIL, "HgfsDirOpen: default error (%s).\n", HGFS_VP_TO_FILENAME(vp)); ret = EPROTO; - goto destroy_out; + goto out; } /* Set the search open handle for use in HgfsReaddir() */ @@ -3754,17 +3746,16 @@ HgfsDirOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer if (ret) { DEBUG(VM_DEBUG_FAIL, "HgfsDirOpen: couldn't assign handle=%d to %s\n", reply->search, HGFS_VP_TO_FILENAME(vp)); - HgfsReqSetState(req, HGFS_REQ_ERROR); + req->state = HGFS_REQ_ERROR; ret = EINVAL; - goto destroy_out; + goto out; } ret = 0; /* Return success */ -destroy_out: +out: /* Make sure we put the request back on the list */ HgfsDestroyReq(sip, req); -out: DEBUG(VM_DEBUG_DONE, "HgfsDirOpen: done\n"); return ret; } @@ -3822,8 +3813,8 @@ HgfsFileOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: HgfsGetOpenMode failed.\n"); ret = EINVAL; /* We need to set the request state to error before destroying. */ - HgfsReqSetState(req, HGFS_REQ_ERROR); - goto destroy_out; + req->state = HGFS_REQ_ERROR; + goto out; } request->mode = ret; @@ -3835,8 +3826,8 @@ HgfsFileOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: HgfsGetOpenFlags failed.\n"); ret = EINVAL; /* We need to set the request state to error before destroying. */ - HgfsReqSetState(req, HGFS_REQ_ERROR); - goto destroy_out; + req->state = HGFS_REQ_ERROR; + goto out; } request->flags = ret; @@ -3851,8 +3842,8 @@ HgfsFileOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: CPName_ConvertTo failed.\n"); ret = ENAMETOOLONG; /* We need to set the request state to error before destroying. */ - HgfsReqSetState(req, HGFS_REQ_ERROR); - goto destroy_out; + req->state = HGFS_REQ_ERROR; + goto out; } request->fileName.length = ret; @@ -3861,7 +3852,6 @@ HgfsFileOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer ret = HgfsSubmitRequest(sip, req); if (ret) { - /* HgfsSubmitRequest will destroy the request if necessary. */ DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: could not submit request.\n"); goto out; } @@ -3871,7 +3861,7 @@ HgfsFileOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer if (HgfsValidateReply(req, sizeof reply->header) != 0) { DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: request not valid.\n"); ret = EPROTO; - goto destroy_out; + goto out; } switch (reply->header.status) { @@ -3879,7 +3869,7 @@ HgfsFileOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer if (req->packetSize != sizeof *reply) { DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: size of reply is incorrect.\n"); ret = EIO; - goto destroy_out; + goto out; } /* Success case is handled after switch. */ @@ -3890,31 +3880,31 @@ HgfsFileOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: no such file \"%s\".\n", HGFS_VP_TO_FILENAME(vp)); ret = ENOENT; - goto destroy_out; + goto out; case HGFS_STATUS_FILE_EXISTS: DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: \"%s\" exists.\n", HGFS_VP_TO_FILENAME(vp)); ret = EEXIST; - goto destroy_out; + goto out; case HGFS_STATUS_OPERATION_NOT_PERMITTED: DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: operation not permitted on %s.\n", HGFS_VP_TO_FILENAME(vp)); ret = EACCES; - goto destroy_out; + goto out; case HGFS_STATUS_ACCESS_DENIED: DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: access denied on %s.\n", HGFS_VP_TO_FILENAME(vp)); ret = EACCES; - goto destroy_out; + goto out; default: DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: default/unknown error %d.\n", reply->header.status); ret = EACCES; - goto destroy_out; + goto out; } /* @@ -3925,17 +3915,16 @@ HgfsFileOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer if (ret) { DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: couldn't assign handle %d (%s)\n", reply->file, HGFS_VP_TO_FILENAME(vp)); - HgfsReqSetState(req, HGFS_REQ_ERROR); + req->state = HGFS_REQ_ERROR; ret = EINVAL; - goto destroy_out; + goto out; } ret = 0; -destroy_out: - HgfsDestroyReq(sip, req); out: + HgfsDestroyReq(sip, req); return ret; } @@ -3989,15 +3978,14 @@ HgfsDirClose(HgfsSuperInfo *sip, // IN: Superinfo pointer HGFS_VP_TO_FILENAME(vp)); ret = EINVAL; /* We need to set the request state to error before destroying. */ - HgfsReqSetState(req, HGFS_REQ_ERROR); - goto destroy_out; + req->state = HGFS_REQ_ERROR; + goto out; } req->packetSize = sizeof *request; /* Submit the request to the Hgfs server */ ret = HgfsSubmitRequest(sip, req); if (ret) { - /* HgfsSubmitRequest destroys the request if necessary */ goto out; } @@ -4007,7 +3995,7 @@ HgfsDirClose(HgfsSuperInfo *sip, // IN: Superinfo pointer if (HgfsValidateReply(req, sizeof reply->header) != 0) { DEBUG(VM_DEBUG_FAIL, "HgfsDirClose: invalid reply received.\n"); ret = EPROTO; - goto destroy_out; + goto out; } DEBUG(VM_DEBUG_COMM, "HgfsDirClose: received reply for ID %d\n", reply->header.id); @@ -4016,7 +4004,7 @@ HgfsDirClose(HgfsSuperInfo *sip, // IN: Superinfo pointer /* Ensure server was able to close directory. */ if (reply->header.status != HGFS_STATUS_SUCCESS) { ret = EIO; - goto destroy_out; + goto out; } /* Now clear this open file's handle for future use. */ @@ -4024,17 +4012,16 @@ HgfsDirClose(HgfsSuperInfo *sip, // IN: Superinfo pointer if (ret) { DEBUG(VM_DEBUG_FAIL, "HgfsDirClose: couldn't clear handle.\n"); ret = EINVAL; - HgfsReqSetState(req, HGFS_REQ_ERROR); - goto destroy_out; + req->state = HGFS_REQ_ERROR; + goto out; } /* The directory was closed successfully so we return success. */ ret = 0; -destroy_out: - HgfsDestroyReq(sip, req); out: + HgfsDestroyReq(sip, req); return ret; } @@ -4084,15 +4071,14 @@ HgfsFileClose(HgfsSuperInfo *sip, // IN: Superinfo pointer DEBUG(VM_DEBUG_FAIL, "HgfsFileClose: couldn't get handle.\n"); ret = EINVAL; /* We need to set the request state to error before destroying. */ - HgfsReqSetState(req, HGFS_REQ_ERROR); - goto destroy_out; + req->state = HGFS_REQ_ERROR; + goto out; } req->packetSize = sizeof *request; ret = HgfsSubmitRequest(sip, req); if (ret) { - /* HgfsSubmitRequest will destroy the request if necessary. */ DEBUG(VM_DEBUG_FAIL, "HgfsFileClose: submit request failed.\n"); goto out; } @@ -4100,7 +4086,7 @@ HgfsFileClose(HgfsSuperInfo *sip, // IN: Superinfo pointer if (HgfsValidateReply(req, sizeof *reply) != 0) { DEBUG(VM_DEBUG_FAIL, "HgfsFileClose: reply was invalid.\n"); ret = EPROTO; - goto destroy_out; + goto out; } reply = (HgfsReplyClose *)req->packet; @@ -4115,32 +4101,31 @@ HgfsFileClose(HgfsSuperInfo *sip, // IN: Superinfo pointer if (ret) { DEBUG(VM_DEBUG_FAIL, "HgfsFileClose: couldn't clear handle.\n"); ret = EINVAL; - HgfsReqSetState(req, HGFS_REQ_ERROR); - goto destroy_out; + req->state = HGFS_REQ_ERROR; + goto out; } ret = 0; - goto destroy_out; + goto out; case HGFS_STATUS_INVALID_HANDLE: DEBUG(VM_DEBUG_FAIL, "HgfsFileClose: invalid handle error.\n"); ret = EPROTO; - goto destroy_out; + goto out; case HGFS_STATUS_OPERATION_NOT_PERMITTED: DEBUG(VM_DEBUG_FAIL, "HgfsFileClose: operation not permitted error.\n"); ret = EACCES; - goto destroy_out; + goto out; default: DEBUG(VM_DEBUG_FAIL, "HgfsFileClose: other/unknown error.\n"); ret = EPROTO; - goto destroy_out; + goto out; } -destroy_out: - HgfsDestroyReq(sip, req); out: + HgfsDestroyReq(sip, req); DEBUG(VM_DEBUG_DONE, "HgfsFileClose: returning %d\n", ret); return ret; } @@ -4203,7 +4188,6 @@ HgfsGetNextDirEntry(HgfsSuperInfo *sip, // IN: Superinfo pointer ret = HgfsSubmitRequest(sip, req); if (ret) { - /* HgfsSubmitRequest will destroy the request if necessary. */ DEBUG(VM_DEBUG_FAIL, "HgfsGetNextDirEntry: HgfsSubmitRequest failed.\n"); goto out; } @@ -4214,7 +4198,7 @@ HgfsGetNextDirEntry(HgfsSuperInfo *sip, // IN: Superinfo pointer if (HgfsValidateReply(req, sizeof reply->header) != 0) { DEBUG(VM_DEBUG_FAIL, "HgfsGetNextDirEntry: reply not valid.\n"); ret = EPROTO; - goto destroy_out; + goto out; } DEBUG(VM_DEBUG_COMM, "HgfsGetNextDirEntry: received reply for ID %d\n", @@ -4226,14 +4210,14 @@ HgfsGetNextDirEntry(HgfsSuperInfo *sip, // IN: Superinfo pointer DEBUG(VM_DEBUG_FAIL, "HgfsGetNextDirEntry: server didn't return success (%d).\n", reply->header.status); ret = EINVAL; - goto destroy_out; + goto out; } /* Make sure we got an entire reply (excluding filename) */ if (req->packetSize < sizeof *reply) { DEBUG(VM_DEBUG_FAIL, "HgfsGetNextDirEntry: server didn't provide entire reply.\n"); ret = EIO; - goto destroy_out; + goto out; } /* See if there are no more filenames to read */ @@ -4241,7 +4225,7 @@ HgfsGetNextDirEntry(HgfsSuperInfo *sip, // IN: Superinfo pointer DEBUG(VM_DEBUG_DONE, "HgfsGetNextDirEntry: no more directory entries.\n"); *done = TRUE; ret = 0; /* return success */ - goto destroy_out; + goto out; } /* Make sure filename isn't too long */ @@ -4249,7 +4233,7 @@ HgfsGetNextDirEntry(HgfsSuperInfo *sip, // IN: Superinfo pointer (reply->fileName.length > HGFS_PAYLOAD_MAX(reply)) ) { DEBUG(VM_DEBUG_FAIL, "HgfsGetNextDirEntry: filename is too long.\n"); ret = EOVERFLOW; - goto destroy_out; + goto out; } /* @@ -4262,9 +4246,8 @@ HgfsGetNextDirEntry(HgfsSuperInfo *sip, // IN: Superinfo pointer ret = 0; DEBUG(VM_DEBUG_DONE, "HgfsGetNextDirEntry: done.\n"); -destroy_out: - HgfsDestroyReq(sip, req); out: + HgfsDestroyReq(sip, req); return ret; } @@ -4332,8 +4315,7 @@ HgfsDoRead(HgfsSuperInfo *sip, // IN: Superinfo pointer if (ret) { /* * We need to flip the sign of the return value to indicate error; see - * the comment in the function header. HgfsSubmitRequest() handles - * destroying the request if necessary, so we don't here. + * the comment in the function header. */ ret = -ret; goto out; @@ -4345,13 +4327,13 @@ HgfsDoRead(HgfsSuperInfo *sip, // IN: Superinfo pointer if (HgfsValidateReply(req, sizeof reply->header) != 0) { DEBUG(VM_DEBUG_FAIL, "HgfsDoRead: invalid reply received.\n"); ret = -EPROTO; - goto destroy_out; + goto out; } if (reply->header.status != HGFS_STATUS_SUCCESS) { DEBUG(VM_DEBUG_FAIL, "HgfsDoRead: request not completed successfully.\n"); ret = -EACCES; - goto destroy_out; + goto out; } /* @@ -4370,7 +4352,7 @@ HgfsDoRead(HgfsSuperInfo *sip, // IN: Superinfo pointer ret = uiomove(reply->payload, reply->actualSize, UIO_READ, uiop); if (ret) { ret = -EIO; - goto destroy_out; + goto out; } /* We successfully copied the payload to the user's buffer */ @@ -4380,16 +4362,15 @@ HgfsDoRead(HgfsSuperInfo *sip, // IN: Superinfo pointer /* We got too much data: server error. */ DEBUG(VM_DEBUG_FAIL, "HgfsDoRead: received too much data in payload.\n"); ret = -EPROTO; - goto destroy_out; + goto out; } success: ret = reply->actualSize; DEBUG(VM_DEBUG_DONE, "HgfsDoRead: successfully read %d bytes to user.\n", ret); -destroy_out: - HgfsDestroyReq(sip, req); out: + HgfsDestroyReq(sip, req); return ret; } @@ -4464,8 +4445,8 @@ HgfsDoWrite(HgfsSuperInfo *sip, // IN: Superinfo pointer "HgfsDoWrite: uiomove(9F) failed copying data from user.\n"); ret = -EIO; /* We need to set the request state to error before destroying. */ - HgfsReqSetState(req, HGFS_REQ_ERROR); - goto destroy_out; + req->state = HGFS_REQ_ERROR; + goto out; } /* We subtract one so request's 'char payload[1]' member isn't double counted. */ @@ -4487,30 +4468,29 @@ HgfsDoWrite(HgfsSuperInfo *sip, // IN: Superinfo pointer if (HgfsValidateReply(req, sizeof reply->header) != 0) { DEBUG(VM_DEBUG_FAIL, "HgfsDoWrite: invalid reply received.\n"); ret = -EPROTO; - goto destroy_out; + goto out; } if (reply->header.status != HGFS_STATUS_SUCCESS) { DEBUG(VM_DEBUG_FAIL, "HgfsDoWrite: write failed (status=%d).\n", reply->header.status); ret = -EACCES; - goto destroy_out; + goto out; } if (req->packetSize != sizeof *reply) { DEBUG(VM_DEBUG_FAIL, "HgfsDoWrite: invalid size of reply on successful reply.\n"); ret = -EPROTO; - goto destroy_out; + goto out; } /* The write was completed successfully, so return the amount written. */ ret = reply->actualSize; DEBUG(VM_DEBUG_DONE, "HgfsDoWrite: wrote %d bytes.\n", ret); -destroy_out: - HgfsDestroyReq(sip, req); out: + HgfsDestroyReq(sip, req); return ret; } @@ -4561,8 +4541,8 @@ HgfsDelete(HgfsSuperInfo *sip, // IN: Superinfo if (ret < 0) { ret = ENAMETOOLONG; /* We need to set the request state to error before destroying. */ - HgfsReqSetState(req, HGFS_REQ_ERROR); - goto destroy_out; + req->state = HGFS_REQ_ERROR; + goto out; } request->fileName.length = ret; @@ -4572,10 +4552,9 @@ HgfsDelete(HgfsSuperInfo *sip, // IN: Superinfo DEBUG(VM_DEBUG_COMM, "HgfsDelete: deleting \"%s\"\n", filename); - /* Submit our request to guestd. */ + /* Submit our request. */ ret = HgfsSubmitRequest(sip, req); if (ret) { - /* HgfsSubmitRequest() handles destroying the request if necessary. */ goto out; } @@ -4585,7 +4564,7 @@ HgfsDelete(HgfsSuperInfo *sip, // IN: Superinfo if (HgfsValidateReply(req, sizeof *reply) != 0) { DEBUG(VM_DEBUG_FAIL, "HgfsDelete: invalid reply received.\n"); ret = EPROTO; - goto destroy_out; + goto out; } /* Return the appropriate value. */ @@ -4597,40 +4576,39 @@ HgfsDelete(HgfsSuperInfo *sip, // IN: Superinfo case HGFS_STATUS_OPERATION_NOT_PERMITTED: DEBUG(VM_DEBUG_FAIL, "HgfsDelete: operation not permitted.\n"); ret = EACCES; - goto destroy_out; + goto out; case HGFS_STATUS_NOT_DIRECTORY: DEBUG(VM_DEBUG_FAIL, "HgfsDelete: not a directory.\n"); ret = ENOTDIR; - goto destroy_out; + goto out; case HGFS_STATUS_DIR_NOT_EMPTY: DEBUG(VM_DEBUG_FAIL, "HgfsDelete: directory not empty.\n"); ret = EEXIST; - goto destroy_out; + goto out; case HGFS_STATUS_NO_SUCH_FILE_OR_DIR: case HGFS_STATUS_INVALID_NAME: DEBUG(VM_DEBUG_FAIL, "HgfsDelete: no such file or directory.\n"); ret = ENOENT; - goto destroy_out; + goto out; case HGFS_STATUS_ACCESS_DENIED: DEBUG(VM_DEBUG_FAIL, "HgfsDelete: access denied.\n"); /* XXX: Add retry behavior after removing r/o bit. */ ret = EACCES; - goto destroy_out; + goto out; default: DEBUG(VM_DEBUG_FAIL, "HgfsDelete: default error.\n"); ret = EPROTO; - goto destroy_out; + goto out; } DEBUG(VM_DEBUG_DONE, "HgfsDelete: done.\n"); -destroy_out: - HgfsDestroyReq(sip, req); out: + HgfsDestroyReq(sip, req); return ret; } @@ -4658,23 +4636,65 @@ out: */ inline HgfsSuperInfo * -HgfsGetSuperInfo() +HgfsGetSuperInfo(void) { - HgfsSuperInfo *sip; + return hgfsSuperInfo.vfsp ? &hgfsSuperInfo : NULL; +} - /* - * The superInfoHead and hgfsInstance are global variables set by the device - * have of the driver at attach-time (HgfsDevAttach()). We access the soft - * state from the filesystem in this way to ensure consistency with the - * driver. - */ - sip = (HgfsSuperInfo *)ddi_get_soft_state(superInfoHead, hgfsInstance); - if (!sip) { - return NULL; - } - /* Only return the superinfo if the filesystem is mounted. */ - return (sip->vfsp) ? sip : NULL; +/* + *---------------------------------------------------------------------------- + * + * HgfsInitSuperInfo -- + * + * Initializes superinfo structure to indicate that filesystem has been + * mounted and can be used now. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +void +HgfsInitSuperInfo(struct vfs *vfsp) // IN: pointer to vfs being mounted +{ + hgfsSuperInfo.vfsp = vfsp; + + /* For now we are only using the backdoor transport. */ + hgfsSuperInfo.sendRequest = HgfsBackdoorSendRequest; + hgfsSuperInfo.cancelRequest = HgfsBackdoorCancelRequest; + hgfsSuperInfo.transportInit = HgfsBackdoorInit; + hgfsSuperInfo.transportCleanup = HgfsBackdoorCleanup; + + HgfsInitRequestList(&hgfsSuperInfo); + HgfsInitFileHashTable(&hgfsSuperInfo.fileHashTable); +} + +/* + *---------------------------------------------------------------------------- + * + * HgfsClearSuperInfo -- + * + * Clears superinfo structure to indicate that filesystem has been + * unmounted. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +void +HgfsClearSuperInfo(void) +{ + hgfsSuperInfo.vfsp = NULL; } @@ -4685,16 +4705,6 @@ HgfsGetSuperInfo() * * Sets the vnode operations for the provided vnode. * - * Solaris 9: - * We just set the vnode's operations field to point to our statically - * created array of entry points. - * - * Solaris 10: - * The first time this function is called it will create the vnode - * operations and place a pointer to them in the superinfo structure. On - * subsequent invocations the same operations structure will be assigned to - * vnodes. - * * Results: * Returns zero on success and a non-zero error code on error. * @@ -4705,40 +4715,22 @@ HgfsGetSuperInfo() */ int -HgfsSetVnodeOps(HgfsSuperInfo *sip, // IN: Superinfo pointer - struct vnode *vp) // IN: vnode for this file -#if HGFS_VFS_VERSION == 2 +HgfsSetVnodeOps(struct vnode *vp) // IN: vnode for this file { ASSERT(vp); - vp->v_op = &HgfsVnodeOps; - return 0; -} +#if HGFS_VFS_VERSION == 2 + vp->v_op = &hgfsVnodeOps; #else -{ - int ret; - - ASSERT(sip); - ASSERT(vp); - - /* See if we need to create the vnode operations structure */ - if (!sip->vnodeOps) { - ret = HgfsMakeVnodeOps(sip); - if (ret) { - DEBUG(VM_DEBUG_FAIL, "HgfsSetVnodeOps: couldn't create vnode ops.\n"); - return HGFS_ERR; - } else { - DEBUG(VM_DEBUG_INFO, "HgfsSetVnodeOps: made vnode ops.\n"); - } - } - - if (vn_getops(vp) == sip->vnodeOps) { + /* hgfsVnodeOpsP is set up when we mounted HGFS volume. */ + if (vn_getops(vp) == hgfsVnodeOpsP) { DEBUG(VM_DEBUG_INFO, "HgfsSetVnodeOps: vnode ops already set.\n"); } else { DEBUG(VM_DEBUG_INFO, "HgfsSetVnodeOps: we had to set the vnode ops.\n"); - /* Set the operations for this vnode */ - vn_setops(vp, sip->vnodeOps); + /* Set the operations for this vnode. */ + vn_setops(vp, hgfsVnodeOpsP); } +#endif return 0; } @@ -4753,10 +4745,6 @@ HgfsSetVnodeOps(HgfsSuperInfo *sip, // IN: Superinfo pointer * completes, all calls to vn_alloc() for our filesystem should return vnodes * with the correct operations. * - * The operation structure allocated here will be freed in - * HgfsFreevfs() which is called by the Kernel when the filesystem is - * unmounted. - * * Results: * Return 0 on success and non-zero on failure. * @@ -4766,11 +4754,12 @@ HgfsSetVnodeOps(HgfsSuperInfo *sip, // IN: Superinfo pointer *---------------------------------------------------------------------------- */ -static int -HgfsMakeVnodeOps(HgfsSuperInfo *sip) // IN: Superinfo +int +HgfsMakeVnodeOps(void) { +#if HGFS_VFS_VERSION > 2 int ret; - fs_operation_def_t vnodeOpsArr[] = { + static fs_operation_def_t vnodeOpsArr[] = { HGFS_VOP(VOPNAME_OPEN, vop_open, HgfsOpen), HGFS_VOP(VOPNAME_CLOSE, vop_close, HgfsClose), HGFS_VOP(VOPNAME_READ, vop_read, HgfsRead), @@ -4824,11 +4813,9 @@ HgfsMakeVnodeOps(HgfsSuperInfo *sip) // IN: Superinfo HGFS_VOP(VOPNAME_GETSECATTR, vop_getsecattr, fs_fab_acl), HGFS_VOP(VOPNAME_SETSECATTR, vop_setsecattr, HgfsSetsecattr), HGFS_VOP(VOPNAME_SHRLOCK, vop_shrlock, HgfsShrlock), -#if HGFS_VFS_VERSION > 2 HGFS_VOP(VOPNAME_VNEVENT, vop_vnevent, HgfsVnevent), -#endif #if HGFS_VFS_VERSION <= 3 - { NULL, NULL} + { NULL, NULL } #else { NULL, { NULL } } #endif @@ -4837,21 +4824,47 @@ HgfsMakeVnodeOps(HgfsSuperInfo *sip) // IN: Superinfo DEBUG(VM_DEBUG_ENTRY, "HgfsMakeVnodeOps: making vnode ops.\n"); /* - * Create a vnodeops structure and register it with the kernel. We save the - * operations structure in the superinfo so it can be assigned in the + * Create a vnodeops structure and register it with the kernel. + * We save the operations structure so it can be assigned in the * future. */ - ret = vn_make_ops(HGFS_FS_NAME, vnodeOpsArr, &sip->vnodeOps); + ret = vn_make_ops(HGFS_FS_NAME, vnodeOpsArr, &hgfsVnodeOpsP); if (ret) { DEBUG(VM_DEBUG_FAIL, "HgfsMakeVnodeOps: vn_make_ops returned %d\n", ret); - return HGFS_ERR; + return ret; } - DEBUG(VM_DEBUG_DONE, "HgfsMakeVnodeOps: sip->vnodeOps=%p\n", sip->vnodeOps); + DEBUG(VM_DEBUG_DONE, "HgfsMakeVnodeOps: hgfsVnodeOpsP=%p\n", hgfsVnodeOpsP); +#endif return 0; } + + +/* + *---------------------------------------------------------------------------- + * + * HgfsFreeVnodeOps -- + * + * Unregisters vnode operations from the kernel. + * + * Results: + * None. + * + * Side effects: + * The kernel frees memory allocated for our operations structure. + * + *---------------------------------------------------------------------------- + */ + +void +HgfsFreeVnodeOps(void) +{ +#if HGFS_VFS_VERSION > 2 + if (hgfsVnodeOpsP) + vn_freevnodeops(hgfsVnodeOpsP); #endif +} /* @@ -4864,7 +4877,7 @@ HgfsMakeVnodeOps(HgfsSuperInfo *sip) // IN: Superinfo * * HgfsSubmitRequest -- * - * Places a request on the queue for submission to guestd, then waits for + * Sends request through the transport channel, then waits for * the response. * * Both submitting request and waiting for reply are in this function @@ -4886,64 +4899,32 @@ HgfsSubmitRequest(HgfsSuperInfo *sip, // IN: Superinfo containing request list // condition variable, and mutex HgfsReq *req) // IN: Request to submit { + int ret = 0; + ASSERT(sip); ASSERT(req); - /* The device must be open to submit requests */ - if (!sip->devOpen) { - /* - * Destroy the request here to make things simpler for callers: they - * only need to destroy the request if they get a reply. - */ - HgfsReqSetState(req, HGFS_REQ_ERROR); - HgfsDestroyReq(sip, req); - return ENODEV; - } - - /* - * The process of submitting the request involves putting it on the request - * list, waking up guestd if it is waiting for a request, then atomically - * waiting for the reply. We must loop on cv_wait_sig(9F) because Solaris - * says it may return prematurely. We must signify that we have abandoned - * the request if we are interrupted. - */ - mutex_enter(&sip->reqMutex); - HgfsEnqueueRequest(sip, req); + if (sip->vfsp->vfs_flag & VFS_UNMOUNTED) { + DEBUG(VM_DEBUG_REQUEST, "HgfsSubmitRequest(): filesystem not mounted.\n"); + ret = ENODEV; + goto out; + } + + ret = HgfsSendRequest(sip, req); + if (ret) { + DEBUG(VM_DEBUG_REQUEST, "HgfsSubmitRequest(): transport failed.\n"); + goto out; + } /* - * We unconditionally signal the request list's condition variable because - * if guestd is waiting then we want to wake it up and if guestd isn't - * waiting then this has no effect. - * - * We also call pollwakeup to let callers of poll(2) on the device know - * there is now something to read. XXX: This is bad because a caller of - * poll(2) could end up blocked in read(2) if operations are interleaved - * just so. For now we only allow the device to be opened once to prevent - * this. - */ - cv_signal(&sip->reqCondVar); - /* - * XXX: This will wake any process waiting for a POLLRDNORM event on our - * device. We should probably change this so HgfsDevChpoll() tells us what - * event to signal since it knows what guestd is waiting for. This would - * prevent having to change this macro should guestd or the poll() - * implementation ever change. It seems like that would be a bit messy - * though, so we've left this as is. - * - * We release and reacquire the request mutex around the call to - * pollwakeup() since HgfsDevChpoll() also acquires it, and that's not - * allowed (see chpoll(9E) man page). This is completely safe since nothing - * bad can happen to the request by allowing other code to run; we release - * this same lock in the loop below when we call cv_wait_sig(). + * If we are using synchronous transport we should have the result right + * here and status will not be equal HGFS_REQ_SUBMITTED. If we are using + * async transport we'll sleep till somebody wakes us up. */ - mutex_exit(&sip->reqMutex); - pollwakeup(&sip->hgfsPollhead, POLLRDNORM); - mutex_enter(&sip->reqMutex); - - while (HgfsReqGetState(req) == HGFS_REQ_SUBMITTED) { + while (req->state == HGFS_REQ_SUBMITTED) { k_sigset_t oldIgnoreSet; //DEBUG(VM_DEBUG_SIG, "HgfsSubmitRequest: currproc is %s.\n", u.u_comm); @@ -4953,7 +4934,7 @@ HgfsSubmitRequest(HgfsSuperInfo *sip, // IN: Superinfo containing request list if (cv_wait_sig(&req->condVar, &sip->reqMutex) == 0) { /* * We received a system signal (e.g., SIGKILL) while waiting for the - * reply from guestd. + * reply. * * Since we gave up the mutex while waiting on the condition * variable, we must make sure the reply didn't come /after/ we were @@ -4970,20 +4951,18 @@ HgfsSubmitRequest(HgfsSuperInfo *sip, // IN: Superinfo containing request list DEBUG(VM_DEBUG_SIG, "HgfsSubmitRequest(): interrupted while waiting for reply.\n"); - if (HgfsReqGetState(req) != HGFS_REQ_SUBMITTED) { + if (req->state != HGFS_REQ_SUBMITTED) { /* It it's not SUBMITTED, it must be COMPLETED or ERROR */ - ASSERT((HgfsReqGetState(req) == HGFS_REQ_COMPLETED) || - (HgfsReqGetState(req) == HGFS_REQ_ERROR)); - - DEBUG(VM_DEBUG_REQUEST, "HgfsSubmitRequest(): destroying request.\n"); - HgfsDestroyReq(sip, req); + ASSERT(req->state == HGFS_REQ_COMPLETED || + req->state == HGFS_REQ_ERROR); + DEBUG(VM_DEBUG_REQUEST, "HgfsSubmitRequest(): request not in submitted status.\n"); } else { DEBUG(VM_DEBUG_REQUEST, "HgfsSubmitRequest(): setting request state to abandoned.\n"); - HgfsReqSetState(req, HGFS_REQ_ABANDONED); + req->state = HGFS_REQ_ABANDONED; } - mutex_exit(&sip->reqMutex); - return EINTR; + ret = EINTR; + goto out; } HgfsRestoreSignals(&oldIgnoreSet); @@ -4992,9 +4971,9 @@ HgfsSubmitRequest(HgfsSuperInfo *sip, // IN: Superinfo containing request list /* The reply should now be in req->packet. */ DEBUG(VM_DEBUG_SIG, "HgfsSubmitRequest(): awoken because reply received.\n"); + out: mutex_exit(&sip->reqMutex); - - return 0; + return ret; } @@ -5023,7 +5002,7 @@ HgfsValidateReply(HgfsReq *req, // IN: Request that contains reply data ASSERT(req); ASSERT(minSize <= HGFS_PACKET_MAX); /* we want to know if this fails */ - switch (HgfsReqGetState(req)) { + switch (req->state) { case HGFS_REQ_ERROR: DEBUG(VM_DEBUG_FAIL, "HgfsValidateReply(): received reply with error.\n"); return -1; @@ -5519,7 +5498,7 @@ HgfsMakeFullName(const char *path, // IN: Path of directory containing fil DEBUG(VM_DEBUG_INFO, "HgfsMakeFullName:\n" " path: \"%s\" (%d)\n" " file: \"%s\" (%ld)\n", - path, pathLen, file, strlen(file)); + path, pathLen, file, (long) strlen(file)); /* * Here there are three possibilities: diff --git a/open-vm-tools/modules/solaris/vmhgfs/vnode.h b/open-vm-tools/modules/solaris/vmhgfs/vnode.h index 3ea551945..e1a1551a7 100644 --- a/open-vm-tools/modules/solaris/vmhgfs/vnode.h +++ b/open-vm-tools/modules/solaris/vmhgfs/vnode.h @@ -25,7 +25,11 @@ #include "hgfsSolaris.h" -int HgfsSetVnodeOps(HgfsSuperInfo *sip, struct vnode *vp); +int HgfsSetVnodeOps(struct vnode *vp); inline HgfsSuperInfo *HgfsGetSuperInfo(void); +void HgfsInitSuperInfo(struct vfs *vfsp); +void HgfsClearSuperInfo(void); +int HgfsMakeVnodeOps(void); +void HgfsFreeVnodeOps(void); #endif diff --git a/open-vm-tools/modules/solaris/vmhgfs/x86cpuid.h b/open-vm-tools/modules/solaris/vmhgfs/x86cpuid.h index eae114d77..31b6cdd24 100644 --- a/open-vm-tools/modules/solaris/vmhgfs/x86cpuid.h +++ b/open-vm-tools/modules/solaris/vmhgfs/x86cpuid.h @@ -438,7 +438,7 @@ FLAGDEF( 87, EDX, AMD, 4, 1, TM, NA, IGNORE, 0, FALSE) FLAGDEF( 87, EDX, AMD, 5, 1, STC, NA, IGNORE, 0, FALSE) \ FLAGDEF( 87, EDX, AMD, 6, 1, 100MHZSTEPS, NA, IGNORE, 0, FALSE) \ FLAGDEF( 87, EDX, AMD, 7, 1, HWPSTATE, NA, IGNORE, 0, FALSE) \ -FLAGDEF( 87, EDX, AMD, 8, 1, TSC_INVARIANT, NA, IGNORE, 0, FALSE) \ +FLAGDEF( 87, EDX, COMMON, 8, 1, TSC_INVARIANT, NA, IGNORE, 0, FALSE) \ FIELDDEFA(88, EAX, COMMON, 0, 8, PHYSBITS, NA, IGNORE, 0, FALSE, PHYS_BITS) \ FIELDDEFA(88, EAX, COMMON, 8, 8, VIRTBITS, NA, IGNORE, 0, FALSE, VIRT_BITS) \ FIELDDEFA(88, ECX, AMD, 0, 8, CORE_COUNT, NA, IGNORE, 0, FALSE, AMD_CORE_COUNT) \ @@ -642,6 +642,9 @@ FIELD_FUNC(MWAIT_C4_SUBSTATE, CPUID_INTEL_ID5EDX_MWAIT_C4_SUBSTATE) #define CPUID_MODEL_PIII_08 8 #define CPUID_MODEL_PIII_0A 10 +/* AMD model information */ +#define CPUID_MODEL_BARCELONA_02 0x02 // Barcelona (both Opteron & Phenom kind) + /* *---------------------------------------------------------------------- * @@ -813,6 +816,17 @@ CPUID_FAMILY_IS_K8STAR(uint32 _eax) CPUID_FAMILY_IS_K8MOBILE(_eax); } +/* + * AMD Barcelona (of either Opteron or Phenom kind). + */ +static INLINE Bool +CPUID_MODEL_IS_BARCELONA(uint32 v) // IN: %eax from CPUID with %eax=1. +{ + /* Assumes the CPU manufacturer is AMD. */ + return CPUID_FAMILY_IS_K8L(v) && + CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_BARCELONA_02; +} + #define CPUID_TYPE_PRIMARY 0 #define CPUID_TYPE_OVERDRIVE 1 diff --git a/open-vm-tools/modules/solaris/vmxnet/COPYING.CDDL b/open-vm-tools/modules/solaris/vmxnet/COPYING.CDDL new file mode 100644 index 000000000..883d599cc --- /dev/null +++ b/open-vm-tools/modules/solaris/vmxnet/COPYING.CDDL @@ -0,0 +1,365 @@ + +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 + +1. Definitions. + + 1.1. "Contributor" means each individual or entity that creates + or contributes to the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Software, prior Modifications used by a Contributor (if any), + and the Modifications made by that particular Contributor. + + 1.3. "Covered Software" means (a) the Original Software, or (b) + Modifications, or (c) the combination of files containing + Original Software with files containing Modifications, in + each case including portions thereof. + + 1.4. "Executable" means the Covered Software in any form other + than Source Code. + + 1.5. "Initial Developer" means the individual or entity that first + makes Original Software available under this License. + + 1.6. "Larger Work" means a work which combines Covered Software or + portions thereof with code not governed by the terms of this + License. + + 1.7. "License" means this document. + + 1.8. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed + herein. + + 1.9. "Modifications" means the Source Code and Executable form of + any of the following: + + A. Any file that results from an addition to, deletion from or + modification of the contents of a file containing Original + Software or previous Modifications; + + B. Any new file that contains any part of the Original + Software or previous Modifications; or + + C. Any new file that is contributed or otherwise made + available under the terms of this License. + + 1.10. "Original Software" means the Source Code and Executable + form of computer software code that is originally released + under this License. + + 1.11. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, + process, and apparatus claims, in any patent Licensable by + grantor. + + 1.12. "Source Code" means (a) the common form of computer software + code in which modifications are made and (b) associated + documentation included in or with such code. + + 1.13. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms + of, this License. For legal entities, "You" includes any + entity which controls, is controlled by, or is under common + control with You. For purposes of this definition, + "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty + percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants. + + 2.1. The Initial Developer Grant. + + Conditioned upon Your compliance with Section 3.1 below and + subject to third party intellectual property claims, the Initial + Developer hereby grants You a world-wide, royalty-free, + non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer, to use, + reproduce, modify, display, perform, sublicense and + distribute the Original Software (or portions thereof), + with or without Modifications, and/or as part of a Larger + Work; and + + (b) under Patent Claims infringed by the making, using or + selling of Original Software, to make, have made, use, + practice, sell, and offer for sale, and/or otherwise + dispose of the Original Software (or portions thereof). + + (c) The licenses granted in Sections 2.1(a) and (b) are + effective on the date Initial Developer first distributes + or otherwise makes the Original Software available to a + third party under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: (1) for code that You delete from the Original + Software, or (2) for infringements caused by: (i) the + modification of the Original Software, or (ii) the + combination of the Original Software with other software + or devices. + + 2.2. Contributor Grant. + + Conditioned upon Your compliance with Section 3.1 below and + subject to third party intellectual property claims, each + Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor to use, reproduce, + modify, display, perform, sublicense and distribute the + Modifications created by such Contributor (or portions + thereof), either on an unmodified basis, with other + Modifications, as Covered Software and/or as part of a + Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either + alone and/or in combination with its Contributor Version + (or portions of such combination), to make, use, sell, + offer for sale, have made, and/or otherwise dispose of: + (1) Modifications made by that Contributor (or portions + thereof); and (2) the combination of Modifications made by + that Contributor with its Contributor Version (or portions + of such combination). + + (c) The licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first distributes or + otherwise makes the Modifications available to a third + party. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: (1) for any code that Contributor has deleted + from the Contributor Version; (2) for infringements caused + by: (i) third party modifications of Contributor Version, + or (ii) the combination of Modifications made by that + Contributor with other software (except as part of the + Contributor Version) or other devices; or (3) under Patent + Claims infringed by Covered Software in the absence of + Modifications made by that Contributor. + +3. Distribution Obligations. + + 3.1. Availability of Source Code. + + Any Covered Software that You distribute or otherwise make + available in Executable form must also be made available in Source + Code form and that Source Code form must be distributed only under + the terms of this License. You must include a copy of this + License with every copy of the Source Code form of the Covered + Software You distribute or otherwise make available. You must + inform recipients of any such Covered Software in Executable form + as to how they can obtain such Covered Software in Source Code + form in a reasonable manner on or through a medium customarily + used for software exchange. + + 3.2. Modifications. + + The Modifications that You create or to which You contribute are + governed by the terms of this License. You represent that You + believe Your Modifications are Your original creation(s) and/or + You have sufficient rights to grant the rights conveyed by this + License. + + 3.3. Required Notices. + + You must include a notice in each of Your Modifications that + identifies You as the Contributor of the Modification. You may + not remove or alter any copyright, patent or trademark notices + contained within the Covered Software, or any notices of licensing + or any descriptive text giving attribution to any Contributor or + the Initial Developer. + + 3.4. Application of Additional Terms. + + You may not offer or impose any terms on any Covered Software in + Source Code form that alters or restricts the applicable version + of this License or the recipients' rights hereunder. You may + choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of + Covered Software. However, you may do so only on Your own behalf, + and not on behalf of the Initial Developer or any Contributor. + You must make it absolutely clear that any such warranty, support, + indemnity or liability obligation is offered by You alone, and You + hereby agree to indemnify the Initial Developer and every + Contributor for any liability incurred by the Initial Developer or + such Contributor as a result of warranty, support, indemnity or + liability terms You offer. + + 3.5. Distribution of Executable Versions. + + You may distribute the Executable form of the Covered Software + under the terms of this License or under the terms of a license of + Your choice, which may contain terms different from this License, + provided that You are in compliance with the terms of this License + and that the license for the Executable form does not attempt to + limit or alter the recipient's rights in the Source Code form from + the rights set forth in this License. If You distribute the + Covered Software in Executable form under a different license, You + must make it absolutely clear that any terms which differ from + this License are offered by You alone, not by the Initial + Developer or Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred + by the Initial Developer or such Contributor as a result of any + such terms You offer. + + 3.6. Larger Works. + + You may create a Larger Work by combining Covered Software with + other code not governed by the terms of this License and + distribute the Larger Work as a single product. In such a case, + You must make sure the requirements of this License are fulfilled + for the Covered Software. + +4. Versions of the License. + + 4.1. New Versions. + + Sun Microsystems, Inc. is the initial license steward and may + publish revised and/or new versions of this License from time to + time. Each version will be given a distinguishing version number. + Except as provided in Section 4.3, no one other than the license + steward has the right to modify this License. + + 4.2. Effect of New Versions. + + You may always continue to use, distribute or otherwise make the + Covered Software available under the terms of the version of the + License under which You originally received the Covered Software. + If the Initial Developer includes a notice in the Original + Software prohibiting it from being distributed or otherwise made + available under any subsequent version of the License, You must + distribute and make the Covered Software available under the terms + of the version of the License under which You originally received + the Covered Software. Otherwise, You may also choose to use, + distribute or otherwise make the Covered Software available under + the terms of any subsequent version of the License published by + the license steward. + + 4.3. Modified Versions. + + When You are an Initial Developer and You want to create a new + license for Your Original Software, You may create and use a + modified version of this License if You: (a) rename the license + and remove any references to the name of the license steward + (except to note that the license differs from this License); and + (b) otherwise make it clear that the license contains terms which + differ from this License. + +5. DISCLAIMER OF WARRANTY. + + COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" + BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED + SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR + PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND + PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY + COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE + INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY + NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF + WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS + DISCLAIMER. + +6. TERMINATION. + + 6.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to + cure such breach within 30 days of becoming aware of the breach. + Provisions which, by their nature, must remain in effect beyond + the termination of this License shall survive. + + 6.2. If You assert a patent infringement claim (excluding + declaratory judgment actions) against Initial Developer or a + Contributor (the Initial Developer or Contributor against whom You + assert such claim is referred to as "Participant") alleging that + the Participant Software (meaning the Contributor Version where + the Participant is a Contributor or the Original Software where + the Participant is the Initial Developer) directly or indirectly + infringes any patent, then any and all rights granted directly or + indirectly to You by such Participant, the Initial Developer (if + the Initial Developer is not the Participant) and all Contributors + under Sections 2.1 and/or 2.2 of this License shall, upon 60 days + notice from Participant terminate prospectively and automatically + at the expiration of such 60 day notice period, unless if within + such 60 day period You withdraw Your claim with respect to the + Participant Software against such Participant either unilaterally + or pursuant to a written agreement with Participant. + + 6.3. In the event of termination under Sections 6.1 or 6.2 above, + all end user licenses that have been validly granted by You or any + distributor hereunder prior to termination (excluding licenses + granted to You by any distributor) shall survive termination. + +7. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE + INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF + COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE + LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR + CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT + LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK + STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL + INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT + APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO + NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR + CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT + APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. + + The Covered Software is a "commercial item," as that term is + defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial + computer software" (as that term is defined at 48 + C.F.R. 252.227-7014(a)(1)) and "commercial computer software + documentation" as such terms are used in 48 C.F.R. 12.212 + (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 + C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all + U.S. Government End Users acquire Covered Software with only those + rights set forth herein. This U.S. Government Rights clause is in + lieu of, and supersedes, any other FAR, DFAR, or other clause or + provision that addresses Government rights in computer software + under this License. + +9. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed + by the law of the jurisdiction specified in a notice contained + within the Original Software (except to the extent applicable law, + if any, provides otherwise), excluding such jurisdiction's + conflict-of-law provisions. Any litigation relating to this + License shall be subject to the jurisdiction of the courts located + in the jurisdiction and venue specified in a notice contained + within the Original Software, with the losing party responsible + for costs, including, without limitation, court costs and + reasonable attorneys' fees and expenses. The application of the + United Nations Convention on Contracts for the International Sale + of Goods is expressly excluded. Any law or regulation which + provides that the language of a contract shall be construed + against the drafter shall not apply to this License. You agree + that You alone are responsible for compliance with the United + States export administration regulations (and the export control + laws and regulation of any other countries) when You use, + distribute or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or + indirectly, out of its utilization of rights under this License + and You agree to work with Initial Developer and Contributors to + distribute such responsibility on an equitable basis. Nothing + herein is intended or shall be deemed to constitute any admission + of liability. + diff --git a/open-vm-tools/modules/solaris/vmxnet/Makefile b/open-vm-tools/modules/solaris/vmxnet/Makefile new file mode 100644 index 000000000..9d94e0ff9 --- /dev/null +++ b/open-vm-tools/modules/solaris/vmxnet/Makefile @@ -0,0 +1,111 @@ +#!/usr/bin/make -f +########################################################## +# Copyright (C) 2009 VMware, Inc. All rights reserved. +# +# The contents of this file are subject to the terms of the Common +# Development and Distribution License (the "License") version 1.0 +# and no later version. You may not use this file except in +# compliance with the License. +# +# You can obtain a copy of the License at +# http://www.opensource.org/licenses/cddl1.php +# +# See the License for the specific language governing permissions +# and limitations under the License. +# +########################################################## + +## +## General build locations and variables +## + +MODULE := vmxnet +MODULE_32 := i386/$(MODULE) +MODULE_64 := amd64/$(MODULE) + +CFLAGS := +LDFLAGS := + +CFLAGS += -O2 +CFLAGS += -Wall -Werror +CFLAGS += -U_NO_LONGLONG +CFLAGS += -D_KERNEL +CFLAGS += -I../../../lib/include # for buildNumber.h + +## +## Objects needed to build the HGFS kernel module +## +VMXNET_OBJS := vmxnet.o + +VMXNET_32_OBJS := $(addprefix i386/, $(VMXNET_OBJS)) +VMXNET_64_OBJS := $(addprefix amd64/, $(VMXNET_OBJS)) + +ifeq ($(shell echo "$(VM_UNAME)" | cut -c-4),5.9) # { + +# Solaris 9 +MODULES := $(MODULE_32) + +else # } { +ifeq ($(shell echo "$(VM_UNAME)" | cut -c-4),5.10) # { + +# Solaris 10 +MODULES := $(MODULE_32) $(MODULE_64) +CFLAGS += -ffreestanding +CFLAGS += -nodefaultlibs + +else # } { + +$(error "This makefile is only used for Solaris 9 and 10") + +endif # } +endif # } + +CFLAGS_32 := $(CFLAGS) +CFLAGS_32 += -m32 +LDFLAGS_32 := $(LDFLAGS) + +CFLAGS_64 := $(CFLAGS) +CFLAGS_64 += -m64 +CFLAGS_64 += -mcmodel=kernel +CFLAGS_64 += -mno-red-zone +LDFLAGS_64 := $(LDFLAGS) +ifdef HAVE_GNU_LD +LDFLAGS_64 += -m elf_x86_64 +else +LDFLAGS_64 += -64 +endif + +all: prepare_dirs $(MODULES) + +prepare_dirs: + @echo "Creating build directories" + mkdir -p i386 + mkdir -p amd64 + +$(MODULE_32): $(VMXNET_32_OBJS) + @echo "Linking $@" + $(LD) $(LDFLAGS_32) -r $(VMXNET_32_OBJS) -o $@ + +$(VMXNET_32_OBJS): i386/%.o: %.c + @echo "Compiling $(>= 1) { + crc = (crc << 1) ^ ((((crc < 0 ? 1 : 0) ^ bit) & 0x01) ? + CRC_POLYNOMIAL_BE : 0); + } + } + hashcode = (crc & 1); /* hashcode is 6 LSb of CRC ... */ + for (j = 0; j < 5; j++) { /* ... in reverse order. */ + hashcode = (hashcode << 1) | ((crc>>=1) & 1); + } + + ladrf[hashcode >> 3] |= 1 << (hashcode & 0x07); +} + +#endif // USERLEVEL + +#endif // VMWARE_DEVICES_NET_H diff --git a/open-vm-tools/modules/solaris/vmxnet/net_sg.h b/open-vm-tools/modules/solaris/vmxnet/net_sg.h new file mode 100644 index 000000000..c90e37693 --- /dev/null +++ b/open-vm-tools/modules/solaris/vmxnet/net_sg.h @@ -0,0 +1,70 @@ +/********************************************************* + * Copyright (C) 2000 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * net_sg.h -- + * + * Network packet scatter gather structure. + */ + + +#ifndef _NET_SG_H +#define _NET_SG_H + +#define INCLUDE_ALLOW_USERLEVEL +#define INCLUDE_ALLOW_VMMEXT +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_VMNIXMOD +#define INCLUDE_ALLOW_VMK_MODULE +#define INCLUDE_ALLOW_VMKERNEL +#define INCLUDE_ALLOW_DISTRIBUTE +#include "includeCheck.h" + +#define NET_SG_DEFAULT_LENGTH 16 + +/* + * A single scatter-gather element for a network packet. + * The address is split into low and high to save space. + * If we make it 64 bits then Windows pads things out such that + * we lose a lot of space for each scatter gather array. + * This adds up when you have embedded scatter-gather + * arrays for transmit and receive ring buffers. + */ +typedef struct NetSG_Elem { + uint32 addrLow; + uint16 addrHi; + uint16 length; +} NetSG_Elem; + +typedef enum NetSG_AddrType { + NET_SG_MACH_ADDR, + NET_SG_PHYS_ADDR, + NET_SG_VIRT_ADDR, + NET_SG_VMM_STACK_OFFSET, +} NetSG_AddrType; + +typedef struct NetSG_Array { + uint16 addrType; + uint16 length; + NetSG_Elem sg[NET_SG_DEFAULT_LENGTH]; +} NetSG_Array; + +#define NET_SG_SIZE(len) (sizeof(NetSG_Array) + (len - NET_SG_DEFAULT_LENGTH) * sizeof(NetSG_Elem)) + +#define NET_SG_MAKE_PA(elem) (PA)QWORD(elem.addrHi, elem.addrLow) +#define NET_SG_MAKE_PTR(elem) (char *)(uintptr_t)QWORD(elem.addrHi, elem.addrLow) + +#endif diff --git a/open-vm-tools/modules/solaris/vmxnet/vm_basic_types.h b/open-vm-tools/modules/solaris/vmxnet/vm_basic_types.h new file mode 100644 index 000000000..dc0ad6108 --- /dev/null +++ b/open-vm-tools/modules/solaris/vmxnet/vm_basic_types.h @@ -0,0 +1,956 @@ +/********************************************************* + * Copyright (C) 1998-2008 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * + * vm_basic_types.h -- + * + * basic data types. + */ + + +#ifndef _VM_BASIC_TYPES_H_ +#define _VM_BASIC_TYPES_H_ + +#define INCLUDE_ALLOW_USERLEVEL +#define INCLUDE_ALLOW_VMMEXT +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_VMMON +#define INCLUDE_ALLOW_VMNIXMOD +#define INCLUDE_ALLOW_VMKERNEL +#define INCLUDE_ALLOW_VMKDRIVERS +#define INCLUDE_ALLOW_VMK_MODULE +#define INCLUDE_ALLOW_DISTRIBUTE +#define INCLUDE_ALLOW_VMCORE +#define INCLUDE_ALLOW_VMIROM +#include "includeCheck.h" + +/* STRICT ANSI means the Xserver build and X defines Bool differently. */ +#if !defined(__STRICT_ANSI__) || defined(__FreeBSD__) +typedef char Bool; +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#define IsBool(x) (((x) & ~1) == 0) +#define IsBool2(x, y) ((((x) | (y)) & ~1) == 0) + +/* + * Macros __i386__ and __ia64 are intrinsically defined by GCC + */ +#ifdef __i386__ +#define VM_I386 +#endif + +#ifdef _WIN64 +#define __x86_64__ +#endif + +#ifdef __x86_64__ +#define VM_X86_64 +#define VM_I386 +#define vm_x86_64 (1) +#else +#define vm_x86_64 (0) +#endif + + + +#ifdef _WIN32 +/* safe assumption for a while */ +#define VM_I386 +#endif + +#ifdef _MSC_VER + +#pragma warning (3 :4505) // unreferenced local function +#pragma warning (disable :4018) // signed/unsigned mismatch +#pragma warning (disable :4761) // integral size mismatch in argument; conversion supplied +#pragma warning (disable :4305) // truncation from 'const int' to 'short' +#pragma warning (disable :4244) // conversion from 'unsigned short' to 'unsigned char' +#pragma warning (disable :4267) // truncation of 'size_t' +#pragma warning (disable :4146) // unary minus operator applied to unsigned type, result still unsigned +#pragma warning (disable :4142) // benign redefinition of type + +#endif + +#if defined(__APPLE__) || defined(HAVE_STDINT_H) + +/* + * TODO: This is a C99 standard header. We should be able to test for + * #if __STDC_VERSION__ >= 199901L, but that breaks the Netware build + * (which doesn't have stdint.h). + */ + +#include + +typedef uint64_t uint64; +typedef int64_t int64; +typedef uint32_t uint32; +typedef int32_t int32; +typedef uint16_t uint16; +typedef int16_t int16; +typedef uint8_t uint8; + +/* + * XXX: int8_t is defined to be 'signed char' on Mac hosts. + * + * Unfortunately, GCC 4.0.1 warns when doing pointer assignment or + * comparison between signed char * and char * (even if char is + * signed). + * + * If we want to use int8_t to define int8, we need to go through and + * replace uses of char * with signed char * to prevent warnings. + */ +typedef char int8; + +#else /* !HAVE_STDINT_H */ + +#ifdef _MSC_VER + +typedef unsigned __int64 uint64; +typedef signed __int64 int64; + +#elif __GNUC__ +/* The Xserver source compiles with -ansi -pendantic */ +#ifndef __STRICT_ANSI__ +#if defined(VM_X86_64) +typedef unsigned long uint64; +typedef long int64; +#else +typedef unsigned long long uint64; +typedef long long int64; +#endif +#elif __FreeBSD__ +typedef unsigned long long uint64; +typedef long long int64; +#endif +#else +#error - Need compiler define for int64/uint64 +#endif /* _MSC_VER */ + +typedef unsigned int uint32; +typedef unsigned short uint16; +typedef unsigned char uint8; + +typedef int int32; +typedef short int16; +typedef char int8; + +#endif /* HAVE_STDINT_H */ + +/* + * FreeBSD (for the tools build) unconditionally defines these in + * sys/inttypes.h so don't redefine them if this file has already + * been included. [greg] + * + * This applies to Solaris as well. + */ + +/* + * Before trying to do the includes based on OS defines, see if we can use + * feature-based defines to get as much functionality as possible + */ + +#ifdef HAVE_INTTYPES_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_INTTYPES_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef __FreeBSD__ +#include /* For __FreeBSD_version */ +#endif + +#if !defined(USING_AUTOCONF) +# if defined(__FreeBSD__) || defined(sun) +# ifdef KLD_MODULE +# include +# else +# if !defined(VMKERNEL) && (__FreeBSD_version >= 500043) +# include +# include +# else +# include +# endif +# endif +# elif defined __APPLE__ +# if KERNEL +# include +# include /* mostly for size_t */ +# include +# else +# include +# include +# include +# include +# endif +# else +# if !defined(__intptr_t_defined) && !defined(intptr_t) +# define __intptr_t_defined +# define intptr_t intptr_t +# ifdef VM_I386 +# ifdef VM_X86_64 +typedef int64 intptr_t; +# else +typedef int32 intptr_t; +# endif +# endif +# endif + +# ifndef _STDINT_H +# ifdef VM_I386 +# ifdef VM_X86_64 +typedef uint64 uintptr_t; +# else +typedef uint32 uintptr_t; +# endif +# endif +# endif +# endif +#endif + + +/* + * Time + * XXX These should be cleaned up. -- edward + */ + +typedef int64 VmTimeType; /* Time in microseconds */ +typedef int64 VmTimeRealClock; /* Real clock kept in microseconds */ +typedef int64 VmTimeVirtualClock; /* Virtual Clock kept in CPU cycles */ + +/* + * Printf format specifiers for size_t and 64-bit number. + * Use them like this: + * printf("%"FMT64"d\n", big); + * + * FMTH is for handles/fds. + */ + +#ifdef _MSC_VER + #define FMT64 "I64" + #ifdef VM_X86_64 + #define FMTSZ "I64" + #define FMTPD "I64" + #define FMTH "I64" + #else + #define FMTSZ "I" + #define FMTPD "I" + #define FMTH "I" + #endif +#elif defined __APPLE__ + /* Mac OS hosts use the same formatters for 32- and 64-bit. */ + #define FMT64 "ll" + #define FMTSZ "z" + #define FMTPD "l" + #define FMTH "" +#elif __GNUC__ + #define FMTH "" + #if defined(N_PLAT_NLM) || defined(sun) || \ + (defined(__FreeBSD__) && (__FreeBSD__ + 0) && ((__FreeBSD__ + 0) < 5)) + /* + * Why (__FreeBSD__ + 0)? See bug 141008. + * Yes, we really need to test both (__FreeBSD__ + 0) and + * ((__FreeBSD__ + 0) < 5). No, we can't remove "+ 0" from + * ((__FreeBSD__ + 0) < 5). + */ + #ifdef VM_X86_64 + #define FMTSZ "l" + #define FMTPD "l" + #else + #define FMTSZ "" + #define FMTPD "" + #endif + #elif defined(__linux__) \ + || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) \ + || (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L) \ + || (defined(_POSIX2_VERSION) && _POSIX2_VERSION >= 200112L) + /* BSD, Linux */ + #define FMTSZ "z" + + #if defined(VM_X86_64) + #define FMTPD "l" + #else + #define FMTPD "" + #endif + #else + /* Systems with a pre-C99 libc */ + #define FMTSZ "Z" + #ifdef VM_X86_64 + #define FMTPD "l" + #else + #define FMTPD "" + #endif + #endif + #ifdef VM_X86_64 + #define FMT64 "l" + #elif defined(sun) || defined(__FreeBSD__) + #define FMT64 "ll" + #else + #define FMT64 "L" + #endif +#else + #error - Need compiler define for FMT64 and FMTSZ +#endif + +/* + * Suffix for 64-bit constants. Use it like this: + * CONST64(0x7fffffffffffffff) for signed or + * CONST64U(0x7fffffffffffffff) for unsigned. + * + * 2004.08.30(thutt): + * The vmcore/asm64/gen* programs are compiled as 32-bit + * applications, but must handle 64 bit constants. If the + * 64-bit-constant defining macros are already defined, the + * definition will not be overwritten. + */ + +#if !defined(CONST64) || !defined(CONST64U) +#ifdef _MSC_VER +#define CONST64(c) c##I64 +#define CONST64U(c) c##uI64 +#elif defined __APPLE__ +#define CONST64(c) c##LL +#define CONST64U(c) c##uLL +#elif __GNUC__ +#ifdef VM_X86_64 +#define CONST64(c) c##L +#define CONST64U(c) c##uL +#else +#define CONST64(c) c##LL +#define CONST64U(c) c##uLL +#endif +#else +#error - Need compiler define for CONST64 +#endif +#endif + +/* + * Use CONST3264/CONST3264U if you want a constant to be + * treated as a 32-bit number on 32-bit compiles and + * a 64-bit number on 64-bit compiles. Useful in the case + * of shifts, like (CONST3264U(1) << x), where x could be + * more than 31 on a 64-bit compile. + */ + +#ifdef VM_X86_64 + #define CONST3264(a) CONST64(a) + #define CONST3264U(a) CONST64U(a) +#else + #define CONST3264(a) (a) + #define CONST3264U(a) (a) +#endif + +#define MIN_INT32 ((int32)0x80000000) +#define MAX_INT32 ((int32)0x7fffffff) + +#define MIN_UINT32 ((uint32)0) +#define MAX_UINT32 ((uint32)0xffffffff) + +#define MIN_INT64 (CONST64(0x8000000000000000)) +#define MAX_INT64 (CONST64(0x7fffffffffffffff)) + +#define MIN_UINT64 (CONST64U(0)) +#define MAX_UINT64 (CONST64U(0xffffffffffffffff)) + +typedef uint8 *TCA; /* Pointer into TC (usually). */ + +/* + * Type big enough to hold an integer between 0..100 + */ +typedef uint8 Percent; +#define AsPercent(v) ((Percent)(v)) +#define CHOOSE_PERCENT AsPercent(101) + + +typedef uintptr_t VA; +typedef uintptr_t VPN; + +typedef uint64 PA; +typedef uint32 PPN; + +typedef uint64 PhysMemOff; +typedef uint64 PhysMemSize; + +/* The Xserver source compiles with -ansi -pendantic */ +#ifndef __STRICT_ANSI__ +typedef uint64 BA; +#endif +typedef uint32 BPN; +typedef uint32 PageNum; +typedef unsigned MemHandle; +typedef int32 World_ID; + +#define INVALID_WORLD_ID ((World_ID)-1) + +typedef World_ID User_CartelID; +#define INVALID_CARTEL_ID INVALID_WORLD_ID + +typedef User_CartelID User_SessionID; +#define INVALID_SESSION_ID INVALID_CARTEL_ID + +typedef User_CartelID User_CartelGroupID; +#define INVALID_CARTELGROUP_ID INVALID_CARTEL_ID + +typedef uint32 Worldlet_ID; +#define INVALID_WORLDLET_ID ((Worldlet_ID)-1) + +/* world page number */ +typedef uint32 WPN; + +/* The Xserver source compiles with -ansi -pendantic */ +#ifndef __STRICT_ANSI__ +typedef uint64 MA; +typedef uint32 MPN; +#endif + +/* + * This type should be used for variables that contain sector + * position/quantity. + */ +typedef uint64 SectorType; + +/* + * Linear address + */ + +typedef uintptr_t LA; +typedef uintptr_t LPN; +#define LA_2_LPN(_la) ((_la) >> PAGE_SHIFT) +#define LPN_2_LA(_lpn) ((_lpn) << PAGE_SHIFT) + +#define LAST_LPN ((((LA) 1) << (8 * sizeof(LA) - PAGE_SHIFT)) - 1) +#define LAST_LPN32 ((((LA32)1) << (8 * sizeof(LA32) - PAGE_SHIFT)) - 1) +#define LAST_LPN64 ((((LA64)1) << (8 * sizeof(LA64) - PAGE_SHIFT)) - 1) + +/* Valid bits in a LPN. */ +#define LPN_MASK LAST_LPN +#define LPN_MASK32 LAST_LPN32 +#define LPN_MASK64 LAST_LPN64 + +/* + * On 64 bit platform, address and page number types default + * to 64 bit. When we need to represent a 32 bit address, we use + * types defined below. + * + * On 32 bit platform, the following types are the same as the + * default types. + */ +typedef uint32 VA32; +typedef uint32 VPN32; +typedef uint32 LA32; +typedef uint32 LPN32; +typedef uint32 PA32; +typedef uint32 PPN32; +typedef uint32 MA32; +typedef uint32 MPN32; + +/* + * On 64 bit platform, the following types are the same as the + * default types. + */ +typedef uint64 VA64; +typedef uint64 VPN64; +typedef uint64 LA64; +typedef uint64 LPN64; +typedef uint64 PA64; +typedef uint64 PPN64; +typedef uint64 MA64; +typedef uint64 MPN64; + +/* + * VA typedefs for user world apps. + */ +typedef VA32 UserVA32; +typedef VA64 UserVA64; +typedef UserVA64 UserVAConst; /* Userspace ptr to data that we may only read. */ +typedef UserVA32 UserVA32Const; /* Userspace ptr to data that we may only read. */ +typedef UserVA64 UserVA64Const; /* Used by 64-bit syscalls until conversion is finished. */ +#ifdef VMKERNEL +typedef UserVA64 UserVA; +#else +typedef void * UserVA; +#endif + + +/* + * Maximal possible PPN value (errors too) that PhysMem can handle. + * Must be at least as large as MAX_PPN which is the maximum PPN + * for any region other than buserror. + */ +#define PHYSMEM_MAX_PPN ((PPN)0xffffffff) +#define MAX_PPN ((PPN)0x1fffffff) /* Maximal observable PPN value. */ +#define INVALID_PPN ((PPN)0xffffffff) + +#define INVALID_BPN ((BPN) 0x1fffffff) + +#define INVALID_MPN ((MPN)-1) +#define MEMREF_MPN ((MPN)-2) +#define RESERVED_MPN ((MPN) 0) +/* Support 43 bits of address space. */ +#define MAX_MPN ((MPN)0x7fffffff) + +#define INVALID_LPN ((LPN)-1) +#define INVALID_VPN ((VPN)-1) +#define INVALID_LPN64 ((LPN64)-1) +#define INVALID_PAGENUM ((PageNum)-1) +#define INVALID_WPN ((WPN) -1) + + +/* + * Format modifier for printing VA, LA, and VPN. + * Use them like this: Log("%#"FMTLA"x\n", laddr) + */ + +#if defined(VMM64) || defined(FROBOS64) || vm_x86_64 || defined __APPLE__ +# define FMTLA "l" +# define FMTVA "l" +# define FMTVPN "l" +#else +# define FMTLA "" +# define FMTVA "" +# define FMTVPN "" +#endif + +#ifndef EXTERN +#define EXTERN extern +#endif +#define CONST const + + +#ifndef INLINE +# ifdef _MSC_VER +# define INLINE __inline +# else +# define INLINE inline +# endif +#endif + + +/* + * Annotation for data that may be exported into a DLL and used by other + * apps that load that DLL and import the data. + */ +#if defined(_WIN32) && defined(VMX86_IMPORT_DLLDATA) +# define VMX86_EXTERN_DATA extern __declspec(dllimport) +#else // !_WIN32 +# define VMX86_EXTERN_DATA extern +#endif + +#if defined(_WIN32) && !defined(VMX86_NO_THREADS) +#define THREADSPECIFIC __declspec(thread) +#else +#define THREADSPECIFIC +#endif + +/* + * Due to the wonderful "registry redirection" feature introduced in + * 64-bit Windows, if you access any key under HKLM\Software in 64-bit + * code, you need to open/create/delete that key with + * VMKEY_WOW64_32KEY if you want a consistent view with 32-bit code. + */ + +#ifdef _WIN32 +#ifdef _WIN64 +#define VMW_KEY_WOW64_32KEY KEY_WOW64_32KEY +#else +#define VMW_KEY_WOW64_32KEY 0x0 +#endif +#endif + + +/* + * Consider the following reasons functions are inlined: + * + * 1) inlined for performance reasons + * 2) inlined because it's a single-use function + * + * Functions which meet only condition 2 should be marked with this + * inline macro; It is not critical to be inlined (but there is a + * code-space & runtime savings by doing so), so when other callers + * are added the inline-ness should be removed. + */ + +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3) +/* + * Starting at version 3.3, gcc does not always inline functions marked + * 'inline' (it depends on their size). To force gcc to do so, one must use the + * extra __always_inline__ attribute. + */ +# define INLINE_SINGLE_CALLER INLINE __attribute__((__always_inline__)) +# if defined(VMM) \ + && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 1)) +# warning Verify INLINE_SINGLE_CALLER '__always_inline__' attribute (did \ + monitor size change?) +# endif +#else +# define INLINE_SINGLE_CALLER INLINE +#endif + +/* + * Used when a hard guaranteed of no inlining is needed. Very few + * instances need this since the absence of INLINE is a good hint + * that gcc will not do inlining. + */ + +#if defined(__GNUC__) && defined(VMM) +#define ABSOLUTELY_NOINLINE __attribute__((__noinline__)) +#endif + +/* + * Attributes placed on function declarations to tell the compiler + * that the function never returns. + */ + +#ifdef _MSC_VER +#define NORETURN __declspec(noreturn) +#elif __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 9) +#define NORETURN __attribute__((__noreturn__)) +#else +#define NORETURN +#endif + +/* + * GCC 3.2 inline asm needs the + constraint for input/ouput memory operands. + * Older GCCs don't know about it --hpreg + */ + +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2) +# define VM_ASM_PLUS 1 +#else +# define VM_ASM_PLUS 0 +#endif + +/* + * Branch prediction hints: + * LIKELY(exp) - Expression exp is likely TRUE. + * UNLIKELY(exp) - Expression exp is likely FALSE. + * Usage example: + * if (LIKELY(excCode == EXC_NONE)) { + * or + * if (UNLIKELY(REAL_MODE(vc))) { + * + * We know how to predict branches on gcc3 and later (hopefully), + * all others we don't so we do nothing. + */ + +#if (__GNUC__ >= 3) +/* + * gcc3 uses __builtin_expect() to inform the compiler of an expected value. + * We use this to inform the static branch predictor. The '!!' in LIKELY + * will convert any !=0 to a 1. + */ +#define LIKELY(_exp) __builtin_expect(!!(_exp), 1) +#define UNLIKELY(_exp) __builtin_expect((_exp), 0) +#else +#define LIKELY(_exp) (_exp) +#define UNLIKELY(_exp) (_exp) +#endif + +/* + * GCC's argument checking for printf-like functions + * This is conditional until we have replaced all `"%x", void *' + * with `"0x%08x", (uint32) void *'. Note that %p prints different things + * on different platforms. Argument checking is enabled for the + * vmkernel, which has already been cleansed. + * + * fmtPos is the position of the format string argument, beginning at 1 + * varPos is the position of the variable argument, beginning at 1 + */ + +#if defined(__GNUC__) +# define PRINTF_DECL(fmtPos, varPos) __attribute__((__format__(__printf__, fmtPos, varPos))) +#else +# define PRINTF_DECL(fmtPos, varPos) +#endif + +#if defined(__GNUC__) +# define SCANF_DECL(fmtPos, varPos) __attribute__((__format__(__scanf__, fmtPos, varPos))) +#else +# define SCANF_DECL(fmtPos, varPos) +#endif + +/* + * UNUSED_PARAM should surround the parameter name and type declaration, + * e.g. "int MyFunction(int var1, UNUSED_PARAM(int var2))" + * + */ + +#ifndef UNUSED_PARAM +# if defined(__GNUC__) +# define UNUSED_PARAM(_parm) _parm __attribute__((__unused__)) +# else +# define UNUSED_PARAM(_parm) _parm +# endif +#endif + +/* + * REGPARM defaults to REGPARM3, i.e., a requent that gcc + * puts the first three arguments in registers. (It is fine + * if the function has fewer than three args.) Gcc only. + * Syntactically, put REGPARM where you'd put INLINE or NORETURN. + */ + +#if defined(__GNUC__) +# define REGPARM0 __attribute__((regparm(0))) +# define REGPARM1 __attribute__((regparm(1))) +# define REGPARM2 __attribute__((regparm(2))) +# define REGPARM3 __attribute__((regparm(3))) +# define REGPARM REGPARM3 +#else +# define REGPARM0 +# define REGPARM1 +# define REGPARM2 +# define REGPARM3 +# define REGPARM +#endif + +/* + * ALIGNED specifies minimum alignment in "n" bytes. + */ + +#ifdef __GNUC__ +#define ALIGNED(n) __attribute__((__aligned__(n))) +#else +#define ALIGNED(n) +#endif + +/* + *********************************************************************** + * STRUCT_OFFSET_CHECK -- */ /** + * + * \brief Check if the actual offsef of a member in a structure + * is what is expected + * + * + * \param[in] STRUCT Structure the member is a part of. + * \param[in] MEMBER Member to check the offset of. + * \param[in] OFFSET Expected offset of MEMBER in STRUCTURE. + * \param[in] DEBUG_EXTRA Additional bytes to be added to OFFSET to + * compensate for extra info in debug builds. + * + *********************************************************************** + */ +#ifdef VMX86_DEBUG +#define STRUCT_OFFSET_CHECK(STRUCT, MEMBER, OFFSET, DEBUG_EXTRA) \ + ASSERT_ON_COMPILE(vmk_offsetof(STRUCT, MEMBER) == (OFFSET + DEBUG_EXTRA)) +#else +#define STRUCT_OFFSET_CHECK(STRUCT, MEMBER, OFFSET, DEBUG_EXTRA) \ + ASSERT_ON_COMPILE(vmk_offsetof(STRUCT, MEMBER) == OFFSET) +#endif + +/* + *********************************************************************** + * STRUCT_SIZE_CHECK -- */ /** + * + * \brief Check if the actual size of a structure is what is expected + * + * + * \param[in] STRUCT Structure whose size is to be checked. + * \param[in] SIZE Expected size of STRUCT. + * \param[in] DEBUG_EXTRA Additional bytes to be added to SIZE to + * compensate for extra info in debug builds. + * + *********************************************************************** + */ +#ifdef VMX86_DEBUG +#define STRUCT_SIZE_CHECK(STRUCT, SIZE, DEBUG_EXTRA) \ + ASSERT_ON_COMPILE(sizeof(STRUCT) == (SIZE + DEBUG_EXTRA)) +#else +#define STRUCT_SIZE_CHECK(STRUCT, SIZE, DEBUG_EXTRA) \ + ASSERT_ON_COMPILE(sizeof(STRUCT) == SIZE) +#endif + +/* + * __func__ is a stringified function name that is part of the C99 standard. The block + * below defines __func__ on older systems where the compiler does not support that + * macro. + */ +#if defined(__GNUC__) \ + && ((__GNUC__ == 2 && __GNUC_MINOR < 96) \ + || (__GNUC__ < 2)) +# define __func__ __FUNCTION__ +#endif + +/* + * Once upon a time, this was used to silence compiler warnings that + * get generated when the compiler thinks that a function returns + * when it is marked noreturn. Don't do it. Use NOT_REACHED(). + */ + +#define INFINITE_LOOP() do { } while (1) + +/* + * On FreeBSD (for the tools build), size_t is typedef'd if _BSD_SIZE_T_ + * is defined. Use the same logic here so we don't define it twice. [greg] + */ +#ifdef __FreeBSD__ +# ifdef _BSD_SIZE_T_ +# undef _BSD_SIZE_T_ +# ifdef VM_I386 +# ifdef VM_X86_64 + typedef uint64 size_t; +# else + typedef uint32 size_t; +# endif +# endif /* VM_I386 */ +# endif + +# ifdef _BSD_SSIZE_T_ +# undef _BSD_SSIZE_T_ +# define _SSIZE_T +# define __ssize_t_defined +# define _SSIZE_T_DECLARED +# ifdef VM_I386 +# ifdef VM_X86_64 + typedef int64 ssize_t; +# else + typedef int32 ssize_t; +# endif +# endif /* VM_I386 */ +# endif + +#else +# ifndef _SIZE_T +# define _SIZE_T +# ifdef VM_I386 +# ifdef VM_X86_64 + typedef uint64 size_t; +# else + typedef uint32 size_t; +# endif +# endif /* VM_I386 */ +# endif + +# if !defined(FROBOS) && !defined(_SSIZE_T) && !defined(ssize_t) && \ + !defined(__ssize_t_defined) && !defined(_SSIZE_T_DECLARED) +# define _SSIZE_T +# define __ssize_t_defined +# define _SSIZE_T_DECLARED +# ifdef VM_I386 +# ifdef VM_X86_64 + typedef int64 ssize_t; +# else + typedef int32 ssize_t; +# endif +# endif /* VM_I386 */ +# endif + +#endif + +/* + * Format modifier for printing pid_t. On sun the pid_t is a ulong, but on + * Linux it's an int. + * Use this like this: printf("The pid is %"FMTPID".\n", pid); + */ +#ifdef sun +# ifdef VM_X86_64 +# define FMTPID "d" +# else +# define FMTPID "lu" +# endif +#else +# define FMTPID "d" +#endif + +/* + * Format modifier for printing uid_t. On sun the uid_t is a ulong, but on + * Linux it's an int. + * Use this like this: printf("The uid is %"FMTUID".\n", uid); + */ +#ifdef sun +# ifdef VM_X86_64 +# define FMTUID "u" +# else +# define FMTUID "lu" +# endif +#else +# define FMTUID "u" +#endif + +/* + * Format modifier for printing mode_t. On sun the mode_t is a ulong, but on + * Linux it's an int. + * Use this like this: printf("The mode is %"FMTMODE".\n", mode); + */ +#ifdef sun +# ifdef VM_X86_64 +# define FMTMODE "o" +# else +# define FMTMODE "lo" +# endif +#else +# define FMTMODE "o" +#endif + +/* + * Format modifier for printing time_t. Most platforms define a time_t to be + * a long int, but on FreeBSD (as of 5.0, it seems), the time_t is a signed + * size quantity. Refer to the definition of FMTSZ to see why we need silly + * preprocessor arithmetic. + * Use this like this: printf("The mode is %"FMTTIME".\n", time); + */ +#if defined(__FreeBSD__) && (__FreeBSD__ + 0) && ((__FreeBSD__ + 0) >= 5) +# define FMTTIME FMTSZ"d" +#else +# define FMTTIME "ld" +#endif + +/* + * Define MXSemaHandle here so both vmmon and vmx see this definition. + */ + +#ifdef _WIN32 +typedef uintptr_t MXSemaHandle; +#else +typedef int MXSemaHandle; +#endif + +/* + * Define type for poll device handles. + */ + +#ifdef _WIN32 +typedef uintptr_t PollDevHandle; +#else +typedef int PollDevHandle; +#endif + +/* + * Define the utf16_t type. + */ + +#if defined(_WIN32) && defined(_NATIVE_WCHAR_T_DEFINED) +typedef wchar_t utf16_t; +#else +typedef uint16 utf16_t; +#endif + +#endif /* _VM_BASIC_TYPES_H_ */ diff --git a/open-vm-tools/modules/solaris/vmxnet/vm_device_version.h b/open-vm-tools/modules/solaris/vmxnet/vm_device_version.h new file mode 100644 index 000000000..a27b23a39 --- /dev/null +++ b/open-vm-tools/modules/solaris/vmxnet/vm_device_version.h @@ -0,0 +1,193 @@ +/********************************************************* + * Copyright (C) 1998 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +#ifndef VM_DEVICE_VERSION_H +#define VM_DEVICE_VERSION_H + +#define INCLUDE_ALLOW_USERLEVEL +#define INCLUDE_ALLOW_VMMEXT +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_VMKERNEL +#define INCLUDE_ALLOW_VMCORE +#include "includeCheck.h" + +#ifdef _WIN32 +#include "guiddef.h" +#endif + +/* LSILogic 53C1030 Parallel SCSI controller + * LSILogic SAS1068 SAS controller + */ +#define PCI_VENDOR_ID_LSILOGIC 0x1000 +#define PCI_DEVICE_ID_LSI53C1030 0x0030 +#define PCI_DEVICE_ID_LSISAS1068 0x0054 + +/* Our own PCI IDs + * VMware SVGA II (Unified VGA) + * VMware SVGA (PCI Accelerator) + * VMware vmxnet (Idealized NIC) + * VMware vmxscsi (Abortive idealized SCSI controller) + * VMware chipset (Subsystem ID for our motherboards) + * VMware e1000 (Subsystem ID) + * VMware vmxnet3 (Uniform Pass Through NIC) + */ +#define PCI_VENDOR_ID_VMWARE 0x15AD +#define PCI_DEVICE_ID_VMWARE_SVGA2 0x0405 +#define PCI_DEVICE_ID_VMWARE_SVGA 0x0710 +#define PCI_DEVICE_ID_VMWARE_NET 0x0720 +#define PCI_DEVICE_ID_VMWARE_SCSI 0x0730 +#define PCI_DEVICE_ID_VMWARE_VMCI 0x0740 +#define PCI_DEVICE_ID_VMWARE_CHIPSET 0x1976 +#define PCI_DEVICE_ID_VMWARE_82545EM 0x0750 /* single port */ +#define PCI_DEVICE_ID_VMWARE_82546EB 0x0760 /* dual port */ +#define PCI_DEVICE_ID_VMWARE_EHCI 0x0770 +#define PCI_DEVICE_ID_VMWARE_1394 0x0780 +#define PCI_DEVICE_ID_VMWARE_BRIDGE 0x0790 +#define PCI_DEVICE_ID_VMWARE_ROOTPORT 0x07A0 +#define PCI_DEVICE_ID_VMWARE_VMXNET3 0x07B0 +#define PCI_DEVICE_ID_VMWARE_PVSCSI 0x07C0 + +/* The hypervisor device might grow. Please leave room + * for 7 more subfunctions. + */ +#define PCI_DEVICE_ID_VMWARE_HYPER 0x0800 +#define PCI_DEVICE_ID_VMWARE_VMI 0x0801 + +#define PCI_DEVICE_VMI_CLASS 0x05 +#define PCI_DEVICE_VMI_SUBCLASS 0x80 +#define PCI_DEVICE_VMI_INTERFACE 0x00 +#define PCI_DEVICE_VMI_REVISION 0x01 + +/* From linux/pci_ids.h: + * AMD Lance Ethernet controller + * BusLogic SCSI controller + * Ensoniq ES1371 sound controller + */ +#define PCI_VENDOR_ID_AMD 0x1022 +#define PCI_DEVICE_ID_AMD_VLANCE 0x2000 +#define PCI_VENDOR_ID_BUSLOGIC 0x104B +#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC 0x0140 +#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER 0x1040 +#define PCI_VENDOR_ID_ENSONIQ 0x1274 +#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371 + +/* From linux/pci_ids.h: + * Intel 82439TX (430 HX North Bridge) + * Intel 82371AB (PIIX4 South Bridge) + * Intel 82443BX (440 BX North Bridge and AGP Bridge) + * Intel 82545EM (e1000, server adapter, single port) + * Intel 82546EB (e1000, server adapter, dual port) + * Intel ICH7_16 (Hight Definition Audio controller) + */ +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_DEVICE_ID_INTEL_82439TX 0x7100 +#define PCI_DEVICE_ID_INTEL_82371AB_0 0x7110 +#define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 +#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 +#define PCI_DEVICE_ID_INTEL_82371AB 0x7111 +#define PCI_DEVICE_ID_INTEL_82443BX 0x7190 +#define PCI_DEVICE_ID_INTEL_82443BX_1 0x7191 +#define PCI_DEVICE_ID_INTEL_82443BX_2 0x7192 /* Used when no AGP support */ +#define PCI_DEVICE_ID_INTEL_82545EM 0x100f +#define PCI_DEVICE_ID_INTEL_82546EB 0x1010 +#define PCI_DEVICE_ID_INTEL_ICH7_16 0x27d8 + + +/************* Strings for IDE Identity Fields **************************/ +#define VIDE_ID_SERIAL_STR "00000000000000000001" /* Must be 20 Bytes */ +#define VIDE_ID_FIRMWARE_STR "00000001" /* Must be 8 Bytes */ + +/* No longer than 40 Bytes */ +#define VIDE_ATA_MODEL_STR PRODUCT_GENERIC_NAME " Virtual IDE Hard Drive" +#define VIDE_ATAPI_MODEL_STR PRODUCT_GENERIC_NAME " Virtual IDE CDROM Drive" + +#define ATAPI_VENDOR_ID "NECVMWar" /* Must be 8 Bytes */ +#define ATAPI_PRODUCT_ID PRODUCT_GENERIC_NAME " IDE CDROM" /* Must be 16 Bytes */ +#define ATAPI_REV_LEVEL "1.00" /* Must be 4 Bytes */ + +#define IDE_NUM_INTERFACES 2 /* support for two interfaces */ +#define IDE_DRIVES_PER_IF 2 + +/************* Strings for SCSI Identity Fields **************************/ +#define SCSI_DISK_MODEL_STR PRODUCT_GENERIC_NAME " Virtual SCSI Hard Drive" +#define SCSI_DISK_VENDOR_NAME COMPANY_NAME +#define SCSI_DISK_REV_LEVEL "1.0" +#define SCSI_CDROM_MODEL_STR PRODUCT_GENERIC_NAME " Virtual SCSI CDROM Drive" +#define SCSI_CDROM_VENDOR_NAME COMPANY_NAME +#define SCSI_CDROM_REV_LEVEL "1.0" + +/************* SCSI implementation limits ********************************/ +#define SCSI_MAX_CONTROLLERS 4 // Need more than 1 for MSCS clustering +#define SCSI_MAX_DEVICES 16 // BT-958 emulates only 16 +#define SCSI_IDE_CHANNEL SCSI_MAX_CONTROLLERS +#define SCSI_IDE_HOSTED_CHANNEL (SCSI_MAX_CONTROLLERS + 1) +#define SCSI_MAX_CHANNELS (SCSI_MAX_CONTROLLERS + 2) + +/************* Strings for the VESA BIOS Identity Fields *****************/ +#define VBE_OEM_STRING COMPANY_NAME " SVGA" +#define VBE_VENDOR_NAME COMPANY_NAME +#define VBE_PRODUCT_NAME PRODUCT_GENERIC_NAME + +/************* PCI implementation limits ********************************/ +#define PCI_MAX_BRIDGES 15 + +/************* Ethernet implementation limits ***************************/ +#define MAX_ETHERNET_CARDS 10 + +/************* PCI Passthrough implementation limits ********************/ +#define MAX_PCI_PASSTHRU_DEVICES 2 + +/************* USB implementation limits ********************************/ +#define MAX_USB_DEVICES_PER_HOST_CONTROLLER 127 + +/************* Strings for Host USB Driver *******************************/ + +#ifdef _WIN32 + +/* + * Globally unique ID for the VMware device interface. Define INITGUID before including + * this header file to instantiate the variable. + */ +DEFINE_GUID(GUID_DEVICE_INTERFACE_VMWARE_USB_DEVICES, +0x2da1fe75, 0xaab3, 0x4d2c, 0xac, 0xdf, 0x39, 0x8, 0x8c, 0xad, 0xa6, 0x65); + +/* + * Globally unique ID for the VMware device setup class. + */ +DEFINE_GUID(GUID_CLASS_VMWARE_USB_DEVICES, +0x3b3e62a5, 0x3556, 0x4d7e, 0xad, 0xad, 0xf5, 0xfa, 0x3a, 0x71, 0x2b, 0x56); + +/* + * This string defines the device ID string of a VMware USB device. + * The format is USB\Vid_XXXX&Pid_YYYY, where XXXX and YYYY are the + * hexadecimal representations of the vendor and product ids, respectively. + * + * The official vendor ID for VMware, Inc. is 0x0E0F. + * The product id for USB generic devices is 0x0001. + */ +#define USB_VMWARE_DEVICE_ID_WIDE L"USB\\Vid_0E0F&Pid_0001" +#define USB_DEVICE_ID_LENGTH (sizeof(USB_VMWARE_DEVICE_ID_WIDE) / sizeof(WCHAR)) + +#ifdef UNICODE +#define USB_PNP_SETUP_CLASS_NAME L"VMwareUSBDevices" +#define USB_PNP_DRIVER_NAME L"vmusb" +#else +#define USB_PNP_SETUP_CLASS_NAME "VMwareUSBDevices" +#define USB_PNP_DRIVER_NAME "vmusb" +#endif +#endif + +#endif /* VM_DEVICE_VERSION_H */ diff --git a/open-vm-tools/modules/solaris/vmxnet/vmnet_def.h b/open-vm-tools/modules/solaris/vmxnet/vmnet_def.h new file mode 100644 index 000000000..c02c5fe2f --- /dev/null +++ b/open-vm-tools/modules/solaris/vmxnet/vmnet_def.h @@ -0,0 +1,62 @@ +/********************************************************* + * Copyright (C) 2004 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +/* + * vmnet_def.h + * + * - definitions which are (mostly) not vmxnet or vlance specific + */ + +#ifndef _VMNET_DEF_H_ +#define _VMNET_DEF_H_ + +#define INCLUDE_ALLOW_USERLEVEL +#define INCLUDE_ALLOW_VMMEXT +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_VMK_MODULE +#define INCLUDE_ALLOW_VMKERNEL +#define INCLUDE_ALLOW_DISTRIBUTE +#define INCLUDE_ALLOW_VMNIXMOD +#include "includeCheck.h" + +/* + * capabilities - not all of these are implemented in the virtual HW + * (eg VLAN support is in the virtual switch) so even vlance + * can use them + */ +#define VMNET_CAP_SG 0x0001 /* Can do scatter-gather transmits. */ +#define VMNET_CAP_IP4_CSUM 0x0002 /* Can checksum only TCP/UDP over IPv4. */ +#define VMNET_CAP_HW_CSUM 0x0004 /* Can checksum all packets. */ +#define VMNET_CAP_HIGH_DMA 0x0008 /* Can DMA to high memory. */ +#define VMNET_CAP_TOE 0x0010 /* Supports TCP/IP offload. */ +#define VMNET_CAP_TSO 0x0020 /* Supports TCP Segmentation offload */ +#define VMNET_CAP_SW_TSO 0x0040 /* Supports SW TCP Segmentation */ +#define VMNET_CAP_VMXNET_APROM 0x0080 /* Vmxnet APROM support */ +#define VMNET_CAP_HW_TX_VLAN 0x0100 /* Can we do VLAN tagging in HW */ +#define VMNET_CAP_HW_RX_VLAN 0x0200 /* Can we do VLAN untagging in HW */ +#define VMNET_CAP_SW_VLAN 0x0400 /* Can we do VLAN tagging/untagging in SW */ +#define VMNET_CAP_WAKE_PCKT_RCV 0x0800 /* Can wake on network packet recv? */ +#define VMNET_CAP_ENABLE_INT_INLINE 0x1000 /* Enable Interrupt Inline */ +#define VMNET_CAP_ENABLE_HEADER_COPY 0x2000 /* copy header for vmkernel */ +#define VMNET_CAP_TX_CHAIN 0x4000 /* Guest can use multiple tx entries for a pkt */ +#define VMNET_CAP_RX_CHAIN 0x8000 /* a pkt can span multiple rx entries */ +#define VMNET_CAP_LPD 0x10000 /* large pkt delivery */ +#define VMNET_CAP_BPF 0x20000 /* BPF Support in VMXNET Virtual Hardware */ +#define VMNET_CAP_SG_SPAN_PAGES 0x40000 /* Can do scatter-gather span multiple pages transmits. */ +#define VMNET_CAP_IP6_CSUM 0x80000 /* Can do IPv6 csum offload. */ +#define VMNET_CAP_TSO6 0x100000 /* Can do TSO segmentation offload for IPv6 pkts. */ +#define VMNET_CAP_TSO256k 0x200000 /* Can do TSO segmentation offload for pkts up to 256kB. */ +#endif // _VMNET_DEF_H_ diff --git a/open-vm-tools/modules/solaris/vmxnet/vmxnet.c b/open-vm-tools/modules/solaris/vmxnet/vmxnet.c new file mode 100644 index 000000000..e0046d8de --- /dev/null +++ b/open-vm-tools/modules/solaris/vmxnet/vmxnet.c @@ -0,0 +1,2438 @@ +/********************************************************* + * Copyright (C) 2004 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This used to be defined in sys/gld.h, but was flagged as private, + * and we used it anyway. Now it no longer exists, and we're stuck + * with it for the time being. + */ +#ifndef GLD_MAX_MULTICAST +#define GLD_MAX_MULTICAST 64 +#endif + +#define __intptr_t_defined +#define _STDINT_H +#include "vm_basic_types.h" +#include "vmxnet2_def.h" +#include "vm_device_version.h" +#include "net.h" +#include "buildNumber.h" + +#define SOLVMXNET_SUCCESS 1 +#define SOLVMXNET_FAILURE 0 + +#ifdef SOLVMXNET_DEBUG_LEVEL +static int vxn_debug = SOLVMXNET_DEBUG_LEVEL; +#define DPRINTF(n, args) if (vxn_debug>(n)) cmn_err args +#else +#define DPRINTF(n, args) +#endif + +static char ident[] = "VMware Ethernet Adapter b" BUILD_NUMBER_NUMERIC_STRING; +char _depends_on[] = {"misc/gld"}; + +#define MAX_NUM_RECV_BUFFERS 128 +#define DEFAULT_NUM_RECV_BUFFERS 100 +#define MAX_NUM_XMIT_BUFFERS 128 +#define DEFAULT_NUM_XMIT_BUFFERS 100 +#define CRC_POLYNOMIAL_LE 0xedb88320UL +#define SOLVMXNET_MAXNAME 20 +#define MAX_TX_WAIT_ON_STOP 2000 + +#define ETHERALIGN 2 +#define SLACKBYTES 4 +#define MAXPKTBUF (14 + ETHERALIGN + ETHERMTU + SLACKBYTES) + + +#define QHIWATER (MAX_NUM_RECV_BUFFERS*ETHERMTU) + +#define OUTB(dp, p, v) \ + ddi_put8((dp)->vxnIOHdl, \ + (uint8_t *)((caddr_t)((dp)->vxnIOp) + (p)), v) +#define OUTW(dp, p, v) \ + ddi_put16((dp)->vxnIOHdl, \ + (uint16_t *)((caddr_t)((dp)->vxnIOp) + (p)), v) +#define OUTL(dp, p, v) \ + ddi_put32((dp)->vxnIOHdl, \ + (uint32_t *)((caddr_t)((dp)->vxnIOp) + (p)), v) +#define INB(dp, p) \ + ddi_get8((dp)->vxnIOHdl, \ + (uint8_t *)(((caddr_t)(dp)->vxnIOp) + (p))) +#define INW(dp, p) \ + ddi_get16((dp)->vxnIOHdl, \ + (uint16_t *)(((caddr_t)(dp)->vxnIOp) + (p))) +#define INL(dp, p) \ + ddi_get32((dp)->vxnIOHdl, \ + (uint32_t *)(((caddr_t)(dp)->vxnIOp) + (p))) + +#define VMXNET_INC(val, max) \ + val++; \ + if (UNLIKELY(val == max)) { \ + val = 0; \ + } + +#define TX_RINGBUF_MBLK(dp, idx) (dp->txRingBuf[idx].mblk) +#define TX_RINGBUF_DMAMEM(dp, idx) (dp->txRingBuf[idx].dmaMem) + +typedef struct { + caddr_t buf; /* Virtual address */ + uint32_t phyBuf; /* Physical address */ + size_t bufLen; /* Buffer length */ + ddi_dma_cookie_t cookie; /* Dma cookie */ + uint_t cookieCount; /* Cookie count */ + ddi_dma_handle_t dmaHdl; /* Dma handle */ + ddi_acc_handle_t dataAccHdl; /* Dada access handle */ +} dma_buf_t; + +typedef struct rx_dma_buf { + dma_buf_t dmaDesc; /* Dma descriptor */ + mblk_t *mblk; /* Streams message block */ + frtn_t freeCB; /* Free callback */ + struct vxn_softc *softc; /* Back pointer to softc */ + struct rx_dma_buf *next; /* Next one in list */ +} rx_dma_buf_t; + +typedef struct vxn_stats { + uint32_t errxmt; /* Transmit errors */ + uint32_t errrcv; /* Receive errors */ + uint32_t runt; /* Runt packets */ + uint32_t norcvbuf; /* Buffer alloc errors */ + uint32_t interrupts; /* Interrupts */ + uint32_t defer; /* Deferred transmits */ +} vxn_stats_t; + +typedef struct tx_ring_buf { + mblk_t *mblk; + dma_buf_t dmaMem; +} tx_ring_buf_t; + +typedef struct vxn_softc { + char drvName[SOLVMXNET_MAXNAME]; /* Driver name string */ + int unit; /* Driver instance */ + vxn_stats_t stats; /* Stats */ + + dev_info_t *dip; /* Info pointer */ + ddi_iblock_cookie_t iblockCookie; /* Interrupt block cookie */ + gld_mac_info_t *macInfo; /* GLD mac info */ + ddi_acc_handle_t confHdl; /* Configuration space handle */ + ddi_acc_handle_t vxnIOHdl; /* I/O space handle */ + caddr_t vxnIOp; /* I/O space pointer */ + boolean_t morphed; /* Adapter morphed ? */ + + kmutex_t intrlock; /* Interrupt lock */ + kmutex_t xmitlock; /* Transmit lock */ + kmutex_t rxlistlock; /* Rx free pool lock */ + + boolean_t nicActive; /* NIC active flag */ + boolean_t inIntr; /* Interrupt processing flag */ + + struct ether_addr devAddr; /* MAC address */ + + uint32_t vxnNumRxBufs; /* Number of reveice buffers */ + uint32_t vxnNumTxBufs; /* Number of transmit buffers */ + + dma_buf_t driverDataDmaMem; /* Driver Data (dma handle) */ + Vmxnet2_DriverData *driverData; /* Driver Data */ + void *driverDataPhy; /* Driver Data busaddr pointer */ + Vmxnet2_RxRingEntry *rxRing; /* Receive ring */ + Vmxnet2_TxRingEntry *txRing; /* Transmit ring */ + ddi_dma_handle_t txDmaHdl; /* Tx buffers dma handle */ + rx_dma_buf_t *rxRingBuffPtr[MAX_NUM_RECV_BUFFERS]; + /* DMA buffers associated with rxRing */ + tx_ring_buf_t txRingBuf[MAX_NUM_XMIT_BUFFERS]; /* tx Ring buffers */ + + rx_dma_buf_t *rxFreeBufList; + uint32_t rxNumFreeBufs; /* current # of buffers in pool */ + uint32_t rxMaxFreeBufs; /* max # of buffers in pool */ + + uint32_t txPending; /* Pending transmits */ + uint32_t maxTxFrags; /* Max Tx fragments */ + + int multiCount; /* Multicast address count */ + struct ether_addr multicastList[GLD_MAX_MULTICAST]; /* Multicast list */ + + struct vxn_softc *next; /* Circular list of instances */ + struct vxn_softc *prev; +} vxn_softc_t; + +/* used for rx buffers or buffers allocated by ddi_dma_mem_alloc() */ +static ddi_dma_attr_t vxn_dma_attrs = { + DMA_ATTR_V0, /* dma_attr version */ + 0, /* dma_attr_addr_lo */ + (uint64_t)0xFFFFFFFF, /* dma_attr_addr_hi */ + 0x7FFFFFFF, /* dma_attr_count_max */ + 4, /* dma_attr_align */ + 0x3F, /* dma_attr_burstsizes */ + 1, /* dma_attr_minxfer */ + (uint64_t)0xFFFFFFFF, /* dma_attr_maxxfer */ + (uint64_t)0xFFFFFFFF, /* dma_attr_seg */ + 1, /* dma_attr_sgllen */ + 1, /* dma_attr_granular */ + 0, /* dma_attr_flags */ +}; + +/* used for tx buffers */ +static ddi_dma_attr_t vxn_dma_attrs_tx = { + DMA_ATTR_V0, /* dma_attr version */ + 0, /* dma_attr_addr_lo */ + (uint64_t)0xFFFFFFFF, /* dma_attr_addr_hi */ + 0x7FFFFFFF, /* dma_attr_count_max */ + 1, /* dma_attr_align */ + 0x3F, /* dma_attr_burstsizes */ + 1, /* dma_attr_minxfer */ + (uint64_t)0xFFFFFFFF, /* dma_attr_maxxfer */ + (uint64_t)0xFFFFFFFF, /* dma_attr_seg */ + 1, /* dma_attr_sgllen */ + 1, /* dma_attr_granular */ + 0, /* dma_attr_flags */ +}; + + +static struct ether_addr etherbroadcastaddr = { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} +}; + +static struct ddi_device_acc_attr vxn_buf_attrs = { + DDI_DEVICE_ATTR_V0, + DDI_STRUCTURE_LE_ACC, + DDI_STRICTORDER_ACC +}; + +static struct ddi_device_acc_attr dev_attr = { + DDI_DEVICE_ATTR_V0, + DDI_STRUCTURE_LE_ACC, + DDI_STRICTORDER_ACC +}; + +static vxn_softc_t vxnList; /* for debugging */ +static kmutex_t vxnListLock; + +static void *Vxn_Memset(void *s, int c, size_t n); +static int Vxn_Reset(gld_mac_info_t *macInfo); +static int Vxn_SetPromiscuous(gld_mac_info_t *macInfo, int flag); +static int Vxn_GetStats(gld_mac_info_t *macInfo, struct gld_stats *gs); +static void Vxn_ApplyAddressFilter(vxn_softc_t *dp); +static int Vxn_SetMulticast(gld_mac_info_t *macinfo, uint8_t *ep, int flag); +static int Vxn_SetMacAddress(gld_mac_info_t *macInfo, uint8_t *mac); +static int Vxn_Start(gld_mac_info_t *macInfo); +static int Vxn_Stop(gld_mac_info_t *macInfo); +static void Vxn_FreeTxBuf(vxn_softc_t *dp, int idx); +static int Vxn_EncapTxBuf(vxn_softc_t *dp, mblk_t *mp, Vmxnet2_TxRingEntry *xre, + tx_ring_buf_t *txBuf); +static int Vxn_Send(gld_mac_info_t *macinfo, mblk_t *mp); +static boolean_t Vxn_TxComplete(vxn_softc_t *dp, boolean_t *reschedp); +static boolean_t Vxn_Receive(vxn_softc_t *dp); +static u_int Vxn_Interrupt(gld_mac_info_t *macInfo); +static void Vxn_ReclaimRxBuf(rx_dma_buf_t *rxDesc); +static void Vxn_FreeRxBuf(rx_dma_buf_t *rxDesc); +static rx_dma_buf_t *Vxn_AllocRxBuf(vxn_softc_t *dp, int cansleep); +static void Vxn_FreeInitBuffers(vxn_softc_t *dp); +static int Vxn_AllocInitBuffers(vxn_softc_t *dp); +static void Vxn_FreeDmaMem(dma_buf_t *dma); +static int Vxn_AllocDmaMem(vxn_softc_t *dp, int size, int cansleep, dma_buf_t *dma); +static void Vxn_FreeDriverData(vxn_softc_t *dp); +static int Vxn_AllocDriverData(vxn_softc_t *dp); +static int Vxn_Attach(dev_info_t *dip, ddi_attach_cmd_t cmd); +static int Vxn_Detach(dev_info_t *dip, ddi_detach_cmd_t cmd); +static int Vxn_AllocRxBufPool(vxn_softc_t *dp); +static void Vxn_FreeRxBufPool(vxn_softc_t *dp); +static rx_dma_buf_t * Vxn_AllocRxBufFromPool(vxn_softc_t *dp); +static void Vxn_FreeRxBufToPool(rx_dma_buf_t *rxDesc); + +/* + *----------------------------------------------------------------------------- + * Vxn_Memset -- + * memset() (Because bzero does not get resolved by module loader) + * + * Results: + * pointer to the memory area s + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static void * +Vxn_Memset(void *s, int c, size_t n) +{ + while (n--) { + ((uint8_t *)s)[n] = c; + } + + return s; +} + +/* + *----------------------------------------------------------------------------- + * Vxn_Reset -- + * Stub routine to reset hardware. Presently does nothing. Start/Stop should + * take care of resets. + * + * Results: + * None + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static int +Vxn_Reset(gld_mac_info_t *macInfo) +{ + return GLD_SUCCESS; +} + +/* + *----------------------------------------------------------------------------- + * Vxn_SetPromiscuous -- + * Set/Reset NIC to/from promiscuous mode + * + * Results: + * GLD_SUCCESS + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static int +Vxn_SetPromiscuous(gld_mac_info_t *macInfo, int flag) +{ + vxn_softc_t *dp = (vxn_softc_t *)macInfo->gldm_private; + Vmxnet2_DriverData *dd = dp->driverData; + + mutex_enter(&dp->intrlock); + if (flag == GLD_MAC_PROMISC_PHYS) { + dd->ifflags |= VMXNET_IFF_PROMISC; + } else if (flag == GLD_MAC_PROMISC_MULTI) { + /* + * This should really set VMXNET_IFF_ALLMULTI, + * but unfortunately it doesn't exist. The next + * best thing would be to set the LADRFs to all + * 0xFFs and set VMXNET_IFF_MULTICAST, but that + * opens up a whole new set of potential pitfalls, + * so this is a reasonable temporary solution. + */ + dd->ifflags |= VMXNET_IFF_PROMISC; + } else if (flag == GLD_MAC_PROMISC_NONE) { + dd->ifflags &= ~VMXNET_IFF_PROMISC; + } else { + /* This could be GLD_MAC_PROMISC_NOOP? */ + mutex_exit(&dp->intrlock); + cmn_err(CE_WARN, "%s%d: Vxn_SetPromiscuous: Unexpected mode flag: 0x%x", + dp->drvName, dp->unit, flag); + + return GLD_FAILURE; + } + + OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_UPDATE_IFF); + mutex_exit(&dp->intrlock); + + return GLD_SUCCESS; +} + +/* + *----------------------------------------------------------------------------- + * Vxn_GetStats -- + * Get driver specific stats + * + * Results: + * GLD_SUCCESS + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static int +Vxn_GetStats(gld_mac_info_t *macInfo, struct gld_stats *gs) +{ + vxn_softc_t *dp = (vxn_softc_t *)macInfo->gldm_private; + + gs->glds_errxmt = dp->stats.errxmt; + gs->glds_errrcv = dp->stats.errrcv; + gs->glds_short = dp->stats.runt; + gs->glds_norcvbuf = dp->stats.norcvbuf; + gs->glds_intr = dp->stats.interrupts; + gs->glds_defer = dp->stats.defer; + + return GLD_SUCCESS; +} + +/* + *----------------------------------------------------------------------------- + * Vxn_ApplyAddressFilter -- + * Go over multicast list and compute/apply address filter + * + * Results: + * None + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static void +Vxn_ApplyAddressFilter(vxn_softc_t *dp) +{ + uint8_t *ep; + int i, j, bit, byte; + uint32_t crc, poly = CRC_POLYNOMIAL_LE; + Vmxnet2_DriverData *dd = dp->driverData; + volatile uint16_t *mcastTable = (uint16_t *)dd->LADRF; + + ASSERT(MUTEX_HELD(&dp->intrlock)); + + /* clear the multicast filter */ + dd->LADRF[0] = 0; + dd->LADRF[1] = 0; + + for (i = 0; i < dp->multiCount; i++) { + crc = 0xffffffff; + ep = (uint8_t *)&dp->multicastList[i].ether_addr_octet; + + for (byte = 0; byte < 6; byte++) { + for (bit = *ep++, j = 0; j < 8; j++, bit >>= 1) { + int test; + + test = ((bit ^ crc) & 0x01); + crc >>= 1; + + if (test) { + crc = crc ^ poly; + } + } + } + + crc = crc >> 26; + mcastTable[crc >> 4] |= 1 << (crc & 0xf); + } +} + +/* + *----------------------------------------------------------------------------- + * Vxn_SetMulticast -- + * Add delete entry from multicast list + * + * Results: + * GLD_FAILURE on failure + * GLD_SUCCESS on success + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static int +Vxn_SetMulticast(gld_mac_info_t *macinfo, uint8_t *ep, int flag) +{ + int i; + int copyLen; + vxn_softc_t *dp = (vxn_softc_t *)macinfo->gldm_private; + Vmxnet2_DriverData *dd = dp->driverData; + + if (flag == GLD_MULTI_ENABLE) { + /* + * Exceeded multicast address limit + */ + if (dp->multiCount >= GLD_MAX_MULTICAST) { + return GLD_FAILURE; + } + + /* + * Add mac address to multicast list + */ + bcopy(ep, dp->multicastList[dp->multiCount].ether_addr_octet, + ETHERADDRL); + dp->multiCount++; + } + else { + for (i=0; imultiCount; i++) { + if (bcmp(ep, dp->multicastList[i].ether_addr_octet, ETHERADDRL) == 0) { + goto found; + } + } + return GLD_FAILURE; + + found: + /* + * Delete mac address from multicast list + */ + copyLen = (dp->multiCount - (i+1)) * sizeof(struct ether_addr); + if (copyLen > 0) { + bcopy(&dp->multicastList[i+1], &dp->multicastList[i], copyLen); + } + dp->multiCount--; + } + + /* + * Compute address filter from list of addressed and apply it + */ + mutex_enter(&dp->intrlock); + Vxn_ApplyAddressFilter(dp); + + if (dp->multiCount) { + ASSERT(dd->LADRF[0] || dd->LADRF[1]); + dd->ifflags |= VMXNET_IFF_MULTICAST; + } else { + ASSERT(!(dd->LADRF[0] || dd->LADRF[1])); + dd->ifflags &= ~VMXNET_IFF_MULTICAST; + } + + OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_UPDATE_IFF); + OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_UPDATE_LADRF); + mutex_exit(&dp->intrlock); + + return GLD_SUCCESS; +} + +/* + *----------------------------------------------------------------------------- + * Vxn_SetMacAddress -- + * Change device MAC address + * + * Results: + * GLD_SUCCESS + * GLD_FAILURE + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static int +Vxn_SetMacAddress(gld_mac_info_t *macInfo, uint8_t *mac) +{ + int i; + int err = GLD_SUCCESS; + vxn_softc_t * dp = (vxn_softc_t *)macInfo->gldm_private; + + mutex_enter(&dp->intrlock); + mutex_enter(&dp->xmitlock); + + /* + * Don't change MAC address on a running NIC + */ + if (dp->nicActive) { + err = GLD_FAILURE; + goto out; + } + + /* + * Save new MAC address + */ + for (i = 0; i < 6; i++) { + dp->devAddr.ether_addr_octet[i] = mac[i]; + } + + /* + * Push new MAC address down into hardware + */ + for (i = 0; i < 6; i++) { + OUTB(dp, VMXNET_MAC_ADDR + i, mac[i]); + } + +out: + mutex_exit(&dp->xmitlock); + mutex_exit(&dp->intrlock); + return err; +} + +/* + *----------------------------------------------------------------------------- + * Vxn_Start -- + * Device start routine. Called on "ifconfig plumb" + * + * Results: + * GLD_SUCCESS + * GLD_FAILURE + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static int +Vxn_Start(gld_mac_info_t *macInfo) +{ + int err = GLD_SUCCESS; + uint32_t r, capabilities, features; + vxn_softc_t * dp = (vxn_softc_t *)macInfo->gldm_private; + + mutex_enter(&dp->intrlock); + mutex_enter(&dp->xmitlock); + + if (!dp->nicActive) { + /* + * Register ring structure with hardware + * + * This downcast is OK because we requested a 32-bit physical address + */ + OUTL(dp, VMXNET_INIT_ADDR, (uint32_t)(uintptr_t)dp->driverDataPhy); + OUTL(dp, VMXNET_INIT_LENGTH, dp->driverData->length); + + /* + * Make sure registeration succeded + */ + r = INL(dp, VMXNET_INIT_LENGTH); + if (!r) { + cmn_err(CE_WARN, "%s%d: Vxn_Start: failed to register ring", + dp->drvName, dp->unit); + err = GLD_FAILURE; + goto out; + } + + /* + * Get maximum tx fragments supported + */ + OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_GET_CAPABILITIES); + capabilities = INL(dp, VMXNET_COMMAND_ADDR); + + OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_GET_FEATURES); + features = INL(dp, VMXNET_COMMAND_ADDR); + + DPRINTF(3, (CE_CONT, "%s%d: chip capabilities=0x%x features=0x%x\n", + dp->drvName, dp->unit, capabilities, features)); + + if ((capabilities & VMNET_CAP_SG) && + (features & VMXNET_FEATURE_ZERO_COPY_TX)) { + dp->maxTxFrags = VMXNET2_SG_DEFAULT_LENGTH; + } else { + dp->maxTxFrags = 1; + } + ASSERT(dp->maxTxFrags >= 1); + + /* + * Alloc Tx DMA handle + */ + vxn_dma_attrs_tx.dma_attr_sgllen = dp->maxTxFrags; + if (ddi_dma_alloc_handle(dp->dip, &vxn_dma_attrs_tx, DDI_DMA_SLEEP, + NULL, &dp->txDmaHdl) != DDI_SUCCESS) { + cmn_err(CE_WARN, "%s%d: Vxn_Start: failed to alloc tx dma handle", + dp->drvName, dp->unit); + err = GLD_FAILURE; + goto out; + } + + /* + * Enable interrupts on the card + */ + dp->driverData->ifflags |= VMXNET_IFF_BROADCAST | VMXNET_IFF_DIRECTED; + + OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_INTR_ENABLE); + OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_UPDATE_IFF); + OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_UPDATE_LADRF); + + dp->nicActive = TRUE; + } + +out: + mutex_exit(&dp->xmitlock); + mutex_exit(&dp->intrlock); + return err; +} + +/* + *----------------------------------------------------------------------------- + * Vxn_Stop -- + * Device stop routine. Called on "ifconfig unplumb" + * + * Results: + * GLD_SUCCESS + * GLD_FAILURE + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static int +Vxn_Stop(gld_mac_info_t *macInfo) +{ + int i; + int err = GLD_SUCCESS; + vxn_softc_t * dp = (vxn_softc_t *)macInfo->gldm_private; + boolean_t resched; + + mutex_enter(&dp->intrlock); + mutex_enter(&dp->xmitlock); + + if (!dp->nicActive) { + goto out; + } + + /* + * Disable interrupts + */ + OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_INTR_DISABLE); + + /* + * Wait for pending transmits + */ + if (dp->txPending) { + for (i=0; i < MAX_TX_WAIT_ON_STOP && dp->txPending; i++) { + delay(drv_usectohz(1000)); + OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_CHECK_TX_DONE); + (void) Vxn_TxComplete(dp, &resched); + /* + * Don't worry about rescheduling transmits - GLD handles + * this automatically. + */ + } + } + if (dp->txPending) { + cmn_err(CE_WARN, "%s%d: Vxn_Stop: giving up on %d pending transmits", + dp->drvName, dp->unit, dp->txPending); + } + + OUTL(dp, VMXNET_INIT_ADDR, 0); + dp->nicActive = FALSE; + + /* + * Free Tx DMA handle + * + * The ddi_dma_free_handle() man page says that ddi_dma_unbind_handle() must be called + * prior to calling ddi_dma_free_handle(). + * However, call to ddi_dma_unbind_handle() is not required here, because + * ddi_dma_addr_bind_handle() and matching ddi_dma_unbind_handle() are called from + * Vxn_EncapTxBuf(). + * xmitlock is held in Vxn_EncapTxBuf() as well as acquired above in Vxn_Stop(). + */ + ddi_dma_free_handle(&dp->txDmaHdl); + dp->txDmaHdl = NULL; + +out: + mutex_exit(&dp->xmitlock); + mutex_exit(&dp->intrlock); + return err; +} + +/* + *----------------------------------------------------------------------------- + * Vxn_FreeTxBuf -- + * Free transmit buffer + * + * Results: + * None + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static void +Vxn_FreeTxBuf(vxn_softc_t *dp, int idx) +{ + mblk_t **txMblkp = &TX_RINGBUF_MBLK(dp, idx); + dma_buf_t *dmaMem = &TX_RINGBUF_DMAMEM(dp, idx); + + if (*txMblkp) { + freemsg(*txMblkp); + *txMblkp = NULL; + } + + if (dmaMem->buf) { + Vxn_FreeDmaMem(dmaMem); + ASSERT(dmaMem->buf == NULL); + } +} + +/* + *----------------------------------------------------------------------------- + * Vxn_EncapTxBuf -- + * Go over dma mappings of Tx buffers and drop buffer physical address + * into ring entry + * + * Results: + * SOLVMXNET_SUCCESS on success + * SOLVMXNET_FAILURE on failure + * + * Side effects: + * None + *---------------- ------------------------------------------------------------- + */ +static int +Vxn_EncapTxBuf(vxn_softc_t *dp, + mblk_t *mp, + Vmxnet2_TxRingEntry *xre, + tx_ring_buf_t *txBuf) +{ + int frag; + int fragcount; + int rval; + mblk_t *tp; + mblk_t *mblk; + boolean_t needPullup = FALSE; + boolean_t dmaMemAlloced = FALSE; + + ASSERT(txBuf); + ASSERT(txBuf->mblk == NULL); + ASSERT(MUTEX_HELD(&dp->xmitlock)); + + xre->sg.length = 0; + xre->flags = 0; + + fragcount = 0; + for (tp = mp; tp != NULL; tp = tp->b_cont) { + fragcount++; + } + if (fragcount > dp->maxTxFrags) { + needPullup = TRUE; + } + +pullup: + frag = 0; + if (needPullup) { + if (!(mblk = msgpullup(mp, -1))) { + cmn_err(CE_WARN, "%s%d: Vxn_EncapTxBuf: msgpullup failed", + dp->drvName, dp->unit); + goto err; + } + } else { + mblk = mp; + } + + /* + * Go through message chain and drop packet pointers into ring + * scatter/gather array + */ + for (tp = mblk; tp != NULL; tp = tp->b_cont) { + + uint_t nCookies; + ddi_dma_cookie_t dmaCookie; + int len = tp->b_wptr - tp->b_rptr; + + if (len) { + /* + * Associate tx buffer with dma handle + */ + ASSERT(dp->txDmaHdl); + if ((rval = ddi_dma_addr_bind_handle(dp->txDmaHdl, NULL, (caddr_t)tp->b_rptr, + len, DDI_DMA_RDWR | DDI_DMA_STREAMING, + DDI_DMA_DONTWAIT, NULL, + &dmaCookie, &nCookies)) + != DDI_DMA_MAPPED) { + + /* + * Try to handle bind failure caused by a page boundary spill + * by allocating a private dma buffer and copying data into it + */ + if ((rval == DDI_DMA_TOOBIG) && !dmaMemAlloced ) { + /* + * Force pullup + */ + if (!needPullup && (dp->maxTxFrags > 1)) { + needPullup = TRUE; + goto pullup; + } + + if (Vxn_AllocDmaMem(dp, len, FALSE, &txBuf->dmaMem) + != SOLVMXNET_SUCCESS) { + goto err; + } + + dmaMemAlloced = TRUE; + + /* + * Copy data into DMA capable buffer + */ + bcopy(tp->b_rptr, txBuf->dmaMem.buf, len); + + /* + * Stick buffer physical addr in the ring + */ + xre->sg.sg[frag].addrLow = txBuf->dmaMem.phyBuf; + xre->sg.sg[frag].length = len; + frag++; + + continue; + + } else { + cmn_err(CE_WARN, "%s%d: Vxn_EncapTxBuf: failed (%d) to bind dma " + "handle for len %d. [dmaMemAlloced=%d]", + dp->drvName, dp->unit, rval, len, dmaMemAlloced); + goto err; + } + } + + /* + * Extract tx buffer physical addresses from cookie + */ + while (nCookies) { + if (UNLIKELY(frag == dp->maxTxFrags)) { + (void)ddi_dma_unbind_handle(dp->txDmaHdl); + + if (!needPullup) { + ASSERT(!dmaMemAlloced); + needPullup = TRUE; + goto pullup; + } else { + cmn_err(CE_WARN, "%s%d: Vxn_EncapTxBuf: " + "exceeded max (%d) fragments in message", + dp->drvName, dp->unit, dp->maxTxFrags); + goto err; + } + } + + /* + * Stick it in the ring + */ + xre->sg.sg[frag].addrLow = dmaCookie.dmac_address; + xre->sg.sg[frag].length = dmaCookie.dmac_size; + frag++; + + if (--nCookies) { + ddi_dma_nextcookie(dp->txDmaHdl, &dmaCookie); + } + } + + (void)ddi_dma_unbind_handle(dp->txDmaHdl); + } + } + + if (frag > 0) { + xre->sg.length = frag; + + /* Give ownership to NIC */ + xre->sg.addrType = NET_SG_PHYS_ADDR; + xre->ownership = VMXNET2_OWNERSHIP_NIC; + xre->flags |= VMXNET2_TX_CAN_KEEP; + txBuf->mblk = mblk; + + /* + * If we called msgpullup to concatenate fragments, free + * original mblk now since we're going to return success. + */ + if (mblk != mp) { + freemsg(mp); + } + + return SOLVMXNET_SUCCESS; + } + +err: + if (mblk != NULL && mblk != mp) { + /* + * Free mblk allocated by msgpullup. + */ + freemsg(mblk); + } + + if (dmaMemAlloced) { + ASSERT(txBuf->dmaMem.buf); + Vxn_FreeDmaMem(&txBuf->dmaMem); + } + + return SOLVMXNET_FAILURE; +} + +/* + *----------------------------------------------------------------------------- + * Vxn_Send -- + * GLD Transmit routine. Starts packet hard tx. + * + * Results: + * GLD_SUCCESS on success + * GLD_FAILURE on failure + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static int +Vxn_Send(gld_mac_info_t *macinfo, mblk_t *mp) +{ + Vmxnet2_TxRingEntry *xre; + int err = GLD_SUCCESS; + vxn_softc_t *dp = (vxn_softc_t *)macinfo->gldm_private; + Vmxnet2_DriverData *dd = dp->driverData; + boolean_t resched = FALSE; + + mutex_enter(&dp->xmitlock); + + /* + * Check if ring entry at drop pointer is available + */ + if (TX_RINGBUF_MBLK(dp, dd->txDriverNext) != NULL) { + DPRINTF(3, (CE_NOTE, "%s%d: Vxn_Send: tx ring full", + dp->drvName, dp->unit)); + err = GLD_NORESOURCES; + dd->txStopped = TRUE; + dp->stats.defer++; + goto out; + } + + xre = &dp->txRing[dd->txDriverNext]; + + /* + * Drop packet into ring entry + */ + if (Vxn_EncapTxBuf(dp, mp, xre, &dp->txRingBuf[dd->txDriverNext]) + != SOLVMXNET_SUCCESS) { + err = GLD_FAILURE; + dp->stats.errxmt++; + goto out; + } + + /* + * Increment drop pointer + */ + VMXNET_INC(dd->txDriverNext, dd->txRingLength); + dd->txNumDeferred++; + dp->txPending++; + + /* + * Transmit, if number of pending packets > tx cluster length + */ + if (dd->txNumDeferred >= dd->txClusterLength) { + dd->txNumDeferred = 0; + + /* + * Call hardware transmit + */ + INL(dp, VMXNET_TX_ADDR); + } + + /* + * Clean up transmit ring. TX completion interrupts are not guaranteed + */ + (void) Vxn_TxComplete(dp, &resched); + +out: + mutex_exit(&dp->xmitlock); + if (resched) { + /* Tell GLD to retry any deferred packets */ + gld_sched(dp->macInfo); + } + return err; +} + +/* + *----------------------------------------------------------------------------- + * Vxn_TxComplete -- + * Scan Tx ring for completed transmits. Reclaim Tx buffers. + * + * Results: + * Returns TRUE if it found a completed transmit, FALSE otherwise. + * Also sets *reschedp to TRUE if the caller should call gld_sched + * to reschedule transmits (once all locks are dropped). + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static boolean_t +Vxn_TxComplete(vxn_softc_t *dp, boolean_t *reschedp) +{ + Vmxnet2_DriverData *dd = dp->driverData; + boolean_t found = FALSE; + boolean_t needresched = FALSE; + + ASSERT(MUTEX_HELD(&dp->xmitlock)); + + while (1) { + Vmxnet2_TxRingEntry *xre = &dp->txRing[dd->txDriverCur]; + + if (xre->ownership != VMXNET2_OWNERSHIP_DRIVER || + (TX_RINGBUF_MBLK(dp, dd->txDriverCur) == NULL)) { + break; + } + + found = TRUE; + Vxn_FreeTxBuf(dp, dd->txDriverCur); + + dp->txPending--; + VMXNET_INC(dd->txDriverCur, dd->txRingLength); + if (dd->txStopped) { + needresched = TRUE; + dd->txStopped = FALSE; + } + } + + *reschedp = needresched; + return found; +} + +/* + *----------------------------------------------------------------------------- + * Vxn_Receive -- + * Rx handler. First assembles the packets into a chain of mblks, + * then drops locks and passes them up the stack to GLD. + * + * Results: + * Returns TRUE if it find a packet ready for processing, FALSE + * otherwise. + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static boolean_t +Vxn_Receive(vxn_softc_t *dp) +{ + int ringnext; + short pktlen; + Vmxnet2_DriverData *dd = dp->driverData; + rx_dma_buf_t *rxDesc; + rx_dma_buf_t *newRxDesc; + mblk_t *mblk; + mblk_t *head = NULL; + mblk_t **tail = &head; + mblk_t *next; + boolean_t found = FALSE; /* Did we find at least one packet? */ + + ASSERT(MUTEX_HELD(&dp->intrlock)); + + /* + * Walk receive ring looking for entries with ownership + * reverted back to driver + */ + while (1) { + Vmxnet2_RxRingEntry *rre; + rx_dma_buf_t **rbuf; + + ringnext = dd->rxDriverNext; + rre = &dp->rxRing[ringnext]; + rbuf = &dp->rxRingBuffPtr[ringnext]; + + if (rre->ownership != VMXNET2_OWNERSHIP_DRIVER) { + break; + } + + found = TRUE; + + pktlen = rre->actualLength; + + if (pktlen < (60 - 4)) { + /* + * Ethernet header vlan tags are 4 bytes. Some vendors generate + * 60byte frames including vlan tags. When vlan tag + * is stripped, such frames become 60 - 4. (PR106153) + */ + dp->stats.errrcv++; + if (pktlen != 0) { + DPRINTF(3, (CE_CONT, "%s%d: runt packet\n", dp->drvName, dp->unit)); + dp->stats.runt++; + } + } else { + /* + * Alloc new Rx buffer to replace current one + */ + newRxDesc = Vxn_AllocRxBufFromPool(dp); + + if (newRxDesc) { + rxDesc = *rbuf; + mblk = rxDesc->mblk; + + *rbuf = newRxDesc; + rre->paddr = newRxDesc->dmaDesc.phyBuf + ETHERALIGN; + rre->bufferLength = MAXPKTBUF - ETHERALIGN; + rre->actualLength = 0; + + /* + * Advance write pointer past packet length + */ + mblk->b_wptr = mblk->b_rptr + pktlen; + + /* + * Add to end of chain. + */ + mblk->b_next = NULL; + *tail = mblk; + tail = &mblk->b_next; + } else { + dp->stats.errrcv++; + dp->stats.norcvbuf++; + } + } + + /* Give the descriptor back to NIC */ + rre->ownership = VMXNET2_OWNERSHIP_NIC; + VMXNET_INC(dd->rxDriverNext, dd->rxRingLength); + } + + /* + * Walk chain and pass mblks up to gld_recv one by one. + */ + mutex_exit(&dp->intrlock); + for (mblk = head; mblk != NULL; mblk = next) { + next = mblk->b_next; + mblk->b_next = NULL; + gld_recv(dp->macInfo, mblk); + } + mutex_enter(&dp->intrlock); + + return (found); +} + +/* + *----------------------------------------------------------------------------- + * Vxn_Interrupt -- + * GLD interrupt handler. Scan: Rx ring for received packets, Tx ring for + * completed transmits + * + * Results: + * - DDI_INTR_CLAIMED (if we found something to do) + * - DDI_INTR_UNCLAIMED (if not) + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static u_int +Vxn_Interrupt(gld_mac_info_t *macInfo) +{ + u_int ret = DDI_INTR_UNCLAIMED; + vxn_softc_t *dp = (vxn_softc_t *)macInfo->gldm_private; + boolean_t foundRx, foundTx; + boolean_t resched = FALSE; + + mutex_enter(&dp->intrlock); + dp->inIntr = TRUE; + + if (!dp->nicActive) { + goto out; + } + + /* + * Ack interrupt + */ + OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_INTR_ACK); + + foundRx = Vxn_Receive(dp); + + mutex_enter(&dp->xmitlock); + foundTx = Vxn_TxComplete(dp, &resched); + mutex_exit(&dp->xmitlock); + + if (foundRx || foundTx) { + ret = DDI_INTR_CLAIMED; + dp->stats.interrupts++; + } + +out: + dp->inIntr = FALSE; + mutex_exit(&dp->intrlock); + + if (resched) { + gld_sched(dp->macInfo); + } + + return ret; +} + + +/* + *----------------------------------------------------------------------------- + * Vxn_ReclaimRxBuf -- + * Callback handler invoked by freemsg(). Frees Rx buffer memory and mappings + * + * Results: + * None + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static void +Vxn_ReclaimRxBuf(rx_dma_buf_t *rxDesc) +{ + Vxn_FreeRxBufToPool(rxDesc); +} + +/* + *----------------------------------------------------------------------------- + * Vxn_FreeRxBuf -- + * Free allocated Rx buffer + * + * Results: + * None + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static void +Vxn_FreeRxBuf(rx_dma_buf_t *rxDesc) +{ + ASSERT(rxDesc); + + if (rxDesc->mblk) { + freemsg(rxDesc->mblk); + } else { + Vxn_FreeDmaMem(&rxDesc->dmaDesc); + kmem_free(rxDesc, sizeof(rx_dma_buf_t)); + } +} + + +/* + *----------------------------------------------------------------------------- + * Vxn_AllocRxBuf -- + * Allocate Rx buffer + * + * Results: + * Pointer to Rx buffer descriptor - on success + * NULL - on failure + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static rx_dma_buf_t * +Vxn_AllocRxBuf(vxn_softc_t *dp, int cansleep) +{ + rx_dma_buf_t *rxDesc; + + rxDesc = (rx_dma_buf_t *)kmem_zalloc(sizeof(rx_dma_buf_t), + cansleep ? KM_SLEEP : KM_NOSLEEP); + if (!rxDesc) { + cmn_err(CE_WARN, "%s%d: Vxn_AllocRxBuf: kmem_zalloc failed", + dp->drvName, dp->unit); + return NULL; + } + + rxDesc->softc = dp; + + /* + * Alloc dma-able packet memory + */ + if (Vxn_AllocDmaMem(dp, MAXPKTBUF, cansleep, &rxDesc->dmaDesc) + != SOLVMXNET_SUCCESS) { + kmem_free(rxDesc, sizeof(rx_dma_buf_t)); + return NULL; + } + + /* + * Fill in free callback; fired by freemsg() + */ + rxDesc->freeCB.free_func = &Vxn_ReclaimRxBuf; + rxDesc->freeCB.free_arg = (caddr_t) rxDesc; + + rxDesc->mblk = NULL; + return rxDesc; +} + +/* + *----------------------------------------------------------------------------- + * Vxn_FreeInitBuffers -- + * Free allocated Tx and Rx buffers + * + * Results: + * None + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static void +Vxn_FreeInitBuffers(vxn_softc_t *dp) +{ + int i; + + for (i=0; ivxnNumRxBufs; i++) { + if (dp->rxRingBuffPtr[i]) { + Vxn_FreeRxBuf(dp->rxRingBuffPtr[i]); + dp->rxRingBuffPtr[i] = NULL; + } + } + + for (i=0; ivxnNumTxBufs; i++) { + if (TX_RINGBUF_MBLK(dp, i)) { + Vxn_FreeTxBuf(dp, i); + } + } + + /* + * Rx pool must get freed last. Rx buffers above will + * show up on the pool when freemsg callback fires. + */ + Vxn_FreeRxBufPool(dp); +} + + +/* + *----------------------------------------------------------------------------- + * Vxn_AllocRxBufPool -- + * Allocate pool of rx buffers - 3 * configured Rx buffers + * + * Results: + * SOLVMXNET_SUCCESS/SOLVMXNET_FAILURE + * + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static int +Vxn_AllocRxBufPool(vxn_softc_t *dp) +{ + int i; + + dp->rxFreeBufList = NULL; + + // Allow list to double in size if needed. Any additional buffers + // that are allocated on the fly will be freed back to main memory. + dp->rxMaxFreeBufs = dp->vxnNumRxBufs * 6; + + for (i = 0; i < dp->vxnNumRxBufs * 3; i++) { + rx_dma_buf_t *rxDesc; + + /* + * Alloc rx buffer + */ + if (!(rxDesc = Vxn_AllocRxBuf(dp, TRUE))) { + cmn_err(CE_WARN, "%s%d: Vxn_AllocRxBufPool: failed to allocate memory", + dp->drvName, dp->unit); + dp->rxNumFreeBufs = i; + return SOLVMXNET_FAILURE; + } + /* + * Add to free list + */ + rxDesc->next = dp->rxFreeBufList; + dp->rxFreeBufList = rxDesc; + } + + dp->rxNumFreeBufs = i; + return SOLVMXNET_SUCCESS; +} + +/* + *----------------------------------------------------------------------------- + * Vxn_FreeRxBufPool -- + * Free rx buffers pool + * + * Results: + * None + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static void +Vxn_FreeRxBufPool(vxn_softc_t *dp) +{ + while (dp->rxFreeBufList) { + rx_dma_buf_t *rxDesc = dp->rxFreeBufList; + + /* unlink */ + dp->rxFreeBufList = rxDesc->next; + + ASSERT(rxDesc->mblk == NULL); + Vxn_FreeDmaMem(&rxDesc->dmaDesc); + kmem_free(rxDesc, sizeof(rx_dma_buf_t)); + } + dp->rxNumFreeBufs = 0; +} + +/* + *----------------------------------------------------------------------------- + * Vxn_AllocRxBufFromPool -- + * Allocate Rx buffer from free pool + * + * Results: + * Pointer to Rx buffer descriptor - on success + * NULL - on failure + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static rx_dma_buf_t * +Vxn_AllocRxBufFromPool(vxn_softc_t *dp) +{ + rx_dma_buf_t *rxDesc = NULL; + + mutex_enter(&dp->rxlistlock); + if (dp->rxFreeBufList) { + rxDesc = dp->rxFreeBufList; + dp->rxFreeBufList = rxDesc->next; + ASSERT(dp->rxNumFreeBufs >= 1); + dp->rxNumFreeBufs--; + } + mutex_exit(&dp->rxlistlock); + + if (!rxDesc) { + /* + * Try to allocate new descriptor from memory. Can't block here + * since we could be being called from interrupt context. + */ + DPRINTF(5, (CE_NOTE, "%s%d: allocating rx buf from memory", + dp->drvName, dp->unit)); + if (!(rxDesc = Vxn_AllocRxBuf(dp, FALSE))) { + cmn_err(CE_WARN, + "%s%d: Vxn_AllocRxBufFromPool : pool rx alloc failed", + dp->drvName, dp->unit); + return NULL; + } + } + + /* + * Allocate new message block for this buffer + */ + rxDesc->mblk = desballoc((uchar_t *)rxDesc->dmaDesc.buf + ETHERALIGN, + rxDesc->dmaDesc.bufLen - ETHERALIGN, + BPRI_MED, &rxDesc->freeCB); + if (!rxDesc->mblk) { + cmn_err(CE_WARN, "%s%d: Vxn_AllocRxBufFromPool : desballoc failed", + dp->drvName, dp->unit); + + /* put back on free list */ + Vxn_FreeRxBufToPool(rxDesc); + return NULL; + } + + return rxDesc; +} + +/* + *----------------------------------------------------------------------------- + * Vxn_FreeRxBufToPool -- + * Return rx buffer to free pool + * + * Results: + * None + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static void +Vxn_FreeRxBufToPool(rx_dma_buf_t *rxDesc) +{ + vxn_softc_t *dp = rxDesc->softc; + + rxDesc->mblk = NULL; + + /* + * Insert on free list, or free if the list is full + */ + mutex_enter(&dp->rxlistlock); + if (dp->rxNumFreeBufs >= dp->rxMaxFreeBufs) { + DPRINTF(5, (CE_NOTE, "%s%d: freeing rx buf to memory", + dp->drvName, dp->unit)); + Vxn_FreeRxBuf(rxDesc); + } else { + rxDesc->next = dp->rxFreeBufList; + dp->rxFreeBufList = rxDesc; + dp->rxNumFreeBufs++; + } + mutex_exit(&dp->rxlistlock); +} + +/* + *----------------------------------------------------------------------------- + * Vxn_AllocInitBuffers -- + * Allocated Rx buffers and init ring entries + * + * Results: + * SOLVMXNET_SUCCESS - on success + * SOLVMXNET_FAILURE - on failure + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static int +Vxn_AllocInitBuffers(vxn_softc_t *dp) +{ + Vmxnet2_DriverData *dd; + uint32_t i, offset; + + dd = dp->driverData; + offset = sizeof(*dd); + + /* + * Init shared structures + */ + dd->rxRingLength = dp->vxnNumRxBufs; + dd->rxRingOffset = offset; + dp->rxRing = (Vmxnet2_RxRingEntry *)((uintptr_t)dd + offset); + offset += dp->vxnNumRxBufs * sizeof(Vmxnet2_RxRingEntry); + + dd->rxRingLength2 = 1; + dd->rxRingOffset2 = offset; + offset += sizeof(Vmxnet2_RxRingEntry); + + dd->txRingLength = dp->vxnNumTxBufs; + dd->txRingOffset = offset; + dp->txRing = (Vmxnet2_TxRingEntry *)((uintptr_t)dd + offset); + offset += dp->vxnNumTxBufs * sizeof(Vmxnet2_TxRingEntry); + + /* + * Alloc Rx buffers pool + */ + if ( Vxn_AllocRxBufPool(dp) != SOLVMXNET_SUCCESS) { + cmn_err(CE_WARN, "%s%d: Vxn_AllocInitBuffers: failed to alloc buf pool", + dp->drvName, dp->unit); + return SOLVMXNET_FAILURE; + } + + /* + * Allocate receive buffers + */ + for (i = 0; i < dp->vxnNumRxBufs; i++) { + rx_dma_buf_t *rxDesc; + Vmxnet2_RxRingEntry *rre = &dp->rxRing[i]; + + if (!(rxDesc = Vxn_AllocRxBufFromPool(dp))) { + cmn_err(CE_WARN, "%s%d: Vxn_AllocInitBuffers: " + "failed to alloc buf from pool", dp->drvName, dp->unit); + goto err; + } + + /* + * Init ring entries + */ + rre->paddr = rxDesc->dmaDesc.phyBuf + ETHERALIGN; + rre->bufferLength = MAXPKTBUF - ETHERALIGN; + rre->actualLength = 0; + dp->rxRingBuffPtr[i] = rxDesc; + rre->ownership = VMXNET2_OWNERSHIP_NIC; + } + + dp->txDmaHdl = NULL; + + /* + * Dummy recvRing2 tacked on to the end, with a single unusable entry + */ + dp->rxRing[i].paddr = 0; + dp->rxRing[i].bufferLength = 0; + dp->rxRing[i].actualLength = 0; + dp->rxRingBuffPtr[i] = NULL; + dp->rxRing[i].ownership = VMXNET2_OWNERSHIP_DRIVER; + + dd->rxDriverNext = 0; + + /* + * Give xmit ring ownership to DRIVER + */ + for (i = 0; i < dp->vxnNumTxBufs; i++) { + dp->txRing[i].ownership = VMXNET2_OWNERSHIP_DRIVER; + dp->txRingBuf[i].mblk = NULL; + dp->txRingBuf[i].dmaMem.buf = NULL; + dp->txRing[i].sg.sg[0].addrHi = 0; + } + + dd->txDriverCur = dd->txDriverNext = 0; + dd->txStopped = FALSE; + + return SOLVMXNET_SUCCESS; + +err: + for (i=0; ivxnNumRxBufs; i++) { + if (dp->rxRingBuffPtr[i]) { + Vxn_FreeRxBuf(dp->rxRingBuffPtr[i]); + dp->rxRingBuffPtr[i] = NULL; + } + } + + Vxn_FreeRxBufPool(dp); + return SOLVMXNET_FAILURE; +} + +/* + *----------------------------------------------------------------------------- + * Vxn_FreeDmaMem -- + * Free allocated dma memory + * + * Results: + * None + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static void +Vxn_FreeDmaMem(dma_buf_t *dma) +{ + ddi_dma_unbind_handle(dma->dmaHdl); + ddi_dma_mem_free(&dma->dataAccHdl); + ddi_dma_free_handle(&dma->dmaHdl); + + dma->buf = NULL; + dma->phyBuf = NULL; + dma->bufLen = 0; +} + +/* + *----------------------------------------------------------------------------- + * Vxn_AllocDmaMem -- + * Allocate dma-able memory and fill passed in dma descriptor pointer + * if successful + * + * Results: + * SOLVMXNET_SUCCESS on success + * SOLVMXNET_FAILURE on failure + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static int +Vxn_AllocDmaMem(vxn_softc_t *dp, int size, int cansleep, dma_buf_t *dma) +{ + /* + * Allocate handle + */ + if (ddi_dma_alloc_handle(dp->dip, &vxn_dma_attrs, + cansleep ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT, + NULL, &dma->dmaHdl) != DDI_SUCCESS) { + cmn_err(CE_WARN, "%s%d: Vxn_AllocDmaMem: failed to allocate handle", + dp->drvName, dp->unit); + return SOLVMXNET_FAILURE; + } + + /* + * Allocate memory + */ + if (ddi_dma_mem_alloc(dma->dmaHdl, size, &vxn_buf_attrs, DDI_DMA_CONSISTENT, + cansleep ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT, NULL, + &dma->buf, &dma->bufLen, &dma->dataAccHdl) + != DDI_SUCCESS) { + cmn_err(CE_WARN, "%s%d: Vxn_AllocDmaMem: " + "ddi_dma_mem_alloc %d bytes failed", + dp->drvName, dp->unit, size); + ddi_dma_free_handle(&dma->dmaHdl); + return SOLVMXNET_FAILURE; + } + + /* + * Mapin memory + */ + if (ddi_dma_addr_bind_handle(dma->dmaHdl, NULL, dma->buf, dma->bufLen, + DDI_DMA_RDWR | DDI_DMA_STREAMING, + cansleep ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT, + NULL, &dma->cookie, &dma->cookieCount) + != DDI_DMA_MAPPED) { + cmn_err(CE_WARN, "%s%d: Vxn_AllocDmaMem: failed to bind handle", + dp->drvName, dp->unit); + ddi_dma_mem_free(&dma->dataAccHdl); + ddi_dma_free_handle(&dma->dmaHdl); + return SOLVMXNET_FAILURE; + } + + if (dma->cookieCount != 1) { + cmn_err(CE_WARN, "%s%d: Vxn_AllocDmaMem: too many DMA cookies", + dp->drvName, dp->unit); + Vxn_FreeDmaMem(dma); + return SOLVMXNET_FAILURE; + } + + /* + * Save physical address (for easy use) + */ + dma->phyBuf = dma->cookie.dmac_address; + + return SOLVMXNET_SUCCESS; +} + +/* + *----------------------------------------------------------------------------- + * Vxn_FreeDriverData -- + * Free driver data structures and Tx Rx buffers + * + * Results: + * None + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static void +Vxn_FreeDriverData(vxn_softc_t *dp) +{ + Vxn_FreeInitBuffers(dp); + Vxn_FreeDmaMem(&dp->driverDataDmaMem); +} + +/* + *----------------------------------------------------------------------------- + * Vxn_AllocDriverData -- + * Allocate driver data structures and Tx Rx buffers on init + * + * Results: + * SOLVMXNET_SUCCESS on success + * SOLVMXNET_FAILURE on failure + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static int +Vxn_AllocDriverData(vxn_softc_t *dp) +{ + uint32_t r, driverDataSize; + + /* + * Get configured receive buffers + */ + OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_GET_NUM_RX_BUFFERS); + r = INL(dp, VMXNET_COMMAND_ADDR); + if (r == 0 || r > MAX_NUM_RECV_BUFFERS) { + r = DEFAULT_NUM_RECV_BUFFERS; + } + dp->vxnNumRxBufs = r; + + /* + * Get configured transmit buffers + */ + OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_GET_NUM_TX_BUFFERS); + r = INL(dp, VMXNET_COMMAND_ADDR); + if (r == 0 || r > MAX_NUM_XMIT_BUFFERS) { + r = DEFAULT_NUM_XMIT_BUFFERS; + } + dp->vxnNumTxBufs = r; + + /* + * Calculate shared data size and allocate memory for it + */ + driverDataSize = + sizeof(Vmxnet2_DriverData) + + /* numRecvBuffers + 1 for the dummy recvRing2 (used only by Windows) */ + (dp->vxnNumRxBufs + 1) * sizeof(Vmxnet2_RxRingEntry) + + dp->vxnNumTxBufs * sizeof(Vmxnet2_TxRingEntry); + + if (Vxn_AllocDmaMem(dp, driverDataSize, TRUE, &dp->driverDataDmaMem) + != SOLVMXNET_SUCCESS) { + return SOLVMXNET_FAILURE; + } + + /* + * Clear memory (bzero isn't resolved by module loader for some reason) + */ + ASSERT(dp->driverDataDmaMem.buf && dp->driverDataDmaMem.bufLen); + Vxn_Memset(dp->driverDataDmaMem.buf, 0, dp->driverDataDmaMem.bufLen); + + dp->driverData = (Vmxnet2_DriverData *)dp->driverDataDmaMem.buf; + dp->driverDataPhy = (void *)(uintptr_t)dp->driverDataDmaMem.phyBuf; + + /* So that the vmkernel can check it is compatible */ + dp->driverData->magic = VMXNET2_MAGIC; + dp->driverData->length = driverDataSize; + + /* + * Alloc rx/tx buffers, init ring, register with hardware etc. + */ + if (Vxn_AllocInitBuffers(dp) != SOLVMXNET_SUCCESS) { + Vxn_FreeDmaMem(&dp->driverDataDmaMem); + return SOLVMXNET_FAILURE; + } + + DPRINTF(3, (CE_CONT, "%s%d: numRxBufs=(%d*%"FMT64"d) numTxBufs=(%d*%"FMT64"d)" + " driverDataSize=%d driverDataPhy=0x%p\n", + dp->drvName, dp->unit, + dp->vxnNumRxBufs, (uint64_t)sizeof(Vmxnet2_RxRingEntry), + dp->vxnNumTxBufs, (uint64_t)sizeof(Vmxnet2_TxRingEntry), + driverDataSize, dp->driverDataPhy)); + + return SOLVMXNET_SUCCESS; +} + + +/* + *----------------------------------------------------------------------------- + * Vxn_Attach -- + * Probe and attach driver to stack + * + * Results: + * DDI_SUCCESS + * DDI_FAILURE + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static int +Vxn_Attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + int i, ret, len, unit; + const char *drvName; + ddi_acc_handle_t confHdl; + uint16_t vid, did; + uint8_t revid; + struct pci_phys_spec *regs; + caddr_t vxnIOp; + ddi_acc_handle_t vxnIOHdl; + uint32_t vLow, vHigh; + gld_mac_info_t *macInfo; + vxn_softc_t *dp; + boolean_t morphed = FALSE; + uint_t regSpaceSize; + uint_t chip; + uint_t vxnIOSize; + + if (cmd != DDI_ATTACH) { + return DDI_FAILURE; + } + + unit = ddi_get_instance(dip); + drvName = ddi_driver_name(dip); + + /* + * Check if chip is supported. + */ + if (pci_config_setup(dip, &confHdl) != DDI_SUCCESS) { + cmn_err(CE_WARN, "%s%d: pci_config_setup() failed", drvName, unit); + return DDI_FAILURE; + } + + vid = pci_config_get16(confHdl, PCI_CONF_VENID); + did = pci_config_get16(confHdl, PCI_CONF_DEVID); + revid = pci_config_get8(confHdl, PCI_CONF_REVID); + + if (vid == PCI_VENDOR_ID_VMWARE && did == PCI_DEVICE_ID_VMWARE_NET) { + /* Found vmxnet */ + chip = VMXNET_CHIP; + } + else if (vid == PCI_VENDOR_ID_AMD && did == PCI_DEVICE_ID_AMD_VLANCE) { + /* Found vlance (maybe a vmxnet disguise) */ + chip = LANCE_CHIP; + } + else { + /* Not Found */ + DPRINTF(3, (CE_WARN, "%s: Vxn_Attach: wrong PCI venid/devid (0x%x, 0x%x)", + drvName, vid, did)); + goto err; + } + + DPRINTF(3, (CE_CONT, "%s%d: (vid: 0x%04x, did: 0x%04x, revid: 0x%02x)\n", + drvName, unit, vid, did, revid)); + + /* + * Get device properties + */ + regs = NULL; + len = 0; + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "reg", (caddr_t)®s, &len) != DDI_PROP_SUCCESS) { + cmn_err(CE_WARN, "%s%d: Vxn_Attach: failed to get reg property", + drvName, unit); + goto err; + } + + ASSERT(regs != NULL && len > 0); + + /* + * Search device properties for IO-space + */ + for (i = 0; i regSpaceSize) { + cmn_err(CE_WARN, "%s%d: Vxn_Attach: " + "vlance device is not supported by this driver", drvName, unit); + goto err_free_regs_map; + } + + /* + * Morph, if we found a vlance adapter + */ + if (chip == LANCE_CHIP) { + uint16_t magic; + + /* Read morph port to verify that we can morph the adapter */ + magic = ddi_get16(vxnIOHdl, (uint16_t *)(vxnIOp - MORPH_PORT_SIZE)); + if (magic != LANCE_CHIP && magic != VMXNET_CHIP) { + cmn_err(CE_WARN, "%s%d: Vxn_Attach: Invalid magic, read: 0x%08X", + drvName, unit, magic); + goto err_free_regs_map; + } + + /* Morph */ + ddi_put16(vxnIOHdl, (uint16_t *)(vxnIOp - MORPH_PORT_SIZE), VMXNET_CHIP); + morphed = TRUE; + + /* Verify that we morphed correctly */ + magic = ddi_get16(vxnIOHdl, (uint16_t *)(vxnIOp - MORPH_PORT_SIZE)); + if (magic != VMXNET_CHIP) { + cmn_err(CE_WARN, "%s%d: Vxn_Attach: Couldn't morph adapter." + " Invalid magic, read:: 0x%08X", drvName, unit, magic); + goto err_morph_back; + } + } + + /* + * Check the version number of the device implementation + */ + vLow = (uint32_t)ddi_get32(vxnIOHdl, + (uint32_t *)(vxnIOp+VMXNET_LOW_VERSION)); + vHigh = (uint32_t)ddi_get32(vxnIOHdl, + (uint32_t *)(vxnIOp+VMXNET_HIGH_VERSION)); + + if ((vLow & 0xffff0000) != (VMXNET2_MAGIC & 0xffff0000) || + ((VMXNET2_MAGIC < vLow) || (VMXNET2_MAGIC > vHigh))) { + cmn_err(CE_WARN, "%s%d: Vxn_Attach: driver version 0x%08X doesn't " + "match device 0x%08X:0x%08X", + drvName, unit, VMXNET2_MAGIC, vLow, vHigh); + goto err_version_mismatch; + } + + /* + * Alloc soft state + */ + macInfo = gld_mac_alloc(dip); + if (!macInfo) { + cmn_err(CE_WARN, "%s%d: Vxn_Attach: gld_mac_alloc failed", + drvName, unit); + goto err_gld_mac_alloc; + } + + dp = (vxn_softc_t *) kmem_zalloc(sizeof(vxn_softc_t), KM_SLEEP); + ASSERT(dp); + + /* + * Get interrupt cookie + */ + if (ddi_get_iblock_cookie(dip, 0, &dp->iblockCookie) != DDI_SUCCESS) { + cmn_err(CE_WARN, "%s%d: Vxn_Attach: ddi_get_iblock_cookie failed", + drvName, unit); + goto err_get_iblock_cookie; + } + + strncpy(dp->drvName, drvName, SOLVMXNET_MAXNAME); + dp->unit = unit; + dp->dip = dip; + dp->macInfo = macInfo; + dp->confHdl = confHdl; + dp->vxnIOHdl = vxnIOHdl; + dp->vxnIOp = vxnIOp; + dp->morphed = morphed; + dp->nicActive = FALSE; + dp->txPending = 0; + dp->maxTxFrags = 1; + + /* + * Initialize mutexes + */ + mutex_init(&dp->intrlock, NULL, MUTEX_DRIVER, (void *)dp->iblockCookie); + mutex_init(&dp->xmitlock, NULL, MUTEX_DRIVER, (void *)dp->iblockCookie); + mutex_init(&dp->rxlistlock, NULL, MUTEX_DRIVER, (void *)dp->iblockCookie); + + /* + * Allocate and initialize our private and shared data structures + */ + if (Vxn_AllocDriverData(dp) != SOLVMXNET_SUCCESS) { + goto err_alloc_driverdata; + } + + /* + * Read the MAC address from the device + */ + for (i = 0; i < 6; i++) { + dp->devAddr.ether_addr_octet[i] = + (uint8_t)ddi_get8(vxnIOHdl, (uint8_t *)(vxnIOp + VMXNET_MAC_ADDR + i)); + } + macInfo->gldm_vendor_addr = dp->devAddr.ether_addr_octet; + macInfo->gldm_broadcast_addr = etherbroadcastaddr.ether_addr_octet; + + DPRINTF(3, (CE_CONT, + "MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", + dp->devAddr.ether_addr_octet[0], + dp->devAddr.ether_addr_octet[1], + dp->devAddr.ether_addr_octet[2], + dp->devAddr.ether_addr_octet[3], + dp->devAddr.ether_addr_octet[4], + dp->devAddr.ether_addr_octet[5])); + + /* + * Configure GLD entry points + */ + macInfo->gldm_devinfo = dip; + macInfo->gldm_private = (caddr_t)dp; + macInfo->gldm_cookie = dp->iblockCookie; + macInfo->gldm_reset = Vxn_Reset; + macInfo->gldm_start = Vxn_Start; + macInfo->gldm_stop = Vxn_Stop; + macInfo->gldm_set_mac_addr = Vxn_SetMacAddress; + macInfo->gldm_send = Vxn_Send; + macInfo->gldm_set_promiscuous = Vxn_SetPromiscuous; + macInfo->gldm_get_stats = Vxn_GetStats; + macInfo->gldm_ioctl = NULL; + macInfo->gldm_set_multicast= Vxn_SetMulticast; + macInfo->gldm_intr = Vxn_Interrupt; + macInfo->gldm_mctl = NULL; + + macInfo->gldm_ident = (char *)ddi_driver_name(dip); + macInfo->gldm_type = DL_ETHER; + macInfo->gldm_minpkt = 0; + macInfo->gldm_maxpkt = ETHERMTU; + macInfo->gldm_addrlen = ETHERADDRL; + macInfo->gldm_saplen = -2; + macInfo->gldm_ppa = unit; + + /* + * Register with GLD (Generic Lan Driver) framework + */ + if (gld_register(dip, + (char *)ddi_driver_name(dip), macInfo) != DDI_SUCCESS) { + goto err_gld_register; + } + + /* + * Add interrupt to system. + */ + if (ddi_add_intr(dip, 0, NULL, NULL, gld_intr, + (caddr_t)macInfo) != DDI_SUCCESS) { + cmn_err(CE_WARN, "%s%d: ddi_add_intr failed", drvName, unit); + goto err_ddi_add_intr; + } + + /* + * Add to list of interfaces. + */ + mutex_enter(&vxnListLock); + dp->next = &vxnList; + dp->prev = vxnList.prev; + vxnList.prev->next = dp; + vxnList.prev = dp; + mutex_exit(&vxnListLock); + + /* + * Success + */ + return DDI_SUCCESS; + +err_ddi_add_intr: + gld_unregister(macInfo); + +err_gld_register: + Vxn_FreeDriverData(dp); + +err_alloc_driverdata: + mutex_destroy(&dp->intrlock); + mutex_destroy(&dp->xmitlock); + +err_get_iblock_cookie: + kmem_free(dp, sizeof(*dp)); + gld_mac_free(macInfo); + +err_gld_mac_alloc: +err_version_mismatch: +err_morph_back: + if (morphed) { + ddi_put16(vxnIOHdl, (uint16_t *)(vxnIOp - MORPH_PORT_SIZE), LANCE_CHIP); + } + +err_free_regs_map: + ddi_regs_map_free(&vxnIOHdl); + +err: + pci_config_teardown(&confHdl); + return DDI_FAILURE; +} + +/* + *----------------------------------------------------------------------------- + * Vxn_Detach -- + * Called on module unload + * + * Results: + * DDI_SUCCESS + * DDI_FAILURE + * + * Side effects: + * None + *----------------------------------------------------------------------------- + */ +static int +Vxn_Detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + gld_mac_info_t *macInfo; + vxn_softc_t *dp; + + macInfo = (gld_mac_info_t *)ddi_get_driver_private(dip); + dp = (vxn_softc_t *)macInfo->gldm_private; + + if (cmd == DDI_DETACH) { + /* + * Tear down interrupt + */ + ddi_remove_intr(dip, 0, macInfo->gldm_cookie); + gld_unregister(macInfo); + + /* + * Quiesce hardware + */ + Vxn_Stop(macInfo); + + /* + * Free driver-data, tx/rx buffers etc + */ + Vxn_FreeDriverData(dp); + + /* + * Destroy locks + */ + mutex_destroy(&dp->intrlock); + mutex_destroy(&dp->xmitlock); + + /* + * Unmorph + */ + if (dp->morphed) { + uint16_t magic; + + /* Verify that we had morphed earlier */ + magic = ddi_get16(dp->vxnIOHdl, + (uint16_t *)(dp->vxnIOp - MORPH_PORT_SIZE)); + if (magic != VMXNET_CHIP) { + cmn_err(CE_WARN, "%s%d: Vxn_Detach: Adapter not morphed" + " magic=0x%08X", dp->drvName, dp->unit, magic); + } + else { + /* Unmorph */ + ddi_put16(dp->vxnIOHdl, + (uint16_t *)(dp->vxnIOp - MORPH_PORT_SIZE), LANCE_CHIP); + + /* Verify */ + magic = ddi_get16(dp->vxnIOHdl, + (uint16_t *)(dp->vxnIOp - MORPH_PORT_SIZE)); + if (magic != LANCE_CHIP) { + cmn_err(CE_WARN, "%s%d: Vxn_Detach: Unable to unmorph adapter" + " magic=0x%08X", dp->drvName, dp->unit, magic); + } + } + } + + /* + * Release resister mappings + */ + ddi_regs_map_free(&dp->vxnIOHdl); + pci_config_teardown(&dp->confHdl); + + /* + * Remove from list of interfaces. + */ + mutex_enter(&vxnListLock); + ASSERT(dp != &vxnList); + dp->prev->next = dp->next; + dp->next->prev = dp->prev; + mutex_exit(&vxnListLock); + + /* + * Release memory + */ + kmem_free(dp, sizeof(*dp)); + gld_mac_free(macInfo); + + return DDI_SUCCESS; + } + else { + return DDI_FAILURE; + } +} + +static struct module_info vxnminfo = { + 0, /* mi_idnum */ + "vmxnet", /* mi_idname */ + 0, /* mi_minpsz */ + ETHERMTU, /* mi_maxpsz */ + QHIWATER, /* mi_hiwat */ + 1, /* mi_lowat */ +}; + +static struct qinit vxnrinit = { + NULL, /* qi_putp */ + gld_rsrv, /* qi_srvp */ + gld_open, /* qi_qopen */ + gld_close, /* qi_qclose */ + NULL, /* qi_qadmin */ + &vxnminfo, /* qi_minfo */ + NULL /* qi_mstat */ +}; + +static struct qinit vxnwinit = { + gld_wput, /* qi_putp */ + gld_wsrv, /* qi_srvp */ + NULL, /* qi_qopen */ + NULL, /* qi_qclose */ + NULL, /* qi_qadmin */ + &vxnminfo, /* qi_minfo */ + NULL /* qi_mstat */ +}; + +static struct streamtab vxn_info = { + &vxnrinit, /* st_rdinit */ + &vxnwinit, /* st_wrinit */ + NULL, /* st_muxrinit */ + NULL /* st_muxwrinit */ +}; + +static struct cb_ops cb_vxn_ops = { + nulldev, /* cb_open */ + nulldev, /* cb_close */ + nodev, /* cb_strategy */ + nodev, /* cb_print */ + nodev, /* cb_dump */ + nodev, /* cb_read */ + nodev, /* cb_write */ + nodev, /* cb_ioctl */ + nodev, /* cb_devmap */ + nodev, /* cb_mmap */ + nodev, /* cb_segmap */ + nochpoll, /* cb_chpoll */ + ddi_prop_op, /* cb_prop_op */ + &vxn_info, /* cb_stream */ + D_NEW|D_MP /* cb_flag */ +}; + +static struct dev_ops vxn_ops = { + DEVO_REV, /* devo_rev */ + 0, /* devo_refcnt */ + gld_getinfo, /* devo_getinfo */ + nulldev, /* devo_identify */ + nulldev, /* devo_probe */ + Vxn_Attach, /* devo_attach */ + Vxn_Detach, /* devo_detach */ + nodev, /* devo_reset */ + &cb_vxn_ops, /* devo_cb_ops */ + NULL, /* devo_bus_ops */ + ddi_power /* devo_power */ +}; + +static struct modldrv modldrv = { + &mod_driverops, + ident, + &vxn_ops, +}; + +static struct modlinkage modlinkage = { + MODREV_1, {&modldrv, NULL,} +}; + + +/* + * Module load entry point + */ +int +_init(void) +{ + int err; + + DPRINTF(5, (CE_CONT, "vxn: _init:\n")); + /* Initialize interface list */ + vxnList.next = vxnList.prev = &vxnList; + mutex_init(&vxnListLock, NULL, MUTEX_DRIVER, NULL); + if ((err = mod_install(&modlinkage)) != 0) { + mutex_destroy(&vxnListLock); + } + return err; +} + +/* + * Module unload entry point + */ +int +_fini(void) +{ + int err; + + DPRINTF(5, (CE_CONT, "vxn: _fini:\n")); + if ((err = mod_remove(&modlinkage)) == 0) { + mutex_destroy(&vxnListLock); + } + return err; +} + +/* + * Module info entry point + */ +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + diff --git a/open-vm-tools/modules/solaris/vmxnet/vmxnet2_def.h b/open-vm-tools/modules/solaris/vmxnet/vmxnet2_def.h new file mode 100644 index 000000000..5fede9240 --- /dev/null +++ b/open-vm-tools/modules/solaris/vmxnet/vmxnet2_def.h @@ -0,0 +1,414 @@ +/********************************************************* + * Copyright (C) 2004 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +#ifndef _VMXNET2_DEF_H_ +#define _VMXNET2_DEF_H_ + +#define INCLUDE_ALLOW_USERLEVEL +#define INCLUDE_ALLOW_VMMEXT +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_VMK_MODULE +#define INCLUDE_ALLOW_VMKERNEL +#define INCLUDE_ALLOW_DISTRIBUTE +#include "includeCheck.h" + +#include "net_sg.h" +#include "vmxnet_def.h" + + +/* + * Magic number that identifies this version of the vmxnet protocol. + */ +#define VMXNET2_MAGIC 0xbabe864f + +/* size of the rx ring */ +#define VMXNET2_MAX_NUM_RX_BUFFERS 128 +#define VMXNET2_DEFAULT_NUM_RX_BUFFERS 100 + + +/* size of the rx ring when enhanced vmxnet is used */ +#define ENHANCED_VMXNET2_MAX_NUM_RX_BUFFERS 512 +#define ENHANCED_VMXNET2_DEFAULT_NUM_RX_BUFFERS 150 + +/* size of the 2nd rx ring */ +#define VMXNET2_MAX_NUM_RX_BUFFERS2 2048 +#define VMXNET2_DEFAULT_NUM_RX_BUFFERS2 512 + +/* size of the tx ring */ +#define VMXNET2_MAX_NUM_TX_BUFFERS 128 +#define VMXNET2_DEFAULT_NUM_TX_BUFFERS 100 + +/* size of the tx ring when tso/jf is used */ +#define VMXNET2_MAX_NUM_TX_BUFFERS_TSO 512 +#define VMXNET2_DEFAULT_NUM_TX_BUFFERS_TSO 256 + +enum { + VMXNET2_OWNERSHIP_DRIVER, + VMXNET2_OWNERSHIP_DRIVER_PENDING, + VMXNET2_OWNERSHIP_NIC, + VMXNET2_OWNERSHIP_NIC_PENDING, + VMXNET2_OWNERSHIP_NIC_FRAG, + VMXNET2_OWNERSHIP_DRIVER_FRAG, +}; + +#define VMXNET2_SG_DEFAULT_LENGTH 6 + +typedef struct Vmxnet2_SG_Array { + uint16 addrType; + uint16 length; + NetSG_Elem sg[VMXNET2_SG_DEFAULT_LENGTH]; +} Vmxnet2_SG_Array; + +typedef struct Vmxnet2_RxRingEntry { + uint64 paddr; /* Physical address of the packet data. */ + uint32 bufferLength; /* The length of the data at paddr. */ + uint32 actualLength; /* The actual length of the received data. */ + uint16 ownership; /* Who owns the packet. */ + uint16 flags; /* Flags as defined below. */ + uint32 index; /* + * Currently: + * + * This is being used as an packet index to + * rx buffers. + * + * Originally: + * + * was void* driverData ("Driver specific data.") + * which was used for sk_buf**s in Linux and + * VmxnetRxBuff*s in Windows. It could not be + * here because the structure needs to be the + * same size between architectures, and it was + * not used on the device side, anyway. Look + * for its replacement in + * Vmxnet_Private.rxRingBuffPtr on Linux and + * VmxnetAdapter.rxRingBuffPtr on Windows. + */ +} Vmxnet2_RxRingEntry; + +/* + * Vmxnet2_RxRingEntry flags: + * + * VMXNET2_RX_HW_XSUM_OK The hardware verified the TCP/UDP checksum. + * VMXNET2_RX_WITH_FRAG More data is in the 2nd ring + * VMXNET2_RX_FRAG_EOP This is the last frag, the only valid flag for + * 2nd ring entry + * + */ +#define VMXNET2_RX_HW_XSUM_OK 0x01 +#define VMXNET2_RX_WITH_FRAG 0x02 +#define VMXNET2_RX_FRAG_EOP 0x04 + +typedef struct Vmxnet2_TxRingEntry { + uint16 flags; /* Flags as defined below. */ + uint16 ownership; /* Who owns this packet. */ + uint32 extra; /* + * was void* driverData ("Driver specific data.") + * which was used for sk_buf*s in Linux and + * VmxnetTxInfo*s in Windows. It could not be + * here because the structure needs to be the + * same size between architectures, and it was + * not used on the device side, anyway. Look + * for its replacement in + * Vmxnet_Private.txRingBuffPtr on Linux and + * VmxnetAdapter.txRingBuffPtr on Windows. + */ + uint32 tsoMss; /* TSO pkt MSS */ + Vmxnet2_SG_Array sg; /* Packet data. */ +} Vmxnet2_TxRingEntry; + +/* + * Vmxnet2_TxRingEntry flags: + * + * VMXNET2_TX_CAN_KEEP The implementation can return the tx ring entry + * to the driver when it is ready as opposed to + * before the transmit call from the driver completes. + * VMXNET2_TX_RING_LOW The driver's transmit ring buffer is low on free + * slots. + * VMXNET2_TX_HW_XSUM The hardware should perform the TCP/UDP checksum + * VMXNET2_TX_TSO The hardware should do TCP segmentation. + * VMXNET2_TX_PINNED_BUFFER The driver used one of the preallocated vmkernel + * buffers *and* it has been pinned with Net_PinTxBuffers. + * VMXNET2_TX_MORE This is *not* the last tx entry for the pkt. + * All flags except VMXNET2_TX_MORE are ignored + * for the subsequent tx entries. + */ +#define VMXNET2_TX_CAN_KEEP 0x0001 +#define VMXNET2_TX_RING_LOW 0x0002 +#define VMXNET2_TX_HW_XSUM 0x0004 +#define VMXNET2_TX_TSO 0x0008 +#define VMXNET2_TX_PINNED_BUFFER 0x0010 +#define VMXNET2_TX_MORE 0x0020 + +/* + * Structure used by implementations. This structure allows the inline + * functions below to be used. + */ +typedef struct Vmxnet2_RxRingInfo { + Vmxnet2_RxRingEntry *base; /* starting addr of the ring */ + uint32 nicNext; /* next entry to use in the ring */ + uint32 ringLength; /* # of entries in the ring */ + PA startPA; /* PA of the starting addr of the ring */ +#ifdef VMX86_DEBUG + const char *name; +#endif +} Vmxnet2_RxRingInfo; + +typedef struct Vmxnet2_TxRingInfo { + Vmxnet2_TxRingEntry *base; /* starting addr of the ring */ + uint32 nicNext; /* next entry to use in the ring */ + uint32 ringLength; /* # of entries in the ring */ + PA startPA; /* PA of the starting addr of the ring */ +#ifdef VMX86_DEBUG + const char *name; +#endif +} Vmxnet2_TxRingInfo; + +typedef struct Vmxnet2_ImplData { + Vmxnet2_RxRingInfo rxRing; + Vmxnet2_RxRingInfo rxRing2; + Vmxnet2_TxRingInfo txRing; + + struct PhysMem_Token *ddPhysMemToken; +} Vmxnet2_ImplData; + +/* + * Used internally for performance studies. By default this will be off so there + * should be no compatibilty or other interferences. + */ + +/* #define ENABLE_VMXNET2_PROFILING */ + + +#ifdef ENABLE_VMXNET2_PROFILING +typedef struct Vmxnet2_VmmStats { + uint64 vIntTSC; /* the time that virtual int was posted */ + uint64 actionsCount; /* Number of actions received */ + uint64 numWasteActions; /* Number of non-productive actions */ +} Vmxnet2_VmmStats; +#endif + +typedef struct Vmxnet2_DriverStats { + uint32 transmits; /* # of times that the drivers transmit function */ + /* is called. The driver could transmit more */ + /* than one packet per call. */ + uint32 pktsTransmitted; /* # of packets transmitted. */ + uint32 noCopyTransmits; /* # of packets that are transmitted without */ + /* copying any data. */ + uint32 copyTransmits; /* # of packets that are transmittted by copying */ + /* the data into a buffer. */ + uint32 maxTxsPending; /* Max # of transmits outstanding. */ + uint32 txStopped; /* # of times that transmits got stopped because */ + /* the tx ring was full. */ + uint32 txRingOverflow; /* # of times that transmits got deferred bc */ + /* the tx ring was full. This must be >= */ + /* txStopped since there will be one */ + /* txStopped when the ring fills up and then */ + /* one txsRingOverflow for each packet that */ + /* that gets deferred until there is space. */ + uint32 interrupts; /* # of times interrupted. */ + uint32 pktsReceived; /* # of packets received. */ + uint32 rxBuffersLow; /* # of times that the driver was low on */ + /* receive buffers. */ +#ifdef ENABLE_VMXNET2_PROFILING + Vmxnet2_VmmStats vmmStats; /* vmm related stats for perf study */ +#endif +} Vmxnet2_DriverStats; + +/* + * Shared data structure between the vm, the vmm, and the vmkernel. + * This structure was originally arranged to try to group common data + * on 32-byte cache lines, but bit rot and the fact that we no longer + * run on many CPUs with that cacheline size killed that optimization. + * vmxnet3 should target 128 byte sizes and alignments to optimize for + * the 64 byte cacheline pairs on P4. + */ +typedef struct Vmxnet2_DriverData { + /* + * Magic must be first. + */ + Vmxnet_DDMagic magic; + + /* + * Receive fields. + */ + uint32 rxRingLength; /* Length of the receive ring. */ + uint32 rxDriverNext; /* Index of the next packet that will */ + /* be filled in by the impl */ + + uint32 rxRingLength2; /* Length of the 2nd receive ring. */ + uint32 rxDriverNext2; /* Index of the next packet that will */ + /* be filled in by the impl */ + + uint32 notUsed1; /* was "irq" */ + + /* + * Interface flags and multicast filter. + */ + uint32 ifflags; + uint32 LADRF[VMXNET_MAX_LADRF]; + + /* + * Transmit fields + */ + uint32 txDontClusterSize; /* All packets <= this will be transmitted */ + /* immediately, regardless of clustering */ + /* settings [was fill[1]] */ + uint32 txRingLength; /* Length of the transmit ring. */ + uint32 txDriverCur; /* Index of the next packet to be */ + /* returned by the implementation.*/ + uint32 txDriverNext; /* Index of the entry in the ring */ + /* buffer to use for the next packet.*/ + uint32 txStopped; /* The driver has stopped transmitting */ + /* because its ring buffer is full.*/ + uint32 txClusterLength; /* Maximum number of packets to */ + /* put in the ring buffer before */ + /* asking the implementation to */ + /* transmit the packets in the buffer.*/ + uint32 txNumDeferred; /* Number of packets that have been */ + /* queued in the ring buffer since */ + /* the last time the implementation */ + /* was asked to transmit. */ + uint32 notUsed3; /* This field is deprecated but still used */ + /* as minXmitPhysLength on the escher branch. */ + /* It cannot be used for other purposes */ + /* until escher vms no longer are allowed */ + /* to install this driver. */ + + uint32 totalRxBuffers; /* used by esx for max rx buffers */ + uint64 rxBufferPhysStart; /* used by esx for pinng rx buffers */ + /* + * Extra fields for future expansion. + */ + uint32 extra[2]; + + uint16 maxFrags; /* # of frags the driver can handle */ + uint16 featureCtl; /* for driver to enable some feature */ + + /* + * The following fields are used to save the nicNext indexes part + * of implData in the vmkernel when disconnecting the adapter, we + * need them when we reconnect. This mechanism is used for + * checkpointing as well. + */ + uint32 savedRxNICNext; + uint32 savedRxNICNext2; + uint32 savedTxNICNext; + + /* + * Fields used during initialization or debugging. + */ + uint32 length; + uint32 rxRingOffset; + uint32 rxRingOffset2; + uint32 txRingOffset; + uint32 debugLevel; + uint32 txBufferPhysStart; + uint32 txBufferPhysLength; + uint32 txPktMaxSize; + + /* + * Driver statistics. + */ + Vmxnet2_DriverStats stats; +} Vmxnet2_DriverData; + +/* + * Shared between VMM and Vmkernel part of vmxnet2 to optimize action posting + * VMM writes 1 (don't post) or 0 (okay to post) and vmk reads this. + */ +typedef struct VmxnetVMKShared { + uint32 dontPostActions; +} VmxnetVMKShared; + +/* + * Inline functions used to assist the implementation of the vmxnet interface. + */ + +/* + * Get the next empty packet out of the receive ring and move to + * the next packet. + */ +static INLINE Vmxnet2_RxRingEntry * +Vmxnet2_GetNextRx(Vmxnet2_RxRingInfo *ri, uint16 ownership) +{ + Vmxnet2_RxRingEntry *rre = ri->base + ri->nicNext; + if (rre->ownership == ownership) { + VMXNET_INC(ri->nicNext, ri->ringLength); + } else { + rre = NULL; + } + + return rre; +} + +/* + * Return ownership of a packet in the receive ring to the driver. + */ +static INLINE void +Vmxnet2_PutRx(Vmxnet2_RxRingEntry *rre, uint32 pktLength, uint16 ownership) +{ + rre->actualLength = pktLength; + rre->ownership = ownership; +} + +/* + * Get the next pending packet out of the transmit ring. + */ +static INLINE Vmxnet2_TxRingEntry * +Vmxnet2_GetNextTx(Vmxnet2_TxRingInfo *ri) +{ + Vmxnet2_TxRingEntry *txre = ri->base + ri->nicNext; + if (txre->ownership == VMXNET2_OWNERSHIP_NIC) { + return txre; + } else { + return NULL; + } +} + +/* + * Move to the next entry in the transmit ring. + */ +static INLINE unsigned int +Vmxnet2_IncNextTx(Vmxnet2_TxRingInfo *ri) +{ + unsigned int prev = ri->nicNext; + Vmxnet2_TxRingEntry *txre = ri->base + ri->nicNext; + + txre->ownership = VMXNET2_OWNERSHIP_NIC_PENDING; + + VMXNET_INC(ri->nicNext, ri->ringLength); + return prev; +} + +/* + * Get the indicated entry from transmit ring. + */ +static INLINE Vmxnet2_TxRingEntry * +Vmxnet2_GetTxEntry(Vmxnet2_TxRingInfo *ri, unsigned int idx) +{ + return ri->base + idx; +} + +/* + * Get the indicated entry from the given rx ring + */ +static INLINE Vmxnet2_RxRingEntry * +Vmxnet2_GetRxEntry(Vmxnet2_RxRingInfo *ri, unsigned int idx) +{ + return ri->base + idx; +} +#endif + diff --git a/open-vm-tools/modules/solaris/vmxnet/vmxnet_def.h b/open-vm-tools/modules/solaris/vmxnet/vmxnet_def.h new file mode 100644 index 000000000..841a250ec --- /dev/null +++ b/open-vm-tools/modules/solaris/vmxnet/vmxnet_def.h @@ -0,0 +1,168 @@ +/********************************************************* + * Copyright (C) 1999 VMware, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of the Common + * Development and Distribution License (the "License") version 1.0 + * and no later version. You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at + * http://www.opensource.org/licenses/cddl1.php + * + * See the License for the specific language governing permissions + * and limitations under the License. + * + *********************************************************/ + +#ifndef _VMXNET_DEF_H_ +#define _VMXNET_DEF_H_ + +#define INCLUDE_ALLOW_USERLEVEL +#define INCLUDE_ALLOW_VMMEXT +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_VMK_MODULE +#define INCLUDE_ALLOW_VMKERNEL +#define INCLUDE_ALLOW_DISTRIBUTE +#include "includeCheck.h" + +#include "net_sg.h" +#include "vmnet_def.h" + + +/* + * Vmxnet I/O ports, used by both the vmxnet driver and + * the device emulation code. + */ + +#define VMXNET_INIT_ADDR 0x00 +#define VMXNET_INIT_LENGTH 0x04 +#define VMXNET_TX_ADDR 0x08 +#define VMXNET_COMMAND_ADDR 0x0c +#define VMXNET_MAC_ADDR 0x10 +#define VMXNET_LOW_VERSION 0x18 +#define VMXNET_HIGH_VERSION 0x1c +#define VMXNET_STATUS_ADDR 0x20 +#define VMXNET_TOE_INIT_ADDR 0x24 +#define VMXNET_APROM_ADDR 0x28 +#define VMXNET_INT_ENABLE_ADDR 0x30 +#define VMXNET_WAKE_PKT_PATTERNS 0x34 + +/* + * Vmxnet command register values. + */ +#define VMXNET_CMD_INTR_ACK 0x0001 +#define VMXNET_CMD_UPDATE_LADRF 0x0002 +#define VMXNET_CMD_UPDATE_IFF 0x0004 +#define VMXNET_CMD_UNUSED 1 0x0008 +#define VMXNET_CMD_UNUSED_2 0x0010 +#define VMXNET_CMD_INTR_DISABLE 0x0020 +#define VMXNET_CMD_INTR_ENABLE 0x0040 +#define VMXNET_CMD_UNUSED_3 0x0080 +#define VMXNET_CMD_CHECK_TX_DONE 0x0100 +#define VMXNET_CMD_GET_NUM_RX_BUFFERS 0x0200 +#define VMXNET_CMD_GET_NUM_TX_BUFFERS 0x0400 +#define VMXNET_CMD_PIN_TX_BUFFERS 0x0800 +#define VMXNET_CMD_GET_CAPABILITIES 0x1000 +#define VMXNET_CMD_GET_FEATURES 0x2000 +#define VMXNET_CMD_SET_POWER_FULL 0x4000 +#define VMXNET_CMD_SET_POWER_LOW 0x8000 + +/* + * Vmxnet status register values. + */ +#define VMXNET_STATUS_CONNECTED 0x0001 +#define VMXNET_STATUS_ENABLED 0x0002 +#define VMXNET_STATUS_TX_PINNED 0x0004 + +/* + * Values for the interface flags. + */ +#define VMXNET_IFF_PROMISC 0x01 +#define VMXNET_IFF_BROADCAST 0x02 +#define VMXNET_IFF_MULTICAST 0x04 +#define VMXNET_IFF_DIRECTED 0x08 + +/* + * Length of the multicast address filter. + */ +#define VMXNET_MAX_LADRF 2 + +/* + * Size of Vmxnet APROM. + */ +#define VMXNET_APROM_SIZE 6 + +/* + * An invalid ring index. + */ +#define VMXNET_INVALID_RING_INDEX (-1) + +/* + * Features that are implemented by the driver. These are driver + * specific so not all features will be listed here. In addition not all + * drivers have to pay attention to these feature flags. + * + * VMXNET_FEATURE_ZERO_COPY_TX The driver won't do any copies as long as + * the packet length is > + * Vmxnet_DriverData.minTxPhysLength. + * + * VMXNET_FEATURE_TSO The driver will use the TSO capabilities + * of the underlying hardware if available + * and enabled. + * + * VMXNET_FEATURE_JUMBO_FRAME The driver can send/rcv jumbo frame + * + * VMXNET_FEATURE_LPD The backend can deliver large pkts + */ +#define VMXNET_FEATURE_ZERO_COPY_TX 0x01 +#define VMXNET_FEATURE_TSO 0x02 +#define VMXNET_FEATURE_JUMBO_FRAME 0x04 +#define VMXNET_FEATURE_LPD 0x08 + +/* + * Define the set of capabilities required by each feature above + */ +#define VMXNET_FEATURE_ZERO_COPY_TX_CAPS VMXNET_CAP_SG +#define VMXNET_FEATURE_TSO_CAPS VMXNET_CAP_TSO +#define VMXNET_HIGHEST_FEATURE_BIT VMXNET_FEATURE_TSO + +#define VMXNET_INC(val, max) \ + val++; \ + if (UNLIKELY(val == max)) { \ + val = 0; \ + } + +/* + * code that just wants to switch on the different versions of the + * guest<->implementation protocol can cast driver data to this. + */ +typedef uint32 Vmxnet_DDMagic; + +/* + * Wake packet pattern commands sent through VMXNET_WAKE_PKT_PATTERNS port + */ + +#define VMXNET_PM_OPCODE_START 3 /* args: cnt of wake packet patterns */ +#define VMXNET_PM_OPCODE_LEN 2 /* args: index of wake packet pattern */ + /* number of pattern byte values */ +#define VMXNET_PM_OPCODE_DATA 1 /* args: index of wake packet pattern */ + /* offset in pattern byte values list */ + /* packet byte offset */ + /* packet byte value */ +#define VMXNET_PM_OPCODE_END 0 /* args: */ + +typedef union Vmxnet_WakePktCmd { + uint32 pktData : 32; + struct { + unsigned cmd : 2; /* wake packet pattern cmd [from list above] */ + unsigned cnt : 3; /* cnt wk pkt pttrns 1..MAX_NUM_FILTER_PTTRNS */ + unsigned ind : 3; /* ind wk pkt pttrn 0..MAX_NUM_FILTER_PTTRNS-1 */ + unsigned lenOff : 8; /* num pttrn byte vals 1..MAX_PKT_FILTER_SIZE */ + /* OR offset in pattern byte values list */ + /* 0..MAX_PKT_FILTER_SIZE-1 */ + unsigned byteOff : 8; /* pkt byte offset 0..MAX_PKT_FILTER_SIZE-1 */ + unsigned byteVal : 8; /* packet byte value 0..255 */ + } pktPttrn; +} Vmxnet_WakePktCmd; + +#endif /* _VMXNET_DEF_H_ */ diff --git a/open-vm-tools/services/Makefile.am b/open-vm-tools/services/Makefile.am new file mode 100644 index 000000000..21b7928e3 --- /dev/null +++ b/open-vm-tools/services/Makefile.am @@ -0,0 +1,25 @@ +################################################################################ +### Copyright 2009 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 +################################################################################ + +SUBDIRS = +SUBDIRS += vmtoolsd +SUBDIRS += plugins + +install-exec-local: + mkdir -p $(DESTDIR)/etc/vmware-tools + ln -sf `dirname $(VMSVC_PLUGIN_INSTALLDIR)` $(DESTDIR)/etc/vmware-tools/plugins + diff --git a/open-vm-tools/services/plugins/COPYING b/open-vm-tools/services/plugins/COPYING new file mode 100644 index 000000000..09f465ab7 --- /dev/null +++ b/open-vm-tools/services/plugins/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/open-vm-tools/services/plugins/Makefile.am b/open-vm-tools/services/plugins/Makefile.am new file mode 100644 index 000000000..cd0a63b84 --- /dev/null +++ b/open-vm-tools/services/plugins/Makefile.am @@ -0,0 +1,31 @@ +################################################################################ +### Copyright 2009 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 +################################################################################ + +SUBDIRS = +SUBDIRS += guestInfo +SUBDIRS += hgfsServer +SUBDIRS += powerOps +if HAVE_X11 + SUBDIRS += resolutionSet +endif +SUBDIRS += timeSync +SUBDIRS += vix +if HAVE_X11 + SUBDIRS += vixUser +endif +SUBDIRS += vmbackup + diff --git a/open-vm-tools/services/plugins/guestInfo/COPYING b/open-vm-tools/services/plugins/guestInfo/COPYING new file mode 100644 index 000000000..09f465ab7 --- /dev/null +++ b/open-vm-tools/services/plugins/guestInfo/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/open-vm-tools/services/plugins/guestInfo/Makefile.am b/open-vm-tools/services/plugins/guestInfo/Makefile.am new file mode 100644 index 000000000..89c5c891e --- /dev/null +++ b/open-vm-tools/services/plugins/guestInfo/Makefile.am @@ -0,0 +1,38 @@ +################################################################################ +### Copyright 2009 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 +################################################################################ + +noinst_LTLIBRARIES = libguestInfo.la + +libguestInfo_la_CPPFLAGS = +libguestInfo_la_CPPFLAGS += @PLUGIN_CPPFLAGS@ + +libguestInfo_la_LDFLAGS = +libguestInfo_la_LDFLAGS += @PLUGIN_LDFLAGS@ + +libguestInfo_la_LIBADD = +libguestInfo_la_LIBADD += @VMTOOLS_LIBS@ +libguestInfo_la_LIBADD += @PROCPS_LIBS@ +libguestInfo_la_LIBADD += @XDR_LIBS@ + +libguestInfo_la_SOURCES = +libguestInfo_la_SOURCES += guestInfoServer.c +libguestInfo_la_SOURCES += perfMonLinux.c + +install-exec-local: + mkdir -p $(DESTDIR)$(VMSVC_PLUGIN_INSTALLDIR) + cp -L $(builddir)/.libs/libguestInfo.so $(DESTDIR)$(VMSVC_PLUGIN_INSTALLDIR) + diff --git a/open-vm-tools/services/plugins/guestInfo/guestInfoInt.h b/open-vm-tools/services/plugins/guestInfo/guestInfoInt.h new file mode 100644 index 000000000..76a27319d --- /dev/null +++ b/open-vm-tools/services/plugins/guestInfo/guestInfoInt.h @@ -0,0 +1,30 @@ +/********************************************************* + * Copyright (C) 2008 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 guestInfoInt.h + * + * Declares internal functions of the guestInfo plugin. + */ + +#include "guestInfo.h" +#define G_LOG_DOMAIN "guestinfo" + +Bool +GuestInfo_PerfMon(struct GuestMemInfo *vmStats); + diff --git a/open-vm-tools/services/plugins/guestInfo/guestInfoServer.c b/open-vm-tools/services/plugins/guestInfo/guestInfoServer.c new file mode 100644 index 000000000..c82b2e98e --- /dev/null +++ b/open-vm-tools/services/plugins/guestInfo/guestInfoServer.c @@ -0,0 +1,1043 @@ +/********************************************************* + * Copyright (C) 1998 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. + * + *********************************************************/ + +/* + * guestInfoServer.c -- + * + * This is the implementation of the common code in the guest tools + * to send out guest information to the host. The guest info server + * runs in the context of the tools daemon's event loop and periodically + * gathers all guest information and sends updates to the host if required. + * This file implements the platform independent framework for this. + */ + + +#include +#include +#include +#include +#include + +#include "vmware.h" +#include "buildNumber.h" +#include "conf.h" +#include "debug.h" +#include "dynxdr.h" +#include "hostinfo.h" +#include "guestInfoInt.h" +#include "guest_msg_def.h" // For GUESTMSG_MAX_IN_SIZE +#include "netutil.h" +#include "rpcChannel.h" +#include "rpcvmx.h" +#include "str.h" +#include "system.h" +#include "util.h" +#include "vm_app.h" +#include "vmtools.h" +#include "vmtoolsApp.h" +#include "xdrutil.h" + +#if !defined(__APPLE__) +#include "embed_version.h" +#include "vmtoolsd_version.h" +VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING); +#endif + + +#define GUESTINFO_DEFAULT_DELIMITER ' ' + +/* + * Stores information about all guest information sent to the vmx. + */ + +typedef struct _GuestInfoCache{ + char value[INFO_MAX][MAX_VALUE_LEN]; /* Stores values of all key-value pairs. */ + GuestNicList nicInfo; + GuestDiskInfo diskInfo; +} GuestInfoCache; + + +/* + * Local variables. + */ +static gboolean gDisableQueryDiskInfo = FALSE; + +/* Local cache of the guest information that was last sent to vmx. */ +static GuestInfoCache gInfoCache; + +/* + * A boolean flag that specifies whether the state of the VM was + * changed since the last time guest info was sent to the VMX. + * Tools daemon sets it to TRUE after the VM was resumed. + */ + +static Bool vmResumed; + +static Bool GuestInfoUpdateVmdb(ToolsAppCtx *ctx, GuestInfoType infoType, void *info); +static Bool SetGuestInfo(ToolsAppCtx *ctx, GuestInfoType key, + const char *value, char delimiter); +static Bool NicInfoChanged(GuestNicList *nicInfo); +static Bool DiskInfoChanged(PGuestDiskInfo diskInfo); +static void GuestInfoClearCache(void); +static int PrintNicInfo(GuestNicList *nicInfo, int (*PrintFunc)(const char *, ...)); + +/** + * Cleanup internal data on shutdown. + * + * @param[in] src The source object. + * @param[in] ctx Unused. + * @param[in] data Unused. + */ + +static void +GuestInfoServerShutdown(gpointer src, + ToolsAppCtx *ctx, + gpointer data) +{ + GuestInfoClearCache(); +} + + +/** + * Reset callback - sets the internal flag that says we should purge all + * caches. + * + * @param[in] src The source object. + * @param[in] ctx Unused. + * @param[in] data Unused. + */ + +static void +GuestInfoServerReset(gpointer src, + ToolsAppCtx *ctx, + gpointer data) +{ + vmResumed = TRUE; +} + + +/** + * Send the guest uptime through the backdoor, if @a set is TRUE. + * + * @param[in] src The source object. + * @param[in] ctx The application context. + * @param[in] set Whether capabilities are being set. + * @param[in] data Unused. + * + * @return NULL + */ + +static GArray * +GuestInfoServerSendUptime(gpointer src, + ToolsAppCtx *ctx, + gboolean set, + gpointer data) +{ + if (set) { + gchar *uptime = g_strdup_printf("%"FMT64"u", System_Uptime()); + g_debug("Setting guest uptime to '%s'\n", uptime); + GuestInfoUpdateVmdb(ctx, INFO_UPTIME, uptime); + g_free(uptime); + } + return NULL; +} + + +/** + * Responds to a "broadcastIP" Set_Option command, by sending the primary IP + * back to the VMX. + * + * @param[in] src The source object. + * @param[in] ctx The application context. + * @param[in] option Option name. + * @param[in] value Option value. + * @param[in] data Unused. + */ + +static gboolean +GuestInfoServerSetOption(gpointer src, + ToolsAppCtx *ctx, + const gchar *option, + const gchar *value, + gpointer data) +{ + char *ip; + Bool ret = FALSE; + + if (strcmp(option, TOOLSOPTION_BROADCASTIP) != 0) { + goto exit; + } + + if (strcmp(value, "0") == 0) { + ret = TRUE; + goto exit; + } + + if (strcmp(value, "1") != 0) { + goto exit; + } + + ip = NetUtil_GetPrimaryIP(); + + if (ip != NULL) { + gchar *msg; + msg = g_strdup_printf("info-set guestinfo.ip %s", ip); + ret = RpcChannel_Send(ctx->rpc, msg, strlen(msg) + 1, NULL, NULL); + vm_free(ip); + g_free(msg); + } + +exit: + return (gboolean) ret; +} + + +/** + * Periodically collects all the desired guest information and updates VMDB. + * + * @param[in] data The application context. + * + * @return TRUE. + */ + +static gboolean +GuestInfoGather(gpointer data) +{ + char name[255]; + char osNameFull[MAX_VALUE_LEN]; + char osName[MAX_VALUE_LEN]; + GuestNicList nicInfo; + GuestDiskInfo diskInfo; +#if defined(_WIN32) || defined(linux) + GuestMemInfo vmStats = {0}; +#endif + ToolsAppCtx *ctx = data; + + g_debug("Entered guest info gather.\n"); + + memset(&nicInfo, 0, sizeof nicInfo); + + /* Send tools version. */ + if (!GuestInfoUpdateVmdb(ctx, INFO_BUILD_NUMBER, BUILD_NUMBER)) { + /* + * An older vmx talking to new tools wont be able to handle + * this message. Continue, if thats the case. + */ + + g_warning("Failed to update VMDB with tools version.\n"); + } + + /* Gather all the relevant guest information. */ + if (!Hostinfo_GetOSName(sizeof osNameFull, sizeof osName, osNameFull, + osName)) { + g_warning("Failed to get OS info.\n"); + } else { + if (!GuestInfoUpdateVmdb(ctx, INFO_OS_NAME_FULL, osNameFull)) { + g_warning("Failed to update VMDB\n"); + } + if (!GuestInfoUpdateVmdb(ctx, INFO_OS_NAME, osName)) { + g_warning("Failed to update VMDB\n"); + } + } + + if (!gDisableQueryDiskInfo) { + if (!GuestInfo_GetDiskInfo(&diskInfo)) { + g_warning("Failed to get disk info.\n"); + } else { + if (!GuestInfoUpdateVmdb(ctx, INFO_DISK_FREE_SPACE, &diskInfo)) { + g_warning("Failed to update VMDB\n."); + } + } + /* Free memory allocated in GuestInfoGetDiskInfo. */ + if (diskInfo.partitionList != NULL) { + vm_free(diskInfo.partitionList); + diskInfo.partitionList = NULL; + } + } + + if (!GuestInfo_GetFqdn(sizeof name, name)) { + g_warning("Failed to get netbios name.\n"); + } else if (!GuestInfoUpdateVmdb(ctx, INFO_DNS_NAME, name)) { + g_warning("Failed to update VMDB.\n"); + } + + /* Get NIC information. */ + if (!GuestInfo_GetNicInfo(&nicInfo)) { + g_warning("Failed to get nic info.\n"); + } else if (NicInfoChanged(&nicInfo)) { + if (GuestInfoUpdateVmdb(ctx, INFO_IPADDRESS, &nicInfo)) { + /* + * Update the cache. Release the memory previously used by the cache, + * and copy the new information into the cache. + */ + VMX_XDR_FREE(xdr_GuestNicList, &gInfoCache.nicInfo); + gInfoCache.nicInfo = nicInfo; + } else { + g_warning("Failed to update VMDB.\n"); + } + } else { + g_debug("Nic info not changed.\n"); + VMX_XDR_FREE(xdr_GuestNicList, &nicInfo); + } + + /* Send the uptime to VMX so that it can detect soft resets. */ + GuestInfoServerSendUptime(NULL, ctx, TRUE, NULL); + +#if defined(_WIN32) || defined(linux) + /* Send the vmstats to the VMX. */ + + if (!GuestInfo_PerfMon(&vmStats)) { + g_warning("Failed to get vmstats.\n"); + } else { + vmStats.version = 1; + if (!GuestInfoUpdateVmdb(ctx, INFO_MEMORY, &vmStats)) { + g_warning("Failed to send vmstats.\n"); + } + } +#endif + + return TRUE; +} + + +/* + *---------------------------------------------------------------------- + * + * GuestInfoConvertNicInfoToNicInfoV1 -- + * + * Convert the new dynamic nicInfoNew to fixed size struct GuestNicInfoV1. + * + * Results: + * TRUE if successfully converted + * FALSE otherwise + * + * Side effects: + * If number of NICs or number of IP addresses on any of the NICs + * exceeding MAX_NICS and MAX_IPS respectively, the extra ones + * are truncated, on successful return. + * + *---------------------------------------------------------------------- + */ + +Bool +GuestInfoConvertNicInfoToNicInfoV1(GuestNicList *info, // IN + GuestNicInfoV1 *infoV1) // OUT +{ + uint32 maxNics; + u_int i; + + if ((NULL == info) || + (NULL == infoV1)) { + return FALSE; + } + + maxNics = MIN(info->nics.nics_len, MAX_NICS); + infoV1->numNicEntries = maxNics; + if (maxNics < info->nics.nics_len) { + g_debug("Truncating NIC list for backwards compatibility.\n"); + } + + XDRUTIL_FOREACH(i, info, nics) { + u_int j; + uint32 maxIPs; + GuestNic *nic = XDRUTIL_GETITEM(info, nics, i); + + Str_Strcpy(infoV1->nicList[i].macAddress, + nic->macAddress, + sizeof infoV1->nicList[i].macAddress); + + maxIPs = MIN(nic->ips.ips_len, MAX_IPS); + infoV1->nicList[i].numIPs = 0; + + XDRUTIL_FOREACH(j, nic, ips) { + VmIpAddress *ip = XDRUTIL_GETITEM(nic, ips, j); + + if (strlen(ip->ipAddress) < sizeof infoV1->nicList[i].ipAddress[j]) { + Str_Strcpy(infoV1->nicList[i].ipAddress[j], + ip->ipAddress, + sizeof infoV1->nicList[i].ipAddress[j]); + infoV1->nicList[i].numIPs++; + if (infoV1->nicList[i].numIPs == maxIPs) { + break; + } + } else { + g_debug("Ignoring IPV6 address for compatibility.\n"); + } + } + + if (infoV1->nicList[i].numIPs != nic->ips.ips_len) { + g_debug("Some IP addresses were ignored for compatibility.\n"); + } + if (i == maxNics) { + break; + } + } + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * GuestInfoUpdateVmdb -- + * + * Update VMDB with new guest information. + * This is the only function that should need to change when the VMDB pipe + * is implemented. Since we dont currently have a VMDB instance in the guest + * the function updates the VMDB instance on the host. Updates are sent only + * if the values have changed. + * + * Result + * TRUE on success, FALSE on failure. + * + * Side-effects + * VMDB is updated if the given value has changed. + * + *----------------------------------------------------------------------------- + */ + +Bool +GuestInfoUpdateVmdb(ToolsAppCtx *ctx, // IN: Application context + GuestInfoType infoType, // IN: guest information type + void *info) // IN: type specific information +{ + ASSERT(info); + g_debug("Entered update vmdb: %d.\n", infoType); + + if (vmResumed) { + vmResumed = FALSE; + GuestInfoClearCache(); + } + + switch (infoType) { + case INFO_DNS_NAME: + case INFO_BUILD_NUMBER: + case INFO_OS_NAME: + case INFO_OS_NAME_FULL: + case INFO_UPTIME: + /* + * This is one of our key value pairs. Update it if it has changed. + * Above fall-through is intentional. + */ + + if (strcmp(gInfoCache.value[infoType], (char *)info) == 0) { + /* The value has not changed */ + g_debug("Value unchanged for infotype %d.\n", infoType); + break; + } + + if (!SetGuestInfo(ctx, infoType, (char *)info, 0)) { + g_warning("Failed to update key/value pair for type %d.\n", infoType); + return FALSE; + } + + /* Update the value in the cache as well. */ + Str_Strcpy(gInfoCache.value[infoType], (char *)info, MAX_VALUE_LEN); + break; + + case INFO_IPADDRESS: + { + static Bool isCmdV1 = FALSE; + char *reply = NULL; + size_t replyLen; + Bool status; + + if (FALSE == isCmdV1) { + /* 13 = max size of string representation of an int + 3 spaces. */ + char request[sizeof GUEST_INFO_COMMAND + 13]; + GuestNicProto message; + XDR xdrs; + + if (DynXdr_Create(&xdrs) == NULL) { + return FALSE; + } + + /* Add the RPC preamble: message name, and type. */ + Str_Sprintf(request, sizeof request, "%s %d ", + GUEST_INFO_COMMAND, INFO_IPADDRESS_V2); + + message.ver = NIC_INFO_V2; + message.GuestNicProto_u.nicsV2 = info; + + /* Write preamble and serialized nic info to XDR stream. */ + if (!DynXdr_AppendRaw(&xdrs, request, strlen(request)) || + !xdr_GuestNicProto(&xdrs, &message)) { + g_warning("Error serializing nic info v2 data."); + DynXdr_Destroy(&xdrs, TRUE); + return FALSE; + } + + status = RpcChannel_Send(ctx->rpc, + DynXdr_Get(&xdrs), + xdr_getpos(&xdrs), + &reply, + &replyLen); + DynXdr_Destroy(&xdrs, TRUE); + if (!status) { + g_warning("Failed to send V2 nic info message.\n"); + } + + if (g_key_file_get_boolean(ctx->config, "guestinfo", "printNicInfo", NULL)) { + PrintNicInfo((GuestNicList *) info, + (int (*)(const char *fmt, ...)) RpcVMX_Log); + } + } else { + status = FALSE; + } + if (!status) { + /* + * Could be that we are talking to the old protocol that GuestNicInfo is + * still fixed size. Another try to send the fixed sized Nic info. + */ + char request[sizeof (GuestNicInfoV1) + sizeof GUEST_INFO_COMMAND + + 2 + /* 2 bytes are for digits of infotype. */ + 3 * sizeof (char)]; /* 3 spaces */ + GuestNicInfoV1 nicInfo; + + vm_free(reply); + reply = NULL; + + Str_Sprintf(request, + sizeof request, + "%s %d ", + GUEST_INFO_COMMAND, + INFO_IPADDRESS); + if (GuestInfoConvertNicInfoToNicInfoV1(info, &nicInfo)) { + memcpy(request + strlen(request), + &nicInfo, + sizeof(GuestNicInfoV1)); + + g_debug("Sending nic info message.\n"); + /* Send all the information in the message. */ + status = RpcChannel_Send(ctx->rpc, + request, + sizeof request, + &reply, + &replyLen); + + g_debug("Just sent fixed sized nic info message.\n"); + if (!status) { + g_debug("Failed to update fixed sized nic information\n"); + vm_free(reply); + return FALSE; + } + isCmdV1 = TRUE; + } else { + return FALSE; + } + } + + g_debug("Updated new NIC information\n"); + vm_free(reply); + reply = NULL; + } + break; + + case INFO_MEMORY: + { + char request[sizeof(GuestMemInfo) + sizeof GUEST_INFO_COMMAND + + 2 + /* 2 bytes are for digits of infotype. */ + 3 * sizeof (char)]; /* 3 spaces */ + Bool status; + + g_debug("Sending GuestMemInfo message.\n"); + Str_Sprintf(request, + sizeof request, + "%s %d ", + GUEST_INFO_COMMAND, + INFO_MEMORY); + memcpy(request + strlen(request), + info, sizeof(GuestMemInfo)); + + /* Send all the information in the message. */ + status = RpcChannel_Send(ctx->rpc, request, sizeof(request), NULL, NULL); + if (!status) { + g_warning("Error sending GuestMemInfo.\n"); + return FALSE; + } + g_debug("GuestMemInfo sent successfully.\n"); + break; + } + + case INFO_DISK_FREE_SPACE: + { + /* + * 2 accounts for the digits of infotype and 3 for the three + * spaces. + */ + unsigned int requestSize = sizeof GUEST_INFO_COMMAND + 2 + 3 * sizeof (char); + uint8 partitionCount; + size_t offset; + char *request; + char *reply; + size_t replyLen; + Bool status; + PGuestDiskInfo pdi = (PGuestDiskInfo)info; + int j = 0; + + if (!DiskInfoChanged(pdi)) { + g_debug("Disk info not changed.\n"); + break; + } + + requestSize += sizeof pdi->numEntries + + sizeof *pdi->partitionList * pdi->numEntries; + request = (char *)calloc(requestSize, sizeof (char)); + if (request == NULL) { + g_warning("Could not allocate memory for request.\n"); + break; + } + + Str_Sprintf(request, requestSize, "%s %d ", GUEST_INFO_COMMAND, + INFO_DISK_FREE_SPACE); + + /* partitionCount is a uint8 and cannot be larger than UCHAR_MAX. */ + if (pdi->numEntries > UCHAR_MAX) { + g_warning("Too many partitions.\n"); + vm_free(request); + return FALSE; + } + partitionCount = pdi->numEntries; + + offset = strlen(request); + + /* + * Construct the disk information message to send to the host. This + * contains a single byte indicating the number partitions followed by + * the PartitionEntry structure for each one. + * + * Note that the use of a uint8 to specify the partitionCount is the + * result of a bug (see bug 117224) but should not cause a problem + * since UCHAR_MAX is 255. Also note that PartitionEntry is packed so + * it's safe to send it from 64-bit Tools to a 32-bit VMX, etc. + */ + memcpy(request + offset, &partitionCount, sizeof partitionCount); + memcpy(request + offset + sizeof partitionCount, pdi->partitionList, + sizeof *pdi->partitionList * pdi->numEntries); + + g_debug("sizeof request is %d\n", requestSize); + status = RpcChannel_Send(ctx->rpc, request, requestSize, &reply, &replyLen); + + if (!status || (strncmp(reply, "", 1) != 0)) { + g_warning("Failed to update disk information.\n"); + vm_free(request); + vm_free(reply); + return FALSE; + } + + g_debug("Updated disk info information\n"); + vm_free(reply); + vm_free(request); + + /* Free any memory previously allocated in the cache. */ + if (gInfoCache.diskInfo.partitionList != NULL) { + vm_free(gInfoCache.diskInfo.partitionList); + gInfoCache.diskInfo.partitionList = NULL; + } + gInfoCache.diskInfo.numEntries = pdi->numEntries; + gInfoCache.diskInfo.partitionList = calloc(pdi->numEntries, + sizeof(PartitionEntry)); + if (gInfoCache.diskInfo.partitionList == NULL) { + g_warning("Could not allocate memory for the disk info cache.\n"); + return FALSE; + } + + for (j = 0; j < pdi->numEntries; j++) { + gInfoCache.diskInfo.partitionList[j] = pdi->partitionList[j]; + } + break; + } + default: + g_error("Invalid info type: %d\n", infoType); + break; + } + + g_debug("Returning after updating guest information\n"); + return TRUE; +} + + +/* + *---------------------------------------------------------------------- + * + * SetGuestInfo -- + * + * Ask Vmx to write some information about the guest into VMDB. + * + * Results: + * + * TRUE/FALSE depending on whether the RPCI succeeded or failed. + * + * Side effects: + * + * None. + * + *---------------------------------------------------------------------- + */ + +Bool +SetGuestInfo(ToolsAppCtx *ctx, // IN: application context + GuestInfoType key, // IN: the VMDB key to set + const char *value, // IN: + char delimiter) // IN: delimiting character for the rpc + // message. 0 indicates default. +{ + Bool status; + char *reply; + gchar *msg; + size_t replyLen; + + ASSERT(key); + ASSERT(value); + + if (!delimiter) { + delimiter = GUESTINFO_DEFAULT_DELIMITER; + } + + msg = g_strdup_printf("%s %c%d%c%s", GUEST_INFO_COMMAND, + delimiter, key, delimiter, value); + + status = RpcChannel_Send(ctx->rpc, msg, strlen(msg) + 1, &reply, &replyLen); + g_free(msg); + + if (!status) { + g_warning("Error sending rpc message: %s\n", reply ? reply : "NULL"); + vm_free(reply); + return FALSE; + } + + /* The reply indicates whether the key,value pair was updated in VMDB. */ + status = (strncmp(reply, "", 1) == 0); + vm_free(reply); + return status; +} + + +/* + *----------------------------------------------------------------------------- + * + * GuestInfoFindMacAddress -- + * + * Locates a NIC with the given MAC address in the NIC list. + * + * Return value: + * If there is an entry in nicInfo which corresponds to this MAC address, + * it is returned. If not NULL is returned. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +GuestNic * +GuestInfoFindMacAddress(GuestNicList *nicInfo, // IN/OUT + const char *macAddress) // IN +{ + u_int i; + + for (i = 0; i < nicInfo->nics.nics_len; i++) { + GuestNic *nic = &nicInfo->nics.nics_val[i]; + if (strncmp(nic->macAddress, macAddress, NICINFO_MAC_LEN) == 0) { + return nic; + } + } + + return NULL; +} + + +/* + *---------------------------------------------------------------------- + * + * NicInfoChanged -- + * + * Checks whether Nic information just obtained is different from + * the information last sent to VMDB. + * + * Results: + * + * TRUE if the NIC info has changed, FALSE otherwise + * + * Side effects: + * + * None. + * + *---------------------------------------------------------------------- + */ + +Bool +NicInfoChanged(GuestNicList *nicInfo) // IN +{ + u_int i; + GuestNicList *cachedNicInfo = &gInfoCache.nicInfo; + + if (cachedNicInfo->nics.nics_len != nicInfo->nics.nics_len) { + g_debug("Number of nics has changed\n"); + return TRUE; + } + + for (i = 0; i < cachedNicInfo->nics.nics_len; i++) { + u_int j; + GuestNic *cachedNic = &cachedNicInfo->nics.nics_val[i]; + GuestNic *matchedNic; + + /* Find the corresponding nic in the new nic info. */ + matchedNic = GuestInfoFindMacAddress(nicInfo, cachedNic->macAddress); + + if (NULL == matchedNic) { + /* This mac address has been deleted. */ + return TRUE; + } + + if (matchedNic->ips.ips_len != cachedNic->ips.ips_len) { + g_debug("Count of ip addresses for mac %d\n", + matchedNic->ips.ips_len); + return TRUE; + } + + /* Which IP addresses have been modified for this NIC? */ + for (j = 0; j < cachedNic->ips.ips_len; j++) { + VmIpAddress *cachedIp = &cachedNic->ips.ips_val[j]; + Bool foundIP = FALSE; + u_int k; + + for (k = 0; k < matchedNic->ips.ips_len; k++) { + VmIpAddress *matchedIp = &matchedNic->ips.ips_val[k]; + if (0 == strncmp(cachedIp->ipAddress, + matchedIp->ipAddress, + NICINFO_MAX_IP_LEN)) { + foundIP = TRUE; + break; + } + } + + if (FALSE == foundIP) { + /* This ip address couldn't be found and has been modified. */ + g_debug("MAC address %s, ipaddress %s deleted\n", + cachedNic->macAddress, + cachedIp->ipAddress); + return TRUE; + } + + } + + } + + return FALSE; +} + + +/* + *---------------------------------------------------------------------------- + * + * PrintNicInfo -- + * + * Print NIC info struct using the specified print function. + * + * Results: + * Sum of return values of print function (for printf, this will be the + * number of characters printed). + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +int +PrintNicInfo(GuestNicList *nicInfo, // IN + int (*PrintFunc)(const char *, ...)) // IN +{ + int ret = 0; + u_int i = 0; + + ret += PrintFunc("NicInfo: count: %ud\n", nicInfo->nics.nics_len); + for (i = 0; i < nicInfo->nics.nics_len; i++) { + u_int j; + GuestNic *nic = &nicInfo->nics.nics_val[i]; + + ret += PrintFunc("NicInfo: nic [%d/%d] mac: %s", + i+1, + nicInfo->nics.nics_len, + nic->macAddress); + + for (j = 0; j < nic->ips.ips_len; j++) { + VmIpAddress *ipAddress = &nic->ips.ips_val[j]; + + ret += PrintFunc("NicInfo: nic [%d/%d] IP [%d/%d]: %s", + i+1, + nicInfo->nics.nics_len, + j+1, + nic->ips.ips_len, + ipAddress->ipAddress); + } + } + + return ret; +} + + +/* + *---------------------------------------------------------------------- + * + * DiskInfoChanged -- + * + * Checks whether disk info information just obtained is different from + * the information last sent to VMDB. + * + * Results: + * + * TRUE if the disk info has changed, FALSE otherwise. + * + * Side effects: + * + * None. + * + *---------------------------------------------------------------------- + */ + +Bool +DiskInfoChanged(PGuestDiskInfo diskInfo) // IN: +{ + int index; + char *name; + int i; + int matchedPartition; + PGuestDiskInfo cachedDiskInfo; + + cachedDiskInfo = &gInfoCache.diskInfo; + + if (cachedDiskInfo->numEntries != diskInfo->numEntries) { + g_debug("Number of disks has changed\n"); + return TRUE; + } + + /* Have any disks been modified? */ + for (index = 0; index < cachedDiskInfo->numEntries; index++) { + name = cachedDiskInfo->partitionList[index].name; + + /* Find the corresponding partition in the new partition info. */ + for (i = 0; i < diskInfo->numEntries; i++) { + if (!strncmp(diskInfo->partitionList[i].name, name, PARTITION_NAME_SIZE)) { + break; + } + } + + matchedPartition = i; + if (matchedPartition == diskInfo->numEntries) { + /* This partition has been deleted. */ + g_debug("Partition %s deleted\n", name); + return TRUE; + } else { + /* Compare the free space. */ + if (diskInfo->partitionList[matchedPartition].freeBytes != + cachedDiskInfo->partitionList[index].freeBytes) { + g_debug("Free space changed\n"); + return TRUE; + } + if (diskInfo->partitionList[matchedPartition].totalBytes != + cachedDiskInfo->partitionList[index].totalBytes) { + g_debug("Total space changed\n"); + return TRUE; + } + } + } + + return FALSE; +} + + +/* + *---------------------------------------------------------------------- + * + * GuestInfoClearCache -- + * + * Clears the cached guest info data. + * + * Results: + * None. + * + * Side effects: + * gInfoCache is cleared. + * + *---------------------------------------------------------------------- + */ + +static void +GuestInfoClearCache(void) +{ + int i; + + for (i = 0; i < INFO_MAX; i++) { + gInfoCache.value[i][0] = 0; + } + + VMX_XDR_FREE(xdr_GuestNicList, &gInfoCache.nicInfo); + memset(&gInfoCache.nicInfo, 0, sizeof gInfoCache.nicInfo); +} + + +/** + * Plugin entry point. Initializes internal plugin state. + * + * @param[in] ctx The app context. + * + * @return The registration data. + */ + +TOOLS_MODULE_EXPORT ToolsPluginData * +ToolsOnLoad(ToolsAppCtx *ctx) +{ + static ToolsPluginData regData = { + "guestInfo", + NULL, + NULL + }; + + GSource *src; + + ToolsPluginSignalCb sigs[] = { + { TOOLS_CORE_SIG_CAPABILITIES, GuestInfoServerSendUptime, NULL }, + { TOOLS_CORE_SIG_RESET, GuestInfoServerReset, NULL }, + { TOOLS_CORE_SIG_SET_OPTION, GuestInfoServerSetOption, NULL }, + { TOOLS_CORE_SIG_SHUTDOWN, GuestInfoServerShutdown, NULL } + }; + ToolsAppReg regs[] = { + { TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) } + }; + + regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs)); + + memset(&gInfoCache, 0, sizeof gInfoCache); + vmResumed = FALSE; + + gDisableQueryDiskInfo = g_key_file_get_boolean(ctx->config, + "guestinfo", + CONFNAME_DISABLEQUERYDISKINFO, + NULL); + + /* Add the first timer event. */ + src = g_timeout_source_new(GUESTINFO_TIME_INTERVAL_MSEC * 10); + VMTOOLSAPP_ATTACH_SOURCE(ctx, src, GuestInfoGather, ctx, NULL); + g_source_unref(src); + + return ®Data; +} + diff --git a/open-vm-tools/services/plugins/guestInfo/perfMonLinux.c b/open-vm-tools/services/plugins/guestInfo/perfMonLinux.c new file mode 100644 index 000000000..dc7afc299 --- /dev/null +++ b/open-vm-tools/services/plugins/guestInfo/perfMonLinux.c @@ -0,0 +1,209 @@ +/********************************************************* + * Copyright (C) 2008 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. + * + *********************************************************/ + +/* + * This file gathers the virtual memory stats from Linux guest to be + * passed on to the vmx. + */ + +#include +#include +#include +#include +#include +#include + +#include "vmware.h" +#include "guestInfo.h" +#include "strutil.h" +#include "debug.h" + +#ifndef NO_PROCPS +#include "vm_procps.h" +static void GuestInfoMonitorGetStat(GuestMemInfo *vmStats); +static Bool GuestInfoMonitorReadMeminfo(GuestMemInfo *vmStats); +#endif + +#define LINUX_MEMINFO_FLAGS (MEMINFO_MEMTOTAL | MEMINFO_MEMFREE | MEMINFO_MEMBUFF |\ + MEMINFO_MEMCACHE | MEMINFO_MEMACTIVE | MEMINFO_MEMINACTIVE |\ + MEMINFO_SWAPINRATE | MEMINFO_SWAPOUTRATE |\ + MEMINFO_IOINRATE | MEMINFO_IOOUTRATE) + +/* + *---------------------------------------------------------------------- + * + * GuestInfo_PerfMon -- + * + * Gather performance stats. + * + * Results: + * Gathered stats. Returns FALSE on failure. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Bool +GuestInfo_PerfMon(GuestMemInfo *vmStats) // OUT: filled vmstats +{ +#ifndef NO_PROCPS + ASSERT(vmStats); + GuestInfoMonitorGetStat(vmStats); + if (GuestInfoMonitorReadMeminfo(vmStats)) { + vmStats->flags = LINUX_MEMINFO_FLAGS; + return TRUE; + } +#endif + return FALSE; +} + + +#ifndef NO_PROCPS +/* + *---------------------------------------------------------------------- + * + * GuestInfoMonitorGetStat -- + * + * Calls getstat() to gather memory stats. + * + * Results: + * Gathered stats in vmStats. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +GuestInfoMonitorGetStat(GuestMemInfo *vmStats) // OUT: filled vmstats +{ + uint32 hz = Hertz; + uint32 dummy; + jiff cpuUse[2]; + jiff cpuNic[2]; + jiff cpuSys[2]; + jiff cpuIdl[2]; + jiff cpuIow[2]; + jiff cpuXxx[2]; + jiff cpuYyy[2]; + jiff cpuZzz[2]; + jiff cpuTotal; + jiff cpuHalf; + unsigned long pageIn[2]; + unsigned long pageOut[2]; + unsigned long swapIn[2]; + unsigned long swapOut[2]; + unsigned int dummy2[2]; + unsigned long kb_per_page = sysconf(_SC_PAGESIZE) / 1024ul; + + meminfo(); + getstat(cpuUse, cpuNic, cpuSys, cpuIdl, cpuIow, cpuXxx, cpuYyy, cpuZzz, + pageIn, pageOut, swapIn, swapOut, dummy2, dummy2, &dummy, &dummy, + &dummy, &dummy); + + cpuTotal = *cpuUse + *cpuNic + *cpuSys + *cpuXxx + + *cpuYyy + *cpuIdl + *cpuIow + *cpuZzz; + cpuHalf = cpuTotal / 2UL; + + vmStats->memFree = kb_main_free; + vmStats->memBuff = kb_main_buffers; + vmStats->memCache = kb_main_cached, + vmStats->memInactive = kb_inactive; + vmStats->memActive = kb_active; + + vmStats->swapInRate = (uint64)((*swapIn * kb_per_page * hz + cpuHalf) / cpuTotal); + vmStats->swapOutRate = (uint64)((*swapOut * kb_per_page * hz + cpuHalf) / cpuTotal); + vmStats->ioInRate = (uint64)((*pageIn * kb_per_page * hz + cpuHalf) / cpuTotal); + vmStats->ioOutRate = (uint64)((*pageOut * kb_per_page * hz + cpuHalf) / cpuTotal); + + + Debug("GuestInfoMonitorGetStat: GuestMemInfo: total: %"FMT64"u free: %"FMT64"u"\ + "buff: %"FMT64"u cache: %"FMT64"u swapin: %"FMT64"u swapout: %"FMT64"u ioin:"\ + "%"FMT64"u ioout: %"FMT64"u inactive: %"FMT64"u active: %"FMT64"u"\ + "hugetotal: %"FMT64"u hugefree: %"FMT64"u\n", vmStats->memTotal, + vmStats->memFree, vmStats->memBuff, vmStats->memCache, vmStats->swapInRate, + vmStats->swapOutRate, vmStats->ioInRate, vmStats->ioOutRate, + vmStats->memInactive, vmStats->memActive, vmStats->hugePagesTotal, + vmStats->hugePagesFree); +} + + +/* + *---------------------------------------------------------------------- + * + * GuestInfoMonitorReadMeminfo -- + * + * Reads /proc/meminfo to gather phsycial memory and huge page stats. + * + * Results: + * Read /proc/meminfo for total physical memory and huge pages info. + * Returns FALSE on failure. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static Bool +GuestInfoMonitorReadMeminfo(GuestMemInfo *vmStats) // OUT: filled vmstats +{ + char buf[512]; + uint64 value; + FILE *fp; + + /* Get the total memory, and huge page info from /proc/meminfo. */ + fp = fopen("/proc/meminfo", "r"); + if (!fp) { + Log("GuestInfoMonitorReadMeminfo: Error opening /proc/meminfo.\n"); + return FALSE; + } + + while(!feof(fp)) { + if (fscanf(fp, "%s %"FMT64"u", buf, &value) != 2) { + continue; + } + if (StrUtil_StartsWith(buf, "MemTotal")) { + vmStats->memTotal = value; + } + if (StrUtil_StartsWith(buf, "HugePages_Total")) { + vmStats->hugePagesTotal = value; + vmStats->flags |= MEMINFO_HUGEPAGESTOTAL; + } + if (StrUtil_StartsWith(buf, "HugePages_Free")) { + vmStats->hugePagesFree = value; + vmStats->flags |= MEMINFO_HUGEPAGESFREE; + } + } + fclose(fp); + + Debug("GuestInfoMonitorReadMeminfo: GuestMemInfo: total: %"FMT64"u free: %"FMT64"u"\ + "buff: %"FMT64"u cache: %"FMT64"u swapin: %"FMT64"u swapout: %"FMT64"u ioin:"\ + "%"FMT64"u ioout: %"FMT64"u inactive: %"FMT64"u active: %"FMT64"u"\ + "hugetotal: %"FMT64"u hugefree: %"FMT64"u\n", vmStats->memTotal, + vmStats->memFree, vmStats->memBuff, vmStats->memCache, vmStats->swapInRate, + vmStats->swapOutRate, vmStats->ioInRate, vmStats->ioOutRate, + vmStats->memInactive, vmStats->memActive, vmStats->hugePagesTotal, + vmStats->hugePagesFree); + + return TRUE; +} +#endif diff --git a/open-vm-tools/services/plugins/hgfsServer/COPYING b/open-vm-tools/services/plugins/hgfsServer/COPYING new file mode 100644 index 000000000..09f465ab7 --- /dev/null +++ b/open-vm-tools/services/plugins/hgfsServer/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/open-vm-tools/services/plugins/hgfsServer/Makefile.am b/open-vm-tools/services/plugins/hgfsServer/Makefile.am new file mode 100644 index 000000000..5839e92f0 --- /dev/null +++ b/open-vm-tools/services/plugins/hgfsServer/Makefile.am @@ -0,0 +1,41 @@ +################################################################################ +### Copyright 2009 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 +################################################################################ + +noinst_LTLIBRARIES = libhgfsServer.la + +libhgfsServer_la_CPPFLAGS = +libhgfsServer_la_CPPFLAGS += @PLUGIN_CPPFLAGS@ + +libhgfsServer_la_LDFLAGS = +libhgfsServer_la_LDFLAGS += @PLUGIN_LDFLAGS@ + +libhgfsServer_la_LIBADD = +libhgfsServer_la_LIBADD += @VMTOOLS_LIBS@ +libhgfsServer_la_LIBADD += $(top_builddir)/lib/hgfsServer/libHgfsServer.la +libhgfsServer_la_LIBADD += $(top_builddir)/lib/hgfsServerPolicyGuest/libHgfsServerPolicyGuest.la + +libhgfsServer_la_SOURCES = +libhgfsServer_la_SOURCES += hgfsPlugin.c + +install-exec-local: + mkdir -p $(DESTDIR)$(VMSVC_PLUGIN_INSTALLDIR) + cp -L $(builddir)/.libs/libhgfsServer.so $(DESTDIR)$(VMSVC_PLUGIN_INSTALLDIR) +if HAVE_X11 + mkdir -p $(DESTDIR)$(VMUSR_PLUGIN_INSTALLDIR) + cp -L $(builddir)/.libs/libhgfsServer.so $(DESTDIR)$(VMUSR_PLUGIN_INSTALLDIR) +endif + diff --git a/open-vm-tools/services/plugins/hgfsServer/hgfsPlugin.c b/open-vm-tools/services/plugins/hgfsServer/hgfsPlugin.c new file mode 100644 index 000000000..6861e3f31 --- /dev/null +++ b/open-vm-tools/services/plugins/hgfsServer/hgfsPlugin.c @@ -0,0 +1,173 @@ +/********************************************************* + * Copyright (C) 2008 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 hgfsPlugin.c + * + * Functionality to utilize the hgfs server in bora/lib as a tools plugin. + */ + +#include + +#define G_LOG_DOMAIN "hgfsd" +#include "vmtoolsApp.h" + +#include "hgfs.h" +#include "hgfsServerPolicy.h" +#include "hgfsServer.h" +#include "hgfsServerManager.h" +#include "vm_app.h" +#include "vm_assert.h" +#include "vmtools.h" + +#if !defined(__APPLE__) +#include "embed_version.h" +#include "vmtoolsd_version.h" +VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING); +#endif + + +/** + * Handles hgfs requests. + * + * @param[in] data RPC request data. + * + * @return TRUE on success, FALSE on error. + */ + +static Bool +HgfsServerRpcInDispatch(RpcInData *data) +{ + size_t packetSize; + static char packet[HGFS_LARGE_PACKET_MAX]; + + + ASSERT(data->clientData == NULL); + + if (data->argsSize == 0) { + return RPCIN_SETRETVALS(data, "1 argument required", FALSE); + } + + packetSize = data->argsSize - 1; + HgfsServer_DispatchPacket(data->args + 1, packet, &packetSize); + + data->result = packet; + data->resultLen = packetSize; + return TRUE; +} + + +/** + * Sends the HGFS capability to the VMX. + * + * @param[in] src The source object. + * @param[in] ctx The app context. + * @param[in] set Whether setting or unsetting the capability. + * @param[in] data Unused. + * + * @return NULL. The function sends the capability directly. + */ + +static GArray * +HgfsServerCapReg(gpointer src, + ToolsAppCtx *ctx, + gboolean set, + gpointer data) +{ + gchar *msg; + const char *appName = NULL; + + if (strcmp(ctx->name, VMTOOLS_GUEST_SERVICE) == 0) { + appName = TOOLS_DAEMON_NAME; + } else if (strcmp(ctx->name, VMTOOLS_USER_SERVICE) == 0) { + appName = TOOLS_DND_NAME; + } else { + g_error("Shouldn't reach this.\n"); + } + + msg = g_strdup_printf("tools.capability.hgfs_server %s %d", + appName, + set ? 1 : 0); + + /* + * Prior to WS55, the VMX did not know about the "hgfs_server" + * capability. This doesn't mean that the HGFS server wasn't needed, it's + * just that the capability was introduced in CS 225439 so that the VMX + * could decide which HGFS server to communicate with. + * + * Long story short, we shouldn't care if this function fails. + */ + if (!RpcChannel_Send(ctx->rpc, msg, strlen(msg) + 1, NULL, NULL)) { + g_warning("Setting HGFS server capability failed!\n"); + } + + g_free(msg); + return NULL; +} + + +/** + * Returns the registration data for the HGFS server. + * + * @param[in] ctx The application context. + * + * @return The registration data. + */ + +TOOLS_MODULE_EXPORT ToolsPluginData * +ToolsOnLoad(ToolsAppCtx *ctx) +{ + static ToolsPluginData regData = { + "hgfsServer", + NULL, + NULL + }; + + /* + * Passing NULL here is safe because the shares maintained by the guest + * policy server never change, invalidating the need for an invalidate + * function. + */ + if (!HgfsServerPolicy_Init(NULL)) { + g_warning("HgfsServerPolicy_Init() failed, aborting HGFS server init.\n"); + return NULL; + } + + if (!HgfsServer_InitState()) { + g_warning("HgfsServer_InitState() failed, aborting HGFS server init.\n"); + HgfsServerPolicy_Cleanup(); + return NULL; + } + + { + RpcChannelCallback rpcs[] = { + { HGFS_SYNC_REQREP_CMD, HgfsServerRpcInDispatch, NULL, NULL, NULL, 0 } + }; + ToolsPluginSignalCb sigs[] = { + { TOOLS_CORE_SIG_CAPABILITIES, HgfsServerCapReg, ®Data } + }; + ToolsAppReg regs[] = { + { TOOLS_APP_GUESTRPC, VMTools_WrapArray(rpcs, sizeof *rpcs, ARRAYSIZE(rpcs)) }, + { TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) } + }; + + regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs)); + } + + return ®Data; +} diff --git a/open-vm-tools/services/plugins/powerOps/COPYING b/open-vm-tools/services/plugins/powerOps/COPYING new file mode 100644 index 000000000..09f465ab7 --- /dev/null +++ b/open-vm-tools/services/plugins/powerOps/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/open-vm-tools/services/plugins/powerOps/Makefile.am b/open-vm-tools/services/plugins/powerOps/Makefile.am new file mode 100644 index 000000000..68fbfadf3 --- /dev/null +++ b/open-vm-tools/services/plugins/powerOps/Makefile.am @@ -0,0 +1,35 @@ +################################################################################ +### Copyright 2009 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 +################################################################################ + +noinst_LTLIBRARIES = libpowerOps.la + +libpowerOps_la_CPPFLAGS = +libpowerOps_la_CPPFLAGS += @PLUGIN_CPPFLAGS@ + +libpowerOps_la_LDFLAGS = +libpowerOps_la_LDFLAGS += @PLUGIN_LDFLAGS@ + +libpowerOps_la_LIBADD = +libpowerOps_la_LIBADD += @VMTOOLS_LIBS@ + +libpowerOps_la_SOURCES = +libpowerOps_la_SOURCES += powerOps.c + +install-exec-local: + mkdir -p $(DESTDIR)$(VMSVC_PLUGIN_INSTALLDIR) + cp -L $(builddir)/.libs/libpowerOps.so $(DESTDIR)$(VMSVC_PLUGIN_INSTALLDIR) + diff --git a/open-vm-tools/services/plugins/powerOps/powerOps.c b/open-vm-tools/services/plugins/powerOps/powerOps.c new file mode 100644 index 000000000..8540cf0bd --- /dev/null +++ b/open-vm-tools/services/plugins/powerOps/powerOps.c @@ -0,0 +1,465 @@ +/********************************************************* + * Copyright (C) 2008 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 powerOps.c + * + * Plugin that handles power operation events from the VMX. + */ + +#define G_LOG_DOMAIN "powerops" + +#include "vm_assert.h" +#include "vm_basic_defs.h" +#include "vmtoolsApp.h" + +#include "conf.h" +#include "procMgr.h" +#include "system.h" +#include "vm_app.h" +#include "vmtools.h" + +#if defined(G_PLATFORM_WIN32) +# define INVALID_PID NULL +#else +# define INVALID_PID (GPid) -1 +#endif + +#if !defined(__APPLE__) +#include "embed_version.h" +#include "vmtoolsd_version.h" +VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING); +#endif + +static const char *stateChgConfNames[] = { + NULL, + CONFNAME_POWEROFFSCRIPT, + CONFNAME_POWEROFFSCRIPT, + CONFNAME_POWERONSCRIPT, + CONFNAME_RESUMESCRIPT, + CONFNAME_SUSPENDSCRIPT, +}; + + +/** Internal plugin state. */ +typedef struct PowerOpState { + GuestOsState stateChgInProgress; + GuestOsState lastFailedStateChg; +#if defined(G_PLATFORM_WIN32) + ProcMgr_AsyncProc *pid; +#else + GPid pid; +#endif + ToolsAppCtx *ctx; +} PowerOpState; + + +/** + * Returns the capabilities of the power ops plugin. + * + * @param[in] src The source object. + * @param[in] ctx The application context. + * @param[in] set Whether capabilities are being set. + * @param[in] data Unused. + * + * @return List of capabilities. + */ + +static GArray * +PowerOpsCapabilityCb(gpointer src, + ToolsAppCtx *ctx, + gboolean set, + gpointer data) +{ + const ToolsAppCapability caps[] = { + { TOOLS_CAP_OLD_NOVAL, "statechange", 0, 1 }, + { TOOLS_CAP_OLD_NOVAL, "softpowerop_retry", 0, 1 }, + }; + + return VMTools_WrapArray(caps, sizeof *caps, ARRAYSIZE(caps)); +} + + +/** + * Clean up internal state on shutdown. + * + * @param[in] src The source object. + * @param[in] ctx Unused. + * @param[in] plugin Plugin registration data. + */ + +static void +PowerOpsShutdown(gpointer src, + ToolsAppCtx *ctx, + ToolsPluginData *plugin) +{ + PowerOpState *state = plugin->_private; + g_free(state); +} + + +/** + * Called when a state change script is done running. Sends the state change + * status to the VMX. + * + * @note This may halt/reboot the VM. Also the VMX may suspend the VM upon + * receipt of a positive status. + * + * @param[in] state Plugin state. + * @param[in] success Whether the script was successful. + */ + +static void +PowerOpsStateChangeDone(PowerOpState *state, + gboolean success) +{ + gchar *msg; + + g_debug("State change complete, success = %d.\n", success); + + /* + * We execute the requested action if the script succeeded, or if the + * same action was tried before but didn't finish due to a script failure. + * See bug 168568 for discussion. + */ + if (success || state->lastFailedStateChg == state->stateChgInProgress) { + success = TRUE; + state->lastFailedStateChg = GUESTOS_STATECHANGE_NONE; + } + + if (!success) { + state->lastFailedStateChg = state->stateChgInProgress; + } + + /* Send the status message to the VMX. */ + msg = g_strdup_printf("tools.os.statechange.status %d %d", + success, + state->stateChgInProgress); + if (!RpcChannel_Send(state->ctx->rpc, msg, strlen(msg) + 1, NULL, NULL)) { + g_warning("Unable to send the status RPC."); + } + + g_free(msg); + + /* Finally, perform the requested operation. */ + if (success) { + if (state->stateChgInProgress == GUESTOS_STATECHANGE_REBOOT) { + g_debug("Initiating reboot.\n"); + System_Shutdown(TRUE); + } else if (state->stateChgInProgress == GUESTOS_STATECHANGE_HALT) { + g_debug("Initiating halt.\n"); + System_Shutdown(FALSE); + } + } + + state->stateChgInProgress = GUESTOS_STATECHANGE_NONE; +} + + +#if defined(G_PLATFORM_WIN32) +/** + * Callback for when the script process finishes on Win32 systems. + * + * @param[in] _state Plugin state. + * + * @return TRUE if the process is not finished yet. + */ + +static gboolean +PowerOpsScriptCallback(gpointer _state) +{ + PowerOpState *state = _state; + + g_assert(state->pid != INVALID_PID); + + if (!ProcMgr_IsAsyncProcRunning(state->pid)) { + int exitcode; + gboolean success; + + success = (ProcMgr_GetExitCode(state->pid, &exitcode) == 0 && + exitcode == 0); + g_debug("Script exit code: %d, success = %d\n", exitcode, success); + PowerOpsStateChangeDone(state, success); + ProcMgr_Free(state->pid); + state->pid = INVALID_PID; + return FALSE; + } + return TRUE; +} + + +/** + * Starts a process using the ProcMgr library. For some reason the glib + * spawn functions are a pain to use under Windows, and ProcMgr works + * fine since we can use the "selectable" handle to monitor the child + * process. + * + * XXX: as soon as I figure out what's wrong with the "gspawn-win32-helper" + * process and why it's not working, this should probably be merged with the + * POSIX code below. + * + * @param[in] state Plugin state. + * @param[in] script Path to the script to be run. + * + * @return Whether started the process successfully. + */ + +static gboolean +PowerOpsRunScript(PowerOpState *state, + gchar *script) +{ + gchar *quoted = NULL; + ProcMgr_ProcArgs procArgs; + + /* + * Pass the CREATE_NO_WINDOW flag to CreateProcess so that the + * cmd.exe window will not be visible to the user in the guest. + */ + memset(&procArgs, 0, sizeof procArgs); + procArgs.bInheritHandles = TRUE; + procArgs.dwCreationFlags = CREATE_NO_WINDOW; + + /* Quote the path if it's not yet quoted. */ + if (script[0] != '"') { + quoted = g_strdup_printf("\"%s\"", script); + } + + g_debug("Executing script: %s\n", (quoted != NULL) ? quoted : script); + state->pid = ProcMgr_ExecAsync((quoted != NULL) ? quoted : script, &procArgs); + g_free(quoted); + + if (state->pid != NULL) { + HANDLE h = ProcMgr_GetAsyncProcSelectable(state->pid); + GSource *watch = VMTools_NewHandleSource(h); + VMTOOLSAPP_ATTACH_SOURCE(state->ctx, watch, PowerOpsScriptCallback, state, NULL); + g_source_unref(watch); + return TRUE; + } else { + g_warning("Failed to start script: out of memory?\n"); + return FALSE; + } +} + +#else + +/** + * Callback for when the script process finishes on POSIX systems. + * + * @param[in] pid Child pid. + * @param[in] status Exit status of script. + * @param[in] _state Plugin state. + * + * @return FALSE. + */ + +static gboolean +PowerOpsScriptCallback(GPid pid, + gint status, + gpointer _state) +{ + PowerOpState *state = _state; + + g_assert(state->pid != INVALID_PID); + + PowerOpsStateChangeDone(_state, status == 0); + g_spawn_close_pid(state->pid); + state->pid = INVALID_PID; + return FALSE; +} + + +/** + * Starts a process using glib. This works better in the non-Windows case, + * since glib provides a nice API for monitoring when a process exits + * (instead of having to poll the process every once in a while when using + * ProcMgr.) + * + * @param[in] state Plugin state. + * @param[in] script Path to the script to be run. + * + * @return Whether started the process successfully. + */ + +static gboolean +PowerOpsRunScript(PowerOpState *state, + gchar *script) +{ + gchar *argv[2]; + GSource *watch; + GError *err = NULL; + + argv[0] = g_filename_from_utf8(script, -1, NULL, NULL, &err); + if (err != NULL) { + g_error("Conversion error: %s\n", err->message); + } + argv[1] = NULL; + + if (!g_spawn_async(NULL, + argv, + NULL, + G_SPAWN_DO_NOT_REAP_CHILD | + G_SPAWN_STDOUT_TO_DEV_NULL | + G_SPAWN_STDERR_TO_DEV_NULL, + NULL, + NULL, + &state->pid, + &err)) { + g_warning("Error starting script: %s\n", err->message); + g_clear_error(&err); + g_free(argv[0]); + return FALSE; + } + + /* Setup a watch for when the child is done. */ + watch = g_child_watch_source_new(state->pid); + VMTOOLSAPP_ATTACH_SOURCE(state->ctx, watch, PowerOpsScriptCallback, state, NULL); + g_source_unref(watch); + g_free(argv[0]); + return TRUE; +} +#endif + + +/** + * Handler for commands which invoke state change scripts. Runs the configured + * script for the power operation signaled by the host. + * + * @param[in] data RPC data. + * + * @return TRUE on success. + */ + +static Bool +PowerOpsStateChange(RpcInData *data) +{ + size_t i; + PowerOpState *state = data->clientData; + + if (state->pid != INVALID_PID) { + g_debug("State change already in progress.\n"); + return RPCIN_SETRETVALS(data, "State change already in progress", FALSE); + } + + g_debug("State change: %s\n", data->name); + + for (i = 0; i < ARRAYSIZE(stateChangeCmdTable); i++) { + if (strcmp(data->name, stateChangeCmdTable[i].tcloCmd) == 0) { + gchar *script; + const char *result; + Bool ret; + + state->stateChgInProgress = stateChangeCmdTable[i].id; + + script = g_key_file_get_string(state->ctx->config, + "powerops", + stateChgConfNames[stateChangeCmdTable[i].id], + NULL); + + if (script == NULL) { + /* Use default script if not set in config file. */ + char *dfltPath; + const char *dfltScript; + + dfltScript = GuestApp_GetDefaultScript(stateChangeCmdTable[i].name); + if (dfltScript == NULL) { + g_debug("No default script to run for state change %s.\n", + stateChangeCmdTable[i].name); + PowerOpsStateChangeDone(state, TRUE); + return RPCIN_SETRETVALS(data, "", TRUE); + } + dfltPath = GuestApp_GetInstallPath(); + g_assert(dfltPath != NULL); + script = g_strdup_printf("%s%c%s", dfltPath, DIRSEPC, dfltScript); + vm_free(dfltPath); + } else if (strlen(script) == 0) { + g_debug("No script to run for state change %s.\n", + stateChangeCmdTable[i].name); + g_free(script); + PowerOpsStateChangeDone(state, TRUE); + return RPCIN_SETRETVALS(data, "", TRUE); + } + + if (PowerOpsRunScript(state, script)) { + result = ""; + ret = TRUE; + } else { + PowerOpsStateChangeDone(state, FALSE); + result = "Error starting script"; + ret = FALSE; + } + + g_free(script); + return RPCIN_SETRETVALS(data, result, ret); + } + } + + g_warning("Invalid state change command.\n"); + return RPCIN_SETRETVALS(data, "Invalid state change command", FALSE); +} + + +/** + * Plugin entry point. Returns the registration data. + * + * @param[in] ctx Unused. + * + * @return The registration data. + */ + +TOOLS_MODULE_EXPORT ToolsPluginData * +ToolsOnLoad(ToolsAppCtx *ctx) +{ + static ToolsPluginData regData = { + "powerops", + NULL, + NULL + }; + + size_t i; + PowerOpState *state; + + ToolsPluginSignalCb sigs[] = { + { TOOLS_CORE_SIG_CAPABILITIES, PowerOpsCapabilityCb, NULL }, + { TOOLS_CORE_SIG_SHUTDOWN, PowerOpsShutdown, ®Data } + }; + ToolsAppReg regs[] = { + { TOOLS_APP_GUESTRPC, NULL }, + { TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) } + }; + + state = g_malloc0(sizeof *state); + state->ctx = ctx; + state->pid = INVALID_PID; + + regs[0].data = g_array_sized_new(FALSE, + TRUE, + sizeof (RpcChannelCallback), + ARRAYSIZE(stateChangeCmdTable)); + + for (i = 0; i < ARRAYSIZE(stateChangeCmdTable); i++) { + RpcChannelCallback *cb = &g_array_index(regs[0].data, RpcChannelCallback, i); + cb->name = stateChangeCmdTable[i].tcloCmd; + cb->callback = PowerOpsStateChange; + cb->clientData = state; + } + + regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs)); + regData._private = state; + return ®Data; +} + diff --git a/open-vm-tools/services/plugins/resolutionSet/COPYING b/open-vm-tools/services/plugins/resolutionSet/COPYING new file mode 100644 index 000000000..09f465ab7 --- /dev/null +++ b/open-vm-tools/services/plugins/resolutionSet/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/open-vm-tools/services/plugins/resolutionSet/Makefile.am b/open-vm-tools/services/plugins/resolutionSet/Makefile.am new file mode 100644 index 000000000..bce9db0ef --- /dev/null +++ b/open-vm-tools/services/plugins/resolutionSet/Makefile.am @@ -0,0 +1,41 @@ +################################################################################ +### Copyright 2009 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 +################################################################################ + +noinst_LTLIBRARIES = libresolutionSet.la + +libresolutionSet_la_CPPFLAGS = +libresolutionSet_la_CPPFLAGS += @GTK_CPPFLAGS@ +libresolutionSet_la_CPPFLAGS += @PLUGIN_CPPFLAGS@ +libresolutionSet_la_CPPFLAGS += -DRESOLUTION_X11 + +libresolutionSet_la_LDFLAGS = +libresolutionSet_la_LDFLAGS += @PLUGIN_LDFLAGS@ + +libresolutionSet_la_LIBADD = +libresolutionSet_la_LIBADD += @COMMON_XLIBS@ +libresolutionSet_la_LIBADD += @GTK_LIBS@ +libresolutionSet_la_LIBADD += @VMTOOLS_LIBS@ + +libresolutionSet_la_SOURCES = +libresolutionSet_la_SOURCES += libvmwarectrl.c +libresolutionSet_la_SOURCES += resolutionSet.c +libresolutionSet_la_SOURCES += resolutionX11.c + +install-exec-local: + mkdir -p $(DESTDIR)$(VMUSR_PLUGIN_INSTALLDIR) + cp -L $(builddir)/.libs/libresolutionSet.so $(DESTDIR)$(VMUSR_PLUGIN_INSTALLDIR) + diff --git a/open-vm-tools/services/plugins/resolutionSet/libvmwarectrl.c b/open-vm-tools/services/plugins/resolutionSet/libvmwarectrl.c new file mode 100644 index 000000000..80abd4190 --- /dev/null +++ b/open-vm-tools/services/plugins/resolutionSet/libvmwarectrl.c @@ -0,0 +1,275 @@ +/* + * Copyright 2006 by VMware, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of the copyright holder(s) + * and author(s) shall not be used in advertising or otherwise to promote + * the sale, use or other dealings in this Software without prior written + * authorization from the copyright holder(s) and author(s). + */ + +/* + * libvmwarectrl.c -- + * + * The VMWARE_CTRL client library. + */ + + +#define NEED_EVENTS +#define NEED_REPLIES +#include +#include "libvmwarectrl.h" +#include "vmwarectrlproto.h" +#include +#include + + +/* + * Static data and functions. + */ + +static XExtensionInfo _vmwarectrl_info_data; +static XExtensionInfo *vmwarectrl_info = &_vmwarectrl_info_data; +static char *vmwarectrl_extension_name = VMWARE_CTRL_PROTOCOL_NAME; + +#define VMwareCtrlCheckExtension(dpy, i, val) \ + XextCheckExtension(dpy, i, vmwarectrl_extension_name, val) + +static int close_display(Display *dpy, XExtCodes *codes); + +static /* const */ XExtensionHooks vmwarectrl_extension_hooks = { + NULL, /* create_gc */ + NULL, /* copy_gc */ + NULL, /* flush_gc */ + NULL, /* free_gc */ + NULL, /* create_font */ + NULL, /* free_font */ + close_display, /* close_display */ + NULL, /* wire_to_event */ + NULL, /* event_to_wire */ + NULL, /* error */ + NULL, /* error_string */ +}; + +static XEXT_GENERATE_CLOSE_DISPLAY (close_display, vmwarectrl_info) + +static XEXT_GENERATE_FIND_DISPLAY (find_display, vmwarectrl_info, + vmwarectrl_extension_name, + &vmwarectrl_extension_hooks, + 0, NULL) + +/* + *---------------------------------------------------------------------------- + * + * VMwareCtrl_QueryExtension -- + * + * Standard QueryExtension implementation. Not very interesting for + * VMWARE_CTRL as it doesn't define any events or errors. + * + * Results: + * True if information is successfully retrieved. False otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +Bool +VMwareCtrl_QueryExtension(Display *dpy, // IN: + int *event_basep, // OUT: + int *error_basep) // OUT: +{ + XExtDisplayInfo *info = find_display(dpy); + + if (XextHasExtension(info)) { + *event_basep = info->codes->first_event; + *error_basep = info->codes->first_error; + return True; + } else { + return False; + } +} + + +/* + *---------------------------------------------------------------------------- + * + * VMwareCtrl_QueryVersion -- + * + * Send the QueryVersion command to the driver and return the results. + * + * Results: + * True if information is successfully retrieved. False otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +Bool +VMwareCtrl_QueryVersion(Display *dpy, // IN: + int *majorVersion, // OUT: + int *minorVersion) // OUT: +{ + xVMwareCtrlQueryVersionReply rep; + xVMwareCtrlQueryVersionReq *req; + XExtDisplayInfo *info = find_display(dpy); + Bool ret = False; + + VMwareCtrlCheckExtension(dpy, info, False); + LockDisplay(dpy); + + GetReq(VMwareCtrlQueryVersion, req); + req->reqType = info->codes->major_opcode; + req->VMwareCtrlReqType = X_VMwareCtrlQueryVersion; + + if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) { + goto exit; + } + *majorVersion = rep.majorVersion; + *minorVersion = rep.minorVersion; + + ret = True; + +exit: + UnlockDisplay(dpy); + SyncHandle(); + + return ret; +} + + +/* + *---------------------------------------------------------------------------- + * + * VMwareCtrl_SetRes -- + * + * Send the SetRes command to the driver. + * + * Results: + * True if the resolution was set. False otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +Bool +VMwareCtrl_SetRes(Display *dpy, // IN: + int screen, // IN: + int x, // IN: + int y) // IN: +{ + xVMwareCtrlSetResReply rep; + xVMwareCtrlSetResReq *req; + XExtDisplayInfo *info = find_display(dpy); + Bool ret = False; + + VMwareCtrlCheckExtension(dpy, info, False); + LockDisplay(dpy); + + GetReq(VMwareCtrlSetRes, req); + req->reqType = info->codes->major_opcode; + req->VMwareCtrlReqType = X_VMwareCtrlSetRes; + req->screen = screen; + req->x = x; + req->y = y; + + if (!_XReply(dpy, (xReply *)&rep, + (SIZEOF(xVMwareCtrlSetResReply) - SIZEOF(xReply)) >> 2, + xFalse)) { + goto exit; + } + + ret = True; + +exit: + UnlockDisplay(dpy); + SyncHandle(); + + return ret; +} + + +/* + *---------------------------------------------------------------------------- + * + * VMwareCtrl_SetTopology -- + * + * Send the SetTopology command to the driver. + * + * Solaris 10 uses a different Xinerama standard than expected here. As a + * result, topology set is not supported and this function is excluded from + * Solaris builds. + * + * Results: + * True if the resolution and xinerama topology were set. False otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +#ifndef NO_MULTIMON +Bool +VMwareCtrl_SetTopology(Display *dpy, // IN: + int screen, // IN: + xXineramaScreenInfo extents[], // IN: + int number) // IN: +{ + xVMwareCtrlSetTopologyReply rep; + xVMwareCtrlSetTopologyReq *req; + XExtDisplayInfo *info = find_display(dpy); + Bool ret = False; + long len; + + VMwareCtrlCheckExtension(dpy, info, False); + LockDisplay(dpy); + + GetReq(VMwareCtrlSetTopology, req); + req->reqType = info->codes->major_opcode; + req->VMwareCtrlReqType = X_VMwareCtrlSetTopology; + req->screen = screen; + req->number = number; + + len = ((long) number) << 1; + SetReqLen(req, len, len); + len <<= 2; + _XSend(dpy, (char *)extents, len); + + if (!_XReply(dpy, (xReply *)&rep, + (SIZEOF(xVMwareCtrlSetTopologyReply) - SIZEOF(xReply)) >> 2, + xFalse)) { + goto exit; + } + + ret = True; + +exit: + UnlockDisplay(dpy); + SyncHandle(); + + return ret; +} +#endif diff --git a/open-vm-tools/services/plugins/resolutionSet/libvmwarectrl.h b/open-vm-tools/services/plugins/resolutionSet/libvmwarectrl.h new file mode 100644 index 000000000..83ae0960d --- /dev/null +++ b/open-vm-tools/services/plugins/resolutionSet/libvmwarectrl.h @@ -0,0 +1,51 @@ +/* + * Copyright 2006 by VMware, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of the copyright holder(s) + * and author(s) shall not be used in advertising or otherwise to promote + * the sale, use or other dealings in this Software without prior written + * authorization from the copyright holder(s) and author(s). + */ + +/* + * libvmwarectrl.c -- + * + * The VMWARE_CTRL client library. + */ + + +#ifndef _LIB_VMWARE_CTRL_H_ +#define _LIB_VMWARE_CTRL_H_ + +#include +#include +#ifndef NO_MULTIMON +#include +#endif + +Bool VMwareCtrl_QueryExtension(Display *dpy, int *event_basep, int *error_basep); +Bool VMwareCtrl_QueryVersion(Display *dpy, int *majorVersion, int *minorVersion); +Bool VMwareCtrl_SetRes(Display *dpy, int screen, int x, int y); +#ifndef NO_MULTIMON +Bool VMwareCtrl_SetTopology(Display *dpy, int screen, xXineramaScreenInfo[], int number); +#endif + +#endif /* _LIB_VMWARE_CTRL_H_ */ diff --git a/open-vm-tools/services/plugins/resolutionSet/resolutionInt.h b/open-vm-tools/services/plugins/resolutionSet/resolutionInt.h new file mode 100644 index 000000000..1cc3462b6 --- /dev/null +++ b/open-vm-tools/services/plugins/resolutionSet/resolutionInt.h @@ -0,0 +1,81 @@ +/********************************************************* + * Copyright (C) 2008 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 resolutionInt.h -- + * + * Internal header file for lib/resolution. + */ + +#ifndef _LIB_RESOLUTIONINT_H_ +#define _LIB_RESOLUTIONINT_H_ + +#define INCLUDE_ALLOW_USERLEVEL +#define INCLUDE_ALLOW_DISTRIBUTE +#include "includeCheck.h" + +#include "resolution.h" +#include "vm_app.h" + +/* + * Data types + */ + + +/* + * Describes internal state of the resolution library. I.e., tracks whether + * a capability is supported, enabled, etc. + */ +typedef struct { + Bool initialized; // TRUE if successfully initialized. + Bool canSetResolution; // TRUE if back-end supports Resolution_Set. + Bool canSetTopology; // TRUE if back-end supports DisplayTopology_Set. +} ResolutionInfoType; + + +/* + * Describes the size and offset of a display. An array of these + * structures describes the entire topology of the guest desktop + */ +typedef struct { + int x; + int y; + int width; + int height; +} DisplayTopologyInfo; + + +/* + * Global variables + */ + +extern ResolutionInfoType resolutionInfo; + +/* + * Global functions + */ + +/* Functions defined by back-end. */ +Bool ResolutionBackendInit(InitHandle handle); +InitHandle ResolutionToolkitInit(void); +void ResolutionBackendCleanup(void); +Bool ResolutionSetResolution(uint32 width, uint32 height); +Bool ResolutionSetTopology(unsigned int ndisplays, DisplayTopologyInfo displays[]); + + +#endif // ifndef _LIB_RESOLUTIONINT_H_ diff --git a/open-vm-tools/services/plugins/resolutionSet/resolutionSet.c b/open-vm-tools/services/plugins/resolutionSet/resolutionSet.c new file mode 100644 index 000000000..4177925ae --- /dev/null +++ b/open-vm-tools/services/plugins/resolutionSet/resolutionSet.c @@ -0,0 +1,385 @@ +/********************************************************* + * Copyright (C) 2008 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 resolution.c -- + * + * Set of functions to handle guest screen resizing for vmware-{user,guestd}. + */ + +#include +#include +#include + +#include "vmware.h" +#include "vm_app.h" +#include "debug.h" +#include "rpcout.h" +#include "str.h" +#include "strutil.h" + +#include "resolutionInt.h" + +#include "vmtools.h" +#include "vmtoolsApp.h" +#include "xdrutil.h" + +/* + * Internal global variables + */ + +/** + * Describes current state of the library. + */ +ResolutionInfoType resolutionInfo; + +/* + * Local function prototypes + */ + +static Bool ResolutionResolutionSetCB(RpcInData *data); +static Bool ResolutionDisplayTopologySetCB(RpcInData *data); + +/* + * Global function definitions + */ + +/** + * + * Initialize the guest resolution library. + * + * @param[in] handle Back-end specific handle, if needed. E.g., in the X11 + case, this refers to the X11 display handle. + * @return TRUE on success, FALSE on failure + */ + +static Bool +ResolutionInit(InitHandle handle) +{ + // Shorter-named convenience alias. I expect this to be optimized out. + ResolutionInfoType *resInfo = &resolutionInfo; + + ASSERT(resInfo->initialized == FALSE); + + if (!ResolutionBackendInit(handle)) { + return FALSE; + } + + resInfo->initialized = TRUE; + + return TRUE; +} + + +/** + * + * Shutdown the plugin, free resources, etc. + * Resolution_* calls will fail until user next calls ResolutionInit(). + */ + +static void +ResolutionCleanup(void) +{ + ResolutionInfoType *resInfo = &resolutionInfo; + + if (!resInfo->initialized) { + return; + } + + ResolutionBackendCleanup(); +} + + +/** + * + * Handler for TCLO 'Resolution_Set'. + * + * Routine unmarshals RPC arguments and passes over to back-end ResolutionSet(). + * + * @param[in] data RPC data + * @return TRUE if we can reply, FALSE otherwise. + */ + +static Bool +ResolutionResolutionSetCB(RpcInData *data) +{ + uint32 width = 0 ; + uint32 height = 0; + unsigned int index = 0; + Bool retval = FALSE; + + /* parse the width and height */ + if (!StrUtil_GetNextUintToken(&width, &index, data->args, " ")) { + goto invalid_arguments; + } + if (!StrUtil_GetNextUintToken(&height, &index, data->args, "")) { + goto invalid_arguments; + } + + retval = ResolutionSetResolution(width, height); + +invalid_arguments: + return RPCIN_SETRETVALS(data, retval ? "" : "Invalid arguments", retval); +} + + +/** + * + * Handler for TCLO 'DisplayTopology_Set'. + * + * Routine unmarshals RPC arguments and passes over to back-end TopologySet(). + * + * @param[in] data RPC data + * @return TRUE if we can reply, FALSE otherwise. + */ + +static Bool +ResolutionDisplayTopologySetCB(RpcInData *data) +{ + DisplayTopologyInfo *displays = NULL; + unsigned int count, i; + Bool success = FALSE; + const char *p; + + /* + * The argument string will look something like: + * [ , ] * count. + * + * e.g. + * 3 , 0 0 640 480 , 640 0 800 600 , 0 480 640 480 + */ + + if (sscanf(data->args, "%u", &count) != 1) { + return RPCIN_SETRETVALS(data, + "Invalid arguments. Expected \"count\"", + FALSE); + } + + displays = malloc(sizeof *displays * count); + if (!displays) { + RPCIN_SETRETVALS(data, + "Failed to alloc buffer for display info", + FALSE); + goto out; + } + + for (p = data->args, i = 0; i < count; i++) { + p = strchr(p, ','); + if (!p) { + RPCIN_SETRETVALS(data, + "Expected comma separated display list", + FALSE); + goto out; + } + p++; /* Skip past the , */ + + if (sscanf(p, " %d %d %d %d ", &displays[i].x, + &displays[i].y, &displays[i].width, &displays[i].height) != 4) { + RPCIN_SETRETVALS(data, + "Expected x, y, w, h in display entry", + FALSE); + goto out; + } + } + + success = ResolutionSetTopology(count, displays); + + RPCIN_SETRETVALS(data, success ? "" : "ResolutionSetTopology failed", success); + +out: + free(displays); + return success; +} + + +/** + * Cleanup internal data on shutdown. + * + * @param[in] src The source object. + * @param[in] ctx Unused. + * @param[in] data Unused. + */ + +static void +ResolutionSetShutdown(gpointer src, + ToolsAppCtx *ctx, + gpointer data) +{ + ResolutionCleanup(); +} + + +/** + * Sends the resolution_server capability to the VMX. + * + * @param[in] src The source object. + * @param[in] ctx The app context. + * @param[in] plugin Plugin registration data. + * @param[in] set Whether setting or unsetting the capability. + * + * @return NULL. The function sends the capability directly. + */ + +static void +ResolutionServerCapReg(ToolsAppCtx *ctx, + gint set) +{ + gchar *msg; + const char *appName = NULL; + + if (strcmp(ctx->name, VMTOOLS_GUEST_SERVICE) == 0) { + appName = TOOLS_DAEMON_NAME; + } else if (strcmp(ctx->name, VMTOOLS_USER_SERVICE) == 0) { + appName = TOOLS_DND_NAME; + } else { + NOT_REACHED(); + } + + msg = g_strdup_printf("tools.capability.resolution_server %s %d", + appName, + set); + + if (!RpcChannel_Send(ctx->rpc, msg, strlen(msg) + 1, NULL, NULL)) { + g_warning("Setting resolution_server capability failed!\n"); + } + + g_free(msg); +} + + +/** + * Returns the list of the plugin's capabilities. + * + * + * @param[in] src The source object. + * @param[in] ctx The app context. + * @param[in] set Whether setting or unsetting the capability. + * @param[in] data Unused. + * + * @return A list of capabilities. + */ + +static GArray * +ResolutionSetCapabilities(gpointer src, + ToolsAppCtx *ctx, + gboolean set, + gpointer data) +{ + enum { + RES_SET_IDX = 0, + DPY_TOPO_SET_IDX = 1, + DPY_GLOBAL_OFFSET_IDX = 2 + }; + + ToolsAppCapability caps[] = { + { TOOLS_CAP_OLD, "resolution_set", 0, 0 }, + { TOOLS_CAP_OLD, "display_topology_set", 0, 0 }, + { TOOLS_CAP_OLD, "display_global_offset", 0, 0 } + }; + + ResolutionInfoType *resInfo = &resolutionInfo; + int resServerCap = 0; + + if (set) { + if (!resInfo->initialized) { + return FALSE; + } + + if (resInfo->canSetResolution) { + caps[RES_SET_IDX].value = 1; + resServerCap = 1; + } + + if (resInfo->canSetTopology) { + caps[DPY_TOPO_SET_IDX].value = 2; + caps[DPY_GLOBAL_OFFSET_IDX].value = 1; + } + } + + ResolutionServerCapReg(ctx, resServerCap); + + return VMTools_WrapArray(caps, sizeof *caps, ARRAYSIZE(caps)); +} + + +/** + * Plugin entry point. Initializes internal plugin state. + * + * @param[in] ctx The app context. + * + * @return The registration data. + */ + +TOOLS_MODULE_EXPORT ToolsPluginData * +ToolsOnLoad(ToolsAppCtx *ctx) +{ + InitHandle handle; + + static ToolsPluginData regData = { + "resolutionSet", + NULL, + NULL + }; + + ToolsPluginSignalCb sigs[] = { + { TOOLS_CORE_SIG_CAPABILITIES, ResolutionSetCapabilities, ®Data }, + { TOOLS_CORE_SIG_SHUTDOWN, ResolutionSetShutdown, ®Data } + }; + ToolsAppReg regs[] = { + { TOOLS_APP_GUESTRPC, NULL }, + { TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) } + }; + + ResolutionInfoType *resInfo = &resolutionInfo; + + resInfo->initialized = FALSE; + + /* + * XXX move to some shared lib or plugin + */ + handle = ResolutionToolkitInit(); + + ResolutionInit(handle); + + /* + * Add one or both of the callbacks based on capabilities. + */ + if (resInfo->canSetResolution || resInfo->canSetTopology) { + int index = 0; + RpcChannelCallback rpcs[2]; + + memset(rpcs, '\0', sizeof rpcs); + + if (resInfo->canSetResolution) { + rpcs[index].name = "Resolution_Set"; + rpcs[index].callback = ResolutionResolutionSetCB; + index++; + } + + if (resInfo->canSetTopology) { + rpcs[index].name = "DisplayTopology_Set"; + rpcs[index].callback = ResolutionDisplayTopologySetCB; + index++; + } + + regs[0].data = VMTools_WrapArray(rpcs, sizeof *rpcs, index); + regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs)); + } + + return ®Data; +} diff --git a/open-vm-tools/services/plugins/resolutionSet/resolutionX11.c b/open-vm-tools/services/plugins/resolutionSet/resolutionX11.c new file mode 100644 index 000000000..c4ed0fde8 --- /dev/null +++ b/open-vm-tools/services/plugins/resolutionSet/resolutionX11.c @@ -0,0 +1,471 @@ +/********************************************************* + * Copyright (C) 2008 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 resolutionX11.c + * + * X11 backend for resolutionSet plugin. + */ + +#include +#include +#include + +#include +#include +#ifndef NO_MULTIMON +#include +#endif +#include +#include +#include +#undef Bool + +#include "vmware.h" + +#include "resolution.h" +#include "resolutionInt.h" + +#include "debug.h" +#include "fileIO.h" +#include "libvmwarectrl.h" +#include "str.h" +#include "strutil.h" + +#define VMWAREDRV_PATH_64 "/usr/X11R6/lib64/modules/drivers/vmware_drv.o" +#define VMWAREDRV_PATH "/usr/X11R6/lib/modules/drivers/vmware_drv.o" +#define VERSION_STRING "VMware Guest X Server" + + +/** + * Describes the state of the X11 back-end of lib/resolution. + */ +typedef struct { + Display *display; // X11 connection / display context + Window rootWindow; // points to display's root window + Bool canUseVMwareCtrl; + // TRUE if VMwareCtrl extension available + Bool canUseVMwareCtrlTopologySet; + // TRUE if VMwareCtrl extension supports topology set +} ResolutionInfoX11Type; + + +/* + * Global variables + */ + +ResolutionInfoX11Type resolutionInfoX11; + +/* + * Local function prototypes + */ + +static Bool ResolutionCanSet(void); +static Bool TopologyCanSet(void); +static Bool SelectResolution(uint32 width, uint32 height); + + +/* + * Global function definitions + */ + + +/** + * X11 back-end initializer. Records caller's X11 display, then determines + * which capabilities are available. + * + * @param[in] handle User's X11 display + * @return TRUE on success, FALSE on failure. + */ + +Bool +ResolutionBackendInit(InitHandle handle) +{ + ResolutionInfoX11Type *resInfoX = &resolutionInfoX11; + ResolutionInfoType *resInfo = &resolutionInfo; + int dummy1; + int dummy2; + + memset(resInfoX, 0, sizeof *resInfoX); + + resInfoX->display = handle; + + if (resInfoX->display == NULL) { + Warning("%s: Called with invalid X display!\n", __func__); + return FALSE; + } + + resInfoX->display = handle; + resInfoX->rootWindow = DefaultRootWindow(resInfoX->display); + resInfoX->canUseVMwareCtrl = VMwareCtrl_QueryVersion(resInfoX->display, &dummy1, + &dummy2); + resInfoX->canUseVMwareCtrlTopologySet = FALSE; + + resInfo->canSetResolution = ResolutionCanSet(); + resInfo->canSetTopology = TopologyCanSet(); + + return TRUE; +} + + +/** + * Stub implementation of ResolutionBackendCleanup for the X11 back-end. + */ + +void +ResolutionBackendCleanup(void) +{ + return; +} + + +/** + * Given a width and height, define a custom resolution (if VMwareCtrl is + * available), then issue a change resolution request via XRandR. + * + * This is called as a result of the Resolution_Set request from the vmx. + * + * @param[in] width requested width + * @param[in] height requested height + * @return TRUE if we are able to set to the exact size requested, FALSE otherwise. + */ + +Bool +ResolutionSetResolution(uint32 width, + uint32 height) +{ + ResolutionInfoX11Type *resInfoX = &resolutionInfoX11; + ASSERT(resolutionInfo.canSetResolution); + + if (resInfoX->canUseVMwareCtrl) { + /* + * If so, use the VMWARE_CTRL extension to provide a custom resolution + * which we'll find as an exact match from XRRConfigSizes() (unless + * the resolution is too large). + * + * As such, we don't care if this succeeds or fails, we'll make a best + * effort attempt to change resolution anyway. + */ + VMwareCtrl_SetRes(resInfoX->display, DefaultScreen(resInfoX->display), + width, height); + } + + return SelectResolution(width, height); +} + + +/** + * Employs the Xinerama extension to declare a new display topology. + * + * @note Solaris 10 uses a different Xinerama standard than expected here. As a + * result, topology set is not supported and this function is excluded from + * Solaris builds. With Solaris 10 shipping X.org, perhaps we should revisit + * this decision. + * + * @param[in] ndisplays number of elements in topology + * @param[in] topology array of display geometries + * @return TRUE if operation succeeded, FALSE otherwise. + */ + +Bool +ResolutionSetTopology(unsigned int ndisplays, + DisplayTopologyInfo *topology) +{ +#ifdef NO_MULTIMON + return FALSE; +#else + ResolutionInfoX11Type *resInfoX = &resolutionInfoX11; + Bool success = FALSE; + unsigned int i; + xXineramaScreenInfo *displays = NULL; + short maxX = 0; + short maxY = 0; + int minX = 0; + int minY = 0; + + ASSERT(resolutionInfo.canSetTopology); + + /* + * Allocate xXineramaScreenInfo array & translate from DisplayTopologyInfo. + * Iterate over displays looking for minimum, maximum dimensions. + * Warn if min isn't at (0,0). + * Transform to (0,0). + * Call out to VMwareCtrl_SetTopology. + * Set new jumbotron resolution. + */ + + displays = malloc(sizeof *displays * ndisplays); + if (!displays) { + goto out; + } + + for (i = 0; i < ndisplays; i++) { + displays[i].x_org = topology[i].x; + displays[i].y_org = topology[i].y; + displays[i].width = topology[i].width; + displays[i].height = topology[i].height; + + maxX = MAX(maxX, displays[i].x_org + displays[i].width); + maxY = MAX(maxY, displays[i].y_org + displays[i].height); + minX = MIN(minX, displays[i].x_org); + minY = MIN(minY, displays[i].y_org); + } + + if (minX != 0 || minY != 0) { + Warning("The bounding box of the display topology does not have an " + "origin of (0,0)\n"); + } + + /* + * Transform the topology so that the bounding box has an origin of (0,0). Since the + * host is already supposed to pass a normalized topology, this should not have any + * effect. + */ + for (i = 0; i < ndisplays; i++) { + displays[i].x_org -= minX; + displays[i].y_org -= minY; + } + + if (!VMwareCtrl_SetTopology(resInfoX->display, DefaultScreen(resInfoX->display), displays, + ndisplays)) { + Debug("Failed to set topology in the driver.\n"); + goto out; + } + + if (!SelectResolution(maxX - minX, maxY - minY)) { + Debug("Failed to set new resolution.\n"); + goto out; + } + + success = TRUE; + +out: + free(displays); + return success; +#endif +} + + +/* + * Local function definitions + */ + + +/** + * Is the VMware SVGA driver a high enough version to support resolution + * changing? We check by searching the driver binary for a known version + * string. + * + * @return TRUE if the driver version is high enough, FALSE otherwise. + */ + +static Bool +ResolutionCanSet(void) +{ + ResolutionInfoX11Type *resInfoX = &resolutionInfoX11; + FileIODescriptor fd; + FileIOResult res; + int64 filePos = 0; + Bool keepSearching = TRUE; + Bool found = FALSE; + char buf[sizeof VERSION_STRING + 10]; // size of VERSION_STRING plus some extra for the version number + const char versionString[] = VERSION_STRING; + size_t bytesRead; + int32 major, minor, level; + unsigned int tokPos; + + /* See if the randr X module is loaded */ + if (!XRRQueryVersion(resInfoX->display, &major, &minor) ) { + return FALSE; + } + + /* See if the VMWARE_CTRL extension is supported */ + if (resInfoX->canUseVMwareCtrl) { + return TRUE; + } + + /* + * XXX: This check does not work with XOrg 6.9/7.0 for two reasons: Both + * versions now use .so for the driver extension and 7.0 moves the drivers + * to a completely different directory. As long as we ship a driver for + * 6.9/7.0, we can instead just use the VMWARE_CTRL check. + */ + buf[sizeof buf - 1] = '\0'; + FileIO_Invalidate(&fd); + res = FileIO_Open(&fd, VMWAREDRV_PATH_64, FILEIO_ACCESS_READ, FILEIO_OPEN); + if (res != FILEIO_SUCCESS) { + res = FileIO_Open(&fd, VMWAREDRV_PATH, FILEIO_ACCESS_READ, FILEIO_OPEN); + } + if (res == FILEIO_SUCCESS) { + /* + * One of the opens succeeded, so start searching thru the file. + */ + while (keepSearching) { + res = FileIO_Read(&fd, buf, sizeof buf - 1, &bytesRead); + if (res != FILEIO_SUCCESS || bytesRead < sizeof buf -1 ) { + keepSearching = FALSE; + } else { + if (Str_Strncmp(versionString, buf, sizeof versionString - 1) == 0) { + keepSearching = FALSE; + found = TRUE; + } + } + filePos = FileIO_Seek(&fd, filePos+1, FILEIO_SEEK_BEGIN); + if (filePos == -1) { + keepSearching = FALSE; + } + } + FileIO_Close(&fd); + if (found) { + /* + * We NUL-terminated buf earlier, but Coverity really wants it to + * be NUL-terminated after the call to FileIO_Read (because + * FileIO_Read doesn't NUL-terminate). So we'll do it again. + */ + buf[sizeof buf - 1] = '\0'; + + /* + * Try and parse the major, minor and level versions + */ + tokPos = sizeof versionString - 1; + if (!StrUtil_GetNextIntToken(&major, &tokPos, buf, ".- ")) { + return FALSE; + } + if (!StrUtil_GetNextIntToken(&minor, &tokPos, buf, ".- ")) { + return FALSE; + } + if (!StrUtil_GetNextIntToken(&level, &tokPos, buf, ".- ")) { + return FALSE; + } + + return ((major > 10) || (major == 10 && minor >= 11)); + } + } + return FALSE; +} + + +/** + * Tests whether or not we can change display topology. + * + * @return TRUE if we're able to reset topology, otherwise FALSE. + * @note resInfoX->canUseVMwareCtrlTopologySet will be set to TRUE on success. + */ + +static Bool +TopologyCanSet(void) +{ + ResolutionInfoX11Type *resInfoX = &resolutionInfoX11; +#ifdef NO_MULTIMON + resInfoX->canUseVMwareCtrlTopologySet = FALSE; +#else + int major; + int minor; + + if (resInfoX->canUseVMwareCtrl && XineramaQueryVersion(resInfoX->display, &major, + &minor)) { + /* + * We need both a new enough VMWARE_CTRL and Xinerama for this to work. + */ + resInfoX->canUseVMwareCtrlTopologySet = (major > 0) || (major == 0 && minor >= 2); + } else { + resInfoX->canUseVMwareCtrlTopologySet = FALSE; + } +#endif + + return resInfoX->canUseVMwareCtrlTopologySet; +} + + +/** + * Given a width and height, find the biggest resolution that will "fit". + * This is called as a result of the resolution set request from the vmx. + * + * @param[in] width + * @param[in] height + * @return TRUE if we are able to set to the exact size requested, FALSE otherwise. + */ + +Bool +SelectResolution(uint32 width, + uint32 height) +{ + ResolutionInfoX11Type *resInfoX = &resolutionInfoX11; + XRRScreenConfiguration* xrrConfig; + XRRScreenSize *xrrSizes; + Rotation xrrCurRotation; + uint32 xrrNumSizes; + uint32 i; + uint32 bestFitIndex = 0; + uint64 bestFitSize = 0; + uint64 potentialSize; + + xrrConfig = XRRGetScreenInfo(resInfoX->display, resInfoX->rootWindow); + xrrSizes = XRRConfigSizes(xrrConfig, &xrrNumSizes); + XRRConfigCurrentConfiguration(xrrConfig, &xrrCurRotation); + + /* + * Iterate thru the list finding the best fit that is still <= in both width + * and height. + */ + for (i = 0; i < xrrNumSizes; i++) { + potentialSize = xrrSizes[i].width * xrrSizes[i].height; + if (xrrSizes[i].width <= width && xrrSizes[i].height <= height && + potentialSize > bestFitSize ) { + bestFitSize = potentialSize; + bestFitIndex = i; + } + } + + if (bestFitSize > 0) { + Debug("Setting guest resolution to: %dx%d (requested: %d, %d)\n", + xrrSizes[bestFitIndex].width, xrrSizes[bestFitIndex].height, width, height); + XRRSetScreenConfig(resInfoX->display, xrrConfig, resInfoX->rootWindow, + bestFitIndex, xrrCurRotation, GDK_CURRENT_TIME); + } else { + Debug("Can't find a suitable guest resolution, ignoring request for %dx%d\n", + width, height); + } + + XRRFreeScreenConfigInfo(xrrConfig); + return xrrSizes[bestFitIndex].width == width && + xrrSizes[bestFitIndex].height == height; +} + +/** + * Obtain a "handle", which for X11, is a display pointer. + * + * @note We will have to move this out of the resolution plugin soon, I am + * just landing this here now for convenience as I port resolution set over + * to the new service architecture. + * + * @return X server display + */ + +InitHandle +ResolutionToolkitInit(void) +{ + int argc = 1; + char *argv[] = {"", NULL}; + GtkWidget *wnd; + Display *display; + gtk_init(&argc, (char ***) &argv); + wnd = gtk_invisible_new(); + display = GDK_WINDOW_XDISPLAY(wnd->window); + return (InitHandle) display; +} diff --git a/open-vm-tools/services/plugins/resolutionSet/vmwarectrl.h b/open-vm-tools/services/plugins/resolutionSet/vmwarectrl.h new file mode 100644 index 000000000..a671ae1dc --- /dev/null +++ b/open-vm-tools/services/plugins/resolutionSet/vmwarectrl.h @@ -0,0 +1,48 @@ +/* + * Copyright 2006 by VMware, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of the copyright holder(s) + * and author(s) shall not be used in advertising or otherwise to promote + * the sale, use or other dealings in this Software without prior written + * authorization from the copyright holder(s) and author(s). + */ + +/* + * vmwarectrl.h -- + * + * The definitions used by the VMWARE_CTRL protocol extension that + * allows X clients to communicate with the driver. + */ + + +#ifndef _VMWARE_CTRL_H_ +#define _VMWARE_CTRL_H_ + +#define VMWARE_CTRL_PROTOCOL_NAME "VMWARE_CTRL" + +#define VMWARE_CTRL_MAJOR_VERSION 0 +#define VMWARE_CTRL_MINOR_VERSION 2 + +#define X_VMwareCtrlQueryVersion 0 +#define X_VMwareCtrlSetRes 1 +#define X_VMwareCtrlSetTopology 2 + +#endif /* _VMWARE_CTRL_H_ */ diff --git a/open-vm-tools/services/plugins/resolutionSet/vmwarectrlproto.h b/open-vm-tools/services/plugins/resolutionSet/vmwarectrlproto.h new file mode 100644 index 000000000..76f157f91 --- /dev/null +++ b/open-vm-tools/services/plugins/resolutionSet/vmwarectrlproto.h @@ -0,0 +1,122 @@ +/* + * Copyright 2006 by VMware, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of the copyright holder(s) + * and author(s) shall not be used in advertising or otherwise to promote + * the sale, use or other dealings in this Software without prior written + * authorization from the copyright holder(s) and author(s). + */ + +/* + * vmwarectrlproto.h -- + * + * The description of the VMWARE_CTRL protocol extension that + * allows X clients to communicate with the driver. + */ + +#ifndef _VMWARE_CTRL_PROTO_H_ +#define _VMWARE_CTRL_PROTO_H_ + + +#include +#include "vmwarectrl.h" + + +/* + * Requests and Replies + */ + +/* Version 0.1 definitions. */ + +typedef struct { + CARD8 reqType; /* always X_VMwareCtrlReqCode */ + CARD8 VMwareCtrlReqType; /* always X_VMwareCtrlQueryVersion */ + CARD16 length B16; + CARD32 majorVersion B32; + CARD32 minorVersion B32; +} xVMwareCtrlQueryVersionReq; +#define sz_xVMwareCtrlQueryVersionReq 12 + +typedef struct { + BYTE type; /* X_Reply */ + BYTE pad1; + CARD16 sequenceNumber B16; + CARD32 length B32; + CARD32 majorVersion B32; + CARD32 minorVersion B32; + CARD32 pad2 B32; + CARD32 pad3 B32; + CARD32 pad4 B32; + CARD32 pad5 B32; +} xVMwareCtrlQueryVersionReply; +#define sz_xVMwareCtrlQueryVersionReply 32 + +typedef struct { + CARD8 reqType; /* always X_VMwareCtrlReqCode */ + CARD8 VMwareCtrlReqType; /* always X_VMwareCtrlSetRes */ + CARD16 length B16; + CARD32 screen B32; + CARD32 x B32; + CARD32 y B32; +} xVMwareCtrlSetResReq; +#define sz_xVMwareCtrlSetResReq 16 + +typedef struct { + BYTE type; /* X_Reply */ + BYTE pad1; + CARD16 sequenceNumber B16; + CARD32 length B32; + CARD32 screen B32; + CARD32 x B32; + CARD32 y B32; + CARD32 pad2 B32; + CARD32 pad3 B32; + CARD32 pad4 B32; +} xVMwareCtrlSetResReply; +#define sz_xVMwareCtrlSetResReply 32 + +/* Version 0.2 definitions. */ + +typedef struct { + CARD8 reqType; /* always X_VMwareCtrlReqCode */ + CARD8 VMwareCtrlReqType; /* always X_VMwareCtrlSetTopology */ + CARD16 length B16; + CARD32 screen B32; + CARD32 number B32; + CARD32 pad1 B32; +} xVMwareCtrlSetTopologyReq; +#define sz_xVMwareCtrlSetTopologyReq 16 + +typedef struct { + BYTE type; /* X_Reply */ + BYTE pad1; + CARD16 sequenceNumber B16; + CARD32 length B32; + CARD32 screen B32; + CARD32 pad2 B32; + CARD32 pad3 B32; + CARD32 pad4 B32; + CARD32 pad5 B32; + CARD32 pad6 B32; +} xVMwareCtrlSetTopologyReply; +#define sz_xVMwareCtrlSetTopologyReply 32 + +#endif /* _VMWARE_CTRL_PROTO_H_ */ diff --git a/open-vm-tools/services/plugins/timeSync/COPYING b/open-vm-tools/services/plugins/timeSync/COPYING new file mode 100644 index 000000000..09f465ab7 --- /dev/null +++ b/open-vm-tools/services/plugins/timeSync/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/open-vm-tools/services/plugins/timeSync/Makefile.am b/open-vm-tools/services/plugins/timeSync/Makefile.am new file mode 100644 index 000000000..3570cae39 --- /dev/null +++ b/open-vm-tools/services/plugins/timeSync/Makefile.am @@ -0,0 +1,35 @@ +################################################################################ +### Copyright 2009 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 +################################################################################ + +noinst_LTLIBRARIES = libtimeSync.la + +libtimeSync_la_CPPFLAGS = +libtimeSync_la_CPPFLAGS += @PLUGIN_CPPFLAGS@ + +libtimeSync_la_LDFLAGS = +libtimeSync_la_LDFLAGS += @PLUGIN_LDFLAGS@ + +libtimeSync_la_LIBADD = +libtimeSync_la_LIBADD += @VMTOOLS_LIBS@ + +libtimeSync_la_SOURCES = +libtimeSync_la_SOURCES += timeSync.c + +install-exec-local: + mkdir -p $(DESTDIR)$(VMSVC_PLUGIN_INSTALLDIR) + cp -L $(builddir)/.libs/libtimeSync.so $(DESTDIR)$(VMSVC_PLUGIN_INSTALLDIR) + diff --git a/open-vm-tools/services/plugins/timeSync/timeSync.c b/open-vm-tools/services/plugins/timeSync/timeSync.c new file mode 100644 index 000000000..22972af74 --- /dev/null +++ b/open-vm-tools/services/plugins/timeSync/timeSync.c @@ -0,0 +1,579 @@ +/********************************************************* + * Copyright (C) 2008 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 timeSync.c + * + * Plugin to handle time synchronization between the guest and host. + */ + +#define G_LOG_DOMAIN "timeSync" + +/* sync the time once a minute */ +#define TIME_SYNC_TIME 60000 +/* only PERCENT_CORRECTION percent is corrected everytime */ +#define PERCENT_CORRECTION 50 + + +#include "backdoor.h" +#include "backdoor_def.h" +#include "conf.h" +#include "msg.h" +#include "strutil.h" +#include "system.h" +#include "vm_app.h" +#include "vmtools.h" +#include "vmtoolsApp.h" + +#if !defined(__APPLE__) +#include "embed_version.h" +#include "vmtoolsd_version.h" +VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING); +#endif + + +typedef struct TimeSyncData { + gboolean slewCorrection; + uint32 slewPercentCorrection; + gint timeSyncState; + uint32 timeSyncPeriod; /* In milliseconds. */ + GSource *timer; +} TimeSyncData; + + +/** + * Set the guest OS time to the host OS time. + * + * @param[in] slewCorrection Is clock slewing enabled? + * @param[in] syncOnce Is this function called in a loop? + * @param[in] _data Time sync data. + * + * @return TRUE on success. + */ + +static gboolean +TimeSyncDoSync(Bool slewCorrection, + Bool syncOnce, + void *_data) +{ + Backdoor_proto bp; + int64 maxTimeLag; + int64 interruptLag; + int64 guestSecs; + int64 guestUsecs; + int64 hostSecs; + int64 hostUsecs; + int64 diffSecs; + int64 diffUsecs; + int64 diff; + TimeSyncData *data = _data; + Bool timeLagCall = FALSE; +#ifdef VMX86_DEBUG + static int64 lastHostSecs = 0; + int64 secs1, usecs1; + int64 secs2, usecs2; + + System_GetCurrentTime(&secs1, &usecs1); +#endif + + g_debug("Synchronizing time.\n"); + + /* + * We need 3 things from the host, and there exist 3 different versions of + * the calls (described further below): + * 1) host time + * 2) maximum time lag allowed (config option), which is a + * threshold that keeps the tools from being over eager about + * resetting the time when it is only a little bit off. + * 3) interrupt lag + * + * First 2 versions of the call add interrupt lag to the maximum allowed + * time lag, where as in the last call it is returned separately. + * + * Three versions of the call: + * + * - BDOOR_CMD_GETTIME: suffers from a 136-year overflow problem that + * cannot be corrected without breaking backwards compatibility with + * older Tools. So, we have the newer BDOOR_CMD_GETTIMEFULL, which is + * overflow safe. + * + * - BDOOR_CMD_GETTIMEFULL: overcomes the problem above. + * + * - BDOOR_CMD_GETTIMEFULL_WITH_LAG: Both BDOOR_CMD_GETTIMEFULL and + * BDOOR_CMD_GETTIME returns max lag limit as interrupt lag + the maximum + * allowed time lag. BDOOR_CMD_GETTIMEFULL_WITH_LAG separates these two + * values. This is helpful when synchronizing time backwards by slewing + * the clock. + * + * We use BDOOR_CMD_GETTIMEFULL_WITH_LAG first and fall back to + * BDOOR_CMD_GETTIMEFULL or BDOOR_CMD_GETTIME. + * + * Note that BDOOR_CMD_GETTIMEFULL and BDOOR_CMD_GETTIMEFULL_WITH_LAG will + * not touch EAX when it succeeds. So we check for errors by comparing EAX to + * BDOOR_MAGIC, which was set by the call to Backdoor() prior to touching the + * backdoor port. + */ + bp.in.cx.halfs.low = BDOOR_CMD_GETTIMEFULL_WITH_LAG; + Backdoor(&bp); + if (bp.out.ax.word == BDOOR_MAGIC) { + hostSecs = ((uint64)bp.out.si.word << 32) | bp.out.dx.word; + interruptLag = bp.out.di.word; + timeLagCall = TRUE; + g_debug("Using BDOOR_CMD_GETTIMEFULL_WITH_LAG\n"); + } else { + g_debug("BDOOR_CMD_GETTIMEFULL_WITH_LAG not supported by current host, " + "attempting BDOOR_CMD_GETTIMEFULL\n"); + interruptLag = 0; + bp.in.cx.halfs.low = BDOOR_CMD_GETTIMEFULL; + Backdoor(&bp); + if (bp.out.ax.word == BDOOR_MAGIC) { + hostSecs = ((uint64)bp.out.si.word << 32) | bp.out.dx.word; + } else { + g_debug("BDOOR_CMD_GETTIMEFULL not supported by current host, " + "attempting BDOOR_CMD_GETTIME\n"); + bp.in.cx.halfs.low = BDOOR_CMD_GETTIME; + Backdoor(&bp); + hostSecs = bp.out.ax.word; + } + } + hostUsecs = bp.out.bx.word; + maxTimeLag = bp.out.cx.word; + + if (hostSecs <= 0) { + g_warning("Invalid host OS time: %"FMT64"d secs, %"FMT64"d usecs.\n\n", + hostSecs, hostUsecs); + return FALSE; + } + + /* Get the guest OS time */ + if (!System_GetCurrentTime(&guestSecs, &guestUsecs)) { + g_warning("Unable to retrieve the guest OS time: %s.\n\n", Msg_ErrString()); + return FALSE; + } + + diffSecs = hostSecs - guestSecs; + diffUsecs = hostUsecs - guestUsecs; + if (diffUsecs < 0) { + diffSecs -= 1; + diffUsecs += 1000000U; + } + diff = diffSecs * 1000000L + diffUsecs; + +#ifdef VMX86_DEBUG + g_debug("Daemon: Guest clock lost %.6f secs; limit=%.2f; " + "%"FMT64"d secs since last update\n", + diff / 1000000.0, maxTimeLag / 1000000.0, hostSecs - lastHostSecs); + g_debug("Daemon: %d, %d, %"FMT64"d, %"FMT64"d, %"FMT64"d.\n", + syncOnce, slewCorrection, diff, maxTimeLag, interruptLag); + lastHostSecs = hostSecs; +#endif + + if (syncOnce) { + /* + * Non-loop behavior: + * + * Perform a step correction if: + * 1) The guest OS is behind the host OS by more than maxTimeLag + interruptLag. + * 2) The guest OS is ahead of the host OS. + */ + if (diff > maxTimeLag + interruptLag) { + System_DisableTimeSlew(); + if (!System_AddToCurrentTime(diffSecs, diffUsecs)) { + g_warning("Unable to set the guest OS time: %s.\n\n", Msg_ErrString()); + return FALSE; + } + } + } else { + + /* + * Loop behavior: + * + * If guest is behind host by more than maxTimeLag + interruptLag + * perform a step correction to the guest clock and ask the monitor + * to drop its accumulated catchup (interruptLag). + * + * Otherwise, perform a slew correction. Adjust the guest's clock + * rate to be either faster or slower than nominal real time, such + * that we expect to correct correctionPercent percent of the error + * during this synchronization cycle. + */ + + if (diff > maxTimeLag + interruptLag) { + System_DisableTimeSlew(); + if (!System_AddToCurrentTime(diffSecs, diffUsecs)) { + g_warning("Unable to set the guest OS time: %s.\n\n", Msg_ErrString()); + return FALSE; + } + } else if (slewCorrection && timeLagCall) { + int64 slewDiff; + + /* Don't consider interruptLag during clock slewing. */ + slewDiff = diff - interruptLag; + + /* Correct only data->slewPercentCorrection percent error. */ + slewDiff = (data->slewPercentCorrection * slewDiff) / 100; + + if (!System_EnableTimeSlew(slewDiff, data->timeSyncPeriod)) { + g_warning("Unable to slew the guest OS time: %s.\n\n", Msg_ErrString()); + return FALSE; + } + } else { + System_DisableTimeSlew(); + } + } + +#ifdef VMX86_DEBUG + System_GetCurrentTime(&secs2, &usecs2); + + g_debug("Time changed from %"FMT64"d.%"FMT64"d -> %"FMT64"d.%"FMT64"d\n", + secs1, usecs1, secs2, usecs2); +#endif + + /* + * If we have stepped the time, ask TimeTracker to reset to normal the rate + * of timer interrupts it forwards from the host to the guest. + */ + if (!System_IsTimeSlewEnabled()) { + bp.in.cx.halfs.low = BDOOR_CMD_STOPCATCHUP; + Backdoor(&bp); + } + + return TRUE; +} + + +/** + * Run the "time synchronization" loop. + * + * @param[in] _data Time sync data. + * + * @return TRUE on success. + */ + +static gboolean +ToolsDaemonTimeSyncLoop(gpointer _data) +{ + TimeSyncData *data = _data; + + g_assert(data != NULL); + + if (!TimeSyncDoSync(data->slewCorrection, FALSE, data)) { + g_warning("Unable to synchronize time.\n"); + if (data->timer != NULL) { + g_source_unref(data->timer); + data->timer = NULL; + } + return FALSE; + } + + return TRUE; +} + + +#if defined(_WIN32) +/** + * Try to disable the Windows Time Daemon. + * + * @return TRUE on success. + */ + +static Bool +TimeSyncDisableWinTimeDaemon(void) +{ + DWORD timeAdjustment; + DWORD timeIncrement; + DWORD error; + BOOL timeAdjustmentDisabled; + BOOL success = FALSE; + + /* + * We need the SE_SYSTEMTIME_NAME privilege to make the change; get + * the privilege now (or bail if we can't). + */ + success = System_SetProcessPrivilege(SE_SYSTEMTIME_NAME, TRUE); + if (!success) { + return FALSE; + } + + /* Actually try to stop the time daemon. */ + if (GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, + &timeAdjustmentDisabled)) { + g_debug("GetSystemTimeAdjustment() succeeded: timeAdjustment %d," + "timeIncrement %d, timeAdjustmentDisabled %s\n", + timeAdjustment, timeIncrement, + timeAdjustmentDisabled ? "TRUE" : "FALSE"); + /* + * timeAdjustmentDisabled means the opposite of what you'd think; + * if it's TRUE, that means the system may be adjusting the time + * on its own using the time daemon. Read MSDN for the details, + * and see Bug 24173 for more discussion on this. + */ + + if (timeAdjustmentDisabled) { + /* + * MSDN is a bit vague on the semantics of this function, but it + * would appear that the timeAdjustment value here is simply the + * total amount that the system will add to the clock on each + * timer tick, i.e. if you set it to zero the system clock will + * not progress at all (and indeed, attempting to set it to zero + * results in an ERROR_INVALID_PARAMETER). In order to have time + * proceed at the normal rate, this needs to be set to the value + * of timeIncrement retrieved from GetSystemTimeAdjustment(). + */ + if (!SetSystemTimeAdjustment(timeIncrement, FALSE)) { + error = GetLastError(); + g_debug("SetSystemTimeAdjustment failed: %d\n", error); + goto exit; + } + } + } else { + error = GetLastError(); + g_debug("GetSystemTimeAdjustment failed: %d\n", error); + goto exit; + } + + success = TRUE; + + exit: + g_debug("Stopping time daemon %s.\n", success ? "succeeded" : "failed"); + System_SetProcessPrivilege(SE_SYSTEMTIME_NAME, FALSE); + return success; +} +#endif + + +/** + * Start or stop the "time synchronization" loop. Nothing will be done if + * start==TRUE & it's already running or start==FALSE & it's not running. + * + * @param[in] ctx The application context. + * @param[in] data Time sync data. + * @param[in] start See above. + * + * @return TRUE on success. + */ + +static Bool +TimeSyncStartStopLoop(ToolsAppCtx *ctx, + TimeSyncData *data, + gboolean start) +{ + g_assert(data != NULL); + + if (start && data->timer == NULL) { + g_debug("Starting time sync loop.\n"); + g_debug("New sync period is %d sec.\n", data->timeSyncPeriod / 1000); + if (!ToolsDaemonTimeSyncLoop(data)) { + return FALSE; + } + data->timer = g_timeout_source_new(data->timeSyncPeriod); + VMTOOLSAPP_ATTACH_SOURCE(ctx, data->timer, ToolsDaemonTimeSyncLoop, data, NULL); + +#if defined(_WIN32) + g_debug("Daemon: Attempting to disable Windows Time daemon\n"); + if (!TimeSyncDisableWinTimeDaemon()) { + g_debug("Daemon: Failed to disable Windows Time daemon\n"); + } +#endif + + return TRUE; + } else if (!start && data->timer != NULL) { + g_debug("Stopping time sync loop.\n"); + System_DisableTimeSlew(); + g_source_destroy(data->timer); + data->timer = NULL; + + return TRUE; + } + + /* + * No need to start time sync b/c it's already running or no + * need to stop it b/c it's not running. + */ + return TRUE; +} + + +/** + * Sync the guest's time with the host's. + * + * @param[in] data RPC request data. + * + * @return TRUE on success. + */ + +static Bool +TimeSyncTcloHandler(RpcInData *data) +{ + Bool slewCorrection = !strcmp(data->args, "1"); + TimeSyncData *syncData = data->clientData; + + if (!TimeSyncDoSync(slewCorrection, TRUE, syncData)) { + return RPCIN_SETRETVALS(data, "Unable to sync time", FALSE); + } else { + return RPCIN_SETRETVALS(data, "", TRUE); + } +} + + +/** + * Handles a "Set_Option" callback. Processes the time sync related options. + * + * @param[in] src The source object. + * @param[in] ctx The app context. + * @param[in] option Option being set. + * @param[in] value Option value. + * @param[in] plugin Plugin registration data. + * + * @return TRUE on success. + */ + +static gboolean +TimeSyncSetOption(gpointer src, + ToolsAppCtx *ctx, + const gchar *option, + const gchar *value, + ToolsPluginData *plugin) +{ + TimeSyncData *data = plugin->_private; + if (strcmp(option, TOOLSOPTION_SYNCTIME) == 0) { + gboolean start; + + if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) { + return FALSE; + } + start = (strcmp(value, "1") == 0); + + /* + * Try the one-shot time sync if time sync transitions from + * 'off' to 'on'. + */ + if (data->timeSyncState == 0 && start && + g_key_file_get_boolean(ctx->config, "timeSync", + TOOLSOPTION_SYNCTIME_ENABLE, NULL)) { + TimeSyncDoSync(data->slewCorrection, TRUE, data); + } + data->timeSyncState = start; + + /* Now start/stop the loop. */ + if (!TimeSyncStartStopLoop(ctx, data, start)) { + return FALSE; + } + } else if (strcmp(option, TOOLSOPTION_SYNCTIME_SLEWCORRECTION) == 0) { + data->slewCorrection = strcmp(value, "0"); + g_debug("Daemon: Setting slewCorrection, %d.\n", data->slewCorrection); + } else if (strcmp(option, TOOLSOPTION_SYNCTIME_PERCENTCORRECTION) == 0) { + int32 percent; + if (!StrUtil_StrToInt(&percent, value)) { + return FALSE; + } + data->slewPercentCorrection = percent; + } else if (strcmp(option, TOOLSOPTION_SYNCTIME_PERIOD) == 0) { + uint32 period; + + if (!StrUtil_StrToUint(&period, value)) { + return FALSE; + } + + /* + * If the sync loop is running and the time sync period has changed, + * restart the loop with the new period value. If the sync loop is + * not running, just remember the new sync period value. + */ + if (period != data->timeSyncPeriod) { + data->timeSyncPeriod = (period > 0) ? period * 1000 : TIME_SYNC_TIME; + + if (data->timer != NULL) { + TimeSyncStartStopLoop(ctx, data, FALSE); + if (!TimeSyncStartStopLoop(ctx, data, TRUE)) { + g_warning("Unable to change time sync period.\n"); + return FALSE; + } + } + } + } else { + return FALSE; + } + return TRUE; +} + + +/** + * Handles a shutdown callback; cleans up internal plugin state. + * + * @param[in] src The source object. + * @param[in] ctx The app context. + * @param[in] plugin Plugin registration data. + */ + +static void +TimeSyncShutdown(gpointer src, + ToolsAppCtx *ctx, + ToolsPluginData *plugin) +{ + TimeSyncData *data = plugin->_private; + if (data->timer != NULL) { + g_source_destroy(data->timer); + } + g_free(data); +} + + +/** + * Plugin entry point. Initializes internal state and returns the registration + * data. + * + * @param[in] ctx The app context. + * + * @return The registration data. + */ + +TOOLS_MODULE_EXPORT ToolsPluginData * +ToolsOnLoad(ToolsAppCtx *ctx) +{ + static ToolsPluginData regData = { + "timeSync", + NULL, + NULL + }; + + TimeSyncData *data = g_malloc(sizeof (TimeSyncData)); + RpcChannelCallback rpcs[] = { + { "Time_Synchronize", TimeSyncTcloHandler, data, NULL, NULL, 0 } + }; + ToolsPluginSignalCb sigs[] = { + { TOOLS_CORE_SIG_SET_OPTION, TimeSyncSetOption, ®Data }, + { TOOLS_CORE_SIG_SHUTDOWN, TimeSyncShutdown, ®Data } + }; + ToolsAppReg regs[] = { + { TOOLS_APP_GUESTRPC, VMTools_WrapArray(rpcs, sizeof *rpcs, ARRAYSIZE(rpcs)) }, + { TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) } + }; + + data->slewCorrection = TRUE; + data->slewPercentCorrection = PERCENT_CORRECTION; + data->timeSyncState = -1; + data->timeSyncPeriod = TIME_SYNC_TIME; + data->timer = NULL; + + regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs)); + regData._private = data; + + return ®Data; +} + diff --git a/open-vm-tools/services/plugins/vix/COPYING b/open-vm-tools/services/plugins/vix/COPYING new file mode 100644 index 000000000..09f465ab7 --- /dev/null +++ b/open-vm-tools/services/plugins/vix/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/open-vm-tools/services/plugins/vix/Makefile.am b/open-vm-tools/services/plugins/vix/Makefile.am new file mode 100644 index 000000000..3a8287581 --- /dev/null +++ b/open-vm-tools/services/plugins/vix/Makefile.am @@ -0,0 +1,46 @@ +################################################################################ +### Copyright 2009 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 +################################################################################ + +noinst_LTLIBRARIES = libvix.la + +libvix_la_CPPFLAGS = +libvix_la_CPPFLAGS += @PLUGIN_CPPFLAGS@ + +libvix_la_LDFLAGS = +libvix_la_LDFLAGS += @PLUGIN_LDFLAGS@ + +libvix_la_LIBADD = +libvix_la_LIBADD += @VIX_LIBADD@ +libvix_la_LIBADD += @VMTOOLS_LIBS@ +libvix_la_LIBADD += ../hgfsServer/libhgfsServer.la +libvix_la_LIBADD += $(top_builddir)/lib/auth/libAuth.la +libvix_la_LIBADD += $(top_builddir)/lib/foundryMsg/libFoundryMsg.la +libvix_la_LIBADD += $(top_builddir)/lib/impersonate/libImpersonate.la + +libvix_la_SOURCES = +libvix_la_SOURCES += foundryToolsDaemon.c +libvix_la_SOURCES += vixPlugin.c +libvix_la_SOURCES += $(top_srcdir)/lib/vixTools/vixTools.c + +install-exec-local: + mkdir -p $(DESTDIR)$(VMSVC_PLUGIN_INSTALLDIR) + cp -L $(builddir)/.libs/libvix.so $(DESTDIR)$(VMSVC_PLUGIN_INSTALLDIR) +if HAVE_X11 + mkdir -p $(DESTDIR)$(VMUSR_PLUGIN_INSTALLDIR) + cp -L $(builddir)/.libs/libvix.so $(DESTDIR)$(VMUSR_PLUGIN_INSTALLDIR) +endif + diff --git a/open-vm-tools/guestd/foundryToolsDaemon.c b/open-vm-tools/services/plugins/vix/foundryToolsDaemon.c similarity index 100% rename from open-vm-tools/guestd/foundryToolsDaemon.c rename to open-vm-tools/services/plugins/vix/foundryToolsDaemon.c diff --git a/open-vm-tools/guestd/foundryToolsDaemon.h b/open-vm-tools/services/plugins/vix/foundryToolsDaemon.h similarity index 100% rename from open-vm-tools/guestd/foundryToolsDaemon.h rename to open-vm-tools/services/plugins/vix/foundryToolsDaemon.h diff --git a/open-vm-tools/services/plugins/vix/vixPlugin.c b/open-vm-tools/services/plugins/vix/vixPlugin.c new file mode 100644 index 000000000..d236da696 --- /dev/null +++ b/open-vm-tools/services/plugins/vix/vixPlugin.c @@ -0,0 +1,100 @@ +/********************************************************* + * Copyright (C) 2008 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 vixPlugin.c + * + * Tools Service entry point for the VIX plugin. + */ + +#define G_LOG_DOMAIN "vix" + +#include + +#include "vmware.h" +#include "foundryToolsDaemon.h" +#include "syncDriver.h" +#include "vixCommands.h" +#include "vixPluginInt.h" +#include "vmtools.h" + +#if !defined(__APPLE__) +#include "embed_version.h" +#include "vmtoolsd_version.h" +VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING); +#endif + + +/** + * Returns the registration data for either the guestd or userd process. + * + * @param[in] ctx The application context. + * + * @return The registration data. + */ + +TOOLS_MODULE_EXPORT ToolsPluginData * +ToolsOnLoad(ToolsAppCtx *ctx) +{ + static ToolsPluginData regData = { + "vix", + NULL, + NULL + }; + + RpcChannelCallback rpcs[] = { + { VIX_BACKDOORCOMMAND_RUN_PROGRAM, + FoundryToolsDaemonRunProgram, NULL, NULL, NULL, 0 }, + { VIX_BACKDOORCOMMAND_GET_PROPERTIES, + FoundryToolsDaemonGetToolsProperties, NULL, NULL, 0 }, + { VIX_BACKDOORCOMMAND_CHECK_USER_ACCOUNT, + ToolsDaemonTcloCheckUserAccount, NULL, NULL, NULL, 0 }, + { VIX_BACKDOORCOMMAND_SEND_HGFS_PACKET, + ToolsDaemonHgfsImpersonated, NULL, NULL, NULL, 0 }, + { VIX_BACKDOORCOMMAND_COMMAND, + ToolsDaemonTcloReceiveVixCommand, NULL, NULL, 0 }, + { VIX_BACKDOORCOMMAND_MOUNT_VOLUME_LIST, + ToolsDaemonTcloMountHGFS, NULL, NULL, NULL, 0 }, +#if defined(_WIN32) || defined(linux) + { VIX_BACKDOORCOMMAND_SYNCDRIVER_FREEZE, + ToolsDaemonTcloSyncDriverFreeze, NULL, NULL, NULL, 0 }, + { VIX_BACKDOORCOMMAND_SYNCDRIVER_THAW, + ToolsDaemonTcloSyncDriverThaw, NULL, NULL, NULL, 0 } +#endif + }; + ToolsAppReg regs[] = { + { TOOLS_APP_GUESTRPC, VMTools_WrapArray(rpcs, sizeof *rpcs, ARRAYSIZE(rpcs)) } + }; + + FoundryToolsDaemon_Initialize(ctx); + regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs)); + + /* + * If running the user daemon or if the sync driver is not active, remove + * the last two elements of the RPC registration array, so that the sync + * driver RPC commands are ignored. + */ +#if defined(_WIN32) || defined(linux) + if (strcmp(ctx->name, VMTOOLS_GUEST_SERVICE) != 0 || !SyncDriver_Init()) { + g_array_remove_range(regs[0].data, regs[0].data->len - 2, 2); + } +#endif + + return ®Data; +} + diff --git a/open-vm-tools/services/plugins/vix/vixPluginInt.h b/open-vm-tools/services/plugins/vix/vixPluginInt.h new file mode 100644 index 000000000..538148976 --- /dev/null +++ b/open-vm-tools/services/plugins/vix/vixPluginInt.h @@ -0,0 +1,60 @@ +/********************************************************* + * Copyright (C) 2008 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 _VIXPLUGININT_H_ +#define _VIXPLUGININT_H_ + +/** + * @file vixPluginInt.h + * + * Prototypes of VIX RPC handlers found in foundryToolsDaemon.c. + */ + +#define Debug g_debug +#define Warning g_warning + +#include "vmtoolsApp.h" + +Bool +ToolsDaemonTcloCheckUserAccount(RpcInData *data); + +Bool +FoundryToolsDaemonGetToolsProperties(RpcInData *data); + +Bool +ToolsDaemonHgfsImpersonated(RpcInData *data); + +Bool +ToolsDaemonTcloMountHGFS(RpcInData *data); + +Bool +ToolsDaemonTcloReceiveVixCommand(RpcInData *data); + +Bool +FoundryToolsDaemonRunProgram(RpcInData *data); + +#if defined(linux) || defined(_WIN32) +Bool +ToolsDaemonTcloSyncDriverFreeze(RpcInData *data); + +Bool +ToolsDaemonTcloSyncDriverThaw(RpcInData *data); +#endif + +#endif /* _VIXPLUGININT_H_ */ + diff --git a/open-vm-tools/services/plugins/vixUser/COPYING b/open-vm-tools/services/plugins/vixUser/COPYING new file mode 100644 index 000000000..09f465ab7 --- /dev/null +++ b/open-vm-tools/services/plugins/vixUser/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/open-vm-tools/services/plugins/vixUser/Makefile.am b/open-vm-tools/services/plugins/vixUser/Makefile.am new file mode 100644 index 000000000..860c630fb --- /dev/null +++ b/open-vm-tools/services/plugins/vixUser/Makefile.am @@ -0,0 +1,39 @@ +################################################################################ +### Copyright 2009 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 +################################################################################ + +noinst_LTLIBRARIES = libvixUser.la + +libvixUser_la_CPPFLAGS = +libvixUser_la_CPPFLAGS += @PLUGIN_CPPFLAGS@ + +libvixUser_la_LDFLAGS = +libvixUser_la_LDFLAGS += @PLUGIN_LDFLAGS@ + +libvixUser_la_LIBADD = +libvixUser_la_LIBADD += @VIX_LIBADD@ +libvixUser_la_LIBADD += @COMMON_XLIBS@ +libvixUser_la_LIBADD += @VMTOOLS_LIBS@ +libvixUser_la_LIBADD += $(top_builddir)/lib/foundryMsg/libFoundryMsg.la +libvixUser_la_LIBADD += $(top_builddir)/lib/guestApp/libGuestAppX11.la + +libvixUser_la_SOURCES = +libvixUser_la_SOURCES += vixUser.c + +install-exec-local: + mkdir -p $(DESTDIR)$(VMUSR_PLUGIN_INSTALLDIR) + cp -L $(builddir)/.libs/libvixUser.so $(DESTDIR)$(VMUSR_PLUGIN_INSTALLDIR) + diff --git a/open-vm-tools/services/plugins/vixUser/vixUser.c b/open-vm-tools/services/plugins/vixUser/vixUser.c new file mode 100644 index 000000000..c424dd064 --- /dev/null +++ b/open-vm-tools/services/plugins/vixUser/vixUser.c @@ -0,0 +1,379 @@ +/********************************************************* + * Copyright (C) 2008 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 vixUser.c + * + * Tools Service entry point for the VIX userd plugin. + */ + +#define G_LOG_DOMAIN "vixUser" + +#include + +#include "vmware.h" +#include "err.h" +#include "guestApp.h" +#include "printer.h" +#include "str.h" +#include "strutil.h" +#include "vixCommands.h" +#include "vixTools.h" +#include "vmtools.h" +#include "vmtoolsApp.h" +#include "util.h" + +#if defined(_WIN32) +# include "win32u.h" +#endif + +#if !defined(__APPLE__) +#include "embed_version.h" +#include "vmtoolsd_version.h" +VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING); +#endif + +/* + * These constants are a bad hack. I really should generate the result + * strings twice, once to compute the length and then allocate the buffer, + * and a second time to write the buffer. (XXX: From foundryToolsDaemon.c.) + */ +#define DEFAULT_RESULT_MSG_MAX_LENGTH 1024 + + +/** + * Extract a quoted string from the middle of an argument string. + * This is different than normal tokenizing in a few ways: + * - Whitespace is a separator outside quotes, but not inside quotes. + * - Quotes always come in pairs, so "" is am empty string. An empty + * string may appear anywhere in the string, even at the end, so + * a string that is "" contains 1 empty string, not 2. + * - The string may use whitespace to separate the op-name from the params, + * and then quoted params to skip whitespace inside a param. + * + * XXX: this is copied from foundryToolsDaemon.c. It's easier to copy than + * to recompile that file and pull in all its dependencies into this plugin. + * + * @param[in] args + * @param[in] engOfArg + * + * @return An allocated string. + */ + +static char * +ToolsDaemonTcloGetQuotedString(const char *args, + const char **endOfArg) +{ + char *resultStr = NULL; + char *endStr; + g_debug(">ToolsDaemonTcloGetQuotedString\n"); + + while ((*args) && ('\"' != *args)) { + args++; + } + if ('\"' == *args) { + args++; + } + + resultStr = Util_SafeStrdup(args); + + endStr = resultStr; + while (*endStr) { + if (('\\' == *endStr) && (*(endStr + 1))) { + endStr += 2; + } else if ('\"' == *endStr) { + *endStr = 0; + endStr++; + break; + } else { + endStr++; + } + } + + if (NULL != endOfArg) { + args += (endStr - resultStr); + while (' ' == *args) { + args++; + } + *endOfArg = args; + } + + g_debug("ToolsDaemonTcloOpenUrl\n"); + + /* + * Parse the arguments + */ + url = ToolsDaemonTcloGetEncodedQuotedString(data->args, &data->args); + windowState = ToolsDaemonTcloGetQuotedString(data->args, &data->args); + // These parameters at the end are optional, so they may be NULL. + credentialTypeStr = ToolsDaemonTcloGetQuotedString(data->args, &data->args); + obfuscatedNamePassword = ToolsDaemonTcloGetQuotedString(data->args, &data->args); + + /* + * Validate the arguments. + */ + if ((NULL == url) || (NULL == windowState)) { + err = VIX_E_INVALID_ARG; + g_debug("Failed to get string args\n"); + goto abort; + } + + g_debug("Opening URL: \"%s\"\n", url); + + /* Actually open the URL. */ + if (!GuestApp_OpenUrl(url, strcmp(windowState, "maximize") == 0)) { + err = VIX_E_FAIL; + g_debug("Failed to open the url \"%s\"\n", url); + goto abort; + } + +abort: + /* + * All Foundry tools commands return results that start with a + * foundry error and a guest-OS-specific error. + */ + Str_Sprintf(resultBuffer, sizeof resultBuffer, "%"FMT64"d %d", err, sysError); + RPCIN_SETRETVALS(data, resultBuffer, TRUE); + + /* + * These were allocated by ToolsDaemonTcloGetQuotedString. + */ + free(url); + free(windowState); + free(credentialTypeStr); + free(obfuscatedNamePassword); + + g_debug("ToolsDaemonTcloSetPrinter\n"); + + /* + * Parse the arguments + */ + printerName = ToolsDaemonTcloGetQuotedString(data->args, &data->args); + defaultString = ToolsDaemonTcloGetQuotedString(data->args, &data->args); + + /* + * Validate the arguments. + */ + if ((NULL == printerName) || (NULL == defaultString)) { + err = VIX_E_INVALID_ARG; + g_debug("Failed to get string args\n"); + goto abort; + } + + if (!StrUtil_StrToInt(&defaultInt, defaultString)) { + err = VIX_E_INVALID_ARG; + g_debug("Failed to convert int arg\n"); + goto abort; + } + + g_debug("Setting printer to: \"%s\", %ssetting as default\n", + printerName, (defaultInt != 0) ? "" : "not "); + + /* Actually set the printer. */ + if (!Printer_AddConnection(printerName, &sysError)) { + err = VIX_E_FAIL; + g_debug("Failed to add printer %s : %d %s\n", printerName, sysError, + Err_Errno2String(sysError)); + goto abort; + } + + /* Set this printer as the default if requested. */ + if (defaultInt != 0) { + if (!Win32U_SetDefaultPrinter(printerName)) { + /* + * We couldn't set this printer as default. Oh well. We'll + * still report success or failure based purely on whether + * the actual printer add succeeded or not. + */ + g_debug("Unable to set \"%s\" as the default printer\n", + printerName); + } + } + +abort: + /* + * All Foundry tools commands return results that start with a + * foundry error and a guest-OS-specific error. + */ + Str_Sprintf(resultBuffer, sizeof resultBuffer, "%"FMT64"d %d", err, sysError); + RPCIN_SETRETVALS(data, resultBuffer, TRUE); + + /* + * These were allocated by ToolsDaemonTcloGetQuotedString. + */ + free(printerName); + free(defaultString); + + g_debug(" + Copyright (C) + + This library 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; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/open-vm-tools/services/plugins/vmbackup/Makefile.am b/open-vm-tools/services/plugins/vmbackup/Makefile.am new file mode 100644 index 000000000..6aab916d6 --- /dev/null +++ b/open-vm-tools/services/plugins/vmbackup/Makefile.am @@ -0,0 +1,37 @@ +################################################################################ +### Copyright 2009 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 +################################################################################ + +noinst_LTLIBRARIES = libvmbackup.la + +libvmbackup_la_CPPFLAGS = +libvmbackup_la_CPPFLAGS += @PLUGIN_CPPFLAGS@ + +libvmbackup_la_LDFLAGS = +libvmbackup_la_LDFLAGS += @PLUGIN_LDFLAGS@ + +libvmbackup_la_LIBADD = +libvmbackup_la_LIBADD += @VMTOOLS_LIBS@ + +libvmbackup_la_SOURCES = +libvmbackup_la_SOURCES += scriptOps.c +libvmbackup_la_SOURCES += stateMachine.c +libvmbackup_la_SOURCES += syncDriverOps.c + +install-exec-local: + mkdir -p $(DESTDIR)$(VMSVC_PLUGIN_INSTALLDIR) + cp -L $(builddir)/.libs/libvmbackup.so $(DESTDIR)$(VMSVC_PLUGIN_INSTALLDIR) + diff --git a/open-vm-tools/services/plugins/vmbackup/README b/open-vm-tools/services/plugins/vmbackup/README new file mode 100644 index 000000000..763d658a4 --- /dev/null +++ b/open-vm-tools/services/plugins/vmbackup/README @@ -0,0 +1,42 @@ +This is an attempt to describe the VmBackup state machine using ASCII diagrams. + +=== Main State Machine === + +The main state machine, implemented in stateMachine.c, works as follows: + + -------------> IDLE <-----------| + | | | + | b, e | a | + | | | + | e \/ | + SCRIPT_ERROR <--- SCRIPT_FREEZE | + ^ | | + | d | b | + | e \/ | b, e + SYNC_ERROR <----- SYNC_FREEZE | + ^ | | + | | c | + | e \/ | + |----------- SYNC_THAW | + | | + | d | + \/ | + SCRIPT_THAW -------- + +The transitions mean the following events / conditions: + +a. vmbackup.start RPC +b. vmbackup operation is finished +c. vmbackup.snapshotDone RPC +d. sync provider operation is finished +e. error condition: runtime error, or vmbackup.abort RPC + +Sending a vmbackup.start RPC while the state machine is not IDLE causes an +error to be returned to the client, but the state machine is not changed. +Same behavior occurs for multiple vmbackup.snapshotDone RPCs. Multiple +vmbackup.abort messages are ignored. + +Transitions to IDLE cause the backup operation to be finalized, and a "done" +event to be sent to the VMX. Transitions from IDLE cause a "reset" event to be +sent to the VMX. + diff --git a/open-vm-tools/services/plugins/vmbackup/scriptOps.c b/open-vm-tools/services/plugins/vmbackup/scriptOps.c new file mode 100644 index 000000000..e4ac10633 --- /dev/null +++ b/open-vm-tools/services/plugins/vmbackup/scriptOps.c @@ -0,0 +1,485 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + +/* + * scriptOps.c -- + * + * Functions for handling freeze/thaw scripts. + */ + +#include "vmBackupInt.h" + +#include +#include + +#include "vm_basic_defs.h" +#include "file.h" +#include "guestApp.h" +#include "procMgr.h" +#include "str.h" +#include "util.h" + + +typedef struct VmBackupScript { + char *path; + ProcMgr_AsyncProc *proc; +} VmBackupScript; + + +typedef struct VmBackupScriptOp { + VmBackupOp callbacks; + Bool canceled; + Bool thawFailed; + VmBackupScriptType type; + VmBackupState *state; +} VmBackupScriptOp; + + +/* + *----------------------------------------------------------------------------- + * + * VmBackupGetScriptPath -- + * + * Returns the path where the scripts to be executed reside. + * + * Result + * A string with the requested path. + * + * Side effects: + * Allocates memory for the path. + * + *----------------------------------------------------------------------------- + */ + +char * +VmBackupGetScriptPath(void) +{ + char *scriptPath = NULL; + char *installPath = GuestApp_GetInstallPath(); + + if (installPath == NULL) { + return NULL; + } + + scriptPath = Str_Asprintf(NULL, "%s%s%s", installPath, DIRSEPS, "backupScripts.d"); + free(installPath); + + return scriptPath; +} + + +/* + *----------------------------------------------------------------------------- + * + * VmBackupRunNextScript -- + * + * Runs the next script for the given operation. If thawing (or running + * scripts after a failure), this function will try as much as possible + * to start a script, meaning that if it fails to start a script it will + * try to start the preceding one until one script is run, or it runs out + * of scripts to try. + * + * Results: + * -1: an error occurred. + * 0: no more scripts to run. + * 1: script was started. + * + * Side effects: + * Increments (or decrements) the "current script" index in the backup state. + * + *----------------------------------------------------------------------------- + */ + +static int +VmBackupRunNextScript(VmBackupScriptOp *op) // IN/OUT +{ + const char *scriptOp; + int ret = 0; + ssize_t index; + VmBackupScript *scripts = op->state->scripts; + + switch (op->type) { + case VMBACKUP_SCRIPT_FREEZE: + index = ++op->state->currentScript; + scriptOp = "freeze"; + break; + + case VMBACKUP_SCRIPT_FREEZE_FAIL: + index = --op->state->currentScript; + scriptOp = "freezeFail"; + break; + + case VMBACKUP_SCRIPT_THAW: + index = --op->state->currentScript; + scriptOp = "thaw"; + break; + + default: + NOT_REACHED(); + } + + while (index >= 0 && scripts[index].path != NULL) { + char *cmd; + + cmd = Str_Asprintf(NULL, "\"%s\" %s", scripts[index].path, scriptOp); + if (cmd == NULL) { + index = --op->state->currentScript; + op->thawFailed = TRUE; + } + + if ((scripts[index].proc = ProcMgr_ExecAsync(cmd, NULL)) == NULL) { + if (op->type == VMBACKUP_SCRIPT_FREEZE) { + ret = -1; + break; + } else { + index = --op->state->currentScript; + op->thawFailed = TRUE; + } + } else { + ret = 1; + break; + } + + /* This happens if all thaw/fail scripts failed to start. */ + if (index == -1 && scripts[0].proc == NULL) { + ret = -1; + } + } + + return ret; +} + + +/* + *----------------------------------------------------------------------------- + * + * VmBackupStringCompare -- + * + * Comparison function used to sort the script list in ascending order. + * + * Result + * The result of strcmp(str1, str2). + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static int +VmBackupStringCompare(const void *str1, // IN + const void *str2) // IN +{ + return strcmp(* (char * const *) str1,* (char * const *) str2); +} + + +/* + *----------------------------------------------------------------------------- + * + * VmBackupScriptOpQuery -- + * + * Checks the status of the current running script. If the script is + * finished, run the next script in the queue or, if no scripts are left, + * return a "finished" status. + * + * Result + * The status of the operation. + * + * Side effects: + * Might start a new process. + * + *----------------------------------------------------------------------------- + */ + +static VmBackupOpStatus +VmBackupScriptOpQuery(VmBackupOp *_op) // IN +{ + VmBackupOpStatus ret = VMBACKUP_STATUS_PENDING; + VmBackupScriptOp *op = (VmBackupScriptOp *) _op; + VmBackupScript *scripts = op->state->scripts; + VmBackupScript *currScript = NULL; + + if (scripts != NULL && op->state->currentScript >= 0) { + currScript = &scripts[op->state->currentScript]; + } + + if (op->canceled) { + ret = VMBACKUP_STATUS_CANCELED; + goto exit; + } else if (scripts == NULL || currScript == NULL || currScript->proc == NULL) { + ret = VMBACKUP_STATUS_FINISHED; + goto exit; + } + + if (!ProcMgr_IsAsyncProcRunning(currScript->proc)) { + int exitCode; + Bool succeeded; + + succeeded = (ProcMgr_GetExitCode(currScript->proc, &exitCode) == 0 && + exitCode == 0); + ProcMgr_Free(currScript->proc); + currScript->proc = NULL; + + /* + * If thaw scripts fail, keep running and only notify the failure after + * all others have run. + */ + if (!succeeded) { + if (op->type == VMBACKUP_SCRIPT_FREEZE) { + ret = VMBACKUP_STATUS_ERROR; + goto exit; + } else if (op->type == VMBACKUP_SCRIPT_THAW) { + op->thawFailed = TRUE; + } + } + + switch (VmBackupRunNextScript(op)) { + case -1: + ret = VMBACKUP_STATUS_ERROR; + break; + + case 0: + ret = op->thawFailed ? VMBACKUP_STATUS_ERROR : VMBACKUP_STATUS_FINISHED; + break; + + default: + break; + } + } + +exit: + if (ret == VMBACKUP_STATUS_ERROR) { + /* Report the script error to the host */ + VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ERROR, + VMBACKUP_SCRIPT_ERROR, + "Custom script failed."); + } + return ret; +} + + +/* + *----------------------------------------------------------------------------- + * + * VmBackupScriptOpRelease -- + * + * Frees memory allocated for the state object. Behavior is undefined + * if the memory is freed before the query function says the operation + * if finished. + * + * Result + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static void +VmBackupScriptOpRelease(VmBackupOp *_op) // IN +{ + size_t i; + VmBackupScriptOp *op = (VmBackupScriptOp *) _op; + + if (op->type != VMBACKUP_SCRIPT_FREEZE && op->state->scripts != NULL) { + VmBackupScript *scripts = op->state->scripts; + for (i = 0; scripts[i].path != NULL; i++) { + free(scripts[i].path); + if (scripts[i].proc != NULL) { + ProcMgr_Free(scripts[i].proc); + } + } + free(op->state->scripts); + op->state->scripts = NULL; + op->state->currentScript = 0; + } + + free(op); +} + + +/* + *----------------------------------------------------------------------------- + * + * VmBackupScriptOpCancel -- + * + * Cancels the current operation. Kills any currently running script and + * flags the operation as canceled. + * + * Result + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static void +VmBackupScriptOpCancel(VmBackupOp *_op) // IN +{ + VmBackupScriptOp *op = (VmBackupScriptOp *) _op; + VmBackupScript *scripts = op->state->scripts; + VmBackupScript *currScript = NULL; + ProcMgr_Pid pid; + + if (scripts != NULL) { + currScript = &scripts[op->state->currentScript]; + ASSERT(currScript->proc != NULL); + + pid = ProcMgr_GetPid(currScript->proc); + if (!ProcMgr_KillByPid(pid)) { + // XXX: what to do in this situation? other than log and cry? + } else { + int exitCode; + ProcMgr_GetExitCode(currScript->proc, &exitCode); + } + } + + op->canceled = TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * VmBackupNewScriptOp -- + * + * Creates a new state object to monitor the execution of OnFreeze or + * OnThaw scripts. This will identify all the scripts in the backup scripts + * directory and add them to an execution queue. + * + * Note: there is some state created when instantianting the "OnFreeze" + * scripts which is only released after the "OnThaw" scripts are run. So + * the caller has to make sure that thaw (or fail) scripts are run every + * time the freeze scripts are run. + * + * Result + * A pointer to the operation state, or NULL on failure. + * + * Side effects: + * If there are scripts to be executed, the first one is started. + * + *----------------------------------------------------------------------------- + */ + +VmBackupOp * +VmBackup_NewScriptOp(VmBackupScriptType type, // IN + VmBackupState *state) // IN +{ + Bool fail = FALSE; + char **fileList = NULL; + char *scriptDir = NULL; + int numFiles = 0; + size_t i; + VmBackupScriptOp *op = NULL; + + scriptDir = VmBackupGetScriptPath(); + if (scriptDir == NULL) { + goto exit; + } + + op = calloc(1, sizeof *op); + if (op == NULL) { + goto exit; + } + + op->state = state; + op->type = type; + op->callbacks.queryFn = VmBackupScriptOpQuery; + op->callbacks.cancelFn = VmBackupScriptOpCancel; + op->callbacks.releaseFn = VmBackupScriptOpRelease; + + g_debug("Trying to run scripts from %s\n", scriptDir); + + /* + * Load the list of scripts to run when freezing. The same list will be + * used later in case of failure, or when thawing, in reverse order. + * + * This logic won't recurse into directories, so only files directly under + * the script dir will be considered. + */ + if (type == VMBACKUP_SCRIPT_FREEZE && File_IsDirectory(scriptDir)) { + size_t scriptCount = 0; + + state->scripts = NULL; + state->currentScript = 0; + numFiles = File_ListDirectory(scriptDir, &fileList); + + if (numFiles > 0) { + VmBackupScript *scripts; + + scripts = calloc(1, (numFiles + 1) * sizeof *scripts); + if (scripts == NULL) { + fail = TRUE; + goto exit; + } + + state->scripts = scripts; + + /* + * VmBackupRunNextScript increments the index, so need to make it point + * to "before the first script". + */ + state->currentScript = -1; + + if (numFiles > 1) { + qsort(fileList, (size_t) numFiles, sizeof *fileList, VmBackupStringCompare); + } + + for (i = 0; i < numFiles; i++) { + char *script; + + script = Str_Asprintf(NULL, "%s%c%s", scriptDir, DIRSEPC, fileList[i]); + if (script == NULL) { + fail = TRUE; + goto exit; + } else if (File_IsFile(script)) { + scripts[scriptCount++].path = script; + } else { + free(script); + } + } + } + } + + /* + * If there are any scripts to be executed, start the first one. If we get to + * this point, we won't free the scripts array until VmBackupScriptOpRelease + * is called after thawing (or after the sync provider failed and the "fail" + * scripts are run). + */ + fail = (state->scripts != NULL && VmBackupRunNextScript(op) == -1); + +exit: + /* Free the file list. */ + for (i = 0; i < numFiles; i++) { + free(fileList[i]); + } + free(fileList); + + if (fail && op != NULL) { + VmBackup_Release((VmBackupOp *) op); + op = NULL; + } + free(scriptDir); + return (VmBackupOp *) op; +} + diff --git a/open-vm-tools/services/plugins/vmbackup/stateMachine.c b/open-vm-tools/services/plugins/vmbackup/stateMachine.c new file mode 100644 index 000000000..c18ab245a --- /dev/null +++ b/open-vm-tools/services/plugins/vmbackup/stateMachine.c @@ -0,0 +1,649 @@ +/********************************************************* + * Copyright (C) 2007 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 stateMachine.c + * + * Implements a generic state machine for executing backup operations + * asynchronously. Since VSS is based on an asynchronous polling model, + * we're basing all backup operations on a similar model controlled by this + * state machine, even if it would be more eficient to use an event-driven + * approach in some cases. + * + * For a description of the state machine, check the README file. + * + * The sync provider state machine depends on the particular implementation. + * For the sync driver, it enables the driver and waits for a "snapshot done" + * message before finishing. For the VSS subsystem, the sync provider just + * implements a VSS backup cycle. + */ + +#include "vmBackupInt.h" + +#include +#include "guestApp.h" +#include "str.h" +#include "strutil.h" +#include "util.h" +#include "vmtools.h" + +#if !defined(__APPLE__) +#include "embed_version.h" +#include "vmtoolsd_version.h" +VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING); +#endif + + +#define VMBACKUP_ENQUEUE_EVENT() do { \ + gBackupState->timerEvent = g_timeout_source_new(gBackupState->pollPeriod); \ + VMTOOLSAPP_ATTACH_SOURCE(gBackupState->ctx, \ + gBackupState->timerEvent, \ + VmBackupAsyncCallback, \ + NULL, \ + NULL); \ +} while (0) + +static VmBackupState *gBackupState = NULL; +static VmBackupSyncProvider *gSyncProvider = NULL; + +static Bool +VmBackupEnableSync(void); + + +/** + * Sends a keep alive backup event to the VMX. + * + * @param[in] clientData Unused. + * + * @return FALSE + */ + +static gboolean +VmBackupKeepAliveCallback(void *clientData) +{ + g_debug("*** %s\n", __FUNCTION__); + ASSERT(gBackupState != NULL); + g_source_unref(gBackupState->keepAlive); + gBackupState->keepAlive = NULL; + VmBackup_SendEvent(VMBACKUP_EVENT_KEEP_ALIVE, 0, ""); + return FALSE; +} + + +/** + * Sends a command to the VMX asking it to update VMDB about a new backup event. + * This will restart the keep-alive timer. + * + * @param[in] event The event to set. + * @param[in] code Error code. + * @param[in] dest Error description. + * + * @return TRUE on success. + */ + +Bool +VmBackup_SendEvent(const char *event, + const uint32 code, + const char *desc) +{ + Bool success; + char *result; + size_t resultLen; + gchar *msg; + + ASSERT(gBackupState != NULL); + + g_debug("*** %s\n", __FUNCTION__); + if (gBackupState->keepAlive != NULL) { + g_source_destroy(gBackupState->keepAlive); + } + + msg = g_strdup_printf(VMBACKUP_PROTOCOL_EVENT_SET" %s %u %s", event, code, desc); + success = RpcChannel_Send(gBackupState->ctx->rpc, msg, strlen(msg) + 1, + &result, &resultLen); + + if (!success) { + g_warning("Failed to send event to the VMX: %s.\n", result); + } + + gBackupState->keepAlive = g_timeout_source_new(VMBACKUP_KEEP_ALIVE_PERIOD / 2); + VMTOOLSAPP_ATTACH_SOURCE(gBackupState->ctx, + gBackupState->keepAlive, + VmBackupKeepAliveCallback, + NULL, + NULL); + return success; +} + + +/** + * Cleans up the backup state object and sends a "done" event to the VMX. + */ + +static void +VmBackupFinalize(void) +{ + g_debug("*** %s\n", __FUNCTION__); + g_assert(gBackupState != NULL); + g_assert(gBackupState->machineState == VMBACKUP_MSTATE_IDLE); + + if (gBackupState->currentOp != NULL) { + VmBackup_Cancel(gBackupState->currentOp); + VmBackup_Release(gBackupState->currentOp); + } + + VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_DONE, VMBACKUP_SUCCESS, ""); + + if (gBackupState->timerEvent != NULL) { + g_source_destroy(gBackupState->timerEvent); + } + + if (gBackupState->keepAlive != NULL) { + g_source_destroy(gBackupState->keepAlive); + } + + free(gBackupState->volumes); + free(gBackupState); + gBackupState = NULL; +} + + +/** + * Starts the execution of the scripts for the given action type. + * + * @param[in] type Type of scripts being started. + * + * @return TRUE, unless starting the scripts fails for some reason. + */ + +static Bool +VmBackupStartScripts(VmBackupScriptType type) +{ + const char *opName; + VmBackupMState nextState; + g_debug("*** %s\n", __FUNCTION__); + + switch (type) { + case VMBACKUP_SCRIPT_FREEZE: + opName = "VmBackupOnFreeze"; + nextState = VMBACKUP_MSTATE_SCRIPT_FREEZE; + break; + + case VMBACKUP_SCRIPT_FREEZE_FAIL: + opName = "VmBackupOnFreezeFail"; + nextState = VMBACKUP_MSTATE_SCRIPT_ERROR; + break; + + case VMBACKUP_SCRIPT_THAW: + opName = "VmBackupOnThaw"; + nextState = VMBACKUP_MSTATE_SCRIPT_THAW; + break; + + default: + NOT_REACHED(); + } + + if (!VmBackup_SetCurrentOp(gBackupState, + VmBackup_NewScriptOp(type, gBackupState), + NULL, + opName)) { + VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ERROR, + VMBACKUP_SCRIPT_ERROR, + "Error when starting backup scripts."); + return FALSE; + } + + gBackupState->machineState = nextState; + return TRUE; +} + + +/** + * Puts the state machine in the right state when an error occurs. The caller + * should check for the state of the backup upon this function returning: if + * it's IDLE, it means the backup state should be cleaned up. + * + * @return Whether the backup operation should be finalized. + */ + +static gboolean +VmBackupOnError(void) +{ + switch (gBackupState->machineState) { + case VMBACKUP_MSTATE_SCRIPT_FREEZE: + case VMBACKUP_MSTATE_SYNC_ERROR: + /* Next state is "script error". */ + if (!VmBackupStartScripts(VMBACKUP_SCRIPT_FREEZE_FAIL)) { + gBackupState->machineState = VMBACKUP_MSTATE_IDLE; + } + break; + + case VMBACKUP_MSTATE_SYNC_FREEZE: + case VMBACKUP_MSTATE_SYNC_THAW: + /* Next state is "sync error". */ + gBackupState->pollPeriod = 1000; + gBackupState->machineState = VMBACKUP_MSTATE_SYNC_ERROR; + break; + + case VMBACKUP_MSTATE_SCRIPT_THAW: + /* Next state is "idle". */ + gBackupState->machineState = VMBACKUP_MSTATE_IDLE; + break; + + default: + g_error("Unexpected machine state on error: %d\n", gBackupState->machineState); + } + + return (gBackupState->machineState == VMBACKUP_MSTATE_IDLE); +} + + +/** + * Callback that checks for the status of the current operation. Calls the + * queued operations as needed. + * + * @param[in] clientData Unused. + * + * @return FALSE + */ + +static gboolean +VmBackupAsyncCallback(void *clientData) +{ + g_debug("*** %s\n", __FUNCTION__); + g_assert(gBackupState != NULL); + + g_source_unref(gBackupState->timerEvent); + gBackupState->timerEvent = NULL; + + if (gBackupState->currentOp != NULL) { + VmBackupOpStatus status; + + g_debug("VmBackupAsyncCallback: checking %s\n", gBackupState->currentOpName); + status = VmBackup_QueryStatus(gBackupState->currentOp); + + switch (status) { + case VMBACKUP_STATUS_PENDING: + goto exit; + + case VMBACKUP_STATUS_FINISHED: + g_debug("Async request completed\n"); + VmBackup_Release(gBackupState->currentOp); + gBackupState->currentOp = NULL; + break; + + default: + { + Bool freeMsg = TRUE; + char *errMsg = Str_Asprintf(NULL, + "Asynchronous operation failed: %s", + gBackupState->currentOpName); + if (errMsg == NULL) { + freeMsg = FALSE; + errMsg = "Asynchronous operation failed."; + } + VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ERROR, + VMBACKUP_UNEXPECTED_ERROR, + errMsg); + if (freeMsg) { + free(errMsg); + } + + VmBackup_Release(gBackupState->currentOp); + gBackupState->currentOp = NULL; + VmBackupOnError(); + goto exit; + } + } + } + + /* + * Keep calling the registered callback until it's either NULL, or + * an asynchronous operation is scheduled. + */ + while (gBackupState->callback != NULL) { + VmBackupCallback cb = gBackupState->callback; + gBackupState->callback = NULL; + + if (cb(gBackupState)) { + if (gBackupState->currentOp != NULL || gBackupState->forceRequeue) { + goto exit; + } + } else { + VmBackupOnError(); + goto exit; + } + } + + /* + * At this point, the current operation can be declared finished, and the + * state machine can move to the next state. + */ + switch (gBackupState->machineState) { + case VMBACKUP_MSTATE_SCRIPT_FREEZE: + /* Next state is "sync freeze". */ + if (!VmBackupEnableSync()) { + VmBackupOnError(); + } + break; + + case VMBACKUP_MSTATE_SYNC_FREEZE: + /* + * The SYNC_FREEZE -> SYNC_THAW transition is handled by the RPC callback, + * so this case is a no-op. + */ + break; + + case VMBACKUP_MSTATE_SYNC_THAW: + /* Next state is "script thaw". */ + if (!VmBackupStartScripts(VMBACKUP_SCRIPT_THAW)) { + VmBackupOnError(); + } + break; + + case VMBACKUP_MSTATE_SCRIPT_ERROR: + case VMBACKUP_MSTATE_SCRIPT_THAW: + /* Next state is "idle". */ + gBackupState->machineState = VMBACKUP_MSTATE_IDLE; + break; + + case VMBACKUP_MSTATE_SYNC_ERROR: + /* Next state is "script error". */ + if (!VmBackupStartScripts(VMBACKUP_SCRIPT_FREEZE_FAIL)) { + VmBackupOnError(); + } + break; + + default: + g_error("Unexpected machine state: %d\n", gBackupState->machineState); + } + +exit: + /* If the state machine is back in IDLE, it means the backup operation finished. */ + if (gBackupState->machineState == VMBACKUP_MSTATE_IDLE) { + VmBackupFinalize(); + } else { + gBackupState->forceRequeue = FALSE; + VMBACKUP_ENQUEUE_EVENT(); + } + return FALSE; +} + + +/** + * Calls the sync provider's start function. + * + * @param[in] state The backup state. + * + * @return Whether the provider's start callback was successful. + */ + +static Bool +VmBackupEnableSync(void) +{ + g_debug("*** %s\n", __FUNCTION__); + if (!gSyncProvider->start(gBackupState, gSyncProvider->clientData)) { + VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ERROR, + VMBACKUP_SYNC_ERROR, + "Error when enabling the sync provider."); + return FALSE; + } + + gBackupState->machineState = VMBACKUP_MSTATE_SYNC_FREEZE; + return TRUE; +} + + +/* RpcIn callbacks. */ + + +/** + * Handler for the "vmbackup.start" message. Starts the "freeze" scripts + * unless there's another backup operation going on or some other + * unexpected error occurs. + * + * @param[in] data RPC data. + * + * @return TRUE on success. + */ + +static Bool +VmBackupStart(RpcInData *data) +{ + g_debug("*** %s\n", __FUNCTION__); + if (gBackupState != NULL) { + return RPCIN_SETRETVALS(data, "Backup operation already in progress.", FALSE); + } + + gBackupState = Util_SafeMalloc(sizeof *gBackupState); + memset(gBackupState, 0, sizeof *gBackupState); + + gBackupState->ctx = data->appCtx; + gBackupState->pollPeriod = 1000; + gBackupState->machineState = VMBACKUP_MSTATE_IDLE; + + if (data->argsSize > 0) { + int generateManifests = 0; + int index = 0; + + if (StrUtil_GetNextIntToken(&generateManifests, &index, data->args, " ")) { + gBackupState->generateManifests = generateManifests; + } + + if (data->args[index] != '\0') { + gBackupState->volumes = Util_SafeStrdup(data->args + index); + } + } + + gBackupState->configDir = GuestApp_GetConfPath(); + if (gBackupState->configDir == NULL) { + free(gBackupState); + gBackupState = NULL; + return RPCIN_SETRETVALS(data, "Error getting configuration directory.", FALSE); + } + + VmBackup_SendEvent(VMBACKUP_EVENT_RESET, VMBACKUP_SUCCESS, ""); + + if (!VmBackupStartScripts(VMBACKUP_SCRIPT_FREEZE)) { + free(gBackupState); + gBackupState = NULL; + return RPCIN_SETRETVALS(data, "Error initializing backup.", FALSE); + } + + VMBACKUP_ENQUEUE_EVENT(); + return RPCIN_SETRETVALS(data, "", TRUE); +} + + +/** + * Aborts the current operation if one is active, and stops the backup + * process. If the sync provider has been activated, tell it to abort + * the ongoing operation. + * + * @param[in] data RPC data. + * + * @return TRUE + */ + +static Bool +VmBackupAbort(RpcInData *data) +{ + g_debug("*** %s\n", __FUNCTION__); + if (gBackupState == NULL) { + return RPCIN_SETRETVALS(data, "Error: no backup in progress", FALSE); + } else if (gBackupState->machineState != VMBACKUP_MSTATE_SCRIPT_ERROR && + gBackupState->machineState != VMBACKUP_MSTATE_SYNC_ERROR) { + /* Mark the current operation as cancelled. */ + if (gBackupState->currentOp != NULL) { + VmBackup_Cancel(gBackupState->currentOp); + VmBackup_Release(gBackupState->currentOp); + gBackupState->currentOp = NULL; + } + + /* If the sync provider is engaged, signal that it should stop. */ + if (gBackupState->machineState == VMBACKUP_MSTATE_SYNC_FREEZE || + gBackupState->machineState == VMBACKUP_MSTATE_SYNC_THAW) { + gSyncProvider->abort(gBackupState, gSyncProvider->clientData); + } + + VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ABORT, + VMBACKUP_REMOTE_ABORT, + "Remote abort."); + + /* Transition to the error state. */ + if (VmBackupOnError()) { + VmBackupFinalize(); + } + } + return RPCIN_SETRETVALS(data, "", TRUE); +} + + +/** + * Notifies the sync provider to thaw the file systems and puts the state + * machine in the SYNC_THAW state. + * + * @param[in] data RPC data. + * + * @return TRUE + */ + +static Bool +VmBackupSnapshotDone(RpcInData *data) +{ + g_debug("*** %s\n", __FUNCTION__); + if (gBackupState == NULL) { + return RPCIN_SETRETVALS(data, "Error: no backup in progress", FALSE); + } else if (gBackupState->machineState != VMBACKUP_MSTATE_SYNC_FREEZE) { + return RPCIN_SETRETVALS(data, + "Error: unexpected state for snapshot done message.", + FALSE); + } else { + if (!gSyncProvider->snapshotDone(gBackupState, gSyncProvider->clientData)) { + VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ERROR, + VMBACKUP_SYNC_ERROR, + "Error when notifying the sync provider."); + if (VmBackupOnError()) { + VmBackupFinalize(); + } + } else { + gBackupState->machineState = VMBACKUP_MSTATE_SYNC_THAW; + } + return RPCIN_SETRETVALS(data, "", TRUE); + } +} + + +/** + * Reset callback. Currently does nothing. + * + * @param[in] src The source object. + * @param[in] ctx Unused. + * @param[in] data Unused. + */ + +static void +VmBackupReset(gpointer src, + ToolsAppCtx *ctx, + gpointer data) +{ +} + + +/** + * Cleans up the plugin. + * + * @param[in] src The source object. + * @param[in] ctx The app context. + * @param[in] data Unused. + */ + +static void +VmBackupShutdown(gpointer src, + ToolsAppCtx *ctx, + gpointer data) +{ + g_debug("*** %s\n", __FUNCTION__); + if (gBackupState != NULL) { + VmBackupFinalize(); + } + + gSyncProvider->release(gSyncProvider); + gSyncProvider = NULL; +} + + +/** + * Plugin entry point. Initializes internal plugin state. + * + * @param[in] ctx The app context. + * + * @return The registration data. Returns NULL if there is no sync provider + * available to quiesce the filesystems. + */ + +TOOLS_MODULE_EXPORT ToolsPluginData * +ToolsOnLoad(ToolsAppCtx *ctx) +{ + static ToolsPluginData regData = { + "vmbackup", + NULL, + NULL + }; + + /* + * For Win32, first check whether we have VSS support available. Otherwise, + * check if there is a sync driver installed. If nothing is found, return + * NULL, since we don't want to register any handlers. + */ + + VmBackupSyncProvider *provider = NULL; + +#if defined(G_PLATFORM_WIN32) + if (ToolsCore_InitializeCOM(ctx)) { + provider = VmBackup_NewVssProvider(); + } +#endif + + if (provider == NULL) { + provider = VmBackup_NewSyncDriverProvider(); + } + + if (provider != NULL) { + RpcChannelCallback rpcs[] = { + { VMBACKUP_PROTOCOL_START, VmBackupStart, NULL, NULL, NULL, 0 }, + { VMBACKUP_PROTOCOL_ABORT, VmBackupAbort, NULL, NULL, NULL, 0 }, + { VMBACKUP_PROTOCOL_SNAPSHOT_DONE, VmBackupSnapshotDone, NULL, NULL, NULL, 0 } + }; + ToolsPluginSignalCb sigs[] = { + { TOOLS_CORE_SIG_RESET, VmBackupReset, NULL }, + { TOOLS_CORE_SIG_SHUTDOWN, VmBackupShutdown, NULL }, + }; + ToolsAppReg regs[] = { + { TOOLS_APP_GUESTRPC, VMTools_WrapArray(rpcs, sizeof *rpcs, ARRAYSIZE(rpcs)) }, + { TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) }, + }; + + gSyncProvider = provider; + regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs)); + return ®Data; + } + + return NULL; +} + diff --git a/open-vm-tools/services/plugins/vmbackup/syncDriverOps.c b/open-vm-tools/services/plugins/vmbackup/syncDriverOps.c new file mode 100644 index 000000000..5acb2a426 --- /dev/null +++ b/open-vm-tools/services/plugins/vmbackup/syncDriverOps.c @@ -0,0 +1,381 @@ +/********************************************************* + * Copyright (C) 2007 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. + * + *********************************************************/ + +/* + * syncDriverOps.c -- + * + * Implements the sync driver provider for the backup state machine. + */ + +#include "vmBackupInt.h" + +#include "file.h" +#include "procMgr.h" +#include "syncDriver.h" +#include "util.h" + +#ifdef _WIN32 +#include +#include +#endif + +typedef struct VmBackupDriverOp { + VmBackupOp callbacks; + const char *volumes; + Bool freeze; + Bool canceled; + SyncDriverHandle syncHandle; +} VmBackupDriverOp; + + +/* + *----------------------------------------------------------------------------- + * + * VmBackupDriverOpQuery -- + * + * Checks the status of the app that is enabling or disabling the + * sync driver. + * + * Result + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static VmBackupOpStatus +VmBackupDriverOpQuery(VmBackupOp *_op) // IN +{ + VmBackupDriverOp *op = (VmBackupDriverOp *) _op; + VmBackupOpStatus ret; + + SyncDriverStatus st; + + st = SyncDriver_QueryStatus(op->syncHandle, 0); + g_debug("SyncDriver status: %d\n", st); + + switch(st) { + case SYNCDRIVER_BUSY: + ret = VMBACKUP_STATUS_PENDING; + break; + + case SYNCDRIVER_IDLE: + if (op->freeze && op->canceled) { + SyncDriver_Thaw(op->syncHandle); + } + ret = (op->canceled) ? VMBACKUP_STATUS_CANCELED : VMBACKUP_STATUS_FINISHED; + break; + + default: + ret = VMBACKUP_STATUS_ERROR; + break; + } + + return ret; +} + + +/* + *----------------------------------------------------------------------------- + * + * VmBackupDriverOpRelease -- + * + * Cleans up data held by the state object. + * + * Result + * The status of the app. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static void +VmBackupDriverOpRelease(VmBackupOp *_op) // IN +{ + VmBackupDriverOp *op = (VmBackupDriverOp *) _op; + if (!op->freeze) { + SyncDriver_CloseHandle(&op->syncHandle); + } + free(op); +} + + +/* + *----------------------------------------------------------------------------- + * + * VmBackupDriverOpCancel -- + * + * Cancel an ongoing sync driver operation. This doesn't actually + * cancel the operation, but rather waits for it to finish, since + * just killing the worker thread might have undesired side effects. + * This will not cancel thaw operations. + * + * Result + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static void +VmBackupDriverOpCancel(VmBackupOp *_op) // IN +{ + VmBackupDriverOp *op = (VmBackupDriverOp *) _op; + op->canceled = TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * VmBackupNewDriverOp -- + * + * Enables or disables the sync driver. Note: "volumes" is not copied, + * to avoid unnecessary waste of memory, since it's kept in the global + * backup state structure. + * + * Result + * A state object, unless some error occurs. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static VmBackupDriverOp * +VmBackupNewDriverOp(Bool freeze, // IN + SyncDriverHandle handle, // IN + const char *volumes) // IN +{ + VmBackupDriverOp *op = NULL; + + op = Util_SafeMalloc(sizeof *op); + memset(op, 0, sizeof *op); + + op->callbacks.queryFn = VmBackupDriverOpQuery; + op->callbacks.cancelFn = VmBackupDriverOpCancel; + op->callbacks.releaseFn = VmBackupDriverOpRelease; + op->freeze = freeze; + op->volumes = volumes; + + if (freeze && !SyncDriver_Freeze(op->volumes, &handle)) { + g_warning("Error freezing filesystems.\n"); + free(op); + op = NULL; + } else if (!freeze && !SyncDriver_Thaw(handle)) { + g_warning("Error thawing filesystems.\n"); + free(op); + op = NULL; + } else { + op->syncHandle = handle; + } + return op; +} + + +/* + *----------------------------------------------------------------------------- + * + * VmBackupSyncDriverReadyForSnapshot -- + * + * Sends an event to the VMX indicating that the guest is ready for a + * snapshot to be taken (i.e., scripts have run and sync driver is + * enabled). + * + * Result + * TRUE, unless sending the message fails. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static Bool +VmBackupSyncDriverReadyForSnapshot(VmBackupState *state) +{ + g_debug("*** %s\n", __FUNCTION__); + return VmBackup_SendEvent(VMBACKUP_EVENT_SNAPSHOT_COMMIT, 0, ""); +} + + +/* Sync provider implementation. */ + + +/* + *----------------------------------------------------------------------------- + * + * VmBackupSyncDriverStart -- + * + * Starts an asynchronous operation to enable the sync driver. + * + * Result + * TRUE, unless an error occurs. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static Bool +VmBackupSyncDriverStart(VmBackupState *state, + void *clientData) +{ + VmBackupDriverOp *op; + + g_debug("*** %s\n", __FUNCTION__); + op = VmBackupNewDriverOp(TRUE, + (SyncDriverHandle) state->clientData, + state->volumes); + + if (op != NULL) { + state->clientData = (intptr_t) op->syncHandle; + } + + return VmBackup_SetCurrentOp(state, + (VmBackupOp *) op, + VmBackupSyncDriverReadyForSnapshot, + __FUNCTION__); +} + + +/* + *----------------------------------------------------------------------------- + * + * VmBackupSyncDriverAbort -- + * + * Does nothing. + * + * Result + * TRUE. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static Bool +VmBackupSyncDriverAbort(VmBackupState *state, + void *clientData) +{ + g_debug("*** %s\n", __FUNCTION__); + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * VmBackupSyncDriverSnapshotDone -- + * + * Starts an asynchronous operation to disable the sync driver. + * + * Result + * TRUE, unless an error occurs. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static Bool +VmBackupSyncDriverSnapshotDone(VmBackupState *state, + void *clientData) +{ + VmBackupDriverOp *op; + + g_debug("*** %s\n", __FUNCTION__); + + op = VmBackupNewDriverOp(FALSE, + (SyncDriverHandle) state->clientData, + NULL); + + return VmBackup_SetCurrentOp(state, (VmBackupOp *) op, NULL, __FUNCTION__); +} + + +/* + *----------------------------------------------------------------------------- + * + * VmBackupSyncDriverRelease -- + * + * Frees the given pointer. + * + * Result + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static void +VmBackupSyncDriverRelease(struct VmBackupSyncProvider *provider) +{ + free(provider); +} + + +/* + *----------------------------------------------------------------------------- + * + * VmBackup_NewSyncDriverProvider -- + * + * Returns a new VmBackupSyncProvider that will enable the sync driver + * as part of the "sync" operation of a backup. + * + * Result + * NULL on error. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +VmBackupSyncProvider * +VmBackup_NewSyncDriverProvider(void) +{ + VmBackupSyncProvider *provider; + + if (!SyncDriver_Init()) { + g_debug("Error initializing the sync driver.\n"); + return NULL; + } + + provider = Util_SafeMalloc(sizeof *provider); + provider->start = VmBackupSyncDriverStart; + provider->abort = VmBackupSyncDriverAbort; + provider->snapshotDone = VmBackupSyncDriverSnapshotDone; + provider->release = VmBackupSyncDriverRelease; + provider->clientData = NULL; + + return provider; +} + diff --git a/open-vm-tools/services/plugins/vmbackup/vmBackupInt.h b/open-vm-tools/services/plugins/vmbackup/vmBackupInt.h new file mode 100644 index 000000000..f9e2da313 --- /dev/null +++ b/open-vm-tools/services/plugins/vmbackup/vmBackupInt.h @@ -0,0 +1,212 @@ +/********************************************************* + * Copyright (C) 2008 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 vmBackupInt.h + * + * Internal definitions used by the vmbackup code. + */ + +#ifndef _VMBACKUPINT_H_ +#define _VMBACKUPINT_H_ + +#define G_LOG_DOMAIN "vmbackup" + +#include +#include "vmware.h" +#include "vmbackup_def.h" +#include "vmtoolsApp.h" + +typedef enum { + VMBACKUP_STATUS_PENDING, + VMBACKUP_STATUS_FINISHED, + VMBACKUP_STATUS_CANCELED, + VMBACKUP_STATUS_ERROR +} VmBackupOpStatus; + +typedef enum { + VMBACKUP_SCRIPT_FREEZE, + VMBACKUP_SCRIPT_FREEZE_FAIL, + VMBACKUP_SCRIPT_THAW +} VmBackupScriptType; + +typedef enum { + VMBACKUP_MSTATE_IDLE, + VMBACKUP_MSTATE_SCRIPT_FREEZE, + VMBACKUP_MSTATE_SYNC_FREEZE, + VMBACKUP_MSTATE_SYNC_THAW, + VMBACKUP_MSTATE_SCRIPT_THAW, + VMBACKUP_MSTATE_SCRIPT_ERROR, + VMBACKUP_MSTATE_SYNC_ERROR +} VmBackupMState; + +/** + * This is a "base struct" for asynchronous operations monitored by the + * state machine. Each implementation should provide these three functions + * at the start of the struct so that the state machine can properly + * interact with it. + */ + +typedef struct VmBackupOp { + VmBackupOpStatus (*queryFn)(struct VmBackupOp *); + void (*releaseFn)(struct VmBackupOp *); + void (*cancelFn)(struct VmBackupOp *); +} VmBackupOp; + + +/** + * Holds information about the current state of the backup operation. + * Don't modify the fields directly - rather, use VmBackup_SetCurrentOp, + * which does most of the handling needed by users of the state machine. + */ + +typedef struct VmBackupState { + ToolsAppCtx *ctx; + VmBackupOp *currentOp; + const char *currentOpName; + char *volumes; + guint pollPeriod; + GSource *timerEvent; + GSource *keepAlive; + Bool (*callback)(struct VmBackupState *); + Bool forceRequeue; + Bool generateManifests; + intptr_t clientData; + void *scripts; + const char *configDir; + ssize_t currentScript; + VmBackupMState machineState; +} VmBackupState; + +typedef Bool (*VmBackupCallback)(VmBackupState *); +typedef Bool (*VmBackupProviderCallback)(VmBackupState *, void *clientData); + + +/** + * Defines the interface between the state machine and the implementation + * of the "sync provider": either the VSS requestor or the sync driver + * provider, currently. + */ + +typedef struct VmBackupSyncProvider { + VmBackupProviderCallback start; + VmBackupProviderCallback abort; + VmBackupProviderCallback snapshotDone; + void (*release)(struct VmBackupSyncProvider *); + void *clientData; +} VmBackupSyncProvider; + + +/** + * Sets the current asynchronous operation being monitored, and an + * optional callback for after it's done executing. If the operation + * is NULL, the callback is set to execute later (currently, later = 200ms). + * + * @param[in] state The backup state. + * @param[in] op The current op to set. + * @param[in] callback Function to call after the operation is finished. + * @param[in] currentOpName Operation name, for debugging. + * + * @return TRUE if @a op is not NULL. + */ + +static INLINE Bool +VmBackup_SetCurrentOp(VmBackupState *state, + VmBackupOp *op, + VmBackupCallback callback, + const char *currentOpName) +{ + ASSERT(state != NULL); + ASSERT(state->currentOp == NULL); + state->currentOp = op; + state->callback = callback; + state->currentOpName = currentOpName; + state->forceRequeue = (callback != NULL && state->currentOp == NULL); + return (op != NULL); +} + + +/** + * Convenience function to call the operation-specific query function. + * + * @param[in] op The backup op. + * + * @return The operation's status. + */ + +static INLINE VmBackupOpStatus +VmBackup_QueryStatus(VmBackupOp *op) +{ + ASSERT(op != NULL); + return op->queryFn(op); +} + + +/** + * Convenience function to call the operation-specific cancel function. + * Code calling this function should still call VmBackup_QueryStatus() + * waiting for it to return a finished status (i.e., something other + * than VMBACKUP_STATUS_PENDING). + * + * @param[in] op The backup op. + */ + +static INLINE void +VmBackup_Cancel(VmBackupOp *op) +{ + ASSERT(op != NULL); + op->cancelFn(op); +} + + +/** + * Convenience function to call the operation-specific release function. + * Releasing a state object that hasn't finished yet (i.e., + * VmBackup_QueryStatus returns VMBACKUP_STATUS_PENDING) can result in + * undefined behavior. + * + * @param[in] op The backup op. + */ + +static INLINE void +VmBackup_Release(VmBackupOp *op) +{ + ASSERT(op != NULL); + op->releaseFn(op); +} + + +VmBackupSyncProvider * +VmBackup_NewSyncDriverProvider(void); + +#if defined(G_PLATFORM_WIN32) +VmBackupSyncProvider * +VmBackup_NewVssProvider(void); +#endif + +VmBackupOp * +VmBackup_NewScriptOp(VmBackupScriptType freeze, + VmBackupState *state); + +Bool +VmBackup_SendEvent(const char *event, + const uint32 code, + const char *desc); + +#endif /* _VMBACKUPINT_H_*/ + diff --git a/open-vm-tools/services/vmtoolsd/COPYING b/open-vm-tools/services/vmtoolsd/COPYING new file mode 100644 index 000000000..09f465ab7 --- /dev/null +++ b/open-vm-tools/services/vmtoolsd/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/open-vm-tools/services/vmtoolsd/Makefile.am b/open-vm-tools/services/vmtoolsd/Makefile.am new file mode 100644 index 000000000..661e99263 --- /dev/null +++ b/open-vm-tools/services/vmtoolsd/Makefile.am @@ -0,0 +1,69 @@ +################################################################################ +### Copyright 2009 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 +################################################################################ + +bin_PROGRAMS = vmtoolsd + +vmtoolsd_CPPFLAGS = +vmtoolsd_CPPFLAGS += @VMTOOLS_CPPFLAGS@ +vmtoolsd_CPPFLAGS += @GMODULE_CPPFLAGS@ +vmtoolsd_CPPFLAGS += @GOBJECT_CPPFLAGS@ +vmtoolsd_CPPFLAGS += @GTHREAD_CPPFLAGS@ +vmtoolsd_CPPFLAGS += -I$(builddir) + +vmtoolsd_LDADD = +vmtoolsd_LDADD += @VMTOOLS_LIBS@ +vmtoolsd_LDADD += @GMODULE_LIBS@ +vmtoolsd_LDADD += @GOBJECT_LIBS@ +vmtoolsd_LDADD += @GTHREAD_LIBS@ + +vmtoolsd_SOURCES = +vmtoolsd_SOURCES += cmdLine.c +vmtoolsd_SOURCES += mainLoop.c +vmtoolsd_SOURCES += mainPosix.c +vmtoolsd_SOURCES += pluginMgr.c +vmtoolsd_SOURCES += serviceObj.c +vmtoolsd_SOURCES += toolsRpc.c +vmtoolsd_SOURCES += svcSignals.c + +BUILT_SOURCES = +BUILT_SOURCES += svcSignals.c +BUILT_SOURCES += svcSignals.h + +CLEANFILES = +CLEANFILES += svcSignals.c +CLEANFILES += svcSignals.h + +EXTRA_DIST = +EXTRA_DIST += svcSignals.gm + +svcSignals.c: $(top_srcdir)/services/vmtoolsd/svcSignals.gm + glib-genmarshal --body $(top_srcdir)/services/vmtoolsd/svcSignals.gm > \ + $@ || (rm -f $@ && exit 1) + +svcSignals.h: $(top_srcdir)/services/vmtoolsd/svcSignals.gm + glib-genmarshal --header $(top_srcdir)/services/vmtoolsd/svcSignals.gm > \ + $@ || (rm -f $@ && exit 1) + +if HAVE_ICU + vmtoolsd_LDADD += @ICU_LIBS@ + vmtoolsd_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXX) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +else + vmtoolsd_LINK = $(LINK) +endif diff --git a/open-vm-tools/services/vmtoolsd/cmdLine.c b/open-vm-tools/services/vmtoolsd/cmdLine.c new file mode 100644 index 000000000..a1c74eec6 --- /dev/null +++ b/open-vm-tools/services/vmtoolsd/cmdLine.c @@ -0,0 +1,255 @@ +/********************************************************* + * Copyright (C) 2008 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 cmdLine.c + * + * Parses the daemon's command line arguments. Some commands may cause the + * process to exit. + */ + +#include "toolsCoreInt.h" + +#include +#include +#include +#if !defined(G_PLATFORM_WIN32) +# include +#endif +#include + +#include "conf.h" +#include "rpcout.h" +#include "str.h" +#include "vmtools.h" +#include "vmtoolsd_version.h" + + +/** + * Runs the given Tools RPC command, printing the result to the terminal and + * exiting the application afterwards. + * + * @param[in] option Unused. + * @param[in] value RPC command. + * @param[in] data Unused. + * @param[out] error Unused. + * + * @return This function doesn't return. + */ + +static gboolean +ToolsCoreRunCommand(const gchar *option, + const gchar *value, + gpointer data, + GError **error) +{ + char *result = NULL; + Bool status = FALSE; + + status = RpcOut_sendOne(&result, NULL, "%s", value); + + if (!status) { + g_printerr("%s\n", result ? result : "NULL"); + } else { + g_print("%s\n", result); + } + + vm_free(result); + exit(status ? 0 : 1); +} + + +#if defined(G_PLATFORM_WIN32) + +/** + * Function used to ignore command line arguments. + * + * @param[in] option Unused. + * @param[in] value Unused. + * @param[in] data Unused. + * @param[out] error Unused. + * + * @return TRUE + */ + +static gboolean +ToolsCoreIgnoreArg(const gchar *option, + const gchar *value, + gpointer data, + GError **error) +{ + return TRUE; +} + + +/** + * Asks a running instance of a service to stop running. + * + * @param[in] svcname Name of the service to be killed. + * + * @return Whether killing the service was successful. + */ + +static gboolean +ToolsCoreKillService(const gchar *svcname) +{ + gboolean ret = FALSE; + gchar *msg; + wchar_t *evt; + HANDLE h = NULL; + + g_assert(svcname != NULL); + + evt = Str_Aswprintf(NULL, QUIT_EVENT_NAME_FMT, svcname); + if (evt == NULL) { + g_printerr("Out of memory!\n"); + goto exit; + } + + h = OpenEvent(EVENT_MODIFY_STATE, FALSE, evt); + if (h == NULL) { + goto error; + } + + if (!SetEvent(h)) { + goto error; + } + + ret = TRUE; + goto exit; + +error: + msg = g_win32_error_message(GetLastError()); + g_printerr("Cannot open quit event: %s\n", msg); + g_free(msg); + +exit: + vm_free(evt); + CloseHandle(h); + return ret; +} + +#endif + + +/** + * Parses the command line. For a list of available options, look at the source + * below, where the option array is declared. + * + * @param[out] state Parsed options will be placed in this struct. + * @param[in] argc Argument count. + * @param[in] argv Argument array. + * + * @return TRUE on success. + */ + +gboolean +ToolsCore_ParseCommandLine(ToolsServiceState *state, + int argc, + char *argv[]) +{ + gboolean ret = FALSE; + gboolean version = FALSE; +#if defined(G_PLATFORM_WIN32) + gboolean kill = FALSE; +#endif + GOptionEntry clOptions[] = { + { "name", 'n', 0, G_OPTION_ARG_STRING, &state->name, + N_("Name of the service being started."), N_("svcname") }, + { "plugin-path", 'p', 0, G_OPTION_ARG_FILENAME, &state->pluginPath, + N_("Path to the plugin directory."), N_("path") }, + { "cmd", '\0', 0, G_OPTION_ARG_CALLBACK, ToolsCoreRunCommand, + N_("Sends an RPC command to the host and exits."), N_("command") }, +#if defined(G_PLATFORM_WIN32) + { "kill", 'k', 0, G_OPTION_ARG_NONE, &kill, + N_("Stops a running instance of a tools service."), 0 }, + { "install", 'i', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, ToolsCoreIgnoreArg, + N_("Installs the service with the Service Control Manager."), N_("args") }, + { "uninstall", 'u', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, ToolsCoreIgnoreArg, + N_("Uninstalls the service from the Service Control Manager."), NULL }, + { "displayname", 'd', 0, G_OPTION_ARG_STRING, &state->displayName, + N_("Service display name (only used with -i)."), N_("name") }, +#else + { "background", 'b', 0, G_OPTION_ARG_FILENAME, &state->pidFile, + N_("Runs in the background and creates a pid file."), N_("pidfile") }, + { "blockFd", '\0', 0, G_OPTION_ARG_INT, &state->ctx.blockFD, + N_("File descriptor for the VMware blocking fs."), N_("fd") }, +#endif + { "config", 'c', 0, G_OPTION_ARG_FILENAME, &state->configFile, + N_("Uses the config file at the given path."), N_("path") }, + { "debug", 'g', 0, G_OPTION_ARG_FILENAME, &state->debugPlugin, + N_("Runs in debug mode, using the given plugin."), N_("path") }, + { "log", 'l', 0, G_OPTION_ARG_NONE, &state->log, + N_("Turns on logging. Overrides the config file."), NULL }, + { "version", 'v', 0, G_OPTION_ARG_NONE, &version, + N_("Prints the daemon version and exits."), NULL }, + { NULL } + }; + GError *error = NULL; + GOptionContext *context = NULL; + +#if !defined(G_PLATFORM_WIN32) + state->ctx.blockFD = -1; +#endif + + context = g_option_context_new(NULL); +#if GLIB_MAJOR_VERSION >= 2 && GLIB_MINOR_VERSION >= 12 + g_option_context_set_summary(context, N_("Runs the VMware Tools daemon.")); +#endif + g_option_context_add_main_entries(context, clOptions, NULL); + + if (!g_option_context_parse(context, &argc, &argv, &error)) { + g_print("%s: %s\n", N_("Command line parsing failed"), error->message); + goto exit; + } + + if (version) { + g_print("%s %s\n", _("VMware Tools daemon, version"), VMTOOLSD_VERSION_STRING); + exit(0); + } + + VMTools_EnableLogging(state->log); + if (state->name == NULL) { + state->name = VMTOOLS_GUEST_SERVICE; + state->mainService = TRUE; + } else { + state->mainService = (strcmp(state->name, VMTOOLS_GUEST_SERVICE) == 0); + } + +#if defined(G_PLATFORM_WIN32) + if (kill) { + exit(ToolsCoreKillService(state->name) ? 0 : 1); + } +#else + /* If not running the "vmusr" service, ignore the blockFd parameter. */ + if (strcmp(state->name, VMTOOLS_USER_SERVICE) != 0) { + if (state->ctx.blockFD >= 0) { + close(state->ctx.blockFD); + } + state->ctx.blockFD = -1; + } +#endif + + ret = TRUE; + +exit: + g_clear_error(&error); + g_option_context_free(context); + return ret; +} + diff --git a/open-vm-tools/services/vmtoolsd/mainLoop.c b/open-vm-tools/services/vmtoolsd/mainLoop.c new file mode 100644 index 000000000..9b3364d0d --- /dev/null +++ b/open-vm-tools/services/vmtoolsd/mainLoop.c @@ -0,0 +1,245 @@ +/********************************************************* + * Copyright (C) 2008 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 mainLoop.c + * + * Functions for running the tools service's main loop. + */ + +#if defined(_WIN32) +# define MODULE_NAME(x) #x ".dll" +#elif defined(__APPLE__) +# define MODULE_NAME(x) "lib" #x ".dylib" +#else +# define MODULE_NAME(x) "lib" #x ".so" +#endif + +#include +#include "toolsCoreInt.h" +#include "conf.h" +#include "guestApp.h" +#include "serviceObj.h" +#include "util.h" +#include "vm_app.h" +#include "vmcheck.h" +#include "vmtools.h" + + +/** + * Loads the debug library and calls its initialization function. This function + * panics is something goes wrong. + * + * @param[in] state Service state. + */ + +static void +ToolsCoreInitializeDebug(ToolsServiceState *state) +{ + RpcDebugLibData *libdata; + RpcDebugInitializeFn initFn; + + state->debugLib = g_module_open(MODULE_NAME(vmrpcdbg), G_MODULE_BIND_LOCAL); + if (state->debugLib == NULL) { + g_error("Cannot load vmrpcdbg library.\n"); + } + + if (!g_module_symbol(state->debugLib, + "RpcDebug_Initialize", + (gpointer *) &initFn)) { + g_error("Cannot find symbol: RpcDebug_Initialize\n"); + } + + libdata = initFn(&state->ctx, state->debugPlugin); + g_assert(libdata != NULL); + g_assert(libdata->debugPlugin != NULL); + + state->debugData = libdata; +} + + +/** + * Cleans up the main loop after it has executed. After this function + * returns, the fields of the state object shouldn't be used anymore. + * + * @param[in] state Service state. + */ + +void +ToolsCore_Cleanup(ToolsServiceState *state) +{ + ToolsCore_UnloadPlugins(state); + if (state->ctx.rpc != NULL) { + RpcChannel_Destroy(state->ctx.rpc); + state->ctx.rpc = NULL; + } + g_key_file_free(state->ctx.config); + g_main_loop_unref(state->ctx.mainLoop); + +#if defined(G_PLATFORM_WIN32) + if (state->ctx.comInitialized) { + CoUninitialize(); + state->ctx.comInitialized = FALSE; + } +#endif + + if (state->debugData != NULL) { + state->debugData->shutdown(&state->ctx, state->debugData); + g_module_close(state->debugLib); + state->debugData = NULL; + state->debugLib = NULL; + } + + g_object_unref(state->ctx.serviceObj); + state->ctx.serviceObj = NULL; + state->ctx.config = NULL; + state->ctx.mainLoop = NULL; +} + + +/** + * Returns the name of the TCLO app name. This will only return non-NULL + * if the service is either the tools "guestd" or "userd" service. + * + * @param[in] state The service state. + * + * @return The app name, or NULL if not running a known TCLO app. + */ + +const char * +ToolsCore_GetTcloName(ToolsServiceState *state) +{ + if (state->mainService) { + return TOOLS_DAEMON_NAME; + } else if (strcmp(state->name, VMTOOLS_USER_SERVICE) == 0) { + return TOOLS_DND_NAME; + } else { + return NULL; + } +} + + +/** + * Performs any initial setup steps for the service's main loop. + * + * @param[in] state Service state. + * + * @return Whether initialization was successful. + */ + +gboolean +ToolsCore_Setup(ToolsServiceState *state) +{ + GMainContext *gctx; + char *confFile = state->configFile; + + if (confFile == NULL) { + confFile = VMTools_GetToolsConfFile(); + } + + if (!g_thread_supported()) { + g_thread_init(NULL); + } + + VMTools_SetDefaultLogDomain(state->name); + + /* Load the tools config file. */ + if (confFile != NULL) { + state->ctx.config = VMTools_LoadConfig(confFile, + G_KEY_FILE_NONE, + state->mainService); + if (state->ctx.config == NULL) { + /* Couldn't load the config file. Just create an empty dictionary. */ + state->ctx.config = g_key_file_new(); + } + } + + VMTools_ConfigLogging(state->ctx.config); + if (state->log) { + VMTools_EnableLogging(state->log); + } + + /* Initializes the app context. */ + gctx = g_main_context_default(); + state->ctx.name = state->name; + state->ctx.errorCode = EXIT_SUCCESS; + state->ctx.mainLoop = g_main_loop_new(gctx, TRUE); + state->ctx.isVMware = VmCheck_IsVirtualWorld(); + + g_type_init(); + state->ctx.serviceObj = g_object_new(TOOLSCORE_TYPE_SERVICE, NULL); + + /* Initializes the debug library if needed. */ + if (state->debugPlugin != NULL) { + ToolsCoreInitializeDebug(state); + } + + /* Initialize the RpcIn channel for the known tools services. */ + if (ToolsCore_GetTcloName(state) != NULL && + !ToolsCore_InitRpc(state)) { + goto error; + } + + if (!state->ctx.rpc->start(state->ctx.rpc)) { + goto error; + } + + if (!ToolsCore_LoadPlugins(state)) { + goto error; + } + + ToolsCore_RegisterPlugins(state); + goto exit; + +error: + if (state->ctx.rpc != NULL) { + state->ctx.rpc->shutdown(state->ctx.rpc); + state->ctx.rpc = NULL; + } + if (state->ctx.mainLoop != NULL) { + g_main_loop_unref(state->ctx.mainLoop); + state->ctx.mainLoop = NULL; + } + +exit: + if (state->configFile == NULL) { + g_free(confFile); + } + return (state->ctx.mainLoop != NULL); +} + + +/** + * Runs the service's main loop. + * + * @param[in] state Service state. + * + * @return Exit code. + */ + +int +ToolsCore_Run(ToolsServiceState *state) +{ +#if defined(__APPLE__) + ToolsCore_CFRunLoop(state); +#else + g_main_loop_run(state->ctx.mainLoop); +#endif + return state->ctx.errorCode; +} + diff --git a/open-vm-tools/services/vmtoolsd/mainPosix.c b/open-vm-tools/services/vmtoolsd/mainPosix.c new file mode 100644 index 000000000..1d3e2964f --- /dev/null +++ b/open-vm-tools/services/vmtoolsd/mainPosix.c @@ -0,0 +1,123 @@ +/********************************************************* + * Copyright (C) 2008 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 mainPosix.c + * + * Service entry point for the POSIX version of the tools daemon. + */ + + +#include "toolsCoreInt.h" +#include +#include +#include +#include +#include "system.h" +#include "unicode.h" +#include "vmtools.h" + +#if !defined(__APPLE__) +#include "embed_version.h" +#include "vmtoolsd_version.h" +VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING); +#endif + +static ToolsServiceState gState = { NULL, }; + +/** + * Handles a signal that would terminate the process. Asks the main loop + * to exit nicely. + * + * @param[in] info Unused. + * @param[in] data Pointer to the main loop to be stopped. + * + * @return FALSE + */ + +gboolean +ToolsCoreSigHandler(const siginfo_t *info, + gpointer data) +{ + g_main_loop_quit((GMainLoop *)data); + return FALSE; +} + + +/** + * Tools daemon entry function. + * + * @param[in] argc Argument count. + * @param[in] argv Argument array. + * + * @return 0 on successful execution, error code otherwise. + */ + +int +main(int argc, + char *argv[]) +{ + int ret = EXIT_FAILURE; + GSource *src; + + Unicode_Init(argc, &argv, NULL); + + if (!ToolsCore_ParseCommandLine(&gState, argc, argv)) { + goto exit; + } + + if (!ToolsCore_Setup(&gState)) { + goto exit; + } + + src = VMTools_NewSignalSource(SIGHUP); + VMTOOLSAPP_ATTACH_SOURCE(&gState.ctx, src, + ToolsCoreSigHandler, gState.ctx.mainLoop, NULL); + g_source_unref(src); + + src = VMTools_NewSignalSource(SIGINT); + VMTOOLSAPP_ATTACH_SOURCE(&gState.ctx, src, + ToolsCoreSigHandler, gState.ctx.mainLoop, NULL); + g_source_unref(src); + + src = VMTools_NewSignalSource(SIGQUIT); + VMTOOLSAPP_ATTACH_SOURCE(&gState.ctx, src, + ToolsCoreSigHandler, gState.ctx.mainLoop, NULL); + g_source_unref(src); + + src = VMTools_NewSignalSource(SIGTERM); + VMTOOLSAPP_ATTACH_SOURCE(&gState.ctx, src, + ToolsCoreSigHandler, gState.ctx.mainLoop, NULL); + g_source_unref(src); + + if (gState.pidFile != NULL && + !System_Daemon(FALSE, FALSE, gState.pidFile)) { + goto exit; + } + + ret = ToolsCore_Run(&gState); + + ToolsCore_Cleanup(&gState); + + if (gState.pidFile != NULL) { + g_unlink(gState.pidFile); + } +exit: + return ret; +} + diff --git a/open-vm-tools/services/vmtoolsd/pluginMgr.c b/open-vm-tools/services/vmtoolsd/pluginMgr.c new file mode 100644 index 000000000..46a065a8e --- /dev/null +++ b/open-vm-tools/services/vmtoolsd/pluginMgr.c @@ -0,0 +1,309 @@ +/********************************************************* + * Copyright (C) 2008 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 pluginMgr.c + * + * Provides functions for loading and manipulating Tools plugins. + */ + +#include +#include "vm_assert.h" +#include "guestApp.h" +#include "toolsCoreInt.h" +#include "util.h" +#include "vmtools.h" + + +/** + * Compares two strings. To be used with g_ptr_array_sort. + * + * @param[in] _str1 Pointer to string for comparison. + * @param[in] _str2 Pointer to string for comparison. + * + * @return Result of strcmp. + */ + +static gint +ToolsCoreStrPtrCompare(gconstpointer _str1, + gconstpointer _str2) +{ + const gchar *str1 = *((const gchar **) _str1); + const gchar *str2 = *((const gchar **) _str2); + return strcmp(str1, str2); +} + + +/** + * Loads all plugins present in the plugin directory. If the plugin path + * is NULL, then default directories are used in case the service is either + * the main tools service of the user daemon, otherwise failure is returned. + * + * @param[in] state The service state. + * + * @return Whether loading the plugins was successful. + */ + +gboolean +ToolsCore_LoadPlugins(ToolsServiceState *state) +{ + gboolean ret = FALSE; + const gchar *staticEntry; + guint i; + GDir *dir = NULL; + GError *err = NULL; + GPtrArray *plugins; + + g_assert(g_module_supported()); + + if (state->pluginPath == NULL) { + if (state->mainService || + strcmp(state->name, VMTOOLS_USER_SERVICE) == 0) { + char *instPath; + instPath = GuestApp_GetInstallPath(); + state->pluginPath = g_strdup_printf("%s%cplugins%c%s", + instPath, + DIRSEPC, + DIRSEPC, + state->name); + vm_free(instPath); + } else { + g_warning("No plugin path provided for service '%s'.\n", state->name); + goto exit; + } + } + + if (!g_file_test(state->pluginPath, G_FILE_TEST_IS_DIR)) { + g_warning("Plugin path is not a directory: %s\n", state->pluginPath); + goto exit; + } + + dir = g_dir_open(state->pluginPath, 0, &err); + if (dir == NULL) { + g_warning("Error opening dir: %s\n", err->message); + goto exit; + } + + plugins = g_ptr_array_new(); + + /* + * Load plugins in alphabetical order, so the load order is the same + * regardless of how the filesystem returns entries. + */ + while ((staticEntry = g_dir_read_name(dir)) != NULL) { + g_ptr_array_add(plugins, g_strdup(staticEntry)); + } + + g_ptr_array_sort(plugins, ToolsCoreStrPtrCompare); + + state->plugins = g_ptr_array_new(); + for (i = 0; i < plugins->len; i++) { + gchar *entry; + gchar *path; + GModule *module = NULL; + ToolsPlugin *plugin = NULL; + ToolsPluginData *data = NULL; + ToolsPluginOnLoad onload; + + entry = g_ptr_array_index(plugins, i); + path = g_strdup_printf("%s%c%s", state->pluginPath, DIRSEPC, entry); + + if (!g_file_test(path, G_FILE_TEST_IS_REGULAR)) { + g_warning("File '%s' is not a regular file, skipping.\n", entry); + goto next; + } + + module = g_module_open(path, G_MODULE_BIND_LOCAL); + if (module == NULL) { + g_warning("Opening plugin '%s' failed.\n", entry); + goto next; + } + + if (!g_module_symbol(module, "ToolsOnLoad", (gpointer *) &onload)) { + g_warning("Lookup of plugin entry point for '%s' failed.\n", entry); + goto next; + } + + if (onload != NULL) { + data = onload(&state->ctx); + } + + if (data == NULL) { + g_message("Plugin '%s' didn't provide deployment data, unloading.\n", entry); + goto next; + } + + g_assert(data->name != NULL); + g_module_make_resident(module); + plugin = g_malloc(sizeof *plugin); + plugin->module = module; + plugin->data = data; + + g_ptr_array_add(state->plugins, plugin); + g_debug("Plugin '%s' initialized.\n", plugin->data->name); + + next: + g_free(path); + if (plugin == NULL && module != NULL) { + if (!g_module_close(module)) { + g_warning("Error unloading plugin '%s': %s\n", entry, g_module_error()); + } + } + } + + for (i = 0; i < plugins->len; i++) { + g_free(g_ptr_array_index(plugins, i)); + } + g_ptr_array_free(plugins, TRUE); + ret = TRUE; + +exit: + if (dir != NULL) { + g_dir_close(dir); + } + g_clear_error(&err); + return ret; +} + + +/** + * Registers all RPC handlers provided by the loaded and enabled plugins. + * + * @param[in] state The service state. + */ + +void +ToolsCore_RegisterPlugins(ToolsServiceState *state) +{ + guint i; + + if (state->plugins == NULL) { + return; + } + + for (i = 0; i < state->plugins->len; i++) { + ToolsPlugin *plugin = g_ptr_array_index(state->plugins, i); + GArray *regs = (plugin->data != NULL) ? plugin->data->regs : NULL; + guint j; + + if (regs == NULL) { + continue; + } + + for (j = 0; j < regs->len; j++) { + guint k; + ToolsAppReg *reg = &g_array_index(regs, ToolsAppReg, j); + + switch (reg->type) { + case TOOLS_APP_GUESTRPC: + ASSERT(reg->data != NULL); + if (state->ctx.rpc == NULL) { + g_warning("Plugin '%s' asked to register a Guest RPC handler, " + "but there's no RPC channel.\n", plugin->data->name); + } else { + for (k = 0; k < reg->data->len; k++) { + RpcChannelCallback *cb = &g_array_index(reg->data, + RpcChannelCallback, + k); + RpcChannel_RegisterCallback(state->ctx.rpc, cb); + } + } + break; + + case TOOLS_APP_SIGNALS: + ASSERT(reg->data != NULL); + for (k = 0; k < reg->data->len; k++) { + ToolsPluginSignalCb *sig = &g_array_index(reg->data, + ToolsPluginSignalCb, + k); + g_signal_connect(state->ctx.serviceObj, + sig->signame, + sig->callback, + sig->clientData); + } + break; + + default: + NOT_IMPLEMENTED(); + } + } + } +} + + +/** + * Calls the shutdown callback for all loaded plugins, and cleans up the list + * of loaded plugins. Plugins are unloaded in the opposite order they were + * loaded. + * + * Note that if a plugin does not provide a shutdown callback, it may leak + * data that may have been dynamically allocated in the plugin registration + * info. Since this function is intended to be called once during service + * shutdown, this it not that big of a deal. + * + * @param[in] state The service state. + */ + +void +ToolsCore_UnloadPlugins(ToolsServiceState *state) +{ + GArray *pcaps = NULL; + + if (state->plugins == NULL) { + return; + } + + g_signal_emit_by_name(state->ctx.serviceObj, + TOOLS_CORE_SIG_CAPABILITIES, + &state->ctx, + FALSE, + &pcaps); + + if (pcaps != NULL) { + ToolsCore_SetCapabilities(state->ctx.rpc, pcaps, FALSE); + g_array_free(pcaps, TRUE); + } + + g_signal_emit_by_name(state->ctx.serviceObj, TOOLS_CORE_SIG_SHUTDOWN, &state->ctx); + + while (state->plugins->len > 0) { + ToolsPlugin *plugin = g_ptr_array_index(state->plugins, state->plugins->len - 1); + GArray *regs = (plugin->data != NULL) ? plugin->data->regs : NULL; + + g_debug("Unloading plugin '%s'.\n", plugin->data->name); + + if (regs != NULL) { + guint i; + for (i = 0; i < regs->len; i++) { + ToolsAppReg *reg = &g_array_index(regs, ToolsAppReg, i); + if (reg->data != NULL) { + g_array_free(reg->data, TRUE); + } + } + g_array_free(regs, TRUE); + } + + g_ptr_array_remove_index(state->plugins, state->plugins->len - 1); + g_module_close(plugin->module); + g_free(plugin); + } + + g_ptr_array_free(state->plugins, TRUE); + state->plugins = NULL; +} + diff --git a/open-vm-tools/services/vmtoolsd/serviceObj.c b/open-vm-tools/services/vmtoolsd/serviceObj.c new file mode 100644 index 000000000..1e1a9946f --- /dev/null +++ b/open-vm-tools/services/vmtoolsd/serviceObj.c @@ -0,0 +1,198 @@ +/********************************************************* + * Copyright (C) 2008 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 serviceObj.c + * + * Implementation of the "ToolsCore_Service" gobject. + */ + +#include "toolsCoreInt.h" +#include "serviceObj.h" +#include "svcSignals.h" +#include "vmtoolsApp.h" + + +/** + * Accumulator function for the "set option" signal. If a handler returns + * TRUE, sets the result of the signal propagation to TRUE. + * + * @param[in] ihint Unused. + * @param[out] retval Return value of the signal. + * @param[in] handlerRet Return value from the current handler. + * @param[in] data Unused. + * + * @return TRUE + */ + +static gboolean +ToolsCoreSetOptionAccumulator(GSignalInvocationHint *ihint, + GValue *retval, + const GValue *handlerRet, + gpointer data) +{ + if (!g_value_get_boolean(retval)) { + g_value_set_boolean(retval, g_value_get_boolean(handlerRet)); + } + return TRUE; +} + + +/** + * Accumulator function for the "capabilities" signal. Just puts the contents + * of all returned GArray instances into a "master" instance. + * + * @param[in] ihint Unused. + * @param[out] retval Return value of the signal. + * @param[in] handlerRet Return value from the current handler. + * @param[in] data Unused. + * + * @return TRUE. + */ + +static gboolean +ToolsCoreCapabilitiesAccumulator(GSignalInvocationHint *ihint, + GValue *retval, + const GValue *handlerRet, + gpointer data) +{ + GArray *caps = g_value_get_pointer(handlerRet); + + if (caps != NULL) { + guint i; + GArray *acc = g_value_get_pointer(retval); + + if (acc == NULL) { + acc = g_array_new(FALSE, TRUE, sizeof (ToolsAppCapability)); + g_value_set_pointer(retval, acc); + } + + for (i = 0; i < caps->len; i++) { + g_array_append_val(acc, g_array_index(caps, ToolsAppCapability, i)); + } + + g_array_free(caps, TRUE); + } + + return TRUE; +} + + +/** + * Initializes the ToolsCoreService class. Sets up the signals that are sent + * by the vmtoolsd service. + * + * @param[in] klass The class instance to initialize. + */ + +static void +ToolsCore_Service_class_init(gpointer _klass, + gpointer klassData) +{ + ToolsCoreServiceClass *klass = _klass; + g_signal_new(TOOLS_CORE_SIG_CAPABILITIES, + G_OBJECT_CLASS_TYPE(klass), + G_SIGNAL_RUN_LAST, + 0, + ToolsCoreCapabilitiesAccumulator, + NULL, + g_cclosure_user_marshal_POINTER__POINTER_BOOLEAN, + G_TYPE_POINTER, + 2, + G_TYPE_POINTER, + G_TYPE_BOOLEAN); + g_signal_new(TOOLS_CORE_SIG_RESET, + G_OBJECT_CLASS_TYPE(klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + g_signal_new(TOOLS_CORE_SIG_SET_OPTION, + G_OBJECT_CLASS_TYPE(klass), + G_SIGNAL_RUN_LAST, + 0, + ToolsCoreSetOptionAccumulator, + NULL, + g_cclosure_user_marshal_BOOLEAN__POINTER_STRING_STRING, + G_TYPE_BOOLEAN, + 3, + G_TYPE_POINTER, + G_TYPE_STRING, + G_TYPE_STRING); + g_signal_new(TOOLS_CORE_SIG_SHUTDOWN, + G_OBJECT_CLASS_TYPE(klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); +#if defined(G_PLATFORM_WIN32) + g_signal_new(TOOLS_CORE_SIG_SESSION_CHANGE, + G_OBJECT_CLASS_TYPE(klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_user_marshal_VOID__POINTER_UINT_UINT, + G_TYPE_NONE, + 3, + G_TYPE_POINTER, + G_TYPE_UINT, + G_TYPE_UINT); +#endif +} + + +/** + * Initializes the ToolsCoreService type if it hasn't been done yet, and + * return the type instance. This method is not thread safe. + * + * @return The ToolsCoreService type. + */ + +GType +ToolsCore_Service_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (ToolsCoreServiceClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + ToolsCore_Service_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (ToolsCoreService), + 0, /* n_preallocs */ + NULL, /* instance_init */ + }; + type = g_type_register_static(G_TYPE_OBJECT, + "ToolsCoreService", + &info, + 0); + } + return type; +} + diff --git a/open-vm-tools/services/vmtoolsd/serviceObj.h b/open-vm-tools/services/vmtoolsd/serviceObj.h new file mode 100644 index 000000000..4b771f3da --- /dev/null +++ b/open-vm-tools/services/vmtoolsd/serviceObj.h @@ -0,0 +1,48 @@ +/********************************************************* + * 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 _SERVICEOBJ_H_ +#define _SERVICEOBJ_H_ + +/** + * @file serviceObj.h + * + * Interface of the "core service" object. This interface is not really + * public, just the type itself, so that plugins can provide their own + * signals for communicating with other plugins in the same process. For + * this reason, it doesn't provide all the GObject boilerplate macros. + */ + +#include +#include "vmtoolsApp.h" + +#define TOOLSCORE_TYPE_SERVICE ToolsCore_Service_get_type() + +typedef struct ToolsCoreService { + GObject parent; +} ToolsCoreService; + +typedef struct ToolsCoreServiceClass { + GObjectClass parentClass; +} ToolsCoreServiceClass; + +GType +ToolsCore_Service_get_type(void); + +#endif /* _SERVICEOBJ_H_ */ + diff --git a/open-vm-tools/services/vmtoolsd/svcSignals.gm b/open-vm-tools/services/vmtoolsd/svcSignals.gm new file mode 100644 index 000000000..973f3a011 --- /dev/null +++ b/open-vm-tools/services/vmtoolsd/svcSignals.gm @@ -0,0 +1,36 @@ +########################################################## +# 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. +# +########################################################## + +## +# @file svcSignals.gm +# +# Defines the custom GClosure marshal functions for the core services signals. +# +# @see vmtoolsApp.h +# + +# The "capabilities" signal. +POINTER:POINTER,BOOLEAN + +# The "set option" signal. +BOOLEAN:POINTER,STRING,STRING + +# The "session change" signal. +# Used on Win32 only. +VOID:POINTER,UINT,UINT + diff --git a/open-vm-tools/services/vmtoolsd/toolsCoreInt.h b/open-vm-tools/services/vmtoolsd/toolsCoreInt.h new file mode 100644 index 000000000..bb8408a0e --- /dev/null +++ b/open-vm-tools/services/vmtoolsd/toolsCoreInt.h @@ -0,0 +1,106 @@ +/********************************************************* + * Copyright (C) 2008 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 _TOOLSCOREINT_H_ +#define _TOOLSCOREINT_H_ + +/** + * @file toolsCoreInt.h + * + * Internal functions for the tools daemon. + */ + +#define G_LOG_DOMAIN "vmtoolsd" + +#include +#include +#include "vmrpcdbg.h" +#include "vmtoolsApp.h" + +/* Used by the Windows implementation to communicate with other processes. */ +#if defined(G_PLATFORM_WIN32) +# define QUIT_EVENT_NAME_FMT L"Global\\VMwareToolsQuitEvent_%s" +#endif + +/** Defines the internal data about a plugin. */ +typedef struct ToolsPlugin { + GModule *module; + ToolsPluginData *data; +} ToolsPlugin; + +/** Defines internal service state. */ +typedef struct ToolsServiceState { + gchar *name; + gchar *configFile; + gboolean log; + gboolean mainService; + gchar *pluginPath; + GPtrArray *plugins; +#if defined(_WIN32) + gchar *displayName; +#else + gchar *pidFile; +#endif + GModule *debugLib; + gchar *debugPlugin; + RpcDebugLibData *debugData; + ToolsAppCtx ctx; +} ToolsServiceState; + + +gboolean +ToolsCore_ParseCommandLine(ToolsServiceState *state, + int argc, + char *argv[]); + +void +ToolsCore_Cleanup(ToolsServiceState *state); + +const char * +ToolsCore_GetTcloName(ToolsServiceState *state); + +int +ToolsCore_Run(ToolsServiceState *state); + +gboolean +ToolsCore_Setup(ToolsServiceState *state); + +gboolean +ToolsCore_InitRpc(ToolsServiceState *state); + +gboolean +ToolsCore_LoadPlugins(ToolsServiceState *state); + +void +ToolsCore_RegisterPlugins(ToolsServiceState *state); + +void +ToolsCore_SetCapabilities(RpcChannel *chan, + GArray *caps, + gboolean set); + +void +ToolsCore_UnloadPlugins(ToolsServiceState *state); + +#if defined(__APPLE__) +void +ToolsCore_CFRunLoop(ToolsServiceState *state); +#endif + +#endif /* _TOOLSCOREINT_H_ */ + diff --git a/open-vm-tools/services/vmtoolsd/toolsRpc.c b/open-vm-tools/services/vmtoolsd/toolsRpc.c new file mode 100644 index 000000000..daaf1f1cd --- /dev/null +++ b/open-vm-tools/services/vmtoolsd/toolsRpc.c @@ -0,0 +1,329 @@ +/********************************************************* + * Copyright (C) 2008 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 toolsRpc.c + * + * Functions related to the GuestRPC channel provided by the service. + * Provides the interface for the service to bring up the RPC channel, + * and handlers for the RPC messages which are handled by the service + * itself. + */ + +#include +#include +#include "vm_assert.h" +#include "conf.h" +#include "str.h" +#include "strutil.h" +#include "toolsCoreInt.h" +#include "vmtools.h" +#include "vm_tools_version.h" + + +/** + * Take action after an RPC channel reset. + * + * @param[in] chan The RPC channel. + * @param[in] success Whether reset was successful. + * @param[in] _state The service state. + */ + +static void +ToolsCoreCheckReset(struct RpcChannel *chan, + gboolean success, + gpointer _state) +{ + ToolsServiceState *state = _state; + + ASSERT(state != NULL); + + if (success) { + const gchar *app; + gchar *msg; + + app = ToolsCore_GetTcloName(state); + g_assert(app != NULL); + + msg = g_strdup_printf("vmx.capability.unified_loop %s", app); + if (!RpcChannel_Send(state->ctx.rpc, msg, strlen(msg) + 1, NULL, NULL)) { + g_warning("VMX doesn't support the Tools unified loop.\n" + "Some functionality (like setting options) may not work.\n"); + } + g_free(msg); + + g_signal_emit_by_name(state->ctx.serviceObj, + TOOLS_CORE_SIG_RESET, + &state->ctx); + } else { + VMTOOLSAPP_ERROR(&state->ctx, EXIT_FAILURE); + } +} + + +/** + * Checks all loaded plugins for their capabilities, and sends the data to the + * host. The code will try to send all capabilities, just logging errors as + * they occur. + * + * @param[in] data The RPC data. + * + * @return TRUE. + */ + +static Bool +ToolsCoreRpcCapReg(RpcInData *data) +{ + GArray *pcaps = NULL; + ToolsServiceState *state = data->clientData; + + g_signal_emit_by_name(state->ctx.serviceObj, + TOOLS_CORE_SIG_CAPABILITIES, + &state->ctx, + TRUE, + &pcaps); + + if (pcaps != NULL) { + ToolsCore_SetCapabilities(state->ctx.rpc, pcaps, TRUE); + g_array_free(pcaps, TRUE); + } + + /* Send the tools version to the VMX. */ + if (state->mainService) { + char *result = NULL; + size_t resultLen; + gboolean disableVersion; + gchar *toolsVersion; + GError *err = NULL; + + disableVersion = g_key_file_get_boolean(state->ctx.config, + "vmtools", + CONFNAME_DISABLETOOLSVERSION, + &err); + g_clear_error(&err); + toolsVersion = g_strdup_printf("tools.set.version %u", + disableVersion ? TOOLS_VERSION_UNMANAGED + : TOOLS_VERSION_CURRENT); + + if (!RpcChannel_Send(state->ctx.rpc, toolsVersion, strlen(toolsVersion) + 1, + &result, &resultLen)) { + g_debug("Error setting tools version: %s.\n", result); + } + vm_free(result); + g_free(toolsVersion); + } + + return RPCIN_SETRETVALS(data, "", TRUE); +} + + +/** + * Handles a "set option" RPC. Calls the plugins which have registered interest + * in the option being set. + * + * @param[in] data The RPC data. + * + * @return TRUE. + */ + +static Bool +ToolsCoreRpcSetOption(RpcInData *data) +{ + + gboolean retVal = FALSE; + char *option; + char *value; + unsigned int index = 0; + ToolsServiceState *state = data->clientData; + + /* Parse the option & value string. */ + option = StrUtil_GetNextToken(&index, data->args, " "); + /* Ignore leading space before value. */ + index++; + value = StrUtil_GetNextToken(&index, data->args, ""); + + if (option == NULL || value == NULL || strlen(value) == 0) { + goto exit; + } + + g_debug("Setting option '%s' to '%s'.\n", option, value); + g_key_file_set_string(state->ctx.config, state->ctx.name, option, value); + + g_signal_emit_by_name(state->ctx.serviceObj, + TOOLS_CORE_SIG_SET_OPTION, + &state->ctx, + option, + value, + &retVal); + +exit: + vm_free(option); + vm_free(value); + if (retVal) { + RPCIN_SETRETVALS(data, "", retVal); + } else { + RPCIN_SETRETVALS(data, "Unknown or invalid option", retVal); + } + return (Bool) retVal; +} + + +/** + * Initializes the RPC channel. Currently this instantiates an RpcIn loop. + * This function should only be called once. + * + * @param[in] state The service state. + * + * @return TRUE on success. + */ + +gboolean +ToolsCore_InitRpc(ToolsServiceState *state) +{ + static RpcChannelCallback rpcs[] = { + { "Capabilities_Register", ToolsCoreRpcCapReg, NULL, NULL, NULL, 0 }, + { "Set_Option", ToolsCoreRpcSetOption, NULL, NULL, NULL, 0 }, + }; + + size_t i; + const gchar *app; + GMainContext *mainCtx = g_main_loop_get_context(state->ctx.mainLoop); + + g_assert(state->ctx.rpc == NULL); + + if (state->debugPlugin != NULL) { + app = "debug"; + state->ctx.rpc = state->debugData->newDebugChannel(&state->ctx, + state->debugData); + } else { + /* + * Currently we try to bring up an RpcIn channel, which will only run + * inside a Virtual Machine. We want to exit with a "success" status + * if this check fails, though. (See bug 297528 for why.) + * + * XXX: this should be relaxed when we try to bring up a VMCI or TCP channel. + */ + if (!state->ctx.isVMware) { + g_warning("The %s service needs to run inside a virtual machine.\n", + state->name); + exit(0); + } + + state->ctx.rpc = RpcChannel_NewBackdoorChannel(mainCtx); + app = ToolsCore_GetTcloName(state); + g_assert(app != NULL); + } + + RpcChannel_Setup(state->ctx.rpc, + app, + mainCtx, + &state->ctx, + ToolsCoreCheckReset, + state); + + /* Register the "built in" RPCs. */ + for (i = 0; i < ARRAYSIZE(rpcs); i++) { + RpcChannelCallback *rpc = &rpcs[i]; + rpc->clientData = state; + RpcChannel_RegisterCallback(state->ctx.rpc, rpc); + } + + return TRUE; +} + + +/** + * Sends a list of capabilities to the host. + * + * @param[in] chan The RPC channel. + * @param[in] caps The list of capabilities. + * @param[in] set TRUE is setting capabilities (otherwise they're set to 0). + */ + +void +ToolsCore_SetCapabilities(RpcChannel *chan, + GArray *caps, + gboolean set) +{ + char *result; + size_t resultLen; + guint i; + gchar *newcaps = NULL; + + for (i = 0; i < caps->len; i++) { + gchar *tmp; + ToolsAppCapability *cap = &g_array_index(caps, ToolsAppCapability, i); + switch (cap->type) { + case TOOLS_CAP_OLD: + result = NULL; + tmp = g_strdup_printf("tools.capability.%s %u", + cap->name, + set ? cap->value : 0); + if (!RpcChannel_Send(chan, tmp, strlen(tmp) + 1, &result, &resultLen)) { + g_warning("Error sending capability %s: %s\n", cap->name, result); + } + vm_free(result); + g_free(tmp); + break; + + case TOOLS_CAP_OLD_NOVAL: + /* + * This is kind of weird, because of the way the VMX treats RPCs and + * what is expected of these capabilities without arguments. For a + * few details, see the comments in RpcOut_sendOne() (rpcout.c). + * Basically, for the VMX handlers not to complain, we need to send the + * RPC with the empty space at the end, and not consider the NULL + * character when counting the bytes. + */ + if (set) { + tmp = g_strdup_printf("tools.capability.%s ", cap->name); + if (!RpcChannel_Send(chan, tmp, strlen(tmp), &result, &resultLen)) { + g_warning("Error sending capability %s: %s\n", cap->name, result); + } + vm_free(result); + g_free(tmp); + } + break; + + case TOOLS_CAP_NEW: + if (newcaps == NULL) { + newcaps = g_strdup(GUEST_CAP_FEATURES); + } + tmp = g_strdup_printf("%s %d=%u", + newcaps, + cap->index, + set ? cap->value : 0); + g_free(newcaps); + newcaps = tmp; + break; + + default: + g_error("Invalid capability type: %d\n", cap->type); + } + } + + if (newcaps != NULL) { + result = NULL; + if (!RpcChannel_Send(chan, newcaps, strlen(newcaps) + 1, &result, &resultLen)) { + g_warning("Error sending new-style capabilities: %s\n", result); + } + vm_free(result); + g_free(newcaps); + } +} + diff --git a/open-vm-tools/tests/Makefile.am b/open-vm-tools/tests/Makefile.am new file mode 100644 index 000000000..f0ce8db4f --- /dev/null +++ b/open-vm-tools/tests/Makefile.am @@ -0,0 +1,22 @@ +################################################################################ +### Copyright 2009 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 +################################################################################ + +SUBDIRS = +SUBDIRS += vmrpcdbg +SUBDIRS += testDebug +SUBDIRS += testPlugin + diff --git a/open-vm-tools/tests/testDebug/COPYING b/open-vm-tools/tests/testDebug/COPYING new file mode 100644 index 000000000..09f465ab7 --- /dev/null +++ b/open-vm-tools/tests/testDebug/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/open-vm-tools/tests/testDebug/Makefile.am b/open-vm-tools/tests/testDebug/Makefile.am new file mode 100644 index 000000000..f35c2257d --- /dev/null +++ b/open-vm-tools/tests/testDebug/Makefile.am @@ -0,0 +1,34 @@ +################################################################################ +### Copyright 2009 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 +################################################################################ + +noinst_LTLIBRARIES = libtestDebug.la + +libtestDebug_la_CPPFLAGS = +libtestDebug_la_CPPFLAGS += @GOBJECT_CPPFLAGS@ +libtestDebug_la_CPPFLAGS += @PLUGIN_CPPFLAGS@ + +libtestDebug_la_LDFLAGS = +libtestDebug_la_LDFLAGS += @PLUGIN_LDFLAGS@ + +libtestDebug_la_LIBADD = +libtestDebug_la_LIBADD += @GOBJECT_LIBS@ +libtestDebug_la_LIBADD += @VMTOOLS_LIBS@ +libtestDebug_la_LIBADD += ../vmrpcdbg/libvmrpcdbg.la + +libtestDebug_la_SOURCES = +libtestDebug_la_SOURCES += testDebug.c + diff --git a/open-vm-tools/tests/testDebug/testDebug.c b/open-vm-tools/tests/testDebug/testDebug.c new file mode 100644 index 000000000..eccb5eead --- /dev/null +++ b/open-vm-tools/tests/testDebug/testDebug.c @@ -0,0 +1,243 @@ +/********************************************************* + * Copyright (C) 2008 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 testDebug.c + * + * A simple debug plugin that validates the messages sent by the service after + * a "reset" is received, and also interacts with the test plugin to test the + * functions provided by the service. + */ + +#define G_LOG_DOMAIN "testDebug" +#include +#include "util.h" +#include "vm_app.h" +#include "vmrpcdbg.h" +#include "vmtools.h" +#include "guestrpc/ghiGetBinaryHandlers.h" + +static gboolean +TestDebugValidateReset(RpcInData *data, Bool ret); + +static gboolean +TestDebugValidateUnknown(RpcInData *data, Bool ret); + +#define SET_OPTION_TEST ("Set_Option " TOOLSOPTION_BROADCASTIP " 1") + +/** RPC messages injected into the application using RpcDebug_SendNext(). */ +static RpcDebugMsgMapping gRpcMessages[] = { + { "reset", sizeof "reset", TestDebugValidateReset, FALSE }, + { "ping", sizeof "ping", NULL, FALSE }, + { "Capabilities_Register", sizeof "Capabilities_Register", NULL, FALSE }, + { "test.rpcin.unknown", sizeof "test.rpcin.unknown", TestDebugValidateUnknown, FALSE }, + /* This one is initialized manually, since it contains dynamic data. */ + { NULL, 0, NULL, TRUE }, + { "test.rpcin.msg2", sizeof "test.rpcin.msg2", NULL, FALSE }, + { "test.rpcin.msg3", sizeof "test.rpcin.msg3", NULL, FALSE }, + { SET_OPTION_TEST, sizeof SET_OPTION_TEST, NULL, FALSE }, + { "Capabilities_Register", sizeof "Capabilities_Register", NULL, FALSE }, + /* NULL terminator. */ + { NULL, 0, NULL, FALSE } +}; + +static gboolean gSignalReceived = FALSE; + + +/** + * Handles a "test-signal" sent by the test plugin. Just sets a static variable + * that will be checked by TestDebugReceiveRpc1 to make sure custom signals are + * working. + * + * @param[in] src Unused. + * @param[in] data Unused. + */ + +static void +TestDebugHandleSignal(gpointer src, + gpointer data) +{ + g_debug("Received test signal.\n"); + gSignalReceived = TRUE; +} + + +/** + * Validates the response from a "reset". + * + * @param[in] data RPC request data. + * @param[in] ret Return value from RPC handler. + * + * @return @a ret. + */ + +static gboolean +TestDebugValidateReset(RpcInData *data, + Bool ret) +{ + ToolsAppCtx *ctx = data->appCtx; + g_assert(data->result != NULL); + if (strcmp(data->result, "ATR debug") != 0) { + g_error("Unexpected response to reset: %s\n", data->result); + } + + /* + * If reset was successful, connect the "test-signal" signal so we + * test custom registration of signals. The test plugin will emit + * this signal after it sends an "test.rpcout.msg1" RPC as part of + * handling a "test.rpcin.msg1" RPC. + */ + g_signal_connect(ctx->serviceObj, + "test-signal", + G_CALLBACK(TestDebugHandleSignal), + NULL); + + return (gboolean) ret; +} + + +/** + * Validates a "test.rpcout.msg1" message sent by the test plugin. This message + * is sent when the plugin receives a "test.rpcin.msg1" RPC, and contains an + * XDR-encoded GHIBinaryHandlersIconDetails struct. + * + * @param[in] data Incoming data. + * @param[in] dataLen Size of incoming data. + * @param[out] result Result sent back to the application. + * @param[out] resultLen Length of result. + * + * @return TRUE (asserts on failure). + */ + +static gboolean +TestDebugReceiveRpc1(char *data, + size_t dataLen, + char **result, + size_t *resultLen) +{ + GHIBinaryHandlersIconDetails *details = (GHIBinaryHandlersIconDetails *) data; + + g_assert(gSignalReceived); + g_assert(details->width == 100); + g_assert(details->height == 200); + g_assert(strcmp(details->identifier, "rpc1test") == 0); + + g_debug("Successfully validated rpc1!\n"); + return TRUE; +} + + +/** + * Validates a "version" message sent during capability registration. + * No validation is actually done - the message is just printed. + * + * @param[in] data Incoming data. + * @param[in] dataLen Size of incoming data. + * @param[out] result Result sent back to the application. + * @param[out] resultLen Length of result. + * + * @return TRUE. + */ + +static gboolean +TestDebugReceiveVersion(char *data, + size_t dataLen, + char **result, + size_t *resultLen) +{ + g_debug("Received tools version message: %s\n", data); + RPCDEBUG_SET_RESULT("", result, resultLen); + return TRUE; +} + + +/** + * Validates the results for an unknown RPC sent to the guest. + * + * @param[in] data RPC request data. + * @param[in] ret Return value from RPC handler. + * + * @return The opposite of @a ret. + */ + +static gboolean +TestDebugValidateUnknown(RpcInData *data, + Bool ret) +{ + g_assert(strcmp(data->result, "Unknown Command") == 0); + return !ret; +} + + +/** + * Populates the RPC request data with the next message in the queue. + * + * @param[in] rpcdata Data for the injected RPC request data. + * + * @return TRUE if sending messages, FALSE if no more messages to be sent. + */ + +static gboolean +TestDebugSendNext(RpcDebugMsgMapping *rpcdata) +{ + static RpcDebugMsgList msgList = { gRpcMessages, 0 }; + return RpcDebug_SendNext(rpcdata, &msgList); +} + + +/** + * Returns the debug plugin's registration data. + * + * @param[in] ctx The application context. + * + * @return The application data. + */ + +TOOLS_MODULE_EXPORT RpcDebugPlugin * +RpcDebugOnLoad(ToolsAppCtx *ctx) +{ + static RpcDebugRecvMapping recvFns[] = { + { "tools.set.version", TestDebugReceiveVersion, NULL, 0 }, + { "test.rpcout.msg1", TestDebugReceiveRpc1, + xdr_GHIBinaryHandlersIconDetails, sizeof (GHIBinaryHandlersIconDetails) }, + { NULL, NULL } + }; + static RpcDebugPlugin regData = { + recvFns, + NULL, + TestDebugSendNext, + NULL + }; + + GHIBinaryHandlersIconDetails details; + details.width = 100; + details.height = 200; + details.identifier = "rpc1test"; + + /* Build the command for the "test.rpcin.msg1" RPC. */ + if (!RpcChannel_BuildXdrCommand("test.rpcin.msg1", + xdr_GHIBinaryHandlersIconDetails, + &details, + &gRpcMessages[4].message, + &gRpcMessages[4].messageLen)) { + g_error("Failed to create test.rpcin.msg1 command.\n"); + } + + return ®Data; +} + diff --git a/open-vm-tools/tests/testPlugin/COPYING b/open-vm-tools/tests/testPlugin/COPYING new file mode 100644 index 000000000..09f465ab7 --- /dev/null +++ b/open-vm-tools/tests/testPlugin/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/open-vm-tools/tests/testPlugin/Makefile.am b/open-vm-tools/tests/testPlugin/Makefile.am new file mode 100644 index 000000000..3cc523bab --- /dev/null +++ b/open-vm-tools/tests/testPlugin/Makefile.am @@ -0,0 +1,60 @@ +################################################################################ +### Copyright 2009 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 +################################################################################ + +noinst_LTLIBRARIES = libtestPlugin.la + +libtestPlugin_la_CPPFLAGS = +libtestPlugin_la_CPPFLAGS += @GOBJECT_CPPFLAGS@ +libtestPlugin_la_CPPFLAGS += @PLUGIN_CPPFLAGS@ + +libtestPlugin_la_LDFLAGS = +libtestPlugin_la_LDFLAGS += @PLUGIN_LDFLAGS@ + +libtestPlugin_la_LIBADD = +libtestPlugin_la_LIBADD += @GOBJECT_LIBS@ +libtestPlugin_la_LIBADD += @VMTOOLS_LIBS@ +libtestPlugin_la_LIBADD += @XDR_LIBS@ + +libtestPlugin_la_SOURCES = +libtestPlugin_la_SOURCES += testData_xdr.c +libtestPlugin_la_SOURCES += testPlugin.c + +BUILT_SOURCES = +BUILT_SOURCES += testData_xdr.c +BUILT_SOURCES += testData.h + +# XXX: see explanation in lib/guestRpc/Makefile.am +CFLAGS += -Wno-unused + +CLEANFILES = +CLEANFILES += testData_xdr.c +CLEANFILES += testData.h + +EXTRA_DIST = +EXTRA_DIST += testData.x + +testData.h: testData.x + if [ $(top_builddir) != $(top_srcdir) ]; then \ + cp -f $(top_srcdir)/tests/testPlugin/testData.x $(builddir); \ + fi + $(RPCGEN) $(RPCGENFLAGS) -h -o $@ $(builddir)/testData.x + $(SED) 's,rpc/rpc\.h,vmxrpc.h,' $@ > $@.tmp + mv $@.tmp $@ + +testData_xdr.c: testData.x testData.h + $(RPCGEN) $(RPCGENFLAGS) -c -o $@ $(builddir)/testData.x + diff --git a/open-vm-tools/tests/testPlugin/testData.x b/open-vm-tools/tests/testPlugin/testData.x new file mode 100644 index 000000000..5c5e86de4 --- /dev/null +++ b/open-vm-tools/tests/testPlugin/testData.x @@ -0,0 +1,28 @@ +/********************************************************* + * Copyright (C) 2008 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 testData.x + * + * Just define a struct for testing automatic XDR serialization. + */ + +struct TestPluginData { + string data<128>; +}; + diff --git a/open-vm-tools/tests/testPlugin/testPlugin.c b/open-vm-tools/tests/testPlugin/testPlugin.c new file mode 100644 index 000000000..5ccb6cd50 --- /dev/null +++ b/open-vm-tools/tests/testPlugin/testPlugin.c @@ -0,0 +1,302 @@ +/********************************************************* + * Copyright (C) 2008 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 testPlugin.c + * + * Implements a test plugin for the tools services. The plugin register for + * a few RPCs that are never sent by the VMX, so to "use" it you have to use + * a debug plugin that sends those RPCs. The test debug plugin does that. + */ + +#define G_LOG_DOMAIN "test" + +#include +#include +#include "testData.h" +#include "util.h" +#include "vmtoolsApp.h" +#include "vm_app.h" +#include "vmtools.h" +#include "guestrpc/ghiGetBinaryHandlers.h" + + +/** + * Handles a "test.rpcin.msg1" RPC message. The incoming data should be an + * XDR-encoded GHIBinaryHandlersIconDetails struct; the struct is written back + * to the RPC channel using RpcChannel_Send (command "test.rpcout.msg1"). + * + * Also emits a "test-signal", to test custom signal registration. + * + * @param[in] data RPC data. + * + * @return TRUE on success. + */ + +static Bool +TestPluginRpc1(RpcInData *data) +{ + ToolsAppCtx *ctx = data->appCtx; + GHIBinaryHandlersIconDetails *details = (GHIBinaryHandlersIconDetails *) data->args; + char *cmd; + size_t cmdLen; + + g_assert(details->width == 100); + g_assert(details->height == 200); + g_assert(strcmp(details->identifier, "rpc1test") == 0); + + g_signal_emit_by_name(ctx->serviceObj, "test-signal"); + + if (!RpcChannel_BuildXdrCommand("test.rpcout.msg1", + xdr_GHIBinaryHandlersIconDetails, + details, + &cmd, + &cmdLen)) { + g_error("Failed to create test.rpcout.msg1 command.\n"); + } + + if (!RpcChannel_Send(ctx->rpc, cmd, cmdLen, NULL, NULL)) { + g_error("Failed to send 'test.rpcout.msg1' message.\n"); + } + + vm_free(cmd); + + g_debug("Successfully handled rpc %s\n", data->name); + return RPCIN_SETRETVALS(data, "", TRUE); +} + + +/** + * Handles a "test.rpcin.msg2" RPC message. + * + * @param[in] data RPC data. + * + * @return TRUE on success. + */ + +static Bool +TestPluginRpc2(RpcInData *data) +{ + g_debug("%s: %s\n", __FUNCTION__, data->name); + return RPCIN_SETRETVALS(data, "", TRUE); +} + + +/** + * Handles a "test.rpcin.msg3" RPC message. Logs information to the + * output and returns data to be serialized using XDR. + * + * @param[in] data RPC data. + * + * @return TRUE on success. + */ + +static Bool +TestPluginRpc3(RpcInData *data) +{ + TestPluginData *ret; + g_debug("%s: %s\n", __FUNCTION__, data->name); + + ret = g_malloc(sizeof *ret); + ret->data = Util_SafeStrdup("Hello World!"); + + data->result = (char *) ret; + return TRUE; +} + + +/** + * Called by the service core when the host requests the capabilities supported + * by the guest tools. + * + * @param[in] src Unused. + * @param[in] ctx The app context. + * @param[in] set Whether capabilities are being set or unset (unused). + * @param[in] plugin Plugin registration data. + * + * @return A list of capabilities to be sent to the host. + */ + +static GArray * +TestPluginCapabilities(gpointer src, + ToolsAppCtx *ctx, + gboolean set, + ToolsPluginData *plugin) +{ + ToolsAppCapability caps[] = { + { TOOLS_CAP_OLD, "resolution_set", 0, 1 }, + { TOOLS_CAP_OLD, "display_topology_set", 0, 2 }, + { TOOLS_CAP_NEW, NULL, UNITY_CAP_START_MENU, 1 }, + { TOOLS_CAP_NEW, NULL, GHI_CAP_SHELL_ACTION_BROWSE, 1 } + }; + + g_debug("%s: got capability signal, setting = %d.\n", __FUNCTION__, set); + return VMTools_WrapArray(caps, sizeof *caps, ARRAYSIZE(caps)); +} + + +/** + * Handles a reset signal; just logs debug information. This callback is + * called when the service receives a "reset" message from the VMX, meaning + * the VMX may be restarting the RPC channel (due, for example, to restoring + * a snapshot, resuming a VM or a VMotion), and should be used to reset any + * application state that depends on the VMX. + * + * @param[in] src Event source. + * @param[in] ctx The app context. + * @param[in] plugin Plugin registration data. + * + * @return TRUE on success. + */ + +static gboolean +TestPluginReset(gpointer src, + ToolsAppCtx *ctx, + ToolsPluginData *plugin) +{ + g_assert(ctx != NULL); + g_debug("%s: reset signal for app %s\n", __FUNCTION__, ctx->name); + return TRUE; +} + + +#if defined(G_PLATFORM_WIN32) +/** + * Handles a session state change callback; this is only called on Windows, + * from both the "vmsvc" instance (handled by SCM notifications) and from + * "vmusr" with the "fast user switch" plugin. + * + * @param[in] src The source object. + * @param[in] ctx The application context. + * @param[in] code Session state change code. + * @param[in] id Session ID. + * @param[in] data Client data. + */ + +static void +TestPluginSessionChange(gpointer src, + ToolsAppCtx *ctx, + DWORD code, + DWORD sessionId, + ToolsPluginData *plugin) +{ + g_debug("Got session state change signal, code = %u, id = %u\n", code, sessionId); +} +#endif + + +/** + * Handles a shutdown callback; just logs debug information. This is called + * before the service is shut down, and should be used to clean up any resources + * that were initialized by the application. + * + * @param[in] src The source object. + * @param[in] ctx The app context. + * @param[in] plugin Plugin registration data. + */ + +static void +TestPluginShutdown(gpointer src, + ToolsAppCtx *ctx, + ToolsPluginData *plugin) +{ + g_debug("%s: shutdown signal.\n", __FUNCTION__); +} + + +/** + * Handles a "Set_Option" callback. Just logs debug information. This callback + * is called when the VMX sends a "Set_Option" command to tools, to configure + * different options whose values are kept outside of the virtual machine. + * + * @param[in] src Event source. + * @param[in] ctx The app context. + * @param[in] plugin Plugin registration data. + * @param[in] option Option being set. + * @param[in] value Option value. + * + * @return TRUE on success. + */ + +static gboolean +TestPluginSetOption(gpointer src, + ToolsAppCtx *ctx, + const gchar *option, + const gchar *value, + ToolsPluginData *plugin) +{ + g_debug("%s: set '%s' to '%s'\n", __FUNCTION__, option, value); + return TRUE; +} + + +/** + * Plugin entry point. Returns the registration data. This is called once when + * the plugin is loaded into the service process. + * + * @param[in] ctx The app context. + * + * @return The registration data. + */ + +TOOLS_MODULE_EXPORT ToolsPluginData * +ToolsOnLoad(ToolsAppCtx *ctx) +{ + static ToolsPluginData regData = { + "testPlugin", + NULL, + NULL + }; + + RpcChannelCallback rpcs[] = { + { "test.rpcin.msg1", + TestPluginRpc1, NULL, xdr_GHIBinaryHandlersIconDetails, NULL, + sizeof (GHIBinaryHandlersIconDetails) }, + { "test.rpcin.msg2", + TestPluginRpc2, NULL, NULL, NULL, 0 }, + { "test.rpcin.msg3", + TestPluginRpc3, NULL, NULL, xdr_TestPluginData, 0 } + }; + ToolsPluginSignalCb sigs[] = { + { TOOLS_CORE_SIG_RESET, TestPluginReset, ®Data }, + { TOOLS_CORE_SIG_SHUTDOWN, TestPluginShutdown, ®Data }, + { TOOLS_CORE_SIG_CAPABILITIES, TestPluginCapabilities, ®Data }, + { TOOLS_CORE_SIG_SET_OPTION, TestPluginSetOption, ®Data }, +#if defined(G_PLATFORM_WIN32) + { TOOLS_CORE_SIG_SESSION_CHANGE, TestPluginSessionChange, ®Data }, +#endif + }; + ToolsAppReg regs[] = { + { TOOLS_APP_GUESTRPC, VMTools_WrapArray(rpcs, sizeof *rpcs, ARRAYSIZE(rpcs)) }, + { TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) } + }; + + g_signal_new("test-signal", + G_OBJECT_TYPE(ctx->serviceObj), + 0, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs)); + return ®Data; +} + diff --git a/open-vm-tools/tests/vmrpcdbg/COPYING b/open-vm-tools/tests/vmrpcdbg/COPYING new file mode 100644 index 000000000..09f465ab7 --- /dev/null +++ b/open-vm-tools/tests/vmrpcdbg/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library 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; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/open-vm-tools/tests/vmrpcdbg/Makefile.am b/open-vm-tools/tests/vmrpcdbg/Makefile.am new file mode 100644 index 000000000..3fa481de0 --- /dev/null +++ b/open-vm-tools/tests/vmrpcdbg/Makefile.am @@ -0,0 +1,35 @@ +################################################################################ +### Copyright 2009 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 +################################################################################ + +noinst_LTLIBRARIES = libvmrpcdbg.la + +libvmrpcdbg_la_CPPFLAGS = +libvmrpcdbg_la_CPPFLAGS += @GMODULE_CPPFLAGS@ +libvmrpcdbg_la_CPPFLAGS += @VMTOOLS_CPPFLAGS@ + +libvmrpcdbg_la_LDFLAGS = +libvmrpcdbg_la_LDFLAGS += @PLUGIN_LDFLAGS@ + +libvmrpcdbg_la_LIBADD = +libvmrpcdbg_la_LIBADD += @GMODULE_LIBS@ +libvmrpcdbg_la_LIBADD += @VMTOOLS_LIBS@ +libvmrpcdbg_la_LIBADD += @XDR_LIBS@ + +libvmrpcdbg_la_SOURCES = +libvmrpcdbg_la_SOURCES += debugChannel.c +libvmrpcdbg_la_SOURCES += vmrpcdbg.c + diff --git a/open-vm-tools/tests/vmrpcdbg/debugChannel.c b/open-vm-tools/tests/vmrpcdbg/debugChannel.c new file mode 100644 index 000000000..3b954b113 --- /dev/null +++ b/open-vm-tools/tests/vmrpcdbg/debugChannel.c @@ -0,0 +1,316 @@ +/********************************************************* + * Copyright (C) 2008 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 debugChannel.c + * + * Implements an RPC Channel that is backed by a "debug plugin". The plugin + * provides information about what data should be "read" by the RPC Channel, + * and sinks for the data the application writes to the channel, so that the + * plugin can perform validation. + */ + +#define G_LOG_DOMAIN "rpcdbg" + +#include + +#include "strutil.h" +#include "vmrpcdbg.h" +#include "vmtools.h" +#include "util.h" +#include "vmxrpc.h" +#include "xdrutil.h" + +typedef struct DbgChannelData { + gboolean hasLibRef; + RpcDebugPlugin *plugin; + GSource *msgTimer; +} DbgChannelData; + +/** + * Reads one RPC from the plugin and dispatches it to the application. + * This function will ask the service process to stop running if a + * failure occurs, where failure is defined either by the validation + * function returning FALSE, or, if no validation function is provided, + * the application's callback returning FALSE. + * + * @param[in] _chan The RPC channel instance. + * + * @return TRUE if the callback should continue to be scheduled. + */ + +static gboolean +RpcDebugDispatch(gpointer _chan) +{ + Bool ret; + RpcChannel *chan = _chan; + DbgChannelData *cdata = chan->_private; + RpcDebugPlugin *plugin = cdata->plugin; + RpcInData data; + RpcDebugMsgMapping rpcdata; + + memset(&data, 0, sizeof data); + memset(&rpcdata, 0, sizeof rpcdata); + + if (!plugin->sendFn(&rpcdata)) { + RpcDebug_DecRef(chan->appCtx); + cdata->hasLibRef = FALSE; + return FALSE; + } else if (rpcdata.message == NULL) { + /* + * Nothing to send. Maybe the debug plugin is waiting for something to + * happen before sending another message. + */ + return TRUE; + } + + data.clientData = chan; + data.appCtx = chan->appCtx; + data.args = rpcdata.message; + data.argsSize = rpcdata.messageLen; + + ret = RpcChannel_Dispatch(&data); + if (rpcdata.validateFn != NULL) { + ret = (Bool) rpcdata.validateFn(&data, ret); + } else if (!ret) { + g_debug("RpcChannel_Dispatch returned error for RPC.\n"); + } + + if (data.freeResult) { + vm_free(data.result); + } + + if (rpcdata.freeMsg) { + vm_free(rpcdata.message); + } + + if (!ret) { + VMTOOLSAPP_ERROR((ToolsAppCtx *) chan->appCtx, 1); + RpcDebug_DecRef(chan->appCtx); + cdata->hasLibRef = FALSE; + return FALSE; + } + return TRUE; +} + + +/** + * Starts sending data to the service. The function will send one RPC + * approximately every 100ms, to somewhat mimic the behavior of the + * backdoor-based channel. + * + * @param[in] chan The RPC channel instance. + * + * @return TRUE. + */ + +static Bool +RpcDebugStart(RpcChannel *chan) +{ + DbgChannelData *data = chan->_private; + + ASSERT(chan->appName != NULL); + ASSERT(data->msgTimer == NULL); + + data->msgTimer = g_timeout_source_new(100); + VMTOOLSAPP_ATTACH_SOURCE((ToolsAppCtx *)chan->appCtx, + data->msgTimer, + RpcDebugDispatch, + chan, + NULL); + return TRUE; +} + + +/** + * Stops the channel; cancel the timer that sends polls the debug plugin for + * messages to send. + * + * @param[in] chan Unused. + * + * @return TRUE. + */ + +static Bool +RpcDebugStop(RpcChannel *chan) +{ + DbgChannelData *data = chan->_private; + if (data->msgTimer != NULL) { + g_source_unref(data->msgTimer); + data->msgTimer = NULL; + } + return TRUE; +} + + +/** + * Sends the given data to the plugin. This function tries to parse the + * incoming data based on the "cmd args" convention, and call the "receive" + * function provided by the debug plugin. + * + * @param[in] chan The RPC channel instance. + * @param[in] data Data to send. + * @param[in] dataLen Number of bytes to send. + * @param[out] result Response from other side. + * @param[out] resultLen Number of bytes in response. + * + * @return The result from the plugin's validation function, or TRUE if + * a validation function was not provided. + */ + +static Bool +RpcDebugSend(RpcChannel *chan, + char *data, + size_t dataLen, + char **result, + size_t *resultLen) +{ + char *copy; + gpointer xdrdata = NULL; + RpcDebugPlugin *plugin = ((DbgChannelData *)chan->_private)->plugin; + RpcDebugRecvMapping *mapping = NULL; + RpcDebugRecvFn recvFn = NULL; + Bool ret = TRUE; + + g_assert(chan->appName != NULL); + + /* Be paranoid. Like the VMX, NULL-terminate the incoming data. */ + copy = g_malloc(dataLen + 1); + memcpy(copy, data, dataLen); + copy[dataLen] = '\0'; + + /* Try to find a mapping the the command in the RPC. */ + if (plugin->recvFns) { + char *cmd; + unsigned int idx = 0; + + cmd = StrUtil_GetNextToken(&idx, copy, " "); + if (cmd != NULL) { + RpcDebugRecvMapping *test; + for (test = plugin->recvFns; test->name != NULL; test++) { + if (strcmp(test->name, cmd) == 0) { + mapping = test; + break; + } + } + } + vm_free(cmd); + } + + if (mapping != NULL) { + recvFn = mapping->recvFn; + if (mapping->xdrProc != NULL) { + char *start; + + g_assert(mapping->xdrSize > 0); + + /* Find out where the XDR data starts. */ + start = strchr(copy, ' '); + if (start == NULL) { + RPCDEBUG_SET_RESULT("Can't find command delimiter.", result, resultLen); + ret = FALSE; + goto exit; + } + start++; + + xdrdata = g_malloc0(mapping->xdrSize); + if (!XdrUtil_Deserialize(start, + dataLen - (start - copy), + mapping->xdrProc, + xdrdata)) { + RPCDEBUG_SET_RESULT("XDR deserialization failed.", result, resultLen); + ret = FALSE; + goto exit; + } + } + } else { + recvFn = plugin->dfltRecvFn; + } + + if (recvFn != NULL) { + ret = recvFn((xdrdata != NULL) ? xdrdata : copy, dataLen, result, resultLen); + } else { + RPCDEBUG_SET_RESULT("", result, resultLen); + } + +exit: + if (xdrdata != NULL) { + VMX_XDR_FREE(mapping->xdrProc, xdrdata); + g_free(xdrdata); + } + g_free(copy); + return ret; +} + + +/** + * Does nothing. + * + * @param[in] chan The RPC channel instance. + * + * @return TRUE. + */ + +static Bool +RpcDebugShutdown(RpcChannel *chan) +{ + DbgChannelData *cdata = chan->_private; + g_assert(chan->appName != NULL); + if (cdata->hasLibRef) { + RpcDebug_DecRef(chan->appCtx); + } + g_free(chan->_private); + return TRUE; +} + + +/** + * Instantiates a new RPC Debug Channel. This function will load and initialize + * the given debug plugin. + * + * This function will panic is something wrong happens while loading the plugin. + * + * @param[in] ctx The application context. + * @param[in] data Debug library data. + * + * @return A new channel. + */ + +RpcChannel * +RpcDebug_NewDebugChannel(ToolsAppCtx *ctx, + RpcDebugLibData *data) +{ + DbgChannelData *cdata; + RpcChannel *ret; + + g_assert(data != NULL); + + ret = g_malloc0(sizeof *ret); + ret->start = RpcDebugStart; + ret->stop = RpcDebugStop; + ret->send = RpcDebugSend; + ret->shutdown = RpcDebugShutdown; + + cdata = g_malloc0(sizeof *cdata); + cdata->plugin = data->debugPlugin; + ret->_private = cdata; + + RpcDebug_IncRef(); + return ret; +} + diff --git a/open-vm-tools/tests/vmrpcdbg/vmrpcdbg.c b/open-vm-tools/tests/vmrpcdbg/vmrpcdbg.c new file mode 100644 index 000000000..9679efca9 --- /dev/null +++ b/open-vm-tools/tests/vmrpcdbg/vmrpcdbg.c @@ -0,0 +1,165 @@ +/********************************************************* + * Copyright (C) 2008 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 vmrpcdbg.c + * + * Implementation of the library functions not related to the RPC channel. + */ + +#define G_LOG_DOMAIN "rpcdbg" + +#include +#include +#include "vmrpcdbg.h" + +#if !defined(__APPLE__) +#include "embed_version.h" +#include "vmtoolsd_version.h" +VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING); +#endif + +static GModule *gPlugin = NULL; + +/* Atomic types are not volatile in old glib versions. */ +#if GLIB_MAJOR_VERSION >= 2 && GLIB_MINOR_VERSION >= 10 +static volatile gint gRefCount = 0; +#else +static gint gRefCount = 0; +#endif + + +/** + * Decreases the internal ref count of the library. When the ref count reaches + * zero, this function will ask the application's main loop to stop running. + * + * @param[in] ctx The application contexnt. + */ + +void +RpcDebug_DecRef(ToolsAppCtx *ctx) +{ + if (g_atomic_int_dec_and_test(&gRefCount)) { + g_main_loop_quit(ctx->mainLoop); + } +} + + +/** + * Increases the internal ref count of the library. Test code that needs the + * process to stay alive should call this function to ensure that. + */ + +void +RpcDebug_IncRef(void) +{ + g_atomic_int_inc(&gRefCount); +} + + +/** + * Initializes the debug library and loads the debug plugin at the given path. + * This function panics if something goes wrong. + * + * @param[in] ctx The application context. + * @param[in] dbgPlugin Path to the debug plugin. + * + * @return Structure containing the debug library's information. + */ + +RpcDebugLibData * +RpcDebug_Initialize(ToolsAppCtx *ctx, + gchar *dbgPlugin) +{ + RpcDebugOnLoadFn onload; + RpcDebugLibData *ldata; + + ldata = g_malloc(sizeof *ldata); + + g_assert(gPlugin == NULL); + gPlugin = g_module_open(dbgPlugin, G_MODULE_BIND_LOCAL); + if (gPlugin == NULL) { + g_error("Can't load plugin: %s\n", dbgPlugin); + } + + if (!g_module_symbol(gPlugin, "RpcDebugOnLoad", (gpointer *) &onload)) { + g_error("No entry point in debug plugin %s\n", dbgPlugin); + } + + ldata->debugPlugin = onload(ctx); + if (ldata->debugPlugin == NULL) { + g_error("No registration data from plugin %s\n", dbgPlugin); + } + + ldata->newDebugChannel = RpcDebug_NewDebugChannel; + ldata->shutdown = RpcDebug_Shutdown; + + return ldata; +} + + +/** + * Places the next item on the given RPC message list into the given RPC data. + * Updates the current index of the list. + * + * @param[in] rpcdata The injected RPC data. + * @param[in] list The message list. + * + * @return TRUE if updated the RPC data, FALSE if reached the end of the list. + */ + +gboolean +RpcDebug_SendNext(RpcDebugMsgMapping *rpcdata, + RpcDebugMsgList *list) +{ + if (list->mappings[list->index].message != NULL) { + rpcdata->message = list->mappings[list->index].message; + rpcdata->messageLen = list->mappings[list->index].messageLen; + rpcdata->validateFn = list->mappings[list->index].validateFn; + rpcdata->freeMsg = list->mappings[list->index].freeMsg; + list->index++; + return TRUE; + } + return FALSE; +} + + +/** + * Shuts down the debug library. Unloads the debug plugin. The plugin's data + * shouldn't be used after this function is called. + * + * @param[in] ctx The application context. + * @param[in] ldata Debug library data. + */ + +void +RpcDebug_Shutdown(ToolsAppCtx *ctx, + RpcDebugLibData *ldata) +{ + g_assert(g_atomic_int_get(&gRefCount) == 0); + g_assert(ldata != NULL); + + if (ldata->debugPlugin != NULL && ldata->debugPlugin->shutdownFn != NULL) { + ldata->debugPlugin->shutdownFn(ctx, ldata->debugPlugin); + } + if (gPlugin != NULL) { + g_module_close(gPlugin); + gPlugin = NULL; + } +} + diff --git a/open-vm-tools/toolbox/Makefile.am b/open-vm-tools/toolbox/Makefile.am index c70d92d14..a223de54d 100644 --- a/open-vm-tools/toolbox/Makefile.am +++ b/open-vm-tools/toolbox/Makefile.am @@ -60,10 +60,14 @@ if HAVE_X11 vmware_toolbox_SOURCES += toolbox-gtk.c vmware_toolbox_SOURCES += toolboxAbout.c vmware_toolbox_SOURCES += toolboxDevices.c + vmware_toolbox_SOURCES += toolboxInt.c vmware_toolbox_SOURCES += toolboxOptions.c vmware_toolbox_SOURCES += toolboxScripts.c vmware_toolbox_SOURCES += toolboxShrink.c - vmware_toolbox_SOURCES += toolboxRecord.c + vmware_toolbox_SOURCES += $(top_srcdir)/libvmtools/vmtoolsConfig.c + + vmware_toolbox_CPPFLAGS = + vmware_toolbox_CPPFLAGS += -DG_LOG_DOMAIN=\"toolbox\" # Here's something wacky: automake will muck up the translation of this # conditional if it is indented. For more details, see: @@ -87,29 +91,27 @@ endif $(INSTALL) -m 644 bigIcon.xpm \ $(DESTDIR)/usr/share/pixmaps/vmware/vmware-toolbox.xpm uninstall-hook: - -$(RM) -f $(DESTDIR)$(datadir)/share/applications/vmware-toolbox.desktop - -$(RM) -rf $(DESTDIR)/usr/share/pixmaps/vmware + rm -f $(DESTDIR)$(datadir)/share/applications/vmware-toolbox.desktop + rm -rf $(DESTDIR)/usr/share/pixmaps/vmware endif -if ENABLE_CORE_SERVICES - bin_PROGRAMS += vmware-toolbox-cmd +bin_PROGRAMS += vmware-toolbox-cmd - vmware_toolbox_cmd_LDADD = - vmware_toolbox_cmd_LDADD += ../libguestlib/libguestlib.la - vmware_toolbox_cmd_LDADD += @VMTOOLS_LIBS@ +vmware_toolbox_cmd_LDADD = +vmware_toolbox_cmd_LDADD += ../libguestlib/libguestlib.la +vmware_toolbox_cmd_LDADD += @VMTOOLS_LIBS@ - vmware_toolbox_cmd_CPPFLAGS = - vmware_toolbox_cmd_CPPFLAGS += @VMTOOLS_CPPFLAGS@ +vmware_toolbox_cmd_CPPFLAGS = +vmware_toolbox_cmd_CPPFLAGS += @VMTOOLS_CPPFLAGS@ - vmware_toolbox_cmd_SOURCES = - vmware_toolbox_cmd_SOURCES += debugStdio.c - vmware_toolbox_cmd_SOURCES += toolbox-cmd.c - vmware_toolbox_cmd_SOURCES += toolboxcmd-devices.c - vmware_toolbox_cmd_SOURCES += toolboxcmd-scripts.c - vmware_toolbox_cmd_SOURCES += toolboxcmd-shrink.c - vmware_toolbox_cmd_SOURCES += toolboxcmd-stat.c - vmware_toolbox_cmd_SOURCES += toolboxcmd-time.c - vmware_toolbox_cmd_SOURCES += toolboxcmd-record.c +vmware_toolbox_cmd_SOURCES = +vmware_toolbox_cmd_SOURCES += debugStdio.c +vmware_toolbox_cmd_SOURCES += toolbox-cmd.c +vmware_toolbox_cmd_SOURCES += toolboxcmd-devices.c +vmware_toolbox_cmd_SOURCES += toolboxcmd-scripts.c +vmware_toolbox_cmd_SOURCES += toolboxcmd-shrink.c +vmware_toolbox_cmd_SOURCES += toolboxcmd-stat.c +vmware_toolbox_cmd_SOURCES += toolboxcmd-time.c if HAVE_ICU vmware_toolbox_cmd_LDADD += @ICU_LIBS@ @@ -121,5 +123,3 @@ else vmware_toolbox_cmd_LINK = $(LINK) endif -endif - diff --git a/open-vm-tools/toolbox/toolbox-cmd.c b/open-vm-tools/toolbox/toolbox-cmd.c index 30e621d36..f5706d9b7 100644 --- a/open-vm-tools/toolbox/toolbox-cmd.c +++ b/open-vm-tools/toolbox/toolbox-cmd.c @@ -78,13 +78,11 @@ static int DiskCommand(char **argv, int argc); static int StatCommand(char **argv, int argc); static int ScriptCommand(char **argv, int argc); static int TimeSyncCommand(char **argv, int argc); -static int RecordCommand(char **argv, int argc); static void DeviceHelp(char *progName); static void DiskHelp(char *progName); static void ScriptHelp(char *progName); static void StatHelp(char *progName); static void TimeSyncHelp(char *progName); -static void RecordHelp(char *progName); static void ToolboxCmdHelp(char *progName); static CmdTable *ParseCommand(char **argv, int argc); static Bool CheckArgumentLength(char **argv, int argc); @@ -100,7 +98,6 @@ static CmdTable commands[] = { { "disk", DiskCommand, TRUE, DiskHelp}, { "stat", StatCommand, FALSE, StatHelp}, { "device", DeviceCommand, FALSE, DeviceHelp}, - { "record", RecordCommand, FALSE, RecordHelp}, { "help", HelpCommand, FALSE, ToolboxCmdHelp}, { NULL, } }; @@ -155,14 +152,14 @@ CheckArgumentLength(char **argv, // IN: The command line arguments. static void DeviceHelp(char *progName) // IN: The name of the program obtained from argv[0] { - printf("device: enable, disable, list devices, or get a device status" + printf("device: functions related to the virtual machine's hardware devices\n" "Usage: %s device [args]\n" " dev is the name of the device.\n\n" "Subcommands:\n" - " enable : enables the device dev\n" + " enable : enable the device dev\n" " disable : disable the device dev\n" - " list: list enabled and disabled devices\n" - " status : prints the status of device dev\n", progName); + " list: list all available devices\n" + " status : print the status of a device\n", progName); } @@ -185,20 +182,20 @@ DeviceHelp(char *progName) // IN: The name of the program obtained from argv[0] static void ToolboxCmdHelp(char *progName) { - printf("usage: %s [options] [subcommand]\n" + printf("Usage: %s [options] [subcommand]\n" "Type \'%s help \' for help on a specific command.\n" "Type \'%s --version' to see the Vmware Tools version.\n" - "Type \'%s --quiet to supress stdout printing\n" - "Most commands take a subcommand\n\n" + "Use --quiet to suppress stdout output.\n" + "Most commands take a subcommand.\n\n" "Available commands:\n" " timesync\n" " device\n" " script\n" " disk\n" " stat\n" - " record\n\n" + "\n" "For additional information please visit http://www.vmware.com/support/\n\n", - progName, progName, progName, progName); + progName, progName, progName); } @@ -221,13 +218,12 @@ ToolboxCmdHelp(char *progName) static void TimeSyncHelp(char *progName) // IN: The name of the program obtained from argv[0] { - printf("timesync: enable, disable, or check the status of time " - "sync on the guest OS\n" + printf("timesync: functions for controlling time synchronization on the guest OS\n" "Usage: %s timesync \n\n" "Subcommands\n" - " enable: enable time sync\n" - " disable: disable time sync\n" - " status: display the time sync status\n", progName); + " enable: enable time synchronization\n" + " disable: disable time synchronization\n" + " status: print the time synchronization status\n", progName); } @@ -250,14 +246,14 @@ TimeSyncHelp(char *progName) // IN: The name of the program obtained from argv[0 static void ScriptHelp(char *progName) // IN: The name of the program obtained from argv[0] { - printf("script: enable, disable, set, or set to default a particular script\n" - "Usage: %s script [args]\n\n" + printf("script: control the scripts run in response to power operations\n" + "Usage: %s script [args]\n\n" "Subcommands:\n" - " enable: enables and restores the script to the default one\n" - " disable: disables a particular script\n" - " set : sets a script to the script given by full_path\n" - " default: returns the path to the default script\n" - " current: returns the path to the current script\n", progName); + " enable: enable the given script and restore its path to the default\n" + " disable: disable the given script\n" + " set : set the given script to the given path\n" + " default: print the default path of the given script\n" + " current: print the current path of the given script\n", progName); } @@ -308,47 +304,20 @@ DiskHelp(char *progName) // IN: The name of the program obtained from argv[0] static void StatHelp(char *progName) // IN: The name of the program obtained from argv[0] { - printf("stat: usefull guest and host information\n" + printf("stat: print useful guest and host information\n" "Usage: %s state \n\n" "Subcommands\n" - " hosttime: gets the time host time\n" - " memory: gets the virtual machine memory in MBs\n" - " speed: gets the virtual machine speed in MHz\n" + " hosttime: print the host time\n" + " memory: print the virtual machine memory in MBs\n" + " speed: print the CPU speed in MHz\n" "ESX guests only subcommands\n" - " sessionid: Prints the current the session id\n" - " balloon: Prints memory ballooning\n" - " swap: Prints memory swapping\n" - " memlimit: Prins memery limit\n" - " memres: Prints memory reservation\n" - " cpures: Prints cpu reservation\n" - " cpulimit: Prints cput limit\n", progName); -} - - -/* - *----------------------------------------------------------------------------- - * - * RecordHelp -- - * - * Prints the help for the record command. - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -static void -RecordHelp(char *progName) // IN: The name of the program obtained from argv[0] -{ - printf("record: control recording process inside guest\n" - "Usage: %s record \n\n" - "Subcommands\n" - " start: start recording\n" - " stop: stop recording\n", progName); + " sessionid: print the current session id\n" + " balloon: print memory ballooning information\n" + " swap: print memory swapping information\n" + " memlimit: print memory limit information\n" + " memres: print memory reservation information\n" + " cpures: print CPU reservation information\n" + " cpulimit: print CPU limit information\n", progName); } @@ -621,42 +590,6 @@ TimeSyncCommand(char **argv, // IN: command line arguments } -/* - *----------------------------------------------------------------------------- - * - * RecordCommand -- - * - * Parse and Handle recording commands. - * - * Results: - * Returns EXIT_SUCCESS on success. - * Returns the appropriate exit code errors. - * - * Side effects: - * Might start and stop recording, guest os may be suspended - * to wait for the recording panel in host. - * - *----------------------------------------------------------------------------- - */ - -static int -RecordCommand(char **argv, // IN: Command line arguments - int argc) // IN: Length of command line argumenst -{ - if (CheckArgumentLength(argv, argc)) { - if (strcmp(argv[optind], "start") == 0) { - return Record_StartRecording(); - } else if (strcmp(argv[optind], "stop") == 0) { - return Record_StopRecording(); - } else { - fprintf(stderr, "Unknown subcommand"); - } - } - RecordHelp(argv[0]); - return EX_USAGE; -} - - /* *----------------------------------------------------------------------------- * @@ -721,7 +654,7 @@ main(int argc, // IN: length of command line arguments * Check if we are in a VM */ if (!VmCheck_IsVirtualWorld()) { - fprintf(stderr, "toolbox-cmd must be run inside a virtual machine.\n"); + fprintf(stderr, "%s must be run inside a virtual machine.\n", argv[0]); exit(EXIT_FAILURE); } diff --git a/open-vm-tools/toolbox/toolbox-gtk.c b/open-vm-tools/toolbox/toolbox-gtk.c index 8f1659e76..69f14976f 100644 --- a/open-vm-tools/toolbox/toolbox-gtk.c +++ b/open-vm-tools/toolbox/toolbox-gtk.c @@ -71,7 +71,7 @@ RpcIn *gRpcInCtlPanel; static GtkWidget *toolsMain; static Bool optionAutoHide; static guint gTimeoutId; -static const char **gNativeEnviron; +const char **gNativeEnviron; /* Help pages. These need to be in the same order as the tabs in the UI. */ static const char *gHelpPages[] = { @@ -80,7 +80,6 @@ static const char *gHelpPages[] = { "tools_devices.htm", "tools_scripts.htm", "tools_shrink.htm", - NULL, // Record/Replay "tools_about.htm", }; @@ -602,8 +601,6 @@ ToolsMain_Create(void) gtk_label_new_with_mnemonic(TAB_LABEL_SCRIPTS)); gtk_notebook_append_page(GTK_NOTEBOOK(notebookMain), Shrink_Create(ToolsMain), gtk_label_new_with_mnemonic(TAB_LABEL_SHRINK)); - gtk_notebook_append_page(GTK_NOTEBOOK(notebookMain), Record_Create(ToolsMain), - gtk_label_new_with_mnemonic(TAB_LABEL_RECORD)); gtk_notebook_append_page(GTK_NOTEBOOK(notebookMain), About_Create(ToolsMain), gtk_label_new_with_mnemonic(TAB_LABEL_ABOUT)); #else @@ -611,8 +608,6 @@ ToolsMain_Create(void) gtk_label_new(TAB_LABEL_SCRIPTS)); gtk_notebook_append_page(GTK_NOTEBOOK(notebookMain), Shrink_Create(ToolsMain), gtk_label_new(TAB_LABEL_SHRINK)); - gtk_notebook_append_page(GTK_NOTEBOOK(notebookMain), Record_Create(ToolsMain), - gtk_lable_new(TAB_LABEL_RECORD)); gtk_notebook_append_page(GTK_NOTEBOOK(notebookMain), About_Create(ToolsMain), gtk_label_new(TAB_LABEL_ABOUT)); #endif @@ -901,13 +896,13 @@ OnViewportSizeRequest(GtkWidget *widget, // IN */ void -InitHelpDir(GuestApp_Dict *pConfDict) // IN +InitHelpDir(GKeyFile *pConfDict) // IN { - const char *tmpDir = NULL; + gchar *tmpDir = NULL; ASSERT(hlpDir == NULL); - tmpDir = GuestApp_GetDictEntry(pConfDict, CONFNAME_HELPDIR); + tmpDir = g_key_file_get_string(pConfDict, "toolbox", CONFNAME_HELPDIR, NULL); if (!tmpDir || !File_Exists(tmpDir)) { unsigned int i; @@ -918,14 +913,14 @@ InitHelpDir(GuestApp_Dict *pConfDict) // IN for (i = 0; i < ARRAYSIZE(candidates); i++) { if (File_Exists(candidates[i])) { - tmpDir = candidates[i]; + tmpDir = g_strdup(candidates[i]); break; } } } if (tmpDir) { - hlpDir = Util_SafeStrdup(tmpDir); + hlpDir = tmpDir; } } @@ -987,7 +982,7 @@ main(int argc, // IN: ARRAY_SIZEOF(argv) { Bool optIconify, optHelp, optVersion; struct sigaction olds[ARRAYSIZE(gSignals)]; - GuestApp_Dict *pConfDict; + GKeyFile *pConfDict; if (!VmCheck_IsVirtualWorld()) { #ifndef ALLOW_TOOLS_IN_FOREIGN_VM @@ -1003,11 +998,14 @@ main(int argc, // IN: ARRAY_SIZEOF(argv) Panic("vmware-toolbox can't set signal handler\n"); } - pConfDict = Conf_Load(); - Debug_Set(GuestApp_GetDictEntryBool(pConfDict, CONFNAME_LOG), DEBUG_PREFIX); - Debug_EnableToFile(GuestApp_GetDictEntry(pConfDict, CONFNAME_LOGFILE), FALSE); + pConfDict = Toolbox_LoadToolsConf(); + Debug_Set(g_key_file_get_boolean(pConfDict, "logging", CONFNAME_LOG, NULL), + DEBUG_PREFIX); + Debug_EnableToFile(g_key_file_get_string(pConfDict, "logging", + CONFNAME_LOGFILE, NULL), + FALSE); InitHelpDir(pConfDict); - GuestApp_FreeDict(pConfDict); + g_key_file_free(pConfDict); optionAutoHide = FALSE; @@ -1130,7 +1128,7 @@ main(int argc, // IN: ARRAY_SIZEOF(argv) gdk_pixmap_unref(pixmap); gdk_bitmap_unref(bitmask); - free(hlpDir); + g_free(hlpDir); return 0; } diff --git a/open-vm-tools/toolbox/toolboxAbout.c b/open-vm-tools/toolbox/toolboxAbout.c index de9b924d1..abf9cdf56 100644 --- a/open-vm-tools/toolbox/toolboxAbout.c +++ b/open-vm-tools/toolbox/toolboxAbout.c @@ -134,7 +134,7 @@ About_Create(GtkWidget* mainWnd) gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment(GTK_MISC(label), 0, 0); -#ifdef _GTK2_ +#ifdef GTK2 Str_Snprintf(buf1, sizeof buf1, "%s %s", UTF8_COPYRIGHT_STRING, RIGHT_RESERVED); diff --git a/open-vm-tools/toolbox/toolboxCmdInt.h b/open-vm-tools/toolbox/toolboxCmdInt.h index 8cf14e3fe..452342bda 100644 --- a/open-vm-tools/toolbox/toolboxCmdInt.h +++ b/open-vm-tools/toolbox/toolboxCmdInt.h @@ -111,10 +111,4 @@ int Stat_GetMemorySwapped(void); int Stat_GetMemoryLimit(void); int Stat_GetMemoryReservation(void); -/* - * Record commands - */ -Bool Record_StartRecording(void); -Bool Record_StopRecording(void); - #endif /*_TOOLBOX_CMD_H_*/ diff --git a/open-vm-tools/toolbox/toolboxGtkInt.h b/open-vm-tools/toolbox/toolboxGtkInt.h index 1cb1d78d4..29ac2bdf2 100644 --- a/open-vm-tools/toolbox/toolboxGtkInt.h +++ b/open-vm-tools/toolbox/toolboxGtkInt.h @@ -48,14 +48,12 @@ #define TAB_LABEL_DEVICES "De_vices" #define TAB_LABEL_SCRIPTS "Scri_pts" #define TAB_LABEL_SHRINK "Shrin_k" -#define TAB_LABEL_RECORD "_Record" #define TAB_LABEL_ABOUT "Abo_ut" #else #define TAB_LABEL_OPTIONS "Options" #define TAB_LABEL_DEVICES "Devices" #define TAB_LABEL_SCRIPTS "Scripts" #define TAB_LABEL_SHRINK "Shrink" -#define TAB_LABEL_RECORD "Record" #define TAB_LABEL_ABOUT "About" #endif @@ -71,7 +69,6 @@ GtkWidget* Devices_Create(GtkWidget* mainWnd); GtkWidget* Options_Create(GtkWidget* mainWnd); GtkWidget* Scripts_Create(GtkWidget* mainWnd); GtkWidget* Shrink_Create(GtkWidget* mainWnd); -GtkWidget* Record_Create(GtkWidget* mainWnd); void Options_OnTimeSyncToggled(gpointer btn, gpointer data); void Devices_OnDeviceToggled(gpointer btn, gpointer data); @@ -84,5 +81,6 @@ extern GdkColormap* colormap; extern GtkWidget *optionsTimeSync; extern DblLnkLst_Links *gEventQueue; extern GtkWidget *scriptsApply; +extern const char **gNativeEnviron; #endif // _TOOLBOX_INT_H_ diff --git a/open-vm-tools/toolbox/toolboxInt.c b/open-vm-tools/toolbox/toolboxInt.c new file mode 100644 index 000000000..886902417 --- /dev/null +++ b/open-vm-tools/toolbox/toolboxInt.c @@ -0,0 +1,112 @@ +/********************************************************* + * Copyright (C) 2008 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. + * + *********************************************************/ + +/* + * toolboxInt.c -- + * + * Internal, shared functions for the GTK toolbox. + */ + +#include +#include +#include +#include +#include "conf.h" +#include "debug.h" +#include "guestApp.h" +#include "util.h" +#include "vmtools.h" + + +/* + *----------------------------------------------------------------------------- + * + * Toolbox_LoadToolsConf -- + * + * Load the Tools configuration file from the default location. + * + * XXX: This function is temporary until the GTK toolbox is refactored + * to be able to use vmtoolslib. + * + * Results: + * The config object. If loading the data fails, returns an empty conf object. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +GKeyFile * +Toolbox_LoadToolsConf(void) +{ + gchar *path = VMTools_GetToolsConfFile(); + GKeyFile *config; + + config = VMTools_LoadConfig(path, + G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, + TRUE); + + if (config == NULL) { + Debug("Unable to load config file.\n"); + config = g_key_file_new(); + } + + g_free(path); + return config; +} + + +/* + *----------------------------------------------------------------------------- + * + * Toolbox_SaveToolsConf -- + * + * Saves the given config data to the default tools config file location. + * + * XXX: This function is temporary until the GTK toolbox is refactored + * to be able to use vmtoolslib. + * + * Results: + * Whether saving was successful. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +gboolean +Toolbox_SaveToolsConf(GKeyFile *config) // IN +{ + gboolean ret = FALSE; + gchar *path = NULL; + GError *err = NULL; + + path = VMTools_GetToolsConfFile(); + ret = VMTools_WriteConfig(path, config, &err); + + if (!ret) { + Warning("Error saving conf data: %s\n", err->message); + g_clear_error(&err); + } + + g_free(path); + return ret; +} + diff --git a/open-vm-tools/toolbox/toolboxInt.h b/open-vm-tools/toolbox/toolboxInt.h index 0d8d12eff..62616a1fc 100644 --- a/open-vm-tools/toolbox/toolboxInt.h +++ b/open-vm-tools/toolbox/toolboxInt.h @@ -24,6 +24,7 @@ #ifndef _TOOLBOX_INT_H_ #define _TOOLBOX_INT_H_ +#include #include "vm_basic_types.h" #include "vm_version.h" #include "dbllnklst.h" @@ -55,12 +56,12 @@ "enabled while the host believes it is disabled.\n\n " \ "Please close and reopen the Toolbox to synchronize " \ "it with the host.\n" -#define RECORD_VMX_ERR "Error, the Record/Replay control operation failed. This could be for " \ - "one of the following reasons:\n" \ - "1. You are running an old version of a VMware product.\n\n" \ - "2. Your product has disabled these controls. To enable them, consult " \ - "the product documentation.\n\n" \ - "3. You tried to start a recording while already recording.\n\n" \ - "4. You tried to stop a recording while not recording.\n\n" \ + + +GKeyFile * +Toolbox_LoadToolsConf(void); + +gboolean +Toolbox_SaveToolsConf(GKeyFile *config); #endif /*_TOOLBOX_INT_H_*/ diff --git a/open-vm-tools/toolbox/toolboxRecord.c b/open-vm-tools/toolbox/toolboxRecord.c deleted file mode 100644 index 7fbfc2b19..000000000 --- a/open-vm-tools/toolbox/toolboxRecord.c +++ /dev/null @@ -1,145 +0,0 @@ -/********************************************************* - * Copyright (C) 2008 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. - * - *********************************************************/ - -/* - * toolboxRecord.c -- - * - * The record tab for the linux gtk toolbox. - */ - -#include - -#include "toolboxGtkInt.h" -#include "debug.h" -#include "statelogger_backdoor_def.h" - -static void RecordOnStart(gpointer, gpointer); -static void RecordOnStop(gpointer, gpointer); - -/* - *----------------------------------------------------------------------------- - * - * Record_Create -- - * - * Create, layout, and init the Record tab UI and all its widgets. - * - * Results: - * The Record tab widget (it's a vbox). - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -GtkWidget * -Record_Create(GtkWidget* mainWnd) -{ - GtkWidget *recordtab; - GtkWidget *hbox[2]; - GtkWidget *startbtn; - GtkWidget *stopbtn; - GtkWidget *label; - recordtab = gtk_vbox_new(FALSE, 50); - gtk_widget_show(recordtab); - gtk_container_set_border_width(GTK_CONTAINER(recordtab), 10); - hbox[0] = gtk_hbox_new(FALSE, 10); - hbox[1] = gtk_hbox_new(FALSE, 10); - label = - gtk_label_new("Press start or stop button to control recording."); - gtk_widget_show(hbox[0]); - gtk_widget_show(hbox[1]); - gtk_widget_show(label); - gtk_box_pack_start(GTK_BOX(recordtab), hbox[0], FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox[0]), label, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(recordtab), hbox[1], FALSE, FALSE, 0); -#ifdef GTK2 - startbtn = gtk_button_new_with_mnemonic("_Start"); - stopbtn = gtk_button_new_with_mnemonic("S_top"); -#else - startbtn = gtk_button_new_with_label("Start"); - stopbtn = gtk_button_new_with_label("Stop"); -#endif - gtk_widget_show(startbtn); - gtk_box_pack_start(GTK_BOX(hbox[1]), startbtn, FALSE, FALSE, 10); - gtk_widget_set_usize(startbtn, 70, 25); - gtk_signal_connect(GTK_OBJECT(startbtn), "clicked", - GTK_SIGNAL_FUNC(RecordOnStart), NULL); - gtk_widget_show(stopbtn); - gtk_box_pack_end(GTK_BOX(hbox[1]), stopbtn, FALSE, FALSE, 10); - gtk_widget_set_usize(stopbtn, 70, 6); - gtk_signal_connect(GTK_OBJECT(stopbtn), "clicked", - GTK_SIGNAL_FUNC(RecordOnStop), NULL); - - return recordtab; -} - - -/* - *----------------------------------------------------------------------------- - * - * RecordOnStart -- - * - * Callback for the gtk signal "clicked" on the Record tab's start - * button. - * - * Results: - * None. - * - * Side effects: - * Host VMware product starts recording this vm. - * - *----------------------------------------------------------------------------- - */ - -static void -RecordOnStart(gpointer btn, // IN: unused - gpointer data) // IN: unused -{ - if (GuestApp_ControlRecord(STATELOGGER_BKDR_START_LOGGING) == FALSE) { - ToolsMain_MsgBox(NULL, RECORD_VMX_ERR); - } -} - - -/* - *----------------------------------------------------------------------------- - * - * RecordOnStop -- - * - * Callback for the gtk signal "clicked" on the Record tab's stop - * button. - * - * Results: - * None. - * - * Side effects: - * Host VMware product stops recording this vm. - * - *----------------------------------------------------------------------------- - */ - -static void -RecordOnStop(gpointer btn, // IN: unused - gpointer data) // IN: unused -{ - if (GuestApp_ControlRecord(STATELOGGER_BKDR_STOP_LOGGING) == FALSE) { - ToolsMain_MsgBox(NULL, RECORD_VMX_ERR); - } -} - diff --git a/open-vm-tools/toolbox/toolboxScripts.c b/open-vm-tools/toolbox/toolboxScripts.c index 45a3d7bed..d0a7470cf 100644 --- a/open-vm-tools/toolbox/toolboxScripts.c +++ b/open-vm-tools/toolbox/toolboxScripts.c @@ -35,9 +35,12 @@ #include "toolboxGtkInt.h" #include "guestApp.h" #include "conf.h" +#include "debug.h" #include "str.h" +#include "posix.h" #include "procMgr.h" #include "file.h" +#include "util.h" /* * From toolboxInt.h @@ -51,7 +54,7 @@ GtkWidget *scriptsApply; /* X terminal application and option (to launch vi to edit scripts). */ static char *termApp; static char *termAppOption; -static GuestApp_Dict *confDict; +static GKeyFile *confDict; static GtkWidget *scriptsUseScript; static GtkWidget *scriptsDefaultScript; static GtkWidget *scriptsCustomScript; @@ -103,7 +106,7 @@ Scripts_Create(GtkWidget* mainWnd) GList *items = NULL; GSList *radiobtn_group = NULL; - confDict = Conf_Load(); + confDict = Toolbox_LoadToolsConf(); scriptstab = gtk_vbox_new(FALSE, 10); gtk_widget_show(scriptstab); @@ -144,7 +147,7 @@ Scripts_Create(GtkWidget* mainWnd) #ifdef GTK2 gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_COMBO(scriptsCombo)->entry); - scriptsUseScript = gtk_check_button_new_with_mnemonic("_Use Script"); + scriptsUseScript = gtk_check_button_new_with_mnemonic("Use Scr_ipt"); #else scriptsUseScript = gtk_check_button_new_with_label("Use Script"); #endif @@ -157,13 +160,13 @@ Scripts_Create(GtkWidget* mainWnd) GTK_SIGNAL_FUNC(Scripts_OnUseScriptToggled), NULL); /* - * Try to find an available x terminal app to launch vi. + * Try to find an available x terminal app to launch a text editor. * - * Options with different X terminal apps to launch vi: - * xterm -e vi foo.txt - * rxvt -e vi foo.txt - * konsole -e vi foo.txt - * gnome-terminal -x vi foo.txt + * Options with different X terminal apps to launch the editor: + * xterm -e [app] foo.txt + * rxvt -e [app] foo.txt + * konsole -e [app] foo.txt + * gnome-terminal -x [app] foo.txt */ termApp = NULL; termAppOption = "-e"; @@ -341,26 +344,33 @@ void Scripts_OnComboChanged(gpointer entry, // IN: the entry selected gpointer data) // IN: unused { - const char *path, *defaultPath; + const char *defaultPath; const char *currentState; + const char *confName = NULL; + gchar *path; + currentState = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(scriptsCombo)->entry)); if (strcmp(currentState, SCRIPT_SUSPEND) == 0) { - path = GuestApp_GetDictEntry(confDict, CONFNAME_SUSPENDSCRIPT); - defaultPath = GuestApp_GetDictEntryDefault(confDict, CONFNAME_SUSPENDSCRIPT); + confName = CONFNAME_SUSPENDSCRIPT; } else if (strcmp(currentState, SCRIPT_RESUME) == 0) { - path = GuestApp_GetDictEntry(confDict, CONFNAME_RESUMESCRIPT); - defaultPath = GuestApp_GetDictEntryDefault(confDict, CONFNAME_RESUMESCRIPT); + confName = CONFNAME_RESUMESCRIPT; } else if (strcmp(currentState, SCRIPT_OFF) == 0) { - path = GuestApp_GetDictEntry(confDict, CONFNAME_POWEROFFSCRIPT); - defaultPath = GuestApp_GetDictEntryDefault(confDict, CONFNAME_POWEROFFSCRIPT); + confName = CONFNAME_POWEROFFSCRIPT; } else if (strcmp(currentState, SCRIPT_ON) == 0) { - path = GuestApp_GetDictEntry(confDict, CONFNAME_POWERONSCRIPT); - defaultPath = GuestApp_GetDictEntryDefault(confDict, CONFNAME_POWERONSCRIPT); + confName = CONFNAME_POWERONSCRIPT; + } + + if (confName != NULL) { + path = g_key_file_get_string(confDict, "powerops", confName, NULL); + if (path == NULL) { + path = g_strdup(""); + } + defaultPath = GuestApp_GetDefaultScript(confName); } else { - path = ""; + path = g_strdup(""); defaultPath = ""; } - + gtk_signal_handler_block_by_func(GTK_OBJECT(scriptsUseScript), GTK_SIGNAL_FUNC(Scripts_OnUseScriptToggled), NULL); @@ -369,7 +379,7 @@ Scripts_OnComboChanged(gpointer entry, // IN: the entry selected NULL); if (strcmp(path, "") == 0) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(scriptsUseScript), FALSE); - path = defaultPath; + path = g_strdup(defaultPath); } else { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(scriptsUseScript), TRUE); } @@ -394,6 +404,7 @@ Scripts_OnComboChanged(gpointer entry, // IN: the entry selected GTK_SIGNAL_FUNC(Scripts_OnUseScriptToggled), NULL); Scripts_UpdateEnabled(); + g_free(path); } @@ -472,8 +483,9 @@ Scripts_OnApply(gpointer btn, // IN: unused gpointer data) // IN: unused { const char *path; + const char *defaultPath; const char *currentState; - char *confName; + const char *confName = NULL; Bool enabledUse, enabledDef; currentState = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(scriptsCombo)->entry)); @@ -486,9 +498,11 @@ Scripts_OnApply(gpointer btn, // IN: unused } else if (strcmp(currentState, SCRIPT_ON) == 0) { confName = CONFNAME_POWERONSCRIPT; } else { - confName = ""; + /* Not reached? */ + return; } + defaultPath = GuestApp_GetDefaultScript(confName); enabledUse = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(scriptsUseScript)); enabledDef = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(scriptsDefaultScript)); @@ -496,15 +510,15 @@ Scripts_OnApply(gpointer btn, // IN: unused path = ""; } else { if (enabledDef) { - path = GuestApp_GetDictEntryDefault(confDict, confName); + path = defaultPath; } else { path = gtk_editable_get_chars(GTK_EDITABLE(scriptsPath), 0, -1); } } - GuestApp_SetDictEntry(confDict, confName, path); + g_key_file_set_string(confDict, "powerops", confName, path); - GuestApp_WriteDict(confDict); + Toolbox_SaveToolsConf(confDict); Scripts_UpdateEnabled(); gtk_widget_set_sensitive(scriptsApply, FALSE); @@ -539,8 +553,10 @@ void Scripts_OnEdit(gpointer btn, // IN: unused gpointer data) // IN: unused { + char *editor; const char *scriptName; - char *cmd; + const char *argv[5]; + size_t idx = 0; if (!termApp) { ToolsMain_MsgBox("Error", "Unable to locate terminal application in " @@ -548,20 +564,46 @@ Scripts_OnEdit(gpointer btn, // IN: unused return; } - scriptName = gtk_entry_get_text(GTK_ENTRY(scriptsPath)); - cmd = Str_Asprintf(NULL, "%s %s vi %s >/dev/null 2>&1", - termApp, termAppOption, scriptName); - if (!cmd) { - ToolsMain_MsgBox("Error", "Failure creating command to edit script."); - return; + editor = Posix_Getenv("EDITOR"); + if (editor == NULL || strlen(editor) == 0) { + Debug("EDITOR not set, defaulting to vi.\n"); + free(editor); + editor = g_find_program_in_path("vi"); + if (editor == NULL) { + ToolsMain_MsgBox("Error", + "Cannot edit script because the an editor was not found."); + goto exit; + } else { + /* + * Make sure "editor" is allocated with malloc(), so that the call to + * free() later does the right thing. + */ + char *tmp = editor; + editor = Util_SafeStrdup(tmp); + g_free(tmp); + } } - if (!ProcMgr_ExecSync(cmd, NULL)) { - ToolsMain_MsgBox("Error", "Cannot edit script because the vi editor " - "was not found."); + scriptName = gtk_entry_get_text(GTK_ENTRY(scriptsPath)); + argv[idx++] = termApp; + if (termAppOption != NULL && strlen(termAppOption) > 0) { + argv[idx++] = termAppOption; + } + argv[idx++] = editor; + argv[idx++] = scriptName; + argv[idx++] = NULL; + + /* Restore the original LD_LIBRARY_PATH before invoking external apps. */ + if (!g_spawn_sync(NULL, (gchar **) argv, (gchar **) gNativeEnviron, + G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, NULL, NULL)) { + gchar *msg = g_strdup_printf("Cannot edit script: failed to execute editor %s.", + editor); + ToolsMain_MsgBox("Error", msg); + g_free(msg); } - free(cmd); +exit: + free(editor); } @@ -587,12 +629,22 @@ Scripts_OnRun(gpointer btn, // IN: unused gpointer data) // IN: unused { const char *scriptName; + gchar *cmd = NULL; scriptName = gtk_entry_get_text(GTK_ENTRY(scriptsPath)); - if (!ProcMgr_ExecSync(scriptName, NULL)) { + if (!g_path_is_absolute(scriptName)) { + char *toolsPath = GuestApp_GetInstallPath(); + ASSERT_MEM_ALLOC(toolsPath); + cmd = g_strdup_printf("%s%c%s", toolsPath, DIRSEPC, scriptName); + free(toolsPath); + } + + if (!ProcMgr_ExecSync((cmd != NULL) ? cmd : scriptName, NULL)) { ToolsMain_MsgBox("Error", "Failure executing script, please ensure the " "file exists and is executable."); } + + g_free(cmd); } diff --git a/open-vm-tools/toolbox/toolboxcmd-record.c b/open-vm-tools/toolbox/toolboxcmd-record.c deleted file mode 100644 index f759769d0..000000000 --- a/open-vm-tools/toolbox/toolboxcmd-record.c +++ /dev/null @@ -1,83 +0,0 @@ -/********************************************************* - * Copyright (C) 2008 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. - * - *********************************************************/ - -/* - * toolboxcmd-record.c -- - * - * Record operations for toolbox command line console. - */ - -#include "toolboxCmdInt.h" -#include "statelogger_backdoor_def.h" - - -/* - *----------------------------------------------------------------------------- - * - * Record_StartRecording -- - * - * Start recording process. - * - * Results: - * TRUE if the call is successful, FALSE otherwise. - * - * Side effects: - * Host VMware product starts recording this vm. - * - *----------------------------------------------------------------------------- - */ - -Bool -Record_StartRecording(void) -{ - if (GuestApp_ControlRecord(STATELOGGER_BKDR_START_LOGGING) == TRUE) { - return TRUE; - } else { - fprintf(stderr, RECORD_VMX_ERR); - return FALSE; - } -} - - -/* - *----------------------------------------------------------------------------- - * - * Record_StopRecording -- - * - * Stop recording process, - * - * Results: - * TRUE if the call is successful, FALSE otherwise. - * - * Side effects: - * Host VMware product stops recording this vm. - * - *----------------------------------------------------------------------------- - */ - -Bool -Record_StopRecording(void) -{ - if (GuestApp_ControlRecord(STATELOGGER_BKDR_STOP_LOGGING) == TRUE) { - return TRUE; - } else { - fprintf(stderr, RECORD_VMX_ERR); - return FALSE; - } -} - diff --git a/open-vm-tools/toolbox/toolboxcmd-scripts.c b/open-vm-tools/toolbox/toolboxcmd-scripts.c index 67ed129c9..2b3f170ae 100644 --- a/open-vm-tools/toolbox/toolboxcmd-scripts.c +++ b/open-vm-tools/toolbox/toolboxcmd-scripts.c @@ -22,12 +22,15 @@ * The scripts functions for the linux toolbox-cmd */ +#include #include #include #include #include +#include "system.h" #include "toolboxCmdInt.h" +#include "vmtools.h" #define SCRIPT_SUSPEND "suspend" @@ -43,7 +46,6 @@ typedef enum ScriptType { static int ScriptToggle(char *apm, Bool enable, int quiet_flag); static char* GetConfName(char *apm); static int GetConfEntry(char *apm, ScriptType type); -static int WriteDict(GuestApp_Dict *confDict, int quiet_flag); /* @@ -82,12 +84,13 @@ GetConfName(char *apm) // IN: apm name. /* *----------------------------------------------------------------------------- * - * GetConfEntry -- + * LoadConfFile -- * - * Gets the entry in the ConfDict. + * Loads the tools configuration file. If running as admin, tries to + * upgrade it if an old-style file is found. * * Results: - * The conf entry. + * A new GKeyFile *. If the conf file is not valid, it will be empty. * * Side effects: * None. @@ -95,66 +98,79 @@ GetConfName(char *apm) // IN: apm name. *----------------------------------------------------------------------------- */ -static int -GetConfEntry(char *apm, // IN: apm name - ScriptType type) // IN: Script type (default or current) +static GKeyFile * +LoadConfFile(void) { - const char *entry = NULL; - GuestApp_Dict *confDict; - char *confName; - confDict = Conf_Load(); - confName = GetConfName(apm); - if (!confName) { - fprintf(stderr, "Unknown operation\n"); - return EX_USAGE; - } - if (type == Default) { - entry = GuestApp_GetDictEntryDefault(confDict, confName); - } else if (type == Current) { - entry = GuestApp_GetDictEntry(confDict, confName); - } - if (entry) { - printf("%s\n", entry); - return EXIT_SUCCESS; - } else { - fprintf(stderr, "Error retreiving the path for script %s\n", apm); - return EX_TEMPFAIL; + gchar *confPath; + GKeyFile *confDict; + + confPath = VMTools_GetToolsConfFile(); + confDict = VMTools_LoadConfig(confPath, + G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, + System_IsUserAdmin()); + + if (confDict == NULL) { + confDict = g_key_file_new(); } + + g_free(confPath); + return confDict; } /* *----------------------------------------------------------------------------- * - * WriteDict -- + * GetConfEntry -- * - * Writes a ConfDict to the hard disk. + * Gets the entry in the config dictionary. * * Results: - * EXIT_SUCCESS on success. - * EXIT_TEMPFAIL on failure + * EXIT_SUCCESS on success, error code otherwise. * * Side effects: - * Writes the Dict to the hard disk. + * None. * *----------------------------------------------------------------------------- */ static int -WriteDict(GuestApp_Dict *confDict, // IN: The Dict to be writen - int quiet_flag) // IN: Verbosity flag. +GetConfEntry(char *apm, // IN: apm name + ScriptType type) // IN: Script type (default or current) { - if (!GuestApp_WriteDict(confDict)) { - fprintf(stderr, "Unable to write dictionary\n"); - GuestApp_FreeDict(confDict); - return EX_TEMPFAIL; - } else { - if (!quiet_flag) { - printf("Script Set\n"); + gchar *entry = NULL; + GKeyFile *confDict = NULL; + char *confName; + int ret; + + confName = GetConfName(apm); + if (!confName) { + fprintf(stderr, "Unknown operation\n"); + return EX_USAGE; + } + + confDict = LoadConfFile(); + + if (type == Default) { + entry = g_strdup(GuestApp_GetDefaultScript(confName)); + } else if (type == Current) { + entry = g_key_file_get_string(confDict, "powerops", confName, NULL); + if (entry == NULL) { + entry = g_strdup(GuestApp_GetDefaultScript(confName)); } - GuestApp_FreeDict(confDict); - return EXIT_SUCCESS; } + + if (strlen(entry) > 0) { + printf("%s\n", entry); + ret = EXIT_SUCCESS; + } else { + fprintf(stderr, "No script for operation %s\n", apm); + ret = EX_TEMPFAIL; + } + + g_free(entry); + g_key_file_free(confDict); + return ret; } @@ -234,8 +250,11 @@ ScriptToggle(char *apm, // IN: APM name { const char *path; char *confName; - GuestApp_Dict *confDict; - confDict = Conf_Load(); + gchar *confPath; + int ret = EXIT_SUCCESS; + GKeyFile *confDict; + GError *err = NULL; + confName = GetConfName(apm); if (!confName) { @@ -243,15 +262,25 @@ ScriptToggle(char *apm, // IN: APM name return EX_USAGE; } + confDict = LoadConfFile(); + if (!enable) { path = ""; } else { - path = GuestApp_GetDictEntryDefault (confDict, confName); + path = GuestApp_GetDefaultScript(confName); } - GuestApp_SetDictEntry(confDict, confName, path); + g_key_file_set_string(confDict, "powerops", confName, path); + confPath = VMTools_GetToolsConfFile(); + if (!VMTools_WriteConfig(confPath, confDict, &err)) { + fprintf(stderr, "Error writing config: %s\n", err->message); + g_clear_error(&err); + ret = EX_TEMPFAIL; + } - return WriteDict(confDict, quiet_flag); + g_key_file_free(confDict); + g_free(confPath); + return ret; } @@ -328,20 +357,35 @@ Script_Set(char *apm, // IN: APM name int quiet_flag) // IN: Verbosity flag { char *confName; - GuestApp_Dict *confDict; + int ret = EXIT_SUCCESS; + gchar *confPath = NULL; + GKeyFile *confDict = NULL; + GError *err = NULL; + if (!File_Exists(path)) { fprintf(stderr, "%s doesn't exists\n", path); return EX_OSFILE; } - confDict = Conf_Load(); + confName = GetConfName(apm); if (!confName) { fprintf(stderr, "Unknown operation\n"); return EX_USAGE; } - GuestApp_SetDictEntry(confDict, confName, path); - return WriteDict(confDict, quiet_flag); + confPath = VMTools_GetToolsConfFile(); + confDict = LoadConfFile(); + + g_key_file_set_string(confDict, "powerops", confName, path); + + if (!VMTools_WriteConfig(confPath, confDict, &err)) { + fprintf(stderr, "Error writing config: %s\n", err->message); + g_clear_error(&err); + ret = EX_TEMPFAIL; + } + g_key_file_free(confDict); + g_free(confPath); + return ret; } diff --git a/open-vm-tools/vmware-user/Makefile.am b/open-vm-tools/vmware-user/Makefile.am index e2ef67b31..814386bff 100644 --- a/open-vm-tools/vmware-user/Makefile.am +++ b/open-vm-tools/vmware-user/Makefile.am @@ -19,13 +19,21 @@ bin_PROGRAMS = vmware-user AM_CFLAGS = AM_CFLAGS += @GTK_CPPFLAGS@ +if HAVE_GTKMM + AM_CFLAGS += @GTKMM_CPPFLAGS@ +endif AM_CFLAGS += @VMWARE_USER_CPPFLAGS@ AM_CFLAGS += -I$(top_builddir)/include AM_CFLAGS += -DRESOLUTION_X11 +AM_CFLAGS += -I$(top_srcdir)/services/plugins/vix + +AM_CXXFLAGS = $(AM_CFLAGS) vmware_user_LDADD = vmware_user_LDADD += ../lib/conf/libConf.la -vmware_user_LDADD += ../lib/dnd/libDnd.la +if HAVE_GTKMM + vmware_user_LDADD += ../lib/dndGuest/libDndGuest.la +endif vmware_user_LDADD += ../lib/eventManager/libEventManager.la vmware_user_LDADD += ../lib/file/libFile.la if ENABLE_UNITY @@ -70,6 +78,7 @@ vmware_user_LDADD += ../lib/err/libErr.la vmware_user_LDADD += ../lib/foundryMsg/libFoundryMsg.la vmware_user_LDADD += ../lib/backdoor/libBackdoor.la vmware_user_LDADD += ../lib/dict/libDict.la +vmware_user_LDADD += ../lib/dnd/libDnd.la vmware_user_LDADD += ../lib/dynxdr/libDynxdr.la vmware_user_LDADD += ../lib/hgfs/libHgfs.la vmware_user_LDADD += ../lib/message/libMessage.la @@ -87,7 +96,14 @@ if ENABLE_UNITY vmware_user_LDADD += ../lib/region/libRegion.la endif vmware_user_LDADD += -lcrypt +vmware_user_LDADD += -lstdc++ +if LINUX +vmware_user_LDADD += -lgcc_s +endif vmware_user_LDADD += @GTK_LIBS@ +if HAVE_GTKMM + vmware_user_LDADD += @GTKMM_LIBS@ +endif vmware_user_LDADD += @VMWARE_USER_LDADD@ vmware_user_LDADD += @COMMON_XLIBS@ if ENABLE_UNITY @@ -98,14 +114,21 @@ endif vmware_user_SOURCES = vmware_user_SOURCES += copyPaste.c +if HAVE_GTKMM + vmware_user_SOURCES += copyPasteUI.cpp +endif +vmware_user_SOURCES += copyPasteWrapper.cpp vmware_user_SOURCES += debugStdio.c vmware_user_SOURCES += dnd.c vmware_user_SOURCES += foreignVMToolsDaemon.c vmware_user_SOURCES += foreignVMToolsNetworking.c -vmware_user_SOURCES += foundryToolsDaemon.c +vmware_user_SOURCES += $(top_srcdir)/services/plugins/vix/foundryToolsDaemon.c vmware_user_SOURCES += notify.c vmware_user_SOURCES += pointer.c -vmware_user_SOURCES += vmware-user.c +if HAVE_GTKMM + vmware_user_SOURCES += stringxx/string.cc +endif +vmware_user_SOURCES += vmware-user.cpp if HAVE_ICU vmware_user_LDADD += @ICU_LIBS@ @@ -118,7 +141,7 @@ endif install-exec-hook: $(INSTALL) -d $(DESTDIR)$(datadir)/applications/ - $(INSTALL) -m 644 ../scripts/common/vmware-user.desktop \ + $(INSTALL) -m 644 $(top_srcdir)/scripts/common/vmware-user.desktop \ $(DESTDIR)$(datadir)/applications/ uninstall-hook: - -$(RM) -f $(DESTDIR)$(datadir)/applications/vmware-user.desktop + rm -f $(DESTDIR)$(datadir)/applications/vmware-user.desktop diff --git a/open-vm-tools/vmware-user/copyPaste.cpp b/open-vm-tools/vmware-user/copyPaste.cpp new file mode 100644 index 000000000..75ba68661 --- /dev/null +++ b/open-vm-tools/vmware-user/copyPaste.cpp @@ -0,0 +1,2088 @@ +/********************************************************* + * Copyright (C) 2005 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. + * + *********************************************************/ + +/* + * copyPaste.c -- + * + * Set of functions in guest side for copy/paste (both file and text). + * Currently there are 2 versions copy/paste. Version 1 only supports + * text copy/paste, and based on backdoor cmd. Version 2 supports both + * text and file copy/paste, and based on guestRPC. + * + * G->H Text Copy/Paste (version 1) + * -------------------- + * When Ungrab, CopyPaste_RequestSelection got called, which try to get + * selection text and send to backdoor. + * + * H->G Text Copy/Paste (version 1) + * -------------------- + * When grab, CopyPaste_GetBackdoorSelections got called, which first + * get host selection text, then claim as selection owner. If some app + * asks for selection, CopyPasteSelectionGetCB will reply with host + * selection text. + * + * G->H Copy/Paste (version 2) + * -------------------- + * When Ungrab, host vmx will send out rpc command "copypaste.gh.data.get", + * which handler is CopyPasteRpcInGHSetDataCB. It first tries to get selection + * contents and sends it back as rpc result. For file transfer, host vmx + * will transfer files with hgFileCopy lib. Function CopyPasteGHFileListGetNext + * will handle the file list during transfer. + * + * H->G Copy/Paste (version 2) + * -------------------- + * When grab, host vmx will send host selection content to guest with + * out rpc command "copypaste.hg.data.set", which handler is + * CopyPasteRpcInHGSetDataCB. It first keeps a copy then claim as selection + * owner. If some app asks for files, CopyPasteSelectionGetCB will ask host + * vmx to transfer files into a temp dir with rpc "copypaste.hgCopyFiles", + * then reply with host selection file list. For KDE and gnome the file list + * format is different. If someapp asks for text, CopyPasteSelectionGetCB + * will provide the data. + */ + +#include "vmwareuserInt.h" +#include +#include +#include +#include +#include + +#include "vm_assert.h" +#include "debug.h" +#include "str.h" +#include "strutil.h" +#include "vm_app.h" +#include "eventManager.h" +#include "guestApp.h" +#include "dnd.h" +#include "util.h" +#include "cpName.h" +#include "cpNameUtil.h" +#include "guestInfoServer.h" +#include "vmblock.h" +#include "file.h" +#include "codeset.h" +#include "escape.h" +#include "hostinfo.h" +#include "wiper.h" + + +/* + * Gtk 1.2 doesn't know about the CLIPBOARD selection, but that doesn't matter, we + * just create the atom we need directly in main(). + */ +#ifndef GDK_SELECTION_CLIPBOARD +GdkAtom GDK_SELECTION_CLIPBOARD; +#endif + +#ifndef GDK_SELECTION_TYPE_TIMESTAMP +GdkAtom GDK_SELECTION_TYPE_TIMESTAMP; +#endif + +#ifndef GDK_SELECTION_TYPE_UTF8_STRING +GdkAtom GDK_SELECTION_TYPE_UTF8_STRING; +#endif + +typedef struct FCPGHState { + char *fileList; + char *fileListNext; + size_t fileListSize; +} FCPGHState; + +/* + * Currently there are 2 versions copy/paste. + * Key points in copy/paste version 1: + * 1. Only text copy/paste + * 2. copy/paste is based on backdoor directly + * + * Key points in copy/paste version 2: + * 1. Support both file/text copy/paste + * 2. Both file/text copy/paste are based on guestRPC + */ +static int32 gVmxCopyPasteVersion = 1; + +/* + * Getting a selection is an asyncronous event, so we have to keep track of both + * selections globablly in order to decide which one to use. + */ +static Bool gWaitingOnGuestSelection = FALSE; +static char gGuestSelPrimaryBuf[MAX_SELECTION_BUFFER_LENGTH]; +static char gGuestSelClipboardBuf[MAX_SELECTION_BUFFER_LENGTH]; +static uint64 gGuestSelPrimaryTime = 0; +static uint64 gGuestSelClipboardTime = 0; +static char gHostClipboardBuf[MAX_SELECTION_BUFFER_LENGTH]; + +/* Guest->Host state. */ +FCPGHState gFcpGHState; +/* RPC buffer for Guest->Host FCP. */ +static char *gGHFCPRpcResultBuffer; +/* File list size for Guest->Host FCP. */ +static size_t gGHFCPListSize; +static Bool gHGFCPPending; +/* Current selection is text or file list (for FCP). */ +static Bool gHGIsClipboardFCP; +/* + * Total file size in selection list. This is used to check if there is enough + * space in guest OS for host->guest file transfer. + */ +static uint64 gHGFCPTotalSize; + +static GdkAtom gFCPAtom[NR_FCP_TARGETS]; + +/* Host->guest file transfer status, used for sync between transfer and paste. */ +int gHGFCPFileTransferStatus; + +static char gFileRoot[DND_MAX_PATH]; +static size_t gFileRootSize; +static Bool gIsOwner; +static VmTimeType gHGGetListTime; + +/* + * Forward Declarations + */ +static INLINE void CopyPasteStateInit(void); +static void CopyPasteSelectionReceivedCB(GtkWidget *widget, + GtkSelectionData *selection_data, + gpointer data); +static void CopyPasteSelectionGetCB(GtkWidget *widget, + GtkSelectionData *selection_data, + guint info, + guint time_stamp, + gpointer data); +static gint CopyPasteSelectionClearCB(GtkWidget *widget, + GdkEventSelection *event, + gpointer data); + +static void CopyPasteSetBackdoorSelections(void); +static Bool CopyPasteRpcInGHSetDataCB(char const **result, size_t *resultLen, + const char *name, const char *args, + size_t argsSize,void *clientData); +static Bool CopyPasteRpcInHGSetDataCB(char const **result, size_t *resultLen, + const char *name, const char *args, + size_t argsSize,void *clientData); + +static INLINE void CopyPasteGHFileListClear(void); +static INLINE void CopyPasteGHFileListSet(char *fileList, size_t fileListSize); + +/* This struct is only used by CopyPasteSelectionRemoveTarget. */ +struct SelectionTargetList { + GdkAtom selection; + GtkTargetList *list; +}; + + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteSelectionRemoveTarget -- + * + * To remove a target from a selection target list. The reason to develop + * this function is that in gtk there is only gtk_selection_add_target to + * add supported target to selection list, but no function to remove one. + * + * Results: + * None. + * + * Side effects: + * If no more target, the selection list will be removed too. + * + *----------------------------------------------------------------------------- + */ + +void +CopyPasteSelectionRemoveTarget(GtkWidget *widget, + GdkAtom selection, + GdkAtom target) +{ + const char *selection_handler_key = "gtk-selection-handlers"; + struct SelectionTargetList *targetList; + GList *tempList; + GList *selectionLists; + + /* Get selection list. */ + selectionLists = gtk_object_get_data(GTK_OBJECT (widget), selection_handler_key); + tempList = selectionLists; + while (tempList) { + /* Enumerate the list to find the selection. */ + targetList = tempList->data; + if (targetList->selection == selection) { + /* Remove target. */ + gtk_target_list_remove(targetList->list, target); + /* If no more target, remove selection from list. */ + if (!targetList->list->list) { + /* Free target list. */ + gtk_target_list_unref(targetList->list); + g_free(targetList); + /* Remove and free selection node. */ + selectionLists = g_list_remove_link(selectionLists, tempList); + g_list_free_1(tempList); + } + break; + } + tempList = tempList->next; + } + /* Put new selection list back. */ + gtk_object_set_data (GTK_OBJECT (widget), selection_handler_key, selectionLists); +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPaste_RequestSelection -- + * + * Request the guest's text clipboard (asynchronously), we'll give it to + * the host when the request completes. For version 1 guest->host text + * copy/paste. + * + * Results: + * None. + * + * Side effects: + * The owner of the clipboard will get a request from our application. + * + *----------------------------------------------------------------------------- + */ + +void +CopyPaste_RequestSelection(void) +{ + if (gVmxCopyPasteVersion > 1) { + return; + } + + /* + * Ask for both the PRIMARY and CLIPBOARD selections. + */ + gGuestSelPrimaryBuf[0] = '\0'; + gGuestSelClipboardBuf[0] = '\0'; + + /* Only send out request if we are not the owner. */ + if (!gIsOwner) { + /* Try to get utf8 text from primary and clipboard. */ + gWaitingOnGuestSelection = TRUE; + gtk_selection_convert(gUserMainWidget, + GDK_SELECTION_PRIMARY, + GDK_SELECTION_TYPE_UTF8_STRING, + GDK_CURRENT_TIME); + while (gWaitingOnGuestSelection) gtk_main_iteration(); + + gWaitingOnGuestSelection = TRUE; + gtk_selection_convert(gUserMainWidget, + GDK_SELECTION_CLIPBOARD, + GDK_SELECTION_TYPE_UTF8_STRING, + GDK_CURRENT_TIME); + while (gWaitingOnGuestSelection) gtk_main_iteration(); + + if (gGuestSelPrimaryBuf[0] == '\0' && gGuestSelClipboardBuf[0] == '\0') { + /* + * If we cannot get utf8 text, try to get localized text from primary + * and clipboard. + */ + gWaitingOnGuestSelection = TRUE; + gtk_selection_convert(gUserMainWidget, + GDK_SELECTION_PRIMARY, + GDK_SELECTION_TYPE_STRING, + GDK_CURRENT_TIME); + while (gWaitingOnGuestSelection) gtk_main_iteration(); + + gWaitingOnGuestSelection = TRUE; + gtk_selection_convert(gUserMainWidget, + GDK_SELECTION_CLIPBOARD, + GDK_SELECTION_TYPE_STRING, + GDK_CURRENT_TIME); + while (gWaitingOnGuestSelection) gtk_main_iteration(); + } + } + /* Send text to host. */ + Debug("CopyPaste_RequestSelection: Prim is [%s], Clip is [%s]\n", + gGuestSelPrimaryBuf, gGuestSelClipboardBuf); + CopyPasteSetBackdoorSelections(); +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteSelectionReceivedCB -- + * + * Callback for the gtk signal "selection_recieved". + * Called because we previously requested a copy/paste selection and + * finally got results of that asynchronous operation. After some basic + * sanity checks, send the result (in selection_data) thru the backdoor + * (version 1) or guestRPC (version 2) so the vmx can copy it to host + * clipboard. + * + * We made several requests for selections, the string (actual data) and + * file list for each of PRIMARY and CLIPBOARD selections. So this funtion + * will get called several times, once for each request. + * + * For guest->host copy/paste (both text and file). + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +void +CopyPasteSelectionReceivedCB(GtkWidget *widget, // IN: unused + GtkSelectionData *selection_data, // IN: requested data + gpointer data) // IN: unused +{ + char *target; + char *utf8Str = NULL; + size_t len; + size_t aligned_len; + + if ((widget == NULL) || (selection_data == NULL)) { + Debug("CopyPasteSelectionReceivedCB: Error, widget or selection_data is invalid\n"); + goto exit; + } + + if (selection_data->length < 0) { + Debug("CopyPasteSelectionReceivedCB: Error, length less than 0\n"); + goto exit; + } + + /* Try to get clipboard or selection timestamp. */ + if (selection_data->target == GDK_SELECTION_TYPE_TIMESTAMP) { + if (selection_data->selection == GDK_SELECTION_PRIMARY) { + if (selection_data->length == 4) { + gGuestSelPrimaryTime = *(uint32 *)selection_data->data; + Debug("CopyPasteSelectionReceivedCB: Got pri time [%"FMT64"u]\n", + gGuestSelPrimaryTime); + } else if (selection_data->length == 8) { + gGuestSelPrimaryTime = *(uint64 *)selection_data->data; + Debug("CopyPasteSelectionReceivedCB: Got pri time [%"FMT64"u]\n", + gGuestSelPrimaryTime); + } else { + Debug("CopyPasteSelectionReceivedCB: Unknown pri time. Size %d\n", + selection_data->length); + } + } + if (selection_data->selection == GDK_SELECTION_CLIPBOARD) { + if (selection_data->length == 4) { + gGuestSelClipboardTime = *(uint32 *)selection_data->data; + Debug("CopyPasteSelectionReceivedCB: Got clip time [%"FMT64"u]\n", + gGuestSelClipboardTime); + } else if (selection_data->length == 8) { + gGuestSelClipboardTime = *(uint64 *)selection_data->data; + Debug("CopyPasteSelectionReceivedCB: Got clip time [%"FMT64"u]\n", + gGuestSelClipboardTime); + } else { + Debug("CopyPasteSelectionReceivedCB: Unknown clip time. Size %d\n", + selection_data->length); + } + } + goto exit; + } + + if (selection_data->selection == GDK_SELECTION_PRIMARY) { + target = gGuestSelPrimaryBuf; + } else if (selection_data->selection == GDK_SELECTION_CLIPBOARD) { + target = gGuestSelClipboardBuf; + } else { + goto exit; + } + + utf8Str = selection_data->data; + len = strlen(selection_data->data); + + if (selection_data->target != GDK_SELECTION_TYPE_STRING && + selection_data->target != GDK_SELECTION_TYPE_UTF8_STRING) { + /* It is a file list. */ + if (len >= MAX_SELECTION_BUFFER_LENGTH - 1) { + Warning("CopyPasteSelectionReceivedCB file list too long\n"); + } else { + memcpy(target, selection_data->data, len + 1); + } + goto exit; + } + + /* + * If target is GDK_SELECTION_TYPE_STRING, assume encoding is local code + * set. Convert to utf8 before send to vmx. + */ + if (selection_data->target == GDK_SELECTION_TYPE_STRING && + !CodeSet_CurrentToUtf8(selection_data->data, + selection_data->length, + &utf8Str, + &len)) { + Debug("CopyPasteSelectionReceivedCB: Couldn't convert to utf8 code set\n"); + gWaitingOnGuestSelection = FALSE; + return; + } + + /* + * String in backdoor communication is 4 bytes by 4 bytes, so the len + * should be aligned to 4; + */ + aligned_len = (len + 4) & ~3; + if (aligned_len >= MAX_SELECTION_BUFFER_LENGTH) { + /* With alignment, len is still possible to be less than max. */ + if (len < (MAX_SELECTION_BUFFER_LENGTH - 1)) { + memcpy(target, utf8Str, len + 1); + } else { + memcpy(target, utf8Str, MAX_SELECTION_BUFFER_LENGTH - 1); + target[MAX_SELECTION_BUFFER_LENGTH - 1] ='\0'; + } + } else { + memcpy(target, utf8Str, len + 1); + } + +exit: + if (selection_data->target == GDK_SELECTION_TYPE_STRING) { + free(utf8Str); + } + gWaitingOnGuestSelection = FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteSelectionGetCB -- + * + * Callback for the gtk signal "selection_get". + * This is called when some other app requests the copy/paste selection, + * probably because we declare oursleves the selection owner on mouse + * grab. In text copy/paste case, we simply respond with contents of + * gHostClipboardBuf, which should have been set on mouse grab. In file + * copy/paste case, send file transfer request to host vmx, then return + * file list with right format according to different request. + * For host->guest copy/paste (both text and file). + * + * Results: + * None + * + * Side effects: + * An X message is sent to the requesting app containing the data, it + * will likely act on it in some way. In FCP case, may first start a + * host->guest file transfer. Add block if blocking driver is available, + * otherwise wait till file copy done. + * + *----------------------------------------------------------------------------- + */ + +void +CopyPasteSelectionGetCB(GtkWidget *widget, // IN: unused + GtkSelectionData *selection_data, // IN: requested type + // OUT:the data to be sent + guint info, // IN: unused + guint time_stamp, // IN: unsued + gpointer data) // IN: unused +{ + const char *begin = NULL; + const char *end = NULL; + const char *next = NULL; + const char *pre = NULL; + const char *post = NULL; + size_t preLen = 0; + size_t postLen = 0; + size_t len = 0; + char *text = NULL; + size_t textLen = 1; + Bool blockAdded = FALSE; + Bool gnomeFCP = FALSE; + VmTimeType curTime; + + if ((widget == NULL) || (selection_data == NULL)) { + Debug("CopyPasteSelectionGetCB: Error, widget or selection_data is invalid\n"); + return; + } + + /* If it is text copy paste, return gHostClipboardBuf. */ + if (GDK_SELECTION_TYPE_STRING == selection_data->target || + GDK_SELECTION_TYPE_UTF8_STRING == selection_data->target) { + char *outBuf = gHostClipboardBuf; + size_t len = strlen(gHostClipboardBuf); + + /* + * If target is GDK_SELECTION_TYPE_STRING, assume encoding is local code + * set. Convert from utf8 to local one. + */ + if (GDK_SELECTION_TYPE_STRING == selection_data->target && + !CodeSet_Utf8ToCurrent(gHostClipboardBuf, + strlen(gHostClipboardBuf), + &outBuf, + &len)) { + Debug("CopyPasteSelectionGetCB: can not convert to current codeset\n"); + return; + } + + gtk_selection_data_set(selection_data, selection_data->target, 8, + outBuf, len); + Debug("CopyPasteSelectionGetCB: Set text [%s]\n", outBuf); + + if (GDK_SELECTION_TYPE_STRING == selection_data->target) { + free(outBuf); + } + + return; + } + + if (selection_data->target != gFCPAtom[FCP_TARGET_INFO_URI_LIST] && + selection_data->target != gFCPAtom[FCP_TARGET_INFO_GNOME_COPIED_FILES]) { + Debug("CopyPasteSelectionGetCB: Got unknown target\n"); + return; + } + + if (!gHGIsClipboardFCP) { + Debug("CopyPasteSelectionGetCB: no file list available\n"); + return; + } + + /* + * KDE may ask for clipboard content right after clipboard owner changed, + * and cause unexpected HG file copy. So HG FCP will return nothing for 1 + * second after switch from host OS to guest OS. Please refer to bug 301971. + */ + Hostinfo_GetTimeOfDay(&curTime); + if (curTime - gHGGetListTime < FCP_COPY_DELAY) { + Debug("%s: waiting for delay\n", __FUNCTION__); + return; + } + + if (gHGFCPFileTransferStatus == FCP_FILE_TRANSFER_NOT_YET) { + if (GuestInfo_GetAvailableDiskSpace(gFileRoot) < gHGFCPTotalSize) { + Debug("CopyPasteSelectionGetCB no enough space to copy file from host.\n"); + return; + } + /* Send host a rpc to start file transfer. */ + if (!GuestApp_RpcSendOneCPName("copypaste.hg.copy.files", ' ', + gFileRoot, gFileRootSize)) { + Debug("CopyPasteSelectionGetCB: failed sending copypaste.hg.copy.files " + "with CPName"); + return; + } + gHGFCPFileTransferStatus = FCP_FILE_TRANSFERRING; + } + + if (gBlockFd > 0) { + /* Add a block on the staging directory for this command. */ + if (DnD_AddBlock(gBlockFd, gFileRoot)) { + Debug("CopyPasteSelectionGetCB: add block [%s].\n", gFileRoot); + blockAdded = TRUE; + } else { + Warning("CopyPasteSelectionGetCB: Unable to add block [%s].\n", gFileRoot); + } + } + + if (!blockAdded) { + /* + * If there is no blocking driver, wait here till file copy is done. + * 2 reasons to keep this: + * 1. If run vmware-user stand-alone as non-root, blocking driver can not + * be opened. Debug purpose only. + * 2. Other platforms (Solaris, FreeBSD, etc) may also use this code, and there + * is no blocking driver yet. + */ + Debug("CopyPasteSelectionGetCB no blocking driver, waiting for " + "HG file copy done ...\n"); + while (gHGFCPFileTransferStatus != FCP_FILE_TRANSFERRED) { + struct timeval tv; + int nr; + + tv.tv_sec = 0; + nr = EventManager_ProcessNext(gEventQueue, (uint64 *)&tv.tv_usec); + if (nr != 1) { + Debug("CopyPasteSelectionGetCB unexpected end of loop: returned " + "value is %d.\n", nr); + return; + } + if (select(0, NULL, NULL, NULL, &tv) == -1) { + Debug("CopyPasteSelectionGetCB error in select (%s).\n", + strerror(errno)); + return; + } + } + + Debug("CopyPasteSelectionGetCB file transfer done!\n"); + } + + /* Setup the format string components */ + if (selection_data->target == gFCPAtom[FCP_TARGET_INFO_URI_LIST]) { + Debug("CopyPasteSelectionGetCB Got uri_list request!\n"); + pre = DND_URI_LIST_PRE_KDE; + preLen = sizeof DND_URI_LIST_PRE_KDE - 1; + post = DND_URI_LIST_POST; + postLen = sizeof DND_URI_LIST_POST - 1; + } + if (selection_data->target == gFCPAtom[FCP_TARGET_INFO_GNOME_COPIED_FILES]) { + Debug("CopyPasteSelectionGetCB Got gnome_copied request!\n"); + pre = FCP_GNOME_LIST_PRE; + preLen = sizeof FCP_GNOME_LIST_PRE - 1; + post = FCP_GNOME_LIST_POST; + postLen = sizeof FCP_GNOME_LIST_POST - 1; + gnomeFCP = TRUE; + + textLen += 5; + text = Util_SafeRealloc(text, textLen); + Str_Snprintf(text, 6, "copy\n"); + } + + if (!pre) { + Debug("CopyPasteSelectionGetCB: invalid drag target info\n"); + return; + } + + + /* + * Set begin to first non-NUL character and end to last NUL character to + * prevent errors in calling CPName_GetComponent(). + */ + for(begin = gHostClipboardBuf; *begin == '\0'; begin++) + ; + end = CPNameUtil_Strrchr(gHostClipboardBuf, gGHFCPListSize + 1, '\0'); + ASSERT(end); + + /* Build up selection data */ + while ((len = CPName_GetComponent(begin, end, &next)) != 0) { + const size_t origTextLen = textLen; + Bool freeBegin = FALSE; + + if (len < 0) { + Debug("CopyPasteSelectionGetCB: error getting next component\n"); + if (text) { + free(text); + } + return; + } + + /* + * A URI list will expect the provided path to be escaped. If we cannot + * escape the path for some reason we just use the unescaped version and + * hope that it works. + */ + if (selection_data->target == gFCPAtom[FCP_TARGET_INFO_URI_LIST]) { + size_t newLen; + char *escapedComponent; + int escIndex; + int bytesToEsc[256] = { 0, }; + + /* We escape the following characters based on RFC 1630. */ + bytesToEsc['#'] = 1; + bytesToEsc['?'] = 1; + bytesToEsc['*'] = 1; + bytesToEsc['!'] = 1; + bytesToEsc['%'] = 1; /* Escape character */ + + /* Escape non-ASCII characters so we can pass UTF-8 filenames */ + for (escIndex = 0x80; escIndex < 0x100; escIndex++) { + bytesToEsc[escIndex] = 1; + } + + escapedComponent = Escape_Do('%', bytesToEsc, begin, len, &newLen); + if (escapedComponent) { + begin = escapedComponent; + len = newLen; + freeBegin = TRUE; + } + } + + /* + * Append component. NUL terminator was accounted for by initializing + * textLen to one above. + */ + textLen += preLen + len + postLen; + text = Util_SafeRealloc(text, textLen); + + /* + * Bug 143147: Gnome FCP does not like the trailing newlines. We don't + * have this problem for targets that ask for URI lists. So we don't see + * this problem on: + * - KDE which asks for URI lists during FCP + * - DnD in both Gnome and KDE since they ask for URI lists. + * + * This is a problem only for Gnome FCP which expects a specially + * formatted 'copy' command string containing the file list which it then + * converts into a URI list internally. + */ + Str_Snprintf(text + origTextLen - 1, + textLen - origTextLen + 1, + "%s%s%s", pre, begin, gnomeFCP && next == end ? "" : post); + + if (freeBegin) { + free((void *)begin); + } + + /* Iterate to next component */ + begin = next; + } + + /* + * Send out the data using the selection system. When sending a string, GTK will + * ensure that a null terminating byte is added to the end so we do not need to + * add it. GTK also copies the data so the original will never be modified. + */ + Debug("CopyPasteSelectionGetCB: set file list [%s]\n", text); + gtk_selection_data_set(selection_data, selection_data->target, + 8, /* 8 bits per character. */ + text, textLen); + free(text); +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteSelectionClearCB -- + * + * Callback for the gtk signal "selection_clear". + * + * Results: + * Always TRUE. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static gint +CopyPasteSelectionClearCB(GtkWidget *widget, // IN: unused + GdkEventSelection *event, // IN: unused + gpointer data) // IN: unused +{ + Debug("CopyPasteSelectionClearCB got clear signal\n"); + gIsOwner = FALSE; + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteSetBackdoorSelections -- + * + * Set the clipboard one of two ways, the old way or the new way. + * The old way uses GuestApp_SetSel and there's only one selection. + * Set backdoor selection with either primary selection or clipboard. + * The primary selection is the first priority, then clipboard. + * If both unavailable, set backdoor selection length to be 0. + * This will be used by older VMXs or VMXs on Windows hosts (which + * has only one clipboard). Doing this gives us backwards + * compatibility. + * + * The new way uses new sets both PRIMARY and CLIPBOARD. Newer Linux + * VMXs will use these rather than the above method and have the two + * selections set separately. + * + * XXX: The "new way" doesn't exist yet, the vmx has no support for it. + * + * Results: + * None. + * + * Side effects: + * The VMX probably changes some string buffers. + * + *----------------------------------------------------------------------------- + */ + +void +CopyPasteSetBackdoorSelections(void) +{ + uint32 const *p; + size_t len; + size_t aligned_len; + size_t primaryLen; + size_t clipboardLen; + unsigned int i; + + primaryLen = strlen(gGuestSelPrimaryBuf); + clipboardLen = strlen(gGuestSelClipboardBuf); + + if (primaryLen) { + /* + * Send primary selection to backdoor if it exists. + */ + p = (uint32 const *)gGuestSelPrimaryBuf; + } else if (clipboardLen) { + /* + * Otherwise send clipboard to backdoor if it exists. + */ + p = (uint32 const *)gGuestSelClipboardBuf; + } else { + /* + * Neither selection is set + */ + p = NULL; + } + + if (p == NULL) { + GuestApp_SetSelLength(0); + Debug("CopyPasteSetBackdoorSelections Set empty text.\n"); + } else { + len = strlen((char *)p); + Debug("CopyPasteSetBackdoorSelections Set text [%s].\n", (char *)p); + aligned_len = (len + 4) & ~3; + + /* Here long string should already be truncated. */ + ASSERT(aligned_len <= MAX_SELECTION_BUFFER_LENGTH); + + GuestApp_SetSelLength(len); + for (i = 0; i < len; i += 4, p++) { + GuestApp_SetNextPiece(*p); + } + } +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPaste_GetBackdoorSelections -- + * + * Get the clipboard "the old way". + * The old way uses GuestApp_SetSel and there's only one selection. + * We don't have to do anything for the "new way", since the host + * will just push PRIMARY and/or CLIPBOARD when they are available + * on the host. + * + * XXX: the "new way" isn't availble yet because the vmx doesn't + * implement separate clipboards. Even when it does this + * function will still exist for backward compatibility + * + * Results: + * TRUE if selection length>=0, FLASE otherwise. + * + * Side effects: + * This application becomes the selection owner for PRIMARY and/or + CLIPBOARD selections. + * + *----------------------------------------------------------------------------- + */ + +Bool +CopyPaste_GetBackdoorSelections(void) +{ + int selLength; + int iAtom; + + if (gVmxCopyPasteVersion > 1) { + return TRUE; + } + + selLength = GuestApp_GetHostSelectionLen(); + if (selLength < 0) { + return FALSE; + } else if (selLength > 0) { + memset(gHostClipboardBuf, 0, sizeof (gHostClipboardBuf)); + GuestApp_GetHostSelection(selLength, gHostClipboardBuf); + Debug("CopyPaste_GetBackdoorSelections Get text [%s].\n", gHostClipboardBuf); + gtk_selection_owner_set(gUserMainWidget, + GDK_SELECTION_CLIPBOARD, + GDK_CURRENT_TIME); + gtk_selection_owner_set(gUserMainWidget, + GDK_SELECTION_PRIMARY, + GDK_CURRENT_TIME); + gIsOwner = TRUE; + gHGIsClipboardFCP = FALSE; + for (iAtom = 0; iAtom < NR_FCP_TARGETS; iAtom++) { + CopyPasteSelectionRemoveTarget(gUserMainWidget, + GDK_SELECTION_PRIMARY, + gFCPAtom[iAtom]); + CopyPasteSelectionRemoveTarget(gUserMainWidget, + GDK_SELECTION_CLIPBOARD, + gFCPAtom[iAtom]); + } + } + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteRpcInGHSetDataCB -- + * + * Handler function for the "copypaste.gh.data.get" RPC command. Host is + * asking for clipboard contents for guest->host copy/paste. If both primary + * selection and clipboard are empty, the empty list should also be sent back + * because Host should release clipboard owner. + * + * For Guest->Host copy/paste operations only. + * + * Results: + * TRUE on success, FALSE on failure. + * + * Side effects: + * The owner of the clipboard will get requests from our application. + * + *----------------------------------------------------------------------------- + */ + +static Bool +CopyPasteRpcInGHSetDataCB(char const **result, // OUT + size_t *resultLen, // OUT + const char *name, // IN + const char *args, // IN + size_t argsSize, // Ignored + void *clientData) // Ignored +{ + int iAtom; + GdkAtom activeSelection = GDK_SELECTION_PRIMARY; + char *source = gGuestSelPrimaryBuf; + char format[256]; + char *rpcBody = NULL; + size_t rpcBodySize = 0; + + gGuestSelPrimaryBuf[0] = '\0'; + gGuestSelClipboardBuf[0] = '\0'; + + if (gIsOwner) { + Debug("CopyPasteRpcInGHSetDataCB Send empty buf to host\n"); + return RpcIn_SetRetVals(result, resultLen, "", TRUE); + } + + /* First check which one is newer, primary selection or clipboard. */ + gGuestSelPrimaryTime = 0; + gGuestSelClipboardTime = 0; + + gWaitingOnGuestSelection = TRUE; + gtk_selection_convert(gUserMainWidget, + GDK_SELECTION_PRIMARY, + GDK_SELECTION_TYPE_TIMESTAMP, + GDK_CURRENT_TIME); + while (gWaitingOnGuestSelection) gtk_main_iteration(); + + gWaitingOnGuestSelection = TRUE; + gtk_selection_convert(gUserMainWidget, + GDK_SELECTION_CLIPBOARD, + GDK_SELECTION_TYPE_TIMESTAMP, + GDK_CURRENT_TIME); + while (gWaitingOnGuestSelection) gtk_main_iteration(); + + if (gGuestSelPrimaryTime < gGuestSelClipboardTime) { + activeSelection = GDK_SELECTION_CLIPBOARD; + source = gGuestSelClipboardBuf; + } + +try_again: + /* Check if it is file list in the active selection. */ + for (iAtom = 0; iAtom < NR_FCP_TARGETS; iAtom++) { + gWaitingOnGuestSelection = TRUE; + gtk_selection_convert(gUserMainWidget, + activeSelection, + gFCPAtom[iAtom], + GDK_CURRENT_TIME); + while (gWaitingOnGuestSelection) gtk_main_iteration(); + if (source[0] != '\0') { + if (gVmxCopyPasteVersion < 2) { + /* Only vmx version greater than 2 support file copy/paste. */ + Debug("CopyPasteRpcInGHSetDataCB invalid operation\n"); + return RpcIn_SetRetVals(result, resultLen, + "invalid operation", FALSE); + } + break; + } + } + + if (source[0] != '\0') { + char *currName; + size_t currSize; + size_t index = 0; + char *ghFileList = NULL; + size_t ghFileListSize = 0; + + /* + * In gnome, before file list there may be a extra line indicating it + * is a copy or cut. + */ + if (strncmp(source, "copy", 4) == 0) { + source += 4; + } + if (strncmp(source, "cut", 3) == 0) { + source += 3; + } + + while (*source == '\n' || *source == '\r' || *source == ' ') { + source++; + } + + /* + * Get the the full filenames and last components from the URI list. The + * body of the RPC message will be these last components delimited with + * NUL characters; the Guest->Host file list will be the full paths + * delimited by NUL characters. + */ + while ((currName = DnD_UriListGetNextFile(source, + &index, + &currSize))) { + size_t lastComponentSize; + char *lastComponentStart; + + /* Append current filename to Guest->Host list */ + ghFileList = Util_SafeRealloc(ghFileList, + ghFileListSize + currSize + 1); + memcpy(ghFileList + ghFileListSize, currName, currSize); + ghFileListSize += currSize; + ghFileList[ghFileListSize] = '\0'; + ghFileListSize++; + + /* Append last component to RPC body */ + lastComponentStart = CPNameUtil_Strrchr(currName, currSize, DIRSEPC); + if (!lastComponentStart) { + /* + * This shouldn't happen since filenames are absolute, but handle + * it as if the file name is the last component + */ + lastComponentStart = currName; + } else { + /* Skip the last directory separator */ + lastComponentStart++; + } + + lastComponentSize = currName + currSize - lastComponentStart; + rpcBody = Util_SafeRealloc(rpcBody, rpcBodySize + lastComponentSize + 1); + memcpy(rpcBody + rpcBodySize, lastComponentStart, lastComponentSize); + rpcBodySize += lastComponentSize; + rpcBody[rpcBodySize] = '\0'; + rpcBodySize++; + + free(currName); + } + + if (!ghFileList || !rpcBody) { + Warning("CopyPasteRpcInGHSetDataCB: no filenames retrieved " + "from URI list\n"); + free(ghFileList); + free(rpcBody); + return RpcIn_SetRetVals(result, resultLen, + "error retrieving file name", FALSE); + } + + /* Set the list of full paths */ + CopyPasteGHFileListSet(ghFileList, ghFileListSize); + + /* rpcBody (and its size) will always contain a trailing NUL character */ + rpcBodySize--; + Debug("CopyPasteRpcInGHSetDataCB: Sending: [%s] (%zu)\n", + CPName_Print(rpcBody, rpcBodySize), rpcBodySize); + + Str_Sprintf(format, sizeof format, "%d ", CPFORMAT_FILELIST); + *resultLen = rpcBodySize + strlen(format); + + free(gGHFCPRpcResultBuffer); + gGHFCPRpcResultBuffer = Util_SafeCalloc(1, rpcBodySize + strlen(format)); + + memcpy(gGHFCPRpcResultBuffer, format, strlen(format)); + memcpy(gGHFCPRpcResultBuffer + strlen(format), rpcBody, rpcBodySize); + free(rpcBody); + *result = gGHFCPRpcResultBuffer; + return TRUE; + } else { + /* Try to get utf8 text from active selection. */ + gWaitingOnGuestSelection = TRUE; + gtk_selection_convert(gUserMainWidget, + activeSelection, + GDK_SELECTION_TYPE_UTF8_STRING, + GDK_CURRENT_TIME); + while (gWaitingOnGuestSelection) gtk_main_iteration(); + + if (source[0] == '\0') { + /* Try to get text from active selection. */ + gWaitingOnGuestSelection = TRUE; + gtk_selection_convert(gUserMainWidget, + activeSelection, + GDK_SELECTION_TYPE_STRING, + GDK_CURRENT_TIME); + while (gWaitingOnGuestSelection) gtk_main_iteration(); + } + + /* + * With 'cut' operation OpenOffice will put data into clipboard but + * set same timestamp for both clipboard and primary selection. + * If primary timestamp is same as clipboard timestamp, we should try + * clipboard again if primary selection is empty. For details please + * refer to bug 300780. + */ + if (source[0] == '\0' && + gGuestSelPrimaryTime == gGuestSelClipboardTime && + gGuestSelPrimaryTime != 0) { + gGuestSelPrimaryTime = 0; + gGuestSelClipboardTime = 0; + activeSelection = GDK_SELECTION_CLIPBOARD; + source = gGuestSelClipboardBuf; + goto try_again; + } + + if (source[0] != '\0') { + free(gGHFCPRpcResultBuffer); + + gGHFCPRpcResultBuffer = + Str_Asprintf(NULL, "%d %s", CPFORMAT_TEXT, source); + + if (!gGHFCPRpcResultBuffer) { + Debug("CopyPasteRpcInGHSetDataCB failed to alloc memory.\n"); + return RpcIn_SetRetVals(result, resultLen, + "error allocating memory", FALSE); + } + + *result = gGHFCPRpcResultBuffer; + *resultLen = strlen(gGHFCPRpcResultBuffer); + + Debug("CopyPasteRpcInGHSetDataCB creating text: %s\n", source); + + return TRUE; + } + /* Neither file list nor text is available, send empty list back. */ + Debug("CopyPasteRpcInGHSetDataCB Send empty buf to host\n"); + return RpcIn_SetRetVals(result, resultLen, "", TRUE); + } +} + + +/* + *---------------------------------------------------------------------------- + * + * CopyPasteRpcInGHFinishCB -- + * + * For Guest->Host operations only. + * + * Invoked when host side of copyPaste operation has finished. + * + * Results: + * TRUE on success, FALSE on failure. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +static Bool +CopyPasteRpcInGHFinishCB(char const **result, // OUT + size_t *resultLen, // OUT + const char *name, // IN + const char *args, // IN + size_t argsSize, // Ignored + void *clientData) // IN +{ + char *effect = NULL; + unsigned int index = 0; + + gFcpGHState.fileListNext = gFcpGHState.fileList; + + effect = StrUtil_GetNextToken(&index, args, " "); + if (!effect) { + Warning("CopyPasteRpcInGHFinishCB: no drop effect provided\n"); + return RpcIn_SetRetVals(result, resultLen, + "drop effect not provided", FALSE); + } + + Debug("CopyPasteRpcInGHFinishCB got effect %s\n", effect); + + free(effect); + return RpcIn_SetRetVals(result, resultLen, "", TRUE); +} + + +/* + *---------------------------------------------------------------------------- + * + * CopyPasteGHFileListClear -- + * + * Clears existing Guest->Host file list, releasing any used resources. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +static INLINE void +CopyPasteGHFileListClear(void) +{ + Debug("CopyPasteGHFileListClear: clearing G->H file list\n"); + if (gFcpGHState.fileList) { + free(gFcpGHState.fileList); + gFcpGHState.fileList = NULL; + } + gFcpGHState.fileListSize = 0; + gFcpGHState.fileListNext = NULL; +} + + +/* + *---------------------------------------------------------------------------- + * + * CopyPasteGHFileListSet -- + * + * Sets the Guest->Host file list that is accessed through + * CopyPasteGHFileListGetNext(). + * + * Results: + * None. + * + * Side effects: + * Clears the existing Guest->Host file list if it exists. + * + *---------------------------------------------------------------------------- + */ + +static INLINE void +CopyPasteGHFileListSet(char *fileList, // IN: new Guest->Host file list + size_t fileListSize) // IN: size of the provided list +{ + CopyPasteGHFileListClear(); + gFcpGHState.fileList = fileList; + gFcpGHState.fileListSize = fileListSize; + gFcpGHState.fileListNext = fileList; + + Debug("CopyPasteGHFileListSet: [%s] (%"FMTSZ"u)\n", + CPName_Print(gFcpGHState.fileList, gFcpGHState.fileListSize), + gFcpGHState.fileListSize); +} + + +/* + *---------------------------------------------------------------------------- + * + * CopyPasteGHFileListGetNext -- + * + * Retrieves the next file in the Guest->Host file list. + * + * Note that this function may only be called after calling + * CopyPasteGHFileListSet() and before calling CopyPasteGHFileListClear(). + * + * Results: + * TRUE on success, FALSE on failure. If TRUE is returned, fileName is + * given a pointer to the filename's location or NULL if there are no more + * files, and fileNameSize is given the length of fileName. + * + * Side effects: + * The fileListNext value of the Guest->Host global state is updated. + * + *---------------------------------------------------------------------------- + */ + +Bool +CopyPasteGHFileListGetNext(char **fileName, // OUT: fill with filename location + size_t *fileNameSize) // OUT: fill with filename length +{ + char const *end; + char const *next; + int len; + + ASSERT(gFcpGHState.fileList); + ASSERT(gFcpGHState.fileListNext); + ASSERT(gFcpGHState.fileListSize > 0); + + /* Ensure end is the last NUL character */ + end = CPNameUtil_Strrchr(gFcpGHState.fileList, + gFcpGHState.fileListSize, + '\0'); + ASSERT(end); + + /* Get the length of this filename and a pointer to the next one */ + len = CPName_GetComponent(gFcpGHState.fileListNext, end, &next); + if (len < 0) { + Warning("CopyPasteGHFileListGetNext: error retrieving next component\n"); + return FALSE; + } + + /* No more entries in the list */ + if (len == 0) { + Debug("CopyPasteGHFileListGetNext: no more entries\n"); + *fileName = NULL; + *fileNameSize = 0; + gFcpGHState.fileListNext = gFcpGHState.fileList; + return TRUE; + } + + Debug("CopyPasteGHFileListGetNext: returning [%s] (%d)\n", + gFcpGHState.fileListNext, len); + + *fileName = gFcpGHState.fileListNext; + *fileNameSize = len; + gFcpGHState.fileListNext = (char *)next; + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteHGSetData -- + * + * Host is sending text for copy/paste. + * + * RPC command format: + * 1. Format + * 2. Size of text + * 3. If text size > 0, then followed by text, otherwise nothing + * + * For Host->Guest operations only. + * + * Results: + * TRUE on success, FALSE otherwise. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static Bool +CopyPasteHGSetData(char const **result, // OUT + size_t *resultLen, // OUT + const char *args) // IN +{ + char *format = NULL; + char *sSize = NULL; + uint32 textSize; + unsigned int index = 0; + Bool ret = FALSE; + int iAtom; + + /* Parse value string. */ + format = StrUtil_GetNextToken(&index, args, " "); + index++; /* Ignore leading space before data. */ + sSize = StrUtil_GetNextToken(&index, args, " "); + index++; + if (!format || !sSize) { + Debug("CopyPasteHGSetData failed to parse format & size\n"); + ret = RpcIn_SetRetVals(result, resultLen, + "format and size is not completed", FALSE); + goto exit; + } + + textSize = atoi(sSize); + gHostClipboardBuf[0] = '\0'; + + if (textSize > 0) { + if (textSize >= MAX_SELECTION_BUFFER_LENGTH) { + textSize = MAX_SELECTION_BUFFER_LENGTH - 1; + } + memcpy(gHostClipboardBuf, args + index, textSize); + gHostClipboardBuf[textSize] = '\0'; + Debug("CopyPasteHGSetData: Set text [%s]\n", gHostClipboardBuf); + } + + gtk_selection_owner_set(gUserMainWidget, + GDK_SELECTION_CLIPBOARD, + GDK_CURRENT_TIME); + gtk_selection_owner_set(gUserMainWidget, + GDK_SELECTION_PRIMARY, + GDK_CURRENT_TIME); + gIsOwner = TRUE; + gHGIsClipboardFCP = FALSE; + + /* We put text into selection, so remove file target types from list. */ + for (iAtom = 0; iAtom < NR_FCP_TARGETS; iAtom++) { + CopyPasteSelectionRemoveTarget(gUserMainWidget, + GDK_SELECTION_PRIMARY, + gFCPAtom[iAtom]); + CopyPasteSelectionRemoveTarget(gUserMainWidget, + GDK_SELECTION_CLIPBOARD, + gFCPAtom[iAtom]); + } + + ret = RpcIn_SetRetVals(result, resultLen, "", TRUE); + +exit: + free(format); + free(sSize); + return ret; +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteRpcInHGDataFinishCB -- + * + * For Host->Guest operations only. + * Host has finished transferring copyPaste data to the guest. We do any + * post H->G operation cleanup here, like picking a new file root. + * + * Results: + * TRUE on success, FALSE otherwise + * + * Side effects: + * Copied files will be deleted in error or cancel case. + * + *----------------------------------------------------------------------------- + */ + +static Bool +CopyPasteRpcInHGDataFinishCB(char const **result, // OUT + size_t *resultLen, // OUT + const char *name, // IN + const char *args, // IN + size_t argsSize, // Ignored + void *clientData) // IN: pointer to mainWnd +{ + unsigned int index = 0; + char *state; + + Debug("CopyPasteRpcInHGDataFinishCB received copypaste data finish\n"); + + state = StrUtil_GetNextToken(&index, args, " "); + + if (!state) { + Debug("CopyPasteRpcInHGDataFinishCB failed to parse data state\n"); + return RpcIn_SetRetVals(result, resultLen, + "must specify data finish state", FALSE); + } + + if (strcmp(state, "success") != 0) { + Debug("CopyPasteRpcInHGDataFinishCB data transfer error\n"); + /* + * Delete staging directory in error or cancel case, otherwise + * target application may still try to get copied files because + * the file list is provided right after adding block to staging + * directory. + */ + File_DeleteDirectoryTree(gFileRoot); + } + + free(state); + + ASSERT(gHGFCPFileTransferStatus == FCP_FILE_TRANSFERRING); + gHGFCPFileTransferStatus = FCP_FILE_TRANSFERRED; + + if (gBlockFd > 0 && !DnD_RemoveBlock(gBlockFd, gFileRoot)) { + Warning("CopyPasteRpcInHGDataFinishCB: Unable to remove block [%s].\n", + gFileRoot); + } + + /* get new root dir for next FCP operation. */ + gFileRootSize = DnD_GetNewFileRoot(gFileRoot, sizeof gFileRoot); + + Debug("CopyPasteRpcInHGDataFinishCB create staging dir [%s]\n", gFileRoot); + + return RpcIn_SetRetVals(result, resultLen, "", TRUE); +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteHGSetFileList -- + * + * Host is sending file list for FCP (file copy/paste). + * + * RPC command format: + * 1. Format + * 2. Total size of all files in the list + * 3. Size of file list string + * 4. If list size > 0, then followed by file list, otherwise nothing + * + * For Host->Guest FCP operations only. + * + * Results: + * TRUE on success, FALSE on failure. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static Bool +CopyPasteHGSetFileList(char const **result, // OUT + size_t *resultLen, // OUT + const char *args) // IN +{ + char *format = NULL; + char *data = NULL; + char *sListSize = NULL; + size_t listSize; + char *sTotalSize = NULL; + char *stagingDirName = NULL; + char mountDirName[DND_MAX_PATH]; + unsigned int index = 0; + Bool ret = FALSE; + char *retStr; + int iAtom; + + gHGFCPFileTransferStatus = FCP_FILE_TRANSFER_NOT_YET; + /* Parse value string. */ + format = StrUtil_GetNextToken(&index, args, " "); + index++; /* Ignore leading space before data. */ + sTotalSize = StrUtil_GetNextToken(&index, args, " "); + index++; + sListSize = StrUtil_GetNextToken(&index, args, " "); + index++; + if (!format || !sTotalSize || !sListSize) { + Debug("CopyPasteHGSetFileList failed to parse format & size\n"); + retStr = "format or size is not completed"; + goto exit; + } + + listSize = atoi(sListSize); + /* + * Total file size in selection list. This is used to check if there is enough + * space in guest OS for host->guest file transfer. + */ + gHGFCPTotalSize = atol(sTotalSize); + + if (listSize <= 0) { + Debug("CopyPasteHGSetFileList: got empty list\n"); + gHostClipboardBuf[0] = '\0'; + retStr = ""; + ret = TRUE; + goto exit; + } + + /* + * XXX Should do code set convertion here from utf8 to current for file list, + * but right now should not do that. The reason is that the hgfs server + * always puts utf8 file name into guest, which is not right if local guest + * encoding is non-utf8. DnD has same problem. + */ + + data = (char *)Util_SafeCalloc(1, listSize + 1); + memcpy(data, args + index, listSize); + data[listSize] = '\0'; + + if (gBlockFd > 0) { + /* + * Here we take the last component of the actual file root, which is + * a temporary directory for this DnD operation, and append it to the + * mount point for vmblock. This is where we want the target application + * to access the file since it will enable vmblock to block that + * application's progress if necessary. + */ + stagingDirName = DnD_GetLastDirName(gFileRoot); + if (!stagingDirName) { + Debug("CopyPasteHGSetFileList: error construct stagingDirName\n"); + retStr = "error construct stagingDirName"; + goto exit; + } + if (sizeof VMBLOCK_MOUNT_POINT - 1 + + (sizeof DIRSEPS - 1) * 2 + strlen(stagingDirName) >= sizeof mountDirName) { + Debug("CopyPasteHGSetFileList: directory name too large.\n"); + retStr = "directory name too large"; + goto exit; + } + Str_Sprintf(mountDirName, sizeof mountDirName, + VMBLOCK_MOUNT_POINT DIRSEPS"%s"DIRSEPS, stagingDirName); + } + + /* Add the file root to the relative paths received from host */ + if (!DnD_PrependFileRoot(gBlockFd > 0 ? mountDirName : gFileRoot, + &data, &listSize)) { + Debug("CopyPasteHGSetFileList: error prepending guest file root\n"); + retStr = "error prepending file root"; + goto exit; + } + + if (listSize + 1 > sizeof gHostClipboardBuf) { + Debug("CopyPasteHGSetFileList: data too large\n"); + retStr = "data too large"; + goto exit; + } + + memcpy(gHostClipboardBuf, data, listSize + 1); + gGHFCPListSize = listSize; + gHGIsClipboardFCP = TRUE; + Debug("CopyPasteHGSetFileList: get file list [%s] (%zu)\n", + CPName_Print(gHostClipboardBuf, gGHFCPListSize), gGHFCPListSize); + + for (iAtom = 0; iAtom < NR_FCP_TARGETS; iAtom++) { + gtk_selection_add_target(gUserMainWidget, GDK_SELECTION_PRIMARY, + gFCPAtom[iAtom], 0); + gtk_selection_add_target(gUserMainWidget, GDK_SELECTION_CLIPBOARD, + gFCPAtom[iAtom], 0); + } + + Debug("CopyPasteHGSetFileList: added targets\n"); + gtk_selection_owner_set(gUserMainWidget, + GDK_SELECTION_CLIPBOARD, + GDK_CURRENT_TIME); + gtk_selection_owner_set(gUserMainWidget, + GDK_SELECTION_PRIMARY, + GDK_CURRENT_TIME); + gIsOwner = TRUE; + + retStr = ""; + ret = TRUE; + +exit: + free(format); + free(data); + free(sTotalSize); + free(sListSize); + free(stagingDirName); + Hostinfo_GetTimeOfDay(&gHGGetListTime); + return RpcIn_SetRetVals(result, resultLen, retStr, ret); +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteRpcInHGSetDataCB -- + * + * Host is sending data for copy/paste. The data can be text, file list, etc. + * + * For Host->Guest operations only. + * + * Results: + * TRUE if success, FALSE otherwise. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static Bool +CopyPasteRpcInHGSetDataCB(char const **result, // OUT + size_t *resultLen, // OUT + const char *name, // IN + const char *args, // IN + size_t argsSize, // Ignored + void *clientData) // IN: ignored +{ + char *formatStr = NULL; + DND_CPFORMAT format; + Bool ret = FALSE; + + unsigned int index = 0; + + if (gHGFCPFileTransferStatus == FCP_FILE_TRANSFERRING) { + return RpcIn_SetRetVals(result, resultLen, + "", TRUE); + } + + /* Parse value string. */ + formatStr = StrUtil_GetNextToken(&index, args, " "); + index++; /* Ignore leading space before data. */ + + if (!formatStr) { + Debug("CopyPasteTcloHGDataSet failed to parse format\n"); + return RpcIn_SetRetVals(result, resultLen, + "format and size is not completed", FALSE); + } + + format = (DND_CPFORMAT)atoi(formatStr); + free(formatStr); + + switch (format) { + case CPFORMAT_TEXT: + ret = CopyPasteHGSetData(result, resultLen, args); + break; + case CPFORMAT_FILELIST: + /* Only vmx version greater than 2 support file copy/paste. */ + if (gVmxCopyPasteVersion < 2) { + Debug("CopyPasteRpcInHGSetDataCB invalid operation\n"); + return RpcIn_SetRetVals(result, resultLen, + "invalid operation", FALSE); + } + ret = CopyPasteHGSetFileList(result, resultLen, args); + break; + default: + Debug("CopyPasteTcloHGDataSet unknown format\n"); + ret = RpcIn_SetRetVals(result, resultLen, + "unknown format", FALSE); + break; + } + + return ret; +} + + +/* + *---------------------------------------------------------------------------- + * + * CopyPasteRpcInGHGetNextFileCB -- + * + * For Guest->Host operations only. + * + * Invoked when the host is compiling its list of files to copy from the + * guest. Here we provide the path of the next file in our Guest->Host file + * list in guest path format (for display purposes) and CPName format (for + * file copy operation). + * + * Results: + * TRUE on success, FALSE on failure. + * + * Side effects: + * Iterator pointer within file list of GH state is iterated to next list + * entry (through call to CopyPasteGHFileListGetNext()). + * + *---------------------------------------------------------------------------- + */ + +static Bool +CopyPasteRpcInGHGetNextFileCB(char const **result, // OUT + size_t *resultLen, // OUT + const char *name, // IN + const char *args, // IN + size_t argsSize, // Ignored + void *clientData) // IN +{ + static char resultBuffer[DND_MAX_PATH]; + char *fileName; + size_t fileNameSize; + uint32 cpNameSize; + Bool res; + + /* + * Retrieve a pointer to the next filename and its size from the list stored + * in the G->H DnD state. Note that fileName should not be free(3)d here + * since an additional copy is not allocated. + */ + res = CopyPasteGHFileListGetNext(&fileName, &fileNameSize); + + if (!res) { + Warning("CopyPasteRpcInGHGetNextFileCB: error retrieving file name\n"); + return RpcIn_SetRetVals(result, resultLen, "error getting file", FALSE); + } + + if (!fileName) { + /* There are no more files to send */ + Debug("CopyPasteRpcInGHGetNextFileCB: reached end of Guest->Host file list\n"); + return RpcIn_SetRetVals(result, resultLen, "|end|", TRUE); + } + + if (fileNameSize + 1 + fileNameSize > sizeof resultBuffer) { + Warning("CopyPasteRpcInGHGetNextFileCB: filename too large (%"FMTSZ"u)\n", fileNameSize); + return RpcIn_SetRetVals(result, resultLen, "filename too large", FALSE); + } + + /* + * Construct a reply message of the form: + * + */ + memcpy(resultBuffer, fileName, fileNameSize); + resultBuffer[fileNameSize] = '\0'; + + cpNameSize = CPNameUtil_ConvertToRoot(fileName, + sizeof resultBuffer - (fileNameSize + 1), + resultBuffer + fileNameSize + 1); + if (cpNameSize < 0) { + Warning("CopyPasteRpcInGHGetNextFileCB: could not convert to CPName\n"); + return RpcIn_SetRetVals(result, resultLen, + "error on CPName conversion", FALSE); + } + + /* Set manually because RpcIn_SetRetVals() assumes no NUL characters */ + *result = resultBuffer; + *resultLen = fileNameSize + 1 + cpNameSize; + + Debug("CopyPasteRpcInGHGetNextFileCB: [%s] (%"FMTSZ"u)\n", + CPName_Print(*result, *resultLen), *resultLen); + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPaste_GetVmxCopyPasteVersion -- + * + * Ask the vmx for it's copy/paste version. + * + * Results: + * The copy/paste version the vmx supports, 1 if the vmx doesn't know + * what we're talking about. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +int32 +CopyPaste_GetVmxCopyPasteVersion(void) +{ + char *reply = NULL; + size_t replyLen; + + if (!RpcOut_sendOne(&reply, &replyLen, "vmx.capability.copypaste_version")) { + Debug("CopyPaste_GetVmxCopyPasteVersion: could not get VMX copyPaste " + "version capability: %s\n", reply ? reply : "NULL"); + gVmxCopyPasteVersion = 1; + } else { + gVmxCopyPasteVersion = atoi(reply); + } + + free(reply); + Debug("CopyPaste_GetVmxCopyPasteVersion: got version %d\n", gVmxCopyPasteVersion); + return gVmxCopyPasteVersion; +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPaste_RegisterCapability -- + * + * Register the "copypaste" capability. Sometimes this needs to be done + * separately from the rest of copy/paste registration, so we provide it + * separately here. + * + * Results: + * TRUE on success + * FALSE on failure + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +CopyPaste_RegisterCapability(void) +{ + /* Tell the VMX about the copyPaste version we support. */ + if (!RpcOut_sendOne(NULL, NULL, "tools.capability.copypaste_version 2")) { + Debug("CopyPaste_RegisterCapability: could not set guest copypaste " + "version capability\n"); + gVmxCopyPasteVersion = 1; + return FALSE; + } + Debug("CopyPaste_RegisterCapability: set copypaste version 2\n"); + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPaste_Register -- + * + * Setup callbacks, initialize. + * + * Results: + * Always TRUE. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +CopyPaste_Register(GtkWidget* mainWnd) +{ + /* Text copy/paste initialization for all versions. */ +#ifndef GDK_SELECTION_CLIPBOARD + GDK_SELECTION_CLIPBOARD = gdk_atom_intern("CLIPBOARD", FALSE); +#endif + +#ifndef GDK_SELECTION_TYPE_TIMESTAMP + GDK_SELECTION_TYPE_TIMESTAMP = gdk_atom_intern("TIMESTAMP", FALSE); +#endif + +#ifndef GDK_SELECTION_TYPE_UTF8_STRING + GDK_SELECTION_TYPE_UTF8_STRING = gdk_atom_intern("UTF8_STRING", FALSE); +#endif + + gFCPAtom[FCP_TARGET_INFO_GNOME_COPIED_FILES] = + gdk_atom_intern(FCP_TARGET_NAME_GNOME_COPIED_FILES, FALSE); + gFCPAtom[FCP_TARGET_INFO_URI_LIST] = + gdk_atom_intern(FCP_TARGET_NAME_URI_LIST, FALSE); + + /* + * String is always in supported list. FCP atoms will dynamically be + * added and removed. + */ + gtk_selection_add_target(mainWnd, GDK_SELECTION_PRIMARY, + GDK_SELECTION_TYPE_STRING, 0); + gtk_selection_add_target(mainWnd, GDK_SELECTION_CLIPBOARD, + GDK_SELECTION_TYPE_STRING, 0); + gtk_selection_add_target(mainWnd, GDK_SELECTION_PRIMARY, + GDK_SELECTION_TYPE_UTF8_STRING, 0); + gtk_selection_add_target(mainWnd, GDK_SELECTION_CLIPBOARD, + GDK_SELECTION_TYPE_UTF8_STRING, 0); + + gtk_signal_connect(GTK_OBJECT(mainWnd), "selection_received", + GTK_SIGNAL_FUNC(CopyPasteSelectionReceivedCB), mainWnd); + gtk_signal_connect(GTK_OBJECT(mainWnd), "selection_get", + GTK_SIGNAL_FUNC(CopyPasteSelectionGetCB), mainWnd); + gtk_signal_connect(GTK_OBJECT(mainWnd), "selection_clear_event", + GTK_SIGNAL_FUNC(CopyPasteSelectionClearCB), mainWnd); + + RpcIn_RegisterCallback(gRpcIn, "copypaste.hg.data.set", + CopyPasteRpcInHGSetDataCB, NULL); + RpcIn_RegisterCallback(gRpcIn, "copypaste.hg.data.finish", + CopyPasteRpcInHGDataFinishCB, NULL); + RpcIn_RegisterCallback(gRpcIn, "copypaste.gh.data.get", + CopyPasteRpcInGHSetDataCB, NULL); + RpcIn_RegisterCallback(gRpcIn, "copypaste.gh.get.next.file", + CopyPasteRpcInGHGetNextFileCB, NULL); + RpcIn_RegisterCallback(gRpcIn, "copypaste.gh.finish", + CopyPasteRpcInGHFinishCB, NULL); + + CopyPasteStateInit(); + Wiper_Init(NULL); + + return CopyPaste_RegisterCapability(); +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPaste_Unregister -- + * + * Cleanup copy/paste related things. + * + * Results: + * None. + * + * Side effects: + * copy/paste is stopped, the rpc channel to the vmx is closed. + * + *----------------------------------------------------------------------------- + */ + +void +CopyPaste_Unregister(GtkWidget* mainWnd) +{ + gtk_signal_disconnect_by_func(GTK_OBJECT(mainWnd), + GTK_SIGNAL_FUNC(CopyPasteSelectionReceivedCB), + mainWnd); + gtk_signal_disconnect_by_func(GTK_OBJECT(mainWnd), + GTK_SIGNAL_FUNC(CopyPasteSelectionGetCB), + mainWnd); + gtk_signal_disconnect_by_func(GTK_OBJECT(mainWnd), + GTK_SIGNAL_FUNC(CopyPasteSelectionClearCB), + mainWnd); +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPaste_OnReset -- + * + * Handles reinitializing Copy Paste state on a reset. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +void +CopyPaste_OnReset(void) +{ + if (gHGFCPFileTransferStatus == FCP_FILE_TRANSFERRING) { + File_DeleteDirectoryTree(gFileRoot); + if (gBlockFd > 0 && !DnD_RemoveBlock(gBlockFd, gFileRoot)) { + Warning("CopyPasteRpcInHGDataFinishCB: Unable to remove block [%s].\n", + gFileRoot); + } + gFileRootSize = DnD_GetNewFileRoot(gFileRoot, sizeof gFileRoot); + } + + CopyPasteStateInit(); +} + +/* + *---------------------------------------------------------------------------- + * + * CopyPaste_InProgress -- + * + * Indicates whether a copy/paste data transfer is currently in progress. + * + * Results: + * TRUE if in progress, FALSE otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +Bool +CopyPaste_InProgress(void) +{ + /* XXX We currently have no way to determine if a G->H FCP is ongoing. */ + return gHGFCPFileTransferStatus == FCP_FILE_TRANSFERRING; +} + + +/* + *---------------------------------------------------------------------------- + * + * CopyPaste_IsRpcCPSupported -- + * + * Check if RPC copy/paste is supported by vmx or not. + * + * Results: + * TRUE if RPC copy/paste is supported, FALSE otherwise. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +Bool +CopyPaste_IsRpcCPSupported(void) +{ + return gVmxCopyPasteVersion > 1; +} + + +/* + *---------------------------------------------------------------------------- + * + * CopyPasteStateInit -- + * + * Initalialize CopyPaste State. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +void +CopyPasteStateInit(void) +{ + gHostClipboardBuf[0] = '\0'; + gGuestSelPrimaryBuf[0] = '\0'; + gGuestSelClipboardBuf[0] = '\0'; + gIsOwner = FALSE; + gGHFCPRpcResultBuffer = NULL; + gHGFCPPending = FALSE; + gHGFCPFileTransferStatus = FCP_FILE_TRANSFER_NOT_YET; + + if (CopyPaste_GetVmxCopyPasteVersion() >= 2) { + /* + * Create staging directory for file copy/paste. This is for vmx with version 2 + * or greater. + */ + gFileRootSize = DnD_GetNewFileRoot(gFileRoot, sizeof gFileRoot); + Debug("CopyPaste_Register create file root [%s]\n", gFileRoot); + } +} diff --git a/open-vm-tools/vmware-user/copyPasteUI.cpp b/open-vm-tools/vmware-user/copyPasteUI.cpp new file mode 100644 index 000000000..bba97811d --- /dev/null +++ b/open-vm-tools/vmware-user/copyPasteUI.cpp @@ -0,0 +1,934 @@ +/********************************************************* + * 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. + * + *********************************************************/ + +/* + * copyPasteUI.cpp -- + * + * This class implements the methods that allows CopyPaste between host + * and guest. + * + * For a perspective on X copy/paste, see + * http://www.jwz.org/doc/x-cut-and-paste.html + */ + +#include +#include +#include "copyPasteUI.h" +#include "dndFileList.hh" + +extern "C" { + #include "vmblock.h" + #include "vm_app.h" + #include "file.h" + #include "dnd.h" + #include "dndMsg.h" + #include "dndClipboard.h" + #include "cpName.h" + #include "debug.h" + #include "cpNameUtil.h" + #include "rpcout.h" +} + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteUI::CopyPasteUI -- + * + * Constructor. + * + * Results: + * None + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +CopyPasteUI::CopyPasteUI() + : mClipboardEmpty(true), + mHGStagingDir(""), + mIsClipboardOwner(false), + mClipTimePrev(0), + mPrimTimePrev(0) +{ + Gtk::TargetEntry gnome(FCP_TARGET_NAME_GNOME_COPIED_FILES); + Gtk::TargetEntry kde(FCP_TARGET_NAME_URI_LIST); + gnome.set_info(FCP_TARGET_INFO_GNOME_COPIED_FILES); + kde.set_info(FCP_TARGET_INFO_URI_LIST); + + mListTargets.push_back(gnome); + mListTargets.push_back(kde); + + CPClipboard_Init(&mClipboard); + + /* Tell the VMX about the copyPaste version we support. */ + if (!RpcOut_sendOne(NULL, NULL, "tools.capability.copypaste_version 3")) { + Debug("%s: could not set guest copypaste version capability\n", + __FUNCTION__); + return; + } + Debug("%s: set copypaste version 3\n", __FUNCTION__); + + mCP.newClipboard.connect( + sigc::mem_fun(this, &CopyPasteUI::GetRemoteClipboardCB)); + mCP.localGetClipboard.connect( + sigc::mem_fun(this, &CopyPasteUI::GetLocalClipboard)); + mCP.localGetFilesDoneChanged.connect( + sigc::mem_fun(this, &CopyPasteUI::GetLocalFilesDone)); +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteUI::~CopyPaste -- + * + * Destructor. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +CopyPasteUI::~CopyPasteUI() +{ + CPClipboard_Destroy(&mClipboard); +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteUI::VmxCopyPasteVersionChanged -- + * + * Update version information in mCP. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +void +CopyPasteUI::VmxCopyPasteVersionChanged(struct RpcIn *rpcIn, // IN + uint32 version) // IN +{ + Debug("%s: new version is %d\n", __FUNCTION__, version); + mCP.VmxCopyPasteVersionChanged(rpcIn, version); +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteUI::GetLocalClipboard -- + * + * Retrives the data from local clipboard and sends it to host. Send empty + * data back if there is no data or can not get data successfully. For + * guest->host copy/paste. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +bool +CopyPasteUI::GetLocalClipboard(CPClipboard *clip) // OUT +{ + Debug("%s: enter.\n", __FUNCTION__); + + if (mIsClipboardOwner) { + Debug("%s: is clipboard owner, set changed to false and return.\n", __FUNCTION__); + CPClipboard_SetChanged(clip, FALSE); + return true; + } + + if (!mCP.IsCopyPasteAllowed()) { + Debug("%s: copyPaste is not allowed\n", __FUNCTION__); + return true; + } + + Glib::RefPtr refClipboard = + Gtk::Clipboard::get(GDK_SELECTION_CLIPBOARD); + + mClipTime = 0; + mPrimTime = 0; + mGHSelection = GDK_SELECTION_CLIPBOARD; + Debug("%s: retrieving timestamps\n", __FUNCTION__); + refClipboard->request_contents(TARGET_NAME_TIMESTAMP, + sigc::mem_fun(this, &CopyPasteUI::LocalClipboardTimestampCB)); + return false; +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteUI::GetCurrentTime -- + * + * Get current time in microseconds. + * + * Results: + * Time in microseconds. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +VmTimeType +CopyPasteUI::GetCurrentTime(void) +{ + struct timeval tv; + VmTimeType curTime; + + if (gettimeofday(&tv, NULL) != 0) { + Debug("%s: gettimeofday failed!\n", __FUNCTION__); + return (VmTimeType) 0; + } + curTime = (tv.tv_sec * 1000000 + tv.tv_usec); + return curTime; +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteUI::LocalGetFileRequestCB -- + * + * Callback from a file paste request from another host application. + * Begins copying the files from guest to host and return the file list. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +void +CopyPasteUI::LocalGetFileRequestCB(Gtk::SelectionData& sd, // IN: + guint info) // IN: +{ + Debug("%s: enter.\n", __FUNCTION__); + mHGCopiedUriList = ""; + VmTimeType curTime; + + curTime = GetCurrentTime(); + + /* + * Some applications may ask for clipboard contents right after clipboard + * owner changed. So GH FCP will return nothing for some time after switch + * from guest OS to host OS. + */ + if ((curTime - mHGGetListTime) < FCP_COPY_DELAY) { + return; + } + + if (!mIsClipboardOwner || !mCP.IsCopyPasteAllowed()) { + return; + } + + Debug("%s: Got paste request, target is %s\n", __FUNCTION__, + sd.get_target().c_str()); + + /* Copy the files. */ + if (!mHGGetFilesInitated) { + utf::string str; + utf::string hgStagingDir; + utf::string stagingDirName; + utf::string pre; + utf::string post; + size_t index = 0; + + hgStagingDir = static_cast(mCP.GetFiles()); + Debug("%s: Getting files. Staging dir: %s", __FUNCTION__, + hgStagingDir.c_str()); + + if (0 == hgStagingDir.bytes()) { + Debug("%s: Can not create staging directory\n", __FUNCTION__); + return; + } + mHGGetFilesInitated = true; + + /* Provide URIs for each path in the guest's file list. */ + if (FCP_TARGET_INFO_GNOME_COPIED_FILES == info) { + mHGCopiedUriList = "copy\n"; + pre = FCP_GNOME_LIST_PRE; + post = FCP_GNOME_LIST_POST; + } else if (FCP_TARGET_INFO_URI_LIST == info) { + pre = DND_URI_LIST_PRE_KDE; + post = DND_URI_LIST_POST; + } else { + Debug("%s: Unknown request target: %s\n", __FUNCTION__, + sd.get_target().c_str()); + return; + } + + /* Provide path within vmblock file system instead of actual path. */ + stagingDirName = GetLastDirName(hgStagingDir); + if (0 == stagingDirName.bytes()) { + Debug("%s: Can not get staging directory name\n", __FUNCTION__); + return; + } + + while ((str = GetNextPath(mHGFCPData, index).c_str()).bytes() != 0) { + Debug("%s: Path: %s", __FUNCTION__, str.c_str()); + mHGCopiedUriList += pre; + mHGCopiedUriList += VMBLOCK_MOUNT_POINT; + mHGCopiedUriList += DIRSEPS + stagingDirName + DIRSEPS + str + post; + } + + /* Nautilus does not expect FCP_GNOME_LIST_POST after the last uri. See bug 143147. */ + if (FCP_TARGET_INFO_GNOME_COPIED_FILES == info) { + mHGCopiedUriList.erase(mHGCopiedUriList.size() - 1, 1); + } + } + + if (0 == mHGCopiedUriList.bytes()) { + Debug("%s: Can not get uri list\n", __FUNCTION__); + return; + } + + Debug("%s: providing file list [%s]\n", __FUNCTION__, + mHGCopiedUriList.c_str()); + + sd.set(sd.get_target().c_str(), mHGCopiedUriList.c_str()); +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPaste::LocalClearClipboardCB -- + * + * Clear clipboard request from another host application. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +void +CopyPasteUI::LocalClearClipboardCB(void) +{ + Debug("%s: got clear callback\n", __FUNCTION__); + mIsClipboardOwner = FALSE; +} + + +/* + *---------------------------------------------------------------------- + * + * CopyPasteUI::LocalClipboardTimestampCB -- + * + * Got the local clipboard timestamp. Ask for the primary timestamp. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +CopyPasteUI::LocalClipboardTimestampCB(const Gtk::SelectionData& sd) // IN +{ + int length = sd.get_length(); + Debug("%s: enter sd.get_length() %d.\n", __FUNCTION__, + length); + if (length == 4) { + mClipTime = ((uint32*) sd.get_data())[0]; + Debug("%s: mClipTime: %"FMT64"u.", __FUNCTION__, mClipTime); + } else if (length == 8) { + mClipTime = ((uint64*) sd.get_data())[0]; + Debug("%s: mClipTime: %"FMT64"u.", __FUNCTION__, mClipTime); + } else { + Debug("%s: Unable to get mClipTime.", __FUNCTION__); + } + + Glib::RefPtr refClipboard + = Gtk::Clipboard::get(GDK_SELECTION_PRIMARY); + refClipboard->request_contents(TARGET_NAME_TIMESTAMP, + sigc::mem_fun(this, &CopyPasteUI::LocalPrimTimestampCB)); +} + + +/* + *---------------------------------------------------------------------- + * + * CopyPasteUI::LocalPrimTimestampCB -- + * + * Got the local primary timestamp. Choose the most recently changed + * clipboard and get the selection from it. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +CopyPasteUI::LocalPrimTimestampCB(const Gtk::SelectionData& sd) // IN +{ + bool unchanged = FALSE; + int length = sd.get_length(); + Debug("%s: enter sd.get_length() is %d.\n", __FUNCTION__, length); + if (length == 4) { + mPrimTime = ((uint32*) sd.get_data())[0]; + Debug("%s: mPrimTime: %"FMT64"u.", __FUNCTION__, mPrimTime); + } else if (length == 8) { + mPrimTime = ((uint64*) sd.get_data())[0]; + Debug("%s: mPrimTime: %"FMT64"u.", __FUNCTION__, mPrimTime); + } else { + Debug("%s: Unable to get mPrimTime.", __FUNCTION__); + } + + /* After got both timestamp, choose latest one as active selection. */ + mGHSelection = GDK_SELECTION_PRIMARY; + if (mClipTime > mPrimTime) { + mGHSelection = GDK_SELECTION_CLIPBOARD; + Debug("%s: mClipTimePrev: %"FMT64"u.", __FUNCTION__, mClipTimePrev); + unchanged = mClipTime <= mClipTimePrev; + } else { + Debug("%s: mPrimTimePrev: %"FMT64"u.", __FUNCTION__, mPrimTimePrev); + unchanged = mPrimTime <= mPrimTimePrev; + } + + if (unchanged) { + Debug("%s: clipboard is unchanged, get out\n", __FUNCTION__); + CPClipboard_SetChanged(&mClipboard, FALSE); + return; + } + + mPrimTimePrev = mPrimTime; + mClipTimePrev = mClipTime; + + /* Ask for available targets from active selection. */ + Glib::RefPtr refClipboard = + Gtk::Clipboard::get(mGHSelection); + + refClipboard->request_targets( + sigc::mem_fun(this, &CopyPasteUI::LocalReceivedTargetsCB)); +} + + +/* + *---------------------------------------------------------------------- + * + * CopyPasteUI::LocalReceivedTargetsCB -- + * + * Received targets. Check if any desired target available. If so, + * ask for all supported contents. + * + * Results: + * None. + * + * Side effects: + * Clipboard owner may send us file list. + * + *---------------------------------------------------------------------- + */ + +void +CopyPasteUI::LocalReceivedTargetsCB(const Glib::StringArrayHandle& targetsArray) // IN +{ + std::list targets = targetsArray; + Glib::RefPtr refClipboard = Gtk::Clipboard::get(mGHSelection); + + Debug("%s: enter", __FUNCTION__); + CPClipboard_Clear(&mClipboard); + + if (std::find(targets.begin(), + targets.end(), + FCP_TARGET_NAME_GNOME_COPIED_FILES) != targets.end()) { + Debug("%s: gnome copy file list available\n", __FUNCTION__); + refClipboard->request_contents(FCP_TARGET_NAME_GNOME_COPIED_FILES, + sigc::mem_fun(this, + &CopyPasteUI::LocalReceivedFileListCB)); + return; + } + + if (std::find(targets.begin(), + targets.end(), + FCP_TARGET_NAME_URI_LIST) != targets.end()) { + Debug("%s: KDE copy file list available\n", __FUNCTION__); + refClipboard->request_contents(FCP_TARGET_NAME_URI_LIST, + sigc::mem_fun(this, + &CopyPasteUI::LocalReceivedFileListCB)); + return; + } + + Debug("%s: ask for text\n", __FUNCTION__); + refClipboard->request_text(sigc::mem_fun(this, + &CopyPasteUI::LocalReceivedTextCB)); +} + + +/* + *---------------------------------------------------------------------- + * + * CopyPasteUI::LocalReceivedFileListCB -- + * + * Got clipboard or primary selection file list. Parse it and add + * it to the crossplaform clipboard. Send clipboard to the guest. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +CopyPasteUI::LocalReceivedFileListCB(const Gtk::SelectionData& sd) // IN +{ + Debug("%s: enter", __FUNCTION__); + const utf::string target = sd.get_target().c_str(); + + if (target == FCP_TARGET_NAME_GNOME_COPIED_FILES || + target == FCP_TARGET_NAME_URI_LIST) { + LocalGetSelectionFileList(sd); + mCP.SetRemoteClipboard(&mClipboard); + } +} + + +/* + *---------------------------------------------------------------------- + * + * CopyPasteUI::LocalReceivedTextCB -- + * + * Got clipboard (or primary selection) text, add to local clipboard + * cache and send clipboard to guest. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +CopyPasteUI::LocalReceivedTextCB(const Glib::ustring& text) // IN +{ + Debug("%s: enter", __FUNCTION__); + utf::string source = text; + + /* + * With 'cut' operation OpenOffice will put data into clipboard but + * set same timestamp for both clipboard and primary selection. + * If primary timestamp is same as clipboard timestamp, we should try + * clipboard again if primary selection is empty. For details please + * refer to bug 300780. + */ + if (0 == source.bytes() && + mClipTime == mPrimTime && + mClipTime != 0) { + mClipTime = 0; + mPrimTime = 0; + mGHSelection = GDK_SELECTION_CLIPBOARD; + + /* Ask for available targets from active selection. */ + Glib::RefPtr refClipboard = + Gtk::Clipboard::get(mGHSelection); + + refClipboard->request_targets( + sigc::mem_fun(this, &CopyPasteUI::LocalReceivedTargetsCB)); + return; + } + + Debug("%s: Got text: %s", __FUNCTION__, source.c_str()); + + /* Add NUL terminator. */ + CPClipboard_SetItem(&mClipboard, CPFORMAT_TEXT, source.c_str(), + source.bytes() + 1); + mCP.SetRemoteClipboard(&mClipboard); +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteUI::LocalGetSelectionFileList -- + * + * Contruct local file list and remote file list from selection data. + * Called by both DnD and FCP. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +void +CopyPasteUI::LocalGetSelectionFileList(const Gtk::SelectionData& sd) // IN +{ + utf::string source; + char *newPath; + char *newRelPath; + size_t newPathLen; + size_t index = 0; + DnDFileList fileList; + DynBuf buf; + + /* + * Turn the uri list into two \0 delimited lists. One for full paths and + * one for just the last path component. + */ + source = sd.get_data_as_string().c_str(); + Debug("%s: Got file list: [%s]\n", __FUNCTION__, source.c_str()); + + /* + * In gnome, before file list there may be a extra line indicating it + * is a copy or cut. + */ + if (source.startsWith("copy\n")) { + source = source.erase(0, 5); + } + + if (source.startsWith("cut\n")) { + source = source.erase(0, 4); + } + + while (source.bytes() > 0 && + (source[0] == '\n' || source[0] == '\r' || source[0] == ' ')) { + source = source.erase(0, 1); + } + + while ((newPath = DnD_UriListGetNextFile(source.c_str(), + &index, + &newPathLen)) != NULL) { + + /* + * Parse relative path. + */ + newRelPath = Str_Strrchr(newPath, DIRSEPC) + 1; // Point to char after '/' + + fileList.AddFile(newPath, newRelPath); + free(newPath); + } + + DynBuf_Init(&buf); + fileList.ToCPClipboard(&buf, true); + CPClipboard_SetItem(&mClipboard, CPFORMAT_FILELIST, DynBuf_Get(&buf), + DynBuf_GetSize(&buf)); + DynBuf_Destroy(&buf); +} + + +/* + *---------------------------------------------------------------------------- + * + * CopyPasteUI::GetNextPath -- + * + * Provides a substring containing the next path from the provided + * NUL-delimited string starting at the provided index. + * + * Results: + * A string with the next path or "" if there are no more paths. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +utf::utf8string +CopyPasteUI::GetNextPath(utf::utf8string& str, // IN: NUL-delimited path list + size_t& index) // IN/OUT: current index into string +{ + utf::utf8string ret; + size_t start; + + if (index >= str.length()) { + return ""; + } + + for (start = index; str[index] != '\0' && index < str.length(); index++) { + /* + * Escape reserved characters according to RFC 1630. We'd use + * Escape_Do() if this wasn't a utf::string, but let's use the same table + * replacement approach. + */ + static char const Dec2Hex[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', + }; + + unsigned char ubyte = str[index]; + + if (ubyte == '#' || /* Fragment identifier delimiter */ + ubyte == '?' || /* Query string delimiter */ + ubyte == '*' || /* "Special significance within specific schemes" */ + ubyte == '!' || /* "Special significance within specific schemes" */ + ubyte == '%' || /* Escape character */ + ubyte >= 0x80) { /* UTF-8 encoding bytes */ + str.replace(index, 1, "%"); + str.insert(index + 1, 1, Dec2Hex[ubyte >> 4]); + str.insert(index + 2, 1, Dec2Hex[ubyte & 0xF]); + index += 2; + } + } + + ret = str.substr(start, index - start); + Debug("%s: nextpath: %s", __FUNCTION__, ret.c_str()); + index++; + return ret; +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteUI::GetLastDirName -- + * + * Try to get last directory name from a full path name. + * + * Results: + * Last dir name in the full path name if sucess, empty str otherwise + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +utf::string +CopyPasteUI::GetLastDirName(const utf::string &str) // IN +{ + utf::string ret; + size_t start; + size_t end; + + end = str.bytes() - 1; + if (end >= 0 && DIRSEPC == str[end]) { + end--; + } + + if (end <= 0 || str[0] != DIRSEPC) { + return ""; + } + + start = end; + + while (str[start] != DIRSEPC) { + start--; + } + + return str.substr(start + 1, end - start); +} + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteUI::GetRemoteClipboardCB -- + * + * Invoked when got data from host. Update the internal data to get the file + * names or the text that needs to be transferred. + * + * Method for copy and paste from host to guest. + * + * Results: + * None + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +void +CopyPasteUI::GetRemoteClipboardCB(const CPClipboard *clip) // IN +{ + Glib::RefPtr refClipboard = + Gtk::Clipboard::get(GDK_SELECTION_CLIPBOARD); + Glib::RefPtr refPrimary = + Gtk::Clipboard::get(GDK_SELECTION_PRIMARY); + void *buf; + size_t sz; + + if (!clip) { + Debug("%s: No clipboard contents.", __FUNCTION__); + return; + } + + /* Clear the clipboard contents if we are the owner. */ + if (mIsClipboardOwner) { + refClipboard->clear(); + refPrimary->clear(); + mIsClipboardOwner = FALSE; + Debug("%s: Cleared local clipboard", __FUNCTION__); + } + + if (CPClipboard_GetItem(clip, CPFORMAT_TEXT, &buf, &sz)) { + Debug("%s: Text data: %s.\n", __FUNCTION__, (char *)buf); + refClipboard->set_text((const char *) buf); + refPrimary->set_text((const char *) buf); + } + if (CPClipboard_GetItem(clip, CPFORMAT_FILELIST, &buf, &sz)) { + Debug("%s: File data.\n", __FUNCTION__); + DnDFileList flist; + flist.FromCPClipboard(buf, sz); + mHGFCPData = flist.GetRelPathsStr(); + + refClipboard->set(mListTargets, + sigc::mem_fun(this, &CopyPasteUI::LocalGetFileRequestCB), + sigc::mem_fun(this, &CopyPasteUI::LocalClearClipboardCB)); + refPrimary->set(mListTargets, + sigc::mem_fun(this, &CopyPasteUI::LocalGetFileRequestCB), + sigc::mem_fun(this, &CopyPasteUI::LocalClearClipboardCB)); + + mIsClipboardOwner = TRUE; + mHGGetListTime = GetCurrentTime(); + mHGGetFilesInitated = FALSE; + mHGCopiedUriList = ""; + } +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteUI::GetLocalFiles -- + * + * Invoke the copy files from host to guest. + * + * Results: + * true if the request was sent. false otherwise. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +bool +CopyPasteUI::GetLocalFiles(void) +{ + Debug("%s: enter try to start HG file copy.\n", __FUNCTION__); +#if defined(NOT_YET) + ASSERT_IN_SAFE_CONTEXT(mSyncWnd); +#endif + + mHGStagingDir = mCP.GetFiles(); + +#if defined(NOT_YET) + if (mHGStagingDir.empty()) { + mDataObject->NotifyGetFilesDone(false); + return false; + } + mDataObject->SetStagingDir(mHGStagingDir); +#endif + return true; +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteUI::GetLocalFilesDone -- + * + * Callback when CopyPasteUI::GetLocalFiles is done, which finishes the file + * copying from host to guest staging directory. This function notifies + * the Copy/Paste data object and end its waiting state in order to continue + * the file copying from local staging directory to local target directory. + * + * Results: + * None + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +CopyPasteUI::GetLocalFilesDone(bool success) +{ + Debug("%s: enter success %d\n", __FUNCTION__, success); + if (success) { + /* + * Mark current staging dir to be deleted on next reboot for FCP. The + * file will not be deleted after reboot if it is moved to another + * location by target application. + */ + DnD_DeleteStagingFiles(mHGStagingDir.c_str(), TRUE); + } else { + /* Copied files are already removed in common layer. */ + mHGStagingDir.clear(); + } +#if defined(NOT_YET) + mDataObject->NotifyGetFilesDone(success); +#endif +} + + +/* + *----------------------------------------------------------------------------- + * + * CopyPasteUI::Reset -- + * + * + * Results: + * None + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +CopyPasteUI::Reset(void) +{ + Debug("%s: enter\n", __FUNCTION__); + /* Cancel any pending file transfer. */ +} + diff --git a/open-vm-tools/vmware-user/copyPasteUI.h b/open-vm-tools/vmware-user/copyPasteUI.h new file mode 100644 index 000000000..efc3a0b0d --- /dev/null +++ b/open-vm-tools/vmware-user/copyPasteUI.h @@ -0,0 +1,101 @@ +/********************************************************* + * 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. + * + *********************************************************/ + +/** + * @file copyPasteUI.h + * + * This class implements the methods that allows Copy/Paste + * between host and guest using version 3+ of the protocol. + * + */ + +#ifndef COPYPASTE_UI_H +#define COPYPASTE_UI_H + +#include "stringxx/string.hh" + +extern "C" { + #include "dnd.h" + #include "debug.h" + #include "str.h" +} + +#include "unicodeOperations.h" + +#include "copyPaste.hh" + +#include +#include + +class CopyPasteUI : public sigc::trackable +{ +public: + CopyPasteUI(); + virtual ~CopyPasteUI(); + void VmxCopyPasteVersionChanged(struct RpcIn *rpcIn, + uint32 version); + void SetCopyPasteAllowed(bool isCopyPasteAllowed) + { mCP.SetCopyPasteAllowed(isCopyPasteAllowed); } + void Reset(void); + bool GetLocalFiles(void); + +private: + + /* hg */ + void GetRemoteClipboardCB(const CPClipboard *clip); + void RemoteGetFilesDone(void); + void LocalGetFileRequestCB(Gtk::SelectionData& selection_data, guint info); + void LocalClearClipboardCB(void); + + /* gh */ + bool GetLocalClipboard(CPClipboard *clip); + void LocalClipboardTimestampCB(const Gtk::SelectionData& sd); + void LocalPrimTimestampCB(const Gtk::SelectionData& sd); + void LocalReceivedTargetsCB(const Glib::StringArrayHandle& targets_array); + void LocalReceivedFileListCB(const Gtk::SelectionData& selection_data); + void LocalReceivedTextCB(const Glib::ustring& text); + void GetLocalFilesDone(bool success); + + /* Conversion methods. */ + void LocalGetSelectionFileList(const Gtk::SelectionData& sd); + utf::utf8string GetNextPath(utf::utf8string &str, size_t& index); + utf::string GetLastDirName(const utf::string &str); + + VmTimeType GetCurrentTime(void); + + // Member variables + CopyPaste mCP; + bool mClipboardEmpty; + std::string mHGStagingDir; + std::list mListTargets; + bool mIsClipboardOwner; + uint64 mClipTime; + uint64 mPrimTime; + uint64 mClipTimePrev; + uint64 mPrimTimePrev; + GdkAtom mGHSelection; + CPClipboard mClipboard; + + /* File vars. */ + bool mHGGetFilesInitated; + VmTimeType mHGGetListTime; + utf::string mHGCopiedUriList; + utf::utf8string mHGFCPData; +}; + +#endif // COPYPASTE_UI_H diff --git a/open-vm-tools/vmware-user/copyPasteV3.h b/open-vm-tools/vmware-user/copyPasteV3.h deleted file mode 100644 index cdd9ab72d..000000000 --- a/open-vm-tools/vmware-user/copyPasteV3.h +++ /dev/null @@ -1,90 +0,0 @@ -/********************************************************* - * Copyright (C) 2007 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. - * - *********************************************************/ - -/* - * copyPaste.h -- - * - * Handles the linux specific copy paste operations. - * - * TODO: Data conversion out into another class to be shared between dnd - * and copypaste. - */ - -#ifndef LUI_COPYPASTE_HH -#define LUI_COPYPASTE_HH - -#include "cui/dnd/copyPaste.hh" -#include "cui/vm.hh" - -extern "C" { -#include "dnd.h" -#include "../dnd/dndClipboard.h" -} - -namespace lui { - class CopyPaste - : public sigc::trackable - { - public: - CopyPaste(cui::dnd::CopyPaste *cp); - ~CopyPaste(void); - - void UpdateLocalClipboard(void); - void UpdateRemoteClipboard(void); - - private: - /* gh */ - void GetRemoteClipboardCB(const CPClipboard *clip); - void RemoteGetFilesDone(void); - /* clipboard stuff */ - void LocalGetFileRequestCB(Gtk::SelectionData& selection_data, guint info); - void LocalClearClipboardCB(void); - - /* hg */ - /* Clipboard stuff */ - void LocalClipboardTimestampCB(const Gtk::SelectionData& sd); - void LocalPrimTimestampCB(const Gtk::SelectionData& sd); - void LocalReceivedTargetsCB(const Glib::StringArrayHandle& targets_array); - void LocalReceivedFileListCB(const Gtk::SelectionData& selection_data); - void LocalReceivedTextCB(const Glib::ustring& text); - - /* Convertion methods. */ - void LocalGetSelectionFileList(const Gtk::SelectionData& sd); - utf::utf8string GetNextPath(utf::utf8string &str, size_t& index); - utf::string GetLastDirName(const utf::string &str); - - cui::dnd::CopyPaste * const mCP; - - CPClipboard mClipboard; - bool mIsClipboardOwner; - uint64 mClipTime; - uint64 mPrimTime; - GdkAtom mHGSelection; - - /* File vars. */ - bool mGHGetFilesInitated; - VmTimeType mGHGetListTime; - utf::string mGHCopiedUriList; - utf::utf8string mGHFCPData; - - std::list mListTargets; - }; - -} //namespace lui - -#endif // LUI_COPYPASTE_HH diff --git a/open-vm-tools/vmware-user/copyPasteWrapper.cpp b/open-vm-tools/vmware-user/copyPasteWrapper.cpp new file mode 100644 index 000000000..61b492448 --- /dev/null +++ b/open-vm-tools/vmware-user/copyPasteWrapper.cpp @@ -0,0 +1,241 @@ +/********************************************************* + * 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. + * + *********************************************************/ + +/** + * @file copyPasteWrapper.cpp + * + * This singleton class implements a wrapper around various versions of + * copy and paste protocols, and provides some convenience functions that + * help to make VMwareUser a bit cleaner. + */ + +#include "copyPasteWrapper.h" +extern "C" { +#include "vmwareuserInt.h" +#include "debug.h" +} + + +/** + * CopyPasteWrapper is a singleton, here is a pointer to its only instance. + */ +CopyPasteWrapper *CopyPasteWrapper::m_instance = 0; + +/** + * + * Get an instance of CopyPasteWrapper, which is an application singleton. + * + * @return a pointer to the singleton CopyPasteWrapper object, or NULL if + * for some reason it could not be allocated. + */ + +CopyPasteWrapper * +CopyPasteWrapper::GetInstance() +{ + if (!m_instance) { + m_instance = new CopyPasteWrapper; + } + return m_instance; +} + + +/** + * + * Constructor. + */ + +CopyPasteWrapper::CopyPasteWrapper() : +#if defined(HAVE_GTKMM) + m_copyPasteUI(NULL), +#endif + m_isRegistered(FALSE), + m_userData(NULL), + m_version(-1) +{ +} + + +/** + * + * Destructor. + */ + +CopyPasteWrapper::~CopyPasteWrapper() +{ + if (IsRegistered()) { + Unregister(); + } +} + + +/** + * + * Attach implementation-specific data (in reality, a GtkWidget * that is + * needed by the legacy copy paste code. Going forward, any new protocol + * versions should be implemented as classes, and should not need such a + * crutch). + * + * @param[in] userData a GtkWidget created by VMwareUser and used by the + * legacy copy and paste implementation. + */ + +void +CopyPasteWrapper::SetUserData(const void *userData) +{ + m_userData = userData; +} + + +/** + * + * Register copy and paste capabilities with the VMX. Try newest version + * first, then fall back to the legacy implementation. + * + * @return TRUE on success, FALSE on failure + */ + +bool +CopyPasteWrapper::Register() +{ + if (IsRegistered()) { + return TRUE; + } + + /* + * Try to get version 3, and if that fails, go for the compatibility + * versions (1 and 2). + */ + +#if defined(HAVE_GTKMM) + Debug("%s: enter\n", __FUNCTION__); + m_copyPasteUI = new CopyPasteUI(); + if (m_copyPasteUI) { + SetIsRegistered(TRUE); + int version = GetVersion(); + Debug("%s: version is %d\n", __FUNCTION__, version); + if (version >= 3) { + m_copyPasteUI->VmxCopyPasteVersionChanged(gRpcIn, version); + m_copyPasteUI->SetCopyPasteAllowed(TRUE); + } else { + Debug("%s: version < 3, unregistering.\n", __FUNCTION__); + Unregister(); + } + } +#endif + if (!IsRegistered()) { + SetIsRegistered(CopyPaste_Register((GtkWidget *)m_userData)); + if (IsRegistered()) { + if (!CopyPaste_RegisterCapability()) { + Unregister(); + } + } + } + return IsRegistered(); +} + +/** + * + * Unregister copy paste capabilities and do general cleanup. + */ + +void +CopyPasteWrapper::Unregister() +{ + if (!IsRegistered()) { + return; + } +#if defined(HAVE_GTKMM) + if (m_copyPasteUI) { + delete m_copyPasteUI; + m_copyPasteUI = NULL; + } else { +#endif + CopyPaste_Unregister((GtkWidget *)m_userData); +#if defined(HAVE_GTKMM) + } +#endif + SetIsRegistered(FALSE); + m_version = -1; +} + + +/** + * + * Get the version of the copy paste protocol being wrapped. + * + * @return copy paste protocol version. + */ + +int +CopyPasteWrapper::GetVersion() +{ + if (IsRegistered()) { + m_version = CopyPaste_GetVmxCopyPasteVersion(); + } + Debug("%s: got version %d\n", __FUNCTION__, m_version); + return m_version; +} + +/** + * + * Set a flag indicating that we are wrapping an initialized and registered + * copy paste implementation, or not. + * + * @param[in] isRegistered If TRUE, protocol is registered, otherwise FALSE. + */ + +void +CopyPasteWrapper::SetIsRegistered(bool isRegistered) +{ + m_isRegistered = isRegistered; +} + +/** + * + * Get the flag indication that we are wrapping an initialized and registered + * copy paste implementation, or not. + * + * @return TRUE if copy paste is initialized, otherwise FALSE. + */ + +bool +CopyPasteWrapper::IsRegistered() +{ + return m_isRegistered; +} + + +/** + * + * Handle reset by calling protocol dependent handlers. + */ + +void +CopyPasteWrapper::OnReset() +{ + Debug("%s: enter\n", __FUNCTION__); + if (IsRegistered()) { + Unregister(); + } + Register(); + if (!IsRegistered()) { + Debug("%s: unable to reset!\n", __FUNCTION__); + } +} + + diff --git a/open-vm-tools/vmware-user/copyPasteWrapper.h b/open-vm-tools/vmware-user/copyPasteWrapper.h new file mode 100644 index 000000000..95b1e3f3a --- /dev/null +++ b/open-vm-tools/vmware-user/copyPasteWrapper.h @@ -0,0 +1,63 @@ +/********************************************************* + * 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. + * + *********************************************************/ + +/** + * @file copyPasteWrapper.h + * + * This singleton class implements a wrapper around various versions of + * copy and paste. + * + */ + +#ifndef COPYPASTEWRAPPER_H +#define COPYPASTEWRAPPER_H + +#if defined(HAVE_GTKMM) +#include "copyPasteUI.h" +#endif + +class CopyPasteWrapper +{ +public: + ~CopyPasteWrapper(); + void SetUserData(const void *userData); + bool Register(); + void Unregister(); + static CopyPasteWrapper *GetInstance(); + int GetVersion(); + void SetIsRegistered(bool isRegistered); + bool IsRegistered(); + void OnReset(); +private: + /* + * We're a singleton, so it is a compile time error to call these. + */ + CopyPasteWrapper(); + CopyPasteWrapper(const CopyPasteWrapper &wrapper); + CopyPasteWrapper& operator=(const CopyPasteWrapper &wrapper); +private: +#if defined(HAVE_GTKMM) + CopyPasteUI *m_copyPasteUI; +#endif + bool m_isRegistered; + const void *m_userData; + int m_version; + static CopyPasteWrapper *m_instance; +}; + +#endif // COPYPASTEWRAPPER_H diff --git a/open-vm-tools/vmware-user/dnd.cpp b/open-vm-tools/vmware-user/dnd.cpp new file mode 100644 index 000000000..926a85f3a --- /dev/null +++ b/open-vm-tools/vmware-user/dnd.cpp @@ -0,0 +1,2698 @@ +/********************************************************* + * Copyright (C) 2005 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. + * + *********************************************************/ + +/* + * dnd.c -- + * + * Handles the guest side of host<->guest DnD operations. + * + * Guest->Host DnD + * --------------- + * + * The DnD process within the guest starts when we receive a "dnd.ungrab" RPC + * message from the host, which invokes DnDRpcInMouseUngrabCB(). The MKS + * sends this RPC when it sees the mouse stray outside of the clip (guest's + * viewable area). RpcInMouseUngrabCB() will determine whether a DnD is + * pending by calling DnDDragPending(): + * o if a DnD is not pending, it replies with a "dnd.notpending" RPC and we + * are done, + * o if a DnD is pending, we send fake X events to the X server that place + * our invisible window at the location of the mouse pointer and generate + * mouse movements over the window. + * + * Faking mouse movement over our window causes Gtk to send us a "drag_motion" + * signal, which invokes DnDGtkDragMotionCB(). Here we find a common target + * (drop type) and request the data from the drag source via + * gtk_drag_get_data(). + * + * When the data is ready, Gtk signals us with a "data_received" signal. We + * parse the provided data and send the file names to the host with + * a "dnd.data.set" RPC. Then we start the DnD operation with a "dnd.enter" + * RPC. Upon receiving the "dnd.enter", the MKS will allow the ungrab of the + * mouse from the guest window and the user will be able to select a location + * to drop the files. + * + * (Note that it is important that the guest reply to the "dnd.ungrab" with + * either a "dnd.notpending" or a "dnd.enter" in a timely manner, since the + * MKS will delay mouse packets until it has received a reply from the + * guest.) + * + * When the user drops the files, the host will send us a "dnd.data.get.file" + * for each file, which invokes DnDRpcInGetNextFileCB(). On each invocation, + * we reply with the next file from the Guest->Host file list (obtained from + * DnDGHFileListGetNext()), and "|end|" when there are no more files. With + * this information, the host copies the files from the guest using HGFS. + * + * When the host has finished copying the files, it sends us a "dnd.finish" + * RPC, which invokes DnDRpcInFinishCB(). At this point, we fake X events + * that cause a mouse button release over our window. + * + * This button release causes Gtk to send us a "drag_drop" signal, which + * invokes DnDGtkDragDropCB(). Here we simply clean up our state and + * indicate that the drag finished successfully by calling gtk_drag_finish(). + * + * If an error occurs at any point, the host sends us a "dnd.finish cancel" + * RPC. We will fake an ESC key press and release to cancel the pending DnD + * in the guest. + * + * + * Host->Guest DnD + * --------------- + * + * A host->guest DnD begins with a "dnd.data.set" from the vmx to provide the + * list of files being dragged into the guest, then a "dnd.enter" to begin the + * DnD operation. When the "dnd.enter" is received, this process will send + * a fake mouse button press and mouse movement on its window, starting the + * DnD operation within the guest. At this point the mouse still has not been + * grabbed by the guest and all mouse movements go only to the host. + * + * As part of the normal DnD protocol on the host, the UI in the host will + * receive updates on the location of the mouse within its target window. + * This location is translated to guest coordinates and sent to us via the + * "dnd.move" RPC, at which point we fake additional mouse movements to that + * location. When the user releases the mouse, the host UI is again notified + * and sends us a "dnd.drop" RPC. + * + * When the drop occurs, we add a block (via vmblock) on the directory + * containing the files to be given to the target application, then fake + * a mouse release at the location of the drop. This will cause the target + * application to request the data, which we provide through our + * "drag_data_get" handler (DnDGtkDataRequestCB()). When the application + * attempts to access these files it will be blocked by vmblock. + * + * After the drop is sent, the host will send the files to the hgfs server + * running inside this process, and will notify us when that transfer is + * complete via the "dnd.data.finish" RPC. If the transfer is successful, we + * remove the block to allow the target application to access the files. If + * the transfer is unsuccessful, we remove any partially copied files then + * remove the block; this has the effect of failing the DnD operation since + * the target cannot access the necessary files. Once this is done, we + * generate a new file root within the staging directory and send that to the + * host for the next DnD operation. + * + * Note that we used to fake the mouse release only after the data transfer + * completed (and Windows guests still behave that way), but this was changed + * since the Linux UI was modified to allow guest interaction while the + * progress dialog (for the file transfer) was displayed and updating. This + * caused a lot of instability since the mouse was no longer in a predictable + * state when the fake release was sent. vmblock let us work around this by + * changing where the block occurred. + */ + + +#include +#include +#include /* for XTest*() */ +#include /* for XK_Escape */ +#include /* for XA_WINDOW */ + +#include "vmwareuserInt.h" +#include "vm_app.h" +#include "vm_assert.h" +#include "vm_basic_defs.h" +#include "eventManager.h" +#include "debug.h" +#include "strutil.h" +#include "str.h" +#include "file.h" +#include "guestApp.h" +#include "cpName.h" +#include "cpNameUtil.h" +#include "dnd.h" +#include "util.h" +#include "hgfsVirtualDir.h" +#include "hgfsServerPolicy.h" +#include "vmblock.h" +#include "escape.h" + + +#define DND_MAX_PATH 6144 +#define DRAG_TARGET_NAME_URI_LIST "text/uri-list" +#define DRAG_TARGET_INFO_URI_LIST 0 +#define DRAG_TARGET_NAME_TEXT_PLAIN "text/plain" +#define DRAG_TARGET_INFO_TEXT_PLAIN 1 +#define DRAG_TARGET_NAME_STRING "STRING" +#define DRAG_TARGET_INFO_STRING 2 +/* + * We support all three drag targets from Host->Guest since we can present + * filenames in any of these forms if an application requests. However, we + * only support file drag targets (text/uri-list) from Guest->Host since we + * can only DnD files across the backdoor. + */ +#define NR_DRAG_TARGETS 3 +#define NR_GH_DRAG_TARGETS 1 + +#define DROPEFFECT_NONE 0 +#define DROPEFFECT_COPY 1 +#define DROPEFFECT_MOVE 2 +#define DROPEFFECT_LINK 4 + +/* + * More friendly names for calling DnDFakeXEvents(). This is really ugly but + * it allows us to keep all of the X fake event code in one place. + * + * Operation | showWidget | buttonEvent | buttonPress | moveWindow | coordsProvided + * ----------+------------+-------------+-------------+------------+--------------- + * G->H Drag | Yes | No | n/a | Yes | No + * G->H Drop | No | Yes | Release | Yes | No + * H->G Drag | Yes | Yes | Press | Yes | No + * H->G Move | No | No | n/a | No | Yes + * H->G Drop | No | Yes | Release | No | Yes + * ----------+------------+-------------+-------------+------------+--------------- + */ +#define DnDGHFakeDrag(widget) \ + DnDFakeXEvents(widget, TRUE, FALSE, FALSE, TRUE, FALSE, 0, 0) +#define DnDGHFakeDrop(widget) \ + DnDFakeXEvents(widget, FALSE, TRUE, FALSE, TRUE, FALSE, 0, 0) +#define DnDHGFakeDrag(widget) \ + DnDFakeXEvents(widget, TRUE, TRUE, TRUE, TRUE, FALSE, 0, 0) +#define DnDHGFakeMove(widget, x, y) \ + DnDFakeXEvents(widget, FALSE, FALSE, FALSE, FALSE, TRUE, x, y) +#define DnDHGFakeDrop(widget, x, y) \ + DnDFakeXEvents(widget, FALSE, TRUE, FALSE, FALSE, TRUE, x, y) + +#ifdef GTK2 +# define GDKATOM_TO_ATOM(gdkAtom) gdk_x11_atom_to_xatom(gdkAtom) +#else +# define GDKATOM_TO_ATOM(gdkAtom) gdkAtom +#endif + +/* + * Forward Declarations + */ +static Bool DnDRpcInEnterCB (char const **result, size_t *resultLen, + const char *name, const char *args, + size_t argsSize,void *clientData); +static Bool DnDRpcInDataSetCB (char const **result, size_t *resultLen, + const char *name, const char *args, + size_t argsSize,void *clientData); +static Bool DnDRpcInMoveCB (char const **result, size_t *resultLen, + const char *name, const char *args, + size_t argsSize,void *clientData); +static Bool DnDRpcInDataFinishCB (char const **result, size_t *resultLen, + const char *name, const char *args, + size_t argsSize,void *clientData); +static Bool DnDRpcInDropCB (char const **result, size_t *resultLen, + const char *name, const char *args, + size_t argsSize,void *clientData); +static Bool DnDRpcInMouseUngrabCB(char const **result, size_t *resultLen, + const char *name, const char *args, + size_t argsSize,void *clientData); +static Bool DnDRpcInGetNextFileCB(char const **result, size_t *resultLen, + const char *name, const char *args, + size_t argsSize,void *clientData); +static Bool DnDRpcInFinishCB (char const **result, size_t *resultLen, + const char *name, const char *args, + size_t argsSize,void *clientData); + +/* + * Gtk DnD specific event/signal callbacks. + */ +/* For Host->Guest DnD */ +static void DnDGtkBeginCB(GtkWidget *widget, GdkDragContext *dc, gpointer data); +static void DnDGtkEndCB(GtkWidget *widget, GdkDragContext *dc, gpointer data); +static void DnDGtkDataRequestCB(GtkWidget *widget, GdkDragContext *dc, + GtkSelectionData *selection_data, + guint info, guint time, gpointer data); + +/* For Guest->Host DnD */ +static gboolean DnDGtkDragMotionCB(GtkWidget *widget, GdkDragContext *dc, gint x, + gint y, guint time, gpointer data); +static void DnDGtkDragDataReceivedCB(GtkWidget *widget, GdkDragContext *dc, + gint x, gint y, GtkSelectionData *dragData, + guint info, guint time, gpointer data); +static gboolean DnDGtkDragDropCB(GtkWidget *widget, GdkDragContext *dc, + gint x, gint y, guint time, gpointer data); + +/* + * Utility + */ +static Bool DnDSendVmxNewFileRoot(char *rpcCmd); +static Bool DnDFakeXEvents(GtkWidget *widget, Bool showWidget, + Bool buttonEvent, Bool buttonPress, + Bool moveWindow, + Bool coordsProvided, int x, int y); +static void DnDSendEscapeKey(GtkWidget *mainWnd); +static INLINE Bool DnDGHDragPending(GtkWidget *widget); +static INLINE Bool DnDGHXdndDragPending(GtkWidget *widget); +static INLINE void DnDGHXdndClearPending(GtkWidget *widget); +static INLINE Bool DnDGHMotifDragPending(GtkWidget *widget); +static INLINE void DnDGHFileListClear(void); +static INLINE void DnDGHFileListSet(char *fileList, size_t fileListSize); +static INLINE Bool DnDGHFileListGetNext(char **fileName, size_t *fileNameSize); +static INLINE void DnDGHStateInit(GtkWidget *widget); +static INLINE void DnDHGStateInit(void); +static INLINE Bool DnDGHCancel(GtkWidget *widget); +static Bool DnDGHXEventTimeout(void *clientData); + +/* + * Globals + */ +struct ghState { + Bool dragInProgress; + Bool ungrabReceived; + char *dndFileList; + char *dndFileListNext; + size_t dndFileListSize; + GdkDragContext *dragContext; + guint time; + Event *event; +} gGHState; +static Bool gHGDnDInProgress; +static Bool gDoneDragging; +static Bool gHGDataPending; +static char gDnDData[1024]; +static GdkDragContext *gDragCtx; +static GtkTargetEntry gTargetEntry[NR_DRAG_TARGETS]; +static GdkAtom gTargetEntryAtom[NR_GH_DRAG_TARGETS]; +static char gFileRoot[DND_MAX_PATH]; +static size_t gFileRootSize; +static size_t gDnDDataSize; +static Bool gUnity; + +/* + * From vmwareuserInt.h + */ +RpcIn *gRpcIn; +Display *gXDisplay; +Window gXRoot; + + +/* + * Host->Guest RPC callback implementations + */ + +/* + *----------------------------------------------------------------------------- + * + * DnDRpcInEnterCB -- + * + * For Host->Guest operations only. + * User has dragged something over this guest's MKS window + * + * Results: + * TRUE on success, FALSE otherwise + * + * Side effects: + * Some GdkEvents are generated which will "drag" the mouse. A directory + * is created. + * + *----------------------------------------------------------------------------- + */ + +static Bool +DnDRpcInEnterCB(char const **result, // OUT + size_t *resultLen, // OUT + const char *name, // IN + const char *args, // IN + size_t argsSize, // Ignored + void *clientData) // IN +{ + char *numFormats; + char *pFormat; + unsigned int index = 0; + int nFormats; + int i; + GtkWidget *mainWnd; + + Debug("Got DnDRpcInEnterCB\n"); + mainWnd = GTK_WIDGET(clientData); + if (mainWnd == NULL) { + return RpcIn_SetRetVals(result, resultLen, + "bad clientData passed to callback", FALSE); + } + + if (gBlockFd < 0) { + Debug("DnDRpcInEnterCB: cannot allow H->G DnD without vmblock.\n"); + return RpcIn_SetRetVals(result, resultLen, + "blocking file system unavailable", FALSE); + } + + numFormats = StrUtil_GetNextToken(&index, args, " "); + if (!numFormats) { + Debug("DnDRpcInEnterCB: Failed to parse numformats\n"); + return RpcIn_SetRetVals(result, resultLen, + "must specify number of formats", FALSE); + } + + /* Skip whitespace character. */ + index++; + + nFormats = atoi(numFormats); + free(numFormats); + + for (i = 0; i < nFormats; i++) { + pFormat = StrUtil_GetNextToken(&index, args, ","); + + if (!pFormat) { + Debug("DnDRpcInEnterCB: Failed to parse format list\n"); + return RpcIn_SetRetVals(result, resultLen, + "Failed to read format list", FALSE); + } else { + /* + * TODO: check that formats are ok for us to handle. For now, this is + * ok since there should only be a CF_HDROP. But, we really should figure + * out a much more cross-platform format scheme + */ + free(pFormat); + } + } + + if (!DnDHGFakeDrag(mainWnd)) { + Debug("DnDRpcInEnterCB: Failed to fake X events\n"); + return RpcIn_SetRetVals(result, resultLen, + "failed to fake drag", FALSE); + } + + RpcIn_SetRetVals(result, resultLen, "", TRUE); + RpcOut_sendOne(NULL, NULL, "dnd.feedback copy"); + Debug("DnDRpcInEnterCB finished\n"); + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * DnDRpcInDataSetCB -- + * + * For Host->Guest operations only. + * Host is sending data from a DnD operation. + * + * Results: + * TRUE on success, FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static Bool +DnDRpcInDataSetCB(char const **result, // OUT + size_t *resultLen, // OUT + const char *name, // IN + const char *args, // IN + size_t argsSize, // IN: Size of args + void *clientData) // Ignored +{ + char blockDir[DND_MAX_PATH]; + char *perDnDDir = NULL; + char *format; + char *data; + unsigned int index = 0; + size_t dataSize; + char *retStr; + Bool ret = FALSE; + + Debug("DnDRpcInDataSetCB: enter\n"); + + if (gBlockFd < 0) { + Debug("DnDRpcInDataSetCB: blocking file system not available.\n"); + return RpcIn_SetRetVals(result, resultLen, + "blocking file system not available", FALSE); + } + + /* Parse the data type & value string. */ + format = StrUtil_GetNextToken(&index, args, " "); + if (!format) { + Debug("DnDRpcInDataSetCB: Failed to parse format\n"); + return RpcIn_SetRetVals(result, resultLen, "need format", FALSE); + } + + index++; /* Ignore leading space before data. */ + dataSize = argsSize - index; + data = Util_SafeMalloc(dataSize); + memcpy(data, args + index, dataSize); + + Debug("DnDRpcInDataSetCB: Received data from host: (%s) [%s] (%"FMTSZ"u)\n", + format, CPName_Print(data, dataSize), dataSize); + + /* + * Here we take the last component of the actual file root, which is + * a temporary directory for this DnD operation, and append it to the mount + * point for vmblock. This is where we want the target application to + * access the file since it will enable vmblock to block that application's + * progress if necessary. + */ + perDnDDir = DnD_GetLastDirName(gFileRoot); + if (!perDnDDir) { + Debug("DnDRpcInDataSetCB: cannot obtain dirname of root.\n"); + retStr = "error obtaining dirname of root"; + goto out; + } + + if (sizeof VMBLOCK_MOUNT_POINT - 1 + + (sizeof DIRSEPS - 1) * 2 + strlen(perDnDDir) >= sizeof blockDir) { + Debug("DnDRpcInDataSetCB: blocking directory path too large.\n"); + retStr = "blocking directory path too large"; + goto out; + } + + Str_Sprintf(blockDir, sizeof blockDir, + VMBLOCK_MOUNT_POINT DIRSEPS "%s" DIRSEPS, perDnDDir); + + /* Add the file root to the relative paths received from host */ + if (!DnD_PrependFileRoot(blockDir, &data, &dataSize)) { + Debug("DnDRpcInDataSsetCB: error prepending guest file root\n"); + retStr = "error prepending file root"; + goto out; + } + if (dataSize + 1 > sizeof gDnDData) { + Debug("DnDRpcInDataSetCB: data too large\n"); + retStr = "data too large"; + goto out; + } + + memcpy(gDnDData, data, dataSize + 1); + gDnDDataSize = dataSize; + Debug("DnDRpcInDataSetCB: prepended file root [%s] (%"FMTSZ"u)\n", + CPName_Print(gDnDData, gDnDDataSize), gDnDDataSize); + + retStr = ""; + ret = TRUE; + +out: + free(format); + free(data); + free(perDnDDir); + return RpcIn_SetRetVals(result, resultLen, retStr, ret); +} + + +/* + *----------------------------------------------------------------------------- + * + * DnDRpcInMoveCB -- + * + * For Host->Guest operations only. + * Host user is dragging data over this guest's MKS window + * + * Results: + * TRUE on success, FALSE otherwise. + * + * Side effects: + * Send a gdk event that "moves" the mouse. + * + *----------------------------------------------------------------------------- + */ + +static Bool +DnDRpcInMoveCB(char const **result, // OUT + size_t *resultLen, // OUT + const char *name, // IN + const char *args, // IN + size_t argsSize, // Ignored + void *clientData) // IN: pointer to mainWnd +{ + GtkWidget *mainWnd; + char *sXCoord; + char *sYCoord; + unsigned int index = 0; + int xCoord, yCoord; + + mainWnd = GTK_WIDGET(clientData); + if (mainWnd == NULL) { + return RpcIn_SetRetVals(result, resultLen, + "bad clientData passed to callback", FALSE); + } + + sXCoord = StrUtil_GetNextToken(&index, args, " "); + sYCoord = StrUtil_GetNextToken(&index, args, " "); + + if (!sXCoord || !sYCoord) { + Debug("DnDRpcInMove: Failed to parse coords\n"); + free(sXCoord); + free(sYCoord); + return RpcIn_SetRetVals(result, resultLen, + "error reading mouse move data", FALSE); + } + + xCoord = atoi(sXCoord); + yCoord = atoi(sYCoord); + + free(sXCoord); + free(sYCoord); + + /* Fake a mouse move */ + if (!DnDHGFakeMove(mainWnd, xCoord, yCoord)) { + Debug("DnDRpcInMove: Failed to fake mouse movement\n"); + return RpcIn_SetRetVals(result, resultLen, + "failed to move mouse", FALSE); + } + + return RpcIn_SetRetVals(result, resultLen, "", TRUE); +} + + +/* + *----------------------------------------------------------------------------- + * + * DnDRpcInDataFinishCB -- + * + * For Host->Guest operations only. + * Host has finished transferring DnD data to the guest. We do any post + * H->G operation cleanup here, like removing the block on the staging + * directory, picking a new file root, and informing the host of the new + * root. + * + * Results: + * TRUE on success, FALSE otherwise + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static Bool +DnDRpcInDataFinishCB(char const **result, // OUT + size_t *resultLen, // OUT + const char *name, // IN + const char *args, // IN + size_t argsSize, // Ignored + void *clientData) // Ignored +{ + unsigned int index = 0; + char *state; + + Debug("DnDRpcInDataFinishCB: enter\n"); + + state = StrUtil_GetNextToken(&index, args, " "); + if (!state) { + Debug("DnDRpcInDataFinishCB: could not get dnd finish state.\n"); + return RpcIn_SetRetVals(result, resultLen, + "could not get dnd finish state", FALSE); + } + + /* + * If the guest doesn't support vmblock, we'll have bailed out of + * DndRpcInDropCB before setting gHGDataPending. Thus, it doesn't make sense + * to pop a warning here, but let's keep the message around just in case + * there can be a failure worth hearing about. + */ + if (!gHGDataPending) { + Debug("DnDRpcInDataFinishCB: expected gHGDataPending to be set.\n"); + } + + gHGDataPending = FALSE; + + /* + * The host will send us "success" or "error", depending on whether the + * transfer finished successfully. In either case we remove the pending + * block, but in the "error" case we also need to delete all the files so + * the destination application doesn't access the partially copied files and + * mistake them for a successful drop. + */ + if (strcmp(state, "success") != 0) { + /* On any non-success input, delete the files. */ + DnD_DeleteStagingFiles(gFileRoot, FALSE); + } + + free(state); + + if (gBlockFd >= 0 && !DnD_RemoveBlock(gBlockFd, gFileRoot)) { + Warning("DnDRpcInDataFinishCB: could not remove block on %s\n", + gFileRoot); + } + + /* Pick a new file root and send that to the host for the next DnD. */ + if (!DnDSendVmxNewFileRoot("dnd.setGuestFileRoot")) { + Debug("DnDRpcInDataFinishCB: Failed to send dnd.setGuestFileRoot " + "message to host\n"); + return RpcIn_SetRetVals(result, resultLen, "could not send guest root", FALSE); + } + + return RpcIn_SetRetVals(result, resultLen, "", TRUE); +} + + +/* + *----------------------------------------------------------------------------- + * + * DnDRpcInDropCB -- + * + * For Host->Guest operations only. + * Host user has dropped data over this guest's MKS window. We add + * a block on the staging directory then send a fake mouse release to + * invoke the drop completion (from Gtk's point of view). + * + * Results: + * TRUE on success, FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static Bool +DnDRpcInDropCB(char const **result, // OUT + size_t *resultLen, // OUT + const char *name, // IN + const char *args, // IN + size_t argsSize, // Ignored + void *clientData) // IN: Gtk window widget +{ + GtkWidget *mainWnd; + char *sXCoord; + char *sYCoord; + unsigned int index = 0; + int xCoord, yCoord; + + Debug("DnDRpcInDropCB: enter\n"); + + gDoneDragging = TRUE; + + mainWnd = GTK_WIDGET(clientData); + if (mainWnd == NULL) { + return RpcIn_SetRetVals(result, resultLen, + "bad clientData passed to callback", FALSE); + } + + sXCoord = StrUtil_GetNextToken(&index, args, " "); + sYCoord = StrUtil_GetNextToken(&index, args, " "); + + if (!sXCoord || !sYCoord) { + Debug("DnDRpcInDropCB: Failed to parse coords\n"); + free(sXCoord); + free(sYCoord); + return RpcIn_SetRetVals(result, resultLen, + "must specify drop coordinates", FALSE); + } + + xCoord = atoi(sXCoord); + yCoord = atoi(sYCoord); + + free(sXCoord); + free(sYCoord); + + Debug("DnDRpcInDropCB: Received drop notification at (%d,%d)\n", + xCoord, yCoord); + + /* + * Add a block on the guest file root, warp the pointer, then fake the mouse + * release. Make sure we'll succeed before modifying any mouse state in the + * guest. + */ + if (gBlockFd < 0) { + /* + * We shouldn't get here since DnDRpcInEnterCB() checks this, but we'll + * check rather than ASSERT just in case. + */ + return RpcIn_SetRetVals(result, resultLen, + "blocking file system unavailable", FALSE); + } + + if (!DnD_AddBlock(gBlockFd, gFileRoot)) { + return RpcIn_SetRetVals(result, resultLen, "could not add block", FALSE); + } + + /* Update state before causing faking any mouse or keyboard changes. */ + gHGDataPending = TRUE; + + + if (!DnDHGFakeDrop(mainWnd, xCoord, yCoord)) { + Debug("DnDRpcInDropCB: failed to fake drop\n"); + return RpcIn_SetRetVals(result, resultLen, "failed to fake drop", FALSE); + } + + return RpcIn_SetRetVals(result, resultLen, "", TRUE); +} + + +/* + * Guest->Host RPC callback implementations + */ + +/* + *---------------------------------------------------------------------------- + * + * DnDRpcInMouseUngrabCB -- + * + * For Guest->Host operations only. + * + * Called when a mouse ungrab is attempted with the mouse button down. When + * the MKS sees mouse movements outside of the clip (the viewable portion of + * the guest's display) while a mouse button is down, this function is + * called so we can inform the MKS whether to allow the ungrab (and start + * a DnD if one is pending). + * + * Results: + * TRUE on success, FALSE on failure. + * + * Side effects: + * The GDK window is moved and resized, and the mouse is moved over it. + * + *---------------------------------------------------------------------------- + */ + +static Bool +DnDRpcInMouseUngrabCB(char const **result, // OUT + size_t *resultLen, // OUT + const char *name, // IN + const char *args, // IN + size_t argsSize, // Ignored + void *clientData) // IN +{ + unsigned int index = 0; + GtkWidget *mainWnd; + int32 xPos; + int32 yPos; + + Debug("Got DnDRpcInMouseUngrabCB\n"); + mainWnd = GTK_WIDGET(clientData); + if (mainWnd == NULL) { + Warning("DnDRpcInMouseUngrabCB: invalid clientData\n"); + return RpcIn_SetRetVals(result, resultLen, + "bad clientData passed to callback", FALSE); + } + + /* + * If there is already a DnD or copy/paste in progress (including the file + * transfer), don't allow another. + */ + if (gHGDataPending || gGHState.dragInProgress || CopyPaste_InProgress()) { + RpcOut_sendOne(NULL, NULL, "dnd.notpending"); + return RpcIn_SetRetVals(result, resultLen, + "dnd already in progress", FALSE); + } + + if (!StrUtil_GetNextIntToken(&xPos, &index, args, " ")) { + Warning("DnDRpcInMouseUngrabCB: could not parse x coordinate\n"); + RpcOut_sendOne(NULL, NULL, "dnd.notpending"); + return RpcIn_SetRetVals(result, resultLen, + "Failed to parse x coordinate", FALSE); + } + + if (!StrUtil_GetNextIntToken(&yPos, &index, args, " ")) { + Warning("DnDRpcInMouseUngrabCB: could not parse y coordinate\n"); + RpcOut_sendOne(NULL, NULL, "dnd.notpending"); + return RpcIn_SetRetVals(result, resultLen, + "Failed to parse y coordinate", FALSE); + } + + Debug("DnDRpcInMouseUngrabCB: Received (%d,%d)\n", xPos, yPos); + + /* + * If there is no DnD pending, inform the host so the MKS can start sending + * mouse packets again. + */ + if (!DnDGHDragPending(mainWnd)) { + RpcOut_sendOne(NULL, NULL, "dnd.notpending"); + return RpcIn_SetRetVals(result, resultLen, "DnD not pending", TRUE); + } + + /* The host only gives us coordinates within our screen */ + ASSERT(xPos >= 0); + ASSERT(yPos >= 0); + + /* + * Fake mouse movements over the window to try and generate a "drag_motion" + * signal from GTK. If a drag is pending, that signal will be sent to our + * widget and DnDGtkDragMotionCB will be invoked to start the DnD + * operation. + */ + if (!DnDGHFakeDrag(mainWnd)) { + Warning("DnDRpcInMouseUngrabCB: could not fake X events\n"); + RpcOut_sendOne(NULL, NULL, "dnd.notpending"); + return RpcIn_SetRetVals(result, resultLen, + "error faking X events", FALSE); + } + + /* + * Add event to fire and hide our widget if a DnD is not pending. Note that + * this is here in case our drag pending heuristic for Xdnd and Motif does + * not encompass all cases, or if the X events we generate don't cause the + * "drag_motion" for some other reason. + */ + gGHState.event = EventManager_Add(gEventQueue, RPCIN_POLL_TIME * 100, + DnDGHXEventTimeout, mainWnd); + if (!gGHState.event) { + Warning("DnDRpcInMouseUngrabCB: could not create event\n"); + RpcOut_sendOne(NULL, NULL, "dnd.notpending"); + return RpcIn_SetRetVals(result, resultLen, + "could not create timeout event", FALSE); + } + + gGHState.dragInProgress = FALSE; + gGHState.ungrabReceived = TRUE; + + Debug("DnDRpcInMouseUngrabCB finished\n"); + return RpcIn_SetRetVals(result, resultLen, "", TRUE); +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDRpcInGetNextFileCB -- + * + * For Guest->Host operations only. + * + * Invoked when the host is compiling its list of files to copy from the + * guest. Here we provide the path of the next file in our Guest->Host file + * list in guest path format (for display purposes) and CPName format (for + * file copy operation). + * + * Results: + * TRUE on success, FALSE on failure. + * + * Side effects: + * Iterator pointer within file list of GH state is iterated to next list + * entry (through call to DnDGHFileListGetNext()). + * + *---------------------------------------------------------------------------- + */ + +static Bool +DnDRpcInGetNextFileCB(char const **result, // OUT + size_t *resultLen, // OUT + const char *name, // IN + const char *args, // IN + size_t argsSize, // Ignored + void *clientData) // IN +{ + static char resultBuffer[DND_MAX_PATH]; + char *fileName; + size_t fileNameSize; + uint32 cpNameSize; + GtkWidget *mainWnd; + Bool res; + + mainWnd = GTK_WIDGET(clientData); + if (mainWnd == NULL) { + DnDGHCancel(NULL); + return RpcIn_SetRetVals(result, resultLen, + "bad clientData passed to callback", FALSE); + } + + /* + * Retrieve a pointer to the next filename and its size from the list stored + * in the G->H DnD state. Note that fileName should not be free(3)d here + * since an additional copy is not allocated. + */ + res = DnDGHFileListGetNext(&fileName, &fileNameSize); + + if (!res) { + Warning("DnDRpcInGetNextFileCB: error retrieving file name\n"); + DnDGHCancel(mainWnd); + return RpcIn_SetRetVals(result, resultLen, "error getting file", FALSE); + } + + if (!fileName) { + /* There are no more files to send */ + Debug("DnDRpcInGetNextFileCB: reached end of Guest->Host file list\n"); + return RpcIn_SetRetVals(result, resultLen, "|end|", TRUE); + } + + if (fileNameSize + 1 + fileNameSize > sizeof resultBuffer) { + Warning("DnDRpcInGetNextFileCB: filename too large (%"FMTSZ"u)\n", fileNameSize); + DnDGHCancel(mainWnd); + return RpcIn_SetRetVals(result, resultLen, "filename too large", FALSE); + } + + /* + * Construct a reply message of the form: + * + */ + memcpy(resultBuffer, fileName, fileNameSize); + resultBuffer[fileNameSize] = '\0'; + + cpNameSize = CPNameUtil_ConvertToRoot(fileName, + sizeof resultBuffer - (fileNameSize + 1), + resultBuffer + fileNameSize + 1); + if (cpNameSize < 0) { + Warning("DnDRpcInGetNextFileCB: could not convert to CPName\n"); + DnDGHCancel(mainWnd); + return RpcIn_SetRetVals(result, resultLen, + "error on CPName conversion", FALSE); + } + + /* Set manually because RpcIn_SetRetVals() assumes no NUL characters */ + *result = resultBuffer; + *resultLen = fileNameSize + 1 + cpNameSize; + + Debug("DnDRpcInGetNextFileCB: [%s] (%"FMTSZ"u)\n", + CPName_Print(*result, *resultLen), *resultLen); + + return TRUE; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDRpcInFinishCB -- + * + * For Guest->Host operations only. + * + * Invoked when host side of DnD operation has finished. + * + * Results: + * TRUE on success, FALSE on failure. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +static Bool +DnDRpcInFinishCB(char const **result, // OUT + size_t *resultLen, // OUT + const char *name, // IN + const char *args, // IN + size_t argsSize, // Ignored + void *clientData) // IN +{ + GtkWidget *mainWnd; + char *effect = NULL; + unsigned int index = 0; + char *retStr; + Bool ret = FALSE; + + mainWnd = GTK_WIDGET(clientData); + if (mainWnd == NULL) { + retStr = "bad clientData passed to callback"; + goto exit; + } + + effect = StrUtil_GetNextToken(&index, args, " "); + if (!effect) { + Warning("DnDRpcInFinishCB: no drop effect provided\n"); + retStr = "drop effect not provided"; + goto exit; + } + + if (strcmp(effect, "cancel") == 0) { + DnDSendEscapeKey(mainWnd); + DnDGHCancel(mainWnd); + } else { + /* + * The drop happened on the host. Fake X events such that our window is + * placed at the mouse's coordinates and raised, then fake a button + * release on the window. This causes us to get a "drag_drop" signal + * from GTK on our widget. + */ + if (!DnDGHFakeDrop(mainWnd)) { + Warning("DnDRpcInFinishCB: could not fake X events\n"); + retStr = "error faking X events"; + goto exit; + } + + gGHState.event = EventManager_Add(gEventQueue, RPCIN_POLL_TIME * 10, + DnDGHXEventTimeout, mainWnd); + if (!gGHState.event) { + Warning("DnDRpcInFinishCB: could not create event\n"); + retStr = "could not create timeout event"; + goto exit; + } + } + + retStr = ""; + ret = TRUE; + +exit: + if (!ret) { + DnDGHCancel(mainWnd); + } + + free(effect); + gGHState.dragInProgress = FALSE; + return RpcIn_SetRetVals(result, resultLen, retStr, ret); +} + + +/* + * Host->Guest (drop source) Gtk callback implementations + */ + +/* + *----------------------------------------------------------------------------- + * + * DnDGtkBeginCB -- + * + * "drag_begin" signal handler for GTK. This signal will be received + * after the fake mouse press sent in DnDRpcInEnterCB() is performed. + * Here we simply initialize our state variables. + * + * Results: + * None + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static void +DnDGtkBeginCB(GtkWidget *widget, // IN: the widget under the drag + GdkDragContext *dc, // IN: the drag context maintained by gdk + gpointer data) // IN: unused +{ + GtkWidget *mainWnd = GTK_WIDGET(data); + + Debug("DnDGtkBeginCB: entry\n"); + + if ((widget == NULL) || (mainWnd == NULL) || (dc == NULL)) { + return; + } + + gHGDnDInProgress = TRUE; + gDoneDragging = FALSE; + gHGDataPending = FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * DnDGtkEndCB -- + * + * "drag_end" signal handler for GTK. This is called when a drag and drop has + * completed. So this function is the last one to be called in any given DnD + * operation. + * + * Results: + * None + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static void +DnDGtkEndCB(GtkWidget *widget, // IN: the widget under the drag + GdkDragContext *dc, // IN: the drag context maintained by gdk + gpointer data) // IN: unused +{ + + GtkWidget *mainWnd = GTK_WIDGET(data); + + Debug("DnDGtkEndCB: enter\n"); + + if (mainWnd == NULL || dc == NULL) { + return; + } + + /* + * Do not set gHGDataPending to FALSE since DnD operation completes before + * the data transfer. + */ + gDoneDragging = FALSE; + gHGDnDInProgress = FALSE; + + RpcOut_sendOne(NULL, NULL, "dnd.finish %d", DROPEFFECT_COPY); +} + + +/* + *----------------------------------------------------------------------------- + * + * DnDGtkDataRequestCB -- + * DnD "drag_data_get" handler, for handling requests for DnD data on the + * specified widget. This function is called when there is need for DnD data + * on thesource, so this function is responsible for setting up the dynamic + * data exchange buffer and sending it out. + * + * Results: + * None + * + * Side effects: + * Data is avaiable to drop target. + * + *----------------------------------------------------------------------------- + */ + +static void +DnDGtkDataRequestCB(GtkWidget *widget, // IN + GdkDragContext *dc, // IN + GtkSelectionData *selection_data, // IN/OUT: buffer for the data + guint info, // IN: the requested fo the data + guint time, // IN: unused + gpointer data) // IN: unused +{ + const char *begin; + const char *end; + const char *next; + const char *pre; + const char *post; + size_t preLen; + size_t postLen; + size_t len; + Bool insertSpace; + char *text = NULL; + size_t textLen = 1; + + Debug("DnDGtkDataRequestCB: enter\n"); + + if ((widget == NULL) || (dc == NULL) || (selection_data == NULL)) { + Debug("DnDGtkDataRequestCB: Error, widget or dc or selection_data is invalid\n"); + return; + } + + /* Do nothing if we have not finished dragging yet */ + if (!gDoneDragging) { + Debug("DnDGtkDataRequestCB: not done dragging yet\n"); + return; + } + + /* Setup the format string components */ + switch (info) { + case DRAG_TARGET_INFO_URI_LIST: /* text/uri-list */ + pre = DND_URI_LIST_PRE; + preLen = sizeof DND_URI_LIST_PRE - 1; + post = DND_URI_LIST_POST; + postLen = sizeof DND_URI_LIST_POST - 1; + insertSpace = FALSE; + break; + case DRAG_TARGET_INFO_TEXT_PLAIN: /* text/plain */ + pre = DND_TEXT_PLAIN_PRE; + preLen = sizeof DND_TEXT_PLAIN_PRE - 1; + post = DND_TEXT_PLAIN_POST; + postLen = sizeof DND_TEXT_PLAIN_POST - 1; + insertSpace = TRUE; + break; + case DRAG_TARGET_INFO_STRING: /* STRING */ + pre = DND_STRING_PRE; + preLen = sizeof DND_STRING_PRE - 1; + post = DND_STRING_POST; + postLen = sizeof DND_STRING_POST - 1; + insertSpace = TRUE; + break; + default: + Log("DnDGtkDataRequestCB: invalid drag target info\n"); + return; + } + + + /* + * Set begin to first non-NUL character and end to last NUL character to + * prevent errors in calling CPName_GetComponent(). + */ + for(begin = gDnDData; *begin == '\0'; begin++) + ; + end = CPNameUtil_Strrchr(gDnDData, gDnDDataSize + 1, '\0'); + ASSERT(end); + + /* Build up selection data */ + while ((len = CPName_GetComponent(begin, end, &next)) != 0) { + const size_t origTextLen = textLen; + Bool freeBegin = FALSE; + + if (len < 0) { + Log("DnDGtkDataRequestCB: error getting next component\n"); + if (text) { + free(text); + } + return; + } + + /* + * A URI list will expect the provided path to be escaped. If we cannot + * escape the path for some reason we just use the unescaped version and + * hope that it works. + */ + if (info == DRAG_TARGET_INFO_URI_LIST) { + size_t newLen; + char *escapedComponent; + int escIndex; + int bytesToEsc[256] = { 0, }; + + /* We escape the following characters based on RFC 1630. */ + bytesToEsc['#'] = 1; + bytesToEsc['?'] = 1; + bytesToEsc['*'] = 1; + bytesToEsc['!'] = 1; + bytesToEsc['%'] = 1; /* Escape character */ + + /* Escape non-ASCII characters so we can pass UTF-8 filenames */ + for (escIndex = 0x80; escIndex < 0x100; escIndex++) { + bytesToEsc[escIndex] = 1; + } + + escapedComponent = Escape_Do('%', bytesToEsc, begin, len, &newLen); + if (escapedComponent) { + begin = escapedComponent; + len = newLen; + freeBegin = TRUE; + } + } + + /* + * Append component. NUL terminator was accounted for by initializing + * textLen to one above. + */ + textLen += preLen + len + postLen + (insertSpace ? 1 : 0); + text = Util_SafeRealloc(text, textLen); + Str_Snprintf(text + origTextLen - 1, + textLen - origTextLen + 1, + "%s%s%s", pre, begin, post); + + if (insertSpace && next != end) { + ASSERT(textLen - 2 >= 0); + text[textLen - 2] = ' '; + text[textLen - 1] = '\0'; + } + + if (freeBegin) { + free((void *)begin); + } + + /* Iterate to next component */ + begin = next; + } + + /* + * Send out the data using the selection system. When sending a string, GTK will + * ensure that a null terminating byte is added to the end so we do not need to + * add it. GTK also copies the data so the original will never be modified. + */ + Debug("DnDGtkDataRequestCB: calling gtk_selection_data_set with [%s]\n", text); + gtk_selection_data_set(selection_data, selection_data->target, + 8, /* 8 bits per character. */ + text, textLen); + free(text); +} + + +/* + * Guest->Host (drop target) Gtk callback implementations + */ + +/* + *---------------------------------------------------------------------------- + * + * DnDGtkDragMotionCB -- + * + * "drag_motion" signal handler for GTK. This is invoked each time the + * mouse moves over the drag target (destination) window when a DnD is + * pending. + * + * Results: + * TRUE on success, FALSE on failure. + * + * Side effects: + * RPC messages are sent to the host to proxy the DnD over. + * + *---------------------------------------------------------------------------- + */ + +static gboolean +DnDGtkDragMotionCB(GtkWidget *widget, // IN: target widget + GdkDragContext *dc, // IN: the GDK drag context + gint x, // IN: x position of mouse + gint y, // IN: y position of mouse + guint time, // IN: time of event + gpointer data) // IN: our private data +{ + GdkAtom commonTarget = 0; + Bool found = FALSE; + uint32 i; + + ASSERT(widget); + ASSERT(widget == data); + ASSERT(dc); + + Debug("DnDGtkDragMotionCB: entry (x=%d, y=%d, time=%d)\n", x, y, time); + + /* + * We'll get a number of these and should only carry on these operations on + * the first one. + * + * XXX Unity mode needs to know if there is a g->h->g dnd operation by + * detecting if the mouse has left the detection window. This code is + * currently not in the guest and should be ported from the host. + */ + if (gGHState.dragInProgress && !gUnity) { + Debug("DnDGtkDragMotionCB: drag already in progress\n"); + return FALSE; + } + + /* + * Sometimes (rarely) real user mouse movements will trigger "drag_motion" + * signals after we have already handled them. Prevent resetting the data + * and trying to start a new DnD operation. + */ + if (!gGHState.ungrabReceived && !gUnity) { + Debug("DnDGtkDragMotionCB: extra drag motion without ungrab\n"); + return FALSE; + } + + gGHState.ungrabReceived = FALSE; + + /* Remove event that hides our widget out of band from the DnD protocol. */ + if (gGHState.event) { + Debug("DnDGtkDragMotionCB: removed pending event\n"); + EventManager_Remove(gGHState.event); + gGHState.event = NULL; + } + + /* + * Note that gdk_drag_status() is called for us by GTK since we passed in + * GTK_DEST_DEFAULT_MOTION to gtk_drag_dest_set(). We'd handle it + * ourselves, but GTK 1.2.10 has a "bug" that requires us to provide this + * flag to get drag_leave and drag_drop signals. + */ + + /* + * We need to try and find a common target format with the list of formats + * offered by the drag source. This list is stored in the drag context's + * targets field, and each list member's data variable is a GdkAtom. We + * translated our supported targets into GdkAtoms in gTargetEntryAtom at + * initialization. Note that the GdkAtom value is an index into a table of + * strings maintained by the X server, so if they are equivalent then + * a common mime type is found. + */ + for (i = 0; i < ARRAYSIZE(gTargetEntryAtom) && !found; i++) { + GList *currContextTarget = dc->targets; + + while (currContextTarget) { + if (gTargetEntryAtom[i] == (GdkAtom)currContextTarget->data) { + commonTarget = gTargetEntryAtom[i]; + found = TRUE; + break; + } + currContextTarget = currContextTarget->next; + } + } + + if (!found) { + Warning("DnDGtkDragMotionCB: could not find a common target format\n"); + DnDGHCancel(widget); + return FALSE; + } + + /* + * Request the data. A "drag_data_received" signal will be sent to widget + * (that's us) upon completion. + */ + gtk_drag_get_data(widget, dc, commonTarget, time); + + + gGHState.dragInProgress = TRUE; + return TRUE; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDGtkDragDataReceivedCB -- + * + * "drag_data_received" signal handler for GTK. Invoked when the data + * requested by a gtk_drag_get_data() call is ready. + * + * This function actually begins the drag operation with the host by first + * setting the data ("dnd.data.set" RPC command) and then starting the DnD + * ("dnd.enter" RPC command). + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +static void +DnDGtkDragDataReceivedCB(GtkWidget *widget, // IN + GdkDragContext *dc, // IN + gint x, // IN + gint y, // IN + GtkSelectionData *dragData, // IN + guint info, // IN + guint time, // IN + gpointer data) // IN +{ + const char rpcHeader[] = "dnd.data.set CF_HDROP "; + const size_t rpcHeaderSize = sizeof rpcHeader - 1; + char *rpcBody = NULL; + size_t rpcBodySize = 0; + char *rpc; + size_t rpcSize; + + + Debug("DnDGtkDragDataReceivedCB: entry\n"); + + if (dragData->length < 0) { + Warning("DnDGtkDragDataReceivedCB: received length < 0 error\n"); + goto error; + } + + gGHState.dragContext = dc; + gGHState.time = time; + + /* + * Construct the body of the RPC message and our Guest->Host file list. + */ + if (dragData->target == gTargetEntryAtom[DRAG_TARGET_INFO_URI_LIST]) { + char *currName; + size_t currSize; + size_t index = 0; + char *ghFileList = NULL; + size_t ghFileListSize = 0; + + Debug("DnDGtkDragDataReceivedCB: uri-list [%s]\n", dragData->data); + + /* + * Get the the full filenames and last components from the URI list. The + * body of the RPC message will be these last components delimited with + * NUL characters; the Guest->Host file list will be the full paths + * delimited by NUL characters. + */ + while ((currName = DnD_UriListGetNextFile(dragData->data, + &index, + &currSize))) { + size_t lastComponentSize; + char *lastComponentStart; + + /* Append current filename to Guest->Host list */ + ghFileList = Util_SafeRealloc(ghFileList, + ghFileListSize + currSize + 1); + memcpy(ghFileList + ghFileListSize, currName, currSize); + ghFileListSize += currSize; + ghFileList[ghFileListSize] = '\0'; + ghFileListSize++; + + /* Append last component to RPC body */ + lastComponentStart = CPNameUtil_Strrchr(currName, currSize, DIRSEPC); + if (!lastComponentStart) { + /* + * This shouldn't happen since filenames are absolute, but handle + * it as if the file name is the last component + */ + lastComponentStart = currName; + } else { + /* Skip the last directory separator */ + lastComponentStart++; + } + + lastComponentSize = currName + currSize - lastComponentStart; + rpcBody = Util_SafeRealloc(rpcBody, rpcBodySize + lastComponentSize + 1); + memcpy(rpcBody + rpcBodySize, lastComponentStart, lastComponentSize); + rpcBodySize += lastComponentSize; + rpcBody[rpcBodySize] = '\0'; + rpcBodySize++; + + free(currName); + } + + if (!ghFileList || !rpcBody) { + Warning("DnDGtkDragDataReceivedCB: no filenames retrieved " + "from URI list\n"); + free(ghFileList); + free(rpcBody); + goto error; + } + + /* Set the list of full paths for use in the "dnd.data.get.file" callback */ + DnDGHFileListSet(ghFileList, ghFileListSize); + + /* rpcBody (and its size) will always contain a trailing NUL character */ + rpcBodySize--; + } else { + Warning("DnDGtkDragDataReceivedCB: unknown target format used [%s]\n", + dragData->data); + goto error; + } + + /* + * Set the drag data on the host, followed by sending the drag enter + */ + rpcSize = rpcHeaderSize + rpcBodySize; + rpc = Util_SafeMalloc(rpcSize); + memcpy(rpc, rpcHeader, rpcHeaderSize); + memcpy(rpc + rpcHeaderSize, rpcBody, rpcBodySize); + free(rpcBody); + + Debug("DnDGtkDragMotionCB: Sending: [%s] (%"FMTSZ"u)\n", + CPName_Print(rpc, rpcSize), rpcSize); + if (!RpcOut_SendOneRaw(rpc, rpcSize, NULL, NULL)) { + Warning("DnDGtkDragMotionCB: failed to send dnd.data.set message\n"); + free(rpc); + goto error; + } + + free(rpc); + + if (!RpcOut_sendOne(NULL, NULL, "dnd.enter 1 CF_HDROP")) { + Warning("DnDGtkDragMotionCB: failed to send dnd.enter message\n"); + goto error; + } + + return; + +error: + RpcOut_sendOne(NULL, NULL, "dnd.notpending"); + DnDGHCancel(widget); +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDGtkDragDropCB -- + * + * "drag_drop" signal handler for GTK. This is invoked when a mouse button + * release occurs on our widget. We generate that mouse button release in + * DnDRpcInFinishCB() when the host indicates that the drop has occurred and + * the files have been successfully transferred to the guest. + * + * Results: + * TRUE indicates to GTK that it need not run other handlers, FALSE + * otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +static gboolean +DnDGtkDragDropCB(GtkWidget *widget, // IN: widget event occurred on + GdkDragContext *dc, // IN: Destination drag context + gint x, // IN: x coordinate of drop + gint y, // IN: y coordinate of drop + guint time, // IN: time of event + gpointer data) // IN: our private data +{ + ASSERT(widget); + ASSERT(widget == data); + + Debug("DnDGtkDragDropCB: entry (%d, %d)\n", x, y); + + /* Remove timeout callback that was set in case we didn't get here */ + if (gGHState.event) { + Debug("DnDGtkDragDropCB: removed pending event\n"); + EventManager_Remove(gGHState.event); + gGHState.event = NULL; + } + + /* Hide our window so we don't receive stray signals */ + if (!gUnity) { + gtk_widget_hide(widget); + } + + gtk_drag_finish(dc, TRUE, FALSE, time); + + /* Reset all Guest->Host state */ + DnDGHStateInit(widget); + + return FALSE; +} + + +/* + * Utility functions + */ + +/* + *---------------------------------------------------------------------------- + * + * DnD_GetNewFileRoot -- + * + * Convenience function that gets a new file root for use on a single DnD + * operation and sets the global file root variable accordingly. + * + * Results: + * Size of root string (not including NUL terminator). + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +int +DnD_GetNewFileRoot(char *fileRoot, // IN/OUT + int bufSize) // IN: sizeof fileRoot +{ + char *newDir = NULL; + size_t fileRootSize; + + newDir = DnD_CreateStagingDirectory(); + if (newDir == NULL) { + /* + * Fallback on base of file root if we couldn't create a staging + * directory for this DnD operation. This is what Windows DnD does. + */ + Str_Strcpy(fileRoot, DnD_GetFileRoot(), bufSize); + return strlen(fileRoot); + } else { + fileRootSize = strlen(newDir); + ASSERT(fileRootSize < bufSize); + memcpy(fileRoot, newDir, fileRootSize); + fileRoot[fileRootSize] = '\0'; + free(newDir); + return fileRootSize; + } +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDSendVmxNewFileRoot -- + * + * Sends the VMX a new file root with the provided RPC command. + * + * Results: + * TRUE on success, FALSE on failure + * + * Side effects: + * gFileRoot is repopulated. + * + *---------------------------------------------------------------------------- + */ + +static Bool +DnDSendVmxNewFileRoot(char *rpcCmd) // IN: RPC command +{ + int32 rpcCommandSize; + int32 cpNameSize; + int32 rpcMessageSize; + char *rpcMessage; + char *cur; + + /* Repopulate gFileRoot */ + gFileRootSize = DnD_GetNewFileRoot(gFileRoot, sizeof gFileRoot); + + /* + * Here we must convert the file root before sending it across the + * backdoor. We can only communicate with new VMXs (v2 DnD), so we only + * need to handle that case here. + * + * + */ + rpcCommandSize = strlen(rpcCmd); + + /* + * ConvertToRoot below will append the root share name, so we need to + * make room for it in our buffer. + */ + rpcMessageSize = rpcCommandSize + 1 + + gFileRootSize + 1 + + HGFS_STR_LEN(HGFS_SERVER_POLICY_ROOT_SHARE_NAME) + 1 + + gFileRootSize + 1; + + rpcMessage = Util_SafeCalloc(1, rpcMessageSize); + memcpy(rpcMessage, rpcCmd, rpcCommandSize); + rpcMessage[rpcCommandSize] = ' '; + cur = rpcMessage + rpcCommandSize + 1; + + memcpy(cur, gFileRoot, gFileRootSize); + cur += gFileRootSize; + *cur = '\0'; + cur++; + + Debug("DnDSendVmxNewFileRoot: calling CPNameUtil_ConvertToRoot(%s, %"FMTSZ"u, %p)\n", + gFileRoot, rpcMessageSize - (cur - rpcMessage), cur); + cpNameSize = CPNameUtil_ConvertToRoot(gFileRoot, + rpcMessageSize - (cur - rpcMessage), + cur); + if (cpNameSize < 0) { + Debug("DnDSendVmxNewFileRoot: Could not convert file root to CPName\n"); + free(rpcMessage); + return FALSE; + } + + /* Readjust message size for actual length */ + rpcMessageSize = rpcCommandSize + 1 + + gFileRootSize + 1 + + cpNameSize + 1; + + Debug("DnDSendVmxNewFileRoot: sending root [%s] (%d)\n", + CPName_Print(rpcMessage, rpcMessageSize), rpcMessageSize); + + /* + * We must use RpcOut_SendOneRaw() here since RpcOut_sendOne() assumes a + * string and we are using CPName format. + */ + if (!RpcOut_SendOneRaw(rpcMessage, rpcMessageSize, NULL, NULL)) { + Debug("DnDSendVmxNewFileRoot: Failed to send %s message to host\n", rpcCmd); + free(rpcMessage); + return FALSE; + } + + free(rpcMessage); + return TRUE; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDFakeXEvents -- + * + * Fake X mouse events and window movement for the provided Gtk widget. + * + * This function will optionally show the widget, move the provided widget + * to either the provided location or the current mouse position if no + * coordinates are provided, and cause a button press or release event. + * + * + * Results: + * TRUE on success, FALSE on failure. + * + * Side effects: + * Other X events should be generated from those faked here. + * + *---------------------------------------------------------------------------- + */ + +static Bool +DnDFakeXEvents(GtkWidget *widget, // IN: the Gtk widget + Bool showWidget, // IN: whether to show Gtk widget + Bool buttonEvent, // IN: whether to send a button event + Bool buttonPress, // IN: whether to press or release mouse + Bool moveWindow, // IN: whether to move our window too + Bool coordsProvided,// IN: whether coordinates provided + int xCoord, // IN: x coordinate + int yCoord) // IN: y coordinate +{ + Window rootWnd; + Bool ret; + Display *dndXDisplay; + Window dndXWindow; + + ASSERT(widget); + + dndXDisplay = GDK_WINDOW_XDISPLAY(widget->window); + dndXWindow = GDK_WINDOW_XWINDOW(widget->window); + + /* + * Turn on X synchronization in order to ensure that our X events occur in + * the order called. In particular, we want the window movement to occur + * before the mouse movement so that the events we are coercing do in fact + * happen. + */ + XSynchronize(dndXDisplay, True); + + if (showWidget) { + Debug("DnDFakeXEvents: showing Gtk widget\n"); + gtk_widget_show(widget); + gdk_window_show(widget->window); + } + + /* Get the current location of the mouse if coordinates weren't provided. */ + if (!coordsProvided) { + Window rootReturn; + Window childReturn; + int rootXReturn; + int rootYReturn; + int winXReturn; + int winYReturn; + unsigned int maskReturn; + + rootWnd = RootWindow(dndXDisplay, DefaultScreen(dndXDisplay)); + ret = XQueryPointer(dndXDisplay, rootWnd, &rootReturn, &childReturn, + &rootXReturn, &rootYReturn, &winXReturn, &winYReturn, + &maskReturn); + if (ret == False) { + Warning("DnDFakeXEvents: XQueryPointer() returned False.\n"); + XSynchronize(dndXDisplay, False); + return FALSE; + } + + Debug("DnDFakeXEvents: mouse is at (%d, %d)\n", rootXReturn, rootYReturn); + + xCoord = rootXReturn; + yCoord = rootYReturn; + } + + if (moveWindow) { + /* + * Make sure the window is at this point and at the top (raised). The + * window is resized to be a bit larger than we would like to increase + * the likelihood that mouse events are attributed to our window -- this + * is okay since the window is invisible and hidden on cancels and DnD + * finish. + */ + XMoveResizeWindow(dndXDisplay, dndXWindow, xCoord, yCoord, 25, 25); + XRaiseWindow(dndXDisplay, dndXWindow); + } + + /* + * Generate mouse movements over the window. The second one makes ungrabs + * happen more reliably on KDE, but isn't necessary on GNOME. + */ + XTestFakeMotionEvent(dndXDisplay, -1, xCoord, yCoord, CurrentTime); + XTestFakeMotionEvent(dndXDisplay, -1, xCoord + 1, yCoord + 1, CurrentTime); + + if (buttonEvent) { + Debug("DnDFakeXEvents: faking left mouse button %s\n", + buttonPress ? "press" : "release"); + XTestFakeButtonEvent(dndXDisplay, 1, buttonPress, CurrentTime); + } + + XSynchronize(dndXDisplay, False); + + return TRUE; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDSendEscapeKey -- + * + * Sends the escape key, canceling any pending drag and drop on the guest. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +static void +DnDSendEscapeKey(GtkWidget *mainWnd) // IN +{ + Display *dndXDisplay; + uint32 escKeycode; + + Debug("DnDRpcInFinishCB: faking ESC key press/release\n"); + + dndXDisplay = GDK_WINDOW_XDISPLAY(mainWnd->window); + escKeycode = XKeysymToKeycode(dndXDisplay, XK_Escape); + + XTestFakeKeyEvent(dndXDisplay, escKeycode, TRUE, CurrentTime); + XTestFakeKeyEvent(dndXDisplay, escKeycode, FALSE, CurrentTime); +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDGHDragPending -- + * + * Determine whether a drag is currently pending within the guest by + * inspecting the internal state of the X server. Note that Gtk supports + * both the Xdnd and Motif protocols, so we check each one of those. + * + * Results: + * TRUE if a Drag operation is pending (waiting for a drop), FALSE + * otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +static INLINE Bool +DnDGHDragPending(GtkWidget *widget) // IN: our widget +{ + /* Xdnd is much more prevalent, so call it first */ + return DnDGHXdndDragPending(widget) || DnDGHMotifDragPending(widget); +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDGHXdndDragPending -- + * + * Determines whether an Xdnd protocol drag is pending. + * + * Results: + * TRUE is a drag is pending, FALSE otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +static INLINE Bool +DnDGHXdndDragPending(GtkWidget *widget) // IN: our widget +{ + GdkAtom xDnDSelection; + Window owner; + + xDnDSelection = gdk_atom_intern("XdndSelection", TRUE); + if (xDnDSelection == None) { + Warning("DnDGHXdndDragPending: could not obtain Xdnd selection atom\n"); + return FALSE; + } + + /* + * The XdndSelection atom will only have an owner if there is a drag in + * progress. + */ + owner = XGetSelectionOwner(GDK_WINDOW_XDISPLAY(widget->window), + GDKATOM_TO_ATOM(xDnDSelection)); + + Debug("DnDGHXdndDragPending: an Xdnd drag is %spending\n", + owner != None ? "" : "not "); + + return owner != None; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDGHXdndClearPending -- + * + * Clear the ownership of the XdndSelection selection atom that we use to + * determine if a Xdnd drag is pending. + * + * Note that this function should only be called when a DnD is not in + * progress. + * + * Also note that this is function is only necessary to handle desktop + * environments that don't clear the selection owner themselves (read KDE). + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +static INLINE void +DnDGHXdndClearPending(GtkWidget *widget) // IN: program's widget +{ + GdkAtom xDnDSelection; + + ASSERT(!gGHState.dragInProgress); + + xDnDSelection = gdk_atom_intern("XdndSelection", TRUE); + if (xDnDSelection == None) { + return; + } + + /* Clear current owner by setting owner to None */ + XSetSelectionOwner(GDK_WINDOW_XDISPLAY(widget->window), + GDKATOM_TO_ATOM(xDnDSelection), None, CurrentTime); +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDMotifDragPending -- + * + * Determines whether a Motif protocol drag is pending. + * + * XXX This has not yet been tested (looking for an app that actually uses + * the Motif protocol) + * + * Results: + * TRUE if a drag is pending, FALSE otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +static INLINE Bool +DnDGHMotifDragPending(GtkWidget *widget) // IN: our widget +{ + GdkAtom motifDragWindow; + Display *dndXDisplay; + int ret; + Window rootXWindow; + Atom type; + int format; + unsigned long nitems; + unsigned long bytesAfter; + unsigned char *prop; + + motifDragWindow = gdk_atom_intern("_MOTIF_DRAG_WINDOW", TRUE); + if (motifDragWindow == None) { + Warning("DnDGHMotifDragPending: could not obtain Motif " + "drag window atom\n"); + return FALSE; + } + + dndXDisplay = GDK_WINDOW_XDISPLAY(widget->window); + rootXWindow = RootWindow(dndXDisplay, DefaultScreen(dndXDisplay)); + + /* + * Try and get the Motif drag window property from X's root window. If one + * is provided, a DnD is pending. + */ + ret = XGetWindowProperty(dndXDisplay, rootXWindow, GDKATOM_TO_ATOM(motifDragWindow), + 0, 1, False, XA_WINDOW, + &type, &format, &nitems, &bytesAfter, &prop); + if (ret != Success) { + Warning("DnDGHMotifDragPending: XGetWindowProperty() error.\n"); + return FALSE; + } + + if (type == None) { + Debug("DnDGHXdndDragPending: a Motif drag is not pending\n"); + return FALSE; + } + + Debug("DnDGHXdndDragPending: a Motif drag is pending\n"); + + XFree(prop); + return TRUE; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDGHFileListClear -- + * + * Clears existing Guest->Host file list, releasing any used resources. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +static INLINE void +DnDGHFileListClear(void) +{ + Debug("DnDGHFileListClear: clearing G->H file list\n"); + if (gGHState.dndFileList) { + free(gGHState.dndFileList); + gGHState.dndFileList = NULL; + } + gGHState.dndFileListSize = 0; + gGHState.dndFileListNext = NULL; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDGHFileListSet -- + * + * Sets the Guest->Host file list that is accessed through + * DnDGHFileListGetNext(). + * + * Results: + * None. + * + * Side effects: + * Clears the existing Guest->Host file list if it exists. + * + *---------------------------------------------------------------------------- + */ + +static INLINE void +DnDGHFileListSet(char *fileList, // IN: new Guest->Host file list + size_t fileListSize) // IN: size of the provided list +{ + DnDGHFileListClear(); + gGHState.dndFileList = fileList; + gGHState.dndFileListSize = fileListSize; + gGHState.dndFileListNext = fileList; + + Debug("DnDGHFileListSet: [%s] (%"FMTSZ"u)\n", + CPName_Print(gGHState.dndFileList, gGHState.dndFileListSize), + gGHState.dndFileListSize); +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDGHFileListGetNext -- + * + * Retrieves the next file in the Guest->Host file list. + * + * Note that this function may only be called after calling + * DnDGHFileListSet() and before calling DnDGHFileListClear(). + * + * Results: + * TRUE on success, FALSE on failure. If TRUE is returned, fileName is + * given a pointer to the filename's location or NULL if there are no more + * files, and fileNameSize is given the length of fileName. + * + * Side effects: + * The fileListNext value of the Guest->Host global state is updated. + * + *---------------------------------------------------------------------------- + */ + +static INLINE Bool +DnDGHFileListGetNext(char **fileName, // OUT: fill with filename location + size_t *fileNameSize) // OUT: fill with filename length +{ + char const *end; + char const *next; + int len; + + ASSERT(gGHState.dndFileList); + ASSERT(gGHState.dndFileListNext); + ASSERT(gGHState.dndFileListSize > 0); + + /* Ensure end is the last NUL character */ + end = CPNameUtil_Strrchr(gGHState.dndFileList, gGHState.dndFileListSize, '\0'); + ASSERT(end); + + /* Get the length of this filename and a pointer to the next one */ + len = CPName_GetComponent(gGHState.dndFileListNext, end, &next); + if (len < 0) { + Warning("DnDGHFileListGetNext: error retrieving next component\n"); + return FALSE; + } + + /* No more entries in the list */ + if (len == 0) { + Debug("DnDGHFileListGetNext: no more entries\n"); + *fileName = NULL; + *fileNameSize = 0; + return TRUE; + } + + Debug("DnDGHFileListGetNext: returning [%s] (%d)\n", + gGHState.dndFileListNext, len); + + *fileName = gGHState.dndFileListNext; + *fileNameSize = len; + gGHState.dndFileListNext = (char *)next; + return TRUE; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDGHStateInit -- + * + * Initializes the Guest->Host DnD state. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +static INLINE void +DnDGHStateInit(GtkWidget *widget) // IN +{ + Debug("DnDGHStateInit: initializing guest->host state\n"); + gGHState.time = 0; + gGHState.dragContext = NULL; + gGHState.dragInProgress = FALSE; + gGHState.ungrabReceived = FALSE; + gGHState.event = NULL; + DnDGHXdndClearPending(widget); + if (!gUnity) { + gtk_widget_hide(widget); + } +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDHGStateInit -- + * + * Initialize the Host->Guest DnD state. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +static INLINE void +DnDHGStateInit(void) +{ + gHGDnDInProgress = FALSE; + gDoneDragging = FALSE; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDGHCancel -- + * + * Resets state and sends a DnD cancel message to the host. + * + * Results: + * TRUE on success, FALSE on failure. + * + * Side effects: + * DnD operation is cancelled. + * + *---------------------------------------------------------------------------- + */ + +static INLINE Bool +DnDGHCancel(GtkWidget *widget) // IN: program's widget +{ + /* Hide our widget so we don't receive stray signals */ + if (widget && !gUnity) { + gtk_widget_hide(widget); + } + + if (gGHState.dragContext) { + gdk_drag_status(gGHState.dragContext, 0, gGHState.time); + } + + gGHState.dragInProgress = FALSE; + + /* + * We don't initialize Guest->Host state here since an ungrab/grab/ungrab + * will cause a cancel but we want the drop of the DnD to still work. + */ + return RpcOut_sendOne(NULL, NULL, "dnd.finish cancel"); +} + + +/* + *---------------------------------------------------------------------------- + * + * DnDGHXEventTimeout -- + * + * Cleans up after fake X events do not cause intended events. Hides the + * provided widget and resets all Guest->Host DnD state. + * + * Note that this is expected to occur on ungrab if there is not a DnD + * pending, but may also occur at other times (sometimes we do not receive + * the drag drop after the mouse button release is faked on KDE). + * + * This function is invoked by the event manager; it is added/removed + * to/from the queue in both DnDRpcInMouseUngrabCB() and DnDRpcInFinishCB(), + * and DnDGtkDragMotionCB() and DnDGtkDragDropCB() respectively. + * + * Results: + * TRUE always, so the event manager doesn't stop running. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +static Bool +DnDGHXEventTimeout(void *clientData) // IN: our widget +{ + GtkWidget *widget = (GtkWidget *)clientData; + + Debug("DnDGHXEventTimeout time out \n"); + + RpcOut_sendOne(NULL, NULL, "dnd.notpending"); + + if (!gGHState.dragInProgress && !gUnity) { + gtk_widget_hide(widget); + } + + /* gGHState.event is cleared with the rest of Guest->Host state */ + DnDGHStateInit(widget); + + return TRUE; +} + + +/* + * Public functions invoked by the rest of vmware-user + */ + +/* + *----------------------------------------------------------------------------- + * + * DnD_GetVmxDnDVersion -- + * + * Ask the vmx for it's dnd version. + * + * Results: + * The dnd version the vmx supports, 0 if the vmx doesn't know + * what we're talking about. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +uint32 +DnD_GetVmxDnDVersion(void) +{ + char *reply = NULL; + size_t replyLen; + uint32 vmxVersion; + + if (!RpcOut_sendOne(&reply, &replyLen, "vmx.capability.dnd_version")) { + Debug("DnD_GetVmxDnDVersion: could not get VMX DnD version " + "capability: %s\n", reply ? reply : "NULL"); + vmxVersion = 0; + } else { + vmxVersion = atoi(reply); + ASSERT(vmxVersion > 1); /* DnD versions start at 2 */ + } + + free(reply); + return vmxVersion; +} + + +/* + *----------------------------------------------------------------------------- + * + * DnD_RegisterCapability -- + * + * Register the "dnd" capability. Sometimes this needs to be done separately + * from the rest of DnD registration, so we provide it separately here. + * + * Results: + * TRUE on success + * FALSE on failure + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +DnD_RegisterCapability(void) +{ + /* Tell the VMX about the DnD version we support. */ + if (!RpcOut_sendOne(NULL, NULL, "tools.capability.dnd_version 2")) { + Debug("DnD_RegisterCapability: could not set guest DnD version capability\n"); + return FALSE; + } else if (!DnDSendVmxNewFileRoot("dnd.ready enable")) { + Debug("DnD_RegisterCapability: failed to send dnd.ready message to host\n"); + return FALSE; + } + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * DnD_Register -- + * + * Register the DnD capability, setup callbacks, initialize. + * + * Results: + * TRUE on success, FALSE otherwise. + * + * Side effects: + * mainWnd will be a dragSource in the guest, and dnd will work from + * host to guest. + * + *----------------------------------------------------------------------------- + */ + +Bool +DnD_Register(GtkWidget *hgWnd, // IN: The widget to register as a drag source. + GtkWidget *ghWnd) // IN: The widget to register as a drag target. +{ + uint32 i; + + gDragCtx = NULL; + + ASSERT(hgWnd); + ASSERT(ghWnd); + + if (DnD_GetVmxDnDVersion() < 2) { + goto error; + } + + /* + * We can't pass in NULL to XTestQueryExtension(), so pass in a dummy + * variable to avoid segfaults. If we have a reason to check the major and + * minor numbers of the running extension, that would go here. + */ + if (!XTestQueryExtension(GDK_WINDOW_XDISPLAY(hgWnd->window), + &i, &i, &i, &i)) { + goto error; + } + + /* Host->Guest RPC callbacks */ + RpcIn_RegisterCallback(gRpcIn, "dnd.data.set", DnDRpcInDataSetCB, hgWnd); + RpcIn_RegisterCallback(gRpcIn, "dnd.enter", DnDRpcInEnterCB, hgWnd); + RpcIn_RegisterCallback(gRpcIn, "dnd.move", DnDRpcInMoveCB, hgWnd); + RpcIn_RegisterCallback(gRpcIn, "dnd.drop", DnDRpcInDropCB, hgWnd); + RpcIn_RegisterCallback(gRpcIn, "dnd.data.finish", DnDRpcInDataFinishCB, + hgWnd); + + /* Guest->Host RPC callbacks */ + RpcIn_RegisterCallback(gRpcIn, "dnd.ungrab", + DnDRpcInMouseUngrabCB, ghWnd); + RpcIn_RegisterCallback(gRpcIn, "dnd.data.get.file", + DnDRpcInGetNextFileCB, ghWnd); + RpcIn_RegisterCallback(gRpcIn, "dnd.finish", + DnDRpcInFinishCB, ghWnd); + + /* + * Setup mainWnd as a DND source/dest. + * + * Note that G->H drag targets should come first in this array. Currently + * G->H only supports text/uri-list targets. + */ + gTargetEntry[0].target = DRAG_TARGET_NAME_URI_LIST; + gTargetEntry[0].info = DRAG_TARGET_INFO_URI_LIST; + gTargetEntry[0].flags = 0; + gTargetEntry[1].target = DRAG_TARGET_NAME_TEXT_PLAIN; + gTargetEntry[1].info = DRAG_TARGET_INFO_TEXT_PLAIN; + gTargetEntry[1].flags = 0; + gTargetEntry[2].target = DRAG_TARGET_NAME_STRING; + gTargetEntry[2].info = DRAG_TARGET_INFO_STRING; + gTargetEntry[2].flags = 0; + + /* Populate our GdkAtom table for our supported Guest->Host targets */ + for (i = 0; + i < ARRAYSIZE(gTargetEntry) && i < ARRAYSIZE(gTargetEntryAtom); + i++) { + gTargetEntryAtom[i] = gdk_atom_intern(gTargetEntry[i].target, FALSE); + } + + /* Drag source for Host->Guest */ + gtk_drag_source_set(hgWnd, GDK_BUTTON1_MASK, + gTargetEntry, ARRAYSIZE(gTargetEntry), + GDK_ACTION_COPY | GDK_ACTION_MOVE); + + gtk_signal_connect(GTK_OBJECT(hgWnd), "drag_begin", + GTK_SIGNAL_FUNC(DnDGtkBeginCB), hgWnd); + gtk_signal_connect(GTK_OBJECT(hgWnd), "drag_end", + GTK_SIGNAL_FUNC(DnDGtkEndCB), hgWnd); + gtk_signal_connect(GTK_OBJECT(hgWnd), "drag_data_get", + GTK_SIGNAL_FUNC(DnDGtkDataRequestCB), hgWnd); + + + /* + * Drop target (destination) for Guest->Host + * + * We provide NR_GH_DRAG_TARGETS (rather than ARRAYSIZE(gTargetEntry)) to + * gtk_drag_dest_set() since we support less targets for G->H than H->G. + */ + gtk_drag_dest_set(ghWnd, + GTK_DEST_DEFAULT_MOTION, + gTargetEntry, NR_GH_DRAG_TARGETS, + GDK_ACTION_COPY | GDK_ACTION_MOVE); + + gtk_signal_connect(GTK_OBJECT(ghWnd), "drag_motion", + GTK_SIGNAL_FUNC(DnDGtkDragMotionCB), ghWnd); + gtk_signal_connect(GTK_OBJECT(ghWnd), "drag_data_received", + GTK_SIGNAL_FUNC(DnDGtkDragDataReceivedCB), + ghWnd); + gtk_signal_connect(GTK_OBJECT(ghWnd), "drag_drop", + GTK_SIGNAL_FUNC(DnDGtkDragDropCB), ghWnd); + + DnD_OnReset(hgWnd, ghWnd); + + if (DnD_RegisterCapability()) { + return TRUE; + } + + /* + * We get here if DnD registration fails for some reason + */ +error: + DnD_Unregister(hgWnd, ghWnd); + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * DnD_Unregister -- + * + * Cleanup dnd related things. + * + * Results: + * None. + * + * Side effects: + * DnD is stopped, the rpc channel to the vmx is closed. + * + *----------------------------------------------------------------------------- + */ + +void +DnD_Unregister(GtkWidget *hgWnd, // IN: The widget for hg dnd + GtkWidget *ghWnd) // IN: The widget for gh dnd +{ + RpcOut_sendOne(NULL, NULL, "dnd.ready disable"); + + DnDGHFileListClear(); + + /* Unregister source for Host->Guest DnD. */ + gtk_drag_source_unset(hgWnd); + gtk_signal_disconnect_by_func(GTK_OBJECT(hgWnd), + GTK_SIGNAL_FUNC(DnDGtkBeginCB), + hgWnd); + gtk_signal_disconnect_by_func(GTK_OBJECT(hgWnd), + GTK_SIGNAL_FUNC(DnDGtkEndCB), + hgWnd); + gtk_signal_disconnect_by_func(GTK_OBJECT(hgWnd), + GTK_SIGNAL_FUNC(DnDGtkDataRequestCB), + hgWnd); + + /* Unregister destination for Guest->Host DnD. */ + gtk_drag_dest_unset(ghWnd); + gtk_signal_disconnect_by_func(GTK_OBJECT(ghWnd), + GTK_SIGNAL_FUNC(DnDGtkDragMotionCB), + ghWnd); + gtk_signal_disconnect_by_func(GTK_OBJECT(ghWnd), + GTK_SIGNAL_FUNC(DnDGtkDragDataReceivedCB), + ghWnd); + gtk_signal_disconnect_by_func(GTK_OBJECT(ghWnd), + GTK_SIGNAL_FUNC(DnDGtkDragDropCB), + ghWnd); +} + + +/* + *---------------------------------------------------------------------------- + * + * DnD_OnReset -- + * + * Handles reinitializing DnD state on a reset. + * + * Results: + * None. + * + * Side effects: + * DnD is stopped and restarted. + * + *---------------------------------------------------------------------------- + */ + +void +DnD_OnReset(GtkWidget *hgWnd, // IN: The widget for hg dnd + GtkWidget *ghWnd) // IN: The widget for gh dnd + +{ + Debug("DnD_OnReset: entry\n"); + + /* Cancel file transfer. */ + if (gHGDnDInProgress || gHGDataPending) { + DnD_DeleteStagingFiles(gFileRoot, FALSE); + if (gBlockFd >= 0 && !DnD_RemoveBlock(gBlockFd, gFileRoot)) { + Warning("DnD_OnReset: could not remove block on %s\n", + gFileRoot); + } + } + + /* + * If a DnD in either direction was in progress during suspend, send an + * escape to cancel the operation and reset the pointer state. + */ + if (gHGDnDInProgress) { + Debug("DnD_OnReset: sending hgWnd escape\n"); + DnDSendEscapeKey(hgWnd); + } + + if (gGHState.dragInProgress) { + Debug("DnD_OnReset: sending ghWnd escape\n"); + DnDSendEscapeKey(ghWnd); + } + + if (gGHState.dragInProgress) { + Debug("DnD_OnReset: canceling host->guest DnD\n"); + DnDGHCancel(ghWnd); + } + + /* Reset DnD state. */ + DnDHGStateInit(); + DnDGHStateInit(ghWnd); + DnDGHFileListClear(); + DnD_SetMode(FALSE); +} + + +/* + *---------------------------------------------------------------------------- + * + * DnD_InProgress -- + * + * Indicates whether a DnD (or its data transfer) is currently in progress. + * + * Results: + * TRUE if a DnD is in progress, FALSE otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------------- + */ + +Bool +DnD_InProgress(void) +{ + return gGHState.dragInProgress || gHGDnDInProgress || gHGDataPending; +} + + +/* + *---------------------------------------------------------------------------- + * + * DnD_SetMode -- + * + * Sets dnd mode to single window or unity mode. + * + * Results: + * None. + * + * Side effects: + * Controls if the g->h det window is automatically hidden. + * + *---------------------------------------------------------------------------- + */ + +void +DnD_SetMode(Bool unity) // IN +{ + gUnity = unity; +} diff --git a/open-vm-tools/vmware-user/foundryToolsDaemon.c b/open-vm-tools/vmware-user/foundryToolsDaemon.c deleted file mode 100644 index a0b8a2e05..000000000 --- a/open-vm-tools/vmware-user/foundryToolsDaemon.c +++ /dev/null @@ -1,1835 +0,0 @@ -/********************************************************* - * Copyright (C) 2003 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. - * - *********************************************************/ - -/* - * foundryToolsDaemon.c -- - * - * VIX-specific TCLO cmds that are called through the backdoor - */ - -#include -#include -#include -#include -#include -#if defined(linux) -#include -#include -#include -#endif - - -#ifdef _WIN32 -#include -#else -#include -#include -#endif - -#ifdef _MSC_VER -# include -# include -# include -# include "win32u.h" -#elif _WIN32 -# include "win95.h" -#endif - -#include "vmware.h" -#include "procMgr.h" -#include "vm_version.h" -#include "vm_app.h" -#include "message.h" - -#if defined(VMTOOLS_USE_GLIB) -# include "vixPluginInt.h" -# include "vmtools.h" -#else -# include "debug.h" -# include "eventManager.h" -# include "rpcin.h" -# include "rpcout.h" -#endif - -#include "util.h" -#include "strutil.h" -#include "str.h" -#include "file.h" -#include "err.h" -#include "hostinfo.h" -#include "guest_os.h" -#include "conf.h" -#include "vixCommands.h" -#include "foundryToolsDaemon.h" -#include "printer.h" -#include "base64.h" -#include "syncDriver.h" -#include "hgfsServer.h" -#include "hgfs.h" -#include "system.h" -#include "codeset.h" - -#if defined(linux) -#include "hgfsDevLinux.h" -#endif - -#ifndef __FreeBSD__ -#include "netutil.h" -#endif - -/* Only Win32 and Linux use impersonation functions. */ -#if !defined(__FreeBSD__) && !defined(sun) -#include "impersonate.h" -#endif - -#include "vixTools.h" -#include "vixOpenSource.h" - -#if !defined(VMTOOLS_USE_GLIB) -static DblLnkLst_Links *globalEventQueue; // event queue for main event loop -#endif - -#define GUESTMSG_MAX_IN_SIZE (64 * 1024) /* vmx/main/guest_msg.c */ -#define MAX64_DECIMAL_DIGITS 20 /* 2^64 = 18,446,744,073,709,551,616 */ - -#if defined(linux) || defined(_WIN32) - -# if defined(_WIN32) -# define DECLARE_SYNCDRIVER_ERROR(name) DWORD name = ERROR_SUCCESS -# define SYNCDRIVERERROR ERROR_GEN_FAILURE -# else -# define DECLARE_SYNCDRIVER_ERROR(name) int name = 0 -# define SYNCDRIVERERROR errno -# endif - -static SyncDriverHandle gSyncDriverHandle = SYNCDRIVER_INVALID_HANDLE; - -static Bool ToolsDaemonSyncDriverThawCallback(void *clientData); -#endif - -static char *ToolsDaemonTcloGetQuotedString(const char *args, - const char **endOfArg); - -static char * ToolsDaemonTcloGetEncodedQuotedString(const char *args, - const char **endOfArg); - -Bool ToolsDaemonTcloReceiveVixCommand(RpcInData *data); - -#if !defined(N_PLAT_NLM) -Bool ToolsDaemonHgfsImpersonated(RpcInData *data); -#endif - -#if defined(linux) || defined(_WIN32) -Bool ToolsDaemonTcloSyncDriverFreeze(RpcInData *data); - -Bool ToolsDaemonTcloSyncDriverThaw(RpcInData *data); -#endif - -Bool ToolsDaemonTcloMountHGFS(RpcInData *data); - -void ToolsDaemonTcloReportProgramCompleted(const char *requestName, - VixError err, - int exitCode, - int64 pid, - void *clientData); - -/* - * These constants are a bad hack. I really should generate the result - * strings twice, once to compute the length and then allocate the buffer, - * and a second time to write the buffer. - */ -#define DEFAULT_RESULT_MSG_MAX_LENGTH 1024 - -static Bool thisProcessRunsAsRoot = FALSE; - - - -/* - *----------------------------------------------------------------------------- - * - * FoundryToolsDaemonRunProgram -- - * - * Run a named program on the guest. - * - * Return value: - * TRUE on success - * FALSE on failure - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -FoundryToolsDaemonRunProgram(RpcInData *data) // IN -{ - VixError err = VIX_OK; - char *requestName = NULL; - char *commandLine = NULL; - char *commandLineArgs = NULL; - char *credentialTypeStr = NULL; - char *obfuscatedNamePassword = NULL; - char *directoryPath = NULL; - char *environmentVariables = NULL; - static char resultBuffer[DEFAULT_RESULT_MSG_MAX_LENGTH]; - Bool impersonatingVMWareUser = FALSE; - void *userToken = NULL; - ProcMgr_Pid pid; -#if defined(VMTOOLS_USE_GLIB) - GMainLoop *eventQueue = ((ToolsAppCtx *)data->appCtx)->mainLoop; -#else - DblLnkLst_Links *eventQueue = data->clientData; -#endif - - /* - * Parse the arguments. Some of these are optional, so they - * may be NULL. - */ - requestName = ToolsDaemonTcloGetQuotedString(data->args, &data->args); - commandLine = ToolsDaemonTcloGetEncodedQuotedString(data->args, &data->args); - commandLineArgs = ToolsDaemonTcloGetEncodedQuotedString(data->args, &data->args); - credentialTypeStr = ToolsDaemonTcloGetQuotedString(data->args, &data->args); - obfuscatedNamePassword = ToolsDaemonTcloGetQuotedString(data->args, &data->args); - directoryPath = ToolsDaemonTcloGetQuotedString(data->args, &data->args); - environmentVariables = ToolsDaemonTcloGetQuotedString(data->args, &data->args); - - /* - * Make sure we are passed the correct arguments. - * Some of these arguments (like credentialTypeStr and obfuscatedNamePassword) are optional, - * so they may be NULL. - */ - if ((NULL == requestName) || (NULL == commandLine)) { - err = VIX_E_INVALID_ARG; - goto abort; - } - - if ((NULL != credentialTypeStr) - && (*credentialTypeStr) - && (thisProcessRunsAsRoot)) { - impersonatingVMWareUser = VixToolsImpersonateUserImpl(credentialTypeStr, - VIX_USER_CREDENTIAL_NONE, - obfuscatedNamePassword, - &userToken); - if (!impersonatingVMWareUser) { - err = VIX_E_GUEST_USER_PERMISSIONS; - goto abort; - } - } - - err = VixToolsRunProgramImpl(requestName, - commandLine, - commandLineArgs, - 0, - userToken, - eventQueue, - (int64 *) &pid); - -abort: - if (impersonatingVMWareUser) { - VixToolsUnimpersonateUser(userToken); - } - VixToolsLogoutUser(userToken); - - /* - * All VMXI tools commands return results that start with a VMXI error - * and a guest-OS-specific error. - */ - Str_Sprintf(resultBuffer, - sizeof(resultBuffer), - "%"FMT64"d %d %"FMT64"d", - err, - Err_Errno(), - (int64) pid); - RPCIN_SETRETVALS(data, resultBuffer, TRUE); - - /* - * These were allocated by ToolsDaemonTcloGetQuotedString. - */ - free(requestName); - free(commandLine); - free(credentialTypeStr); - free(obfuscatedNamePassword); - free(directoryPath); - free(environmentVariables); - free(commandLineArgs); - - return TRUE; -} // FoundryToolsDaemonRunProgram - - -/* - *----------------------------------------------------------------------------- - * - * FoundryToolsDaemonGetToolsProperties -- - * - * Get information about test features. - * - * Return value: - * TRUE on success - * FALSE on failure - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -FoundryToolsDaemonGetToolsProperties(RpcInData *data) // IN -{ - VixError err = VIX_OK; - int additionalError = 0; - static char resultBuffer[DEFAULT_RESULT_MSG_MAX_LENGTH]; - char *serializedBuffer = NULL; - size_t serializedBufferLength = 0; - char *base64Buffer = NULL; - size_t base64BufferLength = 0; - Bool success; - char *returnBuffer = NULL; -#if defined(VMTOOLS_USE_GLIB) - GKeyFile *confDictRef; -#else - GuestApp_Dict **confDictRef; -#endif - - /* - * Collect some values about the host. - */ - confDictRef = data->clientData; - - err = VixTools_GetToolsPropertiesImpl(confDictRef, - &serializedBuffer, - &serializedBufferLength); - if (VIX_OK == err) { - base64BufferLength = Base64_EncodedLength(serializedBuffer, serializedBufferLength) + 1; - base64Buffer = Util_SafeMalloc(base64BufferLength); - success = Base64_Encode(serializedBuffer, - serializedBufferLength, - base64Buffer, - base64BufferLength, - &base64BufferLength); - if (!success) { - base64Buffer[0] = 0; - err = VIX_E_FAIL; - goto abort; - } - base64Buffer[base64BufferLength] = 0; - } - - -abort: - returnBuffer = base64Buffer; - if (NULL == base64Buffer) { - returnBuffer = ""; - } - if (VIX_OK != err) { - additionalError = Err_Errno(); - } - - /* - * All VMXI tools commands return results that start with a VMXI error - * and a guest-OS-specific error. - */ - Str_Sprintf(resultBuffer, - sizeof(resultBuffer), - "%"FMT64"d %d %s", - err, - additionalError, - returnBuffer); - RPCIN_SETRETVALS(data, resultBuffer, TRUE); - - free(serializedBuffer); - free(base64Buffer); - - return TRUE; -} // FoundryToolsDaemonGetToolsProperties - - -/* - *----------------------------------------------------------------------------- - * - * ToolsDaemonTcloCheckUserAccount -- - * - * - * Return value: - * TRUE on success - * FALSE on failure - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -ToolsDaemonTcloCheckUserAccount(RpcInData *data) // IN -{ - VixError err = VIX_OK; - char *credentialTypeStr = NULL; - char *obfuscatedNamePassword = NULL; - static char resultBuffer[DEFAULT_RESULT_MSG_MAX_LENGTH]; - Bool impersonatingVMWareUser = FALSE; - void *userToken = NULL; - Debug(">ToolsDaemonTcloCheckUserAccount\n"); - - /* - * Parse the argument - */ - credentialTypeStr = ToolsDaemonTcloGetQuotedString(data->args, &data->args); - obfuscatedNamePassword = ToolsDaemonTcloGetQuotedString(data->args, &data->args); - - /* - * Make sure we are passed the correct arguments. - */ - if ((NULL == credentialTypeStr) || (NULL == obfuscatedNamePassword)) { - err = VIX_E_INVALID_ARG; - goto abort; - } - - if (thisProcessRunsAsRoot) { - impersonatingVMWareUser = VixToolsImpersonateUserImpl(credentialTypeStr, - VIX_USER_CREDENTIAL_NONE, - obfuscatedNamePassword, - &userToken); - if (!impersonatingVMWareUser) { - err = VIX_E_GUEST_USER_PERMISSIONS; - goto abort; - } - } - -abort: - if (impersonatingVMWareUser) { - VixToolsUnimpersonateUser(userToken); - } - VixToolsLogoutUser(userToken); - - /* - * All VMXI tools commands return results that start with a VMXI error - * and a guest-OS-specific error. - */ - Str_Sprintf(resultBuffer, - sizeof(resultBuffer), - "%"FMT64"d %d", - err, - Err_Errno()); - RPCIN_SETRETVALS(data, resultBuffer, TRUE); - - /* - * These were allocated by ToolsDaemonTcloGetQuotedString. - */ - free(credentialTypeStr); - free(obfuscatedNamePassword); - - return TRUE; -} // ToolsDaemonTcloCheckUserAccount - -#if defined(VMTOOLS_USE_GLIB) - -/** - * Initializes internal state of the Foundry daemon. - * - * @param[in] ctx Application context. - */ - -void -FoundryToolsDaemon_Initialize(ToolsAppCtx *ctx) -{ - thisProcessRunsAsRoot = (strcmp(ctx->name, VMTOOLS_GUEST_SERVICE) == 0); - (void) VixTools_Initialize(thisProcessRunsAsRoot, - ToolsDaemonTcloReportProgramCompleted, - ctx); - -#if defined(linux) || defined(_WIN32) - if (thisProcessRunsAsRoot) { - Impersonate_Init(); - } -#endif - -} - -#else - -/* - *----------------------------------------------------------------------------- - * - * FoundryToolsDaemon_RegisterRoutines -- - * - * Register the Foundry RPC callbacks - * - * Return value: - * None - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -void -FoundryToolsDaemon_RegisterRoutines(RpcIn *in, // IN - GuestApp_Dict **confDictRef, // IN - DblLnkLst_Links *eventQueue, // IN - Bool runAsRoot) // IN -{ - static Bool inited = FALSE; -#if defined(linux) || defined(_WIN32) - static Bool sync_driver_inited = FALSE; -#endif - - ASSERT(in); - ASSERT(confDictRef); - ASSERT(*confDictRef); - - thisProcessRunsAsRoot = runAsRoot; - globalEventQueue = eventQueue; - - (void) VixTools_Initialize(thisProcessRunsAsRoot, - ToolsDaemonTcloReportProgramCompleted, - NULL); - -#if defined(linux) || defined(_WIN32) - /* - * Be careful, Impersonate_Init should only be ever called once per process. - * - * We can get back here if the tools re-inits due to an error state, - * such as hibernation. - */ - if (!inited && thisProcessRunsAsRoot) { - Impersonate_Init(); - } -#endif - - RpcIn_RegisterCallbackEx(in, - VIX_BACKDOORCOMMAND_RUN_PROGRAM, - FoundryToolsDaemonRunProgram, - eventQueue); - RpcIn_RegisterCallbackEx(in, - VIX_BACKDOORCOMMAND_GET_PROPERTIES, - FoundryToolsDaemonGetToolsProperties, - confDictRef); - RpcIn_RegisterCallbackEx(in, - VIX_BACKDOORCOMMAND_CHECK_USER_ACCOUNT, - ToolsDaemonTcloCheckUserAccount, - NULL); -#if !defined(N_PLAT_NLM) - RpcIn_RegisterCallbackEx(in, - VIX_BACKDOORCOMMAND_SEND_HGFS_PACKET, - ToolsDaemonHgfsImpersonated, - NULL); -#endif - RpcIn_RegisterCallbackEx(in, - VIX_BACKDOORCOMMAND_COMMAND, - ToolsDaemonTcloReceiveVixCommand, - confDictRef); - RpcIn_RegisterCallbackEx(in, - VIX_BACKDOORCOMMAND_MOUNT_VOLUME_LIST, - ToolsDaemonTcloMountHGFS, - NULL); - -#if defined(linux) || defined(_WIN32) - - /* - * Only init once, but always register the RpcIn. - */ - if (!sync_driver_inited) { - sync_driver_inited = SyncDriver_Init(); - } - if (sync_driver_inited) { - /* - * These only get registered if SyncDriver_Init succeeds. We do - * support running the sync/thaw scripts even on guests where - * the Sync driver is not supported (Linux, Windows older than - * Win2k) but the running of the scripts is implemented using - * VIX_BACKDOORCOMMAND_RUN_PROGRAM. - */ - RpcIn_RegisterCallbackEx(in, - VIX_BACKDOORCOMMAND_SYNCDRIVER_FREEZE, - ToolsDaemonTcloSyncDriverFreeze, - eventQueue); - RpcIn_RegisterCallbackEx(in, - VIX_BACKDOORCOMMAND_SYNCDRIVER_THAW, - ToolsDaemonTcloSyncDriverThaw, - NULL); - } else { - Debug("FoundryToolsDaemon: Failed to init SyncDriver, skipping command handlers.\n"); - } -#endif - inited = TRUE; -} // FoundryToolsDaemon_RegisterRoutines - -#endif - -/* - *----------------------------------------------------------------------------- - * - * ToolsDaemonTcloGetQuotedString -- - * - * Extract a quoted string from the middle of an argument string. - * This is different than normal tokenizing in a few ways: - * * Whitespace is a separator outside quotes, but not inside quotes. - * * Quotes always come in pairs, so "" is am empty string. An empty - * string may appear anywhere in the string, even at the end, so - * a string that is "" contains 1 empty string, not 2. - * * The string may use whitespace to separate the op-name from the params, - * and then quoted params to skip whitespace inside a param. - * - * Return value: - * Allocates the string. - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -static char * -ToolsDaemonTcloGetQuotedString(const char *args, // IN - const char **endOfArg) // OUT -{ - char *resultStr = NULL; - char *endStr; - Debug(">ToolsDaemonTcloGetQuotedString\n"); - - while ((*args) && ('\"' != *args)) { - args++; - } - if ('\"' == *args) { - args++; - } - - resultStr = Util_SafeStrdup(args); - - endStr = resultStr; - while (*endStr) { - if (('\\' == *endStr) && (*(endStr + 1))) { - endStr += 2; - } else if ('\"' == *endStr) { - *endStr = 0; - endStr++; - break; - } else { - endStr++; - } - } - - if (NULL != endOfArg) { - args += (endStr - resultStr); - while (' ' == *args) { - args++; - } - *endOfArg = args; - } - - Debug("ToolsDaemonTcloOpenUrl\n"); - - /* - * Parse the arguments - */ - url = ToolsDaemonTcloGetEncodedQuotedString(data->args, &data->args); - windowState = ToolsDaemonTcloGetQuotedString(data->args, &data->args); - // These parameters at the end are optional, so they may be NULL. - credentialTypeStr = ToolsDaemonTcloGetQuotedString(data->args, &data->args); - obfuscatedNamePassword = ToolsDaemonTcloGetQuotedString(data->args, &data->args); - - /* - * Validate the arguments. - */ - if ((NULL == url) || (NULL == windowState)) { - err = VIX_E_INVALID_ARG; - Debug("Failed to get string args\n"); - goto abort; - } - - if ((NULL != credentialTypeStr) - && (*credentialTypeStr) - && (thisProcessRunsAsRoot)) { - impersonatingVMWareUser = VixToolsImpersonateUserImpl(credentialTypeStr, - VIX_USER_CREDENTIAL_NONE, - obfuscatedNamePassword, - &userToken); - if (!impersonatingVMWareUser) { - err = VIX_E_GUEST_USER_PERMISSIONS; - goto abort; - } - } - - Debug("Opening URL: \"%s\"\n", url); - - /* Actually open the URL. */ - if (!GuestApp_OpenUrl(url, strcmp(windowState, "maximize") == 0)) { - err = VIX_E_FAIL; - Debug("Failed to open the url \"%s\"\n", url); - goto abort; - } - -abort: - if (impersonatingVMWareUser) { - VixToolsUnimpersonateUser(userToken); - } - VixToolsLogoutUser(userToken); - - /* - * All Foundry tools commands return results that start with a - * foundry error and a guest-OS-specific error. - */ - Str_Sprintf(resultBuffer, sizeof resultBuffer, "%"FMT64"d %d", err, sysError); - RPCIN_SETRETVALS(data, resultBuffer, TRUE); - - /* - * These were allocated by ToolsDaemonTcloGetQuotedString. - */ - free(url); - free(windowState); - free(credentialTypeStr); - free(obfuscatedNamePassword); - - Debug("ToolsDaemonTcloSetPrinter\n"); - - /* - * Parse the arguments - */ - printerName = ToolsDaemonTcloGetQuotedString(data->args, &data->args); - defaultString = ToolsDaemonTcloGetQuotedString(data->args, &data->args); - - /* - * Validate the arguments. - */ - if ((NULL == printerName) || (NULL == defaultString)) { - err = VIX_E_INVALID_ARG; - Debug("Failed to get string args\n"); - goto abort; - } - - if (!StrUtil_StrToInt(&defaultInt, defaultString)) { - err = VIX_E_INVALID_ARG; - Debug("Failed to convert int arg\n"); - goto abort; - } - - Debug("Setting printer to: \"%s\", %ssetting as default\n", - printerName, (defaultInt != 0) ? "" : "not "); - - /* Actually set the printer. */ - if (!Printer_AddConnection(printerName, &sysError)) { - err = VIX_E_FAIL; - Debug("Failed to add printer %s : %d %s\n", printerName, sysError, - Err_Errno2String(sysError)); - goto abort; - } - - /* Set this printer as the default if requested. */ - if (defaultInt != 0) { - if (!Win32U_SetDefaultPrinter(printerName)) { - /* - * We couldn't set this printer as default. Oh well. We'll - * still report success or failure based purely on whether - * the actual printer add succeeded or not. - */ - Debug("Unable to set \"%s\" as the default printer\n", - printerName); - } - } - -abort: - /* - * All Foundry tools commands return results that start with a - * foundry error and a guest-OS-specific error. - */ - Str_Sprintf(resultBuffer, sizeof resultBuffer, "%"FMT64"d %d", err, sysError); - RPCIN_SETRETVALS(data, resultBuffer, TRUE); - - /* - * These were allocated by ToolsDaemonTcloGetQuotedString. - */ - free(printerName); - free(defaultString); - - Debug("appCtx; - GSource *timer; -#else - Event *cbEvent; -#endif - - Debug(">ToolsDaemonTcloSyncDriverFreeze\n"); - - /* - * Parse the arguments - */ - driveList = ToolsDaemonTcloGetQuotedString(data->args, &data->args); - timeout = ToolsDaemonTcloGetQuotedString(data->args, &data->args); - - /* - * Validate the arguments. - */ - if (NULL == driveList || NULL == timeout) { - err = VIX_E_INVALID_ARG; - Debug("ToolsDaemonTcloSyncDriverFreeze: Failed to get string args\n"); - goto abort; - } - - if (!StrUtil_StrToInt(&timeoutVal, timeout) || timeoutVal < 0) { - Debug("ToolsDaemonTcloSyncDriverFreeze: Bad args, timeout '%s'\n", timeout); - err = VIX_E_INVALID_ARG; - goto abort; - } - - Debug("SYNCDRIVE: Got request to freeze '%s', timeout %d\n", driveList, - timeoutVal); - - /* Disallow multiple freeze calls. */ - if (gSyncDriverHandle != SYNCDRIVER_INVALID_HANDLE) { - err = VIX_E_OBJECT_IS_BUSY; - goto abort; - } - - /* Perform the actual freeze. */ - if (!SyncDriver_Freeze(driveList, &gSyncDriverHandle) || - SyncDriver_QueryStatus(gSyncDriverHandle, INFINITE) != SYNCDRIVER_IDLE) { - Debug("ToolsDaemonTcloSyncDriverFreeze: Failed to Freeze drives '%s'\n", - driveList); - err = VIX_E_FAIL; - sysError = SYNCDRIVERERROR; - if (gSyncDriverHandle != SYNCDRIVER_INVALID_HANDLE) { - SyncDriver_Thaw(gSyncDriverHandle); - SyncDriver_CloseHandle(&gSyncDriverHandle); - } - goto abort; - } - - /* Start the timer callback to automatically thaw. */ - if (0 != timeoutVal) { - Debug("ToolsDaemonTcloSyncDriverFreeze: Starting timer callback %d\n", timeoutVal); -#if defined(VMTOOLS_USE_GLIB) - timer = g_timeout_source_new(timeoutVal * 10); - VMTOOLSAPP_ATTACH_SOURCE(ctx, timer, ToolsDaemonSyncDriverThawCallback, NULL, NULL); - g_source_unref(timer); -#else - cbEvent = EventManager_Add(data->clientData, - timeoutVal, - ToolsDaemonSyncDriverThawCallback, - NULL); - if (!cbEvent) { - Debug("ToolsDaemonTcloSyncDriverFreeze: Failed to start callback, aborting\n"); - if (!SyncDriver_Thaw(gSyncDriverHandle)) { - Debug("ToolsDaemonTcloSyncDriverFreeze: Unable to abort freeze. Oh well.\n"); - } - SyncDriver_CloseHandle(&gSyncDriverHandle); - err = VIX_E_FAIL; - sysError = SYNCDRIVERERROR; - goto abort; - } -#endif - } - -abort: - /* - * These were allocated by ToolsDaemonTcloGetQuotedString. - */ - free(driveList); - free(timeout); - - /* - * All Foundry tools commands return results that start with a - * foundry error and a guest-OS-specific error. - */ - Str_Sprintf(resultBuffer, sizeof resultBuffer, "%"FMT64"d %d", err, sysError); - Debug("ToolsDaemonSyncDriverThawCallback\n"); - Debug("ToolsDaemonSyncDriverThawCallback: Timed out waiting for thaw.\n"); - - /* Don't bother calling freeze if no drives are frozen. */ - if (gSyncDriverHandle == SYNCDRIVER_INVALID_HANDLE || - !SyncDriver_DrivesAreFrozen()) { - Debug("ToolsDaemonTcloSyncDriverThaw\n"); - - /* - * This function has no arguments that we care about. - */ - - Debug("SYNCDRIVE: Got request to thaw\n"); - - if (gSyncDriverHandle == SYNCDRIVER_INVALID_HANDLE || - !SyncDriver_DrivesAreFrozen()) { - err = VIX_E_GUEST_VOLUMES_NOT_FROZEN; - sysError = SYNCDRIVERERROR; - Debug("ToolsDaemonTcloSyncDriverThaw: No drives are frozen.\n"); - } else if (!SyncDriver_Thaw(gSyncDriverHandle)) { - err = VIX_E_FAIL; - sysError = SYNCDRIVERERROR; - Debug("ToolsDaemonTcloSyncDriverThaw: Failed to Thaw drives\n"); - } - - SyncDriver_CloseHandle(&gSyncDriverHandle); - - /* - * All Foundry tools commands return results that start with a - * foundry error and a guest-OS-specific error. - */ - Str_Sprintf(resultBuffer, sizeof resultBuffer, "%"FMT64"d %d", err, sysError); - Debug("mnt_fsname, ".host:/") == 0) && - (strcmp(mnt->mnt_type, HGFS_NAME) == 0) && - (strcmp(mnt->mnt_dir, "/mnt/hgfs") == 0)) { - vmhgfsMntFound = TRUE; - break; - } - } - endmntent(mtab); - } - - if (vmhgfsMntFound) { - /* - * We need to call the mount program, not the mount system call. The - * mount program does several additional things, like compute the mount - * options from the contents of /etc/fstab, and invoke custom mount - * programs like the one needed for HGFS. - */ - int ret = system("mount -t vmhgfs .host:/ /mnt/hgfs"); - if (ret == -1 || WIFSIGNALED(ret) || - (WIFEXITED(ret) && WEXITSTATUS(ret) != 0)) { - err = VIX_E_FAIL; - } - } -#endif - - /* - * All tools commands return results that start with an error - * and a guest-OS-specific error. - */ - Str_Sprintf(resultBuffer, - sizeof(resultBuffer), - "%"FMT64"d %d", - err, - Err_Errno()); - RPCIN_SETRETVALS(data, resultBuffer, TRUE); - - return TRUE; -} // ToolsDaemonTcloMountHGFS - - -#if !defined(N_PLAT_NLM) -/* - *----------------------------------------------------------------------------- - * - * ToolsDaemonHgfsImpersonated -- - * - * Tclo cmd handler for hgfs requests. - * - * Here we receive guest user credentials and an HGFS packet to - * be processed by the HGFS server under the context of - * the guest user credentials. - * - * We pre-allocate a HGFS reply packet buffer and leave some space at - * the beginning of the buffer for foundry error codes. - * The format of the foundry error codes is a 64 bit number (as text), - * followed by a 32 bit number (as text), followed by a hash, - * all delimited by space (' '). The hash is needed - * to make it easier for text parsers to know where the - * HGFS reply packet begins, since it can start with a space. - * - * We do this funky "allocate an HGFS packet with extra - * room for foundry error codes" to avoid copying buffers - * around. The HGFS packet buffer is roughly 62k for large V3 Hgfs request - * or 6k for other request , so it would be bad to copy that for every packet. - * - * It is guaranteed that we will not be called twice - * at the same time, so it is safe for resultPacket to be static. - * The TCLO processing loop (RpcInLoop()) is synchronous. - * - * - * Results: - * TRUE on TCLO success (*result contains the hgfs reply) - * FALSE on TCLO error (not supposed to happen) - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -ToolsDaemonHgfsImpersonated(RpcInData *data) // IN -{ - VixError err; - size_t hgfsPacketSize = 0; - const char *origArgs = data->args; - Bool impersonatingVMWareUser = FALSE; - char *credentialTypeStr = NULL; - char *obfuscatedNamePassword = NULL; - void *userToken = NULL; - int actualUsed; -#define STRLEN_OF_MAX_64_BIT_NUMBER_AS_STRING 20 -#define OTHER_TEXT_SIZE 4 /* strlen(space zero space quote) */ - static char resultPacket[STRLEN_OF_MAX_64_BIT_NUMBER_AS_STRING - + OTHER_TEXT_SIZE - + HGFS_LARGE_PACKET_MAX]; - char *hgfsReplyPacket = resultPacket - + STRLEN_OF_MAX_64_BIT_NUMBER_AS_STRING - + OTHER_TEXT_SIZE; - - Debug(">ToolsDaemonHgfsImpersonated\n"); - - err = VIX_OK; - - /* - * We assume VixError is 64 bits. If it changes, we need - * to adjust STRLEN_OF_MAX_64_BIT_NUMBER_AS_STRING. - * - * There isn't much point trying to return gracefully - * if sizeof(VixError) is larger than we expected: we didn't - * allocate enough space to actually represent the error! - * So we're stuck. Panic at this point. - */ - ASSERT_ON_COMPILE(sizeof (uint64) == sizeof err); - - /* - * Get the authentication information. - */ - credentialTypeStr = ToolsDaemonTcloGetQuotedString(data->args, &data->args); - obfuscatedNamePassword = ToolsDaemonTcloGetQuotedString(data->args, &data->args); - - /* - * Make sure we are passed the correct arguments. - */ - if ((NULL == credentialTypeStr) || (NULL == obfuscatedNamePassword)) { - err = VIX_E_INVALID_ARG; - goto abort; - } - - /* - * Skip over our token that is right before the HGFS packet. - * This makes ToolsDaemonTcloGetQuotedString parsing predictable, - * since it will try to eat trailing spaces after a quoted string, - * and the HGFS packet might begin with a space. - */ - if (((data->args - origArgs) >= data->argsSize) || ('#' != *(data->args))) { - /* - * Buffer too small or we got an unexpected token. - */ - err = VIX_E_FAIL; - goto abort; - } - data->args++; - - /* - * At this point args points to the HGFS packet. - * If we're pointing beyond the end of the buffer, we'll - * get a negative HGFS packet length and abort. - */ - hgfsPacketSize = data->argsSize - (data->args - origArgs); - if (hgfsPacketSize <= 0) { - err = VIX_E_FAIL; - goto abort; - } - - if (thisProcessRunsAsRoot) { - impersonatingVMWareUser = VixToolsImpersonateUserImpl(credentialTypeStr, - VIX_USER_CREDENTIAL_NONE, - obfuscatedNamePassword, - &userToken); - if (!impersonatingVMWareUser) { - err = VIX_E_GUEST_USER_PERMISSIONS; - hgfsPacketSize = 0; - goto abort; - } - } - - /* - * Impersonation was okay, so let's give our packet to - * the HGFS server and forward the reply packet back. - */ - HgfsServer_DispatchPacket(data->args, // packet in buf - hgfsReplyPacket, // packet out buf - &hgfsPacketSize); // in/out size - -abort: - if (impersonatingVMWareUser) { - VixToolsUnimpersonateUser(userToken); - } - VixToolsLogoutUser(userToken); - - /* - * These were allocated by ToolsDaemonTcloGetQuotedString. - */ - free(credentialTypeStr); - free(obfuscatedNamePassword); - - data->result = resultPacket; - data->resultLen = STRLEN_OF_MAX_64_BIT_NUMBER_AS_STRING - + OTHER_TEXT_SIZE - + hgfsPacketSize; - - /* - * Render the foundry error codes into the buffer. - */ - actualUsed = Str_Snprintf(resultPacket, - STRLEN_OF_MAX_64_BIT_NUMBER_AS_STRING - + OTHER_TEXT_SIZE, - "%"FMT64"d 0 ", - err); - - if (actualUsed < 0) { - /* - * We computed our string length wrong! This should never happen. - * But if it does, let's try to recover gracefully. The "1" in - * the string below is VIX_E_FAIL. We don't try to use %d since - * we couldn't even do that right the first time around. - * That hash is needed for the parser on the other - * end to stop before the HGFS packet, since the HGFS packet - * can contain a space (and the parser can eat trailing spaces). - */ - ASSERT(0); - actualUsed = Str_Snprintf(resultPacket, - STRLEN_OF_MAX_64_BIT_NUMBER_AS_STRING, - "1 0 #"); - data->resultLen = actualUsed; - } else { - /* - * We computed the string length correctly. Great! - * - * We allocated enough space to cover a large 64 bit number - * for VixError. Chances are we didn't use all that space. - * Instead, pad it with whitespace so the text parser can skip - * over it. - */ - memset(resultPacket + actualUsed, - ' ', - STRLEN_OF_MAX_64_BIT_NUMBER_AS_STRING - + OTHER_TEXT_SIZE - - actualUsed); - /* - * Put a hash right before the HGFS packet. - * So the buffer will look something like this: - * "0 0 #" followed by the HGFS packet. - */ - resultPacket[STRLEN_OF_MAX_64_BIT_NUMBER_AS_STRING - + OTHER_TEXT_SIZE - 1] = '#'; - } - - Debug("<<rpc, msg, strlen(msg) + 1, NULL, NULL); - g_free(msg); -#else - sentResult = RpcOut_sendOne(NULL, - NULL, - "%s %s %"FMT64"d %d %d %"FMT64"d", - VIX_BACKDOORCOMMAND_RUN_PROGRAM_DONE, - requestName, - err, - Err_Errno(), - exitCode, - (int64) pid); -#endif - - if (!sentResult) { - Warning("Unable to send results from polling the result program.\n\n"); - } -} // ToolsDaemonTcloReportProgramCompleted - - -/* - *----------------------------------------------------------------------------- - * - * ToolsDaemonTcloReceiveVixCommand -- - * - * - * Return value: - * TRUE on success - * FALSE on failure - * - * Side effects: - * None - * - *----------------------------------------------------------------------------- - */ - -Bool -ToolsDaemonTcloReceiveVixCommand(RpcInData *data) // IN -{ - VixError err = VIX_OK; - char *requestName = NULL; - VixCommandRequestHeader *requestMsg = NULL; - size_t maxResultBufferSize; - size_t tcloBufferLen; - char *resultValue = NULL; - size_t resultValueLength = 0; - Bool deleteResultValue = FALSE; - char *destPtr = NULL; - int vixPrefixDataSize = (MAX64_DECIMAL_DIGITS * 2) - + (sizeof(' ') * 2) - + sizeof('\0'); - - /* - * Our temporary buffer will be the same size as what the - * Tclo/RPC system can handle, which is GUESTMSG_MAX_IN_SIZE. - */ - static char tcloBuffer[GUESTMSG_MAX_IN_SIZE]; - -#if defined(VMTOOLS_USE_GLIB) - ToolsAppCtx *ctx = data->appCtx; - GMainLoop *eventQueue = ctx->mainLoop; - GKeyFile *confDictRef = ctx->config; -#else - DblLnkLst_Links *eventQueue = globalEventQueue; - GuestApp_Dict **confDictRef = data->clientData; -#endif - - requestName = ToolsDaemonTcloGetQuotedString(data->args, &data->args); - - /* - * Skip the NULL, char, and then the rest of the buffer should just - * be a Vix command object. - */ - while (*data->args) { - data->args += 1; - } - data->args += 1; - err = VixMsg_ValidateMessage((char *) data->args, data->argsSize); - if (VIX_OK != err) { - goto abort; - } - requestMsg = (VixCommandRequestHeader *) data->args; - maxResultBufferSize = sizeof(tcloBuffer) - vixPrefixDataSize; - - err = VixTools_ProcessVixCommand(requestMsg, - requestName, - maxResultBufferSize, - confDictRef, - eventQueue, - &resultValue, - &resultValueLength, - &deleteResultValue); - - -abort: - tcloBufferLen = resultValueLength + vixPrefixDataSize; - - /* - * If we generated a message larger than tclo/Rpc can handle, - * we did something wrong. Our code should never have done this. - */ - if (tcloBufferLen > sizeof tcloBuffer) { - ASSERT(0); - resultValue[0] = 0; - tcloBufferLen = tcloBufferLen - resultValueLength; - err = VIX_E_OUT_OF_MEMORY; - } - - /* - * This should never happen since tcloBuffer is pretty huge. - * But, there is no harm in being paranoid and I don't want to - * find out the buffer is too small when we are halfway through - * formatting the response. - */ - if ((32 + resultValueLength + 32) >= sizeof(tcloBuffer)) { - err = VIX_E_OUT_OF_MEMORY; - resultValueLength = 0; - } - - - /* - * All Foundry tools commands return results that start with a foundry error - * and a guest-OS-specific error. - */ - Str_Sprintf(tcloBuffer, - sizeof tcloBuffer, - "%"FMT64"d %d ", - err, - Err_Errno()); - destPtr = tcloBuffer + strlen(tcloBuffer); - - /* - * If this is a binary result, then we put a # at the end of the ascii to - * mark the end of ascii and the start of the binary data. - */ - if ((NULL != requestMsg) - && (requestMsg->commonHeader.commonFlags & VIX_COMMAND_GUEST_RETURNS_BINARY)) { - *(destPtr++) = '#'; - data->resultLen = destPtr - tcloBuffer + sizeof '#' + resultValueLength; - } - - /* - * Copy the result. Don't use a strcpy, since this may be a binary buffer. - */ - memcpy(destPtr, resultValue, resultValueLength); - destPtr += resultValueLength; - - /* - * If this is not binary data, then it should be a NULL terminated string. - */ - if ((NULL == requestMsg) - || !(requestMsg->commonHeader.commonFlags & VIX_COMMAND_GUEST_RETURNS_BINARY)) { - *(destPtr++) = 0; - data->resultLen = strlen(tcloBuffer) + 1; - } - - data->result = tcloBuffer; - - if (deleteResultValue) { - free(resultValue); - } - free(requestName); - - Debug(" +#include + +#include "string.hh" +#include "unicode.h" +#include "util.h" + +namespace utf { + +/* Initialize static scope variables */ +const string::size_type string::npos = Glib::ustring::npos; + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::string -- + * + * Constructor. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string::string() + : mUstr(), + mUtf16Cache(NULL) +{ +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::string -- + * + * Constructor. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string::string(ConstUnicode s) // IN + : mUstr(), + mUtf16Cache(NULL) +{ + ASSERT(s); + mUstr = Unicode_GetUTF8(s); + ASSERT(Validate(mUstr)); +} + + +#ifdef _WIN32 + +/* + *----------------------------------------------------------------------------- + * + * utf::string::string -- + * + * Constructor from a ubstr_t object. Copies the UTF-16 representation of + * the ubstr_t. + * + * Results: + * None. + * + * Side effects: + * Makes a copy of the ubstr_t data and frees that data when the + * utf::string is destroyed. + * + * Note: + * WIN32 only call + * + *----------------------------------------------------------------------------- + */ + +string::string(const ubstr_t &s) // IN + : mUstr(), + mUtf16Cache(NULL) +{ + // If the input is empty, then there's nothing to do. + if (s.length() == 0) { + return; + } + + mUstr = Glib::ustring(static_cast(s)); + ASSERT(Validate(mUstr)); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::string -- + * + * Constructor from a _bstr_t object. Copies the UTF-16 representation of + * the _bstr_t. Needed for dealing with _com_error::Description(). + * + * Results: + * None. + * + * Side effects: + * Makes a copy of the _bstr_t data and frees that data when + * the utf::string is destroyed. + * + * Note: + * WIN32 only call + * + *----------------------------------------------------------------------------- + */ + +string::string(const _bstr_t &s) // IN + : mUstr(), + mUtf16Cache(NULL) +{ + // If the input is empty, then there's nothing to do. + if (s.length() == 0) { + return; + } + + Unicode utf8 = Unicode_AllocWithUTF16(static_cast(s)); + + try { + mUstr = Glib::ustring(utf8); + Unicode_Free(utf8); + } catch (...) { + Unicode_Free(utf8); + throw; + } + + ASSERT(Validate(mUstr)); +} + +#endif + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::string -- + * + * Constructor. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string::string(const utf16string &s) // IN + : mUstr(), + mUtf16Cache(NULL) +{ + // If the input is empty, then there's nothing to do. + if (s.empty()) { + return; + } + + string copy(s.c_str()); + swap(copy); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::string -- + * + * Constructor. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string::string(const utf16_t *s) // IN + : mUstr(), + mUtf16Cache(NULL) +{ + ASSERT(s != NULL); + + /* + * Since we already have a UTF-16 representation of the string, copy it + * now. + */ + mUtf16Cache = Unicode_UTF16Strdup(s); + + Unicode utf8 = Unicode_AllocWithUTF16(s); + + try { + mUstr = Glib::ustring(utf8); + Unicode_Free(utf8); + } catch (...) { + Unicode_Free(utf8); + throw; + } + + ASSERT(Validate(mUstr)); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::string -- + * + * Constructor. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string::string(const char *s, // IN + StringEncoding encoding) // IN + : mUstr(), + mUtf16Cache(NULL) +{ + ASSERT(s != NULL); + + Unicode utf8 = Unicode_Alloc(s, encoding); + + try { + mUstr = Glib::ustring(utf8); + Unicode_Free(utf8); + } catch (...) { + Unicode_Free(utf8); + throw; + } + + ASSERT(Validate(mUstr)); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::string -- + * + * Constructor. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string::string(const Glib::ustring &s) // IN + : mUstr(s), + mUtf16Cache(NULL) +{ + ASSERT(Validate(s)); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::string -- + * + * Copy constructor. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string::string(const string &s) // IN + : mUstr(s.mUstr), + mUtf16Cache(NULL) +{ +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::~string -- + * + * Destructor. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +string::~string() +{ + InvalidateCache(); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::operator Glib::ustring -- + * + * Implicit conversion to Glib::ustring operator + * + * Results: + * The internal Glib::ustring object. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +string::operator const Glib::ustring& () + const +{ + return ustr(); +} + + +#ifdef _WIN32 + +/* + *----------------------------------------------------------------------------- + * + * utf::string::operator ubstr_t -- + * + * Implicit conversion to ubstr_t + * + * Results: + * The current ubstr_t string. NUL-terminated. + * + * Side effects: + * None + * + * Note: + * This function is only defined in _WIN32 + * + *----------------------------------------------------------------------------- + */ + +string::operator const ubstr_t() + const +{ + return ubstr_t(GetUtf16Cache()); +} + +#endif + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::operator= -- + * + * Assignment operator. + * + * Results: + * A reference to this string. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string& +string::operator=(const string &s) // IN +{ + return assign(s); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::operator+= -- + * + * Append operator of the utf::string class. + * + * Results: + * A reference to this string. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string& +string::operator+=(const string &s) // IN +{ + return append(s); +} + + +string& +string::operator+=(value_type uc) // IN +{ + push_back(uc); + return *this; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::swap -- + * + * Swaps the contents with a given utf::string. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +string::swap(string &s) // IN/OUT +{ + mUstr.swap(s.mUstr); + std::swap(mUtf16Cache, s.mUtf16Cache); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::resize -- + * + * Change the size of this utf::string. + * + * Results: + * None. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +string::resize(size_type n, // IN + value_type c) // IN/OPT +{ + InvalidateCache(); + mUstr.resize(n, c); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::c_str -- + * + * Get the UTF-8 representation of this string. + * + * Results: + * The current string with UTF-8 encoding. NUL-terminated. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +const char * +string::c_str() + const +{ + return mUstr.c_str(); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::w_str -- + * + * Get the UTF-16 representation of this string. + * + * Results: + * The current string with UTF-16 (host-endian) encoding. NUL-terminated. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +const utf16_t * +string::w_str() + const +{ + return GetUtf16Cache(); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::ustr -- + * + * Get the Glib::ustring backing of this string. + * + * Results: + * The internal Glib::ustring object. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +const Glib::ustring& +string::ustr() + const +{ + return mUstr; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::empty -- + * + * Test if this is an empty string. + * + * Results: + * true if it's an empty string, otherwise false. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +bool +string::empty() + const +{ + return mUstr.empty(); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::size -- + * + * Results: + * Returns the length of this string, in characters (code points), + * excluding NUL. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string::size_type +string::size() + const +{ + return mUstr.size(); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::length -- + * + * Results: + * Returns the length of this string, in characters (code points), + * excluding NUL. (Same as size().) + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string::size_type +string::length() + const +{ + return size(); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::bytes -- + * + * Results: + * Returns the number of bytes used by the UTF-8 representation of this + * string, excluding NUL. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string::size_type +string::bytes() + const +{ + return mUstr.bytes(); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::foldCase -- + * + * Returns the case-folded string of this string. + * + * Results: + * The newly created string. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string +string::foldCase() + const +{ + return string(mUstr.casefold()); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::trim -- + * + * Returns the whitespace-trimmed version of this string. + * + * Results: + * The newly created string. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string +string::trim() + const +{ + Unicode trim = Unicode_Trim(c_str()); + string result(trim); + Unicode_Free(trim); + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::trimLeft -- + * + * Get the left-trimmed version of this string. + * + * Results: + * The newly created string. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string +string::trimLeft() + const +{ + Unicode trim = Unicode_TrimLeft(c_str()); + string result(trim); + Unicode_Free(trim); + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::trimRight -- + * + * Get the right-trimmed version of this string. + * + * Results: + * The newly created string. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string +string::trimRight() + const +{ + Unicode trim = Unicode_TrimRight(c_str()); + string result(trim); + Unicode_Free(trim); + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::normalize -- + * + * Creates a new string by normalizing the input string. + * + * Results: + * The newly created string. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string +string::normalize(NormalizeMode mode) // IN + const +{ + return mUstr.normalize((Glib::NormalizeMode)mode); +} + + +#ifdef SUPPORT_UNICODE + +/* + *----------------------------------------------------------------------------- + * + * utf::string::toLower -- + * + * Creates a new string by lower-casing the input string using + * the rules of the specified locale. + * + * Results: + * The newly created string. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string +string::toLower(const char *locale) // IN + const +{ + Unicode lower = Unicode_ToLower(c_str(), locale); + string results(lower); + Unicode_Free(lower); + + return results; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::toUpper -- + * + * Creates a new string by upper-casing the input string using + * the rules of the specified locale. + * + * Results: + * The newly created string. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string +string::toUpper(const char *locale) // IN + const +{ + Unicode upper = Unicode_ToUpper(c_str(), locale); + string results(upper); + Unicode_Free(upper); + + return results; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::toTitle -- + * + * Creates a new string by title-casing the input string using + * the rules of the specified locale. + * + * Results: + * The newly created string. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string +string::toTitle(const char *locale) // IN + const +{ + Unicode title = Unicode_ToTitle(c_str(), locale); + string results(title); + Unicode_Free(title); + + return results; +} + +#endif + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::append -- + * + * Appends the argument string to this utf::string. + * + * Results: + * A reference to this object. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string& +string::append(const string &s) // IN +{ + InvalidateCache(); + mUstr.append(s.mUstr); + + return *this; +} + + +string& +string::append(const string &s, // IN + size_type i, // IN + size_type n) // IN +{ + InvalidateCache(); + mUstr.append(s.mUstr, i, n); + + return *this; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::push_back -- + * + * Appends the character at the end of this string. + * + * Results: + * None + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +string::push_back(value_type uc) // IN +{ + InvalidateCache(); + mUstr.push_back(uc); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::assign -- + * + * Assigns the passed in string to this string. + * + * Results: + * A reference to this object + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string& +string::assign(const string &s) // IN +{ + string copy(s); + swap(copy); + return *this; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::insert -- + * + * Inserts the argument string to this string at index i, return this + * string. + * + * These are passthrough calls to the Glib::insert calls. + * + * Results: + * A reference to this object + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string& +string::insert(size_type i, // IN + const string &s) // IN +{ + InvalidateCache(); + mUstr.insert(i, s.mUstr); + return *this; +} + + +string& +string::insert(size_type i, // IN + size_type n, // IN + value_type uc) +{ + InvalidateCache(); + mUstr.insert(i, n, uc); + return *this; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::clear -- + * + * Clears this string. + * + * Results: + * None + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +string::clear() +{ + InvalidateCache(); + mUstr.clear(); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::zero_clear -- + * + * Zeroes and clears this string. + * + * XXX: This is temporary until we have a separate string class for + * passwords. + * + * Results: + * None + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +string::zero_clear() +{ + if (mUtf16Cache != NULL) { + Util_ZeroFree(mUtf16Cache, + Unicode_UTF16Strlen(mUtf16Cache) * sizeof *mUtf16Cache); + mUtf16Cache = NULL; + } + + /* + * This is a best effort. We aren't guaranteed that Glib::ustring doesn't + * leave behind any internal copies of the string. + */ + if (mUstr.c_str() != mUstr.data()) { + Util_Zero(const_cast(mUstr.c_str()), mUstr.bytes()); + } + Util_Zero(const_cast(mUstr.data()), mUstr.bytes()); + mUstr.clear(); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::erase -- + * + * Erase the contents of this string in the specified index range. + * + * Results: + * A reference to this object + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string& +string::erase(size_type i, // IN + size_type n) // IN +{ + InvalidateCache(); + mUstr.erase(i, n); + return *this; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::erase -- + * + * Erase the contents of this string with given iterator. + * + * Results: + * The current iterator. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string::iterator +string::erase(iterator p) // IN +{ + InvalidateCache(); + return mUstr.erase(p); +} + + +string::iterator +string::erase(iterator pbegin, // IN + iterator pend) // IN +{ + InvalidateCache(); + return mUstr.erase(pbegin, pend); +} + +/* + *----------------------------------------------------------------------------- + * + * utf::string::replace -- + * + * Replace the string contents specified by the range, with the passed in + * string. + * + * Results: + * A reference to this object. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string& +string::replace(size_type i, // IN + size_type n, // IN + const string &s) // IN +{ + InvalidateCache(); + mUstr.replace(i, n, s.mUstr); + return *this; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::replace -- + * + * Replace all occurences of the given string with another string. + * + * Results: + * A utf::string created with the substring replaced. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string& +string::replace(const string &from, // IN + const string &to) // IN +{ + size_type end; + size_type start = 0; + size_type fromSize = from.length(); + string result; + + while ((end = find(from, start)) != string::npos) { + result += substr(start, end - start); + result += to; + + start = end + fromSize; + } + + if (start < length()) { + result += substr(start); + } + + swap(result); + return *this; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::compare -- + * + * A 3-way (output -1, 0, or 1) string comparison. Compares each Unicode + * code point of this string to the argument string. + * + * Results: + * -1 if *this < s, 0 if *this == s, 1 if *this > s. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +int +string::compare(const string &s, // IN + bool ignoreCase) // IN/OPT: false by default + const +{ + return ignoreCase + ? Unicode_CompareIgnoreCase(c_str(), s.c_str()) + : Unicode_Compare(c_str(), s.c_str()); +} + + +int +string::compare(size_type i, // IN + size_type n, // IN + const string &s) // IN + const +{ + return mUstr.compare(i, n, s.mUstr); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::compareLength -- + * + * A 3-way (output -1, 0, or 1) string comparison with given length. + * Compares only the first len characters (in code units) of the strings. + * + * Results: + * -1 if *this < s, 0 if *this == s, 1 if *this > s. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +int +string::compareLength(const string &s, // IN + size_type len, // IN: length in code-point + bool ignoreCase) // IN/OPT: false by default + const +{ + return substr(0, len).compare(s.substr(0, len), ignoreCase); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::compareRange -- + * + * A 3-way (output -1, 0, or 1) string comparison with given length. + * Compares the substrings from this string [thisStart ~ thisStart + thisLength-1] + * with the input string str [strStart ~ strStart + strLength - 1]. + * + * Results: + * -1 if *this < s, 0 if *this == s, 1 if *this > s. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +int +string::compareRange(size_type thisStart, // IN: index in code-point + size_type thisLength, // IN: length in code-point + const string &str, // IN + size_type strStart, // IN: index in code-point + size_type strLength, // IN: length in code-point + bool ignoreCase) // IN/OPT: false by default + const +{ + return substr(thisStart, thisLength).compare(str.substr(strStart, strLength), ignoreCase); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::find -- + * + * Searches for the first occurrence of the input string inside this string. + * + * Results: + * If s is found, then, it returns the first starting index of the input string. + * Otherwise, returns npos. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string::size_type +string::find(const string &s, // IN + size_type pos) // IN/OPT + const +{ + return mUstr.find(s.mUstr, pos); +} + + +string::size_type +string::find(value_type uc, // IN + size_type pos) // IN/OPT + const +{ + return mUstr.find(uc, pos); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::rfind -- + * + * Searches for the last occurrence of the input string inside this string. + * + * Results: + * If s is found, then, it returns the last starting index of the input string. + * Otherwise, returns npos. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string::size_type +string::rfind(const string &s, // IN + size_type pos) // IN/OPT + const +{ + return mUstr.rfind(s.mUstr, pos); +} + + +string::size_type +string::rfind(value_type uc, // IN + size_type pos) // IN/OPT + const +{ + return mUstr.rfind(uc, pos); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::find_first_of -- + * + * Find the first occurence of 's' in this string. 'i' determines where in + * the current string we start searching for 's' + * + * Results: + * If s is found, then, it returns the index where s occurs in this + * string. + * Otherwise, returns npos. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string::size_type +string::find_first_of(const string &s, // IN + size_type i) // IN/OPT + const +{ + return mUstr.find_first_of(s.mUstr, i); +} + + +string::size_type +string::find_first_of(value_type uc, // IN + size_type i) // IN/OPT + const +{ + return mUstr.find_first_of(uc, i); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::find_first_not_of -- + * + * Find the first occurence of a string NOT in 's' in this string. 'i' + * determines where in this string we start searching to NOT 's'. + * + * Results: + * Returns the index of the first sequence in this string that is not 's' + * Otherwise, returns npos. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string::size_type +string::find_first_not_of(const string &s, // IN + size_type i) // IN/OPT + const +{ + return mUstr.find_first_not_of(s.mUstr, i); +} + + +string::size_type +string::find_first_not_of(value_type uc, // IN + size_type i) // IN/OPT + const +{ + return mUstr.find_first_not_of(uc, i); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::find_last_of -- + * + * Does a reverse search in this string for 's'. 'i' determines where we + * start the search for in this string. + * + * Results: + * If s is found, then, it returns the index where s occurs in this + * string. + * Otherwise, returns npos. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string::size_type +string::find_last_of(const string &s, // IN + size_type i) // IN/OPT + const +{ + return mUstr.find_last_of(s.mUstr, i); +} + + +string::size_type +string::find_last_of(value_type uc, // IN + size_type i) // IN/OPT + const +{ + return mUstr.find_last_of(uc, i); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::find_last_not_of -- + * + * Searches for the last character within the current string that does + * not match any characters in 's'. 'i' determines where we start the + * search for in this string. (moving backwards). + * + * Results: + * If NOT 's' is found, then, it returns the index where s does not occurs + * in this string. + * Otherwise, returns npos. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string::size_type +string::find_last_not_of(const string &s, // IN + size_type i) // IN/OPT + const +{ + return mUstr.find_last_not_of(s.mUstr, i); +} + + +string::size_type +string::find_last_not_of(value_type uc, // IN + size_type i) // IN/OPT + const +{ + return mUstr.find_last_not_of(uc, i); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::substr -- + * + * Create a substring of this string with given range. + * + * Results: + * The newly created string. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string +string::substr(size_type start, // IN + size_type len) // IN + const +{ + return string(mUstr.substr(start, len)); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::operator[] -- + * + * Get the UTF-32 character at given index in this string. + * + * Results: + * UTF-32 character (gunichar). + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string::value_type +string::operator[](size_type i) // IN + const +{ + return mUstr[i]; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::startsWith -- + * + * Tests if the current string starts with 's' + * + * Results: + * true if current string starts with 's', false otherwise + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +bool +string::startsWith(const string &s, // IN + bool ignoreCase) // IN/OPT: false by default + const +{ + return UnicodeStartsWith(c_str(), s.c_str(), ignoreCase); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::endsWith -- + * + * Tests if the current string ends with 's' + * + * Results: + * true if current string ends with 's', false otherwise + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +bool +string::endsWith(const string &s, // IN + bool ignoreCase) // IN/OPT: false by default + const +{ + return UnicodeEndsWith(c_str(), s.c_str(), ignoreCase); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::split -- + * + * Return a vector of utf::strings. The vector contains the elements of + * the string split by the passed in separator. Empty tokens are not + * skipped. + * + * "1,2,3".split(",") -> ["1", "2", "3"] + * "1,,".split(",") -> ["1", "", ""] + * "1".split(",") -> ["1"] + * + * XXX If this is to be used for things like command line parsing, support + * for quoted strings needs to be added. + * + * Results: + * A vector of utf::strings + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +std::vector +string::split(const string &sep) // IN + const +{ + std::vector splitStrings; + size_type sIndex = 0; + size_type sepLen = sep.length(); + + ASSERT(sepLen > 0); + + while (true) { + size_type index = find(sep, sIndex); + if (index == npos) { + splitStrings.push_back(substr(sIndex)); + break; + } + + splitStrings.push_back(substr(sIndex, index - sIndex)); + sIndex = index + sepLen; + } + + return splitStrings; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::GetUtf16Cache -- + * + * Return the UTF-16 representation of the current string, this value is + * cached, in the object. If the cache is not valid (NULL), then create + * the cache value + * + * Results: + * A UTF-16 representation of the current string + * + * Side effects: + * Allocates a UTF16 string + * + *----------------------------------------------------------------------------- + */ + +const utf16_t * +string::GetUtf16Cache() + const +{ + if (mUtf16Cache == NULL) { + mUtf16Cache = Unicode_GetAllocUTF16(c_str()); + } + + return mUtf16Cache; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::InvalidateCache -- + * + * Frees the cache in this string. + * + * Results: + * None + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +string::InvalidateCache() +{ + free(mUtf16Cache); + mUtf16Cache = NULL; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::operator+ -- + * + * Create a new string by appending the input string to this string. + * + * NOTE: This is not the same as append. append() will modify the + * current object, while this will return a new object. + * + * Results: + * The newly created string. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string +string::operator+(const string &rhs) // IN + const +{ + return mUstr + rhs.mUstr; +} + + +string +string::operator+(value_type uc) // IN + const +{ + return mUstr + uc; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::operator== -- + * + * Equality operator for string objects + * + * Results: + * true or false (true if equal) + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +bool +string::operator==(const string &rhs) // IN + const +{ + return compare(rhs) == 0; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::operator!= -- + * + * Inequality operator for string objects + * + * Results: + * true or false (true if not equal) + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +bool +string::operator!=(const string &rhs) // IN + const +{ + return compare(rhs) != 0; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::operator< -- + * + * Less than operator for string objects + * + * Results: + * true or false (true if lhs is < rhs) + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +bool +string::operator<(const string &rhs) // IN + const +{ + return compare(rhs) < 0; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::operator> -- + * + * Greater than operator for string objects + * + * Results: + * true or false (true if lhs is > rhs) + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +bool +string::operator>(const string &rhs) // IN + const +{ + return compare(rhs) > 0; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::operator<= -- + * + * Less than or equal than operator for string objects + * + * Results: + * true or false (true if lhs is <= rhs) + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +bool +string::operator<=(const string &rhs) // IN + const +{ + return compare(rhs) <= 0; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::operator>= -- + * + * Greater than or equal than operator for string objects + * + * Results: + * true or false (true if lhs is >= rhs) + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +bool +string::operator>=(const string &rhs) // IN + const +{ + return compare(rhs) >= 0; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::begin -- + * + * Returns an iterator to the start of the string. + * + * Results: + * iterator + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string::iterator +string::begin() +{ + return mUstr.begin(); +} + + +string::const_iterator +string::begin() + const +{ + return mUstr.begin(); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::string::end -- + * + * Returns an iterator to the end of the string. + * + * Results: + * iterator + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string::iterator +string::end() +{ + return mUstr.end(); +} + + +string::const_iterator +string::end() + const +{ + return mUstr.end(); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::Validate -- + * + * Validates the string. + * + * Results: + * true if the string contains is valid UTF-8, false otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +bool +Validate(const Glib::ustring& s) // IN +{ + bool isValid = s.validate(); + if (!isValid) { + char *escaped = Unicode_EscapeBuffer(s.c_str(), -1, STRING_ENCODING_UTF8); + Warning("Invalid UTF-8 string: \"%s\"\n", escaped); + free(escaped); + } + return isValid; +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::CreateWithLength -- + * + * A wrapper function for Unicode_AllocWithLength() that returns a utf::string. + * + * Results: + * A utf::string created with given parameters. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string +CreateWithLength(const void *buffer, // IN + ssize_t lengthInBytes, // IN: NUL not included + StringEncoding encoding) // IN +{ + if (!Unicode_IsBufferValid(buffer, lengthInBytes, encoding)) { + throw ConversionError(); + } + + Unicode utf8 = Unicode_AllocWithLength(buffer, lengthInBytes, encoding); + + try { + string result(utf8); + Unicode_Free(utf8); + return result; + } catch (...) { + Unicode_Free(utf8); + throw; + } +} + + +/* + *---------------------------------------------------------------------- + * + * utf::CreateWithBOMBuffer -- + * + * Convert a text buffer with BOM (byte-order mark) to utf::string. + * If BOM not present, assume it's UTF-8. + * + * Results: + * A utf::string containing the text buffer. + * + * Side Effect: + * None. + * + *---------------------------------------------------------------------- + */ + +string +CreateWithBOMBuffer(const void *buffer, // IN + ssize_t lengthInBytes) // IN: NUL not included +{ + struct BOMMap { + uint8 bom[4]; // BOM with max size. + ssize_t len; // Length of BOM. + StringEncoding encoding; // Encoding if a BOM is present. + }; + + static const BOMMap mapBOM[] = { + {{0}, 0, STRING_ENCODING_UTF8 }, // Default encoding. + {{0xEF, 0xBB, 0xBF}, 3, STRING_ENCODING_UTF8 }, + {{0xFE, 0xFF}, 2, STRING_ENCODING_UTF16_BE }, + {{0xFF, 0xFE}, 2, STRING_ENCODING_UTF16_LE }, + {{0x00, 0x00, 0xFE, 0xFF}, 4, STRING_ENCODING_UTF32_BE }, + {{0xFF, 0xFE, 0x00, 0x00}, 4, STRING_ENCODING_UTF32_LE } + }; + + ASSERT(lengthInBytes >= 0); + unsigned int index = 0; // Default encoding, no need to check. + for (unsigned int i = 1; i < ARRAYSIZE(mapBOM); i++) { + if ( lengthInBytes >= mapBOM[i].len + && memcmp(mapBOM[i].bom, buffer, mapBOM[i].len) == 0) { + index = i; + break; + } + } + + return CreateWithLength(reinterpret_cast(buffer) + mapBOM[index].len, + lengthInBytes - mapBOM[index].len, + mapBOM[index].encoding); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::IntToStr -- + * + * Converts an integer to a utf::string. + * + * Results: + * A utf::string created with the given integer. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +string +IntToStr(int64 val) // IN +{ + std::ostringstream ostream; + ostream << val; + return ostream.str().c_str(); +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::CopyArray -- + * + * Copies an array to a vector. + * + * Results: + * A vector containing a shallow copy of the array. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +template +static void +CopyArray(const T *p, // IN: + size_t n, // IN: The number of array elements to copy. + std::vector& buf) // OUT: +{ + buf.resize(n); + if (!buf.empty()) { + ASSERT(p != NULL); + memcpy(&buf[0], p, buf.size() * sizeof buf[0]); + } +} + + +/* + *----------------------------------------------------------------------------- + * + * utf::CreateWritableBuffer -- + * + * Copies a utf::string to a writable buffer. + * + * Results: + * A std::vector containing the string data. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +void +CreateWritableBuffer(const string& s, // IN: + std::vector& buf) // OUT: A copy of the string, as UTF-8. +{ + CopyArray(s.c_str(), s.bytes() + 1, buf); +} + + +void +CreateWritableBuffer(const string& s, // IN: + std::vector& buf) // OUT: A copy of the string, as UTF-16. +{ + CopyArray(s.w_str(), Unicode_UTF16Strlen(s.w_str()) + 1, buf); +} + + +} // namespace utf diff --git a/open-vm-tools/vmware-user/stringxx/string.hh b/open-vm-tools/vmware-user/stringxx/string.hh new file mode 100644 index 000000000..442e78602 --- /dev/null +++ b/open-vm-tools/vmware-user/stringxx/string.hh @@ -0,0 +1,308 @@ +/********************************************************* + * Copyright (C) 2008 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. + * + *********************************************************/ + +/* + * string.hh -- + * + * A string wrapper for bora/lib/unicode. This class is intended to provide + * more c++ features such as operator overloading, automatic string conversion + * between different types of string classes. + * + * This class uses glib::ustring as the underlying storage for its data, + * we chose this object because of its internal support for unicode. + * + */ + +#ifndef UTF_STRING_HH +#define UTF_STRING_HH + +#include +#include +#include + +#ifdef _WIN32 +#pragma pack(push, 8) +#endif // _WIN32 + +#include + +#ifdef _WIN32 +#pragma pack(pop) +#endif // _WIN32 + +#ifdef _WIN32 +#include "ubstr_t.hh" +#endif + +#include "libExport.hh" +extern "C" { +#include "unicodeTypes.h" +} + +#ifdef _WIN32 +/* + * Disabling Windows warning 4251 + * This is a warning msg about requiring Glib::ustring to be DLL + * exportable. + */ +#pragma warning(push) +#pragma warning(disable:4251) +#endif + +namespace utf { + + +/* utf8string should be replaced with an opaque type. It is temporarily used + * to replace the std::string in our codebase. + */ +typedef std::string utf8string; +typedef std::basic_string utf16string; + +class VMSTRING_EXPORT string +{ +public: + // type definitions + typedef Glib::ustring::size_type size_type; + typedef Glib::ustring::value_type value_type; + typedef Glib::ustring::iterator iterator; + typedef Glib::ustring::const_iterator const_iterator; + + // constant definitions + static const size_type npos; + + // Normalize mode map to Glib::NormalizeMode + typedef enum { + NORMALIZE_DEFAULT = Glib::NORMALIZE_DEFAULT, + NORMALIZE_NFD = Glib::NORMALIZE_NFD, + NORMALIZE_DEFAULT_COMPOSE = Glib::NORMALIZE_DEFAULT_COMPOSE, + NORMALIZE_NFC = Glib::NORMALIZE_NFC, + NORMALIZE_ALL = Glib::NORMALIZE_ALL, + NORMALIZE_NFKD = Glib::NORMALIZE_NFKD, + NORMALIZE_ALL_COMPOSE = Glib::NORMALIZE_ALL_COMPOSE, + NORMALIZE_NFKC = Glib::NORMALIZE_NFKC + } NormalizeMode; + + string(); + string(ConstUnicode s); + +#ifdef _WIN32 + string(const ubstr_t &s); + explicit string(const _bstr_t &s); +#endif + + string(const utf16string &s); + string(const utf16_t *s); + string(const char *s, StringEncoding encoding); + string(const Glib::ustring &s); + string(const string &s); + + ~string(); + + // Implicit conversions + operator const Glib::ustring& () const; +#ifdef _WIN32 + operator const ubstr_t() const; +#endif + + // Conversions to other i18n types (utf8, utf16, unicode) + const char *c_str() const; + const utf16_t *w_str() const; + const Glib::ustring& ustr() const; + + // Mapping functions to Glib::ustring + void swap(string &s); + void resize(size_type n, value_type c = '\0'); + bool empty() const; + size_type size() const; + size_type length() const; + size_type bytes() const; + string foldCase() const; + string trim() const; + string trimLeft() const; + string trimRight() const; + string normalize(NormalizeMode mode = NORMALIZE_DEFAULT_COMPOSE) const; + +#ifdef SUPPORT_UNICODE + string toLower(const char *locale) const; + string toUpper(const char *locale) const; + string toTitle(const char *locale) const; +#endif + + // String-level member methods. + string& append(const string &s); + string& append(const string &s, size_type i, size_type n); + string& assign(const string &s); + void push_back(value_type uc); + void clear(); + void zero_clear(); + string& insert(size_type i, const string& s); + string& insert(size_type i, size_type n, value_type uc); + string& erase(size_type i, size_type n = npos); + iterator erase(iterator p); + iterator erase(iterator pbegin, iterator pend); + string& replace(size_type i, size_type n, const string& s); + string& replace(const string &from, const string &to); + + int compare(const string &s, bool ignoreCase = false) const; + int compare(size_type i, size_type n, const string &s) const; + int compareLength(const string &s, size_type len, bool ignoreCase = false) const; + int compareRange(size_type thisStart, size_type thisLength, const string &str, + size_type strStart, size_type strLength, + bool ignoreCase = false) const; + size_type find(const string &s, size_type pos = 0) const; + size_type rfind(const string &s, size_type pos = npos) const; + size_type find_first_of(const string &s, size_type i = 0) const; + size_type find_first_not_of(const string &s, size_type i = 0) const; + size_type find_last_of(const string &s, size_type i = npos) const; + size_type find_last_not_of(const string &s, size_type i = npos) const; + string substr(size_type start = 0, size_type len = npos) const; + + // Character-level member methods. + value_type operator[](size_type i) const; + size_type find(value_type uc, size_type pos = 0) const; + size_type rfind(value_type uc, size_type pos = npos) const; + size_type find_first_of(value_type uc, size_type i = 0) const; + size_type find_first_not_of(value_type uc, size_type i = 0) const; + size_type find_last_of(value_type uc, size_type i = npos) const; + size_type find_last_not_of(value_type uc, size_type i = npos) const; + + // Sequence accessor. + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + + // Operator overloads + string& operator=(const string &s); + string& operator+=(const string &s); + string& operator+=(value_type uc); + + // Some helper functions that are nice to have + bool startsWith(const string &s, bool ignoreCase = false) const; + bool endsWith(const string &s, bool ignoreCase = false) const; + std::vector split(const string &sep) const; + + // Overloaded operators + string operator+(const string &rhs) const; + string operator+(value_type uc) const; + bool operator==(const string &rhs) const; + bool operator!=(const string &rhs) const; + bool operator<(const string &rhs) const; + bool operator>(const string &rhs) const; + bool operator<=(const string &rhs) const; + bool operator>=(const string &rhs) const; + +private: + // Cache operations + void InvalidateCache(); + + // Cache accessors + const utf16_t *GetUtf16Cache() const; + + // utf::string is internally backed by Glib::ustring. + Glib::ustring mUstr; + + // Cached representations. + mutable utf16_t *mUtf16Cache; +}; + +// Helper operators + +string inline +operator+(ConstUnicode lhs, const string &rhs) { + return string(lhs) + rhs; +} + +bool inline +operator==(ConstUnicode lhs, const string &rhs) { + return string(lhs) == rhs; +} + +bool inline +operator!=(ConstUnicode lhs, const string &rhs) { + return string(lhs) != rhs; +} + +bool inline +operator<(ConstUnicode lhs, const string &rhs) { + return string(lhs) < rhs; +} + +bool inline +operator>(ConstUnicode lhs, const string &rhs) { + return string(lhs) > rhs; +} + +bool inline +operator<=(ConstUnicode lhs, const string &rhs) { + return string(lhs) <= rhs; +} + +bool inline +operator>=(ConstUnicode lhs, const string &rhs) { + return string(lhs) >= rhs; +} + + +// ConversionError class for exception + +class ConversionError {}; + + +// Helper functions + +bool VMSTRING_EXPORT Validate(const Glib::ustring& s); + +string VMSTRING_EXPORT +CreateWithLength(const void *buffer, ssize_t lengthInBytes, StringEncoding encoding); + +string VMSTRING_EXPORT +CreateWithBOMBuffer(const void *buffer, ssize_t lengthInBytes); + +string VMSTRING_EXPORT +IntToStr(int64 val); + +void VMSTRING_EXPORT CreateWritableBuffer(const string& s, + std::vector& buf); +void VMSTRING_EXPORT CreateWritableBuffer(const string& s, + std::vector& buf); + + +} // namespace utf + + + +// Template specializations for utf::string. +namespace std { + +template<> +inline void +swap(utf::string& s1, // IN/OUT + utf::string& s2) // IN/OUT +{ + s1.swap(s2); +} + +} // namespace std + + +#ifdef _WIN32 +#pragma warning(pop) +#endif + +#endif diff --git a/open-vm-tools/vmware-user/stringxx/ubstr_t.hh b/open-vm-tools/vmware-user/stringxx/ubstr_t.hh new file mode 100644 index 000000000..233093496 --- /dev/null +++ b/open-vm-tools/vmware-user/stringxx/ubstr_t.hh @@ -0,0 +1,923 @@ +/********************************************************* + * Copyright (C) 2008 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. + * + *********************************************************/ + +/* + * ubstr_t.hh -- + * + * A string wrapper for _bstr_t. _bstr_t assumes all char* strings use + * the local MBCS encoding, but we want to require that char* strings be + * interpreted as UTF-8. + */ + +#ifndef _WIN32 +#error This file should be built on Windows only. +#endif + +#pragma once + +#include +#include +#include + +extern "C" { +#include "unicode.h" +#include "util.h" +} + + +#ifdef I_LOVE_BSTR_T_CLASSIC + +typedef _bstr_t ubstr_t; +typedef _variant_t uvariant_t; + +#else + +class ubstr_t +{ +public: + ubstr_t(); + ubstr_t(const char *s); + ubstr_t(const wchar_t *s); + ubstr_t(const _variant_t& var); + ubstr_t(BSTR bstr, bool copy); + ubstr_t(const ubstr_t& s); + + ubstr_t& operator=(const ubstr_t& s); + ubstr_t& operator=(const char *s); + ubstr_t& operator=(const wchar_t *s); + ubstr_t& operator=(const _variant_t& var); + + // Wrappers around standard _bstr_t methods. + void Assign(BSTR s); + BSTR copy(bool copy = true) const; + + void Attach(BSTR s); + BSTR Detach(); + + BSTR *GetAddress(); + BSTR& GetBSTR(); + + unsigned int length() const; + + ubstr_t& operator+=(const ubstr_t& s); + ubstr_t operator+(const ubstr_t& s) const; + ubstr_t operator+(const char *s) const; + ubstr_t operator+(const wchar_t *s) const; + + bool operator!() const; + bool operator==(const ubstr_t& s) const; + bool operator!=(const ubstr_t& s) const; + bool operator<(const ubstr_t& s) const; + bool operator>(const ubstr_t& s) const; + bool operator<=(const ubstr_t& s) const; + bool operator>=(const ubstr_t& s) const; + + operator const wchar_t*() const; + operator wchar_t*() const; // XXX: Get rid of this. + operator const char*() const; +#ifndef I_HATE_NON_CONST_BSTR_T_CASTS + operator char*() const; // XXX: Get rid of this. +#endif + + /* + * _variant_t is implicitly constructible from a _bstr_t. Since we can't + * add a constructor to _variant_t, instead provide an implicit conversion + * from ubstr_t to _variant_t. + */ + operator _variant_t() const; + + void swap(ubstr_t& s); + +private: + class UTF8Data + { + public: + // Takes ownership of the input string. + UTF8Data(char *utf8String = NULL) // IN/OUT: May be NULL + : mUTF8String(utf8String), + mRefCount(1) + { + } + + // For Glib::RefPtr. + void reference() + { + ++mRefCount; + } + + // For Glib::RefPtr. + void unreference() + { + --mRefCount; + if (mRefCount == 0) { + delete this; + } + } + + // Takes ownership of the input string. + void Set(char *utf8String) // IN/OUT: May be NULL. + { + free(mUTF8String); + mUTF8String = utf8String; + } + + char *Get() + { + return mUTF8String; + } + + private: + // Only destructible via unreference(). + ~UTF8Data() + { + free(mUTF8String); + } + + char *mUTF8String; + unsigned int mRefCount; + + private: + // Intentionally unimplemented. + UTF8Data(const UTF8Data&); + UTF8Data& operator=(const UTF8Data&); + }; + + char *GetUTF8Cache() const; + void InvalidateCache(); + + /* + * Anything that mutates mBstr (all non-const methods) must call + * InvalidateCache(). + */ + _bstr_t mBstr; + + // mUTF8 is allocated and initialized lazily. + mutable Glib::RefPtr mUTF8; +}; + + +/* + * _variant_t does string conversions too, so we also need to wrap that. + * Since _variant_t doesn't keep a cached version of the locally-encoded + * MBCS string and since we don't use _variant_t nearly as much as _bstr_t, + * substitutability isn't as much of a concern, so we can use inheritance. + */ +class uvariant_t + : public _variant_t +{ +public: + /* + * Wrappers around standard _variant_t constructors. Unfortunately we + * have to wrap all the constructors since they aren't inherited. + */ + uvariant_t() : _variant_t() { } + uvariant_t(const VARIANT& var) : _variant_t(var) { } + uvariant_t(const VARIANT *var) : _variant_t(var) { } + uvariant_t(const _variant_t& var) : _variant_t(var) { } + uvariant_t(VARIANT& var, bool copy) : _variant_t(var, copy) { } + uvariant_t(short i, VARTYPE vt = VT_I2) : _variant_t(i, vt) { } + uvariant_t(long i, VARTYPE vt = VT_I4) : _variant_t(i, vt) { } + uvariant_t(float f) : _variant_t(f) { } + uvariant_t(double d, VARTYPE vt = VT_R8) : _variant_t(d, vt) { } + uvariant_t(const CY& cy) : _variant_t(cy) { } + uvariant_t(const _bstr_t& s) : _variant_t(s) { } + uvariant_t(const wchar_t *s) : _variant_t(s) { } + uvariant_t(IDispatch *dispatch, bool addRef = true) : _variant_t(dispatch, addRef) { } + uvariant_t(bool b) : _variant_t(b) { } + uvariant_t(IUnknown *unknown, bool addRef = true) : _variant_t(unknown, addRef) { } + uvariant_t(const DECIMAL& dec) : _variant_t(dec) { } + uvariant_t(BYTE i) : _variant_t(i) { } + uvariant_t(char c) : _variant_t(c) { } + uvariant_t(unsigned short i) : _variant_t(i) { } + uvariant_t(unsigned long i) : _variant_t(i) { } + uvariant_t(int i) : _variant_t(i) { } + uvariant_t(unsigned int i) : _variant_t(i) { } +#if _WIN32_WINNT >= 0x0501 + uvariant_t(__int64 i) : _variant_t(i) { } + uvariant_t(unsigned __int64 i) : _variant_t(i) { } +#endif + + // Override the _variant_t constructor that assumes locally-encoded strings. + uvariant_t(const char *s) : _variant_t(ubstr_t(s)) { } + + // Provide conversion from our ubstr_t wrapper. + uvariant_t(const ubstr_t& s) : _variant_t(s) { } +}; + + +/* + *----------------------------------------------------------------------------- + * + * ubstr_t::ubstr_t -- + * + * ubstr_t constructors. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +inline +ubstr_t::ubstr_t() + : mBstr(), + mUTF8() +{ +} + + +inline +ubstr_t::ubstr_t(const char *s) // IN: A UTF-8-encoded string. + : mBstr(), + mUTF8() +{ + if (s != NULL) { + // Since we already have the UTF-8 version of the string, cache it now. + mUTF8 = Glib::RefPtr(new UTF8Data(Util_SafeStrdup(s))); + utf16_t *utf16Str = Unicode_GetAllocUTF16(s); + try { + mBstr = utf16Str; + free(utf16Str); + } catch (...) { + free(utf16Str); + throw; + } + } +} + + +inline +ubstr_t::ubstr_t(const wchar_t *s) // IN + : mBstr(s), + mUTF8() +{ +} + + +inline +ubstr_t::ubstr_t(const _variant_t& var) // IN + : mBstr(var), + mUTF8() +{ +} + + +inline +ubstr_t::ubstr_t(BSTR bstr, // IN + bool copy) // IN + : mBstr(bstr, copy), + mUTF8() +{ +} + + +inline +ubstr_t::ubstr_t(const ubstr_t& s) // IN + : mBstr(s.mBstr), + mUTF8(s.mUTF8) +{ + if (static_cast(mBstr) != NULL && !mUTF8) { + mUTF8 = s.mUTF8 = Glib::RefPtr(new UTF8Data()); + } +} + + +/* + *----------------------------------------------------------------------------- + * + * ubstr_t::operator= -- + * + * ubstr_t assignment operators. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +inline ubstr_t& +ubstr_t::operator=(const ubstr_t& s) // IN +{ + ubstr_t copy(s); + swap(copy); + return *this; +} + + +inline ubstr_t& +ubstr_t::operator=(const char *s) // IN: A UTF-8-encoded string. +{ + return operator=(ubstr_t(s)); +} + + +inline ubstr_t& +ubstr_t::operator=(const wchar_t *s) // IN +{ + return operator=(ubstr_t(s)); +} + + +inline ubstr_t& +ubstr_t::operator=(const _variant_t& var) // IN +{ + return operator=(ubstr_t(var)); +} + + + +/* + *----------------------------------------------------------------------------- + * + * ubstr_t::Assign -- + * + * Wrapper around _bstr_t::Assign. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +inline void +ubstr_t::Assign(BSTR s) // IN +{ + InvalidateCache(); + mBstr.Assign(s); +} + + +/* + *----------------------------------------------------------------------------- + * + * ubstr_t::copy -- + * + * Wrapper around _bstr_t::copy. + * + * Results: + * A copy of the underlying BSTR. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +inline BSTR +ubstr_t::copy(bool copy) // IN/OPT + const +{ + return mBstr.copy(copy); +} + + +/* + *----------------------------------------------------------------------------- + * + * ubstr_t::Attach -- + * + * Wrapper around _bstr_t::Attach. + * + * Results: + * None. + * + * Side effects: + * Invalidates the UTF-8 cache. + * + *----------------------------------------------------------------------------- + */ + +inline void +ubstr_t::Attach(BSTR s) // IN +{ + InvalidateCache(); + mBstr.Attach(s); +} + + +/* + *----------------------------------------------------------------------------- + * + * ubstr_t::Detach -- + * + * Wrapper around _bstr_t::Detach. + * + * Results: + * The underlying BSTR, which is no longer managed. + * + * Side effects: + * Invalidates the UTF-8 cache. + * + *----------------------------------------------------------------------------- + */ + +inline BSTR +ubstr_t::Detach() +{ + InvalidateCache(); + return mBstr.Detach(); +} + + +/* + *----------------------------------------------------------------------------- + * + * ubstr_t::GetAddress -- + * + * Wrapper around _bstr_t::GetAddress. + * + * Results: + * A pointer to the underlying BSTR. + * + * Side effects: + * Invalidates the UTF-8 cache. + * + *----------------------------------------------------------------------------- + */ + +inline BSTR * +ubstr_t::GetAddress() +{ + /* + * We don't know if the underlying BSTR will be modified via the returned + * pointer. We can only assume it will. + */ + InvalidateCache(); + return mBstr.GetAddress(); +} + + +/* + *----------------------------------------------------------------------------- + * + * ubstr_t::GetBSTR -- + * + * Wrapper around _bstr_t::GetBSTR. + * + * Results: + * A reference to the underlying BSTR. + * + * Side effects: + * Invalidates the UTF-8 cache. + * + *----------------------------------------------------------------------------- + */ + +inline BSTR& +ubstr_t::GetBSTR() +{ + /* + * We don't know if the underlying BSTR will be modified via the returned + * reference. We can only assume it will. + */ + InvalidateCache(); + return mBstr.GetBSTR(); +} + + +/* + *----------------------------------------------------------------------------- + * + * ubstr_t::length -- + * + * Wrapper around _bstr_t::length. + * + * Results: + * The length of the string, in TCHARs. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +inline unsigned int +ubstr_t::length() + const +{ + return mBstr.length(); +} + + +/* + *----------------------------------------------------------------------------- + * + * ubstr_t::operator+= -- + * + * Mutating concatenation operator. + * + * Results: + * A reference to this ubstr_t object. + * + * Side effects: + * Invalidates the UTF-8 cache. + * + *----------------------------------------------------------------------------- + */ + +inline ubstr_t& +ubstr_t::operator+=(const ubstr_t& s) // IN +{ + InvalidateCache(); + mBstr += s.mBstr; + return *this; +} + + +/* + *----------------------------------------------------------------------------- + * + * ubstr_t::operator+ -- + * + * Concatenation operators. + * + * Results: + * A copy of the concatenated string. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +inline ubstr_t +ubstr_t::operator+(const ubstr_t& s) // IN + const +{ + ubstr_t copy(*this); + copy += s; + return copy; +} + + +inline ubstr_t +ubstr_t::operator+(const char *s) // IN + const +{ + return operator+(ubstr_t(s)); +} + + +inline ubstr_t +ubstr_t::operator+(const wchar_t *s) // IN + const +{ + return operator+(ubstr_t(s)); +} + + +/* + *----------------------------------------------------------------------------- + * + * ubstr_t::operator! -- + * + * Wrapper around _bstr_t::operator!. + * + * Results: + * true if the underlying _bstr_t is NULL, false otherwise. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +inline bool +ubstr_t::operator!() + const +{ + return !mBstr; +} + + +/* + *----------------------------------------------------------------------------- + * + * ubstr_t::operator== -- + * ubstr_t::operator!= -- + * ubstr_t::operator< -- + * ubstr_t::operator> -- + * ubstr_t::operator<= -- + * ubstr_t::operator>= -- + * + * Wrappers around _bstr_t's comparison operators. + * + * Results: + * true if the comparisons are hold, false otherwise. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +inline bool +ubstr_t::operator==(const ubstr_t& s) // IN + const +{ + return mBstr == s.mBstr; +} + + +inline bool +ubstr_t::operator!=(const ubstr_t& s) // IN + const +{ + return mBstr != s.mBstr; +} + + +inline bool +ubstr_t::operator<(const ubstr_t& s) // IN + const +{ + return mBstr < s.mBstr; +} + + +inline bool +ubstr_t::operator>(const ubstr_t& s) // IN + const +{ + return mBstr > s.mBstr; +} + + +inline bool +ubstr_t::operator<=(const ubstr_t& s) // IN + const +{ + return mBstr <= s.mBstr; +} + + +inline bool +ubstr_t::operator>=(const ubstr_t& s) // IN + const +{ + return mBstr >= s.mBstr; +} + + +/* + *----------------------------------------------------------------------------- + * + * ubstr_t::operator const wchar_t* -- + * ubstr_t::operator wchar_t* -- + * + * Wrappers around _bstr_t's cast operators. + * + * Results: + * A pointer to the underlying UTF-16 string. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +inline +ubstr_t::operator const wchar_t*() + const +{ + return static_cast(mBstr); +} + + +inline +ubstr_t::operator wchar_t*() + const +{ + return static_cast(mBstr); +} + + +/* + *----------------------------------------------------------------------------- + * + * ubstr_t::operator const char_t* -- + * ubstr_t::operator char_t* -- + * + * Cast operators to UTF-8 strings. + * + * Results: + * A pointer to a UTF-8 string. + * + * Side effects: + * Might initializes the UTF-8 cache. + * + *----------------------------------------------------------------------------- + */ + +inline +ubstr_t::operator const char*() + const +{ + return GetUTF8Cache(); +} + + +#ifndef I_HATE_NON_CONST_BSTR_T_CASTS +inline +ubstr_t::operator char*() + const +{ + return GetUTF8Cache(); +} +#endif + + +/* + *----------------------------------------------------------------------------- + * + * ubstr_t::operator _variant_t -- + * + * Cast operator to _variant_t. + * + * Results: + * A _variant_t equivalent to this ubstr_t object. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +inline +ubstr_t::operator _variant_t() + const +{ + return static_cast(mBstr); +} + + +/* + *----------------------------------------------------------------------------- + * + * ubstr_t::swap -- + * + * Swaps this ubstr_t object with another. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +inline void +ubstr_t::swap(ubstr_t& s) // IN/OUT +{ + std::swap(mBstr, s.mBstr); + mUTF8.swap(s.mUTF8); +} + + +/* + *----------------------------------------------------------------------------- + * + * ubstr_t::GetUTF8Cache -- + * + * Retrieves the UTF-8 cache, initializing it if necessary. + * + * Results: + * The cached UTF-8 string. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +inline char * +ubstr_t::GetUTF8Cache() + const +{ + if (static_cast(mBstr) == NULL) { + // Nothing to do. + return NULL; + } + + char *utf8Str = NULL; + try { + if (!mUTF8) { + mUTF8 = Glib::RefPtr(new UTF8Data()); + } + + if (mUTF8->Get() == NULL) { + utf8Str = Unicode_AllocWithUTF16(static_cast(mBstr)); + mUTF8->Set(utf8Str); + } + } catch (...) { + free(utf8Str); + throw; + } + + return mUTF8->Get(); +} + + +/* + *----------------------------------------------------------------------------- + * + * ubstr_t::InvalidateCache -- + * + * Clears the UTF-8 cache. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +inline void +ubstr_t::InvalidateCache() +{ + mUTF8.clear(); +} + + +/* + *----------------------------------------------------------------------------- + * + * operator+ -- + * + * Non-member concatenation operators. + * + * Results: + * A copy of the concatenated string. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +inline ubstr_t +operator+(const char *s1, // IN: A UTF-8-encoded sting. + const ubstr_t& s2) // IN +{ + return ubstr_t(s1) + s2; +} + + +inline ubstr_t +operator+(const wchar_t *s1, // IN + const ubstr_t& s2) // IN +{ + return ubstr_t(s1) + s2; +} + + +/* + * These are not part of the _bstr_t interface but are provided to catch + * misuse. They are intentionally unimplemented to avoid a maintenance + * burden, and they intentionally return nothing so that accidental usage + * normally results in compile-time errors instead of link-time ones. + */ + +void operator==(const char *s1, const ubstr_t& s2); +void operator!=(const char *s1, const ubstr_t& s2); +void operator<(const char *s1, const ubstr_t& s2); +void operator>(const char *s1, const ubstr_t& s2); +void operator<=(const char *s1, const ubstr_t& s2); +void operator>=(const char *s1, const ubstr_t& s2); + +void operator==(const WCHAR *s1, const ubstr_t& s2); +void operator!=(const WCHAR *s1, const ubstr_t& s2); +void operator<(const WCHAR *s1, const ubstr_t& s2); +void operator>(const WCHAR *s1, const ubstr_t& s2); +void operator<=(const WCHAR *s1, const ubstr_t& s2); +void operator>=(const WCHAR *s1, const ubstr_t& s2); + + +#endif // I_LOVE_BSTR_T_CLASSIC diff --git a/open-vm-tools/vmware-user/vmware-user.c b/open-vm-tools/vmware-user/vmware-user.cpp similarity index 94% rename from open-vm-tools/vmware-user/vmware-user.c rename to open-vm-tools/vmware-user/vmware-user.cpp index 6a8d0b5ab..e489d88f5 100644 --- a/open-vm-tools/vmware-user/vmware-user.c +++ b/open-vm-tools/vmware-user/vmware-user.cpp @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2005 VMware, Inc. All rights reserved. + * 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 @@ -16,15 +16,18 @@ * *********************************************************/ -/* - * vmware-user.c -- +/** + * @file vmware-user.cpp * - * The linux vmware-user app. It's a hidden window app that is supposed - * to run on session start. It handles tools features which we want - * active all the time, but don't want to impose a visable window on the - * user. + * The linux vmware-user app. It's a hidden window app that is supposed + * to run on session start. It handles tools features which we want + * active all the time, but don't want to impose a visible window on the + * user. */ +#include "copyPasteWrapper.h" + +extern "C" { #include #include #include @@ -62,6 +65,7 @@ #include "vmwareuser_version.h" #include "embed_version.h" +} // extern "C" VM_EMBED_VERSION(VMWAREUSER_VERSION_STRING); #ifdef _DEBUG @@ -94,14 +98,17 @@ Bool VMwareUserRpcInCapRegCB (char const **result, size_t *resultLen, size_t argsSize, void *clientData); void VMwareUserRpcInErrorCB (void *clientdata, char const *status); +extern "C" { extern Bool ForeignTools_Initialize(GuestApp_Dict *configDictionaryParam, DblLnkLst_Links *eventQueue); extern void ForeignTools_Shutdown(void); +} static Bool InitGroupLeader(Window *groupLeader, Window *rootWindow); static Bool AcquireDisplayLock(void); static Bool QueryX11Lock(Display *dpy, Window w, Atom lockAtom); static void ReloadSelf(void); +static void VMwareUserRegisterCopyPaste(bool reg); /* @@ -110,7 +117,6 @@ static void ReloadSelf(void); static Bool gOpenUrlRegistered; static Bool gDnDRegistered; -static Bool gCopyPasteRegistered; static Bool gHgfsServerRegistered; static pid_t gParentPid; static char gLogFilePath[PATH_MAX]; @@ -135,7 +141,6 @@ GtkWidget *gGHWnd; Window gXRoot; DblLnkLst_Links *gEventQueue; -Bool optionCopyPaste; Bool optionDnD; Bool gCanUseVMwareCtrl; Bool gCanUseVMwareCtrlTopologySet; @@ -198,9 +203,10 @@ void VMwareUserCleanupRpc(void) DnD_Unregister(gHGWnd, gGHWnd); gDnDRegistered = FALSE; } - if (gCopyPasteRegistered) { - CopyPaste_Unregister(gUserMainWidget); - gCopyPasteRegistered = FALSE; + + CopyPasteWrapper *p = CopyPasteWrapper::GetInstance(); + if (p && p->IsRegistered()) { + p->Unregister(); } RpcIn_Destruct(gRpcIn); gRpcIn = NULL; @@ -244,7 +250,11 @@ void VMwareUserSignalHandler(int sig) // IN VMwareUserCleanupRpc(); } +#if defined(HAVE_GTKMM) + Gtk::Main::quit(); +#else gtk_main_quit(); +#endif } @@ -270,7 +280,11 @@ VMwareUser_OnDestroy(GtkWidget *widget, // IN: Unused gpointer data) // IN: Unused { VMwareUserCleanupRpc(); +#if defined(HAVE_GTKMM) + Gtk::Main::quit(); +#else gtk_main_quit(); +#endif } @@ -361,8 +375,9 @@ VMwareUserRpcInResetCB(RpcInData *data) // IN/OUT if (gDnDRegistered) { DnD_OnReset(gHGWnd, gGHWnd); } - if (gCopyPasteRegistered) { - CopyPaste_OnReset(); + CopyPasteWrapper *p = CopyPasteWrapper::GetInstance(); + if (p) { + p->OnReset(); } return RPCIN_SETRETVALS(data, "ATR " TOOLS_DND_NAME, TRUE); } @@ -439,16 +454,7 @@ VMwareUserRpcInCapRegCB(char const **result, // OUT } } - if (!gCopyPasteRegistered) { - gCopyPasteRegistered = CopyPaste_Register(gUserMainWidget); - } - - if (gCopyPasteRegistered) { - if (!CopyPaste_RegisterCapability()) { - CopyPaste_Unregister(gUserMainWidget); - gCopyPasteRegistered = FALSE; - } - } + VMwareUserRegisterCopyPaste(TRUE); if (!HgfsServerManager_CapReg(TOOLS_DND_NAME, gHgfsServerRegistered)) { Debug("VMwareUserRpcInCapRegCB: Failed to register HGFS server capability.\n"); @@ -461,6 +467,39 @@ VMwareUserRpcInCapRegCB(char const **result, // OUT } +/* + *----------------------------------------------------------------------------- + * + * VMwareUserRegisterCopyPaste + * + * Call the CopyPasteWrapper singleton to register, or unregister, + * copy and paste with the host. The wrapper class will try whatever + * versions are supported, in highest to lowest order when registering. + * + * Results: + * None. + * + * Side effects: + * Copy and paste capabilities will be (un)registered. + * + *----------------------------------------------------------------------------- + */ + +static void +VMwareUserRegisterCopyPaste(bool reg) // IN: if TRUE, register, else unregister +{ + CopyPasteWrapper *p = CopyPasteWrapper::GetInstance(); + if (p) { + p->SetUserData(static_cast(gUserMainWidget)); + if (reg) { + p->Register(); + } else { + p->Unregister(); + } + } +} + + /* *----------------------------------------------------------------------------- * @@ -519,17 +558,9 @@ VMwareUserRpcInSetOptionCB(char const **result, // OUT */ if (strcmp(option, TOOLSOPTION_COPYPASTE) == 0) { if (strcmp(value, "1") == 0) { - optionCopyPaste = TRUE; - if (!gCopyPasteRegistered) { - CopyPaste_Register(gUserMainWidget); - gCopyPasteRegistered = TRUE; - } + VMwareUserRegisterCopyPaste(TRUE); } else if (strcmp(value, "0") == 0) { - optionCopyPaste = FALSE; - if (gCopyPasteRegistered) { - CopyPaste_Unregister(gUserMainWidget); - gCopyPasteRegistered = FALSE; - } + VMwareUserRegisterCopyPaste(FALSE); } else { retStr = INVALID_VALUE; goto free_value; @@ -711,7 +742,6 @@ main(int argc, char *argv[]) gOpenUrlRegistered = FALSE; gDnDRegistered = FALSE; - gCopyPasteRegistered = FALSE; gHgfsServerRegistered = FALSE; gBlockFd = -1; gReloadSelf = FALSE; @@ -734,7 +764,11 @@ main(int argc, char *argv[]) /* Set to system locale. */ setlocale(LC_CTYPE, ""); gtk_set_locale(); +#if defined(HAVE_GTKMM) + Gtk::Main main(&argc, &argv); +#else gtk_init(&argc, &argv); +#endif /* * Running more than 1 VMware user process (vmware-user) per X11 session @@ -918,7 +952,11 @@ main(int argc, char *argv[]) /* * We'll block here until the window is destroyed or a signal is recieved */ +#if defined(HAVE_GTKMM) + main.run(); +#else gtk_main(); +#endif if (gSigExit) { break; @@ -931,9 +969,9 @@ main(int argc, char *argv[]) DnD_Unregister(gHGWnd, gGHWnd); gDnDRegistered = FALSE; } - if (gCopyPasteRegistered) { - CopyPaste_Unregister(gUserMainWidget); - gCopyPasteRegistered = FALSE; + CopyPasteWrapper *p = CopyPasteWrapper::GetInstance(); + if (p) { + p->Unregister(); } if (gBlockFd >= 0 && !DnD_UninitializeBlocking(gBlockFd)) { Debug("vmware-user failed to uninitialize blocking.\n"); @@ -1013,7 +1051,9 @@ InitGroupLeader(Window *groupLeader, // OUT: group leader window { Window myGroupLeader; Window myRootWindow; - XSetWindowAttributes attr = { .override_redirect = True }; + XSetWindowAttributes attr; + + attr.override_redirect = True; ASSERT(groupLeader); ASSERT(rootWindow); diff --git a/open-vm-tools/vmware-user/vmwareuserInt.h b/open-vm-tools/vmware-user/vmwareuserInt.h index a64971756..f27598785 100644 --- a/open-vm-tools/vmware-user/vmwareuserInt.h +++ b/open-vm-tools/vmware-user/vmwareuserInt.h @@ -119,7 +119,6 @@ extern Display *gXDisplay; extern Window gXRoot; extern DblLnkLst_Links *gEventQueue; extern GtkWidget *gUserMainWidget; -extern Bool optionCopyPaste; extern int gBlockFd; #endif // _VMWAREUSER_INT_H_