/*++

Copyright (c) 1999-1999 Microsoft Corporation

Module Name:

    dtsetup.c

Abstract:

    Simple Duct-Tape setup.

Author:

    Keith Moore (keithmo)        02-Sep-1999

Revision History:

--*/


#include "precomp.h"
#pragma hdrstop


//
// Configuration data.
//

#define UL_SERVICE_NAME     L"UL"
#define UL_DISPLAY_NAME     L"UL"
#define UL_DRIVER_NAME      L"UL.SYS"
#define UL_DEPENDENCIES     NULL

#define WAS_SERVICE_NAME    L"IISW3ADM"
#define WAS_DISPLAY_NAME    L"IIS Web Admin Service"
#define WAS_DLL_NAME        L"iisw3adm.dll"
#define WAS_DEPENDENCIES    L"UL"
#define WAS_COMMAND_LINE    L"%SystemRoot%\\System32\\svchost.exe -k iissvcs"
#define WAS_PARAM_KEY       L"System\\CurrentControlSet\\Services\\iisw3adm\\Parameters"
#define WAS_EVENT_KEY       L"System\\CurrentControlSet\\Services\\EventLog\\System\\WAS"
#define WAS_SVCHOST_KEY     L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Svchost"

#define SERVICE_DLL_VALUE   L"ServiceDll"
#define EVENT_MESSAGE_VALUE L"EventMessageFile"
#define SVCHOST_VALUE       L"iissvcs"

#define CATALOG_DLL_NAME    L"catalog.dll"
#define CATALOG_KEY         L"Software\\Microsoft\\Catalog42"
#define CATALOG_URT_VALUE   L"URT"


//
// Our command table.
//

typedef
VOID
(* PFN_COMMAND)(
    IN INT argc,
    IN PWSTR argv[]
    );

typedef struct _COMMAND_ENTRY
{
    PWSTR pCommandName;
    PWSTR pUsageHelp;
    PFN_COMMAND pCommandHandler;

} COMMAND_ENTRY, *PCOMMAND_ENTRY;


//
// Private prototypes.
//

VOID
Usage(
    VOID
    );

PCOMMAND_ENTRY
FindCommandByName(
    IN PWSTR pCommand
    );

VOID
DoInstall(
    IN INT argc,
    IN PWSTR argv[]
    );

VOID
DoRemove(
    IN INT argc,
    IN PWSTR argv[]
    );

DWORD
InstallUL(
    IN PWSTR pBaseDirectory
    );

DWORD
InstallWAS(
    IN PWSTR pBaseDirectory
    );

DWORD
InstallCatalog(
    IN PWSTR pBaseDirectory
    );

VOID
RemoveCatalog(
    VOID
    );

VOID
RemoveWAS(
    VOID
    );

VOID
RemoveUL(
    VOID
    );

DWORD
InstallService(
    IN PWSTR pServiceName,
    IN PWSTR pDisplayName OPTIONAL,
    IN PWSTR pBinPath,
    IN PWSTR pDependencies OPTIONAL,
    IN DWORD ServiceType,
    IN DWORD StartType,
    IN DWORD ErrorControl
    );

DWORD
RemoveService(
    IN PWSTR pServiceName
    );

DWORD
WriteRegValue(
    IN PWSTR pKeyName,
    IN PWSTR pValueName,
    IN PWSTR pValue
    );

DWORD
DeleteRegKey(
    IN PWSTR pKeyName
    );


//
// Private globals.
//

COMMAND_ENTRY CommandTable[] =
    {
        {
            L"install",
            L"install UL and WAS",
            &DoInstall
        },

        {
            L"remove",
            L"remove UL and WAS",
            &DoRemove
        }

    };

#define NUM_COMMAND_ENTRIES (sizeof(CommandTable) / sizeof(CommandTable[0]))


//
// Public functions.
//


INT
__cdecl
wmain(
    INT argc,
    PWSTR argv[]
    )
{
    PCOMMAND_ENTRY pEntry;

    //
    // Find the command handler.
    //

    if (argc == 1)
    {
        pEntry = NULL;
    }
    else
    {
        pEntry = FindCommandByName( argv[1] );
    }

    if (pEntry == NULL)
    {
        Usage();
        return 1;
    }

    //
    // Call the handler.
    //

    argc--;
    argv++;

    (pEntry->pCommandHandler)( argc, argv );

    return 0;

}   // wmain


