[CONSRV]: Still some code refactoring....
[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 "procinit.h"
19
20 #define NDEBUG
21 #include <debug.h>
22
23 // FIXME: Add this prototype to winternl.h / rtlfuncs.h / ...
24 NTSTATUS NTAPI RtlGetLastNtStatus(VOID);
25
26 /* GLOBALS ********************************************************************/
27
28 static ULONG ConsoleListSize;
29 static PCONSRV_CONSOLE* ConsoleList; /* The list of the ConSrv consoles */
30 static RTL_RESOURCE ListLock;
31
32 #define ConSrvLockConsoleListExclusive() \
33 RtlAcquireResourceExclusive(&ListLock, TRUE)
34
35 #define ConSrvLockConsoleListShared() \
36 RtlAcquireResourceShared(&ListLock, TRUE)
37
38 #define ConSrvUnlockConsoleList() \
39 RtlReleaseResource(&ListLock)
40
41
42 static NTSTATUS
43 InsertConsole(OUT PHANDLE Handle,
44 IN PCONSRV_CONSOLE Console)
45 {
46 #define CONSOLE_HANDLES_INCREMENT 2 * 3
47
48 NTSTATUS Status = STATUS_SUCCESS;
49 ULONG i = 0;
50 PCONSRV_CONSOLE* Block;
51
52 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
53 (ConsoleList != NULL && ConsoleListSize != 0) );
54
55 /* All went right, so add the console to the list */
56 ConSrvLockConsoleListExclusive();
57 DPRINT1("Insert in the list\n");
58
59 if (ConsoleList)
60 {
61 for (i = 0; i < ConsoleListSize; i++)
62 {
63 if (ConsoleList[i] == NULL) break;
64 }
65 }
66
67 if (i >= ConsoleListSize)
68 {
69 DPRINT1("Creation of a new handles table\n");
70 /* Allocate a new handles table */
71 Block = ConsoleAllocHeap(HEAP_ZERO_MEMORY,
72 (ConsoleListSize +
73 CONSOLE_HANDLES_INCREMENT) * sizeof(PCONSRV_CONSOLE));
74 if (Block == NULL)
75 {
76 Status = STATUS_UNSUCCESSFUL;
77 goto Quit;
78 }
79
80 /* If we previously had a handles table, free it and use the new one */
81 if (ConsoleList)
82 {
83 /* Copy the handles from the old table to the new one */
84 RtlCopyMemory(Block,
85 ConsoleList,
86 ConsoleListSize * sizeof(PCONSRV_CONSOLE));
87 ConsoleFreeHeap(ConsoleList);
88 }
89 ConsoleList = Block;
90 ConsoleListSize += CONSOLE_HANDLES_INCREMENT;
91 }
92
93 ConsoleList[i] = Console;
94 *Handle = ULongToHandle((i << 2) | 0x3);
95
96 Quit:
97 /* Unlock the console list and return status */
98 ConSrvUnlockConsoleList();
99 return Status;
100 }
101
102 /* Unused */
103 #if 0
104 static NTSTATUS
105 RemoveConsoleByHandle(IN HANDLE Handle)
106 {
107 NTSTATUS Status = STATUS_SUCCESS;
108 PCONSRV_CONSOLE Console;
109
110 BOOLEAN ValidHandle = ((HandleToULong(Handle) & 0x3) == 0x3);
111 ULONG Index = HandleToULong(Handle) >> 2;
112
113 if (!ValidHandle) return STATUS_INVALID_HANDLE;
114
115 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
116 (ConsoleList != NULL && ConsoleListSize != 0) );
117
118 /* Remove the console from the list */
119 ConSrvLockConsoleListExclusive();
120
121 if (Index >= ConsoleListSize ||
122 (Console = ConsoleList[Index]) == NULL)
123 {
124 Status = STATUS_INVALID_HANDLE;
125 goto Quit;
126 }
127
128 ConsoleList[Index] = NULL;
129
130 Quit:
131 /* Unlock the console list and return status */
132 ConSrvUnlockConsoleList();
133 return Status;
134 }
135 #endif
136
137 static NTSTATUS
138 RemoveConsoleByPointer(IN PCONSRV_CONSOLE Console)
139 {
140 ULONG i = 0;
141
142 if (!Console) return STATUS_INVALID_PARAMETER;
143
144 ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
145 (ConsoleList != NULL && ConsoleListSize != 0) );
146
147 /* Remove the console from the list */
148 ConSrvLockConsoleListExclusive();
149
150 if (ConsoleList)
151 {
152 for (i = 0; i < ConsoleListSize; i++)
153 {
154 if (ConsoleList[i] == Console) ConsoleList[i] = NULL;
155 }
156 }
157
158 /* Unlock the console list */
159 ConSrvUnlockConsoleList();
160
161 return STATUS_SUCCESS;
162 }
163
164 BOOLEAN NTAPI
165 ConSrvValidateConsole(OUT PCONSRV_CONSOLE* Console,
166 IN HANDLE ConsoleHandle,
167 IN CONSOLE_STATE ExpectedState,
168 IN BOOLEAN LockConsole)
169 {
170 BOOLEAN RetVal = FALSE;
171 PCONSRV_CONSOLE ValidatedConsole;
172
173 BOOLEAN ValidHandle = ((HandleToULong(ConsoleHandle) & 0x3) == 0x3);
174 ULONG Index = HandleToULong(ConsoleHandle) >> 2;
175
176 if (!ValidHandle) return FALSE;
177
178 if (!Console) return FALSE;
179 *Console = NULL;
180
181 /*
182 * Forbid creation or deletion of consoles when
183 * checking for the existence of a console.
184 */
185 ConSrvLockConsoleListShared();
186
187 if (Index >= ConsoleListSize ||
188 (ValidatedConsole = ConsoleList[Index]) == NULL)
189 {
190 /* Unlock the console list */
191 ConSrvUnlockConsoleList();
192
193 return FALSE;
194 }
195
196 ValidatedConsole = ConsoleList[Index];
197
198 /* Unlock the console list and return */
199 ConSrvUnlockConsoleList();
200
201 RetVal = ConDrvValidateConsoleUnsafe(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(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(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 Console->QuickEdit = ConsoleInfo.QuickEdit;
431
432 /* Colour table */
433 memcpy(Console->Colors, ConsoleInfo.Colors, sizeof(ConsoleInfo.Colors));
434
435 /* Attach the ConSrv terminal to the console */
436 Status = ConDrvRegisterTerminal(Console, &Terminal);
437 if (!NT_SUCCESS(Status))
438 {
439 DPRINT1("Failed to register terminal to the given console, Status = 0x%08lx\n", Status);
440 ConDrvDeleteConsole(Console);
441 ConSrvDeinitTerminal(&Terminal);
442 return Status;
443 }
444 DPRINT("Terminal registered\n");
445
446 /* All went right, so add the console to the list */
447 Status = InsertConsole(&ConsoleHandle, Console);
448
449 /* Return the newly created console to the caller and a success code too */
450 *NewConsoleHandle = ConsoleHandle;
451 *NewConsole = Console;
452 return STATUS_SUCCESS;
453 }
454
455 VOID NTAPI
456 ConSrvDeleteConsole(PCONSRV_CONSOLE Console)
457 {
458 DPRINT("ConSrvDeleteConsole\n");
459
460 // FIXME: Send a terminate message to all the processes owning this console
461
462 /* Remove the console from the list */
463 RemoveConsoleByPointer(Console);
464
465 /* Clean aliases and history */
466 IntDeleteAllAliases(Console);
467 HistoryDeleteBuffers(Console);
468
469 /* Now, call the driver. ConDrvDeregisterTerminal is called on-demand. */
470 ConDrvDeleteConsole(Console);
471 }
472
473
474
475
476
477
478 static NTSTATUS
479 ConSrvConsoleCtrlEventTimeout(IN ULONG CtrlEvent,
480 IN PCONSOLE_PROCESS_DATA ProcessData,
481 IN ULONG Timeout)
482 {
483 NTSTATUS Status = STATUS_SUCCESS;
484
485 DPRINT("ConSrvConsoleCtrlEventTimeout Parent ProcessId = %x\n", ProcessData->Process->ClientId.UniqueProcess);
486
487 if (ProcessData->CtrlDispatcher)
488 {
489 _SEH2_TRY
490 {
491 HANDLE Thread = NULL;
492
493 _SEH2_TRY
494 {
495 Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0,
496 ProcessData->CtrlDispatcher,
497 UlongToPtr(CtrlEvent), 0, NULL);
498 if (NULL == Thread)
499 {
500 Status = RtlGetLastNtStatus();
501 DPRINT1("Failed thread creation, Status = 0x%08lx\n", Status);
502 }
503 else
504 {
505 DPRINT("ProcessData->CtrlDispatcher remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n", ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process);
506 WaitForSingleObject(Thread, Timeout);
507 }
508 }
509 _SEH2_FINALLY
510 {
511 CloseHandle(Thread);
512 }
513 _SEH2_END;
514 }
515 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
516 {
517 Status = _SEH2_GetExceptionCode();
518 DPRINT1("ConSrvConsoleCtrlEventTimeout - Caught an exception, Status = 0x%08lx\n", Status);
519 }
520 _SEH2_END;
521 }
522
523 return Status;
524 }
525
526 NTSTATUS
527 ConSrvConsoleCtrlEvent(IN ULONG CtrlEvent,
528 IN PCONSOLE_PROCESS_DATA ProcessData)
529 {
530 return ConSrvConsoleCtrlEventTimeout(CtrlEvent, ProcessData, 0);
531 }
532
533 PCONSOLE_PROCESS_DATA NTAPI
534 ConSrvGetConsoleLeaderProcess(IN PCONSRV_CONSOLE Console)
535 {
536 if (Console == NULL) return NULL;
537
538 return CONTAINING_RECORD(Console->ProcessList.Blink,
539 CONSOLE_PROCESS_DATA,
540 ConsoleLink);
541 }
542
543 NTSTATUS NTAPI
544 ConSrvGetConsoleProcessList(IN PCONSRV_CONSOLE Console,
545 IN OUT PULONG ProcessIdsList,
546 IN ULONG MaxIdListItems,
547 OUT PULONG ProcessIdsTotal)
548 {
549 PCONSOLE_PROCESS_DATA current;
550 PLIST_ENTRY current_entry;
551
552 if (Console == NULL || ProcessIdsList == NULL || ProcessIdsTotal == NULL)
553 return STATUS_INVALID_PARAMETER;
554
555 *ProcessIdsTotal = 0;
556
557 for (current_entry = Console->ProcessList.Flink;
558 current_entry != &Console->ProcessList;
559 current_entry = current_entry->Flink)
560 {
561 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
562 if (++(*ProcessIdsTotal) <= MaxIdListItems)
563 {
564 *ProcessIdsList++ = HandleToUlong(current->Process->ClientId.UniqueProcess);
565 }
566 }
567
568 return STATUS_SUCCESS;
569 }
570
571 // ConSrvGenerateConsoleCtrlEvent
572 NTSTATUS NTAPI
573 ConSrvConsoleProcessCtrlEvent(IN PCONSRV_CONSOLE Console,
574 IN ULONG ProcessGroupId,
575 IN ULONG CtrlEvent)
576 {
577 NTSTATUS Status = STATUS_SUCCESS;
578 PLIST_ENTRY current_entry;
579 PCONSOLE_PROCESS_DATA current;
580
581 /* If the console is already being destroyed, just return */
582 if (!ConDrvValidateConsoleState(Console, CONSOLE_RUNNING))
583 return STATUS_UNSUCCESSFUL;
584
585 /*
586 * Loop through the process list, from the most recent process
587 * (the active one) to the oldest one (the first created, i.e.
588 * the console leader process), and for each, send an event
589 * (new processes are inserted at the head of the console process list).
590 */
591 current_entry = Console->ProcessList.Flink;
592 while (current_entry != &Console->ProcessList)
593 {
594 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
595 current_entry = current_entry->Flink;
596
597 /*
598 * Only processes belonging to the same process group are signaled.
599 * If the process group ID is zero, then all the processes are signaled.
600 */
601 if (ProcessGroupId == 0 || current->Process->ProcessGroupId == ProcessGroupId)
602 {
603 Status = ConSrvConsoleCtrlEvent(CtrlEvent, current);
604 }
605 }
606
607 return Status;
608 }
609
610
611
612
613
614 /* PUBLIC SERVER APIS *********************************************************/
615
616 CSR_API(SrvAllocConsole)
617 {
618 NTSTATUS Status = STATUS_SUCCESS;
619 PCONSOLE_ALLOCCONSOLE AllocConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AllocConsoleRequest;
620 PCSR_PROCESS CsrProcess = CsrGetClientThread()->Process;
621 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrProcess);
622
623 if (ProcessData->ConsoleHandle != NULL)
624 {
625 DPRINT1("Process already has a console\n");
626 return STATUS_ACCESS_DENIED;
627 }
628
629 if (!CsrValidateMessageBuffer(ApiMessage,
630 (PVOID*)&AllocConsoleRequest->ConsoleStartInfo,
631 1,
632 sizeof(CONSOLE_START_INFO)))
633 {
634 return STATUS_INVALID_PARAMETER;
635 }
636
637 /* Initialize a new Console owned by the Console Leader Process */
638 Status = ConSrvAllocateConsole(ProcessData,
639 &AllocConsoleRequest->InputHandle,
640 &AllocConsoleRequest->OutputHandle,
641 &AllocConsoleRequest->ErrorHandle,
642 AllocConsoleRequest->ConsoleStartInfo);
643 if (!NT_SUCCESS(Status))
644 {
645 DPRINT1("Console allocation failed\n");
646 return Status;
647 }
648
649 /* Return the console handle and the input wait handle to the caller */
650 AllocConsoleRequest->ConsoleHandle = ProcessData->ConsoleHandle;
651 AllocConsoleRequest->InputWaitHandle = ProcessData->InputWaitHandle;
652
653 /* Set the Property-Dialog and Control-Dispatcher handlers */
654 ProcessData->PropDispatcher = AllocConsoleRequest->PropDispatcher;
655 ProcessData->CtrlDispatcher = AllocConsoleRequest->CtrlDispatcher;
656
657 return STATUS_SUCCESS;
658 }
659
660 CSR_API(SrvAttachConsole)
661 {
662 NTSTATUS Status = STATUS_SUCCESS;
663 PCONSOLE_ATTACHCONSOLE AttachConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AttachConsoleRequest;
664 PCSR_PROCESS SourceProcess = NULL; // The parent process.
665 PCSR_PROCESS TargetProcess = CsrGetClientThread()->Process; // Ourselves.
666 HANDLE ProcessId = ULongToHandle(AttachConsoleRequest->ProcessId);
667 PCONSOLE_PROCESS_DATA SourceProcessData, TargetProcessData;
668
669 TargetProcessData = ConsoleGetPerProcessData(TargetProcess);
670
671 if (TargetProcessData->ConsoleHandle != NULL)
672 {
673 DPRINT1("Process already has a console\n");
674 return STATUS_ACCESS_DENIED;
675 }
676
677 /* Check whether we try to attach to the parent's console */
678 if (ProcessId == ULongToHandle(ATTACH_PARENT_PROCESS))
679 {
680 PROCESS_BASIC_INFORMATION ProcessInfo;
681 ULONG Length = sizeof(ProcessInfo);
682
683 /* Get the real parent's ID */
684
685 Status = NtQueryInformationProcess(TargetProcess->ProcessHandle,
686 ProcessBasicInformation,
687 &ProcessInfo,
688 Length, &Length);
689 if (!NT_SUCCESS(Status))
690 {
691 DPRINT1("SrvAttachConsole - Cannot retrieve basic process info, Status = %lu\n", Status);
692 return Status;
693 }
694
695 ProcessId = ULongToHandle(ProcessInfo.InheritedFromUniqueProcessId);
696 }
697
698 /* Lock the source process via its PID */
699 Status = CsrLockProcessByClientId(ProcessId, &SourceProcess);
700 if (!NT_SUCCESS(Status)) return Status;
701
702 SourceProcessData = ConsoleGetPerProcessData(SourceProcess);
703
704 if (SourceProcessData->ConsoleHandle == NULL)
705 {
706 Status = STATUS_INVALID_HANDLE;
707 goto Quit;
708 }
709
710 /*
711 * Inherit the console from the parent,
712 * if any, otherwise return an error.
713 */
714 Status = ConSrvInheritConsole(TargetProcessData,
715 SourceProcessData->ConsoleHandle,
716 TRUE,
717 &AttachConsoleRequest->InputHandle,
718 &AttachConsoleRequest->OutputHandle,
719 &AttachConsoleRequest->ErrorHandle);
720 if (!NT_SUCCESS(Status))
721 {
722 DPRINT1("Console inheritance failed\n");
723 goto Quit;
724 }
725
726 /* Return the console handle and the input wait handle to the caller */
727 AttachConsoleRequest->ConsoleHandle = TargetProcessData->ConsoleHandle;
728 AttachConsoleRequest->InputWaitHandle = TargetProcessData->InputWaitHandle;
729
730 /* Set the Property-Dialog and Control-Dispatcher handlers */
731 TargetProcessData->PropDispatcher = AttachConsoleRequest->PropDispatcher;
732 TargetProcessData->CtrlDispatcher = AttachConsoleRequest->CtrlDispatcher;
733
734 Status = STATUS_SUCCESS;
735
736 Quit:
737 /* Unlock the "source" process and exit */
738 CsrUnlockProcess(SourceProcess);
739 return Status;
740 }
741
742 CSR_API(SrvFreeConsole)
743 {
744 ConSrvRemoveConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process));
745 return STATUS_SUCCESS;
746 }
747
748 NTSTATUS NTAPI
749 ConDrvGetConsoleMode(IN PCONSOLE Console,
750 IN PCONSOLE_IO_OBJECT Object,
751 OUT PULONG ConsoleMode);
752 CSR_API(SrvGetConsoleMode)
753 {
754 NTSTATUS Status;
755 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
756 PCONSOLE_IO_OBJECT Object;
757
758 Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
759 ConsoleModeRequest->Handle,
760 &Object, NULL, GENERIC_READ, TRUE, 0);
761 if (!NT_SUCCESS(Status)) return Status;
762
763 Status = ConDrvGetConsoleMode(Object->Console, Object,
764 &ConsoleModeRequest->Mode);
765
766 ConSrvReleaseObject(Object, TRUE);
767 return Status;
768 }
769
770 NTSTATUS NTAPI
771 ConDrvSetConsoleMode(IN PCONSOLE Console,
772 IN PCONSOLE_IO_OBJECT Object,
773 IN ULONG ConsoleMode);
774 CSR_API(SrvSetConsoleMode)
775 {
776 NTSTATUS Status;
777 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
778 PCONSOLE_IO_OBJECT Object;
779
780 Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
781 ConsoleModeRequest->Handle,
782 &Object, NULL, GENERIC_WRITE, TRUE, 0);
783 if (!NT_SUCCESS(Status)) return Status;
784
785 Status = ConDrvSetConsoleMode(Object->Console, Object,
786 ConsoleModeRequest->Mode);
787
788 ConSrvReleaseObject(Object, TRUE);
789 return Status;
790 }
791
792 NTSTATUS NTAPI
793 ConDrvGetConsoleTitle(IN PCONSOLE Console,
794 IN BOOLEAN Unicode,
795 IN OUT PVOID TitleBuffer,
796 IN OUT PULONG BufLength);
797 CSR_API(SrvGetConsoleTitle)
798 {
799 NTSTATUS Status;
800 PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
801 PCONSRV_CONSOLE Console;
802
803 if (!CsrValidateMessageBuffer(ApiMessage,
804 (PVOID)&TitleRequest->Title,
805 TitleRequest->Length,
806 sizeof(BYTE)))
807 {
808 return STATUS_INVALID_PARAMETER;
809 }
810
811 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
812 if (!NT_SUCCESS(Status))
813 {
814 DPRINT1("Can't get console\n");
815 return Status;
816 }
817
818 Status = ConDrvGetConsoleTitle(Console,
819 TitleRequest->Unicode,
820 TitleRequest->Title,
821 &TitleRequest->Length);
822
823 ConSrvReleaseConsole(Console, TRUE);
824 return Status;
825 }
826
827 NTSTATUS NTAPI
828 ConDrvSetConsoleTitle(IN PCONSOLE Console,
829 IN BOOLEAN Unicode,
830 IN PVOID TitleBuffer,
831 IN ULONG BufLength);
832 CSR_API(SrvSetConsoleTitle)
833 {
834 NTSTATUS Status;
835 PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
836 PCONSRV_CONSOLE Console;
837
838 if (!CsrValidateMessageBuffer(ApiMessage,
839 (PVOID)&TitleRequest->Title,
840 TitleRequest->Length,
841 sizeof(BYTE)))
842 {
843 return STATUS_INVALID_PARAMETER;
844 }
845
846 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
847 if (!NT_SUCCESS(Status))
848 {
849 DPRINT1("Can't get console\n");
850 return Status;
851 }
852
853 Status = ConDrvSetConsoleTitle(Console,
854 TitleRequest->Unicode,
855 TitleRequest->Title,
856 TitleRequest->Length);
857 if (NT_SUCCESS(Status)) TermChangeTitle(Console);
858
859 ConSrvReleaseConsole(Console, TRUE);
860 return Status;
861 }
862
863 NTSTATUS NTAPI
864 ConDrvGetConsoleCP(IN PCONSOLE Console,
865 OUT PUINT CodePage,
866 IN BOOLEAN OutputCP);
867 CSR_API(SrvGetConsoleCP)
868 {
869 NTSTATUS Status;
870 PCONSOLE_GETINPUTOUTPUTCP GetConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetConsoleCPRequest;
871 PCONSRV_CONSOLE Console;
872
873 DPRINT("SrvGetConsoleCP, getting %s Code Page\n",
874 GetConsoleCPRequest->OutputCP ? "Output" : "Input");
875
876 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
877 if (!NT_SUCCESS(Status)) return Status;
878
879 Status = ConDrvGetConsoleCP(Console,
880 &GetConsoleCPRequest->CodePage,
881 GetConsoleCPRequest->OutputCP);
882
883 ConSrvReleaseConsole(Console, TRUE);
884 return Status;
885 }
886
887 NTSTATUS NTAPI
888 ConDrvSetConsoleCP(IN PCONSOLE Console,
889 IN UINT CodePage,
890 IN BOOLEAN OutputCP);
891 CSR_API(SrvSetConsoleCP)
892 {
893 NTSTATUS Status = STATUS_INVALID_PARAMETER;
894 PCONSOLE_SETINPUTOUTPUTCP SetConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetConsoleCPRequest;
895 PCONSRV_CONSOLE Console;
896
897 DPRINT("SrvSetConsoleCP, setting %s Code Page\n",
898 SetConsoleCPRequest->OutputCP ? "Output" : "Input");
899
900 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
901 if (!NT_SUCCESS(Status)) return Status;
902
903 Status = ConDrvSetConsoleCP(Console,
904 SetConsoleCPRequest->CodePage,
905 SetConsoleCPRequest->OutputCP);
906
907 ConSrvReleaseConsole(Console, TRUE);
908 return Status;
909 }
910
911 CSR_API(SrvGetConsoleProcessList)
912 {
913 NTSTATUS Status;
914 PCONSOLE_GETPROCESSLIST GetProcessListRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetProcessListRequest;
915 PCONSRV_CONSOLE Console;
916
917 if (!CsrValidateMessageBuffer(ApiMessage,
918 (PVOID)&GetProcessListRequest->ProcessIdsList,
919 GetProcessListRequest->ProcessCount,
920 sizeof(DWORD)))
921 {
922 return STATUS_INVALID_PARAMETER;
923 }
924
925 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
926 if (!NT_SUCCESS(Status)) return Status;
927
928 Status = ConSrvGetConsoleProcessList(Console,
929 GetProcessListRequest->ProcessIdsList,
930 GetProcessListRequest->ProcessCount,
931 &GetProcessListRequest->ProcessCount);
932
933 ConSrvReleaseConsole(Console, TRUE);
934 return Status;
935 }
936
937 CSR_API(SrvGenerateConsoleCtrlEvent)
938 {
939 NTSTATUS Status;
940 PCONSOLE_GENERATECTRLEVENT GenerateCtrlEventRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GenerateCtrlEventRequest;
941 PCONSRV_CONSOLE Console;
942
943 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
944 if (!NT_SUCCESS(Status)) return Status;
945
946 Status = ConSrvConsoleProcessCtrlEvent(Console,
947 GenerateCtrlEventRequest->ProcessGroupId,
948 GenerateCtrlEventRequest->CtrlEvent);
949
950 ConSrvReleaseConsole(Console, TRUE);
951 return Status;
952 }
953
954 CSR_API(SrvConsoleNotifyLastClose)
955 {
956 NTSTATUS Status;
957 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
958 PCONSRV_CONSOLE Console;
959
960 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
961 if (!NT_SUCCESS(Status)) return Status;
962
963 /* Only one process is allowed to be registered for last close notification */
964 if (!Console->NotifyLastClose)
965 {
966 Console->NotifyLastClose = TRUE;
967 Console->NotifiedLastCloseProcess = ProcessData;
968 Status = STATUS_SUCCESS;
969 }
970 else
971 {
972 Status = STATUS_ACCESS_DENIED;
973 }
974
975 ConSrvReleaseConsole(Console, TRUE);
976 return Status;
977 }
978
979
980
981 CSR_API(SrvGetConsoleMouseInfo)
982 {
983 NTSTATUS Status;
984 PCONSOLE_GETMOUSEINFO GetMouseInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetMouseInfoRequest;
985 PCONSRV_CONSOLE Console;
986
987 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
988 if (!NT_SUCCESS(Status)) return Status;
989
990 /* Just retrieve the number of buttons of the mouse attached to this console */
991 GetMouseInfoRequest->NumButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
992
993 ConSrvReleaseConsole(Console, TRUE);
994 return STATUS_SUCCESS;
995 }
996
997 CSR_API(SrvSetConsoleKeyShortcuts)
998 {
999 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1000 return STATUS_NOT_IMPLEMENTED;
1001 }
1002
1003 CSR_API(SrvGetConsoleKeyboardLayoutName)
1004 {
1005 NTSTATUS Status;
1006 PCONSOLE_GETKBDLAYOUTNAME GetKbdLayoutNameRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetKbdLayoutNameRequest;
1007 PCONSRV_CONSOLE Console;
1008
1009 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1010 if (!NT_SUCCESS(Status)) return Status;
1011
1012 /* Retrieve the keyboard layout name of the system */
1013 if (GetKbdLayoutNameRequest->Ansi)
1014 GetKeyboardLayoutNameA((PCHAR)GetKbdLayoutNameRequest->LayoutBuffer);
1015 else
1016 GetKeyboardLayoutNameW((PWCHAR)GetKbdLayoutNameRequest->LayoutBuffer);
1017
1018 ConSrvReleaseConsole(Console, TRUE);
1019 return STATUS_SUCCESS;
1020 }
1021
1022 CSR_API(SrvGetConsoleCharType)
1023 {
1024 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1025 return STATUS_NOT_IMPLEMENTED;
1026 }
1027
1028 CSR_API(SrvSetConsoleLocalEUDC)
1029 {
1030 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1031 return STATUS_NOT_IMPLEMENTED;
1032 }
1033
1034 CSR_API(SrvSetConsoleCursorMode)
1035 {
1036 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1037 return STATUS_NOT_IMPLEMENTED;
1038 }
1039
1040 CSR_API(SrvGetConsoleCursorMode)
1041 {
1042 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1043 return STATUS_NOT_IMPLEMENTED;
1044 }
1045
1046 CSR_API(SrvGetConsoleNlsMode)
1047 {
1048 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1049 return STATUS_NOT_IMPLEMENTED;
1050 }
1051
1052 CSR_API(SrvSetConsoleNlsMode)
1053 {
1054 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1055 return STATUS_NOT_IMPLEMENTED;
1056 }
1057
1058 CSR_API(SrvGetConsoleLangId)
1059 {
1060 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1061 return STATUS_NOT_IMPLEMENTED;
1062 }
1063
1064 /* EOF */