|
|
/*++
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 );
|