//
// Private functions.
//

PCOMMAND_ENTRY
FindCommandByName(
    IN PWSTR pCommand
    )
{
    PCOMMAND_ENTRY pEntry;
    INT i;

    for (i = NUM_COMMAND_ENTRIES, pEntry = &CommandTable[0] ;
         i > 0 ;
         i--, pEntry++)
     {
        if (_wcsicmp( pCommand, pEntry->pCommandName ) == 0)
        {
            return pEntry;
        }
    }

    return NULL;

}   // FindCommandByName


VOID
Usage(
    VOID
    )
{
    PCOMMAND_ENTRY pEntry;
    INT i;
    INT maxLength;
    INT len;

    //
    // Scan the command table, searching for the longest command name.
    // (This makes the output much prettier...)
    //

    maxLength = 0;

    for (i = NUM_COMMAND_ENTRIES, pEntry = &CommandTable[0] ;
         i > 0 ;
         i--, pEntry++)
    {
        len = wcslen( pEntry->pCommandName );

        if (len > maxLength)
        {
            maxLength = len;
        }
    }

    //
    // Now display the usage information.
    //

    wprintf(
        L"use: dtsetup action [options]\n"
        L"\n"
        L"valid actions are:\n"
        L"\n"
        );

    for (i = NUM_COMMAND_ENTRIES, pEntry = &CommandTable[0] ;
         i > 0 ;
         i--, pEntry++)
    {
        wprintf(
            L"    %-*s - %s\n",
            maxLength,
            pEntry->pCommandName,
            pEntry->pUsageHelp
            );
    }

}   // Usage


VOID
DoInstall(
    IN INT argc,
    IN PWSTR argv[]
    )
{
    PWSTR pBaseDirectory;
    DWORD err;

    //
    // Validate the arguments.
    //

    if (argc != 2)
    {
        wprintf( L"use: dtsetup install directory\n" );
        return;
    }

    //
    // Install 'em.
    //

    wprintf( L"installing UL..." );
    err = InstallUL( argv[1] );

    if (err != NO_ERROR)
    {
        wprintf( L"error %lu\n", err );
        return;
    }

    wprintf( L"done\n" );

    wprintf( L"installing WAS..." );
    err = InstallWAS( argv[1] );

    if (err != NO_ERROR)
    {
        wprintf( L"error %lu\n", err );
        RemoveUL();
        return;
    }

    wprintf( L"done\n" );

    wprintf( L"installing Catalog..." );
    err = InstallCatalog( argv[1] );

    if (err != NO_ERROR)
    {
        wprintf( L"error %lu\n", err );
        RemoveWAS();
        RemoveUL();
        return;
    }

    wprintf( L"done\n" );

    //
    // Success!
    //

}   // DoInstall


VOID
DoRemove(
    IN INT argc,
    IN PWSTR argv[]
    )
{
    //
    // Validate the arguments.
    //

    if (argc != 1)
    {
        wprintf( L"use: dtsetup remove\n" );
        return;
    }

    //
    // Remove 'em.
    //

    wprintf( L"removing Catalog..." );
    RemoveCatalog();
    wprintf( L"done\n" );

    wprintf( L"removing WAS..." );
    RemoveWAS();
    wprintf( L"done\n" );

    wprintf( L"removing UL..." );
    RemoveUL();
    wprintf( L"done\n" );

}   // DoRemove


DWORD
InstallUL(
    IN PWSTR pBaseDirectory
    )
{
    DWORD err;
    WCHAR binPath[MAX_PATH];

    //
    // Build the path to the driver.
    //

    wcscpy( binPath, pBaseDirectory );

    if (binPath[wcslen(binPath) - 1] != L'\\')
    {
        wcscat( binPath, L"\\" );
    }

    wcscat( binPath, UL_DRIVER_NAME );

    //
    // Install it.
    //

    err = InstallService(
                UL_SERVICE_NAME,                // pServiceName
                UL_DISPLAY_NAME,                // pDisplayName
                binPath,                        // pBinPath
                UL_DEPENDENCIES,                // pDependencies
                SERVICE_KERNEL_DRIVER,          // ServiceType
                SERVICE_DEMAND_START,           // StartType
                SERVICE_ERROR_NORMAL            // ErrorControl
                );

    return err;

}   // InstallUL


