launcher: SvcInstance implementation

This commit is contained in:
Bill Zissimopoulos 2016-05-10 16:48:21 -07:00
parent dc4109fc22
commit 457e151fa5
3 changed files with 285 additions and 3 deletions

View File

@ -175,6 +175,9 @@
<Project>{4a7c0b21-9e10-4c81-92de-1493efcf24eb}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\src\shared\minimal.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>

View File

@ -5,10 +5,21 @@
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Include">
<UniqueIdentifier>{11e7c0f2-7782-43ee-84fa-9e56efbe39de}</UniqueIdentifier>
</Filter>
<Filter Include="Include\shared">
<UniqueIdentifier>{d83ea433-d9f7-494c-90b9-3a8997483cd9}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\src\launcher\launcher.c">
<Filter>Source</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\src\shared\minimal.h">
<Filter>Include\shared</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -16,21 +16,289 @@
*/
#include <winfsp/winfsp.h>
#include <shared/minimal.h>
#include <sddl.h>
#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);
}