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