| 1 | // 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. |
| 2 | |
| 3 | #include "precomp.h" |
| 4 | #include <restartmanager.h> |
| 5 | |
| 6 | // Include space for the terminating null. |
| 7 | #define CCH_SESSION_KEY CCH_RM_SESSION_KEY + 1 |
| 8 | |
| 9 | enum eRmuResourceType |
| 10 | { |
| 11 | etInvalid, |
| 12 | etFilename, |
| 13 | etApplication, |
| 14 | etServiceName, |
| 15 | |
| 16 | // Mask types from Attributes. |
| 17 | etTypeMask = 0xf, |
| 18 | }; |
| 19 | |
| 20 | LPCWSTR vcsRestartResourceQuery = |
| 21 | L"SELECT `Wix4RestartResource`.`RestartResource`, `Wix4RestartResource`.`Component_`, `Wix4RestartResource`.`Resource`, `Wix4RestartResource`.`Attributes` " |
| 22 | L"FROM `Wix4RestartResource`"; |
| 23 | enum eRestartResourceQuery { rrqRestartResource = 1, rrqComponent, rrqResource, rrqAttributes }; |
| 24 | |
| 25 | /******************************************************************** |
| 26 | WixRegisterRestartResources - Immediate CA to register resources with RM. |
| 27 | |
| 28 | Enumerates components before InstallValidate and registers resources |
| 29 | to be restarted by Restart Manager if the component action |
| 30 | is anything other than None. |
| 31 | |
| 32 | Do not disable file system redirection. |
| 33 | |
| 34 | ********************************************************************/ |
| 35 | extern "C" UINT __stdcall WixRegisterRestartResources( |
| 36 | __in MSIHANDLE hInstall |
| 37 | ) |
| 38 | { |
| 39 | HRESULT hr = S_OK; |
| 40 | UINT er = ERROR_SUCCESS; |
| 41 | |
| 42 | PMSIHANDLE hView = NULL; |
| 43 | PMSIHANDLE hRec = NULL; |
| 44 | |
| 45 | LPWSTR wzSessionKey = NULL; |
| 46 | size_t cchSessionKey = 0; |
| 47 | PRMU_SESSION pSession = NULL; |
| 48 | |
| 49 | LPWSTR wzRestartResource = NULL; |
| 50 | LPWSTR wzComponent = NULL; |
| 51 | LPWSTR wzResource = NULL; |
| 52 | int iAttributes = NULL; |
| 53 | BOOL fIsComponentNull = FALSE; |
| 54 | WCA_TODO todo = WCA_TODO_UNKNOWN; |
| 55 | int iType = etInvalid; |
| 56 | |
| 57 | hr = WcaInitialize(hInstall, "WixRegisterRestartResources"); |
| 58 | ExitOnFailure(hr, "Failed to initialize."); |
| 59 | |
| 60 | // Skip if the table doesn't exist. |
| 61 | if (S_OK != WcaTableExists(L"Wix4RestartResource")) |
| 62 | { |
| 63 | WcaLog(LOGMSG_STANDARD, "The Wix4RestartResource table does not exist; there are no resources to register with Restart Manager."); |
| 64 | ExitFunction(); |
| 65 | } |
| 66 | |
| 67 | // Get the existing Restart Manager session if available. |
| 68 | hr = WcaGetProperty(L"MsiRestartManagerSessionKey", &wzSessionKey); |
| 69 | ExitOnFailure(hr, "Failed to get the MsiRestartManagerSessionKey property."); |
| 70 | |
| 71 | hr = ::StringCchLengthW(wzSessionKey, CCH_SESSION_KEY, &cchSessionKey); |
| 72 | ExitOnFailure(hr, "Failed to get the MsiRestartManagerSessionKey string length."); |
| 73 | |
| 74 | // Skip if the property doesn't exist. |
| 75 | if (0 == cchSessionKey) |
| 76 | { |
| 77 | WcaLog(LOGMSG_STANDARD, "The MsiRestartManagerSessionKey property is not available to join."); |
| 78 | ExitFunction(); |
| 79 | } |
| 80 | |
| 81 | // Join the existing Restart Manager session if supported. |
| 82 | hr = RmuJoinSession(&pSession, wzSessionKey); |
| 83 | if (E_MODNOTFOUND == hr) |
| 84 | { |
| 85 | WcaLog(LOGMSG_STANDARD, "The Restart Manager is not supported on this platform. Skipping."); |
| 86 | ExitFunction1(hr = S_OK); |
| 87 | } |
| 88 | else if (FAILED(hr)) |
| 89 | { |
| 90 | WcaLog(LOGMSG_STANDARD, "Failed to join the existing Restart Manager session %ls.", wzSessionKey); |
| 91 | ExitFunction1(hr = S_OK); |
| 92 | } |
| 93 | |
| 94 | // Loop through each record in the table. |
| 95 | hr = WcaOpenExecuteView(vcsRestartResourceQuery, &hView); |
| 96 | ExitOnFailure(hr, "Failed to open a view on the RestartResource table."); |
| 97 | |
| 98 | while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) |
| 99 | { |
| 100 | hr = WcaGetRecordString(hRec, rrqRestartResource, &wzRestartResource); |
| 101 | ExitOnFailure(hr, "Failed to get the RestartResource field value."); |
| 102 | |
| 103 | hr = WcaGetRecordString(hRec, rrqComponent, &wzComponent); |
| 104 | ExitOnFailure(hr, "Failed to get the Component_ field value."); |
| 105 | |
| 106 | hr = WcaGetRecordFormattedString(hRec, rrqResource, &wzResource); |
| 107 | ExitOnFailure(hr, "Failed to get the Resource formatted field value."); |
| 108 | |
| 109 | hr = WcaGetRecordInteger(hRec, rrqAttributes, &iAttributes); |
| 110 | ExitOnFailure(hr, "Failed to get the Attributes field value."); |
| 111 | |
| 112 | fIsComponentNull = ::MsiRecordIsNull(hRec, rrqComponent); |
| 113 | todo = WcaGetComponentToDo(wzComponent); |
| 114 | |
| 115 | // Only register resources for components that are null, or being installed, reinstalled, or uninstalled. |
| 116 | if (!fIsComponentNull && WCA_TODO_UNKNOWN == todo) |
| 117 | { |
| 118 | WcaLog(LOGMSG_VERBOSE, "Skipping resource %ls.", wzRestartResource); |
| 119 | continue; |
| 120 | } |
| 121 | |
| 122 | // Get the type from Attributes and add to the Restart Manager. |
| 123 | iType = iAttributes & etTypeMask; |
| 124 | switch (iType) |
| 125 | { |
| 126 | case etFilename: |
| 127 | WcaLog(LOGMSG_VERBOSE, "Registering file name %ls with the Restart Manager.", wzResource); |
| 128 | hr = RmuAddFile(pSession, wzResource); |
| 129 | ExitOnFailure(hr, "Failed to register the file name with the Restart Manager session."); |
| 130 | break; |
| 131 | |
| 132 | case etApplication: |
| 133 | WcaLog(LOGMSG_VERBOSE, "Registering process name %ls with the Restart Manager.", wzResource); |
| 134 | hr = RmuAddProcessesByName(pSession, wzResource); |
| 135 | if (E_NOTFOUND == hr) |
| 136 | { |
| 137 | // ERROR_ACCESS_DENIED was returned when trying to register this process. |
| 138 | // Since other instances may have been registered, log a message and continue the setup rather than failing. |
| 139 | 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); |
| 140 | hr = S_OK; |
| 141 | } |
| 142 | else |
| 143 | { |
| 144 | ExitOnFailure(hr, "Failed to register the process name with the Restart Manager session."); |
| 145 | } |
| 146 | break; |
| 147 | |
| 148 | case etServiceName: |
| 149 | WcaLog(LOGMSG_VERBOSE, "Registering service name %ls with the Restart Manager.", wzResource); |
| 150 | hr = RmuAddService(pSession, wzResource); |
| 151 | ExitOnFailure(hr, "Failed to register the service name with the Restart Manager session."); |
| 152 | break; |
| 153 | |
| 154 | default: |
| 155 | WcaLog(LOGMSG_VERBOSE, "The resource type %d for %ls is not supported and will not be registered.", iType, wzRestartResource); |
| 156 | break; |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | if (E_NOMOREITEMS == hr) |
| 161 | { |
| 162 | hr = S_OK; |
| 163 | } |
| 164 | ExitOnFailure(hr, "Failed while looping through all rows to register resources."); |
| 165 | |
| 166 | // Register the resources and unjoin the session. |
| 167 | hr = RmuEndSession(pSession); |
| 168 | if (FAILED(hr)) |
| 169 | { |
| 170 | WcaLog(LOGMSG_VERBOSE, "Failed to register the resources with the Restart Manager."); |
| 171 | ExitFunction1(hr = S_OK); |
| 172 | } |
| 173 | |
| 174 | LExit: |
| 175 | ReleaseStr(wzRestartResource); |
| 176 | ReleaseStr(wzComponent); |
| 177 | ReleaseStr(wzResource); |
| 178 | |
| 179 | if (FAILED(hr)) |
| 180 | { |
| 181 | er = ERROR_INSTALL_FAILURE; |
| 182 | } |
| 183 | |
| 184 | return WcaFinalize(er); |
| 185 | } |