Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

473 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // JobSearchDlg.cpp : implementation file
  9. //
  10. #include "stdafx.h"
  11. #include "JobSearchDlg.h"
  12. #include "imysqlwrapper.h"
  13. #include "tier1/strtools.h"
  14. #include "utllinkedlist.h"
  15. #include "vmpi_browser_helpers.h"
  16. #include "vmpi_defs.h"
  17. #include "net_view_thread.h"
  18. #ifdef _DEBUG
  19. #define new DEBUG_NEW
  20. #undef THIS_FILE
  21. static char THIS_FILE[] = __FILE__;
  22. #endif
  23. // These are stored with jobs to help with sorting and to remember the job ID.
  24. class CJobInfo
  25. {
  26. public:
  27. unsigned long m_JobID;
  28. CString m_StartTimeUnformatted;
  29. CString m_MachineName;
  30. CString m_BSPFilename;
  31. DWORD m_RunningTimeMS;
  32. };
  33. /////////////////////////////////////////////////////////////////////////////
  34. // CJobSearchDlg dialog
  35. CJobSearchDlg::CJobSearchDlg(CWnd* pParent /*=NULL*/)
  36. : CDialog(CJobSearchDlg::IDD, pParent)
  37. {
  38. //{{AFX_DATA_INIT(CJobSearchDlg)
  39. //}}AFX_DATA_INIT
  40. m_pSQL = NULL;
  41. m_hMySQLDLL = NULL;
  42. }
  43. CJobSearchDlg::~CJobSearchDlg()
  44. {
  45. if ( m_pSQL )
  46. {
  47. m_pSQL->Release();
  48. }
  49. if ( m_hMySQLDLL )
  50. {
  51. Sys_UnloadModule( m_hMySQLDLL );
  52. }
  53. }
  54. void CJobSearchDlg::DoDataExchange(CDataExchange* pDX)
  55. {
  56. CDialog::DoDataExchange(pDX);
  57. //{{AFX_DATA_MAP(CJobSearchDlg)
  58. DDX_Control(pDX, IDC_WORKER_LIST, m_WorkerList);
  59. DDX_Control(pDX, IDC_USER_LIST, m_UserList);
  60. DDX_Control(pDX, IDC_JOBS_LIST, m_JobsList);
  61. //}}AFX_DATA_MAP
  62. }
  63. BEGIN_MESSAGE_MAP(CJobSearchDlg, CDialog)
  64. //{{AFX_MSG_MAP(CJobSearchDlg)
  65. ON_NOTIFY(NM_DBLCLK, IDC_JOBS_LIST, OnDblclkJobsList)
  66. ON_LBN_DBLCLK(IDC_USER_LIST, OnDblclkUserList)
  67. ON_LBN_DBLCLK(IDC_WORKER_LIST, OnDblclkWorkerList)
  68. ON_BN_CLICKED(IDC_QUIT, OnQuit)
  69. ON_WM_SIZE()
  70. //}}AFX_MSG_MAP
  71. END_MESSAGE_MAP()
  72. int CJobSearchDlg::GetSelectedJobIndex()
  73. {
  74. POSITION pos = m_JobsList.GetFirstSelectedItemPosition();
  75. if ( pos )
  76. return m_JobsList.GetNextSelectedItem( pos );
  77. else
  78. return -1;
  79. }
  80. /////////////////////////////////////////////////////////////////////////////
  81. // CJobSearchDlg message handlers
  82. void CJobSearchDlg::OnDblclkJobsList(NMHDR* pNMHDR, LRESULT* pResult)
  83. {
  84. int iItem = GetSelectedJobIndex();
  85. if ( iItem != -1 )
  86. {
  87. CJobInfo *pInfo = (CJobInfo*)m_JobsList.GetItemData( iItem );
  88. CString cmdLine;
  89. cmdLine.Format( "vmpi_job_watch -JobID %d -dbname \"%s\" -hostname \"%s\" -username \"%s\"",
  90. pInfo->m_JobID, (const char*)m_DBName, (const char*)m_HostName, (const char*)m_UserName );
  91. STARTUPINFO si;
  92. memset( &si, 0, sizeof( si ) );
  93. si.cb = sizeof( si );
  94. PROCESS_INFORMATION pi;
  95. memset( &pi, 0, sizeof( pi ) );
  96. if ( !CreateProcess(
  97. NULL,
  98. (char*)(const char*)cmdLine,
  99. NULL, // security
  100. NULL,
  101. TRUE,
  102. 0, // flags
  103. NULL, // environment
  104. NULL, // current directory
  105. &si,
  106. &pi ) )
  107. {
  108. CString errStr;
  109. errStr.Format( "Error launching '%s'", cmdLine.GetBuffer() );
  110. MessageBox( errStr, "Error", MB_OK );
  111. }
  112. }
  113. *pResult = 0;
  114. }
  115. static int CALLBACK JobsSortFn( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
  116. {
  117. CJobInfo *pInfo1 = (CJobInfo*)iItem1;
  118. CJobInfo *pInfo2 = (CJobInfo*)iItem2;
  119. return strcmp( pInfo2->m_StartTimeUnformatted, pInfo1->m_StartTimeUnformatted );
  120. }
  121. void CJobSearchDlg::ClearJobsList()
  122. {
  123. // First, delete all the JobInfo structures we have in it.
  124. int nItems = m_JobsList.GetItemCount();
  125. for ( int i=0; i < nItems; i++ )
  126. {
  127. CJobInfo *pInfo = (CJobInfo*)m_JobsList.GetItemData( i );
  128. delete pInfo;
  129. }
  130. m_JobsList.DeleteAllItems();
  131. }
  132. void CJobSearchDlg::RepopulateJobsList()
  133. {
  134. // It's assumed coming into this routine that the caller just executed a query that we can iterate over.
  135. ClearJobsList();
  136. CUtlLinkedList<CJobInfo*, int> jobInfos;
  137. while ( GetMySQL()->NextRow() )
  138. {
  139. CJobInfo *pInfo = new CJobInfo;
  140. pInfo->m_StartTimeUnformatted = GetMySQL()->GetColumnValue( "StartTime" ).String();
  141. pInfo->m_JobID = GetMySQL()->GetColumnValue( "JobID" ).Int32();
  142. pInfo->m_MachineName = GetMySQL()->GetColumnValue( "MachineName" ).String();
  143. pInfo->m_BSPFilename = GetMySQL()->GetColumnValue( "BSPFilename" ).String();
  144. pInfo->m_RunningTimeMS = GetMySQL()->GetColumnValue( "RunningTimeMS" ).Int32();
  145. jobInfos.AddToTail( pInfo );
  146. }
  147. FOR_EACH_LL( jobInfos, j )
  148. {
  149. CJobInfo *pInfo = jobInfos[j];
  150. // Add the item.
  151. int iItem = m_JobsList.InsertItem( 0, "", NULL );
  152. // Associate it with the job structure.
  153. m_JobsList.SetItemData( iItem, (DWORD)pInfo );
  154. char dateStr[128];
  155. const char *pDate = pInfo->m_StartTimeUnformatted;
  156. if ( strlen( pDate ) == 14 ) // yyyymmddhhmmss
  157. {
  158. Q_snprintf( dateStr, sizeof( dateStr ), "%c%c/%c%c %c%c:%c%c:00",
  159. pDate[4], pDate[5],
  160. pDate[6], pDate[7],
  161. pDate[8], pDate[9],
  162. pDate[10], pDate[11] );
  163. }
  164. m_JobsList.SetItemText( iItem, 0, dateStr );
  165. m_JobsList.SetItemText( iItem, 1, pInfo->m_MachineName );
  166. m_JobsList.SetItemText( iItem, 2, pInfo->m_BSPFilename );
  167. char timeStr[512];
  168. if ( pInfo->m_RunningTimeMS == RUNNINGTIME_MS_SENTINEL )
  169. {
  170. Q_strncpy( timeStr, "?", sizeof( timeStr ) );
  171. }
  172. else
  173. {
  174. FormatTimeString( pInfo->m_RunningTimeMS / 1000, timeStr, sizeof( timeStr ) );
  175. }
  176. m_JobsList.SetItemText( iItem, 3, timeStr );
  177. char jobIDStr[512];
  178. Q_snprintf( jobIDStr, sizeof( jobIDStr ), "%d", pInfo->m_JobID );
  179. m_JobsList.SetItemText( iItem, 4, jobIDStr );
  180. }
  181. m_JobsList.SortItems( JobsSortFn, (LPARAM)&m_JobsList );
  182. }
  183. void CJobSearchDlg::OnDblclkUserList()
  184. {
  185. int sel = m_UserList.GetCurSel();
  186. if ( sel != LB_ERR )
  187. {
  188. CString computerName;
  189. m_UserList.GetText( sel, computerName );
  190. // Look for jobs that this user initiated.
  191. char query[4096];
  192. Q_snprintf( query, sizeof( query ), "select RunningTimeMS, JobID, BSPFilename, StartTime, MachineName from job_master_start where MachineName=\"%s\"", (const char*)computerName );
  193. GetMySQL()->Execute( query );
  194. RepopulateJobsList();
  195. }
  196. }
  197. void CJobSearchDlg::OnDblclkWorkerList()
  198. {
  199. int sel = m_WorkerList.GetCurSel();
  200. if ( sel != LB_ERR )
  201. {
  202. CString computerName;
  203. m_WorkerList.GetText( sel, computerName );
  204. // This query does:
  205. // 1. Take the workers with the specified MachineName.
  206. // 2. Only use IsMaster = 0.
  207. // 3. Now get all the job_master_start records with the same JobID.
  208. char query[4096];
  209. 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 "
  210. "from job_master_start, job_worker_start "
  211. "where job_worker_start.MachineName = \"%s\" and "
  212. "IsMaster = 0 and "
  213. "job_master_start.JobID = job_worker_start.JobID",
  214. (const char*)computerName );
  215. GetMySQL()->Execute( query );
  216. RepopulateJobsList();
  217. }
  218. }
  219. bool ReadStringFromFile( FILE *fp, char *pStr, int strSize )
  220. {
  221. int i=0;
  222. for ( i; i < strSize-2; i++ )
  223. {
  224. if ( fread( &pStr[i], 1, 1, fp ) != 1 ||
  225. pStr[i] == '\n' )
  226. {
  227. break;
  228. }
  229. }
  230. pStr[i] = 0;
  231. return i != 0;
  232. }
  233. const char* FindArg( const char *pArgName, const char *pDefault="" )
  234. {
  235. for ( int i=1; i < __argc; i++ )
  236. {
  237. if ( Q_stricmp( pArgName, __argv[i] ) == 0 )
  238. {
  239. if ( (i+1) < __argc )
  240. return __argv[i+1];
  241. else
  242. return pDefault;
  243. }
  244. }
  245. return NULL;
  246. }
  247. BOOL CJobSearchDlg::OnInitDialog()
  248. {
  249. CDialog::OnInitDialog();
  250. m_JobsList.SetExtendedStyle( LVS_EX_FULLROWSELECT );
  251. char str[512];
  252. // Init the mysql database.
  253. const char *pDBName = FindArg( "-dbname", NULL );
  254. const char *pHostName = FindArg( "-hostname", NULL );
  255. const char *pUserName = FindArg( "-username", NULL );
  256. if ( pDBName && pHostName && pUserName )
  257. {
  258. m_DBName = pDBName;
  259. m_HostName = pHostName;
  260. m_UserName = pUserName;
  261. }
  262. else
  263. {
  264. // Load the dbinfo_browser.txt file to get the database information.
  265. const char *pFilename = FindArg( "-dbinfo", NULL );
  266. if ( !pFilename )
  267. pFilename = "dbinfo_job_search.txt";
  268. FILE *fp = fopen( pFilename, "rt" );
  269. if ( !fp )
  270. {
  271. Q_snprintf( str, sizeof( str ), "Can't open '%s' for database info.", pFilename );
  272. MessageBox( str, "Error", MB_OK );
  273. EndDialog( 0 );
  274. return FALSE;
  275. }
  276. char hostName[512], dbName[512], userName[512];
  277. if ( !ReadStringFromFile( fp, hostName, sizeof( hostName ) ) ||
  278. !ReadStringFromFile( fp, dbName, sizeof( dbName ) ) ||
  279. !ReadStringFromFile( fp, userName, sizeof( userName ) )
  280. )
  281. {
  282. fclose( fp );
  283. Q_snprintf( str, sizeof( str ), "'%s' has invalid format.", pFilename );
  284. MessageBox( str, "Error", MB_OK );
  285. EndDialog( 0 );
  286. return FALSE;
  287. }
  288. m_DBName = dbName;
  289. m_HostName = hostName;
  290. m_UserName = userName;
  291. fclose( fp );
  292. }
  293. // Get the mysql interface.
  294. if ( !Sys_LoadInterface( "mysql_wrapper", MYSQL_WRAPPER_VERSION_NAME, &m_hMySQLDLL, (void**)&m_pSQL ) )
  295. return false;
  296. if ( !m_pSQL->InitMySQL( m_DBName, m_HostName, m_UserName ) )
  297. {
  298. 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 );
  299. MessageBox( str, "Error", MB_OK );
  300. EndDialog( 0 );
  301. return FALSE;
  302. }
  303. // Setup the headers for the job info list.
  304. struct
  305. {
  306. char *pText;
  307. int width;
  308. } titles[] =
  309. {
  310. {"Date", 100},
  311. {"User", 100},
  312. {"BSP Filename", 100},
  313. {"Running Time", 100},
  314. {"Job ID", 100}
  315. };
  316. for ( int i=0; i < ARRAYSIZE( titles ); i++ )
  317. {
  318. m_JobsList.InsertColumn( i, titles[i].pText, LVCFMT_LEFT, titles[i].width, i );
  319. }
  320. CUtlVector<char*> computerNames;
  321. CNetViewThread netView;
  322. netView.Init();
  323. DWORD startTime = GetTickCount();
  324. while ( 1 )
  325. {
  326. netView.GetComputerNames( computerNames );
  327. if ( computerNames.Count() > 0 )
  328. break;
  329. Sleep( 30 );
  330. if ( GetTickCount() - startTime > 5000 )
  331. {
  332. 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 );
  333. MessageBox( str, "Error", MB_OK );
  334. EndDialog( 0 );
  335. return FALSE;
  336. }
  337. }
  338. PopulateWorkerList( computerNames );
  339. PopulateUserList( computerNames );
  340. // Auto-select a worker?
  341. const char *pSelectWorker = FindArg( "-SelectWorker", NULL );
  342. if ( pSelectWorker )
  343. {
  344. int index = m_WorkerList.FindString( -1, pSelectWorker );
  345. if ( index != LB_ERR )
  346. {
  347. m_WorkerList.SetCurSel( index );
  348. OnDblclkWorkerList();
  349. }
  350. }
  351. // Setup our anchors.
  352. m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_SEARCH_BY_USER_PANEL ), ANCHOR_LEFT, ANCHOR_TOP, ANCHOR_WIDTH_PERCENT, ANCHOR_HEIGHT_PERCENT );
  353. m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_USER_LIST ), ANCHOR_LEFT, ANCHOR_TOP, ANCHOR_WIDTH_PERCENT, ANCHOR_HEIGHT_PERCENT );
  354. m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_WORKER_PANEL ), ANCHOR_WIDTH_PERCENT, ANCHOR_TOP, ANCHOR_RIGHT, ANCHOR_HEIGHT_PERCENT );
  355. m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_WORKER_LIST ), ANCHOR_WIDTH_PERCENT, ANCHOR_TOP, ANCHOR_RIGHT, ANCHOR_HEIGHT_PERCENT );
  356. m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_JOBS_PANEL ), ANCHOR_LEFT, ANCHOR_HEIGHT_PERCENT, ANCHOR_RIGHT, ANCHOR_BOTTOM );
  357. m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_JOBS_LIST ), ANCHOR_LEFT, ANCHOR_HEIGHT_PERCENT, ANCHOR_RIGHT, ANCHOR_BOTTOM );
  358. m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_QUIT ), ANCHOR_WIDTH_PERCENT, ANCHOR_BOTTOM, ANCHOR_WIDTH_PERCENT, ANCHOR_BOTTOM );
  359. return TRUE; // return TRUE unless you set the focus to a control
  360. // EXCEPTION: OCX Property Pages should return FALSE
  361. }
  362. void CJobSearchDlg::PopulateWorkerList( CUtlVector<char*> &computerNames )
  363. {
  364. m_WorkerList.ResetContent();
  365. for ( int i=0; i < computerNames.Count(); i++ )
  366. {
  367. m_WorkerList.AddString( computerNames[i] );
  368. }
  369. }
  370. void CJobSearchDlg::PopulateUserList( CUtlVector<char*> &computerNames )
  371. {
  372. m_UserList.ResetContent();
  373. for ( int i=0; i < computerNames.Count(); i++ )
  374. {
  375. m_UserList.AddString( computerNames[i] );
  376. }
  377. }
  378. void CJobSearchDlg::OnQuit()
  379. {
  380. EndDialog( 0 );
  381. }
  382. void CJobSearchDlg::OnSize(UINT nType, int cx, int cy)
  383. {
  384. CDialog::OnSize(nType, cx, cy);
  385. m_AnchorMgr.UpdateAnchors( this );
  386. }