Ejemplo n.º 1
0
extern "C" HRESULT LoggingSetPackageVariable(
    __in BURN_PACKAGE* pPackage,
    __in_z_opt LPCWSTR wzSuffix,
    __in BOOL fRollback,
    __in BURN_LOGGING* pLog,
    __in BURN_VARIABLES* pVariables,
    __out_opt LPWSTR* psczLogPath
    )
{
    HRESULT hr = S_OK;
    LPWSTR sczLogPath = NULL;

    if (BURN_LOGGING_STATE_DISABLED == pLog->state)
    {
        if (psczLogPath)
        {
            *psczLogPath = NULL;
        }

        ExitFunction();
    }

    if ((!fRollback && pPackage->sczLogPathVariable && *pPackage->sczLogPathVariable) ||
        (fRollback && pPackage->sczRollbackLogPathVariable && *pPackage->sczRollbackLogPathVariable))
    {
        hr = StrAllocFormatted(&sczLogPath, L"%ls%hs%ls_%03u_%ls%ls.%ls", pLog->sczPrefix, wzSuffix && *wzSuffix ? "_" : "", wzSuffix && *wzSuffix ? wzSuffix : L"", vdwPackageSequence, pPackage->sczId, fRollback ? L"_rollback" : L"", pLog->sczExtension);
        ExitOnFailure(hr, "Failed to allocate path for package log.");

        hr = VariableSetString(pVariables, fRollback ? pPackage->sczRollbackLogPathVariable : pPackage->sczLogPathVariable, sczLogPath, FALSE);
        ExitOnFailure(hr, "Failed to set log path into variable.");

        if (psczLogPath)
        {
            hr = StrAllocString(psczLogPath, sczLogPath, 0);
            ExitOnFailure(hr, "Failed to copy package log path.");
        }
    }

LExit:
    ReleaseStr(sczLogPath);

    return hr;
}
Ejemplo n.º 2
0
extern "C" HRESULT ExeEngineExecutePackage(
    __in BURN_EXECUTE_ACTION* pExecuteAction,
    __in BURN_VARIABLES* pVariables,
    __in BOOL fRollback,
    __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
    __in LPVOID pvContext,
    __out BOOTSTRAPPER_APPLY_RESTART* pRestart
    )
{
    HRESULT hr = S_OK;
    WCHAR wzCurrentDirectory[MAX_PATH] = { };
    BOOL fChangedCurrentDirectory = FALSE;
    int nResult = IDNOACTION;
    LPCWSTR wzArguments = NULL;
    LPWSTR sczArgumentsFormatted = NULL;
    LPWSTR sczArgumentsObfuscated = NULL;
    LPWSTR sczCachedDirectory = NULL;
    LPWSTR sczExecutablePath = NULL;
    LPWSTR sczCommand = NULL;
    LPWSTR sczCommandObfuscated = NULL;
    STARTUPINFOW si = { };
    PROCESS_INFORMATION pi = { };
    DWORD dwExitCode = 0;
    GENERIC_EXECUTE_MESSAGE message = { };

    // get cached executable path
    hr = CacheGetCompletedPath(pExecuteAction->exePackage.pPackage->fPerMachine, pExecuteAction->exePackage.pPackage->sczCacheId, &sczCachedDirectory);
    ExitOnFailure1(hr, "Failed to get cached path for package: %ls", pExecuteAction->exePackage.pPackage->sczId);

    // Best effort to set the execute package cache folder variable.
    VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE);

    hr = PathConcat(sczCachedDirectory, pExecuteAction->exePackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczExecutablePath);
    ExitOnFailure(hr, "Failed to build executable path.");

    // pick arguments
    switch (pExecuteAction->exePackage.action)
    {
    case BOOTSTRAPPER_ACTION_STATE_INSTALL:
        wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczInstallArguments;
        break;

    case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
        wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczUninstallArguments;
        break;

    case BOOTSTRAPPER_ACTION_STATE_REPAIR:
        wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczRepairArguments;
        break;

    default:
        hr = E_UNEXPECTED;
        ExitOnFailure(hr, "Failed to get action arguments for executable package.");
    }

    // build command
    if (wzArguments && *wzArguments)
    {
        hr = VariableFormatString(pVariables, wzArguments, &sczArgumentsFormatted, NULL);
        ExitOnFailure(hr, "Failed to format argument string.");

        hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", sczExecutablePath, sczArgumentsFormatted);
        ExitOnFailure(hr, "Failed to create executable command.");

        hr = VariableFormatStringObfuscated(pVariables, wzArguments, &sczArgumentsObfuscated, NULL);
        ExitOnFailure(hr, "Failed to format obfuscated argument string.");

        hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", sczExecutablePath, sczArgumentsObfuscated);
    }
    else
    {
        hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", sczExecutablePath);
        ExitOnFailure(hr, "Failed to create executable command.");

        hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", sczExecutablePath);
    }
    ExitOnFailure(hr, "Failed to create obfuscated executable command.");

    if (BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol)
    {
        // Add the list of dependencies to ignore, if any, to the burn command line.
        if (pExecuteAction->exePackage.sczIgnoreDependencies && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol)
        {
            hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies);
            ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line.");

            hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies);
            ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the obfuscated command line.");
        }

        // Add the list of ancestors, if any, to the burn command line.
        if (pExecuteAction->exePackage.sczAncestors)
        {
            hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors);
            ExitOnFailure(hr, "Failed to append the list of ancestors to the command line.");

            hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors);
            ExitOnFailure(hr, "Failed to append the list of ancestors to the obfuscated command line.");
        }
    }

    // Log before we add the secret pipe name and client token for embedded processes.
    LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->exePackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), sczExecutablePath, sczCommandObfuscated);

    if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol)
    {
        hr = EmbeddedRunBundle(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode);
        ExitOnFailure1(hr, "Failed to run bundle as embedded from path: %ls", sczExecutablePath);
    }
    else if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_NETFX4 == pExecuteAction->exePackage.pPackage->Exe.protocol)
    {
        hr = NetFxRunChainer(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode);
        ExitOnFailure1(hr, "Failed to run netfx chainer: %ls", sczExecutablePath);
    }
    else // create and wait for the executable process while sending fake progress to allow cancel.
    {
        // Make the cache location of the executable the current directory to help those executables
        // that expect stuff to be relative to them.
        if (::GetCurrentDirectoryW(countof(wzCurrentDirectory), wzCurrentDirectory))
        {
            fChangedCurrentDirectory = ::SetCurrentDirectoryW(sczCachedDirectory);
        }

        si.cb = sizeof(si); // TODO: hookup the stdin/stdout/stderr pipes for logging purposes?
        if (!::CreateProcessW(sczExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
        {
            ExitWithLastError1(hr, "Failed to CreateProcess on path: %ls", sczExecutablePath);
        }

        if (pExecuteAction->exePackage.fFireAndForget)
        {
            ::WaitForInputIdle(pi.hProcess, 5000);
            ExitFunction();
        }

        do
        {
            message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
            message.dwAllowedResults = MB_OKCANCEL;
            message.progress.dwPercentage = 50;
            nResult = pfnGenericMessageHandler(&message, pvContext);
            hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
            ExitOnRootFailure(hr, "Bootstrapper application aborted during EXE progress.");

            hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode);
            if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr)
            {
                ExitOnFailure1(hr, "Failed to wait for executable to complete: %ls", sczExecutablePath);
            }
        } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr);
    }

    hr = HandleExitCode(pExecuteAction->exePackage.pPackage, dwExitCode, pRestart);
    ExitOnRootFailure1(hr, "Process returned error: 0x%x", dwExitCode);

