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