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);
}