/******************************************************************** SetVerboseLoggingAtom() - Sets one of two global Atoms to specify if the install should do verbose logging. Communicates the verbose setting to deferred CAs. Set a negative case atom so that we can distinguish between an unset atom and the non-verbose case. This helps prevent the expensive regkey lookup for non-verbose. ********************************************************************/ HRESULT WIXAPI SetVerboseLoggingAtom(BOOL bValue) { HRESULT hr = S_OK; ATOM atomVerbose = 0; atomVerbose = ::GlobalFindAtomW(L"WcaVerboseLogging"); if (0 == atomVerbose && bValue) { atomVerbose = ::GlobalAddAtomW(L"WcaVerboseLogging"); ExitOnNullWithLastError(atomVerbose, hr, "Failed to create WcaVerboseLogging global atom."); } else if (0 != atomVerbose && !bValue) { ::SetLastError(ERROR_SUCCESS); ::GlobalDeleteAtom(atomVerbose); ExitOnLastError(hr, "Failed to delete WcaVerboseLogging global atom."); } atomVerbose = ::GlobalFindAtomW(L"WcaNotVerboseLogging"); if (0 == atomVerbose && !bValue) { atomVerbose = ::GlobalAddAtomW(L"WcaNotVerboseLogging"); ExitOnNullWithLastError(atomVerbose, hr, "Failed to create WcaNotVerboseLogging global atom."); } else if (0 != atomVerbose && bValue) { ::SetLastError(ERROR_SUCCESS); ::GlobalDeleteAtom(atomVerbose); ExitOnLastError(hr, "Failed to delete WcaNotVerboseLogging global atom."); } LExit: return hr; }
static HRESULT ParseWxlControls( __in IXMLDOMElement* pElement, __in WIX_LOCALIZATION* pWixLoc ) { HRESULT hr = S_OK; IXMLDOMNode* pixn = NULL; IXMLDOMNodeList* pixnl = NULL; DWORD dwIdx = 0; hr = XmlSelectNodes(pElement, L"UI|Control", &pixnl); ExitOnLastError(hr, "Failed to get UI child nodes of Wxl File."); hr = pixnl->get_length(reinterpret_cast<long*>(&pWixLoc->cLocControls)); ExitOnLastError(hr, "Failed to get number of UI child nodes in Wxl File."); if (0 < pWixLoc->cLocControls) { pWixLoc->rgLocControls = static_cast<LOC_CONTROL*>(MemAlloc(sizeof(LOC_CONTROL) * pWixLoc->cLocControls, TRUE)); ExitOnNull(pWixLoc->rgLocControls, hr, E_OUTOFMEMORY, "Failed to allocate memory for localized controls."); while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL))) { hr = ParseWxlControl(pixn, dwIdx, pWixLoc); ExitOnFailure(hr, "Failed to parse localized control."); ++dwIdx; ReleaseNullObject(pixn); } hr = S_OK; ExitOnFailure(hr, "Failed to enumerate all localized controls."); } LExit: if (FAILED(hr) && pWixLoc->rgLocControls) { for (DWORD idx = 0; idx < pWixLoc->cLocControls; ++idx) { ReleaseStr(pWixLoc->rgLocControls[idx].wzControl); ReleaseStr(pWixLoc->rgLocControls[idx].wzText); } ReleaseMem(pWixLoc->rgLocControls); } ReleaseObject(pixn); ReleaseObject(pixnl); return hr; }
/******************************************************************** LoadEulaText - Reads data for Richedit control ********************************************************************/ void LoadEulaText( __in HWND hWnd ) { HRESULT hr = S_OK; ExitOnNull(hWnd, hr, ERROR_INVALID_HANDLE, "Invalid Handle passed to LoadEulaText"); // Docs say this doesn't return any value ::SendMessageW(hWnd, EM_LIMITTEXT, static_cast<WPARAM>(lstrlen(vpszEulaText)), 0); EDITSTREAM es; ::ZeroMemory(&es, sizeof(es)); es.pfnCallback = (EDITSTREAMCALLBACK)ReadStreamCallback; es.dwCookie = (DWORD)0; ::SendMessageW(hWnd, EM_STREAMIN, SF_RTF, (LPARAM)&es); if (0 != es.dwError) { ExitOnLastError(hr, "failed to load the EULA into the control"); } LExit: vhr = hr; }
/****************************************************************** CaSchedServiceConfig - entry point for CaSchedServiceConfig Custom Action called as Type 1 CustomAction (binary DLL) from Windows Installer in InstallExecuteSequence before CaExecServiceConfig ********************************************************************/ extern "C" UINT __stdcall SchedServiceConfig( __in MSIHANDLE hInstall ) { // AssertSz(FALSE, "debug SchedServiceConfig"); HRESULT hr = S_OK; UINT uiResult = ERROR_SUCCESS; DWORD dwError = 0; LPWSTR pwzData = NULL; int iData = 0; BOOL fExistingService = FALSE; PMSIHANDLE hView = NULL; PMSIHANDLE hRec = NULL; INSTALLSTATE isInstalled; INSTALLSTATE isAction; SC_HANDLE hSCM = NULL; SC_HANDLE hService = NULL; LPSERVICE_FAILURE_ACTIONSW psfa; LPWSTR pwzCustomActionData = NULL; LPWSTR pwzRollbackCustomActionData = NULL; DWORD cServices = 0; DWORD dwRestartDelay = 0; WCHAR wzActionName[32] = { 0 }; DWORD dwSizeNeeded = 0; // initialize hr = WcaInitialize(hInstall, "SchedServiceConfig"); ExitOnFailure(hr, "failed to initialize"); //Get a handle to the service control manager hSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); if (hSCM == NULL) ExitOnLastError(hr, "failed to get handle to SCM"); // loop through all the services to be configured hr = WcaOpenExecuteView(wzQUERY_SERVICECONFIG, &hView); ExitOnFailure(hr, "failed to open view on ServiceConfig table"); while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) { hr = WcaGetRecordInteger(hRec, QSC_NEWSERVICE, &iData); ExitOnFailure(hr, "failed to get object NewService"); fExistingService = 1 != iData; // Get component name hr = WcaGetRecordString(hRec, QSC_COMPONENT, &pwzData); ExitOnFailure(hr, "failed to get component name"); // check if we are installing this Component hr = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction); ExitOnFailure1(hr = HRESULT_FROM_WIN32(hr), "failed to get install state for Component: %S", pwzData); // We want to configure either a service we're installing or one already on the box if (WcaIsInstalling(isInstalled, isAction)) { // Check if we're configuring an existing service if (fExistingService) { // Confirm the service is actually on the box hr = WcaGetRecordFormattedString(hRec, QSC_SERVICENAME, &pwzData); ExitOnFailure(hr, "failed to get object NewService"); //Get a handle to the service hService = ::OpenServiceW(hSCM, pwzData, SERVICE_QUERY_CONFIG); if (hService == NULL) { dwError = ::GetLastError(); hr = HRESULT_FROM_WIN32(dwError); if (hr == ERROR_SERVICE_DOES_NOT_EXIST) { ExitOnFailure1(hr, "Service \"%s\" does not exist on this system.", pwzData); } else { ExitOnFailure1(hr, "Failed to get handle to the service \"%S\".", pwzData); } } // Get Current Service Config info if(!::QueryServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, NULL, 0, &dwSizeNeeded) && ERROR_INSUFFICIENT_BUFFER != ::GetLastError()) { ExitOnLastError(hr, "Failed to get current service config info."); } // Alloc space we were told we needed psfa = (LPSERVICE_FAILURE_ACTIONSW) MemAlloc(dwSizeNeeded, TRUE); ExitOnNull(psfa, hr, E_OUTOFMEMORY, "failed to allocate memory for service failure actions."); // Now do the real query if (!::QueryServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, (LPBYTE)psfa, dwSizeNeeded, &dwSizeNeeded)) ExitOnLastError(hr, "failed to Query Service."); // Build up rollback CA data so we can restore service state if necessary hr = WcaWriteStringToCaData(pwzData, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // If this service struct is empty, fill in defualt values if(psfa->cActions < 3) { hr = WcaWriteStringToCaData(c_wzActionTypeNone, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); hr = WcaWriteStringToCaData(c_wzActionTypeNone, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); hr = WcaWriteStringToCaData(c_wzActionTypeNone, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); } else { // psfa actually had actions defined, so use them // action 1 hr = GetSCActionTypeString(psfa->lpsaActions[0].Type, (LPWSTR)wzActionName, 32); ExitOnFailure(hr, "failed to query SFA object"); if (SC_ACTION_RESTART == psfa->lpsaActions[0].Type) dwRestartDelay = psfa->lpsaActions[0].Delay / 1000; hr = WcaWriteStringToCaData(wzActionName, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // action 2 hr = GetSCActionTypeString(psfa->lpsaActions[1].Type, (LPWSTR)wzActionName, 32); ExitOnFailure(hr, "failed to query SFA object"); if (SC_ACTION_RESTART == psfa->lpsaActions[1].Type) dwRestartDelay = psfa->lpsaActions[1].Delay / 1000; hr = WcaWriteStringToCaData(wzActionName, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // action 3 hr = GetSCActionTypeString(psfa->lpsaActions[2].Type, (LPWSTR)wzActionName, 32); ExitOnFailure(hr, "failed to query SFA object"); if (SC_ACTION_RESTART == psfa->lpsaActions[2].Type) dwRestartDelay = psfa->lpsaActions[2].Delay / 1000; hr = WcaWriteStringToCaData(wzActionName, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); } hr = WcaWriteIntegerToCaData(psfa->dwResetPeriod / (24 * 60 * 60), &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaWriteIntegerToCaData(dwRestartDelay, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); // check for value being null if(!psfa->lpCommand) psfa->lpCommand = L""; hr = WcaWriteStringToCaData(psfa->lpCommand, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // check for value being null if(!psfa->lpRebootMsg) psfa->lpRebootMsg = L""; hr = WcaWriteStringToCaData(psfa->lpRebootMsg, &pwzRollbackCustomActionData); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // Clear up per-service values if(psfa) MemFree(psfa); } // add the data to the CustomActionData (for install) hr = WcaGetRecordFormattedString(hRec, QSC_SERVICENAME, &pwzData); ExitOnFailure(hr, "failed to get name of service"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSC_FIRSTFAILUREACTIONTYPE, &pwzData); ExitOnFailure(hr, "failed to get first failure action type"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSC_SECONDFAILUREACTIONTYPE, &pwzData); ExitOnFailure(hr, "failed to get second failure action type"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSC_THIRDFAILUREACTIONTYPE, &pwzData); ExitOnFailure(hr, "failed to get third failure action type"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordInteger(hRec, QSC_RESETPERIODINDAYS, &iData); if (hr == S_FALSE) // deal w/ possible null value iData = 0; ExitOnFailure(hr, "failed to get reset period in days between service restart attempts."); hr = WcaWriteIntegerToCaData(iData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordInteger(hRec, QSC_RESTARTSERVICEDELAYINSECONDS, &iData); if (hr == S_FALSE) // deal w/ possible null value iData = 0; ExitOnFailure(hr, "failed to get server restart delay value."); hr = WcaWriteIntegerToCaData(iData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSC_PROGRAMCOMMANDLINE, &pwzData); // null value already dealt w/ properly ExitOnFailure(hr, "failed to get command line to run on service failure."); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSC_REBOOTMESSAGE, &pwzData); // null value already dealt w/ properly ExitOnFailure(hr, "failed to get message to send to users when server reboots due to service failure."); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); cServices++; ::CloseServiceHandle(hService); hService = NULL; } } // if we looped through all records all is well if (E_NOMOREITEMS == hr) hr = S_OK; ExitOnFailure(hr, "failed while looping through all objects to secure"); // setup CustomActionData and add to progress bar for download if (pwzRollbackCustomActionData && *pwzRollbackCustomActionData) { Assert(0 < cServices); hr = WcaDoDeferredAction(L"ExecServiceConfigRollback", pwzRollbackCustomActionData, cServices * COST_SERVICECONFIG); ExitOnFailure(hr, "failed to schedule ExecSecureObjects action"); } // schedule the custom action and add to progress bar if (pwzCustomActionData && *pwzCustomActionData) { Assert(0 < cServices); hr = WcaDoDeferredAction(L"ExecServiceConfig", pwzCustomActionData, cServices * COST_SERVICECONFIG); ExitOnFailure(hr, "failed to schedule ExecSecureObjects action"); } LExit: // Clean up handles if (hService != NULL) ::CloseServiceHandle(hService); if (hSCM != NULL) ::CloseServiceHandle(hSCM); ReleaseStr(pwzCustomActionData); ReleaseStr(pwzRollbackCustomActionData); ReleaseStr(pwzData); if (FAILED(hr)) uiResult = ERROR_INSTALL_FAILURE; return WcaFinalize(uiResult); }
/******************************************************************** CreateShare - create the file share on this computer ********************************************************************/ HRESULT CreateShare(SCA_SMBP* pssp) { if (!pssp || !(pssp->wzKey)) return E_INVALIDARG; HRESULT hr = S_OK; PACL pACL = NULL; SHARE_INFO_502 si; NET_API_STATUS s; DWORD dwParamErr = 0; BOOL fShareExists = SUCCEEDED(DoesShareExist(pssp->wzKey)); PSECURITY_DESCRIPTOR pSD = static_cast<PSECURITY_DESCRIPTOR>(MemAlloc(SECURITY_DESCRIPTOR_MIN_LENGTH, TRUE)); ExitOnNull(pSD, hr, E_OUTOFMEMORY, "Failed to allocate memory for security descriptor"); #pragma prefast(push) #pragma prefast(disable:25029) if (!::InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) #pragma prefast(pop) { ExitOnLastError(hr, "failed to initialize security descriptor"); } hr = AllocateAcl(pssp, &pACL); ExitOnFailure(hr, "Failed to allocate ACL for fileshare"); if (NULL == pACL) { WcaLog(LOGMSG_VERBOSE, "Ignoring NULL DACL."); } #pragma prefast(push) #pragma prefast(disable:25028) // We only call this when pACL isn't NULL, so this call is safe according to the docs // add the ACL to the security descriptor. else if (!::SetSecurityDescriptorDacl(pSD, TRUE, pACL, FALSE)) { ExitOnLastError(hr, "Failed to set security descriptor"); } #pragma prefast(pop) // all that is left is to create the share FillShareInfo(&si, pssp, pSD); // Fail if the directory doesn't exist if (!DirExists(pssp->wzDirectory, NULL)) ExitOnFailure1(hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND), "Can't create a file share on directory that doesn't exist: %ls.", pssp->wzDirectory); WcaLog(LOGMSG_VERBOSE, "Creating file share on directory \'%ls\' named \'%ls\'.", pssp->wzDirectory, pssp->wzKey); if (!fShareExists) { s = ::NetShareAdd(NULL, 502, (BYTE*) &si, &dwParamErr); WcaLog(LOGMSG_VERBOSE, "Adding a new file share."); } else { // The share exists. Write our new permissions over the top. s = ::NetShareSetInfo(NULL, pssp->wzKey, 502, (BYTE*) &si, &dwParamErr); WcaLog(LOGMSG_VERBOSE, "Setting permissions on existing share."); } if (NERR_Success != s) { hr = E_FAIL; if (!fShareExists && NERR_DuplicateShare == s) WcaLog(LOGMSG_VERBOSE, "Duplicate error when existence check failed."); // error codes listed above. ExitOnFailure1(hr, "Failed to create/modify file share: Err: %d", s); } LExit: if (pACL) { ::LocalFree(pACL); } ReleaseMem(pSD); return hr; }
/****************************************************************** CaExecServiceConfig - entry point for ServiceConfig Custom Action. NOTE: deferred CustomAction since it modifies the machine NOTE: CustomActionData == wzServiceName\tfNewService\twzFirstFailureActionType\twzSecondFailureActionType\twzThirdFailureActionType\tdwResetPeriodInDays\tdwRestartServiceDelayInSeconds\twzProgramCommandLine\twzRebootMessage\twzServiceName\tfNewService\t... *******************************************************************/ extern "C" UINT __stdcall ExecServiceConfig( __in MSIHANDLE hInstall ) { //AssertSz(FALSE, "debug ExecServiceConfig"); HRESULT hr = S_OK; DWORD er = 0; LPWSTR pwzCustomActionData = NULL; LPWSTR pwz = NULL; LPWSTR pwzScriptKey = NULL; WCA_CASCRIPT_HANDLE hRollbackScript = NULL; LPWSTR pwzServiceName = NULL; BOOL fNewService = FALSE; LPWSTR pwzFirstFailureActionType = NULL; LPWSTR pwzSecondFailureActionType = NULL; LPWSTR pwzThirdFailureActionType = NULL; LPWSTR pwzProgramCommandLine = NULL; LPWSTR pwzRebootMessage = NULL; DWORD dwResetPeriodInDays = 0; DWORD dwRestartServiceDelayInSeconds = 0; LPVOID lpMsgBuf = NULL; SC_HANDLE hSCM = NULL; SC_HANDLE hService = NULL; DWORD dwRestartDelay = 0; WCHAR wzActionName[32] = { 0 }; DWORD cbExistingServiceConfig = 0; SERVICE_FAILURE_ACTIONSW* psfa = NULL; // initialize hr = WcaInitialize(hInstall, "ExecServiceConfig"); ExitOnFailure(hr, "failed to initialize"); // Open the Services Control Manager up front. hSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); if (NULL == hSCM) { er = ::GetLastError(); hr = HRESULT_FROM_WIN32(er); #pragma prefast(push) #pragma prefast(disable:25028) ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, er, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL); #pragma prefast(pop) ExitOnFailure1(hr, "Failed to get handle to SCM. Error: %ls", (LPWSTR)lpMsgBuf); } // First, get the script key out of the CustomActionData and // use that to create the rollback script for this action. hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData); ExitOnFailure(hr, "failed to get CustomActionData"); WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData); pwz = pwzCustomActionData; hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey); if (!pwzScriptKey) { hr = E_UNEXPECTED; ExitOnFailure(hr, "Failed due to unexpected CustomActionData passed."); } ExitOnFailure(hr, "Failed to read encoding key from CustomActionData."); hr = WcaCaScriptCreate(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, FALSE, &hRollbackScript); ExitOnFailure(hr, "Failed to open rollback CustomAction script."); // Next, loop through the rest of the CustomActionData, processing // each service config row in turn. while (pwz && *pwz) { hr = WcaReadStringFromCaData(&pwz, &pwzServiceName); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&fNewService)); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzFirstFailureActionType); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzSecondFailureActionType); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzThirdFailureActionType); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwResetPeriodInDays)); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwRestartServiceDelayInSeconds)); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzProgramCommandLine); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzRebootMessage); ExitOnFailure(hr, "failed to process CustomActionData"); WcaLog(LOGMSG_VERBOSE, "Configuring Service: %ls", pwzServiceName); // Open the handle with all the permissions we might need: // SERVICE_QUERY_CONFIG is needed for QueryServiceConfig2(). // SERVICE_CHANGE_CONFIG is needed for ChangeServiceConfig2(). // SERVICE_START is required in order to handle SC_ACTION_RESTART action. hr = GetService(hSCM, pwzServiceName, SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_START, &hService); ExitOnFailure1(hr, "Failed to get service: %ls", pwzServiceName); // If we are configuring a service that existed on the machine, we need to // read the existing service configuration and write it out to the rollback // log so rollback can put it back if anything goes wrong. if (!fNewService) { // First, read the existing service config. if (!::QueryServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, NULL, 0, &cbExistingServiceConfig) && ERROR_INSUFFICIENT_BUFFER != ::GetLastError()) { ExitWithLastError(hr, "Failed to get current service config info."); } psfa = static_cast<LPSERVICE_FAILURE_ACTIONSW>(MemAlloc(cbExistingServiceConfig, TRUE)); ExitOnNull(psfa, hr, E_OUTOFMEMORY, "failed to allocate memory for service failure actions."); if (!::QueryServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, (LPBYTE)psfa, cbExistingServiceConfig, &cbExistingServiceConfig)) { ExitOnLastError(hr, "failed to Query Service."); } // Build up rollback log so we can restore service state if necessary hr = WcaCaScriptWriteString(hRollbackScript, pwzServiceName); ExitOnFailure(hr, "Failed to add service name to Rollback Log"); // If this service struct is empty, fill in default values if (3 > psfa->cActions) { hr = WcaCaScriptWriteString(hRollbackScript, c_wzActionTypeNone); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); hr = WcaCaScriptWriteString(hRollbackScript, c_wzActionTypeNone); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); hr = WcaCaScriptWriteString(hRollbackScript, c_wzActionTypeNone); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); } else { // psfa actually had actions defined, so use the first three. for (int i = 0; i < 3; ++i) { hr = GetSCActionTypeString(psfa->lpsaActions[i].Type, wzActionName, countof(wzActionName)); ExitOnFailure(hr, "failed to query SFA object"); if (SC_ACTION_RESTART == psfa->lpsaActions[i].Type) { dwRestartDelay = psfa->lpsaActions[i].Delay / 1000; } hr = WcaCaScriptWriteString(hRollbackScript, wzActionName); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); } } hr = WcaCaScriptWriteNumber(hRollbackScript, psfa->dwResetPeriod / (24 * 60 * 60)); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaCaScriptWriteNumber(hRollbackScript, dwRestartDelay); ExitOnFailure(hr, "failed to add data to CustomActionData"); // Handle the null cases. if (!psfa->lpCommand) { psfa->lpCommand = L""; } hr = WcaCaScriptWriteString(hRollbackScript, psfa->lpCommand); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // Handle the null cases. if (!psfa->lpRebootMsg) { psfa->lpRebootMsg = L""; } hr = WcaCaScriptWriteString(hRollbackScript, psfa->lpRebootMsg); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // Nudge the system to get all our rollback data written to disk. WcaCaScriptFlush(hRollbackScript); ReleaseNullMem(psfa); } hr = ConfigureService(hSCM, hService, pwzServiceName, dwRestartServiceDelayInSeconds, pwzFirstFailureActionType, pwzSecondFailureActionType, pwzThirdFailureActionType, dwResetPeriodInDays, pwzRebootMessage, pwzProgramCommandLine); ExitOnFailure1(hr, "Failed to configure service: %ls", pwzServiceName); hr = WcaProgressMessage(COST_SERVICECONFIG, FALSE); ExitOnFailure(hr, "failed to send progress message"); // Per-service cleanup ::CloseServiceHandle(hService); hService = NULL; dwResetPeriodInDays = 0; dwRestartServiceDelayInSeconds = 0; } LExit: WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_PRESERVE); if (lpMsgBuf) { ::LocalFree(lpMsgBuf); } if (hService) { ::CloseServiceHandle(hService); } if (hSCM) { ::CloseServiceHandle(hSCM); } ReleaseMem(psfa); ReleaseStr(pwzRebootMessage); ReleaseStr(pwzProgramCommandLine); ReleaseStr(pwzThirdFailureActionType); ReleaseStr(pwzSecondFailureActionType); ReleaseStr(pwzFirstFailureActionType); ReleaseStr(pwzServiceName); ReleaseStr(pwzScriptKey); ReleaseStr(pwzCustomActionData); er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); }
extern "C" UINT __stdcall ExecSecureObjectsRollback( __in MSIHANDLE hInstall ) { // AssertSz(FALSE, "debug ExecSecureObjectsRollback"); HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; LPWSTR pwz = NULL; LPWSTR pwzData = NULL; LPWSTR pwzObject = NULL; LPWSTR pwzTable = NULL; LPWSTR pwzSecurityInfo = NULL; SE_OBJECT_TYPE objectType = SE_UNKNOWN_OBJECT_TYPE; PSECURITY_DESCRIPTOR psd = NULL; ULONG psdSize; SECURITY_DESCRIPTOR_CONTROL sdc = {0}; SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION; PACL pDacl = NULL; BOOL bDaclPresent = false; BOOL bDaclDefaulted = false; DWORD dwRevision = 0; int iProtected; // initialize hr = WcaInitialize(hInstall, "ExecSecureObjectsRollback"); ExitOnFailure(hr, "failed to initialize"); hr = WcaGetProperty(L"CustomActionData", &pwzData); ExitOnFailure(hr, "failed to get CustomActionData"); WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData); pwz = pwzData; hr = WcaReadStringFromCaData(&pwz, &pwzObject); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzTable); ExitOnFailure(hr, "failed to process CustomActionData"); objectType = SEObjectTypeFromString(const_cast<LPCWSTR> (pwzTable)); if (SE_UNKNOWN_OBJECT_TYPE != objectType) { hr = WcaReadStringFromCaData(&pwz, &pwzSecurityInfo); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadIntegerFromCaData(&pwz, &iProtected); ExitOnFailure(hr, "failed to process CustomActionData"); if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(pwzSecurityInfo,SDDL_REVISION_1,&psd,&psdSize)) { ExitOnLastError(hr, "failed to convert security descriptor string to a valid security descriptor"); } if (!::GetSecurityDescriptorDacl(psd,&bDaclPresent,&pDacl,&bDaclDefaulted)) { hr = E_UNEXPECTED; ExitOnFailure2(hr, "failed to get security descriptor's DACL - error code: %d",pwzSecurityInfo,GetLastError()); } // The below situation may always be caught by the above if block - the documentation isn't very clear. To be safe, we're going to test for it. if (!bDaclPresent) { hr = E_UNEXPECTED; ExitOnFailure(hr, "security descriptor does not contain a DACL"); } //Need to see if DACL is protected so getting Descriptor information if (!::GetSecurityDescriptorControl(psd, &sdc, &dwRevision)) { ExitOnLastError1(hr, "failed to get security descriptor control for object: %ls", pwzObject); } // Write a 1 if DACL is protected, 0 otherwise switch (iProtected) { case 0: // Unnecessary to do anything - leave si to the default flags break; case 1: si = si | PROTECTED_DACL_SECURITY_INFORMATION; break; default: hr = E_UNEXPECTED; ExitOnFailure(hr, "unrecognized value in CustomActionData"); break; } er = ::SetNamedSecurityInfoW(pwzObject, objectType, si, NULL, NULL, pDacl, NULL); ExitOnFailure2(hr = HRESULT_FROM_WIN32(er), "failed to set security info for object: %ls error code: %d", pwzObject, GetLastError()); } else { MessageExitOnFailure1(hr = E_UNEXPECTED, msierrSecureObjectsUnknownType, "unknown object type: %ls", pwzTable); } LExit: ReleaseStr(pwzData); ReleaseStr(pwzObject); ReleaseStr(pwzTable); ReleaseStr(pwzSecurityInfo); if (psd) { ::LocalFree(psd); } if (FAILED(hr)) { er = ERROR_INSTALL_FAILURE; } return WcaFinalize(er); }
HRESULT WIXAPI QuietExecEx( __inout_z LPWSTR wzCommand, __in DWORD dwTimeout, __in BOOL fLogCommand, __in BOOL fLogOutput ) { HRESULT hr = S_OK; PROCESS_INFORMATION oProcInfo; STARTUPINFOW oStartInfo; DWORD dwExitCode = ERROR_SUCCESS; HANDLE hOutRead = INVALID_HANDLE_VALUE; HANDLE hOutWrite = INVALID_HANDLE_VALUE; HANDLE hErrWrite = INVALID_HANDLE_VALUE; HANDLE hInRead = INVALID_HANDLE_VALUE; HANDLE hInWrite = INVALID_HANDLE_VALUE; memset(&oProcInfo, 0, sizeof(oProcInfo)); memset(&oStartInfo, 0, sizeof(oStartInfo)); // Create output redirect pipes hr = CreatePipes(&hOutRead, &hOutWrite, &hErrWrite, &hInRead, &hInWrite); ExitOnFailure(hr, "Failed to create output pipes"); // Set up startup structure oStartInfo.cb = sizeof(STARTUPINFOW); oStartInfo.dwFlags = STARTF_USESTDHANDLES; oStartInfo.hStdInput = hInRead; oStartInfo.hStdOutput = hOutWrite; oStartInfo.hStdError = hErrWrite; // Log command if we were asked to do so if (fLogCommand) { WcaLog(LOGMSG_VERBOSE, "%ls", wzCommand); } #pragma prefast(suppress:25028) if (::CreateProcessW(NULL, wzCommand, // command line NULL, // security info NULL, // thread info TRUE, // inherit handles ::GetPriorityClass(::GetCurrentProcess()) | CREATE_NO_WINDOW, // creation flags NULL, // environment NULL, // cur dir &oStartInfo, &oProcInfo)) { ReleaseFile(oProcInfo.hThread); // Close child output/input handles so it doesn't hang ReleaseFile(hOutWrite); ReleaseFile(hErrWrite); ReleaseFile(hInRead); // Log output if we were asked to do so; otherwise just read the output handle HandleOutput(fLogOutput, hOutRead); // Wait for everything to finish ::WaitForSingleObject(oProcInfo.hProcess, dwTimeout); if (!::GetExitCodeProcess(oProcInfo.hProcess, &dwExitCode)) { dwExitCode = ERROR_SEM_IS_SET; } ReleaseFile(hOutRead); ReleaseFile(hInWrite); ReleaseFile(oProcInfo.hProcess); } else { ExitOnLastError(hr, "Command failed to execute."); } ExitOnWin32Error(dwExitCode, hr, "Command line returned an error."); LExit: return hr; }
static HRESULT CreatePipes( __out HANDLE *phOutRead, __out HANDLE *phOutWrite, __out HANDLE *phErrWrite, __out HANDLE *phInRead, __out HANDLE *phInWrite ) { Assert(phOutRead); Assert(phOutWrite); Assert(phErrWrite); Assert(phInRead); Assert(phInWrite); HRESULT hr = S_OK; SECURITY_ATTRIBUTES sa; HANDLE hOutTemp = INVALID_HANDLE_VALUE; HANDLE hInTemp = INVALID_HANDLE_VALUE; HANDLE hOutRead = INVALID_HANDLE_VALUE; HANDLE hOutWrite = INVALID_HANDLE_VALUE; HANDLE hErrWrite = INVALID_HANDLE_VALUE; HANDLE hInRead = INVALID_HANDLE_VALUE; HANDLE hInWrite = INVALID_HANDLE_VALUE; // Fill out security structure so we can inherit handles ::ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; // Create pipes if (!::CreatePipe(&hOutTemp, &hOutWrite, &sa, 0)) { ExitOnLastError(hr, "Failed to create output pipe"); } if (!::CreatePipe(&hInRead, &hInTemp, &sa, 0)) { ExitOnLastError(hr, "Failed to create input pipe"); } // Duplicate output pipe so standard error and standard output write to // the same pipe if (!::DuplicateHandle(::GetCurrentProcess(), hOutWrite, ::GetCurrentProcess(), &hErrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS)) { ExitOnLastError(hr, "Failed to duplicate write handle"); } // We need to create new output read and input write handles that are // non inheritable. Otherwise it creates handles that can't be closed. if (!::DuplicateHandle(::GetCurrentProcess(), hOutTemp, ::GetCurrentProcess(), &hOutRead, 0, FALSE, DUPLICATE_SAME_ACCESS)) { ExitOnLastError(hr, "Failed to duplicate output pipe"); } if (!::DuplicateHandle(::GetCurrentProcess(), hInTemp, ::GetCurrentProcess(), &hInWrite, 0, FALSE, DUPLICATE_SAME_ACCESS)) { ExitOnLastError(hr, "Failed to duplicate input pipe"); } // now that everything has succeeded, assign to the outputs *phOutRead = hOutRead; hOutRead = INVALID_HANDLE_VALUE; *phOutWrite = hOutWrite; hOutWrite = INVALID_HANDLE_VALUE; *phErrWrite = hErrWrite; hErrWrite = INVALID_HANDLE_VALUE; *phInRead = hInRead; hInRead = INVALID_HANDLE_VALUE; *phInWrite = hInWrite; hInWrite = INVALID_HANDLE_VALUE; LExit: ReleaseFile(hOutRead); ReleaseFile(hOutWrite); ReleaseFile(hErrWrite); ReleaseFile(hInRead); ReleaseFile(hInWrite); ReleaseFile(hOutTemp); ReleaseFile(hInTemp); return hr; }
static HRESULT HandleOutput( __in BOOL fLogOutput, __in HANDLE hRead ) { BYTE *pBuffer = NULL; LPWSTR szLog = NULL; LPWSTR szTemp = NULL; LPWSTR pEnd = NULL; LPWSTR pNext = NULL; LPWSTR sczEscaped = NULL; LPSTR szWrite = NULL; DWORD dwBytes = OUTPUT_BUFFER; BOOL bFirst = TRUE; BOOL bUnicode = TRUE; HRESULT hr = S_OK; // Get buffer for output pBuffer = static_cast<BYTE *>(MemAlloc(OUTPUT_BUFFER, FALSE)); ExitOnNull(pBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for output."); while (0 != dwBytes) { ::ZeroMemory(pBuffer, OUTPUT_BUFFER); if (!::ReadFile(hRead, pBuffer, OUTPUT_BUFFER - 1, &dwBytes, NULL) && GetLastError() != ERROR_BROKEN_PIPE) { ExitOnLastError(hr, "Failed to read from handle."); } if (fLogOutput) { // Check for UNICODE or ANSI output if (bFirst) { if ((isgraph(pBuffer[0]) && isgraph(pBuffer[1])) || (isgraph(pBuffer[0]) && isspace(pBuffer[1])) || (isspace(pBuffer[0]) && isgraph(pBuffer[1])) || (isspace(pBuffer[0]) && isspace(pBuffer[1]))) { bUnicode = FALSE; } bFirst = FALSE; } // Keep track of output if (bUnicode) { hr = StrAllocConcat(&szLog, (LPCWSTR)pBuffer, 0); ExitOnFailure(hr, "Failed to concatenate output strings"); } else { hr = StrAllocStringAnsi(&szTemp, (LPCSTR)pBuffer, 0, CP_OEMCP); ExitOnFailure(hr, "Failed to allocate output string"); hr = StrAllocConcat(&szLog, szTemp, 0); ExitOnFailure(hr, "Failed to concatenate output strings"); } // Log each line of the output pNext = szLog; pEnd = wcschr(szLog, L'\r'); if (NULL == pEnd) { pEnd = wcschr(szLog, L'\n'); } while (pEnd && *pEnd) { // Find beginning of next line pEnd[0] = 0; ++pEnd; if ((pEnd[0] == L'\r') || (pEnd[0] == L'\n')) { ++pEnd; } // Log output hr = StrAllocString(&sczEscaped, pNext, 0); ExitOnFailure(hr, "Failed to allocate copy of string"); hr = StrReplaceStringAll(&sczEscaped, L"%", L"%%"); ExitOnFailure(hr, "Failed to escape percent signs in string"); hr = StrAnsiAllocString(&szWrite, sczEscaped, 0, CP_OEMCP); ExitOnFailure(hr, "Failed to convert output to ANSI"); WcaLog(LOGMSG_STANDARD, szWrite); // Next line pNext = pEnd; pEnd = wcschr(pNext, L'\r'); if (NULL == pEnd) { pEnd = wcschr(pNext, L'\n'); } } hr = StrAllocString(&szTemp, pNext, 0); ExitOnFailure(hr, "Failed to allocate string"); hr = StrAllocString(&szLog, szTemp, 0); ExitOnFailure(hr, "Failed to allocate string"); } } // Print any text that didn't end with a new line if (szLog && *szLog) { hr = StrReplaceStringAll(&szLog, L"%", L"%%"); ExitOnFailure(hr, "Failed to escape percent signs in string"); hr = StrAnsiAllocString(&szWrite, szLog, 0, CP_OEMCP); ExitOnFailure(hr, "Failed to convert output to ANSI"); WcaLog(LOGMSG_VERBOSE, szWrite); } LExit: ReleaseMem(pBuffer); ReleaseStr(szLog); ReleaseStr(szTemp); ReleaseStr(szWrite); ReleaseStr(sczEscaped); return hr; }