Namestitev sem dopolnil še z možnostjo zagona/zaustavitve poljubnega sistemskega servisa (v tej fazi brez odvisnih servisov).

This commit is contained in:
Simon Rozman 2014-03-21 13:35:01 +00:00
parent ed18da4ebb
commit ddde7488fa
4 changed files with 139 additions and 21 deletions

View File

@ -40,6 +40,7 @@ ScheduledTask Component_ N Component 1 Identifier Component the task is part
ServiceConfigure ServiceConfigure N Identifier Primary key, non-localized token. ServiceConfigure ServiceConfigure N Identifier Primary key, non-localized token.
ServiceConfigure Name N Formatted Name of a service. /, \, comma and space are invalid ServiceConfigure Name N Formatted Name of a service. /, \, comma and space are invalid
ServiceConfigure StartType N DoubleInteger -1;0;1;2;3;4 Start type: -1=don't change, 0=boot, 1=system, 2=auto, 3=demand, 4=disabled ServiceConfigure StartType N DoubleInteger -1;0;1;2;3;4 Start type: -1=don't change, 0=boot, 1=system, 2=auto, 3=demand, 4=disabled
ServiceConfigure Control N Integer 0;2;3;4;5;7 Bitwise combination of: 0=no action, 1=wait to finish, 2=start, 4=stop
ServiceConfigure Condition Y Condition Optional expression which skips the service configuration if evaluates to expFalse. If the expression syntax is invalid, the engine will terminate, returning iesBadActionData. ServiceConfigure Condition Y Condition Optional expression which skips the service configuration if evaluates to expFalse. If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.
ServiceConfigure Sequence Y Number that determines the sort order in which the configurations are to be executed. ServiceConfigure Sequence Y Number that determines the sort order in which the configurations are to be executed.
TaskTrigger Trigger N Identifier Primary key, non-localized token. TaskTrigger Trigger N Identifier Primary key, non-localized token.
@ -198,8 +199,8 @@ All :: "$(LANG).$(PLAT).$(CFG).ServiceConfigure-1.idt"
"$(LANG).$(PLAT).$(CFG).ServiceConfigure-1.idt" : "Makefile" "..\..\..\include\MSIBuildCfg.mak" "$(LANG).$(PLAT).$(CFG).ServiceConfigure-1.idt" : "Makefile" "..\..\..\include\MSIBuildCfg.mak"
-if exist $@ del /f /q $@ -if exist $@ del /f /q $@
move /y << $@ > NUL move /y << $@ > NUL
ServiceConfigure Name StartType Condition Sequence ServiceConfigure Name StartType Control Condition Sequence
s$(MSIBUILD_LENGTH_ID) l255 i4 S255 I2 s$(MSIBUILD_LENGTH_ID) l255 i4 i2 S255 I2
ServiceConfigure ServiceConfigure ServiceConfigure ServiceConfigure
<<NOKEEP <<NOKEEP

View File

