terminal/src/server/ProcessHandle.cpp

68 lines
2.6 KiB
C++
Raw Normal View History

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "ProcessHandle.h"
#include "../host/globals.h"
#include "../host/telemetry.hpp"
// Routine Description:
// - Constructs an instance of the ConsoleProcessHandle Class
// - NOTE: Can throw if allocation fails or if there is a console policy we do not understand.
// - NOTE: Not being able to open the process by ID isn't a failure. It will be logged and continued.
ConsoleProcessHandle::ConsoleProcessHandle(const DWORD dwProcessId,
const DWORD dwThreadId,
const ULONG ulProcessGroupId) :
pWaitBlockQueue(std::make_unique<ConsoleWaitQueue>()),
pInputHandle(nullptr),
pOutputHandle(nullptr),
fRootProcess(false),
dwProcessId(dwProcessId),
dwThreadId(dwThreadId),
_ulTerminateCount(0),
_ulProcessGroupId(ulProcessGroupId),
_hProcess(LOG_LAST_ERROR_IF_NULL(OpenProcess(MAXIMUM_ALLOWED,
FALSE,
dwProcessId))),
Implement a pair of shims for `cls`, `Clear-Host` in conpty mode (#5627) ## Summary of the Pull Request This PR implements a pair of shims for `cmd` and `powershell`, so that their `cls` and `Clear-Host` functions will clear the entire terminal buffer (like they do in conhost), instead of just the viewport. With the conpty viewport and buffer being the same size, there's effectively no way to know if an application is calling these API's in this way with the intention of clearing the buffer or the viewport. We absolutely have to guess. Each of these shims checks to see if the way that the API is being called exactly matches the way `cmd` or `powershell` would call these APIs. If it does, we manually write a `^[[3J` to the connected terminal, to get he Terminal to clear it's own scrollback. ~~_⚠️ If another application were trying to clear the **viewport** with an exactly similar API call, this would also cause the terminal scrollback to get cleared ⚠️_~~ * [x] Should these shims be restricted to when the process that's calling them is actually `cmd.exe` or `powershell.exe`? Can I even do this? I think we've done such a good job of isolating the client process information from the rest of the host code that I can't figure out how to do this. - YES, this can be done, and I did it. * [ ] **TODO**: _While I'm here_, should I have `DoSrvPrivateEraseAll` (the implementation for `^[[2J`, in `getset.cpp`) also manually trigger a EraseAll in the terminal in conpty mode? ## PR Checklist * [x] Closes #3126 * [x] Actually closes #1305 too, which is really the same thing, but probably deserves a callout * [x] I work here * [x] Tests added/passed * [n/a] Requires documentation to be updated ## Validation Steps Performed * ran tests * checked `cls` in the Terminal * checked `Clear-Host` in the Terminal * Checked running `powershell clear-host` from `cmd.exe`
2020-04-30 23:53:31 +02:00
_policy(ConsoleProcessPolicy::s_CreateInstance(_hProcess.get())),
_shimPolicy(ConsoleShimPolicy::s_CreateInstance(_hProcess.get()))
{
if (nullptr != _hProcess.get())
{
Telemetry::Instance().LogProcessConnected(_hProcess.get());
}
}
Convert DeviceComm into an interface and add handle exchange (#8367) This commit replaces DeviceComm with the interface IDeviceComm and the concrete implementation type ConDrvDeviceComm. This work is done in preparation for different device backends. In addition to separating out ConDrv-specific behavior, I've introduced a "handle exchange" interface. HANDLE EXCHANGE --------------- There are points where we give ConDrv opaque handle identifiers to our input buffer, output buffer and process data. The exact content of the opaque identifier is meaningless to ConDrv: the driver's only interaction with these identifiers is to hold onto them and send back whichever are pertinent for each API call. Because of that, we used the raw register-width pointer value of the input buffer, output buffer or process data _as_ the opaque handle value. This works very well for ConDrv <-> conhost using the ConDrvDeviceComm. It works less well for something like the "logging" DeviceComm that will log packets to a file: those packets *cannot* contain pointer values (!) To address this, and to afford flexibility to DeviceComm implementers, I've introduced a two-member complement of handle management functions: * `ULONG_PTR PutHandle(void*)` registers an object with the DeviceComm and returns an opaque identifier. * `void* GetHandle(ULONG_PTR)` takes an opaque identifier and returns the original object. ConDrvDeviceComm implements PutHandle and GetHandle by casting the object pointer to the "opaque handle value", which maintains wire format compatibility[1] with revisions of conhost prior to this change. Simpler DeviceComm implementations that require handle tracking but cannot bear raw pointers can implement these functions by returning an autoincrementing ID (or similar) and registering the raw object pointer in a mapping. I've audited all existing handle exchanges with the driver and updated them to use Put/GetHandle. (I intended to add DestroyHandle, but we are not equipped for handle removal at the moment. ConsoleHandleData/ConsoleProcessHandle are destroyed during wait routine completion, on client disconnect, etc. This does mean that an id<->pointer mapping will grow without bound, but at a cost of ~8 bytes per entry and a short-lived console session I am not too concerned about the cost.) [1] Wire format compatibility is not required, and later we may want to switch ConDrvDeviceComm to `EncodePointer` and `DecodePointer` just to insulate us against a spurious ConDrv packet allowing for an arbitrary 4/8-byte read and subsequent liftoff into space.
2020-12-16 00:07:43 +01:00
// Routine Description:
// - Creates a CD_CONNECTION_INFORMATION (packet) that communicates the
// process, input and output handles to the driver as transformed by the
// DeviceComm's handle exchanger.
// Arguments:
// - deviceComm: IDeviceComm implementation with which to exchange handles.
CD_CONNECTION_INFORMATION ConsoleProcessHandle::GetConnectionInformation(IDeviceComm* deviceComm) const
{
CD_CONNECTION_INFORMATION result = { 0 };
Convert DeviceComm into an interface and add handle exchange (#8367) This commit replaces DeviceComm with the interface IDeviceComm and the concrete implementation type ConDrvDeviceComm. This work is done in preparation for different device backends. In addition to separating out ConDrv-specific behavior, I've introduced a "handle exchange" interface. HANDLE EXCHANGE --------------- There are points where we give ConDrv opaque handle identifiers to our input buffer, output buffer and process data. The exact content of the opaque identifier is meaningless to ConDrv: the driver's only interaction with these identifiers is to hold onto them and send back whichever are pertinent for each API call. Because of that, we used the raw register-width pointer value of the input buffer, output buffer or process data _as_ the opaque handle value. This works very well for ConDrv <-> conhost using the ConDrvDeviceComm. It works less well for something like the "logging" DeviceComm that will log packets to a file: those packets *cannot* contain pointer values (!) To address this, and to afford flexibility to DeviceComm implementers, I've introduced a two-member complement of handle management functions: * `ULONG_PTR PutHandle(void*)` registers an object with the DeviceComm and returns an opaque identifier. * `void* GetHandle(ULONG_PTR)` takes an opaque identifier and returns the original object. ConDrvDeviceComm implements PutHandle and GetHandle by casting the object pointer to the "opaque handle value", which maintains wire format compatibility[1] with revisions of conhost prior to this change. Simpler DeviceComm implementations that require handle tracking but cannot bear raw pointers can implement these functions by returning an autoincrementing ID (or similar) and registering the raw object pointer in a mapping. I've audited all existing handle exchanges with the driver and updated them to use Put/GetHandle. (I intended to add DestroyHandle, but we are not equipped for handle removal at the moment. ConsoleHandleData/ConsoleProcessHandle are destroyed during wait routine completion, on client disconnect, etc. This does mean that an id<->pointer mapping will grow without bound, but at a cost of ~8 bytes per entry and a short-lived console session I am not too concerned about the cost.) [1] Wire format compatibility is not required, and later we may want to switch ConDrvDeviceComm to `EncodePointer` and `DecodePointer` just to insulate us against a spurious ConDrv packet allowing for an arbitrary 4/8-byte read and subsequent liftoff into space.
2020-12-16 00:07:43 +01:00
result.Process = deviceComm->PutHandle(this);
result.Input = deviceComm->PutHandle(pInputHandle.get());
result.Output = deviceComm->PutHandle(pOutputHandle.get());
return result;
}
// Routine Description:
// - Retrieves the policies set on this particular process handle
// - This specifies restrictions that may apply to the calling console client application
const ConsoleProcessPolicy ConsoleProcessHandle::GetPolicy() const
{
return _policy;
}
Implement a pair of shims for `cls`, `Clear-Host` in conpty mode (#5627) ## Summary of the Pull Request This PR implements a pair of shims for `cmd` and `powershell`, so that their `cls` and `Clear-Host` functions will clear the entire terminal buffer (like they do in conhost), instead of just the viewport. With the conpty viewport and buffer being the same size, there's effectively no way to know if an application is calling these API's in this way with the intention of clearing the buffer or the viewport. We absolutely have to guess. Each of these shims checks to see if the way that the API is being called exactly matches the way `cmd` or `powershell` would call these APIs. If it does, we manually write a `^[[3J` to the connected terminal, to get he Terminal to clear it's own scrollback. ~~_⚠️ If another application were trying to clear the **viewport** with an exactly similar API call, this would also cause the terminal scrollback to get cleared ⚠️_~~ * [x] Should these shims be restricted to when the process that's calling them is actually `cmd.exe` or `powershell.exe`? Can I even do this? I think we've done such a good job of isolating the client process information from the rest of the host code that I can't figure out how to do this. - YES, this can be done, and I did it. * [ ] **TODO**: _While I'm here_, should I have `DoSrvPrivateEraseAll` (the implementation for `^[[2J`, in `getset.cpp`) also manually trigger a EraseAll in the terminal in conpty mode? ## PR Checklist * [x] Closes #3126 * [x] Actually closes #1305 too, which is really the same thing, but probably deserves a callout * [x] I work here * [x] Tests added/passed * [n/a] Requires documentation to be updated ## Validation Steps Performed * ran tests * checked `cls` in the Terminal * checked `Clear-Host` in the Terminal * Checked running `powershell clear-host` from `cmd.exe`
2020-04-30 23:53:31 +02:00
// Routine Description:
// - Retrieves the policies set on this particular process handle
// - This specifies compatibility shims that we might need to make for certain applications.
const ConsoleShimPolicy ConsoleProcessHandle::GetShimPolicy() const
{
return _shimPolicy;
}