aboutsummaryrefslogtreecommitdiff
path: root/src/ext/Util/ca/RestartManager.cpp
blob: 96390152e5b80f2697a893491ac9924358714fe6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.

#include "precomp.h"
#include <restartmanager.h>

// Include space for the terminating null.
#define CCH_SESSION_KEY CCH_RM_SESSION_KEY + 1

enum eRmuResourceType
{
    etInvalid,
    etFilename,
    etApplication,
    etServiceName,

    // Mask types from Attributes.
    etTypeMask = 0xf,
};

LPCWSTR vcsRestartResourceQuery =
    L"SELECT `Wix4RestartResource`.`RestartResource`, `Wix4RestartResource`.`Component_`, `Wix4RestartResource`.`Resource`, `Wix4RestartResource`.`Attributes` "
    L"FROM `Wix4RestartResource`";
enum eRestartResourceQuery { rrqRestartResource = 1, rrqComponent, rrqResource, rrqAttributes };

/********************************************************************
WixRegisterRestartResources - Immediate CA to register resources with RM.

Enumerates components before InstallValidate and registers resources
to be restarted by Restart Manager if the component action
is anything other than None.

Do not disable file system redirection.

********************************************************************/
extern "C" UINT __stdcall WixRegisterRestartResources(
    __in MSIHANDLE hInstall
    )
{
    HRESULT hr = S_OK;
    UINT er = ERROR_SUCCESS;

    PMSIHANDLE hView = NULL;
    PMSIHANDLE hRec = NULL;

    LPWSTR wzSessionKey = NULL;
    size_t cchSessionKey = 0;
    PRMU_SESSION pSession = NULL;

    LPWSTR wzRestartResource = NULL;
    LPWSTR wzComponent = NULL;
    LPWSTR wzResource = NULL;
    int iAttributes = NULL;
    BOOL fIsComponentNull = FALSE;
    WCA_TODO todo = WCA_TODO_UNKNOWN;
    int iType = etInvalid;

    hr = WcaInitialize(hInstall, "WixRegisterRestartResources");
    ExitOnFailure(hr, "Failed to initialize.");

    // Skip if the table doesn't exist.
    if (S_OK != WcaTableExists(L"Wix4RestartResource"))
    {
        WcaLog(LOGMSG_STANDARD, "The Wix4RestartResource table does not exist; there are no resources to register with Restart Manager.");
        ExitFunction();
    }

    // Get the existing Restart Manager session if available.
    hr = WcaGetProperty(L"MsiRestartManagerSessionKey", &wzSessionKey);
    ExitOnFailure(hr, "Failed to get the MsiRestartManagerSessionKey property.");

    hr = ::StringCchLengthW(wzSessionKey, CCH_SESSION_KEY, &cchSessionKey);
    ExitOnFailure(hr, "Failed to get the MsiRestartManagerSessionKey string length.");

    // Skip if the property doesn't exist.
    if (0 == cchSessionKey)
    {
        WcaLog(LOGMSG_STANDARD, "The MsiRestartManagerSessionKey property is not available to join.");
        ExitFunction();
    }

    // Join the existing Restart Manager session if supported.
    hr = RmuJoinSession(&pSession, wzSessionKey);
    if (E_MODNOTFOUND == hr)
    {
        WcaLog(LOGMSG_STANDARD, "The Restart Manager is not supported on this platform. Skipping.");
        ExitFunction1(hr = S_OK);
    }
    else if (FAILED(hr))
    {
        WcaLog(LOGMSG_STANDARD, "Failed to join the existing Restart Manager session %ls.", wzSessionKey);
        ExitFunction1(hr = S_OK);
    }

    // Loop through each record in the table.
    hr = WcaOpenExecuteView(vcsRestartResourceQuery, &hView);
    ExitOnFailure(hr, "Failed to open a view on the RestartResource table.");

    while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
    {
        hr = WcaGetRecordString(hRec, rrqRestartResource, &wzRestartResource);
        ExitOnFailure(hr, "Failed to get the RestartResource field value.");

        hr = WcaGetRecordString(hRec, rrqComponent, &wzComponent);
        ExitOnFailure(hr, "Failed to get the Component_ field value.");

        hr = WcaGetRecordFormattedString(hRec, rrqResource, &wzResource);
        ExitOnFailure(hr, "Failed to get the Resource formatted field value.");

        hr = WcaGetRecordInteger(hRec, rrqAttributes, &iAttributes);
        ExitOnFailure(hr, "Failed to get the Attributes field value.");

        fIsComponentNull = ::MsiRecordIsNull(hRec, rrqComponent);
        todo = WcaGetComponentToDo(wzComponent);

        // Only register resources for components that are null, or being installed, reinstalled, or uninstalled.
        if (!fIsComponentNull && WCA_TODO_UNKNOWN == todo)
        {
            WcaLog(LOGMSG_VERBOSE, "Skipping resource %ls.", wzRestartResource);
            continue;
        }

        // Get the type from Attributes and add to the Restart Manager.
        iType = iAttributes & etTypeMask;
        switch (iType)
        {
        case etFilename:
            WcaLog(LOGMSG_VERBOSE, "Registering file name %ls with the Restart Manager.", wzResource);
            hr = RmuAddFile(pSession, wzResource);
            ExitOnFailure(hr, "Failed to register the file name with the Restart Manager session.");
            break;

        case etApplication:
            WcaLog(LOGMSG_VERBOSE, "Registering process name %ls with the Restart Manager.", wzResource);
            hr = RmuAddProcessesByName(pSession, wzResource);
            if (E_NOTFOUND == hr)
            {
                // ERROR_ACCESS_DENIED was returned when trying to register this process.
                // Since other instances may have been registered, log a message and continue the setup rather than failing.
                WcaLog(LOGMSG_STANDARD, "The process, %ls, could not be registered with the Restart Manager (probably because the setup is not elevated and the process is in another user context). A reboot may be requested later.", wzResource);
                hr = S_OK;
            }
            else
            {
                ExitOnFailure(hr, "Failed to register the process name with the Restart Manager session.");
            }
            break;

        case etServiceName:
            WcaLog(LOGMSG_VERBOSE, "Registering service name %ls with the Restart Manager.", wzResource);
            hr = RmuAddService(pSession, wzResource);
            ExitOnFailure(hr, "Failed to register the service name with the Restart Manager session.");
            break;

        default:
            WcaLog(LOGMSG_VERBOSE, "The resource type %d for %ls is not supported and will not be registered.", iType, wzRestartResource);
            break;
        }
    }

    if (E_NOMOREITEMS == hr)
    {
        hr = S_OK;
    }
    ExitOnFailure(hr, "Failed while looping through all rows to register resources.");

    // Register the resources and unjoin the session.
    hr = RmuEndSession(pSession);
    if (FAILED(hr))
    {
        WcaLog(LOGMSG_VERBOSE, "Failed to register the resources with the Restart Manager.");
        ExitFunction1(hr = S_OK);
    }

LExit:
    ReleaseStr(wzRestartResource);
    ReleaseStr(wzComponent);
    ReleaseStr(wzResource);

    if (FAILED(hr))
    {
        er = ERROR_INSTALL_FAILURE;
    }

    return WcaFinalize(er);
}