/*++

Copyright (c) 1995-1999  Microsoft Corporation

Module Name:

    dllinit.c

Abstract:

    This module contians the DLL attach/detach event entry point for
    the pdh.dll

Author:

    Bob Watson (a-robw) Jul 95

Revision History:

--*/

#include <windows.h>
#include <assert.h>
#include "pdhitype.h"
#include "pdhidef.h"
#include "pdhmsg.h"
#include "strings.h"
#include "pdhp.h"

HANDLE  ThisDLLHandle = NULL;
WCHAR   szStaticLocalMachineName[MAX_PATH] = {0};
HANDLE  hPdhDataMutex    = NULL;
HANDLE  hPdhContextMutex = NULL;
HANDLE  hPdhHeap         = NULL;
HANDLE  hEventLog        = NULL;

LONGLONG    llRemoteRetryTime = RETRY_TIME_INTERVAL;
BOOL        bEnableRemotePdhAccess = TRUE;
DWORD       dwPdhiLocalDefaultDataSource = DATA_SOURCE_REGISTRY;
LONG        dwCurrentRealTimeDataSource = 0;
BOOL        bProcessIsDetaching = FALSE;


LPWSTR
GetStringResource (
    DWORD   dwResId
)
{
    LPWSTR  szReturnString = NULL;
    LPWSTR  szTmpString    = NULL;
    DWORD   dwStrLen = (2048 * sizeof(WCHAR));

    szReturnString = (LPWSTR) G_ALLOC (dwStrLen);
    if (szReturnString != NULL) {
        dwStrLen /= sizeof(WCHAR);
        dwStrLen = LoadStringW (
            ThisDLLHandle,
            (UINT)dwResId, 
            szReturnString, 
            dwStrLen);
        if (dwStrLen > 0) {
            // then realloc down to the size used
            dwStrLen++; // to include the NULL
            dwStrLen *= sizeof (WCHAR);
            szTmpString    = szReturnString;
            szReturnString = G_REALLOC (szReturnString, dwStrLen);
            if (szReturnString == NULL) {
                G_FREE(szTmpString);
                szTmpString = NULL;
            }
        } else {
            // free the memory since the look up failed
            G_FREE (szReturnString);
            szReturnString = NULL;
        }
    } //else  allocation failed

    return szReturnString;
}

STATIC_BOOL
PdhiOpenEventLog (
    HANDLE  *phEventLogHandle
)
{
    HANDLE  hReturn;
    BOOL    bReturn;

    if ((hReturn = RegisterEventSourceW (
        NULL, // on the local machine
        cszAppShortName    // for the PDH.DLL
        )) != NULL) {
        *phEventLogHandle = hReturn;
        bReturn = TRUE;
    } else {
        bReturn = FALSE;
    }
    return bReturn;
}

STATIC_BOOL
PdhiGetRegistryDefaults ()
{
    DWORD dwStatus;
    DWORD dwType, dwSize, dwValue;

    HKEY    hKeyPDH;

    // the local data source is not initialized so use it
    dwStatus = RegOpenKeyExW (
        HKEY_LOCAL_MACHINE,
        cszPdhKey,
        0L,
        KEY_READ,
        &hKeyPDH);

    if (dwStatus == ERROR_SUCCESS) {
        //
        // get the default null data source
        //
        dwValue = 0;
        dwType = 0;
        dwSize = sizeof (dwValue);
        dwStatus = RegQueryValueExW (
            hKeyPDH,
            cszDefaultNullDataSource,
            NULL,
            &dwType,
            (LPBYTE)&dwValue,
            &dwSize);
        if (!(dwStatus == ERROR_SUCCESS) || !(dwType == REG_DWORD)) {
            dwValue = DATA_SOURCE_REGISTRY;
        } else {
            // check the value for validity
            switch (dwValue) {
            case DATA_SOURCE_WBEM:
            case DATA_SOURCE_REGISTRY:
                // this is OK
                break;
            case DATA_SOURCE_LOGFILE:
            default:
                // these are not OK so insert default
                dwValue = DATA_SOURCE_REGISTRY;
                break;
            }
        }
        dwPdhiLocalDefaultDataSource = dwValue;

        //
        //  get the retry timeout
        //
        dwValue = 0;
        dwType = 0;
        dwSize = sizeof (dwValue);
        dwStatus = RegQueryValueExW (
            hKeyPDH,
            cszRemoteMachineRetryTime,
            NULL,
            &dwType,
            (LPBYTE)&dwValue,
            &dwSize);
        if (!(dwStatus == ERROR_SUCCESS) || !(dwType == REG_DWORD)) {
            dwValue = 0;
        } else {
            // check the value for validity
            // must be 30 seconds or more yet no more than an hour
            if ((dwValue <= 30) || (dwValue > 3600)) {
                dwValue = 0;
            }
        }
        if (dwValue != 0) {
            // convert to 100NS units
            llRemoteRetryTime = dwValue * 10000000;   
        } else {
            // use default
            llRemoteRetryTime = RETRY_TIME_INTERVAL;
        }

        // 
        //  get the remote access mode
        //
        dwValue = 0;
        dwType = 0;
        dwSize = sizeof (dwValue);
        dwStatus = RegQueryValueExW (
            hKeyPDH,
            cszEnableRemotePdhAccess,
            NULL,
            &dwType,
            (LPBYTE)&dwValue,
            &dwSize);
        if (!(dwStatus == ERROR_SUCCESS) || !(dwType == REG_DWORD)) {
            dwValue = TRUE;
        } else {
            // check the value for validity
            if (dwValue != 0) {
                dwValue = TRUE;
            }            
        }
        bEnableRemotePdhAccess = (BOOL)dwValue;

        // close the registry key
        RegCloseKey (hKeyPDH);
    }
    return TRUE;
}

