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