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