#include "vmware/tools/log.h"
#include "vmware/tools/utils.h"
#include "vmware/tools/vmbackup.h"
-
+#if defined(_WIN32)
+# include "windowsu.h"
+#else
+# include "posix.h"
+#endif
/*
* Establish the default and maximum vmusr RPC channel error limits
g_key_file_free(state->ctx.config);
g_main_loop_unref(state->ctx.mainLoop);
-#if defined(G_PLATFORM_WIN32)
+#if defined(_WIN32)
if (state->ctx.comInitialized) {
CoUninitialize();
state->ctx.comInitialized = FALSE;
}
+#if defined(_WIN32)
+
+/**
+ * Gets error message for the last error.
+ *
+ * @param[in] error Error code to be converted to string message.
+ *
+ * @return The error message, or NULL in case of failure.
+ */
+
+static char *
+ToolCoreGetLastErrorMsg(DWORD error)
+{
+ char *msg = Win32U_FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ error,
+ 0, // Default language
+ NULL);
+ if (msg == NULL) {
+ g_warning("Failed to get error message for %d, error=%d.\n",
+ error, GetLastError());
+ return NULL;
+ }
+
+ return msg;
+}
+
+#endif
+
+
+/**
+ * Gets an environment variable for the current process.
+ *
+ * @param[in] name Name of the env variable.
+ *
+ * @return The value of env variable, or NULL in case of error.
+ */
+
+static gchar *
+ToolsCoreEnvGetVar(const char *name) // IN
+{
+ gchar *value;
+
+#if defined(_WIN32)
+ DWORD valueSize;
+ /*
+ * Win32U_GetEnvironmentVariable requires buffer to be accurate size.
+ * So, we need to get the value size first.
+ *
+ * Windows bug: GetEnvironmentVariable() does not clear stale
+ * error when the return value is 0 because of env variable
+ * holding empty string value (just NUL-char). So, we need to
+ * clear it before we call the Win32 API.
+ */
+ SetLastError(ERROR_SUCCESS);
+ valueSize = Win32U_GetEnvironmentVariable(name, NULL, 0);
+ if (valueSize == 0) {
+ goto error;
+ }
+
+ value = g_malloc(valueSize);
+ SetLastError(ERROR_SUCCESS);
+ if (Win32U_GetEnvironmentVariable(name, value, valueSize) == 0) {
+ g_free(value);
+ goto error;
+ }
+
+ return value;
+
+error:
+{
+ DWORD error = GetLastError();
+ if (error == ERROR_SUCCESS) {
+ g_message("Env variable %s is empty.\n", name);
+ //} else if (error == ERROR_ENVVAR_NOT_FOUND) {
+ // g_message("Env variable %s not found.\n", name);
+ } else {
+ char *errorMsg = ToolCoreGetLastErrorMsg(error);
+ if (errorMsg != NULL) {
+ g_warning("Failed to get env variable size %s, error=%s.\n",
+ name, errorMsg);
+ free(errorMsg);
+ } else {
+ g_warning("Failed to get env variable size %s, error=%d.\n",
+ name, error);
+ }
+ }
+ return NULL;
+}
+#else
+ value = Posix_Getenv(name);
+ return value == NULL ? value : g_strdup(value);
+#endif
+}
+
+
+/**
+ * Sets an environment variable for the current process.
+ *
+ * @param[in] name Name of the env variable.
+ * @param[in] value Value for the env variable.
+ *
+ * @return gboolean, TRUE on success or FALSE in case of error.
+ */
+
+static gboolean
+ToolsCoreEnvSetVar(const char *name, // IN
+ const char *value) // IN
+{
+#if defined(_WIN32)
+ if (!Win32U_SetEnvironmentVariable(name, value)) {
+ char *errorMsg;
+ DWORD error = GetLastError();
+
+ errorMsg = ToolCoreGetLastErrorMsg(error);
+ if (errorMsg != NULL) {
+ g_warning("Failed to set env variable %s=%s, error=%s.\n",
+ name, value, errorMsg);
+ free(errorMsg);
+ } else {
+ g_warning("Failed to set env variable %s=%s, error=%d.\n",
+ name, value, error);
+ }
+ return FALSE;
+ }
+#else
+ if (Posix_Setenv(name, value, TRUE) != 0) {
+ g_warning("Failed to set env variable %s=%s, error=%s.\n",
+ name, value, strerror(errno));
+ return FALSE;
+ }
+#endif
+ return TRUE;
+}
+
+
+/**
+ * Unsets an environment variable for the current process.
+ *
+ * @param[in] name Name of the env variable.
+ *
+ * @return gboolean, TRUE on success or FALSE in case of error.
+ */
+
+static gboolean
+ToolsCoreEnvUnsetVar(const char *name) // IN
+{
+#if defined(_WIN32)
+ if (!Win32U_SetEnvironmentVariable(name, NULL)) {
+ char *errorMsg;
+ DWORD error = GetLastError();
+
+ errorMsg = ToolCoreGetLastErrorMsg(error);
+ if (errorMsg != NULL) {
+ g_warning("Failed to unset env variable %s, error=%s.\n",
+ name, errorMsg);
+ free(errorMsg);
+ } else {
+ g_warning("Failed to unset env variable %s, error=%d.\n",
+ name, error);
+ }
+ return FALSE;
+ }
+#else
+ if (Posix_Unsetenv(name) != 0) {
+ g_warning("Failed to unset env variable %s, error=%s.\n",
+ name, strerror(errno));
+ return FALSE;
+ }
+#endif
+ return TRUE;
+}
+
+
+/**
+ * Setup environment variables for the current process from
+ * a given config group.
+ *
+ * @param[in] ctx Application context.
+ * @param[in] group Configuration group to be read.
+ * @param[in] doUnset Whether to unset the environment vars.
+ */
+
+static void
+ToolsCoreInitEnvGroup(ToolsAppCtx *ctx, // IN
+ const gchar *group, // IN
+ gboolean doUnset) // IN
+{
+ gsize i;
+ gsize length;
+ GError *err = NULL;
+ gchar **keys = g_key_file_get_keys(ctx->config, group, &length, &err);
+ if (err != NULL) {
+ if (err->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) {
+ g_warning("Failed to get keys for config group %s (err=%d).\n",
+ group, err->code);
+ }
+ g_clear_error(&err);
+ g_info("Skipping environment initialization for %s from %s config.\n",
+ ctx->name, group);
+ return;
+ }
+
+ g_info("Found %"FMTSZ"d environment variable(s) in %s config.\n",
+ length, group);
+
+ /*
+ * Following 2 formats are supported:
+ * 1. <variableName> = <value>
+ * 2. <serviceName>.<variableName> = <value>
+ *
+ * Variables specified in format #1 are applied to all services and
+ * variables specified in format #2 are applied to specified service only.
+ */
+ for (i = 0; i < length; i++) {
+ const gchar *name = NULL;
+ const gchar *key = keys[i];
+ const gchar *delim;
+
+ /*
+ * Pick the keys that have service name prefix or no prefix.
+ */
+ delim = strchr(key, '.');
+ if (delim == NULL) {
+ name = key;
+ } else if (strncmp(key, ctx->name, delim - key) == 0) {
+ name = delim + 1;
+ }
+
+ /*
+ * Ignore entries with empty env variable names.
+ */
+ if (name != NULL && *name != '\0') {
+ gchar *oldValue = ToolsCoreEnvGetVar(name);
+ if (doUnset) {
+ /*
+ * We can't avoid duplicate removals, but removing a non-existing
+ * environment variable is a no-op anyway.
+ */
+ if (ToolsCoreEnvUnsetVar(name)) {
+ g_message("Removed env var %s=[%s]\n",
+ name, oldValue == NULL ? "(null)" : oldValue);
+ }
+ } else {
+ gchar *value = VMTools_ConfigGetString(ctx->config, group,
+ key, NULL);
+ if (value != NULL) {
+ /*
+ * Get rid of trailing space.
+ */
+ g_strchomp(value);
+
+ /*
+ * Avoid updating environment var if it is already set to
+ * the same value.
+ *
+ * Also, g_key_file_get_keys() does not filter out duplicates
+ * but, VMTools_ConfigGetString returns only last entry
+ * for the key. So, by comparing old value, we avoid setting
+ * the environment multiple times when there are duplicates.
+ *
+ * NOTE: Need to use g_strcmp0 because oldValue can be NULL.
+ * As value can't be NULL but oldValue can be NULL, we might
+ * still do an unnecessary update in cases like setting a
+ * variable to empty/no value twice. However, it does not harm
+ * and is not worth avoiding it.
+ */
+ if (g_strcmp0(oldValue, value) == 0) {
+ g_info("Env var %s already set to [%s], skipping.\n",
+ name, oldValue);
+ g_free(oldValue);
+ g_free(value);
+ continue;
+ }
+ g_debug("Changing env var %s from [%s] -> [%s]\n",
+ name, oldValue == NULL ? "(null)" : oldValue, value);
+ if (ToolsCoreEnvSetVar(name, value)) {
+ g_message("Updated env var %s from [%s] -> [%s]\n",
+ name, oldValue == NULL ? "(null)" : oldValue,
+ value);
+ }
+ g_free(value);
+ }
+ }
+ g_free(oldValue);
+ }
+ }
+
+ g_info("Initialized environment for %s from %s config.\n",
+ ctx->name, group);
+ g_strfreev(keys);
+}
+
+
+/**
+ * Setup environment variables for the current process.
+ *
+ * @param[in] ctx Application context.
+ */
+
+static void
+ToolsCoreInitEnv(ToolsAppCtx *ctx)
+{
+ /*
+ * First apply unset environment configuration to start clean.
+ */
+ ToolsCoreInitEnvGroup(ctx, CONFGROUPNAME_UNSET_ENVIRONMENT, TRUE);
+ ToolsCoreInitEnvGroup(ctx, CONFGROUPNAME_SET_ENVIRONMENT, FALSE);
+}
+
+
/**
* Performs any initial setup steps for the service's main loop.
*
ToolsCoreService_RegisterProperty(state->ctx.serviceObj,
&ctxProp);
g_object_set(state->ctx.serviceObj, TOOLS_CORE_PROP_CTX, &state->ctx, NULL);
+ /* Initialize the environment from config. */
+ ToolsCoreInitEnv(&state->ctx);
ToolsCorePool_Init(&state->ctx);
/* Initializes the debug library if needed. */
# VMware Tools services every 5 seconds."
#
# Lines must not end with trailing white space
-# All file paths need to be in Unix format
-# (forward slashes) and in utf-8, for all operating systems.
-[logging]
+[unsetenvironment]
+# Defines environment variables to be removed from the service reading
+# the configuration file. Supported formats are:
+#
+# 1. <variableName>=
+# 2. <serviceName>.<variableName>=
+#
+# Where <serviceName> refers to the 'vmsvc' and 'vmusr',
+# <variableName> refers to the name of the environment
+# variable to be removed. '=' sign after <variableName>
+# is mandatory to maintain the configuration file syntax.
+# However, anything after '=' is ignored.
+#
+# Case-sensitive behavior is defined by the operating system.
+#
+# Note: unsetenvironment group is processed before setenvironment group.
+# As the service environment is setup at start up time, any changes
+# in this group require service to be restarted in order to take effect.
+#
+# Unsetting PATH for all services:
+# PATH=
+#
+# Unsetting PATH for vmsvc only:
+# vmsvc.PATH=
+#
+# Unsetting PATH for vmusr only:
+# vmusr.PATH=
+[setenvironment]
+# Defines environment variables to be set for the service reading
+# the configuration file. Supported formats are:
+#
+# 1. <variableName>=<variableValue>
+# 2. <serviceName>.<variableName>=<variableValue>
+#
+# Where <serviceName> refers to the 'vmsvc' and 'vmusr',
+# <variableName> refers to the name of the environment
+# variable to be set, and <variableValue> refers to the
+# value to be assigned to the environment variable.
+#
+# Case-sensitive behavior is defined by the operating system.
+#
+# Note: setenvironment group is processed after unsetenvironment group.
+# As the service environment is setup at start up time, any changes
+# in this group require service to be restarted in order to take effect.
+#
+# Setting TMPDIR for all services:
+# TMPDIR=/vmware/temp
+#
+# Setting TMPDIR for vmsvc only:
+# vmsvc.TMPDIR=/vmware/vmsvc/temp
+#
+# Setting TMPDIR for vmusr only:
+# vmusr.TMPDIR=/vmware/vmusr/temp
+
+[logging]
# set to false to disable logging
#log = true
# Possible values for handler are:
# file: logs to a file. Set *.data to the file name
# file+: same as 'file', but appends to the file
+# All file paths used in *.data value need to be in Unix
+# format (forward slashes) and in utf-8, for all operating
+# systems.
# vmx: logs to the host (ESXi, Workstation, Fusion)
# std: Logs to stdout for level >= 'message',
# and to stderr for more severe than 'message'.