LExit:
    if (fChangedCurrentDirectory)
    {
        ::SetCurrentDirectoryW(wzCurrentDirectory);
    }

    StrSecureZeroFreeString(sczArgumentsFormatted);
    ReleaseStr(sczArgumentsObfuscated);
    ReleaseStr(sczCachedDirectory);
    ReleaseStr(sczExecutablePath);
    StrSecureZeroFreeString(sczCommand);
    ReleaseStr(sczCommandObfuscated);

    ReleaseHandle(pi.hThread);
    ReleaseHandle(pi.hProcess);

    // Best effort to clear the execute package cache folder variable.
    VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE);

    return hr;
}
Ejemplo n.º 3
0
extern "C" HRESULT MspEngineExecutePackage(
    __in_opt HWND hwndParent,
    __in BURN_EXECUTE_ACTION* pExecuteAction,
    __in BURN_VARIABLES* pVariables,
    __in BOOL fRollback,
    __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
    __in LPVOID pvContext,
    __out BOOTSTRAPPER_APPLY_RESTART* pRestart
    )
{
    HRESULT hr = S_OK;
    INSTALLUILEVEL uiLevel = pExecuteAction->mspTarget.pPackage->Msp.fDisplayInternalUI ? INSTALLUILEVEL_DEFAULT : static_cast<INSTALLUILEVEL>(INSTALLUILEVEL_NONE | INSTALLUILEVEL_SOURCERESONLY);
    WIU_MSI_EXECUTE_CONTEXT context = { };
    WIU_RESTART restart = WIU_RESTART_NONE;

    LPWSTR sczCachedDirectory = NULL;
    LPWSTR sczMspPath = NULL;
    LPWSTR sczPatches = NULL;
    LPWSTR sczProperties = NULL;
    LPWSTR sczObfuscatedProperties = NULL;

    // default to "verbose" logging
    DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE;

    // get cached MSP paths
    for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i)
    {
        LPCWSTR wzAppend = NULL;
        BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage;
        AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Invalid package type added to ordered patches.");

        if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->mspTarget.action)
        {
            hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory);
            ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId);

            // Best effort to set the execute package cache folder variable.
            VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE);

            hr = PathConcat(sczCachedDirectory, pMspPackage->rgPayloads[0].pPayload->sczFilePath, &sczMspPath);
            ExitOnFailure(hr, "Failed to build MSP path.");

            wzAppend = sczMspPath;
        }
        else // uninstall
        {
            wzAppend = pMspPackage->Msp.sczPatchCode;
        }

        if (NULL != sczPatches)
        {
            hr = StrAllocConcat(&sczPatches, L";", 0);
            ExitOnFailure(hr, "Failed to semi-colon delimit patches.");
        }

        hr = StrAllocConcat(&sczPatches, wzAppend, 0);
        ExitOnFailure(hr, "Failed to append patch.");
    }

    // Wire up the external UI handler and logging.
    hr = WiuInitializeExternalUI(pfnMessageHandler, uiLevel, hwndParent, pvContext, fRollback, &context);
    ExitOnFailure(hr, "Failed to initialize external UI handler.");

    //if (BURN_LOGGING_LEVEL_DEBUG == logLevel)
    //{
    //    dwLogMode | INSTALLLOGMODE_EXTRADEBUG;
    //}

    if (pExecuteAction->mspTarget.sczLogPath && *pExecuteAction->mspTarget.sczLogPath)
    {
        hr = WiuEnableLog(dwLogMode, pExecuteAction->mspTarget.sczLogPath, 0);
        ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pExecuteAction->mspTarget.pPackage->sczId, pExecuteAction->mspTarget.sczLogPath);
    }

    // set up properties
    hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczProperties, FALSE);
    ExitOnFailure(hr, "Failed to add properties to argument string.");

    hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE);
    ExitOnFailure(hr, "Failed to add properties to obfuscated argument string.");

    LogId(REPORT_STANDARD, MSG_APPLYING_PATCH_PACKAGE, pExecuteAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pExecuteAction->mspTarget.action), sczPatches, sczObfuscatedProperties, pExecuteAction->mspTarget.sczTargetProductCode);

    //
    // Do the actual action.
    //
    switch (pExecuteAction->mspTarget.action)
    {
    case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough;
    case BOOTSTRAPPER_ACTION_STATE_REPAIR:
        hr = StrAllocConcatSecure(&sczProperties, L" PATCH=\"", 0);
        ExitOnFailure(hr, "Failed to add PATCH property on install.");

        hr = StrAllocConcatSecure(&sczProperties, sczPatches, 0);
        ExitOnFailure(hr, "Failed to add patches to PATCH property on install.");

        hr = StrAllocConcatSecure(&sczProperties, L"\" REBOOT=ReallySuppress", 0);
        ExitOnFailure(hr, "Failed to add reboot suppression property on install.");

        hr = WiuConfigureProductEx(pExecuteAction->mspTarget.sczTargetProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, sczProperties, &restart);
        ExitOnFailure(hr, "Failed to install MSP package.");
        break;

    case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
        hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0);
        ExitOnFailure(hr, "Failed to add reboot suppression property on uninstall.");

        // Ignore all dependencies, since the Burn engine already performed the check.
        hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES);
        ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties.");

        hr = WiuRemovePatches(sczPatches, pExecuteAction->mspTarget.sczTargetProductCode, sczProperties, &restart);
        ExitOnFailure(hr, "Failed to uninstall MSP package.");
        break;
    }

