Staging source files and changes for a future feature.
################################################################################
-### Copyright (C) 2007-2020 VMware, Inc. All rights reserved.
+### Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
###
### Top-level Makefile for building the VMware OSS Tools.
###
SUBDIRS =
SUBDIRS += lib
+if LINUX
+ SUBDIRS += libguestStoreClient
+endif
SUBDIRS += libvmtools
SUBDIRS += libhgfs
SUBDIRS += hgfsclient
lib/glibUtils/Makefile \
lib/guestApp/Makefile \
lib/guestRpc/Makefile \
+ lib/guestStoreClientHelper/Makefile \
lib/hgfs/Makefile \
lib/hgfsBd/Makefile \
lib/hgfsHelper/Makefile \
services/plugins/desktopEvents/Makefile \
services/plugins/dndcp/Makefile \
services/plugins/guestInfo/Makefile \
+ services/plugins/guestStore/Makefile \
services/plugins/hgfsServer/Makefile \
services/plugins/powerOps/Makefile \
services/plugins/resolutionSet/Makefile \
libDeployPkg/Makefile \
libDeployPkg/libDeployPkg.pc \
libhgfs/Makefile \
+ libguestStoreClient/Makefile \
libvmtools/Makefile \
xferlogs/Makefile \
modules/Makefile \
################################################################################
-### Copyright (C) 2007-2016 VMware, Inc. All rights reserved.
+### Copyright (c) 2007-2016,2020 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
SUBDIRS += foundryMsg
SUBDIRS += glibUtils
SUBDIRS += guestApp
+if LINUX
+ SUBDIRS += guestStoreClientHelper
+endif
SUBDIRS += hgfs
SUBDIRS += hgfsBd
SUBDIRS += hgfsHelper
--- /dev/null
+################################################################################
+### Copyright (c) 2020 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 = libGuestStoreClientHelper.la
+
+libGuestStoreClientHelper_la_SOURCES =
+libGuestStoreClientHelper_la_SOURCES += guestStoreClient.c
+
+libGuestStoreClientHelper_la_CPPFLAGS =
+libGuestStoreClientHelper_la_CPPFLAGS += @GLIB2_CPPFLAGS@
\ No newline at end of file
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2020 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.
+ *
+ *********************************************************/
+
+/*
+ * guestStoreClient.c --
+ *
+ * Wrapper functions to load/unload and get content from GuestStore.
+ */
+
+#define G_LOG_DOMAIN "guestStoreClient"
+
+#include <glib-object.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#if defined(_WIN32)
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <dlfcn.h>
+#endif
+
+#include "vm_assert.h"
+#include "vm_basic_defs.h"
+#include "guestStoreClient.h"
+
+
+#if defined(_WIN32)
+#define GUESTSTORE_CLIENTLIB_DLL WSTR("guestStoreClient.dll")
+#else
+#define GUESTSTORE_CLIENTLIB_DLL "libguestStoreClient.so"
+#endif
+
+/*
+ * Track whether the library has been initialized or not.
+ */
+static gboolean gsClientInit = FALSE;
+
+/*
+ * Module handle of GuestStore client library.
+ */
+#if defined(_WIN32)
+static HMODULE gsClientLibModule = NULL;
+#else
+static void *gsClientLibModule = NULL;
+#endif
+
+
+/*
+ * Function pointer types for GuestStore client library exports.
+ */
+typedef GuestStoreLibError (*GuestStoreLibInit)(void);
+typedef GuestStoreLibError (*GuestStoreLibDeInit)(void);
+typedef GuestStoreLibError (*GuestStoreLibGetContent)(const char* contentPath,
+ const char* outputPath,
+ GuestStore_Logger logger,
+ GuestStore_Panic panic,
+ GuestStore_GetContentCallback getContentCb,
+ void* clientData);
+
+/*
+ * Function pointer definitions for GuestStore client library exports.
+ */
+
+static GuestStoreLibInit GuestStoreLib_Init;
+static GuestStoreLibDeInit GuestStoreLib_DeInit;
+static GuestStoreLibGetContent GuestStoreLib_GetContent;
+
+/*
+ * Macro to get the export function address from GuestStore client library.
+ */
+#if defined(_WIN32)
+
+#define GET_GUESTSTORELIB_FUNC_ADDR(funcname) \
+ do { \
+ (FARPROC) XXCONC(GuestStoreLib_,funcname) = GetProcAddress(gsClientLibModule, \
+ "GuestStore_" #funcname); \
+ if (XXCONC(GuestStoreLib_,funcname) == NULL) { \
+ error = GetLastError(); \
+ g_critical("GetProcAddress failed for \'%s\': error=%u.\n", \
+ "GuestStore_" #funcname, error); \
+ return FALSE; \
+ } \
+ } while (0)
+
+#else
+
+#define GET_GUESTSTORELIB_FUNC_ADDR(funcname) \
+ do { \
+ dlerror(); \
+ *(void **)(&XXCONC(GuestStoreLib_,funcname)) = dlsym(gsClientLibModule, \
+ "GuestStore_" #funcname); \
+ if ((dlErrStr = dlerror()) != NULL) { \
+ g_critical("dlsym failed for \'%s\': %s\n", \
+ "GuestStore_" #funcname, dlErrStr); \
+ return FALSE; \
+ } \
+ } while (0)
+
+#endif
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreGetLibExportFunctions --
+ *
+ * Get the export function addresses from GuestStore client library.
+ *
+ * Results:
+ * TRUE on success.
+ * FALSE on failure.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+GuestStoreGetLibExportFunctions(void)
+{
+
+#if defined(_WIN32)
+ DWORD error;
+
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ gsClientLibModule = LoadLibraryW(GUESTSTORE_CLIENTLIB_DLL);
+ if (gsClientLibModule == NULL) {
+ error = GetLastError();
+ g_critical("%s: LoadLibrary failed: error=%u.\n", __FUNCTION__, error);
+ return FALSE;
+ }
+#else
+ char const *dlErrStr;
+
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ gsClientLibModule = dlopen(GUESTSTORE_CLIENTLIB_DLL, RTLD_NOW);
+ if (gsClientLibModule == NULL) {
+ dlErrStr = dlerror();
+ g_critical("%s: dlopen failed: %s\n", __FUNCTION__, dlErrStr);
+ return FALSE;
+ }
+#endif
+
+ GET_GUESTSTORELIB_FUNC_ADDR(Init); // For GuestStore_Init
+ GET_GUESTSTORELIB_FUNC_ADDR(GetContent); // For GuestStore_GetContent
+ GET_GUESTSTORELIB_FUNC_ADDR(DeInit); // For GuestStore_DeInit
+
+ return TRUE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreClientLogger --
+ *
+ * Log messages from GuestStore client library.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+GuestStoreClientLogger(GuestStoreLibLogLevel level, // IN
+ const char *message, // IN
+ void *clientData) // IN
+{
+ switch (level) {
+ case GSLIBLOGLEVEL_ERROR:
+ g_critical("%s: Error: %s\n", __FUNCTION__, message);
+ break;
+
+ case GSLIBLOGLEVEL_WARNING:
+ g_warning("%s: Warning: %s\n", __FUNCTION__, message);
+ break;
+
+ case GSLIBLOGLEVEL_INFO:
+ g_info("%s: Info: %s\n", __FUNCTION__, message);
+ break;
+
+ case GSLIBLOGLEVEL_DEBUG:
+ g_debug("%s: Debug: %s\n", __FUNCTION__, message);
+ break;
+
+ default:
+ ASSERT(0);
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreClientPanic --
+ *
+ * Panic handler for GuestStore client library.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * Process crashes?
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+GuestStoreClientPanic(const char *message, // IN
+ void *clientData) // IN
+{
+ g_critical("%s: %s\n", __FUNCTION__, message);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreClient_Init --
+ *
+ * Initialize the guest store client library access
+ *
+ * Results:
+ * TRUE on success., FALSE otherwise
+ *
+ * Error code from GuestStore client library or
+ * general process error exit code.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+gboolean
+GuestStoreClient_Init(void)
+{
+ GuestStoreLibError libErr;
+
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ if (!GuestStoreGetLibExportFunctions()) {
+ goto exit;
+ }
+
+ libErr = GuestStoreLib_Init();
+ if (libErr != GSLIBERR_SUCCESS) {
+ g_critical("%s: GuestStoreLib_Init failed: error=%d.\n",
+ __FUNCTION__, libErr);
+ goto exit;
+ }
+ gsClientInit = TRUE;
+
+exit:
+ g_debug("%s: Exit -> %d.\n", __FUNCTION__, gsClientInit);
+ return gsClientInit;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreClient_DeInit --
+ *
+ * Deinitialize the guest store client library access
+ *
+ * Results:
+ * TRUE on success, FALSE otherwise
+ *
+ * Error code from GuestStore client library or
+ * general process error exit code.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+gboolean
+GuestStoreClient_DeInit(void)
+{
+ GuestStoreLibError libErr;
+
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ if (!gsClientInit) {
+ return gsClientInit;
+ }
+
+ libErr = GuestStoreLib_DeInit();
+ if (libErr != GSLIBERR_SUCCESS) {
+ g_critical("%s: GuestStore_DeInit failed: error=%d.\n",
+ __FUNCTION__, libErr);
+ }
+
+ if (gsClientLibModule != NULL) {
+#if defined(_WIN32)
+ if (!FreeLibrary(gsClientLibModule)) {
+ g_critical("%s: FreeLibrary failed: error=%d.\n",
+ __FUNCTION__, GetLastError());
+ }
+#else
+ if (dlclose(gsClientLibModule) != 0) {
+ g_critical("%s: dlclose failed with error: %s\n",
+ __FUNCTION__, dlerror());
+ }
+#endif
+
+ gsClientLibModule = NULL;
+ }
+
+ g_debug("Exiting %s.\n", __FUNCTION__);
+
+ gsClientInit = FALSE;
+
+ return TRUE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreClient_GetContent --
+ *
+ * Handle and parse gueststore command.
+ *
+ * Results:
+ * Error code from GuestStore client library or
+ * general process error exit code.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+GuestStoreClientError
+GuestStoreClient_GetContent(const char *contentPath, // IN: content file path
+ const char *outputPath, // IN: output file path
+ GuestStoreClient_GetContentCb getContentCb, // IN: OPTIONAL callback
+ void *clientCbData) // IN: OPTIONAL callback data
+{
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ if (!gsClientInit) {
+ return GSLIBERR_NOT_INITIALIZED;
+ }
+
+ return GuestStoreLib_GetContent(contentPath, outputPath,
+ GuestStoreClientLogger,
+ GuestStoreClientPanic,
+ getContentCb, clientCbData);
+}
\ No newline at end of file
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2020 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.
+ *
+ *********************************************************/
+
+/*
+ * guestStoreClient.h --
+ *
+ * Wrapper functions to load the GuestStore libraries.
+ */
+
+#ifndef _GUEST_STORE_CLIENT_H_
+#define _GUEST_STORE_CLIENT_H_
+
+#include "vmware/tools/guestStoreClientLib.h"
+
+typedef GuestStoreLibError GuestStoreClientError;
+
+/*
+ * Caller provided callback to get total content size in bytes and so far
+ * received bytes. Return FALSE to cancel content download.
+ */
+typedef GuestStore_GetContentCallback GuestStoreClient_GetContentCb;
+
+gboolean
+GuestStoreClient_Init(void);
+
+gboolean
+GuestStoreClient_DeInit(void);
+
+GuestStoreClientError
+GuestStoreClient_GetContent(const char *contentPath,
+ const char *outputPath,
+ GuestStoreClient_GetContentCb getContentCb,
+ void *clientCbData);
+
+#endif /* _GUEST_STORE_CLIENT_H_ */
\ No newline at end of file
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2019-2020 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.
+ *
+ *********************************************************/
+
+/*
+ * guestStoreConst.h --
+ *
+ * GuestStore common constant definitions.
+ */
+
+#ifndef _GUESTSTORE_CONST_H_
+#define _GUESTSTORE_CONST_H_
+
+/*
+ * GuestStore maximum content path length, set it
+ * to be less than USERLINUX_PATH_MAX.
+ */
+#define GUESTSTORE_CONTENT_PATH_MAX 1024
+
+/*
+ * Buffer size to handle GuestStore content request. The request is
+ * a content path encoded in HTTP protocol or DataMap.
+ */
+#define GUESTSTORE_REQUEST_BUFFER_SIZE (1024 * 4)
+
+/*
+ * Buffer size to receive and forward GuestStore content bytes, set to
+ * the maximum size of an IP packet.
+ */
+#define GUESTSTORE_RESPONSE_BUFFER_SIZE (1024 * 64)
+
+/*
+ * GuestStore vmx to guest connection pending timeout value.
+ */
+#define GUESTSTORE_VMX_TO_GUEST_CONN_TIMEOUT 5 // seconds
+
+/*
+ * GuestStore default connection inactivity timeout value. This value shall
+ * be greater than gstored timeout value which is currently 60 seconds.
+ */
+#define GUESTSTORE_DEFAULT_CONN_TIMEOUT 900 // seconds
+
+
+/*
+ * NOTE: changing the following IDs may break data map encoding compatibility.
+ */
+
+/* Tools to VMX field IDs */
+enum {
+ GUESTSTORE_REQ_FLD_CMD = 1,
+ GUESTSTORE_REQ_FLD_PATH = 2,
+ GUESTSTORE_REQ_FLD_MAX
+};
+
+/* Command Types */
+enum {
+ GUESTSTORE_REQ_CMD_GET = 1,
+ GUESTSTORE_REQ_CMD_CLOSE = 2,
+};
+
+/* VMX to tools field IDs */
+enum {
+ GUESTSTORE_RES_FLD_ERROR_CODE = 1,
+ GUESTSTORE_RES_FLD_CONTENT_SIZE = 2,
+};
+
+#endif /* _GUESTSTORE_CONST_H_ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2019 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.
+ *
+ *********************************************************/
+
+/*
+ * guestStoreDefs.h --
+ * Common definitions for VMware Tools guestStore plugin and client library.
+ */
+
+#ifndef __GUESTSTOREDEFS_H__
+#define __GUESTSTOREDEFS_H__
+
+#include "vm_basic_defs.h"
+
+/*
+ * GuestStore client connection definitions.
+ */
+#ifdef _WIN32
+#define GUESTSTORE_LOOPBACK_PORT_MIN 7332
+#define GUESTSTORE_LOOPBACK_PORT_MAX 7342
+#else
+#define GUESTSTORE_PIPE_DIR "/var/run/vmware"
+#define GUESTSTORE_PIPE_NAME GUESTSTORE_PIPE_DIR "/guestStorePipe"
+#endif
+
+
+/*
+ * HTTP definitions.
+ */
+#define HTTP_VER "HTTP/1.1"
+
+#define HTTP_LINE_END "\r\n"
+
+#define HTTP_HEADER_END HTTP_LINE_END HTTP_LINE_END
+#define HTTP_HEADER_END_LEN (sizeof HTTP_HEADER_END - 1)
+
+#define HTTP_REQ_METHOD_GET "GET"
+
+#define HTTP_STATUS_CODE_OK 200
+#define HTTP_STATUS_CODE_FORBIDDEN 403
+#define HTTP_STATUS_CODE_NOT_FOUND 404
+
+#define HTTP_RES_OK_LINE HTTP_VER " " \
+ XSTR(HTTP_STATUS_CODE_OK) " OK" HTTP_LINE_END
+#define HTTP_RES_FORBIDDEN_LINE HTTP_VER " " \
+ XSTR(HTTP_STATUS_CODE_FORBIDDEN) " Forbidden" HTTP_LINE_END
+#define HTTP_RES_NOT_FOUND_LINE HTTP_VER " " \
+ XSTR(HTTP_STATUS_CODE_NOT_FOUND) " Not Found" HTTP_LINE_END
+
+#define CONTENT_LENGTH_HEADER "Content-Length: "
+#define CONTENT_LENGTH_HEADER_LEN ((int)(sizeof(CONTENT_LENGTH_HEADER) - 1))
+
+#define HTTP_RES_COMMON_HEADERS "Date: %s" HTTP_LINE_END \
+ "Server: VMGuestStore" HTTP_LINE_END \
+ "Accept-Ranges: bytes" HTTP_LINE_END \
+ CONTENT_LENGTH_HEADER "%" FMT64 "d" HTTP_LINE_END \
+ "Content-Type: application/octet-stream" HTTP_LINE_END \
+ "Connection: close" HTTP_LINE_END \
+ HTTP_LINE_END
+
+#define HTTP_RES_OK HTTP_RES_OK_LINE HTTP_RES_COMMON_HEADERS
+#define HTTP_RES_FORBIDDEN HTTP_RES_FORBIDDEN_LINE HTTP_RES_COMMON_HEADERS
+#define HTTP_RES_NOT_FOUND HTTP_RES_NOT_FOUND_LINE HTTP_RES_COMMON_HEADERS
+
+#endif /* __GUESTSTOREDEFS_H__ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2020 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 _GUESTSTORE_H_
+#define _GUESTSTORE_H_
+
+/**
+ * @file guestStore.h
+ *
+ * Public interface for the GuestStore plugin.
+ *
+ * @addtogroup vmtools_plugins
+ * @{
+ */
+
+#include <glib-object.h>
+#include "vmware/tools/plugin.h"
+
+/**
+ * Signal sent when GuestStore is enabled or disabled.
+ *
+ * @param[in] src The source object.
+ * @param[in] enabled TRUE if VMX GuestStore access is enabled, FALSE otherwise.
+ * @param[in] data Client data.
+ */
+#define TOOLS_CORE_SIG_GUESTSTORE_STATE "tcs_gueststore_state"
+
+/*
+ * Property name of the guestStore plugin in the tools application context
+ * service object.
+ */
+#define TOOLS_PLUGIN_SVC_PROP_GUESTSTORE "tps_prop_gueststore"
+
+/**
+ * @brief Type of the public interface of the guestStore plugin.
+ *
+ * This struct is published in the tools application context service object's
+ * TOOLS_PLUGIN_SVC_PROP_GUESTSTORE property.
+ */
+typedef struct ToolsPluginSvcGuestStore {
+ void (*shutdown)(void);
+} ToolsPluginSvcGuestStore;
+
+
+/*
+ ******************************************************************************
+ * ToolsPluginSvcGuestStore_Shutdown -- */ /**
+ *
+ * @brief Shuts down guestStore plugin.
+ *
+ * To avoid possible deadlock at vmtoolsd shutdown time, guestStore plugin
+ * needs to be shut down before tools core thread pool. This function provides
+ * a special way to shut down guestStore plugin other than regular in-plugin
+ * TOOLS_CORE_SIG_SHUTDOWN signal handler.
+ *
+ * @param[in] ctx The app context
+ *
+ ******************************************************************************
+ */
+
+static inline void
+ToolsPluginSvcGuestStore_Shutdown(ToolsAppCtx *ctx) // IN
+{
+ ToolsPluginSvcGuestStore *svcGuestStore = NULL;
+ g_object_get(ctx->serviceObj, TOOLS_PLUGIN_SVC_PROP_GUESTSTORE,
+ &svcGuestStore, NULL);
+ if (svcGuestStore != NULL && svcGuestStore->shutdown != NULL) {
+ svcGuestStore->shutdown();
+ }
+}
+
+/** @} */
+
+#endif /* _GUESTSTORE_H_ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2019 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.
+ *
+ *********************************************************/
+
+/*
+ * guestStoreClientLib.h --
+ * Definitions for VMware Tools guestStore client library.
+ */
+
+#ifndef __GUESTSTORECLIENTLIB_H__
+#define __GUESTSTORECLIENTLIB_H__
+
+#include "vm_basic_types.h"
+
+#define GUESTSTORE_LIB_ERR_LIST \
+ GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_SUCCESS = 0, \
+ gsliberr.success, \
+ "Success") \
+ GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_GENERIC, \
+ gsliberr.generic, \
+ "Generic error") \
+ GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_TLS, \
+ gsliberr.tls, \
+ "TLS error") \
+ GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_NOT_INITIALIZED, \
+ gsliberr.not.initialized, \
+ "Not initialized") \
+ GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_INVALID_PARAMETER, \
+ gsliberr.invalid.parameter, \
+ "Invalid parameter") \
+ GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_NOT_ENOUGH_MEMORY, \
+ gsliberr.not.enough.memory, \
+ "Not enough memory") \
+ GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_CREATE_OUTPUT_FILE, \
+ gsliberr.create.output.file, \
+ "Create output file error") \
+ GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_WRITE_OUTPUT_FILE, \
+ gsliberr.write.output.file, \
+ "Write output file error") \
+ GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_CONNECT_GENERIC, \
+ gsliberr.connect.generic, \
+ "Connect generic error") \
+ GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_CONNECT_SERVICE_NOT_RUNNING, \
+ gsliberr.connect.service.not.running, \
+ "Connect service not running") \
+ GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_CONNECT_PERMISSION_DENIED, \
+ gsliberr.connect.permission.denied, \
+ "Connect permission denied") \
+ GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_CONNECT_SECURITY_VIOLATION, \
+ gsliberr.connect.security.violation, \
+ "Connect security violation") \
+ GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_CONNECT_PEER_RESET, \
+ gsliberr.connect.peer.reset, \
+ "Connect peer reset") \
+ GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_SEND, \
+ gsliberr.send, \
+ "Send error") \
+ GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_RECV, \
+ gsliberr.recv, \
+ "Receive error") \
+ GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_CONTENT_FORBIDDEN, \
+ gsliberr.content.forbidden, \
+ "Content forbidden") \
+ GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_CONTENT_NOT_FOUND, \
+ gsliberr.content.not.found, \
+ "Content not found") \
+ GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_SERVER, \
+ gsliberr.server, \
+ "Server error") \
+ GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_CANCELLED, \
+ gsliberr.cancelled, \
+ "Cancelled") \
+ GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_CHECKSUM, \
+ gsliberr.checksum, \
+ "Checksum error")
+
+/*
+ * Error codes
+ */
+#define GUESTSTORE_LIB_ERR_ITEM(a, b, c) a,
+typedef enum {
+GUESTSTORE_LIB_ERR_LIST
+GUESTSTORE_LIB_ERR_MAX
+} GuestStoreLibError;
+#undef GUESTSTORE_LIB_ERR_ITEM
+
+/*
+ * Log levels
+ */
+typedef enum {
+ GSLIBLOGLEVEL_ERROR = 1,
+ GSLIBLOGLEVEL_WARNING,
+ GSLIBLOGLEVEL_INFO,
+ GSLIBLOGLEVEL_DEBUG,
+} GuestStoreLibLogLevel;
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Caller provided function to receive log messages from GuestStore client
+ * library. Caller can log the messages to its own logging facilities.
+ */
+typedef void (*GuestStore_Logger) (GuestStoreLibLogLevel level,
+ const char *message,
+ void *clientData);
+
+/*
+ * Caller provided Panic function in non-recoverable error situations.
+ * This function shall exit the library host process.
+ */
+typedef void (*GuestStore_Panic) (const char *message,
+ void *clientData);
+
+/*
+ * Caller provided callback to get total content size in bytes and so far
+ * received bytes. Return FALSE to cancel content download.
+ */
+typedef Bool (*GuestStore_GetContentCallback) (int64 contentSize,
+ int64 contentBytesReceived,
+ void *clientData);
+
+/*
+ * GuestStore client library Init entry point function.
+ */
+GuestStoreLibError
+GuestStore_Init(void);
+
+/*
+ * GuestStore client library GetContent entry point function.
+ */
+GuestStoreLibError
+GuestStore_GetContent(
+ const char *contentPath, // IN
+ const char *outputPath, // IN
+ GuestStore_Logger logger, // IN, OPTIONAL
+ GuestStore_Panic panic, // IN, OPTIONAL
+ GuestStore_GetContentCallback getContentCb, // IN, OPTIONAL
+ void *clientData); // IN, OPTIONAL
+
+/*
+ * GuestStore client library DeInit entry point function.
+ * Call of GuestStore_DeInit should match succeeded GuestStore_Init call.
+ */
+GuestStoreLibError
+GuestStore_DeInit(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GUESTSTORECLIENTLIB_H__ */
--- /dev/null
+ 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.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
--- /dev/null
+################################################################################
+### Copyright (c) 2020 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
+################################################################################
+
+lib_LTLIBRARIES = libguestStoreClient.la
+
+libguestStoreClient_la_LIBADD =
+libguestStoreClient_la_LIBADD += ../lib/err/libErr.la
+libguestStoreClient_la_LIBADD += ../lib/string/libString.la
+libguestStoreClient_la_LIBADD += ../lib/unicode/libUnicode.la
+libguestStoreClient_la_LIBADD += ../lib/misc/libMisc.la
+libguestStoreClient_la_LIBADD += -lpthread
+libguestStoreClient_la_LIBADD += -ldl
+
+libguestStoreClient_la_SOURCES =
+libguestStoreClient_la_SOURCES += guestStoreClientLib.c
+
+libguestStoreClient_la_LDFLAGS =
+# We require GCC, so we're fine passing compiler-specific flags.
+libguestStoreClient_la_LDFLAGS += -Wl,-z,defs
+# Needed for OS's that don't link shared libraries against libc by
+#default, e.g. FreeBSD
+libguestStoreClient_la_LDFLAGS += -Wl,-lc
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2019-2020 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 guestStoreClientLib.c
+ *
+ * GuestStore client library, connecting to vmtoolsd GuestStore plugin.
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include<stdarg.h>
+#include <errno.h>
+
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+/*
+ * #define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF)
+ */
+#define TLS_INDEX_TYPE DWORD
+#else
+#define _GNU_SOURCE // For struct ucred
+#define __USE_GNU // For struct ucred (glibc 2.17)
+#include <sys/socket.h>
+#undef __USE_GNU
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <sys/uio.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <pthread.h>
+/*
+ * typedef unsigned int pthread_key_t;
+ * #define PTHREAD_KEYS_MAX 1024
+ */
+#define TLS_INDEX_TYPE pthread_key_t
+#define TLS_OUT_OF_INDEXES ((pthread_key_t)-1)
+#endif
+
+#include "guestStoreClientLibInt.h"
+
+#include "vm_version.h"
+#include "embed_version.h"
+#include "gueststoreclientlib_version.h"
+VM_EMBED_VERSION(GUESTSTORECLIENTLIB_VERSION_STRING);
+
+#define GSLIBLOG_TAG "[guestStoreClientLib] "
+#define GSLIBLOG_TAG_LEN (sizeof(GSLIBLOG_TAG) - 1)
+
+
+/*
+ * Library Init/DeInit reference count.
+ */
+static Atomic_uint32 initLibCount = { 0 };
+
+/*
+ * Thread local storage index for CallCtx pointer.
+ */
+static TLS_INDEX_TYPE callCtxTlsIndex = TLS_OUT_OF_INDEXES;
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreAllocTls --
+ *
+ * Allocate a thread local storage index for CallCtx pointer.
+ *
+ * Results:
+ * GSLIBERR_SUCCESS or GSLIBERR_TLS.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static GuestStoreLibError
+GuestStoreAllocTls(void)
+{
+ ASSERT(callCtxTlsIndex == TLS_OUT_OF_INDEXES);
+
+#ifdef _WIN32
+ callCtxTlsIndex = TlsAlloc();
+#else
+ if (pthread_key_create(&callCtxTlsIndex, NULL) != 0) {
+ callCtxTlsIndex = TLS_OUT_OF_INDEXES;
+ }
+#endif
+
+ return (callCtxTlsIndex == TLS_OUT_OF_INDEXES) ? GSLIBERR_TLS :
+ GSLIBERR_SUCCESS;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreFreeTls --
+ *
+ * Free the allocated thread local storage index for CallCtx pointer.
+ *
+ * Results:
+ * GSLIBERR_SUCCESS or GSLIBERR_TLS.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static GuestStoreLibError
+GuestStoreFreeTls(void)
+{
+ Bool res;
+
+ ASSERT(callCtxTlsIndex != TLS_OUT_OF_INDEXES);
+
+#ifdef _WIN32
+ res = TlsFree(callCtxTlsIndex) ? TRUE : FALSE;
+#else
+ res = pthread_key_delete(callCtxTlsIndex) == 0 ? TRUE : FALSE;
+#endif
+
+ callCtxTlsIndex = TLS_OUT_OF_INDEXES;
+
+ return res ? GSLIBERR_SUCCESS : GSLIBERR_TLS;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreSetTls --
+ *
+ * Set CallCtx pointer to the slot of the thread local storage index.
+ * When ctx is NULL, clear the slot.
+ *
+ * Results:
+ * GSLIBERR_SUCCESS or GSLIBERR_TLS.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static GuestStoreLibError
+GuestStoreSetTls(CallCtx *ctx) // IN
+{
+ Bool res;
+
+ ASSERT(callCtxTlsIndex != TLS_OUT_OF_INDEXES);
+
+#ifdef _WIN32
+ res = TlsSetValue(callCtxTlsIndex, ctx) ? TRUE : FALSE;
+#else
+ res = pthread_setspecific(callCtxTlsIndex, ctx) == 0 ? TRUE : FALSE;
+#endif
+
+ return res ? GSLIBERR_SUCCESS : GSLIBERR_TLS;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreGetTls --
+ *
+ * Get CallCtx pointer from the slot of the thread local storage index.
+ *
+ * Results:
+ * CallCtx/NULL pointer
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static CallCtx *
+GuestStoreGetTls(void)
+{
+ ASSERT(callCtxTlsIndex != TLS_OUT_OF_INDEXES);
+
+#ifdef _WIN32
+ return (CallCtx *)TlsGetValue(callCtxTlsIndex);
+#else
+ return (CallCtx *)pthread_getspecific(callCtxTlsIndex);
+#endif
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStore_Init --
+ *
+ * GuestStore client library Init entry point function.
+ *
+ * Results:
+ * GSLIBERR_SUCCESS or an error code of GSLIBERR_*.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+GuestStoreLibError
+GuestStore_Init(void)
+{
+ if (Atomic_ReadInc32(&initLibCount) == 0) {
+ GuestStoreLibError retVal = GuestStoreAllocTls();
+ if (retVal != GSLIBERR_SUCCESS) {
+ Atomic_Dec32(&initLibCount);
+ }
+ return retVal;
+ }
+
+ return GSLIBERR_SUCCESS;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStore_DeInit --
+ *
+ * GuestStore client library DeInit entry point function.
+ * Call of GuestStore_DeInit should match succeeded GuestStore_Init call.
+ *
+ * Results:
+ * GSLIBERR_SUCCESS or an error code of GSLIBERR_*.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+GuestStoreLibError
+GuestStore_DeInit(void)
+{
+ uint32 oldVal;
+ uint32 newVal;
+
+ do {
+ oldVal = Atomic_Read32(&initLibCount);
+ if (oldVal == 0) {
+ return GSLIBERR_NOT_INITIALIZED;
+ }
+
+ newVal = oldVal - 1;
+ } while (Atomic_ReadIfEqualWrite32(&initLibCount,
+ oldVal, newVal) != oldVal);
+
+ if (oldVal == 1) {
+ return GuestStoreFreeTls();
+ }
+
+ return GSLIBERR_SUCCESS;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreLogV --
+ *
+ * Internal log function.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+GuestStoreLogV(CallCtx *ctx, // IN
+ GuestStoreLibLogLevel level, // IN
+ const char *fmt, // IN
+ va_list args) // IN
+{
+ char buf[1024] = GSLIBLOG_TAG;
+
+ ASSERT(ctx != NULL && ctx->logger != NULL);
+
+ Str_Vsnprintf(buf + GSLIBLOG_TAG_LEN, sizeof(buf) - GSLIBLOG_TAG_LEN,
+ fmt, args);
+ ctx->logger(level, buf, ctx->clientData);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreLog --
+ *
+ * Internal log function.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+GuestStoreLog(CallCtx *ctx, // IN
+ GuestStoreLibLogLevel level, // IN
+ const char *fmt, ...) // IN
+{
+ va_list args;
+
+ ASSERT(ctx != NULL && ctx->logger != NULL);
+
+ va_start(args, fmt);
+ GuestStoreLogV(ctx, level, fmt, args);
+ va_end(args);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreFreeCtxResources --
+ *
+ * Free resources allocated in each GuestStore_GetContent call.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+GuestStoreFreeCtxResources(CallCtx *ctx) // IN / OUT
+{
+ int res;
+
+ if (ctx->output != NULL) {
+ fclose(ctx->output);
+ ctx->output = NULL;
+
+ /*
+ * The output file was created after the content size was received.
+ */
+ ASSERT(ctx->contentSize >= 0);
+
+ /*
+ * Delete the output file if not all the content bytes were received.
+ */
+ if (ctx->contentBytesReceived != ctx->contentSize) {
+ res = Posix_Unlink(ctx->outputPath);
+ if (res != 0) {
+ LOG_ERR(ctx, "Posix_Unlink failed: outputPath='%s', error=%d.",
+ ctx->outputPath, errno);
+ }
+ }
+ }
+
+ if (ctx->sd != INVALID_SOCKET) {
+#ifdef _WIN32
+ res = closesocket(ctx->sd);
+#else
+ res = close(ctx->sd);
+#endif
+
+ if (res == SOCKET_ERROR) {
+ LOG_ERR(ctx, "close failed on socket %d: error=%d.",
+ ctx->sd, SocketGetLastError());
+ }
+
+ ctx->sd = INVALID_SOCKET;
+ }
+
+ free(ctx->buf);
+ ctx->buf = NULL;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreCreateOutputFile --
+ *
+ * Create an output file stream for writing.
+ *
+ * If the given file exists, its content is destroyed.
+ *
+ * Results:
+ * GSLIBERR_SUCCESS or an error code of GSLIBERR_*.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static GuestStoreLibError
+GuestStoreCreateOutputFile(CallCtx *ctx) // IN / OUT
+{
+ FILE *output = Posix_Fopen(ctx->outputPath, "wb");
+ if (output == NULL) {
+ LOG_ERR(ctx, "Posix_Fopen failed: outputPath='%s', error=%d.",
+ ctx->outputPath, errno);
+ return GSLIBERR_CREATE_OUTPUT_FILE;
+ }
+
+ ctx->output = output;
+ return GSLIBERR_SUCCESS;
+}
+
+
+#ifndef _WIN32
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreConnect --
+ *
+ * Connect to vmtoolsd GuestStore plugin.
+ *
+ * Results:
+ * GSLIBERR_SUCCESS or an error code of GSLIBERR_*.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+GuestStoreLibError
+GuestStoreConnect(CallCtx *ctx) // IN / OUT
+{
+ struct sockaddr_un svcAddr;
+ int res;
+ int err;
+ struct ucred peerCred;
+ socklen_t peerCredLen;
+
+ ctx->sd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (ctx->sd == INVALID_SOCKET) {
+ LOG_ERR(ctx, "socket failed: error=%d.", SocketGetLastError());
+ return GSLIBERR_CONNECT_GENERIC;
+ }
+
+ svcAddr.sun_family = AF_UNIX;
+ ASSERT(sizeof(GUESTSTORE_PIPE_NAME) < sizeof(svcAddr.sun_path));
+ memcpy(svcAddr.sun_path, GUESTSTORE_PIPE_NAME,
+ sizeof(GUESTSTORE_PIPE_NAME));
+
+ do {
+ res = connect(ctx->sd, (struct sockaddr*)&svcAddr,
+ (socklen_t)sizeof(svcAddr));
+ } while (res == SOCKET_ERROR &&
+ (err = SocketGetLastError()) == SYSERR_EINTR);
+
+ if (res == SOCKET_ERROR) {
+ LOG_ERR(ctx, "connect failed on socket %d: error=%d.",
+ ctx->sd, err);
+
+ if (err == SYSERR_ECONNREFUSED) {
+ return GSLIBERR_CONNECT_SERVICE_NOT_RUNNING;
+ } else if (err == SYSERR_EACCESS) {
+ return GSLIBERR_CONNECT_PERMISSION_DENIED;
+ } else {
+ return GSLIBERR_CONNECT_GENERIC;
+ }
+ }
+
+ /*
+ * On Linux, the SO_PEERCRED socket option will give us the PID,
+ * effective UID, and GID of the peer (the server in this case).
+ */
+ peerCredLen = (socklen_t)sizeof(peerCred);
+ res = getsockopt(ctx->sd, SOL_SOCKET, SO_PEERCRED, &peerCred, &peerCredLen);
+ if (res == SOCKET_ERROR) {
+ LOG_ERR(ctx, "getsockopt SO_PEERCRED failed: error=%d.",
+ SocketGetLastError());
+ return GSLIBERR_CONNECT_GENERIC;
+ } else if (peerCred.uid != 0) {
+ LOG_ERR(ctx, "Peer is not supper user.");
+ return GSLIBERR_CONNECT_SECURITY_VIOLATION;
+ }
+
+ return GSLIBERR_SUCCESS;
+}
+
+#endif
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreRecvBytes --
+ *
+ * Partially receive bytes from vmtoolsd GuestStore plugin.
+ *
+ * Results:
+ * GSLIBERR_SUCCESS or an error code of GSLIBERR_*.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static GuestStoreLibError
+GuestStoreRecvBytes(CallCtx *ctx, // IN
+ char *buf, // OUT
+ int bytesToRecv, // IN
+ int *bytesReceived) // OUT
+{
+ GuestStoreLibError retVal;
+
+ do {
+ int res;
+
+ res = recv(ctx->sd, // Synchronous recv
+ buf,
+ bytesToRecv,
+ 0);
+ if (res > 0) {
+ *bytesReceived = res;
+ retVal = GSLIBERR_SUCCESS;
+ break;
+ } else if (res == 0) {
+ LOG_ERR(ctx, "peer closed on socket %d.", ctx->sd);
+ retVal = GSLIBERR_CONNECT_PEER_RESET;
+ break;
+ } else { // SOCKET_ERROR
+ int err = SocketGetLastError();
+ if (err == SYSERR_EINTR) {
+ continue;
+ }
+
+ LOG_ERR(ctx, "recv failed on socket %d: error=%d.",
+ ctx->sd, err);
+ retVal = GSLIBERR_RECV;
+ break;
+ }
+ } while (TRUE);
+
+ return retVal;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreSendBytes --
+ *
+ * Send the specified amount of bytes to vmtoolsd GuestStore plugin.
+ *
+ * Results:
+ * GSLIBERR_SUCCESS or an error code of GSLIBERR_*.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static GuestStoreLibError
+GuestStoreSendBytes(CallCtx *ctx, // IN
+ char *buf, // IN
+ int bytesToSend) // IN
+{
+ GuestStoreLibError retVal = GSLIBERR_SUCCESS;
+ int bytesSent = 0;
+
+ while (bytesSent < bytesToSend) {
+ int res;
+ res = send(ctx->sd, // Synchronous send
+ buf + bytesSent,
+ bytesToSend - bytesSent,
+ 0);
+ if (res == SOCKET_ERROR) {
+ int err = SocketGetLastError();
+ if (err == SYSERR_EINTR) {
+ continue;
+ }
+
+ LOG_ERR(ctx, "send failed on socket %d: error=%d.",
+ ctx->sd, err);
+ retVal = GSLIBERR_SEND;
+ break;
+ }
+
+ bytesSent += res;
+ }
+
+ return retVal;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreSendHTTPRequest --
+ *
+ * Send HTTP request for content download to vmtoolsd GuestStore plugin.
+ *
+ * Results:
+ * GSLIBERR_SUCCESS or an error code of GSLIBERR_*.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static GuestStoreLibError
+GuestStoreSendHTTPRequest(const char *contentPath, // IN
+ CallCtx *ctx) // IN
+{
+ int len = 0;
+ int pathLen;
+ int index;
+
+ /*
+ * HTTP GET request: GET <path> HTTP/1.1\r\n\r\n
+ * excluding <path>, it is 17 bytes.
+ * Reserve 1024 bytes for it.
+ *
+ * Length of contentPath is restricted to GUESTSTORE_CONTENT_PATH_MAX (1024)
+ * maximum length after URL escaping is 3 * GUESTSTORE_CONTENT_PATH_MAX.
+ *
+ * ctx->bufSize is GUESTSTORE_RESPONSE_BUFFER_SIZE (64 * 1024)
+ */
+ ASSERT((1024 + 3 * GUESTSTORE_CONTENT_PATH_MAX) < ctx->bufSize);
+
+ #define COPY_STR_TO_BUF(str) \
+ memcpy(ctx->buf + len, str, sizeof(str) - 1); \
+ len += (int)(sizeof(str) - 1)
+
+ COPY_STR_TO_BUF(HTTP_REQ_METHOD_GET);
+ COPY_STR_TO_BUF(" ");
+
+ /*
+ * ' ', '?' and '%' are the 3 protocol characters GuestStore plugin parses.
+ */
+ pathLen = (int)strlen(contentPath);
+ for (index = 0; index < pathLen; index++) {
+ char c = contentPath[index];
+ if (c == ' ') {
+ COPY_STR_TO_BUF("%20");
+ } else if (c == '%') {
+ COPY_STR_TO_BUF("%25");
+ } else if (c == '?') {
+ COPY_STR_TO_BUF("%3F");
+ } else {
+ *(ctx->buf + len++) = c;
+ }
+ }
+
+ COPY_STR_TO_BUF(" ");
+ COPY_STR_TO_BUF(HTTP_VER);
+ COPY_STR_TO_BUF(HTTP_HEADER_END);
+
+ return GuestStoreSendBytes(ctx, ctx->buf, len);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreRecvHTTPResponseHeader --
+ *
+ * Receive HTTP response header from vmtoolsd GuestStore plugin.
+ *
+ * Results:
+ * GSLIBERR_SUCCESS or an error code of GSLIBERR_*.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static GuestStoreLibError
+GuestStoreRecvHTTPResponseHeader(CallCtx *ctx) // IN
+{
+ int totalBytesReceived = 0;
+ int recvBufSize = ctx->bufSize - 1; // Reserve the last byte for '\0'
+ char *httpHeaderEnd;
+ char *next_token;
+ char *httpVer;
+ char *httpStatus;
+ int status;
+ char *contentLengthHeader;
+ char *content;
+ int httpResHeaderLen;
+ GuestStoreLibError retVal;
+
+ do {
+ int bytesReceived = 0;
+
+ retVal = GuestStoreRecvBytes(ctx, ctx->buf + totalBytesReceived,
+ recvBufSize - totalBytesReceived,
+ &bytesReceived);
+ if (retVal != GSLIBERR_SUCCESS) {
+ return retVal;
+ }
+
+ totalBytesReceived += bytesReceived;
+ ctx->buf[totalBytesReceived] = '\0';
+ httpHeaderEnd = strstr(ctx->buf, HTTP_HEADER_END);
+ if (httpHeaderEnd != NULL) {
+ *httpHeaderEnd = '\0';
+ break;
+ }
+
+ if (totalBytesReceived == recvBufSize) {
+ LOG_ERR(ctx, "Protocol header end mark not found.");
+ return GSLIBERR_SERVER;
+ }
+ } while (TRUE);
+
+ httpVer = strtok_r(ctx->buf, " ", &next_token);
+ if (NULL == httpVer || strcmp(httpVer, HTTP_VER) != 0) {
+ LOG_ERR(ctx, "Protocol version not correct.");
+ return GSLIBERR_SERVER;
+ }
+
+ httpStatus = strtok_r(NULL, " ", &next_token);
+ if (NULL == httpStatus) {
+ LOG_ERR(ctx, "Protocol status code not found.");
+ return GSLIBERR_SERVER;
+ }
+
+ status = atoi(httpStatus);
+
+ if (status == HTTP_STATUS_CODE_FORBIDDEN) {
+ LOG_ERR(ctx, "Content forbidden.");
+ return GSLIBERR_CONTENT_FORBIDDEN;
+ }
+
+ if (status == HTTP_STATUS_CODE_NOT_FOUND) {
+ LOG_ERR(ctx, "Content not found.");
+ return GSLIBERR_CONTENT_NOT_FOUND;
+ }
+
+ if (status != HTTP_STATUS_CODE_OK) {
+ LOG_ERR(ctx, "Invalid protocol status '%s'.", httpStatus);
+ return GSLIBERR_SERVER;
+ }
+
+ contentLengthHeader = strstr(httpStatus + strlen(httpStatus) + 1,
+ CONTENT_LENGTH_HEADER);
+ if (NULL == contentLengthHeader) {
+ LOG_ERR(ctx, "Protocol content length not found.");
+ return GSLIBERR_SERVER;
+ }
+
+ contentLengthHeader += CONTENT_LENGTH_HEADER_LEN;
+ while (*contentLengthHeader >= '0' && *contentLengthHeader <= '9') {
+ ctx->contentSize = ctx->contentSize * 10 + (*contentLengthHeader - '0');
+ contentLengthHeader++;
+ }
+
+ if (ctx->contentSize < 0) {
+ LOG_ERR(ctx, "Invalid protocol content length.");
+ return GSLIBERR_SERVER;
+ }
+
+ /*
+ * We've got content to save, create the output file now.
+ */
+ retVal = GuestStoreCreateOutputFile(ctx);
+ if (retVal != GSLIBERR_SUCCESS) {
+ return retVal;
+ }
+
+ /*
+ * Save content bytes that follow HTTP response header.
+ */
+ content = httpHeaderEnd + HTTP_HEADER_END_LEN;
+ httpResHeaderLen = (int)(content - ctx->buf);
+ if (httpResHeaderLen < totalBytesReceived) {
+ int contentLen = totalBytesReceived - httpResHeaderLen;
+
+ ctx->contentBytesReceived += contentLen;
+ if (ctx->contentBytesReceived > ctx->contentSize) {
+ LOG_ERR(ctx, "Bytes received exceeded content size.");
+ return GSLIBERR_SERVER;
+ }
+
+ if (fwrite(content, sizeof(char), contentLen, ctx->output) != contentLen) {
+ LOG_ERR(ctx, "fwrite failed: error=%d.", errno);
+ return GSLIBERR_WRITE_OUTPUT_FILE;
+ }
+
+ if (!REPORT_PROGRESS(ctx)) {
+ LOG_ERR(ctx, "Request cancelled.");
+ return GSLIBERR_CANCELLED;
+ }
+ }
+
+ return GSLIBERR_SUCCESS;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreRecvHTTPResponseBody --
+ *
+ * Receive HTTP response body, i.e., content bytes, from vmtoolsd
+ * GuestStore plugin.
+ *
+ * Results:
+ * GSLIBERR_SUCCESS or an error code of GSLIBERR_*.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static GuestStoreLibError
+GuestStoreRecvHTTPResponseBody(CallCtx *ctx) // IN
+{
+ GuestStoreLibError retVal = GSLIBERR_SUCCESS;
+
+ while (ctx->contentBytesReceived < ctx->contentSize) {
+ int bytesReceived = 0;
+
+ retVal = GuestStoreRecvBytes(ctx, ctx->buf, ctx->bufSize, &bytesReceived);
+ if (retVal != GSLIBERR_SUCCESS) {
+ break;
+ }
+
+ ctx->contentBytesReceived += bytesReceived;
+ if (ctx->contentBytesReceived > ctx->contentSize) {
+ LOG_ERR(ctx, "Bytes received exceeded content size.");
+ retVal = GSLIBERR_SERVER;
+ break;
+ }
+
+ if (fwrite(ctx->buf, sizeof(char), bytesReceived, ctx->output) != bytesReceived) {
+ LOG_ERR(ctx, "fwrite failed: error=%d.", errno);
+ retVal = GSLIBERR_WRITE_OUTPUT_FILE;
+ break;
+ }
+
+ if (!REPORT_PROGRESS(ctx)) {
+ LOG_ERR(ctx, "Request cancelled.");
+ retVal = GSLIBERR_CANCELLED;
+ break;
+ }
+ }
+
+ return retVal;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStore_GetContent --
+ *
+ * GuestStore client library GetContent entry point function.
+ *
+ * Results:
+ * GSLIBERR_SUCCESS or an error code of GSLIBERR_*.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+GuestStoreLibError
+GuestStore_GetContent(
+ const char *contentPath, // IN
+ const char *outputPath, // IN
+ GuestStore_Logger logger, // IN, OPTIONAL
+ GuestStore_Panic panic, // IN, OPTIONAL
+ GuestStore_GetContentCallback getContentCb, // IN, OPTIONAL
+ void *clientData) // IN, OPTIONAL
+{
+ GuestStoreLibError retVal;
+ CallCtx ctx = { 0 };
+#ifdef _WIN32
+ WSADATA wsaData;
+ int res;
+#endif
+
+ /*
+ * Set ctx before first LOG_ERR.
+ */
+ ctx.contentPath = contentPath ? contentPath : "";
+ ctx.outputPath = outputPath ? outputPath : "";
+ ctx.logger = logger;
+ ctx.panic = panic;
+ ctx.getContentCb = getContentCb;
+ ctx.clientData = clientData;
+ ctx.sd = INVALID_SOCKET;
+
+ if (contentPath == NULL || *contentPath != '/' ||
+ strlen(contentPath) > GUESTSTORE_CONTENT_PATH_MAX) {
+ LOG_ERR(&ctx, "Invalid content path.");
+ return GSLIBERR_INVALID_PARAMETER;
+ }
+
+ if (outputPath == NULL || *outputPath == '\0') {
+ LOG_ERR(&ctx, "Invalid output file path.");
+ return GSLIBERR_INVALID_PARAMETER;
+ }
+
+ if (Atomic_Read32(&initLibCount) == 0 ||
+ callCtxTlsIndex == TLS_OUT_OF_INDEXES) {
+ LOG_ERR(&ctx, "Library is not properly initialized.");
+ return GSLIBERR_NOT_INITIALIZED;
+ }
+
+ retVal = GuestStoreSetTls(&ctx);
+ if (retVal != GSLIBERR_SUCCESS) {
+ LOG_ERR(&ctx, "GuestStoreSetTls failed.");
+ return retVal;
+ }
+
+#ifdef _WIN32
+ res = WSAStartup(MAKEWORD(2, 2), &wsaData);
+ if (res != 0) {
+ retVal = GSLIBERR_CONNECT_GENERIC;
+ LOG_ERR(&ctx, "WSAStartup failed: error=%d.", res);
+ goto exit;
+ }
+#endif
+
+ retVal = GuestStoreConnect(&ctx);
+ if (retVal != GSLIBERR_SUCCESS) {
+ goto exit;
+ }
+
+ ctx.bufSize = GUESTSTORE_RESPONSE_BUFFER_SIZE;
+ ctx.buf = (char *)Util_SafeMalloc(ctx.bufSize);
+
+ retVal = GuestStoreSendHTTPRequest(contentPath, &ctx);
+ if (retVal != GSLIBERR_SUCCESS) {
+ goto exit;
+ }
+
+ retVal = GuestStoreRecvHTTPResponseHeader(&ctx);
+ if (retVal != GSLIBERR_SUCCESS) {
+ goto exit;
+ }
+
+ retVal = GuestStoreRecvHTTPResponseBody(&ctx);
+
+exit:
+
+ GuestStoreFreeCtxResources(&ctx); // Should be before WSACleanup()
+
+#ifdef _WIN32
+ if (res == 0) {
+ if (retVal != GSLIBERR_SUCCESS) {
+ /*
+ * WSASetLastError needs a successful WSAStartup call,
+ * WSAGetLastError does not.
+ *
+ * Note: WSACleanup may change WSA last error again.
+ */
+ WSASetLastError(ctx.winWSAErrNum);
+ }
+
+ WSACleanup();
+ }
+#endif
+
+ GuestStoreSetTls(NULL); // Ignore its return value
+
+ /*
+ * Restore the last error in the end.
+ */
+ if (retVal != GSLIBERR_SUCCESS) {
+ Err_SetErrno(ctx.errNum);
+#ifdef _WIN32
+ errno = ctx.winErrNum;
+#endif
+ }
+
+ return retVal;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Debug --
+ *
+ * Stub for Debug.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+Debug(const char *fmt, ...)
+{
+ WITH_ERRNO(__errNum__,
+ {
+ CallCtx *ctx = GuestStoreGetTls();
+
+ if (ctx != NULL && ctx->logger != NULL) {
+ va_list args;
+
+ va_start(args, fmt);
+ GuestStoreLogV(ctx, GSLIBLOGLEVEL_DEBUG, fmt, args);
+ va_end(args);
+ }
+ }
+ );
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Log --
+ *
+ * Stub for Log.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+Log(const char *fmt, ...)
+{
+ WITH_ERRNO(__errNum__,
+ {
+ CallCtx *ctx = GuestStoreGetTls();
+
+ if (ctx != NULL && ctx->logger != NULL) {
+ va_list args;
+
+ va_start(args, fmt);
+ GuestStoreLogV(ctx, GSLIBLOGLEVEL_INFO, fmt, args);
+ va_end(args);
+ }
+ }
+ );
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Warning --
+ *
+ * Stub for Warning.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+Warning(const char *fmt, ...)
+{
+ WITH_ERRNO(__errNum__,
+ {
+ CallCtx *ctx = GuestStoreGetTls();
+
+ if (ctx != NULL && ctx->logger != NULL) {
+ va_list args;
+
+ va_start(args, fmt);
+ GuestStoreLogV(ctx, GSLIBLOGLEVEL_WARNING, fmt, args);
+ va_end(args);
+ }
+ }
+ );
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Panic --
+ *
+ * Stub for Panic.
+ *
+ * Results:
+ * Does not return.
+ *
+ * Side-effects:
+ * Process exits.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+Panic(const char *fmt, ...)
+{
+ va_list args;
+ CallCtx *ctx;
+
+ va_start(args, fmt);
+
+ ctx = GuestStoreGetTls();
+ if (ctx != NULL && ctx->panic != NULL) {
+ char buf[1024] = GSLIBLOG_TAG;
+
+ Str_Vsnprintf(buf + GSLIBLOG_TAG_LEN, sizeof(buf) - GSLIBLOG_TAG_LEN,
+ fmt, args);
+ ctx->panic(buf, ctx->clientData); // No return
+ } else {
+ fprintf(stderr, "Panic: " GSLIBLOG_TAG);
+ vfprintf(stderr, fmt, args);
+ }
+
+ va_end(args);
+
+ exit(-1);
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2019 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.
+ *
+ *********************************************************/
+
+/*
+ * guestStoreClientLibInt.h --
+ * Private definitions for guestStoreClientLib.
+ */
+
+#ifndef __GUESTSTORECLIENTLIBINT_H__
+#define __GUESTSTORECLIENTLIBINT_H__
+
+#include "vm_assert.h"
+#include "vm_atomic.h"
+#include "vm_basic_types.h"
+#include "vm_basic_defs.h"
+#include "err.h"
+#include "str.h"
+#include "util.h"
+#include "posix.h"
+
+#include "guestStoreConst.h"
+#include "guestStoreDefs.h"
+#include "vmware/tools/guestStoreClientLib.h"
+
+#ifdef _WIN32
+
+#define SYSERR_EADDRINUSE WSAEADDRINUSE
+#define SYSERR_EACCESS WSAEACCES
+#define SYSERR_EINTR WSAEINTR
+#define SYSERR_ECONNRESET WSAECONNRESET
+#define SYSERR_ECONNREFUSED WSAECONNREFUSED
+
+#define SHUTDOWN_RECV SD_RECEIVE
+#define SHUTDOWN_SEND SD_SEND
+#define SHUTDOWN_BOTH SD_BOTH
+
+typedef int socklen_t;
+
+#else
+
+#define SYSERR_EADDRINUSE EADDRINUSE
+#define SYSERR_EACCESS EACCES
+#define SYSERR_EINTR EINTR
+#define SYSERR_ECONNRESET ECONNRESET
+#define SYSERR_ECONNREFUSED ECONNREFUSED
+
+#define SHUTDOWN_RECV SHUT_RD
+#define SHUTDOWN_SEND SHUT_WR
+#define SHUTDOWN_BOTH SHUT_RDWR
+
+typedef int SOCKET;
+
+#define SOCKET_ERROR (-1)
+#define INVALID_SOCKET ((SOCKET) -1)
+
+#endif
+
+
+/*
+ * Context of each GuestStore_GetContent call.
+ */
+typedef struct _CallCtx {
+ const char *contentPath; // Requested content path
+ const char *outputPath; // Output file path
+ GuestStore_Logger logger; // Caller provided logger function
+ GuestStore_Panic panic; // Caller provided panic function
+ GuestStore_GetContentCallback getContentCb; // Progress callback
+ void *clientData; // Parameter for caller provided functions
+ FILE *output; // Output file stream
+ SOCKET sd; // Socket descriptor connecting to vmtoolsd GuestStore plugin
+ int64 contentSize; // Total content bytes
+ int64 contentBytesReceived; // Received content bytes
+ int bufSize; // Content download buffer size
+ char *buf; // Content download buffer
+ Err_Number errNum; // Preserve the last error
+#ifdef _WIN32
+ int winErrNum;
+ int winWSAErrNum;
+#endif
+} CallCtx;
+
+/*
+ * Preserve the first last error that fails API GuestStore_GetContent() and
+ * restore it when the API returns in case API exit resource freeing calls
+ * change the last error.
+ *
+ * WSASetLastError needs a successful WSAStartup call,
+ * WSAGetLastError does not.
+ */
+#ifdef _WIN32
+#define LOG_ERR(ctx, format, ...) \
+ if ((ctx)->errNum == 0 && \
+ (ctx)->winErrNum == 0 && \
+ (ctx)->winWSAErrNum == 0) { \
+ (ctx)->errNum = Err_Errno(); \
+ (ctx)->winErrNum = errno; \
+ (ctx)->winWSAErrNum = WSAGetLastError(); \
+ } \
+ if ((ctx)->logger != NULL) { \
+ GuestStoreLog((ctx), GSLIBLOGLEVEL_ERROR, \
+ format, ##__VA_ARGS__); \
+ }
+#else
+#define LOG_ERR(ctx, format, ...) \
+ if ((ctx)->errNum == 0) { \
+ (ctx)->errNum = Err_Errno(); \
+ } \
+ if ((ctx)->logger != NULL) { \
+ GuestStoreLog((ctx), GSLIBLOGLEVEL_ERROR, \
+ format, ##__VA_ARGS__); \
+ }
+#endif
+
+#define LOG_WARN(ctx, format, ...) \
+ if ((ctx)->logger != NULL) { \
+ GuestStoreLog((ctx), GSLIBLOGLEVEL_WARNING, \
+ format, ##__VA_ARGS__); \
+ }
+
+#define LOG_INFO(ctx, format, ...) \
+ if ((ctx)->logger != NULL) { \
+ GuestStoreLog((ctx), GSLIBLOGLEVEL_INFO, \
+ format, ##__VA_ARGS__); \
+ }
+
+#define LOG_DEBUG(ctx, format, ...) \
+ if ((ctx)->logger != NULL) { \
+ GuestStoreLog((ctx), GSLIBLOGLEVEL_DEBUG, \
+ format, ##__VA_ARGS__); \
+ }
+
+#define REPORT_PROGRESS(ctx) \
+ (((ctx)->getContentCb != NULL) ? (ctx)->getContentCb( \
+ (ctx)->contentSize, \
+ (ctx)->contentBytesReceived, \
+ (ctx)->clientData) \
+ : TRUE)
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * SocketGetLastError --
+ *
+ * Get the last failed sock function error code.
+ *
+ * Results:
+ * error code.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static inline int
+SocketGetLastError(void)
+{
+#ifdef _WIN32
+ return WSAGetLastError();
+#else
+ return errno;
+#endif
+}
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+GuestStoreLog(CallCtx *ctx, // IN
+ GuestStoreLibLogLevel level, // IN
+ const char *fmt, ...); // IN
+
+GuestStoreLibError
+GuestStoreConnect(CallCtx *ctx); // IN / OUT
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GUESTSTORECLIENTLIBINT_H__ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2019 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 _GUESTSTORECLIENTLIB_VERSION_H_
+#define _GUESTSTORECLIENTLIB_VERSION_H_
+
+#include "vm_tools_version.h"
+#define GUESTSTORECLIENTLIB_VERSION_COMMAS TOOLS_VERSION_EXT_CURRENT_CSV
+#define GUESTSTORECLIENTLIB_VERSION_STRING TOOLS_VERSION_EXT_CURRENT_STR
+
+#endif /* _GUESTSTORECLIENTLIB_VERSION_H_ */
################################################################################
-### Copyright (C) 2008-2020 VMware, Inc. All rights reserved.
+### Copyright (c) 2008-2020 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
libvmtools_la_LIBADD += ../lib/glibUtils/libGlibUtils.la
libvmtools_la_LIBADD += ../lib/guestApp/libGuestApp.la
libvmtools_la_LIBADD += ../lib/guestRpc/libGuestRpc.la
+if LINUX
+ libvmtools_la_LIBADD += ../lib/guestStoreClientHelper/libGuestStoreClientHelper.la
+endif
libvmtools_la_LIBADD += ../lib/message/libMessage.la
libvmtools_la_LIBADD += ../lib/netUtil/libNetUtil.la
libvmtools_la_LIBADD += ../lib/nicInfo/libNicInfo.la
SUBDIRS =
if LINUX
- SUBDIRS += gdp
SUBDIRS += appInfo
+ SUBDIRS += gdp
+ SUBDIRS += guestStore
endif
if ENABLE_SDMP
SUBDIRS += serviceDiscovery
--- /dev/null
+ 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.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
--- /dev/null
+################################################################################
+### Copyright (c) 2020 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 =
+
+plugindir = @VMSVC_PLUGIN_INSTALLDIR@
+plugin_LTLIBRARIES = libguestStore.la
+
+libguestStore_la_CPPFLAGS =
+libguestStore_la_CPPFLAGS += @PLUGIN_CPPFLAGS@
+
+libguestStore_la_LDFLAGS =
+libguestStore_la_LDFLAGS += @PLUGIN_LDFLAGS@
+
+libguestStore_la_LIBADD =
+libguestStore_la_LIBADD += @VMTOOLS_LIBS@
+libguestStore_la_LIBADD += @GOBJECT_LIBS@
+
+libguestStore_la_SOURCES =
+libguestStore_la_SOURCES += guestStorePlugin.c
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2019-2020 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 guestStorePlugin.c
+ *
+ * GuestStore plugin, allow client to download content from GuestStore.
+ */
+
+
+#define G_LOG_DOMAIN "guestStore"
+
+#include <glib-object.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <iphlpapi.h>
+#else
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include "posix.h"
+#include "file.h"
+#endif
+
+#include "vm_assert.h"
+#include "vm_basic_types.h"
+#include "vmware/tools/guestStore.h"
+#include "vmware/tools/log.h"
+#include "vmware/tools/plugin.h"
+#include "vmware/tools/utils.h"
+#include "vmware/guestrpc/tclodefs.h"
+#include "vm_version.h"
+#include "embed_version.h"
+#include "vmtoolsd_version.h"
+#include "dataMap.h"
+#include "guestStoreConst.h"
+#include "guestStoreDefs.h"
+#include "rpcout.h"
+#include "poll.h"
+#ifdef OPEN_VM_TOOLS
+#include "vmci_sockets.h"
+#else
+#include "vsockCommon.h"
+#endif
+#include "asyncsocket.h"
+#include "str.h"
+#include "util.h"
+#include "guestApp.h"
+#include "vmcheck.h"
+#ifdef _WIN32
+#include "guestStoreWin32.h"
+#endif
+
+VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING);
+
+#ifndef sockerr
+#ifdef _WIN32
+#define sockerr() WSAGetLastError()
+#else
+#define sockerr() errno
+#endif
+#endif // sockerr
+
+/*
+ * User level send/recv buffers
+ */
+
+/*
+ * Client connection send/recv buffer size
+ */
+#define CLIENT_CONN_SEND_RECV_BUF_SIZE GUESTSTORE_REQUEST_BUFFER_SIZE
+
+/*
+ * VMX connection send/recv buffer size
+ */
+#define VMX_CONN_SEND_RECV_BUF_SIZE GUESTSTORE_RESPONSE_BUFFER_SIZE
+
+/*
+ * Maximum concurrent client connections
+ */
+#define DEFAULT_MAX_CLIENT_CONNECTIONS 8
+
+/*
+ * Default timeout value in seconds for receiving from client connections
+ */
+#define DEFAULT_CLIENT_RECV_TIMEOUT 3 // seconds
+
+
+/*
+ * Client connection details
+ */
+typedef struct _ClientConnInfo {
+ AsyncSocket *asock;
+
+ char *buf; // Send/recv buffer for HTTP request/response head
+ int32 bufLen; // Send/recv buffer length
+
+ Bool shutDown; // Close connection in send callback.
+
+ Bool isCurrent; // True for the current client connection
+ char *requestPath; // Requested GuestStore content path
+ GSource *timeoutSource; // Timeout source for receiving HTTP request
+} ClientConnInfo;
+
+/*
+ * VMX connection details
+ */
+typedef struct _VmxConnInfo {
+ AsyncSocket *asock;
+
+ char *buf; // Send/recv buffer for content transfer
+ int32 bufLen; // Send/recv buffer length
+
+ Bool shutDown; // Close connection in send callback.
+
+ int32 dataMapLen; // Recv buffer for VMX data map size
+ int32 connTimeout; // Connection inactivity timeout
+ int64 bytesRemaining; // Track remaining content size to transfer
+ GSource *timeoutSource; // Timeout source for connection inactivity
+} VmxConnInfo;
+
+typedef struct {
+ AsyncSocket *vmxListenSock; // For vsocket connections from VMX
+ AsyncSocket *clientListenSock; // For connections from clients
+
+ GList *clientConnWaitList; // Client connections in waiting list
+
+ ClientConnInfo *clientConn; // The current client connection being served
+ VmxConnInfo *vmxConn; // The VMX connection providing service
+
+ ToolsAppCtx *ctx; // vmtoolsd application context
+
+ Bool featureDisabled; // Track tools.conf [guestStore]disabled change
+ Bool adminOnly; // Track tools.conf [guestStore]adminOnly change
+
+ Bool guestStoreAccessEnabled; // VMX GuestStore access enable status
+
+ Bool vmxConnectRequested; // VMX connect request sent status
+ GSource *timeoutSource; // Timeout source for VMX to guest connection
+ Bool shutdown; // vmtoolsd shutdown
+} PluginData;
+
+static PluginData pluginData = {0};
+
+#define currentClientConn pluginData.clientConn
+#define theVmxConn pluginData.vmxConn
+
+#define ReceivedHttpRequestFromCurrentClientConn() \
+ (currentClientConn->requestPath != NULL)
+
+/*
+ * Macros to read values from config file
+ */
+#define GUESTSTORE_CONFIG_GET_BOOL(key, defVal) \
+ VMTools_ConfigGetBoolean(pluginData.ctx->config, "guestStore", key, defVal)
+
+#define GUESTSTORE_CONFIG_GET_INT(key, defVal) \
+ VMTools_ConfigGetInteger(pluginData.ctx->config, "guestStore", key, defVal)
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GetCurrentUtcStr --
+ *
+ * Get the current UTC time in a string format suitable for usage
+ * in HTTP response.
+ *
+ * Results:
+ * Return the current UTC time string, e.g.
+ *
+ * Wed, 07 Nov 2018 20:50:11 GMT
+ *
+ * The caller should call g_free() to free it.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static gchar *
+GetCurrentUtcStr(void)
+{
+ gchar *res = NULL;
+ GDateTime *utcTime = g_date_time_new_now_utc();
+
+ if (NULL != utcTime) {
+ res = g_date_time_format(utcTime, "%a, %d %b %Y %T GMT");
+ g_date_time_unref(utcTime);
+ }
+
+ return res;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * IsFeatureDisabled --
+ *
+ * Check if guest admin/root has disabled GuestStore access.
+ *
+ * Results:
+ * Return the configured boolean value, default is FALSE.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static inline Bool
+IsFeatureDisabled(void)
+{
+ return GUESTSTORE_CONFIG_GET_BOOL("disabled", FALSE);
+}
+
+#define CheckAndUpdateFeatureDisabled() \
+ (pluginData.featureDisabled = IsFeatureDisabled())
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * IsAdminOnly --
+ *
+ * Check if only guest admin/root has access to GuestStore.
+ *
+ * Results:
+ * Return the configured boolean value, default is FALSE.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static inline Bool
+IsAdminOnly(void)
+{
+ return GUESTSTORE_CONFIG_GET_BOOL("adminOnly", FALSE);
+}
+
+#define CheckAndUpdateAdminOnly() \
+ (pluginData.adminOnly = IsAdminOnly())
+
+
+static void
+StartServeNextClientConn(void);
+
+static void
+CloseClientConn(ClientConnInfo *clientConn); // IN
+
+#define CloseCurrentClientConn() \
+ if (currentClientConn != NULL) { \
+ CloseClientConn(currentClientConn); \
+ }
+
+#define CloseCurrentClientConnIfReceivedHttpRequest() \
+ if (currentClientConn != NULL && \
+ currentClientConn->requestPath != NULL) { \
+ CloseClientConn(currentClientConn); \
+ }
+
+#define CloseClientConnsInWait() \
+ while (pluginData.clientConnWaitList != NULL) { \
+ ClientConnInfo *clientConn = \
+ (ClientConnInfo *)(pluginData.clientConnWaitList->data); \
+ CloseClientConn(clientConn); \
+ }
+
+static void
+CloseVmxConn(void);
+
+static void
+CloseActiveConnections(void);
+
+static void
+HandleCurrentClientConnError(void);
+
+static void
+HandleVmxConnError(void);
+
+static Bool
+RecvHttpRequestFromCurrentClientConn(void *buf, // OUT
+ int len); // IN
+
+static Bool
+StartRecvHttpRequestFromCurrentClientConn(void);
+
+static inline void
+StopRecvFromCurrentClientConn(void);
+
+static Bool
+SendToCurrentClientConn(void *buf, // IN
+ int len); // IN
+
+static Bool
+SendHttpResponseToCurrentClientConn(const char *headFmt, // IN
+ int64 contentLen, // IN
+ Bool shutdown); // IN
+
+#define SendHttpResponseOKToCurrentClientConn(contentSize) \
+ SendHttpResponseToCurrentClientConn( \
+ HTTP_RES_OK, \
+ contentSize, \
+ (0 == contentSize ? TRUE : FALSE))
+
+#define SendHttpResponseForbiddenToCurrentClientConn() \
+ SendHttpResponseToCurrentClientConn( \
+ HTTP_RES_FORBIDDEN, \
+ 0, \
+ TRUE)
+
+#define SendHttpResponseNotFoundToCurrentClientConn() \
+ SendHttpResponseToCurrentClientConn( \
+ HTTP_RES_NOT_FOUND, \
+ 0, \
+ TRUE)
+
+static Bool
+SendConnectRequestToVmx(void);
+
+static Bool
+SendDataMapToVmxConn(void);
+
+#define CheckSendShutdownDataMapToVmxConn() \
+ ASSERT(currentClientConn == NULL); \
+ if (theVmxConn != NULL && !theVmxConn->shutDown) { \
+ SendDataMapToVmxConn(); \
+ }
+
+#define CheckSendRequestDataMapToVmxConn() \
+ ASSERT(currentClientConn != NULL); \
+ if (ReceivedHttpRequestFromCurrentClientConn() && \
+ theVmxConn != NULL && !theVmxConn->shutDown) { \
+ SendDataMapToVmxConn(); \
+ }
+
+static Bool
+RecvDataMapFromVmxConn(void *buf, // OUT
+ int len); // IN
+
+static inline void
+StopRecvFromVmxConn(void);
+
+static Bool
+ProcessVmxDataMap(const DataMap *map); // IN
+
+static Bool
+RecvContentFromVmxConn(void);
+
+static void
+StartCurrentClientConnRecvTimeout(void);
+
+static inline void
+StopClientConnRecvTimeout(ClientConnInfo *clientConn); // IN
+
+static inline void
+StopCurrentClientConnRecvTimeout(void);
+
+static Bool
+CurrentClientConnRecvTimeoutCb(gpointer clientData); // IN
+
+static inline void
+StartVmxToGuestConnTimeout(void);
+
+static inline void
+StopVmxToGuestConnTimeout(void);
+
+static Bool
+VmxToGuestConnTimeoutCb(gpointer clientData); // IN
+
+static inline void
+StartConnInactivityTimeout(void);
+
+static inline void
+StopConnInactivityTimeout(void);
+
+static Bool
+ConnInactivityTimeoutCb(gpointer clientData); // IN
+
+static void
+ClientConnErrorCb(int err, // IN
+ AsyncSocket *asock, // IN
+ void *clientData); // IN
+
+static void
+CurrentClientConnSendCb(void *buf, // IN
+ int len, // IN
+ AsyncSocket *asock, // IN
+ void *clientData); // IN
+
+static void
+CurrentClientConnRecvHttpRequestCb(void *buf, // IN
+ int len, // IN
+ AsyncSocket *asock, // IN
+ void *clientData); // IN
+
+static void
+ClientConnectCb(AsyncSocket *asock, // IN
+ void *clientData); // IN
+
+static void
+VmxConnErrorCb(int err, // IN
+ AsyncSocket *asock, // IN
+ void *clientData); // IN
+
+static void
+VmxConnSendDataMapCb(void *buf, // IN
+ int len, // IN
+ AsyncSocket *asock, // IN
+ void *clientData); // IN
+
+static void
+VmxConnRecvDataMapCb(void *buf, // IN
+ int len, // IN
+ AsyncSocket *asock, // IN
+ void *clientData); // IN
+
+static void
+VmxConnRecvContentCb(void *buf, // IN
+ int len, // IN
+ AsyncSocket *asock, // IN
+ void *clientData); // IN
+
+static void
+VmxConnectCb(AsyncSocket *asock, // IN
+ void *clientData); // IN
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * StartServeNextClientConn --
+ *
+ * Remove the next client connection from the waiting list, make it
+ * the current client connection and start receiving HTTP request
+ * from it.
+ *
+ * If the waiting list is empty, initiate shutdown VMX connection.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+StartServeNextClientConn(void)
+{
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ ASSERT(currentClientConn == NULL);
+
+ if (pluginData.clientConnWaitList != NULL) {
+ currentClientConn = (ClientConnInfo *)
+ (pluginData.clientConnWaitList->data);
+ pluginData.clientConnWaitList = g_list_remove(
+ pluginData.clientConnWaitList, currentClientConn);
+ currentClientConn->isCurrent = TRUE;
+
+ StartRecvHttpRequestFromCurrentClientConn();
+ } else {
+ CheckSendShutdownDataMapToVmxConn();
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CloseClientConn --
+ *
+ * Close a client connection and remove its reference.
+ *
+ * Note: AsyncSocket does not differentiate read/write errors yet and
+ * does not try to send any data to the other end on close, so pending
+ * send data is dropped when a connection is closed even the socket may
+ * be still good for write.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+CloseClientConn(ClientConnInfo *clientConn) // IN
+{
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ ASSERT(clientConn != NULL);
+ ASSERT(clientConn->asock != NULL);
+
+ g_info("Closing client connection %d.\n",
+ AsyncSocket_GetFd(clientConn->asock));
+
+ AsyncSocket_Close(clientConn->asock);
+ clientConn->asock = NULL;
+
+ if (clientConn->buf != NULL) {
+ free(clientConn->buf);
+ clientConn->buf = NULL;
+ }
+
+ if (clientConn->requestPath != NULL) {
+ free(clientConn->requestPath);
+ clientConn->requestPath = NULL;
+ }
+
+ StopClientConnRecvTimeout(clientConn);
+
+ if (clientConn->isCurrent) {
+ ASSERT(currentClientConn == clientConn);
+ /*
+ * AsyncSocketSendFn (CurrentClientConnSendCb) can be invoked inside
+ * AsyncSocket_Close().
+ */
+ currentClientConn = NULL;
+ } else {
+ /*
+ * This client connection is in the waiting list.
+ */
+ pluginData.clientConnWaitList =
+ g_list_remove(pluginData.clientConnWaitList, clientConn);
+ }
+
+ free(clientConn);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CloseVmxConn --
+ *
+ * Close the VMX connection.
+ *
+ * Note: AsyncSocket does not differentiate read/write errors yet and
+ * does not try to send any data to the other end on close, so pending
+ * send data is dropped when a connection is closed even the socket may
+ * be still good for write.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+CloseVmxConn(void)
+{
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ if (theVmxConn == NULL) {
+ return;
+ }
+
+ ASSERT(theVmxConn->asock != NULL);
+
+ g_info("Closing VMX connection %d.\n",
+ AsyncSocket_GetFd(theVmxConn->asock));
+
+ /*
+ * AsyncSocketSendFn (VmxConnSendDataMapCb) can be invoked inside
+ * AsyncSocket_Close().
+ */
+ AsyncSocket_Close(theVmxConn->asock);
+ theVmxConn->asock = NULL;
+
+ if (theVmxConn->buf != NULL) {
+ free(theVmxConn->buf);
+ theVmxConn->buf = NULL;
+ }
+
+ StopConnInactivityTimeout();
+
+ free(theVmxConn);
+ theVmxConn = NULL;
+ pluginData.vmxConnectRequested = FALSE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CloseActiveConnections --
+ *
+ * Close the current client connection and the VMX connection, force to
+ * restart from the next client connection in the waiting list if it
+ * exists.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+CloseActiveConnections(void)
+{
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ CloseCurrentClientConn();
+
+ if (theVmxConn != NULL && !theVmxConn->shutDown) {
+ /*
+ * After CloseCurrentClientConn(), send shutdown data map to VMX.
+ */
+ SendDataMapToVmxConn();
+ } else {
+ /*
+ * Force to restart.
+ */
+ CloseVmxConn();
+ StartServeNextClientConn();
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HandleCurrentClientConnError --
+ *
+ * Handle the current client connection error.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+HandleCurrentClientConnError(void)
+{
+ Bool requestReceived;
+
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ ASSERT(currentClientConn != NULL);
+ ASSERT(currentClientConn->asock != NULL);
+
+ requestReceived = ReceivedHttpRequestFromCurrentClientConn();
+
+ CloseCurrentClientConn();
+
+ if (requestReceived) {
+ /*
+ * The VMX connection that serves the current client connection after
+ * it has received HTTP request has to be reset too.
+ */
+ CheckSendShutdownDataMapToVmxConn();
+ } else {
+ /*
+ * HTTP request not received from the current client connection yet,
+ * the VMX connection is still clean.
+ */
+ StartServeNextClientConn();
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HandleVmxConnError --
+ *
+ * Handle the VMX connection error.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+HandleVmxConnError(void)
+{
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ CloseVmxConn();
+
+ /*
+ * The current client connection being served after received HTTP request
+ * has to be reset too.
+ */
+ CloseCurrentClientConnIfReceivedHttpRequest();
+
+ if (pluginData.guestStoreAccessEnabled &&
+ currentClientConn == NULL) {
+ StartServeNextClientConn();
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * RecvHttpRequestFromCurrentClientConn --
+ *
+ * Receive HTTP request from the current client connection.
+ *
+ * Results:
+ * TURE on success, FALSE otherwise.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+RecvHttpRequestFromCurrentClientConn(void *buf, // OUT
+ int len) // IN
+{
+ int res;
+
+ g_debug("Entering %s: len=%d.\n", __FUNCTION__, len);
+
+ ASSERT(currentClientConn != NULL);
+ ASSERT(currentClientConn->asock != NULL);
+
+ res = AsyncSocket_RecvPartial(currentClientConn->asock, buf, len,
+ CurrentClientConnRecvHttpRequestCb,
+ currentClientConn);
+ if (res != ASOCKERR_SUCCESS) {
+ g_warning("AsyncSocket_RecvPartial failed "
+ "on current client connection %d: %s\n",
+ AsyncSocket_GetFd(currentClientConn->asock),
+ AsyncSocket_Err2String(res));
+ HandleCurrentClientConnError();
+ return FALSE;
+ }
+
+ if (currentClientConn->timeoutSource == NULL) {
+ StartCurrentClientConnRecvTimeout();
+ }
+
+ return TRUE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * StartRecvHttpRequestFromCurrentClientConn --
+ *
+ * Start receiving HTTP request, with timeout, from
+ * the current client connection.
+ *
+ * Results:
+ * TURE on success, FALSE otherwise.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+StartRecvHttpRequestFromCurrentClientConn(void)
+{
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ ASSERT(currentClientConn != NULL);
+ ASSERT(currentClientConn->asock != NULL);
+ ASSERT(currentClientConn->buf == NULL);
+
+ currentClientConn->bufLen = CLIENT_CONN_SEND_RECV_BUF_SIZE;
+ currentClientConn->buf = Util_SafeMalloc(currentClientConn->bufLen);
+
+ return RecvHttpRequestFromCurrentClientConn(currentClientConn->buf,
+ currentClientConn->bufLen);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * StopRecvFromCurrentClientConn --
+ *
+ * Stop receiving from the current client connection, safe to call in
+ * the same connection recv callback.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static inline void
+StopRecvFromCurrentClientConn(void)
+{
+ int res = AsyncSocket_CancelRecvEx(currentClientConn->asock,
+ NULL, NULL, NULL, TRUE);
+ if (res != ASOCKERR_SUCCESS) {
+ g_warning("AsyncSocket_CancelRecvEx failed "
+ "on current client connection %d: %s\n",
+ AsyncSocket_GetFd(currentClientConn->asock),
+ AsyncSocket_Err2String(res));
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * SendToCurrentClientConn --
+ *
+ * Send to the current client connection.
+ *
+ * Results:
+ * TRUE on success, FALSE otherwise.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+SendToCurrentClientConn(void *buf, // IN
+ int len) // IN
+{
+ int res;
+
+ //g_debug("Entering %s: len=%d.\n", __FUNCTION__, len);
+
+ ASSERT(currentClientConn != NULL);
+ ASSERT(currentClientConn->asock != NULL);
+
+ res = AsyncSocket_Send(currentClientConn->asock, buf, len,
+ CurrentClientConnSendCb, currentClientConn);
+ if (res != ASOCKERR_SUCCESS) {
+ g_warning("AsyncSocket_Send failed "
+ "on current client connection %d: %s\n",
+ AsyncSocket_GetFd(currentClientConn->asock),
+ AsyncSocket_Err2String(res));
+ HandleCurrentClientConnError();
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * SendHttpResponseToCurrentClientConn --
+ *
+ * Send HTTP response head to the current client connection.
+ *
+ * Results:
+ * TRUE on success, FALSE otherwise.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+SendHttpResponseToCurrentClientConn(const char *headFmt, // IN
+ int64 contentLen, // IN
+ Bool shutdown) // IN
+{
+ gchar *utcStr;
+ int len;
+
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ ASSERT(currentClientConn != NULL);
+ ASSERT(currentClientConn->asock != NULL);
+
+ utcStr = GetCurrentUtcStr();
+ len = Str_Sprintf(currentClientConn->buf, currentClientConn->bufLen,
+ headFmt,
+ utcStr != NULL ? utcStr : "", contentLen);
+ g_free(utcStr);
+
+ currentClientConn->shutDown = shutdown;
+ return SendToCurrentClientConn(currentClientConn->buf, len);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * SendConnectRequestToVmx --
+ *
+ * Request VMX to connect to our VSOCK listening port via RPC command.
+ *
+ * This function should be called when pluginData.vmxConnectRequested
+ * is FALSE.
+ *
+ * Results:
+ * TRUE on success, FALSE otherwise
+ *
+ * Side-effects:
+ * All outstanding client connections are closed if failed.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+SendConnectRequestToVmx(void)
+{
+ Bool retVal;
+ int fd;
+ struct sockaddr_vm addr;
+#ifdef _WIN32
+ int addrLen = (int)sizeof(addr);
+#else
+ socklen_t addrLen = (socklen_t)sizeof(addr);
+#endif
+ char msg[32];
+ int msgLen;
+ char *result;
+ size_t resultLen;
+ RpcChannelType rpcChannelType;
+
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ ASSERT(!pluginData.vmxConnectRequested);
+ ASSERT(theVmxConn == NULL);
+ ASSERT(pluginData.vmxListenSock != NULL);
+
+ fd = AsyncSocket_GetFd(pluginData.vmxListenSock);
+
+ /*
+ * Get the listening port.
+ */
+#ifdef _WIN32
+ /* coverity[negative_returns] */
+ if (0 != getsockname((SOCKET)fd, (struct sockaddr *)&addr, &addrLen))
+#else
+ /* coverity[negative_returns] */
+ if (0 != getsockname(fd, (struct sockaddr *)&addr, &addrLen))
+#endif
+ {
+ g_warning("getsockname failed on VMX listening socket %d: sockerr=%d.\n",
+ fd, sockerr());
+ retVal = FALSE;
+ goto exit;
+ }
+
+ msgLen = Str_Sprintf(msg, sizeof msg,
+ "guestStore.connect %d", addr.svm_port);
+ result = NULL;
+ rpcChannelType = RpcChannel_GetType(pluginData.ctx->rpc);
+ g_debug("Current guest RPC channel type: %d.\n", rpcChannelType);
+
+ /*
+ * "guestStore.connect" is a privileged guest RPC that should
+ * go through a privileged vSock RPC channel.
+ */
+ if (rpcChannelType == RPCCHANNEL_TYPE_PRIV_VSOCK) {
+ retVal = RpcChannel_Send(pluginData.ctx->rpc, msg, msgLen,
+ &result, &resultLen);
+ } else {
+ /*
+ * After the vmsvc RPC channel falls back to backdoor, it could not
+ * send through privileged guest RPC any more.
+ */
+ retVal = RpcChannel_SendOneRawPriv(msg, msgLen,
+ &result, &resultLen);
+ }
+
+ if (retVal) {
+ g_info("Connect request sent to VMX.\n");
+ } else {
+ g_warning("Failed to send connect request to VMX: %s.\n",
+ result != NULL ? result : "");
+ }
+ vm_free(result);
+
+exit:
+ if (!retVal) {
+ CloseCurrentClientConn();
+ CloseClientConnsInWait();
+ } else {
+ StartVmxToGuestConnTimeout();
+ }
+
+ pluginData.vmxConnectRequested = retVal;
+ return retVal;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * SendDataMapToVmxConn --
+ *
+ * Send a data map to the VMX connection.
+ *
+ * After received request path from the current client connection, data
+ * map field GUESTSTORE_REQ_FLD_PATH with the request path is sent to
+ * the VMX connection. VMX will send back a response data map with error
+ * code.
+ *
+ * When no more client to serve, initiate shutdown VMX connection by
+ * sending data map field GUESTSTORE_REQ_FLD_NONE to the VMX connection
+ * so that VMX side can close its vsocket.
+ *
+ * Results:
+ * TRUE on success, FALSE otherwise.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+SendDataMapToVmxConn(void)
+{
+ int fd;
+ ErrorCode res;
+ DataMap map;
+ Bool mapCreated = FALSE;
+ int cmdType;
+ char *serBuf = NULL;
+ uint32 serBufLen;
+ int resSock;
+ Bool retVal = FALSE;
+
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ ASSERT(theVmxConn != NULL);
+ ASSERT(theVmxConn->asock != NULL);
+
+ fd = AsyncSocket_GetFd(theVmxConn->asock);
+
+ res = DataMap_Create(&map);
+ if (res != DMERR_SUCCESS) {
+ g_warning("DataMap_Create failed for VMX connection %d: error=%d.\n",
+ fd, res);
+ goto exit;
+ }
+
+ mapCreated = TRUE;
+
+ if (currentClientConn == NULL) {
+ /*
+ * No client to serve, inform VMX side to close its vsocket proactively,
+ * rather than waiting for ASOCKERR_REMOTE_DISCONNECT (4) error callback
+ * which may never happen.
+ */
+ ASSERT(!theVmxConn->shutDown);
+
+ theVmxConn->shutDown = TRUE;
+ StopRecvFromVmxConn();
+ cmdType = GUESTSTORE_REQ_CMD_CLOSE;
+ } else {
+ char *str;
+
+ ASSERT(ReceivedHttpRequestFromCurrentClientConn());
+
+ str = Util_SafeStrdup(currentClientConn->requestPath);
+ res = DataMap_SetString(&map, GUESTSTORE_REQ_FLD_PATH, str, -1, TRUE);
+ if (res != DMERR_SUCCESS) {
+ g_warning("DataMap_SetString (field path) failed "
+ "for VMX connection %d: error=%d.\n", fd, res);
+ free(str);
+ goto exit;
+ }
+
+ cmdType = GUESTSTORE_REQ_CMD_GET;
+ }
+
+ res = DataMap_SetInt64(&map, GUESTSTORE_REQ_FLD_CMD, cmdType, TRUE);
+ if (res != DMERR_SUCCESS) {
+ g_warning("DataMap_SetInt64 (field cmd) failed "
+ "for VMX connection %d: error=%d.\n", fd, res);
+ goto exit;
+ }
+
+ res = DataMap_Serialize(&map, &serBuf, &serBufLen);
+ if (res != DMERR_SUCCESS) {
+ g_warning("DataMap_Serialize failed "
+ "for VMX connection %d: error=%d.\n", fd, res);
+ goto exit;
+ }
+
+ if (serBufLen > theVmxConn->bufLen) {
+ g_warning("Data map to VMX connection %d is too large: length=%d.\n",
+ fd, serBufLen);
+ goto exit;
+ }
+
+ memcpy(theVmxConn->buf, serBuf, serBufLen);
+ resSock = AsyncSocket_Send(theVmxConn->asock,
+ theVmxConn->buf, serBufLen,
+ VmxConnSendDataMapCb, theVmxConn);
+ if (resSock != ASOCKERR_SUCCESS) {
+ g_warning("AsyncSocket_Send failed on VMX connection %d: %s\n",
+ fd, AsyncSocket_Err2String(resSock));
+ goto exit;
+ }
+
+ retVal = TRUE;
+
+exit:
+ if (mapCreated) {
+ free(serBuf);
+ DataMap_Destroy(&map);
+ }
+
+ if (!retVal) {
+ HandleVmxConnError();
+ }
+
+ return retVal;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * RecvDataMapFromVmxConn --
+ *
+ * Start receiving data map from the VMX connection.
+ *
+ * Results:
+ * TURE on success, FALSE otherwise.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+RecvDataMapFromVmxConn(void *buf, // OUT
+ int len) // IN
+{
+ int res;
+
+ g_debug("Entering %s: len=%d.\n", __FUNCTION__, len);
+
+ ASSERT(theVmxConn != NULL);
+ ASSERT(theVmxConn->asock != NULL);
+
+ res = AsyncSocket_Recv(theVmxConn->asock, buf, len,
+ VmxConnRecvDataMapCb, theVmxConn);
+ if (res != ASOCKERR_SUCCESS) {
+ g_warning("AsyncSocket_Recv failed on VMX connection %d: %s\n",
+ AsyncSocket_GetFd(theVmxConn->asock),
+ AsyncSocket_Err2String(res));
+ HandleVmxConnError();
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * StopRecvFromVmxConn --
+ *
+ * Stop receiving from the VMX connection, safe to call in the same
+ * connection recv callback.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static inline void
+StopRecvFromVmxConn(void)
+{
+ int res = AsyncSocket_CancelRecvEx(theVmxConn->asock,
+ NULL, NULL, NULL, TRUE);
+ if (res != ASOCKERR_SUCCESS) {
+ g_warning("AsyncSocket_CancelRecvEx failed on VMX connection %d: %s\n",
+ AsyncSocket_GetFd(theVmxConn->asock),
+ AsyncSocket_Err2String(res));
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * ProcessVmxDataMap --
+ *
+ * Process the data map received from the VMX connection.
+ *
+ * The data map should contain field GUESTSTORE_RES_FLD_ERROR_CODE. In
+ * success case, field GUESTSTORE_RES_FLD_CONTENT_SIZE should also exist
+ * with the content size and the data map is followed by content bytes.
+ *
+ * Results:
+ * TRUE on success, FALSE on error.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+ProcessVmxDataMap(const DataMap *map) // IN
+{
+ int fd;
+ ErrorCode res;
+ int64 errorCode;
+
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ ASSERT(theVmxConn != NULL);
+ ASSERT(theVmxConn->asock != NULL);
+
+ fd = AsyncSocket_GetFd(theVmxConn->asock);
+
+ res = DataMap_GetInt64(map, GUESTSTORE_RES_FLD_ERROR_CODE, &errorCode);
+ if (res != DMERR_SUCCESS) {
+ g_warning("DataMap_GetInt64 (field error code) failed in data map "
+ "from VMX connection %d: error=%d.\n", fd, res);
+ goto error;
+ }
+
+ ASSERT(currentClientConn != NULL);
+ ASSERT(currentClientConn->asock != NULL);
+
+ switch ((int32)errorCode) {
+ case 0: // ERROR_SUCCESS
+ {
+ int64 contentSize;
+ res = DataMap_GetInt64(map, GUESTSTORE_RES_FLD_CONTENT_SIZE,
+ &contentSize);
+ if (res != DMERR_SUCCESS) {
+ g_warning("DataMap_GetInt64 (field content size) failed "
+ "in data map from VMX connection %d: error=%d.\n",
+ fd, res);
+ goto error;
+ }
+
+ if (contentSize < 0) {
+ g_warning("Invalid content size in data map "
+ "from VMX connection %d: contentSize=%" FMT64 "d.\n",
+ fd, contentSize);
+ goto error;
+ }
+
+ theVmxConn->bytesRemaining = contentSize;
+ return SendHttpResponseOKToCurrentClientConn(contentSize);
+ }
+ case EPERM:
+ {
+ return SendHttpResponseForbiddenToCurrentClientConn();
+ }
+ case ENOENT:
+ {
+ return SendHttpResponseNotFoundToCurrentClientConn();
+ }
+ default:
+ g_warning("Unexpected error code value %" FMT64 "d in data map "
+ "from VMX connection %d.\n", errorCode, fd);
+ break;
+ }
+
+error:
+ HandleVmxConnError();
+ return FALSE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * RecvContentFromVmxConn --
+ *
+ * Start receiving content bytes from the VMX connection.
+ *
+ * Results:
+ * TURE on success, FALSE otherwise.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+RecvContentFromVmxConn(void)
+{
+ int res;
+
+ //g_debug("Entering %s.\n", __FUNCTION__);
+
+ ASSERT(theVmxConn != NULL);
+ ASSERT(theVmxConn->asock != NULL);
+
+ res = AsyncSocket_RecvPartial(theVmxConn->asock,
+ theVmxConn->buf,
+ theVmxConn->bufLen,
+ VmxConnRecvContentCb,
+ theVmxConn);
+ if (res != ASOCKERR_SUCCESS) {
+ g_warning("AsyncSocket_RecvPartial failed on VMX connection %d: %s\n",
+ AsyncSocket_GetFd(theVmxConn->asock),
+ AsyncSocket_Err2String(res));
+ HandleVmxConnError();
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * StartCurrentClientConnRecvTimeout --
+ *
+ * Start the current client connection recv timeout.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+StartCurrentClientConnRecvTimeout(void)
+{
+ int clientRecvTimeout;
+
+ ASSERT(currentClientConn->timeoutSource == NULL);
+
+ clientRecvTimeout = GUESTSTORE_CONFIG_GET_INT("clientRecvTimeout",
+ DEFAULT_CLIENT_RECV_TIMEOUT);
+ if (clientRecvTimeout <= 0) {
+ g_warning("Invalid clientRecvTimeout (%d); Using default (%d).\n",
+ clientRecvTimeout, DEFAULT_CLIENT_RECV_TIMEOUT);
+ clientRecvTimeout = DEFAULT_CLIENT_RECV_TIMEOUT;
+ }
+
+ currentClientConn->timeoutSource = g_timeout_source_new(
+ clientRecvTimeout * 1000);
+ VMTOOLSAPP_ATTACH_SOURCE(pluginData.ctx,
+ currentClientConn->timeoutSource,
+ CurrentClientConnRecvTimeoutCb,
+ currentClientConn, NULL);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * StopClientConnRecvTimeout --
+ *
+ * Stop client connection recv timeout.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static inline void
+StopClientConnRecvTimeout(ClientConnInfo *clientConn) // IN
+{
+ ASSERT(clientConn != NULL);
+
+ if (clientConn->timeoutSource != NULL) {
+ g_source_destroy(clientConn->timeoutSource);
+ g_source_unref(clientConn->timeoutSource);
+ clientConn->timeoutSource = NULL;
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * StopCurrentClientConnRecvTimeout --
+ *
+ * Stop the current client connection recv timeout.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static inline void
+StopCurrentClientConnRecvTimeout(void)
+{
+ StopClientConnRecvTimeout(currentClientConn);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CurrentClientConnRecvTimeoutCb --
+ *
+ * Poll callback function for the current client connection recv timeout.
+ *
+ * Results:
+ * The current client connection is closed.
+ * The timeout source is removed from poll.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+CurrentClientConnRecvTimeoutCb(gpointer clientData) // IN
+{
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ ASSERT(currentClientConn == clientData);
+ ASSERT(currentClientConn != NULL);
+ ASSERT(currentClientConn->asock != NULL);
+
+ g_warning("The current client connection %d recv timed out.\n",
+ AsyncSocket_GetFd(currentClientConn->asock));
+
+ /*
+ * Follow the pattern in ConnInactivityTimeoutCb()
+ */
+ StopCurrentClientConnRecvTimeout();
+
+ HandleCurrentClientConnError();
+
+ return G_SOURCE_REMOVE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * StartVmxToGuestConnTimeout --
+ *
+ * Start VMX to guest connection timeout.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static inline void
+StartVmxToGuestConnTimeout(void)
+{
+ ASSERT(pluginData.timeoutSource == NULL);
+
+ pluginData.timeoutSource = g_timeout_source_new(
+ GUESTSTORE_VMX_TO_GUEST_CONN_TIMEOUT * 1000);
+ VMTOOLSAPP_ATTACH_SOURCE(pluginData.ctx,
+ pluginData.timeoutSource,
+ VmxToGuestConnTimeoutCb,
+ NULL, NULL);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * StopVmxToGuestConnTimeout --
+ *
+ * Stop VMX to guest connection timeout.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static inline void
+StopVmxToGuestConnTimeout(void)
+{
+ if (pluginData.timeoutSource != NULL) {
+ g_source_destroy(pluginData.timeoutSource);
+ g_source_unref(pluginData.timeoutSource);
+ pluginData.timeoutSource = NULL;
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VmxToGuestConnTimeoutCb --
+ *
+ * Poll callback function for VMX to guest connection timeout.
+ *
+ * Results:
+ * All outstanding client connections are closed.
+ * The timeout source is removed from poll.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+VmxToGuestConnTimeoutCb(gpointer clientData) // IN
+{
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ ASSERT(theVmxConn == NULL);
+
+ g_warning("VMX to guest connection timed out.\n");
+
+ StopVmxToGuestConnTimeout();
+
+ CloseCurrentClientConn();
+ CloseClientConnsInWait();
+
+ pluginData.vmxConnectRequested = FALSE;
+
+ return G_SOURCE_REMOVE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * StartConnInactivityTimeout --
+ *
+ * Start connection inactivity timeout.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static inline void
+StartConnInactivityTimeout(void)
+{
+ ASSERT(theVmxConn != NULL);
+ ASSERT(theVmxConn->timeoutSource == NULL);
+ ASSERT(theVmxConn->connTimeout != 0);
+
+ theVmxConn->timeoutSource = g_timeout_source_new(
+ theVmxConn->connTimeout * 1000);
+ VMTOOLSAPP_ATTACH_SOURCE(pluginData.ctx,
+ theVmxConn->timeoutSource,
+ ConnInactivityTimeoutCb,
+ theVmxConn, NULL);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * StopConnInactivityTimeout --
+ *
+ * Stop connection inactivity timeout.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static inline void
+StopConnInactivityTimeout(void)
+{
+ ASSERT(theVmxConn != NULL);
+
+ if (theVmxConn->timeoutSource != NULL) {
+ g_source_destroy(theVmxConn->timeoutSource);
+ g_source_unref(theVmxConn->timeoutSource);
+ theVmxConn->timeoutSource = NULL;
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * ConnInactivityTimeoutCb --
+ *
+ * Poll callback function for connection inactivity timeout.
+ *
+ * Results:
+ * The VMX connection and the current client connection are closed.
+ * The timeout source is removed from poll.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+ConnInactivityTimeoutCb(gpointer clientData) // IN
+{
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ ASSERT(theVmxConn == clientData);
+ ASSERT(theVmxConn != NULL);
+ ASSERT(theVmxConn->asock != NULL);
+
+ g_warning("Connection inactivity timed out.\n");
+
+ /*
+ * Issue observed:
+ * If g_source_destroy() is not called on the inactivity timeout source
+ * and the next client connection in the waiting list becomes current
+ * and starts its new recv timeout source, g_main_dispatch() does not
+ * remove the inactivity timeout source after this callback returns
+ * G_SOURCE_REMOVE (FALSE).
+ *
+ * Solution:
+ * Call g_source_destroy() before the new timeout source starts.
+ * After this callback returns G_SOURCE_REMOVE (FALSE), g_main_dispatch()
+ * detects the inactivity timeout source destroyed and skips same action.
+ */
+ StopConnInactivityTimeout();
+
+ CloseActiveConnections();
+
+ return G_SOURCE_REMOVE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * ClientConnErrorCb --
+ *
+ * Client connection error handler for asyncsocket.
+ *
+ * Results:
+ * The connection is closed.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+ClientConnErrorCb(int err, // IN
+ AsyncSocket *asock, // IN
+ void *clientData) // IN
+{
+ ClientConnInfo *clientConn = (ClientConnInfo *)clientData;
+
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ ASSERT(clientConn->asock != NULL);
+ g_info("Client connection %d error callback: %s\n",
+ AsyncSocket_GetFd(clientConn->asock),
+ AsyncSocket_Err2String(err));
+
+ if (clientConn->isCurrent) {
+ ASSERT(currentClientConn == clientConn);
+ HandleCurrentClientConnError();
+ } else {
+ CloseClientConn(clientConn);
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CurrentClientConnSendCb --
+ *
+ * Callback function after sent to the current client connection.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+CurrentClientConnSendCb(void *buf, // IN
+ int len, // IN
+ AsyncSocket *asock, // IN
+ void *clientData) // IN
+{
+ //g_debug("Entering %s: len=%d.\n", __FUNCTION__, len);
+
+ ASSERT(currentClientConn == clientData);
+ ASSERT(currentClientConn != NULL);
+ ASSERT(currentClientConn->asock != NULL);
+
+ if (AsyncSocket_GetState(currentClientConn->asock) !=
+ AsyncSocketConnected) {
+ /*
+ * This callback may be called after the connection is closed for
+ * freeing the send buffer.
+ */
+ return;
+ }
+
+ ASSERT(theVmxConn != NULL);
+ ASSERT(theVmxConn->timeoutSource != NULL);
+
+ /*
+ * Restart connection inactivity timeout.
+ */
+ StopConnInactivityTimeout();
+ StartConnInactivityTimeout();
+
+ if (currentClientConn->shutDown) {
+ g_info("Finished with current client connection %d.\n",
+ AsyncSocket_GetFd(currentClientConn->asock));
+
+ CloseCurrentClientConn();
+ StartServeNextClientConn();
+ } else {
+ ASSERT(theVmxConn->bytesRemaining > 0);
+
+ RecvContentFromVmxConn();
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CurrentClientConnRecvHttpRequestCb --
+ *
+ * Callback function after received from the current client connection.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+CurrentClientConnRecvHttpRequestCb(void *buf, // IN
+ int len, // IN
+ AsyncSocket *asock, // IN
+ void *clientData) // IN
+{
+ int fd;
+ int recvLen;
+ char *next_token;
+ char *requestMethod;
+ char *requestPath;
+
+ g_debug("Entering %s: len=%d.\n", __FUNCTION__, len);
+
+ ASSERT(currentClientConn == clientData);
+ ASSERT(currentClientConn != NULL);
+ ASSERT(currentClientConn->asock != NULL);
+
+ fd = AsyncSocket_GetFd(currentClientConn->asock);
+
+ recvLen = (int)((char *)buf - currentClientConn->buf) + len;
+ if (recvLen >= currentClientConn->bufLen) {
+ g_warning("Recv from current client connection %d "
+ "reached buffer limit.\n", fd);
+ goto error;
+ }
+
+ /*
+ * Check for HTTP request end.
+ */
+ if (recvLen < HTTP_HEADER_END_LEN ||
+ strncmp(currentClientConn->buf + recvLen - HTTP_HEADER_END_LEN,
+ HTTP_HEADER_END, HTTP_HEADER_END_LEN) != 0) {
+ RecvHttpRequestFromCurrentClientConn(currentClientConn->buf +
+ recvLen,
+ currentClientConn->bufLen -
+ recvLen);
+ return;
+ }
+
+ StopCurrentClientConnRecvTimeout();
+
+ *(currentClientConn->buf + recvLen) = '\0';
+ g_debug("HTTP request from current client connection %d:\n%s\n",
+ fd, currentClientConn->buf);
+
+ requestMethod = strtok_r(currentClientConn->buf, " ", &next_token);
+ if (NULL == requestMethod ||
+ strcmp(requestMethod, HTTP_REQ_METHOD_GET) != 0) {
+ g_warning("Invalid HTTP request method.\n");
+ goto error;
+ }
+
+ /*
+ * Ignore HTTP query part.
+ */
+ requestPath = strtok_r(NULL, "? ", &next_token);
+ if (NULL == requestPath) {
+ g_warning("HTTP request path not found.\n");
+ goto error;
+ }
+
+ currentClientConn->requestPath = g_uri_unescape_string(requestPath, NULL);
+ if (NULL == currentClientConn->requestPath ||
+ '/' != *currentClientConn->requestPath ||
+ strlen(currentClientConn->requestPath) > GUESTSTORE_CONTENT_PATH_MAX) {
+ g_warning("Invalid HTTP request path.\n");
+ goto error;
+ }
+
+ g_info("HTTP request path from current client connection %d: \"%s\"",
+ fd, currentClientConn->requestPath);
+
+ StopRecvFromCurrentClientConn();
+
+ if (!pluginData.vmxConnectRequested) {
+ ASSERT(theVmxConn == NULL);
+ SendConnectRequestToVmx();
+ } else {
+ CheckSendRequestDataMapToVmxConn();
+ }
+
+ return;
+
+error:
+ HandleCurrentClientConnError();
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * ClientConnectCb --
+ *
+ * Poll callback function for a new client connection.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+ClientConnectCb(AsyncSocket *asock, // IN
+ void *clientData) // IN
+{
+ int fd = AsyncSocket_GetFd(asock);
+ int maxConnections;
+ ClientConnInfo *clientConn = NULL;
+ int res;
+
+ g_debug("Entering %s.\n", __FUNCTION__);
+ g_info("Got new client connection %d.\n", fd);
+
+ if (AsyncSocket_GetState(asock) != AsyncSocketConnected) {
+ g_info("Client connection %d is not in connected state.\n", fd);
+ goto error;
+ }
+
+ maxConnections = GUESTSTORE_CONFIG_GET_INT("maxConnections",
+ DEFAULT_MAX_CLIENT_CONNECTIONS);
+ if ((g_list_length(pluginData.clientConnWaitList) +
+ ((currentClientConn != NULL) ? 1 : 0)) >= maxConnections) {
+ g_info("Client connection %d has exceeded maximum limit "
+ "of %d client connections.\n", fd, maxConnections);
+ goto error;
+ }
+
+#ifdef _WIN32
+ CheckAndUpdateAdminOnly();
+
+ /* coverity[negative_returns] */
+ if (pluginData.adminOnly && !IsAdminClient(fd)) {
+ g_info("Decline non admin/root client connection %d.\n", fd);
+ goto error;
+ }
+#endif
+
+ if (!AsyncSocket_EstablishMinBufferSizes(asock,
+ GUESTSTORE_RESPONSE_BUFFER_SIZE, // sendSz
+ GUESTSTORE_REQUEST_BUFFER_SIZE)) { // recvSz
+ g_warning("AsyncSocket_EstablishMinBufferSizes failed "
+ "on client connection %d.\n", fd);
+ goto error;
+ }
+
+ clientConn = (ClientConnInfo *)Util_SafeCalloc(1, sizeof *clientConn);
+ clientConn->asock = asock;
+
+ res = AsyncSocket_SetErrorFn(asock, ClientConnErrorCb, clientConn);
+ if (res != ASOCKERR_SUCCESS) {
+ g_warning("AsyncSocket_SetErrorFn failed on client connection %d: %s\n",
+ fd, AsyncSocket_Err2String(res));
+ goto error;
+ }
+
+ if (currentClientConn == NULL) {
+ /*
+ * Make the first client connection be the current client connection.
+ */
+ currentClientConn = clientConn;
+ currentClientConn->isCurrent = TRUE;
+ StartRecvHttpRequestFromCurrentClientConn();
+ } else {
+ pluginData.clientConnWaitList = g_list_append(
+ pluginData.clientConnWaitList, clientConn);
+ }
+
+ return;
+
+error:
+ g_info("Closing client connection %d.\n", fd);
+ AsyncSocket_Close(asock);
+ free(clientConn);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VmxConnErrorCb --
+ *
+ * The VMX connection error handler for asyncsocket.
+ *
+ * Results:
+ * The connection is closed.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+VmxConnErrorCb(int err, // IN
+ AsyncSocket *asock, // IN
+ void *clientData) // IN
+{
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ ASSERT(theVmxConn == clientData);
+ ASSERT(theVmxConn != NULL);
+ ASSERT(theVmxConn->asock != NULL);
+ g_info("VMX connection %d error callback: %s\n",
+ AsyncSocket_GetFd(theVmxConn->asock),
+ AsyncSocket_Err2String(err));
+
+ HandleVmxConnError();
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VmxConnSendDataMapCb --
+ *
+ * Callback function after sent to the VMX connection.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+VmxConnSendDataMapCb(void *buf, // IN
+ int len, // IN
+ AsyncSocket *asock, // IN
+ void *clientData) // IN
+{
+ int fd;
+
+ g_debug("Entering %s: len=%d.\n", __FUNCTION__, len);
+
+ ASSERT(theVmxConn == clientData);
+ ASSERT(theVmxConn != NULL);
+ ASSERT(theVmxConn->asock != NULL);
+
+ fd = AsyncSocket_GetFd(theVmxConn->asock);
+
+ if (AsyncSocket_GetState(theVmxConn->asock) != AsyncSocketConnected) {
+ /*
+ * This callback may be called after the connection is closed for
+ * freeing the send buffer.
+ */
+ return;
+ }
+
+ if (theVmxConn->shutDown) {
+ g_info("Shut down VMX connection %d.\n", fd);
+ CloseVmxConn();
+
+ if (pluginData.guestStoreAccessEnabled) {
+ if (currentClientConn == NULL) {
+ StartServeNextClientConn();
+ } else if (ReceivedHttpRequestFromCurrentClientConn()) {
+ SendConnectRequestToVmx();
+ }
+ }
+ } else {
+ RecvDataMapFromVmxConn(&theVmxConn->dataMapLen,
+ (int)sizeof(theVmxConn->dataMapLen));
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VmxConnRecvDataMapCb --
+ *
+ * Callback function after received data map from the VMX connection.
+ *
+ * VMX responds with a data map, followed by content bytes if no error.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+VmxConnRecvDataMapCb(void *buf, // IN
+ int len, // IN
+ AsyncSocket *asock, // IN
+ void *clientData) // IN
+{
+ int fd;
+
+ g_debug("Entering %s: len=%d.\n", __FUNCTION__, len);
+
+ ASSERT(theVmxConn == clientData);
+ ASSERT(theVmxConn != NULL);
+ ASSERT(theVmxConn->asock != NULL);
+
+ fd = AsyncSocket_GetFd(theVmxConn->asock);
+
+ if (buf == &theVmxConn->dataMapLen) {
+ int dataMapLen = ntohl(theVmxConn->dataMapLen);
+
+ ASSERT(len == sizeof theVmxConn->dataMapLen);
+
+ if (dataMapLen > (theVmxConn->bufLen - sizeof theVmxConn->dataMapLen)) {
+ g_warning("Data map from VMX connection %d "
+ "is too large: length=%d.\n", fd, dataMapLen);
+ goto error;
+ }
+
+ *((int32 *)(theVmxConn->buf)) = theVmxConn->dataMapLen;
+ RecvDataMapFromVmxConn(theVmxConn->buf + sizeof theVmxConn->dataMapLen,
+ dataMapLen);
+ } else {
+ ErrorCode res;
+ DataMap map;
+
+ ASSERT(buf == (theVmxConn->buf + sizeof theVmxConn->dataMapLen));
+ ASSERT(len == ntohl(theVmxConn->dataMapLen));
+
+ res = DataMap_Deserialize(theVmxConn->buf,
+ len + (int)sizeof(theVmxConn->dataMapLen),
+ &map);
+ if (res != DMERR_SUCCESS) {
+ g_warning("DataMap_Deserialize failed for data map "
+ "from VMX connection %d: error=%d.\n", fd, res);
+ goto error;
+ }
+
+ StopRecvFromVmxConn();
+ ProcessVmxDataMap(&map);
+ DataMap_Destroy(&map);
+ }
+
+ return;
+
+error:
+ HandleVmxConnError();
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VmxConnRecvContentCb --
+ *
+ * Callback function after received content bytes from the VMX connection.
+ *
+ * VMX responds with a data map, followed by content bytes if no error.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+VmxConnRecvContentCb(void *buf, // IN
+ int len, // IN
+ AsyncSocket *asock, // IN
+ void *clientData) // IN
+{
+ //g_debug("Entering %s: len=%d.\n", __FUNCTION__, len);
+
+ ASSERT(theVmxConn == clientData);
+ ASSERT(theVmxConn != NULL);
+ ASSERT(theVmxConn->asock != NULL);
+
+ theVmxConn->bytesRemaining -= len;
+ if (theVmxConn->bytesRemaining < 0) {
+ g_warning("Recv from VMX connection %d exceeded content size.\n",
+ AsyncSocket_GetFd(theVmxConn->asock));
+ HandleVmxConnError();
+ return;
+ }
+
+ StopRecvFromVmxConn();
+
+ if (theVmxConn->bytesRemaining == 0) {
+ currentClientConn->shutDown = TRUE;
+ }
+
+ SendToCurrentClientConn(buf, len);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VmxConnectCb --
+ *
+ * Poll callback function for a new VMX connection.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+VmxConnectCb(AsyncSocket *asock, // IN
+ void *clientData) // IN
+{
+ int fd = AsyncSocket_GetFd(asock);
+ int res;
+
+ g_debug("Entering %s.\n", __FUNCTION__);
+ g_info("Got new VMX connection %d.\n", fd);
+
+ StopVmxToGuestConnTimeout();
+
+ if (!pluginData.vmxConnectRequested) {
+ g_warning("Closing the unexpected VMX connection %d.\n", fd);
+ AsyncSocket_Close(asock);
+ return;
+ }
+
+ if (theVmxConn != NULL) {
+ g_warning("The VMX connection already exists, closing the extra "
+ "VMX connection %d.\n", fd);
+ AsyncSocket_Close(asock);
+ return;
+ }
+
+ if (AsyncSocket_GetState(asock) != AsyncSocketConnected) {
+ g_info("VMX connection %d is not in connected state.\n", fd);
+ goto error;
+ }
+
+ if (!AsyncSocket_EstablishMinBufferSizes(asock,
+ GUESTSTORE_REQUEST_BUFFER_SIZE, // sendSz
+ GUESTSTORE_RESPONSE_BUFFER_SIZE)) { // recvSz
+ g_warning("AsyncSocket_EstablishMinBufferSizes failed "
+ "on VMX connection %d.\n", fd);
+ goto error;
+ }
+
+ theVmxConn = (VmxConnInfo *)Util_SafeCalloc(1, sizeof *theVmxConn);
+
+ theVmxConn->asock = asock;
+
+ res = AsyncSocket_SetErrorFn(asock, VmxConnErrorCb, theVmxConn);
+ if (res != ASOCKERR_SUCCESS) {
+ g_warning("AsyncSocket_SetErrorFn failed "
+ "on VMX connection %d: %s\n",
+ fd, AsyncSocket_Err2String(res));
+ goto error;
+ }
+
+ theVmxConn->bufLen = VMX_CONN_SEND_RECV_BUF_SIZE;
+ theVmxConn->buf = Util_SafeMalloc(theVmxConn->bufLen);
+
+ theVmxConn->connTimeout = GUESTSTORE_CONFIG_GET_INT("connTimeout",
+ GUESTSTORE_DEFAULT_CONN_TIMEOUT);
+ if (theVmxConn->connTimeout <= 0) {
+ g_warning("Invalid connTimeout (%d); Using default (%d).\n",
+ theVmxConn->connTimeout, GUESTSTORE_DEFAULT_CONN_TIMEOUT);
+ theVmxConn->connTimeout = GUESTSTORE_DEFAULT_CONN_TIMEOUT;
+ }
+
+ StartConnInactivityTimeout();
+
+ if (currentClientConn == NULL) {
+ StartServeNextClientConn();
+ } else {
+ CheckSendRequestDataMapToVmxConn();
+ }
+
+ return;
+
+error:
+ g_info("Closing VMX connection %d.\n", fd);
+ AsyncSocket_Close(asock);
+ if (theVmxConn != NULL) {
+ free(theVmxConn);
+ theVmxConn = NULL;
+ }
+
+ CloseCurrentClientConn();
+ CloseClientConnsInWait();
+ pluginData.vmxConnectRequested = FALSE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CreateVmxListenSocket --
+ *
+ * Create listening vsocket to accept connection from VMX.
+ * The auto-assigned port number will be sent to VMX via guest RPC.
+ *
+ * Results:
+ * TRUE on success, FALSE otherwise
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+CreateVmxListenSocket(void)
+{
+ int res = ASOCKERR_SUCCESS;
+ AsyncSocket *asock;
+
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ ASSERT(pluginData.vmxListenSock == NULL);
+
+ asock = AsyncSocket_ListenVMCI(VMCISock_GetLocalCID(), VMADDR_PORT_ANY,
+ VmxConnectCb, NULL, NULL, &res);
+ if (NULL == asock || res != ASOCKERR_SUCCESS) {
+ g_warning("AsyncSocket_ListenVMCI failed: %s\n",
+ AsyncSocket_Err2String(res));
+ if (asock != NULL) {
+ AsyncSocket_Close(asock);
+ }
+ return FALSE;
+ }
+
+ pluginData.vmxListenSock = asock;
+
+ return TRUE;
+}
+
+
+#ifdef _WIN32
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CreateClientListenSocket --
+ *
+ * Create listening socket to accept connections from clients.
+ *
+ * Results:
+ * TRUE on success, FALSE otherwise
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+CreateClientListenSocket(void)
+{
+ AsyncSocket *asock = NULL;
+ uint16 port;
+ PortUsage portUseMap[GUESTSTORE_LOOPBACK_PORT_MAX -
+ GUESTSTORE_LOOPBACK_PORT_MIN + 1] = { 0 };
+
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ ASSERT(pluginData.clientListenSock == NULL);
+
+ /*
+ * Use output of GetPortUseMap as a hint, it does not matter
+ * if GetPortUseMap fails.
+ */
+ GetPortUseMap(GUESTSTORE_LOOPBACK_PORT_MIN,
+ GUESTSTORE_LOOPBACK_PORT_MAX,
+ portUseMap);
+
+ for (port = GUESTSTORE_LOOPBACK_PORT_MIN;
+ port <= GUESTSTORE_LOOPBACK_PORT_MAX;
+ port++) {
+ int res = ASOCKERR_SUCCESS;
+ PortUsage *portUse = &portUseMap[port - GUESTSTORE_LOOPBACK_PORT_MIN];
+
+ /*
+ * Use || instead of && to avoid confusion to see a port used by
+ * one service on tcp but another service on tcp6.
+ */
+ if (portUse->inet4 || portUse->inet6) {
+ continue;
+ }
+
+ asock = AsyncSocket_ListenLoopback(port, ClientConnectCb, NULL, NULL, &res);
+ if (asock != NULL) {
+ break;
+ }
+
+ if (res == ASOCKERR_BINDADDRINUSE || res == ASOCK_EADDRINUSE) {
+ g_info("Port %u is already in use.\n", port);
+ } else {
+ g_warning("AsyncSocket_ListenLoopback failed on port %u: %s\n",
+ port, AsyncSocket_Err2String(res));
+ break;
+ }
+ }
+
+ if (asock == NULL) {
+ return FALSE;
+ }
+
+ pluginData.clientListenSock = asock;
+ return TRUE;
+}
+
+#else
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CreateSocketDir --
+ *
+ * Create the UNIX domain socket directory with proper permissions.
+ *
+ * Results:
+ * Return TRUE if the directory is created or already exists, and the
+ * permissions are set properly. Otherwise FALSE.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+CreateSocketDir(const char *sockDir) // IN
+{
+ Bool retVal;
+ int mode = 0755; // Same mode as VGAuth service socket dir
+ struct stat st;
+
+ ASSERT(sockDir != NULL && *sockDir != '\0');
+
+ retVal = File_EnsureDirectoryEx(sockDir, mode);
+ if (!retVal) {
+ g_warning("Unable to create folder %s: error=%d.\n",
+ sockDir, errno);
+ goto exit;
+ }
+
+ /*
+ * Verify the directory owner and permissions.
+ */
+ if (Posix_Lstat(sockDir, &st) != 0) {
+ g_warning("Unable to retrieve the attributes of %s: error=%d.\n",
+ sockDir, errno);
+ retVal = FALSE;
+ goto exit;
+ }
+
+ if (st.st_uid != getuid()) {
+ g_warning("%s has the wrong owner.\n", sockDir);
+ retVal = FALSE;
+ goto exit;
+ }
+
+ if ((st.st_mode & 0777) != mode &&
+ !File_SetFilePermissions(sockDir, (st.st_mode & 07000) | mode)) {
+ g_warning("%s has improper permissions.\n", sockDir);
+ retVal = FALSE;
+ }
+
+exit:
+ return retVal;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * AdjustSocketFilePermissions --
+ *
+ * Adjust the UNIX domain socket file permissions to control who can
+ * connect.
+ *
+ * Results:
+ * Return TRUE if the permissions are set properly. Otherwise FALSE.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+AdjustSocketFilePermissions(const char *sockFile, // IN
+ Bool onlyRootCanConnect) // IN
+{
+ Bool retVal = FALSE;
+ int mode;
+ struct stat st;
+
+ ASSERT(sockFile != NULL && *sockFile != '\0');
+
+ /*
+ * Add sticky bit if everyone can connect.
+ */
+ mode = (onlyRootCanConnect ? 0755 : 01777);
+
+ if (Posix_Lstat(sockFile, &st) != 0) {
+ g_warning("Unable to retrieve the attributes of %s: error=%d.\n",
+ sockFile, errno);
+ goto exit;
+ }
+
+ if ((st.st_mode & 01777) != mode &&
+ !File_SetFilePermissions(sockFile, (st.st_mode & 07000) | mode)) {
+ g_warning("%s has improper permissions.\n", sockFile);
+ goto exit;
+ }
+
+ retVal = TRUE;
+
+exit:
+ return retVal;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CreateClientListenSocket --
+ *
+ * Create listening socket to accept connections from clients.
+ *
+ * Results:
+ * TRUE on success, FALSE otherwise
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+CreateClientListenSocket(void)
+{
+ int res = ASOCKERR_SUCCESS;
+ AsyncSocket *asock;
+
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ ASSERT(pluginData.clientListenSock == NULL);
+
+ CheckAndUpdateAdminOnly();
+
+ if (!CreateSocketDir(GUESTSTORE_PIPE_DIR)) {
+ g_warning("CreateSocketDir failed.\n");
+ return FALSE;
+ }
+
+ File_Unlink(GUESTSTORE_PIPE_NAME);
+
+ asock = AsyncSocket_ListenSocketUDS(
+ GUESTSTORE_PIPE_NAME,
+ ClientConnectCb, NULL,
+ NULL, &res);
+
+ if (asock == NULL || res != ASOCKERR_SUCCESS) {
+ g_warning("AsyncSocket_ListenSocketUDS failed: %s\n",
+ AsyncSocket_Err2String(res));
+
+ if (asock != NULL) {
+ AsyncSocket_Close(asock);
+ }
+
+ return FALSE;
+ }
+
+ /*
+ * Ideally, this should be done after bind() and before listen() and
+ * accept(). Since asyncsocket library shares TCP socket implementation
+ * code, there is no such interface to do it. Doing it here is fine,
+ * because the initial permission settings allow root to connect only.
+ */
+ if (!AdjustSocketFilePermissions(GUESTSTORE_PIPE_NAME,
+ pluginData.adminOnly)) {
+ g_warning("AdjustSocketFilePermissions failed.\n");
+ AsyncSocket_Close(asock);
+ return FALSE;
+ }
+
+ pluginData.clientListenSock = asock;
+ return TRUE;
+}
+
+#endif
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * InitPluginData --
+ *
+ * Init pluginData structure.
+ * - 'ctx': ToolsAppCtx
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+InitPluginData(ToolsAppCtx *ctx) // IN
+{
+ pluginData.ctx = ctx;
+ CheckAndUpdateFeatureDisabled();
+ CheckAndUpdateAdminOnly();
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * InitPluginSignals --
+ *
+ * Init signals for notification.
+ * - 'ctx': ToolsAppCtx
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+InitPluginSignals(ToolsAppCtx* ctx) // IN
+{
+ /* Register the signals we'll use to notify people interested in this event. */
+ g_signal_new(TOOLS_CORE_SIG_GUESTSTORE_STATE,
+ G_OBJECT_TYPE(ctx->serviceObj),
+ 0,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_BOOLEAN);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * GuestStoreAccessDisable --
+ *
+ * Close all sockets/connections and reset plugin internal states.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static void
+GuestStoreAccessDisable(void)
+{
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ if (!pluginData.shutdown) {
+ g_signal_emit_by_name(pluginData.ctx->serviceObj,
+ TOOLS_CORE_SIG_GUESTSTORE_STATE,
+ FALSE);
+ }
+
+ pluginData.guestStoreAccessEnabled = FALSE;
+
+ if (pluginData.vmxListenSock) {
+ AsyncSocket_Close(pluginData.vmxListenSock);
+ pluginData.vmxListenSock = NULL;
+ }
+
+ if (pluginData.clientListenSock) {
+ AsyncSocket_Close(pluginData.clientListenSock);
+ pluginData.clientListenSock = NULL;
+ }
+
+ CloseCurrentClientConn();
+ CloseClientConnsInWait();
+
+ if (theVmxConn != NULL && !theVmxConn->shutDown) {
+ /*
+ * After CloseCurrentClientConn(), send shutdown data map to VMX.
+ */
+ SendDataMapToVmxConn();
+ } else {
+ /*
+ * Force to stop.
+ */
+ CloseVmxConn();
+ StopVmxToGuestConnTimeout();
+ pluginData.vmxConnectRequested = FALSE; // To make sure
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * GuestStoreAccessEnable --
+ *
+ * Create the sockets and start listening.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static void
+GuestStoreAccessEnable(void)
+{
+ g_debug("Entering %s.\n", __FUNCTION__);
+
+ ASSERT(!pluginData.guestStoreAccessEnabled);
+
+ if (!CreateVmxListenSocket() || !CreateClientListenSocket()) {
+ g_warning("GuestStore access is disabled "
+ "due to initialization error.\n");
+ GuestStoreAccessDisable();
+ return;
+ }
+
+ pluginData.guestStoreAccessEnabled = TRUE;
+ g_signal_emit_by_name(pluginData.ctx->serviceObj,
+ TOOLS_CORE_SIG_GUESTSTORE_STATE,
+ TRUE);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GetVmxGuestStoreAccessEnabledState --
+ *
+ * Send a GuestRpc command to VMX to retrieve guestStore.accessEnabled
+ * state.
+ *
+ * Result:
+ * TRUE : access enabled
+ * FALSE: access disabled
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+GetVmxGuestStoreAccessEnabledState(void)
+{
+ Bool retVal;
+ const char *msg = "guestStore.accessEnabled";
+ char *result = NULL;
+ size_t resultLen;
+
+ retVal = RpcChannel_Send(pluginData.ctx->rpc, msg, strlen(msg),
+ &result, &resultLen);
+ if (retVal) {
+ retVal = (strcmp(result, "true") == 0 ? TRUE : FALSE);
+ } else {
+ g_warning("Failed to send accessEnabled message to VMX: %s.\n",
+ result != NULL ? result : "");
+ }
+
+ vm_free(result);
+
+ return retVal;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreShutdown --
+ *
+ * Disable GuestStore access before shutdown.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+GuestStoreShutdown(void)
+{
+ pluginData.shutdown = TRUE;
+ g_object_set(pluginData.ctx->serviceObj, TOOLS_PLUGIN_SVC_PROP_GUESTSTORE,
+ NULL, NULL);
+
+ if (pluginData.guestStoreAccessEnabled) {
+ GuestStoreAccessDisable();
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreConfReload --
+ *
+ * Disable/enable GuestStore access after guest side config change.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+GuestStoreConfReload(gpointer src, // IN
+ ToolsAppCtx *ctx, // IN
+ gpointer data) // IN
+{
+ Bool featureDisabled = IsFeatureDisabled();
+
+ if (pluginData.featureDisabled != featureDisabled) {
+ pluginData.featureDisabled = featureDisabled;
+
+ if (pluginData.guestStoreAccessEnabled && featureDisabled) {
+ g_info("Disable GuestStore access after guest side "
+ "config change.\n");
+ GuestStoreAccessDisable();
+ } else if (!pluginData.guestStoreAccessEnabled &&
+ !featureDisabled &&
+ GetVmxGuestStoreAccessEnabledState()) {
+ g_info("Enable GuestStore access after guest side "
+ "config change.\n");
+ GuestStoreAccessEnable();
+ }
+ } else {
+ Bool adminOnly = IsAdminOnly();
+
+ if (pluginData.adminOnly != adminOnly) {
+ pluginData.adminOnly = adminOnly;
+
+ if (pluginData.guestStoreAccessEnabled) {
+ g_info("Reset GuestStore access after guest side "
+ "config change.\n");
+ GuestStoreAccessDisable();
+ GuestStoreAccessEnable();
+ }
+ }
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreReset --
+ *
+ * VMX connection ASOCKERR_REMOTE_DISCONNECT (4) error callback is not
+ * seen on Windows guests after suspend/resume, address this in tools
+ * reset signal handler.
+ *
+ * Results:
+ * None
+ *
+ * Side-effects:
+ * Client connections will also be interrupted by tools reset due to
+ * sporadic guest hang.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+GuestStoreReset(gpointer src, // IN
+ ToolsAppCtx *ctx, // IN
+ gpointer data) // IN
+{
+ if (theVmxConn != NULL) {
+#ifdef _WIN32
+ /*
+ * After suspend/resume, VMX side vsocket is closed, VMX connection is
+ * broken, but VmxConnErrorCb() is not called on Windows guests.
+ * We still send shutdown data map to VMX connection here. We see
+ * AsyncSocket_Send() succeeds and either VmxConnSendDataMapCb or
+ * VmxConnErrorCb() is called in tests. This minimizes impact on
+ * sporadic guest hang case where VMX connection is not broken and
+ * we want VMX to close its side vsocket proactively.
+ */
+ g_info("Perform tools reset by closing active connections.\n");
+ CloseActiveConnections();
+#endif
+ } else if (pluginData.vmxConnectRequested) {
+ /*
+ * Closing pluginData.vmxListenSock cancels pending VmxConnectCb() call,
+ * second call of AsyncSocket_ListenVMCI() results in a new vsocket
+ * listening port number.
+ */
+ g_info("Perform tools reset without VMX connection "
+ "but VMX connect request was made.\n");
+ GuestStoreAccessDisable(); // Calls StopVmxToGuestConnTimeout()
+ if (pluginData.guestStoreAccessEnabled &&
+ !CheckAndUpdateFeatureDisabled()) {
+ GuestStoreAccessEnable();
+ }
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * GuestStoreSetOption --
+ *
+ * Handle TOOLSOPTION_ENABLE_GUESTSTORE_ACCESS Set_Option callback.
+ *
+ * Results:
+ * TRUE on success.
+ *
+ * Side-effects:
+ * None
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static gboolean
+GuestStoreSetOption(gpointer src, // IN
+ ToolsAppCtx *ctx, // IN
+ const gchar *option, // IN
+ const gchar *value, // IN
+ gpointer data) // IN
+{
+ gboolean retVal = FALSE;
+
+ if (strcmp(option, TOOLSOPTION_ENABLE_GUESTSTORE_ACCESS) == 0) {
+ g_debug("Tools set option %s=%s.\n",
+ TOOLSOPTION_ENABLE_GUESTSTORE_ACCESS, value);
+
+ if (strcmp(value, "1") == 0 &&
+ !pluginData.guestStoreAccessEnabled) {
+ if (CheckAndUpdateFeatureDisabled()) {
+ g_info("GuestStore access is disabled on guest side.\n");
+ } else {
+ GuestStoreAccessEnable();
+ retVal = TRUE;
+ }
+ } else if (strcmp(value, "0") == 0 &&
+ pluginData.guestStoreAccessEnabled) {
+ GuestStoreAccessDisable();
+ retVal = TRUE;
+ }
+ }
+
+ return retVal;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * ToolsOnLoad --
+ *
+ * Return the registration data for the GuestStore plugin.
+ *
+ * Results:
+ * Return the registration data.
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+TOOLS_MODULE_EXPORT ToolsPluginData *
+ToolsOnLoad(ToolsAppCtx *ctx) // IN
+{
+ static ToolsPluginData regData = { "guestStore", NULL, NULL, NULL };
+ static ToolsPluginSvcGuestStore svcGuestStore = { GuestStoreShutdown };
+
+ ToolsServiceProperty propGuestStore = { TOOLS_PLUGIN_SVC_PROP_GUESTSTORE };
+
+ uint32 vmxVersion = 0;
+ uint32 vmxType = VMX_TYPE_UNSET;
+
+ ToolsPluginSignalCb sigs[] = {
+ { TOOLS_CORE_SIG_CONF_RELOAD, GuestStoreConfReload, NULL },
+ { TOOLS_CORE_SIG_RESET, GuestStoreReset, NULL },
+ { TOOLS_CORE_SIG_SET_OPTION, GuestStoreSetOption, NULL }
+ };
+ ToolsAppReg regs[] = {
+ { TOOLS_APP_SIGNALS,
+ VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) }
+ };
+
+ /*
+ * Return NULL to disable the plugin if not running in vmsvc daemon.
+ */
+ if (!TOOLS_IS_MAIN_SERVICE(ctx)) {
+ g_info("Not running in vmsvc daemon: container name='%s'.\n",
+ ctx->name);
+ return NULL;
+ }
+
+ /*
+ * Return NULL to disable the plugin if not running in a VMware VM.
+ */
+ if (!ctx->isVMware) {
+ g_info("Not running in a VMware VM.\n");
+ return NULL;
+ }
+
+ /*
+ * Return NULL to disable the plugin if VM is not running on ESX host.
+ */
+ if (!VmCheck_GetVersion(&vmxVersion, &vmxType) ||
+ vmxType != VMX_TYPE_SCALABLE_SERVER) {
+ g_info("VM is not running on ESX host.\n");
+ return NULL;
+ }
+
+ InitPluginData(ctx);
+ InitPluginSignals(ctx);
+ Poll_InitGtk();
+
+ ctx->registerServiceProperty(ctx->serviceObj, &propGuestStore);
+ g_object_set(ctx->serviceObj, TOOLS_PLUGIN_SVC_PROP_GUESTSTORE,
+ &svcGuestStore, NULL);
+
+ regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs));
+
+ return ®Data;
+}
#include "vmware/tools/utils.h"
#include "vmware/tools/vmbackup.h"
-#if defined(_WIN32)
-#include "vmware/tools/guestStore.h"
+#if defined(_WIN32) || \
+ (defined(__linux__) && !defined(USERWORLD))
+# include "vmware/tools/guestStore.h"
#endif
#if defined(_WIN32)
static void
ToolsCoreCleanup(ToolsServiceState *state)
{
-#if defined(_WIN32)
+#if defined(_WIN32) || \
+ (defined(__linux__) && !defined(USERWORLD))
if (state->mainService) {
/*
* Shut down guestStore plugin first to prevent worker threads from being
################################################################################
-### Copyright (C) 2007-2018 VMware, Inc. All rights reserved.
+### Copyright (c) 2007-2018,2020 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
vmware_toolbox_cmd_SOURCES += toolboxcmd-config.c
vmware_toolbox_cmd_SOURCES += toolboxcmd-devices.c
vmware_toolbox_cmd_SOURCES += toolboxcmd-info.c
+if LINUX
+ vmware_toolbox_cmd_SOURCES += toolboxcmd-gueststore.c
+endif
vmware_toolbox_cmd_SOURCES += toolboxcmd-logging.c
vmware_toolbox_cmd_SOURCES += toolboxcmd-scripts.c
vmware_toolbox_cmd_SOURCES += toolboxcmd-shrink.c
#if defined(_WIN32) || \
(defined(__linux__) && !defined(OPEN_VM_TOOLS) && !defined(USERWORLD))
{ "upgrade", Upgrade_Command, TRUE, TRUE, Upgrade_Help},
+#endif
+#if defined(_WIN32) || \
+ (defined(__linux__) && !defined(USERWORLD))
{ "gueststore", GuestStore_Command, TRUE, FALSE, GuestStore_Help},
#endif
#if defined(_WIN32)
#if defined(_WIN32) || \
(defined(__linux__) && !defined(OPEN_VM_TOOLS) && !defined(USERWORLD))
DECLARE_COMMAND(Upgrade);
+#endif
+
+#if defined(_WIN32) || \
+ (defined(__linux__) && !defined(USERWORLD))
DECLARE_COMMAND(GuestStore);
#endif
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2019-2020 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-gueststore.c --
+ *
+ * GuestStore getcontent operation for toolbox-cmd.
+ */
+
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#if defined(_WIN32)
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <dlfcn.h>
+#endif
+
+#include "vm_assert.h"
+#include "vm_basic_defs.h"
+#include "toolboxCmdInt.h"
+#include "vmware/tools/i18n.h"
+#include "guestStoreClient.h"
+
+
+/*
+ * GuestStore client library error messages
+ *
+ * Localization can be done in this way if needed:
+ *
+ * #define GUESTSTORE_LIB_ERR_ITEM(a, b, c) MSGID(b) c,
+ *
+ * call this to get localized error message:
+ *
+ * VMTools_GetString(VMW_TEXT_DOMAIN, guestStoreLibErrMsgs[errCode])
+ */
+#define GUESTSTORE_LIB_ERR_ITEM(a, b, c) c,
+static const char * const guestStoreLibErrMsgs[] = {
+GUESTSTORE_LIB_ERR_LIST
+};
+#undef GUESTSTORE_LIB_ERR_ITEM
+
+/*
+ * Passed in from main(), command line --quiet (q) option.
+ */
+static gboolean gQuiet = FALSE;
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreReportProgress --
+ *
+ * Report progress from GuestStore client library.
+ *
+ * Results:
+ * TRUE to continue getting content.
+ * FALSE to cancel getting content.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+GuestStoreReportProgress(int64 fileSize, // IN
+ int64 bytesReceived, // IN
+ void *clientData) // IN
+{
+ static Bool first = TRUE;
+ static int lastProgress = 0;
+ int percentage;
+ int progress;
+ int i;
+
+ if (gQuiet) {
+ return TRUE;
+ }
+
+ if (first) {
+ g_print("%s", SU_(gueststore.content_size,
+ "Content size in bytes: "));
+
+ g_print("%" FMT64 "d\n", fileSize);
+
+ first = FALSE;
+ }
+
+ percentage = (int)((bytesReceived * 100) / fileSize);
+ progress = percentage / 5; // increment by every 5%
+ if (progress == lastProgress) {
+ return TRUE;
+ }
+
+ lastProgress = progress;
+
+ g_print(SU_(gueststore.progress,
+ "\rProgress: %d%%"),
+ percentage);
+ g_print(" [");
+
+ for (i = 0; i < progress; i++) {
+ putchar('=');
+ }
+
+ g_print(">%*c", 20 - i, ']');
+ fflush(stdout);
+
+ if (percentage == 100) {
+ g_print("\n");
+ }
+
+ return TRUE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStoreRemovePathEnclosingQuotes --
+ *
+ * Remove the beginning and ending quotes in a path.
+ *
+ * Results:
+ * Path without enclosing quotes.
+ *
+ * Side effects:
+ * Input path may have been changed.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static char *
+GuestStoreRemovePathEnclosingQuotes(char *path) // IN/OUT
+{
+ char *lastChar;
+
+ if (*path != '"') {
+ return path;
+ }
+
+ path++;
+ lastChar = path + strlen(path) - 1;
+ if (*lastChar == '"') {
+ *lastChar = '\0';
+ }
+
+ return path;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStore_Command --
+ *
+ * Handle and parse gueststore command.
+ *
+ * Results:
+ * 0 on success.
+ *
+ * Error code from GuestStore client library or
+ * general process error exit code.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+GuestStore_Command(char **argv, // IN: Command line arguments
+ int argc, // IN: Length of the command line arguments
+ gboolean quiet) // IN
+{
+ int exitCode;
+ char *contentPath;
+ char *outputPath;
+
+ if (toolbox_strcmp(argv[optind], "getcontent") != 0) {
+ ToolsCmd_UnknownEntityError(argv[0],
+ SU_(arg.subcommand, "subcommand"),
+ argv[optind]);
+ return EX_USAGE;
+ }
+
+ if (optind != (argc - 3)) {
+ return EX_USAGE;
+ }
+
+ gQuiet = quiet;
+
+ if (!GuestStoreClient_Init()) {
+ g_critical("GuestStoreClient_Init failed.\n");
+ exitCode = EX_SOFTWARE;
+ goto exit;
+ }
+
+ contentPath = GuestStoreRemovePathEnclosingQuotes(argv[argc - 2]);
+ outputPath = GuestStoreRemovePathEnclosingQuotes(argv[argc - 1]);
+
+ exitCode = GuestStoreClient_GetContent(contentPath, outputPath,
+ GuestStoreReportProgress, NULL);
+ if (exitCode != GSLIBERR_SUCCESS) {
+ g_critical("GuestStoreClient_GetContent failed: error=%d.\n", exitCode);
+ }
+
+ if (!GuestStoreClient_DeInit()) {
+ g_warning("GuestStoreClient_DeInit failed.\n");
+ }
+
+exit:
+
+ if (exitCode == GSLIBERR_SUCCESS) {
+ ToolsCmd_Print(SU_(result.succeeded,
+ "'%s' succeeded.\n"),
+ argv[optind]);
+ } else if (exitCode < GUESTSTORE_LIB_ERR_MAX) {
+ ToolsCmd_PrintErr(SU_(gueststore.error.client_lib,
+ "'%s' failed, GuestStore client library "
+ "error: %s.\n"),
+ argv[optind], guestStoreLibErrMsgs[exitCode]);
+ } else {
+ ToolsCmd_PrintErr(SU_(result.error.failed,
+ "'%s' failed, check %s log for "
+ "more information.\n"),
+ argv[optind], argv[0]);
+ }
+
+ return exitCode;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GuestStore_Help --
+ *
+ * Prints the help for the gueststore command.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+GuestStore_Help(const char *progName, // IN: Name of the program from argv[0]
+ const char *cmd) // IN
+{
+ g_print(SU_(help.gueststore,
+ "%s: get resource content from GuestStore\n"
+ "Usage: %s %s <subcommand>\n\n"
+ "ESX guests only subcommands:\n"
+ " getcontent <resource path> <output file>: "
+ "get resource content from GuestStore and "
+ "save to output file.\n\n"
+ "<resource path> starts with / and represents a unique "
+ "resource in GuestStore. If it ends with /, defaults to "
+ "retrieve the underlying 'metadata.json' resource.\n"
+ "<output file> is the path of a file "
+ "to save resource content to.\n"),
+ cmd, progName, cmd);
+}