]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
Changes to common source files not immediately applicable to open-vm-tools.
authorJohn Wolfe <jwolfe@vmware.com>
Tue, 22 Dec 2020 20:22:04 +0000 (12:22 -0800)
committerJohn Wolfe <jwolfe@vmware.com>
Tue, 22 Dec 2020 20:22:04 +0000 (12:22 -0800)
Staging source files and changes for a future feature.

25 files changed:
open-vm-tools/Makefile.am
open-vm-tools/configure.ac
open-vm-tools/lib/Makefile.am
open-vm-tools/lib/guestStoreClientHelper/Makefile.am [new file with mode: 0644]
open-vm-tools/lib/guestStoreClientHelper/guestStoreClient.c [new file with mode: 0644]
open-vm-tools/lib/include/guestStoreClient.h [new file with mode: 0644]
open-vm-tools/lib/include/guestStoreConst.h [new file with mode: 0644]
open-vm-tools/lib/include/guestStoreDefs.h [new file with mode: 0644]
open-vm-tools/lib/include/vmware/tools/guestStore.h [new file with mode: 0644]
open-vm-tools/lib/include/vmware/tools/guestStoreClientLib.h [new file with mode: 0644]
open-vm-tools/libguestStoreClient/COPYING [new file with mode: 0644]
open-vm-tools/libguestStoreClient/Makefile.am [new file with mode: 0644]
open-vm-tools/libguestStoreClient/guestStoreClientLib.c [new file with mode: 0644]
open-vm-tools/libguestStoreClient/guestStoreClientLibInt.h [new file with mode: 0644]
open-vm-tools/libguestStoreClient/gueststoreclientlib_version.h [new file with mode: 0644]
open-vm-tools/libvmtools/Makefile.am
open-vm-tools/services/plugins/Makefile.am
open-vm-tools/services/plugins/guestStore/COPYING [new file with mode: 0644]
open-vm-tools/services/plugins/guestStore/Makefile.am [new file with mode: 0644]
open-vm-tools/services/plugins/guestStore/guestStorePlugin.c [new file with mode: 0644]
open-vm-tools/services/vmtoolsd/mainLoop.c
open-vm-tools/toolbox/Makefile.am
open-vm-tools/toolbox/toolbox-cmd.c
open-vm-tools/toolbox/toolboxCmdInt.h
open-vm-tools/toolbox/toolboxcmd-gueststore.c [new file with mode: 0644]

index a58c65db97d0819d4fb7d133d6fdb2dac35941bb..9f75ce3056a3bb3b9f84ddd6e7bfe3c0aaf2a34d 100644 (file)
@@ -1,5 +1,5 @@
 ################################################################################
-### 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.
 ###
@@ -24,6 +24,9 @@ ACLOCAL_AMFLAGS = -I m4
 
 SUBDIRS =
 SUBDIRS += lib
+if LINUX
+   SUBDIRS += libguestStoreClient
+endif
 SUBDIRS += libvmtools
 SUBDIRS += libhgfs
 SUBDIRS += hgfsclient
