mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
761 lines
17 KiB
761 lines
17 KiB
/*++
|
|
|
|
Copyright (c) 1992, 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
timesvc.c
|
|
|
|
Abstract:
|
|
|
|
Resource DLL to control and monitor the Cluster Time Service
|
|
|
|
Author:
|
|
|
|
Rod Gamache, 21-July-1996.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#define UNICODE 1
|
|
#include "clusres.h"
|
|
#include "tsapi.h"
|
|
|
|
|
|
#define TIMESVC_PRINT printf
|
|
|
|
#define NOTIFY_KEY_RESOURCE_STATE 1
|
|
|
|
#define TimeSvcLogEvent ClusResLogEvent
|
|
|
|
#define SEMAPHORE_NAME L"Cluster$TimeSvcSemaphore"
|
|
|
|
#define MAX_RESOURCE_NAME_LENGTH 256
|
|
//
|
|
// Global Data
|
|
//
|
|
|
|
typedef struct _SERVICE_INFORMATION {
|
|
RESOURCE_HANDLE ResourceHandle;
|
|
HCLUSTER ClusterHandle;
|
|
HCHANGE ClusterChange;
|
|
HRESOURCE hResource;
|
|
} SERVICE_INFORMATION, *PSERVICE_INFORMATION;
|
|
|
|
|
|
// Global event handle
|
|
|
|
HANDLE TimeSvcSemaphore = NULL;
|
|
|
|
// The resource handle for the active instance.
|
|
|
|
PSERVICE_INFORMATION TimeSvcInfo = NULL;
|
|
|
|
// Local Computer Name
|
|
|
|
WCHAR TimeSvcLocalComputerName[MAX_COMPUTERNAME_LENGTH + 1];
|
|
|
|
// Notify thread handle
|
|
|
|
CLUS_WORKER TimeSvcNotifyHandle = {NULL, TRUE};
|
|
|
|
extern CLRES_FUNCTION_TABLE TimeSvcFunctionTable;
|
|
|
|
|
|
//
|
|
// Forward routines
|
|
//
|
|
|
|
BOOL
|
|
VerifyService(
|
|
IN RESID Resource,
|
|
IN BOOL IsAliveFlag
|
|
);
|
|
|
|
|
|
|
|
BOOLEAN
|
|
WINAPI
|
|
TimeSvcDllEntryPoint(
|
|
IN HINSTANCE DllHandle,
|
|
IN DWORD Reason,
|
|
IN LPVOID Reserved
|
|
)
|
|
{
|
|
switch ( Reason ) {
|
|
|
|
case DLL_PROCESS_ATTACH:
|
|
TimeSvcSemaphore = CreateSemaphoreW( NULL,
|
|
0,
|
|
1,
|
|
SEMAPHORE_NAME );
|
|
if ( TimeSvcSemaphore == NULL ) {
|
|
return(FALSE);
|
|
}
|
|
if (GetLastError() != ERROR_ALREADY_EXISTS)
|
|
{
|
|
//if the semaphore didnt exist, set its initial count to 1
|
|
ReleaseSemaphore(TimeSvcSemaphore, 1, NULL);
|
|
}
|
|
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
if ( TimeSvcSemaphore ) {
|
|
CloseHandle( TimeSvcSemaphore );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return(TRUE);
|
|
|
|
} // TimeSvcDllEntryPoint
|
|
|
|
|
|
|
|
DWORD
|
|
TimeSvcNotifyThread(
|
|
IN PCLUS_WORKER Worker,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Thread to listen for resource change events on the Time Service resource.
|
|
|
|
Arguments:
|
|
|
|
Worker - Supplies the worker structure
|
|
|
|
Context - not used.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
HRESOURCE resource;
|
|
WCHAR* buffer;
|
|
DWORD bufSize;
|
|
DWORD bufAlloced;
|
|
DWORD failures = 0;
|
|
DWORD_PTR notifyKey;
|
|
DWORD filter;
|
|
CLUSTER_RESOURCE_STATE resourceState;
|
|
DWORD notUsed = 0;
|
|
|
|
//
|
|
// Now register for Notification of resource state change events.
|
|
//
|
|
if ( TimeSvcInfo->ClusterHandle == NULL ) {
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Create a real notification port.
|
|
//
|
|
if ( TimeSvcInfo->ClusterChange != NULL ) {
|
|
CloseClusterNotifyPort( TimeSvcInfo->ClusterChange );
|
|
}
|
|
TimeSvcInfo->ClusterChange = CreateClusterNotifyPort( INVALID_HANDLE_VALUE,
|
|
TimeSvcInfo->ClusterHandle,
|
|
(DWORD) CLUSTER_CHANGE_RESOURCE_STATE |
|
|
CLUSTER_CHANGE_HANDLE_CLOSE,
|
|
NOTIFY_KEY_RESOURCE_STATE);
|
|
|
|
if ( TimeSvcInfo->ClusterChange == NULL ) {
|
|
(TimeSvcLogEvent)(
|
|
TimeSvcInfo->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Failed to create notify port, status %1!u!. Stopped Listening...\n",
|
|
GetLastError() );
|
|
goto error_exit;
|
|
}
|
|
|
|
bufAlloced = 100;
|
|
buffer = LocalAlloc( LMEM_FIXED, bufAlloced * sizeof( WCHAR ) );
|
|
|
|
if ( buffer == NULL ) {
|
|
(TimeSvcLogEvent)(
|
|
TimeSvcInfo->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Failed to allocate a Notify buffer.\n");
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Loop listening for resource state change events...
|
|
//
|
|
|
|
while ( TRUE ) {
|
|
bufSize = bufAlloced;
|
|
|
|
status = GetClusterNotify( TimeSvcInfo->ClusterChange,
|
|
¬ifyKey,
|
|
&filter,
|
|
buffer,
|
|
&bufSize,
|
|
INFINITE );
|
|
if ( ClusWorkerCheckTerminate( &TimeSvcNotifyHandle ) ) {
|
|
break;
|
|
}
|
|
|
|
if ( status == ERROR_MORE_DATA ) {
|
|
//
|
|
// resize the buffer and loop again
|
|
//
|
|
|
|
LocalFree( buffer );
|
|
|
|
bufSize++; //add one for NULL
|
|
bufAlloced = bufSize;
|
|
|
|
buffer = LocalAlloc( LMEM_FIXED, bufAlloced * sizeof( WCHAR ) );
|
|
|
|
if ( buffer == NULL ) {
|
|
(TimeSvcLogEvent)(
|
|
TimeSvcInfo->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Failed to allocate a Notify buffer.\n");
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
} else if ( status != ERROR_SUCCESS ) {
|
|
(TimeSvcLogEvent)(
|
|
TimeSvcInfo->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Getting notification event failed, status %1!u!. \n",
|
|
status);
|
|
break;
|
|
} else {
|
|
WCHAR lpszResourceName[MAX_RESOURCE_NAME_LENGTH];
|
|
DWORD bytesReturned;
|
|
|
|
//
|
|
// Re-fetch our name each time, in case it changes.
|
|
//
|
|
status = ClusterResourceControl(
|
|
TimeSvcInfo->hResource,
|
|
NULL,
|
|
CLUSCTL_RESOURCE_GET_NAME,
|
|
0,
|
|
0,
|
|
(PUCHAR)&lpszResourceName,
|
|
MAX_RESOURCE_NAME_LENGTH * sizeof(WCHAR),
|
|
&bytesReturned );
|
|
if (status != ERROR_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
if ( (filter == CLUSTER_CHANGE_RESOURCE_STATE) &&
|
|
(lstrcmpiW( buffer, lpszResourceName ) == 0) ) {
|
|
bufSize = bufAlloced;
|
|
resourceState = GetClusterResourceState( TimeSvcInfo->hResource,
|
|
buffer,
|
|
&bufSize,
|
|
NULL,
|
|
¬Used );
|
|
if ( resourceState == ClusterResourceOnline ) {
|
|
status = TSNewSource( TimeSvcLocalComputerName,
|
|
buffer,
|
|
0 );
|
|
(TimeSvcLogEvent)(
|
|
TimeSvcInfo->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Status of Time Service request to sync from node %1!ws! is %2!u!.\n",
|
|
buffer,
|
|
status);
|
|
}
|
|
} else if (filter == CLUSTER_CHANGE_HANDLE_CLOSE) {
|
|
//
|
|
// Clean up and exit.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
error_exit:
|
|
|
|
// Don't close the ClusterChange notification handle, let the close
|
|
// routine do that!
|
|
|
|
return(ERROR_SUCCESS);
|
|
|
|
} // TimeSvcNotifyThread
|
|
|
|
|
|
|
|
RESID
|
|
WINAPI
|
|
TimeSvcOpen(
|
|
IN LPCWSTR ResourceName,
|
|
IN HKEY ResourceKey,
|
|
IN RESOURCE_HANDLE ResourceHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open routine for Cluster Time Service resource.
|
|
This routine gets a handle to the service controller, if we dont already have one,
|
|
and then gets a handle to the specified service. The service handle is saved
|
|
in the TimeSvcInfo structure.
|
|
|
|
Arguments:
|
|
|
|
ResourceName - supplies the resource name
|
|
|
|
ResourceKey - supplies a handle to the resource's cluster registry key
|
|
|
|
ResourceHandle - the resource handle to be supplied with SetResourceStatus
|
|
is called.
|
|
|
|
Return Value:
|
|
|
|
RESID of created resource
|
|
Zero on failure
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG index = 1;
|
|
DWORD status;
|
|
HKEY parametersKey = NULL;
|
|
PSERVICE_INFORMATION serviceInfo = NULL;
|
|
DWORD computerNameSize = MAX_COMPUTERNAME_LENGTH + 1;
|
|
WCHAR nodeName[MAX_COMPUTERNAME_LENGTH+1];
|
|
DWORD nodeNameSize = MAX_COMPUTERNAME_LENGTH + 1;
|
|
DWORD notUsed = 0;
|
|
CLUSTER_RESOURCE_STATE resourceState;
|
|
DWORD threadId;
|
|
DWORD nameLength;
|
|
|
|
(TimeSvcLogEvent)(
|
|
ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Creating resource.\n" );
|
|
|
|
//
|
|
// Make sure this is the only time we've been called!
|
|
//
|
|
if ( WaitForSingleObject( TimeSvcSemaphore, 0 ) == WAIT_TIMEOUT ) {
|
|
//
|
|
// A version of this service is already running
|
|
//
|
|
(TimeSvcLogEvent)(
|
|
ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Service is already running.\n" );
|
|
status = ERROR_SERVICE_ALREADY_RUNNING;
|
|
goto error_exit2;
|
|
}
|
|
|
|
serviceInfo = LocalAlloc( LMEM_FIXED, sizeof(SERVICE_INFORMATION) );
|
|
if ( serviceInfo == NULL ) {
|
|
(TimeSvcLogEvent)(
|
|
ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Failed to allocate a service info structure.\n");
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto error_exit;
|
|
}
|
|
|
|
ZeroMemory( serviceInfo, sizeof(SERVICE_INFORMATION) );
|
|
|
|
//
|
|
// Get the local computer name.
|
|
//
|
|
status = GetComputerNameW( TimeSvcLocalComputerName, &computerNameSize );
|
|
if ( !status ) {
|
|
(TimeSvcLogEvent)(
|
|
ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Failed to get local computer name, error %1!u!.\n",
|
|
status);
|
|
goto error_exit;
|
|
}
|
|
|
|
if ( serviceInfo->ClusterHandle == NULL ) {
|
|
serviceInfo->ClusterHandle = OpenCluster( NULL );
|
|
}
|
|
if ( serviceInfo->ClusterHandle == NULL ) {
|
|
status = GetLastError();
|
|
(TimeSvcLogEvent)(
|
|
ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Failed to open local cluster, error %1!u!.\n",
|
|
status);
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Sync our time from whatever system currently 'owns' the time service.
|
|
//
|
|
if ( serviceInfo->hResource == NULL ) {
|
|
serviceInfo->hResource = OpenClusterResource( serviceInfo->ClusterHandle,
|
|
ResourceName );
|
|
}
|
|
if ( serviceInfo->hResource == NULL ) {
|
|
status = GetLastError();
|
|
(TimeSvcLogEvent)(
|
|
ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Failed to open Time Service resource.\n");
|
|
goto error_exit;
|
|
}
|
|
|
|
resourceState = GetClusterResourceState( serviceInfo->hResource,
|
|
nodeName,
|
|
&nodeNameSize,
|
|
NULL,
|
|
¬Used );
|
|
if ( resourceState == ClusterResourceOnline ) {
|
|
status = TSNewSource( TimeSvcLocalComputerName,
|
|
nodeName,
|
|
0 );
|
|
}
|
|
|
|
//
|
|
// Create a notification thread to watch if the time service moves!
|
|
//
|
|
ClusWorkerTerminate(&TimeSvcNotifyHandle);
|
|
status = ClusWorkerCreate(&TimeSvcNotifyHandle,
|
|
TimeSvcNotifyThread,
|
|
serviceInfo);
|
|
if ( status != ERROR_SUCCESS ) {
|
|
(TimeSvcLogEvent)(
|
|
serviceInfo->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Error creating notify thread, status %1!u!.\n",
|
|
status );
|
|
goto error_exit;
|
|
}
|
|
|
|
TimeSvcInfo = serviceInfo;
|
|
serviceInfo->ResourceHandle = ResourceHandle;
|
|
|
|
return((RESID)serviceInfo);
|
|
|
|
error_exit:
|
|
|
|
ReleaseSemaphore ( TimeSvcSemaphore, 1 , 0 );
|
|
if ( TimeSvcInfo == serviceInfo ) {
|
|
TimeSvcInfo = NULL;
|
|
}
|
|
|
|
error_exit2:
|
|
|
|
if ( parametersKey != NULL ) {
|
|
ClusterRegCloseKey( parametersKey );
|
|
}
|
|
|
|
if ( serviceInfo != NULL ) {
|
|
if ( serviceInfo->hResource ) {
|
|
CloseClusterResource( serviceInfo->hResource );
|
|
}
|
|
if ( serviceInfo->ClusterHandle ) {
|
|
CloseCluster( serviceInfo->ClusterHandle );
|
|
}
|
|
LocalFree( serviceInfo );
|
|
}
|
|
|
|
SetLastError(status);
|
|
|
|
return(0);
|
|
|
|
} // TimeSvcOpen
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
TimeSvcOnline(
|
|
IN RESID Resource,
|
|
IN OUT PHANDLE EventHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Online routine for Cluster Time Service resource.
|
|
|
|
Arguments:
|
|
|
|
Resource - supplies resource id to be brought online
|
|
|
|
EventHandle - supplies a pointer to a handle to signal on error.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
ERROR_RESOURCE_NOT_FOUND if RESID is not valid.
|
|
ERROR_RESOURCE_NOT_AVAILABLE if resource was arbitrated but failed to
|
|
acquire 'ownership'.
|
|
Win32 error code if other failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
if ( TimeSvcInfo == NULL ) {
|
|
TIMESVC_PRINT("TimeSvc: Online request for a nonexistent resource id 0x%p.\n",
|
|
Resource);
|
|
return(ERROR_RESOURCE_NOT_FOUND);
|
|
}
|
|
|
|
if ( TimeSvcInfo != (PSERVICE_INFORMATION)Resource ) {
|
|
(TimeSvcLogEvent)(
|
|
TimeSvcInfo->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Online service info checked failed! Resource = %1!lx!.\n",
|
|
Resource);
|
|
return(ERROR_RESOURCE_NOT_FOUND);
|
|
}
|
|
|
|
return(ERROR_SUCCESS);
|
|
|
|
} // TimeSvcOnline
|
|
|
|
|
|
VOID
|
|
WINAPI
|
|
TimeSvcTerminate(
|
|
IN RESID Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Terminate routine for Cluster Time Service resource.
|
|
|
|
Arguments:
|
|
|
|
Resource - supplies resource id to be terminated
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
if ( TimeSvcInfo == NULL ) {
|
|
TIMESVC_PRINT("TimeSvc: Offline request for a nonexistent resource id 0x%p.\n",
|
|
Resource);
|
|
return;
|
|
}
|
|
|
|
if ( TimeSvcInfo != (PSERVICE_INFORMATION)Resource ) {
|
|
(TimeSvcLogEvent)(
|
|
TimeSvcInfo->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Offline service info check failed! Resource = %1!u!.\n",
|
|
Resource);
|
|
return;
|
|
}
|
|
|
|
(TimeSvcLogEvent)(
|
|
TimeSvcInfo->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Terminate request, returning.\n");
|
|
|
|
return;
|
|
|
|
} // TimeSvcTerminate
|
|
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
TimeSvcOffline(
|
|
IN RESID Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Offline routine for Cluster Time Service resource.
|
|
|
|
Arguments:
|
|
|
|
Resource - supplies the resource to be taken offline
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - always successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
TimeSvcTerminate( Resource );
|
|
|
|
return(ERROR_SUCCESS);
|
|
|
|
} // Offline
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
TimeSvcIsAlive(
|
|
IN RESID Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
IsAlive routine for Cluster Time Service resource.
|
|
|
|
Arguments:
|
|
|
|
Resource - supplies the resource id to be polled.
|
|
|
|
Return Value:
|
|
|
|
TRUE - if service is running
|
|
|
|
FALSE - if service is in any other state
|
|
|
|
--*/
|
|
{
|
|
|
|
return( TRUE );
|
|
|
|
} // TimeSvcIsAlive
|
|
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
TimeSvcLooksAlive(
|
|
IN RESID Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
LooksAlive routine for Cluster Time Service resource.
|
|
|
|
Arguments:
|
|
|
|
Resource - supplies the resource id to be polled.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Resource looks like it is alive and well
|
|
|
|
FALSE - Resource looks like it is toast.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
return( TRUE );
|
|
|
|
} // TimeSvcLooksAlive
|
|
|
|
|
|
|
|
VOID
|
|
WINAPI
|
|
TimeSvcClose(
|
|
IN RESID Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Close routine for Cluster Time Service resource.
|
|
This routine will stop the service, and delete the cluster
|
|
information regarding that service.
|
|
|
|
Arguments:
|
|
|
|
Resource - supplies resource id to be closed
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
PSERVICE_INFORMATION serviceInfo = NULL;
|
|
|
|
serviceInfo = (PSERVICE_INFORMATION)Resource;
|
|
|
|
if ( serviceInfo == NULL ) {
|
|
TIMESVC_PRINT("TimeSvc: Close request for a nonexistent resource id 0x%p\n",
|
|
Resource);
|
|
return;
|
|
}
|
|
|
|
(TimeSvcLogEvent)(
|
|
serviceInfo->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Close request for %1!lx!, TimeSvcInfo = %2!lx!.\n",
|
|
serviceInfo, TimeSvcInfo);
|
|
|
|
//
|
|
// Shut it down if it's on line
|
|
//
|
|
|
|
TimeSvcTerminate(Resource);
|
|
|
|
if ( serviceInfo->ClusterHandle ) {
|
|
CloseCluster( serviceInfo->ClusterHandle );
|
|
}
|
|
|
|
if ( serviceInfo == TimeSvcInfo ) {
|
|
ClusWorkerTerminate( &TimeSvcNotifyHandle );
|
|
TimeSvcInfo = NULL;
|
|
//SetEvent ( TimeSvcSemaphore );
|
|
ReleaseSemaphore ( TimeSvcSemaphore, 1 , 0 );
|
|
}
|
|
|
|
if ( serviceInfo->hResource ) {
|
|
CloseClusterResource( serviceInfo->hResource );
|
|
}
|
|
|
|
if ( serviceInfo->ClusterChange != NULL ) {
|
|
CloseClusterNotifyPort( serviceInfo->ClusterChange );
|
|
}
|
|
|
|
LocalFree( serviceInfo );
|
|
|
|
return;
|
|
|
|
} // TimeSvcClose
|
|
|
|
|
|
//***********************************************************
|
|
//
|
|
// Define Function Table
|
|
//
|
|
//***********************************************************
|
|
|
|
CLRES_V1_FUNCTION_TABLE( TimeSvcFunctionTable,
|
|
CLRES_VERSION_V1_00,
|
|
TimeSvc,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL );
|
|
|