dll: add FSP_SERVICE and EventLog functionality

This commit is contained in:
Bill Zissimopoulos 2016-05-06 11:41:45 -07:00
parent de973fa5ab
commit d7a6f33d26
8 changed files with 554 additions and 0 deletions

View File

@ -25,6 +25,7 @@
<ClInclude Include="..\..\src\dll\library.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\src\dll\eventlog.c" />
<ClCompile Include="..\..\src\dll\np.c" />
<ClCompile Include="..\..\src\dll\security.c" />
<ClCompile Include="..\..\src\dll\debug.c" />
@ -34,6 +35,7 @@
<ClCompile Include="..\..\src\dll\fs.c" />
<ClCompile Include="..\..\src\dll\ntstatus.c" />
<ClCompile Include="..\..\src\dll\path.c" />
<ClCompile Include="..\..\src\dll\service.c" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\src\dll\library.def" />

View File

@ -52,6 +52,12 @@
<ClCompile Include="..\..\src\dll\np.c">
<Filter>Source</Filter>
</ClCompile>
<ClCompile Include="..\..\src\dll\service.c">
<Filter>Source</Filter>
</ClCompile>
<ClCompile Include="..\..\src\dll\eventlog.c">
<Filter>Source</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\..\src\dll\ntstatus.i">

View File

@ -838,10 +838,52 @@ FSP_API VOID FspPathPrefix(PWSTR Path, PWSTR *PPrefix, PWSTR *PRemain, PWSTR Roo
FSP_API VOID FspPathSuffix(PWSTR Path, PWSTR *PRemain, PWSTR *PSuffix, PWSTR Root);
FSP_API VOID FspPathCombine(PWSTR Prefix, PWSTR Suffix);
/*
* Service Framework
*/
typedef struct _FSP_SERVICE FSP_SERVICE;
typedef NTSTATUS FSP_SERVICE_START(FSP_SERVICE *, ULONG, PWSTR *);
typedef NTSTATUS FSP_SERVICE_STOP(FSP_SERVICE *);
typedef NTSTATUS FSP_SERVICE_CONTROL(FSP_SERVICE *, ULONG, ULONG, PVOID);
#pragma warning(push)
#pragma warning(disable:4200) /* zero-sized array in struct/union */
typedef struct _FSP_SERVICE
{
UINT16 Version;
PVOID UserContext;
FSP_SERVICE_START *OnStart;
FSP_SERVICE_STOP *OnStop;
FSP_SERVICE_CONTROL *OnControl;
ULONG AcceptControl;
ULONG ExitCode;
SERVICE_STATUS_HANDLE StatusHandle;
CRITICAL_SECTION ServiceStatusGuard;
SERVICE_STATUS ServiceStatus;
BOOLEAN AllowInteractive;
HANDLE InteractiveEvent;
WCHAR ServiceName[];
} FSP_SERVICE;
#pragma warning(pop)
FSP_API NTSTATUS FspServiceCreate(PWSTR ServiceName,
FSP_SERVICE_START *OnStart,
FSP_SERVICE_STOP *OnStop,
FSP_SERVICE_CONTROL *OnControl,
FSP_SERVICE **PService);
FSP_API VOID FspServiceDelete(FSP_SERVICE *Service);
FSP_API VOID FspServiceAllowInteractive(FSP_SERVICE *Service);
FSP_API VOID FspServiceAcceptControl(FSP_SERVICE *Service, ULONG Control);
FSP_API VOID FspServiceRequestTime(FSP_SERVICE *Service, ULONG Time);
FSP_API VOID FspServiceSetExitCode(FSP_SERVICE *Service, ULONG ExitCode);
FSP_API NTSTATUS FspServiceRun(FSP_SERVICE *Service);
FSP_API VOID FspServiceStop(FSP_SERVICE *Service);
/*
* Utility
*/
FSP_API NTSTATUS FspNtStatusFromWin32(DWORD Error);
FSP_API DWORD FspWin32FromNtStatus(NTSTATUS Status);
FSP_API VOID FspEventLog(ULONG Type, PWSTR Format, ...);
FSP_API VOID FspEventLogV(ULONG Type, PWSTR Format, va_list ap);
FSP_API VOID FspDebugLog(const char *format, ...);
FSP_API VOID FspDebugLogSD(const char *format, PSECURITY_DESCRIPTOR SecurityDescriptor);
FSP_API VOID FspDebugLogFT(const char *format, PFILETIME FileTime);

56
src/dll/eventlog.c Normal file
View File

@ -0,0 +1,56 @@
/**
* @file dll/eventlog.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 <dll/library.h>
#include <stdarg.h>
static HANDLE FspEventLogHandle;
static INIT_ONCE FspEventLogInitOnce = INIT_ONCE_STATIC_INIT;
static BOOL WINAPI FspEventLogRegisterEventSource(
PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context);
FSP_API VOID FspEventLog(ULONG Type, PWSTR Format, ...)
{
va_list ap;
va_start(ap, Format);
FspEventLogV(Type, Format, ap);
va_end(ap);
}
FSP_API VOID FspEventLogV(ULONG Type, PWSTR Format, va_list ap)
{
InitOnceExecuteOnce(&FspEventLogInitOnce, FspEventLogRegisterEventSource, 0, 0);
if (0 == FspEventLogHandle)
return;
WCHAR Buf[1024], *Strings[1];
/* wvsprintfW is only safe with a 1024 WCHAR buffer */
wvsprintfW(Buf, Format, ap);
Buf[(sizeof Buf / sizeof Buf[0]) - 1] = L'\0';
Strings[0] = Buf;
ReportEventW(FspEventLogHandle, (WORD)Type, 0, 1, 0, 1, 0, Strings, 0);
}
static BOOL WINAPI FspEventLogRegisterEventSource(
PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
{
FspEventLogHandle = RegisterEventSourceW(0, L"" LIBRARY_NAME);
return TRUE;
}

View File

@ -29,6 +29,7 @@ BOOL WINAPI DllMain(HINSTANCE Instance, DWORD Reason, PVOID Reserved)
ProcessHeap = GetProcessHeap();
if (0 == ProcessHeap)
return FALSE;
FspNtStatusInitialize();
FspFileSystemInitialize();
break;
case DLL_PROCESS_DETACH:

View File

@ -103,6 +103,8 @@ BOOLEAN RemoveEntryList(PLIST_ENTRY Entry)
return Flink == Blink;
}
VOID FspNtStatusInitialize(VOID);
VOID FspFileSystemInitialize(VOID);
VOID FspFileSystemFinalize(VOID);