index 4f2e7bcf9038938f8925ee3732079ccf800da15a..6e1baf798dec90698995e8731b764b8bd4dcb7d0 100644 (file)
@@ -1592,6 +1592,7 @@ AC_CONFIG_FILES([                      \
    lib/glibUtils/Makefile              \
    lib/guestApp/Makefile               \
    lib/guestRpc/Makefile               \
+   lib/guestStoreClientHelper/Makefile \
    lib/hgfs/Makefile                   \
    lib/hgfsBd/Makefile                 \
    lib/hgfsHelper/Makefile             \
@@ -1632,6 +1633,7 @@ AC_CONFIG_FILES([                      \
    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 \
@@ -1658,6 +1660,7 @@ AC_CONFIG_FILES([                      \
    libDeployPkg/Makefile               \
    libDeployPkg/libDeployPkg.pc        \
    libhgfs/Makefile                    \
+   libguestStoreClient/Makefile        \
    libvmtools/Makefile                 \
    xferlogs/Makefile                   \
    modules/Makefile                    \
index f9d84805a100fbd93071055f7b9d67ba2c4888e7..dee38ed79197a173247009dcc97f0a2f1dbbdd9e 100644 (file)
@@ -1,5 +1,5 @@
 ################################################################################
-### 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
@@ -37,6 +37,9 @@ SUBDIRS += file
 SUBDIRS += foundryMsg
 SUBDIRS += glibUtils
 SUBDIRS += guestApp
+if LINUX
+   SUBDIRS += guestStoreClientHelper
+endif
 SUBDIRS += hgfs
 SUBDIRS += hgfsBd
 SUBDIRS += hgfsHelper
diff --git a/open-vm-tools/lib/guestStoreClientHelper/Makefile.am b/open-vm-tools/lib/guestStoreClientHelper/Makefile.am
new file mode 100644 (file)
index 0000000..7f19b0e
--- /dev/null
@@ -0,0 +1,24 @@
+################################################################################
+### 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
diff --git a/open-vm-tools/lib/guestStoreClientHelper/guestStoreClient.c b/open-vm-tools/lib/guestStoreClientHelper/guestStoreClient.c
new file mode 100644 (file)
index 0000000..38139bb
--- /dev/null
@@ -0,0 +1,379 @@
+/*********************************************************
+ * 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
diff --git a/open-vm-tools/lib/include/guestStoreClient.h b/open-vm-tools/lib/include/guestStoreClient.h
new file mode 100644 (file)
index 0000000..53ac717
--- /dev/null
@@ -0,0 +1,50 @@
+/*********************************************************
+ * 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
diff --git a/open-vm-tools/lib/include/guestStoreConst.h b/open-vm-tools/lib/include/guestStoreConst.h
new file mode 100644 (file)
index 0000000..2729356
--- /dev/null
@@ -0,0 +1,81 @@
+/*********************************************************
+ * 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_ */
diff --git a/open-vm-tools/lib/include/guestStoreDefs.h b/open-vm-tools/lib/include/guestStoreDefs.h
new file mode 100644 (file)
index 0000000..1e297fb
--- /dev/null
@@ -0,0 +1,79 @@
+/*********************************************************
+ * 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__ */
diff --git a/open-vm-tools/lib/include/vmware/tools/guestStore.h b/open-vm-tools/lib/include/vmware/tools/guestStore.h
new file mode 100644 (file)
index 0000000..aea06c5
--- /dev/null
@@ -0,0 +1,89 @@
+/*********************************************************
+ * 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_ */
diff --git a/open-vm-tools/lib/include/vmware/tools/guestStoreClientLib.h b/open-vm-tools/lib/include/vmware/tools/guestStoreClientLib.h
new file mode 100644 (file)
index 0000000..2f147c2
--- /dev/null
@@ -0,0 +1,168 @@
+/*********************************************************
+ * 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__ */
diff --git a/open-vm-tools/libguestStoreClient/COPYING b/open-vm-tools/libguestStoreClient/COPYING
new file mode 100644 (file)
index 0000000..09f465a
--- /dev/null
@@ -0,0 +1,502 @@
+                 GNU LESSER GENERAL PUBLIC LICENSE
+                      Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                 GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <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!
diff --git a/open-vm-tools/libguestStoreClient/Makefile.am b/open-vm-tools/libguestStoreClient/Makefile.am
new file mode 100644 (file)
index 0000000..adf6930
--- /dev/null
@@ -0,0 +1,36 @@
+################################################################################
+### 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
diff --git a/open-vm-tools/libguestStoreClient/guestStoreClientLib.c b/open-vm-tools/libguestStoreClient/guestStoreClientLib.c
new file mode 100644 (file)
index 0000000..4854f62
--- /dev/null
@@ -0,0 +1,1163 @@
+/*********************************************************
+ * 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);
+}
diff --git a/open-vm-tools/libguestStoreClient/guestStoreClientLibInt.h b/open-vm-tools/libguestStoreClient/guestStoreClientLibInt.h
new file mode 100644 (file)
index 0000000..5f178ad
--- /dev/null
@@ -0,0 +1,198 @@
+/*********************************************************
+ * 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__ */
diff --git a/open-vm-tools/libguestStoreClient/gueststoreclientlib_version.h b/open-vm-tools/libguestStoreClient/gueststoreclientlib_version.h
new file mode 100644 (file)
index 0000000..1951a4d
--- /dev/null
@@ -0,0 +1,26 @@
+/*********************************************************
+ * 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_ */
index 290e87d61c671cf7e32be3017cdf38e7368b634f..837c9dae2678453295b370ee9bfa2506cf5bbf14 100644 (file)
@@ -1,5 +1,5 @@
 ################################################################################
-### 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
@@ -35,6 +35,9 @@ libvmtools_la_LIBADD += ../lib/file/libFile.la
 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
index 08b7c81b489c74e47efa29e0092ac7f11961b4e9..eb725a672166742da3d768989da70afd6e6274b2 100644 (file)
@@ -17,8 +17,9 @@
 
 SUBDIRS =
 if LINUX
-   SUBDIRS += gdp
    SUBDIRS += appInfo
+   SUBDIRS += gdp
+   SUBDIRS += guestStore
 endif
 if ENABLE_SDMP
    SUBDIRS += serviceDiscovery
diff --git a/open-vm-tools/services/plugins/guestStore/COPYING b/open-vm-tools/services/plugins/guestStore/COPYING
new file mode 100644 (file)
index 0000000..09f465a
--- /dev/null
@@ -0,0 +1,502 @@
+                 GNU LESSER GENERAL PUBLIC LICENSE
+                      Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                 GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <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!
diff --git a/open-vm-tools/services/plugins/guestStore/Makefile.am b/open-vm-tools/services/plugins/guestStore/Makefile.am
new file mode 100644 (file)
index 0000000..a9c650f
--- /dev/null
@@ -0,0 +1,34 @@
+################################################################################
+### 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
diff --git a/open-vm-tools/services/plugins/guestStore/guestStorePlugin.c b/open-vm-tools/services/plugins/guestStore/guestStorePlugin.c
new file mode 100644 (file)
index 0000000..51b3c1c
--- /dev/null
@@ -0,0 +1,3072 @@
+/*********************************************************
+ * 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 &regData;
+}
index 7189caa38d61cb43643e87dfe683018fb8376169..32d74b0b7576961a66135ba76b599196a20fc700 100644 (file)
@@ -45,8 +45,9 @@
 #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)
@@ -109,7 +110,8 @@ static gboolean gGlobalConfEnabled = FALSE;
 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
index 75f81b2a36d23b882173586e57ca5ac2e3ff1fda..000d391d0d6d14ec7364ec234bcd81feb1684a6b 100644 (file)
@@ -1,5 +1,5 @@
 ################################################################################
-### 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
@@ -31,6 +31,9 @@ vmware_toolbox_cmd_SOURCES += toolbox-cmd.c
 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
index 89eaadbcc89a68eed2aaccd58206042502e54549..f50d013ef73fcf7fde5d1ef52229b1aa8f3a9e96 100644 (file)
@@ -112,6 +112,9 @@ static CmdTable commands[] = {
 #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)
index 88190e39ce5b3517acbf757bc74e98b3b4af4133..005eb0ae5c6c1d596fcad2f8043a7ab8adb52bda 100644 (file)
@@ -149,6 +149,10 @@ DECLARE_COMMAND(Config);
 #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
 
diff --git a/open-vm-tools/toolbox/toolboxcmd-gueststore.c b/open-vm-tools/toolbox/toolboxcmd-gueststore.c
new file mode 100644 (file)
index 0000000..9972f5d
--- /dev/null
@@ -0,0 +1,288 @@
+/*********************************************************
+ * 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);
+}