Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions msipackage/package.wix.in
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,14 @@
</RegistryKey>
</RegistryKey>

<!-- IWSLCPluginNotifier-->
<RegistryKey Root="HKCR" Key="Interface\{F3E6D5B2-1D40-4E8B-9C39-7A45D1C0F8A2}">
<RegistryValue Value="IWSLCPluginNotifier" Type="string" />
<RegistryKey Key="ProxyStubClsid32">
<RegistryValue Value="{4EA0C6DD-E9FF-48E7-994E-13A31D10DC60}" Type="string" />
</RegistryKey>
</RegistryKey>

<File Id="wslcsession.exe" Source="${BIN}/wslcsession.exe" />
</Component>
<Component Id="wslg" Guid="F0C8D6BA-1502-41E7-BF72-D93DFA134731" UninstallWhenSuperseded="yes" DisableRegistryReflection="yes" Bitness="always64">
Expand Down
103 changes: 102 additions & 1 deletion src/windows/inc/WslPluginApi.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ extern "C" {
#define WSLPLUGINAPI_ENTRYPOINTV1 WSLPluginAPIV1_EntryPoint
#define WSL_E_PLUGIN_REQUIRES_UPDATE MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x032A)

// Maximum size for mount points returned by WSLCPluginAPI_MountFolder. This includes the null terminator.
#define WSLC_MOUNTPOINT_LENGTH 256

#define WSL_PLUGIN_REQUIRE_VERSION(_Major, _Minor, _Revision, Api) \
if (Api->Version.Major < (_Major) || (Api->Version.Major == (_Major) && Api->Version.Minor < (_Minor)) || \
(Api->Version.Major == (_Major) && Api->Version.Minor == (_Minor) && Api->Version.Revision < (_Revision))) \
Expand Down Expand Up @@ -85,13 +88,94 @@ struct WslOfflineDistributionInformation
LPCWSTR Version; // Distribution version. Introduced in 2.4.4
};

// Identifies a WSLC session inside the WSLC plugin API. Distinct from WSLSessionId.
typedef DWORD WSLCSessionId;

// Information about a WSLC session passed to plugin notifications.
struct WSLCSessionInformation
{
WSLCSessionId SessionId;
LPCWSTR DisplayName;
DWORD ApplicationPid;
HANDLE UserToken;
PSID UserSid;
};

// Opaque handle to a WSLC process created via WSLCPluginAPI_CreateProcess.
// Must be released with WSLCPluginAPI_ReleaseProcess.
typedef void* WSLCProcessHandle;

typedef enum _WSLCProcessFd
{
WSLCProcessFdStdin = 0,
WSLCProcessFdStdout = 1,
WSLCProcessFdStderr = 2
} WSLCProcessFd;

// Create plan9 mount between Windows & Linux
typedef HRESULT (*WSLPluginAPI_MountFolder)(WSLSessionId Session, LPCWSTR WindowsPath, LPCWSTR LinuxPath, BOOL ReadOnly, LPCWSTR Name);

// Execute a program in the root namespace.
// On success, 'Socket' is connected to stdin & stdout (stderr goes to dmesg) // 'Arguments' is expected to be NULL terminated
typedef HRESULT (*WSLPluginAPI_ExecuteBinary)(WSLSessionId Session, LPCSTR Path, LPCSTR* Arguments, SOCKET* Socket);

//
// WSLC plugin hooks.
//

// Called when a WSLC session is created. Returning an error prevents the session creation.
typedef HRESULT (*WSLPluginAPI_OnSessionCreated)(const struct WSLCSessionInformation* Session);

// Called when a WSLC session is about to stop. Errors are ignored.
typedef HRESULT (*WSLPluginAPI_OnSessionStopping)(const struct WSLCSessionInformation* Session);

// Called when a container starts. Returning an error prevents the container creation.
// 'InspectContainer' is a JSON document that follows the wslc_schema::InspectContainer format.
typedef HRESULT (*WSLPluginAPI_ContainerStarted)(const struct WSLCSessionInformation* Session, LPCSTR InspectContainer);

