Sync with trunk r63831.
[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 PCONSOLE* 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 PCONSOLE Console)
45 {
46 #define CONSOLE_HANDLES_INCREMENT 2 * 3
47
48 NTSTATUS Status = STATUS_SUCCESS;
49 ULONG i = 0;
50 PCONSOLE* 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(PCONSOLE));
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(PCONSOLE));
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 PCONSOLE 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 PCONSOLE 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 PCONSOLE* Console,
166 IN HANDLE ConsoleHandle,
167 IN CONSOLE_STATE ExpectedState,
168 IN BOOLEAN LockConsole)
169 {
170 BOOLEAN RetVal = FALSE;
171 PCONSOLE 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(PCONSOLE Console, UINT Flags)
214 {
215 Console->PauseFlags |= Flags;
216 ConDrvPause(Console);
217 }
218
219 VOID
220 ConioUnpause(PCONSOLE 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 PCONSOLE* Console,
243 IN BOOLEAN LockConsole)
244 {
245 NTSTATUS Status = STATUS_INVALID_HANDLE;
246 PCONSOLE 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 PCONSOLE 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 PCONSOLE* NewConsole,
323 IN OUT PCONSOLE_START_INFO ConsoleStartInfo,
324 IN ULONG ConsoleLeaderProcessId)
325 {
326 NTSTATUS Status;
327 HANDLE ConsoleHandle;
328 PCONSOLE 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 /* Attach the ConSrv terminal to the console */
433 Status = ConDrvRegisterTerminal(Console, &Terminal);
434 if (!NT_SUCCESS(Status))
435 {
436 DPRINT1("Failed to register terminal to the given console, Status = 0x%08lx\n", Status);
437 ConDrvDeleteConsole(Console);
438 ConSrvDeinitTerminal(&Terminal);
439 return Status;
440 }
441 DPRINT("Terminal registered\n");
442
443 /* All went right, so add the console to the list */
444 Status = InsertConsole(&ConsoleHandle, Console);
445
446 /* Return the newly created console to the caller and a success code too */
447 *NewConsoleHandle = ConsoleHandle;
448 *NewConsole = Console;
449 return STATUS_SUCCESS;
450 }
451
452 VOID NTAPI
453 ConSrvDeleteConsole(PCONSOLE Console)
454 {
455 DPRINT("ConSrvDeleteConsole\n");
456
457 // FIXME: Send a terminate message to all the processes owning this console
458
459 /* Remove the console from the list */
460 RemoveConsoleByPointer(Console);
461
462 /* Clean aliases and history */
463 IntDeleteAllAliases(Console);
464 HistoryDeleteBuffers(Console);
465
466 /* Now, call the driver. ConDrvDeregisterTerminal is called on-demand. */
467 ConDrvDeleteConsole(Console);
468 }
469
470
471
472
473
474
475 static NTSTATUS
476 ConSrvConsoleCtrlEventTimeout(IN ULONG CtrlEvent,
477 IN PCONSOLE_PROCESS_DATA ProcessData,
478 IN ULONG Timeout)
479 {
480 NTSTATUS Status = STATUS_SUCCESS;
481
482 DPRINT("ConSrvConsoleCtrlEventTimeout Parent ProcessId = %x\n", ProcessData->Process->ClientId.UniqueProcess);
483
484 if (ProcessData->CtrlDispatcher)
485 {
486 _SEH2_TRY
487 {
488 HANDLE Thread = NULL;
489
490 _SEH2_TRY
491 {
492 Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0,
493 ProcessData->CtrlDispatcher,
494 UlongToPtr(CtrlEvent), 0, NULL);
495 if (NULL == Thread)
496 {
497 Status = RtlGetLastNtStatus();
498 DPRINT1("Failed thread creation, Status = 0x%08lx\n", Status);
499 }
500 else
501 {
502 DPRINT("ProcessData->CtrlDispatcher remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n", ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process);
503 WaitForSingleObject(Thread, Timeout);
504 }
505 }
506 _SEH2_FINALLY
507 {
508 CloseHandle(Thread);
509 }
510 _SEH2_END;
511 }
512 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
513 {
514 Status = _SEH2_GetExceptionCode();
515 DPRINT1("ConSrvConsoleCtrlEventTimeout - Caught an exception, Status = 0x%08lx\n", Status);
516 }
517 _SEH2_END;
518 }
519
520 return Status;
521 }
522
523 NTSTATUS
524 ConSrvConsoleCtrlEvent(IN ULONG CtrlEvent,
525 IN PCONSOLE_PROCESS_DATA ProcessData)
526 {
527 return ConSrvConsoleCtrlEventTimeout(CtrlEvent, ProcessData, 0);
528 }
529
530 PCONSOLE_PROCESS_DATA NTAPI
531 ConSrvGetConsoleLeaderProcess(IN PCONSOLE Console)
532 {
533 if (Console == NULL) return NULL;
534
535 return CONTAINING_RECORD(Console->ProcessList.Blink,
536 CONSOLE_PROCESS_DATA,
537 ConsoleLink);
538 }
539
540 NTSTATUS NTAPI
541 ConSrvGetConsoleProcessList(IN PCONSOLE Console,
542 IN OUT PULONG ProcessIdsList,
543 IN ULONG MaxIdListItems,
544 OUT PULONG ProcessIdsTotal)
545 {
546 PCONSOLE_PROCESS_DATA current;
547 PLIST_ENTRY current_entry;
548
549 if (Console == NULL || ProcessIdsList == NULL || ProcessIdsTotal == NULL)
550 return STATUS_INVALID_PARAMETER;
551
552 *ProcessIdsTotal = 0;
553
554 for (current_entry = Console->ProcessList.Flink;
555 current_entry != &Console->ProcessList;
556 current_entry = current_entry->Flink)
557 {
558 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
559 if (++(*ProcessIdsTotal) <= MaxIdListItems)
560 {
561 *ProcessIdsList++ = HandleToUlong(current->Process->ClientId.UniqueProcess);
562 }
563 }
564
565 return STATUS_SUCCESS;
566 }
567
568 // ConSrvGenerateConsoleCtrlEvent
569 NTSTATUS NTAPI
570 ConSrvConsoleProcessCtrlEvent(IN PCONSOLE Console,
571 IN ULONG ProcessGroupId,
572 IN ULONG CtrlEvent)
573 {
574 NTSTATUS Status = STATUS_SUCCESS;
575 PLIST_ENTRY current_entry;
576 PCONSOLE_PROCESS_DATA current;
577
578 /* If the console is already being destroyed, just return */
579 if (!ConDrvValidateConsoleState(Console, CONSOLE_RUNNING))
580 return STATUS_UNSUCCESSFUL;
581
582 /*
583 * Loop through the process list, from the most recent process
584 * (the active one) to the oldest one (the first created, i.e.
585 * the console leader process), and for each, send an event
586 * (new processes are inserted at the head of the console process list).
587 */
588 current_entry = Console->ProcessList.Flink;
589 while (current_entry != &Console->ProcessList)
590 {
591 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
592 current_entry = current_entry->Flink;
593
594 /*
595 * Only processes belonging to the same process group are signaled.
596 * If the process group ID is zero, then all the processes are signaled.
597 */
598 if (ProcessGroupId == 0 || current->Process->ProcessGroupId == ProcessGroupId)
599 {
600 Status = ConSrvConsoleCtrlEvent(CtrlEvent, current);
601 }
602 }
603
604 return Status;
605 }
606
607
608
609
610
611 /* PUBLIC SERVER APIS *********************************************************/
612
613 CSR_API(SrvAllocConsole)
614 {
615 NTSTATUS Status = STATUS_SUCCESS;
616 PCONSOLE_ALLOCCONSOLE AllocConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AllocConsoleRequest;
617 PCSR_PROCESS CsrProcess = CsrGetClientThread()->Process;
618 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrProcess);
619
620 if (ProcessData->ConsoleHandle != NULL)
621 {
622 DPRINT1("Process already has a console\n");
623 return STATUS_ACCESS_DENIED;
624 }
625
626 if (!CsrValidateMessageBuffer(ApiMessage,
627 (PVOID*)&AllocConsoleRequest->ConsoleStartInfo,
628 1,
629 sizeof(CONSOLE_START_INFO)))
630 {
631 return STATUS_INVALID_PARAMETER;
632 }
633
634 /* Initialize a new Console owned by the Console Leader Process */
635 Status = ConSrvAllocateConsole(ProcessData,
636 &AllocConsoleRequest->InputHandle,
637 &AllocConsoleRequest->OutputHandle,
638 &AllocConsoleRequest->ErrorHandle,
639 AllocConsoleRequest->ConsoleStartInfo);
640 if (!NT_SUCCESS(Status))
641 {
642 DPRINT1("Console allocation failed\n");
643 return Status;
644 }
645
646 /* Return the console handle and the input wait handle to the caller */
647 AllocConsoleRequest->ConsoleHandle = ProcessData->ConsoleHandle;
648 AllocConsoleRequest->InputWaitHandle = ProcessData->InputWaitHandle;
649
650 /* Set the Property-Dialog and Control-Dispatcher handlers */
651 ProcessData->PropDispatcher = AllocConsoleRequest->PropDispatcher;
652 ProcessData->CtrlDispatcher = AllocConsoleRequest->CtrlDispatcher;
653
654 return STATUS_SUCCESS;
655 }
656
657 CSR_API(SrvAttachConsole)
658 {
659 NTSTATUS Status = STATUS_SUCCESS;
660 PCONSOLE_ATTACHCONSOLE AttachConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AttachConsoleRequest;
661 PCSR_PROCESS SourceProcess = NULL; // The parent process.
662 PCSR_PROCESS TargetProcess = CsrGetClientThread()->Process; // Ourselves.
663 HANDLE ProcessId = ULongToHandle(AttachConsoleRequest->ProcessId);
664 PCONSOLE_PROCESS_DATA SourceProcessData, TargetProcessData;
665
666 TargetProcessData = ConsoleGetPerProcessData(TargetProcess);
667
668 if (TargetProcessData->ConsoleHandle != NULL)
669 {
670 DPRINT1("Process already has a console\n");
671 return STATUS_ACCESS_DENIED;
672 }
673
674 /* Check whether we try to attach to the parent's console */
675 if (ProcessId == ULongToHandle(ATTACH_PARENT_PROCESS))
676 {
677 PROCESS_BASIC_INFORMATION ProcessInfo;
678 ULONG Length = sizeof(ProcessInfo);
679
680 /* Get the real parent's ID */
681
682 Status = NtQueryInformationProcess(TargetProcess->ProcessHandle,
683 ProcessBasicInformation,
684 &ProcessInfo,
685 Length, &Length);
686 if (!NT_SUCCESS(Status))
687 {
688 DPRINT1("SrvAttachConsole - Cannot retrieve basic process info, Status = %lu\n", Status);
689 return Status;
690 }
691
692 ProcessId = ULongToHandle(ProcessInfo.InheritedFromUniqueProcessId);
693 }
694
695 /* Lock the source process via its PID */
696 Status = CsrLockProcessByClientId(ProcessId, &SourceProcess);
697 if (!NT_SUCCESS(Status)) return Status;
698
699 SourceProcessData = ConsoleGetPerProcessData(SourceProcess);
700
701 if (SourceProcessData->ConsoleHandle == NULL)
702 {
703 Status = STATUS_INVALID_HANDLE;
704 goto Quit;
705 }
706
707 /*
708 * Inherit the console from the parent,
709 * if any, otherwise return an error.
710 */
711 Status = ConSrvInheritConsole(TargetProcessData,
712 SourceProcessData->ConsoleHandle,
713 TRUE,
714 &AttachConsoleRequest->InputHandle,
715 &AttachConsoleRequest->OutputHandle,
716 &AttachConsoleRequest->ErrorHandle);
717 if (!NT_SUCCESS(Status))
718 {
719 DPRINT1("Console inheritance failed\n");
720 goto Quit;
721 }
722
723 /* Return the console handle and the input wait handle to the caller */
724 AttachConsoleRequest->ConsoleHandle = TargetProcessData->ConsoleHandle;
725 AttachConsoleRequest->InputWaitHandle = TargetProcessData->InputWaitHandle;
726
727 /* Set the Property-Dialog and Control-Dispatcher handlers */
728 TargetProcessData->PropDispatcher = AttachConsoleRequest->PropDispatcher;
729 TargetProcessData->CtrlDispatcher = AttachConsoleRequest->CtrlDispatcher;
730
731 Status = STATUS_SUCCESS;
732
733 Quit:
734 /* Unlock the "source" process and exit */
735 CsrUnlockProcess(SourceProcess);
736 return Status;
737 }
738
739 CSR_API(SrvFreeConsole)
740 {
741 ConSrvRemoveConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process));
742 return STATUS_SUCCESS;
743 }
744
745 NTSTATUS NTAPI
746 ConDrvGetConsoleMode(IN PCONSOLE Console,
747 IN PCONSOLE_IO_OBJECT Object,
748 OUT PULONG ConsoleMode);
749 CSR_API(SrvGetConsoleMode)
750 {
751 NTSTATUS Status;
752 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
753 PCONSOLE_IO_OBJECT Object;
754
755 Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
756 ConsoleModeRequest->Handle,
757 &Object, NULL, GENERIC_READ, TRUE, 0);
758 if (!NT_SUCCESS(Status)) return Status;
759
760 Status = ConDrvGetConsoleMode(Object->Console, Object,
761 &ConsoleModeRequest->Mode);
762
763 ConSrvReleaseObject(Object, TRUE);
764 return Status;
765 }
766
767 NTSTATUS NTAPI
768 ConDrvSetConsoleMode(IN PCONSOLE Console,
769 IN PCONSOLE_IO_OBJECT Object,
770 IN ULONG ConsoleMode);
771 CSR_API(SrvSetConsoleMode)
772 {
773 NTSTATUS Status;
774 PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
775 PCONSOLE_IO_OBJECT Object;
776
777 Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
778 ConsoleModeRequest->Handle,
779 &Object, NULL, GENERIC_WRITE, TRUE, 0);
780 if (!NT_SUCCESS(Status)) return Status;
781
782 Status = ConDrvSetConsoleMode(Object->Console, Object,
783 ConsoleModeRequest->Mode);
784
785 ConSrvReleaseObject(Object, TRUE);
786 return Status;
787 }
788
789 NTSTATUS NTAPI
790 ConDrvGetConsoleTitle(IN PCONSOLE Console,
791 IN BOOLEAN Unicode,
792 IN OUT PVOID TitleBuffer,
793 IN OUT PULONG BufLength);
794 CSR_API(SrvGetConsoleTitle)
795 {
796 NTSTATUS Status;
797 PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
798 PCONSOLE Console;
799
800 if (!CsrValidateMessageBuffer(ApiMessage,
801 (PVOID)&TitleRequest->Title,
802 TitleRequest->Length,
803 sizeof(BYTE)))
804 {
805 return STATUS_INVALID_PARAMETER;
806 }
807
808 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
809 if (!NT_SUCCESS(Status))
810 {
811 DPRINT1("Can't get console\n");
812 return Status;
813 }
814
815 Status = ConDrvGetConsoleTitle(Console,
816 TitleRequest->Unicode,
817 TitleRequest->Title,
818 &TitleRequest->Length);
819
820 ConSrvReleaseConsole(Console, TRUE);
821 return Status;
822 }
823
824 NTSTATUS NTAPI
825 ConDrvSetConsoleTitle(IN PCONSOLE Console,
826 IN BOOLEAN Unicode,
827 IN PVOID TitleBuffer,
828 IN ULONG BufLength);
829 CSR_API(SrvSetConsoleTitle)
830 {
831 NTSTATUS Status;
832 PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
833 PCONSOLE Console;
834
835 if (!CsrValidateMessageBuffer(ApiMessage,
836 (PVOID)&TitleRequest->Title,
837 TitleRequest->Length,
838 sizeof(BYTE)))
839 {
840 return STATUS_INVALID_PARAMETER;
841 }
842
843 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
844 if (!NT_SUCCESS(Status))
845 {
846 DPRINT1("Can't get console\n");
847 return Status;
848 }
849
850 Status = ConDrvSetConsoleTitle(Console,
851 TitleRequest->Unicode,
852 TitleRequest->Title,
853 TitleRequest->Length);
854 if (NT_SUCCESS(Status)) TermChangeTitle(Console);
855
856 ConSrvReleaseConsole(Console, TRUE);
857 return Status;
858 }
859
860 NTSTATUS NTAPI
861 ConDrvGetConsoleCP(IN PCONSOLE Console,
862 OUT PUINT CodePage,
863 IN BOOLEAN OutputCP);
864 CSR_API(SrvGetConsoleCP)
865 {
866 NTSTATUS Status;
867 PCONSOLE_GETINPUTOUTPUTCP GetConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetConsoleCPRequest;
868 PCONSOLE Console;
869
870 DPRINT("SrvGetConsoleCP, getting %s Code Page\n",
871 GetConsoleCPRequest->OutputCP ? "Output" : "Input");
872
873 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
874 if (!NT_SUCCESS(Status)) return Status;
875
876 Status = ConDrvGetConsoleCP(Console,
877 &GetConsoleCPRequest->CodePage,
878 GetConsoleCPRequest->OutputCP);
879
880 ConSrvReleaseConsole(Console, TRUE);
881 return Status;
882 }
883
884 NTSTATUS NTAPI
885 ConDrvSetConsoleCP(IN PCONSOLE Console,
886 IN UINT CodePage,
887 IN BOOLEAN OutputCP);
888 CSR_API(SrvSetConsoleCP)
889 {
890 NTSTATUS Status = STATUS_INVALID_PARAMETER;
891 PCONSOLE_SETINPUTOUTPUTCP SetConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetConsoleCPRequest;
892 PCONSOLE Console;
893
894 DPRINT("SrvSetConsoleCP, setting %s Code Page\n",
895 SetConsoleCPRequest->OutputCP ? "Output" : "Input");
896
897 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
898 if (!NT_SUCCESS(Status)) return Status;
899
900 Status = ConDrvSetConsoleCP(Console,
901 SetConsoleCPRequest->CodePage,
902 SetConsoleCPRequest->OutputCP);
903
904 ConSrvReleaseConsole(Console, TRUE);
905 return Status;
906 }
907
908 CSR_API(SrvGetConsoleProcessList)
909 {
910 NTSTATUS Status;
911 PCONSOLE_GETPROCESSLIST GetProcessListRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetProcessListRequest;
912 PCONSOLE Console;
913
914 if (!CsrValidateMessageBuffer(ApiMessage,
915 (PVOID)&GetProcessListRequest->ProcessIdsList,
916 GetProcessListRequest->ProcessCount,
917 sizeof(DWORD)))
918 {
919 return STATUS_INVALID_PARAMETER;
920 }
921
922 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
923 if (!NT_SUCCESS(Status)) return Status;
924
925 Status = ConSrvGetConsoleProcessList(Console,
926 GetProcessListRequest->ProcessIdsList,
927 GetProcessListRequest->ProcessCount,
928 &GetProcessListRequest->ProcessCount);
929
930 ConSrvReleaseConsole(Console, TRUE);
931 return Status;
932 }
933
934 CSR_API(SrvGenerateConsoleCtrlEvent)
935 {
936 NTSTATUS Status;
937 PCONSOLE_GENERATECTRLEVENT GenerateCtrlEventRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GenerateCtrlEventRequest;
938 PCONSOLE Console;
939
940 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
941 if (!NT_SUCCESS(Status)) return Status;
942
943 Status = ConSrvConsoleProcessCtrlEvent(Console,
944 GenerateCtrlEventRequest->ProcessGroupId,
945 GenerateCtrlEventRequest->CtrlEvent);
946
947 ConSrvReleaseConsole(Console, TRUE);
948 return Status;
949 }
950
951 CSR_API(SrvConsoleNotifyLastClose)
952 {
953 NTSTATUS Status;
954 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
955 PCONSOLE Console;
956
957 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
958 if (!NT_SUCCESS(Status)) return Status;
959
960 /* Only one process is allowed to be registered for last close notification */
961 if (!Console->NotifyLastClose)
962 {
963 Console->NotifyLastClose = TRUE;
964 Console->NotifiedLastCloseProcess = ProcessData;
965 Status = STATUS_SUCCESS;
966 }
967 else
968 {
969 Status = STATUS_ACCESS_DENIED;
970 }
971
972 ConSrvReleaseConsole(Console, TRUE);
973 return Status;
974 }
975
976
977
978 CSR_API(SrvGetConsoleMouseInfo)
979 {
980 NTSTATUS Status;
981 PCONSOLE_GETMOUSEINFO GetMouseInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetMouseInfoRequest;
982 PCONSOLE Console;
983
984 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
985 if (!NT_SUCCESS(Status)) return Status;
986
987 /* Just retrieve the number of buttons of the mouse attached to this console */
988 GetMouseInfoRequest->NumButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
989
990 ConSrvReleaseConsole(Console, TRUE);
991 return STATUS_SUCCESS;
992 }
993
994 CSR_API(SrvSetConsoleKeyShortcuts)
995 {
996 DPRINT1("%s not yet implemented\n", __FUNCTION__);
997 return STATUS_NOT_IMPLEMENTED;
998 }
999
1000 CSR_API(SrvGetConsoleKeyboardLayoutName)
1001 {
1002 NTSTATUS Status;
1003 PCONSOLE_GETKBDLAYOUTNAME GetKbdLayoutNameRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetKbdLayoutNameRequest;
1004 PCONSOLE Console;
1005
1006 Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1007 if (!NT_SUCCESS(Status)) return Status;
1008
1009 /* Retrieve the keyboard layout name of the system */
1010 if (GetKbdLayoutNameRequest->Ansi)
1011 GetKeyboardLayoutNameA((PCHAR)GetKbdLayoutNameRequest->LayoutBuffer);
1012 else
1013 GetKeyboardLayoutNameW((PWCHAR)GetKbdLayoutNameRequest->LayoutBuffer);
1014
1015 ConSrvReleaseConsole(Console, TRUE);
1016 return STATUS_SUCCESS;
1017 }
1018
1019 CSR_API(SrvGetConsoleCharType)
1020 {
1021 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1022 return STATUS_NOT_IMPLEMENTED;
1023 }
1024
1025 CSR_API(SrvSetConsoleLocalEUDC)
1026 {
1027 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1028 return STATUS_NOT_IMPLEMENTED;
1029 }
1030
1031 CSR_API(SrvSetConsoleCursorMode)
1032 {
1033 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1034 return STATUS_NOT_IMPLEMENTED;
1035 }
1036
1037 CSR_API(SrvGetConsoleCursorMode)
1038 {
1039 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1040 return STATUS_NOT_IMPLEMENTED;
1041 }
1042
1043 CSR_API(SrvGetConsoleNlsMode)
1044 {
1045 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1046 return STATUS_NOT_IMPLEMENTED;
1047 }
1048
1049 CSR_API(SrvSetConsoleNlsMode)
1050 {
1051 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1052 return STATUS_NOT_IMPLEMENTED;
1053 }
1054
1055 CSR_API(SrvGetConsoleLangId)
1056 {
1057 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1058 return STATUS_NOT_IMPLEMENTED;
1059 }
1060
1061 /* EOF */