STATIC_BOOL
PdhiCloseEventLog (
    HANDLE  * phEventLogHandle
)
{
    BOOL    bReturn;

    if (*phEventLogHandle != NULL) {
        bReturn = DeregisterEventSource (*phEventLogHandle);
        *phEventLogHandle = NULL;
    } else {
        // it's already closed so that's OK
        bReturn = TRUE;
    }
    return bReturn;
}

HRESULT
PdhiPlaInitMutex()
{
    HRESULT hr = ERROR_SUCCESS;
    BOOL bResult = TRUE;

    PSECURITY_DESCRIPTOR  SD = NULL;
    SECURITY_ATTRIBUTES sa;
    PSID AuthenticatedUsers    = NULL;
    PSID BuiltInAdministrators = NULL;
    PSID NetworkService        = NULL;
    DWORD dwAclSize;
    ACL  *Acl;

    SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;

    bResult = AllocateAndInitializeSid(
                        &NtAuthority,
                        2,
                        SECURITY_BUILTIN_DOMAIN_RID,
                        DOMAIN_ALIAS_RID_ADMINS,
                        0,0,0,0,0,0,
                        &BuiltInAdministrators);
    if( !bResult ){goto cleanup;}

    bResult = AllocateAndInitializeSid(
                        &NtAuthority,
                        1,
                        SECURITY_AUTHENTICATED_USER_RID,
                        0,0,0,0,0,0,0,
                        &AuthenticatedUsers);
    if( !bResult ){goto cleanup;}

    bResult = AllocateAndInitializeSid(
                        &NtAuthority,
                        1,
                        SECURITY_NETWORK_SERVICE_RID,
                        0,0,0,0,0,0,0,
                        &NetworkService);
    if( !bResult ){goto cleanup;}

    dwAclSize = sizeof (ACL) +
                (3 * (sizeof (ACCESS_ALLOWED_ACE) - sizeof (ULONG))) +
                GetLengthSid(AuthenticatedUsers) +
                GetLengthSid(BuiltInAdministrators) +
                GetLengthSid(NetworkService);

    SD = (PSECURITY_DESCRIPTOR)G_ALLOC(SECURITY_DESCRIPTOR_MIN_LENGTH + dwAclSize);
    if( NULL == SD ){ goto cleanup; }

    ZeroMemory( SD, sizeof(SD) );
    
    Acl = (ACL *)((BYTE *)SD + SECURITY_DESCRIPTOR_MIN_LENGTH);

    bResult = InitializeAcl( Acl, dwAclSize, ACL_REVISION);
    if( !bResult ){goto cleanup;}

    bResult = AddAccessAllowedAce(Acl, ACL_REVISION, SYNCHRONIZE | GENERIC_READ, AuthenticatedUsers );
    if( !bResult ){goto cleanup;}

    bResult = AddAccessAllowedAce(Acl, ACL_REVISION, MUTEX_ALL_ACCESS , NetworkService );
    if( !bResult ){goto cleanup;}
    
    bResult = AddAccessAllowedAce(Acl, ACL_REVISION, GENERIC_ALL, BuiltInAdministrators );
    if( !bResult ){goto cleanup;}

    bResult = InitializeSecurityDescriptor(SD, SECURITY_DESCRIPTOR_REVISION);
    if( !bResult ){goto cleanup;}

    bResult = SetSecurityDescriptorDacl(SD, TRUE, Acl,  FALSE);
    if( !bResult ){goto cleanup;}
    
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = SD;
    sa.bInheritHandle = FALSE;

    hPdhPlaMutex = CreateMutexW( &sa, FALSE, PDH_PLA_MUTEX );

cleanup:
    if( hPdhPlaMutex == NULL || !bResult ){
        hr = GetLastError();
    }
    if( NULL != AuthenticatedUsers ){
        FreeSid(AuthenticatedUsers);
    }
    if( NULL != BuiltInAdministrators){
        FreeSid(BuiltInAdministrators);
    }
    if( NULL != NetworkService){
        FreeSid(NetworkService);
    }
    G_FREE(SD);

    return hr;
}

