It just for learning Hello .... now I am making a change... FIS PUNE AUNDH
#define G_LOG_DOMAIN "powerops"
#include "vm_assert.h"
#include "vm_basic_defs.h"
#include "conf.h"
#include "procMgr.h"
#include "system.h"
#include "vmware/guestrpc/powerops.h"
#include "vmware/tools/plugin.h"
#include "vmware/tools/utils.h"
#if defined(G_PLATFORM_WIN32)
#else
#include <sys/wait.h>
#endif
#if !defined(APPLE)
#include "vm_version.h"
#include "embed_version.h"
#include "vmtoolsd_version.h"
VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING);
#endif
static const char *stateChgConfNames[] = {
NULL,
CONFNAME_POWEROFFSCRIPT,
CONFNAME_POWEROFFSCRIPT,
CONFNAME_POWERONSCRIPT,
CONFNAME_RESUMESCRIPT,
CONFNAME_SUSPENDSCRIPT,
};
/** Internal plugin state. */
typedef struct PowerOpState {
GuestOsState stateChgInProgress;
GuestOsState lastFailedStateChg;
#if defined(G_PLATFORM_WIN32)
ProcMgr_AsyncProc *pid;
#else
GPid pid;
#endif
ToolsAppCtx *ctx;
gboolean scriptEnabled[GUESTOS_STATECHANGE_LAST];
} PowerOpState;
/**
-
Returns the capabilities of the power ops plugin.
-
@param[in] src The source object.
-
@param[in] ctx The application context.
-
@param[in] set Whether capabilities are being set.
-
@param[in] data Unused.
-
@return List of capabilities.
*/
static GArray *
PowerOpsCapabilityCb(gpointer src,
ToolsAppCtx *ctx,
gboolean set,
gpointer data)
{
const ToolsAppCapability caps[] = {
{ TOOLS_CAP_OLD_NOVAL, "statechange", 0, 1 },
{ TOOLS_CAP_OLD_NOVAL, "softpowerop_retry", 0, 1 },
};
return VMTools_WrapArray(caps, sizeof *caps, ARRAYSIZE(caps));
}
/**
-
Handles power ops-related options.
-
@param[in] src The source object.
-
@param[in] ctx The app context.
-
@param[in] option Option being set.
-
@param[in] value Option value.
-
@param[in] plugin Plugin registration data.
-
@return TRUE on success.
*/
static gboolean
PowerOpsSetOption(gpointer src,
ToolsAppCtx *ctx,
const gchar *option,
const gchar *value,
ToolsPluginData *plugin)
{
gboolean enabled;
PowerOpState *state = plugin->_private;
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
return FALSE;
}
enabled = (strcmp(value, "1") == 0);
if (strcmp(option, TOOLSOPTION_SCRIPTS_POWERON) == 0) {
state->scriptEnabled[GUESTOS_STATECHANGE_POWERON] = enabled;
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_POWEROFF) == 0) {
state->scriptEnabled[GUESTOS_STATECHANGE_HALT] =
state->scriptEnabled[GUESTOS_STATECHANGE_REBOOT] = enabled;
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_SUSPEND) == 0) {
state->scriptEnabled[GUESTOS_STATECHANGE_SUSPEND] = enabled;
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_RESUME) == 0) {
state->scriptEnabled[GUESTOS_STATECHANGE_RESUME] = enabled;
} else {
return FALSE;
}
return TRUE;
}
/**
-
Clean up internal state on shutdown.
-
@param[in] src The source object.
-
@param[in] ctx Unused.
-
@param[in] plugin Plugin registration data.
*/
static void
PowerOpsShutdown(gpointer src,
ToolsAppCtx *ctx,
ToolsPluginData *plugin)
{
PowerOpState *state = plugin->_private;
g_free(state);
}
/**
-
Called when a state change script is done running. Sends the state change
-
status to the VMX.
-
@note This may halt/reboot the VM. Also the VMX may suspend the VM upon
-
receipt of a positive status.
-
@param[in] state Plugin state.
-
@param[in] success Whether the script was successful.
*/
static void
PowerOpsStateChangeDone(PowerOpState *state,
gboolean success)
{
gchar *msg;
g_debug("State change complete, success = %d.\n", success);
/*
* We execute the requested action if the script succeeded, or if the
* same action was tried before but didn't finish due to a script failure.
* See bug 168568 for discussion.
*/
if (success || state->lastFailedStateChg == state->stateChgInProgress) {
success = TRUE;
state->lastFailedStateChg = GUESTOS_STATECHANGE_NONE;
}
if (!success) {
state->lastFailedStateChg = state->stateChgInProgress;
}
/* Send the status message to the VMX. */
msg = g_strdup_printf("tools.os.statechange.status %d %d",
success,
state->stateChgInProgress);
if (!RpcChannel_Send(state->ctx->rpc, msg, strlen(msg) + 1, NULL, NULL)) {
g_warning("Unable to send the status RPC.");
}
g_free(msg);
/* Finally, perform the requested operation. */
if (success) {
if (state->stateChgInProgress == GUESTOS_STATECHANGE_REBOOT) {
g_message("Initiating reboot.\n");
System_Shutdown(TRUE);
} else if (state->stateChgInProgress == GUESTOS_STATECHANGE_HALT) {
g_message("Initiating halt.\n");
System_Shutdown(FALSE);
}
}
state->stateChgInProgress = GUESTOS_STATECHANGE_NONE;
}
#if defined(G_PLATFORM_WIN32)
/**
-
Callback for when the script process finishes on Win32 systems.
-
@param[in] _state Plugin state.
-
@return TRUE if the process is not finished yet.
*/
static gboolean
PowerOpsScriptCallback(gpointer _state)
{
PowerOpState *state = _state;
ASSERT(state->pid != INVALID_PID);
if (!ProcMgr_IsAsyncProcRunning(state->pid)) {
int exitcode;
gboolean success;
success = (ProcMgr_GetExitCode(state->pid, &exitcode) == 0 &&
exitcode == 0);
g_message("Script exit code: %d, success = %d\n", exitcode, success);
PowerOpsStateChangeDone(state, success);
ProcMgr_Free(state->pid);
state->pid = INVALID_PID;
return FALSE;
}
return TRUE;
}
/**
-
Starts a process using the ProcMgr library. For some reason the glib
-
spawn functions are a pain to use under Windows, and ProcMgr works
-
fine since we can use the "selectable" handle to monitor the child
-
process.
-
XXX: as soon as I figure out what's wrong with the "gspawn-win32-helper"
-
process and why it's not working, this should probably be merged with the
-
POSIX code below.
-
@param[in] state Plugin state.
-
@param[in] script Path to the script to be run.
-
@return Whether started the process successfully.
*/
static gboolean
PowerOpsRunScript(PowerOpState *state,
gchar *script)
{
gchar *quoted = NULL;
ProcMgr_ProcArgs procArgs;
/*
* Pass the CREATE_NO_WINDOW flag to CreateProcess so that the
* cmd.exe window will not be visible to the user in the guest.
*/
memset(&procArgs, 0, sizeof procArgs);
procArgs.bInheritHandles = TRUE;
procArgs.dwCreationFlags = CREATE_NO_WINDOW;
/* Quote the path if it's not yet quoted. */
if (script[0] != '"') {
quoted = g_strdup_printf("\"%s\"", script);
}
g_message("Executing script: %s\n", (quoted != NULL) ? quoted : script);
state->pid = ProcMgr_ExecAsync((quoted != NULL) ? quoted : script, &procArgs);
g_free(quoted);
if (state->pid != NULL) {
HANDLE h = ProcMgr_GetAsyncProcSelectable(state->pid);
GSource *watch = VMTools_NewHandleSource(h);
VMTOOLSAPP_ATTACH_SOURCE(state->ctx, watch, PowerOpsScriptCallback, state, NULL);
g_source_unref(watch);
return TRUE;
} else {
g_warning("Failed to start script: out of memory?\n");
return FALSE;
}
}
#else
/**
-
Callback for when the script process finishes on POSIX systems.
-
@param[in] pid Child pid.
-
@param[in] exitStatus Exit status of script.
-
@param[in] _state Plugin state.
-
@return FALSE.
*/
static gboolean
PowerOpsScriptCallback(GPid pid,
gint exitStatus,
gpointer _state)
{
PowerOpState *state = _state;
gboolean success = exitStatus == 0;
ASSERT(state->pid != INVALID_PID);
if (WIFEXITED(exitStatus)) {
g_message("Script exit code: %d, success = %d\n",
WEXITSTATUS(exitStatus), success);
} else if (WIFSIGNALED(exitStatus)) {
g_message("Script killed by signal: %d, success = %d\n",
WTERMSIG(exitStatus), success);
} else if (WIFSTOPPED(exitStatus)) {
g_message("Script stopped by signal: %d, success = %d\n",
WSTOPSIG(exitStatus), success);
} else {
g_message("Script exit status: %d, success = %d\n", exitStatus, success);
}
PowerOpsStateChangeDone(_state, success);
g_spawn_close_pid(state->pid);
state->pid = INVALID_PID;
return FALSE;
}
/**
-
Starts a process using glib. This works better in the non-Windows case,
-
since glib provides a nice API for monitoring when a process exits
-
(instead of having to poll the process every once in a while when using
-
ProcMgr.)
-
@param[in] state Plugin state.
-
@param[in] script Path to the script to be run.
-
@return Whether started the process successfully.
*/
static gboolean
PowerOpsRunScript(PowerOpState *state,
gchar *script)
{
gchar *argv[2];
GSource *watch;
GError *err = NULL;
argv[0] = g_locale_from_utf8(script, -1, NULL, NULL, &err);
if (err != NULL) {
g_debug("Conversion error: %s\n", err->message);
g_clear_error(&err);
/*
* If we could not convert to current locate let's hope that
* what we have is a useable script name and use it directly.
*/
argv[0] = g_strdup(script);
}
argv[1] = NULL;
g_message("Executing script: '%s'\n", script);
if (!g_spawn_async(NULL,
argv,
NULL,
G_SPAWN_DO_NOT_REAP_CHILD |
G_SPAWN_STDOUT_TO_DEV_NULL |
G_SPAWN_STDERR_TO_DEV_NULL,
NULL,
NULL,
&state->pid,
&err)) {
g_warning("Error starting script: %s\n", err->message);
g_clear_error(&err);
g_free(argv[0]);
return FALSE;
}
/* Setup a watch for when the child is done. */
watch = g_child_watch_source_new(state->pid);
VMTOOLSAPP_ATTACH_SOURCE(state->ctx, watch, PowerOpsScriptCallback, state, NULL);
g_source_unref(watch);
g_free(argv[0]);
return TRUE;
}
#endif
/**
-
Handler for commands which invoke state change scripts. Runs the configured
-
script for the power operation signaled by the host.
-
@param[in] data RPC data.
-
@return TRUE on success.
*/
static gboolean
PowerOpsStateChange(RpcInData *data)
{
size_t i;
PowerOpState *state = data->clientData;
if (state->pid != INVALID_PID) {
g_debug("State change already in progress.\n");
return RPCIN_SETRETVALS(data, "State change already in progress", FALSE);
}
g_debug("State change: %s\n", data->name);
for (i = 0; i < ARRAYSIZE(stateChangeCmdTable); i++) {
if (strcmp(data->name, stateChangeCmdTable[i].tcloCmd) == 0) {
gchar *script;
const char *result;
const char *confName;
Bool ret;
state->stateChgInProgress = stateChangeCmdTable[i].id;
/* Check for the toolScripts option. */
if (!state->scriptEnabled[stateChangeCmdTable[i].id]) {
PowerOpsStateChangeDone(state, TRUE);
g_debug("Script for %s not configured to run\n",
stateChangeCmdTable[i].tcloCmd);
return RPCIN_SETRETVALS(data, "", TRUE);
}
confName = stateChgConfNames[stateChangeCmdTable[i].id];
script = g_key_file_get_string(state->ctx->config,
"powerops",
confName,
NULL);
if (script == NULL) {
/* Use default script if not set in config file. */
const char *dfltScript = GuestApp_GetDefaultScript(confName);
if (dfltScript == NULL) {
g_debug("No default script to run for state change %s.\n",
stateChangeCmdTable[i].name);
PowerOpsStateChangeDone(state, TRUE);
return RPCIN_SETRETVALS(data, "", TRUE);
}
script = g_strdup(dfltScript);
} else if (strlen(script) == 0) {
g_debug("No script to run for state change %s.\n",
stateChangeCmdTable[i].name);
g_free(script);
PowerOpsStateChangeDone(state, TRUE);
return RPCIN_SETRETVALS(data, "", TRUE);
}
/* If script path is not absolute, assume the Tools install path. */
if (!g_path_is_absolute(script)) {
char *dfltPath;
char *tmp;
dfltPath = GuestApp_GetInstallPath();
ASSERT(dfltPath != NULL);
/*
* Before the switch to vmtoolsd, the config file was saved with
* quotes around the script path to make the old VMware dict code
* happy. Now we need to undo that when modifying the script path.
*
* PowerOpsRunScript will "re-quote" the script path.
*/
if (script[0] == '"') {
script[strlen(script) - 1] = '\0';
tmp = g_strdup_printf("%s%c%s", dfltPath, DIRSEPC, script + 1);
} else {
tmp = g_strdup_printf("%s%c%s", dfltPath, DIRSEPC, script);
}
g_free(script);
vm_free(dfltPath);
script = tmp;
}
if (PowerOpsRunScript(state, script)) {
result = "";
ret = TRUE;
} else {
PowerOpsStateChangeDone(state, FALSE);
result = "Error starting script";
ret = FALSE;
}
g_free(script);
return RPCIN_SETRETVALS(data, (char *) result, ret);
}
}
g_warning("Invalid state change command.\n");
return RPCIN_SETRETVALS(data, "Invalid state change command", FALSE);
}
/**
-
Plugin entry point. Returns the registration data.
-
@param[in] ctx Unused.
-
@return The registration data.
*/
TOOLS_MODULE_EXPORT ToolsPluginData *
ToolsOnLoad(ToolsAppCtx *ctx)
{
static ToolsPluginData regData = {
"powerops",
NULL,
NULL
};
size_t i;
PowerOpState *state;
ToolsPluginSignalCb sigs[] = {
{ TOOLS_CORE_SIG_CAPABILITIES, PowerOpsCapabilityCb, NULL },
{ TOOLS_CORE_SIG_SET_OPTION, PowerOpsSetOption, ®Data },
{ TOOLS_CORE_SIG_SHUTDOWN, PowerOpsShutdown, ®Data }
};
ToolsAppReg regs[] = {
{ TOOLS_APP_GUESTRPC, NULL },
{ TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) }
};
state = g_malloc0(sizeof *state);
state->ctx = ctx;
state->pid = INVALID_PID;
for (i = 0; i < GUESTOS_STATECHANGE_LAST; i++) {
state->scriptEnabled[i] = TRUE;
}
regs[0].data = g_array_sized_new(FALSE,
TRUE,
sizeof (RpcChannelCallback),
ARRAYSIZE(stateChangeCmdTable));
for (i = 0; i < ARRAYSIZE(stateChangeCmdTable); i++) {
RpcChannelCallback cb = { stateChangeCmdTable[i].tcloCmd,
PowerOpsStateChange,
state, NULL, NULL, 0 };
g_array_append_val(regs[0].data, cb);
}
regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs));
regData._private = state;
return ®Data;
}