[SACDRV]
[reactos.git] / reactos / drivers / sac / driver / concmd.c
1 /*
2 * PROJECT: ReactOS Drivers
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: drivers/sac/driver/concmd.c
5 * PURPOSE: Driver for the Server Administration Console (SAC) for EMS
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "sacdrv.h"
12
13 /* GLOBALS ********************************************************************/
14
15 PVOID GlobalBuffer;
16 ULONG GlobalBufferSize;
17
18 /* FUNCTIONS ******************************************************************/
19
20 NTSTATUS
21 DoChannelListCommand(
22 VOID
23 )
24 {
25 return STATUS_NOT_IMPLEMENTED;
26 }
27
28 NTSTATUS
29 DoChannelCloseByNameCommand(
30 IN PCHAR Count
31 )
32 {
33 return STATUS_NOT_IMPLEMENTED;
34 }
35
36 NTSTATUS
37 DoChannelCloseByIndexCommand(
38 IN ULONG ChannelIndex
39 )
40 {
41 return STATUS_NOT_IMPLEMENTED;
42 }
43
44 NTSTATUS
45 DoChannelSwitchByNameCommand(
46 IN PCHAR Count
47 )
48 {
49 return STATUS_NOT_IMPLEMENTED;
50 }
51
52 NTSTATUS
53 DoChannelSwitchByIndexCommand(
54 IN ULONG ChannelIndex
55 )
56 {
57 return STATUS_NOT_IMPLEMENTED;
58 }
59
60 typedef struct _SAC_SYSTEM_INFORMATION
61 {
62 SYSTEM_BASIC_INFORMATION BasicInfo;
63 SYSTEM_TIMEOFDAY_INFORMATION TimeInfo;
64 SYSTEM_FILECACHE_INFORMATION CacheInfo;
65 SYSTEM_PERFORMANCE_INFORMATION PerfInfo;
66 ULONG RemainingSize;
67 ULONG ProcessDataOffset;
68 // SYSTEM_PAGEFILE_INFORMATION PageFileInfo;
69 // SYSTEM_PROCESS_INFORMATINO ProcessInfo;
70 } SAC_SYSTEM_INFORMATION, *PSAC_SYSTEM_INFORMATION;
71
72 NTSTATUS
73 NTAPI
74 GetTListInfo(IN PSAC_SYSTEM_INFORMATION SacInfo,
75 IN ULONG InputSize,
76 OUT PULONG TotalSize)
77 {
78 NTSTATUS Status;
79 ULONG BufferLength, ReturnLength, RemainingSize;
80 PSYSTEM_PAGEFILE_INFORMATION PageFileInfo;
81 PSYSTEM_PROCESS_INFORMATION ProcessInfo;
82 ULONG_PTR P;
83 SAC_DBG(SAC_DBG_ENTRY_EXIT, "Entering.\n");
84
85 /* Assume failure */
86 *TotalSize = 0;
87
88 /* Bail out if the buffer is way too small */
89 if (InputSize < 4)
90 {
91 SAC_DBG(SAC_DBG_ENTRY_EXIT, "Exiting, no memory.\n");
92 return STATUS_NO_MEMORY;
93 }
94
95 /* Make sure it's at least big enough to hold the static structure */
96 BufferLength = InputSize - sizeof(SAC_SYSTEM_INFORMATION);
97 if (InputSize < sizeof(SAC_SYSTEM_INFORMATION))
98 {
99 SAC_DBG(SAC_DBG_ENTRY_EXIT, "Exiting, no memory (2).\n");
100 return STATUS_NO_MEMORY;
101 }
102
103 /* Query the time */
104 Status = ZwQuerySystemInformation(SystemTimeOfDayInformation,
105 &SacInfo->TimeInfo,
106 sizeof(SacInfo->TimeInfo),
107 NULL);
108 if (!NT_SUCCESS(Status))
109 {
110 SAC_DBG(SAC_DBG_ENTRY_EXIT, "Exiting, error.\n");
111 return Status;
112 }
113
114 /* Query basic information */
115 Status = ZwQuerySystemInformation(SystemBasicInformation,
116 &SacInfo->BasicInfo,
117 sizeof(SacInfo->BasicInfo),
118 NULL);
119 if (!NT_SUCCESS(Status))
120 {
121 SAC_DBG(SAC_DBG_ENTRY_EXIT, "Exiting, error (2).\n");
122 return Status;
123 }
124
125 /* Now query the pagefile information, which comes right after */
126 P = (ULONG_PTR)(SacInfo + 1);
127 PageFileInfo = (PSYSTEM_PAGEFILE_INFORMATION)P;
128 Status = ZwQuerySystemInformation(SystemPageFileInformation,
129 PageFileInfo,
130 BufferLength,
131 &ReturnLength);
132 if (!NT_SUCCESS(Status) || !(ReturnLength))
133 {
134 /* We failed -- is it because our buffer was too small? */
135 if (BufferLength < ReturnLength)
136 {
137 /* Bail out */
138 SAC_DBG(SAC_DBG_ENTRY_EXIT, "Exiting, no memory(5).\n");
139 return STATUS_NO_MEMORY;
140 }
141
142 /* Some other reason, assume the buffer is now full */
143 SacInfo->RemainingSize = 0;
144 }
145 else
146 {
147 /* This is the leftover data */
148 SacInfo->RemainingSize = InputSize - BufferLength;
149
150 /* This much has now been consumed, and where we are now */
151 BufferLength -= ReturnLength;
152 P += ReturnLength;
153
154 /* Are we out of memory? */
155 if ((LONG)BufferLength < 0)
156 {
157 /* Bail out */
158 SAC_DBG(SAC_DBG_ENTRY_EXIT, "Exiting, no memory(3).\n");
159 return STATUS_NO_MEMORY;
160 }
161
162 /* All good, loop the pagefile data now */
163 while (TRUE)
164 {
165 /* Is the pagefile name too big to fit? */
166 if (PageFileInfo->PageFileName.Length > (LONG)BufferLength)
167 {
168 /* Bail out */
169 SAC_DBG(SAC_DBG_ENTRY_EXIT, "Exiting, error(3).\n");
170 return STATUS_INFO_LENGTH_MISMATCH;
171 }
172
173 /* Copy the name into our own buffer */
174 RtlCopyMemory((PVOID)P,
175 PageFileInfo->PageFileName.Buffer,
176 PageFileInfo->PageFileName.Length);
177 PageFileInfo->PageFileName.Buffer = (PWCHAR)P;
178
179 /* Update buffer lengths and offset */
180 BufferLength -= PageFileInfo->PageFileName.Length;
181 P += PageFileInfo->PageFileName.Length;
182
183 /* Are we out of memory? */
184 if ((LONG)BufferLength < 0)
185 {
186 /* Bail out */
187 SAC_DBG(SAC_DBG_ENTRY_EXIT, "Exiting, no memory(4).\n");
188 return STATUS_NO_MEMORY;
189 }
190
191 /* If this was the only pagefile, break out */
192 if (!PageFileInfo->NextEntryOffset) break;
193
194 /* Otherwise, move to the next one */
195 PageFileInfo = (PVOID)((ULONG_PTR)PageFileInfo +
196 PageFileInfo->NextEntryOffset);
197 }
198 }
199
200 /* Next, query the file cache information */
201 Status = ZwQuerySystemInformation(SystemFileCacheInformation,
202 &SacInfo->CacheInfo,
203 sizeof(SacInfo->CacheInfo),
204 NULL);
205 if (!NT_SUCCESS(Status))
206 {
207 SAC_DBG(SAC_DBG_ENTRY_EXIT, "Exiting, error (4).\n");
208 return Status;
209 }
210
211 /* And then the performance information */
212 Status = ZwQuerySystemInformation(SystemPerformanceInformation,
213 &SacInfo->PerfInfo,
214 sizeof(SacInfo->PerfInfo),
215 NULL);
216 if (!NT_SUCCESS(Status))
217 {
218 SAC_DBG(SAC_DBG_ENTRY_EXIT, "Exiting, error(5).\n");
219 return Status;
220 }
221
222 /* Finally, align the buffer to query process and thread information */
223 P = ALIGN_UP(P, SYSTEM_PROCESS_INFORMATION);
224 RemainingSize = (ULONG_PTR)SacInfo + InputSize - P;
225
226 /* Are we out of memory? */
227 if ((LONG)RemainingSize < 0)
228 {
229 /* Bail out */
230 SAC_DBG(SAC_DBG_ENTRY_EXIT, "Exiting, no memory (6).\n");
231 return STATUS_NO_MEMORY;
232 }
233
234 /* Now query the processes and threads */
235 ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)P;
236 Status = ZwQuerySystemInformation(SystemProcessInformation,
237 ProcessInfo,
238 RemainingSize,
239 &ReturnLength);
240 if (!NT_SUCCESS(Status))
241 {
242 SAC_DBG(SAC_DBG_ENTRY_EXIT, "Exiting, error(6).\n");
243 return Status;
244 }
245
246 /* The first process name will be right after this buffer */
247 P += ReturnLength;
248
249 /* The caller should look for process info over here */
250 SacInfo->ProcessDataOffset = InputSize - RemainingSize;
251
252 /* This is how much buffer data we have left -- are we out? */
253 BufferLength = RemainingSize - ReturnLength;
254 if ((LONG)BufferLength < 0)
255 {
256 /* Bail out */
257 SAC_DBG(SAC_DBG_ENTRY_EXIT, "Exiting, no memory(7).\n");
258 return STATUS_NO_MEMORY;
259 }
260
261 /* All good and ready to parse the process and thread list */
262 while (TRUE)
263 {
264 /* Does the process have a name? */
265 if (ProcessInfo->ImageName.Buffer)
266 {
267 /* Is the process name too big to fit? */
268 if ((LONG)BufferLength < ProcessInfo->ImageName.Length)
269 {
270 /* Bail out */
271 SAC_DBG(SAC_DBG_ENTRY_EXIT, "Exiting, error(7).\n");
272 return STATUS_INFO_LENGTH_MISMATCH;
273 }
274
275 /* Copy the name into our own buffer */
276 RtlCopyMemory((PVOID)P,
277 ProcessInfo->ImageName.Buffer,
278 ProcessInfo->ImageName.Length);
279 ProcessInfo->ImageName.Buffer = (PWCHAR)P;
280
281 /* Update buffer lengths and offset */
282 BufferLength -= ProcessInfo->ImageName.Length;
283 P += ProcessInfo->ImageName.Length;
284
285 /* Are we out of memory? */
286 if ((LONG)BufferLength < 0)
287 {
288 /* Bail out */
289 SAC_DBG(SAC_DBG_ENTRY_EXIT, "Exiting, no memory(8).\n");
290 return STATUS_NO_MEMORY;
291 }
292 }
293
294 /* If this was the only process, break out */
295 if (!ProcessInfo->NextEntryOffset) break;
296
297 /* Otherwise, move to the next one */
298 ProcessInfo = (PVOID)((ULONG_PTR)ProcessInfo +
299 ProcessInfo->NextEntryOffset);
300 }
301
302 /* All done! */
303 SAC_DBG(SAC_DBG_ENTRY_EXIT, "Exiting.\n");
304 *TotalSize = InputSize - BufferLength;
305 return STATUS_SUCCESS;
306 }
307
308 VOID
309 NTAPI
310 PrintTListInfo(IN PSAC_SYSTEM_INFORMATION SacInfo)
311 {
312 SAC_DBG(1, "Testing: %d %d %I64d\n",
313 SacInfo->BasicInfo.NumberOfPhysicalPages,
314 SacInfo->PerfInfo.AvailablePages,
315 SacInfo->TimeInfo.BootTime);
316 }
317
318 VOID
319 NTAPI
320 PutMore(OUT PBOOLEAN ScreenFull)
321 {
322 *ScreenFull = FALSE;
323 }
324
325 BOOLEAN
326 RetrieveIpAddressFromString(
327 IN PWCHAR IpString,
328 OUT PULONG IpAddress
329 )
330 {
331 return FALSE;
332 }
333
334 NTSTATUS
335 CallQueryIPIOCTL(
336 IN HANDLE DriverHandle,
337 IN PVOID DriverObject,
338 IN HANDLE WaitEvent,
339 IN PIO_STATUS_BLOCK IoStatusBlock,
340 IN PVOID InputBuffer,
341 IN ULONG InputBufferLength,
342 IN PVOID OutputBuffer,
343 IN ULONG OutputBufferLength,
344 IN BOOLEAN PrintMessage,
345 OUT PBOOLEAN MessagePrinted
346 )
347 {
348 return STATUS_NOT_IMPLEMENTED;
349 }
350
351 VOID
352 NTAPI
353 DoRebootCommand(IN BOOLEAN Reboot)
354 {
355 LARGE_INTEGER Timeout, TickCount;
356 NTSTATUS Status;
357 KEVENT Event;
358 SAC_DBG(1, "SAC DoRebootCommand: Entering.\n");
359
360 /* Get the current time now, and setup a timeout in 1 second */
361 KeQueryTickCount(&TickCount);
362 Timeout.QuadPart = TickCount.QuadPart / (10000000 / KeQueryTimeIncrement());
363
364 /* Check if the timeout is small enough */
365 if (Timeout.QuadPart < 60 )
366 {
367 /* Show the prompt */
368 ConMgrSimpleEventMessage(Reboot ?
369 SAC_RESTART_PROMPT : SAC_SHUTDOWN_PROMPT,
370 TRUE);
371
372 /* Do the wait */
373 KeInitializeEvent(&Event, SynchronizationEvent, 0);
374 Timeout.QuadPart = -10000000 * (60 - Timeout.LowPart);
375 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &Timeout);
376 }
377
378 /* Do a shutdown or a reboot, based on the request */
379 Status = NtShutdownSystem(Reboot ? ShutdownReboot : ShutdownPowerOff);
380
381 /* Check if anyone in the command channel already allocated this */
382 if (!GlobalBuffer)
383 {
384 /* Allocate it */
385 GlobalBuffer = SacAllocatePool(PAGE_SIZE, GLOBAL_BLOCK_TAG);
386 if (!GlobalBuffer)
387 {
388 /* We need the global buffer, bail out without it*/
389 SacPutSimpleMessage(SAC_OUT_OF_MEMORY_PROMPT);
390 SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC DoRebootCommand: Exiting (1).\n");
391 return;
392 }
393
394 /* Set the size of the buffer */
395 GlobalBufferSize = PAGE_SIZE;
396 }
397
398 /* We came back from a reboot, this doesn't make sense, tell the user */
399 SacPutSimpleMessage(Reboot ? SAC_RESTART_FAIL_PROMPT : SAC_SHUTDOWN_FAIL_PROMPT);
400 swprintf(GlobalBuffer, GetMessage(SAC_FAIL_PROMPT), Status);
401 SacPutString(GlobalBuffer);
402 SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC DoRebootCommand: Exiting.\n");
403 }
404
405 VOID
406 NTAPI
407 DoFullInfoCommand(VOID)
408 {
409 /* Flip the flag */
410 GlobalDoThreads = !GlobalDoThreads;
411
412 /* Print out the new state */
413 SacPutSimpleMessage(GlobalDoThreads ? 8 : 7);
414 }
415
416 VOID
417 NTAPI
418 DoPagingCommand(VOID)
419 {
420 /* Flip the flag */
421 GlobalPagingNeeded = !GlobalPagingNeeded;
422
423 /* Print out the new state */
424 SacPutSimpleMessage(GlobalPagingNeeded ? 10 : 9);
425 }
426
427 VOID
428 NTAPI
429 DoSetTimeCommand(IN PCHAR InputTime)
430 {
431 SAC_DBG(1, "Entering\n");
432 }
433
434 VOID
435 NTAPI
436 DoKillCommand(IN PCHAR KillString)
437 {
438 SAC_DBG(1, "Entering\n");
439 }
440
441 VOID
442 NTAPI
443 DoLowerPriorityCommand(IN PCHAR PrioString)
444 {
445 SAC_DBG(1, "Entering\n");
446 }
447
448 VOID
449 NTAPI
450 DoRaisePriorityCommand(IN PCHAR PrioString)
451 {
452 SAC_DBG(1, "Entering\n");
453 }
454
455 VOID
456 NTAPI
457 DoLimitMemoryCommand(IN PCHAR LimitString)
458 {
459 SAC_DBG(1, "Entering\n");
460 }
461
462 VOID
463 NTAPI
464 DoCrashCommand(VOID)
465 {
466 SAC_DBG(1, "SAC DoCrashCommand: Entering.\n");
467
468 /* Crash the machine */
469 KeBugCheckEx(MANUALLY_INITIATED_CRASH, 0, 0, 0, 0);
470 __debugbreak();
471 }
472
473 VOID
474 NTAPI
475 DoMachineInformationCommand(VOID)
476 {
477 SAC_DBG(1, "Entering\n");
478 }
479
480 VOID
481 NTAPI
482 DoChannelCommand(IN PCHAR ChannelString)
483 {
484 SAC_DBG(1, "Entering\n");
485 }
486
487 VOID
488 NTAPI
489 DoCmdCommand(IN PCHAR InputString)
490 {
491 SAC_DBG(1, "Entering\n");
492 }
493
494 VOID
495 NTAPI
496 DoLockCommand(VOID)
497 {
498 SAC_DBG(1, "Entering\n");
499 }
500
501 FORCEINLINE
502 BOOLEAN
503 PrintHelpMessage(IN ULONG MessageId,
504 IN OUT PULONG Count)
505 {
506 BOOLEAN ScreenFull;
507 ULONG NewCount;
508
509 /* Get the amount of lines this message will take */
510 NewCount = GetMessageLineCount(MessageId);
511 if ((NewCount + *Count) > SAC_VTUTF8_ROW_HEIGHT)
512 {
513 /* We are going to overflow the screen, wait for input */
514 PutMore(&ScreenFull);
515 if (ScreenFull) return FALSE;
516 *Count = 0;
517 }
518
519 /* Print out the message and update the amount of lines printed */
520 SacPutSimpleMessage(MessageId);
521 *Count += NewCount;
522 return TRUE;
523 }
524
525 VOID
526 NTAPI
527 DoHelpCommand(VOID)
528 {
529 ULONG Count = 0;
530
531 /* Print out all the help messages */
532 if (!PrintHelpMessage(112, &Count)) return;
533 if (!PrintHelpMessage(12, &Count)) return;
534 if (!PrintHelpMessage(13, &Count)) return;
535 if (!PrintHelpMessage(14, &Count)) return;
536 if (!PrintHelpMessage(15, &Count)) return;
537 if (!PrintHelpMessage(16, &Count)) return;
538 if (!PrintHelpMessage(31, &Count)) return;
539 if (!PrintHelpMessage(18, &Count)) return;
540 if (!PrintHelpMessage(19, &Count)) return;
541 if (!PrintHelpMessage(32, &Count)) return;
542 if (!PrintHelpMessage(20, &Count)) return;
543 if (!PrintHelpMessage(21, &Count)) return;
544 if (!PrintHelpMessage(22, &Count)) return;
545 if (!PrintHelpMessage(23, &Count)) return;
546 if (!PrintHelpMessage(24, &Count)) return;
547 if (!PrintHelpMessage(25, &Count)) return;
548 if (!PrintHelpMessage(27, &Count)) return;
549 if (!PrintHelpMessage(28, &Count)) return;
550 if (!PrintHelpMessage(29, &Count)) return;
551 }
552
553 VOID
554 NTAPI
555 DoGetNetInfo(IN BOOLEAN DoPrint)
556 {
557 SAC_DBG(1, "Entering\n");
558 }
559
560 VOID
561 NTAPI
562 DoSetIpAddressCommand(IN PCHAR IpString)
563 {
564 SAC_DBG(1, "Entering\n");
565 }
566
567 VOID
568 NTAPI
569 DoTlistCommand(VOID)
570 {
571 NTSTATUS Status;
572 PVOID NewGlobalBuffer;
573 ULONG Size;
574 SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC DoTlistCommand: Entering.\n");
575
576 /* Check if a global buffer already exists */
577 if (!GlobalBuffer)
578 {
579 /* It doesn't, allocate one */
580 GlobalBuffer = SacAllocatePool(4096, GLOBAL_BLOCK_TAG);
581 if (GlobalBuffer)
582 {
583 /* Remember its current size */
584 GlobalBufferSize = 4096;
585 }
586 else
587 {
588 /* Out of memory, bail out */
589 SacPutSimpleMessage(11);
590 SAC_DBG(1, "SAC DoTlistCommand: Exiting.\n");
591 return;
592 }
593 }
594
595 /* Loop as long as the buffer is too small */
596 while (TRUE)
597 {
598 /* Get the process list */
599 Status = GetTListInfo(GlobalBuffer, GlobalBufferSize, &Size);
600 if ((Status != STATUS_NO_MEMORY) &&
601 (Status != STATUS_INFO_LENGTH_MISMATCH))
602 {
603 /* It fits! Bail out */
604 break;
605 }
606
607 /* We need a new bigger buffer */
608 NewGlobalBuffer = SacAllocatePool(GlobalBufferSize + 4096,
609 GLOBAL_BLOCK_TAG);
610 if (!NewGlobalBuffer)
611 {
612 /* Out of memory, bail out */
613 SacPutSimpleMessage(11);
614 SAC_DBG(1, "SAC DoTlistCommand: Exiting.\n");
615 return;
616 }
617
618 /* Free the old one, update state */
619 SacFreePool(GlobalBuffer);
620 GlobalBufferSize += 4096;
621 GlobalBuffer = NewGlobalBuffer;
622 }
623
624 /* Did we get here because we have the whole list? */
625 if (!NT_SUCCESS(Status))
626 {
627 /* Nope, print out a failure message */
628 SacPutSimpleMessage(68);
629 swprintf(GlobalBuffer, GetMessage(48), Status);
630 SacPutString(GlobalBuffer);
631 }
632 else
633 {
634 /* Yep, print out the list */
635 PrintTListInfo(GlobalBuffer);
636 }
637
638 SAC_DBG(1, "SAC DoTlistCommand: Exiting.\n");
639 }