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