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