Refactor driver custom actions

- also fixes VBoxUSBMon logging
This commit is contained in:
Frans van Dorsselaer committed 2024-02-11 01:06:27 +01:00
1 parent 594f9b5544
commit 078dc95eb3
34 files changed
+287 -439

No files matched your search

-6
View File
@@ -22,12 +22,6 @@
*.cs text eol=crlf
*.props text
*.vcxproj text
*.filters text
*.cpp text
*.h text
*.def text
*.wixproj text
*.wxs text
*.xslt text
-6
View File
@@ -59,12 +59,6 @@ jobs:
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v2
- name: Build (CustomActions)
run: msbuild -p:Configuration=Release CustomActions
- name: Build (Installer)
run: dotnet build --configuration Release Installer
-9
View File
@@ -1,9 +0,0 @@
# SPDX-FileCopyrightText: 2024 Frans van Dorsselaer
#
# SPDX-License-Identifier: GPL-3.0-only
set noparent
linelength=160
# incompatible with pre-compiled headers
filter=-build/include_subdir
-9
View File
@@ -1,9 +0,0 @@
; SPDX-FileCopyrightText: 2024 Frans van Dorsselaer
;
; SPDX-License-Identifier: GPL-3.0-only
LIBRARY
EXPORTS
InstallDrivers
UninstallDrivers
-97
View File
@@ -1,97 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: 2024 Frans van Dorsselaer
SPDX-License-Identifier: GPL-3.0-only
-->
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{cbc206bd-0e45-4514-b148-9035c7459268}</ProjectGuid>
<RootNamespace>CustomActions</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<GenerateManifest>false</GenerateManifest>
<OutDir>bin\$(Platform)\$(Configuration)\</OutDir>
<IntDir>obj\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;CUSTOMACTIONS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<LanguageStandard>stdcpp20</LanguageStandard>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<Optimization>MinSpace</Optimization>
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
<OmitFramePointers>true</OmitFramePointers>
<DebugInformationFormat>None</DebugInformationFormat>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<EnableUAC>false</EnableUAC>
<ModuleDefinitionFile>CustomActions.def</ModuleDefinitionFile>
<SetChecksum>true</SetChecksum>
<MinimumRequiredVersion>10.0</MinimumRequiredVersion>
<ImportLibrary />
<IncrementalLinkDatabaseFile />
<ManifestFile />
<GenerateDebugInformation>false</GenerateDebugInformation>
<ProgramDatabaseFile />
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
<ProfileGuidedDatabase />
<LinkTimeCodeGenerationObjectFile />
<TypeLibraryResourceID />
<AdditionalOptions>/NOIMPLIB /NOEXP %(AdditionalOptions)</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="stdafx.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="actions.cpp" />
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="CustomActions.def" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -1,39 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: 2024 Frans van Dorsselaer
SPDX-License-Identifier: GPL-3.0-only
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="stdafx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="actions.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="CustomActions.def">
<Filter>Source Files</Filter>
</None>
</ItemGroup>
</Project>
-90
View File
@@ -1,90 +0,0 @@
// SPDX-FileCopyrightText: 2024 Frans van Dorsselaer
//
// SPDX-License-Identifier: GPL-3.0-only
#include "stdafx.h"
using std::wstring;
static void log(MSIHANDLE hInstall, wstring fmt, ...) {
auto hRecord = MsiCreateRecord(0);
if (!hRecord) {
return;
}
va_list ap;
va_start(ap, fmt);
va_list ap_copy;
va_copy(ap_copy, ap);
size_t len = _vscwprintf(fmt.c_str(), ap_copy);
va_end(ap_copy);
wstring message;
message.resize(len);
vswprintf_s(message.data(), len + 1, fmt.c_str(), ap);
va_end(ap);
MsiRecordSetString(hRecord, 0, (L"CustomActions: " + message).c_str());
MsiProcessMessage(hInstall, INSTALLMESSAGE_INFO, hRecord);
MsiCloseHandle(hRecord);
}
static wstring get_property(MSIHANDLE hInstall, const wstring& name) {
DWORD valueSize = 0;
if (MsiGetProperty(hInstall, name.c_str(), (LPWSTR)L"", &valueSize) != ERROR_MORE_DATA) {
return wstring();
}
++valueSize; // add NUL
auto value = std::make_unique<WCHAR[]>(valueSize);
if (MsiGetProperty(hInstall, name.c_str(), value.get(), &valueSize) != ERROR_SUCCESS) {
return wstring();
}
return wstring(value.get(), valueSize);
}
// This action must run deferred, between InstallFiles and InstallFinalize.
UINT __stdcall InstallDrivers(MSIHANDLE hInstall) {
auto data = get_property(hInstall, L"CustomActionData");
{
// See: https://learn.microsoft.com/en-us/windows-hardware/drivers/install/preinstalling-driver-packages
log(hInstall, L"Installing VBoxUSB");
if (!SetupCopyOEMInf((data + L"Drivers\\VBoxUSB.inf").c_str(), NULL, SPOST_PATH, 0, NULL, 0, NULL, NULL)) {
log(hInstall, L"ERROR installing VBoxUSB: 0x%08x", GetLastError());
return ERROR_INSTALL_FAILURE;
}
}
{
log(hInstall, L"Installing VBoxUSBMon");
SC_HANDLE manager = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
if (!manager) {
log(hInstall, L"ERROR OpenSCManager: 0x%08x", GetLastError());
return ERROR_INSTALL_FAILURE;
}
SC_HANDLE service = CreateService(manager, L"VBoxUSBMon", L"VirtualBox USB Monitor Service", GENERIC_ALL, SERVICE_KERNEL_DRIVER,
SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, (data + L"Drivers\\VBoxUSBMon.sys").c_str(), NULL, NULL, NULL, NULL, NULL);
if (!service) {
log(hInstall, L"ERROR CreateService: 0x%08x", GetLastError());
CloseServiceHandle(manager);
return ERROR_INSTALL_FAILURE;
}
CloseServiceHandle(service);
CloseServiceHandle(manager);
}
return ERROR_SUCCESS;
}
// This action must run deferred, between InstallFiles and InstallFinalize.
UINT __stdcall UninstallDrivers(MSIHANDLE hInstall) {
auto data = get_property(hInstall, L"CustomActionData");
{
BOOL need_reboot = FALSE;
log(hInstall, L"Uninstalling VBoxUSB");
if (!DiUninstallDriver(NULL, (data + L"Drivers\\VBoxUSB.inf").c_str(), 0, &need_reboot)) {
log(hInstall, L"ERROR uninstalling VBoxUSB: 0x%08x", GetLastError());
// continue
}
// ignore need_reboot
}
return ERROR_SUCCESS;
}
-16
View File
@@ -1,16 +0,0 @@
// SPDX-FileCopyrightText: 2024 Frans van Dorsselaer
//
// SPDX-License-Identifier: GPL-3.0-only
#include "stdafx.h"
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
-5
View File
@@ -1,5 +0,0 @@
// SPDX-FileCopyrightText: 2024 Frans van Dorsselaer
//
// SPDX-License-Identifier: GPL-3.0-only
#include "stdafx.h"
-25
View File
@@ -1,25 +0,0 @@
// SPDX-FileCopyrightText: 2024 Frans van Dorsselaer
//
// SPDX-License-Identifier: GPL-3.0-only
#pragma once
#include <cstdarg>
#include <memory>
#include <string>
// Target Windows 10 or newer (including Windows Server 2019) only
#define WINVER 0x0A00
#define _WIN32_WINNT 0x0A00
#define WIN32_LEAN_AND_MEAN
// Windows Header Files
#include <Windows.h>
#include <Msi.h>
#include <MsiQuery.h>
#pragma comment(lib, "msi.lib")
#include <Newdev.h>
#pragma comment(lib, "newdev.lib")
#include <Setupapi.h>
#pragma comment(lib, "setupapi.lib")
+12 -1
View File
@@ -4,7 +4,10 @@ SPDX-FileCopyrightText: 2020 Frans van Dorsselaer
SPDX-License-Identifier: GPL-3.0-only
-->
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Wix
xmlns="http://wixtoolset.org/schemas/v4/wxs"
xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util"
>
<Fragment>
<DirectoryRef Id="APPLICATIONFOLDER">
<Directory Id="Drivers" Name="Drivers" FileSource="$(var.DriversDir)">
@@ -22,6 +25,14 @@ SPDX-License-Identifier: GPL-3.0-only
</Component>
<Component Id="VBoxUSBMon.sys" Bitness="always64">
<File Id="VBoxUSBMon.sys" Name="VBoxUSBMon.sys" />
<util:EventSource
Name="VBoxUSBMon"
Log="System"
EventMessageFile="%SystemRoot%\System32\IoLogMsg.dll"
SupportsInformationals="true"
SupportsWarnings="true"
SupportsErrors="true"
/>
</Component>
</Directory>
</DirectoryRef>
+4 -6
View File
@@ -90,19 +90,17 @@ SPDX-License-Identifier: GPL-3.0-only
</Component>
</DirectoryRef>
<Binary Id="CustomActions" SourceFile="../CustomActions/bin/x64/Release/CustomActions.dll" />
<!-- This will restore the original Windows drivers for devices that were forced bound. -->
<SetProperty Id="UnbindAll" Value="&quot;[#usbipd.exe]&quot; unbind --all" Sequence="execute" Before="UnbindAll" Condition="(?usbipd.exe=3) AND ($usbipd.exe=2)" />
<CustomAction Id="UnbindAll" BinaryRef="Wix4UtilCA_X64" DllEntry="WixQuietExec" Return="ignore" Impersonate="no" Execute="deferred" />
<!-- This will uninstall the drivers. -->
<SetProperty Id="UninstallDrivers" Value="[APPLICATIONFOLDER]" Sequence="execute" Before="UninstallDrivers" Condition="(?usbipd.exe=3) AND ($usbipd.exe=2)" />
<CustomAction Id="UninstallDrivers" BinaryRef="CustomActions" DllEntry="UninstallDrivers" Impersonate="no" Execute="deferred" />
<SetProperty Id="UninstallDrivers" Value="&quot;[#usbipd.exe]&quot; uninstall" Sequence="execute" Before="UninstallDrivers" Condition="(?usbipd.exe=3) AND ($usbipd.exe=2)" />
<CustomAction Id="UninstallDrivers" BinaryRef="Wix4UtilCA_X64" DllEntry="WixQuietExec" Return="ignore" Impersonate="no" Execute="deferred" />
<!-- This will install the drivers. -->
<SetProperty Id="InstallDrivers" Value="[APPLICATIONFOLDER]" Sequence="execute" Before="InstallDrivers" Condition="$usbipd.exe=3" />
<CustomAction Id="InstallDrivers" BinaryRef="CustomActions" DllEntry="InstallDrivers" Impersonate="no" Execute="deferred" />
<SetProperty Id="InstallDrivers" Value="&quot;[#usbipd.exe]&quot; install" Sequence="execute" Before="InstallDrivers" Condition="$usbipd.exe=3" />
<CustomAction Id="InstallDrivers" BinaryRef="Wix4UtilCA_X64" DllEntry="WixQuietExec" Return="check" Impersonate="no" Execute="deferred" />
<!--
This will *try to* start the service. However, unlike ServiceControl, it will not fail if it can't.
-2
View File
@@ -4,5 +4,3 @@
FILE_ALLOCATED_RANGE_BUFFER
FSCTL_QUERY_ALLOCATED_RANGES
DEVPKEY_NAME
+2 -2
View File
@@ -10,12 +10,12 @@ abstract class ParseTestBase
{
internal static Mock<ICommandHandlers> CreateMock() => new(MockBehavior.Strict);
internal static void Test(Program.ExitCode expect, params string[] args)
internal static void Test(ExitCode expect, params string[] args)
{
Test(expect, CreateMock(), args);
}
internal static void Test(Program.ExitCode expect, Mock<ICommandHandlers> mock, params string[] args)
internal static void Test(ExitCode expect, Mock<ICommandHandlers> mock, params string[] args)
{
Assert.AreEqual(MockBehavior.Strict, mock.Behavior);
var exitCode = Program.Run(new TestConsole(), mock.Object, args);
-2
View File
@@ -7,8 +7,6 @@ using Usbipd.Automation;
namespace UnitTests;
using ExitCode = Program.ExitCode;
[TestClass]
sealed class Parse_attach_Tests
: ParseTestBase
-2
View File
@@ -7,8 +7,6 @@ using Usbipd.Automation;
namespace UnitTests;
using ExitCode = Program.ExitCode;
[TestClass]
sealed class Parse_bind_Tests
: ParseTestBase
-2
View File
@@ -7,8 +7,6 @@ using Usbipd.Automation;
namespace UnitTests;
using ExitCode = Program.ExitCode;
[TestClass]
sealed class Parse_detach_Tests
: ParseTestBase
-2
View File
@@ -6,8 +6,6 @@ using System.CommandLine;
namespace UnitTests;
using ExitCode = Program.ExitCode;
[TestClass]
sealed class Parse_license_Tests
: ParseTestBase
-2
View File
@@ -6,8 +6,6 @@ using System.CommandLine;
namespace UnitTests;
using ExitCode = Program.ExitCode;
[TestClass]
sealed class Parse_list_Tests
: ParseTestBase
-2
View File
@@ -6,8 +6,6 @@ using System.CommandLine;
namespace UnitTests;
using ExitCode = Program.ExitCode;
[TestClass]
sealed class Parse_server_Tests
: ParseTestBase
-2
View File
@@ -6,8 +6,6 @@ using System.CommandLine;
namespace UnitTests;
using ExitCode = Program.ExitCode;
[TestClass]
sealed class Parse_state_Tests
: ParseTestBase
-2
View File
@@ -7,8 +7,6 @@ using Usbipd.Automation;
namespace UnitTests;
using ExitCode = Program.ExitCode;
[TestClass]
sealed class Parse_unbind_Tests
: ParseTestBase
-2
View File
@@ -4,8 +4,6 @@
namespace UnitTests;
using ExitCode = Program.ExitCode;
[TestClass]
sealed class Parse_usbipd_Tests
: ParseTestBase
-2
View File
@@ -4,8 +4,6 @@
namespace UnitTests;
using ExitCode = Program.ExitCode;
[TestClass]
sealed class Parse_wsl_Tests
: ParseTestBase
-2
View File
@@ -6,8 +6,6 @@ using System.CommandLine;
namespace UnitTests;
using ExitCode = Program.ExitCode;
[TestClass]
sealed class Program_Tests
: ParseTestBase
@@ -8,33 +8,12 @@ using System.ComponentModel;
using System.Diagnostics;
using System.Text.Encodings.Web;
using System.Text.Json;
using Microsoft.Extensions.Hosting.WindowsServices;
using Usbipd.Automation;
using static Usbipd.ConsoleTools;
using ExitCode = Usbipd.Program.ExitCode;
namespace Usbipd;
interface ICommandHandlers
{
public Task<ExitCode> AttachWsl(BusId busId, bool autoAttach, string? distribution, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> AttachWsl(VidPid vidPid, bool autoAttach, string? distribution, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> Bind(BusId busId, bool force, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> Bind(VidPid vidPid, bool force, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> Detach(BusId busId, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> Detach(VidPid vidPid, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> DetachAll(IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> License(IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> List(bool usbids, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> Server(string[] args, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> Unbind(BusId busId, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> Unbind(Guid guid, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> Unbind(VidPid vidPid, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> UnbindAll(IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> State(IConsole console, CancellationToken cancellationToken);
}
sealed class CommandHandlers : ICommandHandlers
sealed partial class CommandHandlers : ICommandHandlers
{
static List<UsbDevice> GetDevicesByHardwareId(VidPid vidPid, bool connectedOnly, IConsole console)
{
@@ -245,72 +224,6 @@ sealed class CommandHandlers : ICommandHandlers
return Task.FromResult(Bind(busId, force, console));
}
async Task<ExitCode> ICommandHandlers.Server(string[] args, IConsole console, CancellationToken cancellationToken)
{
// Pre-conditions that may fail due to user mistakes. Fail gracefully...
if (!CheckWriteAccess(console))
{
return ExitCode.AccessDenied;
}
using var mutex = new Mutex(true, Server.SingletonMutexName, out var createdNew);
if (!createdNew)
{
console.ReportError("Another instance is already running.");
return ExitCode.Failure;
}
// From here on, the server should run without error. Any further errors (exceptions) are probably bugs...
using var host = Host.CreateDefaultBuilder()
.UseWindowsService()
.ConfigureAppConfiguration((context, builder) =>
{
var defaultConfig = new Dictionary<string, string?>();
if (WindowsServiceHelpers.IsWindowsService())
{
// EventLog defaults to Warning, which is OK for .NET components,
// but we want to specifically log Information from our own component.
defaultConfig.Add($"Logging:EventLog:LogLevel:{nameof(Usbipd)}", "Information");
}
else
{
// When not running as a Windows service, do not spam the EventLog.
defaultConfig.Add("Logging:EventLog:LogLevel:Default", "None");
}
// set the above as defaults
builder.AddInMemoryCollection(defaultConfig);
// allow overrides from the environment
builder.AddEnvironmentVariables();
// allow overrides from the command line
builder.AddCommandLine(args);
})
.ConfigureLogging((context, logging) =>
{
if (!EventLog.SourceExists(Program.Product))
{
EventLog.CreateEventSource(Program.Product, "Application");
}
logging.AddEventLog(settings =>
{
settings.SourceName = Program.Product;
});
})
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Server>();
services.AddSingleton<PcapNg>();
services.AddScoped<ClientContext>();
services.AddScoped<ConnectedClient>();
services.AddScoped<AttachedClient>();
})
.Build();
await host.RunAsync(cancellationToken);
return ExitCode.Success;
}
Task<ExitCode> ICommandHandlers.Unbind(BusId busId, IConsole console, CancellationToken cancellationToken)
{
var device = UsbDevice.GetAll().Where(d => d.BusId.HasValue && d.BusId.Value == busId).SingleOrDefault();
+107
View File
@@ -0,0 +1,107 @@
// SPDX-FileCopyrightText: 2024 Frans van Dorsselaer
//
// SPDX-License-Identifier: GPL-3.0-only
using System.CommandLine;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using Windows.Win32;
using Windows.Win32.Devices.DeviceAndDriverInstallation;
using Windows.Win32.Foundation;
using Windows.Win32.Security;
using Windows.Win32.System.Services;
namespace Usbipd;
sealed partial class CommandHandlers : ICommandHandlers
{
sealed class SafeScHandle : SafeHandleZeroOrMinusOneIsInvalid
{
public SafeScHandle(SC_HANDLE handle)
: base(true)
{
SetHandle(handle);
}
public static implicit operator SC_HANDLE(SafeScHandle scHandle) =>
scHandle.IsClosed || scHandle.IsInvalid ? (SC_HANDLE)0 : (SC_HANDLE)scHandle.handle;
protected override bool ReleaseHandle()
{
return PInvoke.CloseServiceHandle((SC_HANDLE)handle);
}
}
Task<ExitCode> ICommandHandlers.Install(IConsole console, CancellationToken cancellationToken)
{
ConsoleTools.ReportInfo(console, "install helper");
var path = Path.GetDirectoryName(Environment.ProcessPath)!;
ConsoleTools.ReportInfo(console, $"path = {path}");
{
ConsoleTools.ReportInfo(console, $"Installing VBoxUSB");
// See: https://learn.microsoft.com/en-us/windows-hardware/drivers/install/preinstalling-driver-packages
unsafe
{
fixed (char* inf = Path.Combine(path, "Drivers", "VBoxUSB.inf"))
{
if (!PInvoke.SetupCopyOEMInf(inf, null, OEM_SOURCE_MEDIA_TYPE.SPOST_PATH, 0, null, 0))
{
console.ReportError($"SetupCopyOEMInf -> {Marshal.GetLastWin32Error()}");
return Task.FromResult(ExitCode.Failure);
}
}
}
}
{
ConsoleTools.ReportInfo(console, $"Installing VBoxUSBMon");
// NOTE: This cannot be done from WiX, since WiX cannot create SERVICE_KERNEL_DRIVER; removal from WiX works though.
using var manager = new SafeScHandle(PInvoke.OpenSCManager(string.Empty, PInvoke.SERVICES_ACTIVE_DATABASE, PInvoke.SC_MANAGER_ALL_ACCESS));
if (manager.IsInvalid)
{
console.ReportError($"OpenSCManager -> {Marshal.GetLastWin32Error()}");
return Task.FromResult(ExitCode.Failure);
}
unsafe
{
using var service = new SafeScHandle(PInvoke.CreateService(manager, "VBoxUSBMon", "VirtualBox USB Monitor Service",
(uint)GENERIC_ACCESS_RIGHTS.GENERIC_ALL, ENUM_SERVICE_TYPE.SERVICE_KERNEL_DRIVER, SERVICE_START_TYPE.SERVICE_DEMAND_START,
SERVICE_ERROR.SERVICE_ERROR_NORMAL, Path.Combine(path, "Drivers", "VBoxUSBMon.sys"), null, null, null, null, null));
if (service.IsInvalid)
{
console.ReportError($"CreateService -> {Marshal.GetLastWin32Error()}");
return Task.FromResult(ExitCode.Failure);
}
}
}
return Task.FromResult(ExitCode.Success);
}
Task<ExitCode> ICommandHandlers.Uninstall(IConsole console, CancellationToken cancellationToken)
{
ConsoleTools.ReportInfo(console, $"uninstall helper");
var path = Path.GetDirectoryName(Environment.ProcessPath)!;
ConsoleTools.ReportInfo(console, $"path = {path}");
var success = true;
{
ConsoleTools.ReportInfo(console, $"Uninstalling VBoxUSB");
unsafe
{
BOOL needReboot;
if (!PInvoke.DiUninstallDriver(HWND.Null, Path.Combine(path, "Drivers", "VBoxUSB.inf"), 0, &needReboot))
{
console.ReportError($"DiUninstallDriver -> {Marshal.GetLastWin32Error()}");
success = false;
// continue
}
// ignore needReboot
}
}
return Task.FromResult(success ? ExitCode.Success : ExitCode.Failure);
}
}
+79
View File
@@ -0,0 +1,79 @@
// SPDX-FileCopyrightText: 2022 Frans van Dorsselaer
//
// SPDX-License-Identifier: GPL-3.0-only
using System.CommandLine;
using System.Diagnostics;
using Microsoft.Extensions.Hosting.WindowsServices;
using static Usbipd.ConsoleTools;
namespace Usbipd;
sealed partial class CommandHandlers : ICommandHandlers
{
async Task<ExitCode> ICommandHandlers.Server(string[] args, IConsole console, CancellationToken cancellationToken)
{
// Pre-conditions that may fail due to user mistakes. Fail gracefully...
if (!CheckWriteAccess(console))
{
return ExitCode.AccessDenied;
}
using var mutex = new Mutex(true, Server.SingletonMutexName, out var createdNew);
if (!createdNew)
{
console.ReportError("Another instance is already running.");
return ExitCode.Failure;
}
// From here on, the server should run without error. Any further errors (exceptions) are probably bugs...
using var host = Host.CreateDefaultBuilder()
.UseWindowsService()
.ConfigureAppConfiguration((context, builder) =>
{
var defaultConfig = new Dictionary<string, string?>();
if (WindowsServiceHelpers.IsWindowsService())
{
// EventLog defaults to Warning, which is OK for .NET components,
// but we want to specifically log Information from our own component.
defaultConfig.Add($"Logging:EventLog:LogLevel:{nameof(Usbipd)}", "Information");
}
else
{
// When not running as a Windows service, do not spam the EventLog.
defaultConfig.Add("Logging:EventLog:LogLevel:Default", "None");
}
// set the above as defaults
builder.AddInMemoryCollection(defaultConfig);
// allow overrides from the environment
builder.AddEnvironmentVariables();
// allow overrides from the command line
builder.AddCommandLine(args);
})
.ConfigureLogging((context, logging) =>
{
if (!EventLog.SourceExists(Program.Product))
{
EventLog.CreateEventSource(Program.Product, "Application");
}
logging.AddEventLog(settings =>
{
settings.SourceName = Program.Product;
});
})
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Server>();
services.AddSingleton<PcapNg>();
services.AddScoped<ClientContext>();
services.AddScoped<ConnectedClient>();
services.AddScoped<AttachedClient>();
})
.Build();
await host.RunAsync(cancellationToken);
return ExitCode.Success;
}
}
+14
View File
@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: 2024 Frans van Dorsselaer
//
// SPDX-License-Identifier: GPL-3.0-only
namespace Usbipd;
public enum ExitCode
{
Success = 0,
Failure = 1,
ParseError = 2,
AccessDenied = 3,
Canceled = 4,
};
+29
View File
@@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: 2024 Frans van Dorsselaer
//
// SPDX-License-Identifier: GPL-3.0-only
using System.CommandLine;
using Usbipd.Automation;
namespace Usbipd;
interface ICommandHandlers
{
public Task<ExitCode> AttachWsl(BusId busId, bool autoAttach, string? distribution, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> AttachWsl(VidPid vidPid, bool autoAttach, string? distribution, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> Bind(BusId busId, bool force, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> Bind(VidPid vidPid, bool force, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> Detach(BusId busId, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> Detach(VidPid vidPid, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> DetachAll(IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> License(IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> List(bool usbids, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> Server(string[] args, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> Unbind(BusId busId, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> Unbind(Guid guid, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> Unbind(VidPid vidPid, IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> UnbindAll(IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> State(IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> Install(IConsole console, CancellationToken cancellationToken);
public Task<ExitCode> Uninstall(IConsole console, CancellationToken cancellationToken);
}
+9
View File
@@ -24,15 +24,20 @@ CM_SETUP_DEVNODE_READY
MAX_PATH
AdjustTokenPrivileges
CloseServiceHandle
CreateFile
CreateService
DeviceIoControl
DiInstallDevice
DiUninstallDriver
GetCurrentProcess
LookupPrivilegeValue
OpenProcessToken
OpenSCManager
RegDeleteKey
RegNotifyChangeKeyValue
SetEvent
SetupCopyOEMInf
SetupDiBuildDriverInfoList
SetupDiCreateDeviceInfoList
SetupDiEnumDriverInfo
@@ -97,8 +102,12 @@ FILE_DEVICE_UNKNOWN
METHOD_BUFFERED
FILE_ACCESS_RIGHTS
GENERIC_ACCESS_RIGHTS
FILE_WRITE_ACCESS
SC_MANAGER_ALL_ACCESS
SERVICES_ACTIVE_DATABASE
TOKEN_ELEVATION_TYPE
+30 -9
View File
@@ -22,15 +22,6 @@ static class Program
public static readonly string Copyright = Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyCopyrightAttribute>()!.Copyright;
public static readonly string ApplicationName = Path.GetFileName(Process.GetCurrentProcess().ProcessName);
public enum ExitCode
{
Success = 0,
Failure = 1,
ParseError = 2,
AccessDenied = 3,
Canceled = 4,
};
static BusId ParseCompatibleBusId(ArgumentResult argumentResult)
{
if (!BusId.TryParse(argumentResult.Tokens[0].Value, out var busId) || busId.IsIncompatibleHub)
@@ -569,6 +560,36 @@ static class Program
});
rootCommand.AddCommand(wslCommand);
}
{
//
// install
//
var installCommand = new Command("install")
{
IsHidden = true,
};
installCommand.SetHandler(async invocationContext =>
{
invocationContext.ExitCode = (int)
await commandHandlers.Install(invocationContext.Console, invocationContext.GetCancellationToken());
});
rootCommand.AddCommand(installCommand);
}
{
//
// uninstall
//
var uninstallCommand = new Command("uninstall")
{
IsHidden = true,
};
uninstallCommand.SetHandler(async invocationContext =>
{
invocationContext.ExitCode = (int)
await commandHandlers.Uninstall(invocationContext.Console, invocationContext.GetCancellationToken());
});
rootCommand.AddCommand(uninstallCommand);
}
// Same as UseDefaults() minus exception handling.
var commandLine = new CommandLineBuilder(rootCommand)
-1
View File
@@ -13,7 +13,6 @@ using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using Usbipd.Automation;
using static Usbipd.Program;
namespace Usbipd;
-6
View File
@@ -25,7 +25,6 @@ Project("{B7DD6F7E-DEF8-4E67-B5B7-07EF123DB6F0}") = "Installer", "Installer\Inst
ProjectSection(ProjectDependencies) = postProject
{3C3ED2C8-54D0-48D4-8BF4-78220D4A9E20} = {3C3ED2C8-54D0-48D4-8BF4-78220D4A9E20}
{5CE75792-6F4F-4B16-83E6-29052C6E0D8D} = {5CE75792-6F4F-4B16-83E6-29052C6E0D8D}
{CBC206BD-0E45-4514-B148-9035C7459268} = {CBC206BD-0E45-4514-B148-9035C7459268}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests", "UnitTests\UnitTests.csproj", "{03DA15B4-7F40-4EF3-814B-9050C7A671B5}"
@@ -36,8 +35,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Usbipd.Automation", "Usbipd
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UsbIds", "UsbIds\UsbIds.csproj", "{FC600539-9D82-4822-B2A0-7157D166A444}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CustomActions", "CustomActions\CustomActions.vcxproj", "{CBC206BD-0E45-4514-B148-9035C7459268}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -66,9 +63,6 @@ Global
{FC600539-9D82-4822-B2A0-7157D166A444}.Debug|x64.Build.0 = Debug|Any CPU
{FC600539-9D82-4822-B2A0-7157D166A444}.Release|x64.ActiveCfg = Release|Any CPU
{FC600539-9D82-4822-B2A0-7157D166A444}.Release|x64.Build.0 = Release|Any CPU
{CBC206BD-0E45-4514-B148-9035C7459268}.Debug|x64.ActiveCfg = Release|x64
{CBC206BD-0E45-4514-B148-9035C7459268}.Debug|x64.Build.0 = Release|x64
{CBC206BD-0E45-4514-B148-9035C7459268}.Release|x64.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE