//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// // JobSearchDlg.cpp : implementation file // #include "stdafx.h" #include "JobSearchDlg.h" #include "imysqlwrapper.h" #include "tier1/strtools.h" #include "utllinkedlist.h" #include "vmpi_browser_helpers.h" #include "vmpi_defs.h" #include "net_view_thread.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif // These are stored with jobs to help with sorting and to remember the job ID. class CJobInfo { public: unsigned long m_JobID; CString m_StartTimeUnformatted; CString m_MachineName; CString m_BSPFilename; DWORD m_RunningTimeMS; }; ///////////////////////////////////////////////////////////////////////////// // CJobSearchDlg dialog CJobSearchDlg::CJobSearchDlg(CWnd* pParent /*=NULL*/) : CDialog(CJobSearchDlg::IDD, pParent) { //{{AFX_DATA_INIT(CJobSearchDlg) //}}AFX_DATA_INIT m_pSQL = NULL; m_hMySQLDLL = NULL; } CJobSearchDlg::~CJobSearchDlg() { if ( m_pSQL ) { m_pSQL->Release(); } if ( m_hMySQLDLL ) { Sys_UnloadModule( m_hMySQLDLL ); } } void CJobSearchDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CJobSearchDlg) DDX_Control(pDX, IDC_WORKER_LIST, m_WorkerList); DDX_Control(pDX, IDC_USER_LIST, m_UserList); DDX_Control(pDX, IDC_JOBS_LIST, m_JobsList); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CJobSearchDlg, CDialog) //{{AFX_MSG_MAP(CJobSearchDlg) ON_NOTIFY(NM_DBLCLK, IDC_JOBS_LIST, OnDblclkJobsList) ON_LBN_DBLCLK(IDC_USER_LIST, OnDblclkUserList) ON_LBN_DBLCLK(IDC_WORKER_LIST, OnDblclkWorkerList) ON_BN_CLICKED(IDC_QUIT, OnQuit) ON_WM_SIZE() //}}AFX_MSG_MAP END_MESSAGE_MAP() int CJobSearchDlg::GetSelectedJobIndex() { POSITION pos = m_JobsList.GetFirstSelectedItemPosition(); if ( pos ) return m_JobsList.GetNextSelectedItem( pos ); else return -1; } ///////////////////////////////////////////////////////////////////////////// // CJobSearchDlg message handlers void CJobSearchDlg::OnDblclkJobsList(NMHDR* pNMHDR, LRESULT* pResult) { int iItem = GetSelectedJobIndex(); if ( iItem != -1 ) { CJobInfo *pInfo = (CJobInfo*)m_JobsList.GetItemData( iItem ); CString cmdLine; cmdLine.Format( "vmpi_job_watch -JobID %d -dbname \"%s\" -hostname \"%s\" -username \"%s\"", pInfo->m_JobID, (const char*)m_DBName, (const char*)m_HostName, (const char*)m_UserName ); 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 ) ) { CString errStr; errStr.Format( "Error launching '%s'", cmdLine.GetBuffer() ); MessageBox( errStr, "Error", MB_OK ); } } *pResult = 0; } static int CALLBACK JobsSortFn( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam ) { CJobInfo *pInfo1 = (CJobInfo*)iItem1; CJobInfo *pInfo2 = (CJobInfo*)iItem2; return strcmp( pInfo2->m_StartTimeUnformatted, pInfo1->m_StartTimeUnformatted ); } void CJobSearchDlg::ClearJobsList() { // First, delete all the JobInfo structures we have in it. int nItems = m_JobsList.GetItemCount(); for ( int i=0; i < nItems; i++ ) { CJobInfo *pInfo = (CJobInfo*)m_JobsList.GetItemData( i ); delete pInfo; } m_JobsList.DeleteAllItems(); } void CJobSearchDlg::RepopulateJobsList() { // It's assumed coming into this routine that the caller just executed a query that we can iterate over. ClearJobsList(); CUtlLinkedList jobInfos; while ( GetMySQL()->NextRow() ) { CJobInfo *pInfo = new CJobInfo; pInfo->m_StartTimeUnformatted = GetMySQL()->GetColumnValue( "StartTime" ).String(); pInfo->m_JobID = GetMySQL()->GetColumnValue( "JobID" ).Int32(); pInfo->m_MachineName = GetMySQL()->GetColumnValue( "MachineName" ).String(); pInfo->m_BSPFilename = GetMySQL()->GetColumnValue( "BSPFilename" ).String(); pInfo->m_RunningTimeMS = GetMySQL()->GetColumnValue( "RunningTimeMS" ).Int32(); jobInfos.AddToTail( pInfo ); } FOR_EACH_LL( jobInfos, j ) { CJobInfo *pInfo = jobInfos[j]; // Add the item. int iItem = m_JobsList.InsertItem( 0, "", NULL ); // Associate it with the job structure. m_JobsList.SetItemData( iItem, (DWORD)pInfo ); char dateStr[128]; const char *pDate = pInfo->m_StartTimeUnformatted; if ( strlen( pDate ) == 14 ) // yyyymmddhhmmss { Q_snprintf( dateStr, sizeof( dateStr ), "%c%c/%c%c %c%c:%c%c:00", pDate[4], pDate[5], pDate[6], pDate[7], pDate[8], pDate[9], pDate[10], pDate[11] ); } m_JobsList.SetItemText( iItem, 0, dateStr ); m_JobsList.SetItemText( iItem, 1, pInfo->m_MachineName ); m_JobsList.SetItemText( iItem, 2, pInfo->m_BSPFilename ); char timeStr[512]; if ( pInfo->m_RunningTimeMS == RUNNINGTIME_MS_SENTINEL ) { Q_strncpy( timeStr, "?", sizeof( timeStr ) ); } else { FormatTimeString( pInfo->m_RunningTimeMS / 1000, timeStr, sizeof( timeStr ) ); } m_JobsList.SetItemText( iItem, 3, timeStr ); char jobIDStr[512]; Q_snprintf( jobIDStr, sizeof( jobIDStr ), "%d", pInfo->m_JobID ); m_JobsList.SetItemText( iItem, 4, jobIDStr ); } m_JobsList.SortItems( JobsSortFn, (LPARAM)&m_JobsList ); } void CJobSearchDlg::OnDblclkUserList() { int sel = m_UserList.GetCurSel(); if ( sel != LB_ERR ) { CString computerName; m_UserList.GetText( sel, computerName ); // Look for jobs that this user initiated. char query[4096]; Q_snprintf( query, sizeof( query ), "select RunningTimeMS, JobID, BSPFilename, StartTime, MachineName from job_master_start where MachineName=\"%s\"", (const char*)computerName ); GetMySQL()->Execute( query ); RepopulateJobsList(); } } void CJobSearchDlg::OnDblclkWorkerList() { int sel = m_WorkerList.GetCurSel(); if ( sel != LB_ERR ) { CString computerName; m_WorkerList.GetText( sel, computerName ); // This query does: // 1. Take the workers with the specified MachineName. // 2. Only use IsMaster = 0. // 3. Now get all the job_master_start records with the same JobID. char query[4096]; Q_snprintf( query, sizeof( query ), "select job_master_start.RunningTimeMS, job_master_start.JobID, job_master_start.BSPFilename, job_master_start.StartTime, job_master_start.MachineName " "from job_master_start, job_worker_start " "where job_worker_start.MachineName = \"%s\" and " "IsMaster = 0 and " "job_master_start.JobID = job_worker_start.JobID", (const char*)computerName ); GetMySQL()->Execute( query ); RepopulateJobsList(); } } bool ReadStringFromFile( FILE *fp, char *pStr, int strSize ) { int i=0; for ( i; i < strSize-2; i++ ) { if ( fread( &pStr[i], 1, 1, fp ) != 1 || pStr[i] == '\n' ) { break; } } pStr[i] = 0; return i != 0; } const char* FindArg( const char *pArgName, const char *pDefault="" ) { for ( int i=1; i < __argc; i++ ) { if ( Q_stricmp( pArgName, __argv[i] ) == 0 ) { if ( (i+1) < __argc ) return __argv[i+1]; else return pDefault; } } return NULL; } BOOL CJobSearchDlg::OnInitDialog() { CDialog::OnInitDialog(); m_JobsList.SetExtendedStyle( LVS_EX_FULLROWSELECT ); char str[512]; // Init the mysql database. const char *pDBName = FindArg( "-dbname", NULL ); const char *pHostName = FindArg( "-hostname", NULL ); const char *pUserName = FindArg( "-username", NULL ); if ( pDBName && pHostName && pUserName ) { m_DBName = pDBName; m_HostName = pHostName; m_UserName = pUserName; } else { // Load the dbinfo_browser.txt file to get the database information. const char *pFilename = FindArg( "-dbinfo", NULL ); if ( !pFilename ) pFilename = "dbinfo_job_search.txt"; FILE *fp = fopen( pFilename, "rt" ); if ( !fp ) { Q_snprintf( str, sizeof( str ), "Can't open '%s' for database info.", pFilename ); MessageBox( str, "Error", MB_OK ); EndDialog( 0 ); return FALSE; } char hostName[512], dbName[512], userName[512]; if ( !ReadStringFromFile( fp, hostName, sizeof( hostName ) ) || !ReadStringFromFile( fp, dbName, sizeof( dbName ) ) || !ReadStringFromFile( fp, userName, sizeof( userName ) ) ) { fclose( fp ); Q_snprintf( str, sizeof( str ), "'%s' has invalid format.", pFilename ); MessageBox( str, "Error", MB_OK ); EndDialog( 0 ); return FALSE; } m_DBName = dbName; m_HostName = hostName; m_UserName = userName; fclose( fp ); } // Get the mysql interface. if ( !Sys_LoadInterface( "mysql_wrapper", MYSQL_WRAPPER_VERSION_NAME, &m_hMySQLDLL, (void**)&m_pSQL ) ) return false; if ( !m_pSQL->InitMySQL( m_DBName, m_HostName, m_UserName ) ) { Q_snprintf( str, sizeof( str ), "Can't init MYSQL db (db = '%s', host = '%s', user = '%s')", (const char*)m_DBName, (const char*)m_HostName, (const char*)m_UserName ); MessageBox( str, "Error", MB_OK ); EndDialog( 0 ); return FALSE; } // Setup the headers for the job info list. struct { char *pText; int width; } titles[] = { {"Date", 100}, {"User", 100}, {"BSP Filename", 100}, {"Running Time", 100}, {"Job ID", 100} }; for ( int i=0; i < ARRAYSIZE( titles ); i++ ) { m_JobsList.InsertColumn( i, titles[i].pText, LVCFMT_LEFT, titles[i].width, i ); } CUtlVector computerNames; CNetViewThread netView; netView.Init(); DWORD startTime = GetTickCount(); while ( 1 ) { netView.GetComputerNames( computerNames ); if ( computerNames.Count() > 0 ) break; Sleep( 30 ); if ( GetTickCount() - startTime > 5000 ) { Q_snprintf( str, sizeof( str ), "Unable to get computer names Can't init MYSQL db (db = '%s', host = '%s', user = '%s')", (const char*)m_DBName, (const char*)m_HostName, (const char*)m_UserName ); MessageBox( str, "Error", MB_OK ); EndDialog( 0 ); return FALSE; } } PopulateWorkerList( computerNames ); PopulateUserList( computerNames ); // Auto-select a worker? const char *pSelectWorker = FindArg( "-SelectWorker", NULL ); if ( pSelectWorker ) { int index = m_WorkerList.FindString( -1, pSelectWorker ); if ( index != LB_ERR ) { m_WorkerList.SetCurSel( index ); OnDblclkWorkerList(); } } // Setup our anchors. m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_SEARCH_BY_USER_PANEL ), ANCHOR_LEFT, ANCHOR_TOP, ANCHOR_WIDTH_PERCENT, ANCHOR_HEIGHT_PERCENT ); m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_USER_LIST ), ANCHOR_LEFT, ANCHOR_TOP, ANCHOR_WIDTH_PERCENT, ANCHOR_HEIGHT_PERCENT ); m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_WORKER_PANEL ), ANCHOR_WIDTH_PERCENT, ANCHOR_TOP, ANCHOR_RIGHT, ANCHOR_HEIGHT_PERCENT ); m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_WORKER_LIST ), ANCHOR_WIDTH_PERCENT, ANCHOR_TOP, ANCHOR_RIGHT, ANCHOR_HEIGHT_PERCENT ); m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_JOBS_PANEL ), ANCHOR_LEFT, ANCHOR_HEIGHT_PERCENT, ANCHOR_RIGHT, ANCHOR_BOTTOM ); m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_JOBS_LIST ), ANCHOR_LEFT, ANCHOR_HEIGHT_PERCENT, ANCHOR_RIGHT, ANCHOR_BOTTOM ); m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_QUIT ), ANCHOR_WIDTH_PERCENT, ANCHOR_BOTTOM, ANCHOR_WIDTH_PERCENT, ANCHOR_BOTTOM ); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } void CJobSearchDlg::PopulateWorkerList( CUtlVector &computerNames ) { m_WorkerList.ResetContent(); for ( int i=0; i < computerNames.Count(); i++ ) { m_WorkerList.AddString( computerNames[i] ); } } void CJobSearchDlg::PopulateUserList( CUtlVector &computerNames ) { m_UserList.ResetContent(); for ( int i=0; i < computerNames.Count(); i++ ) { m_UserList.AddString( computerNames[i] ); } } void CJobSearchDlg::OnQuit() { EndDialog( 0 ); } void CJobSearchDlg::OnSize(UINT nType, int cx, int cy) { CDialog::OnSize(nType, cx, cy); m_AnchorMgr.UpdateAnchors( this ); }