/*++ Copyright (c) 1990 Microsoft Corporation Module Name: msgmain.c Abstract: This is the main routine for the NT OS/2 LAN Manager Messenger Service. Functions in the file include: MESSENGER_main ParseArgs Author: Dan Lafferty (danl) 20-Mar-1991 Environment: User Mode - Win32 Revision History: 14-Jun-1994 danl Fixed problem where messenger put up an empty message as if it received a mailslot message during init. The problem was the order of the following events: CreateMailslot -> wait on handle -> submit an async _read with that handle. The new order was changed to: CreateMailslot -> submit async _read -> wait on handle. This causes the handle to not get signaled right away. 20-Mar-1991 danl created --*/ // // INCLUDES // #include "msrv.h" // AdapterThread prototype,SESSION_STATUS #include // RPC_HANDLE #include // NetpStopRpcServer #include // strlen #include // sprintf #include // Unicode string macros #include // #include "msgdbg.h" // MSG_LOG & STATIC definitions #include "msgdata.h" // msrv_status #include // LMSVCS_ENTRY_POINT, LMSVCS_GLOBAL_DATA extern RPC_IF_HANDLE msgsvc_ServerIfHandle; // // GLOBALS // // // This is the messenger services reference handle. It is used // to make calls to the service controller for adding work items // to the work queue. // HANDLE MsgSvcRefHandle = NULL; // // Local Function Prototypes // STATIC VOID MsgParseArgs( DWORD argc, LPTSTR *argv ); STATIC VOID Msgdummy_complete( short c, int a, char b ); DWORD MsgGrpEventCompletion( PVOID NetNum, // This passed in as context. DWORD dwWaitStatus ); DWORD MsgNetEventCompletion( PVOID NetNum, // This passed in as context. DWORD dwWaitStatus ); VOID LMSVCS_ENTRY_POINT ( // MESSENGER_main IN DWORD argc, IN LPTSTR argv[], PLMSVCS_GLOBAL_DATA SvcsGlobalData, IN HANDLE SvcRefHandle ) /*++ Routine Description: This is the main routine for the Messenger Service Arguments: Return Value: None. Note: --*/ { DWORD msgrState; HANDLE hGrpEvent; HANDLE hNetEvent; MsgParseArgs(argc,argv); // FOR DEBUG ONLY MsgSvcRefHandle = SvcRefHandle; MsgsvcGlobalData = SvcsGlobalData; msgrState = MsgInitializeMsgr(argc, argv); if (msgrState != RUNNING) { MSG_LOG(ERROR,"[MSG],Shutdown during initialization\n",0); MsgsvcGlobalData->NetBiosClose(); // // To get here, the msgrState must either be STOPPING or STOPPED. // Shutdown the Messenger Service // if (msgrState == STOPPING) { MsgrShutdown(); MsgStatusUpdate(STOPPED); } // // Release Thread/Status critical section. // MsgThreadManagerEnd(); MSG_LOG(TRACE,"MESSENGER_main: Messenger main thread is returning\n\n",0); return; } else { // // Post an async read on the Group Mailslot. // MSG_LOG0(GROUP,"MESSENGER_main: Submit the Group Mailslot ReadFile\n"); hGrpEvent = MsgsvcGlobalData->SvcsAddWorkItem( NULL, MsgReadGroupMailslot, (PVOID)NULL, SVC_IMMEDIATE_CALLBACK, INFINITE, MsgSvcRefHandle ); if (hGrpEvent == NULL) { MSG_LOG1(ERROR,"MESSENGER_main: Failed to setup ReadGroupMailslot %d\n", GetLastError()); } // // Submit the work item that will wait on the mailslot handle. // When the handle becomes signaled, the MsgGrpEventCompletion // function will be called. // MSG_LOG1(GROUP,"MESSENGER_main: Mailslot handle to wait on " " = 0x%lx\n",wakeupSem[SD_NUMNETS()]); hGrpEvent = MsgsvcGlobalData->SvcsAddWorkItem( wakeupSem[SD_NUMNETS()], MsgGrpEventCompletion, (PVOID)SD_NUMNETS(), SVC_QUEUE_WORK_ITEM, INFINITE, MsgSvcRefHandle ); if (hGrpEvent == NULL) { // // If we can't add the work item, then we won't ever listen // for these kind of messages again. // MSG_LOG1(ERROR,"MESSENGER_main: SvcsAddWorkItem failed %d\n", GetLastError()); // // We want to stop the messenger in this case. // MsgBeginForcedShutdown(PENDING,GetLastError()); MsgDisplayThreadWakeup(); CloseHandle(wakeupSem[SD_NUMNETS()]); MsgDisplayEnd(); MsgrShutdown(); MsgStatusUpdate(STOPPED); MsgThreadManagerEnd(); return; } hNetEvent = MsgsvcGlobalData->SvcsAddWorkItem( wakeupSem[0], MsgNetEventCompletion, (PVOID)NULL, SVC_QUEUE_WORK_ITEM, INFINITE, MsgSvcRefHandle ); if (hNetEvent == NULL) { // // If we can't add the work item, then we won't ever listen // for these kind of messages again. // MSG_LOG1(ERROR,"MsgNetEventCompletion: SvcsAddWorkItem failed %d\n", GetLastError()); // // We want to stop the messenger in this case. // MsgBeginForcedShutdown(PENDING,GetLastError()); MsgDisplayThreadWakeup(); MsgGrpThreadShutdown(); MsgDisplayEnd(); MsgrShutdown(); MsgStatusUpdate(STOPPED); MsgThreadManagerEnd(); } MSG_LOG(TRACE,"MESSENGER_main: Messenger main thread is returning\n\n",0); return; } } DWORD MsgPause( VOID ) /*++ Routine Description: Pause - wait for an event to occur This function causes the Message Server to yield control of the CPU until an event occurs that reqires processing by the Message Server. Arguments: None Return: The network which has a message waiting. Return Value: none --*/ { DWORD waitStatus; // // Use event to wait for signal. // waitStatus = WaitForMultipleObjects ( SD_NUMNETS() + 1, wakeupSem, // events to wait on. FALSE, // Wait for any event INFINITE); // Wait forever if (waitStatus == WAIT_FAILED) { MSG_LOG(ERROR,"[MSG]Pause:Wait for event error %lc\n",GetLastError()); } return ( waitStatus ); } VOID MsgParseArgs( DWORD argc, LPTSTR *argv ) /*++ Routine Description: NOTE: This is just temporary, or should be removed with an #ifdef DBG. Arguments: Return Value: --*/ { DWORD i; if (MsgsvcDebugLevel == 0) { MsgsvcDebugLevel = DEBUG_ERROR; } for (i=0; iStopRpcServer( msgsvc_ServerIfHandle ); // *** SHUTDOWN HINT *** MsgStatusUpdate (STOPPING); // // Now clean up the NCB's // MSG_LOG(TRACE,"MsgrShutdown: Clean up NCBs\n",0); for ( neti = 0; neti < SD_NUMNETS(); neti++ ) // For all nets { clearncb(&l_ncb); // // First check for any incomplete Async NCBs and cancel them. // As a precaution set the function handler for all the // async NCBs to point to a dummy function which will not reissue // the NCBs when the complete with cancelled status. // l_ncb.ncb_lana_num = net_lana_num[neti]; // Use the LANMAN adapter l_ncb.ncb_command = NCBCANCEL; // Cancel (wait) for(ncb_i = 0; ncb_i < NCBMAX; ++ncb_i) { mpncbifun[neti][ncb_i] = (LPNCBIFCN)Msgdummy_complete;// Set function pointer if((ncbArray[neti][ncb_i].ncb_cmd_cplt == (UCHAR) 0xff) && (ncbArray[neti][ncb_i].ncb_retcode == (UCHAR) 0xff)) { // // If pending NCB found // l_ncb.ncb_buffer = (char far *)&(ncbArray[neti][ncb_i]); // // There will always be an NCB reserved for cancels in the rdr // but it may be in use so loop if the cancel status // is NRC_NORES. // while( (ncbStatus = Msgsendncb(&l_ncb, neti)) == NRC_NORES) { // // Wait for half a sec // Sleep(500L); } MSG_LOG(TRACE,"Shutdown:Net #%d\n",neti); MSG_LOG(TRACE,"Shutdown:Attempt to cancel rc = 0x%x\n", ncbStatus); // // Now loop waiting for the cancelled ncb to complete. // Any ncbs types which are not valid to cancel (eg Delete // name) must complete so a wait loop here is safe. // // NT Change - This will only loop for 30 seconds before // leaving - whether or not the CANCEL is complete. // status = NERR_InternalError; for (i=0; i<60; i++) { if (ncbArray[neti][ncb_i].ncb_cmd_cplt != (UCHAR) 0xff) { status = NERR_Success; break; } // // Wait for half a sec // Sleep(500L); } if (status != NERR_Success) { MSG_LOG(ERROR, "MsgrShutdown: NCBCANCEL did not complete\n",0); } } } // // All asyncronous ncbs cancelled completed. Now delete any // messaging names active on the network card. // MSG_LOG(TRACE,"MsgrShutdown: All Async NCBs are cancelled\n",0); MSG_LOG(TRACE,"MsgrShutdown: Delete messaging names\n",0); for(i = 0; i < NCBMAX; ++i) // Loop to find active names slot { // // If any of the NFDEL or NFDEL_PENDING flags are set for // this name slot then there is no name on the card associated // with it. // clearncb(&l_ncb); if(!(SD_NAMEFLAGS(neti, i) & (NFDEL | NFDEL_PENDING))) { // // If there is a session active on this name, hang it up // now or the delete name will fail // l_ncb.ncb_command = NCBSSTAT; // session status (wait) memcpy(l_ncb.ncb_name, (SD_NAMES(neti, i)), NCBNAMSZ); l_ncb.ncb_buffer = (char far *)&sess_stat; l_ncb.ncb_length = sizeof(sess_stat); l_ncb.ncb_lana_num = net_lana_num[neti]; nbStatus = Msgsendncb(&l_ncb, neti); if(nbStatus == NRC_GOODRET) // If success { for (index=0; index < sess_stat.SessHead.num_sess ;index++) { l_ncb.ncb_command = NCBHANGUP; // Hangup (wait) l_ncb.ncb_lsn = sess_stat.SessBuffer[index].lsn; l_ncb.ncb_lana_num = net_lana_num[neti]; nbStatus = Msgsendncb(&l_ncb, neti); MSG_LOG3(TRACE,"HANGUP NetBios for Net #%d Session #%d " "status = 0x%x\n", neti, sess_stat.SessBuffer[index].lsn, nbStatus); } } else { MSG_LOG2(TRACE,"SessionSTAT NetBios Net #%d failed = 0x%x\n", neti, nbStatus); } // // With the current design of the message server there can // be only one session per name so the name should now be // clear of sessions and the delete name should work. // l_ncb.ncb_command = NCBDELNAME; // Del name (wait) l_ncb.ncb_lana_num = net_lana_num[neti]; // // Name is still in l_ncb.ncb_name from previous SESSTAT // nbStatus = Msgsendncb(&l_ncb, neti); MSG_LOG2(TRACE,"DELNAME NetBios Net #%d status = 0x%x\n", neti, nbStatus); } } } // End for all nets loop // *** SHUTDOWN HINT *** MsgStatusUpdate (STOPPING); MsgsvcGlobalData->NetBiosClose(); // // All cleaning up done. Now free up all resources. The list of // possible resources is as follows: // // memory to free: Handles to Close: // --------------- ----------------- // ncbArray wakeupSems // mpncbistate threadHandles // net_lana_num // MessageFileName // dataPtr // MSG_LOG(TRACE,"MsgrShutdown: Free up Messenger Resources\n",0); if (wakeupSem[0] != NULL) { MsgCloseWakeupSems(); // wakeupSems } MsgThreadCloseAll(); // Thread Handles if (ncbArray != NULL) { MsgFreeNCBSeg(); // ncbArray } if (mpncbistate != NULL) { MsgFreeServiceSeg(); // mpncbistate } if (net_lana_num != NULL) { MsgFreeSupportSeg(); // net_lana_num } if (dataPtr != NULL) { LocalFree (dataPtr); // dataPtr } if (MessageFileName != NULL) { LocalFree (MessageFileName); // MessageFileName } if (GlobalAllocatedMsgTitle != NULL) { LocalFree(GlobalAllocatedMsgTitle); } LocalFree(GlobalTimeFormat.AMString); // Time Format Structure LocalFree(GlobalTimeFormat.PMString); LocalFree(GlobalTimeFormat.DateFormat); LocalFree(GlobalTimeFormat.TimeSeparator); DeleteCriticalSection(&TimeFormatCritSec); MSG_LOG(TRACE,"MsgrShutdown: Done with shutdown\n",0); return; } VOID Msgdummy_complete( short c, int a, char b ) { // just to shut up compiler MSG_LOG(TRACE,"In dummy_complete module\n",0); UNUSED (a); UNUSED (b); UNUSED (c); } DWORD MsgNetEventCompletion( PVOID NetNum, // This passed in as context. DWORD dwWaitStatus ) /*++ Routine Description: This function is called when the event handle for one of the nets becomes signaled. Arguments: NetNum - This should always be zero. dwWaitStatus - This is the return value from the WaitForMultipleObjects. It indicates if the wait timed out, or if it terminated for some other reason. Return Value: --*/ { DWORD neti; HANDLE hNetEvent; DWORD msgrState; BOOL ncbComplete=FALSE; if (dwWaitStatus == WAIT_FAILED) { MSG_LOG1(ERROR, "MsgNetEventCompletion: The Wait Failed %d\n", GetLastError()); // // We can't really do anything here, so we will go on as // if the handle were signaled and everything is ok. // } msgrState = GetMsgrState(); if (msgrState == STOPPING) { // // Net 0 is considered the main Net, and this thread will // stay around until all the other messenger threads are // done shutting down. // Threads for all the other nets simply return. // MsgGrpThreadShutdown(); MsgDisplayEnd(); MsgrShutdown(); MsgStatusUpdate(STOPPED); // // Release Thread/Status critical section. // MsgThreadManagerEnd(); MSG_LOG(TRACE,"MsgNetEventCompletion: Messenger main thread is returning\n\n",0); } else { // // Look through the NCB's for all nets and service all // NCB's that are complete. Continue looping until one pass // is made through the loop without any complete NCB's being found. // do { ncbComplete = FALSE; MSG_LOG0(TRACE,"MsgNetEventCompletion: Loop through all nets to look " "for any complete NCBs\n"); for ( neti = 0; neti < SD_NUMNETS(); neti++ ) { // For all nets ncbComplete |= MsgServeNCBs((DWORD)neti); MsgServeNameReqs((DWORD)neti); } } while (ncbComplete); // // Setup for the next request. // (submit another WorkItem to the service controller.) // MSG_LOG0(TRACE,"MsgNetEventCompletion: Setup for next Net Event\n"); hNetEvent = MsgsvcGlobalData->SvcsAddWorkItem( wakeupSem[0], MsgNetEventCompletion, (PVOID)NULL, SVC_QUEUE_WORK_ITEM, INFINITE, MsgSvcRefHandle ); if (hNetEvent == NULL) { // // If we can't add the work item, then we won't ever listen // for these kind of messages again. // MSG_LOG1(ERROR,"MsgNetEventCompletion: SvcsAddWorkItem failed %d\n", GetLastError()); } } // // This thread has done all that it can do. So we can return it // to the service controller. // return(0); } DWORD MsgGrpEventCompletion( PVOID NetNum, // This passed in as context. DWORD dwWaitStatus ) /*++ Routine Description: This function is called when the mailslot handle for group (domain-wide) messages becomes signalled. Arguments: NetNum - This is the offset into the wakeupSem array where the Mailslot handle is stored. This offset should be the same as SD_NUMNETS(). dwWaitStatus - This is the return value from the WaitForMultipleObjects. It indicates if the wait timed out, or if it terminated for some other reason. Return Value: --*/ { HANDLE hGrpEvent; DWORD msgrState; MSG_LOG0(GROUP,"MsgGroupEventCompletion: entry point\n",); if (dwWaitStatus == WAIT_FAILED) { MSG_LOG1(ERROR, "MsgGrpEventCompletion: The Wait Failed %d\n", GetLastError()); // // We can't really do anything here, so we will go on as // if the handle were signaled and everything is ok. // } msgrState = MsgServeGroupMailslot(); if (msgrState == STOPPING) { // // Close the Mailslot Handle, and set the wakeupSem array value // to NULL. // CloseHandle(wakeupSem[ SD_NUMNETS() ]); // Group Mailslot Handle wakeupSem[SD_NUMNETS()] = NULL; MSG_LOG0(TRACE,"MsgGroupEventCompletion: No longer listening for " "group messages\n"); } else { // // Setup for the next ReadFile. We have the service controller // watcher thread make the call because it never goes away. // hGrpEvent = MsgsvcGlobalData->SvcsAddWorkItem( NULL, MsgReadGroupMailslot, (PVOID)NULL, SVC_IMMEDIATE_CALLBACK, INFINITE, MsgSvcRefHandle ); if (hGrpEvent == NULL) { MSG_LOG1(ERROR,"MsgGrpEventCompletion: Failed to setup ReadGroupMailslot %d\n", GetLastError()); } // // Setup for the next request. // (submit another WorkItem to the service controller.) // MSG_LOG0(TRACE,"MsgGroupEventCompletion: Setup for next Group Event\n"); MSG_LOG1(GROUP,"MsgGroupEventCompletion: Mailslot handle to wait on " " = 0x%lx\n",wakeupSem[SD_NUMNETS()]); hGrpEvent = MsgsvcGlobalData->SvcsAddWorkItem( wakeupSem[SD_NUMNETS()], MsgGrpEventCompletion, (PVOID)SD_NUMNETS(), SVC_QUEUE_WORK_ITEM, INFINITE, MsgSvcRefHandle ); if (hGrpEvent == NULL) { // // If we can't add the work item, then we won't ever listen // for these kind of messages again. // MSG_LOG1(ERROR,"MsgGrpEventCompletion: SvcsAddWorkItem failed %d\n", GetLastError()); } } // // This thread has done all that it can do. So we can return it // to the service controller. // return(0); }