diff --git a/packages/sysutils/open-vm-tools/modules-load.d/open-vm-tools.conf b/packages/sysutils/open-vm-tools/modules-load.d/open-vm-tools.conf
new file mode 100644
index 0000000000..c0ee39caed
--- /dev/null
+++ b/packages/sysutils/open-vm-tools/modules-load.d/open-vm-tools.conf
@@ -0,0 +1,2 @@
+# load vmxnet kernel module
+vmxnet
diff --git a/packages/sysutils/open-vm-tools/package.mk b/packages/sysutils/open-vm-tools/package.mk
new file mode 100644
index 0000000000..137700fa93
--- /dev/null
+++ b/packages/sysutils/open-vm-tools/package.mk
@@ -0,0 +1,74 @@
+################################################################################
+# This file is part of OpenELEC - http://www.openelec.tv
+# Copyright (C) 2009-2014 Stephan Raue (stephan@openelec.tv)
+# Copyright (C) 2011 Anthony Nash (nash.ant@gmail.com)
+#
+# OpenELEC 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, either version 2 of the License, or
+# (at your option) any later version.
+#
+# OpenELEC 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 OpenELEC. If not, see .
+################################################################################
+
+PKG_NAME="open-vm-tools"
+PKG_VERSION="stable-10.0.7"
+PKG_REV="1"
+PKG_ARCH="x86_64"
+PKG_LICENSE="GPL"
+PKG_SITE="http://open-vm-tools.sourceforge.net"
+PKG_URL="https://github.com/vmware/open-vm-tools/archive/${PKG_VERSION}.tar.gz"
+PKG_DEPENDS_TARGET="toolchain glib:host glib libdnet"
+PKG_PRIORITY="optional"
+PKG_SECTION="virtualization"
+PKG_SHORTDESC="open-vm-tools: open source implementation of VMware Tools"
+PKG_LONGDESC="open-vm-tools: open source implementation of VMware Tools"
+
+PKG_IS_ADDON="no"
+PKG_AUTORECONF="yes"
+
+OPENVMTOOLS_KERNEL_VER=$(basename $(ls -d $ROOT/$BUILD/linux-[0-9]*)| sed 's|linux-||g')
+
+PKG_CONFIGURE_OPTS_TARGET="--disable-docs \
+ --disable-tests \
+ --disable-deploypkg \
+ --without-pam \
+ --without-gtk2 \
+ --without-gtkmm \
+ --without-ssl \
+ --without-x \
+ --without-xerces \
+ --without-icu \
+ --without-procps \
+ --without-kernel-modules \
+ --with-sysroot=$SYSROOT_PREFIX"
+
+unpack() {
+ # ugly
+ $SCRIPTS/extract $PKG_NAME "$PKG_VERSION.tar.gz" $BUILD
+ mv $PKG_BUILD/$PKG_NAME/* $PKG_BUILD/
+ rm -r $PKG_BUILD/$PKG_NAME
+}
+
+pre_configure_target() {
+ export LIBS="-ldnet"
+}
+
+makeinstall_target() {
+ mkdir -p $INSTALL/usr/lib
+ cp -PR libvmtools/.libs/libvmtools.so* $INSTALL/usr/lib
+
+ mkdir -p $INSTALL/usr/bin
+ cp -PR services/vmtoolsd/.libs/vmtoolsd $INSTALL/usr/bin
+ cp -PR checkvm/.libs/vmware-checkvm $INSTALL/usr/bin
+}
+
+post_install() {
+ enable_service open-vm-tools.service
+}
diff --git a/packages/sysutils/open-vm-tools/patches/open-vm-tools-0001_glib-static.patch b/packages/sysutils/open-vm-tools/patches/open-vm-tools-0001_glib-static.patch
new file mode 100644
index 0000000000..da5671f3ea
--- /dev/null
+++ b/packages/sysutils/open-vm-tools/patches/open-vm-tools-0001_glib-static.patch
@@ -0,0 +1,1507 @@
+diff -Naur a/lib/glibUtils/fileLogger.c b/lib/glibUtils/fileLogger.c
+--- a/lib/glibUtils/fileLogger.c 2015-11-24 07:59:42.000000000 +0100
++++ b/lib/glibUtils/fileLogger.c 2016-01-15 10:22:59.976153011 +0100
+@@ -44,7 +44,7 @@
+ guint maxFiles;
+ gboolean append;
+ gboolean error;
+- GStaticMutex lock;
++ GMutex lock;
+ } FileLogger;
+
+
+@@ -327,7 +327,7 @@
+ FileLogger *logger = data;
+ gsize written;
+
+- g_static_mutex_lock(&logger->lock);
++ g_mutex_lock(&logger->lock);
+
+ if (logger->error) {
+ goto exit;
+@@ -366,7 +366,7 @@
+ }
+
+ exit:
+- g_static_mutex_unlock(&logger->lock);
++ g_mutex_unlock(&logger->lock);
+ }
+
+
+@@ -388,7 +388,7 @@
+ if (logger->file != NULL) {
+ g_io_channel_unref(logger->file);
+ }
+- g_static_mutex_free(&logger->lock);
++ g_mutex_free(&logger->lock);
+ g_free(logger->path);
+ g_free(logger);
+ }
+@@ -435,7 +435,7 @@
+ data->append = append;
+ data->maxSize = maxSize * 1024 * 1024;
+ data->maxFiles = maxFiles + 1; /* To account for the active log file. */
+- g_static_mutex_init(&data->lock);
++ g_mutex_init(&data->lock);
+
+ return &data->handler;
+ }
+diff -Naur a/lib/glibUtils/stdLogger.c b/lib/glibUtils/stdLogger.c
+--- a/lib/glibUtils/stdLogger.c 2015-11-24 07:59:42.000000000 +0100
++++ b/lib/glibUtils/stdLogger.c 2016-01-15 10:24:07.078256206 +0100
+@@ -66,12 +66,12 @@
+ StdLogger *sdata = data;
+
+ if (!sdata->attached) {
+- g_static_mutex_lock(&gConsoleLock);
++ g_mutex_lock(&gConsoleLock);
+ if (gRefCount != 0 || GlibUtils_AttachConsole()) {
+ gRefCount++;
+ sdata->attached = TRUE;
+ }
+- g_static_mutex_unlock(&gConsoleLock);
++ g_mutex_unlock(&gConsoleLock);
+ }
+
+ if (!sdata->attached) {
+@@ -105,11 +105,11 @@
+ {
+ #if defined(_WIN32)
+ StdLogger *sdata = data;
+- g_static_mutex_lock(&gConsoleLock);
++ g_mutex_lock(&gConsoleLock);
+ if (sdata->attached && --gRefCount == 0) {
+ FreeConsole();
+ }
+- g_static_mutex_unlock(&gConsoleLock);
++ g_mutex_unlock(&gConsoleLock);
+ #endif
+ g_free(data);
+ }
+diff -Naur a/lib/glibUtils/sysLogger.c b/lib/glibUtils/sysLogger.c
+--- a/lib/glibUtils/sysLogger.c 2015-11-24 07:59:42.000000000 +0100
++++ b/lib/glibUtils/sysLogger.c 2016-01-15 10:37:53.148968674 +0100
+@@ -38,7 +38,7 @@
+
+
+ static SysLogger *gSysLogger;
+-static GStaticMutex gSysLoggerLock = G_STATIC_MUTEX_INIT;
++static GMutex gSysLoggerLock;
+
+
+ /*
+@@ -105,7 +105,7 @@
+ {
+ g_return_if_fail(data == gSysLogger);
+ g_return_if_fail(gSysLogger->refcount > 0);
+- g_static_mutex_lock(&gSysLoggerLock);
++ g_mutex_lock(&gSysLoggerLock);
+ gSysLogger->refcount -= 1;
+ if (gSysLogger->refcount == 0) {
+ closelog();
+@@ -113,7 +113,7 @@
+ g_free(gSysLogger);
+ gSysLogger = NULL;
+ }
+- g_static_mutex_unlock(&gSysLoggerLock);
++ g_mutex_unlock(&gSysLoggerLock);
+ }
+
+
+@@ -140,7 +140,7 @@
+ GlibUtils_CreateSysLogger(const char *domain,
+ const char *facility)
+ {
+- g_static_mutex_lock(&gSysLoggerLock);
++ g_mutex_lock(&gSysLoggerLock);
+ if (gSysLogger == NULL) {
+ int facid = LOG_USER;
+
+@@ -203,7 +203,7 @@
+ } else {
+ gSysLogger->refcount += 1;
+ }
+- g_static_mutex_unlock(&gSysLoggerLock);
++ g_mutex_unlock(&gSysLoggerLock);
+ return &gSysLogger->handler;
+ }
+
+diff -Naur a/lib/rpcChannel/rpcChannelInt.h b/lib/rpcChannel/rpcChannelInt.h
+--- a/lib/rpcChannel/rpcChannelInt.h 2015-11-24 07:59:42.000000000 +0100
++++ b/lib/rpcChannel/rpcChannelInt.h 2016-01-15 11:30:31.403376775 +0100
+@@ -53,7 +53,7 @@
+ GMainContext *mainCtx;
+ const char *appName;
+ gpointer appCtx;
+- GStaticMutex outLock;
++ GMutex outLock;
+ struct RpcIn *in;
+ gboolean inStarted;
+ gboolean outStarted;
+diff -Naur a/libvmtools/vmtoolsLog.c b/libvmtools/vmtoolsLog.c
+--- a/libvmtools/vmtoolsLog.c 2015-11-24 07:59:42.000000000 +0100
++++ b/libvmtools/vmtoolsLog.c 2016-01-15 11:34:45.221864504 +0100
+@@ -160,7 +160,7 @@
+ static LogHandler *gErrorSyslog;
+ static GPtrArray *gDomains = NULL;
+ static gboolean gLogInitialized = FALSE;
+-static GStaticRecMutex gLogStateMutex = G_STATIC_REC_MUTEX_INIT;
++static GRecMutex gLogStateMutex;
+ static gboolean gLoggingStopped = FALSE;
+ static gboolean gLogIOSuspended = FALSE;
+
+@@ -1055,7 +1055,7 @@
+ gLogEnabled |= force;
+ if (!gLogInitialized) {
+ gLogInitialized = TRUE;
+- g_static_rec_mutex_init(&gLogStateMutex);
++ g_rec_mutex_init(&gLogStateMutex);
+ }
+
+ gMaxCacheEntries = g_key_file_get_integer(cfg, LOGGING_GROUP,
+@@ -1221,7 +1221,7 @@
+ void
+ VMTools_AcquireLogStateLock(void)
+ {
+- g_static_rec_mutex_lock(&gLogStateMutex);
++ g_rec_mutex_lock(&gLogStateMutex);
+ }
+
+
+@@ -1232,7 +1232,7 @@
+ void
+ VMTools_ReleaseLogStateLock(void)
+ {
+- g_static_rec_mutex_unlock(&gLogStateMutex);
++ g_rec_mutex_unlock(&gLogStateMutex);
+ }
+
+
+diff -Naur a/libvmtools/i18n.c b/libvmtools/i18n.c
+--- a/libvmtools/i18n.c 2015-11-24 07:59:42.000000000 +0100
++++ b/libvmtools/i18n.c 2016-01-15 11:37:18.900142179 +0100
+@@ -54,7 +54,7 @@
+
+ typedef struct MsgState {
+ HashTable *domains; /* List of text domains. */
+- GStaticMutex lock; /* Mutex to protect shared state. */
++ GMutex lock; /* Mutex to protect shared state. */
+ } MsgState;
+
+
+@@ -132,7 +132,7 @@
+ {
+ ASSERT(gMsgState == NULL);
+ gMsgState = g_new0(MsgState, 1);
+- g_static_mutex_init(&gMsgState->lock);
++ g_mutex_init(&gMsgState->lock);
+ return NULL;
+ }
+
+@@ -343,7 +343,7 @@
+ * This lock is pretty coarse-grained, but a lot of the code below just runs
+ * in exceptional situations, so it should be OK.
+ */
+- g_static_mutex_lock(&state->lock);
++ g_mutex_lock(&state->lock);
+
+ catalog = MsgGetCatalog(domain);
+ if (catalog != NULL) {
+@@ -414,7 +414,7 @@
+ }
+ }
+
+- g_static_mutex_unlock(&state->lock);
++ g_mutex_unlock(&state->lock);
+
+ return strp;
+ }
+@@ -681,7 +681,7 @@
+ if (gMsgState->domains != NULL) {
+ HashTable_Free(gMsgState->domains);
+ }
+- g_static_mutex_free(&gMsgState->lock);
++ g_mutex_free(&gMsgState->lock);
+ g_free(gMsgState);
+ }
+ }
+@@ -774,9 +774,9 @@
+ "catalog dir '%s'.\n", domain, lang, catdir);
+ }
+ } else {
+- g_static_mutex_lock(&state->lock);
++ g_mutex_lock(&state->lock);
+ MsgSetCatalog(domain, catalog);
+- g_static_mutex_unlock(&state->lock);
++ g_mutex_unlock(&state->lock);
+ }
+ g_free(file);
+ free(dfltdir);
+diff -Naur a/lib/rpcChannel/rpcChannel.c b/lib/rpcChannel/rpcChannel.c
+--- a/lib/rpcChannel/rpcChannel.c 2015-11-24 07:59:42.000000000 +0100
++++ b/lib/rpcChannel/rpcChannel.c 2016-01-15 11:46:08.061147289 +0100
+@@ -675,7 +675,7 @@
+ chan = BackdoorChannel_New();
+ #endif
+ if (chan) {
+- g_static_mutex_init(&chan->outLock);
++ g_mutex_init(&chan->outLock);
+ }
+ return chan;
+ }
+@@ -691,7 +691,7 @@
+ RpcChannel_Shutdown(RpcChannel *chan)
+ {
+ if (chan != NULL) {
+- g_static_mutex_free(&chan->outLock);
++ g_mutex_free(&chan->outLock);
+ }
+
+ if (chan != NULL && chan->funcs != NULL && chan->funcs->shutdown != NULL) {
+@@ -776,7 +776,7 @@
+ g_return_if_fail(chan->funcs != NULL);
+ g_return_if_fail(chan->funcs->stop != NULL);
+
+- g_static_mutex_lock(&chan->outLock);
++ g_mutex_lock(&chan->outLock);
+ chan->funcs->stop(chan);
+
+ if (chan->in != NULL) {
+@@ -787,7 +787,7 @@
+ } else {
+ ASSERT(!chan->inStarted);
+ }
+- g_static_mutex_unlock(&chan->outLock);
++ g_mutex_unlock(&chan->outLock);
+ }
+
+
+@@ -853,7 +853,7 @@
+
+ ASSERT(chan && chan->funcs);
+
+- g_static_mutex_lock(&chan->outLock);
++ g_mutex_lock(&chan->outLock);
+
+ funcs = chan->funcs;
+ ASSERT(funcs->send);
+@@ -902,7 +902,7 @@
+ }
+
+ exit:
+- g_static_mutex_unlock(&chan->outLock);
++ g_mutex_unlock(&chan->outLock);
+ return ok;
+ }
+
+diff -Naur a/lib/rpcChannel/rpcChannel.c.save b/lib/rpcChannel/rpcChannel.c.save
+--- a/lib/rpcChannel/rpcChannel.c.save 1970-01-01 01:00:00.000000000 +0100
++++ b/lib/rpcChannel/rpcChannel.c.save 2016-01-15 11:44:57.245012048 +0100
+@@ -0,0 +1,1049 @@
++/*********************************************************
++ * Copyright (C) 2008-2015 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 "vm_assert.h"
++#include "dynxdr.h"
++#include "rpcChannelInt.h"
++#include "str.h"
++#include "strutil.h"
++#include "vmxrpc.h"
++#include "xdrutil.h"
++#include "rpcin.h"
++#include "debug.h"
++
++/** Internal state of a channel. */
++typedef struct RpcChannelInt {
++ RpcChannel impl;
++ gchar *appName;
++ GHashTable *rpcs;
++ GMainContext *mainCtx;
++ GSource *resetCheck;
++ gpointer appCtx;
++ RpcChannelCallback resetReg;
++ RpcChannelResetCb resetCb;
++ gpointer resetData;
++ gboolean rpcError;
++ guint rpcErrorCount;
++} RpcChannelInt;
++
++/** Max number of times to attempt a channel restart. */
++#define RPCIN_MAX_RESTARTS 60
++
++#define LGPFX "RpcChannel: "
++
++static gboolean
++RpcChannelPing(RpcInData *data);
++
++static RpcChannelCallback gRpcHandlers[] = {
++ { "ping", RpcChannelPing, NULL, NULL, NULL, 0 }
++};
++
++static gboolean gUseBackdoorOnly = FALSE;
++
++/*
++ * Track the vSocket connection failure, so that we can
++ * avoid using vSockets until a channel reset/restart or
++ * the service itself gets restarted.
++ */
++static gboolean gVSocketFailed = FALSE;
++
++/**
++ * Handler for a "ping" message. Does nothing.
++ *
++ * @param[in] data The RPC data.
++ *
++ * @return TRUE.
++ */
++
++static gboolean
++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)
++{
++ RpcChannelInt *chan = _chan;
++
++ RpcChannel_Stop(&chan->impl);
++ /* Clear vSocket channel failure */
++ Debug(LGPFX "Clearing backdoor behavior ...\n");
++ gVSocketFailed = FALSE;
++
++ if (!RpcChannel_Start(&chan->impl)) {
++ Warning("Channel restart failed [%d]\n", chan->rpcErrorCount);
++ if (chan->resetCb != NULL) {
++ chan->resetCb(&chan->impl, 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;
++ RpcChannelInt *chan = _chan;
++
++ /* Check the channel state. */
++ if (chan->rpcError) {
++ GSource *src;
++
++ if (++(chan->rpcErrorCount) > channelTimeoutAttempts) {
++ Warning("Failed to reset channel after %u attempts\n",
++ chan->rpcErrorCount - 1);
++ if (chan->resetCb != NULL) {
++ chan->resetCb(&chan->impl, FALSE, chan->resetData);
++ }
++ goto exit;
++ }
++
++ /* Schedule the channel restart for 1 sec in the future. */
++ Debug(LGPFX "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. */
++ Debug(LGPFX "Channel was reset successfully.\n");
++ chan->rpcErrorCount = 0;
++ Debug(LGPFX "Clearing backdoor behavior ...\n");
++ gVSocketFailed = FALSE;
++
++ if (chan->resetCb != NULL) {
++ chan->resetCb(&chan->impl, 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 gboolean
++RpcChannelReset(RpcInData *data)
++{
++ gchar *msg;
++ RpcChannelInt *chan = data->clientData;
++
++ if (chan->resetCheck == NULL) {
++ chan->resetCheck = g_idle_source_new();
++ g_source_set_priority(chan->resetCheck, G_PRIORITY_HIGH);
++ 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);
++ return RPCIN_SETRETVALSF(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 - 1,
++ 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.
++ */
++
++gboolean
++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;
++}
++
++
++/**
++ * Creates a new RpcChannel without any implementation.
++ *
++ * This is mainly for use of code that is implementing a custom RpcChannel.
++ * Such implementations should provide their own "constructor"-type function
++ * which should then call this function to get an RpcChannel instance. They
++ * should then fill in the function pointers that provide the implementation
++ * for the channel before making the channel available to the callers.
++ *
++ * @return A new RpcChannel instance.
++ */
++
++RpcChannel *
++RpcChannel_Create(void)
++{
++ RpcChannelInt *chan = g_new0(RpcChannelInt, 1);
++ return &chan->impl;
++}
++
++
++/**
++ * 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.
++ */
++
++gboolean
++RpcChannel_Dispatch(RpcInData *data)
++{
++ char *name = NULL;
++ unsigned int index = 0;
++ size_t nameLen;
++ Bool status;
++ RpcChannelCallback *rpc = NULL;
++ RpcChannelInt *chan = data->clientData;
++
++ name = StrUtil_GetNextToken(&index, data->args, " ");
++ if (name == NULL) {
++ Debug(LGPFX "Bad command (null) received.\n");
++ status = RPCIN_SETRETVALS(data, "Bad command", FALSE);
++ goto exit;
++ }
++
++ if (chan->rpcs != NULL) {
++ rpc = g_hash_table_lookup(chan->rpcs, name);
++ }
++
++ if (rpc == NULL) {
++ Debug(LGPFX "Unknown Command '%s': Handler not registered.\n", name);
++ 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);
++ }
++
++ 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;
++ RpcChannelInt *cdata = (RpcChannelInt *) chan;
++
++ if (cdata->impl.funcs != NULL && cdata->impl.funcs->shutdown != NULL) {
++ cdata->impl.funcs->shutdown(chan);
++ }
++
++ RpcChannel_UnregisterCallback(chan, &cdata->resetReg);
++ for (i = 0; i < ARRAYSIZE(gRpcHandlers); i++) {
++ RpcChannel_UnregisterCallback(chan, &gRpcHandlers[i]);
++ }
++
++ if (cdata->rpcs != NULL) {
++ g_hash_table_destroy(cdata->rpcs);
++ cdata->rpcs = NULL;
++ }
++
++ cdata->resetCb = NULL;
++ cdata->resetData = NULL;
++ cdata->appCtx = NULL;
++
++ g_free(cdata->appName);
++ cdata->appName = NULL;
++
++ if (cdata->mainCtx != NULL) {
++ g_main_context_unref(cdata->mainCtx);
++ cdata->mainCtx = NULL;
++ }
++
++ if (cdata->resetCheck != NULL) {
++ g_source_destroy(cdata->resetCheck);
++ cdata->resetCheck = NULL;
++ }
++
++ g_free(cdata);
++ 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)
++{
++ RpcChannelInt *chan = _chan;
++ chan->rpcError = TRUE;
++ /*
++ * XXX: Workaround for PR 935520.
++ * Revert the log call to Warning() after fixing PR 955746.
++ */
++ Debug(LGPFX "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 inbound operations.
++ *
++ * This function must be called before starting the channel if the application
++ * wants to receive messages on the channel. Applications don't need to call it
++ * if only using the outbound functionality.
++ *
++ * @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;
++ RpcChannelInt *cdata = (RpcChannelInt *) chan;
++
++ cdata->appName = g_strdup(appName);
++ cdata->appCtx = appCtx;
++ cdata->mainCtx = g_main_context_ref(mainCtx);
++ cdata->resetCb = resetCb;
++ cdata->resetData = resetData;
++
++ cdata->resetReg.name = "reset";
++ cdata->resetReg.callback = RpcChannelReset;
++ cdata->resetReg.clientData = chan;
++
++ /* Register the callbacks handled by the rpcChannel library. */
++ RpcChannel_RegisterCallback(chan, &cdata->resetReg);
++
++ for (i = 0; i < ARRAYSIZE(gRpcHandlers); i++) {
++ RpcChannel_RegisterCallback(chan, &gRpcHandlers[i]);
++ }
++
++ if (chan->funcs != NULL && chan->funcs->setup != NULL) {
++ chan->funcs->setup(chan, mainCtx, appName, appCtx);
++ } else {
++ chan->mainCtx = g_main_context_ref(mainCtx);
++ chan->in = RpcIn_Construct(mainCtx, RpcChannel_Dispatch, chan);
++ ASSERT(chan->in != NULL);
++ }
++}
++
++
++/**
++ * Sets the non-freeable result of the given RPC context to the given value.
++ * The result should be a NULL-terminated string.
++ *
++ * @param[in] data RPC context.
++ * @param[in] result Result string.
++ * @param[in] retVal Return value of this function.
++ *
++ * @return @a retVal
++ */
++
++gboolean
++RpcChannel_SetRetVals(RpcInData *data,
++ char const *result,
++ gboolean retVal)
++{
++ ASSERT(data);
++
++ /* This cast is safe: data->result will not be freed. */
++ data->result = (char *)result;
++ data->resultLen = strlen(data->result);
++ data->freeResult = FALSE;
++
++ return retVal;
++}
++
++
++/**
++ * Sets the freeable result of the given RPC context to the given value.
++ * The result should be a NULL-terminated string.
++ *
++ * @param[in] data RPC context.
++ * @param[in] result Result string.
++ * @param[in] retVal Return value of this function.
++ *
++ * @return @a retVal
++ */
++
++gboolean
++RpcChannel_SetRetValsF(RpcInData *data,
++ char *result,
++ gboolean retVal)
++{
++ ASSERT(data);
++
++ data->result = result;
++ data->resultLen = strlen(data->result);
++ data->freeResult = TRUE;
++
++ return retVal;
++}
++
++
++/**
++ * 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)
++{
++ RpcChannelInt *cdata = (RpcChannelInt *) chan;
++ ASSERT(rpc->name != NULL && strlen(rpc->name) > 0);
++ ASSERT(rpc->callback);
++ ASSERT(rpc->xdrIn == NULL || rpc->xdrInSize > 0);
++ if (cdata->rpcs == NULL) {
++ cdata->rpcs = g_hash_table_new(g_str_hash, g_str_equal);
++ }
++ if (g_hash_table_lookup(cdata->rpcs, rpc->name) != NULL) {
++ Panic("Trying to overwrite existing RPC registration for %s!\n", rpc->name);
++ }
++ g_hash_table_insert(cdata->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)
++{
++ RpcChannelInt *cdata = (RpcChannelInt *) chan;
++ if (cdata->rpcs != NULL) {
++ g_hash_table_remove(cdata->rpcs, rpc->name);
++ }
++}
++
++
++/**
++ * Force to create backdoor channels only.
++ * This provides a kill-switch to disable vsocket channels if needed.
++ * This needs to be called before RpcChannel_New to take effect.
++ */
++
++void
++RpcChannel_SetBackdoorOnly(void)
++{
++ gUseBackdoorOnly = TRUE;
++ Debug(LGPFX "Using vsocket is disabled.\n");
++}
++
++
++/**
++ * Create an RpcChannel instance using a prefered channel implementation,
++ * currently this is VSockChannel.
++ *
++ * @return RpcChannel
++ */
++
++RpcChannel *
++RpcChannel_New(void)
++{
++ RpcChannel *chan;
++#if (defined(__linux__) && !defined(USERWORLD)) || defined(_WIN32)
++ chan = (gUseBackdoorOnly || gVSocketFailed) ?
++ BackdoorChannel_New() : VSockChannel_New();
++#else
++ chan = BackdoorChannel_New();
++#endif
++ if (chan) {
++ g_mutex_init(&chan->outLock);
++ }
++ return chan;
++}
++
++
++/**
++ * Wrapper for the shutdown function of an RPC channel struct.
++ *
++ * @param[in] chan The RPC channel instance.
++ */
++
++void
++RpcChannel_Shutdown(RpcChannel *chan)
++{
++ if (chan != NULL) {
++ g_static_mutex_free(&chan->outLock);
++ }
++
++ if (chan != NULL && chan->funcs != NULL && chan->funcs->shutdown != NULL) {
++ if (chan->in != NULL) {
++ if (chan->inStarted) {
++ RpcIn_stop(chan->in);
++ }
++ chan->inStarted = FALSE;
++ RpcIn_Destruct(chan->in);
++ chan->in = NULL;
++ } else {
++ ASSERT(!chan->inStarted);
++ }
++
++ if (chan->mainCtx != NULL) {
++ g_main_context_unref(chan->mainCtx);
++ }
++ chan->funcs->shutdown(chan);
++ }
++}
++
++
++/**
++ * Start an RPC channel. We may fallback to backdoor channel when other type
++ * of channel fails to start.
++ *
++ * @param[in] chan The RPC channel instance.
++ *
++ * @return TRUE on success.
++ */
++
++gboolean
++RpcChannel_Start(RpcChannel *chan)
++{
++ gboolean ok;
++ const RpcChannelFuncs *funcs;
++
++ if (chan == NULL || chan->funcs == NULL || chan->funcs->start == NULL) {
++ return FALSE;
++ }
++
++ if (chan->outStarted) {
++ /* Already started. Make sure both channels are in sync and return. */
++ ASSERT(chan->in == NULL || chan->inStarted);
++ return TRUE;
++ }
++
++ if (chan->in != NULL && !chan->inStarted) {
++ ok = RpcIn_start(chan->in, RPCIN_MAX_DELAY, RpcChannel_Error, chan);
++ chan->inStarted = ok;
++ }
++
++ funcs = chan->funcs;
++ ok = funcs->start(chan);
++
++ if (!ok && funcs->onStartErr != NULL) {
++ Debug(LGPFX "Fallback to backdoor ...\n");
++ funcs->onStartErr(chan);
++ ok = BackdoorChannel_Fallback(chan);
++ /*
++ * As vSocket is not available, we stick the backdoor
++ * behavior until the channel is reset/restarted.
++ */
++ Debug(LGPFX "Sticking backdoor behavior ...\n");
++ gVSocketFailed = TRUE;
++ }
++
++ return ok;
++}
++
++
++/**
++ * Wrapper for the stop function of an RPC channel struct.
++ *
++ * @param[in] chan The RPC channel instance.
++ */
++
++void
++RpcChannel_Stop(RpcChannel *chan)
++{
++ g_return_if_fail(chan != NULL);
++ g_return_if_fail(chan->funcs != NULL);
++ g_return_if_fail(chan->funcs->stop != NULL);
++
++ g_static_mutex_lock(&chan->outLock);
++ chan->funcs->stop(chan);
++
++ if (chan->in != NULL) {
++ if (chan->inStarted) {
++ RpcIn_stop(chan->in);
++ }
++ chan->inStarted = FALSE;
++ } else {
++ ASSERT(!chan->inStarted);
++ }
++ g_static_mutex_unlock(&chan->outLock);
++}
++
++
++/**
++ * Wrapper for get channel type function of an RPC channel struct.
++ *
++ * @param[in] chan The RPC channel instance.
++ */
++
++RpcChannelType
++RpcChannel_GetType(RpcChannel *chan)
++{
++ if (chan == NULL || chan->funcs == NULL || chan->funcs->getType == NULL) {
++ return RPCCHANNEL_TYPE_INACTIVE;
++ }
++ return chan->funcs->getType(chan);
++}
++
++
++/**
++ * Free the allocated memory for the results from RpcChannel_Send* calls.
++ *
++ * @param[in] ptr result from RpcChannel_Send* calls.
++ *
++ * @return none
++ */
++
++void
++RpcChannel_Free(void *ptr)
++{
++ free(ptr);
++}
++
++
++/**
++ * Send function of an RPC channel struct. Retry once if it fails for
++ * non-backdoor Channels. Backdoor channel already tries inside. A second try
++ * may create a different type of channel.
++ *
++ * @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 (should be freed by
++ * calling RpcChannel_Free).
++ * @param[out] resultLen Number of bytes in response.
++ *
++ * @return The status from the remote end (TRUE if call was successful).
++ */
++
++gboolean
++RpcChannel_Send(RpcChannel *chan,
++ char const *data,
++ size_t dataLen,
++ char **result,
++ size_t *resultLen)
++{
++ gboolean ok;
++ char *res = NULL;
++ size_t resLen = 0;
++ const RpcChannelFuncs *funcs;
++
++ Debug(LGPFX "Sending: %"FMTSZ"u bytes\n", dataLen);
++
++ ASSERT(chan && chan->funcs);
++
++ g_static_mutex_lock(&chan->outLock);
++
++ funcs = chan->funcs;
++ ASSERT(funcs->send);
++
++ if (result != NULL) {
++ *result = NULL;
++ }
++ if (resultLen != NULL) {
++ *resultLen = 0;
++ }
++
++ ok = funcs->send(chan, data, dataLen, &res, &resLen);
++
++ if (!ok && (funcs->getType(chan) != RPCCHANNEL_TYPE_BKDOOR) &&
++ (funcs->stopRpcOut != NULL)) {
++
++ free(res);
++ res = NULL;
++ resLen = 0;
++
++ /* retry once */
++ Debug(LGPFX "Stop RpcOut channel and try to send again ...\n");
++ funcs->stopRpcOut(chan);
++ if (RpcChannel_Start(chan)) {
++ /* The channel may get switched from vsocket to backdoor */
++ funcs = chan->funcs;
++ ASSERT(funcs->send);
++ ok = funcs->send(chan, data, dataLen, &res, &resLen);
++ goto done;
++ }
++
++ ok = FALSE;
++ goto exit;
++ }
++
++done:
++ if (ok) {
++ Debug(LGPFX "Recved %"FMTSZ"u bytes\n", resLen);
++ }
++
++ if (result != NULL) {
++ *result = res;
++ }
++ if (resultLen != NULL) {
++ *resultLen = resLen;
++ }
++
++exit:
++ g_static_mutex_unlock(&chan->outLock);
++ return ok;
++}
++
++
++/**
++ * Open/close RpcChannel each time for sending a Rpc message, this is a wrapper
++ * for RpcChannel APIs.
++ *
++ * @param[in] data request data
++ * @param[in] dataLen data length
++ * @param[in] result reply, should be freed by calling RpcChannel_Free.
++ * @param[in] resultLen reply length
++
++ * @returns TRUE on success.
++ */
++
++gboolean
++RpcChannel_SendOneRaw(const char *data,
++ size_t dataLen,
++ char **result,
++ size_t *resultLen)
++{
++ RpcChannel *chan;
++ gboolean status;
++
++ status = FALSE;
++
++ chan = RpcChannel_New();
++ if (chan == NULL) {
++ if (result != NULL) {
++ *result = Util_SafeStrdup("RpcChannel: Unable to create "
++ "the RpcChannel object");
++ if (resultLen != NULL) {
++ *resultLen = strlen(*result);
++ }
++ }
++ goto sent;
++ } else if (!RpcChannel_Start(chan)) {
++ if (result != NULL) {
++ *result = Util_SafeStrdup("RpcChannel: Unable to open the "
++ "communication channel");
++ if (resultLen != NULL) {
++ *resultLen = strlen(*result);
++ }
++ }
++ goto sent;
++ } else if (!RpcChannel_Send(chan, data, dataLen, result, resultLen)) {
++ /* We already have the description of the error */
++ goto sent;
++ }
++
++ status = TRUE;
++
++sent:
++ Debug(LGPFX "Request %s: reqlen=%"FMTSZ"u, replyLen=%"FMTSZ"u\n",
++ status ? "OK" : "FAILED", dataLen, resultLen ? *resultLen : 0);
++ if (chan) {
++ RpcChannel_Stop(chan);
++ RpcChannel_Destroy(chan);
++ }
++
++ return status;
++}
++
++
++/**
++ * Open/close RpcChannel each time for sending a Rpc message, this is a wrapper
++ * for RpcChannel APIs.
++ *
++ * @param[out] reply reply, should be freed by calling RpcChannel_Free.
++ * @param[out] repLen reply length
++ * @param[in] reqFmt request data
++ * @param[in] ... optional arguments depending on reqFmt.
++
++ * @returns TRUE on success.
++ */
++
++gboolean
++RpcChannel_SendOne(char **reply,
++ size_t *repLen,
++ char const *reqFmt,
++ ...)
++{
++ va_list args;
++ gboolean 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) {
++ goto error;
++ }
++
++ /*
++ * 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 (request[reqLen - 1] != ' ') {
++ 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) {
++ goto error;
++ }
++ }
++
++ status = RpcChannel_SendOneRaw(request, reqLen, reply, repLen);
++
++ free(request);
++
++ return status;
++
++error:
++ if (reply) {
++ *reply = NULL;
++ }
++
++ if (repLen) {
++ *repLen = 0;
++ }
++ return FALSE;
++}
+diff -Naur a/services/vmtoolsd/mainLoop.c b/services/vmtoolsd/mainLoop.c
+--- a/services/vmtoolsd/mainLoop.c 2015-11-24 07:59:42.000000000 +0100
++++ b/services/vmtoolsd/mainLoop.c 2016-01-15 11:52:19.332545573 +0100
+@@ -384,9 +384,7 @@
+ GMainContext *gctx;
+ ToolsServiceProperty ctxProp = { TOOLS_CORE_PROP_CTX };
+
+- if (!g_thread_supported()) {
+- g_thread_init(NULL);
+- }
++ g_thread_init(NULL);
+
+ /*
+ * Useful for debugging purposes. Log the vesion and build information.
+diff -Naur a/services/plugins/vmbackup/stateMachine.c b/services/plugins/vmbackup/stateMachine.c
+--- a/services/plugins/vmbackup/stateMachine.c 2015-11-24 07:59:42.000000000 +0100
++++ b/services/plugins/vmbackup/stateMachine.c 2016-01-15 11:58:56.387357439 +0100
+@@ -279,12 +279,12 @@
+ g_source_unref(gBackupState->abortTimer);
+ }
+
+- g_static_mutex_lock(&gBackupState->opLock);
++ g_mutex_lock(&gBackupState->opLock);
+ if (gBackupState->currentOp != NULL) {
+ VmBackup_Cancel(gBackupState->currentOp);
+ VmBackup_Release(gBackupState->currentOp);
+ }
+- g_static_mutex_unlock(&gBackupState->opLock);
++ g_mutex_unlock(&gBackupState->opLock);
+
+ VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_DONE, VMBACKUP_SUCCESS, "");
+
+@@ -299,7 +299,7 @@
+ }
+
+ gBackupState->provider->release(gBackupState->provider);
+- g_static_mutex_free(&gBackupState->opLock);
++ g_mutex_free(&gBackupState->opLock);
+ g_free(gBackupState->scriptArg);
+ g_free(gBackupState->volumes);
+ g_free(gBackupState->snapshots);
+@@ -418,13 +418,13 @@
+ if (gBackupState->machineState != VMBACKUP_MSTATE_SCRIPT_ERROR &&
+ gBackupState->machineState != VMBACKUP_MSTATE_SYNC_ERROR) {
+ /* Mark the current operation as cancelled. */
+- g_static_mutex_lock(&gBackupState->opLock);
++ g_mutex_lock(&gBackupState->opLock);
+ if (gBackupState->currentOp != NULL) {
+ VmBackup_Cancel(gBackupState->currentOp);
+ VmBackup_Release(gBackupState->currentOp);
+ gBackupState->currentOp = NULL;
+ }
+- g_static_mutex_unlock(&gBackupState->opLock);
++ g_mutex_unlock(&gBackupState->opLock);
+
+ VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ABORT,
+ VMBACKUP_REMOTE_ABORT,
+@@ -478,32 +478,32 @@
+ g_source_unref(gBackupState->timerEvent);
+ gBackupState->timerEvent = NULL;
+
+- g_static_mutex_lock(&gBackupState->opLock);
++ g_mutex_lock(&gBackupState->opLock);
+ if (gBackupState->currentOp != NULL) {
+ g_debug("VmBackupAsyncCallback: checking %s\n", gBackupState->currentOpName);
+ status = VmBackup_QueryStatus(gBackupState->currentOp);
+ }
+- g_static_mutex_unlock(&gBackupState->opLock);
++ g_mutex_unlock(&gBackupState->opLock);
+
+ switch (status) {
+ case VMBACKUP_STATUS_PENDING:
+ goto exit;
+
+ case VMBACKUP_STATUS_FINISHED:
+- g_static_mutex_lock(&gBackupState->opLock);
++ g_mutex_lock(&gBackupState->opLock);
+ if (gBackupState->currentOpName != NULL) {
+ g_debug("Async request '%s' completed\n", gBackupState->currentOpName);
+ VmBackup_Release(gBackupState->currentOp);
+ gBackupState->currentOpName = NULL;
+ }
+ gBackupState->currentOp = NULL;
+- g_static_mutex_unlock(&gBackupState->opLock);
++ g_mutex_unlock(&gBackupState->opLock);
+ break;
+
+ default:
+ {
+ gchar *msg;
+- g_static_mutex_lock(&gBackupState->opLock);
++ g_mutex_lock(&gBackupState->opLock);
+ if (gBackupState->errorMsg != NULL) {
+ msg = g_strdup_printf("'%s' operation failed: %s",
+ gBackupState->currentOpName,
+@@ -519,7 +519,7 @@
+
+ VmBackup_Release(gBackupState->currentOp);
+ gBackupState->currentOp = NULL;
+- g_static_mutex_unlock(&gBackupState->opLock);
++ g_mutex_unlock(&gBackupState->opLock);
+ VmBackupOnError();
+ goto exit;
+ }
+@@ -534,12 +534,12 @@
+ gBackupState->callback = NULL;
+
+ if (cb(gBackupState)) {
+- g_static_mutex_lock(&gBackupState->opLock);
++ g_mutex_lock(&gBackupState->opLock);
+ if (gBackupState->currentOp != NULL || gBackupState->forceRequeue) {
+- g_static_mutex_unlock(&gBackupState->opLock);
++ g_mutex_unlock(&gBackupState->opLock);
+ goto exit;
+ }
+- g_static_mutex_unlock(&gBackupState->opLock);
++ g_mutex_unlock(&gBackupState->opLock);
+ } else {
+ VmBackupOnError();
+ goto exit;
+@@ -823,7 +823,7 @@
+ gBackupState->freezeStatus = VMBACKUP_FREEZE_FINISHED;
+ gBackupState->provider = provider;
+ gBackupState->needsPriv = FALSE;
+- g_static_mutex_init(&gBackupState->opLock);
++ g_mutex_init(&gBackupState->opLock);
+ gBackupState->enableNullDriver = VmBackupConfigGetBoolean(ctx->config,
+ "enableNullDriver",
+ TRUE);
+diff -Naur a/services/plugins/vmbackup/vmBackupInt.h b/services/plugins/vmbackup/vmBackupInt.h
+--- a/services/plugins/vmbackup/vmBackupInt.h 2015-11-24 07:59:42.000000000 +0100
++++ b/services/plugins/vmbackup/vmBackupInt.h 2016-01-15 11:55:41.944020643 +0100
+@@ -98,7 +98,7 @@
+ ToolsAppCtx *ctx;
+ VmBackupOp *currentOp;
+ const char *currentOpName;
+- GStaticMutex opLock; // See note above
++ GMutex opLock; // See note above
+ char *volumes;
+ char *snapshots;
+ guint pollPeriod;
+@@ -171,14 +171,14 @@
+ ASSERT(state->currentOp == NULL);
+ ASSERT(currentOpName != NULL);
+
+- g_static_mutex_lock(&state->opLock);
++ g_mutex_lock(&state->opLock);
+
+ state->currentOp = op;
+ state->callback = callback;
+ state->currentOpName = currentOpName;
+ state->forceRequeue = (callback != NULL && op == NULL);
+
+- g_static_mutex_unlock(&state->opLock);
++ g_mutex_unlock(&state->opLock);
+
+ return (op != NULL);
+ }
diff --git a/packages/sysutils/open-vm-tools/system.d/open-vm-tools.service b/packages/sysutils/open-vm-tools/system.d/open-vm-tools.service
new file mode 100644
index 0000000000..7117805fe2
--- /dev/null
+++ b/packages/sysutils/open-vm-tools/system.d/open-vm-tools.service
@@ -0,0 +1,15 @@
+[Unit]
+Description=OpenVMTools Server
+After=xorg-server.service
+
+ConditionPathExists=/usr/bin/vmware-checkvm
+ConditionPathExists=/usr/bin/vmtoolsd
+
+[Service]
+Type=forking
+PIDFile=/var/run/vmtoolsd.pid
+ExecStart=-/bin/sh -c '/usr/bin/vmware-checkvm && /usr/bin/vmtoolsd --background /var/run/vmtoolsd.pid'
+ExecReload=/bin/kill -HUP $MAINPID
+
+[Install]
+WantedBy=graphical.target