BOOL
_stdcall
PdhDllInitRoutine(
    IN HANDLE DLLHandle,
    IN DWORD  Reason,
    IN LPVOID ReservedAndUnused
    )
{
    BOOL    bStatus;
    BOOL    bReturn = TRUE;
    OSVERSIONINFOW   os;
    ReservedAndUnused;

    switch(Reason) {
        case DLL_PROCESS_ATTACH:
            bProcessIsDetaching = FALSE;
            {
                DWORD   dwBufferLength = 0;

                ThisDLLHandle = DLLHandle;

                // make sure this is the correct operating system
                memset (&os, 0, sizeof(os));
                os.dwOSVersionInfoSize = sizeof(os);
                bReturn = GetVersionExW (&os);

                if (bReturn) {
                    // check for windows NT v4.0
                    if (os.dwPlatformId != VER_PLATFORM_WIN32_NT) {
                        // not WINDOWS NT
                        bReturn = FALSE;
                    } else if (os.dwMajorVersion < 4) {
                        // it's windows NT, but an old one
                        bReturn = FALSE;
                    }
                } else {
                    // unable to read version so give up
                }

                if (bReturn) {

                    // disable thread init calls
                    DisableThreadLibraryCalls (DLLHandle);

                    // initialize the event log so events can be reported
                    bStatus = PdhiOpenEventLog (&hEventLog);

                    bStatus = PdhiGetRegistryDefaults ();

                    // initialize the local computer name buffer
                    if (szStaticLocalMachineName[0] == 0) {
                        // initialize the computer name for this computer
                        szStaticLocalMachineName[0] = BACKSLASH_L;
                        szStaticLocalMachineName[1] = BACKSLASH_L;
                        dwBufferLength = (sizeof(szStaticLocalMachineName) / sizeof(WCHAR)) - 2;
                        GetComputerNameW (&szStaticLocalMachineName[2], &dwBufferLength);
                    }
                
                    hPdhDataMutex = CreateMutexW (NULL, FALSE, NULL);
                    hPdhContextMutex = CreateMutexW(NULL, FALSE, NULL);

                    hPdhHeap = HeapCreate (0, 0, 0);
                    if (hPdhHeap == NULL) {
                        // unable to create our own heap, so use the
                        // process heap
                        hPdhHeap = GetProcessHeap();
                        assert (hPdhHeap != NULL);
                    }

                    PdhiPlaInitMutex();
                }
            }
            break;

        case DLL_PROCESS_DETACH:

            // close all pending loggers
            //

            bProcessIsDetaching = (ReservedAndUnused != NULL)
                                ? (TRUE) : (FALSE);

            PdhiCloseAllLoggers();

            // walk down query list and close (at least disconnect) queries.
            PdhiQueryCleanup ();

            FreeAllMachines(bProcessIsDetaching);

            PdhiFreeAllWbemServers ();

            if (hPdhDataMutex != NULL) {
                bStatus = CloseHandle (hPdhDataMutex);
                assert (bStatus);
                hPdhDataMutex = NULL;
            }
            if (hPdhContextMutex != NULL) {
                bStatus = CloseHandle (hPdhContextMutex);
                assert (bStatus);
                hPdhContextMutex = NULL;
            }

            if (hPdhHeap != GetProcessHeap()) {
                HeapDestroy (hPdhHeap);
                hPdhHeap = NULL;
            }

            // lastly close the event log interface
            bStatus = PdhiCloseEventLog (&hEventLog);
            bReturn = TRUE;

            break ;

        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
            bReturn = TRUE;
            break;
    }

    return (bReturn);
}