762 lines
17 KiB
C
762 lines
17 KiB
C
/*++
|
||
|
||
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 );
|
||
|