LExit:
    WiuUninitializeExternalUI(&context);

    ReleaseStr(sczCachedDirectory);
    ReleaseStr(sczMspPath);
    StrSecureZeroFreeString(sczProperties);
    ReleaseStr(sczObfuscatedProperties);
    ReleaseStr(sczPatches);

    switch (restart)
    {
        case WIU_RESTART_NONE:
            *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
            break;

        case WIU_RESTART_REQUIRED:
            *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
            break;

        case WIU_RESTART_INITIATED:
            *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
            break;
    }

    // Best effort to clear the execute package cache folder variable.
    VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE);

    return hr;
}
Ejemplo n.º 4
0
static HRESULT RunNormal(
    __in HINSTANCE hInstance,
    __in BURN_ENGINE_STATE* pEngineState
    )
{
    HRESULT hr = S_OK;
    HANDLE hPipesCreatedEvent = NULL;
    BOOL fContinueExecution = TRUE;
    BOOL fReloadApp = FALSE;

    // Initialize logging.
    hr = LoggingOpen(&pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->registration.sczDisplayName);
    ExitOnFailure(hr, "Failed to open log.");

    // Ensure the cache functions are initialized since we might use them soon.
    hr = CacheInitialize(&pEngineState->registration, &pEngineState->variables);
    ExitOnFailure(hr, "Failed to initialize internal cache functionality.");

    // When launched explicitly unelevated, create the pipes so the elevated process can connect.
    if (BURN_ELEVATION_STATE_UNELEVATED_EXPLICITLY == pEngineState->elevationState)
    {
        Assert(pEngineState->companionConnection.dwProcessId);
        Assert(pEngineState->companionConnection.sczName);
        Assert(pEngineState->companionConnection.sczSecret);
        Assert(!pEngineState->companionConnection.hProcess);
        Assert(INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe);
        Assert(INVALID_HANDLE_VALUE == pEngineState->companionConnection.hCachePipe);

        hr = PipeCreatePipes(&pEngineState->companionConnection, TRUE, &hPipesCreatedEvent);
        ExitOnFailure(hr, "Failed to create pipes to connect to elevated parent process.");

        hr = PipeWaitForChildConnect(&pEngineState->companionConnection);
        ExitOnFailure(hr, "Failed to connect to elevated parent process.");

        ReleaseHandle(hPipesCreatedEvent);
    }

    // Ensure we're on a supported operating system.
    hr = ConditionGlobalCheck(&pEngineState->variables, &pEngineState->condition, pEngineState->command.display, pEngineState->registration.sczDisplayName, &pEngineState->userExperience.dwExitCode, &fContinueExecution);
    ExitOnFailure(hr, "Failed to check global conditions");

    if (!fContinueExecution)
    {
        LogId(REPORT_STANDARD, MSG_FAILED_CONDITION_CHECK);

        // If the block told us to abort, abort!
        ExitFunction1(hr = S_OK);
    }

    if (pEngineState->userExperience.fSplashScreen && BOOTSTRAPPER_DISPLAY_NONE < pEngineState->command.display)
    {
        SplashScreenCreate(hInstance, NULL, &pEngineState->command.hwndSplashScreen);
    }

    // Create a top-level window to handle system messages.
    hr = UiCreateMessageWindow(hInstance, pEngineState);
    ExitOnFailure(hr, "Failed to create the message window.");

    // Query registration state.
    hr = CoreQueryRegistration(pEngineState);
    ExitOnFailure(hr, "Failed to query registration.");

    // Set some built-in variables before loading the BA.
    hr = PlanSetVariables(pEngineState->command.action, &pEngineState->variables);
    ExitOnFailure(hr, "Failed to set action variables.");

    hr = RegistrationSetVariables(&pEngineState->registration, &pEngineState->variables);
    ExitOnFailure(hr, "Failed to set registration variables.");

    // If a layout directory was specified on the command-line, set it as a well-known variable.
    if (pEngineState->command.wzLayoutDirectory && *pEngineState->command.wzLayoutDirectory)
    {
        hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_LAYOUT_DIRECTORY, pEngineState->command.wzLayoutDirectory, FALSE);
        ExitOnFailure(hr, "Failed to set layout directory variable to value provided from command-line.");
    }

    do
    {
        fReloadApp = FALSE;

        hr = RunApplication(pEngineState, &fReloadApp);
        ExitOnFailure(hr, "Failed while running ");
    } while (fReloadApp);

