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