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