LExit:
    // If the message window is still around, close it.
    UiCloseMessageWindow(pEngineState);

    VariablesDump(&pEngineState->variables);

    // end per-machine process if running
    if (INVALID_HANDLE_VALUE != pEngineState->companionConnection.hPipe)
    {
        PipeTerminateChildProcess(&pEngineState->companionConnection, pEngineState->userExperience.dwExitCode, (BURN_ELEVATION_STATE_UNELEVATED_EXPLICITLY == pEngineState->elevationState) ? pEngineState->fRestart : FALSE);
    }

    // If the splash screen is still around, close it.
    if (::IsWindow(pEngineState->command.hwndSplashScreen))
    {
        ::PostMessageW(pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0);
    }

    ReleaseHandle(hPipesCreatedEvent);

    return hr;
}
Ejemplo n.º 5
0
extern "C" HRESULT MsuEngineExecutePackage(
    __in BURN_EXECUTE_ACTION* pExecuteAction,
    __in BURN_VARIABLES* pVariables,
    __in BOOL fRollback,
    __in BOOL fStopWusaService,
    __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
    __in LPVOID pvContext,
    __out BOOTSTRAPPER_APPLY_RESTART* pRestart
    )
{
    HRESULT hr = S_OK;
    int nResult = IDNOACTION;
    LPWSTR sczCachedDirectory = NULL;
    LPWSTR sczMsuPath = NULL;
    LPWSTR sczWindowsPath = NULL;
    LPWSTR sczSystemPath = NULL;
    LPWSTR sczWusaPath = NULL;
    LPWSTR sczCommand = NULL;
    SC_HANDLE schWu = NULL;
    BOOL fWuWasDisabled = FALSE;
    STARTUPINFOW si = { };
    PROCESS_INFORMATION pi = { };
    GENERIC_EXECUTE_MESSAGE message = { };
    DWORD dwExitCode = 0;
    BOOL fUseSysNativePath = FALSE;

#if !defined(_WIN64)
    hr = ProcWow64(::GetCurrentProcess(), &fUseSysNativePath);
    ExitOnFailure(hr, "Failed to determine WOW64 status.");
#endif

    *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;

    // get wusa.exe path
    if (fUseSysNativePath)
    {
        hr = PathGetKnownFolder(CSIDL_WINDOWS, &sczWindowsPath);
        ExitOnFailure(hr, "Failed to find Windows directory.");

        hr = PathConcat(sczWindowsPath, L"SysNative\\", &sczSystemPath);
        ExitOnFailure(hr, "Failed to append SysNative directory.");
    }
    else
    {
        hr = PathGetKnownFolder(CSIDL_SYSTEM, &sczSystemPath);
        ExitOnFailure(hr, "Failed to find System32 directory.");
    }

    hr = PathConcat(sczSystemPath, L"wusa.exe", &sczWusaPath);
    ExitOnFailure(hr, "Failed to allocate WUSA.exe path.");

    // build command
    switch (pExecuteAction->msuPackage.action)
    {
    case BOOTSTRAPPER_ACTION_STATE_INSTALL:
        // get cached MSU path
        hr = CacheGetCompletedPath(TRUE, pExecuteAction->msuPackage.pPackage->sczCacheId, &sczCachedDirectory);
        ExitOnFailure1(hr, "Failed to get cached path for package: %ls", pExecuteAction->msuPackage.pPackage->sczId);

        // Best effort to set the execute package cache folder variable.
        VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE);

        hr = PathConcat(sczCachedDirectory, pExecuteAction->msuPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsuPath);
        ExitOnFailure(hr, "Failed to build MSU path.");

        // format command
        hr = StrAllocFormatted(&sczCommand, L"\"%ls\" \"%ls\" /quiet /norestart", sczWusaPath, sczMsuPath);
        ExitOnFailure(hr, "Failed to format MSU install command.");
        break;

    case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
        // format command
        hr = StrAllocFormatted(&sczCommand, L"\"%ls\" /uninstall /kb:%ls /quiet /norestart", sczWusaPath, pExecuteAction->msuPackage.pPackage->Msu.sczKB);
        ExitOnFailure(hr, "Failed to format MSU uninstall command.");
        break;

    default:
        hr = E_UNEXPECTED;
        ExitOnFailure(hr, "Failed to get action arguments for MSU package.");
    }

    if (pExecuteAction->msuPackage.sczLogPath && *pExecuteAction->msuPackage.sczLogPath)
    {
        hr = StrAllocConcat(&sczCommand, L" /log:", 0);
        ExitOnFailure(hr, "Failed to append log switch to MSU command-line.");

        hr = StrAllocConcat(&sczCommand, pExecuteAction->msuPackage.sczLogPath, 0);
        ExitOnFailure(hr, "Failed to append log path to MSU command-line.");
    }

    LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msuPackage.action), sczMsuPath ? sczMsuPath : pExecuteAction->msuPackage.pPackage->Msu.sczKB, sczCommand);

    hr = EnsureWUServiceEnabled(fStopWusaService, &schWu, &fWuWasDisabled);
    ExitOnFailure(hr, "Failed to ensure WU service was enabled to install MSU package.");

    // create process
    si.cb = sizeof(si);
    if (!::CreateProcessW(sczWusaPath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
    {
        ExitWithLastError1(hr, "Failed to CreateProcess on path: %ls", sczWusaPath);
    }

    do
    {
        message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
        message.dwAllowedResults = MB_OKCANCEL;
        message.progress.dwPercentage = 50;
        nResult = pfnGenericMessageHandler(&message, pvContext);
        hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
        ExitOnRootFailure(hr, "Bootstrapper application aborted during MSU progress.");

        // wait for process to terminate
        hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode);
        if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr)
        {
            ExitOnFailure1(hr, "Failed to wait for executable to complete: %ls", sczWusaPath);
        }
    } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr);

    // get process exit code
    if (!::GetExitCodeProcess(pi.hProcess, &dwExitCode))
    {
        ExitWithLastError(hr, "Failed to get process exit code.");
    }

    // We'll normalize the restart required error code from wusa.exe just in case. Most likely
    // that on reboot we'll actually get WU_S_REBOOT_REQUIRED.
    if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast<HRESULT>(dwExitCode))
    {
        dwExitCode = ERROR_SUCCESS_REBOOT_REQUIRED;
    }

    // handle exit code
    switch (dwExitCode)
    {
    case S_OK: __fallthrough;
    case S_FALSE: __fallthrough;
    case WU_S_ALREADY_INSTALLED:
        hr = S_OK;
        break;

    case ERROR_SUCCESS_REBOOT_REQUIRED: __fallthrough;
    case WU_S_REBOOT_REQUIRED:
        *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
        hr = S_OK;
        break;

    default:
        hr = static_cast<HRESULT>(dwExitCode);
        break;
    }

