1162 lines
33 KiB
C++
1162 lines
33 KiB
C++
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//
|
||
|
//=============================================================================//
|
||
|
// ServicesDlg.cpp : implementation file
|
||
|
//
|
||
|
|
||
|
#include "stdafx.h"
|
||
|
#include "ServicesDlg.h"
|
||
|
#include "vmpi.h"
|
||
|
#include "bitbuf.h"
|
||
|
#include "tier1/strtools.h"
|
||
|
#include "patchtimeout.h"
|
||
|
#include "SetPasswordDlg.h"
|
||
|
#include "vmpi_browser_helpers.h"
|
||
|
#include <io.h>
|
||
|
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
#define new DEBUG_NEW
|
||
|
#undef THIS_FILE
|
||
|
static char THIS_FILE[] = __FILE__;
|
||
|
#endif
|
||
|
|
||
|
#define SERVICE_OFF_TIMEOUT (20*1000) // If we haven't heard from a service in this long,
|
||
|
// then we assume the service is off.
|
||
|
|
||
|
#define SERVICES_PING_INTERVAL (3*1000) // ping the services every so often
|
||
|
|
||
|
#define SERVICE_MAX_UPDATE_INTERVAL (8*1000) // Update each service in the listbox at least this often.
|
||
|
|
||
|
|
||
|
|
||
|
int V_AfxMessageBox( int mbType, const char *pFormat, ... )
|
||
|
{
|
||
|
char msg[4096];
|
||
|
va_list marker;
|
||
|
va_start( marker, pFormat );
|
||
|
_vsnprintf( msg, sizeof( msg ), pFormat, marker );
|
||
|
va_end( marker );
|
||
|
|
||
|
return AfxMessageBox( msg, mbType );
|
||
|
}
|
||
|
|
||
|
|
||
|
bool CServiceInfo::IsOff() const
|
||
|
{
|
||
|
return (Plat_MSTime() - m_LastPingTimeMS) > SERVICE_OFF_TIMEOUT;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Returns the argument following pName.
|
||
|
// If pName is the last argument on the command line, returns pEndArgDefault.
|
||
|
// Returns NULL if there is no argument with pName.
|
||
|
const char* FindArg( const char *pName, const char *pEndArgDefault="" )
|
||
|
{
|
||
|
for ( int i=0; i < __argc; i++ )
|
||
|
{
|
||
|
if ( stricmp( pName, __argv[i] ) == 0 )
|
||
|
{
|
||
|
if ( (i+1) < __argc )
|
||
|
return __argv[i+1];
|
||
|
else
|
||
|
return pEndArgDefault;
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void AppendStr( char *dest, int destSize, const char *pFormat, ... )
|
||
|
{
|
||
|
char str[4096];
|
||
|
va_list marker;
|
||
|
va_start( marker, pFormat );
|
||
|
_vsnprintf( str, sizeof( str ), pFormat, marker );
|
||
|
va_end( marker );
|
||
|
str[sizeof( str ) - 1] = 0;
|
||
|
|
||
|
V_strncat( dest, str, destSize );
|
||
|
}
|
||
|
|
||
|
|
||
|
char* GetLastErrorString()
|
||
|
{
|
||
|
static char err[2048];
|
||
|
|
||
|
LPVOID lpMsgBuf;
|
||
|
FormatMessage(
|
||
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
||
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||
|
NULL,
|
||
|
GetLastError(),
|
||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
||
|
(LPTSTR) &lpMsgBuf,
|
||
|
0,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
strncpy( err, (char*)lpMsgBuf, sizeof( err ) );
|
||
|
LocalFree( lpMsgBuf );
|
||
|
|
||
|
err[ sizeof( err ) - 1 ] = 0;
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
const char* GetStatusString( CServiceInfo *pInfo )
|
||
|
{
|
||
|
if ( pInfo->IsOff() )
|
||
|
return "off";
|
||
|
else if ( pInfo->m_iState == VMPI_STATE_BUSY )
|
||
|
return "busy";
|
||
|
else if ( pInfo->m_iState == VMPI_STATE_PATCHING )
|
||
|
return "patching";
|
||
|
else if ( pInfo->m_iState == VMPI_STATE_DISABLED )
|
||
|
return "disabled";
|
||
|
else if ( pInfo->m_iState == VMPI_STATE_SCREENSAVER_DISABLED )
|
||
|
return "disabled (screensaver)";
|
||
|
else if ( pInfo->m_iState == VMPI_STATE_DOWNLOADING )
|
||
|
return "downloading";
|
||
|
else
|
||
|
return "idle";
|
||
|
}
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------------------------------------- //
|
||
|
// Column sort functions.
|
||
|
// --------------------------------------------------------------------------------------------------------- //
|
||
|
typedef int (CALLBACK *ServicesSortFn)( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam );
|
||
|
|
||
|
static int CALLBACK SortByName( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
|
||
|
{
|
||
|
CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
|
||
|
CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
|
||
|
|
||
|
return strcmp( pInfo1->m_ComputerName, pInfo2->m_ComputerName );
|
||
|
}
|
||
|
|
||
|
static int CALLBACK SortByStatus( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
|
||
|
{
|
||
|
CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
|
||
|
CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
|
||
|
|
||
|
return -strcmp( GetStatusString( pInfo2 ), GetStatusString( pInfo1 ) );
|
||
|
}
|
||
|
|
||
|
static int CALLBACK SortByRunningTime( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
|
||
|
{
|
||
|
CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
|
||
|
CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
|
||
|
|
||
|
int v1 = pInfo1->m_LiveTimeMS / (1000*60); // Sort on minutes so it doesn't constantly resort the list.
|
||
|
int v2 = pInfo2->m_LiveTimeMS / (1000*60);
|
||
|
if ( v2 > v1 )
|
||
|
return 1;
|
||
|
else if ( v2 < v1 )
|
||
|
return -1;
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int CALLBACK SortByWorkerAppRunningTime( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
|
||
|
{
|
||
|
CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
|
||
|
CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
|
||
|
|
||
|
int v1 = pInfo1->m_WorkerAppTimeMS / (1000*60); // Sort on minutes so it doesn't constantly resort the list.
|
||
|
int v2 = pInfo2->m_WorkerAppTimeMS / (1000*60);
|
||
|
if ( v2 > v1 )
|
||
|
return 1;
|
||
|
else if ( v2 < v1 )
|
||
|
return -1;
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int CALLBACK SortByMasterName( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
|
||
|
{
|
||
|
CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
|
||
|
CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
|
||
|
|
||
|
return -strcmp( pInfo2->m_MasterName, pInfo1->m_MasterName );
|
||
|
}
|
||
|
|
||
|
static int CALLBACK SortByProtocol( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
|
||
|
{
|
||
|
CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
|
||
|
CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
|
||
|
|
||
|
return pInfo1->m_ProtocolVersion > pInfo2->m_ProtocolVersion;
|
||
|
}
|
||
|
|
||
|
static int CALLBACK SortByPassword( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
|
||
|
{
|
||
|
CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
|
||
|
CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
|
||
|
|
||
|
return -strcmp( pInfo2->m_Password, pInfo1->m_Password );
|
||
|
}
|
||
|
|
||
|
static int CALLBACK SortByServiceVersion( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
|
||
|
{
|
||
|
CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
|
||
|
CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
|
||
|
|
||
|
return -strcmp( pInfo2->m_ServiceVersion, pInfo1->m_ServiceVersion );
|
||
|
}
|
||
|
|
||
|
static int CALLBACK SortByExe( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
|
||
|
{
|
||
|
CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
|
||
|
CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
|
||
|
|
||
|
return -strcmp( pInfo2->m_ExeName, pInfo1->m_ExeName );
|
||
|
}
|
||
|
|
||
|
static int CALLBACK SortByMap( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
|
||
|
{
|
||
|
CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
|
||
|
CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
|
||
|
|
||
|
return -strcmp( pInfo2->m_MapName, pInfo1->m_MapName );
|
||
|
}
|
||
|
|
||
|
static int CALLBACK SortByCPUPercentage( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
|
||
|
{
|
||
|
CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
|
||
|
CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
|
||
|
|
||
|
if ( pInfo2->m_CPUPercentage > pInfo1->m_CPUPercentage )
|
||
|
return 1;
|
||
|
else if ( pInfo2->m_CPUPercentage < pInfo1->m_CPUPercentage )
|
||
|
return -1;
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int CALLBACK SortByMemUsage( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
|
||
|
{
|
||
|
CServiceInfo *pInfo1 = (CServiceInfo*)iItem1;
|
||
|
CServiceInfo *pInfo2 = (CServiceInfo*)iItem2;
|
||
|
|
||
|
if ( pInfo2->m_MemUsageMB > pInfo1->m_MemUsageMB )
|
||
|
return 1;
|
||
|
else if ( pInfo2->m_MemUsageMB < pInfo1->m_MemUsageMB )
|
||
|
return -1;
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------------------------------------- //
|
||
|
// Column information.
|
||
|
// --------------------------------------------------------------------------------------------------------- //
|
||
|
|
||
|
struct
|
||
|
{
|
||
|
char *pText;
|
||
|
int width;
|
||
|
ServicesSortFn sortFn;
|
||
|
} g_ColumnInfos[] =
|
||
|
{
|
||
|
{"Computer Name", 150, SortByName},
|
||
|
{"Status", 95, SortByStatus},
|
||
|
{"Service Run Time", 100, SortByRunningTime},
|
||
|
{"Worker App Run Time", 125, SortByWorkerAppRunningTime},
|
||
|
{"Master", 150, SortByMasterName},
|
||
|
{"Password", 60, SortByPassword},
|
||
|
{"Protocol", 60, SortByProtocol},
|
||
|
{"Version", 50, SortByServiceVersion},
|
||
|
{"CPU", 50, SortByCPUPercentage},
|
||
|
{"Memory (MB)", 80, SortByMemUsage},
|
||
|
{"Exe", 80, SortByExe},
|
||
|
{"Map", 90, SortByMap},
|
||
|
};
|
||
|
#define COLUMN_COMPUTER_NAME 0
|
||
|
#define COLUMN_STATUS 1
|
||
|
#define COLUMN_RUNNING_TIME 2
|
||
|
#define COLUMN_WORKER_APP_RUNNING_TIME 3
|
||
|
#define COLUMN_MASTER_NAME 4
|
||
|
#define COLUMN_PASSWORD 5
|
||
|
#define COLUMN_PROTOCOL_VERSION 6
|
||
|
#define COLUMN_SERVICE_VERSION 7
|
||
|
#define COLUMN_CPU_PERCENTAGE 8
|
||
|
#define COLUMN_MEM_USAGE 9
|
||
|
#define COLUMN_EXE_NAME 10
|
||
|
#define COLUMN_MAP_NAME 11
|
||
|
|
||
|
|
||
|
// Used to sort all the columns.
|
||
|
// When they click on a column, we add that index to entry 0 in here.
|
||
|
// Then when sorting, if 2 columns are equal, we move to the next sort function.
|
||
|
CUtlVector<int> g_SortColumns;
|
||
|
|
||
|
static void PushSortColumn( int iColumn )
|
||
|
{
|
||
|
// First, get rid of all entries with this same index.
|
||
|
int iPrev = g_SortColumns.Find( iColumn );
|
||
|
if ( iPrev != g_SortColumns.InvalidIndex() )
|
||
|
g_SortColumns.Remove( iPrev );
|
||
|
|
||
|
// Now add this one.
|
||
|
g_SortColumns.AddToTail( iColumn );
|
||
|
}
|
||
|
|
||
|
static int CALLBACK MainSortFn( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
|
||
|
{
|
||
|
int curStatus = 0;
|
||
|
for ( int i = (int)g_SortColumns.Count()-1; i >= 0; i-- )
|
||
|
{
|
||
|
curStatus = g_ColumnInfos[g_SortColumns[i]].sortFn( iItem1, iItem2, lpParam );
|
||
|
if ( curStatus != 0 )
|
||
|
break;
|
||
|
}
|
||
|
return curStatus;
|
||
|
}
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CServicesDlg dialog
|
||
|
|
||
|
|
||
|
CServicesDlg::CServicesDlg(CWnd* pParent /*=NULL*/)
|
||
|
: CIdleDialog(CServicesDlg::IDD, pParent)
|
||
|
{
|
||
|
//{{AFX_DATA_INIT(CServicesDlg)
|
||
|
// NOTE: the ClassWizard will add member initialization here
|
||
|
//}}AFX_DATA_INIT
|
||
|
m_pServicesPingSocket = NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
void CServicesDlg::DoDataExchange(CDataExchange* pDX)
|
||
|
{
|
||
|
CDialog::DoDataExchange(pDX);
|
||
|
//{{AFX_DATA_MAP(CServicesDlg)
|
||
|
DDX_Control(pDX, IDC_NUM_SERVICES, m_NumServicesControl);
|
||
|
DDX_Control(pDX, IDC_NUM_DISABLED_SERVICES, m_NumDisabledServicesControl);
|
||
|
DDX_Control(pDX, IDC_NUM_WORKING_SERVICES, m_NumWorkingServicesControl);
|
||
|
DDX_Control(pDX, IDC_NUM_WAITING_SERVICES, m_NumWaitingServicesControl);
|
||
|
DDX_Control(pDX, IDC_NUM_OFF_SERVICES, m_NumOffServicesControl);
|
||
|
DDX_Control(pDX, IDC_CURRENT_PASSWORD, m_PasswordDisplay);
|
||
|
DDX_Control(pDX, IDC_SERVICES_LIST, m_ServicesList);
|
||
|
//}}AFX_DATA_MAP
|
||
|
}
|
||
|
|
||
|
|
||
|
BEGIN_MESSAGE_MAP(CServicesDlg, CIdleDialog)
|
||
|
//{{AFX_MSG_MAP(CServicesDlg)
|
||
|
ON_BN_CLICKED(ID_PATCH_SERVICES, OnPatchServices)
|
||
|
ON_BN_CLICKED(ID_STOP_SERVICES, OnStopServices)
|
||
|
ON_BN_CLICKED(ID_STOP_JOBS, OnStopJobs)
|
||
|
ON_BN_CLICKED(ID_FILTER_BY_PASSWORD, OnFilterByPassword)
|
||
|
ON_BN_CLICKED(ID_FORCE_PASSWORD, OnForcePassword)
|
||
|
ON_BN_CLICKED(ID_COPY_TO_CLIPBOARD, OnCopyToClipboard)
|
||
|
ON_NOTIFY(NM_DBLCLK, IDC_SERVICES_LIST, OnDblclkServicesList)
|
||
|
ON_WM_SIZE()
|
||
|
//}}AFX_MSG_MAP
|
||
|
END_MESSAGE_MAP()
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CServicesDlg message handlers
|
||
|
|
||
|
BOOL CServicesDlg::OnInitDialog()
|
||
|
{
|
||
|
CDialog::OnInitDialog();
|
||
|
|
||
|
// Initially, just sort by computer name.
|
||
|
PushSortColumn( COLUMN_COMPUTER_NAME );
|
||
|
|
||
|
HICON hIcon = LoadIcon( AfxGetInstanceHandle(), MAKEINTRESOURCE( IDR_MAINFRAME ) );
|
||
|
SetIcon( hIcon, true );
|
||
|
|
||
|
m_ServicesList.SetExtendedStyle( LVS_EX_FULLROWSELECT );
|
||
|
|
||
|
// Setup the headers.
|
||
|
for ( int i=0; i < ARRAYSIZE( g_ColumnInfos ); i++ )
|
||
|
{
|
||
|
m_ServicesList.InsertColumn( i, g_ColumnInfos[i].pText, LVCFMT_LEFT, g_ColumnInfos[i].width, i );
|
||
|
}
|
||
|
|
||
|
m_pServicesPingSocket = CreateIPSocket();
|
||
|
if ( m_pServicesPingSocket )
|
||
|
{
|
||
|
m_pServicesPingSocket->BindToAny( 0 );
|
||
|
}
|
||
|
|
||
|
m_dwLastServicesPing = GetTickCount() - SERVICES_PING_INTERVAL;
|
||
|
StartIdleProcessing( 100 ); // get idle messages every half second
|
||
|
|
||
|
m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_NUM_SERVICES_LABEL ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
|
||
|
m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_NUM_SERVICES ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
|
||
|
|
||
|
m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_NUM_DISABLED_SERVICES_LABEL ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
|
||
|
m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_NUM_DISABLED_SERVICES ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
|
||
|
|
||
|
m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_NUM_WORKING_SERVICES_LABEL ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
|
||
|
m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_NUM_WORKING_SERVICES ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
|
||
|
|
||
|
m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_NUM_WAITING_SERVICES_LABEL ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
|
||
|
m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_NUM_WAITING_SERVICES ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
|
||
|
|
||
|
m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_NUM_OFF_SERVICES_LABEL ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
|
||
|
m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_NUM_OFF_SERVICES ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
|
||
|
|
||
|
m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_CURRENT_PASSWORD_LABEL ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
|
||
|
m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_CURRENT_PASSWORD ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
|
||
|
m_AnchorMgr.AddAnchor( this, GetDlgItem( ID_PATCH_SERVICES ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
|
||
|
m_AnchorMgr.AddAnchor( this, GetDlgItem( ID_STOP_SERVICES ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
|
||
|
m_AnchorMgr.AddAnchor( this, GetDlgItem( ID_STOP_JOBS ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
|
||
|
m_AnchorMgr.AddAnchor( this, GetDlgItem( ID_FORCE_PASSWORD ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
|
||
|
m_AnchorMgr.AddAnchor( this, GetDlgItem( ID_FILTER_BY_PASSWORD ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
|
||
|
|
||
|
m_AnchorMgr.AddAnchor( this, GetDlgItem( ID_COPY_TO_CLIPBOARD ), ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_BOTTOM );
|
||
|
|
||
|
m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_SERVICES_LIST ), ANCHOR_LEFT, ANCHOR_TOP, ANCHOR_RIGHT, ANCHOR_BOTTOM );
|
||
|
|
||
|
// Unless they specify admin mode, hide all the controls that can mess with the services.
|
||
|
if ( !FindArg( "-Admin" ) )
|
||
|
{
|
||
|
::ShowWindow( ::GetDlgItem( m_hWnd, ID_PATCH_SERVICES ), SW_HIDE );
|
||
|
::ShowWindow( ::GetDlgItem( m_hWnd, ID_STOP_SERVICES ), SW_HIDE );
|
||
|
::ShowWindow( ::GetDlgItem( m_hWnd, ID_STOP_JOBS ), SW_HIDE );
|
||
|
::ShowWindow( ::GetDlgItem( m_hWnd, ID_FORCE_PASSWORD ), SW_HIDE );
|
||
|
}
|
||
|
|
||
|
m_NetViewThread.Init();
|
||
|
|
||
|
return TRUE; // return TRUE unless you set the focus to a control
|
||
|
// EXCEPTION: OCX Property Pages should return FALSE
|
||
|
}
|
||
|
|
||
|
|
||
|
void CServicesDlg::BuildVMPIPingPacket( CUtlVector<char> &out, char cPacketID, unsigned char protocolVersion, bool bIgnorePassword )
|
||
|
{
|
||
|
out.Purge();
|
||
|
out.AddToTail( protocolVersion );
|
||
|
|
||
|
const char *pPassword = m_Password;
|
||
|
if ( pPassword[0] == 0 || bIgnorePassword )
|
||
|
{
|
||
|
// If they haven't set a password to filter by, then we want all services to report in.
|
||
|
out.AddToTail( VMPI_PASSWORD_OVERRIDE );
|
||
|
out.AddToTail( 0 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
out.AddMultipleToTail( strlen( pPassword ) + 1, pPassword ); // password.
|
||
|
}
|
||
|
|
||
|
out.AddToTail( cPacketID );
|
||
|
}
|
||
|
|
||
|
|
||
|
void CServicesDlg::UpdateServicesFromNetMessages()
|
||
|
{
|
||
|
while ( 1 )
|
||
|
{
|
||
|
char in[1024];
|
||
|
CIPAddr ipFrom;
|
||
|
|
||
|
int len = m_pServicesPingSocket->RecvFrom( in, sizeof( in ), &ipFrom );
|
||
|
if ( len < 4 )
|
||
|
break;
|
||
|
|
||
|
bf_read buf( in, len );
|
||
|
unsigned char protocolVersion = buf.ReadByte();
|
||
|
if ( protocolVersion == 4 || protocolVersion == VMPI_PROTOCOL_VERSION ) // Protocol version 4 is almost the same.
|
||
|
{
|
||
|
int packetID = buf.ReadByte();
|
||
|
if ( packetID == VMPI_PING_RESPONSE )
|
||
|
{
|
||
|
int iState = buf.ReadByte();
|
||
|
unsigned long liveTimeMS = (unsigned long)buf.ReadLong();
|
||
|
|
||
|
int iPort = buf.ReadLong();
|
||
|
char computerName[512];
|
||
|
buf.ReadString( computerName, sizeof( computerName ) );
|
||
|
|
||
|
char masterName[512];
|
||
|
if ( buf.GetNumBitsLeft() )
|
||
|
buf.ReadString( masterName, sizeof( masterName ) );
|
||
|
else
|
||
|
masterName[0] = 0;
|
||
|
|
||
|
unsigned long workerAppTimeMS = 0;
|
||
|
if ( buf.GetNumBitsLeft() )
|
||
|
workerAppTimeMS = buf.ReadLong();
|
||
|
|
||
|
char password[512] = {0};
|
||
|
if ( protocolVersion == VMPI_PROTOCOL_VERSION )
|
||
|
buf.ReadString( password, sizeof( password ) );
|
||
|
|
||
|
char serviceVersion[32];
|
||
|
if ( protocolVersion == VMPI_PROTOCOL_VERSION )
|
||
|
buf.ReadString( serviceVersion, sizeof( serviceVersion ) );
|
||
|
else
|
||
|
V_strncpy( serviceVersion, "old", sizeof( serviceVersion ) );
|
||
|
|
||
|
int cpuPercentage = -1;
|
||
|
if ( buf.GetNumBytesLeft() >= 1 )
|
||
|
cpuPercentage = buf.ReadByte();
|
||
|
|
||
|
char exeName[512];
|
||
|
if ( buf.GetNumBytesLeft() >= 1 )
|
||
|
buf.ReadString( exeName, sizeof( exeName ) );
|
||
|
else
|
||
|
V_strncpy( exeName, "-", sizeof( exeName ) );
|
||
|
|
||
|
short memUsage = -1;
|
||
|
if ( buf.GetNumBytesLeft() >= 2 )
|
||
|
memUsage = buf.ReadShort();
|
||
|
|
||
|
char mapName[256];
|
||
|
if ( buf.GetNumBytesLeft() >= 1 )
|
||
|
buf.ReadString( mapName, sizeof( mapName ) );
|
||
|
else
|
||
|
V_strncpy( mapName, "-", sizeof( mapName ) );
|
||
|
|
||
|
|
||
|
CServiceInfo *pInfo = FindServiceByComputerName( computerName );
|
||
|
if ( !pInfo )
|
||
|
{
|
||
|
pInfo = new CServiceInfo;
|
||
|
m_Services.AddToTail( pInfo );
|
||
|
pInfo->m_ComputerName = computerName;
|
||
|
|
||
|
pInfo->m_pLastStatusText = NULL;
|
||
|
|
||
|
pInfo->m_Addr.port = iPort;
|
||
|
pInfo->m_LastPingTimeMS = Plat_MSTime();
|
||
|
pInfo->m_MasterName = "?";
|
||
|
|
||
|
int iItem = m_ServicesList.InsertItem( COLUMN_COMPUTER_NAME, pInfo->m_ComputerName, NULL );
|
||
|
m_ServicesList.SetItemData( iItem, (DWORD)pInfo );
|
||
|
|
||
|
// Update the display of # of services.
|
||
|
UpdateServiceCountDisplay();
|
||
|
}
|
||
|
|
||
|
pInfo->m_ProtocolVersion = protocolVersion;
|
||
|
V_strncpy( pInfo->m_ServiceVersion, serviceVersion, sizeof( pInfo->m_ServiceVersion ) );
|
||
|
pInfo->m_Addr = ipFrom;
|
||
|
pInfo->m_CPUPercentage = cpuPercentage;
|
||
|
pInfo->m_MemUsageMB = memUsage;
|
||
|
pInfo->m_ExeName = exeName;
|
||
|
pInfo->m_MapName = mapName;
|
||
|
pInfo->m_MasterName = masterName;
|
||
|
pInfo->m_LiveTimeMS = liveTimeMS;
|
||
|
pInfo->m_WorkerAppTimeMS = workerAppTimeMS;
|
||
|
pInfo->m_iState = iState;
|
||
|
pInfo->m_LastPingTimeMS = Plat_MSTime();
|
||
|
pInfo->m_Password = password;
|
||
|
|
||
|
UpdateServiceInListbox( pInfo );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void CServicesDlg::UpdateServicesFromNetView()
|
||
|
{
|
||
|
CUtlVector<char*> computerNames;
|
||
|
m_NetViewThread.GetComputerNames( computerNames );
|
||
|
|
||
|
for ( int i=0; i < computerNames.Count(); i++ )
|
||
|
{
|
||
|
CServiceInfo *pInfo = FindServiceByComputerName( computerNames[i] );
|
||
|
if ( !pInfo )
|
||
|
{
|
||
|
pInfo = new CServiceInfo;
|
||
|
m_Services.AddToTail( pInfo );
|
||
|
pInfo->m_ComputerName = computerNames[i];
|
||
|
pInfo->m_LastPingTimeMS = Plat_MSTime() - SERVICE_OFF_TIMEOUT*2; // so it's marked as "off"
|
||
|
pInfo->m_LastLiveTimeMS = Plat_MSTime();
|
||
|
pInfo->m_LiveTimeMS = 0;
|
||
|
pInfo->m_WorkerAppTimeMS = 0;
|
||
|
pInfo->m_ProtocolVersion = 0;
|
||
|
pInfo->m_ServiceVersion[0] = 0;
|
||
|
pInfo->m_CPUPercentage = -1;
|
||
|
pInfo->m_MemUsageMB = -1;
|
||
|
|
||
|
int iItem = m_ServicesList.InsertItem( COLUMN_COMPUTER_NAME, pInfo->m_ComputerName, NULL );
|
||
|
m_ServicesList.SetItemData( iItem, (DWORD)pInfo );
|
||
|
|
||
|
// Update the display of # of services.
|
||
|
UpdateServiceCountDisplay();
|
||
|
|
||
|
UpdateServiceInListbox( pInfo );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
computerNames.PurgeAndDeleteElements();
|
||
|
}
|
||
|
|
||
|
|
||
|
void CServicesDlg::OnIdle()
|
||
|
{
|
||
|
DWORD curTime = GetTickCount();
|
||
|
|
||
|
if ( !m_pServicesPingSocket )
|
||
|
return;
|
||
|
|
||
|
// Broadcast out to all the services?
|
||
|
if ( curTime - m_dwLastServicesPing >= SERVICES_PING_INTERVAL )
|
||
|
{
|
||
|
m_dwLastServicesPing = curTime;
|
||
|
|
||
|
for ( int i=VMPI_SERVICE_PORT; i <= VMPI_LAST_SERVICE_PORT; i++ )
|
||
|
{
|
||
|
CUtlVector<char> data;
|
||
|
BuildVMPIPingPacket( data, VMPI_PING_REQUEST );
|
||
|
m_pServicesPingSocket->Broadcast( data.Base(), data.Count(), i );
|
||
|
|
||
|
// Also send out a version 4 one because we understand a version 4 response.
|
||
|
BuildVMPIPingPacket( data, VMPI_PING_REQUEST, 4 );
|
||
|
m_pServicesPingSocket->Broadcast( data.Base(), data.Count(), i );
|
||
|
}
|
||
|
|
||
|
UpdateServiceCountDisplay();
|
||
|
}
|
||
|
|
||
|
m_ServicesList.SetRedraw( false );
|
||
|
|
||
|
// Check for messages from services.
|
||
|
UpdateServicesFromNetMessages();
|
||
|
|
||
|
// Issue a "net view" command, parse the output, and add any computers it lists.
|
||
|
// This lets us figure out which PCs on the network are not running the service.
|
||
|
UpdateServicesFromNetView();
|
||
|
|
||
|
|
||
|
FOR_EACH_LL( m_Services, iService )
|
||
|
{
|
||
|
CServiceInfo *pInfo = m_Services[iService];
|
||
|
if ( Plat_MSTime() - pInfo->m_LastUpdateTime > SERVICE_MAX_UPDATE_INTERVAL )
|
||
|
{
|
||
|
UpdateServiceInListbox( pInfo );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( m_bListChanged )
|
||
|
{
|
||
|
ResortItems();
|
||
|
m_bListChanged = false;
|
||
|
}
|
||
|
|
||
|
m_ServicesList.SetRedraw( true );
|
||
|
}
|
||
|
|
||
|
|
||
|
CServiceInfo* CServicesDlg::FindServiceByComputerName( const char *pComputerName )
|
||
|
{
|
||
|
FOR_EACH_LL( m_Services, i )
|
||
|
{
|
||
|
if ( Q_stricmp( m_Services[i]->m_ComputerName, pComputerName ) == 0 )
|
||
|
return m_Services[i];
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
void CServicesDlg::SendToSelectedServices( const char *pData, int len )
|
||
|
{
|
||
|
POSITION pos = m_ServicesList.GetFirstSelectedItemPosition();
|
||
|
while ( pos )
|
||
|
{
|
||
|
int iItem = m_ServicesList.GetNextSelectedItem( pos );
|
||
|
|
||
|
CServiceInfo *pInfo = (CServiceInfo*)m_ServicesList.GetItemData( iItem );
|
||
|
m_pServicesPingSocket->SendTo( &pInfo->m_Addr, pData, len );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void UpdateItemText( CListCtrl &ctrl, int iItem, int iColumn, const char *pNewVal )
|
||
|
{
|
||
|
CString str = ctrl.GetItemText( iItem, iColumn );
|
||
|
if ( V_stricmp( str, pNewVal ) != 0 )
|
||
|
ctrl.SetItemText( iItem, iColumn, pNewVal );
|
||
|
}
|
||
|
|
||
|
|
||
|
void CServicesDlg::UpdateServiceInListbox( CServiceInfo *pInfo )
|
||
|
{
|
||
|
// First, find this item in the listbox.
|
||
|
LVFINDINFO info;
|
||
|
info.flags = LVFI_PARAM;
|
||
|
info.lParam = (LPARAM)pInfo;
|
||
|
int iItem = m_ServicesList.FindItem( &info );
|
||
|
if ( iItem != -1 )
|
||
|
{
|
||
|
UpdateItemText( m_ServicesList, iItem, COLUMN_COMPUTER_NAME, pInfo->m_ComputerName );
|
||
|
|
||
|
const char *pText = GetStatusString( pInfo );
|
||
|
UpdateItemText( m_ServicesList, iItem, COLUMN_STATUS, pText );
|
||
|
|
||
|
char timeStr[512];
|
||
|
FormatTimeString( pInfo->m_LiveTimeMS / 1000, timeStr, sizeof( timeStr ) );
|
||
|
UpdateItemText( m_ServicesList, iItem, COLUMN_RUNNING_TIME, timeStr );
|
||
|
|
||
|
FormatTimeString( pInfo->m_WorkerAppTimeMS / 1000, timeStr, sizeof( timeStr ) );
|
||
|
UpdateItemText( m_ServicesList, iItem, COLUMN_WORKER_APP_RUNNING_TIME, timeStr );
|
||
|
|
||
|
UpdateItemText( m_ServicesList, iItem, COLUMN_MASTER_NAME, pInfo->m_MasterName );
|
||
|
|
||
|
char str[512];
|
||
|
V_snprintf( str, sizeof( str ), "%d", pInfo->m_ProtocolVersion );
|
||
|
UpdateItemText( m_ServicesList, iItem, COLUMN_PROTOCOL_VERSION, str );
|
||
|
|
||
|
UpdateItemText( m_ServicesList, iItem, COLUMN_PASSWORD, pInfo->m_Password );
|
||
|
UpdateItemText( m_ServicesList, iItem, COLUMN_SERVICE_VERSION, pInfo->m_ServiceVersion );
|
||
|
|
||
|
if ( pInfo->m_CPUPercentage == -1 )
|
||
|
V_snprintf( str, sizeof( str ), "-" );
|
||
|
else if ( pInfo->m_CPUPercentage == 101 )
|
||
|
V_snprintf( str, sizeof( str ), "(err)" );
|
||
|
else
|
||
|
V_snprintf( str, sizeof( str ), "%d%%", pInfo->m_CPUPercentage );
|
||
|
UpdateItemText( m_ServicesList, iItem, COLUMN_CPU_PERCENTAGE, str );
|
||
|
|
||
|
UpdateItemText( m_ServicesList, iItem, COLUMN_EXE_NAME, pInfo->m_ExeName );
|
||
|
UpdateItemText( m_ServicesList, iItem, COLUMN_MAP_NAME, pInfo->m_MapName );
|
||
|
|
||
|
if ( pInfo->m_MemUsageMB == -1 )
|
||
|
V_snprintf( str, sizeof( str ), "-" );
|
||
|
else
|
||
|
V_snprintf( str, sizeof( str ), "%d", pInfo->m_MemUsageMB );
|
||
|
UpdateItemText( m_ServicesList, iItem, COLUMN_MEM_USAGE, str );
|
||
|
|
||
|
pInfo->m_pLastStatusText = pText;
|
||
|
pInfo->m_LastLiveTimeMS = pInfo->m_LiveTimeMS;
|
||
|
pInfo->m_LastMasterName = pInfo->m_MasterName;
|
||
|
pInfo->m_LastUpdateTime = Plat_MSTime();
|
||
|
|
||
|
// Detect changes.
|
||
|
if ( !m_bListChanged && iItem > 0 )
|
||
|
{
|
||
|
CServiceInfo *pPrevItem = (CServiceInfo*)m_ServicesList.GetItemData( iItem-1 );
|
||
|
if ( pPrevItem && MainSortFn( (LPARAM)pPrevItem, (LPARAM)pInfo, NULL ) > 0 )
|
||
|
m_bListChanged = true;
|
||
|
}
|
||
|
|
||
|
if ( !m_bListChanged && (iItem+1) < m_ServicesList.GetItemCount() )
|
||
|
{
|
||
|
CServiceInfo *pNextItem = (CServiceInfo*)m_ServicesList.GetItemData( iItem+1 );
|
||
|
if ( pNextItem && MainSortFn( (LPARAM)pInfo, (LPARAM)pNextItem, NULL ) > 0 )
|
||
|
m_bListChanged = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void CServicesDlg::ResortItems()
|
||
|
{
|
||
|
m_ServicesList.SortItems( MainSortFn, (LPARAM)this );
|
||
|
}
|
||
|
|
||
|
|
||
|
void CServicesDlg::UpdateServiceCountDisplay()
|
||
|
{
|
||
|
char str[512];
|
||
|
Q_snprintf( str, sizeof( str ), "%d", m_Services.Count() );
|
||
|
m_NumServicesControl.SetWindowText( str );
|
||
|
|
||
|
// Now count the various types.
|
||
|
int nDisabled = 0, nWorking = 0, nWaiting = 0, nOff = 0;
|
||
|
FOR_EACH_LL( m_Services, i )
|
||
|
{
|
||
|
if ( m_Services[i]->IsOff() )
|
||
|
{
|
||
|
++nOff;
|
||
|
}
|
||
|
else if ( m_Services[i]->m_iState == VMPI_STATE_BUSY || m_Services[i]->m_iState == VMPI_STATE_DOWNLOADING )
|
||
|
{
|
||
|
++nWorking;
|
||
|
}
|
||
|
else if ( m_Services[i]->m_iState == VMPI_STATE_IDLE )
|
||
|
{
|
||
|
++nWaiting;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
++nDisabled;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Q_snprintf( str, sizeof( str ), "%d", nDisabled );
|
||
|
m_NumDisabledServicesControl.SetWindowText( str );
|
||
|
|
||
|
Q_snprintf( str, sizeof( str ), "%d", nWorking );
|
||
|
m_NumWorkingServicesControl.SetWindowText( str );
|
||
|
|
||
|
Q_snprintf( str, sizeof( str ), "%d", nWaiting );
|
||
|
m_NumWaitingServicesControl.SetWindowText( str );
|
||
|
|
||
|
Q_snprintf( str, sizeof( str ), "%d", nOff );
|
||
|
m_NumOffServicesControl.SetWindowText( str );
|
||
|
}
|
||
|
|
||
|
|
||
|
// This monstrosity is here because of the way they bundle string resources into groups in an exe file.
|
||
|
// See http://support.microsoft.com/kb/q196774/.
|
||
|
bool FindStringResourceEx( HINSTANCE hinst, UINT uId, UINT langId, char *pStr, int outLen )
|
||
|
{
|
||
|
// Convert the string ID into a bundle number
|
||
|
bool bRet = false;
|
||
|
HRSRC hrsrc = FindResourceEx(hinst, RT_STRING, MAKEINTRESOURCE(uId / 16 + 1), langId);
|
||
|
if (hrsrc)
|
||
|
{
|
||
|
HGLOBAL hglob = LoadResource(hinst, hrsrc);
|
||
|
if (hglob)
|
||
|
{
|
||
|
LPCWSTR pwsz = reinterpret_cast<LPCWSTR>( LockResource(hglob) );
|
||
|
if (pwsz)
|
||
|
{
|
||
|
// okay now walk the string table
|
||
|
for (UINT i = 0; i < (uId & 15); i++)
|
||
|
{
|
||
|
pwsz += 1 + (UINT)*pwsz;
|
||
|
}
|
||
|
|
||
|
// First word in the resource is the length and the rest is the data.
|
||
|
int nChars = min( (int)pwsz[0], outLen-1 );
|
||
|
++pwsz;
|
||
|
V_wcstostr( pwsz, nChars, pStr, outLen );
|
||
|
pStr[nChars] = 0;
|
||
|
bRet = true;
|
||
|
}
|
||
|
FreeResource(hglob);
|
||
|
}
|
||
|
}
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
int CheckServiceVersion( const char *pPatchDir, char *pServiceVersion, int maxServiceVersionLen )
|
||
|
{
|
||
|
char filename[MAX_PATH];
|
||
|
V_ComposeFileName( pPatchDir, "vmpi_service.exe", filename, sizeof( filename ) );
|
||
|
|
||
|
int ret = IDCANCEL;
|
||
|
HINSTANCE hInst = LoadLibrary( filename );
|
||
|
if ( hInst )
|
||
|
{
|
||
|
bool bFound = FindStringResourceEx( hInst, VMPI_SERVICE_IDS_VERSION_STRING, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), pServiceVersion, maxServiceVersionLen );
|
||
|
if ( bFound )
|
||
|
{
|
||
|
ret = V_AfxMessageBox( MB_YESNOCANCEL, "Service version in %s is %s.\n\nIs this correct?", filename, pServiceVersion );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
V_AfxMessageBox( MB_OK, "Can't get IDS_VERSION_STRING resource from %s.", filename );
|
||
|
}
|
||
|
|
||
|
FreeLibrary( hInst );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
V_AfxMessageBox( MB_OK, "Can't load %s to get service version.", filename );
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void CServicesDlg::OnPatchServices()
|
||
|
{
|
||
|
// Inquire about the timeout.
|
||
|
CPatchTimeout dlg;
|
||
|
dlg.m_PatchDirectory = "\\\\fileserver\\vmpi\\testservice";
|
||
|
dlg.m_VMPITransferDirectory = dlg.m_PatchDirectory;
|
||
|
dlg.m_bForcePatch = false;
|
||
|
|
||
|
TryAgain:;
|
||
|
|
||
|
if ( dlg.DoModal() == IDOK )
|
||
|
{
|
||
|
// Launch the transfer app.
|
||
|
char commandLine[32 * 1024] = {0};
|
||
|
|
||
|
char transferExe[MAX_PATH];
|
||
|
V_ComposeFileName( dlg.m_VMPITransferDirectory, "vmpi_transfer.exe", transferExe, sizeof( transferExe ) );
|
||
|
if ( _access( transferExe, 0 ) != 0 )
|
||
|
{
|
||
|
V_AfxMessageBox( MB_OK, "Can't find '%s' to run the patch.", transferExe );
|
||
|
goto TryAgain;
|
||
|
}
|
||
|
|
||
|
char strServiceVersion[64];
|
||
|
int ret = CheckServiceVersion( dlg.m_PatchDirectory, strServiceVersion, sizeof( strServiceVersion ) );
|
||
|
if ( ret == IDCANCEL )
|
||
|
return;
|
||
|
else if ( ret == IDNO )
|
||
|
goto TryAgain;
|
||
|
|
||
|
AppendStr( commandLine, sizeof( commandLine ), "\"%s\" -PatchHost", transferExe );
|
||
|
AppendStr( commandLine, sizeof( commandLine ), " -mpi_PatchVersion %s", strServiceVersion );
|
||
|
AppendStr( commandLine, sizeof( commandLine ), " -mpi_PatchDirectory \"%s\"", (const char*)dlg.m_PatchDirectory );
|
||
|
|
||
|
if ( dlg.m_bForcePatch )
|
||
|
AppendStr( commandLine, sizeof( commandLine ), " -mpi_ForcePatch" );
|
||
|
|
||
|
// Collect the list of addresses.
|
||
|
CUtlVector<CIPAddr> addrs;
|
||
|
POSITION pos = m_ServicesList.GetFirstSelectedItemPosition();
|
||
|
while ( pos )
|
||
|
{
|
||
|
int iItem = m_ServicesList.GetNextSelectedItem( pos );
|
||
|
|
||
|
CServiceInfo *pInfo = (CServiceInfo*)m_ServicesList.GetItemData( iItem );
|
||
|
if ( pInfo->m_Addr.ip[0] != 0 ) // "off" services won't have an IP
|
||
|
addrs.AddToTail( pInfo->m_Addr );
|
||
|
}
|
||
|
if ( addrs.Count() == 0 )
|
||
|
{
|
||
|
AfxMessageBox( "No workers selected, or they all are off." );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
AppendStr( commandLine, sizeof( commandLine ), " -mpi_PatchWorkers %d", addrs.Count() );
|
||
|
for ( int i=0; i < addrs.Count(); i++ )
|
||
|
{
|
||
|
AppendStr( commandLine, sizeof( commandLine ), " %d.%d.%d.%d", addrs[i].ip[0], addrs[i].ip[1], addrs[i].ip[2], addrs[i].ip[3] );
|
||
|
}
|
||
|
|
||
|
STARTUPINFO si;
|
||
|
memset( &si, 0, sizeof( si ) );
|
||
|
si.cb = sizeof( si );
|
||
|
|
||
|
PROCESS_INFORMATION pi;
|
||
|
memset( &pi, 0, sizeof( pi ) );
|
||
|
|
||
|
if ( CreateProcess( NULL, commandLine,
|
||
|
NULL, NULL, false,
|
||
|
0,
|
||
|
NULL,
|
||
|
(const char *)dlg.m_PatchDirectory,
|
||
|
&si,
|
||
|
&pi ) )
|
||
|
{
|
||
|
CloseHandle( pi.hProcess );
|
||
|
CloseHandle( pi.hThread );
|
||
|
|
||
|
V_AfxMessageBox( MB_OK, "Patch master successfully started.\nServices patching now.\nClose the patch master console app when finished." );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
V_AfxMessageBox( MB_OK, "Error starting patch master: %s", GetLastErrorString() );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void CServicesDlg::OnStopServices()
|
||
|
{
|
||
|
if ( MessageBox( "Warning: if you stop these services, you won't be able to control them from this application, and must restart them manually. Contine?", "Warning", MB_YESNO ) == IDYES )
|
||
|
{
|
||
|
CUtlVector<char> data;
|
||
|
BuildVMPIPingPacket( data, VMPI_STOP_SERVICE );
|
||
|
SendToSelectedServices( data.Base(), data.Count() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CServicesDlg::OnStopJobs()
|
||
|
{
|
||
|
CUtlVector<char> data;
|
||
|
BuildVMPIPingPacket( data, VMPI_KILL_PROCESS );
|
||
|
SendToSelectedServices( data.Base(), data.Count() );
|
||
|
}
|
||
|
|
||
|
|
||
|
void CServicesDlg::OnFilterByPassword()
|
||
|
{
|
||
|
CSetPasswordDlg dlg( IDD_SET_PASSWORD );
|
||
|
dlg.m_Password = m_Password;
|
||
|
|
||
|
if ( dlg.DoModal() == IDOK )
|
||
|
{
|
||
|
m_Password = dlg.m_Password;
|
||
|
m_PasswordDisplay.SetWindowText( m_Password );
|
||
|
|
||
|
m_Services.PurgeAndDeleteElements();
|
||
|
m_ServicesList.DeleteAllItems();
|
||
|
|
||
|
UpdateServiceCountDisplay();
|
||
|
|
||
|
// Re-ping everyone immediately.
|
||
|
m_dwLastServicesPing = GetTickCount() - SERVICES_PING_INTERVAL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// This sets a new password on the selected services.
|
||
|
void CServicesDlg::OnForcePassword()
|
||
|
{
|
||
|
CSetPasswordDlg dlg( IDD_FORCE_PASSWORD );
|
||
|
dlg.m_Password = "password";
|
||
|
|
||
|
if ( dlg.DoModal() == IDOK )
|
||
|
{
|
||
|
CUtlVector<char> data;
|
||
|
|
||
|
BuildVMPIPingPacket( data, VMPI_FORCE_PASSWORD_CHANGE, VMPI_PROTOCOL_VERSION, true );
|
||
|
const char *pNewPassword = dlg.m_Password;
|
||
|
data.AddMultipleToTail( V_strlen( pNewPassword ) + 1, pNewPassword );
|
||
|
|
||
|
SendToSelectedServices( data.Base(), data.Count() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CServicesDlg::OnDblclkServicesList(NMHDR* pNMHDR, LRESULT* pResult)
|
||
|
{
|
||
|
POSITION pos = m_ServicesList.GetFirstSelectedItemPosition();
|
||
|
if ( pos )
|
||
|
{
|
||
|
int iItem = m_ServicesList.GetNextSelectedItem( pos );
|
||
|
if ( iItem != -1 )
|
||
|
{
|
||
|
CServiceInfo *pInfo = (CServiceInfo*)m_ServicesList.GetItemData( iItem );
|
||
|
if ( pInfo )
|
||
|
{
|
||
|
// Launch vmpi_browser_job_search and have it auto-select this worker.
|
||
|
char cmdLine[1024];
|
||
|
Q_snprintf( cmdLine, sizeof( cmdLine ), "vmpi_job_search -SelectWorker %s", (const char*)pInfo->m_ComputerName );
|
||
|
|
||
|
STARTUPINFO si;
|
||
|
memset( &si, 0, sizeof( si ) );
|
||
|
si.cb = sizeof( si );
|
||
|
|
||
|
PROCESS_INFORMATION pi;
|
||
|
memset( &pi, 0, sizeof( pi ) );
|
||
|
|
||
|
if ( !CreateProcess(
|
||
|
NULL,
|
||
|
(char*)(const char*)cmdLine,
|
||
|
NULL, // security
|
||
|
NULL,
|
||
|
TRUE,
|
||
|
0, // flags
|
||
|
NULL, // environment
|
||
|
NULL, // current directory
|
||
|
&si,
|
||
|
&pi ) )
|
||
|
{
|
||
|
char err[512];
|
||
|
Q_snprintf( err, sizeof( err ), "Can't run '%s'", (LPCTSTR)cmdLine );
|
||
|
MessageBox( err, "Error", MB_OK );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*pResult = 0;
|
||
|
}
|
||
|
|
||
|
void CServicesDlg::OnSize(UINT nType, int cx, int cy)
|
||
|
{
|
||
|
CIdleDialog::OnSize(nType, cx, cy);
|
||
|
|
||
|
m_AnchorMgr.UpdateAnchors( this );
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL CServicesDlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
|
||
|
{
|
||
|
NMHDR *pHdr = (NMHDR*)lParam;
|
||
|
if ( pHdr->idFrom == IDC_SERVICES_LIST )
|
||
|
{
|
||
|
if ( pHdr->code == LVN_COLUMNCLICK )
|
||
|
{
|
||
|
LPNMLISTVIEW pListView = (LPNMLISTVIEW)lParam;
|
||
|
|
||
|
// Now sort by this column.
|
||
|
int iSortColumn = max( 0, min( pListView->iSubItem, (int)ARRAYSIZE( g_ColumnInfos ) - 1 ) );
|
||
|
PushSortColumn( iSortColumn );
|
||
|
ResortItems();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return CIdleDialog::OnNotify(wParam, lParam, pResult);
|
||
|
}
|
||
|
|
||
|
|
||
|
void CServicesDlg::BuildClipboardText( CUtlVector<char> &clipboardText )
|
||
|
{
|
||
|
// Add the header information.
|
||
|
CHeaderCtrl *pHeader = m_ServicesList.GetHeaderCtrl();
|
||
|
for ( int i=0; i < pHeader->GetItemCount(); i++ )
|
||
|
{
|
||
|
char tempBuffer[512];
|
||
|
HDITEM item;
|
||
|
memset( &item, 0, sizeof( item ) );
|
||
|
item.mask = HDI_TEXT;
|
||
|
item.pszText = tempBuffer;
|
||
|
item.cchTextMax = sizeof( tempBuffer ) - 1;
|
||
|
|
||
|
if ( !pHeader->GetItem( i, &item ) )
|
||
|
item.pszText = "<bug>";
|
||
|
|
||
|
clipboardText.AddMultipleToTail( strlen( item.pszText ), item.pszText );
|
||
|
clipboardText.AddToTail( '\t' );
|
||
|
}
|
||
|
clipboardText.AddMultipleToTail( 2, "\r\n" );
|
||
|
|
||
|
// Now add each line of data.
|
||
|
int nItem = -1;
|
||
|
while ( (nItem = m_ServicesList.GetNextItem( nItem, LVNI_ALL )) != -1 )
|
||
|
{
|
||
|
char tempBuffer[512];
|
||
|
LVITEM item;
|
||
|
memset( &item, 0, sizeof( item ) );
|
||
|
item.mask = LVIF_TEXT;
|
||
|
item.iItem = nItem;
|
||
|
item.pszText = tempBuffer;
|
||
|
item.cchTextMax = sizeof( tempBuffer ) - 1;
|
||
|
|
||
|
for ( int i=0; i < pHeader->GetItemCount(); i++ )
|
||
|
{
|
||
|
item.iSubItem = i;
|
||
|
if ( !m_ServicesList.GetItem( &item ) )
|
||
|
{
|
||
|
item.pszText = "<bug>";
|
||
|
}
|
||
|
|
||
|
clipboardText.AddMultipleToTail( strlen( item.pszText ), item.pszText );
|
||
|
clipboardText.AddToTail( '\t' );
|
||
|
}
|
||
|
clipboardText.AddMultipleToTail( 2, "\r\n" );
|
||
|
}
|
||
|
clipboardText.AddToTail( 0 );
|
||
|
}
|
||
|
|
||
|
|
||
|
void CServicesDlg::OnCopyToClipboard()
|
||
|
{
|
||
|
// Open and clear the clipboard.
|
||
|
if ( !OpenClipboard() )
|
||
|
return;
|
||
|
|
||
|
EmptyClipboard();
|
||
|
|
||
|
// Setup the clipboard text.
|
||
|
CUtlVector<char> clipboardText;
|
||
|
BuildClipboardText( clipboardText );
|
||
|
|
||
|
// Put the clipboard text into a global memory object.
|
||
|
HANDLE hMem = GlobalAlloc( GMEM_MOVEABLE, clipboardText.Count() );
|
||
|
void *ptr = GlobalLock( hMem );
|
||
|
memcpy( ptr, clipboardText.Base(), clipboardText.Count() );
|
||
|
GlobalUnlock( hMem );
|
||
|
|
||
|
// Put it in the clipboard.
|
||
|
SetClipboardData( CF_TEXT, hMem );
|
||
|
|
||
|
// Cleanup.
|
||
|
GlobalFree( hMem );
|
||
|
CloseClipboard();
|
||
|
}
|