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