/*++ Copyright (c) 1989 Microsoft Corporation Module Name: srvsm.c Abstract: Session API Author: Mark Lucovsky (markl) 10-Jul-1990 Revision History: --*/ #define INCL_OS2V20_SESSIONMGR #define INCL_OS2V20_TASKING #define INCL_OS2V20_ERRORS #define INCL_OS2V20_EXCEPTIONS #include "os2srv.h" #define NTOS2_ONLY #include "sesport.h" #include "os2win.h" extern ULONG Os2GlobalInfoSeg; ULONG WaitOnWinSessionObject( IN ULONG Parm ); VOID StopSessionAndChildSessions( IN POS2_SESSION Session ); BOOLEAN CheckSessionIfChild( IN POS2_SESSION ParentSession, IN POS2_SESSION ChildSession ); BOOLEAN Os2WaitNewSession( IN OS2_WAIT_REASON WaitReason, IN POS2_THREAD WaitingThread, IN POS2_API_MSG WaitReplyMessage, IN PVOID WaitParameter, IN PVOID SatisfyParameter1, IN PVOID SatisfyParameter2 ); VOID Os2SessionWaitCheck( POS2_SESSION Session, ULONG RetCode ); APIRET Os2CheckIfSessionTreeInFG( IN POS2_SESSION Session ); #if DBG VOID DumpSessionEntry( IN PSZ Str, IN POS2_SESSION Session ); VOID DumpSessionTable( IN PSZ Str ); #endif POS2_SESSION Os2AllocateSession( POS2_DOSSTARTSESSION_INFO SessionInfo OPTIONAL, ULONG UniqueId, PAPIRET ApiRet ) /*++ Routine Description: This function allocates and initializes an OS/2 session control block. Arguments: SessionInfo - Supplies the session information ApiRet - If a session was not created, ApiRet returns the reason the session creation failed. Return Value: NON-NULL - Returns the address of the referenced and allocated session. NULL - No session was created --*/ { POS2_SESSION Session; POS2_QUEUE TerminationQueue = NULL; ULONG i; *ApiRet = NO_ERROR; if (UniqueId && (Session = Os2GetSessionByUniqueId(UniqueId))) { // This call is done by conthrds.c for session which was started // by DosStartSession. Instead of allocating a new session, returns // the session with the same UniqueId. return(Session); } Session = RtlAllocateHeap( Os2Heap, 0, sizeof( OS2_SESSION ) ); if ( Session == NULL ) { #if DBG IF_OS2_DEBUG( SESSIONMGR ) KdPrint(("Os2AllocateSession: cannot allocate session\n")); #endif *ApiRet = ERROR_NOT_ENOUGH_MEMORY; return NULL; } RtlZeroMemory(Session, sizeof( OS2_SESSION )); Session->ReferenceCount = 1; // // Needs more work... // session attributes, termination queue... // if ( ARGUMENT_PRESENT(SessionInfo) ) { if ( SessionInfo->QueueHandleIndex ) { SessionInfo->QueueHandleIndex &= 0x7fffffff; TerminationQueue = Os2OpenQueueByHandle( (HQUEUE)SessionInfo->QueueHandleIndex ); if ( TerminationQueue ) { Session->TerminationQueue = TerminationQueue; Session->TerminationQueueHandle = SessionInfo->QueueHandleIndex; } else { *ApiRet = ERROR_QUE_NAME_NOT_EXIST; RtlFreeHeap( Os2Heap, 0, Session ); #if DBG IF_OS2_DEBUG( SESSIONMGR ) KdPrint(("Os2AllocateSession: cannot open queue 0x%lx\n", SessionInfo->QueueHandleIndex)); #endif return NULL; } } } for ( i = 0 ; (i < OS2_MAX_SESSION) && SessionTable[i].Session ; i++ ) ; if (i == OS2_MAX_SESSION) { *ApiRet = ERROR_TOO_MANY_SESS; if ( TerminationQueue ) { Os2CloseQueueByHandle( (HQUEUE)Session->TerminationQueueHandle, 1, (PID)(-1), // (-1) means ignore this parameter Os2RootProcess); } #if DBG IF_OS2_DEBUG( SESSIONMGR ) KdPrint(("Os2AllocateSession: too many session 0x%lx\n", OS2_MAX_SESSION)); #endif RtlFreeHeap( Os2Heap, 0, Session ); return NULL; } SessionTable[i].Session = Session; Session->SessionId = i + 1; Session->Selectable = TRUE; //Session->ProcessId = 0; //Session->BindSession = NULL; //Session->RelatedSession = NULL; //Session->InheritOpt = 0; //Session->FgBg = 0; //Session->ChildSession = FALSE; //Session->ConsolePort = NULL; //Session->Thread = NULL; InitializeListHead(&(SessionTable[i].Waiters)); InsertHeadList( &Os2SessionList, &Session->SessionLink ); if ( ARGUMENT_PRESENT(SessionInfo) ) { SessionInfo->ResultSessionId = Session->SessionId; //Session->InheritOpt = SessionInfo->InheritOpt; //Session->FgBg = SessionInfo->FgBg; } #if DBG IF_OS2_DEBUG( SESSIONMGR ) { DumpSessionEntry("Os2AllocateSession", Session); DumpSessionTable("Os2AllocateSession"); } #endif return( Session ); } VOID Os2ReferenceSession( POS2_SESSION Session ) /*++ Routine Description: This function is called for each new process that does not create a new session. Its function is to increment the sessions reference count to account for the new process. Arguments: Session - Supplies the address of the session being referenced. Return Value: None. --*/ { APIRET Rc = NO_ERROR; try { if (( Session->SessionId == 0 ) || ( Session->SessionId > OS2_MAX_SESSION ) || ( Session->ReferenceCount == 0 ) || ( SessionTable[Session->SessionId - 1].Session != Session )) { ASSERT( FALSE ); Rc = ERROR_SMG_SESSION_NOT_FOUND; } } except ( EXCEPTION_EXECUTE_HANDLER ) { Rc = ERROR_SMG_SESSION_NOT_FOUND; } if (Rc) { return ; } Session->ReferenceCount++; #if DBG IF_OS2_DEBUG( SESSIONMGR ) DumpSessionEntry("Os2ReferenceSession", Session); #endif } POS2_SESSION Os2DereferenceSession( POS2_SESSION Session, POS2_TERMINATEPROCESS_MSG msg, BOOLEAN Bailout ) /*++ Routine Description: This function is called for each process termination to release the processes reference to the session. If this is the last process in a session to terminate, the reference count will go to zero freeing the session and sending a session termination status code. Arguments: Session - Supplies the address of the session being dereferenced. msg - Exit message from Client Bailout - Supplies a flag which if set inhibits reporting of session termination. Return Value: None. --*/ { OS2_DOSWRITEQUEUE_MSG a; ULONG i; APIRET Rc = NO_ERROR; ULONG SessionId; if (Session == NULL) { return NULL; } try { if (( Session->SessionId == 0 ) || ( Session->SessionId > OS2_MAX_SESSION ) || ( Session->ReferenceCount == 0 ) || ( SessionTable[Session->SessionId - 1].Session != Session )) { ASSERT( FALSE ); Rc = ERROR_SMG_SESSION_NOT_FOUND; } } except ( EXCEPTION_EXECUTE_HANDLER ) { Rc = ERROR_SMG_SESSION_NOT_FOUND; } if (Rc) { return NULL; } #if DBG IF_OS2_DEBUG( SESSIONMGR ) DumpSessionEntry("Os2DereferenceSession", Session); #endif if (--Session->ReferenceCount == 0) { #if DBG IF_OS2_DEBUG( SESSIONMGR ) KdPrint(("Os2DereferenceSession - exit\n")); #endif SessionTable[Session->SessionId - 1].Session = NULL; Session->ProcessId = 0; SessionId = Session->SessionId; Session->SessionId = 0; Session->ReferenceCount = (ULONG)-1; RemoveEntryList( &Session->SessionLink ); //if (Session->hWaitThread) //{ //NtAlertThread(Session->hWaitThread); //TerminateThread(Session->hWaitThread, 0L); //WaitForSingleObject(Session->hWaitThread, (ULONG) SEM_INDEFINITE_WAIT); //CloseHandle(Session->hWaitThread); //Session->hWaitThread = NULL; //} if (( Session->RelatedSession ) && ( Session->RelatedSession->BindSession == Session )) { Session->RelatedSession->BindSession = NULL; } for ( i = 0 ; i < OS2_MAX_SESSION ; i++ ) { if ( SessionTable[i].Session && ( SessionTable[i].Session->RelatedSession == Session )) { SessionTable[i].Session->RelatedSession = NULL; StopSessionAndChildSessions(SessionTable[i].Session); } } // // If it's a win32 session, terminate child sessions, by pid. // if (Session->WinSession) { for ( i = 0 ; i < OS2_MAX_SESSION ; i++ ) { if ( SessionTable[i].Session && ( SessionTable[i].Session->dwParentProcessId == Session->dwProcessId )) { StopSessionAndChildSessions(SessionTable[i].Session); } } } Session->dwProcessId = 0; Session->dwParentProcessId = 0; if ( Session->TerminationQueue ) { if ( !Bailout ) { // // I guess I really have to allocate vm. This is // dumb. I would propose using data/data length as // sid, result code // a.QueueHandle = (HQUEUE)Session->TerminationQueueHandle; a.SenderData = 0L; a.DataLength = 0x80000000 | SessionId; a.Data = (PVOID) msg->ExitResult; a.ElementPriority = 0; Os2WriteQueueByHandle(&a,Os2RootProcess->ProcessId); } Os2CloseQueueByHandle( (HQUEUE)Session->TerminationQueueHandle, 1, (PID)(-1), // (-1) means ignore this parameter Os2RootProcess ); } if (Session->SesGrpAddress) { NtUnmapViewOfSection( NtCurrentProcess(), Session->SesGrpAddress); } if (Session->SesGrpHandle) { NtClose( Session->SesGrpHandle ); } if ( Session->ConsolePort != NULL ) { if ( msg != NULL ) { Os2TerminateConSession(Session, msg); } NtClose(Session->ConsolePort); } if ( !IsListEmpty(&(SessionTable[Session->SessionId - 1].Waiters)) ) { Os2NotifyWait(WaitSession, (PVOID)ERROR_SYS_INTERNAL, (PVOID)ERROR_SYS_INTERNAL); } if (Os2SrvExitNow) { for ( i = 0 ; (i < OS2_MAX_SESSION) && SessionTable[i].Session ; i++ ) ; if ( i == OS2_MAX_SESSION ) { Os2SrvExitProcess(0); } } RtlFreeHeap( Os2Heap, 0, Session ); #if DBG IF_OS2_DEBUG( SESSIONMGR ) DumpSessionTable("Os2DereferenceSession"); #endif // DBG return NULL; } return Session; } POS2_SESSION Os2GetSessionByUniqueId( ULONG UniqueId ) { ULONG i; for ( i = 0 ; ( i < OS2_MAX_SESSION ) ; i++ ) { if ( SessionTable[i].Session == (POS2_SESSION)UniqueId ) { return (SessionTable[i].Session); } } return (NULL); } BOOLEAN Os2DosStartSession( IN POS2_THREAD t, IN POS2_API_MSG m ) { POS2_DOSEXECPGM_MSG a = &m->u.DosStartSession.ExecPgmInformation; POS2_DOSSTARTSESSION_INFO b = &m->u.DosStartSession.StartSessionInformation; POS2_THREAD NewThread = NULL; POS2_SESSION Session; NTSTATUS Status; APIRET RetCode; ULONG Tid; Session = Os2AllocateSession(b, 0, &RetCode); if ( !Session ) { #if DBG IF_OS2_DEBUG( SESSIONMGR ) KdPrint(("Os2DosStartSession: can't allocate\n")); #endif m->ReturnedErrorValue = RetCode; return( TRUE ); } Session->ChildSession = TRUE; Session->Thread = (PVOID)t; Session->WinSession = b->WinSession; m->ReturnedErrorValue = Os2CreateProcess( NULL, t, a, Session, &NewThread ); if ( m->ReturnedErrorValue == NO_ERROR ) { #if DBG KdPrint(( "OS2SRV: Starting new session for 16-bit program - %s\n", a->ApplName)); #endif // DBG if (b->Related) { Session->RelatedSession = t->Process->Session; t->Process->Session->BindSession = Session; } #if DBG IF_OS2_DEBUG( SESSIONMGR ) DumpSessionEntry("Os2DosStartSession(after Os2CreateProcess)", Session); #endif if(Session->WinSession) { Session->hProcess = a->hProcess; Session->dwProcessId = b->dwProcessId; if((Session->hWaitThread = CreateThread( NULL, 0, WaitOnWinSessionObject, (PVOID)Session, 0, &Tid )) == NULL) { #if DBG KdPrint(("OS2SRV: CreateThread for WinSession error %lu\n", m->ReturnedErrorValue = GetLastError())); ASSERT( FALSE ); #endif return( TRUE ); } } if (!b->FgBg) { // ForeGround OS2SESREQUESTMSG RequestMsg; RequestMsg.Session = Session; RequestMsg.d.FocusSet = 1; Os2SessionFocusSet(&RequestMsg); } Status = NtResumeThread( NewThread->ThreadHandle, NULL ); ASSERT(NT_SUCCESS(Status)); return( TRUE ); } else { #if DBG IF_OS2_DEBUG( SESSIONMGR ) KdPrint(("Os2DosStartSession: can't Os2CreateProcesst\n")); #endif //m->ReturnedErrorValue = ERROR_NOT_ENOUGH_MEMORY; //Os2DeallocateThread( NewThread ); //Os2DeallocateProcess( NewThread->Process ); //NtClose(m->u.DosExecPgm.hThread); //NtClose(m->u.DosExecPgm.hProcess); Os2DereferenceSession(Session, 0, (BOOLEAN)TRUE); return( TRUE ); } } ULONG WaitOnWinSessionObject( IN ULONG Parm ) { POS2_SESSION Session = (POS2_SESSION)Parm; ULONG ExitCode = 0; HANDLE hCurrThread = Session->hWaitThread; OS2_TERMINATEPROCESS_MSG a; NTSTATUS Status; if (NtTestAlert() != STATUS_ALERTED) { Status = NtWaitForSingleObject( Session->hProcess, TRUE, NULL ); #if DBG IF_OS2_DEBUG( SESSIONMGR ) KdPrint(("WaitOnWinSessionObject: Wait for Win32 process, status=0x%0X\n", Status)); #endif // DBG if (Status != STATUS_ALERTED) { if (!GetExitCodeProcess(Session->hProcess, &ExitCode)) { ExitCode = GetLastError(); } } else { TerminateProcess(Session->hProcess, 0); } } CloseHandle(Session->hProcess); if (Session->ReferenceCount) { Session->hWaitThread = NULL; a.ExitResult = ExitCode; a.ExitReason = 0; a.ErrorText[0] = '\0'; Os2DereferenceSession(Session, &a, FALSE); } CloseHandle(hCurrThread); ExitThread(0L); return(0L); } BOOLEAN Os2DosSelectSession( IN POS2_THREAD t, IN POS2_API_MSG m ) { ULONG SessionId = m->u.DosSelectSession.SessionId; POS2_SESSION Session; //NTSTATUS Status; //SCREQUESTMSG Request; BOOLEAN RetVal; if ( SessionId == 0 ) { Session = t->Process->Session; } else { if (( SessionId > OS2_MAX_SESSION ) || (( Session = SessionTable[SessionId - 1].Session ) == NULL ) || ( Session->RelatedSession != t->Process->Session )) { m->ReturnedErrorValue = ERROR_SMG_SESSION_NOT_PARENT; return( TRUE ); } } if (m->ReturnedErrorValue = Os2CheckIfSessionTreeInFG(t->Process->Session)) { return( TRUE ); } m->ReturnedErrorValue = NO_ERROR; if (!Session->WinSession) { if (Session->Win32ForegroundWindow != NULL) { RetVal = OpenIcon(Session->Win32ForegroundWindow) && SetForegroundWindow(Session->Win32ForegroundWindow); if (RetVal) { // ForeGround OS2SESREQUESTMSG RequestMsg; RequestMsg.Session = Session; RequestMsg.d.FocusSet = 1; Os2SessionFocusSet(&RequestMsg); } } } return( TRUE ); } BOOLEAN Os2DosSetSession( IN POS2_THREAD t, IN POS2_API_MSG m ) { POS2_DOSSETSESSION_MSG a = &m->u.DosSetSession; POS2_SESSION Session; BOOLEAN Selectable; if ( !a->SessionId || ( a->SessionId > OS2_MAX_SESSION ) || (( Session = SessionTable[a->SessionId - 1].Session ) == NULL ) || ( Session->RelatedSession != t->Process->Session )) { m->ReturnedErrorValue = ERROR_SMG_SESSION_NOT_PARENT; return( TRUE ); } if (m->ReturnedErrorValue = Os2CheckIfSessionTreeInFG(Session)) { return( TRUE ); } m->ReturnedErrorValue = NO_ERROR; if (( a->StatusData.Length >= 4 ) && ( a->StatusData.SelectInd != TARGET_UNCHANGED )) { Selectable = (BOOLEAN)((a->StatusData.SelectInd == TARGET_SELECTABLE ) ? TRUE : FALSE); if ( Selectable != Session->Selectable ) { Session->Selectable = Selectable; // BUGBUG - send request to OS2.EXE } } if (( a->StatusData.Length >= 6 ) && ( a->StatusData.BondInd != BOND_UNCHANGED )) { if ( a->StatusData.BondInd == BOND_CHILD ) { if ( t->Process->Session->BindSession != Session ) { // BUGBUG - send request to OS2.EXE t->Process->Session->BindSession = Session; } } else { if ( t->Process->Session->BindSession == NULL ) { m->ReturnedErrorValue = ERROR_SMG_NOT_BOUND; } else { // BUGBUG - send request to OS2.EXE t->Process->Session->BindSession = NULL; } } } return( TRUE ); } BOOLEAN Os2DosStopSession( IN POS2_THREAD t, IN POS2_API_MSG m ) { POS2_DOSSTOPSESSION_MSG a = &m->u.DosStopSession; POS2_SESSION Session = t->Process->Session; ULONG i; if ( a->fScope == DSS_SESSION ) { if ( !a->SessionId || ( a->SessionId > OS2_MAX_SESSION ) || (( Session = SessionTable[a->SessionId - 1].Session ) == NULL ) || ( Session->RelatedSession != t->Process->Session )) { m->ReturnedErrorValue ==ERROR_SMG_INVALID_SESSION_ID; //m->ReturnedErrorValue ==ERROR_SMG_SESSION_NOT_FOUND; return( TRUE ); } StopSessionAndChildSessions(Session); m->ReturnedErrorValue = NO_ERROR; } else { m->ReturnedErrorValue = ERROR_SMG_SESSION_NOT_FOUND; for ( i = 0 ; i < OS2_MAX_SESSION ; i++ ) { if (( SessionTable[i].Session != NULL ) && ( SessionTable[i].Session->RelatedSession == Session )) { StopSessionAndChildSessions(SessionTable[i].Session); m->ReturnedErrorValue = NO_ERROR; } } } return( TRUE ); } VOID StopSessionAndChildSessions( IN POS2_SESSION Session ) { ULONG i; OS2SESREQUESTMSG RequestMsg; HANDLE hThread; /* * stop all child sessions */ if (!Session->WinSession) { for ( i = 0 ; i < OS2_MAX_SESSION ; i++ ) { if (( SessionTable[i].Session != NULL ) && ( SessionTable[i].Session->RelatedSession == Session )) { StopSessionAndChildSessions(SessionTable[i].Session); } } /* * stop current session */ if (Session->SesGrpAddress) ((POS2_SES_GROUP_PARMS)Session->SesGrpAddress)->InTermination |= 2; RequestMsg.d.Signal.Type = XCPT_SIGNAL_KILLPROC; RequestMsg.Session = Session; Os2CtrlSignalHandler(&RequestMsg, NULL); } else { //TerminateProcess(Session->hProcess, 0); GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, Session->dwProcessId); if (hThread = Session->hWaitThread) { #if DBG DbgPrint("OS2SRV: ALERT !!! StopSessionAndChildSessions NtAlertThread(%x)\n", hThread); #endif NtAlertThread(hThread); } } } BOOLEAN Os2DosSmSetTitle( IN POS2_THREAD t, IN POS2_API_MSG m ) { // POS2_SESSION Session; // NTSTATUS Status; // APIRET RetCode; // ULONG UniqueId; // HANDLE SessionPort; // SCREQUESTMSG Request; UNREFERENCED_PARAMETER(t); m->ReturnedErrorValue = NO_ERROR; return( TRUE ); } BOOLEAN Os2DosGetCtrlPortForSessionID( IN POS2_THREAD t, IN POS2_API_MSG m ) /*++ Routine Description: This function finds and returns an OS/2 session according to the session ID and duplicate the control port handle for the process. Arguments: Return Value: hControlPort field <= NULL - No session was found NON-NULL - Returns the address of the required session --*/ { POS2_DOSGETCTRLPORTFORSESSION_MSG a = &m->u.DosGetCtrlPortForSession; POS2_SESSION Session; NTSTATUS Status; #if DBG IF_OS2_DEBUG( SESSIONMGR ) { DumpSessionTable("Os2DosGetCtrlPortForSessionID"); } #endif Session = Os2GetSessionByUniqueId(a->SessionUniqueId); if ( Session == NULL ) { #if DBG IF_OS2_DEBUG( SESSIONMGR ) { KdPrint(("Os2DosGetCtrlPortForSessionID: not found 0x%lx\n", a->SessionUniqueId)); } #endif a->hControlPort = NULL; } else { #if DBG IF_OS2_DEBUG( SESSIONMGR ) { KdPrint(("Os2DosGetCtrlPortForSessionID: Session %lx for Id 0x%lx\n", Session, a->SessionUniqueId)); } #endif Status = NtDuplicateObject( NtCurrentProcess(), Session->ConsolePort, t->Process->ProcessHandle, &a->hControlPort, 0, 0, DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES ); if( !NT_SUCCESS( Status ) ) { ASSERT( FALSE ); a->hControlPort = NULL; } } return( TRUE ); } NTSTATUS Os2SessionFocusSet( IN OUT PVOID RequestMsg ) { POS2_SESSION Session = (POS2_SESSION) ((POS2SESREQUESTMSG)RequestMsg)->Session; ULONG FocusSet = ((POS2SESREQUESTMSG)RequestMsg)->d.FocusSet; GINFOSEG *pGlobalInfo = (GINFOSEG *) Os2GlobalInfoSeg; #if DBG IF_OS2_DEBUG( SESSIONMGR ) { KdPrint(("Os2SessionFocusSet: Session %d (%p), state %x (%s), sgCurrent %d\n", Session->SessionId, Session, FocusSet, ((FocusSet) ? "Set" : "Reset"), pGlobalInfo->sgCurrent)); } #endif if (FocusSet) { #if DBG IF_OS2_DEBUG( SESSIONMGR ) { KdPrint(("Os2SessionFocusSet: set focus\n")); } #endif pGlobalInfo->sgCurrent = (UCHAR)Session->SessionId; } else if (pGlobalInfo->sgCurrent == (UCHAR)Session->SessionId) { #if DBG IF_OS2_DEBUG( SESSIONMGR ) { KdPrint(("Os2SessionFocusSet: reset focus to none\n")); } #endif pGlobalInfo->sgCurrent = 0; } else { #if DBG IF_OS2_DEBUG( SESSIONMGR ) { KdPrint(("Os2SessionFocusSet: no reset focus\n")); } #endif } return(0L); } APIRET Os2CheckIfSessionTreeInFG( IN POS2_SESSION Session ) { // BUGBUG - check if any of the descendant is currently in the foreground POS2_SESSION NextSession; ULONG SessionId = (ULONG)((GINFOSEG *) Os2GlobalInfoSeg)->sgCurrent; if (( SessionId > OS2_MAX_SESSION ) || !SessionId || (( NextSession = SessionTable[SessionId - 1].Session ) == NULL )) { return( ERROR_SMG_SESSION_NOT_PARENT ); } while (( NextSession != Session ) && ( NextSession->RelatedSession )) { NextSession = NextSession->RelatedSession; } if ( NextSession == Session ) { return( NO_ERROR ); } return( ERROR_SMG_PROCESS_NOT_PARENT ); } #if DBG VOID DumpSessionEntry( IN PSZ Str, IN POS2_SESSION Session ) { ULONG Id = Session->SessionId; KdPrint(("\n*** %s SESSION ENTRY st 0x%lx (%lx) ***\n", Str, Session, Id)); KdPrint((" Id 0x%lx, RefCnt 0x%lx, Related 0x%lx, Port 0x%lx, PId 0x%lx\n", Session->SessionId, Session->ReferenceCount, Session->RelatedSession , Session->ConsolePort, Session->ProcessId)); if (( Id && (Id <= OS2_MAX_SESSION ) && SessionTable[Id].Session )) { KdPrint((" SessionTable-Waiters 0x%lx\n", SessionTable[Id - 1].Waiters.Flink->Flink)); } } VOID DumpSessionTable( IN PSZ Str ) { ULONG i; KdPrint(("\n*** %s SESSION TABLE st ***\n", Str)); KdPrint(("Ent Session Waiters\n")); for ( i = 0 ; i < 8 ; i++ ) { KdPrint(("%1.1lx. %8.8lx %8.8lx\n", i + 1, SessionTable[i].Session, (SessionTable[i].Session) ? SessionTable[i].Waiters.Flink->Flink : NULL)); } KdPrint(("*** End Table ***\n")); } #endif