// Called when a container is about to stop. 'ContainerId' is the container identifier. Errors are ignored.
typedef HRESULT (*WSLPluginAPI_ContainerStopping)(const struct WSLCSessionInformation* Session, LPCSTR ContainerId);

// Called when an image is created (either pulled, or imported). Errors are ignored.
// 'InspectImage' is a JSON document that follows the wslc_schema::InspectImage format.
// N.B. This callback is currently only invoked when images are pulled or imported. Images created via load or build are not reported.
typedef HRESULT (*WSLPluginAPI_ImageCreated)(const struct WSLCSessionInformation* Session, LPCSTR InspectImage);

// Called when an image is deleted. 'ImageId' is the deleted image identifier. Errors are ignored.
typedef HRESULT (*WSLPluginAPI_ImageDeleted)(const struct WSLCSessionInformation* Session, LPCSTR ImageId);

//
// WSLC plugin API calls.
//

// Mount a Windows folder into the WSLC session VM. The mount path is returned via 'Mountpoint'.
// 'Mountpoint' must point to a buffer of at least WSLC_MOUNTPOINT_LENGTH chars, including the null terminator.
typedef HRESULT (*WSLCPluginAPI_MountFolder)(WSLCSessionId Session, LPCWSTR WindowsPath, BOOL ReadOnly, LPCWSTR Name, LPSTR Mountpoint);

// Unmount a folder previously mounted via WSLCPluginAPI_MountFolder.
typedef HRESULT (*WSLCPluginAPI_UnmountFolder)(WSLCSessionId Session, LPCSTR Mountpoint);

// Create a process in the WSLC session's root namespace.
// 'Arguments' and 'Env' are NULL-terminated arrays. 'Env' may be NULL.
// 'Errno' is optional and receives the errno value if the process creation fails.
// On success, 'Process' receives an opaque handle that must be released with WSLCPluginAPI_ReleaseProcess.
typedef HRESULT (*WSLCPluginAPI_CreateProcess)(
WSLCSessionId Session, LPCSTR Executable, LPCSTR* Arguments, LPCSTR* Env, WSLCProcessHandle* Process, int* Errno);

// Get a stdio handle from a WSLC process. The caller takes ownership and must close it with CloseHandle().
typedef HRESULT (*WSLCPluginAPI_ProcessGetFd)(WSLCProcessHandle Process, WSLCProcessFd Fd, HANDLE* Handle);
Comment thread
OneBlue marked this conversation as resolved.

// Get the exit event for a WSLC process. Signaled when the process exits.
// The caller takes ownership and must close it with CloseHandle().
typedef HRESULT (*WSLCPluginAPI_ProcessGetExitEvent)(WSLCProcessHandle Process, HANDLE* ExitEvent);

// Get the exit code of a WSLC process. The process must have exited.
typedef HRESULT (*WSLCPluginAPI_ProcessGetExitCode)(WSLCProcessHandle Process, int* ExitCode);

// Release a WSLC process handle. All outstanding handles obtained via
// WSLCPluginAPI_ProcessGetFd/GetExitEvent must be closed before calling this.
typedef void (*WSLCPluginAPI_ReleaseProcess)(WSLCProcessHandle Process);

// Execute a program in a user distribution
// On success, 'Socket' is connected to stdin & stdout (stderr goes to dmesg) // 'Arguments' is expected to be NULL terminated
typedef HRESULT (*WSLPluginAPI_ExecuteBinaryInDistribution)(WSLSessionId Session, const GUID* Distribution, LPCSTR Path, LPCSTR* Arguments, SOCKET* Socket);
Expand Down Expand Up @@ -132,6 +216,14 @@ struct WSLPluginHooksV1
WSLPluginAPI_OnDistributionStopping OnDistributionStopping;
WSLPluginAPI_OnDistributionRegistered OnDistributionRegistered; // Introduced in 2.1.2
WSLPluginAPI_OnDistributionRegistered OnDistributionUnregistered; // Introduced in 2.1.2

// WSLC hooks. Plugins compiled against older headers leave these zero-initialized.
WSLPluginAPI_OnSessionCreated OnSessionCreated;
WSLPluginAPI_OnSessionStopping OnSessionStopping;
WSLPluginAPI_ContainerStarted ContainerStarted;
WSLPluginAPI_ContainerStopping ContainerStopping;
WSLPluginAPI_ImageCreated ImageCreated;
WSLPluginAPI_ImageDeleted ImageDeleted;
};