View File

@ -17,6 +17,25 @@
#include <dll/library.h>
static ULONG (WINAPI *FspRtlNtStatusToDosError)(NTSTATUS Status);
VOID FspNtStatusInitialize(VOID)
{
/*
* This function is called during DLL_PROCESS_ATTACH. We must therefore keep initialization
* tasks to a minimum.
*
* GetModuleHandle/GetProcAddress is allowed (because they are kernel32 API's)! See:
* https://msdn.microsoft.com/en-us/library/windows/desktop/dn633971(v=vs.85).aspx
*/
HANDLE Handle;
Handle = GetModuleHandleW(L"ntdll.dll");
if (0 != Handle)
FspRtlNtStatusToDosError = (PVOID)GetProcAddress(Handle, "RtlNtStatusToDosError");
}
FSP_API NTSTATUS FspNtStatusFromWin32(DWORD Error)
{
switch (Error)
@ -26,3 +45,11 @@ FSP_API NTSTATUS FspNtStatusFromWin32(DWORD Error)
return STATUS_ACCESS_DENIED;
}
}
FSP_API DWORD FspWin32FromNtStatus(NTSTATUS Status)
{
if (0 == FspRtlNtStatusToDosError)
return ERROR_MR_MID_NOT_FOUND;
return FspRtlNtStatusToDosError(Status);
}

418
src/dll/service.c Normal file
View File

