CTM in rosapps now compiles again.
[reactos.git] / rosapps / sysutils / ctm / ctm.c
1 /* Console Task Manager
2
3 ctm.c - main program file
4
5 Written by: Aleksey Bragin (aleksey@studiocerebral.com)
6
7 Most of the code dealing with getting system parameters is taken from
8 ReactOS Task Manager written by Brian Palmer (brianp@reactos.org)
9
10 History:
11 09 April 2003 - v0.1, fixed bugs, added features, ported to mingw
12 20 March 2003 - v0.03, works good under ReactOS, and allows process
13 killing
14 18 March 2003 - Initial version 0.01, doesn't work under RectOS
15
16 This program is free software; you can redistribute it and/or modify
17 it under the terms of the GNU General Public License as published by
18 the Free Software Foundation; either version 2 of the License, or
19 (at your option) any later version.
20
21 This program is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
25
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
29
30
31 //#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows //headers
32 #include <windows.h>
33 #include <stdlib.h>
34 #include <malloc.h>
35 #include <memory.h>
36 #include <tchar.h>
37 #include <process.h>
38 #include <stdio.h>
39
40 #include <ddk/ntddk.h>
41 #include <epsapi.h>
42 #include <ntos/zwtypes.h>
43
44 #include "ctm.h"
45
46 #define MAX_PROC 17
47 //#define TIMES
48
49 HANDLE hStdin;
50 HANDLE hStdout;
51
52 DWORD inConMode;
53 DWORD outConMode;
54
55 //PROCNTQSI NtQuerySystemInformation= NULL;
56
57 const int ProcPerScreen = 17; // 17 processess are displayed on one page
58 ULONG ProcessCountOld = 0;
59 ULONG ProcessCount = 0;
60
61 double dbIdleTime;
62 double dbKernelTime;
63 double dbSystemTime;
64 LARGE_INTEGER liOldIdleTime = {{0,0}};
65 double OldKernelTime = 0;
66 LARGE_INTEGER liOldSystemTime = {{0,0}};
67
68 PPERFDATA pPerfDataOld = NULL; // Older perf data (saved to establish delta values)
69 PPERFDATA pPerfData = NULL; // Most recent copy of perf data
70
71 int selection=0;
72 int scrolled=0; // offset from which process start showing
73
74 #define NEW_CONSOLE
75
76 void *PsaiMalloc(SIZE_T size) { return malloc(size); }
77 void *PsaiRealloc(void *ptr, SIZE_T size) { return realloc(ptr, size); }
78 void PsaiFree(void *ptr) { free(ptr); }
79
80 // Prototypes
81 unsigned int GetKeyPressed();
82
83 void GetInputOutputHandles()
84 {
85 #ifdef NEW_CONSOLE
86 HANDLE console = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
87 FILE_SHARE_READ | FILE_SHARE_WRITE,
88 0, CONSOLE_TEXTMODE_BUFFER, 0);
89
90 if (SetConsoleActiveScreenBuffer(console) == FALSE)
91 {
92 hStdin = GetStdHandle(STD_INPUT_HANDLE);
93 hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
94 }
95 else
96 {
97 hStdin = GetStdHandle(STD_INPUT_HANDLE);//console;
98 hStdout = console;
99 }
100 #else
101 hStdin = GetStdHandle(STD_INPUT_HANDLE);
102 hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
103 #endif
104 }
105
106 void RestoreConsole()
107 {
108 SetConsoleMode(hStdin, inConMode);
109 SetConsoleMode(hStdout, outConMode);
110
111 #ifdef NEW_CONSOLE
112 SetConsoleActiveScreenBuffer(GetStdHandle(STD_OUTPUT_HANDLE));
113 #endif
114 }
115
116 void DisplayScreen()
117 {
118 COORD pos;
119 char lpStr[80];
120 int idx;
121 DWORD numChars;
122 int lines;
123
124 // Header
125 pos.X = 2; pos.Y = 2;
126 strcpy(lpStr, "Console TaskManager v0.1 by Aleksey Bragin <aleksey@studiocerebral.com>");
127 WriteConsoleOutputCharacter(hStdout, lpStr, strlen(lpStr), pos, &numChars);
128
129 pos.X = 2; pos.Y = 3;
130 strcpy(lpStr, "+-------------------------------+-------+-----+-----------+-------------+");
131 WriteConsoleOutputCharacter(hStdout, lpStr, strlen(lpStr), pos, &numChars);
132
133 pos.X = 2; pos.Y = 4;
134 strcpy(lpStr, "| Image name | PID | CPU | Mem Usage | Page Faults |");
135 WriteConsoleOutputCharacter(hStdout, lpStr, strlen(lpStr), pos, &numChars);
136
137 pos.X = 2; pos.Y = 5;
138 strcpy(lpStr, "+-------------------------------+-------+-----+-----------+-------------+");
139 WriteConsoleOutputCharacter(hStdout, lpStr, strlen(lpStr), pos, &numChars);
140
141 // Footer
142 pos.X = 2; pos.Y = 23;
143 strcpy(lpStr, "+-------------------------------+-------+-----+-----------+-------------+");
144 WriteConsoleOutputCharacter(hStdout, lpStr, strlen(lpStr), pos, &numChars);
145
146 // Menu
147 pos.X = 2; pos.Y = 24;
148 strcpy(lpStr, "Press: q - quit, k - kill process ");
149 WriteConsoleOutputCharacter(hStdout, lpStr, strlen(lpStr), pos, &numChars);
150
151 // Processess
152 lines = ProcessCount;
153 if (lines > MAX_PROC)
154 lines = MAX_PROC;
155 for (idx=0; idx<lines; idx++)
156 {
157 int len;
158 char imgName[MAX_PATH];
159 char lpPid[8];
160 char lpCpu[6];
161 char lpMemUsg[12];
162 char lpPageFaults[15];
163 WORD wColor;
164
165 // data
166 // image name
167 pos.X = 3; pos.Y = 6+idx;
168 memset(imgName, 0, MAX_PATH);
169 WideCharToMultiByte(CP_ACP, 0, pPerfData[scrolled+idx].ImageName, -1,
170 imgName, MAX_PATH, NULL, NULL);
171 len = strlen(imgName);
172 WriteConsoleOutputCharacter(hStdout, " ", 30, pos, &numChars);
173 WriteConsoleOutputCharacter(hStdout, imgName, (len > 30) ? 30 : len, pos, &numChars);
174
175 // PID
176 pos.X = 35; pos.Y = 6+idx;
177 sprintf(lpPid, "%6ld", pPerfData[scrolled+idx].ProcessId);
178 WriteConsoleOutputCharacter(hStdout, lpPid, strlen(lpPid), pos, &numChars);
179
180 // CPU
181 pos.X = 43; pos.Y = 6+idx;
182 sprintf(lpCpu, "%3d%%", pPerfData[scrolled+idx].CPUUsage);
183 WriteConsoleOutputCharacter(hStdout, lpCpu, strlen(lpCpu), pos, &numChars);
184
185 // Mem usage
186 pos.X = 49; pos.Y = 6+idx;
187 sprintf(lpMemUsg, "%6ld", pPerfData[scrolled+idx].WorkingSetSizeBytes / 1024);
188 WriteConsoleOutputCharacter(hStdout, lpMemUsg, strlen(lpMemUsg), pos, &numChars);
189
190 // Page Fault
191 pos.X = 61; pos.Y = 6+idx;
192 sprintf(lpPageFaults, "%12ld", pPerfData[scrolled+idx].PageFaultCount);
193 WriteConsoleOutputCharacter(hStdout, lpPageFaults, strlen(lpPageFaults), pos, &numChars);
194
195 // columns
196 pos.X = 2; pos.Y = 6+idx;
197 WriteConsoleOutputCharacter(hStdout, "|", 1, pos, &numChars);
198 pos.X = 34; pos.Y = 6+idx;
199 WriteConsoleOutputCharacter(hStdout, "|", 1, pos, &numChars);
200 pos.X = 42; pos.Y = 6+idx;
201 WriteConsoleOutputCharacter(hStdout, "|", 1, pos, &numChars);
202 pos.X = 48; pos.Y = 6+idx;
203 WriteConsoleOutputCharacter(hStdout, "|", 1, pos, &numChars);
204 pos.X = 60; pos.Y = 6+idx;
205 WriteConsoleOutputCharacter(hStdout, "|", 1, pos, &numChars);
206 pos.X = 74; pos.Y = 6+idx;
207 WriteConsoleOutputCharacter(hStdout, "|", 1, pos, &numChars);
208
209
210 // Attributes now...
211 pos.X = 3; pos.Y = 6+idx;
212 if (selection == idx)
213 {
214 wColor = BACKGROUND_GREEN |
215 FOREGROUND_RED |
216 FOREGROUND_GREEN |
217 FOREGROUND_BLUE;
218 }
219 else
220 {
221 wColor = BACKGROUND_BLUE |
222 FOREGROUND_RED |
223 FOREGROUND_GREEN |
224 FOREGROUND_BLUE;
225 }
226
227 FillConsoleOutputAttribute(
228 hStdout, // screen buffer handle
229 wColor, // color to fill with
230 31, // number of cells to fill
231 pos, // first cell to write to
232 &numChars); // actual number written
233 }
234
235 return;
236 }
237
238 // returns TRUE if exiting
239 int ProcessKeys(int numEvents)
240 {
241 if ((ProcessCount-scrolled < 17) && (ProcessCount > 17))
242 scrolled = ProcessCount-17;
243
244 unsigned char key = GetKeyPressed(numEvents);
245 if (key == VK_Q)
246 return TRUE;
247 else if (key == VK_K)
248 {
249 // user wants to kill some process, get his acknowledgement
250 DWORD pId;
251 COORD pos;
252 char lpStr[100];
253
254 pos.X = 2; pos.Y = 24;
255 strcpy(lpStr, "Are you sure you want to kill this process? (y/n)");
256 WriteConsoleOutputCharacter(hStdout, lpStr, strlen(lpStr), pos, &pId);
257
258 do {
259 GetNumberOfConsoleInputEvents(hStdin, &pId);
260 key = GetKeyPressed(pId);
261 } while (key == 0);
262
263 if (key == VK_Y)
264 {
265 HANDLE hProcess;
266 pId = pPerfData[selection+scrolled].ProcessId;
267 hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pId);
268
269 if (hProcess)
270 {
271 if (!TerminateProcess(hProcess, 0))
272 {
273 strcpy(lpStr, "Unable to terminate this process... ");
274 WriteConsoleOutputCharacter(hStdout, lpStr, strlen(lpStr), pos, &pId);
275 Sleep(1000);
276 }
277
278 CloseHandle(hProcess);
279 }
280 else
281 {
282 sprintf(lpStr, "Unable to terminate process %3d (unable to OpenProcess) ", pId);
283 WriteConsoleOutputCharacter(hStdout, lpStr, strlen(lpStr), pos, &pId);
284 Sleep(1000);
285 }
286 }
287 }
288 else if (key == VK_UP)
289 {
290 if (selection > 0)
291 selection--;
292 else if ((selection == 0) && (scrolled > 0))
293 scrolled--;
294 }
295 else if (key == VK_DOWN)
296 {
297 if ((selection < MAX_PROC-1) && (selection < ProcessCount-1))
298 selection++;
299 else if ((selection == MAX_PROC-1) && (selection+scrolled < ProcessCount-1))
300 scrolled++;
301 }
302
303 return FALSE;
304 }
305
306 void PerfInit()
307 {
308 // NtQuerySystemInformation = //(PROCNTQSI)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), //"NtQuerySystemInformation");
309 }
310
311 void PerfDataRefresh()
312 {
313 LONG status;
314 ULONG ulSize;
315 LPBYTE pBuffer;
316 ULONG BufferSize;
317 ULONG Idx, Idx2;
318 HANDLE hProcess;
319 HANDLE hProcessToken;
320 PSYSTEM_PROCESSES pSPI;
321 PPERFDATA pPDOld;
322 TCHAR szTemp[MAX_PATH];
323 DWORD dwSize;
324 double CurrentKernelTime;
325 PSYSTEM_PROCESSORTIME_INFO SysProcessorTimeInfo;
326 SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo;
327 SYSTEM_TIMEOFDAY_INFORMATION SysTimeInfo;
328
329 #ifdef TIMES
330 // Get new system time
331 status = NtQuerySystemInformation(SystemTimeInformation, &SysTimeInfo, sizeof(SysTimeInfo), 0);
332 if (status != NO_ERROR)
333 return;
334
335 // Get new CPU's idle time
336 status = NtQuerySystemInformation(SystemPerformanceInformation, &SysPerfInfo, sizeof(SysPerfInfo), NULL);
337 if (status != NO_ERROR)
338 return;
339 #endif
340 // Get processor information
341 SysProcessorTimeInfo = (PSYSTEM_PROCESSORTIME_INFO)malloc(sizeof(SYSTEM_PROCESSORTIME_INFO) * 1/*SystemBasicInfo.bKeNumberProcessors*/);
342 status = NtQuerySystemInformation(SystemProcessorTimes, SysProcessorTimeInfo, sizeof(SYSTEM_PROCESSORTIME_INFO) * 1/*SystemBasicInfo.bKeNumberProcessors*/, &ulSize);
343
344
345 // Get process information
346 PsaCaptureProcessesAndThreads((PSYSTEM_PROCESSES *)&pBuffer);
347
348 #ifdef TIMES
349 for (CurrentKernelTime=0, Idx=0; Idx<1/*SystemBasicInfo.bKeNumberProcessors*/; Idx++) {
350 CurrentKernelTime += Li2Double(SysProcessorTimeInfo[Idx].KernelTime);
351 CurrentKernelTime += Li2Double(SysProcessorTimeInfo[Idx].DpcTime);
352 CurrentKernelTime += Li2Double(SysProcessorTimeInfo[Idx].InterruptTime);
353 }
354
355 // If it's a first call - skip idle time calcs
356 if (liOldIdleTime.QuadPart != 0) {
357 // CurrentValue = NewValue - OldValue
358 dbIdleTime = Li2Double(SysPerfInfo.liIdleTime) - Li2Double(liOldIdleTime);
359 dbKernelTime = CurrentKernelTime - OldKernelTime;
360 dbSystemTime = Li2Double(SysTimeInfo.CurrentTime) - Li2Double(liOldSystemTime);
361
362 // CurrentCpuIdle = IdleTime / SystemTime
363 dbIdleTime = dbIdleTime / dbSystemTime;
364 dbKernelTime = dbKernelTime / dbSystemTime;
365
366 // CurrentCpuUsage% = 100 - (CurrentCpuIdle * 100) / NumberOfProcessors
367 dbIdleTime = 100.0 - dbIdleTime * 100.0; /* / (double)SystemBasicInfo.bKeNumberProcessors;// + 0.5; */
368 dbKernelTime = 100.0 - dbKernelTime * 100.0; /* / (double)SystemBasicInfo.bKeNumberProcessors;// + 0.5; */
369 }
370
371 // Store new CPU's idle and system time
372 liOldIdleTime = SysPerfInfo.liIdleTime;
373 liOldSystemTime = SysTimeInfo.CurrentTime;
374 OldKernelTime = CurrentKernelTime;
375 #endif
376
377 // Determine the process count
378 // We loop through the data we got from PsaCaptureProcessesAndThreads
379 // and count how many structures there are (until PsaWalkNextProcess
380 // returns NULL)
381 ProcessCountOld = ProcessCount;
382 ProcessCount = 0;
383 pSPI = PsaWalkFirstProcess((PSYSTEM_PROCESSES)pBuffer);
384 while (pSPI) {
385 ProcessCount++;
386 pSPI = PsaWalkNextProcess(pSPI);
387 }
388
389 // Now alloc a new PERFDATA array and fill in the data
390 if (pPerfDataOld) {
391 //delete[] pPerfDataOld;
392 free(pPerfDataOld);
393 }
394 pPerfDataOld = pPerfData;
395 //pPerfData = new PERFDATA[ProcessCount];
396 pPerfData = (PPERFDATA)malloc(sizeof(PERFDATA) * ProcessCount);
397 pSPI = PsaWalkFirstProcess((PSYSTEM_PROCESSES)pBuffer);
398 for (Idx=0; Idx<ProcessCount; Idx++) {
399 // Get the old perf data for this process (if any)
400 // so that we can establish delta values
401 pPDOld = NULL;
402 for (Idx2=0; Idx2<ProcessCountOld; Idx2++) {
403 if (pPerfDataOld[Idx2].ProcessId == pSPI->ProcessId) {
404 pPDOld = &pPerfDataOld[Idx2];
405 break;
406 }
407 }
408
409 // Clear out process perf data structure
410 memset(&pPerfData[Idx], 0, sizeof(PERFDATA));
411
412 if (pSPI->ProcessName.Buffer)
413 wcsncpy(pPerfData[Idx].ImageName, pSPI->ProcessName.Buffer, pSPI->ProcessName.MaximumLength);
414 else
415 wcscpy(pPerfData[Idx].ImageName, L"System Idle Process");
416
417 pPerfData[Idx].ProcessId = pSPI->ProcessId;
418
419 if (pPDOld) {
420 #ifdef TIMES
421 double CurTime = Li2Double(pSPI->KernelTime) + Li2Double(pSPI->UserTime);
422 double OldTime = Li2Double(pPDOld->KernelTime) + Li2Double(pPDOld->UserTime);
423 double CpuTime = (CurTime - OldTime) / dbSystemTime;
424 CpuTime = CpuTime * 100.0; /* / (double)SystemBasicInfo.bKeNumberProcessors;// + 0.5;*/
425
426 pPerfData[Idx].CPUUsage = (ULONG)CpuTime;
427 #else
428 pPerfData[Idx].CPUUsage = 0;
429 #endif
430 }
431
432 pPerfData[Idx].CPUTime.QuadPart = pSPI->UserTime.QuadPart + pSPI->KernelTime.QuadPart;
433 pPerfData[Idx].WorkingSetSizeBytes = pSPI->VmCounters.WorkingSetSize;
434 pPerfData[Idx].PeakWorkingSetSizeBytes = pSPI->VmCounters.PeakWorkingSetSize;
435 if (pPDOld)
436 pPerfData[Idx].WorkingSetSizeDelta = labs((LONG)pSPI->VmCounters.WorkingSetSize - (LONG)pPDOld->WorkingSetSizeBytes);
437 else
438 pPerfData[Idx].WorkingSetSizeDelta = 0;
439 pPerfData[Idx].PageFaultCount = pSPI->VmCounters.PageFaultCount;
440 if (pPDOld)
441 pPerfData[Idx].PageFaultCountDelta = labs((LONG)pSPI->VmCounters.PageFaultCount - (LONG)pPDOld->PageFaultCount);
442 else
443 pPerfData[Idx].PageFaultCountDelta = 0;
444 pPerfData[Idx].VirtualMemorySizeBytes = pSPI->VmCounters.VirtualSize;
445 pPerfData[Idx].PagedPoolUsagePages = pSPI->VmCounters.QuotaPagedPoolUsage;
446 pPerfData[Idx].NonPagedPoolUsagePages = pSPI->VmCounters.QuotaNonPagedPoolUsage;
447 pPerfData[Idx].BasePriority = pSPI->BasePriority;
448 pPerfData[Idx].HandleCount = pSPI->HandleCount;
449 pPerfData[Idx].ThreadCount = pSPI->ThreadCount;
450 //pPerfData[Idx].SessionId = pSPI->SessionId;
451
452 #ifdef EXTRA_INFO
453 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pSPI->ProcessId);
454 if (hProcess) {
455 if (OpenProcessToken(hProcess, TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_IMPERSONATE, &hProcessToken)) {
456 ImpersonateLoggedOnUser(hProcessToken);
457 memset(szTemp, 0, sizeof(TCHAR[MAX_PATH]));
458 dwSize = MAX_PATH;
459 GetUserName(szTemp, &dwSize);
460 #ifndef UNICODE
461 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szTemp, -1, pPerfData[Idx].UserName, MAX_PATH);
462 /*
463 int MultiByteToWideChar(
464 UINT CodePage, // code page
465 DWORD dwFlags, // character-type options
466 LPCSTR lpMultiByteStr, // string to map
467 int cbMultiByte, // number of bytes in string
468 LPWSTR lpWideCharStr, // wide-character buffer
469 int cchWideChar // size of buffer
470 );
471 */
472 #endif
473 RevertToSelf();
474 CloseHandle(hProcessToken);
475 }
476 CloseHandle(hProcess);
477 }
478 #endif
479 #ifdef TIMES
480 pPerfData[Idx].UserTime.QuadPart = pSPI->UserTime.QuadPart;
481 pPerfData[Idx].KernelTime.QuadPart = pSPI->KernelTime.QuadPart;
482 #endif
483 pSPI = PsaWalkNextProcess(pSPI);
484 }
485 //delete[] pBuffer;
486 PsaFreeCapture(pBuffer);
487
488 free(SysProcessorTimeInfo);
489 }
490
491 // Code partly taken from slw32tty.c from mc/slang
492 unsigned int GetKeyPressed(int events)
493 {
494 long key;
495 DWORD bytesRead;
496 INPUT_RECORD record;
497 int i;
498
499
500 for (i=0; i<events; i++)
501 {
502 if (!ReadConsoleInput(hStdin, &record, 1, &bytesRead)) {
503 return 0;
504 }
505
506 if (record.EventType == KEY_EVENT && record.Event.KeyEvent.bKeyDown)
507 return record.Event.KeyEvent.wVirtualKeyCode;//.uChar.AsciiChar;
508 }
509
510 return 0;
511 }
512
513
514 int main(int *argc, char **argv)
515 {
516 GetInputOutputHandles();
517
518 if (hStdin == INVALID_HANDLE_VALUE || hStdout == INVALID_HANDLE_VALUE)
519 {
520 printf("ctm: can't use console.");
521 return -1;
522 }
523
524 if (GetConsoleMode(hStdin, &inConMode) == 0)
525 {
526 printf("ctm: can't GetConsoleMode() for input console.");
527 return -1;
528 }
529
530 if (GetConsoleMode(hStdout, &outConMode) == 0)
531 {
532 printf("ctm: can't GetConsoleMode() for output console.");
533 return -1;
534 }
535
536 SetConsoleMode(hStdin, 0); //FIXME: Should check for error!
537 SetConsoleMode(hStdout, 0); //FIXME: Should check for error!
538
539 PerfInit();
540
541 while (1)
542 {
543 DWORD numEvents;
544
545 PerfDataRefresh();
546 DisplayScreen();
547
548 //WriteConsole(hStdin, " ", 1, &numEvents, NULL); // TODO: Make another way (this is ugly, I know)
549 GetNumberOfConsoleInputEvents(hStdin, &numEvents);
550
551 if (numEvents > 0)
552 {
553 if (ProcessKeys(numEvents) == TRUE)
554 break;
555 }
556 else
557 Sleep(40); // TODO: Should be done more efficient (might be another thread handling input/etc)*/
558 }
559
560 RestoreConsole();
561 return 0;
562 }