From 457e151fa536d7e790b7812917ddf0055906bb8a Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Tue, 10 May 2016 16:48:21 -0700 Subject: [PATCH] launcher: SvcInstance implementation --- build/VStudio/launcher/launcher.vcxproj | 3 + .../VStudio/launcher/launcher.vcxproj.filters | 11 + src/launcher/launcher.c | 274 +++++++++++++++++- 3 files changed, 285 insertions(+), 3 deletions(-) diff --git a/build/VStudio/launcher/launcher.vcxproj b/build/VStudio/launcher/launcher.vcxproj index e6a3dd9c..42d22f38 100644 --- a/build/VStudio/launcher/launcher.vcxproj +++ b/build/VStudio/launcher/launcher.vcxproj @@ -175,6 +175,9 @@ {4a7c0b21-9e10-4c81-92de-1493efcf24eb} + + + diff --git a/build/VStudio/launcher/launcher.vcxproj.filters b/build/VStudio/launcher/launcher.vcxproj.filters index dd6a7994..7e2d9316 100644 --- a/build/VStudio/launcher/launcher.vcxproj.filters +++ b/build/VStudio/launcher/launcher.vcxproj.filters @@ -5,10 +5,21 @@ {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + {11e7c0f2-7782-43ee-84fa-9e56efbe39de} + + + {d83ea433-d9f7-494c-90b9-3a8997483cd9} + Source + + + Include\shared + + \ No newline at end of file diff --git a/src/launcher/launcher.c b/src/launcher/launcher.c index 116c2013..8fca5f01 100644 --- a/src/launcher/launcher.c +++ b/src/launcher/launcher.c @@ -16,21 +16,289 @@ */ #include +#include +#include -#define PROGNAME "winfsp-launcher" +#define PROGNAME "WinFsp-Launcher" +#define REGKEY "SYSTEM\\CurrentControlSet\\Services\\" PROGNAME "\\Services" -NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) +HANDLE ProcessHeap; + +typedef struct { + PWSTR ClassName; + PWSTR InstanceName; + PWSTR CommandLine; + DWORD ProcessId; + HANDLE Process; + HANDLE ProcessWait; + LIST_ENTRY ListEntry; + WCHAR Buffer[]; +} SVC_INSTANCE; + +static CRITICAL_SECTION SvcInstanceLock; +static LIST_ENTRY SvcInstanceList = { &SvcInstanceList, &SvcInstanceList }; + +static VOID CALLBACK SvcInstanceTerminated(PVOID Context, BOOLEAN Fired); + +static SVC_INSTANCE *SvcInstanceFromName(PWSTR InstanceName) +{ + SVC_INSTANCE *SvcInstance; + PLIST_ENTRY ListEntry; + + for (ListEntry = SvcInstanceList.Flink; + &SvcInstanceList != ListEntry; + ListEntry = ListEntry->Flink) + { + SvcInstance = CONTAINING_RECORD(ListEntry, SVC_INSTANCE, ListEntry); + + if (0 == lstrcmpW(InstanceName, SvcInstance->InstanceName)) + return SvcInstance; + } + + return 0; +} + +static ULONG SvcInstanceArgumentLength(PWSTR Arg) +{ + ULONG Length; + + Length = 2; /* for beginning and ending quotes */ + for (PWSTR P = Arg; *P; P++) + if (L'"' != *P) + Length++; + + return Length; +} + +static PWSTR SvcInstanceArgumentCopy(PWSTR Dest, PWSTR Arg) +{ + *Dest++ = L'"'; + for (PWSTR P = Arg; *P; P++) + if (L'"' != *P) + *Dest++ = *P; + *Dest++ = L'"'; + + return Dest; +} + +static NTSTATUS SvcInstanceReplaceArguments(PWSTR String, ULONG Argc, PWSTR *Argv, + PWSTR *PNewString) +{ + PWSTR NewString = 0; + ULONG Length; + + *PNewString = 0; + + Length = 0; + for (PWSTR P = String; *P; P++) + { + switch (*P) + { + case L'%': + P++; + if (L'0' <= *P && *P <= '9' && Argc > (ULONG)(*P - L'0')) + Length += SvcInstanceArgumentLength(Argv[*P - L'0']); + break; + default: + Length++; + break; + } + } + + NewString = MemAlloc((Length + 1) * sizeof(WCHAR)); + if (0 == NewString) + return STATUS_INSUFFICIENT_RESOURCES; + + *PNewString = NewString; + for (PWSTR P = String, Q = NewString; *P; P++) + { + switch (*P) + { + case L'%': + P++; + if (L'0' <= *P && *P <= '9' && Argc > (ULONG)(*P - L'0')) + Q = SvcInstanceArgumentCopy(Q, Argv[*P - L'0']); + break; + default: + Q++; + break; + } + } + return STATUS_SUCCESS; } -NTSTATUS SvcStop(FSP_SERVICE *Service) +NTSTATUS SvcInstanceCreate(PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv, + SVC_INSTANCE **PSvcInstance) { + SVC_INSTANCE *SvcInstance = 0; + HKEY RegKey = 0; + DWORD RegResult, RegSize; + DWORD ClassNameSize, InstanceNameSize; + WCHAR Executable[MAX_PATH], CommandLine[512]; + STARTUPINFOW StartupInfo; + PROCESS_INFORMATION ProcessInfo; + NTSTATUS Result; + + *PSvcInstance = 0; + + EnterCriticalSection(&SvcInstanceLock); + + if (0 != SvcInstanceFromName(InstanceName)) + { + Result = STATUS_OBJECT_NAME_COLLISION; + goto exit; + } + + RegResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"" REGKEY, 0, KEY_READ, &RegKey); + if (ERROR_SUCCESS != RegResult) + { + Result = FspNtStatusFromWin32(RegResult); + goto exit; + } + + RegSize = sizeof Executable; + RegResult = RegGetValueW(RegKey, ClassName, L"Executable", RRF_RT_REG_SZ, 0, + &Executable, &RegSize); + if (ERROR_SUCCESS != RegResult) + { + Result = FspNtStatusFromWin32(RegResult); + goto exit; + } + + RegSize = sizeof CommandLine; + RegResult = RegGetValueW(RegKey, ClassName, L"CommandLine", RRF_RT_REG_SZ, 0, + &CommandLine, &RegSize); + if (ERROR_SUCCESS != RegResult) + { + Result = FspNtStatusFromWin32(RegResult); + goto exit; + } + + RegCloseKey(RegKey); + RegKey = 0; + + ClassNameSize = (lstrlenW(ClassName) + 1) * sizeof(WCHAR); + InstanceNameSize = (lstrlenW(InstanceName) + 1) * sizeof(WCHAR); + + SvcInstance = MemAlloc(sizeof *SvcInstance + ClassNameSize + InstanceNameSize); + if (0 == SvcInstance) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + memset(SvcInstance, 0, sizeof *SvcInstance); + memcpy(SvcInstance->Buffer, ClassName, ClassNameSize); + memcpy(SvcInstance->Buffer + ClassNameSize / sizeof(WCHAR), InstanceName, InstanceNameSize); + SvcInstance->ClassName = SvcInstance->Buffer; + SvcInstance->InstanceName = SvcInstance->Buffer + ClassNameSize / sizeof(WCHAR); + + Result = SvcInstanceReplaceArguments(CommandLine, Argc, Argv, &SvcInstance->CommandLine); + if (!NT_SUCCESS(Result)) + goto exit; + + memset(&StartupInfo, 0, sizeof StartupInfo); + StartupInfo.cb = sizeof StartupInfo; + if (!CreateProcessW(0, SvcInstance->CommandLine, 0, 0, FALSE, CREATE_NEW_PROCESS_GROUP, 0, 0, + &StartupInfo, &ProcessInfo)) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + CloseHandle(ProcessInfo.hThread); + SvcInstance->ProcessId = ProcessInfo.dwProcessId; + SvcInstance->Process = ProcessInfo.hProcess; + + if (!RegisterWaitForSingleObject(&SvcInstance->ProcessWait, SvcInstance->Process, + SvcInstanceTerminated, SvcInstance, INFINITE, WT_EXECUTEONLYONCE)) + { + /* we have no way when the new process will terminate so go ahead and close its handle */ + FspServiceLog(EVENTLOG_WARNING_TYPE, + L"RegisterWaitForSingleObject = %ld", GetLastError()); + CloseHandle(SvcInstance->Process); + SvcInstance->Process = 0; + } + + InsertTailList(&SvcInstanceList, &SvcInstance->ListEntry); + + *PSvcInstance = SvcInstance; + + Result = STATUS_SUCCESS; + +exit: + if (!NT_SUCCESS(Result)) + { + MemFree(SvcInstance->CommandLine); + MemFree(SvcInstance); + } + + if (0 != RegKey) + RegCloseKey(RegKey); + + LeaveCriticalSection(&SvcInstanceLock); + + return Result; +} + +VOID SvcInstanceDelete(SVC_INSTANCE *SvcInstance) +{ + EnterCriticalSection(&SvcInstanceLock); + RemoveEntryList(&SvcInstance->ListEntry); + LeaveCriticalSection(&SvcInstanceLock); + + if (0 != SvcInstance->ProcessWait) + UnregisterWaitEx(SvcInstance->ProcessWait, 0); + if (0 != SvcInstance->Process) + CloseHandle(SvcInstance->Process); + MemFree(SvcInstance->CommandLine); + MemFree(SvcInstance); +} + +static VOID CALLBACK SvcInstanceTerminated(PVOID Context, BOOLEAN Fired) +{ + SVC_INSTANCE *SvcInstance = Context; + + SvcInstanceDelete(SvcInstance); +} + +NTSTATUS SvcInstanceStart(PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv) +{ + SVC_INSTANCE *SvcInstance; + + return SvcInstanceCreate(ClassName, InstanceName, Argc, Argv, &SvcInstance); +} + +VOID SvcInstanceStop(PWSTR InstanceName) +{ + SVC_INSTANCE *SvcInstance; + + EnterCriticalSection(&SvcInstanceLock); + SvcInstance = SvcInstanceFromName(InstanceName); + if (0 != SvcInstance) + GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, SvcInstance->ProcessId); + LeaveCriticalSection(&SvcInstanceLock); +} + +static NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) +{ + InitializeCriticalSection(&SvcInstanceLock); + return STATUS_SUCCESS; +} + +static NTSTATUS SvcStop(FSP_SERVICE *Service) +{ + DeleteCriticalSection(&SvcInstanceLock); return STATUS_SUCCESS; } int wmain(int argc, wchar_t **argv) { + ProcessHeap = GetProcessHeap(); + if (0 == ProcessHeap) + return GetLastError(); return FspServiceRun(L"" PROGNAME, SvcStart, SvcStop, 0); }