mirror of
https://github.com/winfsp/winfsp.git
synced 2025-04-24 01:13:04 -05:00
460 lines
12 KiB
C
460 lines
12 KiB
C
/**
|
|
* @file launcher.c
|
|
*
|
|
* @copyright 2015-2016 Bill Zissimopoulos
|
|
*/
|
|
/*
|
|
* This file is part of WinFsp.
|
|
*
|
|
* You can redistribute it and/or modify it under the terms of the
|
|
* GNU Affero General Public License version 3 as published by the
|
|
* Free Software Foundation.
|
|
*
|
|
* Licensees holding a valid commercial license may use this file in
|
|
* accordance with the commercial license agreement provided with the
|
|
* software.
|
|
*/
|
|
|
|
#include <launcher/launcher.h>
|
|
|
|
#define PROGNAME "WinFsp-Launcher"
|
|
#define REGKEY "SYSTEM\\CurrentControlSet\\Services\\" PROGNAME "\\Services"
|
|
|
|
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 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 HANDLE SvcThread, SvcEvent;
|
|
static HANDLE SvcPipe = INVALID_HANDLE_VALUE;
|
|
static OVERLAPPED SvcOverlapped;
|
|
|
|
static DWORD WINAPI SvcPipeServer(PVOID Context);
|
|
|
|
static NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv)
|
|
{
|
|
InitializeCriticalSection(&SvcInstanceLock);
|
|
|
|
SvcEvent = CreateEventW(0, TRUE, FALSE, 0);
|
|
if (0 == SvcEvent)
|
|
goto fail;
|
|
|
|
SvcOverlapped.hEvent = CreateEventW(0, TRUE, FALSE, 0);
|
|
if (0 == SvcOverlapped.hEvent)
|
|
goto fail;
|
|
|
|
SvcPipe = CreateNamedPipeW(L"" PIPE_NAME,
|
|
PIPE_ACCESS_DUPLEX |
|
|
FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED,
|
|
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS,
|
|
1, PIPE_SRVBUF_SIZE, PIPE_CLIBUF_SIZE, 0, 0);
|
|
if (INVALID_HANDLE_VALUE == SvcPipe)
|
|
goto fail;
|
|
|
|
SvcThread = CreateThread(0, 0, SvcPipeServer, Service, 0, 0);
|
|
if (0 == SvcThread)
|
|
goto fail;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
fail:
|
|
DWORD LastError = GetLastError();
|
|
|
|
if (0 != SvcThread)
|
|
CloseHandle(SvcThread);
|
|
|
|
if (INVALID_HANDLE_VALUE != SvcPipe)
|
|
CloseHandle(SvcPipe);
|
|
|
|
if (0 != SvcOverlapped.hEvent)
|
|
CloseHandle(SvcOverlapped.hEvent);
|
|
|
|
if (0 != SvcEvent)
|
|
CloseHandle(SvcEvent);
|
|
|
|
DeleteCriticalSection(&SvcInstanceLock);
|
|
|
|
return FspNtStatusFromWin32(LastError);
|
|
}
|
|
|
|
static NTSTATUS SvcStop(FSP_SERVICE *Service)
|
|
{
|
|
SetEvent(SvcEvent);
|
|
FspServiceRequestTime(Service, 4500); /* just under 5 sec */
|
|
WaitForSingleObject(SvcThread, 4500);
|
|
|
|
if (0 != SvcThread)
|
|
CloseHandle(SvcThread);
|
|
|
|
if (INVALID_HANDLE_VALUE != SvcPipe)
|
|
CloseHandle(SvcPipe);
|
|
|
|
if (0 != SvcOverlapped.hEvent)
|
|
CloseHandle(SvcOverlapped.hEvent);
|
|
|
|
if (0 != SvcEvent)
|
|
CloseHandle(SvcEvent);
|
|
|
|
DeleteCriticalSection(&SvcInstanceLock);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static inline DWORD SvcPipeWaitResult(BOOL Success, HANDLE StopEvent,
|
|
HANDLE Handle, OVERLAPPED *Overlapped, PDWORD PBytesTransferred)
|
|
{
|
|
HANDLE WaitObjects[2];
|
|
DWORD WaitResult;
|
|
|
|
if (!Success && ERROR_IO_PENDING != GetLastError())
|
|
return GetLastError();
|
|
|
|
WaitObjects[0] = StopEvent;
|
|
WaitObjects[1] = Overlapped->hEvent;
|
|
WaitResult = WaitForMultipleObjects(2, WaitObjects, FALSE, INFINITE);
|
|
if (WAIT_OBJECT_0 == WaitResult)
|
|
return -1; /* special: stop thread */
|
|
else if (WAIT_OBJECT_0 + 1 == WaitResult)
|
|
{
|
|
if (!GetOverlappedResult(Handle, Overlapped, PBytesTransferred, TRUE))
|
|
return GetLastError();
|
|
return 0;
|
|
}
|
|
else
|
|
return GetLastError();
|
|
}
|
|
|
|
static DWORD WINAPI SvcPipeServer(PVOID Context)
|
|
{
|
|
FSP_SERVICE *Service = Context;
|
|
PWSTR PipeBuf = 0;
|
|
DWORD LastError, BytesTransferred;
|
|
|
|
PipeBuf = MemAlloc(PIPE_SRVBUF_SIZE);
|
|
if (0 == PipeBuf)
|
|
{
|
|
FspServiceSetExitCode(Service, ERROR_NO_SYSTEM_RESOURCES);
|
|
goto exit;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
LastError = SvcPipeWaitResult(
|
|
ConnectNamedPipe(SvcPipe, &SvcOverlapped),
|
|
SvcEvent, SvcPipe, &SvcOverlapped, &BytesTransferred);
|
|
if (-1 == LastError)
|
|
break;
|
|
else if (ERROR_PIPE_CONNECTED != LastError && ERROR_NO_DATA != LastError)
|
|
{
|
|
FspServiceLog(EVENTLOG_WARNING_TYPE,
|
|
L"Error in service main loop (ConnectNamedPipe = %ld). Continuing...", LastError);
|
|
continue;
|
|
}
|
|
|
|
LastError = SvcPipeWaitResult(
|
|
ReadFile(SvcPipe, PipeBuf, PIPE_SRVBUF_SIZE, &BytesTransferred, &SvcOverlapped),
|
|
SvcEvent, SvcPipe, &SvcOverlapped, &BytesTransferred);
|
|
if (-1 == LastError)
|
|
break;
|
|
else if (0 != LastError)
|
|
{
|
|
DisconnectNamedPipe(SvcPipe);
|
|
FspServiceLog(EVENTLOG_WARNING_TYPE,
|
|
L"Error in service main loop (ReadFile = %ld). Continuing...", LastError);
|
|
continue;
|
|
}
|
|
|
|
/* handle PipeBuf */
|
|
|
|
LastError = SvcPipeWaitResult(
|
|
WriteFile(SvcPipe, PipeBuf, PIPE_SRVBUF_SIZE, &BytesTransferred, &SvcOverlapped),
|
|
SvcEvent, SvcPipe, &SvcOverlapped, &BytesTransferred);
|
|
if (-1 == LastError)
|
|
break;
|
|
else if (0 != LastError)
|
|
{
|
|
DisconnectNamedPipe(SvcPipe);
|
|
FspServiceLog(EVENTLOG_WARNING_TYPE,
|
|
L"Error in service main loop (WriteFile = %ld). Continuing...", LastError);
|
|
continue;
|
|
}
|
|
|
|
DisconnectNamedPipe(SvcPipe);
|
|
}
|
|
|
|
exit:
|
|
MemFree(PipeBuf);
|
|
|
|
FspServiceStop(Service);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int wmain(int argc, wchar_t **argv)
|
|
{
|
|
ProcessHeap = GetProcessHeap();
|
|
if (0 == ProcessHeap)
|
|
return GetLastError();
|
|
return FspServiceRun(L"" PROGNAME, SvcStart, SvcStop, 0);
|
|
}
|
|
|
|
int wmainCRTStartup(void)
|
|
{
|
|
return wmain(0, 0);
|
|
}
|