@ -0,0 +1,418 @@
/**
* @file dll/service.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 <dll/library.h>
// !!!: NOTIMPLEMENTED
#define FspEventLog(Type, Message, ...)
enum
{
SetStatus_ServiceType = 0x0001,
SetStatus_CurrentState = 0x0002,
SetStatus_ControlsAccepted = 0x0004,
SetStatus_Win32ExitCode = 0x0008,
SetStatus_ServiceSpecificExitCode = 0x0010,
SetStatus_CheckPoint = 0x0020,
SetStatus_WaitHint = 0x0040,
GetStatus_ServiceType = 0x0100,
GetStatus_CurrentState = 0x0200,
GetStatus_ControlsAccepted = 0x0400,
GetStatus_Win32ExitCode = 0x0800,
GetStatus_ServiceSpecificExitCode = 0x1000,
GetStatus_CheckPoint = 0x2000,
GetStatus_WaitHint = 0x4000,
};
static SERVICE_TABLE_ENTRYW *FspServiceTable;
static VOID FspServiceSetStatus(FSP_SERVICE *Service, ULONG Flags, SERVICE_STATUS *ServiceStatus);
static VOID WINAPI FspServiceEntry(DWORD Argc, PWSTR *Argv);
static VOID FspServiceMain(FSP_SERVICE *Service, DWORD Argc, PWSTR *Argv);
static DWORD WINAPI FspServiceCtrlHandler(
DWORD Control, DWORD EventType, PVOID EventData, PVOID Context);
static DWORD WINAPI FspServiceStopRoutine(PVOID Context);
static DWORD WINAPI FspServiceInteractiveThread(PVOID Context);
static BOOL WINAPI FspServiceConsoleCtrlHandler(DWORD CtrlType);
static inline FSP_SERVICE *FspServiceFromTable(VOID)
{
FSP_SERVICE *Service = 0;
if (0 != FspServiceTable)
{
Service = (PVOID)((PUINT8)FspServiceTable[0].lpServiceName -
FIELD_OFFSET(FSP_SERVICE, ServiceName));
FspServiceTable = 0;
}
return Service;
}
FSP_API NTSTATUS FspServiceCreate(PWSTR ServiceName,
FSP_SERVICE_START *OnStart,
FSP_SERVICE_STOP *OnStop,
FSP_SERVICE_CONTROL *OnControl,
FSP_SERVICE **PService)
{
FSP_SERVICE *Service;
DWORD Size;
*PService = 0;
Size = (lstrlenW(ServiceName) + 1) * sizeof(WCHAR);
Service = MemAlloc(sizeof *Service + Size);
if (0 == Service)
return STATUS_INSUFFICIENT_RESOURCES;
memset(Service, 0, sizeof *Service);
memcpy(Service->ServiceName, ServiceName, Size);
Service->OnStart = OnStart;
Service->OnStop = OnStop;
Service->OnControl = OnControl;
Service->AcceptControl = OnStop ? SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN : 0;
InitializeCriticalSection(&Service->ServiceStatusGuard);
Service->ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
Service->ServiceStatus.dwCurrentState = SERVICE_STOPPED;
Service->ServiceStatus.dwControlsAccepted = 0;
Service->ServiceStatus.dwWin32ExitCode = NO_ERROR;
Service->ServiceStatus.dwServiceSpecificExitCode = 0;
Service->ServiceStatus.dwCheckPoint = 0;
Service->ServiceStatus.dwWaitHint = 0;
*PService = Service;
return STATUS_SUCCESS;
}
FSP_API VOID FspServiceDelete(FSP_SERVICE *Service)
{
if (0 != Service->InteractiveEvent)
CloseHandle(Service->InteractiveEvent);
DeleteCriticalSection(&Service->ServiceStatusGuard);
MemFree(Service);
}
static VOID FspServiceSetStatus(FSP_SERVICE *Service, ULONG Flags, SERVICE_STATUS *ServiceStatus)
{
#define XCHG(FIELD)\
if (Flags & SetStatus_##FIELD)\
{\
DWORD Temp = ServiceStatus->dw##FIELD;\
if (Flags & GetStatus_##FIELD)\
ServiceStatus->dw##FIELD = Service->ServiceStatus.dw##FIELD;\
Service->ServiceStatus.dw##FIELD = Temp;\
}
EnterCriticalSection(&Service->ServiceStatusGuard);
//XCHG(ServiceType);
XCHG(CurrentState);
XCHG(ControlsAccepted);
XCHG(Win32ExitCode);
//XCHG(ServiceSpecificExitCode);
if (Flags & SetStatus_CheckPoint)
{
DWORD Temp = ServiceStatus->dwCheckPoint;
if (Flags & GetStatus_CheckPoint)
ServiceStatus->dwCheckPoint = Service->ServiceStatus.dwCheckPoint;
/* treat CheckPoint specially! */
if (0 == Temp)
Service->ServiceStatus.dwCheckPoint = 0;
else
Service->ServiceStatus.dwCheckPoint += Temp;
}
XCHG(WaitHint);
if (0 != Service->StatusHandle)
{
if (!SetServiceStatus(Service->StatusHandle, &Service->ServiceStatus))
FspEventLog(EVENTLOG_ERROR_TYPE,
L"" __FUNCTION__ ": error = %ld", GetLastError());
}
else if (0 != Service->InteractiveEvent &&
SERVICE_STOPPED == Service->ServiceStatus.dwCurrentState)
{
SetEvent(Service->InteractiveEvent);
}
LeaveCriticalSection(&Service->ServiceStatusGuard);
#undef XCHG
}
FSP_API VOID FspServiceAllowInteractive(FSP_SERVICE *Service)
{
Service->AllowInteractive = TRUE;
}
FSP_API VOID FspServiceAcceptControl(FSP_SERVICE *Service, ULONG Control)
{
Service->AcceptControl = Control & ~SERVICE_ACCEPT_PAUSE_CONTINUE;
}
FSP_API VOID FspServiceRequestTime(FSP_SERVICE *Service, ULONG Time)
{
SERVICE_STATUS ServiceStatus;
ServiceStatus.dwCheckPoint = +1;
ServiceStatus.dwWaitHint = Time;
FspServiceSetStatus(Service,
SetStatus_CheckPoint | SetStatus_WaitHint, &ServiceStatus);
}
FSP_API VOID FspServiceSetExitCode(FSP_SERVICE *Service, ULONG ExitCode)
{
Service->ExitCode = ExitCode;
}
FSP_API NTSTATUS FspServiceRun(FSP_SERVICE *Service)
{
SERVICE_TABLE_ENTRYW ServiceTable[2];
ServiceTable[0].lpServiceName = Service->ServiceName;
ServiceTable[0].lpServiceProc = FspServiceEntry;
ServiceTable[1].lpServiceName = 0;
ServiceTable[1].lpServiceProc = 0;
FspServiceTable = ServiceTable;
if (!StartServiceCtrlDispatcherW(ServiceTable))
{
HANDLE Thread;
DWORD WaitResult;
DWORD LastError;
LastError = GetLastError();
if (!Service->AllowInteractive || ERROR_FAILED_SERVICE_CONTROLLER_CONNECT != LastError)
return FspNtStatusFromWin32(LastError);
/* enter INTERACTIVE (DEBUG) mode! */
Service->InteractiveEvent = CreateEventW(0, TRUE, FALSE, 0);
if (0 == Service->InteractiveEvent)
return FspNtStatusFromWin32(GetLastError());
Thread = CreateThread(0, 0, FspServiceInteractiveThread, Service, 0, 0);
if (0 == Thread)
return FspNtStatusFromWin32(GetLastError());
WaitResult = WaitForSingleObject(Thread, INFINITE);
CloseHandle(Thread);
if (WAIT_OBJECT_0 != WaitResult)
return FspNtStatusFromWin32(GetLastError());
if (!SetConsoleCtrlHandler(FspServiceConsoleCtrlHandler, TRUE))
return FspNtStatusFromWin32(GetLastError());
WaitResult = WaitForSingleObject(Service->InteractiveEvent, INFINITE);
if (WAIT_OBJECT_0 != WaitResult)
return FspNtStatusFromWin32(GetLastError());
}
return STATUS_SUCCESS;
}
FSP_API VOID FspServiceStop(FSP_SERVICE *Service)
{
SERVICE_STATUS ServiceStatus;
NTSTATUS Result;
ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
FspServiceSetStatus(Service,
SetStatus_CurrentState | SetStatus_CheckPoint | SetStatus_WaitHint |
GetStatus_CurrentState | GetStatus_CheckPoint | GetStatus_WaitHint,
&ServiceStatus);
Result = STATUS_SUCCESS;
if (0 != Service->OnStop)
Result = Service->OnStop(Service);
if (NT_SUCCESS(Result))
{
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode = Service->ExitCode;
FspServiceSetStatus(Service,
SetStatus_CurrentState | SetStatus_Win32ExitCode, &ServiceStatus);
}
else
{
/* revert the service status */
FspServiceSetStatus(Service,
SetStatus_CurrentState | SetStatus_CheckPoint | SetStatus_WaitHint,
&ServiceStatus);
}
}
static VOID WINAPI FspServiceEntry(DWORD Argc, PWSTR *Argv)
{
FSP_SERVICE *Service;
Service = FspServiceFromTable();
if (0 == Service)
{
FspEventLog(EVENTLOG_ERROR_TYPE,
L"" __FUNCTION__ ": internal error: FspServiceFromTable = 0");
return;
}
Service->StatusHandle = RegisterServiceCtrlHandlerExW(Service->ServiceName,
FspServiceCtrlHandler, Service);
if (0 == Service->StatusHandle)
{
FspEventLog(EVENTLOG_ERROR_TYPE,
L"" __FUNCTION__ ": RegisterServiceCtrlHandlerW = %ld", GetLastError());
return;
}
FspServiceMain(Service, Argc, Argv);
}
static VOID FspServiceMain(FSP_SERVICE *Service, DWORD Argc, PWSTR *Argv)
{
SERVICE_STATUS ServiceStatus;
NTSTATUS Result;
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
ServiceStatus.dwControlsAccepted = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
FspServiceSetStatus(Service,
SetStatus_CurrentState | SetStatus_ControlsAccepted | SetStatus_CheckPoint | SetStatus_WaitHint,
&ServiceStatus);
Result = STATUS_SUCCESS;
if (0 != Service->OnStart)
Result = Service->OnStart(Service, Argc, Argv);
if (NT_SUCCESS(Result))
{
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
ServiceStatus.dwControlsAccepted = Service->AcceptControl;
FspServiceSetStatus(Service,
SetStatus_CurrentState | SetStatus_ControlsAccepted, &ServiceStatus);
}
else
{
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode = FspWin32FromNtStatus(Result);
FspServiceSetStatus(Service,
SetStatus_CurrentState | SetStatus_Win32ExitCode, &ServiceStatus);
}
}
static DWORD WINAPI FspServiceCtrlHandler(
DWORD Control, DWORD EventType, PVOID EventData, PVOID Context)
{
FSP_SERVICE *Service = Context;
NTSTATUS Result;
switch (Control)
{
case SERVICE_CONTROL_SHUTDOWN:
Result = STATUS_NOT_IMPLEMENTED;
if (0 != Service->OnControl)
Result = Service->OnControl(Service, Control, EventType, EventData);
if (STATUS_NOT_IMPLEMENTED != Result)
return FspWin32FromNtStatus(Result);
/* fall through */
case SERVICE_CONTROL_STOP:
if (!QueueUserWorkItem(FspServiceStopRoutine, Service, WT_EXECUTEDEFAULT))
FspServiceStopRoutine(Service);
return NO_ERROR;
case SERVICE_CONTROL_PAUSE:
case SERVICE_CONTROL_CONTINUE:
return ERROR_CALL_NOT_IMPLEMENTED;
case SERVICE_CONTROL_INTERROGATE:
return NO_ERROR;
default:
Result = STATUS_SUCCESS;
if (0 != Service->OnControl)
Result = Service->OnControl(Service, Control, EventType, EventData);
return FspWin32FromNtStatus(Result);
}
}
static DWORD WINAPI FspServiceStopRoutine(PVOID Context)
{
FSP_SERVICE *Service = Context;
FspServiceStop(Service);
return 0;
}
static DWORD WINAPI FspServiceInteractiveThread(PVOID Context)
{
FSP_SERVICE *Service;
PWSTR Args[2] = { 0, 0 };
PWSTR *Argv;
DWORD Argc;
Service = FspServiceFromTable();
if (0 == Service)
{
FspEventLog(EVENTLOG_ERROR_TYPE,
L"" __FUNCTION__ ": internal error: FspServiceFromTable = 0");
return FALSE;
}
Argv = CommandLineToArgvW(GetCommandLineW(), &Argc);
if (0 == Argv)
{
Argv = Args;
Argc = 1;
}
Argv[0] = Service->ServiceName;
FspServiceMain(Service, Argc, Argv);
if (Args != Argv)
LocalFree(Argv);
return 0;
}
static BOOL WINAPI FspServiceConsoleCtrlHandler(DWORD CtrlType)
{
FSP_SERVICE *Service;
Service = FspServiceFromTable();
if (0 == Service)
{
FspEventLog(EVENTLOG_ERROR_TYPE,
L"" __FUNCTION__ ": internal error: FspServiceFromTable = 0");
return FALSE;
}
switch (CtrlType)
{
case CTRL_SHUTDOWN_EVENT:
FspServiceCtrlHandler(SERVICE_CONTROL_SHUTDOWN, 0, 0, Service);
return TRUE;
default:
FspServiceCtrlHandler(SERVICE_CONTROL_STOP, 0, 0, Service);
return TRUE;
}
}