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 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 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 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.
@ -198,8 +199,8 @@ All :: "$(LANG).$(PLAT).$(CFG).ServiceConfigure-1.idt"
"$(LANG).$(PLAT).$(CFG).ServiceConfigure-1.idt" : "Makefile" "..\..\..\include\MSIBuildCfg.mak"
-if exist $@ del /f /q $@
move /y << $@ > NUL
ServiceConfigure Name StartType Condition Sequence
s$(MSIBUILD_LENGTH_ID) l255 i4 S255 I2
ServiceConfigure Name StartType Control Condition Sequence
s$(MSIBUILD_LENGTH_ID) l255 i4 i2 S255 I2
ServiceConfigure ServiceConfigure
<<NOKEEP

View File

@ -7,6 +7,8 @@
#define MSICA_CERT_TICK_SIZE (4*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)
@ -207,12 +209,12 @@ UINT MSICA_API ServiceConfigEval(MSIHANDLE hInstall)
PMSIHANDLE hViewSC;
// 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) {
// Execute query!
uiResult = ::MsiViewExecute(hViewSC, NULL);
if (uiResult == NO_ERROR) {
int iStartType;
int iValue;
for (;;) {
PMSIHANDLE hRecord;
@ -226,7 +228,7 @@ UINT MSICA_API ServiceConfigEval(MSIHANDLE hInstall)
break;
// Read and evaluate service configuration condition.
uiResult = ::MsiRecordGetString(hRecord, 3, sValue);
uiResult = ::MsiRecordGetString(hRecord, 4, sValue);
if (uiResult != NO_ERROR) break;
condition = ::MsiEvaluateCondition(hInstall, sValue);
if (condition == MSICONDITION_FALSE)
@ -241,20 +243,44 @@ UINT MSICA_API ServiceConfigEval(MSIHANDLE hInstall)
if (uiResult != NO_ERROR) break;
// Read service start type.
iStartType = ::MsiRecordGetInteger(hRecord, 2);
if (iStartType == MSI_NULL_INTEGER) {
iValue = ::MsiRecordGetInteger(hRecord, 2);
if (iValue == MSI_NULL_INTEGER) {
uiResult = ERROR_INVALID_FIELD;
break;
}
if (iStartType >= 0) {
if (iValue >= 0) {
// 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.
::MsiRecordSetInteger(hRecordProg, 1, 3 );
::MsiRecordSetInteger(hRecordProg, 2, MSICA_SVC_SET_START_TICK_SIZE);
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);

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
////////////////////////////////////////////////////////////////////////////
class COpSvcStart : public COpTypeSingleString
class COpSvcStart : public COpSvcControl
{
public:
COpSvcStart(LPCWSTR pszService = L"", int iTicks = 0);
COpSvcStart(LPCWSTR pszService = L"", BOOL bWait = FALSE, int iTicks = 0);
virtual HRESULT Execute(CSession *pSession);
};
@ -492,10 +511,10 @@ public:
// COpSvcStop
////////////////////////////////////////////////////////////////////////////
class COpSvcStop : public COpTypeSingleString
class COpSvcStop : public COpSvcControl
{
public:
COpSvcStop(LPCWSTR pszService = L"", int iTicks = 0);
COpSvcStop(LPCWSTR pszService = L"", BOOL bWait = FALSE, int iTicks = 0);
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)
{
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(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;
// 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) {
// Start the service.
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.
pSession->m_olRollback.AddHead(new COpSvcStop(m_sValue));
}
dwError = NO_ERROR;
} else {
dwError = ::GetLastError();
if (dwError == ERROR_SERVICE_ALREADY_RUNNING) {
@ -134,7 +183,7 @@ HRESULT COpSvcStart::Execute(CSession *pSession)
// 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;
// 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) {
SERVICE_STATUS ss;
// Stop the service.
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.
pSession->m_olRollback.AddHead(new COpSvcStart(m_sValue));
}
dwError = NO_ERROR;
} else {
dwError = ::GetLastError();
if (dwError == ERROR_SERVICE_NOT_ACTIVE) {