Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

exitwin.c

Go to the documentation of this file.
00001 /**************************** Module Header ********************************\ 00002 * Module Name: exitwin.c 00003 * 00004 * Copyright (c) 1985 - 1999, Microsoft Corporation 00005 * 00006 * NT: Logoff user 00007 * DOS: Exit windows 00008 * 00009 * History: 00010 * 07-23-92 ScottLu Created. 00011 \***************************************************************************/ 00012 00013 #include "precomp.h" 00014 #pragma hdrstop 00015 00016 #define OPTIONMASK (EWX_SHUTDOWN | EWX_REBOOT | EWX_FORCE) 00017 00018 /* 00019 * Globals local to this file only 00020 */ 00021 PWINDOWSTATION gpwinstaLogoff; 00022 DWORD gdwLocks; 00023 DWORD gdwShutdownFlags; 00024 HANDLE gpidEndSession; 00025 00026 extern PSECURITY_DESCRIPTOR gpsdInitWinSta; 00027 00028 /* 00029 * Called by ExitWindowsEx() to check whether the thread is permitted to logoff 00030 * If it is, and this is WinLogon calling, then also save any of the user's 00031 * setting that have not yet been stored in the profile. 00032 */ 00033 BOOL PrepareForLogoff( 00034 UINT uFlags) 00035 { 00036 PTHREADINFO ptiCurrent = PtiCurrent(); 00037 00038 CheckCritIn(); 00039 00040 if (ptiCurrent->TIF_flags & TIF_RESTRICTED) { 00041 PW32JOB pW32Job; 00042 00043 pW32Job = ptiCurrent->ppi->pW32Job; 00044 00045 UserAssert(pW32Job != NULL); 00046 00047 if (pW32Job->restrictions & JOB_OBJECT_UILIMIT_EXITWINDOWS) { 00048 // Not permitted to ExitWindows. 00049 return FALSE; 00050 } 00051 } 00052 00053 /* 00054 * There are no restrictions, or the restriction do not deny shutdown: 00055 * The caller is about to ExitWindowsEx via CSR, so save the volatile 00056 * elements of the User preferences in their profile 00057 */ 00058 if (ptiCurrent->pEThread->Cid.UniqueProcess == gpidLogon) { 00059 /* 00060 * Save the current user's NumLock state 00061 */ 00062 TL tlName; 00063 PUNICODE_STRING pProfileUserName = CreateProfileUserName(&tlName); 00064 RegisterPerUserKeyboardIndicators(pProfileUserName); 00065 FreeProfileUserName(pProfileUserName, &tlName); 00066 } 00067 00068 return TRUE; 00069 UNREFERENCED_PARAMETER(uFlags); 00070 } 00071 00072 /* 00073 * Bug 294204 - joejo 00074 * Added an extra parameter to NotifyLogon so that we could 00075 * tell people that the logon/logoff was cancelled. 00076 */ 00077 BOOL NotifyLogon( 00078 PWINDOWSTATION pwinsta, 00079 PLUID pluidCaller, 00080 DWORD dwFlags, 00081 NTSTATUS StatusCode) 00082 { 00083 BOOL fNotified = FALSE; 00084 DWORD dwllParam; 00085 DWORD dwStatus; 00086 00087 if (!(dwFlags & EWX_NONOTIFY)) { 00088 00089 if (dwFlags & EWX_CANCELED) { 00090 dwllParam = LOGON_LOGOFFCANCELED; 00091 dwStatus = StatusCode; 00092 } else { 00093 dwllParam = LOGON_LOGOFF; 00094 dwStatus = dwFlags; 00095 } 00096 00097 if (dwFlags & EWX_SHUTDOWN) { 00098 /* 00099 * Post the message to the global logon notify window 00100 */ 00101 if (gspwndLogonNotify != NULL) { 00102 _PostMessage(gspwndLogonNotify, WM_LOGONNOTIFY, 00103 dwllParam, (LONG)dwStatus); 00104 fNotified = TRUE; 00105 } 00106 } else { 00107 if (gspwndLogonNotify != NULL && 00108 (RtlEqualLuid(&pwinsta->luidUser, pluidCaller) || 00109 RtlEqualLuid(&luidSystem, pluidCaller))) { 00110 _PostMessage(gspwndLogonNotify, WM_LOGONNOTIFY, dwllParam, 00111 (LONG)dwStatus); 00112 fNotified = TRUE; 00113 } 00114 } 00115 } 00116 return fNotified; 00117 } 00118 00119 NTSTATUS InitiateShutdown( 00120 PETHREAD Thread, 00121 PULONG lpdwFlags) 00122 { 00123 static PRIVILEGE_SET psShutdown = { 00124 1, PRIVILEGE_SET_ALL_NECESSARY, { SE_SHUTDOWN_PRIVILEGE, 0 } 00125 }; 00126 PEPROCESS Process; 00127 LUID luidCaller; 00128 PPROCESSINFO ppi; 00129 PWINDOWSTATION pwinsta; 00130 HWINSTA hwinsta; 00131 PTHREADINFO ptiClient; 00132 NTSTATUS Status; 00133 DWORD dwFlags; 00134 00135 /* 00136 * Find out the callers sid. Only want to shutdown processes in the 00137 * callers sid. 00138 */ 00139 Process = THREAD_TO_PROCESS(Thread); 00140 ptiClient = PtiFromThread(Thread); 00141 Status = GetProcessLuid(Thread, &luidCaller); 00142 00143 if (!NT_SUCCESS(Status)) { 00144 return Status; 00145 } 00146 00147 /* 00148 * Set the system flag if the caller is a system process. 00149 * Winlogon uses this to determine in which context to perform 00150 * a shutdown operation. 00151 */ 00152 dwFlags = *lpdwFlags; 00153 if (RtlEqualLuid(&luidCaller, &luidSystem)) { 00154 dwFlags |= EWX_SYSTEM_CALLER; 00155 } else { 00156 dwFlags &= ~EWX_SYSTEM_CALLER; 00157 } 00158 00159 /* 00160 * Find a windowstation. If the process does not have one 00161 * assigned, use the standard one. 00162 */ 00163 ppi = PpiFromProcess(Process); 00164 if (ppi == NULL) { 00165 /* 00166 * We ran into a case where the thread was terminated and had already 00167 * been cleaned up by USER. Thus, the ppi and ptiClient was NULL. 00168 */ 00169 return STATUS_INVALID_HANDLE; 00170 } 00171 pwinsta = ppi->rpwinsta; 00172 hwinsta = ppi->hwinsta; 00173 /* 00174 * If we're not being called by Winlogon, validate the call and 00175 * notify the logon process to do the actual shutdown. 00176 */ 00177 if (Thread->Cid.UniqueProcess != gpidLogon) { 00178 dwFlags &= ~EWX_WINLOGON_CALLER; 00179 *lpdwFlags = dwFlags; 00180 00181 if (pwinsta == NULL) { 00182 #ifndef LATER 00183 return STATUS_INVALID_HANDLE; 00184 #else 00185 hwinsta = ppi->pOpenObjectTable[HI_WINDOWSTATION].h; 00186 if (hwinsta == NULL) { 00187 return STATUS_INVALID_HANDLE; 00188 } 00189 pwinsta = (PWINDOWSTATION)ppi->pOpenObjectTable[HI_WINDOWSTATION].phead; 00190 #endif 00191 } 00192 00193 /* 00194 * Check security first - does this thread have access? 00195 */ 00196 if (!RtlAreAllAccessesGranted(ppi->amwinsta, WINSTA_EXITWINDOWS)) { 00197 return STATUS_ACCESS_DENIED; 00198 } 00199 00200 /* 00201 * If the client requested shutdown, reboot, or poweroff they must have 00202 * the shutdown privilege. 00203 */ 00204 if (dwFlags & EWX_SHUTDOWN) { 00205 if (!IsPrivileged(&psShutdown) ) { 00206 return STATUS_PRIVILEGE_NOT_HELD; 00207 } 00208 } else { 00209 00210 /* 00211 * If this is a non-IO windowstation and we are not shutting down, 00212 * fail the call. 00213 */ 00214 if (pwinsta->dwWSF_Flags & WSF_NOIO) { 00215 return STATUS_INVALID_DEVICE_REQUEST; 00216 } 00217 } 00218 } 00219 00220 /* 00221 * Is there a shutdown already in progress? 00222 */ 00223 if (gdwThreadEndSession != 0) { 00224 DWORD dwNew; 00225 00226 /* 00227 * If the current shutdown in another sid and is not being done by 00228 * winlogon, override it. 00229 */ 00230 if (!RtlEqualLuid(&luidCaller, &gpwinstaLogoff->luidEndSession) && 00231 (gpidEndSession != gpidLogon)) { 00232 return STATUS_RETRY; 00233 } 00234 00235 /* 00236 * Calculate new flags 00237 */ 00238 dwNew = dwFlags & OPTIONMASK & (~gdwShutdownFlags); 00239 00240 /* 00241 * Should we override the other shutdown? Make sure 00242 * winlogon does not recurse. 00243 */ 00244 if (dwNew && HandleToUlong(PsGetCurrentThread()->Cid.UniqueThread) != 00245 gdwThreadEndSession) { 00246 /* 00247 * Only one windowstation can be logged off at a time. 00248 */ 00249 if (!(dwFlags & EWX_SHUTDOWN) && 00250 pwinsta != gpwinstaLogoff) { 00251 return STATUS_DEVICE_BUSY; 00252 } 00253 00254 /* 00255 * Set the new flags 00256 */ 00257 gdwShutdownFlags = dwFlags; 00258 00259 if (dwNew & EWX_FORCE) { 00260 return STATUS_RETRY; 00261 } else { 00262 return STATUS_PENDING; 00263 } 00264 } else { 00265 /* 00266 * Don't override 00267 */ 00268 return STATUS_PENDING; 00269 } 00270 } 00271 00272 /* 00273 * If the caller is not winlogon, signal winlogon to start 00274 * the real shutdown. 00275 */ 00276 if (Thread->Cid.UniqueProcess != gpidLogon) { 00277 if (dwFlags & EWX_NOTIFY) { 00278 if (ptiClient && ptiClient->TIF_flags & TIF_16BIT) 00279 gptiShutdownNotify = ptiClient; 00280 dwFlags &= ~EWX_NOTIFY; 00281 *lpdwFlags = dwFlags; 00282 } 00283 00284 if (NotifyLogon(pwinsta, &luidCaller, dwFlags, STATUS_SUCCESS)) 00285 return STATUS_PENDING; 00286 else if (ptiClient && ptiClient->cWindows) 00287 return STATUS_CANT_WAIT; 00288 } 00289 00290 /* 00291 * Mark this thread as the one that is currently processing 00292 * exit windows, and set the global saying someone is exiting 00293 */ 00294 dwFlags |= EWX_WINLOGON_CALLER; 00295 *lpdwFlags = dwFlags; 00296 gdwShutdownFlags = dwFlags; 00297 00298 gdwThreadEndSession = HandleToUlong(PsGetCurrentThread()->Cid.UniqueThread); 00299 gpidEndSession = PsGetCurrentThread()->Cid.UniqueProcess; 00300 gpwinstaLogoff = pwinsta; 00301 pwinsta->luidEndSession = luidCaller; 00302 00303 /* 00304 * Lock the windowstation to prevent apps from starting 00305 * while we're doing shutdown processing. 00306 */ 00307 gdwLocks = pwinsta->dwWSF_Flags & (WSF_SWITCHLOCK | WSF_OPENLOCK); 00308 pwinsta->dwWSF_Flags |= (WSF_OPENLOCK | WSF_SHUTDOWN); 00309 00310 /* 00311 * Set the flag WSF_REALSHUTDOWN if we are not doing just a 00312 * logoff 00313 */ 00314 if (dwFlags & 00315 (EWX_WINLOGON_OLD_SHUTDOWN | EWX_WINLOGON_OLD_REBOOT | 00316 EWX_SHUTDOWN | EWX_REBOOT)) { 00317 00318 pwinsta->dwWSF_Flags |= WSF_REALSHUTDOWN; 00319 } 00320 00321 return STATUS_SUCCESS; 00322 } 00323 00324 NTSTATUS EndShutdown( 00325 PETHREAD Thread, 00326 NTSTATUS StatusShutdown) 00327 { 00328 PWINDOWSTATION pwinsta = gpwinstaLogoff; 00329 PDESKTOP pdesk; 00330 LUID luidCaller; 00331 UserAssert(gpwinstaLogoff); 00332 00333 gpwinstaLogoff = NULL; 00334 gpidEndSession = NULL; 00335 gdwThreadEndSession = 0; 00336 pwinsta->dwWSF_Flags &= ~WSF_SHUTDOWN; 00337 00338 if (!NT_SUCCESS(GetProcessLuid(Thread, &luidCaller))) { 00339 luidCaller = RtlConvertUlongToLuid(0); // null luid 00340 } 00341 00342 if (!NT_SUCCESS(StatusShutdown)) { 00343 00344 /* 00345 * We need to notify the process that called ExitWindows that 00346 * the logoff was aborted. 00347 */ 00348 if (gptiShutdownNotify) { 00349 _PostThreadMessage(gptiShutdownNotify, WM_ENDSESSION, FALSE, 0); 00350 gptiShutdownNotify = NULL; 00351 } 00352 00353 /* 00354 * Reset the windowstation lock flags so apps can start 00355 * again. 00356 */ 00357 pwinsta->dwWSF_Flags = 00358 (pwinsta->dwWSF_Flags & ~WSF_OPENLOCK) | 00359 gdwLocks; 00360 00361 /* 00362 * Bug 294204 - joejo 00363 * Tell winlogon that we we cancelled shutdown/logoff. 00364 */ 00365 NotifyLogon(pwinsta, &luidCaller, gdwShutdownFlags | EWX_CANCELED, StatusShutdown); 00366 00367 return STATUS_SUCCESS; 00368 } 00369 00370 gptiShutdownNotify = NULL; 00371 00372 /* 00373 * If logoff is occuring for the user set by winlogon, perform 00374 * the normal logoff cleanup. Otherwise, clear the open lock 00375 * and continue. 00376 */ 00377 if (((pwinsta->luidUser.LowPart != 0) || (pwinsta->luidUser.HighPart != 0)) && 00378 RtlEqualLuid(&pwinsta->luidUser, &luidCaller)) { 00379 00380 /* 00381 * Zero out the free blocks in all desktop heaps. 00382 */ 00383 for (pdesk = pwinsta->rpdeskList; pdesk != NULL; pdesk = pdesk->rpdeskNext) { 00384 RtlZeroHeap(Win32HeapGetHandle(pdesk->pheapDesktop), 0); 00385 } 00386 00387 /* 00388 * Logoff/shutdown was successful. In case this is a logoff, remove 00389 * everything from the clipboard so the next logged on user can't get 00390 * at this stuff. 00391 */ 00392 ForceEmptyClipboard(pwinsta); 00393 00394 /* 00395 * Destroy all non-pinned atoms in the global atom table. User can't 00396 * create pinned atoms. Currently only the OLE atoms are pinned. 00397 */ 00398 RtlEmptyAtomTable(pwinsta->pGlobalAtomTable, FALSE); 00399 00400 // this code path is hit only on logoff and also on shutdown 00401 // We do not want to unload fonts twice when we attempt shutdown 00402 // so we mark that the fonts have been unloaded at a logoff time 00403 00404 if (TEST_PUDF(PUDF_FONTSARELOADED)) { 00405 LeaveCrit(); 00406 GreRemoveAllButPermanentFonts(); 00407 EnterCrit(); 00408 CLEAR_PUDF(PUDF_FONTSARELOADED); 00409 } 00410 } else { 00411 pwinsta->dwWSF_Flags &= ~WSF_OPENLOCK; 00412 } 00413 00414 /* 00415 * Tell winlogon that we successfully shutdown/logged off. 00416 */ 00417 NotifyLogon(pwinsta, &luidCaller, gdwShutdownFlags, STATUS_SUCCESS); 00418 00419 return STATUS_SUCCESS; 00420 } 00421 00422 /***************************************************************************\ 00423 * xxxClientShutdown2 00424 * 00425 * Called by xxxClientShutdown 00426 \***************************************************************************/ 00427 00428 LONG xxxClientShutdown2( 00429 PBWL pbwl, 00430 UINT msg, 00431 WPARAM wParam) 00432 { 00433 HWND *phwnd; 00434 PWND pwnd; 00435 TL tlpwnd; 00436 BOOL fEnd; 00437 PTHREADINFO ptiCurrent = PtiCurrent(); 00438 BOOL fDestroyTimers; 00439 LPARAM lParam; 00440 00441 /* 00442 * Make sure we don't send this window any more WM_TIMER 00443 * messages if the session is ending. This was causing 00444 * AfterDark to fault when it freed some memory on the 00445 * WM_ENDSESSION and then tried to reference it on the 00446 * WM_TIMER. 00447 * LATER GerardoB: Do we still need to do this?? 00448 * Do this horrible thing only if the process is in the 00449 * context being logged off. 00450 * Perhaps someday we should post a WM_CLOSE so the app 00451 * gets a better chance to clean up (if this process is in 00452 * the context being logged off, winsrv is going to call 00453 * TerminateProcess soon after this). 00454 */ 00455 fDestroyTimers = (wParam & WMCS_EXIT) && (wParam & WMCS_CONTEXTLOGOFF); 00456 00457 /* 00458 * fLogOff and fEndSession parameters (WM_ENDSESSION only) 00459 */ 00460 lParam = wParam & ENDSESSION_LOGOFF; 00461 wParam &= WMCS_EXIT; 00462 00463 /* 00464 * Now enumerate these windows and send the WM_QUERYENDSESSION or 00465 * WM_ENDSESSION messages. 00466 */ 00467 for (phwnd = pbwl->rghwnd; *phwnd != (HWND)1; phwnd++) { 00468 if ((pwnd = RevalidateHwnd(*phwnd)) == NULL) 00469 continue; 00470 00471 ThreadLockAlways(pwnd, &tlpwnd); 00472 00473 /* 00474 * Send the message. 00475 */ 00476 switch (msg) { 00477 case WM_QUERYENDSESSION: 00478 00479 /* 00480 * Windows does not send the WM_QUERYENDSESSION to the app 00481 * that called ExitWindows 00482 */ 00483 if (ptiCurrent == gptiShutdownNotify) { 00484 fEnd = TRUE; 00485 } else { 00486 fEnd = (xxxSendMessage(pwnd, WM_QUERYENDSESSION, FALSE, lParam) != 0); 00487 if (!fEnd) { 00488 RIPMSG2(RIP_WARNING, "xxxClientShutdown2: pwnd:%p canceled shutdown. lParam:%p", 00489 pwnd, lParam); 00490 } 00491 } 00492 break; 00493 00494 case WM_ENDSESSION: 00495 xxxSendMessage(pwnd, WM_ENDSESSION, wParam, lParam); 00496 fEnd = TRUE; 00497 00498 if (fDestroyTimers) { 00499 DestroyWindowsTimers(pwnd); 00500 } 00501 00502 break; 00503 } 00504 00505 ThreadUnlock(&tlpwnd); 00506 00507 if (!fEnd) 00508 return WMCSR_CANCEL; 00509 } 00510 00511 return WMCSR_ALLOWSHUTDOWN; 00512 } 00513 /***************************************************************************\ 00514 * xxxClientShutdown 00515 * 00516 * This is the processing that occurs when an application receives a 00517 * WM_CLIENTSHUTDOWN message. 00518 * 00519 * 10-01-92 ScottLu Created. 00520 \***************************************************************************/ 00521 LONG xxxClientShutdown( 00522 PWND pwnd, 00523 WPARAM wParam) 00524 { 00525 PBWL pbwl; 00526 PTHREADINFO ptiT; 00527 LONG lRet; 00528 00529 /* 00530 * Build a list of windows first. 00531 */ 00532 ptiT = GETPTI(pwnd); 00533 00534 if ((pbwl = BuildHwndList(ptiT->rpdesk->pDeskInfo->spwnd->spwndChild, 00535 BWL_ENUMLIST, ptiT)) == NULL) { 00536 /* 00537 * Can't allocate memory to notify this thread's windows of shutdown. 00538 * Can't do more than kill the app 00539 */ 00540 return WMCSR_ALLOWSHUTDOWN; 00541 } 00542 00543 if (wParam & WMCS_QUERYEND) { 00544 lRet = xxxClientShutdown2(pbwl, WM_QUERYENDSESSION, wParam); 00545 } else { 00546 xxxClientShutdown2(pbwl, WM_ENDSESSION, wParam); 00547 lRet = WMCSR_DONE; 00548 } 00549 00550 FreeHwndList(pbwl); 00551 return lRet; 00552 } 00553 00554 /***************************************************************************\ 00555 * xxxRegisterUserHungAppHandlers 00556 * 00557 * This routine simply records the WOW callback address for notification of 00558 * "hung" wow apps. 00559 * 00560 * History: 00561 * 01-Apr-1992 jonpa Created. 00562 * Added saving and duping of wowexc event handle 00563 \***************************************************************************/ 00564 00565 BOOL xxxRegisterUserHungAppHandlers( 00566 PFNW32ET pfnW32EndTask, 00567 HANDLE hEventWowExec) 00568 { 00569 BOOL bRetVal; 00570 PPROCESSINFO ppi; 00571 PWOWPROCESSINFO pwpi; 00572 00573 // 00574 // Allocate the per wow process info stuff 00575 // ensuring the memory is Zero init. 00576 // 00577 pwpi = (PWOWPROCESSINFO) UserAllocPoolWithQuotaZInit( 00578 sizeof(WOWPROCESSINFO), TAG_WOWPROCESSINFO); 00579 00580 if (!pwpi) 00581 return FALSE; 00582 00583 // 00584 // Reference the WowExec event for kernel access 00585 // 00586 bRetVal = NT_SUCCESS(ObReferenceObjectByHandle( 00587 hEventWowExec, 00588 EVENT_ALL_ACCESS, 00589 *ExEventObjectType, 00590 UserMode, 00591 &pwpi->pEventWowExec, 00592 NULL 00593 )); 00594 00595 // 00596 // if sucess then intialize the pwpi, ppi structs 00597 // else free allocated memory 00598 // 00599 if (bRetVal) { 00600 pwpi->hEventWowExecClient = hEventWowExec; 00601 pwpi->lpfnWowExitTask = pfnW32EndTask; 00602 ppi = PpiCurrent(); 00603 ppi->pwpi = pwpi; 00604 00605 // add to the list, order doesn't matter 00606 pwpi->pwpiNext = gpwpiFirstWow; 00607 gpwpiFirstWow = pwpi; 00608 00609 } 00610 else { 00611 UserFreePool(pwpi); 00612 } 00613 00614 return bRetVal; 00615 }

Generated on Sat May 15 19:39:58 2004 for test by doxygen 1.3.7