2025-11-28 00:35:46 +09:00

410 lines
14 KiB
C++

/******************************************************************************
* <copyright file="PluginContext.cpp" company="Microsoft">
* Copyright (c) Microsoft Corporation. All rights reserved.
* </copyright>
*****************************************************************************/
#include "PluginContext.h"
// Constructor.
CShellContext::CShellContext()
: shellPluginRequest(NULL),
shellShutdownRegisterWait(NULL)
{
}
// Destructor.
CShellContext::~CShellContext()
{
}
// Initialize to start a shell.
DWORD CShellContext::Initialize(__in PVOID pluginContext,
__in WSMAN_PLUGIN_REQUEST * requestDetails,
__in DWORD flags,
__in_opt WSMAN_SHELL_STARTUP_INFO * startupInfo,
__in_opt WSMAN_DATA * inboundShellInformation)
{
shellPluginRequest = requestDetails;
DWORD dwRet = NO_ERROR;
if ((RegisterWaitForSingleObject(&shellShutdownRegisterWait,
requestDetails->shutdownNotificationHandle,
_ShellCancellationCallback,
this,
INFINITE,
WT_EXECUTELONGFUNCTION | WT_EXECUTEONLYONCE) == 0))
{
dwRet = GetLastError();
}
else
{
dwRet = WSManPluginReportContext(requestDetails, 0, (void *) this);
if (dwRet)
{
if (UnregisterWaitEx(shellShutdownRegisterWait, INVALID_HANDLE_VALUE))
{
shellShutdownRegisterWait = NULL;
}
}
}
return dwRet;
}
// Run a command within the shell.
VOID CShellContext::Command(__in WSMAN_PLUGIN_REQUEST * requestDetails,
__in DWORD flags,
__in PCWSTR commandLine,
__in_opt WSMAN_COMMAND_ARG_SET * arguments)
{
CCommandContext * command = new CCommandContext;
if (command == NULL)
{
WSManPluginOperationComplete(requestDetails,
0,
ERROR_OUTOFMEMORY,
L"Not enough memory to carry out the operation");
return;
}
DWORD dwRet = command->Initialize(requestDetails,
flags,
this,
commandLine,
arguments);
if (dwRet != NO_ERROR)
{
WSManPluginOperationComplete(requestDetails,
0,
dwRet,
L"Command::Initialize failed");
delete command;
command = NULL;
}
return;
}
// Send input data to shell.
VOID CShellContext::Send(__in WSMAN_PLUGIN_REQUEST * requestDetails,
__in DWORD flags,
__in PCWSTR stream,
__in WSMAN_DATA * inboundData)
{
WSManPluginOperationComplete(requestDetails,
0,
ERROR_INVALID_OPERATION,
L"Inbound data not accepted for shells");
}
// Receive output data from shell.
VOID CShellContext::Receive(__in WSMAN_PLUGIN_REQUEST * requestDetails,
__in DWORD flags,
__in WSMAN_STREAM_ID_SET * streamSet)
{
WSManPluginOperationComplete(requestDetails,
0,
ERROR_INVALID_OPERATION,
L"Outbound data not available for shells");
}
// send signal to shell.
VOID CShellContext::Signal(__in WSMAN_PLUGIN_REQUEST * requestDetails,
__in DWORD flags,
__in PCWSTR code)
{
WSManPluginOperationComplete(requestDetails,
0,
ERROR_INVALID_OPERATION,
L"Signal not available for shells");
}
// Registered callback for shell shutdown notification.
VOID CALLBACK CShellContext::_ShellCancellationCallback(PVOID context,
BOOLEAN TimerOrWaitFired)
{
CShellContext *shell = (CShellContext*) context;
shell->ShellCancellationCallback();
}
// Complete plugin shell operation when shell shutdown notification happens.
// (shell context is released in WSManPluginReleaseShellContext)
VOID CShellContext::ShellCancellationCallback()
{
if (UnregisterWaitEx(shellShutdownRegisterWait, NULL))
{
shellShutdownRegisterWait = NULL;
}
WSManPluginOperationComplete(shellPluginRequest,
0,
ERROR_CANCELLED,
L"Shell was cancelled by WSMAN");
}
// Constructor.
CCommandContext::CCommandContext()
: commandPluginRequest(NULL),
receivePluginRequest(NULL),
shell(NULL),
commandShutdownRegisterWait(NULL),
receiveShutdownRegisterWait(NULL),
receiveAvailable(NULL)
{
}
// Destructor.
CCommandContext::~CCommandContext()
{
}
// Initialize to run a command within a shell.
DWORD CCommandContext::Initialize(__in WSMAN_PLUGIN_REQUEST * requestDetails,
__in DWORD flags,
__in CShellContext * parentShell,
__in PCWSTR commandLine,
__in_opt WSMAN_COMMAND_ARG_SET * arguments)
{
commandPluginRequest = requestDetails;
shell = parentShell;
receiveAvailable = CreateEvent(NULL, TRUE, FALSE, NULL);
if (receiveAvailable == NULL)
{
return GetLastError();
}
// Command ready for further operations
DWORD dwRet = 0;
if (RegisterWaitForSingleObject(&commandShutdownRegisterWait,
requestDetails->shutdownNotificationHandle,
_CommandCancellationCallback,
this,
INFINITE,
WT_EXECUTELONGFUNCTION | WT_EXECUTEONLYONCE) == 0)
{
dwRet = GetLastError();
}
else
{
dwRet = WSManPluginReportContext(requestDetails, 0, (void *) this);
if (dwRet)
{
if (UnregisterWaitEx(commandShutdownRegisterWait, INVALID_HANDLE_VALUE))
{
commandShutdownRegisterWait = NULL;
}
}
}
return dwRet;
}
// Send input data to command.
VOID CCommandContext::Send(__in WSMAN_PLUGIN_REQUEST * requestDetails,
__in DWORD flags,
__in PCWSTR stream,
__in WSMAN_DATA * inboundData)
{
if (wcscmp(stream, WSMAN_STREAM_ID_STDIN) != 0)
{
// only accept inbound data to be retrieved from stdin
WSManPluginOperationComplete(requestDetails,
0,
ERROR_INVALID_OPERATION,
L"Inbound data only accepted for stdin stream");
return;
}
//Send data back
HANDLE waitHandles[] =
{
receiveAvailable,
requestDetails->shutdownNotificationHandle
};
DWORD waitResult = WaitForMultipleObjects(2,
waitHandles,
FALSE,
INFINITE);
if (waitResult == WAIT_OBJECT_0+1)
{
//Need to fail the operation
WSManPluginOperationComplete(requestDetails,
0,
ERROR_CANCELLED,
L"Operation was cancelled");
return;
}
DWORD receiveFlags = 0;
PCWSTR commandState = NULL;
if (flags == WSMAN_FLAG_SEND_NO_MORE_DATA)
{
receiveFlags = WSMAN_FLAG_RECEIVE_RESULT_NO_MORE_DATA;
commandState = WSMAN_COMMAND_STATE_DONE;
}
else
{
commandState = WSMAN_COMMAND_STATE_RUNNING;
}
receiveFlags |= WSMAN_FLAG_RECEIVE_FLUSH;
DWORD result = WSManPluginReceiveResult(receivePluginRequest,
receiveFlags,
WSMAN_STREAM_ID_STDOUT,
inboundData,
commandState,
NO_ERROR);
if ((result != NO_ERROR) ||
(flags == WSMAN_FLAG_SEND_NO_MORE_DATA))
{
HANDLE h = (HANDLE)InterlockedExchangePointer((PVOID*)&receiveShutdownRegisterWait, NULL);
if (h)
{
if (UnregisterWaitEx(h, INVALID_HANDLE_VALUE))
{
h = NULL;
}
// Input is over so output is over
WSManPluginOperationComplete(receivePluginRequest, 0, result, NULL);
receivePluginRequest = NULL;
ResetEvent(receiveAvailable);
}
}
// done with this send
WSManPluginOperationComplete(requestDetails, 0, NO_ERROR, NULL);
}
// Receive output data from command.
VOID CCommandContext::Receive(__in WSMAN_PLUGIN_REQUEST * requestDetails,
__in DWORD flags,
__in WSMAN_STREAM_ID_SET * streamSet)
{
// support only to receive from stdout and stderr
if (streamSet == NULL ||
streamSet->streamIDsCount != 2)
{
WSManPluginOperationComplete(requestDetails,
0,
ERROR_INVALID_PARAMETER,
L"Expecting 2 streams for receive of stdout and stderr");
return;
}
if (wcscmp(streamSet->streamIDs[0], L"stdout") != 0 ||
wcscmp(streamSet->streamIDs[1], L"stderr") != 0)
{
WSManPluginOperationComplete(requestDetails,
0,
ERROR_INVALID_PARAMETER,
L"Expecting 2 streams for receive of stdout and stderr");
return;
}
if (RegisterWaitForSingleObject(&receiveShutdownRegisterWait,
requestDetails->shutdownNotificationHandle,
_ReceiveCancellationCallback,
this,
INFINITE,
WT_EXECUTELONGFUNCTION | WT_EXECUTEONLYONCE) == 0)
{
WSManPluginOperationComplete(requestDetails,
0,
GetLastError(),
L"RegisterWaitForSingleObject failed");
return;
}
// Set up the data so a Send can pipe data to the receive stream
receivePluginRequest = requestDetails;
SetEvent(receiveAvailable);
}
// send signal to command.
VOID CCommandContext::Signal(__in WSMAN_PLUGIN_REQUEST * requestDetails,
__in DWORD flags,
__in PCWSTR code)
{
if (_wcsicmp(code, WSMAN_SIGNAL_SHELL_CODE_TERMINATE) == 0)
{
// Handle command signal such as to terminate running command
WSManPluginOperationComplete(requestDetails, 0, NO_ERROR, NULL);
HANDLE h = (HANDLE)InterlockedExchangePointer((PVOID*)&commandShutdownRegisterWait, NULL);
if (h)
{
if (UnregisterWaitEx(h, INVALID_HANDLE_VALUE))
{
h = NULL;
}
WSManPluginOperationComplete(commandPluginRequest, 0, NO_ERROR, NULL);
}
}
else
{
// Signal operation is done
WSManPluginOperationComplete(requestDetails, 0, ERROR_INVALID_OPERATION,
L"Unsupported signal");
}
}
// Registered callback for command shutdown notification.
VOID CALLBACK CCommandContext::_CommandCancellationCallback(PVOID context,
BOOLEAN TimerOrWaitFired)
{
CCommandContext *command = (CCommandContext*) context;
command->CommandCancellationCallback();
}
// Complete plugin command operation when command shutdown notification happens.
// (command context is released in WSManPluginReleaseCommandContext)
VOID CCommandContext::CommandCancellationCallback()
{
HANDLE h = (HANDLE)InterlockedExchangePointer((PVOID*)&commandShutdownRegisterWait, NULL);
if (h)
{
if (UnregisterWaitEx(h, NULL))
{
h = NULL;
}
WSManPluginOperationComplete(commandPluginRequest,
0,
ERROR_CANCELLED,
L"Command was shutdown by wsman service");
}
}
// Registered callback for receive shutdown notification.
VOID CALLBACK CCommandContext::_ReceiveCancellationCallback(PVOID context,
BOOLEAN TimerOrWaitFired)
{
CCommandContext * command = (CCommandContext *) context;
command->ReceiveCancellationCallback();
}
VOID CCommandContext::ReceiveCancellationCallback()
{
HANDLE h = (HANDLE)InterlockedExchangePointer((PVOID*)&receiveShutdownRegisterWait, NULL);
if (h)
{
if (UnregisterWaitEx(h, NULL))
{
h = NULL;
}
ResetEvent(receiveAvailable);
WSMAN_PLUGIN_REQUEST * receiveRequest = receivePluginRequest;
receivePluginRequest = NULL;
WSManPluginOperationComplete(receiveRequest,
0,
ERROR_CANCELLED,
L"Receive Shutdown");
}
}