9a3285fe6c60d64c42695d87d40a6d542433d61e
[reactos.git] / reactos / win32ss / user / winsrv / consrv / handle.c
1 /*
2 * LICENSE: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/winsrv/consrv/handle.c
5 * PURPOSE: Console I/O Handles functions
6 * PROGRAMMERS: David Welch
7 * Jeffrey Morlan
8 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
9 */
10
11 /* INCLUDES *******************************************************************/
12
13 #include "consrv.h"
14
15 #include <win/console.h>
16
17 #define NDEBUG
18 #include <debug.h>
19
20 /* GLOBALS ********************************************************************/
21
22 /* Console handle */
23 typedef struct _CONSOLE_IO_HANDLE
24 {
25 PCONSOLE_IO_OBJECT Object; /* The object on which the handle points to */
26 ULONG Access;
27 ULONG ShareMode;
28 BOOLEAN Inheritable;
29 } CONSOLE_IO_HANDLE, *PCONSOLE_IO_HANDLE;
30
31
32 /* PRIVATE FUNCTIONS **********************************************************/
33
34 static LONG
35 AdjustHandleCounts(IN PCONSOLE_IO_HANDLE Handle,
36 IN LONG Change)
37 {
38 PCONSOLE_IO_OBJECT Object = Handle->Object;
39
40 DPRINT("AdjustHandleCounts(0x%p, %d), Object = 0x%p\n",
41 Handle, Change, Object);
42 DPRINT("\tAdjustHandleCounts(0x%p, %d), Object = 0x%p, Object->ReferenceCount = %d, Object->Type = %lu\n",
43 Handle, Change, Object, Object->ReferenceCount, Object->Type);
44
45 if (Handle->Access & GENERIC_READ) Object->AccessRead += Change;
46 if (Handle->Access & GENERIC_WRITE) Object->AccessWrite += Change;
47 if (!(Handle->ShareMode & FILE_SHARE_READ)) Object->ExclusiveRead += Change;
48 if (!(Handle->ShareMode & FILE_SHARE_WRITE)) Object->ExclusiveWrite += Change;
49
50 Object->ReferenceCount += Change;
51
52 return Object->ReferenceCount;
53 }
54
55 static VOID
56 ConSrvCloseHandle(IN PCONSOLE_IO_HANDLE Handle)
57 {
58 PCONSOLE_IO_OBJECT Object = Handle->Object;
59 if (Object != NULL)
60 {
61 /*
62 * If this is a input handle, notify and dereference
63 * all the waits related to this handle.
64 */
65 if (Object->Type == INPUT_BUFFER)
66 {
67 // PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object;
68 PCONSOLE Console = Object->Console;
69
70 /*
71 * Wake up all the writing waiters related to this handle for this
72 * input buffer, if any, then dereference them and purge them all
73 * from the list.
74 * To select them amongst all the waiters for this input buffer,
75 * pass the handle pointer to the waiters, then they will check
76 * whether or not they are related to this handle and if so, they
77 * return.
78 */
79 CsrNotifyWait(&Console->ReadWaitQueue,
80 TRUE,
81 NULL,
82 (PVOID)Handle);
83 if (!IsListEmpty(&Console->ReadWaitQueue))
84 {
85 CsrDereferenceWait(&Console->ReadWaitQueue);
86 }
87 }
88
89 /* If the last handle to a screen buffer is closed, delete it... */
90 if (AdjustHandleCounts(Handle, -1) == 0)
91 {
92 if (Object->Type == TEXTMODE_BUFFER || Object->Type == GRAPHICS_BUFFER)
93 {
94 PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object;
95 /* ...unless it's the only buffer left. Windows allows deletion
96 * even of the last buffer, but having to deal with a lack of
97 * any active buffer might be error-prone. */
98 if (Buffer->ListEntry.Flink != Buffer->ListEntry.Blink)
99 ConDrvDeleteScreenBuffer(Buffer);
100 }
101 else if (Object->Type == INPUT_BUFFER)
102 {
103 DPRINT("Closing the input buffer\n");
104 }
105 else
106 {
107 DPRINT1("Invalid object type %d\n", Object->Type);
108 }
109 }
110
111 /* Invalidate (zero-out) this handle entry */
112 // Handle->Object = NULL;
113 // RtlZeroMemory(Handle, sizeof(*Handle));
114 }
115 RtlZeroMemory(Handle, sizeof(*Handle)); // Be sure the whole entry is invalidated.
116 }
117
118
119
120
121
122
123 /* Forward declaration, used in ConSrvInitHandlesTable */
124 static VOID ConSrvFreeHandlesTable(PCONSOLE_PROCESS_DATA ProcessData);
125
126 static NTSTATUS
127 ConSrvInitHandlesTable(IN OUT PCONSOLE_PROCESS_DATA ProcessData,
128 IN PCONSOLE Console,
129 OUT PHANDLE pInputHandle,
130 OUT PHANDLE pOutputHandle,
131 OUT PHANDLE pErrorHandle)
132 {
133 NTSTATUS Status;
134 HANDLE InputHandle = INVALID_HANDLE_VALUE,
135 OutputHandle = INVALID_HANDLE_VALUE,
136 ErrorHandle = INVALID_HANDLE_VALUE;
137
138 /*
139 * Initialize the handles table. Use temporary variables to store
140 * the handles values in such a way that, if we fail, we don't
141 * return to the caller invalid handle values.
142 *
143 * Insert the IO handles.
144 */
145
146 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
147
148 /* Insert the Input handle */
149 Status = ConSrvInsertObject(ProcessData,
150 &InputHandle,
151 &Console->InputBuffer.Header,
152 GENERIC_READ | GENERIC_WRITE,
153 TRUE,
154 FILE_SHARE_READ | FILE_SHARE_WRITE);
155 if (!NT_SUCCESS(Status))
156 {
157 DPRINT1("Failed to insert the input handle\n");
158 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
159 ConSrvFreeHandlesTable(ProcessData);
160 return Status;
161 }
162
163 /* Insert the Output handle */
164 Status = ConSrvInsertObject(ProcessData,
165 &OutputHandle,
166 &Console->ActiveBuffer->Header,
167 GENERIC_READ | GENERIC_WRITE,
168 TRUE,
169 FILE_SHARE_READ | FILE_SHARE_WRITE);
170 if (!NT_SUCCESS(Status))
171 {
172 DPRINT1("Failed to insert the output handle\n");
173 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
174 ConSrvFreeHandlesTable(ProcessData);
175 return Status;
176 }
177
178 /* Insert the Error handle */
179 Status = ConSrvInsertObject(ProcessData,
180 &ErrorHandle,
181 &Console->ActiveBuffer->Header,
182 GENERIC_READ | GENERIC_WRITE,
183 TRUE,
184 FILE_SHARE_READ | FILE_SHARE_WRITE);
185 if (!NT_SUCCESS(Status))
186 {
187 DPRINT1("Failed to insert the error handle\n");
188 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
189 ConSrvFreeHandlesTable(ProcessData);
190 return Status;
191 }
192
193 /* Return the newly created handles */
194 *pInputHandle = InputHandle;
195 *pOutputHandle = OutputHandle;
196 *pErrorHandle = ErrorHandle;
197
198 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
199 return STATUS_SUCCESS;
200 }
201
202 NTSTATUS
203 ConSrvInheritHandlesTable(IN PCONSOLE_PROCESS_DATA SourceProcessData,
204 IN PCONSOLE_PROCESS_DATA TargetProcessData)
205 {
206 NTSTATUS Status = STATUS_SUCCESS;
207 ULONG i, j;
208
209 RtlEnterCriticalSection(&SourceProcessData->HandleTableLock);
210
211 /* Inherit a handles table only if there is no already */
212 if (TargetProcessData->HandleTable != NULL /* || TargetProcessData->HandleTableSize != 0 */)
213 {
214 Status = STATUS_UNSUCCESSFUL;
215 goto Quit;
216 }
217
218 /* Allocate a new handle table for the child process */
219 TargetProcessData->HandleTable = ConsoleAllocHeap(HEAP_ZERO_MEMORY,
220 SourceProcessData->HandleTableSize
221 * sizeof(CONSOLE_IO_HANDLE));
222 if (TargetProcessData->HandleTable == NULL)
223 {
224 Status = STATUS_NO_MEMORY;
225 goto Quit;
226 }
227
228 TargetProcessData->HandleTableSize = SourceProcessData->HandleTableSize;
229
230 /*
231 * Parse the parent process' handles table and, for each handle,
232 * do a copy of it and reference it, if the handle is inheritable.
233 */
234 for (i = 0, j = 0; i < SourceProcessData->HandleTableSize; i++)
235 {
236 if (SourceProcessData->HandleTable[i].Object != NULL &&
237 SourceProcessData->HandleTable[i].Inheritable)
238 {
239 /*
240 * Copy the handle data and increment the reference count of the
241 * pointed object (via the call to ConSrvCreateHandleEntry == AdjustHandleCounts).
242 */
243 TargetProcessData->HandleTable[j] = SourceProcessData->HandleTable[i];
244 AdjustHandleCounts(&TargetProcessData->HandleTable[j], +1);
245 ++j;
246 }
247 }
248
249 Quit:
250 RtlLeaveCriticalSection(&SourceProcessData->HandleTableLock);
251 return Status;
252 }
253
254 static VOID
255 ConSrvFreeHandlesTable(IN PCONSOLE_PROCESS_DATA ProcessData)
256 {
257 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
258
259 if (ProcessData->HandleTable != NULL)
260 {
261 ULONG i;
262
263 /*
264 * ProcessData->ConsoleHandle is NULL (and the assertion fails) when
265 * ConSrvFreeHandlesTable is called in ConSrvConnect during the
266 * allocation of a new console.
267 */
268 // ASSERT(ProcessData->ConsoleHandle);
269 if (ProcessData->ConsoleHandle != NULL)
270 {
271 /* Close all the console handles */
272 for (i = 0; i < ProcessData->HandleTableSize; i++)
273 {
274 ConSrvCloseHandle(&ProcessData->HandleTable[i]);
275 }
276 }
277 /* Free the handles table memory */
278 ConsoleFreeHeap(ProcessData->HandleTable);
279 ProcessData->HandleTable = NULL;
280 }
281
282 ProcessData->HandleTableSize = 0;
283
284 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
285 }
286
287
288
289
290
291
292 // ConSrvCreateObject
293 VOID
294 ConSrvInitObject(IN OUT PCONSOLE_IO_OBJECT Object,
295 IN CONSOLE_IO_OBJECT_TYPE Type,
296 IN PCONSOLE Console)
297 {
298 ASSERT(Object);
299 // if (!Object) return;
300
301 Object->Type = Type;
302 Object->Console = Console;
303 Object->ReferenceCount = 0;
304
305 Object->AccessRead = Object->AccessWrite = 0;
306 Object->ExclusiveRead = Object->ExclusiveWrite = 0;
307 }
308
309 NTSTATUS
310 ConSrvInsertObject(IN PCONSOLE_PROCESS_DATA ProcessData,
311 OUT PHANDLE Handle,
312 IN PCONSOLE_IO_OBJECT Object,
313 IN ULONG Access,
314 IN BOOLEAN Inheritable,
315 IN ULONG ShareMode)
316 {
317 #define IO_HANDLES_INCREMENT 2 * 3
318
319 ULONG i = 0;
320 PCONSOLE_IO_HANDLE Block;
321
322 // NOTE: Commented out because calling code always lock HandleTableLock before.
323 // RtlEnterCriticalSection(&ProcessData->HandleTableLock);
324
325 ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
326 (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
327
328 if (ProcessData->HandleTable)
329 {
330 for (i = 0; i < ProcessData->HandleTableSize; i++)
331 {
332 if (ProcessData->HandleTable[i].Object == NULL)
333 break;
334 }
335 }
336
337 if (i >= ProcessData->HandleTableSize)
338 {
339 /* Allocate a new handles table */
340 Block = ConsoleAllocHeap(HEAP_ZERO_MEMORY,
341 (ProcessData->HandleTableSize +
342 IO_HANDLES_INCREMENT) * sizeof(CONSOLE_IO_HANDLE));
343 if (Block == NULL)
344 {
345 // RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
346 return STATUS_UNSUCCESSFUL;
347 }
348
349 /* If we previously had a handles table, free it and use the new one */
350 if (ProcessData->HandleTable)
351 {
352 /* Copy the handles from the old table to the new one */
353 RtlCopyMemory(Block,
354 ProcessData->HandleTable,
355 ProcessData->HandleTableSize * sizeof(CONSOLE_IO_HANDLE));
356 ConsoleFreeHeap(ProcessData->HandleTable);
357 }
358 ProcessData->HandleTable = Block;
359 ProcessData->HandleTableSize += IO_HANDLES_INCREMENT;
360 }
361
362 ProcessData->HandleTable[i].Object = Object;
363 ProcessData->HandleTable[i].Access = Access;
364 ProcessData->HandleTable[i].Inheritable = Inheritable;
365 ProcessData->HandleTable[i].ShareMode = ShareMode;
366 AdjustHandleCounts(&ProcessData->HandleTable[i], +1);
367 *Handle = ULongToHandle((i << 2) | 0x3);
368
369 // RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
370
371 return STATUS_SUCCESS;
372 }
373
374 NTSTATUS
375 ConSrvRemoveObject(IN PCONSOLE_PROCESS_DATA ProcessData,
376 IN HANDLE Handle)
377 {
378 ULONG Index = HandleToULong(Handle) >> 2;
379
380 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
381
382 ASSERT(ProcessData->HandleTable);
383 // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
384 // (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
385
386 if (Index >= ProcessData->HandleTableSize ||
387 ProcessData->HandleTable[Index].Object == NULL)
388 {
389 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
390 return STATUS_INVALID_HANDLE;
391 }
392
393 ASSERT(ProcessData->ConsoleHandle);
394 ConSrvCloseHandle(&ProcessData->HandleTable[Index]);
395
396 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
397 return STATUS_SUCCESS;
398 }
399
400 NTSTATUS
401 ConSrvGetObject(IN PCONSOLE_PROCESS_DATA ProcessData,
402 IN HANDLE Handle,
403 OUT PCONSOLE_IO_OBJECT* Object,
404 OUT PVOID* Entry OPTIONAL,
405 IN ULONG Access,
406 IN BOOLEAN LockConsole,
407 IN CONSOLE_IO_OBJECT_TYPE Type)
408 {
409 // NTSTATUS Status;
410 ULONG Index = HandleToULong(Handle) >> 2;
411 PCONSOLE_IO_HANDLE HandleEntry = NULL;
412 PCONSOLE_IO_OBJECT ObjectEntry = NULL;
413 // PCONSOLE ObjectConsole;
414
415 ASSERT(Object);
416 if (Entry) *Entry = NULL;
417
418 DPRINT("ConSrvGetObject -- Object: 0x%x, Handle: 0x%x\n", Object, Handle);
419
420 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
421
422 if ( IsConsoleHandle(Handle) &&
423 Index < ProcessData->HandleTableSize )
424 {
425 HandleEntry = &ProcessData->HandleTable[Index];
426 ObjectEntry = HandleEntry->Object;
427 }
428
429 if ( HandleEntry == NULL ||
430 ObjectEntry == NULL ||
431 (HandleEntry->Access & Access) == 0 ||
432 /*(Type != 0 && ObjectEntry->Type != Type)*/
433 (Type != 0 && (ObjectEntry->Type & Type) == 0) )
434 {
435 DPRINT("ConSrvGetObject -- Invalid handle 0x%x of type %lu with access %lu ; retrieved object 0x%x (handle 0x%x) of type %lu with access %lu\n",
436 Handle, Type, Access, ObjectEntry, HandleEntry, (ObjectEntry ? ObjectEntry->Type : 0), (HandleEntry ? HandleEntry->Access : 0));
437
438 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
439 return STATUS_INVALID_HANDLE;
440 }
441
442 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
443
444 // Status = ConSrvGetConsole(ProcessData, &ObjectConsole, LockConsole);
445 // if (NT_SUCCESS(Status))
446 if (ConDrvValidateConsoleUnsafe(ObjectEntry->Console, CONSOLE_RUNNING, LockConsole))
447 {
448 _InterlockedIncrement(&ObjectEntry->Console->ReferenceCount);
449
450 /* Return the objects to the caller */
451 *Object = ObjectEntry;
452 if (Entry) *Entry = HandleEntry;
453
454 // RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
455 return STATUS_SUCCESS;
456 }
457 else
458 {
459 // RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
460 return STATUS_INVALID_HANDLE;
461 }
462 }
463
464 VOID
465 ConSrvReleaseObject(IN PCONSOLE_IO_OBJECT Object,
466 IN BOOLEAN IsConsoleLocked)
467 {
468 ConSrvReleaseConsole(Object->Console, IsConsoleLocked);
469 }
470
471
472
473 NTSTATUS
474 ConSrvAllocateConsole(PCONSOLE_PROCESS_DATA ProcessData,
475 PHANDLE pInputHandle,
476 PHANDLE pOutputHandle,
477 PHANDLE pErrorHandle,
478 PCONSOLE_INIT_INFO ConsoleInitInfo)
479 {
480 NTSTATUS Status = STATUS_SUCCESS;
481 HANDLE ConsoleHandle;
482 PCONSRV_CONSOLE Console;
483
484 /*
485 * We are about to create a new console. However when ConSrvNewProcess
486 * was called, we didn't know that we wanted to create a new console and
487 * therefore, we by default inherited the handles table from our parent
488 * process. It's only now that we notice that in fact we do not need
489 * them, because we've created a new console and thus we must use it.
490 *
491 * Therefore, free the handles table so that we can recreate
492 * a new one later on.
493 */
494 ConSrvFreeHandlesTable(ProcessData);
495
496 /* Initialize a new Console owned by this process */
497 DPRINT("Initialization of console '%S' for process '%S' on desktop '%S'\n",
498 ConsoleInitInfo->ConsoleTitle ? ConsoleInitInfo->ConsoleTitle : L"n/a",
499 ConsoleInitInfo->AppName ? ConsoleInitInfo->AppName : L"n/a",
500 ConsoleInitInfo->Desktop ? ConsoleInitInfo->Desktop : L"n/a");
501 Status = ConSrvInitConsole(&ConsoleHandle,
502 &Console,
503 ConsoleInitInfo,
504 ProcessData->Process);
505 if (!NT_SUCCESS(Status))
506 {
507 DPRINT1("Console initialization failed\n");
508 return Status;
509 }
510
511 /* Assign the new console handle */
512 ProcessData->ConsoleHandle = ConsoleHandle;
513
514 /* Initialize the handles table */
515 Status = ConSrvInitHandlesTable(ProcessData,
516 Console,
517 pInputHandle,
518 pOutputHandle,
519 pErrorHandle);
520 if (!NT_SUCCESS(Status))
521 {
522 DPRINT1("Failed to initialize the handles table\n");
523 ConSrvDeleteConsole(Console);
524 ProcessData->ConsoleHandle = NULL;
525 return Status;
526 }
527
528 /* Duplicate the Initialization Events */
529 Status = NtDuplicateObject(NtCurrentProcess(),
530 Console->InitEvents[INIT_SUCCESS],
531 ProcessData->Process->ProcessHandle,
532 &ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_SUCCESS],
533 EVENT_ALL_ACCESS, 0, 0);
534 if (!NT_SUCCESS(Status))
535 {
536 DPRINT1("NtDuplicateObject(InitEvents[INIT_SUCCESS]) failed: %lu\n", Status);
537 ConSrvFreeHandlesTable(ProcessData);
538 ConSrvDeleteConsole(Console);
539 ProcessData->ConsoleHandle = NULL;
540 return Status;
541 }
542
543 Status = NtDuplicateObject(NtCurrentProcess(),
544 Console->InitEvents[INIT_FAILURE],
545 ProcessData->Process->ProcessHandle,
546 &ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_FAILURE],
547 EVENT_ALL_ACCESS, 0, 0);
548 if (!NT_SUCCESS(Status))
549 {
550 DPRINT1("NtDuplicateObject(InitEvents[INIT_FAILURE]) failed: %lu\n", Status);
551 NtClose(ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_SUCCESS]);
552 ConSrvFreeHandlesTable(ProcessData);
553 ConSrvDeleteConsole(Console);
554 ProcessData->ConsoleHandle = NULL;
555 return Status;
556 }
557
558 /* Duplicate the Input Event */
559 Status = NtDuplicateObject(NtCurrentProcess(),
560 Console->InputBuffer.ActiveEvent,
561 ProcessData->Process->ProcessHandle,
562 &ConsoleInitInfo->ConsoleStartInfo->InputWaitHandle,
563 EVENT_ALL_ACCESS, 0, 0);
564 if (!NT_SUCCESS(Status))
565 {
566 DPRINT1("NtDuplicateObject(InputWaitHandle) failed: %lu\n", Status);
567 NtClose(ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_FAILURE]);
568 NtClose(ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_SUCCESS]);
569 ConSrvFreeHandlesTable(ProcessData);
570 ConSrvDeleteConsole(Console);
571 ProcessData->ConsoleHandle = NULL;
572 return Status;
573 }
574
575 /* Mark the process as having a console */
576 ProcessData->ConsoleApp = TRUE;
577 ProcessData->Process->Flags |= CsrProcessIsConsoleApp;
578
579 /* Return the console handle to the caller */
580 ConsoleInitInfo->ConsoleStartInfo->ConsoleHandle = ProcessData->ConsoleHandle;
581
582 /*
583 * Insert the process into the processes list of the console,
584 * and set its foreground priority.
585 */
586 InsertHeadList(&Console->ProcessList, &ProcessData->ConsoleLink);
587 ConSrvSetProcessFocus(ProcessData->Process, Console->HasFocus);
588
589 /* Add a reference count because the process is tied to the console */
590 _InterlockedIncrement(&Console->ReferenceCount);
591
592 /* Update the internal info of the terminal */
593 TermRefreshInternalInfo(Console);
594
595 return STATUS_SUCCESS;
596 }
597
598 NTSTATUS
599 ConSrvInheritConsole(PCONSOLE_PROCESS_DATA ProcessData,
600 HANDLE ConsoleHandle,
601 BOOLEAN CreateNewHandlesTable,
602 PHANDLE pInputHandle,
603 PHANDLE pOutputHandle,
604 PHANDLE pErrorHandle,
605 PCONSOLE_START_INFO ConsoleStartInfo)
606 {
607 NTSTATUS Status = STATUS_SUCCESS;
608 PCONSOLE Console;
609
610 /* Validate and lock the console */
611 if (!ConSrvValidateConsole(&Console,
612 ConsoleHandle,
613 CONSOLE_RUNNING, TRUE))
614 {
615 // FIXME: Find another status code
616 return STATUS_UNSUCCESSFUL;
617 }
618
619 /* Inherit the console */
620 ProcessData->ConsoleHandle = ConsoleHandle;
621
622 if (CreateNewHandlesTable)
623 {
624 /*
625 * We are about to create a new console. However when ConSrvNewProcess
626 * was called, we didn't know that we wanted to create a new console and
627 * therefore, we by default inherited the handles table from our parent
628 * process. It's only now that we notice that in fact we do not need
629 * them, because we've created a new console and thus we must use it.
630 *
631 * Therefore, free the handles table so that we can recreate
632 * a new one later on.
633 */
634 ConSrvFreeHandlesTable(ProcessData);
635
636 /* Initialize the handles table */
637 Status = ConSrvInitHandlesTable(ProcessData,
638 Console,
639 pInputHandle,
640 pOutputHandle,
641 pErrorHandle);
642 if (!NT_SUCCESS(Status))
643 {
644 DPRINT1("Failed to initialize the handles table\n");
645 ProcessData->ConsoleHandle = NULL;
646 goto Quit;
647 }
648 }
649
650 /* Duplicate the Initialization Events */
651 Status = NtDuplicateObject(NtCurrentProcess(),
652 Console->InitEvents[INIT_SUCCESS],
653 ProcessData->Process->ProcessHandle,
654 &ConsoleStartInfo->InitEvents[INIT_SUCCESS],
655 EVENT_ALL_ACCESS, 0, 0);
656 if (!NT_SUCCESS(Status))
657 {
658 DPRINT1("NtDuplicateObject(InitEvents[INIT_SUCCESS]) failed: %lu\n", Status);
659 ConSrvFreeHandlesTable(ProcessData);
660 ProcessData->ConsoleHandle = NULL;
661 goto Quit;
662 }
663
664 Status = NtDuplicateObject(NtCurrentProcess(),
665 Console->InitEvents[INIT_FAILURE],
666 ProcessData->Process->ProcessHandle,
667 &ConsoleStartInfo->InitEvents[INIT_FAILURE],
668 EVENT_ALL_ACCESS, 0, 0);
669 if (!NT_SUCCESS(Status))
670 {
671 DPRINT1("NtDuplicateObject(InitEvents[INIT_FAILURE]) failed: %lu\n", Status);
672 NtClose(ConsoleStartInfo->InitEvents[INIT_SUCCESS]);
673 ConSrvFreeHandlesTable(ProcessData);
674 ProcessData->ConsoleHandle = NULL;
675 goto Quit;
676 }
677
678 /* Duplicate the Input Event */
679 Status = NtDuplicateObject(NtCurrentProcess(),
680 Console->InputBuffer.ActiveEvent,
681 ProcessData->Process->ProcessHandle,
682 &ConsoleStartInfo->InputWaitHandle,
683 EVENT_ALL_ACCESS, 0, 0);
684 if (!NT_SUCCESS(Status))
685 {
686 DPRINT1("NtDuplicateObject(InputWaitHandle) failed: %lu\n", Status);
687 NtClose(ConsoleStartInfo->InitEvents[INIT_FAILURE]);
688 NtClose(ConsoleStartInfo->InitEvents[INIT_SUCCESS]);
689 ConSrvFreeHandlesTable(ProcessData); // NOTE: Always free the handles table.
690 ProcessData->ConsoleHandle = NULL;
691 goto Quit;
692 }
693
694 /* Mark the process as having a console */
695 ProcessData->ConsoleApp = TRUE;
696 ProcessData->Process->Flags |= CsrProcessIsConsoleApp;
697
698 /* Return the console handle to the caller */
699 ConsoleStartInfo->ConsoleHandle = ProcessData->ConsoleHandle;
700
701 /*
702 * Insert the process into the processes list of the console,
703 * and set its foreground priority.
704 */
705 InsertHeadList(&Console->ProcessList, &ProcessData->ConsoleLink);
706 ConSrvSetProcessFocus(ProcessData->Process, Console->HasFocus);
707
708 /* Add a reference count because the process is tied to the console */
709 _InterlockedIncrement(&Console->ReferenceCount);
710
711 /* Update the internal info of the terminal */
712 TermRefreshInternalInfo(Console);
713
714 Status = STATUS_SUCCESS;
715
716 Quit:
717 /* Unlock the console and return */
718 LeaveCriticalSection(&Console->Lock);
719 return Status;
720 }
721
722 NTSTATUS
723 ConSrvRemoveConsole(PCONSOLE_PROCESS_DATA ProcessData)
724 {
725 PCONSOLE Console;
726 PCONSOLE_PROCESS_DATA ConsoleLeaderProcess;
727
728 DPRINT("ConSrvRemoveConsole\n");
729
730 /* Mark the process as not having a console anymore */
731 ProcessData->ConsoleApp = FALSE;
732 ProcessData->Process->Flags &= ~CsrProcessIsConsoleApp;
733
734 /* Validate and lock the console */
735 if (!ConSrvValidateConsole(&Console,
736 ProcessData->ConsoleHandle,
737 CONSOLE_RUNNING, TRUE))
738 {
739 // FIXME: Find another status code
740 return STATUS_UNSUCCESSFUL;
741 }
742
743 DPRINT("ConSrvRemoveConsole - Locking OK\n");
744
745 /* Retrieve the console leader process */
746 ConsoleLeaderProcess = ConSrvGetConsoleLeaderProcess(Console);
747
748 /* Close all console handles and free the handles table */
749 ConSrvFreeHandlesTable(ProcessData);
750
751 /* Detach the process from the console */
752 ProcessData->ConsoleHandle = NULL;
753
754 /* Remove the process from the console's list of processes */
755 RemoveEntryList(&ProcessData->ConsoleLink);
756
757 /* Check whether the console should send a last close notification */
758 if (Console->NotifyLastClose)
759 {
760 /* If we are removing the process which wants the last close notification... */
761 if (ProcessData == Console->NotifiedLastCloseProcess)
762 {
763 /* ... just reset the flag and the pointer... */
764 Console->NotifyLastClose = FALSE;
765 Console->NotifiedLastCloseProcess = NULL;
766 }
767 /*
768 * ... otherwise, if we are removing the console leader process
769 * (that cannot be the process wanting the notification, because
770 * the previous case already dealt with it)...
771 */
772 else if (ProcessData == ConsoleLeaderProcess)
773 {
774 /*
775 * ... reset the flag first (so that we avoid multiple notifications)
776 * and then send the last close notification.
777 */
778 Console->NotifyLastClose = FALSE;
779 ConSrvConsoleCtrlEvent(CTRL_LAST_CLOSE_EVENT, Console->NotifiedLastCloseProcess);
780
781 /* Only now, reset the pointer */
782 Console->NotifiedLastCloseProcess = NULL;
783 }
784 }
785
786 /* Update the internal info of the terminal */
787 TermRefreshInternalInfo(Console);
788
789 /* Release the console */
790 DPRINT("ConSrvRemoveConsole - Decrement Console->ReferenceCount = %lu\n", Console->ReferenceCount);
791 ConSrvReleaseConsole(Console, TRUE);
792
793 return STATUS_SUCCESS;
794 }
795
796
797 /* PUBLIC SERVER APIS *********************************************************/
798
799 CSR_API(SrvOpenConsole)
800 {
801 /*
802 * This API opens a handle to either the input buffer or to
803 * a screen-buffer of the console of the current process.
804 */
805
806 NTSTATUS Status;
807 PCONSOLE_OPENCONSOLE OpenConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.OpenConsoleRequest;
808 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
809 PCONSOLE Console;
810
811 DWORD DesiredAccess = OpenConsoleRequest->DesiredAccess;
812 DWORD ShareMode = OpenConsoleRequest->ShareMode;
813 PCONSOLE_IO_OBJECT Object;
814
815 OpenConsoleRequest->Handle = INVALID_HANDLE_VALUE;
816
817 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
818 if (!NT_SUCCESS(Status))
819 {
820 DPRINT1("Can't get console, status %lx\n", Status);
821 return Status;
822 }
823
824 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
825
826 /*
827 * Open a handle to either the active screen buffer or the input buffer.
828 */
829 if (OpenConsoleRequest->HandleType == HANDLE_OUTPUT)
830 {
831 Object = &Console->ActiveBuffer->Header;
832 }
833 else // HANDLE_INPUT
834 {
835 Object = &Console->InputBuffer.Header;
836 }
837
838 if (((DesiredAccess & GENERIC_READ) && Object->ExclusiveRead != 0) ||
839 ((DesiredAccess & GENERIC_WRITE) && Object->ExclusiveWrite != 0) ||
840 (!(ShareMode & FILE_SHARE_READ) && Object->AccessRead != 0) ||
841 (!(ShareMode & FILE_SHARE_WRITE) && Object->AccessWrite != 0))
842 {
843 DPRINT1("Sharing violation\n");
844 Status = STATUS_SHARING_VIOLATION;
845 }
846 else
847 {
848 Status = ConSrvInsertObject(ProcessData,
849 &OpenConsoleRequest->Handle,
850 Object,
851 DesiredAccess,
852 OpenConsoleRequest->InheritHandle,
853 ShareMode);
854 }
855
856 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
857
858 ConSrvReleaseConsole(Console, TRUE);
859 return Status;
860 }
861
862 CSR_API(SrvDuplicateHandle)
863 {
864 NTSTATUS Status;
865 PCONSOLE_DUPLICATEHANDLE DuplicateHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.DuplicateHandleRequest;
866 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
867 PCONSOLE Console;
868
869 HANDLE SourceHandle = DuplicateHandleRequest->SourceHandle;
870 ULONG Index = HandleToULong(SourceHandle) >> 2;
871 PCONSOLE_IO_HANDLE Entry;
872 DWORD DesiredAccess;
873
874 DuplicateHandleRequest->TargetHandle = INVALID_HANDLE_VALUE;
875
876 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
877 if (!NT_SUCCESS(Status))
878 {
879 DPRINT1("Can't get console, status %lx\n", Status);
880 return Status;
881 }
882
883 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
884
885 // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
886 // (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
887
888 if ( /** !IsConsoleHandle(SourceHandle) || **/
889 Index >= ProcessData->HandleTableSize ||
890 (Entry = &ProcessData->HandleTable[Index])->Object == NULL)
891 {
892 DPRINT1("Couldn't duplicate invalid handle 0x%p\n", SourceHandle);
893 Status = STATUS_INVALID_HANDLE;
894 goto Quit;
895 }
896
897 if (DuplicateHandleRequest->Options & DUPLICATE_SAME_ACCESS)
898 {
899 DesiredAccess = Entry->Access;
900 }
901 else
902 {
903 DesiredAccess = DuplicateHandleRequest->DesiredAccess;
904 /* Make sure the source handle has all the desired flags */
905 if ((Entry->Access & DesiredAccess) == 0)
906 {
907 DPRINT1("Handle 0x%p only has access %X; requested %X\n",
908 SourceHandle, Entry->Access, DesiredAccess);
909 Status = STATUS_INVALID_PARAMETER;
910 goto Quit;
911 }
912 }
913
914 /* Insert the new handle inside the process handles table */
915 Status = ConSrvInsertObject(ProcessData,
916 &DuplicateHandleRequest->TargetHandle,
917 Entry->Object,
918 DesiredAccess,
919 DuplicateHandleRequest->InheritHandle,
920 Entry->ShareMode);
921 if (NT_SUCCESS(Status) &&
922 (DuplicateHandleRequest->Options & DUPLICATE_CLOSE_SOURCE))
923 {
924 /* Close the original handle if needed */
925 ConSrvCloseHandle(Entry);
926 }
927
928 Quit:
929 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
930
931 ConSrvReleaseConsole(Console, TRUE);
932 return Status;
933 }
934
935 CSR_API(SrvGetHandleInformation)
936 {
937 NTSTATUS Status;
938 PCONSOLE_GETHANDLEINFO GetHandleInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetHandleInfoRequest;
939 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
940 PCONSOLE Console;
941
942 HANDLE Handle = GetHandleInfoRequest->Handle;
943 ULONG Index = HandleToULong(Handle) >> 2;
944 PCONSOLE_IO_HANDLE Entry;
945
946 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
947 if (!NT_SUCCESS(Status))
948 {
949 DPRINT1("Can't get console, status %lx\n", Status);
950 return Status;
951 }
952
953 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
954
955 ASSERT(ProcessData->HandleTable);
956 // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
957 // (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
958
959 if (!IsConsoleHandle(Handle) ||
960 Index >= ProcessData->HandleTableSize ||
961 (Entry = &ProcessData->HandleTable[Index])->Object == NULL)
962 {
963 Status = STATUS_INVALID_HANDLE;
964 goto Quit;
965 }
966
967 /*
968 * Retrieve the handle information flags. The console server
969 * doesn't support HANDLE_FLAG_PROTECT_FROM_CLOSE.
970 */
971 GetHandleInfoRequest->Flags = 0;
972 if (Entry->Inheritable) GetHandleInfoRequest->Flags |= HANDLE_FLAG_INHERIT;
973
974 Status = STATUS_SUCCESS;
975
976 Quit:
977 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
978
979 ConSrvReleaseConsole(Console, TRUE);
980 return Status;
981 }
982
983 CSR_API(SrvSetHandleInformation)
984 {
985 NTSTATUS Status;
986 PCONSOLE_SETHANDLEINFO SetHandleInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetHandleInfoRequest;
987 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
988 PCONSOLE Console;
989
990 HANDLE Handle = SetHandleInfoRequest->Handle;
991 ULONG Index = HandleToULong(Handle) >> 2;
992 PCONSOLE_IO_HANDLE Entry;
993
994 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
995 if (!NT_SUCCESS(Status))
996 {
997 DPRINT1("Can't get console, status %lx\n", Status);
998 return Status;
999 }
1000
1001 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
1002
1003 ASSERT(ProcessData->HandleTable);
1004 // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
1005 // (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
1006
1007 if (!IsConsoleHandle(Handle) ||
1008 Index >= ProcessData->HandleTableSize ||
1009 (Entry = &ProcessData->HandleTable[Index])->Object == NULL)
1010 {
1011 Status = STATUS_INVALID_HANDLE;
1012 goto Quit;
1013 }
1014
1015 /*
1016 * Modify the handle information flags. The console server
1017 * doesn't support HANDLE_FLAG_PROTECT_FROM_CLOSE.
1018 */
1019 if (SetHandleInfoRequest->Mask & HANDLE_FLAG_INHERIT)
1020 {
1021 Entry->Inheritable = ((SetHandleInfoRequest->Flags & HANDLE_FLAG_INHERIT) != 0);
1022 }
1023
1024 Status = STATUS_SUCCESS;
1025
1026 Quit:
1027 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
1028
1029 ConSrvReleaseConsole(Console, TRUE);
1030 return Status;
1031 }
1032
1033 CSR_API(SrvCloseHandle)
1034 {
1035 NTSTATUS Status;
1036 PCONSOLE_CLOSEHANDLE CloseHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.CloseHandleRequest;
1037 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
1038 PCONSOLE Console;
1039
1040 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
1041 if (!NT_SUCCESS(Status))
1042 {
1043 DPRINT1("Can't get console, status %lx\n", Status);
1044 return Status;
1045 }
1046
1047 Status = ConSrvRemoveObject(ProcessData, CloseHandleRequest->Handle);
1048
1049 ConSrvReleaseConsole(Console, TRUE);
1050 return Status;
1051 }
1052
1053 CSR_API(SrvVerifyConsoleIoHandle)
1054 {
1055 NTSTATUS Status;
1056 PCONSOLE_VERIFYHANDLE VerifyHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.VerifyHandleRequest;
1057 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
1058 PCONSOLE Console;
1059
1060 HANDLE IoHandle = VerifyHandleRequest->Handle;
1061 ULONG Index = HandleToULong(IoHandle) >> 2;
1062
1063 VerifyHandleRequest->IsValid = FALSE;
1064
1065 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
1066 if (!NT_SUCCESS(Status))
1067 {
1068 DPRINT1("Can't get console, status %lx\n", Status);
1069 return Status;
1070 }
1071
1072 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
1073
1074 // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
1075 // (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
1076
1077 if (!IsConsoleHandle(IoHandle) ||
1078 Index >= ProcessData->HandleTableSize ||
1079 ProcessData->HandleTable[Index].Object == NULL)
1080 {
1081 DPRINT("SrvVerifyConsoleIoHandle failed\n");
1082 }
1083 else
1084 {
1085 VerifyHandleRequest->IsValid = TRUE;
1086 }
1087
1088 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
1089
1090 ConSrvReleaseConsole(Console, TRUE);
1091 return STATUS_SUCCESS;
1092 }
1093
1094 /* EOF */