mirror of
https://github.com/winfsp/winfsp.git
synced 2025-04-22 16:33:02 -05:00
launcher: error recovery
Service instances can now be restarted when the registry setting `Recovery=1` is set.
This commit is contained in:
parent
94d8c0452f
commit
ca12b5a19d
@ -455,6 +455,8 @@ static VOID CALLBACK KillProcessWait(PVOID Context, BOOLEAN Timeout)
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
LONG RefCount;
|
LONG RefCount;
|
||||||
|
LIST_ENTRY ListEntry;
|
||||||
|
HANDLE ClientToken;
|
||||||
PWSTR ClassName;
|
PWSTR ClassName;
|
||||||
PWSTR InstanceName;
|
PWSTR InstanceName;
|
||||||
PWSTR CommandLine;
|
PWSTR CommandLine;
|
||||||
@ -463,15 +465,23 @@ typedef struct
|
|||||||
HANDLE Process;
|
HANDLE Process;
|
||||||
HANDLE ProcessWait;
|
HANDLE ProcessWait;
|
||||||
HANDLE StdioHandles[2];
|
HANDLE StdioHandles[2];
|
||||||
LIST_ENTRY ListEntry;
|
DWORD Recovery;
|
||||||
|
ULONG Argc;
|
||||||
|
PWSTR *Argv;
|
||||||
|
BOOLEAN HasSecret;
|
||||||
|
BOOLEAN Started, Stopped;
|
||||||
WCHAR Buffer[];
|
WCHAR Buffer[];
|
||||||
} SVC_INSTANCE;
|
} SVC_INSTANCE;
|
||||||
|
|
||||||
|
static HANDLE SvcJob;
|
||||||
static CRITICAL_SECTION SvcInstanceLock;
|
static CRITICAL_SECTION SvcInstanceLock;
|
||||||
static HANDLE SvcInstanceEvent;
|
static HANDLE SvcInstanceEvent;
|
||||||
static LIST_ENTRY SvcInstanceList = { &SvcInstanceList, &SvcInstanceList };
|
static LIST_ENTRY SvcInstanceList = { &SvcInstanceList, &SvcInstanceList };
|
||||||
|
|
||||||
static VOID CALLBACK SvcInstanceTerminated(PVOID Context, BOOLEAN Timeout);
|
static VOID CALLBACK SvcInstanceTerminated(PVOID Context, BOOLEAN Timeout);
|
||||||
|
NTSTATUS SvcInstanceStart(HANDLE ClientToken,
|
||||||
|
PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv, HANDLE Job,
|
||||||
|
BOOLEAN HasSecret);
|
||||||
|
|
||||||
static SVC_INSTANCE *SvcInstanceLookup(PWSTR ClassName, PWSTR InstanceName)
|
static SVC_INSTANCE *SvcInstanceLookup(PWSTR ClassName, PWSTR InstanceName)
|
||||||
{
|
{
|
||||||
@ -879,13 +889,14 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken,
|
|||||||
{
|
{
|
||||||
SVC_INSTANCE *SvcInstance = 0;
|
SVC_INSTANCE *SvcInstance = 0;
|
||||||
PWSTR ClientUserName = 0;
|
PWSTR ClientUserName = 0;
|
||||||
|
DWORD ClientTokenInformation = -1;
|
||||||
HKEY RegKey = 0;
|
HKEY RegKey = 0;
|
||||||
DWORD RegResult, RegSize;
|
DWORD RegResult, RegSize;
|
||||||
DWORD ClassNameSize, InstanceNameSize;
|
DWORD ClassNameSize, InstanceNameSize;
|
||||||
WCHAR Executable[MAX_PATH], CommandLineBuf[512], WorkDirectory[MAX_PATH],
|
WCHAR Executable[MAX_PATH], CommandLineBuf[512], WorkDirectory[MAX_PATH],
|
||||||
SecurityBuf[512], RunAsBuf[64];
|
SecurityBuf[512], RunAsBuf[64];
|
||||||
PWSTR CommandLine, Security;
|
PWSTR CommandLine, Security;
|
||||||
DWORD JobControl, Credentials;
|
DWORD Credentials, JobControl, Recovery;
|
||||||
PSECURITY_DESCRIPTOR SecurityDescriptor = 0, NewSecurityDescriptor;
|
PSECURITY_DESCRIPTOR SecurityDescriptor = 0, NewSecurityDescriptor;
|
||||||
PWSTR Argv[10];
|
PWSTR Argv[10];
|
||||||
PROCESS_INFORMATION ProcessInfo;
|
PROCESS_INFORMATION ProcessInfo;
|
||||||
@ -912,8 +923,9 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken,
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 != ClientToken)
|
Result = GetTokenUserName(ClientToken, &ClientUserName);
|
||||||
GetTokenUserName(ClientToken, &ClientUserName);
|
if (!NT_SUCCESS(Result))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
RegResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"" FSP_LAUNCH_REGKEY,
|
RegResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"" FSP_LAUNCH_REGKEY,
|
||||||
0, FSP_LAUNCH_REGKEY_WOW64 | KEY_READ, &RegKey);
|
0, FSP_LAUNCH_REGKEY_WOW64 | KEY_READ, &RegKey);
|
||||||
@ -1003,6 +1015,16 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken,
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RegSize = sizeof Recovery;
|
||||||
|
Recovery = 0;
|
||||||
|
RegResult = RegGetValueW(RegKey, ClassName, L"Recovery", RRF_RT_REG_DWORD, 0,
|
||||||
|
&Recovery, &RegSize);
|
||||||
|
if (ERROR_SUCCESS != RegResult && ERROR_FILE_NOT_FOUND != RegResult)
|
||||||
|
{
|
||||||
|
Result = FspNtStatusFromWin32(RegResult);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
RegCloseKey(RegKey);
|
RegCloseKey(RegKey);
|
||||||
RegKey = 0;
|
RegKey = 0;
|
||||||
|
|
||||||
@ -1042,8 +1064,20 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken,
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ClientToken: protect from CloseHandle; do not inherit */
|
||||||
|
if (!GetHandleInformation(ClientToken, &ClientTokenInformation) ||
|
||||||
|
!SetHandleInformation(ClientToken,
|
||||||
|
HANDLE_FLAG_PROTECT_FROM_CLOSE | HANDLE_FLAG_INHERIT,
|
||||||
|
HANDLE_FLAG_PROTECT_FROM_CLOSE | 0))
|
||||||
|
{
|
||||||
|
ClientTokenInformation = -1;
|
||||||
|
Result = FspNtStatusFromWin32(RegResult);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
memset(SvcInstance, 0, sizeof *SvcInstance);
|
memset(SvcInstance, 0, sizeof *SvcInstance);
|
||||||
SvcInstance->RefCount = 2;
|
SvcInstance->RefCount = 2;
|
||||||
|
SvcInstance->ClientToken = ClientToken;
|
||||||
memcpy(SvcInstance->Buffer, ClassName, ClassNameSize);
|
memcpy(SvcInstance->Buffer, ClassName, ClassNameSize);
|
||||||
memcpy(SvcInstance->Buffer + ClassNameSize / sizeof(WCHAR), InstanceName, InstanceNameSize);
|
memcpy(SvcInstance->Buffer + ClassNameSize / sizeof(WCHAR), InstanceName, InstanceNameSize);
|
||||||
SvcInstance->ClassName = SvcInstance->Buffer;
|
SvcInstance->ClassName = SvcInstance->Buffer;
|
||||||
@ -1051,6 +1085,7 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken,
|
|||||||
SvcInstance->SecurityDescriptor = SecurityDescriptor;
|
SvcInstance->SecurityDescriptor = SecurityDescriptor;
|
||||||
SvcInstance->StdioHandles[0] = INVALID_HANDLE_VALUE;
|
SvcInstance->StdioHandles[0] = INVALID_HANDLE_VALUE;
|
||||||
SvcInstance->StdioHandles[1] = INVALID_HANDLE_VALUE;
|
SvcInstance->StdioHandles[1] = INVALID_HANDLE_VALUE;
|
||||||
|
SvcInstance->Recovery = Recovery;
|
||||||
|
|
||||||
Result = SvcInstanceReplaceArguments(CommandLine,
|
Result = SvcInstanceReplaceArguments(CommandLine,
|
||||||
Argc, Argv,
|
Argc, Argv,
|
||||||
@ -1105,6 +1140,11 @@ exit:
|
|||||||
if (0 != ProcessInfo.hThread)
|
if (0 != ProcessInfo.hThread)
|
||||||
CloseHandle(ProcessInfo.hThread);
|
CloseHandle(ProcessInfo.hThread);
|
||||||
|
|
||||||
|
if (-1 != ClientTokenInformation)
|
||||||
|
SetHandleInformation(ClientToken,
|
||||||
|
HANDLE_FLAG_PROTECT_FROM_CLOSE | HANDLE_FLAG_INHERIT,
|
||||||
|
ClientTokenInformation);
|
||||||
|
|
||||||
if (0 != SvcInstance)
|
if (0 != SvcInstance)
|
||||||
{
|
{
|
||||||
if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[0])
|
if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[0])
|
||||||
@ -1149,6 +1189,19 @@ static VOID SvcInstanceRelease(SVC_INSTANCE *SvcInstance)
|
|||||||
SetEvent(SvcInstanceEvent);
|
SetEvent(SvcInstanceEvent);
|
||||||
LeaveCriticalSection(&SvcInstanceLock);
|
LeaveCriticalSection(&SvcInstanceLock);
|
||||||
|
|
||||||
|
SetHandleInformation(SvcInstance->ClientToken,
|
||||||
|
HANDLE_FLAG_PROTECT_FROM_CLOSE,
|
||||||
|
0);
|
||||||
|
|
||||||
|
if (1 == SvcInstance->Recovery && SvcInstance->Started && !SvcInstance->Stopped)
|
||||||
|
SvcInstanceStart(SvcInstance->ClientToken,
|
||||||
|
SvcInstance->ClassName, SvcInstance->InstanceName,
|
||||||
|
SvcInstance->Argc, SvcInstance->Argv,
|
||||||
|
SvcJob,
|
||||||
|
SvcInstance->HasSecret);
|
||||||
|
|
||||||
|
MemFree(SvcInstance->Argv);
|
||||||
|
|
||||||
if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[0])
|
if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[0])
|
||||||
CloseHandle(SvcInstance->StdioHandles[0]);
|
CloseHandle(SvcInstance->StdioHandles[0]);
|
||||||
if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[1])
|
if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[1])
|
||||||
@ -1162,6 +1215,18 @@ static VOID SvcInstanceRelease(SVC_INSTANCE *SvcInstance)
|
|||||||
LocalFree(SvcInstance->SecurityDescriptor);
|
LocalFree(SvcInstance->SecurityDescriptor);
|
||||||
|
|
||||||
MemFree(SvcInstance->CommandLine);
|
MemFree(SvcInstance->CommandLine);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE:
|
||||||
|
* New instances store the ClientToken and protect it from CloseHandle.
|
||||||
|
* This results in an unhandled exception when running under a debugger.
|
||||||
|
* Such exceptions can be ignored.
|
||||||
|
*
|
||||||
|
* See MSDN CloseHandle for details:
|
||||||
|
* https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle
|
||||||
|
*/
|
||||||
|
CloseHandle(SvcInstance->ClientToken);
|
||||||
|
|
||||||
MemFree(SvcInstance);
|
MemFree(SvcInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1175,7 +1240,7 @@ static VOID CALLBACK SvcInstanceTerminated(PVOID Context, BOOLEAN Timeout)
|
|||||||
SvcInstanceRelease(SvcInstance);
|
SvcInstanceRelease(SvcInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
NTSTATUS SvcInstanceStart(HANDLE ClientToken,
|
static NTSTATUS SvcInstanceStartWithArgvCopy(HANDLE ClientToken,
|
||||||
PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv, HANDLE Job,
|
PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv, HANDLE Job,
|
||||||
BOOLEAN HasSecret)
|
BOOLEAN HasSecret)
|
||||||
{
|
{
|
||||||
@ -1265,6 +1330,14 @@ exit:
|
|||||||
SvcInstance->StdioHandles[1] = INVALID_HANDLE_VALUE;
|
SvcInstance->StdioHandles[1] = INVALID_HANDLE_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (NT_SUCCESS(Result))
|
||||||
|
{
|
||||||
|
SvcInstance->Argc = Argc;
|
||||||
|
SvcInstance->Argv = Argv;
|
||||||
|
SvcInstance->HasSecret = HasSecret;
|
||||||
|
SvcInstance->Started = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
SvcInstanceRelease(SvcInstance);
|
SvcInstanceRelease(SvcInstance);
|
||||||
|
|
||||||
if (STATUS_TIMEOUT == Result)
|
if (STATUS_TIMEOUT == Result)
|
||||||
@ -1274,6 +1347,41 @@ exit:
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NTSTATUS SvcInstanceStart(HANDLE ClientToken,
|
||||||
|
PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv, HANDLE Job,
|
||||||
|
BOOLEAN HasSecret)
|
||||||
|
{
|
||||||
|
DWORD ArgvSize;
|
||||||
|
PWSTR *ArgvCopy;
|
||||||
|
NTSTATUS Result;
|
||||||
|
|
||||||
|
ArgvSize = 0;
|
||||||
|
for (ULONG I = 0; Argc > I; I++)
|
||||||
|
ArgvSize += (lstrlenW(Argv[I]) + 1) * sizeof(WCHAR);
|
||||||
|
|
||||||
|
ArgvCopy = MemAlloc(Argc * sizeof(PWSTR) + ArgvSize);
|
||||||
|
if (0 == ArgvCopy)
|
||||||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||||||
|
|
||||||
|
ArgvSize = 0;
|
||||||
|
for (ULONG I = 0; Argc > I; I++)
|
||||||
|
{
|
||||||
|
ULONG L = (lstrlenW(Argv[I]) + 1) * sizeof(WCHAR);
|
||||||
|
ArgvCopy[I] = (PWSTR)((PUINT8)ArgvCopy + Argc * sizeof(PWSTR) + ArgvSize);
|
||||||
|
memcpy(ArgvCopy[I], Argv[I], L);
|
||||||
|
ArgvSize += L;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result = SvcInstanceStartWithArgvCopy(ClientToken,
|
||||||
|
ClassName, InstanceName, Argc, ArgvCopy, Job,
|
||||||
|
HasSecret);
|
||||||
|
|
||||||
|
if (!NT_SUCCESS(Result))
|
||||||
|
MemFree(ArgvCopy);
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
NTSTATUS SvcInstanceStop(HANDLE ClientToken,
|
NTSTATUS SvcInstanceStop(HANDLE ClientToken,
|
||||||
PWSTR ClassName, PWSTR InstanceName)
|
PWSTR ClassName, PWSTR InstanceName)
|
||||||
{
|
{
|
||||||
@ -1293,6 +1401,7 @@ NTSTATUS SvcInstanceStop(HANDLE ClientToken,
|
|||||||
if (!NT_SUCCESS(Result))
|
if (!NT_SUCCESS(Result))
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
||||||
|
SvcInstance->Stopped = TRUE;
|
||||||
KillProcess(SvcInstance->ProcessId, SvcInstance->Process, LAUNCHER_KILL_TIMEOUT);
|
KillProcess(SvcInstance->ProcessId, SvcInstance->Process, LAUNCHER_KILL_TIMEOUT);
|
||||||
|
|
||||||
Result = STATUS_SUCCESS;
|
Result = STATUS_SUCCESS;
|
||||||
@ -1394,6 +1503,7 @@ NTSTATUS SvcInstanceStopAndWaitAll(VOID)
|
|||||||
{
|
{
|
||||||
SvcInstance = CONTAINING_RECORD(ListEntry, SVC_INSTANCE, ListEntry);
|
SvcInstance = CONTAINING_RECORD(ListEntry, SVC_INSTANCE, ListEntry);
|
||||||
|
|
||||||
|
SvcInstance->Stopped = TRUE;
|
||||||
KillProcess(SvcInstance->ProcessId, SvcInstance->Process, LAUNCHER_KILL_TIMEOUT);
|
KillProcess(SvcInstance->ProcessId, SvcInstance->Process, LAUNCHER_KILL_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1451,7 +1561,7 @@ NTSTATUS SvcDefineDosDevice(HANDLE ClientToken,
|
|||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HANDLE SvcJob, SvcThread, SvcEvent;
|
static HANDLE SvcThread, SvcEvent;
|
||||||
static DWORD SvcThreadId;
|
static DWORD SvcThreadId;
|
||||||
static HANDLE SvcPipe = INVALID_HANDLE_VALUE;
|
static HANDLE SvcPipe = INVALID_HANDLE_VALUE;
|
||||||
static OVERLAPPED SvcOverlapped;
|
static OVERLAPPED SvcOverlapped;
|
||||||
@ -1698,6 +1808,15 @@ static DWORD WINAPI SvcPipeServer(PVOID Context)
|
|||||||
|
|
||||||
SvcPipeTransact(ClientToken, PipeBuf, &BytesTransferred);
|
SvcPipeTransact(ClientToken, PipeBuf, &BytesTransferred);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE:
|
||||||
|
* New instances store the ClientToken and protect it from CloseHandle.
|
||||||
|
* This results in an unhandled exception when running under a debugger.
|
||||||
|
* Such exceptions can be ignored.
|
||||||
|
*
|
||||||
|
* See MSDN CloseHandle for details:
|
||||||
|
* https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle
|
||||||
|
*/
|
||||||
CloseHandle(ClientToken);
|
CloseHandle(ClientToken);
|
||||||
|
|
||||||
LastError = SvcPipeWaitResult(
|
LastError = SvcPipeWaitResult(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user