--- /dev/null
+/*********************************************************
+ * Copyright (C) 2021 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.
+ *
+ *********************************************************/
+
+/*
+ * componentMgr.c --
+ *
+ * Adds/removes components in the guest OS. Periodically polls
+ * for all the components managed by the plugin and fetches the guestVar
+ * guestinfo./vmware.components.<comp_name>.desiredstate for present or
+ * absent action and accordingly adds/removes the component from the
+ * system.
+ * All actions on a component runs as an async process.
+ */
+
+#include "componentMgrPlugin.h"
+#include "str.h"
+#include "vm_version.h"
+#include "embed_version.h"
+#include "vmtoolsd_version.h"
+VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING);
+
+
+/*
+ * componentMgr plugin poll interval timeout source.
+ */
+static GSource *gComponentMgrTimeoutSource = NULL;
+
+/*
+ * Tools application context.
+ */
+static ToolsAppCtx *gCtx;
+
+/*
+ * componentMgr plugin poll interval.
+ */
+static guint gComponentMgrPollInterval = 0;
+
+static gboolean ComponentMgrCb(gpointer data);
+
+
+/*
+ *****************************************************************************
+ * ComponentMgr_GetToolsAppCtx --
+ *
+ * A getter function to fetch the tools application context in all points of
+ * the plugin.
+ *
+ * @return
+ * Main ToolsAppCtx of the plugin.
+ *****************************************************************************
+ */
+
+ToolsAppCtx*
+ComponentMgr_GetToolsAppCtx()
+{
+ return gCtx;
+}
+
+
+/*
+ *****************************************************************************
+ * ReconfigureComponentMgrPollLoopEx --
+ *
+ * Start, stop and reconfigure componentMgr plugin poll loop.
+ * This function is responsible for creating, handling, and resetting the
+ * componentMgr loop timeout source.
+ *
+ * @param[in] ctx Tools application context.
+ * @param[in] pollInterval Poll interval in seconds.
+ *
+ * @return
+ * None
+ *
+ * Side effects:
+ * Deletes the existing timeout source and recreates a new one.
+ *****************************************************************************
+ */
+
+static void
+ReconfigureComponentMgrPollLoopEx(ToolsAppCtx *ctx, // IN
+ gint pollInterval) // IN
+{
+ if (gComponentMgrPollInterval == pollInterval) {
+ g_debug("%s: ComponentMgr poll interval has not been changed since"
+ " last time.\n", __FUNCTION__);
+ return;
+ }
+
+ if (gComponentMgrTimeoutSource != NULL) {
+ /*
+ * Destroy the existing timeout source.
+ */
+ g_source_destroy(gComponentMgrTimeoutSource);
+ gComponentMgrTimeoutSource = NULL;
+ }
+
+ if (pollInterval != 0) {
+ if (pollInterval < COMPONENTMGR_POLL_INTERVAL) {
+ g_warning("%s: Invalid poll interval. Using default %us.\n",
+ __FUNCTION__, COMPONENTMGR_POLL_INTERVAL);
+ pollInterval = COMPONENTMGR_POLL_INTERVAL;
+ }
+
+ g_info("%s: New value for %s is %us.\n", __FUNCTION__,
+ COMPONENTMGR_CONF_POLLINTERVAL,
+ pollInterval);
+
+ gComponentMgrTimeoutSource = g_timeout_source_new_seconds(pollInterval);
+ VMTOOLSAPP_ATTACH_SOURCE(ctx, gComponentMgrTimeoutSource,
+ ComponentMgrCb, ctx, NULL);
+ g_source_unref(gComponentMgrTimeoutSource);
+ } else {
+ /*
+ * Plugin will be disabled since poll interval configured is 0.
+ * No components will be managed. Publish guestVar available.
+ */
+ g_info("%s: ComponentMgr plugin disabled.\n", __FUNCTION__);
+ ComponentMgr_PublishAvailableComponents(ctx,
+ COMPONENTMGR_NONECOMPONENTS);
+ }
+
+ gComponentMgrPollInterval = pollInterval;
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgrCb --
+ *
+ * This function updates the component status managed by the plugin.
+ * This function internally calls present/absent actions on the respective
+ * components.
+ *
+ * @param[in] data Tools application context.
+ *
+ * @return
+ * G_SOURCE_CONTINUE To continue polling.
+ *
+ * Side effects:
+ * None.
+ *****************************************************************************
+ */
+
+static gboolean
+ComponentMgrCb(gpointer data) //IN
+{
+ ToolsAppCtx *ctx = data;
+
+ if (ComponentMgr_CheckAnyAsyncProcessRunning()) {
+ g_debug("%s: A component has an async process running. Skipping "
+ "component status update.\n", __FUNCTION__);
+ return G_SOURCE_CONTINUE;
+ }
+
+ /*
+ * Update the enabled components managed by the plugin and publish
+ * guestVar for all the available components.
+ */
+ ComponentMgr_UpdateComponentEnableStatus(ctx);
+
+ /*
+ * Main function which handles the core logic of taking actions present or
+ * absent on the components by reading action from the component guestVars.
+ */
+ ComponentMgr_UpdateComponentStatus(ctx);
+
+ return G_SOURCE_CONTINUE;
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgrPollLoop --
+ *
+ * This function reads the poll interval and included configurations from the
+ * config file and reconfigures the poll loop of the plugin.
+ *
+ * @param[in] data Tools application context.
+ *
+ * @return
+ * None.
+ *
+ * Side effects:
+ * None.
+ *****************************************************************************
+ */
+
+static void
+ComponentMgrPollLoop(ToolsAppCtx *ctx) //IN
+{
+ gint pollInterval;
+ gchar *listString = NULL;
+
+ pollInterval = VMTools_ConfigGetInteger(ctx->config,
+ COMPONENTMGR_CONFGROUPNAME,
+ COMPONENTMGR_CONF_POLLINTERVAL,
+ COMPONENTMGR_POLL_INTERVAL);
+
+ listString = VMTools_ConfigGetString(ctx->config,
+ COMPONENTMGR_CONFGROUPNAME,
+ COMPONENTMGR_CONF_INCLUDEDCOMPONENTS,
+ COMPONENTMGR_ALLCOMPONENTS);
+
+ /*
+ * If the included conf value is not present or is empty, no components will
+ * be enabled and plugin will be disabled until further change.
+ */
+ if (listString == NULL || *listString == '\0' ||
+ Str_Strcmp(listString, COMPONENTMGR_NONECOMPONENTS) == 0) {
+ g_info("%s: No components managed by the plugin. Plugin disabled."
+ " Set value included in configuration.\n", __FUNCTION__);
+ pollInterval = 0;
+ }
+
+ g_free(listString);
+ ReconfigureComponentMgrPollLoopEx(ctx, pollInterval);
+}
+
+
+/*
+ ******************************************************************************
+ * ComponentMgrServerShutdown --
+ *
+ * This function cleans up plugin internal data on shutdown.
+ *
+ * @param[in] src The source object.
+ * @param[in] ctx Tools application context.
+ * @param[in] data Unused.
+ *
+ * @return
+ * None
+ *
+ * Side effects:
+ * Destroys all timeout sources for all the components.
+ * Destroys all running async process for all the components.
+ *
+ ******************************************************************************
+ */
+
+static void
+ComponentMgrServerShutdown(gpointer src, // IN
+ ToolsAppCtx *ctx, // IN
+ gpointer data) // IN
+{
+ if (gComponentMgrTimeoutSource != NULL) {
+ /*
+ * Destroy the existing timeout source.
+ */
+ g_source_destroy(gComponentMgrTimeoutSource);
+ gComponentMgrTimeoutSource = NULL;
+ }
+
+ // Destroy all GSource timers for all managed components.
+ ComponentMgr_Destroytimers();
+
+ // Destroy and free all running async process for all components.
+ ComponentMgr_DestroyAsyncProcess();
+}
+
+
+/*
+ ******************************************************************************
+ * ComponentMgrServerConfReload --
+ *
+ * This function reconfigures the poll loop interval upon config file reload.
+ *
+ * @param[in] src The source object.
+ * @param[in] ctx Tools application context.
+ * @param[in] data Unused.
+ *
+ * @return
+ * None.
+ *
+ * Side effects:
+ * None.
+ ******************************************************************************
+ */
+
+static void
+ComponentMgrServerConfReload(gpointer src, // IN
+ ToolsAppCtx *ctx, // IN
+ gpointer data) // IN
+{
+ ComponentMgrPollLoop(ctx);
+}
+
+
+/*
+ ******************************************************************************
+ * ComponentMgrServerReset --
+ *
+ * Callback function that gets called whenever the RPC channel gets reset.
+ *
+ * @param[in] src The source object.
+ * @param[in] ctx Tools application context.
+ * @param[in] data Unused.
+ *
+ * @return
+ * None.
+ *
+ * Side effects:
+ * Reinitializes the plugin timeout source.
+ ******************************************************************************
+ */
+
+static void
+ComponentMgrServerReset(gpointer src, // IN
+ ToolsAppCtx *ctx, // IN
+ gpointer data) // IN
+{
+ /*
+ * Handle reset for componentMgr loop.
+ */
+ if (gComponentMgrTimeoutSource != NULL) {
+ ASSERT(gComponentMgrPollInterval != 0);
+
+ ReconfigureComponentMgrPollLoopEx(ctx, gComponentMgrPollInterval);
+ } else {
+ ComponentMgrPollLoop(ctx);
+ }
+}
+
+/*
+ *****************************************************************************
+ * ToolsOnLoad --
+ *
+ * Plugin entry point. Initializes internal plugin state.
+ *
+ * @param[in] ctx Tools application context.
+ *
+ * @return
+ * Plugin registration data.
+ *
+ * Side effects:
+ * None.
+ *
+ *****************************************************************************
+ */
+
+TOOLS_MODULE_EXPORT ToolsPluginData *
+ToolsOnLoad(ToolsAppCtx *ctx) // IN
+{
+ static ToolsPluginData regData = {
+ "componentMgr",
+ NULL,
+ NULL
+ };
+
+ /*
+ * Return NULL to disable the plugin if not running in a VMware VM.
+ */
+ if (!ctx->isVMware) {
+ g_info("%s: Not running in a VMware VM.\n", __FUNCTION__);
+ return NULL;
+ }
+
+ /*
+ * Return NULL to disable the plugin if not running in vmsvc daemon.
+ */
+ if (!TOOLS_IS_MAIN_SERVICE(ctx)) {
+ g_info("%s: Not running in vmsvc daemon: container name='%s'.\n",
+ __FUNCTION__, ctx->name);
+ return NULL;
+ }
+
+ gCtx = ctx;
+
+ /*
+ * This plugin is useless without an RpcChannel. If we don't have one,
+ * just bail.
+ */
+ if (ctx->rpc != NULL) {
+ ToolsPluginSignalCb sigs[] = {
+ { TOOLS_CORE_SIG_CONF_RELOAD, ComponentMgrServerConfReload, NULL },
+ { TOOLS_CORE_SIG_RESET, ComponentMgrServerReset, NULL },
+ { TOOLS_CORE_SIG_SHUTDOWN, ComponentMgrServerShutdown, NULL }
+ };
+ ToolsAppReg regs[] = {
+ { TOOLS_APP_SIGNALS,
+ VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs))
+ }
+ };
+
+ regData.regs = VMTools_WrapArray(regs,
+ sizeof *regs,
+ ARRAYSIZE(regs));
+
+ return ®Data;
+ }
+
+ return NULL;
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2021 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.
+ *
+ *********************************************************/
+
+/*
+ * componentMgrInstallAction.c --
+ *
+ * Functions to manage the known and enabled components by the componentMgr.
+ * Consists of functions periodically handling adding/removing of
+ * components in the guest OS. Periodically read the guestVar
+ * guestinfo./vmware.components.<comp_name>.desiredstate and take present or
+ * absent action on the components.
+ * Adding/removing a component managed by the plugin is performed
+ * asynchronously using Proc_Manager API's.
+ *
+ */
+
+
+#include "componentMgrPlugin.h"
+#include "str.h"
+#include "file.h"
+#include "util.h"
+#include "guestApp.h"
+#include "codeset.h"
+
+
+/*
+ * Structure to store information about the scripts to be invoked for
+ * present/absent action on the components by the plugin.
+ * The script to be invoked shall be predefined with default arguments
+ * in the structure according to actions present/absent/checkstatus.
+ */
+
+typedef struct ComponentAction {
+ const char *componentName; /* The name of the enabled
+ component. */
+
+ const char *scriptName; /* The default script to be invoked
+ to take actions for a particular
+ component */
+
+ const char *addActionArguments; /* Default arguments to the script
+ to execute present action towards
+ the component in the guest OS. */
+
+ const char *removeActionArguments; /* Default arguments to the script
+ to execute absent action towards
+ the component in the guest OS. */
+
+ const char *checkStatusActionArguments; /* Default arguments to the script
+ to execute checkstatus towards
+ the component in the guest OS. */
+
+ const char* mandatoryParameters; /* Arguments that are mandatory to
+ be passed to script. */
+
+ const char *componentDirectory; /* The name of directory in which
+ scripts will be installed.*/
+
+ char* (*customizeRemoveAction)(); /* A custom callback function
+ to customize arguments for
+ absent action on the component
+ script. */
+
+ char* (*customizeAddAction)(); /* A custom callback function
+ to customize arguments for
+ present action on the component
+ script. */
+} ComponentAction;
+
+
+static char*
+ComponentMgrCustomizeSaltAddAction();
+
+
+/*
+ * An array to store all the state information of the component over the
+ * life time for the plugin. The array consists of cached values of enabled
+ * status, async process info, GSource timers, status count down counter and
+ * current action to be run on the component.
+ */
+
+static struct componentInfo components[] = {
+ {SALT_MINION, TRUE, NOTINSTALLED, NULL, NULL, COMPONENTMGR_CHECK_STATUS_COUNT_DOWN, INVALIDACTION}
+};
+
+/*
+ * An array containing information of the component and its related
+ * scripts and respective default arguments for actions present/absent
+ * or checkstatus.
+ */
+#if defined(_WIN32)
+static const char powershellExecutable[] = "\\WindowsPowerShell\\v1.0\\PowerShell.exe";
+
+static const char componentMgrExecutionPolicy[] = "-ExecutionPolicy RemoteSigned -File";
+
+static ComponentAction executionScripts[] = {
+ {SALT_MINION,"svtminion.ps1", "-Install", "-Remove", "-Status", "-Loglevel debug", "saltMinion", NULL, &ComponentMgrCustomizeSaltAddAction}
+};
+#else
+static ComponentAction executionScripts[] = {
+ {SALT_MINION,"svtminion.sh", "--install", "--remove", "--status", "--loglevel debug", "saltMinion", NULL, &ComponentMgrCustomizeSaltAddAction}
+};
+#endif
+
+
+/*
+ *****************************************************************************
+ * ComponentMgr_GetComponentName --
+ *
+ * This function fetches the name of component from the components array.
+ *
+ * @param[in] componentIndex Index of the component in the global array of
+ * components.
+ *
+ * @return
+ * Name of component.
+ *
+ * Side effects:
+ * None.
+ *
+ *****************************************************************************
+ */
+
+const char*
+ComponentMgr_GetComponentName(int componentIndex) //IN
+{
+ return components[componentIndex].name;
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgr_CheckAnyAsyncProcessRunning --
+ *
+ * This function checks if any async process is running for any component
+ * inside the plugin.
+ *
+ * @return
+ * FALSE if no async process is running for a component.
+ * TRUE if an async process is running for a component.
+ *
+ * Side effects:
+ * None.
+ *
+ *****************************************************************************
+ */
+
+gboolean
+ComponentMgr_CheckAnyAsyncProcessRunning() //IN
+{
+ int i;
+ for (i = 0; i < ARRAYSIZE(components); i++) {
+ if (components[i].procInfo != NULL) {
+ g_debug("%s: Component %s has an async process still running.\n",
+ __FUNCTION__, components[i].name);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ *****************************************************************************
+ * ComponentMgr_IsAsyncProcessRunning --
+ *
+ * This function indicates whether any async process is already running for
+ * a component.
+ *
+ * @param[in] componentIndex Index of the component in the global array of
+ * components.
+ *
+ * @return
+ * FALSE if no async process is running for a component.
+ * TRUE if an async process is running for a component.
+ *
+ * Side effects:
+ * None.
+ *
+ *****************************************************************************
+ */
+
+gboolean
+ComponentMgr_IsAsyncProcessRunning(int componentIndex) //IN
+{
+ if (components[componentIndex].procInfo != NULL) {
+ g_info("%s: Component %s has an async process still running.\n",
+ __FUNCTION__, components[componentIndex].name);
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgr_SetComponentAsyncProcInfo --
+ *
+ * This function caches info of the async process currently running for a
+ * component.
+ *
+ * @param[in] asyncProcInfo An asyncProcInfo object of the currently running
+ async process.
+ * @param[in] componentIndex Index of the component in the global array of
+ * components.
+ *
+ * @return
+ * None.
+ *
+ * Side effects:
+ * None.
+ *****************************************************************************
+ */
+
+void
+ComponentMgr_SetComponentAsyncProcInfo(asyncProcessInfo *asyncProcInfo,
+ int componentIndex)
+{
+ ASSERT(components[componentIndex].procInfo == NULL);
+ components[componentIndex].procInfo = asyncProcInfo;
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgr_ResetComponentAsyncProcInfo --
+ *
+ * This function resets the state of any async process running for a component.
+ *
+ * @param[in] componentIndex Index of the component in the global array of
+ * components.
+ *
+ * @return
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *****************************************************************************
+ */
+
+void
+ComponentMgr_ResetComponentAsyncProcInfo(int componentIndex)
+{
+ components[componentIndex].procInfo = NULL;
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgr_SetComponentGSourceTimer --
+ *
+ * This function caches the GSource timer running for an async process for a
+ * component. The timer is used to monitor the state of async process.
+ *
+ * @param[in] componentTimer A GSource timer created when performing present
+ * or absent action on a component.
+ * @param[in] componentIndex Index of the component in the global array of
+ * components.
+ *
+ * @return
+ * None.
+ *
+ * Side effects:
+ * Sets the GSource timer of an async process running for a component.
+ *
+ *****************************************************************************
+ */
+
+void
+ComponentMgr_SetComponentGSourceTimer(GSource *componentTimer,
+ int componentIndex)
+{
+ ASSERT(components[componentIndex].sourceTimer == NULL);
+ components[componentIndex].sourceTimer = componentTimer;
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgr_ResetComponentGSourceTimer --
+ *
+ * This function resets the component GSource timer to make way for creation
+ * a new async process.
+ *
+ * @param[in] componentIndex Index of the component in the global array of
+ * components.
+ * @return
+ * None.
+ *
+ * Side effects:
+ * Reset the GSource timer of an async process running for a component.
+ *
+ *****************************************************************************
+ */
+
+void
+ComponentMgr_ResetComponentGSourceTimer(int componentIndex)
+{
+ components[componentIndex].sourceTimer = NULL;
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgrGetScriptFullPath --
+ *
+ * This function returns a full path to the component script based on the
+ * installed path of open-vm-tools or VMware Tools.
+ *
+ * @param[in] scriptName Name of the component script.
+ * @param[in] componentDir Directory of the component.
+ *
+ * @return
+ * A full path to the script file based on open-vm-tools or VMware Tools
+ * installation.
+ *
+ * Side effects:
+ * Caller needs to free the full script path value.
+ *
+ *****************************************************************************
+ */
+
+static gchar*
+ComponentMgrGetScriptFullPath(const char *scriptName, //IN
+ const char *componentDir) //IN
+{
+ gchar *scriptInstallDir;
+ gchar *toolsInstallDir;
+
+#if defined(OPEN_VM_TOOLS)
+ toolsInstallDir = Util_SafeStrdup(VMTOOLS_COMPONENTMGR_PATH);
+ scriptInstallDir = g_strdup_printf("%s%s%s%s", toolsInstallDir,
+ componentDir, DIRSEPS, scriptName);
+#else
+ toolsInstallDir = GuestApp_GetInstallPath();
+ scriptInstallDir = g_strdup_printf("%s%s%s%s%s%s%s", toolsInstallDir, DIRSEPS,
+ COMPONENTMGR_CONFGROUPNAME, DIRSEPS,
+ componentDir, DIRSEPS, scriptName);
+#endif
+
+ g_free(toolsInstallDir);
+ return scriptInstallDir;
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgrCustomizeSaltAddAction --
+ *
+ * This function customizes the custom arguments for the present action for the
+ * salt component script.
+ *
+ * @return
+ * A customized argument string for the salt component script.
+ *
+ * Side effects:
+ * Caller has to free the returned custom arguments.
+ *
+ *****************************************************************************
+ */
+
+static char*
+ComponentMgrCustomizeSaltAddAction()
+{
+ size_t replylen;
+ gchar *msg;
+ gboolean status;
+ char *actionArguments = NULL;
+
+ msg = g_strdup_printf("%s.%s.args", COMPONENTMGR_ACTION, SALT_MINION);
+ status = ComponentMgr_SendRpc(ComponentMgr_GetToolsAppCtx(), msg,
+ &actionArguments, &replylen);
+ g_free(msg);
+
+ if (!status) {
+ vm_free(actionArguments);
+ return NULL;
+ }
+
+ return actionArguments;
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgrConstructCommandline --
+ *
+ * The function constructs the commandline for linux and windows to execute
+ * the script as an async process to perform present/absent action on a
+ * component.
+ *
+ * The windows counterpart is constructed as:
+ * <path to powershell.exe> -ExecutionPolicy RemoteSigned -File \
+ * <path to component script> <args to component script>
+ *
+ * The linux counterpart is constructed as:
+ * <path to component script> <argumnets to the script>
+ *
+ * @param[in] scriptName Name of the component script.
+ * @param[in] defaultArguments Default arguments to the component script.
+ * @param[in] mandatoryParams mandatory params to the component script.
+ * @param[in] customizeAction A callback function to customize the arguments
+ for the component script.
+ *
+ * @return
+ * A commandline to be directly run as an async process.
+ *
+ * Side effects:
+ * Caller needs to free the commandline.
+ *
+ *****************************************************************************
+ */
+
+static char *
+ComponentMgrConstructCommandline(gchar *scriptPath, //IN
+ const char *defaultArguments, //IN
+ const char *mandatoryParams, //IN
+ char* (*customizeAction)()) //IN
+{
+ char *commandline = NULL;
+ const char *mandatoryParamsExists = NULL;
+ char *customArguments = NULL;
+#if defined(_WIN32)
+ WCHAR sysDirW[MAX_PATH];
+ UINT retLen;
+ char *sysDir = NULL;
+#endif
+
+ // Customize the arguments for the specific action via the callback function
+ if (customizeAction != NULL) {
+ g_info("%s: Customizing argumnets with function.\n", __FUNCTION__);
+ customArguments = customizeAction();
+ }
+
+#if defined(_WIN32)
+ retLen = GetSystemDirectoryW(sysDirW, _countof(sysDirW));
+ if (retLen == 0 || retLen >= _countof(sysDirW)) {
+ g_warning("%s: Unable to get system directory.\n", __FUNCTION__);
+ goto proceedexit;
+ }
+
+ if (!CodeSet_Utf16leToUtf8((const char *)sysDirW,
+ wcslen(sysDirW) * sizeof sysDirW[0],
+ &sysDir,
+ NULL)) {
+ g_warning("%s: Could not convert system directory to UTF-8.\n",
+ __FUNCTION__);
+ goto proceedexit;
+ }
+
+ if (customArguments != NULL) {
+ mandatoryParamsExists = strstr(customArguments, mandatoryParams);
+ if(mandatoryParamsExists == NULL) {
+ commandline = Str_SafeAsprintf(NULL, "\"%s%s\" %s \"%s\" %s %s %s",
+ sysDir, powershellExecutable,
+ componentMgrExecutionPolicy,
+ scriptPath,
+ defaultArguments, customArguments,
+ mandatoryParams);
+ } else {
+ commandline = Str_SafeAsprintf(NULL, "\"%s%s\" %s \"%s\" %s %s",
+ sysDir, powershellExecutable,
+ componentMgrExecutionPolicy,
+ scriptPath,
+ defaultArguments, customArguments);
+
+ }
+ } else {
+ commandline = Str_SafeAsprintf(NULL, "\"%s%s\" %s \"%s\" %s %s",
+ sysDir, powershellExecutable,
+ componentMgrExecutionPolicy,
+ scriptPath,
+ defaultArguments,
+ mandatoryParams);
+ }
+
+ free(sysDir);
+#else
+ if (customArguments != NULL) {
+ mandatoryParamsExists = strstr(customArguments, mandatoryParams);
+ if(mandatoryParamsExists == NULL) {
+ commandline = Str_SafeAsprintf(NULL, "%s %s %s %s", scriptPath,
+ defaultArguments, customArguments,
+ mandatoryParams);
+ } else {
+ commandline = Str_SafeAsprintf(NULL, "%s %s %s", scriptPath,
+ defaultArguments, customArguments);
+
+ }
+ } else {
+ commandline = Str_SafeAsprintf(NULL, "%s %s %s", scriptPath,
+ defaultArguments, mandatoryParams);
+ }
+ // To satisfy a complier warning of missing label.
+ goto proceedexit;
+#endif
+
+proceedexit:
+ if (customArguments != NULL) {
+ vm_free(customArguments);
+ }
+ return commandline;
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgr_CheckStatusCommandLine --
+ *
+ * This function fetches the commandline needed to be executed to check the
+ * current status of component installation. It provides the commandline as
+ * <component_script> <checkstatus_arguments>
+ *
+ * @param[in] componentIndex Index of the component in the global array of
+ * components.
+ *
+ * @return
+ * Commandline to check current status of component.
+ *
+ * Side effects:
+ * Caller needs to free the commandline value.
+ *
+ *****************************************************************************
+ */
+
+char *
+ComponentMgr_CheckStatusCommandLine(int componentIndex)
+{
+ char *commandline = NULL;
+ gchar *scriptFullPath;
+
+ /*
+ * Always check for component enabled state before proceeding, since check
+ * status can be called at any part of component action.
+ */
+ if (!components[componentIndex].isEnabled) {
+ g_info("%s: Component %s is disabled.\n", __FUNCTION__,
+ components[componentIndex].name);
+ return NULL;
+ }
+
+ scriptFullPath = ComponentMgrGetScriptFullPath(executionScripts[componentIndex].scriptName,
+ executionScripts[componentIndex].componentDirectory);
+ if (!File_Exists(scriptFullPath)) {
+ g_info("%s: Script file for component %s does not exist at path %s.\n",
+ __FUNCTION__, components[componentIndex].name, scriptFullPath);
+ return NULL;
+ }
+
+ commandline = ComponentMgrConstructCommandline(scriptFullPath,
+ executionScripts[componentIndex].checkStatusActionArguments,
+ executionScripts[componentIndex].mandatoryParameters,
+ NULL);
+
+ g_free(scriptFullPath);
+
+ return commandline;
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgrSetEnabledComponentInfo --
+ *
+ * This function sets the isEnabled state of the component which determines
+ * whether the component managed by the plugin is enabled or disabled.
+ *
+ * @param[in] componentName Name of the component to enable or disable.
+ * @param[in] enabled A boolean value indicating component enabled status.
+ *
+ * @return
+ * None
+ * Side effects:
+ * Sets the enabled/disabled status of a component.
+ *
+ *****************************************************************************
+ */
+
+static void
+ComponentMgrSetEnabledComponentInfo(const char *componentName, //IN
+ gboolean enabled) //IN
+{
+ int i;
+ for (i = 0; i < ARRAYSIZE(components); i++) {
+ if (Str_Strcmp(components[i].name, componentName) == 0) {
+ components[i].isEnabled = enabled;
+ break;
+ }
+ }
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgr_SetStatusComponentInfo --
+ *
+ * This function sets the status of the component managed by the plugin and
+ * publishes guestVar guestinfo.vmware.components.<comp_name>.laststatus.
+ *
+ * @param[in] ctx Tools application context.
+ * @param[in] componentIndex Index of the component in the global array of
+ * components.
+ * @param[in] exitCode The exit code of the async process running check status
+ * operation on a component.
+ *
+ * @return
+ * None.
+ *
+ * Side effects:
+ * Sets the current status for a component.
+ *
+ *****************************************************************************
+ */
+
+void
+ComponentMgr_SetStatusComponentInfo(ToolsAppCtx *ctx, //IN
+ int exitCode, //IN
+ int componentIndex) //IN
+{
+ gchar *msg;
+ gboolean status;
+
+ msg = g_strdup_printf("%s.%s.%s %d", COMPONENTMGR_PUBLISH_COMPONENTS,
+ components[componentIndex].name,
+ COMPONENTMGR_INFOLASTSTATUS,
+ exitCode);
+
+ status = ComponentMgr_SendRpc(ctx, msg, NULL, NULL);
+ g_free(msg);
+ if (!status) {
+ g_info("%s: Error sending RPC message for setting laststatus.\n",
+ __FUNCTION__);
+ }
+
+ components[componentIndex].status = exitCode;
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgrSetEnabledAllComponents --
+ *
+ * This function enables/disables all the components managed by the plugin.
+ *
+ * @param[in] enabled A boolean value to enable/disable all components.
+ *
+ * @return
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *****************************************************************************
+ */
+
+static void
+ComponentMgrSetEnabledAllComponents(gboolean enabled)
+{
+ int i;
+ for (i = 0; i < ARRAYSIZE(components); i++) {
+ if (enabled) {
+ components[i].isEnabled = TRUE;
+ } else {
+ components[i].isEnabled = FALSE;
+ }
+ }
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgr_ExecuteComponentAction --
+ *
+ * This function validates the current status of the component against
+ * current action be to be taken for a component and constructs a commandline
+ * to execute present/absent action on a component as an async process.
+ *
+ * @param[in] componentIndex Index of the component in the global array of
+ * components.
+ *
+ * @return
+ * None.
+ *
+ * Side effects:
+ * None.
+ *****************************************************************************
+ */
+
+void
+ComponentMgr_ExecuteComponentAction(int componentIndex)
+{
+ gchar *scriptFullPath;
+ const char *action = NULL;
+ const char *defaultArguments = NULL;
+ char *commandline = NULL;
+ char* (*customizeAction)() = NULL;
+ Action installaction = components[componentIndex].action;
+
+ if (!components[componentIndex].isEnabled) {
+ g_debug("%s: Component %s is disabled", __FUNCTION__,
+ components[componentIndex].name);
+ return;
+ }
+
+ action = ComponentMgr_GetComponentAction(installaction);
+ if((Str_Strcmp(action, COMPONENTMGR_COMPONENTPRESENT) == 0) &&
+ (components[componentIndex].status == NOTINSTALLED ||
+ components[componentIndex].status == INSTALLFAILED ||
+ components[componentIndex].status == REMOVEFAILED)) {
+ installaction = PRESENT;
+ } else if((Str_Strcmp(action, COMPONENTMGR_COMPONENTABSENT) == 0) &&
+ (components[componentIndex].status == INSTALLED ||
+ components[componentIndex].status == INSTALLFAILED ||
+ components[componentIndex].status == REMOVEFAILED)) {
+ installaction = ABSENT;
+ } else {
+ g_debug("%s: Action %s will not be executed for component %s with "
+ "current status %s.\n", __FUNCTION__, action,
+ components[componentIndex].name,
+ ComponentMgr_GetComponentInstallStatus(components[componentIndex].status));
+ return;
+ }
+
+ g_info("%s: Executing action %s for component %s current status %s.\n",
+ __FUNCTION__, action, components[componentIndex].name,
+ ComponentMgr_GetComponentInstallStatus(components[componentIndex].status));
+
+ /*
+ * The main logic which handles the present/absent action on a component.
+ * Internally spins off async process to add/remove the component
+ * on the guest.
+ */
+ switch(installaction) {
+ case PRESENT:
+ defaultArguments = executionScripts[componentIndex].addActionArguments;
+ customizeAction = executionScripts[componentIndex].customizeAddAction;
+ break;
+ case ABSENT:
+ defaultArguments = executionScripts[componentIndex].removeActionArguments;
+ customizeAction = executionScripts[componentIndex].customizeRemoveAction;
+ break;
+ default:
+ break;
+ } // end switch
+ scriptFullPath = ComponentMgrGetScriptFullPath(executionScripts[componentIndex].scriptName,
+ executionScripts[componentIndex].componentDirectory);
+
+ commandline = ComponentMgrConstructCommandline(scriptFullPath,
+ defaultArguments,
+ executionScripts[componentIndex].mandatoryParameters,
+ customizeAction);
+ g_free(scriptFullPath);
+
+ if (commandline == NULL) {
+ return;
+ }
+
+ g_info("%s: Commandline %s to perform %s action on component %s.\n",
+ __FUNCTION__, commandline, action, components[componentIndex].name);
+ ComponentMgr_AsynchrnousComponentActionStart(ComponentMgr_GetToolsAppCtx(),
+ commandline, componentIndex);
+ free(commandline);
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgrPublishKnownComponents --
+ *
+ * This function publishes guestVar guestinfo.vmware.components.available
+ * with all the components managed by the plugin.
+ *
+ * @param[in] ctx Tools application context
+ *
+ * @return
+ * None.
+ *
+ * Side effects:
+ * Updates the enabled/disabled status of the all the components managed
+ * by the plugin.
+ *
+ *****************************************************************************
+ */
+
+static void
+ComponentMgrPublishKnownComponents(ToolsAppCtx *ctx)
+{
+ int i;
+ DynBuf enabledComponents;
+ DynBuf_Init(&enabledComponents);
+
+ for (i = 0; i < ARRAYSIZE(components); i++) {
+ if (components[i].isEnabled) {
+ gchar *scriptFullPath;
+ /*
+ * We need to check the existence of the script for a particular
+ * component before we begin the preset/absent action on the component.
+ * Skipping the component if no script is installed.
+ */
+ scriptFullPath = ComponentMgrGetScriptFullPath(executionScripts[i].scriptName,
+ executionScripts[i].componentDirectory);
+
+ if (!File_Exists(scriptFullPath)) {
+ g_info("%s: Script file for component %s does not exists "
+ "under path %s.\n", __FUNCTION__, components[i].name,
+ scriptFullPath);
+ g_free(scriptFullPath);
+ components[i].isEnabled = FALSE;
+ continue;
+ }
+
+ g_free(scriptFullPath);
+
+ if (DynBuf_GetSize(&enabledComponents) != 0) {
+ DynBuf_Append(&enabledComponents, ",", 1);
+ }
+ DynBuf_Append(&enabledComponents, components[i].name,
+ strlen(components[i].name));
+ }
+ }
+
+ if (DynBuf_GetSize(&enabledComponents) == 0) {
+ ComponentMgr_PublishAvailableComponents(ctx, COMPONENTMGR_NONECOMPONENTS);
+ } else {
+ ComponentMgr_PublishAvailableComponents(ctx,
+ DynBuf_GetString(&enabledComponents));
+ }
+
+ DynBuf_Destroy(&enabledComponents);
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgrIncludedComponents --
+ *
+ * This function checks and validates the comma seperated list fetched from
+ * included tools.conf configuration and classifies the first occurrence of
+ * all or none which are special values and returns the result.
+ *
+ * @param[in] componentString Comma seperated string from the included
+ * tools.conf configuration.
+ *
+ * @retun
+ * Classify first occurrence of all or none in the string and return.
+ *
+ * Side effects
+ * None.
+ *
+ *****************************************************************************
+ */
+
+static IncludedComponents
+ComponentMgrIncludedComponents(const char* componentString) //IN
+{
+ int i;
+ gchar **componentList = NULL;
+ IncludedComponents include = NOSPECIALVALUES;
+
+ if (componentString == NULL || *componentString == '\0') {
+ g_info("%s: No components included in the ComponentMgr plugin.\n",
+ __FUNCTION__);
+ return NONECOMPONENTS;
+ }
+
+ componentList = g_strsplit(componentString, ",", 0);
+ for (i = 0; componentList[i] != NULL; i++ ) {
+ g_strstrip(componentList[i]);
+
+ if (strcmp(componentList[i], COMPONENTMGR_ALLCOMPONENTS) == 0) {
+ include = ALLCOMPONENTS;
+ break;
+ }
+ if (strcmp(componentList[i], COMPONENTMGR_NONECOMPONENTS) == 0) {
+ include = NONECOMPONENTS;
+ break;
+ }
+ }
+
+ g_strfreev(componentList);
+ return include;
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgr_UpdateComponentEnableStatus --
+ *
+ * This functions reads the comma seperated list of components in the included
+ * tools.conf configuration and sets the enabled/disabled status for all the
+ * components managed by the plugin.
+ * It also publishes guestvar guestinfo.vmware.components.available with
+ * info of all the components managed by the plugin.
+ *
+ * @param[in] ctx Tools application context.
+ *
+ * @return
+ * None.
+ *
+ * Side effects:
+ * Updates the enabled/disabled status of the all the components managed
+ * by the plugin.
+ *
+ *****************************************************************************
+ */
+
+void
+ComponentMgr_UpdateComponentEnableStatus(ToolsAppCtx *ctx) //IN
+{
+ gchar *listString;
+ char *context = NULL;
+ char *token;
+
+ listString = VMTools_ConfigGetString(ctx->config,
+ COMPONENTMGR_CONFGROUPNAME,
+ COMPONENTMGR_CONF_INCLUDEDCOMPONENTS,
+ COMPONENTMGR_ALLCOMPONENTS);
+
+ IncludedComponents included = ComponentMgrIncludedComponents(listString);
+ switch (included) {
+ case ALLCOMPONENTS:
+ ComponentMgrSetEnabledAllComponents(TRUE);
+ goto publishComponents;
+ case NONECOMPONENTS:
+ ComponentMgrSetEnabledAllComponents(FALSE);
+ goto publishComponents;
+ default:
+ break;
+ }
+
+ /*
+ * Setting all components to disabled state.
+ */
+ ComponentMgrSetEnabledAllComponents(FALSE);
+
+ /*
+ * Split the comma separated list of included components and individually
+ * set the status of each component as TRUE.
+ */
+ token = strtok_r(listString, ",", &context);
+ while (token != NULL) {
+ ComponentMgrSetEnabledComponentInfo(token, TRUE);
+ token = strtok_r(NULL, ",", &context);
+ }
+
+publishComponents:
+ g_free(listString);
+ ComponentMgrPublishKnownComponents(ctx);
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgrCheckExecuteComponentAction --
+ *
+ * This function validates the current status of the component against
+ * current action for the component and waits for status update counter
+ * to reach zero to run a check status operation if the component status
+ * and component action are not compliant.
+ * If the component action and component status are compliant, it spins of
+ * an async check status operation.
+ *
+ * @param[in] ctx Tools application context.
+ * @param[in] componentIndex Index of the component in the global array of
+ * components.
+ * @param[in] action The action that shall be performed on a component.
+ *
+ * @return
+ * None.
+ *
+ * Side effects:
+ * Run an asynchronous process to check current status of the component.
+ *
+ *****************************************************************************
+ */
+
+static void
+ComponentMgrCheckExecuteComponentAction(ToolsAppCtx *ctx, //IN
+ int componentIndex, //IN
+ const char *action) //IN
+{
+ Action installaction;
+ char* commandline = NULL;
+ void (*callbackFunction)(int) = &ComponentMgr_ExecuteComponentAction;
+
+ /*
+ * It is possible at this stage, an async process for checkstatus or
+ * present/absent action may be running for the component. In such a sceanrio
+ * the plugin shall not trigger any other async process.
+ */
+ ASSERT(components[componentIndex].isEnabled);
+ if (ComponentMgr_IsAsyncProcessRunning(componentIndex)) {
+ return;
+ }
+
+ commandline = ComponentMgr_CheckStatusCommandLine(componentIndex);
+ if (commandline == NULL) {
+ g_info("%s: Unable to construct commandline instruction to run check "
+ "status for the component %s\n", __FUNCTION__,
+ components[componentIndex].name);
+ return;
+ }
+
+ /*
+ * Add the component to the guest only if it is NOTINSTALLED,
+ * INSTALLFAILED or REMOVEFAILED.
+ * Remove the component on the guest only if it is INSTALLED,
+ * INSTALLFAILED or REMOVEFAILED.
+ */
+ if((Str_Strcmp(action, COMPONENTMGR_COMPONENTPRESENT) == 0) &&
+ (components[componentIndex].status == NOTINSTALLED ||
+ components[componentIndex].status == INSTALLFAILED ||
+ components[componentIndex].status == REMOVEFAILED)) {
+ installaction = PRESENT;
+ } else if((Str_Strcmp(action, COMPONENTMGR_COMPONENTABSENT) == 0) &&
+ (components[componentIndex].status == INSTALLED ||
+ components[componentIndex].status == INSTALLFAILED ||
+ components[componentIndex].status == REMOVEFAILED)) {
+ installaction = ABSENT;
+ } else {
+ components[componentIndex].statuscount -= 1;
+ if (components[componentIndex].statuscount != 0) {
+ /*
+ * Status count down for the component has not reached 0
+ * come back again in next interval.
+ */
+ g_debug("%s: Status count down for component %s is %d.\n",
+ __FUNCTION__, components[componentIndex].name,
+ components[componentIndex].statuscount);
+ free(commandline);
+ return;
+ } else {
+ /*
+ * Status count down has reached 0. We need to call the async
+ * check status once and update the last status of the component.
+ * Set the callback function for the async check status call NULL,
+ * since it's a single check status operation.
+ */
+ callbackFunction = NULL;
+ }
+ }
+
+ /*
+ * Resetting the value of status count of a component, since the action
+ * might have changed or status count down has reached 0.
+ */
+ components[componentIndex].action = installaction;
+ components[componentIndex].statuscount = COMPONENTMGR_CHECK_STATUS_COUNT_DOWN;
+
+ /*
+ * Before invoking any action for a component, we need to check the current
+ * status for that component. We run the pre configured script with pre
+ * configured check status arguments to the script.
+ * An async process in spin off to perform check status of a component with
+ * an option of sequenced operation after check status call.
+ */
+ g_debug("%s: Checking current status of component %s with commandline %s.\n",
+ __FUNCTION__, components[componentIndex].name, commandline);
+ ComponentMgr_AsynchronousComponentCheckStatus(ctx, commandline,
+ componentIndex,
+ callbackFunction);
+ free(commandline);
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgr_DestroyAsyncProcess -
+ *
+ * Destroy and free any or all async process running for a component.
+ *
+ * @return
+ * None.
+ *
+ * Side effects:
+ * Kills the async process runing any action for a component instantly.
+ *
+ *****************************************************************************
+ */
+
+void
+ComponentMgr_DestroyAsyncProcess()
+{
+ int i;
+
+ for (i = 0; i < ARRAYSIZE(components); i++) {
+ if (components[i].procInfo != NULL) {
+ g_debug("%s: Destroying running async process for component %s.\n",
+ __FUNCTION__, components[i].name);
+ ComponentMgr_FreeAsyncProc(components[i].procInfo);
+ } else {
+ g_debug("%s: No async process running for component %s.\n",
+ __FUNCTION__, components[i].name);
+ }
+ }
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgr_Destroytimers -
+ *
+ * This function destroys the GSource timers for all components.
+ *
+ * @return
+ * None.
+ *
+ * Side effects:
+ * Destroys the timeout GSource timers for all the components.
+ *
+ *****************************************************************************
+ */
+
+void
+ComponentMgr_Destroytimers(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAYSIZE(components); i++) {
+ if (components[i].sourceTimer != NULL) {
+ g_debug("%s: Destroying timers for component %s.\n", __FUNCTION__,
+ components[i].name);
+ g_source_destroy(components[i].sourceTimer);
+ components[i].sourceTimer = NULL;
+ } else {
+ g_debug("%s: Source timers for component %s has already been "
+ "destroyed.\n", __FUNCTION__, components[i].name);
+ }
+ }
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgr_UpdateComponentStatus --
+ *
+ * This function loops through all the enabled components in the plugin and
+ * fetches the action for individual components from the guestVar
+ * guestinfo./vmware.components.<comp_name>.desiredstate and triggers
+ * check status and execute action for a component.
+ *
+ * @param[in] ctx Tools application context.
+ *
+ * @return
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *****************************************************************************
+ */
+
+void
+ComponentMgr_UpdateComponentStatus(ToolsAppCtx *ctx)
+{
+ int i;
+
+ for (i = 0; i < ARRAYSIZE(components); i++) {
+ gboolean status;
+ char *componentDesiredState = NULL;
+ size_t replylen;
+ gchar *msg;
+
+ /*
+ * Proceed only if the component script is installed and
+ * the component is enabled by the plugin.
+ */
+ if (!components[i].isEnabled) {
+ continue;
+ }
+
+ msg = g_strdup_printf("%s.%s.%s", COMPONENTMGR_ACTION,
+ components[i].name,
+ COMPONENTMGR_INFODESIREDSTATE);
+ /*
+ * Fetch the action for a component from the guestVar
+ * guestinfo./vmware.components.<comp_name>.desiredstate
+ */
+ status = ComponentMgr_SendRpc(ctx, msg, &componentDesiredState, &replylen);
+ g_free(msg);
+
+ if (!status) {
+ g_info("%s: Install action not available for component %s.\n",
+ __FUNCTION__, components[i].name);
+ vm_free(componentDesiredState);
+ componentDesiredState = NULL;
+ continue;
+ }
+
+ if (componentDesiredState != NULL &&
+ (Str_Strcmp(componentDesiredState, COMPONENTMGR_COMPONENTPRESENT) == 0 ||
+ Str_Strcmp(componentDesiredState, COMPONENTMGR_COMPONENTABSENT) == 0)) {
+ ComponentMgrCheckExecuteComponentAction(ctx, i, componentDesiredState);
+ }
+
+ vm_free(componentDesiredState);
+ componentDesiredState = NULL;
+ }
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2021 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.
+ *
+ *********************************************************/
+
+/*
+ * componentMgrInstallManager.c --
+ *
+ * This file contains all the neccessary functions and handling of performing
+ * check status operation and add/remove of a component.
+ * The operations are triggerred as an async process and GSource timers are
+ * created to monitor the execution status of the async process.
+ * After successful completion of the async process, it's resources are
+ * released to make way for a new async process.
+ * Contains functions related to creation of new async process, monitoring of
+ * an async process and freeing of a async process resources.
+ *
+ */
+
+#include "componentMgrPlugin.h"
+
+
+/*
+ *****************************************************************************
+ * ComponentMgr_FreeAsyncProc --
+ *
+ * This function frees the async process resources.
+ * First we kill the commandline process which is child's child by pid.
+ * Then we kill the child process which is vmtoolsd.
+ *
+ * @param[in] asyncProcessInfo Asynchronous process resources to be freed.
+ *
+ * @return
+ * None.
+ *
+ * Side effects:
+ * Destroys and frees the async process resources.
+ *
+ *****************************************************************************
+ */
+
+void
+ComponentMgr_FreeAsyncProc(asyncProcessInfo *procInfo) //IN
+{
+ int componentIndex = procInfo->componentIndex;
+#if defined(__linux__)
+ if (ProcMgr_IsAsyncProcRunning(procInfo->asyncProc)) {
+ ProcMgr_Pid procPid = ProcMgr_GetPid(procInfo->asyncProc);
+ ProcMgr_KillByPid(procPid);
+ }
+#endif
+ ProcMgr_Kill(procInfo->asyncProc);
+ ProcMgr_Free(procInfo->asyncProc);
+ g_free(procInfo);
+
+ // Reset the async process info in the components array since the async
+ // process is no longer available.
+ ComponentMgr_ResetComponentAsyncProcInfo(componentIndex);
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgrCheckStatusMonitor --
+ *
+ * This function monitors the state the async process running check status
+ * command for a component. On completion of async process, the exit code
+ * is captured and set in the components structure for that component.
+ * On expiry of timer the async process will be killed.
+ *
+ * @param[in] data asyncProcessInfo pointer containing async process and
+ component info.
+ *
+ * @return
+ * G_SOURCE_CONTINUE To continue polling.
+ * G_SOURCE_REMOVE to stop polling.
+ *
+ * Side effects:
+ * Monitor the asyncProcess for its successful termination by polling.
+ * Kill the asyncProcess if it's taking long to terminate.
+ *
+ *****************************************************************************
+ */
+
+static gboolean
+ComponentMgrCheckStatusMonitor(void *data)
+{
+ int exitCode = -1;
+ int componentIndex;
+ const char *componentName = NULL;
+ void (*callbackFunction)(int compIndex) = NULL;
+
+ asyncProcessInfo *procInfo = (asyncProcessInfo*)data;
+ ASSERT(procInfo->asyncProc != NULL);
+
+ /*
+ * For every timeout callback from the timeout source, decrease
+ * the remaining execution time for the component.
+ */
+ procInfo->backoffTimer -= COMPONENTMGR_ASYNC_CHECK_STATUS_POLL_INTERVAL;
+ ProcMgr_Pid procPid = ProcMgr_GetPid(procInfo->asyncProc);
+ componentIndex = procInfo->componentIndex;
+ componentName = ComponentMgr_GetComponentName(componentIndex);
+
+ g_debug("%s: Callback received for process ID %d and component %s."
+ " Remaining time before termination %ds.\n", __FUNCTION__, procPid,
+ componentName, procInfo->backoffTimer);
+
+ if (!ProcMgr_IsAsyncProcRunning(procInfo->asyncProc)) {
+#if defined(__linux__)
+ if (ProcMgr_GetExitCode(procInfo->asyncProc, &exitCode) || exitCode == -1) {
+ exitCode = SCRIPTFAILED;
+ }
+#else
+ if (ProcMgr_GetExitCode(procInfo->asyncProc, &exitCode)) {
+ exitCode = SCRIPTFAILED;
+ }
+#endif
+ g_debug("%s: Checking status of a component has terminated gracefully"
+ " with exit code %d.\n", __FUNCTION__, exitCode);
+
+ ComponentMgr_SetStatusComponentInfo(procInfo->ctx,
+ exitCode,
+ procInfo->componentIndex);
+ callbackFunction = procInfo->callbackFunction;
+
+ /*
+ * At this stage free the asyncProcInfo object to make way for new async
+ * process. Source timer for a component will be no longer valid from here
+ * set it to NULL for next async process.
+ */
+ ComponentMgr_FreeAsyncProc(procInfo);
+ ComponentMgr_ResetComponentGSourceTimer(componentIndex);
+
+ /*
+ * After checkstatus operation has completed sucessfully, we can have a
+ * next sequence of operations to be executed on a component.
+ */
+ if (callbackFunction != NULL) {
+ callbackFunction(componentIndex);
+ }
+
+ return G_SOURCE_REMOVE;
+ } else {
+ /*
+ * At this stage the async process seems to be still running check
+ * status operation on a component. If the backoff timer value is not
+ * reached for a component, we proceed and wait for the async process
+ * to terminate. If the backoff timer has reached 0, the timed wait
+ * for the component is completed hence we kill the async process.
+ * First we kill the command process which is child's child by pid.
+ * Then we kill the child process which is vmtoolsd.
+ */
+ g_debug("%s: Process still running for component %s.\n", __FUNCTION__,
+ componentName);
+
+ if (procInfo->backoffTimer == 0) {
+ g_warning("%s: Backoff timer expired for process %d running check "
+ "status for component %s. Async process will be killed.",
+ __FUNCTION__, procPid, componentName);
+
+ ComponentMgr_SetStatusComponentInfo(procInfo->ctx,
+ SCRIPTTERMINATED,
+ componentIndex);
+
+ /*
+ * At this point the async process has timed out, so we need to
+ * kill the async process to make way for a new async process.
+ * Source timer for a component will be no longer valid from here
+ * set it to NULL for the next async process.
+ */
+ ComponentMgr_FreeAsyncProc(procInfo);
+ ComponentMgr_ResetComponentGSourceTimer(componentIndex);
+ return G_SOURCE_REMOVE;
+ }
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgrProcessMonitor --
+ *
+ * This function monitors the async process running the present/absent action
+ * on a component.
+ *
+ * @param[in] data asyncProcessInfo pointer containing async process and
+ component info.
+ *
+ * @return
+ * G_SOURCE_CONTINUE To continue polling.
+ * G_SOURCE_REMOVE To stop polling.
+ *
+ * Side effects:
+ * Monitor the async process for its successful termination by polling.
+ * Kill the async process if it's taking long to terminate.
+ *
+ *****************************************************************************
+ */
+
+static gboolean
+ComponentMgrProcessMonitor(void *data)
+{
+ char *commandline = NULL;
+ int componentIndex;
+ const char *componentName = NULL;
+
+ asyncProcessInfo *procInfo = (asyncProcessInfo*)data;
+ ASSERT(procInfo->asyncProc != NULL);
+
+ procInfo->backoffTimer -= COMPONENTMGR_ASYNCPROCESS_POLL_INTERVAL;
+ ProcMgr_Pid procPid = ProcMgr_GetPid(procInfo->asyncProc);
+ componentIndex = procInfo->componentIndex;
+ componentName = ComponentMgr_GetComponentName(componentIndex);
+
+ g_debug("%s: Callback received for process ID %d and component %s."
+ " Remaining time before termination %ds.\n", __FUNCTION__, procPid,
+ componentName, procInfo->backoffTimer);
+
+ if (!ProcMgr_IsAsyncProcRunning(procInfo->asyncProc)) {
+ /*
+ * At this stage the async process has completed its execution.
+ * Free all the async process resources and destroy the GSource timer.
+ */
+ g_debug("%s: Async process has exited.\n", __FUNCTION__);
+
+ ComponentMgr_FreeAsyncProc(procInfo);
+ ComponentMgr_ResetComponentGSourceTimer(componentIndex);
+
+ commandline = ComponentMgr_CheckStatusCommandLine(componentIndex);
+ if (commandline == NULL) {
+ g_info("%s: Unable to construct commandline instruction to run check "
+ "status for the component %s\n", __FUNCTION__,
+ ComponentMgr_GetComponentName(componentIndex));
+ ComponentMgr_SetStatusComponentInfo(ComponentMgr_GetToolsAppCtx(),
+ SCRIPTTERMINATED, componentIndex);
+ return G_SOURCE_REMOVE;
+ }
+
+ /*
+ * At this stage the async process running present/absent has completed.
+ * We need to check the status of a component asynchronously and set the
+ * component status.
+ */
+ ComponentMgr_AsynchronousComponentCheckStatus(ComponentMgr_GetToolsAppCtx(),
+ commandline,
+ componentIndex,
+ NULL);
+ free(commandline);
+ return G_SOURCE_REMOVE;
+ } else {
+ /*
+ * At this stage the async process seems to be still running for a
+ * component. If the backoff timer value has not reached 0,
+ * we proceed and wait for the async process to terminate.
+ * If the backoff timer has reached 0, the timed wait
+ * has reached limit for the component and we kill the async process.
+ */
+ g_debug("%s: Process still running for component %s.\n", __FUNCTION__,
+ componentName);
+
+ if (procInfo->backoffTimer == 0) {
+ g_warning("%s: Backoff timer expired for process %d running action for"
+ "component %s. Async process will be killed.",
+ __FUNCTION__, procPid, componentName);
+
+ /*
+ * At this point the async process has to be terminated. We can
+ * free the structure to make way for newer async process.
+ * Source timer for a component will be no longer valid from here
+ * set it to NULL for next async process.
+ */
+ ComponentMgr_FreeAsyncProc(procInfo);
+ ComponentMgr_ResetComponentGSourceTimer(componentIndex);
+
+ commandline = ComponentMgr_CheckStatusCommandLine(componentIndex);
+ if (commandline == NULL) {
+ g_info("%s: Unable to construct commandline instruction to run check "
+ "status for the component %s\n", __FUNCTION__,
+ ComponentMgr_GetComponentName(componentIndex));
+ ComponentMgr_SetStatusComponentInfo(ComponentMgr_GetToolsAppCtx(),
+ SCRIPTTERMINATED, componentIndex);
+ return G_SOURCE_REMOVE;
+ }
+
+ ComponentMgr_AsynchronousComponentCheckStatus(ComponentMgr_GetToolsAppCtx(),
+ commandline,
+ componentIndex,
+ NULL);
+
+ free(commandline);
+ return G_SOURCE_REMOVE;
+ }
+ }
+ /*
+ * The async process has not yet completed its action. So poll again
+ * using the same GSource timer for that particular component.
+ */
+ return G_SOURCE_CONTINUE;
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgrCreateAsyncProcessInfo --
+ *
+ * This function creates the asynProcessInfo object related to an async process
+ *
+ * @param[in] asyncProc A ProcMgr_AsyncProc pointer containing information
+ about the created async process.
+ * @param[in] ctx Tools application context.
+ * @param[in] backoffTimer A timer value after expiry of which the async
+ process will be killed.
+ * @param[in] componentIndex Index of the component in the global array of
+ * components.
+ * @param[in] callbackFunction A callback function to sequence the operation
+ * after async process finishes.
+ *
+ * @return
+ * An asyncProcessInfo object.
+ *
+ * Side effects:
+ * The created asyncProcesInfo object should be freed after process
+ * is completed or terminated.
+ *
+ *****************************************************************************
+ */
+
+static asyncProcessInfo*
+ComponentMgrCreateAsyncProcessInfo(ProcMgr_AsyncProc *asyncProc,
+ ToolsAppCtx *ctx,
+ int backoffTimer,
+ int componentIndex,
+ void (*callbackFunction)(int componentIndex))
+{
+ asyncProcessInfo *procInfo;
+ procInfo = g_malloc(sizeof *procInfo);
+ procInfo->asyncProc = asyncProc;
+ procInfo->ctx = ctx;
+ procInfo->backoffTimer = backoffTimer;
+ procInfo->componentIndex = componentIndex;
+ procInfo->callbackFunction = callbackFunction;
+
+ return procInfo;
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgr_AsynchronousComponentCheckStatus --
+ *
+ * This function launches an async process to check the current status of the
+ * component on the system.
+ *
+ * @param[in] ctx Tools application context.
+ * @param[in] commandline <component_script> <action_arguments> command to be
+ * executed to add/remove the component.
+ * @param[in] componentIndex Index of the component in the global array of
+ * components.
+ * @param[in] callbackFunction A callback function to sequence the operation
+ * after async call completes.
+ *
+ * @return
+ * None.
+ *
+ * Side effects:
+ * Async process creation may fail for many reasons.
+ *
+ *****************************************************************************
+ */
+
+void
+ComponentMgr_AsynchronousComponentCheckStatus(ToolsAppCtx *ctx,
+ char *commandline,
+ int componentIndex,
+ void (*callback)(int compIndex))
+{
+ ProcMgr_ProcArgs userArgs;
+ GSource *sourceTimer;
+ asyncProcessInfo *procInfo;
+
+ /*
+ * If an async process is already running for the component.
+ * Do not spin another async process.
+ */
+ ASSERT(commandline != NULL);
+ ASSERT(!ComponentMgr_IsAsyncProcessRunning(componentIndex));
+
+ memset(&userArgs, 0, sizeof userArgs);
+ ProcMgr_AsyncProc *asyncProc = ProcMgr_ExecAsync(commandline, &userArgs);
+ if (asyncProc == NULL) {
+ g_warning("%s: Failed to create process", __FUNCTION__);
+ return;
+ }
+
+ /*
+ * We need information about the async process, component and backoff
+ * timer value, hence populate the same and store for future references.
+ */
+ procInfo = ComponentMgrCreateAsyncProcessInfo(asyncProc, ctx,
+ COMPONENTMGR_ASYNC_CHECK_STATUS_TERMINATE_PERIOD,
+ componentIndex,
+ callback);
+ /*
+ * Set the asyncProcInfo field and GSource timer in the components array
+ * to cache information of a running async process for a component.
+ */
+ sourceTimer = g_timeout_source_new(COMPONENTMGR_ASYNC_CHECK_STATUS_POLL_INTERVAL * 1000);
+ ComponentMgr_SetComponentAsyncProcInfo(procInfo, componentIndex);
+ ComponentMgr_SetComponentGSourceTimer(sourceTimer, componentIndex);
+ VMTOOLSAPP_ATTACH_SOURCE(ctx, sourceTimer, ComponentMgrCheckStatusMonitor,
+ procInfo, NULL);
+ g_source_unref(sourceTimer);
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgr_AsynchrnousComponentActionStart --
+ *
+ * This function invokes the component script as an async process to perform
+ * present/absent action and a GSource timer to poll the progress.
+ *
+ * @param[in] ctx Tools application context.
+ * @param[in] commandline <component_script> <action_arguments> command to be
+ * executed to add/remove the component.
+ * @param[in] componentIndex Index of component in global array of components.
+ *
+ * @return
+ * None.
+ *
+ * Side effects:
+ * Async process creation may fail for many reasons.
+ *
+ *****************************************************************************
+ */
+
+void
+ComponentMgr_AsynchrnousComponentActionStart(ToolsAppCtx *ctx,
+ char *commandline,
+ int componentIndex)
+{
+ ProcMgr_ProcArgs userArgs;
+ GSource *sourceTimer;
+ asyncProcessInfo *procInfo;
+
+ /*
+ * If an async process is already running for the component.
+ * Do not spin another async process.
+ */
+ ASSERT(commandline != NULL);
+ ASSERT(!ComponentMgr_IsAsyncProcessRunning(componentIndex));
+
+ memset(&userArgs, 0, sizeof userArgs);
+ ProcMgr_AsyncProc *asyncProc = ProcMgr_ExecAsync(commandline, &userArgs);
+ if (asyncProc == NULL) {
+ g_warning("%s: Failed to create process", __FUNCTION__);
+ return;
+ }
+
+ /*
+ * We need information about the async process, component and backoff
+ * timer value, hence populate the same and store for future references.
+ */
+ procInfo = ComponentMgrCreateAsyncProcessInfo(asyncProc, ctx,
+ COMPONENTMGR_ASYNCPROCESS_TERMINATE_PERIOD,
+ componentIndex, NULL);
+ /*
+ * Set the asyncProcInfo field and GSource timer field in the components
+ * array to cache info of running async process for a component.
+ */
+ sourceTimer = g_timeout_source_new(COMPONENTMGR_ASYNCPROCESS_POLL_INTERVAL * 1000);
+ ComponentMgr_SetComponentAsyncProcInfo(procInfo, componentIndex);
+ ComponentMgr_SetComponentGSourceTimer(sourceTimer, componentIndex);
+ VMTOOLSAPP_ATTACH_SOURCE(ctx, sourceTimer,
+ ComponentMgrProcessMonitor, procInfo, NULL);
+ g_source_unref(sourceTimer);
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2021 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 _COMPONENTMGRPlugin_H_
+#define _COMPONENTMGRPlugin_H_
+
+/*
+ * componentMgrPlugin.h --
+ *
+ * This file contains macros used by the componentMgr plugin having references
+ * for timer related information, guestVar information, component information
+ * and configuration realted information.
+ * Defines functions shared across the componentMgr plugin.
+ * Defines states structures to be used to cache and store information related
+ * to a component and async process.
+ *
+ */
+
+#define G_LOG_DOMAIN "componentMgr"
+
+#include "vm_basic_defs.h"
+#include "vmware/tools/plugin.h"
+#include "procMgr.h"
+
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
+
+//********************** Configuration Params Definitions *************
+
+/*
+ * Defines the current poll interval (in seconds).
+ * This value is controlled by the ComponentMgr.poll-interval config file option.
+ */
+#define COMPONENTMGR_CONF_POLLINTERVAL "poll-interval"
+
+/**
+ * Name of section of configuration to be read from tools.conf.
+ */
+#define COMPONENTMGR_CONFGROUPNAME "componentMgr"
+
+/**
+ * Defines the components managed by the componentMgr plugin.
+ * This value is controlled by the componentMgr.included config file option.
+ */
+#define COMPONENTMGR_CONF_INCLUDEDCOMPONENTS "included"
+
+//********************** Timer Definitions ****************************
+
+/**
+ * Default and minimum poll interval for componentMgr in seconds.
+ */
+#define COMPONENTMGR_POLL_INTERVAL 180
+
+/*
+ * Poll interval between 2 consecutive check status operation in seconds.
+ */
+#define COMPONENTMGR_ASYNC_CHECK_STATUS_POLL_INTERVAL 1
+
+/*
+ * Max time in seconds after which the async process running check status
+ * command will be terminated.
+ */
+#define COMPONENTMGR_ASYNC_CHECK_STATUS_TERMINATE_PERIOD 15
+
+/*
+ * Poll interval for waiting on the async process runnning the action for a
+ * component in seconds.
+ */
+#define COMPONENTMGR_ASYNCPROCESS_POLL_INTERVAL 5
+
+/*
+ * The wait period after which the async proces needs to be killed for a
+ * component in seconds.
+ */
+#define COMPONENTMGR_ASYNCPROCESS_TERMINATE_PERIOD 600
+
+/*
+ * The amount of times the check status operation needs to wait before any
+ * change in the guetsVar to trigger another checkstatus opeartion.
+ */
+#define COMPONENTMGR_CHECK_STATUS_COUNT_DOWN 10
+
+//********************** Component Action Definitions *********************
+
+/**
+ * Defines present action for a component to be installed on a system.
+ */
+#define COMPONENTMGR_COMPONENTPRESENT "present"
+
+/**
+ * Defines absent action for a component to be removed from a system.
+ */
+#define COMPONENTMGR_COMPONENTABSENT "absent"
+
+//********************** Guest Variable Definitions *********************
+
+/**
+ * Defines argument to publish installed and enabled components.
+ */
+#define COMPONENTMGR_INFOAVAILABLE "available"
+
+/**
+ * Defines argument to publish last status of a particular component.
+ */
+#define COMPONENTMGR_INFOLASTSTATUS "laststatus"
+
+/**
+ * Defines action to be taken on a component.
+ * guestinfo./vmware.components.<comp_name>.desiredstate
+ */
+#define COMPONENTMGR_INFODESIREDSTATE "desiredstate"
+
+/*
+ * GuestVar prefix string to fetch the action required for a component.
+ */
+#define COMPONENTMGR_ACTION "info-get guestinfo./vmware.components"
+
+/**
+ * String to set informational guestVar exposed by the plugin.
+ */
+#define COMPONENTMGR_PUBLISH_COMPONENTS "info-set guestinfo.vmware.components"
+
+//********************** Component Definitions *********************
+
+/**
+ * Defines none to indicate no component is managed by the plugin.
+ */
+#define COMPONENTMGR_NONECOMPONENTS "none"
+
+/**
+ * Define all the names of components under this section.
+ */
+#define SALT_MINION "salt_minion"
+
+/**
+ * Defines a config all in included to indicate all the components are
+ * managed by the plugin.
+ */
+#define COMPONENTMGR_ALLCOMPONENTS "all"
+
+/*
+ * The included param in the tools.conf contains comma seperated list
+ * of components and can have special values.
+ * Defines various special values present in the included tools.conf param.
+ */
+
+typedef enum IncludedComponents
+{
+ ALLCOMPONENTS,
+ NONECOMPONENTS,
+ NOSPECIALVALUES
+} IncludedComponents;
+
+//*******************************************************************
+
+/*
+ * Installation status of the components managed by the componentMgr plugin.
+ * The status for each component will be updated based on the exit code
+ * returned by the script executing check status operation.
+ */
+
+typedef enum InstallStatus
+{
+ INSTALLED = 100, /* The component is installed on the guest OS. */
+ INSTALLING, /* The component is being installed on the guest
+ OS. */
+ NOTINSTALLED, /* The component is not installed on the guest OS.
+ */
+ INSTALLFAILED, /* The component install failed on the guest OS. */
+ REMOVING, /* The component is being removed on the guest OS.
+ */
+ REMOVEFAILED, /* The component remove failed on the guest OS. */
+ SCRIPTFAILED = 126, /* The component script failed for some reason. */
+ SCRIPTTERMINATED = 130 /* The component script terminated for some reason.
+ */
+} InstallStatus;
+
+
+/*
+ * Actions currently supported by the componentMgr plugin for the known and
+ * enabled components.
+ */
+
+typedef enum Action
+{
+ PRESENT, /* The action adds/installs the components on the guest. */
+ ABSENT, /* The action removes/uninstalls the components on the guest.*/
+ CHECKSTATUS, /* The actions calls the preconfigured script to check the
+ current status of the component. */
+ INVALIDACTION /* Action not recongnised by the plugin. */
+} Action;
+
+
+/*
+ * Structure to store information about the asynchronous process being run
+ * for a particular component.
+ */
+
+typedef struct asyncProcessInfo {
+ ProcMgr_AsyncProc *asyncProc; /* ProcMgr_AsyncProc structure consisting of
+ the process data running an action on the
+ component. */
+ ToolsAppCtx *ctx; /* Tools application context. */
+ int backoffTimer; /* Backoff timer to wait until timeout
+ to kill the asynchronous process. */
+ int componentIndex; /* The index of the component in the global
+ array of components. */
+ void (*callbackFunction)(int componentIndex); /* A callback function to
+ sequence a new operation
+ */
+} asyncProcessInfo;
+
+
+/*
+ * This structure contains all the information related to all the components
+ * managed by the plugin. The component states is maintained in this structure.
+ */
+
+typedef struct componentInfo
+{
+ const char *name; /* The name of the component. */
+ gboolean isEnabled; /* Component enabled/disabled by the plugin. */
+ InstallStatus status; /* Contains current status of the component. */
+ GSource *sourceTimer; /* A GSource timer for async process monitoring running
+ an operation for a component. */
+ asyncProcessInfo *procInfo; /* A structure to store information about the
+ * current running async process for a component.
+ */
+ int statuscount; /* A counter value to store max number of times to
+ wait before starting another checkstatus opeartion
+ */
+ Action action; /* Contains information about the action to be
+ performed on a component. */
+} componentInfo;
+
+
+void
+ComponentMgrUpdateComponentEnableStatus(ToolsAppCtx *ctx);
+
+
+void
+ComponentMgr_UpdateComponentStatus(ToolsAppCtx *ctx);
+
+
+gboolean
+ComponentMgr_SendRpc(ToolsAppCtx *ctx,
+ const char *guestInfoCmd,
+ char **outBuffer,
+ size_t *outBufferLen);
+
+
+void
+ComponentMgr_Destroytimers();
+
+
+const char*
+ComponentMgr_GetComponentInstallStatus(InstallStatus installStatus);
+
+
+const char*
+ComponentMgr_GetComponentAction(Action action);
+
+
+void
+ComponentMgr_AsynchrnousComponentActionStart(ToolsAppCtx *ctx,
+ char *commandline,
+ int componetIndex);
+
+
+void
+ComponentMgr_SetStatusComponentInfo(ToolsAppCtx *ctx,
+ int exitCode,
+ int componentIndex);
+
+
+char *
+ComponentMgr_CheckStatusCommandLine(int componentIndex);
+
+
+void
+ComponentMgr_UpdateComponentEnableStatus(ToolsAppCtx *ctx);
+
+
+void
+ComponentMgr_SetComponentGSourceTimer(GSource *componentTimer,
+ int componentIndex);
+
+void
+ComponentMgr_ResetComponentGSourceTimer(int componentIndex);
+
+
+void
+ComponentMgr_ExecuteComponentAction(int componentIndex);
+
+
+void
+ComponentMgr_AsynchronousComponentCheckStatus(ToolsAppCtx *ctx,
+ char *commandline,
+ int componentIndex,
+ void (*callback)(int compIndex));
+
+
+ToolsAppCtx*
+ComponentMgr_GetToolsAppCtx();
+
+
+const char*
+ComponentMgr_GetIncludedComponents(IncludedComponents pos);
+
+
+void
+ComponentMgr_SetComponentAsyncProcInfo(asyncProcessInfo *asyncProcInfo,
+ int componentIndex);
+
+
+void
+ComponentMgr_ResetComponentAsyncProcInfo(int componentIndex);
+
+
+gboolean
+ComponentMgr_IsAsyncProcessRunning(int componentIndex);
+
+
+const char*
+ComponentMgr_GetComponentName(int componentIndex);
+
+
+void
+ComponentMgr_FreeAsyncProc(asyncProcessInfo *procInfo);
+
+
+void
+ComponentMgr_DestroyAsyncProcess();
+
+
+void
+ComponentMgr_PublishAvailableComponents(ToolsAppCtx *ctx,
+ const char *components);
+
+
+gboolean
+ComponentMgr_CheckAnyAsyncProcessRunning();
+#endif /* _ComponentMgrPlugin_H_ */
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2021 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.
+ *
+ *********************************************************/
+
+/*
+ * componentMgrUtil.c --
+ *
+ * Common utility functions used by the componentMgr plugin.
+ *
+ */
+
+
+#include "componentMgrPlugin.h"
+#include "vmware/tools/log.h"
+
+
+/*
+ ******************************************************************************
+ * ComponentMgr_SendRpc --
+ *
+ * Sends RPC message to fetch the guestVars.
+ *
+ * @param[in] ctx Tools application context.
+ * @param[in] guestInfoCmd Guestinfo command to fetch the guestVar.
+ * @param[out] outBuffer Output buffer to hold RPC result (optional).
+ * @param[out] outBufferLen Output buffer len (optional).
+ *
+ * @return
+ * TRUE if cmd executed successfully, otherwise FALSE
+ *
+ * @note: outBuffer and outBufferLen are optional parameters and be used when
+ * RPC command output required. Both output parameters are required for output.
+ * Users have to call RpcChannel_Free on output buffer if supplied.
+ *
+ * Side effects:
+ * None.
+ ******************************************************************************
+ */
+
+gboolean
+ComponentMgr_SendRpc(ToolsAppCtx *ctx, //IN
+ const char *guestInfoCmd,//IN
+ char **outBuffer, //OUT
+ size_t *outBufferLen) //OUT
+{
+ gboolean status;
+ size_t replyLen;
+ char *reply = NULL;
+
+ ASSERT(ctx != NULL);
+ ASSERT(ctx->rpc != NULL);
+ ASSERT(guestInfoCmd != NULL);
+
+ status = RpcChannel_Send(ctx->rpc,
+ guestInfoCmd,
+ strlen(guestInfoCmd) + 1,
+ &reply,
+ &replyLen);
+ if (!status) {
+ g_info("%s: Failed to send RPC message, request: \'%s\',"
+ " reply: \'%s\'.\n", __FUNCTION__, guestInfoCmd,
+ VM_SAFE_STR(reply));
+ }
+
+ if (outBuffer != NULL && outBufferLen != NULL) {
+ *outBuffer = reply;
+ *outBufferLen = replyLen;
+ } else {
+ RpcChannel_Free(reply);
+ }
+ return status;
+}
+
+
+/*
+ **************************************************************************
+ * ComponentMgr_GetComponentInstallStatus
+ *
+ * This function returns an enum equivalent of the current status of the
+ * component.
+ *
+ * @param[in] installStatus Enum value of status of component.
+ *
+ * @return
+ * String equivalent of the component current status.
+ *
+ * @Side effects
+ * None.
+ *
+ **************************************************************************
+ */
+
+const char*
+ComponentMgr_GetComponentInstallStatus(InstallStatus installStatus) //IN
+{
+ switch (installStatus) {
+ case NOTINSTALLED: return "NOTINSTALLED";
+ case INSTALLING: return "INSTALLING";
+ case INSTALLED: return "INSTALLED";
+ case REMOVING: return "REMOVING";
+ case INSTALLFAILED: return "INSTALLFAILED";
+ case REMOVEFAILED: return "REMOVEFAILED";
+ case SCRIPTFAILED: return "SCRIPTFAILED";
+ case SCRIPTTERMINATED: return "SCRIPTTERMINATED";
+ }
+ return "INVALIDSTATUS";
+}
+
+
+/*
+ **************************************************************************
+ * ComponentMgr_GetComponentAction
+ *
+ * This function returns an enum equivalent of component action to be executed.
+ *
+ * @param[in] action The action to be taken on the component.
+ *
+ * @return
+ * String equivalent of the component action.
+ *
+ * @Side effects
+ * None.
+ *
+ **************************************************************************
+ */
+
+const char*
+ComponentMgr_GetComponentAction(Action action) //IN
+{
+ switch (action) {
+ case PRESENT: return "present";
+ case ABSENT: return "absent";
+ case CHECKSTATUS: return "checkstatus";
+ case INVALIDACTION: return "invalidaction";
+ }
+ return NULL;
+}
+
+
+/*
+ **************************************************************************
+ * ComponentMgr_GetIncludedComponents --
+ *
+ * This function returns an enum equivalent of the special values in the
+ * included tools.conf param.
+ *
+ * @param[in] specialvalue The enum value of special value in the param.
+ *
+ * @return
+ * String equivalent of the special value in the param.
+ *
+ * @Side effects
+ * None.
+ *
+ **************************************************************************
+ */
+
+
+const char*
+ComponentMgr_GetIncludedComponents(IncludedComponents specialValue)
+{
+ switch(specialValue)
+ {
+ case ALLCOMPONENTS: return "ALLCOMPONENTS";
+ case NONECOMPONENTS: return "NONECOMPONENTS";
+ default: return "NOSPECIALVALUES";
+ }
+ return "NOSPECIALVALUES";
+}
+
+
+/*
+ *****************************************************************************
+ * ComponentMgr_PublishAvailableComponents --
+ *
+ * This function publishes guestVar guestinfo.vmware.components.available with
+ * requested components.
+ *
+ * @param[in] ctx Tools application context.
+ * @param[in] components Comma seperated list of available components.
+ *
+ * @return
+ * None
+ *
+ * Side effects:
+ * None.
+ *****************************************************************************
+ */
+
+void
+ComponentMgr_PublishAvailableComponents(ToolsAppCtx *ctx,
+ const char *components)
+{
+ gboolean status;
+ gchar *msg = g_strdup_printf("%s.%s %s", COMPONENTMGR_PUBLISH_COMPONENTS,
+ COMPONENTMGR_INFOAVAILABLE,
+ components);
+
+ status = ComponentMgr_SendRpc(ctx, msg, NULL, NULL);
+ g_free(msg);
+
+ if (!status) {
+ g_info("%s: Error sending RPC message for known components.\n",
+ __FUNCTION__);
+ }
+}
# whether to include reserved space in diskInfo space metrics on Linux
#diskinfo-include-reserved=false
+[componentMgr]
+
+# This plugin manages the known and enabled components add/remove status.
+# The plugin polls at regular interval and triggers action add/remove for
+# all the known and enabled components in the componentMgr plugin.
+
+# Default and minimum polling interval in seconds (0 => polling disabled)
+#poll-interval=180
+
+# Comma separated list of components managed by the plugin. If not specified, default value is all, which means all components and enabled by default. A special value of none means no component, which is equivalent to disabling the plugin completely. Value is parsed left to right and parsing stops at first occurrence of all or none or end of line.
+#included=all
+
[appinfo]
# This plugin collects info about running applications in guest OS.