/*++ Copyright (c) 1994-2000 Microsoft Corporation Module Name : app_pools.cpp Abstract: IIS Application Pools nodes Author: Sergei Antonov (sergeia) Project: Internet Services Manager Revision History: 11/03/2000 sergeia Initial creation --*/ #include "stdafx.h" #include "common.h" #include "inetprop.h" #include "InetMgrApp.h" #include "iisobj.h" #include "shts.h" #include "app_pool_sheet.h" #ifdef _DEBUG #undef THIS_FILE static char BASED_CODE THIS_FILE[] = __FILE__; #endif #define new DEBUG_NEW CAppPoolsContainer::CAppPoolsContainer( CIISMachine * pOwner, CIISService * pService ) : CIISMBNode(pOwner, SZ_MBN_APP_POOLS), m_pWebService(pService) { VERIFY(m_bstrDisplayName.LoadString(IDS_APP_POOLS)); } CAppPoolsContainer::~CAppPoolsContainer() { } /* static */ int CAppPoolsContainer::_rgnLabels[COL_TOTAL] = { IDS_RESULT_SERVICE_DESCRIPTION, IDS_RESULT_SERVICE_STATE, }; /* static */ int CAppPoolsContainer::_rgnWidths[COL_TOTAL] = { 180, 70, }; /* virtual */ HRESULT CAppPoolsContainer::EnumerateScopePane(HSCOPEITEM hParent) { CError err; CMetaEnumerator * pme = NULL; CString strPool; err = CreateEnumerator(pme); while (err.Succeeded()) { CAppPoolNode * pPool; err = pme->Next(strPool); if (err.Succeeded()) { TRACEEOLID("Enumerating node: " << strPool); CString key_type; CMetabasePath path(FALSE, pme->QueryMetaPath(), strPool); CMetaKey mk(pme, path); mk.QueryValue(MD_KEY_TYPE, key_type); if (mk.Succeeded() && 0 == key_type.CompareNoCase(_T("IIsApplicationPool"))) { if (NULL == (pPool = new CAppPoolNode(m_pOwner, this, strPool))) { err = ERROR_NOT_ENOUGH_MEMORY; break; } err = pPool->AddToScopePane(hParent); } } } SAFE_DELETE(pme); if (err.Win32Error() == ERROR_NO_MORE_ITEMS) { err.Reset(); } if (err.Failed()) { DisplayError(err); } SetInterfaceError(err); return err; } HRESULT CAppPoolsContainer::EnumerateAppPools(CPoolList * pList) { CError err; CMetaEnumerator * pme = NULL; CString strPool; err = CreateEnumerator(pme); while (err.Succeeded()) { err = pme->Next(strPool); if (err.Succeeded()) { CString key_type; CMetabasePath path(FALSE, pme->QueryMetaPath(), strPool); CMetaKey mk(pme, path); mk.QueryValue(MD_KEY_TYPE, key_type); if (mk.Succeeded() && 0 == key_type.CompareNoCase(_T("IIsApplicationPool"))) { CAppPoolNode * pPool; if (NULL == (pPool = new CAppPoolNode(m_pOwner, this, strPool))) { err = ERROR_NOT_ENOUGH_MEMORY; break; } pList->AddTail(pPool); } } } SAFE_DELETE(pme); if (err.Win32Error() == ERROR_NO_MORE_ITEMS) { err.Reset(); } return err; } /* virtual */ void CAppPoolsContainer::InitializeChildHeaders( IN LPHEADERCTRL lpHeader ) /*++ Routine Description: Build result view for immediate descendant type Arguments: LPHEADERCTRL lpHeader : Header control Return Value: None --*/ { CIISObject::BuildResultView(lpHeader, COL_TOTAL, _rgnLabels, _rgnWidths); } /* virtual */ HRESULT CAppPoolsContainer::CreatePropertyPages( IN LPPROPERTYSHEETCALLBACK lpProvider, IN LONG_PTR handle, IN IUnknown * pUnk, IN DATA_OBJECT_TYPES type ) /*++ Routine Description: Create the property pages for the given object Arguments: LPPROPERTYSHEETCALLBACK lpProvider : Provider LONG_PTR handle : Handle. IUnknown * pUnk, DATA_OBJECT_TYPES type Return Value: HRESULT --*/ { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); CError err; CComBSTR path; err = BuildMetaPath(path); if (err.Failed()) return err; CAppPoolSheet * pSheet = new CAppPoolSheet( QueryAuthInfo(), path, GetMainWindow(), (LPARAM)this, handle ); if (pSheet != NULL) { pSheet->SetModeless(); // err = AddMMCPage(lpProvider, new CAppPoolGeneral(pSheet)); err = AddMMCPage(lpProvider, new CAppPoolRecycle(pSheet)); err = AddMMCPage(lpProvider, new CAppPoolPerf(pSheet)); err = AddMMCPage(lpProvider, new CAppPoolHealth(pSheet)); err = AddMMCPage(lpProvider, new CAppPoolDebug(pSheet)); err = AddMMCPage(lpProvider, new CAppPoolIdent(pSheet)); } return err; } /* virtual */ HRESULT CAppPoolsContainer::BuildMetaPath( OUT CComBSTR & bstrPath ) const /*++ Routine Description: Recursively build up the metabase path from the current node and its parents. We cannot use CIISMBNode method because AppPools is located under w3svc, but rendered after machine. Arguments: CComBSTR & bstrPath : Returns metabase path Return Value: HRESULT --*/ { HRESULT hr = S_OK; ASSERT(m_pWebService != NULL); hr = m_pWebService->BuildMetaPath(bstrPath); if (SUCCEEDED(hr)) { bstrPath.Append(_cszSeparator); bstrPath.Append(QueryNodeName()); return hr; } // // No service node // ASSERT_MSG("No WebService pointer"); return E_UNEXPECTED; } HRESULT CAppPoolsContainer::QueryDefaultPoolId(CString& id) // // Returns pool id which is set on master node for web service // { CError err; CComBSTR path; CString service; BuildMetaPath(path); CMetabasePath::GetServicePath(path, service); CMetaKey mk(QueryAuthInfo(), service, METADATA_PERMISSION_READ); err = mk.QueryResult(); if (err.Succeeded()) { err = mk.QueryValue(MD_APP_APPPOOL_ID, id); } return err; } /* virtual */ HRESULT CAppPoolsContainer::AddMenuItems( IN LPCONTEXTMENUCALLBACK lpContextMenuCallback, IN OUT long * pInsertionAllowed, IN DATA_OBJECT_TYPES type ) { ASSERT_READ_PTR(lpContextMenuCallback); // // Add base menu items // HRESULT hr = CIISObject::AddMenuItems( lpContextMenuCallback, pInsertionAllowed, type ); if (SUCCEEDED(hr)) { ASSERT(pInsertionAllowed != NULL); if (IsAdministrator() && (*pInsertionAllowed & CCM_INSERTIONALLOWED_NEW) != 0) { AddMenuSeparator(lpContextMenuCallback); AddMenuItemByCommand(lpContextMenuCallback, IDM_NEW_APP_POOL); } } return hr; } HRESULT CAppPoolsContainer::Command( long lCommandID, CSnapInObjectRootBase * pObj, DATA_OBJECT_TYPES type ) { HRESULT hr = S_OK; CString name; switch (lCommandID) { case IDM_NEW_APP_POOL: if ( SUCCEEDED(hr = AddAppPool(pObj, type, this, name)) && !name.IsEmpty() ) { hr = InsertNewPool(name); } break; // // Pass on to base class // default: hr = CIISMBNode::Command(lCommandID, pObj, type); } return hr; } HRESULT CAppPoolsContainer::InsertNewPool(CString& id) { CError err; // Now we should insert and select this new site CAppPoolNode * pPool = new CAppPoolNode(m_pOwner, this, id); if (pPool != NULL) { err = pPool->RefreshData(); if (err.Succeeded()) { // If item is not expanded we will get error and no effect if (!IsExpanded()) { SelectScopeItem(); IConsoleNameSpace2 * pConsole = (IConsoleNameSpace2 *)GetConsoleNameSpace(); pConsole->Expand(QueryScopeItem()); } err = pPool->AddToScopePaneSorted(QueryScopeItem(), FALSE); if (err.Succeeded()) { VERIFY(SUCCEEDED(pPool->SelectScopeItem())); } } } else { err = ERROR_NOT_ENOUGH_MEMORY; } return err; } //////////////////////////////////////////////////////////////////////////////// // CAppPoolNode implementation // // App Pool Result View definition // /* static */ int CAppPoolNode::_rgnLabels[COL_TOTAL] = { IDS_RESULT_SERVICE_DESCRIPTION, IDS_RESULT_PATH, }; /* static */ int CAppPoolNode::_rgnWidths[COL_TOTAL] = { 180, 200, }; CAppPoolNode::CAppPoolNode( CIISMachine * pOwner, CAppPoolsContainer * pContainer, LPCTSTR name ) : CIISMBNode(pOwner, name), m_pContainer(pContainer) { } CAppPoolNode::~CAppPoolNode() { } #if 0 // This is too expensive BOOL CAppPoolNode::IsDeletable() const { // We could delete node if it is empty and it is not default app pool BOOL bRes = TRUE; CComBSTR path; CStringListEx apps; BuildMetaPath(path); CIISMBNode * that = (CIISMBNode *)this; CIISAppPool pool(that->QueryAuthInfo(), (LPCTSTR)path); HRESULT hr = pool.EnumerateApplications(apps); bRes = (SUCCEEDED(hr) && apps.GetCount() == 0); if (bRes) { CString buf; hr = m_pContainer->QueryDefaultPoolId(buf); bRes = buf.CompareNoCase(QueryNodeName()) != 0; } return bRes; } #endif HRESULT CAppPoolNode::DeleteNode(IResultData * pResult) { CError err; CComBSTR path; BuildMetaPath(path); CIISAppPool pool(QueryAuthInfo(), (LPCTSTR)path); err = pool.Delete(GetNodeName()); if (err.Succeeded()) { err = RemoveScopeItem(); } if (err.Win32Error() == ERROR_NOT_EMPTY) { CString msg; msg.LoadString(IDS_ERR_NONEMPTY_APPPOOL); AfxMessageBox(msg); } else { err.MessageBoxOnFailure(); } return err; } /* virtual */ HRESULT CAppPoolNode::BuildMetaPath(CComBSTR & bstrPath) const { HRESULT hr = S_OK; ASSERT(m_pContainer != NULL); hr = m_pContainer->BuildMetaPath(bstrPath); if (SUCCEEDED(hr)) { bstrPath.Append(_cszSeparator); bstrPath.Append(QueryNodeName()); return hr; } // // No service node // ASSERT_MSG("No pointer to container"); return E_UNEXPECTED; } /* virtual */ LPOLESTR CAppPoolNode::GetResultPaneColInfo( IN int nCol ) /*++ Routine Description: Return result pane string for the given column number Arguments: int nCol : Column number Return Value: String --*/ { switch(nCol) { case COL_DESCRIPTION: return QueryDisplayName(); case COL_STATE: return OLESTR(""); } ASSERT_MSG("Bad column number"); return OLESTR(""); } /* virtual */ int CAppPoolNode::QueryImage() const /*++ Routine Description: Return bitmap index for the site Arguments: None Return Value: Bitmap index --*/ { return iFolder; } /* virtual */ LPOLESTR CAppPoolNode::QueryDisplayName() /*++ Routine Description: Return primary display name of this site. Arguments: None Return Value: The display name --*/ { if (m_strDisplayName.IsEmpty()) { CComBSTR path; BuildMetaPath(path); CMetaKey mk(QueryInterface(), path); if (mk.Succeeded()) { mk.QueryValue(MD_APPPOOL_FRIENDLY_NAME, m_strDisplayName); } } return (LPTSTR)(LPCTSTR)m_strDisplayName; } /*virtual*/ HRESULT CAppPoolNode::RenameItem(LPOLESTR new_name) { CComBSTR path; CError err; if (new_name != NULL && lstrlen(new_name) > 0) { BuildMetaPath(path); CMetaKey mk(QueryInterface(), path, METADATA_PERMISSION_WRITE); CError err(mk.QueryResult()); if (err.Succeeded()) { err = mk.SetValue(MD_APPPOOL_FRIENDLY_NAME, CString(new_name)); if (err.Succeeded()) { m_strDisplayName = new_name; } } } return err; } /* virtual */ HRESULT CAppPoolNode::RefreshData() /*++ Routine Description: Refresh relevant configuration data required for display. Arguments: None Return Value: HRESULT --*/ { CError err; // CWaitCursor wait; CComBSTR path; CMetaKey * pKey = NULL; do { ASSERT_PTR(_lpConsoleNameSpace); err = BuildMetaPath(path); if (err.Failed()) { break; } BOOL fContinue = TRUE; while (fContinue) { fContinue = FALSE; if (NULL == (pKey = new CMetaKey(QueryInterface(), path))) { TRACEEOLID("RefreshData: Out Of Memory"); err = ERROR_NOT_ENOUGH_MEMORY; break; } err = pKey->QueryResult(); if (IsLostInterface(err)) { SAFE_DELETE(pKey); fContinue = OnLostInterface(err); } } if (err.Failed()) { break; } CAppPoolProps pool(pKey, _T("")); err = pool.LoadData(); if (err.Failed()) { break; } // Assign the data } while(FALSE); SAFE_DELETE(pKey); return err; } /* virtual */ int CAppPoolNode::CompareResultPaneItem( IN CIISObject * pObject, IN int nCol ) /*++ Routine Description: Compare two CIISObjects on sort item criteria Arguments: CIISObject * pObject : Object to compare against int nCol : Column number to sort on Return Value: 0 if the two objects are identical <0 if this object is less than pObject >0 if this object is greater than pObject --*/ { ASSERT_READ_PTR(pObject); if (nCol == 0) { return CompareScopeItem(pObject); } // // First criteria is object type // int n1 = QuerySortWeight(); int n2 = pObject->QuerySortWeight(); if (n1 != n2) { return n1 - n2; } // // Both are CAppPoolNode objects // CAppPoolNode * pPool = (CAppPoolNode *)pObject; switch(nCol) { case COL_DESCRIPTION: case COL_STATE: default: // // Lexical sort // return ::lstrcmpi( GetResultPaneColInfo(nCol), pObject->GetResultPaneColInfo(nCol) ); } } /* virtual */ void CAppPoolNode::InitializeChildHeaders( IN LPHEADERCTRL lpHeader ) /*++ Routine Description: Build result view for immediate descendant type Arguments: LPHEADERCTRL lpHeader : Header control Return Value: None --*/ { CIISObject::BuildResultView(lpHeader, COL_TOTAL, _rgnLabels, _rgnWidths); } /* virtual */ HRESULT CAppPoolNode::EnumerateScopePane( IN HSCOPEITEM hParent ) /*++ Routine Description: Enumerate scope child items. Arguments: HSCOPEITEM hParent : Parent console handle Return Value: HRESULT --*/ { HRESULT hr = S_OK; CComBSTR path; BuildMetaPath(path); CIISAppPool pool(QueryAuthInfo(), path); if (pool.Succeeded()) { CStringListEx apps; hr = pool.EnumerateApplications(apps); if (SUCCEEDED(hr) && apps.GetCount() > 0) { POSITION pos = apps.GetHeadPosition(); while ( pos != NULL) { CString app = apps.GetNext(pos); DWORD i = CMetabasePath::GetInstanceNumber(app); if (i > 0) { CString name; CMetabasePath::CleanMetaPath(app); CMetabasePath::GetLastNodeName(app, name); CApplicationNode * app_node = new CApplicationNode( GetOwner(), app, name); if (app_node != NULL) { app_node->AddToScopePane(m_hScopeItem, TRUE, TRUE, FALSE); } else hr = ERROR_NOT_ENOUGH_MEMORY; } } } } return hr; } /* virtual */ HRESULT CAppPoolNode::CreatePropertyPages( IN LPPROPERTYSHEETCALLBACK lpProvider, IN LONG_PTR handle, IN IUnknown * pUnk, IN DATA_OBJECT_TYPES type ) /*++ Routine Description: Create the property pages for the given object Arguments: LPPROPERTYSHEETCALLBACK lpProvider : Provider LONG_PTR handle : Handle. IUnknown * pUnk, DATA_OBJECT_TYPES type Return Value: HRESULT --*/ { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); CComBSTR path; CError err(BuildMetaPath(path)); if (err.Succeeded()) { CAppPoolSheet * pSheet = new CAppPoolSheet( QueryAuthInfo(), path, GetMainWindow(), (LPARAM)this, handle ); if (pSheet != NULL) { pSheet->SetModeless(); // err = AddMMCPage(lpProvider, new CAppPoolGeneral(pSheet)); err = AddMMCPage(lpProvider, new CAppPoolRecycle(pSheet)); err = AddMMCPage(lpProvider, new CAppPoolPerf(pSheet)); err = AddMMCPage(lpProvider, new CAppPoolHealth(pSheet)); err = AddMMCPage(lpProvider, new CAppPoolDebug(pSheet)); err = AddMMCPage(lpProvider, new CAppPoolIdent(pSheet)); } } err.MessageBoxOnFailure(); return err; } /* virtual */ HRESULT CAppPoolNode::AddMenuItems( IN LPCONTEXTMENUCALLBACK lpContextMenuCallback, IN OUT long * pInsertionAllowed, IN DATA_OBJECT_TYPES type ) { ASSERT_READ_PTR(lpContextMenuCallback); // // Add base menu items // HRESULT hr = CIISObject::AddMenuItems( lpContextMenuCallback, pInsertionAllowed, type ); if (SUCCEEDED(hr)) { ASSERT(pInsertionAllowed != NULL); if ((*pInsertionAllowed & CCM_INSERTIONALLOWED_NEW) != 0) { AddMenuSeparator(lpContextMenuCallback); AddMenuItemByCommand(lpContextMenuCallback, IDM_NEW_APP_POOL); } } return hr; } /* virtual */ HRESULT CAppPoolNode::Command( IN long lCommandID, IN CSnapInObjectRootBase * pObj, IN DATA_OBJECT_TYPES type ) /*++ Routine Description: Handle command from context menu. Arguments: long lCommandID : Command ID CSnapInObjectRootBase * pObj : Base object DATA_OBJECT_TYPES type : Data object type Return Value: HRESULT --*/ { HRESULT hr = S_OK; CString name; switch (lCommandID) { case IDM_NEW_APP_POOL: if ( SUCCEEDED(hr = AddAppPool(pObj, type, m_pContainer, name)) && !name.IsEmpty() ) { hr = m_pContainer->InsertNewPool(name); } break; // // Pass on to base class // default: hr = CIISMBNode::Command(lCommandID, pObj, type); } return hr; } //////////////////////////////////////////////////////////////////////// LPOLESTR CApplicationNode::QueryDisplayName() /*++ Routine Description: Return primary display name of this site. Arguments: None Return Value: The display name --*/ { if (m_strDisplayName.IsEmpty()) { CMetaKey mk(QueryInterface(), m_meta_path); if (mk.Succeeded()) { mk.QueryValue(MD_APP_FRIENDLY_NAME, m_strDisplayName); if (m_strDisplayName.IsEmpty()) { m_strDisplayName = QueryNodeName(); } } } return (LPTSTR)(LPCTSTR)m_strDisplayName; } HRESULT CApplicationNode::BuildMetaPath(CComBSTR& path) const { path = m_meta_path; return S_OK; } LPOLESTR CApplicationNode::GetResultPaneColInfo( IN int nCol ) /*++ Routine Description: Return result pane string for the given column number Arguments: int nCol : Column number Return Value: String --*/ { switch(nCol) { case COL_ALIAS: return QueryDisplayName(); case COL_PATH: { CString buf; return (LPTSTR)(LPCTSTR)FriendlyAppRoot(m_meta_path, buf); } } ASSERT_MSG("Bad column number"); return OLESTR(""); } LPCTSTR CApplicationNode::FriendlyAppRoot( LPCTSTR lpAppRoot, CString & strFriendly ) /*++ Routine Description: Convert the metabase app root path to a friendly display name format. Arguments: LPCTSTR lpAppRoot : App root CString & strFriendly : Output friendly app root format Return Value: Reference to the output string Notes: App root must have been cleaned from WAM format prior to calling this function (see first ASSERT below) --*/ { if (lpAppRoot != NULL && *lpAppRoot != 0) { // // Make sure we cleaned up WAM format // ASSERT(*lpAppRoot != _T('/')); strFriendly.Empty(); CInstanceProps prop(QueryAuthInfo(), lpAppRoot); HRESULT hr = prop.LoadData(); if (SUCCEEDED(hr)) { CString root, tail; strFriendly.Format(_T("<%s>"), prop.GetDisplayText(root)); CMetabasePath::GetRootPath(lpAppRoot, root, &tail); if (!tail.IsEmpty()) { // // Add rest of dir path // strFriendly += _T("/"); strFriendly += tail; } // // Now change forward slashes in the path to backward slashes // // CvtPathToDosStyle(strFriendly); return strFriendly; } } // // Bogus // VERIFY(strFriendly.LoadString(IDS_APPROOT_UNKNOWN)); return strFriendly; } //////////////////////////////////////////////////////////////////////////