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