Partially fixed up tree after merge from HEAD. More to do.
[reactos.git] / reactos / subsystems / win32 / csrss / win32csr / exitros.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS CSRSS subsystem
5 * FILE: subsys/csrss/win32csr/exitros.c
6 * PURPOSE: Logout/shutdown
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include "w32csr.h"
12 #include <sddl.h>
13 #include "resource.h"
14
15 #define NDEBUG
16 #include <debug.h>
17
18 static HWND LogonNotifyWindow = NULL;
19 static HANDLE LogonProcess = NULL;
20
21 CSR_API(CsrRegisterLogonProcess)
22 {
23 Request->Header.u1.s1.TotalLength = sizeof(CSR_API_MESSAGE);
24 Request->Header.u1.s1.DataLength = sizeof(CSR_API_MESSAGE) - sizeof(PORT_MESSAGE);
25
26 if (Request->Data.RegisterLogonProcessRequest.Register)
27 {
28 if (0 != LogonProcess)
29 {
30 Request->Status = STATUS_LOGON_SESSION_EXISTS;
31 return Request->Status;
32 }
33 LogonProcess = Request->Data.RegisterLogonProcessRequest.ProcessId;
34 }
35 else
36 {
37 if (Request->Header.ClientId.UniqueProcess != LogonProcess)
38 {
39 DPRINT1("Current logon process 0x%x, can't deregister from process 0x%x\n",
40 LogonProcess, Request->Header.ClientId.UniqueProcess);
41 Request->Status = STATUS_NOT_LOGON_PROCESS;
42 return Request->Status;
43 }
44 LogonProcess = 0;
45 }
46
47 Request->Status = STATUS_SUCCESS;
48
49 return Request->Status;
50 }
51
52 CSR_API(CsrSetLogonNotifyWindow)
53 {
54 DWORD WindowCreator;
55
56 Request->Header.u1.s1.TotalLength = sizeof(CSR_API_MESSAGE);
57 Request->Header.u1.s1.DataLength = sizeof(CSR_API_MESSAGE) -
58 sizeof(PORT_MESSAGE);
59
60 if (0 == GetWindowThreadProcessId(Request->Data.SetLogonNotifyWindowRequest.LogonNotifyWindow,
61 &WindowCreator))
62 {
63 DPRINT1("Can't get window creator\n");
64 Request->Status = STATUS_INVALID_HANDLE;
65 return Request->Status;
66 }
67 if (WindowCreator != (DWORD)LogonProcess)
68 {
69 DPRINT1("Trying to register window not created by winlogon as notify window\n");
70 Request->Status = STATUS_ACCESS_DENIED;
71 return Request->Status;
72 }
73
74 LogonNotifyWindow = Request->Data.SetLogonNotifyWindowRequest.LogonNotifyWindow;
75
76 Request->Status = STATUS_SUCCESS;
77
78 return Request->Status;
79 }
80
81 typedef struct tagSHUTDOWN_SETTINGS
82 {
83 BOOL AutoEndTasks;
84 DWORD HungAppTimeout;
85 DWORD WaitToKillAppTimeout;
86 } SHUTDOWN_SETTINGS, *PSHUTDOWN_SETTINGS;
87
88 #define DEFAULT_AUTO_END_TASKS FALSE
89 #define DEFAULT_HUNG_APP_TIMEOUT 5000
90 #define DEFAULT_WAIT_TO_KILL_APP_TIMEOUT 20000
91
92 typedef struct tagNOTIFY_CONTEXT
93 {
94 DWORD ProcessId;
95 UINT Msg;
96 WPARAM wParam;
97 LPARAM lParam;
98 HDESK Desktop;
99 DWORD StartTime;
100 DWORD QueryResult;
101 HWND Dlg;
102 DWORD EndNowResult;
103 BOOL ShowUI;
104 HANDLE UIThread;
105 HWND WndClient;
106 PSHUTDOWN_SETTINGS ShutdownSettings;
107 LPTHREAD_START_ROUTINE SendMessageProc;
108 } NOTIFY_CONTEXT, *PNOTIFY_CONTEXT;
109
110 #define QUERY_RESULT_ABORT 0
111 #define QUERY_RESULT_CONTINUE 1
112 #define QUERY_RESULT_TIMEOUT 2
113 #define QUERY_RESULT_ERROR 3
114 #define QUERY_RESULT_FORCE 4
115
116 static void FASTCALL
117 UpdateProgressBar(HWND ProgressBar, PNOTIFY_CONTEXT NotifyContext)
118 {
119 DWORD Passed;
120
121 Passed = GetTickCount() - NotifyContext->StartTime;
122 Passed -= NotifyContext->ShutdownSettings->HungAppTimeout;
123 if (NotifyContext->ShutdownSettings->WaitToKillAppTimeout < Passed)
124 {
125 Passed = NotifyContext->ShutdownSettings->WaitToKillAppTimeout;
126 }
127 SendMessageW(ProgressBar, PBM_SETPOS, Passed / 2, 0);
128 }
129
130 static INT_PTR CALLBACK
131 EndNowDlgProc(HWND Dlg, UINT Msg, WPARAM wParam, LPARAM lParam)
132 {
133 INT_PTR Result;
134 PNOTIFY_CONTEXT NotifyContext;
135 HWND ProgressBar;
136 DWORD TitleLength;
137 int Len;
138 LPWSTR Title;
139
140 switch(Msg)
141 {
142 case WM_INITDIALOG:
143 NotifyContext = (PNOTIFY_CONTEXT) lParam;
144 NotifyContext->EndNowResult = QUERY_RESULT_ABORT;
145 SetWindowLongPtrW(Dlg, DWLP_USER, (LONG_PTR) lParam);
146 TitleLength = SendMessageW(NotifyContext->WndClient, WM_GETTEXTLENGTH,
147 0, 0) +
148 GetWindowTextLengthW(Dlg);
149 Title = HeapAlloc(Win32CsrApiHeap, 0, (TitleLength + 1) * sizeof(WCHAR));
150 if (NULL != Title)
151 {
152 Len = GetWindowTextW(Dlg, Title, TitleLength + 1);
153 SendMessageW(NotifyContext->WndClient, WM_GETTEXT,
154 TitleLength + 1 - Len, (LPARAM) (Title + Len));
155 SetWindowTextW(Dlg, Title);
156 HeapFree(Win32CsrApiHeap, 0, Title);
157 }
158 ProgressBar = GetDlgItem(Dlg, IDC_PROGRESS);
159 SendMessageW(ProgressBar, PBM_SETRANGE32, 0,
160 NotifyContext->ShutdownSettings->WaitToKillAppTimeout / 2);
161 UpdateProgressBar(ProgressBar, NotifyContext);
162 SetTimer(Dlg, 0, 200, NULL);
163 Result = FALSE;
164 break;
165
166 case WM_TIMER:
167 NotifyContext = (PNOTIFY_CONTEXT) GetWindowLongPtrW(Dlg, DWLP_USER);
168 ProgressBar = GetDlgItem(Dlg, IDC_PROGRESS);
169 UpdateProgressBar(ProgressBar, NotifyContext);
170 Result = TRUE;
171 break;
172
173 case WM_COMMAND:
174 if (BN_CLICKED == HIWORD(wParam) && IDC_END_NOW == LOWORD(wParam))
175 {
176 NotifyContext = (PNOTIFY_CONTEXT) GetWindowLongPtrW(Dlg, DWLP_USER);
177 NotifyContext->EndNowResult = QUERY_RESULT_FORCE;
178 SendMessageW(Dlg, WM_CLOSE, 0, 0);
179 Result = TRUE;
180 }
181 else
182 {
183 Result = FALSE;
184 }
185 break;
186
187 case WM_CLOSE:
188 DestroyWindow(Dlg);
189 Result = TRUE;
190 break;
191
192 case WM_DESTROY:
193 NotifyContext = (PNOTIFY_CONTEXT) GetWindowLongPtrW(Dlg, DWLP_USER);
194 NotifyContext->Dlg = NULL;
195 KillTimer(Dlg, 0);
196 PostQuitMessage(NotifyContext->EndNowResult);
197 Result = TRUE;
198 break;
199
200 default:
201 Result = FALSE;
202 break;
203 }
204
205 return Result;
206 }
207
208 typedef void (STDCALL *INITCOMMONCONTROLS_PROC)(void);
209
210 static void FASTCALL
211 CallInitCommonControls()
212 {
213 static BOOL Initialized = FALSE;
214 HMODULE Lib;
215 INITCOMMONCONTROLS_PROC InitProc;
216
217 if (Initialized)
218 {
219 return;
220 }
221
222 Lib = LoadLibraryW(L"COMCTL32.DLL");
223 if (NULL == Lib)
224 {
225 return;
226 }
227 InitProc = (INITCOMMONCONTROLS_PROC) GetProcAddress(Lib, "InitCommonControls");
228 if (NULL == InitProc)
229 {
230 return;
231 }
232
233 (*InitProc)();
234
235 Initialized = TRUE;
236 }
237
238 static DWORD WINAPI
239 EndNowThreadProc(LPVOID Parameter)
240 {
241 PNOTIFY_CONTEXT NotifyContext = (PNOTIFY_CONTEXT) Parameter;
242 MSG Msg;
243
244 SetThreadDesktop(NotifyContext->Desktop);
245 SwitchDesktop(NotifyContext->Desktop);
246 CallInitCommonControls();
247 NotifyContext->Dlg = CreateDialogParam(Win32CsrDllHandle,
248 MAKEINTRESOURCE(IDD_END_NOW), NULL,
249 EndNowDlgProc, (LPARAM) NotifyContext);
250 if (NULL == NotifyContext->Dlg)
251 {
252 return 0;
253 }
254 ShowWindow(NotifyContext->Dlg, SW_SHOWNORMAL);
255
256 while (GetMessageW(&Msg, NULL, 0, 0))
257 {
258 if (! IsDialogMessage(NotifyContext->Dlg, &Msg))
259 {
260 TranslateMessage(&Msg);
261 DispatchMessageW(&Msg);
262 }
263 }
264
265 return Msg.wParam;
266 }
267
268 typedef struct tagMESSAGE_CONTEXT
269 {
270 HWND Wnd;
271 UINT Msg;
272 WPARAM wParam;
273 LPARAM lParam;
274 DWORD Timeout;
275 } MESSAGE_CONTEXT, *PMESSAGE_CONTEXT;
276
277 static DWORD WINAPI
278 SendQueryEndSession(LPVOID Parameter)
279 {
280 PMESSAGE_CONTEXT Context = (PMESSAGE_CONTEXT) Parameter;
281 DWORD_PTR Result;
282
283 if (SendMessageTimeoutW(Context->Wnd, WM_QUERYENDSESSION, Context->wParam,
284 Context->lParam, SMTO_NORMAL, Context->Timeout,
285 &Result))
286 {
287 return Result ? QUERY_RESULT_CONTINUE : QUERY_RESULT_ABORT;
288 }
289
290 return 0 == GetLastError() ? QUERY_RESULT_TIMEOUT : QUERY_RESULT_ERROR;
291 }
292
293 static DWORD WINAPI
294 SendEndSession(LPVOID Parameter)
295 {
296 PMESSAGE_CONTEXT Context = (PMESSAGE_CONTEXT) Parameter;
297 DWORD_PTR Result;
298
299 if (Context->wParam)
300 {
301 if (SendMessageTimeoutW(Context->Wnd, WM_ENDSESSION, Context->wParam,
302 Context->lParam, SMTO_NORMAL, Context->Timeout,
303 &Result))
304 {
305 return QUERY_RESULT_CONTINUE;
306 }
307 return 0 == GetLastError() ? QUERY_RESULT_TIMEOUT : QUERY_RESULT_ERROR;
308 }
309 else
310 {
311 SendMessage(Context->Wnd, WM_ENDSESSION, Context->wParam,
312 Context->lParam);
313 return QUERY_RESULT_CONTINUE;
314 }
315 }
316
317 static BOOL CALLBACK
318 NotifyTopLevelEnum(HWND Wnd, LPARAM lParam)
319 {
320 PNOTIFY_CONTEXT NotifyContext = (PNOTIFY_CONTEXT) lParam;
321 MESSAGE_CONTEXT MessageContext;
322 DWORD Now, Passed;
323 DWORD Timeout, WaitStatus;
324 DWORD ProcessId;
325 HANDLE MessageThread;
326 HANDLE Threads[2];
327
328 if (0 == GetWindowThreadProcessId(Wnd, &ProcessId))
329 {
330 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
331 return FALSE;
332 }
333
334 if (ProcessId == NotifyContext->ProcessId)
335 {
336 Now = GetTickCount();
337 if (0 == NotifyContext->StartTime)
338 {
339 NotifyContext->StartTime = Now;
340 }
341 /* Note: Passed is computed correctly even when GetTickCount() wraps due
342 to unsigned arithmetic */
343 Passed = Now - NotifyContext->StartTime;
344 MessageContext.Wnd = Wnd;
345 MessageContext.Msg = NotifyContext->Msg;
346 MessageContext.wParam = NotifyContext->wParam;
347 MessageContext.lParam = NotifyContext->lParam;
348 MessageContext.Timeout = NotifyContext->ShutdownSettings->HungAppTimeout;
349 if (! NotifyContext->ShutdownSettings->AutoEndTasks)
350 {
351 MessageContext.Timeout += NotifyContext->ShutdownSettings->WaitToKillAppTimeout;
352 }
353 if (Passed < MessageContext.Timeout)
354 {
355 MessageContext.Timeout -= Passed;
356 MessageThread = CreateThread(NULL, 0, NotifyContext->SendMessageProc,
357 (LPVOID) &MessageContext, 0, NULL);
358 if (NULL == MessageThread)
359 {
360 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
361 return FALSE;
362 }
363 Timeout = NotifyContext->ShutdownSettings->HungAppTimeout;
364 if (Passed < Timeout)
365 {
366 Timeout -= Passed;
367 WaitStatus = WaitForSingleObjectEx(MessageThread, Timeout, FALSE);
368 }
369 else
370 {
371 WaitStatus = WAIT_TIMEOUT;
372 }
373 if (WAIT_TIMEOUT == WaitStatus)
374 {
375 NotifyContext->WndClient = Wnd;
376 if (NULL == NotifyContext->UIThread && NotifyContext->ShowUI)
377 {
378 NotifyContext->UIThread = CreateThread(NULL, 0,
379 EndNowThreadProc,
380 (LPVOID) NotifyContext,
381 0, NULL);
382 }
383 Threads[0] = MessageThread;
384 Threads[1] = NotifyContext->UIThread;
385 WaitStatus = WaitForMultipleObjectsEx(NULL == NotifyContext->UIThread ?
386 1 : 2,
387 Threads, FALSE, INFINITE,
388 FALSE);
389 if (WAIT_OBJECT_0 == WaitStatus)
390 {
391 if (! GetExitCodeThread(MessageThread, &NotifyContext->QueryResult))
392 {
393 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
394 }
395 }
396 else if (WAIT_OBJECT_0 + 1 == WaitStatus)
397 {
398 if (! GetExitCodeThread(NotifyContext->UIThread,
399 &NotifyContext->QueryResult))
400 {
401 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
402 }
403 }
404 else
405 {
406 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
407 }
408 if (WAIT_OBJECT_0 != WaitStatus)
409 {
410 TerminateThread(MessageThread, QUERY_RESULT_TIMEOUT);
411 }
412 }
413 else if (WAIT_OBJECT_0 == WaitStatus)
414 {
415 if (! GetExitCodeThread(MessageThread,
416 &NotifyContext->QueryResult))
417 {
418 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
419 }
420 }
421 else
422 {
423 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
424 }
425 CloseHandle(MessageThread);
426 }
427 else
428 {
429 NotifyContext->QueryResult = QUERY_RESULT_TIMEOUT;
430 }
431 }
432
433 return QUERY_RESULT_CONTINUE == NotifyContext->QueryResult;
434 }
435
436 static BOOL CALLBACK
437 NotifyDesktopEnum(LPWSTR DesktopName, LPARAM lParam)
438 {
439 PNOTIFY_CONTEXT Context = (PNOTIFY_CONTEXT) lParam;
440
441 Context->Desktop = OpenDesktopW(DesktopName, 0, FALSE,
442 DESKTOP_ENUMERATE | DESKTOP_SWITCHDESKTOP);
443 if (NULL == Context->Desktop)
444 {
445 DPRINT1("OpenDesktop failed with error %d\n", GetLastError());
446 Context->QueryResult = QUERY_RESULT_ERROR;
447 return FALSE;
448 }
449
450 EnumDesktopWindows(Context->Desktop, NotifyTopLevelEnum, lParam);
451
452 CloseDesktop(Context->Desktop);
453
454 return QUERY_RESULT_CONTINUE == Context->QueryResult;
455 }
456
457 static BOOL FASTCALL
458 NotifyTopLevelWindows(PNOTIFY_CONTEXT Context)
459 {
460 HWINSTA WindowStation;
461
462 WindowStation = GetProcessWindowStation();
463 if (NULL == WindowStation)
464 {
465 DPRINT1("GetProcessWindowStation failed with error %d\n", GetLastError());
466 return TRUE;
467 }
468
469 EnumDesktopsW(WindowStation, NotifyDesktopEnum, (LPARAM) Context);
470
471 return TRUE;
472 }
473
474 static BOOL FASTCALL
475 NotifyAndTerminateProcess(PCSRSS_PROCESS_DATA ProcessData,
476 PSHUTDOWN_SETTINGS ShutdownSettings,
477 UINT Flags)
478 {
479 NOTIFY_CONTEXT Context;
480 HANDLE Process;
481 DWORD QueryResult = QUERY_RESULT_CONTINUE;
482
483 Context.QueryResult = QUERY_RESULT_CONTINUE;
484
485 if (0 == (Flags & EWX_FORCE))
486 {
487 if (NULL != ProcessData->Console)
488 {
489 ConioConsoleCtrlEventTimeout(CTRL_LOGOFF_EVENT, ProcessData,
490 ShutdownSettings->WaitToKillAppTimeout);
491 }
492 else
493 {
494 Context.ProcessId = (DWORD) ProcessData->ProcessId;
495 Context.wParam = 0;
496 Context.lParam = (0 != (Flags & EWX_INTERNAL_FLAG_LOGOFF) ?
497 ENDSESSION_LOGOFF : 0);
498 Context.StartTime = 0;
499 Context.UIThread = NULL;
500 Context.ShowUI = DtbgIsDesktopVisible();
501 Context.Dlg = NULL;
502 Context.ShutdownSettings = ShutdownSettings;
503 Context.SendMessageProc = SendQueryEndSession;
504
505 NotifyTopLevelWindows(&Context);
506
507 Context.wParam = (QUERY_RESULT_ABORT != Context.QueryResult);
508 Context.lParam = (0 != (Flags & EWX_INTERNAL_FLAG_LOGOFF) ?
509 ENDSESSION_LOGOFF : 0);
510 Context.SendMessageProc = SendEndSession;
511 Context.ShowUI = DtbgIsDesktopVisible() &&
512 (QUERY_RESULT_ABORT != Context.QueryResult);
513 QueryResult = Context.QueryResult;
514 Context.QueryResult = QUERY_RESULT_CONTINUE;
515
516 NotifyTopLevelWindows(&Context);
517
518 if (NULL != Context.UIThread)
519 {
520 if (NULL != Context.Dlg)
521 {
522 SendMessageW(Context.Dlg, WM_CLOSE, 0, 0);
523 }
524 else
525 {
526 TerminateThread(Context.UIThread, QUERY_RESULT_ERROR);
527 }
528 CloseHandle(Context.UIThread);
529 }
530 }
531
532 if (QUERY_RESULT_ABORT == QueryResult)
533 {
534 return FALSE;
535 }
536 }
537
538 /* Terminate this process */
539 Process = OpenProcess(PROCESS_TERMINATE, FALSE,
540 (DWORD) ProcessData->ProcessId);
541 if (NULL == Process)
542 {
543 DPRINT1("Unable to open process %d, error %d\n", ProcessData->ProcessId,
544 GetLastError());
545 return TRUE;
546 }
547 TerminateProcess(Process, 0);
548 CloseHandle(Process);
549
550 return TRUE;
551 }
552
553 typedef struct tagPROCESS_ENUM_CONTEXT
554 {
555 UINT ProcessCount;
556 PCSRSS_PROCESS_DATA *ProcessData;
557 TOKEN_ORIGIN TokenOrigin;
558 DWORD ShellProcess;
559 DWORD CsrssProcess;
560 } PROCESS_ENUM_CONTEXT, *PPROCESS_ENUM_CONTEXT;
561
562 static NTSTATUS STDCALL
563 ExitReactosProcessEnum(PCSRSS_PROCESS_DATA ProcessData, PVOID Data)
564 {
565 HANDLE Process;
566 HANDLE Token;
567 TOKEN_ORIGIN Origin;
568 DWORD ReturnLength;
569 PPROCESS_ENUM_CONTEXT Context = (PPROCESS_ENUM_CONTEXT) Data;
570 PCSRSS_PROCESS_DATA *NewData;
571
572 /* Do not kill winlogon or csrss */
573 if ((DWORD) ProcessData->ProcessId == Context->CsrssProcess ||
574 ProcessData->ProcessId == LogonProcess)
575 {
576 return STATUS_SUCCESS;
577 }
578
579 /* Get the login session of this process */
580 Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE,
581 (DWORD) ProcessData->ProcessId);
582 if (NULL == Process)
583 {
584 DPRINT1("Unable to open process %d, error %d\n", ProcessData->ProcessId,
585 GetLastError());
586 return STATUS_UNSUCCESSFUL;
587 }
588
589 if (! OpenProcessToken(Process, TOKEN_QUERY, &Token))
590 {
591 DPRINT1("Unable to open token for process %d, error %d\n",
592 ProcessData->ProcessId, GetLastError());
593 CloseHandle(Process);
594 return STATUS_UNSUCCESSFUL;
595 }
596 CloseHandle(Process);
597
598 if (! GetTokenInformation(Token, TokenOrigin, &Origin,
599 sizeof(TOKEN_ORIGIN), &ReturnLength))
600 {
601 DPRINT1("GetTokenInformation failed for process %d with error %d\n",
602 ProcessData->ProcessId, GetLastError());
603 CloseHandle(Token);
604 return STATUS_UNSUCCESSFUL;
605 }
606 CloseHandle(Token);
607
608 /* This process will be killed if it's in the correct logon session */
609 if (RtlEqualLuid(&(Context->TokenOrigin.OriginatingLogonSession),
610 &(Origin.OriginatingLogonSession)))
611 {
612 /* Kill the shell process last */
613 if ((DWORD) ProcessData->ProcessId == Context->ShellProcess)
614 {
615 ProcessData->ShutdownLevel = 0;
616 }
617 NewData = HeapAlloc(Win32CsrApiHeap, 0, (Context->ProcessCount + 1)
618 * sizeof(PCSRSS_PROCESS_DATA));
619 if (NULL == NewData)
620 {
621 return STATUS_NO_MEMORY;
622 }
623 if (0 != Context->ProcessCount)
624 {
625 memcpy(NewData, Context->ProcessData,
626 Context->ProcessCount * sizeof(PCSRSS_PROCESS_DATA));
627 HeapFree(Win32CsrApiHeap, 0, Context->ProcessData);
628 }
629 Context->ProcessData = NewData;
630 Context->ProcessData[Context->ProcessCount] = ProcessData;
631 Context->ProcessCount++;
632 }
633
634 return STATUS_SUCCESS;
635 }
636
637 static int
638 ProcessDataCompare(const void *Elem1, const void *Elem2)
639 {
640 const PCSRSS_PROCESS_DATA *ProcessData1 = (PCSRSS_PROCESS_DATA *) Elem1;
641 const PCSRSS_PROCESS_DATA *ProcessData2 = (PCSRSS_PROCESS_DATA *) Elem2;
642
643 if ((*ProcessData1)->ShutdownLevel < (*ProcessData2)->ShutdownLevel)
644 {
645 return +1;
646 }
647 else if ((*ProcessData2)->ShutdownLevel < (*ProcessData1)->ShutdownLevel)
648 {
649 return -1;
650 }
651 else if ((*ProcessData1)->ProcessId < (*ProcessData2)->ProcessId)
652 {
653 return +1;
654 }
655 else if ((*ProcessData2)->ProcessId < (*ProcessData1)->ProcessId)
656 {
657 return -1;
658 }
659
660 return 0;
661 }
662
663 static DWORD FASTCALL
664 GetShutdownSetting(HKEY DesktopKey, LPCWSTR ValueName, DWORD DefaultValue)
665 {
666 BYTE ValueBuffer[16];
667 LONG ErrCode;
668 DWORD Type;
669 DWORD ValueSize;
670 UNICODE_STRING StringValue;
671 ULONG Value;
672
673 ValueSize = sizeof(ValueBuffer);
674 ErrCode = RegQueryValueExW(DesktopKey, ValueName, NULL, &Type, ValueBuffer,
675 &ValueSize);
676 if (ERROR_SUCCESS != ErrCode)
677 {
678 DPRINT("GetShutdownSetting for %S failed with error code %ld\n",
679 ValueName, ErrCode);
680 return DefaultValue;
681 }
682
683 if (REG_SZ == Type)
684 {
685 RtlInitUnicodeString(&StringValue, (LPCWSTR) ValueBuffer);
686 if (! NT_SUCCESS(RtlUnicodeStringToInteger(&StringValue, 10, &Value)))
687 {
688 DPRINT1("Unable to convert value %S for setting %S\n",
689 StringValue.Buffer, ValueName);
690 return DefaultValue;
691 }
692 return (DWORD) Value;
693 }
694 else if (REG_DWORD == Type)
695 {
696 return *((DWORD *) ValueBuffer);
697 }
698
699 DPRINT1("Unexpected registry type %d for setting %S\n", Type, ValueName);
700 return DefaultValue;
701 }
702
703 static void FASTCALL
704 LoadShutdownSettings(PSID Sid, PSHUTDOWN_SETTINGS ShutdownSettings)
705 {
706 static WCHAR Subkey[] = L"\\Control Panel\\Desktop";
707 LPWSTR StringSid;
708 WCHAR InitialKeyName[128];
709 LPWSTR KeyName;
710 HKEY DesktopKey;
711 LONG ErrCode;
712
713 ShutdownSettings->AutoEndTasks = DEFAULT_AUTO_END_TASKS;
714 ShutdownSettings->HungAppTimeout = DEFAULT_HUNG_APP_TIMEOUT;
715 ShutdownSettings->WaitToKillAppTimeout = DEFAULT_WAIT_TO_KILL_APP_TIMEOUT;
716
717 if (! ConvertSidToStringSidW(Sid, &StringSid))
718 {
719 DPRINT1("ConvertSidToStringSid failed with error %d, using default shutdown settings\n",
720 GetLastError());
721 return;
722 }
723 if (wcslen(StringSid) + wcslen(Subkey) + 1 <=
724 sizeof(InitialKeyName) / sizeof(WCHAR))
725 {
726 KeyName = InitialKeyName;
727 }
728 else
729 {
730 KeyName = HeapAlloc(Win32CsrApiHeap, 0,
731 (wcslen(StringSid) + wcslen(Subkey) + 1) *
732 sizeof(WCHAR));
733 if (NULL == KeyName)
734 {
735 DPRINT1("Failed to allocate memory, using default shutdown settings\n");
736 LocalFree(StringSid);
737 return;
738 }
739 }
740 wcscat(wcscpy(KeyName, StringSid), Subkey);
741 LocalFree(StringSid);
742
743 ErrCode = RegOpenKeyExW(HKEY_USERS, KeyName, 0, KEY_QUERY_VALUE, &DesktopKey);
744 if (KeyName != InitialKeyName)
745 {
746 HeapFree(Win32CsrApiHeap, 0, KeyName);
747 }
748 if (ERROR_SUCCESS != ErrCode)
749 {
750 DPRINT1("RegOpenKeyEx failed with error %ld, using default shutdown settings\n", ErrCode);
751 return;
752 }
753
754 ShutdownSettings->AutoEndTasks = (BOOL) GetShutdownSetting(DesktopKey, L"AutoEndTasks",
755 (DWORD) DEFAULT_AUTO_END_TASKS);
756 ShutdownSettings->HungAppTimeout = GetShutdownSetting(DesktopKey,
757 L"HungAppTimeout",
758 DEFAULT_HUNG_APP_TIMEOUT);
759 ShutdownSettings->WaitToKillAppTimeout = GetShutdownSetting(DesktopKey,
760 L"WaitToKillAppTimeout",
761 DEFAULT_WAIT_TO_KILL_APP_TIMEOUT);
762
763 RegCloseKey(DesktopKey);
764 }
765
766 static NTSTATUS FASTCALL
767 InternalExitReactos(DWORD ProcessId, DWORD ThreadId, UINT Flags)
768 {
769 HANDLE CallerThread;
770 HANDLE CallerToken;
771 NTSTATUS Status;
772 PROCESS_ENUM_CONTEXT Context;
773 DWORD ReturnLength;
774 HWND ShellWnd;
775 UINT ProcessIndex;
776 char FixedUserInfo[64];
777 TOKEN_USER *UserInfo;
778 SHUTDOWN_SETTINGS ShutdownSettings;
779
780 if (ProcessId != (DWORD) LogonProcess)
781 {
782 DPRINT1("Internal ExitWindowsEx call not from winlogon\n");
783 return STATUS_ACCESS_DENIED;
784 }
785
786 CallerThread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, ThreadId);
787 if (NULL == CallerThread)
788 {
789 DPRINT1("OpenThread failed with error %d\n", GetLastError());
790 return STATUS_UNSUCCESSFUL;
791 }
792 if (! OpenThreadToken(CallerThread, TOKEN_QUERY, FALSE, &CallerToken))
793 {
794 DPRINT1("OpenThreadToken failed with error %d\n", GetLastError());
795 CloseHandle(CallerThread);
796 return STATUS_UNSUCCESSFUL;
797 }
798 CloseHandle(CallerThread);
799
800 Context.ProcessCount = 0;
801 Context.ProcessData = NULL;
802 if (! GetTokenInformation(CallerToken, TokenOrigin, &Context.TokenOrigin,
803 sizeof(TOKEN_ORIGIN), &ReturnLength))
804 {
805 DPRINT1("GetTokenInformation failed with error %d\n", GetLastError());
806 CloseHandle(CallerToken);
807 return STATUS_UNSUCCESSFUL;
808 }
809 if (! GetTokenInformation(CallerToken, TokenUser, FixedUserInfo,
810 sizeof(FixedUserInfo), &ReturnLength))
811 {
812 if (sizeof(FixedUserInfo) < ReturnLength)
813 {
814 UserInfo = HeapAlloc(Win32CsrApiHeap, 0, ReturnLength);
815 if (NULL == UserInfo)
816 {
817 DPRINT1("Unable to allocate %u bytes for user info\n",
818 (unsigned) ReturnLength);
819 CloseHandle(CallerToken);
820 return STATUS_NO_MEMORY;
821 }
822 if (! GetTokenInformation(CallerToken, TokenUser, UserInfo,
823 ReturnLength, &ReturnLength))
824 {
825 DPRINT1("GetTokenInformation failed with error %d\n",
826 GetLastError());
827 HeapFree(Win32CsrApiHeap, 0, UserInfo);
828 CloseHandle(CallerToken);
829 return STATUS_UNSUCCESSFUL;
830 }
831 }
832 else
833 {
834 DPRINT1("GetTokenInformation failed with error %d\n", GetLastError());
835 CloseHandle(CallerToken);
836 return STATUS_UNSUCCESSFUL;
837 }
838 }
839 else
840 {
841 UserInfo = (TOKEN_USER *) FixedUserInfo;
842 }
843 CloseHandle(CallerToken);
844 LoadShutdownSettings(UserInfo->User.Sid, &ShutdownSettings);
845 if (UserInfo != (TOKEN_USER *) FixedUserInfo)
846 {
847 HeapFree(Win32CsrApiHeap, 0, UserInfo);
848 }
849 Context.CsrssProcess = GetCurrentProcessId();
850 ShellWnd = GetShellWindow();
851 if (NULL == ShellWnd)
852 {
853 DPRINT("No shell present\n");
854 Context.ShellProcess = 0;
855 }
856 else if (0 == GetWindowThreadProcessId(ShellWnd, &Context.ShellProcess))
857 {
858 DPRINT1("Can't get process id of shell window\n");
859 Context.ShellProcess = 0;
860 }
861
862 Status = Win32CsrEnumProcesses(ExitReactosProcessEnum, &Context);
863 if (! NT_SUCCESS(Status))
864 {
865 DPRINT1("Failed to enumerate registered processes, status 0x%x\n",
866 Status);
867 if (NULL != Context.ProcessData)
868 {
869 HeapFree(Win32CsrApiHeap, 0, Context.ProcessData);
870 }
871 return Status;
872 }
873
874 qsort(Context.ProcessData, Context.ProcessCount, sizeof(PCSRSS_PROCESS_DATA),
875 ProcessDataCompare);
876
877 /* Terminate processes, stop if we find one kicking and screaming it doesn't
878 want to die */
879 Status = STATUS_SUCCESS;
880 for (ProcessIndex = 0;
881 ProcessIndex < Context.ProcessCount && NT_SUCCESS(Status);
882 ProcessIndex++)
883 {
884 if (! NotifyAndTerminateProcess(Context.ProcessData[ProcessIndex],
885 &ShutdownSettings, Flags))
886 {
887 Status = STATUS_REQUEST_ABORTED;
888 }
889 }
890
891 /* Cleanup */
892 if (NULL != Context.ProcessData)
893 {
894 HeapFree(Win32CsrApiHeap, 0, Context.ProcessData);
895 }
896
897 return Status;
898 }
899
900 static NTSTATUS FASTCALL
901 UserExitReactos(DWORD UserProcessId, UINT Flags)
902 {
903 NTSTATUS Status;
904
905 if (NULL == LogonNotifyWindow)
906 {
907 DPRINT1("No LogonNotifyWindow registered\n");
908 return STATUS_NOT_FOUND;
909 }
910
911 /* FIXME Inside 2000 says we should impersonate the caller here */
912 Status = SendMessageW(LogonNotifyWindow, PM_WINLOGON_EXITWINDOWS,
913 (WPARAM) UserProcessId,
914 (LPARAM) Flags);
915 /* If the message isn't handled, the return value is 0, so 0 doesn't indicate
916 success. Success is indicated by a 1 return value, if anything besides 0
917 or 1 it's a NTSTATUS value */
918 if (1 == Status)
919 {
920 Status = STATUS_SUCCESS;
921 }
922 else if (0 == Status)
923 {
924 Status = STATUS_NOT_IMPLEMENTED;
925 }
926
927 return Status;
928 }
929
930 CSR_API(CsrExitReactos)
931 {
932 Request->Header.u1.s1.TotalLength = sizeof(CSR_API_MESSAGE);
933 Request->Header.u1.s1.DataLength = sizeof(CSR_API_MESSAGE) -
934 sizeof(PORT_MESSAGE);
935
936 if (0 == (Request->Data.ExitReactosRequest.Flags & EWX_INTERNAL_FLAG))
937 {
938 Request->Status = UserExitReactos((DWORD) Request->Header.ClientId.UniqueProcess,
939 Request->Data.ExitReactosRequest.Flags);
940 }
941 else
942 {
943 Request->Status = InternalExitReactos((DWORD) Request->Header.ClientId.UniqueProcess,
944 (DWORD) Request->Header.ClientId.UniqueThread,
945 Request->Data.ExitReactosRequest.Flags);
946 }
947
948 return Request->Status;
949 }
950
951 /* EOF */