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