From faabe3f5a62807dc8353d7c300532cf73a33c255 Mon Sep 17 00:00:00 2001 From: Lukas Rusak Date: Fri, 15 Jan 2016 23:32:59 +0100 Subject: [PATCH] open-vm-tools: initial import --- .../modules-load.d/open-vm-tools.conf | 2 + packages/sysutils/open-vm-tools/package.mk | 74 + .../open-vm-tools-0001_glib-static.patch | 1507 +++++++++++++++++ .../system.d/open-vm-tools.service | 15 + 4 files changed, 1598 insertions(+) create mode 100644 packages/sysutils/open-vm-tools/modules-load.d/open-vm-tools.conf create mode 100644 packages/sysutils/open-vm-tools/package.mk create mode 100644 packages/sysutils/open-vm-tools/patches/open-vm-tools-0001_glib-static.patch create mode 100644 packages/sysutils/open-vm-tools/system.d/open-vm-tools.service 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