From 62317ded9f7e426851591dd723a15b78fd0f9012 Mon Sep 17 00:00:00 2001 From: Dr Scofield Date: Fri, 14 Nov 2008 10:50:36 +0000 Subject: [PATCH] From: Alan Webb (alan_webb@us.ibm.com) Fixed the IRC code so that it deals with regions coming and going. --- .../Modules/Avatar/Chat/ChannelState.cs | 93 ++++++++++++------- .../Modules/Avatar/Chat/IRCBridgeModule.cs | 82 ++++------------ .../Modules/Avatar/Chat/IRCConnector.cs | 68 +++++++++++--- .../Modules/Avatar/Chat/RegionState.cs | 4 + 4 files changed, 133 insertions(+), 114 deletions(-) diff --git a/OpenSim/Region/Environment/Modules/Avatar/Chat/ChannelState.cs b/OpenSim/Region/Environment/Modules/Avatar/Chat/ChannelState.cs index 626569802d..213ae969c6 100644 --- a/OpenSim/Region/Environment/Modules/Avatar/Chat/ChannelState.cs +++ b/OpenSim/Region/Environment/Modules/Avatar/Chat/ChannelState.cs @@ -256,20 +256,25 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat // values that, while independent of the IRC connetion, do still distinguish // this region's behavior. - foreach (ChannelState xcs in IRCBridgeModule.m_channels) + lock (IRCBridgeModule.m_channels) { - if (cs.IsAPerfectMatchFor(xcs)) + + foreach (ChannelState xcs in IRCBridgeModule.m_channels) { - m_log.DebugFormat("[IRC-Channel-{0}] Channel state matched", cs.idn); - cs = xcs; - break; - } - if (cs.IsAConnectionMatchFor(xcs)) - { - m_log.DebugFormat("[IRC-Channel-{0}] Channel matched", cs.idn); - cs.irc = xcs.irc; - break; + if (cs.IsAPerfectMatchFor(xcs)) + { + m_log.DebugFormat("[IRC-Channel-{0}] Channel state matched", cs.idn); + cs = xcs; + break; + } + if (cs.IsAConnectionMatchFor(xcs)) + { + m_log.DebugFormat("[IRC-Channel-{0}] Channel matched", cs.idn); + cs.irc = xcs.irc; + break; + } } + } // No entry was found, so this is going to be a new entry. @@ -281,6 +286,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat if ((cs.irc = new IRCConnector(cs)) != null) { + IRCBridgeModule.m_channels.Add(cs); m_log.InfoFormat("[IRC-Channel-{0}] New channel initialized for {1}, nick: {2}, commands {3}, private channels {4}", @@ -302,11 +308,12 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat cs.idn, rs.Region, cs.IrcChannel, cs.Server, cs.Port); } - m_log.InfoFormat("[IRC-Channel-{0}] Region {1} connected to channel {2} on server {3}:{4}", + m_log.InfoFormat("[IRC-Channel-{0}] Region {1} associated with channel {2} on server {3}:{4}", cs.idn, rs.Region, cs.IrcChannel, cs.Server, cs.Port); // We're finally ready to commit ourselves + return cs; } @@ -443,6 +450,8 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat // Repeatedly scan the string until all possible // substitutions have been performed. + // m_log.DebugFormat("[IRC-Channel] Parse[1]: {0}", result); + while (arg.IsMatch(result)) { @@ -476,28 +485,26 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat result = result.Replace(vvar, rs.config.GetString(var,var)); break; } + // m_log.DebugFormat("[IRC-Channel] Parse[2]: {0}", result); } + // m_log.DebugFormat("[IRC-Channel] Parse[3]: {0}", result); return result; } public void Close() { - - m_log.InfoFormat("[IRC-Channel-{0}] Closing channel <{1} to server <{2}:{3}>", + m_log.InfoFormat("[IRC-Channel-{0}] Closing channel <{1}> to server <{2}:{3}>", idn, IrcChannel, Server, Port); - m_log.InfoFormat("[IRC-Channel-{0}] There are {1} active clients", idn, clientregions.Count); - irc.Close(); - } public void Open() { - m_log.InfoFormat("[IRC-Channel-{0}] Opening channel <{1} to server <{2}:{3}>", + m_log.InfoFormat("[IRC-Channel-{0}] Opening channel <{1}> to server <{2}:{3}>", idn, IrcChannel, Server, Port); irc.Open(); @@ -514,16 +521,30 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat Open(); } + // Close is called to ensure that the IRC session is terminated if this is the + // only client. + public void Close(RegionState rs) { RemoveRegion(rs); + lock (IRCBridgeModule.m_channels) + { + if (clientregions.Count == 0) + { + Close(); + IRCBridgeModule.m_channels.Remove(this); + m_log.InfoFormat("[IRC-Channel-{0}] Region {1} is last user of channel <{2}> to server <{3}:{4}>", + idn, rs.Region, IrcChannel, Server, Port); + m_log.InfoFormat("[IRC-Channel-{0}] Removed", idn); + } + } } // Add a client region to this channel if it is not already known public void AddRegion(RegionState rs) { - m_log.InfoFormat("[IRC-Channel-{0}] Adding region {1} to channel <{2} to server <{3}:{4}>", + m_log.InfoFormat("[IRC-Channel-{0}] Adding region {1} to channel <{2}> to server <{3}:{4}>", idn, rs.Region, IrcChannel, Server, Port); if (!clientregions.Contains(rs)) { @@ -568,28 +589,30 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat // Note that this code is responsible for completing some of the // settings for the inbound OSChatMessage - foreach (ChannelState cs in IRCBridgeModule.m_channels) + lock (IRCBridgeModule.m_channels) { - if ( p_irc == cs.irc) + foreach (ChannelState cs in IRCBridgeModule.m_channels) { - - // This non-IRC differentiator moved to here - - if (cmsg && !cs.ClientReporting) - continue; - - // This non-IRC differentiator moved to here - - c.Channel = (cs.RelayPrivateChannels ? cs.RelayChannel : 0); - - foreach (RegionState region in cs.clientregions) + if ( p_irc == cs.irc) { - region.OSChat(cs.irc, c); - } + // This non-IRC differentiator moved to here + + if (cmsg && !cs.ClientReporting) + continue; + + // This non-IRC differentiator moved to here + + c.Channel = (cs.RelayPrivateChannels ? cs.RelayChannel : 0); + + foreach (RegionState region in cs.clientregions) + { + region.OSChat(cs.irc, c); + } + + } } } - } catch (Exception ex) { diff --git a/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs b/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs index 47d3265c00..49c4a0683d 100644 --- a/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs +++ b/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs @@ -49,11 +49,13 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat internal static bool enabled = false; internal static IConfig m_config = null; - internal static List m_regions = new List(); internal static List m_channels = new List(); + internal static List m_regions = new List(); internal static string password = String.Empty; + internal RegionState region = null; + #region IRegionModule Members public string Name @@ -63,7 +65,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat public bool IsSharedModule { - get { return true; } + get { return false; } } public void Initialise(Scene scene, IConfigSource config) @@ -110,13 +112,18 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat // Iff the IRC bridge is enabled, then each new region may be // connected to IRC. But it should NOT be obligatory (and it // is not). + // We have to do ALL of the startup here because PostInitialize + // is not called when a region gets created in-flight from the + // command line. if (enabled) { try { m_log.InfoFormat("[IRC-Bridge] Connecting region {0}", scene.RegionInfo.RegionName); - m_regions.Add(new RegionState(scene, m_config)); + region = new RegionState(scene, m_config); + lock(m_regions) m_regions.Add(region); + region.Open(); } catch (Exception e) { @@ -131,37 +138,17 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat } - // Called after all region modules have been loaded. - // Iff the IRC bridge is enabled, then start all of the - // configured channels. The set of channels is a side - // effect of RegionState creation. + // This module can be called in-flight in which case PostInitialize + // is not called following Initialize. So no use is made of this + // call. public void PostInitialise() { - if (!enabled) - return; - - foreach (RegionState region in m_regions) - { - m_log.InfoFormat("[IRC-Bridge] Opening connection for {0}:{1} on IRC server {2}:{3}", - region.Region, region.cs.BaseNickname, region.cs.Server, region.cs.IrcChannel); - try - { - region.Open(); - } - catch (Exception e) - { - m_log.ErrorFormat("[IRC-Bridge] Open failed for {0}:{1} on IRC server {2}:{3} : {4}", - region.Region, region.cs.BaseNickname, region.cs.Server, region.cs.IrcChannel, - e.Message); - } - } - } - // Called immediately before the region module is unloaded. Close all - // associated channels. + // Called immediately before the region module is unloaded. Cleanup + // the region. public void Close() { @@ -169,47 +156,14 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat if (!enabled) return; - // Stop each of the region sessions - - foreach (RegionState region in m_regions) - { - m_log.InfoFormat("[IRC-Bridge] Closing connection for {0}:{1} on IRC server {2}:{3}", - region.Region, region.cs.BaseNickname, region.cs.Server, region.cs.IrcChannel); - try - { - region.Close(); - } - catch (Exception e) - { - m_log.ErrorFormat("[IRC-Bridge] Close failed for {0}:{1} on IRC server {2}:{3} : {4}", - region.Region, region.cs.BaseNickname, region.cs.Server, region.cs.IrcChannel, - e.Message); - } - } - - // Perform final cleanup of the channels (they now have no active clients) - - foreach (ChannelState channel in m_channels) - { - m_log.InfoFormat("[IRC-Bridge] Closing connection for {0} on IRC server {1}:{2}", - channel.BaseNickname, channel.Server, channel.IrcChannel); - try - { - channel.Close(); - } - catch (Exception e) - { - m_log.ErrorFormat("[IRC-Bridge] Close failed for {0} on IRC server {1}:{2} : {3}", - channel.BaseNickname, channel.Server, channel.IrcChannel, - e.Message); - } - } + region.Close(); + lock(m_regions) m_regions.Remove(region); } #endregion - public XmlRpcResponse XmlRpcAdminMethod(XmlRpcRequest request) + public static XmlRpcResponse XmlRpcAdminMethod(XmlRpcRequest request) { m_log.Info("[IRC-Bridge]: XML RPC Admin Entry"); diff --git a/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCConnector.cs b/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCConnector.cs index c52375d960..636e9bfc2c 100644 --- a/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCConnector.cs +++ b/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCConnector.cs @@ -57,6 +57,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat private const int WD_INTERVAL = 1000; // base watchdog interval private static int PING_PERIOD = 15; // WD intervals per PING private static int ICCD_PERIOD = 10; // WD intervals between Connects + private static int L_TIMEOUT = 10; // Login time out interval private static int _idk_ = 0; // core connector identifier private static int _pdk_ = 0; // ping interval counter @@ -118,6 +119,8 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat } private bool m_connected = false; // connection status + private bool m_pending = false; // login disposition + private int m_timeout = L_TIMEOUT; // login timeout counter public bool Connected { get { return m_connected; } @@ -326,6 +329,8 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat if (m_connected) return; m_connected = true; + m_pending = true; + m_timeout = L_TIMEOUT; m_tcp = new TcpClient(m_server, (int)m_port); m_stream = m_tcp.GetStream(); @@ -349,8 +354,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat m_writer.WriteLine(String.Format("JOIN {0}", m_ircChannel)); m_writer.Flush(); - m_log.InfoFormat("[IRC-Connector-{0}]: {1} has joined {2}", idn, m_nick, m_ircChannel); - m_log.InfoFormat("[IRC-Connector-{0}] Connected", idn); + m_log.InfoFormat("[IRC-Connector-{0}]: {1} has asked to join {2}", idn, m_nick, m_ircChannel); } catch (Exception e) @@ -358,6 +362,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat m_log.ErrorFormat("[IRC-Connector-{0}] cannot connect {1} to {2}:{3}: {4}", idn, m_nick, m_server, m_port, e.Message); m_connected = false; + m_pending = false; } } @@ -396,6 +401,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat try { m_tcp.Close(); } catch (Exception) {} m_connected = false; + m_pending = false; } @@ -516,7 +522,12 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat // m_log.Debug(e); } + // This is potentially circular, but harmless if so. + // The connection is marked as not connected the first time + // through reconnect. + if (m_enabled) Reconnect(); + } private Regex RE = new Regex(@":(?[\w-]*)!(?\S*) PRIVMSG (?\S+) :(?.*)", @@ -615,7 +626,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat case "003" : // Welcome ... break; case "004" : // Server information - m_log.DebugFormat("[IRC-Connector-{0}] parms = <{1}>", idn, parms); + m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms); commArgs = parms.Split(CS_SPACE); c_server = commArgs[1]; m_server = c_server; @@ -639,10 +650,10 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat case "366" : // End-of-Name list marker case "372" : // MOTD body case "375" : // MOTD start - m_log.InfoFormat("[IRC-Connector-{0}] {1}", idn, parms.Split(CS_SPACE,2)[1]); + m_log.InfoFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]); break; case "376" : // MOTD end - m_log.InfoFormat("[IRC-Connector-{0}] {1}", idn, parms.Split(CS_SPACE,2)[1]); + m_log.InfoFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]); motd = true; break; case "451" : // Not registered @@ -650,7 +661,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat case "433" : // Nickname in use // Gen a new name m_nick = m_baseNick + Util.RandomClass.Next(1, 99); - m_log.ErrorFormat("[IRC-Connector-{0}]: IRC SERVER reports NicknameInUse, trying {1}", idn, m_nick); + m_log.ErrorFormat("[IRC-Connector-{0}]: [{1}] IRC SERVER reports NicknameInUse, trying {2}", idn, cmd, m_nick); // Retry m_writer.WriteLine(String.Format("NICK {0}", m_nick)); m_writer.Flush(); @@ -659,43 +670,57 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat m_writer.WriteLine(String.Format("JOIN {0}", m_ircChannel)); m_writer.Flush(); break; + case "479" : // Bad channel name, etc. This will never work, so disable the connection + m_log.ErrorFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]); + m_log.ErrorFormat("[IRC-Connector-{0}] [{1}] Connector disabled", idn, cmd); + m_enabled = false; + m_connected = false; + m_pending = false; + break; case "NOTICE" : - m_log.WarnFormat("[IRC-Connector-{0}] {1}", idn, parms.Split(CS_SPACE,2)[1]); + m_log.WarnFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]); break; case "ERROR" : - m_log.ErrorFormat("[IRC-Connector-{0}] {1}", idn, parms.Split(CS_SPACE,2)[1]); + m_log.ErrorFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]); if (parms.Contains("reconnect too fast")) ICCD_PERIOD++; + m_pending = false; + Reconnect(); break; case "PING" : - m_log.DebugFormat("[IRC-Connector-{0}] parms = <{1}>", idn, parms); + m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms); m_writer.WriteLine(String.Format("PONG {0}", parms)); m_writer.Flush(); break; case "PONG" : break; case "JOIN": - m_log.DebugFormat("[IRC-Connector-{0}] parms = <{1}>", idn, parms); + if (m_pending) + { + m_log.InfoFormat("[IRC-Connector-{0}] [{1}] Connected", idn, cmd); + m_pending = false; + } + m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms); eventIrcJoin(pfx, cmd, parms); break; case "PART": - m_log.DebugFormat("[IRC-Connector-{0}] parms = <{1}>", idn, parms); + m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms); eventIrcPart(pfx, cmd, parms); break; case "MODE": - m_log.DebugFormat("[IRC-Connector-{0}] parms = <{1}>", idn, parms); + m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms); eventIrcMode(pfx, cmd, parms); break; case "NICK": - m_log.DebugFormat("[IRC-Connector-{0}] parms = <{1}>", idn, parms); + m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms); eventIrcNickChange(pfx, cmd, parms); break; case "KICK": - m_log.DebugFormat("[IRC-Connector-{0}] parms = <{1}>", idn, parms); + m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms); eventIrcKick(pfx, cmd, parms); break; case "QUIT": - m_log.DebugFormat("[IRC-Connector-{0}] parms = <{1}>", idn, parms); + m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms); eventIrcQuit(pfx, cmd, parms); break; default : @@ -813,6 +838,18 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat } else { + + if (connector.m_pending) + { + if (connector.m_timeout == 0) + { + m_log.ErrorFormat("[IRC-Watchdog] Login timed-out for connector {0}, reconnecting", connector.idn); + connector.Reconnect(); + } + else + connector.m_timeout--; + } + if (_pdk_ == 0) { try @@ -827,6 +864,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat connector.Reconnect(); } } + } } } diff --git a/OpenSim/Region/Environment/Modules/Avatar/Chat/RegionState.cs b/OpenSim/Region/Environment/Modules/Avatar/Chat/RegionState.cs index 025138851b..b5cc3be097 100644 --- a/OpenSim/Region/Environment/Modules/Avatar/Chat/RegionState.cs +++ b/OpenSim/Region/Environment/Modules/Avatar/Chat/RegionState.cs @@ -123,6 +123,10 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat } // Called by IRCBridgeModule.Close immediately prior to unload + // of the module for this region. This happens when the region + // is being removed or the server is terminating. The IRC + // BridgeModule will remove the region from the region list + // when control returns. public void Close() {