12f3a123624c460ce77c09ad6722bd4165dbd112
[reactos.git] / reactos / win32ss / user / winsrv / usersrv / shutdown.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS User API Server DLL
4 * FILE: win32ss/user/winsrv/usersrv/shutdown.c
5 * PURPOSE: Logout/shutdown
6 * PROGRAMMERS:
7 *
8 * NOTE: The shutdown code must be rewritten completely. (hbelusca)
9 */
10
11 /* INCLUDES *******************************************************************/
12
13 #include "usersrv.h"
14 #include <sddl.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 /* GLOBALS ********************************************************************/
20
21 typedef struct tagSHUTDOWN_SETTINGS
22 {
23 BOOL AutoEndTasks;
24 DWORD HungAppTimeout;
25 DWORD WaitToKillAppTimeout;
26 } SHUTDOWN_SETTINGS, *PSHUTDOWN_SETTINGS;
27
28 #define DEFAULT_AUTO_END_TASKS FALSE
29 #define DEFAULT_HUNG_APP_TIMEOUT 5000
30 #define DEFAULT_WAIT_TO_KILL_APP_TIMEOUT 20000
31
32 typedef struct tagNOTIFY_CONTEXT
33 {
34 DWORD ProcessId;
35 UINT Msg;
36 WPARAM wParam;
37 LPARAM lParam;
38 HDESK Desktop;
39 HDESK OldDesktop;
40 DWORD StartTime;
41 DWORD QueryResult;
42 HWND Dlg;
43 DWORD EndNowResult;
44 BOOL ShowUI;
45 HANDLE UIThread;
46 HWND WndClient;
47 PSHUTDOWN_SETTINGS ShutdownSettings;
48 LPTHREAD_START_ROUTINE SendMessageProc;
49 } NOTIFY_CONTEXT, *PNOTIFY_CONTEXT;
50
51 #define QUERY_RESULT_ABORT 0
52 #define QUERY_RESULT_CONTINUE 1
53 #define QUERY_RESULT_TIMEOUT 2
54 #define QUERY_RESULT_ERROR 3
55 #define QUERY_RESULT_FORCE 4
56
57 typedef void (WINAPI *INITCOMMONCONTROLS_PROC)(void);
58
59 typedef struct tagMESSAGE_CONTEXT
60 {
61 HWND Wnd;
62 UINT Msg;
63 WPARAM wParam;
64 LPARAM lParam;
65 DWORD Timeout;
66 } MESSAGE_CONTEXT, *PMESSAGE_CONTEXT;
67
68 typedef struct tagPROCESS_ENUM_CONTEXT
69 {
70 UINT ProcessCount;
71 PCSR_PROCESS *ProcessData;
72 TOKEN_ORIGIN TokenOrigin;
73 DWORD ShellProcess;
74 DWORD CsrssProcess;
75 } PROCESS_ENUM_CONTEXT, *PPROCESS_ENUM_CONTEXT;
76
77
78 /* FUNCTIONS ******************************************************************/
79
80 /*
81 NTSTATUS FASTCALL
82 Win32CsrEnumProcesses(CSRSS_ENUM_PROCESS_PROC EnumProc,
83 PVOID Context)
84 {
85 return CsrEnumProcesses(EnumProc, Context);
86 }
87 */
88
89 static void FASTCALL
90 UpdateProgressBar(HWND ProgressBar, PNOTIFY_CONTEXT NotifyContext)
91 {
92 DWORD Passed;
93
94 Passed = GetTickCount() - NotifyContext->StartTime;
95 Passed -= NotifyContext->ShutdownSettings->HungAppTimeout;
96 if (NotifyContext->ShutdownSettings->WaitToKillAppTimeout < Passed)
97 {
98 Passed = NotifyContext->ShutdownSettings->WaitToKillAppTimeout;
99 }
100 SendMessageW(ProgressBar, PBM_SETPOS, Passed / 2, 0);
101 }
102
103 static INT_PTR CALLBACK
104 EndNowDlgProc(HWND Dlg, UINT Msg, WPARAM wParam, LPARAM lParam)
105 {
106 INT_PTR Result;
107 PNOTIFY_CONTEXT NotifyContext;
108 HWND ProgressBar;
109 DWORD TitleLength;
110 int Len;
111 LPWSTR Title;
112
113 switch(Msg)
114 {
115 case WM_INITDIALOG:
116 NotifyContext = (PNOTIFY_CONTEXT) lParam;
117 NotifyContext->EndNowResult = QUERY_RESULT_ABORT;
118 SetWindowLongPtrW(Dlg, DWLP_USER, (LONG_PTR) lParam);
119 TitleLength = SendMessageW(NotifyContext->WndClient, WM_GETTEXTLENGTH,
120 0, 0) +
121 GetWindowTextLengthW(Dlg);
122 Title = HeapAlloc(UserServerHeap, 0, (TitleLength + 1) * sizeof(WCHAR));
123 if (NULL != Title)
124 {
125 Len = GetWindowTextW(Dlg, Title, TitleLength + 1);
126 SendMessageW(NotifyContext->WndClient, WM_GETTEXT,
127 TitleLength + 1 - Len, (LPARAM) (Title + Len));
128 SetWindowTextW(Dlg, Title);
129 HeapFree(UserServerHeap, 0, Title);
130 }
131 ProgressBar = GetDlgItem(Dlg, IDC_PROGRESS);
132 SendMessageW(ProgressBar, PBM_SETRANGE32, 0,
133 NotifyContext->ShutdownSettings->WaitToKillAppTimeout / 2);
134 UpdateProgressBar(ProgressBar, NotifyContext);
135 SetTimer(Dlg, 0, 200, NULL);
136 Result = FALSE;
137 break;
138
139 case WM_TIMER:
140 NotifyContext = (PNOTIFY_CONTEXT) GetWindowLongPtrW(Dlg, DWLP_USER);
141 ProgressBar = GetDlgItem(Dlg, IDC_PROGRESS);
142 UpdateProgressBar(ProgressBar, NotifyContext);
143 Result = TRUE;
144 break;
145
146 case WM_COMMAND:
147 if (BN_CLICKED == HIWORD(wParam) && IDC_END_NOW == LOWORD(wParam))
148 {
149 NotifyContext = (PNOTIFY_CONTEXT) GetWindowLongPtrW(Dlg, DWLP_USER);
150 NotifyContext->EndNowResult = QUERY_RESULT_FORCE;
151 SendMessageW(Dlg, WM_CLOSE, 0, 0);
152 Result = TRUE;
153 }
154 else
155 {
156 Result = FALSE;
157 }
158 break;
159
160 case WM_CLOSE:
161 DestroyWindow(Dlg);
162 Result = TRUE;
163 break;
164
165 case WM_DESTROY:
166 NotifyContext = (PNOTIFY_CONTEXT) GetWindowLongPtrW(Dlg, DWLP_USER);
167 NotifyContext->Dlg = NULL;
168 KillTimer(Dlg, 0);
169 PostQuitMessage(NotifyContext->EndNowResult);
170 Result = TRUE;
171 break;
172
173 default:
174 Result = FALSE;
175 break;
176 }
177
178 return Result;
179 }
180
181 static void
182 CallInitCommonControls()
183 {
184 static BOOL Initialized = FALSE;
185 HMODULE Lib;
186 INITCOMMONCONTROLS_PROC InitProc;
187
188 if (Initialized) return;
189
190 Lib = LoadLibraryW(L"COMCTL32.DLL");
191 if (NULL == Lib) return;
192
193 InitProc = (INITCOMMONCONTROLS_PROC) GetProcAddress(Lib, "InitCommonControls");
194 if (NULL == InitProc) return;
195
196 (*InitProc)();
197
198 Initialized = TRUE;
199 }
200
201 static DWORD WINAPI
202 EndNowThreadProc(LPVOID Parameter)
203 {
204 PNOTIFY_CONTEXT NotifyContext = (PNOTIFY_CONTEXT) Parameter;
205 MSG Msg;
206
207 SetThreadDesktop(NotifyContext->Desktop);
208 SwitchDesktop(NotifyContext->Desktop);
209 CallInitCommonControls();
210 NotifyContext->Dlg = CreateDialogParam(UserServerDllInstance,
211 MAKEINTRESOURCE(IDD_END_NOW), NULL,
212 EndNowDlgProc, (LPARAM) NotifyContext);
213 if (NULL == NotifyContext->Dlg)
214 {
215 return 0;
216 }
217 ShowWindow(NotifyContext->Dlg, SW_SHOWNORMAL);
218
219 while (GetMessageW(&Msg, NULL, 0, 0))
220 {
221 if (! IsDialogMessage(NotifyContext->Dlg, &Msg))
222 {
223 TranslateMessage(&Msg);
224 DispatchMessageW(&Msg);
225 }
226 }
227
228 return Msg.wParam;
229 }
230
231 static DWORD WINAPI
232 SendQueryEndSession(LPVOID Parameter)
233 {
234 PMESSAGE_CONTEXT Context = (PMESSAGE_CONTEXT) Parameter;
235 DWORD_PTR Result;
236
237 if (SendMessageTimeoutW(Context->Wnd, WM_QUERYENDSESSION, Context->wParam,
238 Context->lParam, SMTO_NORMAL, Context->Timeout,
239 &Result))
240 {
241 return Result ? QUERY_RESULT_CONTINUE : QUERY_RESULT_ABORT;
242 }
243
244 return 0 == GetLastError() ? QUERY_RESULT_TIMEOUT : QUERY_RESULT_ERROR;
245 }
246
247 static DWORD WINAPI
248 SendEndSession(LPVOID Parameter)
249 {
250 PMESSAGE_CONTEXT Context = (PMESSAGE_CONTEXT) Parameter;
251 DWORD_PTR Result;
252
253 if (Context->wParam)
254 {
255 if (SendMessageTimeoutW(Context->Wnd, WM_ENDSESSION, Context->wParam,
256 Context->lParam, SMTO_NORMAL, Context->Timeout,
257 &Result))
258 {
259 return QUERY_RESULT_CONTINUE;
260 }
261 return 0 == GetLastError() ? QUERY_RESULT_TIMEOUT : QUERY_RESULT_ERROR;
262 }
263 else
264 {
265 SendMessage(Context->Wnd, WM_ENDSESSION, Context->wParam,
266 Context->lParam);
267 return QUERY_RESULT_CONTINUE;
268 }
269 }
270
271 static BOOL CALLBACK
272 NotifyTopLevelEnum(HWND Wnd, LPARAM lParam)
273 {
274 PNOTIFY_CONTEXT NotifyContext = (PNOTIFY_CONTEXT) lParam;
275 MESSAGE_CONTEXT MessageContext;
276 DWORD Now, Passed;
277 DWORD Timeout, WaitStatus;
278 DWORD ProcessId;
279 HANDLE MessageThread;
280 HANDLE Threads[2];
281
282 if (0 == GetWindowThreadProcessId(Wnd, &ProcessId))
283 {
284 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
285 return FALSE;
286 }
287
288 if (ProcessId == NotifyContext->ProcessId)
289 {
290 Now = GetTickCount();
291 if (0 == NotifyContext->StartTime)
292 {
293 NotifyContext->StartTime = Now;
294 }
295 /* Note: Passed is computed correctly even when GetTickCount() wraps due
296 to unsigned arithmetic */
297 Passed = Now - NotifyContext->StartTime;
298 MessageContext.Wnd = Wnd;
299 MessageContext.Msg = NotifyContext->Msg;
300 MessageContext.wParam = NotifyContext->wParam;
301 MessageContext.lParam = NotifyContext->lParam;
302 MessageContext.Timeout = NotifyContext->ShutdownSettings->HungAppTimeout;
303 if (! NotifyContext->ShutdownSettings->AutoEndTasks)
304 {
305 MessageContext.Timeout += NotifyContext->ShutdownSettings->WaitToKillAppTimeout;
306 }
307 if (Passed < MessageContext.Timeout)
308 {
309 MessageContext.Timeout -= Passed;
310 MessageThread = CreateThread(NULL, 0, NotifyContext->SendMessageProc,
311 (LPVOID) &MessageContext, 0, NULL);
312 if (NULL == MessageThread)
313 {
314 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
315 return FALSE;
316 }
317 Timeout = NotifyContext->ShutdownSettings->HungAppTimeout;
318 if (Passed < Timeout)
319 {
320 Timeout -= Passed;
321 WaitStatus = WaitForSingleObjectEx(MessageThread, Timeout, FALSE);
322 }
323 else
324 {
325 WaitStatus = WAIT_TIMEOUT;
326 }
327 if (WAIT_TIMEOUT == WaitStatus)
328 {
329 NotifyContext->WndClient = Wnd;
330 if (NULL == NotifyContext->UIThread && NotifyContext->ShowUI)
331 {
332 NotifyContext->UIThread = CreateThread(NULL, 0,
333 EndNowThreadProc,
334 (LPVOID) NotifyContext,
335 0, NULL);
336 }
337 Threads[0] = MessageThread;
338 Threads[1] = NotifyContext->UIThread;
339 WaitStatus = WaitForMultipleObjectsEx(NULL == NotifyContext->UIThread ?
340 1 : 2,
341 Threads, FALSE, INFINITE,
342 FALSE);
343 if (WAIT_OBJECT_0 == WaitStatus)
344 {
345 if (! GetExitCodeThread(MessageThread, &NotifyContext->QueryResult))
346 {
347 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
348 }
349 }
350 else if (WAIT_OBJECT_0 + 1 == WaitStatus)
351 {
352 if (! GetExitCodeThread(NotifyContext->UIThread,
353 &NotifyContext->QueryResult))
354 {
355 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
356 }
357 }
358 else
359 {
360 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
361 }
362 if (WAIT_OBJECT_0 != WaitStatus)
363 {
364 TerminateThread(MessageThread, QUERY_RESULT_TIMEOUT);
365 }
366 }
367 else if (WAIT_OBJECT_0 == WaitStatus)
368 {
369 if (! GetExitCodeThread(MessageThread,
370 &NotifyContext->QueryResult))
371 {
372 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
373 }
374 }
375 else
376 {
377 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
378 }
379 CloseHandle(MessageThread);
380 }
381 else
382 {
383 NotifyContext->QueryResult = QUERY_RESULT_TIMEOUT;
384 }
385 }
386
387 return QUERY_RESULT_CONTINUE == NotifyContext->QueryResult;
388 }
389
390 static BOOL CALLBACK
391 NotifyDesktopEnum(LPWSTR DesktopName, LPARAM lParam)
392 {
393 PNOTIFY_CONTEXT Context = (PNOTIFY_CONTEXT) lParam;
394
395 Context->Desktop = OpenDesktopW(DesktopName, 0, FALSE,
396 DESKTOP_ENUMERATE | DESKTOP_SWITCHDESKTOP);
397 if (NULL == Context->Desktop)
398 {
399 DPRINT1("OpenDesktop failed with error %d\n", GetLastError());
400 Context->QueryResult = QUERY_RESULT_ERROR;
401 return FALSE;
402 }
403
404 Context->OldDesktop = GetThreadDesktop(GetCurrentThreadId());
405 SwitchDesktop(Context->Desktop);
406
407 EnumDesktopWindows(Context->Desktop, NotifyTopLevelEnum, lParam);
408
409 SwitchDesktop(Context->OldDesktop);
410
411 CloseDesktop(Context->Desktop);
412
413 return QUERY_RESULT_CONTINUE == Context->QueryResult;
414 }
415
416 static BOOL FASTCALL
417 NotifyTopLevelWindows(PNOTIFY_CONTEXT Context)
418 {
419 HWINSTA WindowStation;
420
421 WindowStation = GetProcessWindowStation();
422 if (NULL == WindowStation)
423 {
424 DPRINT1("GetProcessWindowStation failed with error %d\n", GetLastError());
425 return TRUE;
426 }
427
428 EnumDesktopsW(WindowStation, NotifyDesktopEnum, (LPARAM) Context);
429
430 return TRUE;
431 }
432
433 static BOOL
434 DtbgIsDesktopVisible(VOID)
435 {
436 return !((BOOL)NtUserCallNoParam(NOPARAM_ROUTINE_ISCONSOLEMODE));
437 }
438
439 /* TODO: Find an other way to do it. */
440 #if 0
441 VOID FASTCALL
442 ConioConsoleCtrlEventTimeout(DWORD Event, PCSR_PROCESS ProcessData, DWORD Timeout)
443 {
444 HANDLE Thread;
445
446 DPRINT("ConioConsoleCtrlEvent Parent ProcessId = %x\n", ProcessData->ClientId.UniqueProcess);
447
448 if (ProcessData->CtrlDispatcher)
449 {
450
451 Thread = CreateRemoteThread(ProcessData->ProcessHandle, NULL, 0,
452 (LPTHREAD_START_ROUTINE) ProcessData->CtrlDispatcher,
453 UlongToPtr(Event), 0, NULL);
454 if (NULL == Thread)
455 {
456 DPRINT1("Failed thread creation (Error: 0x%x)\n", GetLastError());
457 return;
458 }
459 WaitForSingleObject(Thread, Timeout);
460 CloseHandle(Thread);
461 }
462 }
463 #endif
464 /************************************************/
465
466 static BOOL FASTCALL
467 NotifyAndTerminateProcess(PCSR_PROCESS 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 // TODO: Find an other way whether or not the process has a console.
480 #if 0
481 if (NULL != ProcessData->Console)
482 {
483 ConioConsoleCtrlEventTimeout(CTRL_LOGOFF_EVENT, ProcessData,
484 ShutdownSettings->WaitToKillAppTimeout);
485 }
486 else
487 #endif
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 #if 0
549 static NTSTATUS WINAPI
550 ExitReactosProcessEnum(PCSR_PROCESS ProcessData, PVOID Data)
551 {
552 HANDLE Process;
553 HANDLE Token;
554 TOKEN_ORIGIN Origin;
555 DWORD ReturnLength;
556 PPROCESS_ENUM_CONTEXT Context = (PPROCESS_ENUM_CONTEXT) Data;
557 PCSR_PROCESS *NewData;
558
559 /* Do not kill winlogon or csrss */
560 if ((DWORD_PTR) ProcessData->ClientId.UniqueProcess == Context->CsrssProcess ||
561 ProcessData->ClientId.UniqueProcess == LogonProcessId)
562 {
563 return STATUS_SUCCESS;
564 }
565
566 /* Get the login session of this process */
567 Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE,
568 (DWORD_PTR) ProcessData->ClientId.UniqueProcess);
569 if (NULL == Process)
570 {
571 DPRINT1("Unable to open process %d, error %d\n", ProcessData->ClientId.UniqueProcess,
572 GetLastError());
573 return STATUS_UNSUCCESSFUL;
574 }
575
576 if (! OpenProcessToken(Process, TOKEN_QUERY, &Token))
577 {
578 DPRINT1("Unable to open token for process %d, error %d\n",
579 ProcessData->ClientId.UniqueProcess, GetLastError());
580 CloseHandle(Process);
581 return STATUS_UNSUCCESSFUL;
582 }
583 CloseHandle(Process);
584
585 if (! GetTokenInformation(Token, TokenOrigin, &Origin,
586 sizeof(TOKEN_ORIGIN), &ReturnLength))
587 {
588 DPRINT1("GetTokenInformation failed for process %d with error %d\n",
589 ProcessData->ClientId.UniqueProcess, GetLastError());
590 CloseHandle(Token);
591 return STATUS_UNSUCCESSFUL;
592 }
593 CloseHandle(Token);
594
595 /* This process will be killed if it's in the correct logon session */
596 if (RtlEqualLuid(&(Context->TokenOrigin.OriginatingLogonSession),
597 &(Origin.OriginatingLogonSession)))
598 {
599 /* Kill the shell process last */
600 if ((DWORD_PTR) ProcessData->ClientId.UniqueProcess == Context->ShellProcess)
601 {
602 ProcessData->ShutdownLevel = 0;
603 }
604 NewData = HeapAlloc(UserServerHeap, 0, (Context->ProcessCount + 1)
605 * sizeof(PCSR_PROCESS));
606 if (NULL == NewData)
607 {
608 return STATUS_NO_MEMORY;
609 }
610 if (0 != Context->ProcessCount)
611 {
612 memcpy(NewData, Context->ProcessData,
613 Context->ProcessCount * sizeof(PCSR_PROCESS));
614 HeapFree(UserServerHeap, 0, Context->ProcessData);
615 }
616 Context->ProcessData = NewData;
617 Context->ProcessData[Context->ProcessCount] = ProcessData;
618 Context->ProcessCount++;
619 }
620
621 return STATUS_SUCCESS;
622 }
623 #endif
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 GetShutdownSettings(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("GetShutdownSettings 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(UserServerHeap, 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(UserServerHeap, 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) GetShutdownSettings(DesktopKey, L"AutoEndTasks",
743 (DWORD) DEFAULT_AUTO_END_TASKS);
744 ShutdownSettings->HungAppTimeout = GetShutdownSettings(DesktopKey,
745 L"HungAppTimeout",
746 DEFAULT_HUNG_APP_TIMEOUT);
747 ShutdownSettings->WaitToKillAppTimeout = GetShutdownSettings(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) LogonProcessId)
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(UserServerHeap, 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(UserServerHeap, 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(UserServerHeap, 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(UserServerHeap, 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(UserServerHeap, 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 /* FIXME Inside 2000 says we should impersonate the caller here */
897 Status = NtUserCallTwoParam(UserProcessId, Flags, TWOPARAM_ROUTINE_EXITREACTOS);
898
899 /* If the message isn't handled, the return value is 0, so 0 doesn't indicate
900 success. Success is indicated by a 1 return value, if anything besides 0
901 or 1 it's a NTSTATUS value */
902 if (1 == Status)
903 {
904 Status = STATUS_SUCCESS;
905 }
906 else if (0 == Status)
907 {
908 Status = STATUS_NOT_IMPLEMENTED;
909 }
910
911 return Status;
912 }
913
914
915 /* PUBLIC SERVER APIS *********************************************************/
916
917 CSR_API(SrvExitWindowsEx)
918 {
919 PUSER_EXIT_REACTOS ExitReactosRequest = &((PUSER_API_MESSAGE)ApiMessage)->Data.ExitReactosRequest;
920
921 if (0 == (ExitReactosRequest->Flags & EWX_INTERNAL_FLAG))
922 {
923 return UserExitReactos((DWORD_PTR) ApiMessage->Header.ClientId.UniqueProcess,
924 ExitReactosRequest->Flags);
925 }
926 else
927 {
928 return InternalExitReactos((DWORD_PTR) ApiMessage->Header.ClientId.UniqueProcess,
929 (DWORD_PTR) ApiMessage->Header.ClientId.UniqueThread,
930 ExitReactosRequest->Flags);
931 }
932 }
933
934 /* EOF */