DWORD
InstallWAS(
    IN PWSTR pBaseDirectory
    )
{
    DWORD err;
    WCHAR binPath[MAX_PATH];

    //
    // Build the path to the service.
    //

    wcscpy( binPath, pBaseDirectory );

    if (binPath[wcslen(binPath) - 1] != L'\\')
    {
        wcscat( binPath, L"\\" );
    }

    wcscat( binPath, WAS_DLL_NAME );

    //
    // Install it.
    //

    err = InstallService(
                WAS_SERVICE_NAME,               // pServiceName
                WAS_DISPLAY_NAME,               // pDisplayName
                WAS_COMMAND_LINE,               // pBinPath
                WAS_DEPENDENCIES,               // pDependencies
                SERVICE_WIN32_SHARE_PROCESS |   // ServiceType
                    SERVICE_INTERACTIVE_PROCESS,
                SERVICE_DEMAND_START,           // StartType
                SERVICE_ERROR_NORMAL            // ErrorControl
                );

    if (err == NO_ERROR)
    {
        err = WriteRegValue(
                    WAS_PARAM_KEY,              // pKeyName
                    SERVICE_DLL_VALUE,          // pValueName
                    binPath                     // pValue
                    );
    }

    if (err == NO_ERROR)
    {
        err = WriteRegValue(
                    WAS_EVENT_KEY,              // pKeyName
                    EVENT_MESSAGE_VALUE,        // pValueName
                    binPath                     // pValue
                    );
    }

    if (err == NO_ERROR)
    {
        err = WriteRegValue(
                    WAS_SVCHOST_KEY,            // pKeyName
                    SVCHOST_VALUE,              // pValueName
                    WAS_SERVICE_NAME            // pValue
                    );
    }

    return err;

}   // InstallWAS


DWORD
InstallCatalog(
    IN PWSTR pBaseDirectory
    )
{
    DWORD err;
    WCHAR binPath[MAX_PATH];

    //
    // Build the path to the service.
    //

    wcscpy( binPath, pBaseDirectory );

    if (binPath[wcslen(binPath) - 1] != L'\\')
    {
        wcscat( binPath, L"\\" );
    }

    wcscat( binPath, CATALOG_DLL_NAME );

    //
    // Install it.
    //

    err = WriteRegValue(
                CATALOG_KEY,                    // pKeyName
                CATALOG_URT_VALUE,              // pValueName
                binPath                         // pValue
                );

    return err;

}   // InstallCatalog


VOID
RemoveCatalog(
    VOID
    )
{
    (VOID)DeleteRegKey( CATALOG_KEY );

}   // RemoveCatalog


VOID
RemoveWAS(
    VOID
    )
{
    (VOID)RemoveService( WAS_SERVICE_NAME );
    (VOID)DeleteRegKey( WAS_EVENT_KEY );

}   // RemoveWAS


VOID
RemoveUL(
    VOID
    )
{
    (VOID)RemoveService( UL_SERVICE_NAME );

}   // RemoveUL