struct WSLPluginAPIV1
Expand All @@ -141,10 +233,19 @@ struct WSLPluginAPIV1
WSLPluginAPI_ExecuteBinary ExecuteBinary;
WSLPluginAPI_PluginError PluginError;
WSLPluginAPI_ExecuteBinaryInDistribution ExecuteBinaryInDistribution; // Introduced in 2.1.2

// WSLC API calls.
WSLCPluginAPI_MountFolder WSLCMountFolder; // Introduced in 2.9.0
WSLCPluginAPI_UnmountFolder WSLCUnmountFolder; // Introduced in 2.9.0
WSLCPluginAPI_CreateProcess WSLCCreateProcess; // Introduced in 2.9.0
WSLCPluginAPI_ProcessGetFd WSLCProcessGetFd; // Introduced in 2.9.0
WSLCPluginAPI_ProcessGetExitEvent WSLCProcessGetExitEvent; // Introduced in 2.9.0
WSLCPluginAPI_ProcessGetExitCode WSLCProcessGetExitCode; // Introduced in 2.9.0
WSLCPluginAPI_ReleaseProcess WSLCReleaseProcess; // Introduced in 2.9.0
};

typedef HRESULT (*WSLPluginAPI_EntryPointV1)(const struct WSLPluginAPIV1* Api, struct WSLPluginHooksV1* Hooks);

#ifdef __cplusplus
}
#endif
#endif
4 changes: 3 additions & 1 deletion src/windows/service/exe/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ set(SOURCES
HcsVirtualMachine.cpp
WSLCSessionManager.cpp
WSLCSessionManagerFactory.cpp
WSLCPluginNotifier.cpp
main.rc
${CMAKE_CURRENT_BINARY_DIR}/../mc/${TARGET_PLATFORM}/${CMAKE_BUILD_TYPE}/wsleventschema.rc
application.manifest)
Expand Down Expand Up @@ -53,7 +54,8 @@ set(HEADERS
WslCoreVm.h
HcsVirtualMachine.h
WSLCSessionManager.h
WSLCSessionManagerFactory.h)
WSLCSessionManagerFactory.h
WSLCPluginNotifier.h)

add_executable(wslservice ${SOURCES} ${HEADERS})
add_dependencies(wslservice wslserviceidl wslservicemc)
Expand Down
13 changes: 2 additions & 11 deletions src/windows/service/exe/LxssUserSessionFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ srwlock g_sessionLock;
std::optional<std::vector<std::shared_ptr<LxssUserSessionImpl>>> g_sessions =
std::make_optional<std::vector<std::shared_ptr<LxssUserSessionImpl>>>();

std::optional<wsl::windows::service::PluginManager> g_pluginManager;
extern wsl::windows::service::PluginManager g_pluginManager;

extern unique_event g_networkingReady;
extern bool g_lxcoreInitialized;
Expand All @@ -53,9 +53,6 @@ void ClearSessionsAndBlockNewInstancesLockHeld(std::optional<std::vector<std::sh

sessions.reset();
}

// Unload plugins
g_pluginManager.reset();
}

void ClearSessionsAndBlockNewInstances()
Expand Down Expand Up @@ -84,12 +81,6 @@ void SetSessionPolicy(_In_ bool enabled)
{
g_sessions = std::make_optional<std::vector<std::shared_ptr<LxssUserSessionImpl>>>();
}

if (!g_pluginManager.has_value())
{
g_pluginManager.emplace();
g_pluginManager->LoadPlugins();
}
}
else
{
Expand Down Expand Up @@ -236,7 +227,7 @@ std::weak_ptr<LxssUserSessionImpl> CreateInstanceForCurrentUser()

if (!userSession)
{
userSession.reset(new LxssUserSessionImpl(tokenInfo->User.Sid, sessionId, *g_pluginManager));
userSession.reset(new LxssUserSessionImpl(tokenInfo->User.Sid, sessionId, g_pluginManager));
g_sessions->emplace_back(userSession);
}
}
Expand Down
Loading