LExit:
    ReleaseStr(sczCachedDirectory);
    ReleaseStr(sczMsuPath);
    ReleaseStr(sczSystemPath);
    ReleaseStr(sczWindowsPath);
    ReleaseStr(sczWusaPath);
    ReleaseStr(sczCommand);

    ReleaseHandle(pi.hProcess);
    ReleaseHandle(pi.hThread);

    if (fWuWasDisabled)
    {
        SetServiceStartType(schWu, SERVICE_DISABLED);
    }

    // Best effort to clear the execute package cache folder variable.
    VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE);

    return hr;
}
Ejemplo n.º 6
0
extern "C" HRESULT LoggingOpen(
    __in BURN_LOGGING* pLog,
    __in BURN_VARIABLES* pVariables,
    __in BOOTSTRAPPER_DISPLAY display,
    __in_z LPCWSTR wzBundleName
    )
{
    HRESULT hr = S_OK;
    LPWSTR sczLoggingBaseFolder = NULL;

    // Check if the logging policy is set and configure the logging appropriately.
    CheckLoggingPolicy(&pLog->dwAttributes);

    if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_VERBOSE || pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG)
    {
        if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG)
        {
            LogSetLevel(REPORT_DEBUG, FALSE);
        }
        else if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_VERBOSE)
        {
            LogSetLevel(REPORT_VERBOSE, FALSE);
        }

        if ((!pLog->sczPath || !*pLog->sczPath) && (!pLog->sczPrefix || !*pLog->sczPrefix))
        {
            PathCreateTimeBasedTempFile(NULL, L"Setup", NULL, L"log", &pLog->sczPath, NULL);
        }
    }

    // Open the log approriately.
    if (pLog->sczPath && *pLog->sczPath)
    {
        DWORD cRetry = 0;

        hr = DirGetCurrent(&sczLoggingBaseFolder);
        ExitOnFailure(hr, "Failed to get current directory.");

        // Try pretty hard to open the log file when appending.
        do
        {
            if (0 < cRetry)
            {
                ::Sleep(LOG_OPEN_RETRY_WAIT);
            }

            hr = LogOpen(sczLoggingBaseFolder, pLog->sczPath, NULL, NULL, pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND, FALSE, &pLog->sczPath);
            if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND && HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) == hr)
            {
                ++cRetry;
            }
        } while (cRetry > 0 && cRetry <= LOG_OPEN_RETRY_COUNT);

        if (FAILED(hr))
        {
            // Log is not open, so note that.
            LogDisable();
            pLog->state = BURN_LOGGING_STATE_DISABLED;

            if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND)
            {
                // If appending, ignore the failure and continue.
                hr = S_OK;
            }
            else // specifically tried to create a log file so show an error if appropriate and bail.
            {
                HRESULT hrOriginal = hr;

                hr = HRESULT_FROM_WIN32(ERROR_INSTALL_LOG_FAILURE);
                SplashScreenDisplayError(display, wzBundleName, hr);

                ExitOnFailure1(hrOriginal, "Failed to open log: %ls", pLog->sczPath);
            }
        }
        else
        {
            pLog->state = BURN_LOGGING_STATE_OPEN;
        }
    }
    else if (pLog->sczPrefix && *pLog->sczPrefix)
    {
        hr = GetNonSessionSpecificTempFolder(&sczLoggingBaseFolder);
        ExitOnFailure(hr, "Failed to get non-session specific TEMP folder.");

        // Best effort to open default logging.
        hr = LogOpen(sczLoggingBaseFolder, pLog->sczPrefix, NULL, pLog->sczExtension, FALSE, FALSE, &pLog->sczPath);
        if (FAILED(hr))
        {
            LogDisable();
            pLog->state = BURN_LOGGING_STATE_DISABLED;

            hr = S_OK;
        }
        else
        {
            pLog->state = BURN_LOGGING_STATE_OPEN;
        }
    }
    else // no logging enabled.
    {
        LogDisable();
        pLog->state = BURN_LOGGING_STATE_DISABLED;
    }

    // If the log was opened, write the header info and update the prefix and extension to match
    // the log name so future logs are opened with the same pattern.
    if (BURN_LOGGING_STATE_OPEN == pLog->state)
    {
        LPCWSTR wzExtension = PathExtension(pLog->sczPath);
        if (wzExtension && *wzExtension)
        {
            hr = StrAllocString(&pLog->sczPrefix, pLog->sczPath, wzExtension - pLog->sczPath);
            ExitOnFailure(hr, "Failed to copy log path to prefix.");

            hr = StrAllocString(&pLog->sczExtension, wzExtension + 1, 0);
            ExitOnFailure(hr, "Failed to copy log extension to extension.");
        }
        else
        {
            hr = StrAllocString(&pLog->sczPrefix, pLog->sczPath, 0);
            ExitOnFailure(hr, "Failed to copy full log path to prefix.");
        }

        if (pLog->sczPathVariable && *pLog->sczPathVariable)
        {
            VariableSetString(pVariables, pLog->sczPathVariable, pLog->sczPath, FALSE); // Ignore failure.
        }
    }

LExit:
    ReleaseStr(sczLoggingBaseFolder);

    return hr;
}