DWORD
InstallService(
    IN PWSTR pServiceName,
    IN PWSTR pDisplayName OPTIONAL,
    IN PWSTR pBinPath,
    IN PWSTR pDependencies OPTIONAL,
    IN DWORD ServiceType,
    IN DWORD StartType,
    IN DWORD ErrorControl
    )
{
    SC_HANDLE scHandle;
    SC_HANDLE svcHandle;
    DWORD err;

    //
    // Setup locals so we know how to cleanup on exit.
    //

    scHandle = NULL;
    svcHandle = NULL;

    //
    // If no display name specified, just use the service name.
    //

    if (pDisplayName == NULL)
    {
        pDisplayName = pServiceName;
    }

    //
    // Open the service controller.
    //

    scHandle = OpenSCManagerW(
                   NULL,                        // lpMachineName
                   NULL,                        // lpDatabaseName
                   SC_MANAGER_ALL_ACCESS        // dwDesiredAccess
                   );

    if (scHandle == NULL)
    {
        err = GetLastError();
        goto cleanup;
    }

    //
    // Create the service.
    //

    svcHandle = CreateServiceW(
                    scHandle,                   // hSCManager
                    pServiceName,               // lpServiceName
                    pDisplayName,               // lpDisplayName
                    SERVICE_ALL_ACCESS,         // dwDesiredAccess
                    ServiceType,                // dwServiceType
                    StartType,                  // dwStartType
                    ErrorControl,               // dwErrorControl
                    pBinPath,                   // lpBinaryPathName
                    NULL,                       // lpLoadOrderGroup
                    NULL,                       // lpdwTagId
                    pDependencies,              // lpDependencies
                    NULL,                       // lpServiceStartName
                    NULL                        // lpPassword
                    );

    if (svcHandle == NULL)
    {
        err = GetLastError();
        goto cleanup;
    }

    //
    // Success!
    //

    err = 0;

cleanup:

    if (svcHandle != NULL)
    {
        CloseServiceHandle( svcHandle );
    }

    if (scHandle != NULL)
    {
        CloseServiceHandle( scHandle );
    }

    return err;

}   // InstallService


DWORD
RemoveService(
    IN PWSTR pServiceName
    )
{
    SC_HANDLE scHandle;
    SC_HANDLE svcHandle;
    DWORD err;

    //
    // Setup locals so we know how to cleanup on exit.
    //

    scHandle = NULL;
    svcHandle = NULL;

    //
    // Open the service controller.
    //

    scHandle = OpenSCManagerW(
                   NULL,                        // lpMachineName
                   NULL,                        // lpDatabaseName
                   SC_MANAGER_ALL_ACCESS        // dwDesiredAccess
                   );

    if (scHandle == NULL)
    {
        err = GetLastError();
        goto cleanup;
    }

    //
    // Open the service.
    //

    svcHandle = OpenServiceW(
                    scHandle,                   // hSCManager
                    pServiceName,               // lpServiceName
                    SERVICE_ALL_ACCESS          // dwDesiredAccess
                    );

    if (svcHandle == NULL)
    {
        err = GetLastError();
        goto cleanup;
    }

    //
    // Delete it.
    //

    if (!DeleteService( svcHandle ))
    {
        err = GetLastError();
        goto cleanup;
    }

    //
    // Success!
    //

    err = 0;

cleanup:

    if (svcHandle != NULL)
    {
        CloseServiceHandle( svcHandle );
    }

    if (scHandle != NULL)
    {
        CloseServiceHandle( scHandle );
    }

    return err;

}   // RemoveService


DWORD
WriteRegValue(
    IN PWSTR pKeyName,
    IN PWSTR pValueName,
    IN PWSTR pValue
    )
{
    DWORD err;
    HKEY key;
    DWORD disposition;
    DWORD length;

    err = RegCreateKeyExW(
                HKEY_LOCAL_MACHINE,             // hKey
                pKeyName,                       // lpSubKey
                0,                              // Reserved
                NULL,                           // lpClass
                REG_OPTION_NON_VOLATILE,        // dwOptions
                KEY_ALL_ACCESS,                 // samDesired
                NULL,                           // lpSecurityAttributes,
                &key,                           // phkResult
                &disposition                    // lpdwDisposition
                );

    if (err == NO_ERROR)
    {
        length = (wcslen(pValue) + 1) * sizeof(pValue[0]);

        err = RegSetValueExW(
                    key,                        // hKey
                    pValueName,                 // lpValueName
                    0,                          // Reserved
                    REG_EXPAND_SZ,              // dwType
                    (CONST BYTE *)pValue,       // lpData
                    length                      // cbData
                    );

        RegCloseKey( key );
    }

    return err;

}   // WriteRegValue


DWORD
DeleteRegKey(
    IN PWSTR pKeyName
    )
{
    DWORD err;

    err = RegDeleteKeyW(
                HKEY_LOCAL_MACHINE,
                pKeyName
                );

    return err;

}   // DeleteRegKey