//Copyright (c) 1998 - 1999 Microsoft Corporation /******************************************************************************* * * server.cpp * * implementation of the CServer class * * *******************************************************************************/ #include "stdafx.h" #include "winadmin.h" #include "admindoc.h" #include "dialogs.h" #include // for alloca used by Unicode conversion macros #include // for Unicode conversion macros static int _convert; #include #include #include "..\..\inc\utilsub.h" #include "procs.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ////////////////////////////////////////////////////////////////////////////////////////// // // CServer Member Functions // ////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // CServer::CServer // // Constructor for the server class CServer::CServer(CDomain *pDomain, TCHAR *name, BOOL bFoundLater, BOOL bConnect) { ASSERT(name); m_ServerFlags = ULONG(0); m_pDomain = pDomain; if(bFoundLater) SetFoundLater(); //m_State = SS_NONE; m_PreviousState = SS_NONE; m_hTreeItem = NULL; m_hThisServer = NULL; m_hFavTree = NULL; m_pBackgroundThread = NULL; m_bThreadAlive = FALSE; m_pExtensionInfo = NULL; m_pExtServerInfo = NULL; m_pRegistryInfo = NULL; m_fManualFind = FALSE; // Don't call SetState because we don't want to send a message to the views m_State = SS_NOT_CONNECTED; // save off the server name wcscpy(m_Name, name); // Dummy up an ExtServerInfo structure // This is to make it easier for code that tries // to access this structure before the extension DLL // has provided it m_pExtServerInfo = ((CWinAdminDoc*)((CWinAdminApp*)AfxGetApp())->GetDocument())->GetDefaultExtServerInfo(); if(bConnect) Connect(); } // end CServer::CServer ///////////////////////////////////////////////////////////////////////////// // CServer::~CServer // // Destructor for the server class CServer::~CServer() { // Disconnect(); m_hTreeItem = NULL; m_hFavTree = NULL; m_hThisServer = NULL; } // end CServer::~CServer ///////////////////////////////////////////////////////////////////////////// // CServer::RemoveWinStationProcesses // // remove all the processes for a given WinStation void CServer::RemoveWinStationProcesses(CWinStation *pWinStation) { ASSERT(pWinStation); CWinAdminDoc *pDoc = (CWinAdminDoc*)((CWinAdminApp*)AfxGetApp())->GetDocument(); CObList TempList; LockProcessList(); POSITION pos = m_ProcessList.GetHeadPosition(); while(pos) { POSITION pos2 = pos; CProcess *pProcess = (CProcess*)m_ProcessList.GetNext(pos); if(pProcess->GetWinStation() == pWinStation) { // Add the process to our temporary list TempList.AddTail(pProcess); // Remove the process from the list of processes pProcess = (CProcess*)m_ProcessList.GetAt(pos2); m_ProcessList.RemoveAt(pos2); } } UnlockProcessList(); pos = TempList.GetHeadPosition(); while(pos) { POSITION pos2 = pos; CProcess *pProcess = (CProcess*)TempList.GetNext(pos); // Send a message to remove the Process from the view CFrameWnd *p = (CFrameWnd*)pDoc->GetMainWnd(); if(p && ::IsWindow(p->GetSafeHwnd())) { p->SendMessage(WM_ADMIN_REMOVE_PROCESS, 0, (LPARAM)pProcess); } delete pProcess; } TempList.RemoveAll(); } // end CServer::RemoveWinStationProcesses ///////////////////////////////////////////////////////////////////////////// // CServer::ClearAllSelected // // Clears the WAF_SELECTED bit in all of this server's lists // void CServer::ClearAllSelected() { // Clear the WinStation list WAF_SELECTED flags // Iterate through the WinStation list LockWinStationList( ); POSITION pos = m_WinStationList.GetHeadPosition(); while(pos) { CWinStation *pWinStation = (CWinStation*)m_WinStationList.GetNext(pos); pWinStation->ClearSelected(); } m_NumWinStationsSelected = 0; UnlockWinStationList( ); LockProcessList(); // Clear the Process list PF_SELECTED flags // Iterate through the Process list pos = m_ProcessList.GetHeadPosition(); while(pos) { CProcess *pProcess = (CProcess*)m_ProcessList.GetNext(pos); pProcess->ClearSelected(); } m_NumProcessesSelected = 0; UnlockProcessList( ); } // end CServer::ClearAllSelected //Function - htol - start //=========================================================================== // //Description: converts a hex string to a uLONG, handles "0x..." // // //Parameters: // //Return Value: // //Global Variables Affected: // //Remarks: (Side effects, Assumptions, Warnings...) // // //--------------------------------------------------------------------------- WCHAR hextable[] = L"0123456789ABCDEF"; ULONG htol(WCHAR *hexString) { ULONG retValue = 0L; // convert the string to upper case _wcsupr(hexString); WCHAR *p = hexString; while (*p == L' ') p++; while(*p) { if(*p == L'X') { retValue = 0L; } else if ((*p == L' ') || (*p == L'\n') || (*p == L'\r')) break; else if(*p == L'.') { // skip over a decimal point } else { retValue <<= 4; for(int i = 0; i < 16; i++) { if(hextable[i] == *p) { retValue += i; break; } } } *p++; } return retValue; } static TCHAR szMicrosoftKey[] = TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"); static TCHAR szInstallDate[] = TEXT("InstallDate"); static TCHAR szCSDVersion[] = TEXT("CSDVersion"); static TCHAR szCurrentVersion[] = TEXT("CurrentVersion"); static TCHAR szCurrentBuildNumber[] = TEXT("CurrentBuildNumber"); static TCHAR szCurrentProductName[] = TEXT("ProductName"); static TCHAR szHotfixKey[] = TEXT("HOTFIX"); static TCHAR szValid[] = TEXT("Valid"); static TCHAR szInstalledOn[] = TEXT("Installed On"); static TCHAR szInstalledBy[] = TEXT("Installed By"); #define REG_CONTROL_CITRIX REG_CONTROL L"\\Citrix" ///////////////////////////////////////////////////////////////////////////// // CServer::BuildRegistryInfo // // Go out and fill in the registry info structure BOOL CServer::BuildRegistryInfo() { DWORD dwType, dwSize; HKEY hKeyServer; HKEY hKey; if(!IsServerSane()) return FALSE; m_pRegistryInfo = new ServerRegistryInfo; if(!m_pRegistryInfo) return FALSE; memset(m_pRegistryInfo, 0, sizeof(ServerRegistryInfo)); TCHAR Buffer[128]; Buffer[0] = TEXT('\\'); Buffer[1] = TEXT('\\'); Buffer[2] = TEXT('\0'); wcscpy(Buffer+2, m_Name); /* * Connect to the server's registry * (avoid using RPC when the server is the local machine.) */ if(RegConnectRegistry(IsCurrentServer() ? NULL : Buffer, HKEY_LOCAL_MACHINE, &hKeyServer) != ERROR_SUCCESS) return FALSE; /* * Fetch MS information. */ if(RegOpenKeyEx(hKeyServer, szMicrosoftKey, 0, KEY_READ, &hKey) != ERROR_SUCCESS) { RegCloseKey(hKeyServer); return FALSE; } dwSize = sizeof(m_pRegistryInfo->InstallDate); if(RegQueryValueEx(hKey, szInstallDate, NULL, &dwType, (LPBYTE)&m_pRegistryInfo->InstallDate, &dwSize) != ERROR_SUCCESS) { m_pRegistryInfo->InstallDate = 0xFFFFFFFF; } // REMARK: we should check the returned codes for every RegQueryValueEx dwSize = sizeof(m_pRegistryInfo->ServicePackLevel); RegQueryValueEx(hKey, szCSDVersion, NULL, &dwType, (LPBYTE)&m_pRegistryInfo->ServicePackLevel, &dwSize); dwSize = sizeof(m_pRegistryInfo->MSVersion); RegQueryValueEx(hKey, szCurrentVersion, NULL, &dwType, (LPBYTE)&m_pRegistryInfo->MSVersion, &dwSize); m_pRegistryInfo->MSVersionNum = _wtol(m_pRegistryInfo->MSVersion); dwSize = sizeof(m_pRegistryInfo->MSBuild); RegQueryValueEx(hKey, szCurrentBuildNumber, NULL, &dwType, (LPBYTE)&m_pRegistryInfo->MSBuild, &dwSize); dwSize = sizeof(m_pRegistryInfo->MSProductName); RegQueryValueEx(hKey, szCurrentProductName, NULL, &dwType, (LPBYTE)&m_pRegistryInfo->MSProductName, &dwSize); HKEY hKeyHotfix; if(RegOpenKeyEx(hKey, szHotfixKey, 0, KEY_READ, &hKeyHotfix) == ERROR_SUCCESS) { DWORD Index = 0; FILETIME LastWriteTime; dwSize = sizeof(Buffer) / sizeof( TCHAR ); while(RegEnumKeyEx(hKeyHotfix, Index, Buffer, &dwSize, NULL, NULL, NULL, &LastWriteTime) == ERROR_SUCCESS) { HKEY hKeySingleHotfix; if(RegOpenKeyEx(hKeyHotfix, Buffer, 0, KEY_READ, &hKeySingleHotfix) == ERROR_SUCCESS) { // Create a CHotFix object CHotfix *pHotfix = new CHotfix; if(pHotfix) { // Copy the Hotfix name // Get rid of the WF: if it's there if(wcsncmp(Buffer, TEXT("WF:"), 3) == 0) { wcscpy(pHotfix->m_Name, &Buffer[3]); } else wcscpy(pHotfix->m_Name, Buffer); // Get the Valid entry dwSize = sizeof(&pHotfix->m_Valid); if(RegQueryValueEx(hKeySingleHotfix, szValid, NULL, &dwType, (LPBYTE)&pHotfix->m_Valid, &dwSize) != ERROR_SUCCESS) { pHotfix->m_Valid = 0L; } // Get the Installed On entry dwSize = sizeof(&pHotfix->m_InstalledOn); if(RegQueryValueEx(hKeySingleHotfix, szInstalledOn, NULL, &dwType, (LPBYTE)&pHotfix->m_InstalledOn, &dwSize) != ERROR_SUCCESS) { pHotfix->m_InstalledOn = 0xFFFFFFFF; } // Get the Installed By entry dwSize = sizeof(pHotfix->m_InstalledBy); if(RegQueryValueEx(hKeySingleHotfix, szInstalledBy, NULL, &dwType, (LPBYTE)pHotfix->m_InstalledBy, &dwSize) != ERROR_SUCCESS) { pHotfix->m_InstalledBy[0] = '\0'; } pHotfix->m_pServer = this; m_HotfixList.AddTail(pHotfix); RegCloseKey(hKeySingleHotfix); } } dwSize = sizeof(Buffer) / sizeof( TCHAR ); Index++; } RegCloseKey(hKeyHotfix); } RegCloseKey(hKey); if (m_pRegistryInfo->MSVersionNum < 5) // only for TS 4.0 { /* * Fetch Citrix information. */ // Look in the new location LONG result = RegOpenKeyEx(hKeyServer, REG_CONTROL_TSERVER, 0, KEY_READ, &hKey); if(result != ERROR_SUCCESS) { // Look in the old location result = RegOpenKeyEx(hKeyServer, REG_CONTROL_CITRIX, 0, KEY_READ, &hKey); } if(result != ERROR_SUCCESS) { RegCloseKey(hKeyServer); return FALSE; } dwSize = sizeof(m_pRegistryInfo->CTXProductName); RegQueryValueEx(hKey, REG_CITRIX_PRODUCTNAME, NULL, &dwType, (LPBYTE)m_pRegistryInfo->CTXProductName, &dwSize); dwSize = sizeof(m_pRegistryInfo->CTXVersion); RegQueryValueEx(hKey, REG_CITRIX_PRODUCTVERSION, NULL, &dwType, (LPBYTE)m_pRegistryInfo->CTXVersion, &dwSize); m_pRegistryInfo->CTXVersionNum = htol(m_pRegistryInfo->CTXVersion); dwSize = sizeof(m_pRegistryInfo->CTXBuild); RegQueryValueEx(hKey, REG_CITRIX_PRODUCTBUILD, NULL, &dwType, (LPBYTE)m_pRegistryInfo->CTXBuild, &dwSize); RegCloseKey(hKey); } else // for NT 5.0 and beyond, do not query the registry { //REMARK: we should get rid of all this. wcscpy(m_pRegistryInfo->CTXProductName, m_pRegistryInfo->MSProductName); wcscpy(m_pRegistryInfo->CTXVersion, m_pRegistryInfo->MSVersion); m_pRegistryInfo->CTXVersionNum = m_pRegistryInfo->MSVersionNum; wcscpy(m_pRegistryInfo->CTXBuild, m_pRegistryInfo->MSBuild); } RegCloseKey(hKeyServer); // Set the flag to say the info is valid SetRegistryInfoValid(); return TRUE; } // end CServer::BuildRegistryInfo ///////////////////////////////////////////////////////////////////////////// // CServer::AddWinStation // // Add a WinStation to the Server's WinStationList in // sorted order // NOTE: The list should be NOT be locked by the caller // void CServer::AddWinStation(CWinStation *pNewWinStation) { ASSERT(pNewWinStation); LockWinStationList(); ODS( L"CServer!AddWinStation\n" ); BOOLEAN bAdded = FALSE; POSITION pos, oldpos; int Index; // Traverse the WinStationList and insert this new WinStation, // keeping the list sorted by Sort Order, then Protocol. for(Index = 0, pos = m_WinStationList.GetHeadPosition(); pos != NULL; Index++) { oldpos = pos; CWinStation *pWinStation = (CWinStation*)m_WinStationList.GetNext(pos); if((pWinStation->GetSortOrder() > pNewWinStation->GetSortOrder()) || ((pWinStation->GetSortOrder() == pNewWinStation->GetSortOrder()) && (pWinStation->GetSdClass() > pNewWinStation->GetSdClass()))) { // The new object belongs before the current list object. m_WinStationList.InsertBefore(oldpos, pNewWinStation); bAdded = TRUE; break; } } // If we haven't yet added the WinStation, add it now to the tail // of the list. if(!bAdded) { m_WinStationList.AddTail(pNewWinStation); } UnlockWinStationList(); } // end CServer::AddWinStation ///////////////////////////////////////////////////////////////////////////// // CServer::Connect // // Connect to the server // BOOL CServer::Connect() { m_NumProcessesSelected = 0; m_NumWinStationsSelected = 0; m_pExtServerInfo = NULL; BOOL bResult = FALSE; if(m_State != SS_NOT_CONNECTED ) { return FALSE; } // Fire off the background thread for this server LockThreadAlive(); if(m_pBackgroundThread == NULL) { ServerProcInfo *pProcInfo = new ServerProcInfo; if(pProcInfo) { pProcInfo->pServer = this; pProcInfo->pDoc = (CWinAdminDoc*)((CWinAdminApp*)AfxGetApp())->GetDocument(); m_BackgroundContinue = TRUE; m_bThreadAlive = FALSE; m_pBackgroundThread = AfxBeginThread((AFX_THREADPROC)CServer::BackgroundThreadProc, pProcInfo, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED, NULL ); if( m_pBackgroundThread == NULL ) { ODS( L"CServer!Connect possibly low resources no thread created\n" ); delete pProcInfo; return FALSE; } m_pBackgroundThread->m_bAutoDelete = FALSE; if (m_pBackgroundThread->ResumeThread() <= 1) { bResult = TRUE; } } } UnlockThreadAlive(); return TRUE; } ///////////////////////////////////////////////////////////////////////////// // CServer::Disconnect // // Disconnect from the server // void CServer::Disconnect() { // not a good idea for an ods CWinAdminDoc *pDoc = (CWinAdminDoc*)((CWinAdminApp*)AfxGetApp())->GetDocument(); SetState(SS_DISCONNECTING); // If there is an extension DLL, let it cleanup anything it added to this Server LPFNEXSERVERCLEANUPPROC CleanupProc = ((CWinAdminApp*)AfxGetApp())->GetExtServerCleanupProc(); if(CleanupProc && m_pExtensionInfo) { (*CleanupProc)(m_pExtensionInfo); m_pExtensionInfo = NULL; } // Tell the background thread to terminate and // wait for it to do so. LockThreadAlive(); ClearBackgroundContinue(); if( !m_pBackgroundThread ) { // Either there is no background thread at all, // or some other thread is taking care of terminating it. // Just do nothing. //ODS( L"TSADMIN:CServer::Disconnect UnlockThreadAlive\n" ); UnlockThreadAlive(); } else { if( m_bThreadAlive ) { // the thread is alive. Tell him to exit. // keep temporary pointer and handle CWinThread *pBackgroundThread = m_pBackgroundThread; HANDLE hThread = m_pBackgroundThread->m_hThread; // Clear the pointer before releasing the lock m_pBackgroundThread = NULL; // Give the thread a chance to exit UnlockThreadAlive(); // Force him out of waiting for an event if he is ULONG WSEventFlags; if(IsHandleGood()) { ODS( L"TSADMIN:CServer::Disconnect Handle is good flush events\n" ); WinStationWaitSystemEvent(m_Handle, WEVENT_FLUSH, &WSEventFlags); } if( hThread ) // It should always be TRUE { // If this server object is waiting for RPC to timeout, // just kill the thread if( m_PreviousState == SS_NOT_CONNECTED ) { ODS( L"TSADMIN:CServer::Disconnect Previous state not connected termthread\n" ); if( WaitForSingleObject( hThread , 100 ) == WAIT_TIMEOUT ) { TerminateThread(hThread, 0); } } // For all other threads, wait a second and then kill it else if( WaitForSingleObject( hThread , 1000 ) == WAIT_TIMEOUT ) { ODS( L"TSADMIN CServer!Disconnect prevstate was !not_connected termthread\n" ); TerminateThread(hThread, 0); } WaitForSingleObject(hThread, INFINITE); } else { ASSERT(FALSE); } // delete the CWinThread object ODS( L"TSADMIN:CServer::Disconnect delete CWinThread Object m_bThread == TRUE\n" ); delete pBackgroundThread; } else { // the thread is dead or will be dead very soon. Just do the cleanup. ODS( L"TSADMIN:CServer::Disconnect delete CWinThread Object m_bThread == FALSE\n" ); delete m_pBackgroundThread; m_pBackgroundThread = NULL; UnlockThreadAlive(); } } if(IsHandleGood()) { ODS( L"TSADMIN:CServer::Disconnect WinStationCloseServer\n" ); WinStationCloseServer(m_Handle); m_Handle = NULL; } LockWinStationList(); CObList TempList; // Iterate through the WinStation list // Move all the WinStations to a temporary list so that // we don't have to have the WinStationList locked while // sending the WM_ADMIN_REMOVE_WINSTATION message to the views. POSITION pos = m_WinStationList.GetHeadPosition(); while(pos) { CWinStation *pWinStation = (CWinStation*)m_WinStationList.GetNext(pos); TempList.AddTail(pWinStation); } m_WinStationList.RemoveAll(); UnlockWinStationList(); CFrameWnd *p = (CFrameWnd*)pDoc->GetMainWnd(); pos = TempList.GetHeadPosition(); while(pos) { CWinStation *pWinStation = (CWinStation*)TempList.GetNext(pos); if(p && ::IsWindow(p->GetSafeHwnd())) { ODS( L"TSADMIN:CServer::Disconnect Remove WinStation\n" ); p->SendMessage(WM_ADMIN_REMOVE_WINSTATION, 0, (LPARAM)pWinStation); } delete pWinStation; } TempList.RemoveAll(); LockProcessList(); pos = m_ProcessList.GetHeadPosition(); while(pos) { CProcess *pProcess = (CProcess*)m_ProcessList.GetNext(pos); ODS( L"TSADMIN:CServer::Disconnect Delete process\n" ); delete pProcess; } m_ProcessList.RemoveAll(); UnlockProcessList(); LockLicenseList(); pos = m_LicenseList.GetHeadPosition(); while(pos) { CLicense *pLicense = (CLicense*)m_LicenseList.GetNext(pos); ODS( L"TSADMIN:CServer::Disconnect remove license\n" ); delete pLicense; } m_LicenseList.RemoveAll(); UnlockLicenseList(); // pos = m_UserSidList.GetHeadPosition(); while(pos) { CUserSid *pUserSid = (CUserSid*)m_UserSidList.GetNext(pos); ODS( L"TSADMIN:CServer::Disconnect remove sids\n" ); delete pUserSid; } m_UserSidList.RemoveAll(); pos = m_HotfixList.GetHeadPosition(); while(pos) { CHotfix *pHotfix = (CHotfix*)m_HotfixList.GetNext(pos); ODS( L"TSADMIN:CServer::Disconnect Remove hotfixes\n" ); delete pHotfix; } m_HotfixList.RemoveAll(); if( m_pRegistryInfo ) { delete m_pRegistryInfo; ODS( L"TSADMIN:CServer::Disconnect delete reginfo\n" ); m_pRegistryInfo = NULL; } // ODS( L"TSADMIN:CServer::Disconnect Set state not connected\n" ); SetState(SS_NOT_CONNECTED); } ///////////////////////////////////////////////////////////////////////////// // CServer::DoDetail // // Go get detailed information about this server // void CServer::DoDetail() { CWinAdminDoc *pDoc = (CWinAdminDoc*)((CWinAdminApp*)AfxGetApp())->GetDocument(); SetState(SS_GETTING_INFO); ULONG Entries; PLOGONID pLogonId; if(!ShouldBackgroundContinue()) return; // We need to access the registry information for the server // at this time because we must not administer WF 2.00 servers // (RPC structures are incompatible). If we cannot access the // server's registry, or the multi-user version is 2.00, we bail // from this server. if ( !BuildRegistryInfo() || (GetCTXVersionNum() == 0x200) || (GetCTXVersionNum() == 0) ) { ClearHandleGood(); SetLostConnection(); SetState(SS_BAD); ODS( L"CServer::DoDetail - Setting to lost connection\n" ); CFrameWnd *pFrameWnd = (CFrameWnd*)pDoc->GetMainWnd(); if(pFrameWnd && ::IsWindow(pFrameWnd->GetSafeHwnd())) pFrameWnd->SendMessage(WM_ADMIN_REMOVE_SERVER, 0, (LPARAM)this); ClearBackgroundContinue(); return; } // Find all the WinStations BOOL fWinEnum; fWinEnum = WinStationEnumerate(m_Handle, &pLogonId, &Entries); DBGMSG( L"CServer!DoDetail WinEnum last reported error 0x%x\n", GetLastError( ) ); if(!fWinEnum ) { ClearHandleGood(); SetLostConnection(); SetState(SS_BAD); ClearBackgroundContinue(); return; } if(!ShouldBackgroundContinue()) { if(pLogonId) WinStationFreeMemory(pLogonId); return; } // Get information about the WinStations if(pLogonId) { for(ULONG i = 0; i < Entries; i++) { // Create a new WinStation object CWinStation *pWinStation = new CWinStation(this, &pLogonId[i]); if(pWinStation) { // If the queries weren't successful, ignore this WinStation if(!pWinStation->QueriesSuccessful()) { ODS( L"CServer::DoDetail!QueriesSuccessful failed\n" ); delete pWinStation; } else { AddWinStation(pWinStation); pWinStation->SetNew(); } } if( !ShouldBackgroundContinue() ) { if(pLogonId) WinStationFreeMemory(pLogonId); return; } } WinStationFreeMemory(pLogonId); } if(!ShouldBackgroundContinue()) return; // If there is an extension DLL loaded, allow it to add it's own info for this Server LPFNEXSERVERINITPROC InitProc = ((CWinAdminApp*)AfxGetApp())->GetExtServerInitProc(); if(InitProc) { m_pExtensionInfo = (*InitProc)(m_Name, m_Handle); if(m_pExtensionInfo) { LPFNEXGETSERVERINFOPROC GetInfoProc = ((CWinAdminApp*)AfxGetApp())->GetExtGetServerInfoProc(); if(GetInfoProc) { m_pExtServerInfo = (*GetInfoProc)(m_pExtensionInfo); // If this server is running WinFrame or Picasso, set flag if(m_pExtServerInfo->Flags & ESF_WINFRAME) SetWinFrame(); } } } QueryLicenses(); SetState(SS_GOOD); // Send a message to the views to tell it the state of this // server has changed CFrameWnd *p = (CFrameWnd*)pDoc->GetMainWnd(); if(p && ::IsWindow(p->GetSafeHwnd())) { p->SendMessage(WM_ADMIN_UPDATE_SERVER_INFO, 0, (LPARAM)this); p->SendMessage(WM_ADMIN_UPDATE_WINSTATIONS, 0, (LPARAM)this); } } // end CServer::DoDetail ///////////////////////////////////////////////////////////////////////////// // CServer::FindProcessByPID // // returns a pointer to a CProcess from m_ProcessList given a PID CProcess* CServer::FindProcessByPID(ULONG Pid) { LockProcessList(); POSITION pos = m_ProcessList.GetHeadPosition(); while(pos) { CProcess *pProcess = (CProcess*)m_ProcessList.GetNext(pos); if(pProcess->GetPID() == Pid) { UnlockProcessList(); return pProcess; } } UnlockProcessList(); return NULL; } // end CServer::FindProcessByPID ///////////////////////////////////////////////////////////////////////////// // CServer::EnumerateProcesses // // Enumerate this server's processes BOOL CServer::EnumerateProcesses() { ENUMTOKEN EnumToken; ULONG PID; ULONG LogonId; TCHAR ImageName[MAX_PROCESSNAME+1]; PSID pSID; EnumToken.Current = 0; EnumToken.NumberOfProcesses = 0; EnumToken.ProcessArray = NULL; EnumToken.bGAP = TRUE; if(!IsHandleGood()) return 0; CWinAdminDoc *pDoc = (CWinAdminDoc*)((CWinAdminApp*)AfxGetApp())->GetDocument(); LockProcessList(); // Loop processes through and turn off current flag and new flag // Delete any processes that aren't current POSITION pos = m_ProcessList.GetHeadPosition(); while(pos) { POSITION pos2 = pos; CProcess *pProcess = (CProcess*)m_ProcessList.GetNext(pos); if(!pProcess->IsCurrent()) { pProcess = (CProcess*)m_ProcessList.GetAt(pos2); m_ProcessList.RemoveAt(pos2); delete pProcess; } else { pProcess->ClearCurrent(); pProcess->ClearNew(); pProcess->ClearChanged(); // ! } } UnlockProcessList(); // Should we quit? if(!pDoc->ShouldProcessContinue()) { return FALSE; } while(ProcEnumerateProcesses(m_Handle, &EnumToken, ImageName, &LogonId, &PID, &pSID )) { CProcess *pProcess = new CProcess(PID, LogonId, this, pSID, FindWinStationById(LogonId), ImageName); if(pProcess) { // If this process is in the list, we need to see if it has changed CProcess *pOldProcess = FindProcessByPID(PID); if(pOldProcess && pProcess->GetWinStation()) { // Flag the process as current pOldProcess->SetCurrent(); // Update any info that has changed pOldProcess->Update(pProcess); // We don't need this process object anymore delete pProcess; } // It is a new process, add it to the list else if(pProcess->GetWinStation()) { pProcess->SetNew(); LockProcessList(); m_ProcessList.AddTail(pProcess); UnlockProcessList(); } // This process doesn't have a WinStation, delete it else { delete pProcess; } } // Should we quit? if(!pDoc->ShouldProcessContinue()) { // We have to call this one last time with an offset of -1 to // make the function free up the memory allocated by the client side stub. EnumToken.Current = (ULONG)-1; ProcEnumerateProcesses(m_Handle, &EnumToken, ImageName, &LogonId, &PID, &pSID ); return FALSE; } } return TRUE; } // end CServer::EnumerateProcesses ///////////////////////////////////////////////////////////////////////////// // CServer::ClearProcesses // // Clear out the list of processes void CServer::ClearProcesses() { LockProcessList(); POSITION pos = m_ProcessList.GetHeadPosition(); while(pos) { CProcess *pProcess = (CProcess*)m_ProcessList.GetNext(pos); delete pProcess; } m_ProcessList.RemoveAll(); UnlockProcessList(); } // end CServer::ClearProcesses ///////////////////////////////////////////////////////////////////////////// // CServer::FindWinStationById // CWinStation* CServer::FindWinStationById(ULONG Id) { LockWinStationList(); POSITION pos = m_WinStationList.GetHeadPosition(); while(pos) { CWinStation *pWinStation = (CWinStation*)m_WinStationList.GetNext(pos); if(pWinStation->GetLogonId() == Id) { UnlockWinStationList(); return pWinStation; } } UnlockWinStationList(); return NULL; } // end CServer::FindWinStationById ///////////////////////////////////////////////////////////////////////////// // CServer::BackgroundThreadProc // UINT CServer::BackgroundThreadProc(LPVOID pParam) { ASSERT(pParam); ODS( L"CServer::BackgroundThreadProc\n" ); // We need a pointer to the document so we can make // calls to member functions CWinAdminDoc *pDoc = (CWinAdminDoc*)((ServerProcInfo*)pParam)->pDoc; CServer *pServer = ((ServerProcInfo*)pParam)->pServer; HANDLE hServer; delete (ServerProcInfo*)pParam; // Make sure we don't have to quit if(!pServer->ShouldBackgroundContinue()) { return 0; } pServer->SetThreadAlive(); // In case the server is disconnected we wait uselessly here while(!pDoc->AreAllViewsReady()) Sleep(500); // Make sure we don't have to quit if(!pServer->ShouldBackgroundContinue()) { pServer->ClearThreadAlive(); return 0; } if(!pServer->IsCurrentServer()) { // open the server and save the handle hServer = WinStationOpenServer(pServer->GetName()); pServer->SetHandle(hServer); // Make sure we don't have to quit if(!pServer->ShouldBackgroundContinue()) { pServer->ClearThreadAlive(); return 0; } if(hServer == NULL) { DWORD Error = GetLastError(); DBGMSG( L"CServer!BackgroundThreadProc WinStationOpenServer failed with 0x%x\n", Error ); if(Error == RPC_S_SERVER_UNAVAILABLE) { pServer->ClearBackgroundFound(); pServer->SetLostConnection(); pServer->ClearManualFind( ); CFrameWnd *pFrameWnd = (CFrameWnd*)pDoc->GetMainWnd(); if(pFrameWnd && ::IsWindow(pFrameWnd->GetSafeHwnd())) { ODS( L"Server backgrnd thread declares this server RPC_S_SERVER_UNAVAILABLE\n" ); pFrameWnd->SendMessage(WM_ADMIN_REMOVE_SERVER, 0, (LPARAM)pServer); if( pServer->GetTreeItemFromFav() != NULL ) { pFrameWnd->SendMessage( WM_ADMIN_REMOVESERVERFROMFAV , 0 , ( LPARAM )pServer ); } } } else { pServer->SetState(SS_BAD); } pServer->ClearThreadAlive(); return 0; } pServer->SetHandleGood(); pServer->SetState(SS_OPENED); } else { hServer = SERVERNAME_CURRENT; pServer->SetHandle(SERVERNAME_CURRENT); pServer->SetHandleGood(); pServer->SetState(SS_OPENED); } // Make sure we don't have to quit if(!pServer->ShouldBackgroundContinue()) { pServer->ClearThreadAlive(); return 0; } // If we found this server after initial enum, // we need to add it to the views now CFrameWnd *pFrameWnd = (CFrameWnd*)pDoc->GetMainWnd(); if(pServer->WasFoundLater()) { if(pFrameWnd && ::IsWindow(pFrameWnd->GetSafeHwnd())) { pFrameWnd->SendMessage(WM_ADMIN_ADD_SERVER, ( WPARAM )TVI_SORT, (LPARAM)pServer); } } // Make sure we don't have to quit if(!pServer->ShouldBackgroundContinue()) { pServer->ClearThreadAlive(); return 0; } pServer->DoDetail(); // Now go into our loop waiting for WinStation Events while(1) { ULONG WSEventFlags; ULONG Entries; PLOGONID pLogonId; // Make sure we don't have to quit if(!pServer->ShouldBackgroundContinue()) { pServer->ClearThreadAlive(); return 0; } // Wait for the browser to tell us something happened if(!WinStationWaitSystemEvent(hServer, WEVENT_ALL, &WSEventFlags)) { if(GetLastError() != ERROR_OPERATION_ABORTED) { ODS( L"CServer::BackgroundThreadProc ERROR_OPERATION_ABORTED\n" ); pServer->ClearHandleGood(); pServer->SetState(SS_BAD); pServer->SetLostConnection(); pServer->ClearThreadAlive(); pServer->ClearManualFind( ); return 1; } } ODS( L"CServer::BackgroundThreadProc -- some system event has taken place\n" ); // Make sure we don't have to quit if(!pServer->ShouldBackgroundContinue()) { pServer->ClearThreadAlive(); ODS( L"CServer::BackgroundThreadProc -* backgrnd thread should not continue\n" ); return 0; } // Loop through this Server's WinStations and clear the current flag pServer->LockWinStationList(); CObList *pWinStationList = pServer->GetWinStationList(); POSITION pos = pWinStationList->GetHeadPosition(); while(pos) { CWinStation *pWinStation = (CWinStation*)pWinStationList->GetNext(pos); pWinStation->ClearCurrent(); pWinStation->ClearNew(); pWinStation->ClearChanged(); } pServer->UnlockWinStationList(); // Find all the WinStations BOOL fWinEnum = WinStationEnumerate(hServer, &pLogonId, &Entries); DBGMSG( L"CServer!BackgroundThread WinEnum last reported error 0x%x\n", GetLastError( ) ); if(!fWinEnum ) { ODS( L"CServer!BackgroundThread -- server is no longer valid\n" ); pServer->ClearHandleGood(); pServer->SetLostConnection(); pServer->SetState(SS_BAD); pServer->ClearThreadAlive(); pServer->ClearManualFind( ); return 1; } // Make sure we don't have to quit if(!pServer->ShouldBackgroundContinue()) { if(pLogonId) WinStationFreeMemory(pLogonId); pServer->ClearThreadAlive(); ODS( L"CServer!BackgroundThreadProc -# backgrnd thread should not continue\n" ); return 0; } if(pLogonId) { // Get information about the WinStations for(ULONG i = 0; i < Entries; i++) { // Look for this WinStation in the list CWinStation *pWinStation = pServer->FindWinStationById(pLogonId[i].LogonId); if(pWinStation) { // Mark this WinStation as current pWinStation->SetCurrent(); // We found the WinStation in the list // Create a new CWinStation object - he will get his information CWinStation *pTempWinStation = new CWinStation(pServer, &pLogonId[i]); if(pTempWinStation) { // If any information has changed, send a message to update the views if(pWinStation->Update(pTempWinStation)) { CFrameWnd *pFrameWnd = (CFrameWnd*)pDoc->GetMainWnd(); if(pFrameWnd && ::IsWindow(pFrameWnd->GetSafeHwnd())) { pFrameWnd->SendMessage(WM_ADMIN_UPDATE_WINSTATION, 0, (LPARAM)pWinStation); } } // We don't need the temporary CWinStation object anymore delete pTempWinStation; } } else { // We didn't find it in our list // We don't want to add it to our list if the WinStation is down if(pLogonId[i].State != State_Down && pLogonId[i].State != State_Init) { // Create a new CWinStation object CWinStation *pNewWinStation = new CWinStation(pServer, &pLogonId[i]); if(pNewWinStation) { pServer->AddWinStation(pNewWinStation); pNewWinStation->SetNew(); CFrameWnd *pFrameWnd = (CFrameWnd*)pDoc->GetMainWnd(); if(pFrameWnd && ::IsWindow(pFrameWnd->GetSafeHwnd())) { pFrameWnd->SendMessage(WM_ADMIN_ADD_WINSTATION, 0, (LPARAM)pNewWinStation); } } } } } WinStationFreeMemory(pLogonId); // Send a message to the views to update their WinStations CFrameWnd *pFrameWnd = (CFrameWnd*)pDoc->GetMainWnd(); if(pFrameWnd && ::IsWindow(pFrameWnd->GetSafeHwnd())) { pFrameWnd->SendMessage(WM_ADMIN_UPDATE_WINSTATIONS, 0, (LPARAM)pServer); } // Loop through the WinStations for this server and move // any that aren't current to a temporary list CObList TempList; pServer->LockWinStationList(); CObList *pWinStationList = pServer->GetWinStationList(); POSITION pos = pWinStationList->GetHeadPosition(); while(pos) { POSITION pos2 = pos; CWinStation *pWinStation = (CWinStation*)pWinStationList->GetNext(pos); if(!pWinStation->IsCurrent()) { // Add the WinStation to our temporary list TempList.AddTail(pWinStation); // Remove the WinStation from the list of WinStations pWinStation = (CWinStation*)pWinStationList->GetAt(pos2); pWinStationList->RemoveAt(pos2); } } pServer->UnlockWinStationList(); pos = TempList.GetHeadPosition(); while(pos) { POSITION pos2 = pos; CWinStation *pWinStation = (CWinStation*)TempList.GetNext(pos); // Send a message to remove the WinStation from the tree view CFrameWnd *pFrameWnd = (CFrameWnd*)pDoc->GetMainWnd(); if(pFrameWnd && ::IsWindow(pFrameWnd->GetSafeHwnd())) { pFrameWnd->SendMessage(WM_ADMIN_REMOVE_WINSTATION, 0, (LPARAM)pWinStation); } delete pWinStation; } TempList.RemoveAll(); } // end if(pLogonId) // If there is an extension DLL loaded, allow it to update info for this Server LPFNEXSERVEREVENTPROC EventProc = ((CWinAdminApp*)AfxGetApp())->GetExtServerEventProc(); if(EventProc) { // Returns TRUE if anything changed if((*EventProc)(pServer->GetExtensionInfo(), WSEventFlags)) { pServer->QueryLicenses(); CFrameWnd *pFrameWnd = (CFrameWnd*)pDoc->GetMainWnd(); if(pFrameWnd && ::IsWindow(pFrameWnd->GetSafeHwnd())) { pFrameWnd->SendMessage(WM_ADMIN_REDISPLAY_LICENSES, 0, (LPARAM)pServer); } } } // Tell the Server view to show the new load and license counts CFrameWnd *pFrameWnd = (CFrameWnd*)pDoc->GetMainWnd(); if(pFrameWnd && ::IsWindow(pFrameWnd->GetSafeHwnd())) { pFrameWnd->SendMessage(WM_ADMIN_UPDATE_SERVER_INFO, 0, (LPARAM)pServer); } // Make sure we don't have to quit if(!pServer->ShouldBackgroundContinue()) { pServer->ClearThreadAlive(); ODS( L"CServer::BackgroundThreadProc -@ backgrnd thread should not continue\n" ); return 0; } } // end while(1) } // end CServer::BackgroundThreadProc ///////////////////////////////////////////////////////////////////////////// // CServer::QueryLicenses // void CServer::QueryLicenses() { ULONG NumLicenses; ExtLicenseInfo *pExtLicenseInfo = NULL; // If there is an extension DLL loaded, get this server's list of licenses LPFNEXGETSERVERLICENSESPROC LicenseProc = ((CWinAdminApp*)AfxGetApp())->GetExtGetServerLicensesProc(); if(LicenseProc && m_pExtensionInfo) { LockLicenseList(); // Iterate through the License list POSITION pos = m_LicenseList.GetHeadPosition(); while(pos) { CLicense *pLicense = (CLicense*)m_LicenseList.GetNext(pos); delete pLicense; } m_LicenseList.RemoveAll(); UnlockLicenseList(); pExtLicenseInfo = (*LicenseProc)(m_pExtensionInfo, &NumLicenses); if(pExtLicenseInfo) { ExtLicenseInfo *pExtLicense = pExtLicenseInfo; for(ULONG lic = 0; lic < NumLicenses; lic++) { CLicense *pLicense = new CLicense(this, pExtLicense); if(pLicense) { AddLicense(pLicense); } pExtLicense++; } // Get the extension DLL's function to free the license info LPFNEXFREESERVERLICENSESPROC LicenseFreeProc = ((CWinAdminApp*)AfxGetApp())->GetExtFreeServerLicensesProc(); if(LicenseFreeProc) { (*LicenseFreeProc)(pExtLicenseInfo); } else { TRACE0("WAExGetServerLicenses exists without WAExFreeServerLicenseInfo\n"); ASSERT(0); } } } } // end CServer::QueryLicenses ///////////////////////////////////////////////////////////////////////////// // CServer::AddLicense // // Add a License to the Server's LicenseList in // sorted order // NOTE: The list should be NOT be locked by the caller // void CServer::AddLicense(CLicense *pNewLicense) { ASSERT(pNewLicense); LockLicenseList(); BOOLEAN bAdded = FALSE; POSITION pos, oldpos; int Index; // Traverse the LicenseList and insert this new License, // keeping the list sorted by Class, then Name. for(Index = 0, pos = m_LicenseList.GetHeadPosition(); pos != NULL; Index++) { oldpos = pos; CLicense *pLicense = (CLicense*)m_LicenseList.GetNext(pos); if((pLicense->GetClass() > pNewLicense->GetClass()) || ((pLicense->GetClass() == pNewLicense->GetClass()) && lstrcmpi(pLicense->GetSerialNumber(), pNewLicense->GetSerialNumber()) > 0)) { // The new object belongs before the current list object. m_LicenseList.InsertBefore(oldpos, pNewLicense); bAdded = TRUE; break; } } // If we haven't yet added the License, add it now to the tail // of the list. if(!bAdded) { m_LicenseList.AddTail(pNewLicense); } UnlockLicenseList(); } // end CServer::AddLicense ///////////////////////////////////////////////////////////////////////////// // CServer::SetState // void CServer::SetState(SERVER_STATE State) { m_PreviousState = m_State; m_State = State; if(m_State != m_PreviousState) { CWinAdminDoc *pDoc = (CWinAdminDoc*)((CWinAdminApp*)AfxGetApp())->GetDocument(); CFrameWnd *pFrameWnd = (CFrameWnd*)pDoc->GetMainWnd(); if(pFrameWnd && ::IsWindow(pFrameWnd->GetSafeHwnd())) { pFrameWnd->SendMessage(WM_ADMIN_UPDATE_SERVER, 0, (LPARAM)this); } } } // end CServer::SetState