@ -7,6 +7,8 @@
#define MSICA_CERT_TICK_SIZE (4*1024) #define MSICA_CERT_TICK_SIZE (4*1024)
#define MSICA_SVC_SET_START_TICK_SIZE (1*1024) #define MSICA_SVC_SET_START_TICK_SIZE (1*1024)
#define MSICA_SVC_START_TICK_SIZE (1*1024)
#define MSICA_SVC_STOP_TICK_SIZE (1*1024)
#define MSICA_TASK_TICK_SIZE (16*1024) #define MSICA_TASK_TICK_SIZE (16*1024)
@ -207,12 +209,12 @@ UINT MSICA_API ServiceConfigEval(MSIHANDLE hInstall)
PMSIHANDLE hViewSC; PMSIHANDLE hViewSC;
// Prepare a query to get a list/view of service configurations. // Prepare a query to get a list/view of service configurations.
uiResult = ::MsiDatabaseOpenView(hDatabase, _T("SELECT Name,StartType,Condition FROM ServiceConfigure ORDER BY Sequence"), &hViewSC); uiResult = ::MsiDatabaseOpenView(hDatabase, _T("SELECT Name,StartType,Control,Condition FROM ServiceConfigure ORDER BY Sequence"), &hViewSC);
if (uiResult == NO_ERROR) { if (uiResult == NO_ERROR) {
// Execute query! // Execute query!
uiResult = ::MsiViewExecute(hViewSC, NULL); uiResult = ::MsiViewExecute(hViewSC, NULL);
if (uiResult == NO_ERROR) { if (uiResult == NO_ERROR) {
int iStartType; int iValue;
for (;;) { for (;;) {
PMSIHANDLE hRecord; PMSIHANDLE hRecord;
@ -226,7 +228,7 @@ UINT MSICA_API ServiceConfigEval(MSIHANDLE hInstall)
break; break;
// Read and evaluate service configuration condition. // Read and evaluate service configuration condition.
uiResult = ::MsiRecordGetString(hRecord, 3, sValue); uiResult = ::MsiRecordGetString(hRecord, 4, sValue);
if (uiResult != NO_ERROR) break; if (uiResult != NO_ERROR) break;
condition = ::MsiEvaluateCondition(hInstall, sValue); condition = ::MsiEvaluateCondition(hInstall, sValue);
if (condition == MSICONDITION_FALSE) if (condition == MSICONDITION_FALSE)
@ -241,20 +243,44 @@ UINT MSICA_API ServiceConfigEval(MSIHANDLE hInstall)
if (uiResult != NO_ERROR) break; if (uiResult != NO_ERROR) break;
// Read service start type. // Read service start type.
iStartType = ::MsiRecordGetInteger(hRecord, 2); iValue = ::MsiRecordGetInteger(hRecord, 2);
if (iStartType == MSI_NULL_INTEGER) { if (iValue == MSI_NULL_INTEGER) {
uiResult = ERROR_INVALID_FIELD; uiResult = ERROR_INVALID_FIELD;
break; break;
} }
if (iStartType >= 0) { if (iValue >= 0) {
// Set service start type. // Set service start type.
olExecute.AddTail(new MSICA::COpSvcSetStart(sValue, iStartType, MSICA_SVC_SET_START_TICK_SIZE)); olExecute.AddTail(new MSICA::COpSvcSetStart(sValue, iValue, MSICA_SVC_SET_START_TICK_SIZE));
// The amount of tick space to add to progress indicator. // The amount of tick space to add to progress indicator.
::MsiRecordSetInteger(hRecordProg, 1, 3 ); ::MsiRecordSetInteger(hRecordProg, 1, 3 );
::MsiRecordSetInteger(hRecordProg, 2, MSICA_SVC_SET_START_TICK_SIZE); ::MsiRecordSetInteger(hRecordProg, 2, MSICA_SVC_SET_START_TICK_SIZE);
if (::MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL) { uiResult = ERROR_INSTALL_USEREXIT; break; } if (::MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL) { uiResult = ERROR_INSTALL_USEREXIT; break; }
} }
// Read service control.
iValue = ::MsiRecordGetInteger(hRecord, 3);
if (iValue == MSI_NULL_INTEGER) {
uiResult = ERROR_INVALID_FIELD;
break;
}
if ((iValue & 4) != 0) {
// Stop service.
olExecute.AddTail(new MSICA::COpSvcStop(sValue, (iValue & 1) ? TRUE : FALSE, MSICA_SVC_STOP_TICK_SIZE));
// The amount of tick space to add to progress indicator.
::MsiRecordSetInteger(hRecordProg, 1, 3 );
::MsiRecordSetInteger(hRecordProg, 2, MSICA_SVC_STOP_TICK_SIZE);
if (::MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL) { uiResult = ERROR_INSTALL_USEREXIT; break; }
} else if ((iValue & 2) != 0) {
// Start service.
olExecute.AddTail(new MSICA::COpSvcStart(sValue, (iValue & 1) ? TRUE : FALSE, MSICA_SVC_START_TICK_SIZE));
// The amount of tick space to add to progress indicator.
::MsiRecordSetInteger(hRecordProg, 1, 3 );
::MsiRecordSetInteger(hRecordProg, 2, MSICA_SVC_START_TICK_SIZE);
if (::MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL) { uiResult = ERROR_INSTALL_USEREXIT; break; }
}
} }
::MsiViewClose(hViewSC); ::MsiViewClose(hViewSC);

View File

@ -476,14 +476,33 @@ protected:
}; };
////////////////////////////////////////////////////////////////////////////
// COpSvcControl
////////////////////////////////////////////////////////////////////////////
class COpSvcControl : public COpTypeSingleString
{
public:
COpSvcControl(LPCWSTR pszService = L"", BOOL bWait = FALSE, int iTicks = 0);
static DWORD WaitForState(CSession *pSession, SC_HANDLE hService, DWORD dwPendingState, DWORD dwFinalState);
friend inline HRESULT operator <<(ATL::CAtlFile &f, const COpSvcControl &op);
friend inline HRESULT operator >>(ATL::CAtlFile &f, COpSvcControl &op);
protected:
BOOL m_bWait;
};
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// COpSvcStart // COpSvcStart
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
class COpSvcStart : public COpTypeSingleString class COpSvcStart : public COpSvcControl
{ {
public: public:
COpSvcStart(LPCWSTR pszService = L"", int iTicks = 0); COpSvcStart(LPCWSTR pszService = L"", BOOL bWait = FALSE, int iTicks = 0);
virtual HRESULT Execute(CSession *pSession); virtual HRESULT Execute(CSession *pSession);
}; };
@ -492,10 +511,10 @@ public:
// COpSvcStop // COpSvcStop
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
class COpSvcStop : public COpTypeSingleString class COpSvcStop : public COpSvcControl
{ {
public: public:
COpSvcStop(LPCWSTR pszService = L"", int iTicks = 0); COpSvcStop(LPCWSTR pszService = L"", BOOL bWait = FALSE, int iTicks = 0);
virtual HRESULT Execute(CSession *pSession); virtual HRESULT Execute(CSession *pSession);
}; };
@ -1438,6 +1457,29 @@ inline HRESULT operator >>(ATL::CAtlFile &f, COpSvcSetStart &op)
} }
inline HRESULT operator <<(ATL::CAtlFile &f, const COpSvcControl &op)
{
HRESULT hr;
hr = f << (const COpTypeSingleString&)op; if (FAILED(hr)) return hr;
hr = f << (int)(op.m_bWait); if (FAILED(hr)) return hr;
return S_OK;
}
inline HRESULT operator >>(ATL::CAtlFile &f, COpSvcControl &op)
{
HRESULT hr;
int iValue;
hr = f >> (COpTypeSingleString&)op; if (FAILED(hr)) return hr;
hr = f >> iValue; if (FAILED(hr)) return hr; op.m_bWait = iValue ? TRUE : FALSE;
return S_OK;
}
inline HRESULT operator <<(ATL::CAtlFile &f, const COpList &list) inline HRESULT operator <<(ATL::CAtlFile &f, const COpList &list)
{ {
POSITION pos; POSITION pos;

View File

@ -76,11 +76,60 @@ HRESULT COpSvcSetStart::Execute(CSession *pSession)
} }
////////////////////////////////////////////////////////////////////////////
// COpSvcControl
////////////////////////////////////////////////////////////////////////////
COpSvcControl::COpSvcControl(LPCWSTR pszService, BOOL bWait, int iTicks) :
COpTypeSingleString(pszService, iTicks),
m_bWait(bWait)
{
}
DWORD COpSvcControl::WaitForState(CSession *pSession, SC_HANDLE hService, DWORD dwPendingState, DWORD dwFinalState)
{
PMSIHANDLE hRecordProg = ::MsiCreateRecord(3);
// Prepare hRecordProg for progress messages.
::MsiRecordSetInteger(hRecordProg, 1, 2);
::MsiRecordSetInteger(hRecordProg, 3, 0);
// Wait for the service to start.
for (;;) {
SERVICE_STATUS_PROCESS ssp;
DWORD dwSize;
// Check service status.
if (::QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, sizeof(SERVICE_STATUS_PROCESS), &dwSize)) {
if (ssp.dwCurrentState == dwPendingState) {
// Service is pending. Wait some more ...
::Sleep(ssp.dwWaitHint < 1000 ? ssp.dwWaitHint : 1000);
} else if (ssp.dwCurrentState == dwFinalState) {
// Service is in expected state.
return NO_ERROR;
} else {
// Service is in unexpected state.
return ERROR_ASSERTION_FAILURE;
}
} else {
// Service query failed.
return ::GetLastError();
}
// Check if user cancelled installation.
::MsiRecordSetInteger(hRecordProg, 2, 0);
if (::MsiProcessMessage(pSession->m_hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL)
return ERROR_INSTALL_USEREXIT;
}
}
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// COpSvcStart // COpSvcStart
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
COpSvcStart::COpSvcStart(LPCWSTR pszService, int iTicks) : COpTypeSingleString(pszService, iTicks) COpSvcStart::COpSvcStart(LPCWSTR pszService, BOOL bWait, int iTicks) : COpSvcControl(pszService, bWait, iTicks)
{ {
} }
@ -96,15 +145,15 @@ HRESULT COpSvcStart::Execute(CSession *pSession)
SC_HANDLE hService; SC_HANDLE hService;
// Open the specified service. // Open the specified service.
hService = ::OpenServiceW(hSCM, m_sValue, SERVICE_START); hService = ::OpenServiceW(hSCM, m_sValue, SERVICE_START | (m_bWait ? SERVICE_QUERY_STATUS : 0));
if (hService) { if (hService) {
// Start the service. // Start the service.
if (::StartService(hService, 0, NULL)) { if (::StartService(hService, 0, NULL)) {
if (pSession->m_bRollbackEnabled) { dwError = m_bWait ? WaitForState(pSession, hService, SERVICE_START_PENDING, SERVICE_RUNNING) : NO_ERROR;
if (dwError == NO_ERROR && pSession->m_bRollbackEnabled) {
// Order rollback action to stop the service. // Order rollback action to stop the service.
pSession->m_olRollback.AddHead(new COpSvcStop(m_sValue)); pSession->m_olRollback.AddHead(new COpSvcStop(m_sValue));
} }
dwError = NO_ERROR;
} else { } else {
dwError = ::GetLastError(); dwError = ::GetLastError();
if (dwError == ERROR_SERVICE_ALREADY_RUNNING) { if (dwError == ERROR_SERVICE_ALREADY_RUNNING) {
@ -134,7 +183,7 @@ HRESULT COpSvcStart::Execute(CSession *pSession)
// COpSvcStop // COpSvcStop
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
COpSvcStop::COpSvcStop(LPCWSTR pszService, int iTicks) : COpTypeSingleString(pszService, iTicks) COpSvcStop::COpSvcStop(LPCWSTR pszService, BOOL bWait, int iTicks) : COpSvcControl(pszService, bWait, iTicks)
{ {
} }
@ -150,16 +199,16 @@ HRESULT COpSvcStop::Execute(CSession *pSession)
SC_HANDLE hService; SC_HANDLE hService;
// Open the specified service. // Open the specified service.
hService = ::OpenServiceW(hSCM, m_sValue, SERVICE_STOP); hService = ::OpenServiceW(hSCM, m_sValue, SERVICE_STOP | (m_bWait ? SERVICE_QUERY_STATUS : 0));
if (hService) { if (hService) {
SERVICE_STATUS ss; SERVICE_STATUS ss;
// Stop the service. // Stop the service.
if (::ControlService(hService, SERVICE_CONTROL_STOP, &ss)) { if (::ControlService(hService, SERVICE_CONTROL_STOP, &ss)) {
if (pSession->m_bRollbackEnabled) { dwError = m_bWait ? WaitForState(pSession, hService, SERVICE_STOP_PENDING, SERVICE_STOPPED) : NO_ERROR;
if (dwError == NO_ERROR && pSession->m_bRollbackEnabled) {
// Order rollback action to start the service. // Order rollback action to start the service.
pSession->m_olRollback.AddHead(new COpSvcStart(m_sValue)); pSession->m_olRollback.AddHead(new COpSvcStart(m_sValue));
} }
dwError = NO_ERROR;
} else { } else {
dwError = ::GetLastError(); dwError = ::GetLastError();
if (dwError == ERROR_SERVICE_NOT_ACTIVE) { if (dwError == ERROR_SERVICE_NOT_ACTIVE) {