125dfab67538a62ee35557063dc4eefb61753e66
[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 NtDuplicateObject(ProcessData->Process->ProcessHandle,
552 ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_SUCCESS],
553 NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
554 ConSrvFreeHandlesTable(ProcessData);
555 ConSrvDeleteConsole(Console);
556 ProcessData->ConsoleHandle = NULL;
557 return Status;
558 }
559
560 /* Duplicate the Input Event */
561 Status = NtDuplicateObject(NtCurrentProcess(),
562 Console->InputBuffer.ActiveEvent,
563 ProcessData->Process->ProcessHandle,
564 &ConsoleInitInfo->ConsoleStartInfo->InputWaitHandle,
565 EVENT_ALL_ACCESS, 0, 0);
566 if (!NT_SUCCESS(Status))
567 {
568 DPRINT1("NtDuplicateObject(InputWaitHandle) failed: %lu\n", Status);
569 NtDuplicateObject(ProcessData->Process->ProcessHandle,
570 ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_FAILURE],
571 NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
572 NtDuplicateObject(ProcessData->Process->ProcessHandle,
573 ConsoleInitInfo->ConsoleStartInfo->InitEvents[INIT_SUCCESS],
574 NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
575 ConSrvFreeHandlesTable(ProcessData);
576 ConSrvDeleteConsole(Console);
577 ProcessData->ConsoleHandle = NULL;
578 return Status;
579 }
580
581 /* Mark the process as having a console */
582 ProcessData->ConsoleApp = TRUE;
583 ProcessData->Process->Flags |= CsrProcessIsConsoleApp;
584
585 /* Return the console handle to the caller */
586 ConsoleInitInfo->ConsoleStartInfo->ConsoleHandle = ProcessData->ConsoleHandle;
587
588 /*
589 * Insert the process into the processes list of the console,
590 * and set its foreground priority.
591 */
592 InsertHeadList(&Console->ProcessList, &ProcessData->ConsoleLink);
593 ConSrvSetProcessFocus(ProcessData->Process, Console->HasFocus);
594
595 /* Add a reference count because the process is tied to the console */
596 _InterlockedIncrement(&Console->ReferenceCount);
597
598 /* Update the internal info of the terminal */
599 TermRefreshInternalInfo(Console);
600
601 return STATUS_SUCCESS;
602 }
603
604 NTSTATUS
605 ConSrvInheritConsole(PCONSOLE_PROCESS_DATA ProcessData,
606 HANDLE ConsoleHandle,
607 BOOLEAN CreateNewHandlesTable,
608 PHANDLE pInputHandle,
609 PHANDLE pOutputHandle,
610 PHANDLE pErrorHandle,
611 PCONSOLE_START_INFO ConsoleStartInfo)
612 {
613 NTSTATUS Status = STATUS_SUCCESS;
614 PCONSOLE Console;
615
616 /* Validate and lock the console */
617 if (!ConSrvValidateConsole(&Console,
618 ConsoleHandle,
619 CONSOLE_RUNNING, TRUE))
620 {
621 // FIXME: Find another status code
622 return STATUS_UNSUCCESSFUL;
623 }
624
625 /* Inherit the console */
626 ProcessData->ConsoleHandle = ConsoleHandle;
627
628 if (CreateNewHandlesTable)
629 {
630 /*
631 * We are about to create a new console. However when ConSrvNewProcess
632 * was called, we didn't know that we wanted to create a new console and
633 * therefore, we by default inherited the handles table from our parent
634 * process. It's only now that we notice that in fact we do not need
635 * them, because we've created a new console and thus we must use it.
636 *
637 * Therefore, free the handles table so that we can recreate
638 * a new one later on.
639 */
640 ConSrvFreeHandlesTable(ProcessData);
641
642 /* Initialize the handles table */
643 Status = ConSrvInitHandlesTable(ProcessData,
644 Console,
645 pInputHandle,
646 pOutputHandle,
647 pErrorHandle);
648 if (!NT_SUCCESS(Status))
649 {
650 DPRINT1("Failed to initialize the handles table\n");
651 ProcessData->ConsoleHandle = NULL;
652 goto Quit;
653 }
654 }
655
656 /* Duplicate the Initialization Events */
657 Status = NtDuplicateObject(NtCurrentProcess(),
658 Console->InitEvents[INIT_SUCCESS],
659 ProcessData->Process->ProcessHandle,
660 &ConsoleStartInfo->InitEvents[INIT_SUCCESS],
661 EVENT_ALL_ACCESS, 0, 0);
662 if (!NT_SUCCESS(Status))
663 {
664 DPRINT1("NtDuplicateObject(InitEvents[INIT_SUCCESS]) failed: %lu\n", Status);
665 ConSrvFreeHandlesTable(ProcessData);
666 ProcessData->ConsoleHandle = NULL;
667 goto Quit;
668 }
669
670 Status = NtDuplicateObject(NtCurrentProcess(),
671 Console->InitEvents[INIT_FAILURE],
672 ProcessData->Process->ProcessHandle,
673 &ConsoleStartInfo->InitEvents[INIT_FAILURE],
674 EVENT_ALL_ACCESS, 0, 0);
675 if (!NT_SUCCESS(Status))
676 {
677 DPRINT1("NtDuplicateObject(InitEvents[INIT_FAILURE]) failed: %lu\n", Status);
678 NtDuplicateObject(ProcessData->Process->ProcessHandle,
679 ConsoleStartInfo->InitEvents[INIT_SUCCESS],
680 NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
681 ConSrvFreeHandlesTable(ProcessData);
682 ProcessData->ConsoleHandle = NULL;
683 goto Quit;
684 }
685
686 /* Duplicate the Input Event */
687 Status = NtDuplicateObject(NtCurrentProcess(),
688 Console->InputBuffer.ActiveEvent,
689 ProcessData->Process->ProcessHandle,
690 &ConsoleStartInfo->InputWaitHandle,
691 EVENT_ALL_ACCESS, 0, 0);
692 if (!NT_SUCCESS(Status))
693 {
694 DPRINT1("NtDuplicateObject(InputWaitHandle) failed: %lu\n", Status);
695 NtDuplicateObject(ProcessData->Process->ProcessHandle,
696 ConsoleStartInfo->InitEvents[INIT_FAILURE],
697 NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
698 NtDuplicateObject(ProcessData->Process->ProcessHandle,
699 ConsoleStartInfo->InitEvents[INIT_SUCCESS],
700 NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE);
701 ConSrvFreeHandlesTable(ProcessData); // NOTE: Always free the handles table.
702 ProcessData->ConsoleHandle = NULL;
703 goto Quit;
704 }
705
706 /* Mark the process as having a console */
707 ProcessData->ConsoleApp = TRUE;
708 ProcessData->Process->Flags |= CsrProcessIsConsoleApp;
709
710 /* Return the console handle to the caller */
711 ConsoleStartInfo->ConsoleHandle = ProcessData->ConsoleHandle;
712
713 /*
714 * Insert the process into the processes list of the console,
715 * and set its foreground priority.
716 */
717 InsertHeadList(&Console->ProcessList, &ProcessData->ConsoleLink);
718 ConSrvSetProcessFocus(ProcessData->Process, Console->HasFocus);
719
720 /* Add a reference count because the process is tied to the console */
721 _InterlockedIncrement(&Console->ReferenceCount);
722
723 /* Update the internal info of the terminal */
724 TermRefreshInternalInfo(Console);
725
726 Status = STATUS_SUCCESS;
727
728 Quit:
729 /* Unlock the console and return */
730 LeaveCriticalSection(&Console->Lock);
731 return Status;
732 }
733
734 NTSTATUS
735 ConSrvRemoveConsole(PCONSOLE_PROCESS_DATA ProcessData)
736 {
737 PCONSOLE Console;
738 PCONSOLE_PROCESS_DATA ConsoleLeaderProcess;
739
740 DPRINT("ConSrvRemoveConsole\n");
741
742 /* Mark the process as not having a console anymore */
743 ProcessData->ConsoleApp = FALSE;
744 ProcessData->Process->Flags &= ~CsrProcessIsConsoleApp;
745
746 /* Validate and lock the console */
747 if (!ConSrvValidateConsole(&Console,
748 ProcessData->ConsoleHandle,
749 CONSOLE_RUNNING, TRUE))
750 {
751 // FIXME: Find another status code
752 return STATUS_UNSUCCESSFUL;
753 }
754
755 DPRINT("ConSrvRemoveConsole - Locking OK\n");
756
757 /* Retrieve the console leader process */
758 ConsoleLeaderProcess = ConSrvGetConsoleLeaderProcess(Console);
759
760 /* Close all console handles and free the handles table */
761 ConSrvFreeHandlesTable(ProcessData);
762
763 /* Detach the process from the console */
764 ProcessData->ConsoleHandle = NULL;
765
766 /* Remove the process from the console's list of processes */
767 RemoveEntryList(&ProcessData->ConsoleLink);
768
769 /* Check whether the console should send a last close notification */
770 if (Console->NotifyLastClose)
771 {
772 /* If we are removing the process which wants the last close notification... */
773 if (ProcessData == Console->NotifiedLastCloseProcess)
774 {
775 /* ... just reset the flag and the pointer... */
776 Console->NotifyLastClose = FALSE;
777 Console->NotifiedLastCloseProcess = NULL;
778 }
779 /*
780 * ... otherwise, if we are removing the console leader process
781 * (that cannot be the process wanting the notification, because
782 * the previous case already dealt with it)...
783 */
784 else if (ProcessData == ConsoleLeaderProcess)
785 {
786 /*
787 * ... reset the flag first (so that we avoid multiple notifications)
788 * and then send the last close notification.
789 */
790 Console->NotifyLastClose = FALSE;
791 ConSrvConsoleCtrlEvent(CTRL_LAST_CLOSE_EVENT, Console->NotifiedLastCloseProcess);
792
793 /* Only now, reset the pointer */
794 Console->NotifiedLastCloseProcess = NULL;
795 }
796 }
797
798 /* Update the internal info of the terminal */
799 TermRefreshInternalInfo(Console);
800
801 /* Release the console */
802 DPRINT("ConSrvRemoveConsole - Decrement Console->ReferenceCount = %lu\n", Console->ReferenceCount);
803 ConSrvReleaseConsole(Console, TRUE);
804
805 return STATUS_SUCCESS;
806 }
807
808
809 /* PUBLIC SERVER APIS *********************************************************/
810
811 CSR_API(SrvOpenConsole)
812 {
813 /*
814 * This API opens a handle to either the input buffer or to
815 * a screen-buffer of the console of the current process.
816 */
817
818 NTSTATUS Status;
819 PCONSOLE_OPENCONSOLE OpenConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.OpenConsoleRequest;
820 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
821 PCONSOLE Console;
822
823 DWORD DesiredAccess = OpenConsoleRequest->DesiredAccess;
824 DWORD ShareMode = OpenConsoleRequest->ShareMode;
825 PCONSOLE_IO_OBJECT Object;
826
827 OpenConsoleRequest->Handle = INVALID_HANDLE_VALUE;
828
829 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
830 if (!NT_SUCCESS(Status))
831 {
832 DPRINT1("Can't get console, status %lx\n", Status);
833 return Status;
834 }
835
836 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
837
838 /*
839 * Open a handle to either the active screen buffer or the input buffer.
840 */
841 if (OpenConsoleRequest->HandleType == HANDLE_OUTPUT)
842 {
843 Object = &Console->ActiveBuffer->Header;
844 }
845 else // HANDLE_INPUT
846 {
847 Object = &Console->InputBuffer.Header;
848 }
849
850 if (((DesiredAccess & GENERIC_READ) && Object->ExclusiveRead != 0) ||
851 ((DesiredAccess & GENERIC_WRITE) && Object->ExclusiveWrite != 0) ||
852 (!(ShareMode & FILE_SHARE_READ) && Object->AccessRead != 0) ||
853 (!(ShareMode & FILE_SHARE_WRITE) && Object->AccessWrite != 0))
854 {
855 DPRINT1("Sharing violation\n");
856 Status = STATUS_SHARING_VIOLATION;
857 }
858 else
859 {
860 Status = ConSrvInsertObject(ProcessData,
861 &OpenConsoleRequest->Handle,
862 Object,
863 DesiredAccess,
864 OpenConsoleRequest->InheritHandle,
865 ShareMode);
866 }
867
868 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
869
870 ConSrvReleaseConsole(Console, TRUE);
871 return Status;
872 }
873
874 CSR_API(SrvDuplicateHandle)
875 {
876 NTSTATUS Status;
877 PCONSOLE_DUPLICATEHANDLE DuplicateHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.DuplicateHandleRequest;
878 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
879 PCONSOLE Console;
880
881 HANDLE SourceHandle = DuplicateHandleRequest->SourceHandle;
882 ULONG Index = HandleToULong(SourceHandle) >> 2;
883 PCONSOLE_IO_HANDLE Entry;
884 DWORD DesiredAccess;
885
886 DuplicateHandleRequest->TargetHandle = INVALID_HANDLE_VALUE;
887
888 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
889 if (!NT_SUCCESS(Status))
890 {
891 DPRINT1("Can't get console, status %lx\n", Status);
892 return Status;
893 }
894
895 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
896
897 // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
898 // (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
899
900 if ( /** !IsConsoleHandle(SourceHandle) || **/
901 Index >= ProcessData->HandleTableSize ||
902 (Entry = &ProcessData->HandleTable[Index])->Object == NULL)
903 {
904 DPRINT1("Couldn't duplicate invalid handle 0x%p\n", SourceHandle);
905 Status = STATUS_INVALID_HANDLE;
906 goto Quit;
907 }
908
909 if (DuplicateHandleRequest->Options & DUPLICATE_SAME_ACCESS)
910 {
911 DesiredAccess = Entry->Access;
912 }
913 else
914 {
915 DesiredAccess = DuplicateHandleRequest->DesiredAccess;
916 /* Make sure the source handle has all the desired flags */
917 if ((Entry->Access & DesiredAccess) == 0)
918 {
919 DPRINT1("Handle 0x%p only has access %X; requested %X\n",
920 SourceHandle, Entry->Access, DesiredAccess);
921 Status = STATUS_INVALID_PARAMETER;
922 goto Quit;
923 }
924 }
925
926 /* Insert the new handle inside the process handles table */
927 Status = ConSrvInsertObject(ProcessData,
928 &DuplicateHandleRequest->TargetHandle,
929 Entry->Object,
930 DesiredAccess,
931 DuplicateHandleRequest->InheritHandle,
932 Entry->ShareMode);
933 if (NT_SUCCESS(Status) &&
934 (DuplicateHandleRequest->Options & DUPLICATE_CLOSE_SOURCE))
935 {
936 /* Close the original handle if needed */
937 ConSrvCloseHandle(Entry);
938 }
939
940 Quit:
941 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
942
943 ConSrvReleaseConsole(Console, TRUE);
944 return Status;
945 }
946
947 CSR_API(SrvGetHandleInformation)
948 {
949 NTSTATUS Status;
950 PCONSOLE_GETHANDLEINFO GetHandleInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetHandleInfoRequest;
951 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
952 PCONSOLE Console;
953
954 HANDLE Handle = GetHandleInfoRequest->Handle;
955 ULONG Index = HandleToULong(Handle) >> 2;
956 PCONSOLE_IO_HANDLE Entry;
957
958 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
959 if (!NT_SUCCESS(Status))
960 {
961 DPRINT1("Can't get console, status %lx\n", Status);
962 return Status;
963 }
964
965 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
966
967 ASSERT(ProcessData->HandleTable);
968 // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
969 // (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
970
971 if (!IsConsoleHandle(Handle) ||
972 Index >= ProcessData->HandleTableSize ||
973 (Entry = &ProcessData->HandleTable[Index])->Object == NULL)
974 {
975 Status = STATUS_INVALID_HANDLE;
976 goto Quit;
977 }
978
979 /*
980 * Retrieve the handle information flags. The console server
981 * doesn't support HANDLE_FLAG_PROTECT_FROM_CLOSE.
982 */
983 GetHandleInfoRequest->Flags = 0;
984 if (Entry->Inheritable) GetHandleInfoRequest->Flags |= HANDLE_FLAG_INHERIT;
985
986 Status = STATUS_SUCCESS;
987
988 Quit:
989 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
990
991 ConSrvReleaseConsole(Console, TRUE);
992 return Status;
993 }
994
995 CSR_API(SrvSetHandleInformation)
996 {
997 NTSTATUS Status;
998 PCONSOLE_SETHANDLEINFO SetHandleInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetHandleInfoRequest;
999 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
1000 PCONSOLE Console;
1001
1002 HANDLE Handle = SetHandleInfoRequest->Handle;
1003 ULONG Index = HandleToULong(Handle) >> 2;
1004 PCONSOLE_IO_HANDLE Entry;
1005
1006 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
1007 if (!NT_SUCCESS(Status))
1008 {
1009 DPRINT1("Can't get console, status %lx\n", Status);
1010 return Status;
1011 }
1012
1013 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
1014
1015 ASSERT(ProcessData->HandleTable);
1016 // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
1017 // (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
1018
1019 if (!IsConsoleHandle(Handle) ||
1020 Index >= ProcessData->HandleTableSize ||
1021 (Entry = &ProcessData->HandleTable[Index])->Object == NULL)
1022 {
1023 Status = STATUS_INVALID_HANDLE;
1024 goto Quit;
1025 }
1026
1027 /*
1028 * Modify the handle information flags. The console server
1029 * doesn't support HANDLE_FLAG_PROTECT_FROM_CLOSE.
1030 */
1031 if (SetHandleInfoRequest->Mask & HANDLE_FLAG_INHERIT)
1032 {
1033 Entry->Inheritable = ((SetHandleInfoRequest->Flags & HANDLE_FLAG_INHERIT) != 0);
1034 }
1035
1036 Status = STATUS_SUCCESS;
1037
1038 Quit:
1039 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
1040
1041 ConSrvReleaseConsole(Console, TRUE);
1042 return Status;
1043 }
1044
1045 CSR_API(SrvCloseHandle)
1046 {
1047 NTSTATUS Status;
1048 PCONSOLE_CLOSEHANDLE CloseHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.CloseHandleRequest;
1049 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
1050 PCONSOLE Console;
1051
1052 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
1053 if (!NT_SUCCESS(Status))
1054 {
1055 DPRINT1("Can't get console, status %lx\n", Status);
1056 return Status;
1057 }
1058
1059 Status = ConSrvRemoveObject(ProcessData, CloseHandleRequest->Handle);
1060
1061 ConSrvReleaseConsole(Console, TRUE);
1062 return Status;
1063 }
1064
1065 CSR_API(SrvVerifyConsoleIoHandle)
1066 {
1067 NTSTATUS Status;
1068 PCONSOLE_VERIFYHANDLE VerifyHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.VerifyHandleRequest;
1069 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
1070 PCONSOLE Console;
1071
1072 HANDLE IoHandle = VerifyHandleRequest->Handle;
1073 ULONG Index = HandleToULong(IoHandle) >> 2;
1074
1075 VerifyHandleRequest->IsValid = FALSE;
1076
1077 Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
1078 if (!NT_SUCCESS(Status))
1079 {
1080 DPRINT1("Can't get console, status %lx\n", Status);
1081 return Status;
1082 }
1083
1084 RtlEnterCriticalSection(&ProcessData->HandleTableLock);
1085
1086 // ASSERT( (ProcessData->HandleTable == NULL && ProcessData->HandleTableSize == 0) ||
1087 // (ProcessData->HandleTable != NULL && ProcessData->HandleTableSize != 0) );
1088
1089 if (!IsConsoleHandle(IoHandle) ||
1090 Index >= ProcessData->HandleTableSize ||
1091 ProcessData->HandleTable[Index].Object == NULL)
1092 {
1093 DPRINT("SrvVerifyConsoleIoHandle failed\n");
1094 }
1095 else
1096 {
1097 VerifyHandleRequest->IsValid = TRUE;
1098 }
1099
1100 RtlLeaveCriticalSection(&ProcessData->HandleTableLock);
1101
1102 ConSrvReleaseConsole(Console, TRUE);
1103 return STATUS_SUCCESS;
1104 }
1105
1106 /* EOF */