25d4c71f16618461316fb42410170f5096ebf588
[reactos.git] / win32ss / user / winsrv / consrv / console.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/winsrv/consrv/console.c
5 * PURPOSE: Console Management Functions
6 * PROGRAMMERS: Gé van Geldorp
7 * Jeffrey Morlan
8 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
9 */
10
11 /* INCLUDES *******************************************************************/
12
13 #include "consrv.h"
14
15 #include <ndk/psfuncs.h>
16
17 #include <alias.h>
18 #include <history.h>
19 #include "procinit.h"
20
21 #define NDEBUG
22 #include <debug.h>
23
24 // FIXME: Add this prototype to winternl.h / rtlfuncs.h / ...
25 NTSTATUS NTAPI RtlGetLastNtStatus(VOID);
26
27 /* GLOBALS ********************************************************************/
28
29 static ULONG ConsoleListSize;
30 static PCONSRV_CONSOLE* ConsoleList; /* The list of the ConSrv consoles */
31 static RTL_RESOURCE ListLock;
32
33 #define ConSrvLockConsoleListExclusive() \
34 RtlAcquireResourceExclusive(&ListLock, TRUE)
35
36 #define ConSrvLockConsoleListShared() \
37 RtlAcquireResourceShared(&ListLock, TRUE)
38
39 #define ConSrvUnlockConsoleList() \
40 RtlReleaseResource(&ListLock)
41
42
43 static NTSTATUS
44 InsertConsole(OUT PHANDLE Handle,
45 IN PCONSRV_CONSOLE Console)
46 {
47 #define CONSOLE_HANDLES_INCREMENT 2 * 3
48
49 NTSTATUS Status = STATUS_SUCCESS;
50 ULONG i = 0;
51 PCONSRV_CONSOLE* Block;
52
53 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
54 (ConsoleList != NULL && ConsoleListSize != 0) );
55
56 /* All went right, so add the console to the list */
57 ConSrvLockConsoleListExclusive();
58 DPRINT1("Insert in the list\n");
59
60 if (ConsoleList)
61 {
62 for (i = 0; i < ConsoleListSize; i++)
63 {
64 if (ConsoleList[i] == NULL) break;
65 }
66 }
67
68 if (i >= ConsoleListSize)
69 {
70 DPRINT1("Creation of a new handles table\n");
71 /* Allocate a new handles table */
72 Block = ConsoleAllocHeap(HEAP_ZERO_MEMORY,
73 (ConsoleListSize +
74 CONSOLE_HANDLES_INCREMENT) * sizeof(PCONSRV_CONSOLE));
75 if (Block == NULL)
76 {
77 Status = STATUS_UNSUCCESSFUL;
78 goto Quit;
79 }
80
81 /* If we previously had a handles table, free it and use the new one */
82 if (ConsoleList)
83 {
84 /* Copy the handles from the old table to the new one */
85 RtlCopyMemory(Block,
86 ConsoleList,
87 ConsoleListSize * sizeof(PCONSRV_CONSOLE));
88 ConsoleFreeHeap(ConsoleList);
89 }
90 ConsoleList = Block;
91 ConsoleListSize += CONSOLE_HANDLES_INCREMENT;
92 }
93
94 ConsoleList[i] = Console;
95 *Handle = ULongToHandle((i << 2) | 0x3);
96
97 Quit:
98 /* Unlock the console list and return status */
99 ConSrvUnlockConsoleList();
100 return Status;
101 }
102
103 /* Unused */
104 #if 0
105 static NTSTATUS
106 RemoveConsoleByHandle(IN HANDLE Handle)
107 {
108 NTSTATUS Status = STATUS_SUCCESS;
109 PCONSRV_CONSOLE Console;
110
111 BOOLEAN ValidHandle = ((HandleToULong(Handle) & 0x3) == 0x3);
112 ULONG Index = HandleToULong(Handle) >> 2;
113
114 if (!ValidHandle) return STATUS_INVALID_HANDLE;
115
116 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
117 (ConsoleList != NULL && ConsoleListSize != 0) );
118
119 /* Remove the console from the list */
120 ConSrvLockConsoleListExclusive();
121
122 if (Index >= ConsoleListSize ||
123 (Console = ConsoleList[Index]) == NULL)
124 {
125 Status = STATUS_INVALID_HANDLE;
126 goto Quit;
127 }
128
129 ConsoleList[Index] = NULL;
130
131 Quit:
132 /* Unlock the console list and return status */
133 ConSrvUnlockConsoleList();
134 return Status;
135 }
136 #endif
137
138 static NTSTATUS
139 RemoveConsoleByPointer(IN PCONSRV_CONSOLE Console)
140 {
141 ULONG i = 0;
142
143 if (!Console) return STATUS_INVALID_PARAMETER;
144
145 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
146 (ConsoleList != NULL && ConsoleListSize != 0) );
147
148 /* Remove the console from the list */
149 ConSrvLockConsoleListExclusive();
150
151 if (ConsoleList)
152 {
153 for (i = 0; i < ConsoleListSize; i++)
154 {
155 if (ConsoleList[i] == Console) ConsoleList[i] = NULL;
156 }
157 }
158
159 /* Unlock the console list */
160 ConSrvUnlockConsoleList();
161
162 return STATUS_SUCCESS;
163 }
164
165 BOOLEAN NTAPI
166 ConSrvValidateConsole(OUT PCONSRV_CONSOLE* Console,
167 IN HANDLE ConsoleHandle,
168 IN CONSOLE_STATE ExpectedState,
169 IN BOOLEAN LockConsole)
170 {
171 BOOLEAN RetVal = FALSE;
172 PCONSRV_CONSOLE ValidatedConsole;
173
174 BOOLEAN ValidHandle = ((HandleToULong(ConsoleHandle) & 0x3) == 0x3);
175 ULONG Index = HandleToULong(ConsoleHandle) >> 2;
176
177 if (!ValidHandle) return FALSE;
178
179 if (!Console) return FALSE;
180 *Console = NULL;
181
182 /*
183 * Forbid creation or deletion of consoles when
184 * checking for the existence of a console.
185 */
186 ConSrvLockConsoleListShared();
187
188 if (Index >= ConsoleListSize ||
189 (ValidatedConsole = ConsoleList[Index]) == NULL)
190 {
191 /* Unlock the console list */
192 ConSrvUnlockConsoleList();
193
194 return FALSE;
195 }
196
197 ValidatedConsole = ConsoleList[Index];
198
199 /* Unlock the console list and return */
200 ConSrvUnlockConsoleList();
201
202 RetVal = ConDrvValidateConsoleUnsafe(ValidatedConsole,
203 ExpectedState,
204 LockConsole);
205 if (RetVal) *Console = ValidatedConsole;
206
207 return RetVal;
208 }
209
210
211 /* PRIVATE FUNCTIONS **********************************************************/
212
213 VOID
214 ConioPause(PCONSRV_CONSOLE Console, UINT Flags)
215 {
216 Console->PauseFlags |= Flags;
217 ConDrvPause(Console);
218 }
219
220 VOID
221 ConioUnpause(PCONSRV_CONSOLE Console, UINT Flags)
222 {
223 Console->PauseFlags &= ~Flags;
224
225 // if ((Console->PauseFlags & (PAUSED_FROM_KEYBOARD | PAUSED_FROM_SCROLLBAR | PAUSED_FROM_SELECTION)) == 0)
226 if (Console->PauseFlags == 0)
227 {
228 ConDrvUnpause(Console);
229
230 CsrNotifyWait(&Console->WriteWaitQueue,
231 TRUE,
232 NULL,
233 NULL);
234 if (!IsListEmpty(&Console->WriteWaitQueue))
235 {
236 CsrDereferenceWait(&Console->WriteWaitQueue);
237 }
238 }
239 }
240
241 NTSTATUS
242 ConSrvGetConsole(IN PCONSOLE_PROCESS_DATA ProcessData,
243 OUT PCONSRV_CONSOLE* Console,
244 IN BOOLEAN LockConsole)
245 {
246 NTSTATUS Status = STATUS_INVALID_HANDLE;
247 PCONSRV_CONSOLE GrabConsole;
248
249 // if (Console == NULL) return STATUS_INVALID_PARAMETER;
250 ASSERT(Console);
251 *Console = NULL;
252
253 // RtlEnterCriticalSection(&ProcessData->HandleTableLock);
254
255 if (ConSrvValidateConsole(&GrabConsole,
256 ProcessData->ConsoleHandle,
257 CONSOLE_RUNNING,
258 LockConsole))
259 {
260 InterlockedIncrement(&GrabConsole->ReferenceCount);
261 *Console = GrabConsole;
262 Status = STATUS_SUCCESS;
263 }
264
265 // RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
266 return Status;
267 }
268
269 VOID
270 ConSrvReleaseConsole(IN PCONSRV_CONSOLE Console,
271 IN BOOLEAN WasConsoleLocked)
272 {
273 LONG RefCount = 0;
274
275 if (!Console) return;
276 // if (Console->ReferenceCount == 0) return; // This shouldn't happen
277 ASSERT(Console->ReferenceCount > 0);
278
279 /* The console must be locked */
280 // ASSERT(Console_locked);
281
282 /*
283 * Decrement the reference count. Save the new value too,
284 * because Console->ReferenceCount might be modified after
285 * the console gets unlocked but before we check whether we
286 * can destroy it.
287 */
288 RefCount = _InterlockedDecrement(&Console->ReferenceCount);
289
290 /* Unlock the console if needed */
291 if (WasConsoleLocked) LeaveCriticalSection(&Console->Lock);
292
293 /* Delete the console if needed */
294 if (RefCount <= 0) ConSrvDeleteConsole(Console);
295 }
296
297
298 /* CONSOLE INITIALIZATION FUNCTIONS *******************************************/
299
300 VOID NTAPI
301 ConSrvInitConsoleSupport(VOID)
302 {
303 DPRINT("CONSRV: ConSrvInitConsoleSupport()\n");
304
305 /* Initialize the console list and its lock */
306 ConsoleListSize = 0;
307 ConsoleList = NULL;
308 RtlInitializeResource(&ListLock);
309
310 /* Should call LoadKeyboardLayout */
311 }
312
313 NTSTATUS NTAPI
314 ConSrvInitTerminal(IN OUT PTERMINAL Terminal,
315 IN OUT PCONSOLE_INFO ConsoleInfo,
316 IN OUT PVOID ExtraConsoleInfo,
317 IN ULONG ProcessId);
318 NTSTATUS NTAPI
319 ConSrvDeinitTerminal(IN OUT PTERMINAL Terminal);
320
321 NTSTATUS NTAPI
322 ConSrvInitConsole(OUT PHANDLE NewConsoleHandle,
323 OUT PCONSRV_CONSOLE* NewConsole,
324 IN OUT PCONSOLE_START_INFO ConsoleStartInfo,
325 IN ULONG ConsoleLeaderProcessId)
326 {
327 NTSTATUS Status;
328 HANDLE ConsoleHandle;
329 PCONSRV_CONSOLE Console;
330 CONSOLE_INFO ConsoleInfo;
331 SIZE_T Length = 0;
332
333 TERMINAL Terminal; /* The ConSrv terminal for this console */
334
335 if (NewConsole == NULL || ConsoleStartInfo == NULL)
336 return STATUS_INVALID_PARAMETER;
337
338 *NewConsole = NULL;
339
340 /*
341 * Load the console settings
342 */
343
344 /* 1. Load the default settings */
345 ConSrvGetDefaultSettings(&ConsoleInfo, ConsoleLeaderProcessId);
346
347 /* 2. Get the title of the console (initialize ConsoleInfo.ConsoleTitle) */
348 Length = min(wcslen(ConsoleStartInfo->ConsoleTitle),
349 sizeof(ConsoleInfo.ConsoleTitle) / sizeof(ConsoleInfo.ConsoleTitle[0]) - 1);
350 wcsncpy(ConsoleInfo.ConsoleTitle, ConsoleStartInfo->ConsoleTitle, Length);
351 ConsoleInfo.ConsoleTitle[Length] = L'\0';
352
353 /* 3. Initialize the ConSrv terminal */
354 Status = ConSrvInitTerminal(&Terminal,
355 &ConsoleInfo,
356 ConsoleStartInfo,
357 ConsoleLeaderProcessId);
358 if (!NT_SUCCESS(Status))
359 {
360 DPRINT1("CONSRV: Failed to initialize a terminal, Status = 0x%08lx\n", Status);
361 return Status;
362 }
363 DPRINT("CONSRV: Terminal initialized\n");
364
365 /*
366 * 4. Load the remaining console settings via the registry.
367 */
368 if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
369 {
370 /*
371 * Either we weren't created by an app launched via a shell-link,
372 * or we failed to load shell-link console properties.
373 * Therefore, load the console infos for the application from the registry.
374 */
375 ConSrvReadUserSettings(&ConsoleInfo, ConsoleLeaderProcessId);
376
377 /*
378 * Now, update them with the properties the user might gave to us
379 * via the STARTUPINFO structure before calling CreateProcess
380 * (and which was transmitted via the ConsoleStartInfo structure).
381 * We therefore overwrite the values read in the registry.
382 */
383 if (ConsoleStartInfo->dwStartupFlags & STARTF_USEFILLATTRIBUTE)
384 {
385 ConsoleInfo.ScreenAttrib = (USHORT)ConsoleStartInfo->wFillAttribute;
386 }
387 if (ConsoleStartInfo->dwStartupFlags & STARTF_USECOUNTCHARS)
388 {
389 ConsoleInfo.ScreenBufferSize = ConsoleStartInfo->dwScreenBufferSize;
390 }
391 if (ConsoleStartInfo->dwStartupFlags & STARTF_USESIZE)
392 {
393 ConsoleInfo.ConsoleSize = ConsoleStartInfo->dwWindowSize;
394 }
395 }
396
397 /* Set-up the code page */
398 ConsoleInfo.CodePage = GetOEMCP();
399
400 /* Initialize a new console via the driver */
401 Status = ConDrvInitConsole(&Console, &ConsoleInfo);
402 if (!NT_SUCCESS(Status))
403 {
404 DPRINT1("Creating a new console failed, Status = 0x%08lx\n", Status);
405 ConSrvDeinitTerminal(&Terminal);
406 return Status;
407 }
408
409 ASSERT(Console);
410 DPRINT("Console initialized\n");
411
412 /*** Register ConSrv features ***/
413
414 /* Initialize process support */
415 InitializeListHead(&Console->ProcessList);
416 Console->NotifiedLastCloseProcess = NULL;
417 Console->NotifyLastClose = FALSE;
418
419 /* Initialize pausing support */
420 Console->PauseFlags = 0;
421 InitializeListHead(&Console->ReadWaitQueue);
422 InitializeListHead(&Console->WriteWaitQueue);
423
424 /* Initialize the alias and history buffers */
425 Console->Aliases = NULL;
426 InitializeListHead(&Console->HistoryBuffers);
427 Console->HistoryBufferSize = ConsoleInfo.HistoryBufferSize;
428 Console->NumberOfHistoryBuffers = ConsoleInfo.NumberOfHistoryBuffers;
429 Console->HistoryNoDup = ConsoleInfo.HistoryNoDup;
430
431 /* Initialize the Input Line Discipline */
432 Console->LineBuffer = NULL;
433 Console->LinePos = Console->LineMaxSize = Console->LineSize = 0;
434 Console->LineComplete = Console->LineUpPressed = FALSE;
435 // LineWakeupMask
436 Console->LineInsertToggle =
437 Console->InsertMode = ConsoleInfo.InsertMode;
438 Console->QuickEdit = ConsoleInfo.QuickEdit;
439
440 /* Popup windows */
441 InitializeListHead(&Console->PopupWindows);
442
443 /* Colour table */
444 memcpy(Console->Colors, ConsoleInfo.Colors, sizeof(ConsoleInfo.Colors));
445
446 /* Attach the ConSrv terminal to the console */
447 Status = ConDrvRegisterTerminal(Console, &Terminal);
448 if (!NT_SUCCESS(Status))
449 {
450 DPRINT1("Failed to register terminal to the given console, Status = 0x%08lx\n", Status);
451 ConDrvDeleteConsole(Console);
452 ConSrvDeinitTerminal(&Terminal);
453 return Status;
454 }
455 DPRINT("Terminal registered\n");
456
457 /* All went right, so add the console to the list */
458 Status = InsertConsole(&ConsoleHandle, Console);
459
460 /* Return the newly created console to the caller and a success code too */
461 *NewConsoleHandle = ConsoleHandle;
462 *NewConsole = Console;
463 return STATUS_SUCCESS;
464 }
465
466 VOID NTAPI
467 ConSrvDeleteConsole(PCONSRV_CONSOLE Console)
468 {
469 DPRINT("ConSrvDeleteConsole\n");
470
471 // FIXME: Send a terminate message to all the processes owning this console
472
473 /* Remove the console from the list */
474 RemoveConsoleByPointer(Console);
475
476 /* Clean the Input Line Discipline */
477 if (Console->LineBuffer) ConsoleFreeHeap(Console->LineBuffer);
478
479 /* Clean aliases and history */
480 IntDeleteAllAliases(Console);
481 HistoryDeleteBuffers(Console);
482
483 /* Now, call the driver. ConDrvDeregisterTerminal is called on-demand. */
484 ConDrvDeleteConsole(Console);
485 }
486
487
488
489
490
491
492 static NTSTATUS
493 ConSrvConsoleCtrlEventTimeout(IN ULONG CtrlEvent,
494 IN PCONSOLE_PROCESS_DATA ProcessData,
495 IN ULONG Timeout)
496 {
497 NTSTATUS Status = STATUS_SUCCESS;
498
499 DPRINT("ConSrvConsoleCtrlEventTimeout Parent ProcessId = %x\n", ProcessData->Process->ClientId.UniqueProcess);
500
501 if (ProcessData->CtrlDispatcher)
502 {
503 _SEH2_TRY
504 {
505 HANDLE Thread = NULL;
506
507 _SEH2_TRY
508 {
509 Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0,
510 ProcessData->CtrlDispatcher,
511 UlongToPtr(CtrlEvent), 0, NULL);
512 if (NULL == Thread)
513 {
514 Status = RtlGetLastNtStatus();
515 DPRINT1("Failed thread creation, Status = 0x%08lx\n", Status);
516 }
517 else
518 {
519 DPRINT("ProcessData->CtrlDispatcher remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n", ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process);
520 WaitForSingleObject(Thread, Timeout);
521 }
522 }
523 _SEH2_FINALLY
524 {
525 CloseHandle(Thread);
526 }
527 _SEH2_END;
528 }
529 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
530 {
531 Status = _SEH2_GetExceptionCode();
532 DPRINT1("ConSrvConsoleCtrlEventTimeout - Caught an exception, Status = 0x%08lx\n", Status);
533 }
534 _SEH2_END;
535 }
536
537 return Status;
538 }
539
540 NTSTATUS
541 ConSrvConsoleCtrlEvent(IN ULONG CtrlEvent,
542 IN PCONSOLE_PROCESS_DATA ProcessData)
543 {
544 return ConSrvConsoleCtrlEventTimeout(CtrlEvent, ProcessData, 0);
545 }
546
547 PCONSOLE_PROCESS_DATA NTAPI
548 ConSrvGetConsoleLeaderProcess(IN PCONSRV_CONSOLE Console)
549 {
550 if (Console == NULL) return NULL;
551
552 return CONTAINING_RECORD(Console->ProcessList.Blink,
553 CONSOLE_PROCESS_DATA,
554 ConsoleLink);
555 }
556
557 NTSTATUS NTAPI
558 ConSrvGetConsoleProcessList(IN PCONSRV_CONSOLE Console,
559 IN OUT PULONG ProcessIdsList,
560 IN ULONG MaxIdListItems,
561 OUT PULONG ProcessIdsTotal)
562 {
563 PCONSOLE_PROCESS_DATA current;
564 PLIST_ENTRY current_entry;
565
566 if (Console == NULL || ProcessIdsList == NULL || ProcessIdsTotal == NULL)
567 return STATUS_INVALID_PARAMETER;
568
569 *ProcessIdsTotal = 0;
570
571 for (current_entry = Console->ProcessList.Flink;
572 current_entry != &Console->ProcessList;
573 current_entry = current_entry->Flink)
574 {
575 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
576 if (++(*ProcessIdsTotal) <= MaxIdListItems)
577 {
578 *ProcessIdsList++ = HandleToUlong(current->Process->ClientId.UniqueProcess);
579 }
580 }
581
582 return STATUS_SUCCESS;
583 }
584
585 // ConSrvGenerateConsoleCtrlEvent
586 NTSTATUS NTAPI
587 ConSrvConsoleProcessCtrlEvent(IN PCONSRV_CONSOLE Console,
588 IN ULONG ProcessGroupId,
589 IN ULONG CtrlEvent)
590 {
591 NTSTATUS Status = STATUS_SUCCESS;
592 PLIST_ENTRY current_entry;
593 PCONSOLE_PROCESS_DATA current;
594
595 /* If the console is already being destroyed, just return */
596 if (!ConDrvValidateConsoleState(Console, CONSOLE_RUNNING))
597 return STATUS_UNSUCCESSFUL;
598
599 /*
600 * Loop through the process list, from the most recent process
601 * (the active one) to the oldest one (the first created, i.e.
602 * the console leader process), and for each, send an event
603 * (new processes are inserted at the head of the console process list).
604 */
605 current_entry = Console->ProcessList.Flink;
606 while (current_entry != &Console->ProcessList)
607 {
608 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
609 current_entry = current_entry->Flink;
610
611 /*
612 * Only processes belonging to the same process group are signaled.
613 * If the process group ID is zero, then all the processes are signaled.
614 */
615 if (ProcessGroupId == 0 || current->Process->ProcessGroupId == ProcessGroupId)
616 {
617 Status = ConSrvConsoleCtrlEvent(CtrlEvent, current);
618 }
619 }
620
621 return Status;
622 }
623
624
625
626
627
628 /* PUBLIC SERVER APIS *********************************************************/
629
630 CSR_API(SrvAllocConsole)
631 {
632 NTSTATUS Status = STATUS_SUCCESS;
633 PCONSOLE_ALLOCCONSOLE AllocConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AllocConsoleRequest;
634 PCSR_PROCESS CsrProcess = CsrGetClientThread()->Process;
635 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrProcess);
636
637 if (ProcessData->ConsoleHandle != NULL)
638 {
639 DPRINT1("Process already has a console\n");
640 return STATUS_ACCESS_DENIED;
641 }
642
643 if (!CsrValidateMessageBuffer(ApiMessage,
644 (PVOID*)&AllocConsoleRequest->ConsoleStartInfo,
645 1,
646 sizeof(CONSOLE_START_INFO)))
647 {
648 return STATUS_INVALID_PARAMETER;
649 }
650
651 /* Initialize a new Console owned by the Console Leader Process */
652 Status = ConSrvAllocateConsole(ProcessData,
653 &AllocConsoleRequest->InputHandle,
654 &AllocConsoleRequest->OutputHandle,
655 &AllocConsoleRequest->ErrorHandle,
656 AllocConsoleRequest->ConsoleStartInfo);
657 if (!NT_SUCCESS(Status))
658 {
659 DPRINT1("Console allocation failed\n");
660 return Status;
661 }
662
663 /* Return the console handle and the input wait handle to the caller */
664 AllocConsoleRequest->ConsoleHandle = ProcessData->ConsoleHandle;
665 AllocConsoleRequest->InputWaitHandle = ProcessData->InputWaitHandle;
666
667 /* Set the Property-Dialog and Control-Dispatcher handlers */
668 ProcessData->PropDispatcher = AllocConsoleRequest->PropDispatcher;
669 ProcessData->CtrlDispatcher = AllocConsoleRequest->CtrlDispatcher;
670
671 return STATUS_SUCCESS;
672 }
673
674 CSR_API(SrvAttachConsole)
675 {
676 NTSTATUS Status = STATUS_SUCCESS;
677 PCONSOLE_ATTACHCONSOLE AttachConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AttachConsoleRequest;
678 PCSR_PROCESS SourceProcess = NULL; // The parent process.
679 PCSR_PROCESS TargetProcess = CsrGetClientThread()->Process; // Ourselves.
680 HANDLE ProcessId = ULongToHandle(AttachConsoleRequest->ProcessId);
681 PCONSOLE_PROCESS_DATA SourceProcessData, TargetProcessData;
682
683 TargetProcessData = ConsoleGetPerProcessData(TargetProcess);
684
685 if (TargetProcessData->ConsoleHandle != NULL)
686 {
687 DPRINT1("Process already has a console\n");
688 return STATUS_ACCESS_DENIED;
689 }
690
691 /* Check whether we try to attach to the parent's console */
692 if (ProcessId == ULongToHandle(ATTACH_PARENT_PROCESS))
693 {
694 PROCESS_BASIC_INFORMATION ProcessInfo;
695 ULONG Length = sizeof(ProcessInfo);
696
697 /* Get the real parent's ID */
698
699 Status = NtQueryInformationProcess(TargetProcess->ProcessHandle,
700 ProcessBasicInformation,
701 &ProcessInfo,
702 Length, &Length);
703 if (!NT_SUCCESS(Status))
704 {
705 DPRINT1("SrvAttachConsole - Cannot retrieve basic process info, Status = %lu\n", Status);
706 return Status;
707 }
708
709 ProcessId = ULongToHandle(ProcessInfo.InheritedFromUniqueProcessId);
710 }
711
712 /* Lock the source process via its PID */
713 Status = CsrLockProcessByClientId(ProcessId, &SourceProcess);
714 if (!NT_SUCCESS(Status)) return Status;
715
716 SourceProcessData = ConsoleGetPerProcessData(SourceProcess);
717
718 if (SourceProcessData->ConsoleHandle == NULL)
719 {
720 Status = STATUS_INVALID_HANDLE;
721 goto Quit;
722 }
723
724 /*
725 * Inherit the console from the parent,
726 * if any, otherwise return an error.
727 */
728 Status = ConSrvInheritConsole(TargetProcessData,
729 SourceProcessData->ConsoleHandle,
730 TRUE,
731 &AttachConsoleRequest->InputHandle,
732 &AttachConsoleRequest->OutputHandle,
733 &AttachConsoleRequest->ErrorHandle);
734 if (!NT_SUCCESS(Status))
735 {
736 DPRINT1("Console inheritance failed\n");
737 goto Quit;
738 }
739
740 /* Return the console handle and the input wait handle to the caller */
741 AttachConsoleRequest->ConsoleHandle = TargetProcessData->ConsoleHandle;
742 AttachConsoleRequest->InputWaitHandle = TargetProcessData->InputWaitHandle;
743
744 /* Set the Property-Dialog and Control-Dispatcher handlers */
745 TargetProcessData->PropDispatcher = AttachConsoleRequest->PropDispatcher;
746 TargetProcessData->CtrlDispatcher = AttachConsoleRequest->CtrlDispatcher;
747
748 Status = STATUS_SUCCESS;
749
750 Quit:
751 /* Unlock the "source" process and exit */
752 CsrUnlockProcess(SourceProcess);
753 return Status;
754 }
755
756 CSR_API(SrvFreeConsole)
757 {
758 ConSrvRemoveConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process));
759 return STATUS_SUCCESS;
760 }
761
762 NTSTATUS NTAPI
763 ConDrvGetConsoleMode(IN PCONSOLE Console,
764 IN PCONSOLE_IO_OBJECT Object,
765 OUT PULONG ConsoleMode);
766 CSR_API(SrvGetConsoleMode)
767 {
768 NTSTATUS Status;
769 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
770 PCONSOLE_IO_OBJECT Object;
771
772 PULONG ConsoleMode = &ConsoleModeRequest->Mode;
773
774 Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
775 ConsoleModeRequest->Handle,
776 &Object, NULL, GENERIC_READ, TRUE, 0);
777 if (!NT_SUCCESS(Status)) return Status;
778
779 /* Get the standard console modes */
780 Status = ConDrvGetConsoleMode(Object->Console, Object,
781 ConsoleMode);
782 if (NT_SUCCESS(Status))
783 {
784 /*
785 * If getting the console modes succeeds, then retrieve
786 * the extended CONSRV-specific input modes.
787 */
788 if (INPUT_BUFFER == Object->Type)
789 {
790 if (Object->Console->InsertMode || Object->Console->QuickEdit)
791 {
792 /* Windows does this, even if it is not documented on MSDN */
793 *ConsoleMode |= ENABLE_EXTENDED_FLAGS;
794
795 if (Object->Console->InsertMode) *ConsoleMode |= ENABLE_INSERT_MODE;
796 if (Object->Console->QuickEdit ) *ConsoleMode |= ENABLE_QUICK_EDIT_MODE;
797 }
798 }
799 }
800
801 ConSrvReleaseObject(Object, TRUE);
802 return Status;
803 }
804
805 NTSTATUS NTAPI
806 ConDrvSetConsoleMode(IN PCONSOLE Console,
807 IN PCONSOLE_IO_OBJECT Object,
808 IN ULONG ConsoleMode);
809 CSR_API(SrvSetConsoleMode)
810 {
811 #define CONSOLE_VALID_CONTROL_MODES ( ENABLE_EXTENDED_FLAGS | \
812 ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE )
813
814 NTSTATUS Status;
815 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
816 PCONSOLE_IO_OBJECT Object;
817
818 ULONG ConsoleMode = ConsoleModeRequest->Mode;
819
820 Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
821 ConsoleModeRequest->Handle,
822 &Object, NULL, GENERIC_WRITE, TRUE, 0);
823 if (!NT_SUCCESS(Status)) return Status;
824
825 /* Set the standard console modes (without the CONSRV-specific input modes) */
826 ConsoleMode &= ~CONSOLE_VALID_CONTROL_MODES; // Remove CONSRV-specific input modes.
827 Status = ConDrvSetConsoleMode(Object->Console, Object,
828 ConsoleMode);
829 if (NT_SUCCESS(Status))
830 {
831 /*
832 * If setting the console modes succeeds, then set
833 * the extended CONSRV-specific input modes.
834 */
835 if (INPUT_BUFFER == Object->Type)
836 {
837 ConsoleMode = ConsoleModeRequest->Mode;
838
839 if (ConsoleMode & CONSOLE_VALID_CONTROL_MODES)
840 {
841 /*
842 * If we use control mode flags without ENABLE_EXTENDED_FLAGS,
843 * then consider the flags invalid.
844 */
845 if ((ConsoleMode & ENABLE_EXTENDED_FLAGS) == 0)
846 {
847 Status = STATUS_INVALID_PARAMETER;
848 }
849 else
850 {
851 Object->Console->InsertMode = !!(ConsoleMode & ENABLE_INSERT_MODE);
852 Object->Console->QuickEdit = !!(ConsoleMode & ENABLE_QUICK_EDIT_MODE);
853 }
854 }
855 }
856 }
857
858 ConSrvReleaseObject(Object, TRUE);
859 return Status;
860 }
861
862 NTSTATUS NTAPI
863 ConDrvGetConsoleTitle(IN PCONSOLE Console,
864 IN BOOLEAN Unicode,
865 IN OUT PVOID TitleBuffer,
866 IN OUT PULONG BufLength);
867 CSR_API(SrvGetConsoleTitle)
868 {
869 NTSTATUS Status;
870 PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
871 PCONSRV_CONSOLE Console;
872
873 if (!CsrValidateMessageBuffer(ApiMessage,
874 (PVOID)&TitleRequest->Title,
875 TitleRequest->Length,
876 sizeof(BYTE)))
877 {
878 return STATUS_INVALID_PARAMETER;
879 }
880
881 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
882 if (!NT_SUCCESS(Status))
883 {
884 DPRINT1("Can't get console\n");
885 return Status;
886 }
887
888 Status = ConDrvGetConsoleTitle(Console,
889 TitleRequest->Unicode,
890 TitleRequest->Title,
891 &TitleRequest->Length);
892
893 ConSrvReleaseConsole(Console, TRUE);
894 return Status;
895 }
896
897 NTSTATUS NTAPI
898 ConDrvSetConsoleTitle(IN PCONSOLE Console,
899 IN BOOLEAN Unicode,
900 IN PVOID TitleBuffer,
901 IN ULONG BufLength);
902 CSR_API(SrvSetConsoleTitle)
903 {
904 NTSTATUS Status;
905 PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
906 PCONSRV_CONSOLE Console;
907
908 if (!CsrValidateMessageBuffer(ApiMessage,
909 (PVOID)&TitleRequest->Title,
910 TitleRequest->Length,
911 sizeof(BYTE)))
912 {
913 return STATUS_INVALID_PARAMETER;
914 }
915
916 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
917 if (!NT_SUCCESS(Status))
918 {
919 DPRINT1("Can't get console\n");
920 return Status;
921 }
922
923 Status = ConDrvSetConsoleTitle(Console,
924 TitleRequest->Unicode,
925 TitleRequest->Title,
926 TitleRequest->Length);
927 if (NT_SUCCESS(Status)) TermChangeTitle(Console);
928
929 ConSrvReleaseConsole(Console, TRUE);
930 return Status;
931 }
932
933 NTSTATUS NTAPI
934 ConDrvGetConsoleCP(IN PCONSOLE Console,
935 OUT PUINT CodePage,
936 IN BOOLEAN OutputCP);
937 CSR_API(SrvGetConsoleCP)
938 {
939 NTSTATUS Status;
940 PCONSOLE_GETINPUTOUTPUTCP GetConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetConsoleCPRequest;
941 PCONSRV_CONSOLE Console;
942
943 DPRINT("SrvGetConsoleCP, getting %s Code Page\n",
944 GetConsoleCPRequest->OutputCP ? "Output" : "Input");
945
946 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
947 if (!NT_SUCCESS(Status)) return Status;
948
949 Status = ConDrvGetConsoleCP(Console,
950 &GetConsoleCPRequest->CodePage,
951 GetConsoleCPRequest->OutputCP);
952
953 ConSrvReleaseConsole(Console, TRUE);
954 return Status;
955 }
956
957 NTSTATUS NTAPI
958 ConDrvSetConsoleCP(IN PCONSOLE Console,
959 IN UINT CodePage,
960 IN BOOLEAN OutputCP);
961 CSR_API(SrvSetConsoleCP)
962 {
963 NTSTATUS Status = STATUS_INVALID_PARAMETER;
964 PCONSOLE_SETINPUTOUTPUTCP SetConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetConsoleCPRequest;
965 PCONSRV_CONSOLE Console;
966
967 DPRINT("SrvSetConsoleCP, setting %s Code Page\n",
968 SetConsoleCPRequest->OutputCP ? "Output" : "Input");
969
970 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
971 if (!NT_SUCCESS(Status)) return Status;
972
973 Status = ConDrvSetConsoleCP(Console,
974 SetConsoleCPRequest->CodePage,
975 SetConsoleCPRequest->OutputCP);
976
977 ConSrvReleaseConsole(Console, TRUE);
978 return Status;
979 }
980
981 CSR_API(SrvGetConsoleProcessList)
982 {
983 NTSTATUS Status;
984 PCONSOLE_GETPROCESSLIST GetProcessListRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetProcessListRequest;
985 PCONSRV_CONSOLE Console;
986
987 if (!CsrValidateMessageBuffer(ApiMessage,
988 (PVOID)&GetProcessListRequest->ProcessIdsList,
989 GetProcessListRequest->ProcessCount,
990 sizeof(DWORD)))
991 {
992 return STATUS_INVALID_PARAMETER;
993 }
994
995 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
996 if (!NT_SUCCESS(Status)) return Status;
997
998 Status = ConSrvGetConsoleProcessList(Console,
999 GetProcessListRequest->ProcessIdsList,
1000 GetProcessListRequest->ProcessCount,
1001 &GetProcessListRequest->ProcessCount);
1002
1003 ConSrvReleaseConsole(Console, TRUE);
1004 return Status;
1005 }
1006
1007 CSR_API(SrvGenerateConsoleCtrlEvent)
1008 {
1009 NTSTATUS Status;
1010 PCONSOLE_GENERATECTRLEVENT GenerateCtrlEventRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GenerateCtrlEventRequest;
1011 PCONSRV_CONSOLE Console;
1012
1013 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1014 if (!NT_SUCCESS(Status)) return Status;
1015
1016 Status = ConSrvConsoleProcessCtrlEvent(Console,
1017 GenerateCtrlEventRequest->ProcessGroupId,
1018 GenerateCtrlEventRequest->CtrlEvent);
1019
1020 ConSrvReleaseConsole(Console, TRUE);
1021 return Status;
1022 }
1023
1024 CSR_API(SrvConsoleNotifyLastClose)
1025 {
1026 NTSTATUS Status;
1027 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
1028 PCONSRV_CONSOLE Console;
1029
1030 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
1031 if (!NT_SUCCESS(Status)) return Status;
1032
1033 /* Only one process is allowed to be registered for last close notification */
1034 if (!Console->NotifyLastClose)
1035 {
1036 Console->NotifyLastClose = TRUE;
1037 Console->NotifiedLastCloseProcess = ProcessData;
1038 Status = STATUS_SUCCESS;
1039 }
1040 else
1041 {
1042 Status = STATUS_ACCESS_DENIED;
1043 }
1044
1045 ConSrvReleaseConsole(Console, TRUE);
1046 return Status;
1047 }
1048
1049
1050
1051 CSR_API(SrvGetConsoleMouseInfo)
1052 {
1053 NTSTATUS Status;
1054 PCONSOLE_GETMOUSEINFO GetMouseInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetMouseInfoRequest;
1055 PCONSRV_CONSOLE Console;
1056
1057 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1058 if (!NT_SUCCESS(Status)) return Status;
1059
1060 /* Just retrieve the number of buttons of the mouse attached to this console */
1061 GetMouseInfoRequest->NumButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
1062
1063 ConSrvReleaseConsole(Console, TRUE);
1064 return STATUS_SUCCESS;
1065 }
1066
1067 CSR_API(SrvSetConsoleKeyShortcuts)
1068 {
1069 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1070 return STATUS_NOT_IMPLEMENTED;
1071 }
1072
1073 CSR_API(SrvGetConsoleKeyboardLayoutName)
1074 {
1075 NTSTATUS Status;
1076 PCONSOLE_GETKBDLAYOUTNAME GetKbdLayoutNameRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetKbdLayoutNameRequest;
1077 PCONSRV_CONSOLE Console;
1078
1079 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1080 if (!NT_SUCCESS(Status)) return Status;
1081
1082 /* Retrieve the keyboard layout name of the system */
1083 if (GetKbdLayoutNameRequest->Ansi)
1084 GetKeyboardLayoutNameA((PCHAR)GetKbdLayoutNameRequest->LayoutBuffer);
1085 else
1086 GetKeyboardLayoutNameW((PWCHAR)GetKbdLayoutNameRequest->LayoutBuffer);
1087
1088 ConSrvReleaseConsole(Console, TRUE);
1089 return STATUS_SUCCESS;
1090 }
1091
1092 CSR_API(SrvGetConsoleCharType)
1093 {
1094 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1095 return STATUS_NOT_IMPLEMENTED;
1096 }
1097
1098 CSR_API(SrvSetConsoleLocalEUDC)
1099 {
1100 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1101 return STATUS_NOT_IMPLEMENTED;
1102 }
1103
1104 CSR_API(SrvSetConsoleCursorMode)
1105 {
1106 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1107 return STATUS_NOT_IMPLEMENTED;
1108 }
1109
1110 CSR_API(SrvGetConsoleCursorMode)
1111 {
1112 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1113 return STATUS_NOT_IMPLEMENTED;
1114 }
1115
1116 CSR_API(SrvGetConsoleNlsMode)
1117 {
1118 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1119 return STATUS_NOT_IMPLEMENTED;
1120 }
1121
1122 CSR_API(SrvSetConsoleNlsMode)
1123 {
1124 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1125 return STATUS_NOT_IMPLEMENTED;
1126 }
1127
1128 CSR_API(SrvGetConsoleLangId)
1129 {
1130 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1131 return STATUS_NOT_IMPLEMENTED;
1132 }
1133
1134 /* EOF */