From caaf9f7fc19d6c272f9cab669ed78c486b11fded Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Thu, 9 Feb 2017 23:13:37 +0000 Subject: Squelch an error that happens when the asset cache was mounted RO. Nothing we can do about that, blame Docker. --- OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'OpenSim') diff --git a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs index f8a4461..610e279 100644 --- a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs +++ b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs @@ -806,6 +806,9 @@ namespace OpenSim.Region.CoreModules.Asset return; } + catch (UnauthorizedAccessException e) + { + } finally { if (stream != null) -- cgit v1.1 From 27fdf3094d6bf20325ab261b41d3d4831c14c204 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Thu, 9 Feb 2017 23:14:35 +0000 Subject: A god is a god is a god. Let gods access scripts and NCs Support staff are not of much help if they can't see the script a user has issues with. --- OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'OpenSim') diff --git a/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs b/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs index b60cd93..86f0a58 100644 --- a/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs +++ b/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs @@ -1732,6 +1732,10 @@ namespace OpenSim.Region.CoreModules.World.Permissions DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); if (m_bypassPermissions) return m_bypassPermissionsValue; + // A god is a god is a god + if (IsAdministrator(user)) + return true; + if (objectID == UUID.Zero) // User inventory { IInventoryService invService = m_scene.InventoryService; @@ -1828,6 +1832,10 @@ namespace OpenSim.Region.CoreModules.World.Permissions DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); if (m_bypassPermissions) return m_bypassPermissionsValue; + // A god is a god is a god + if (IsAdministrator(user)) + return true; + if (objectID == UUID.Zero) // User inventory { IInventoryService invService = m_scene.InventoryService; -- cgit v1.1 From 91f142bbd2f11580da86c66fad2c08d176b09c97 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Thu, 9 Feb 2017 23:16:23 +0000 Subject: Add the option to link a scene object with a monitoring object For debugging purposes. --- OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs | 2 ++ .../ScriptEngine/Shared/Api/Implementation/LSL_Api.cs | 13 +++++++++++++ 2 files changed, 15 insertions(+) (limited to 'OpenSim') diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 3f531be..6332beb 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -376,6 +376,8 @@ namespace OpenSim.Region.Framework.Scenes public bool m_dupeInProgress = false; internal Dictionary m_savedScriptState; + public UUID MonitoringObject { get; set; } + #region Properties /// diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index 45bdb41..9c67395 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -424,6 +424,19 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return lease; } + protected SceneObjectPart MonitoringObject() + { + UUID m = m_host.ParentGroup.MonitoringObject; + if (m == UUID.Zero) + return null; + + SceneObjectPart p = m_ScriptEngine.World.GetSceneObjectPart(m); + if (p == null) + m_host.ParentGroup.MonitoringObject = UUID.Zero; + + return p; + } + protected virtual void ScriptSleep(int delay) { delay = (int)((float)delay * m_ScriptDelayFactor); -- cgit v1.1 From 995e750c561d3faebe7b7aceb729d2823dd8bb38 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Fri, 24 Feb 2017 01:30:20 +0000 Subject: Change a misleading message that could lead users to believe a grid is down --- OpenSim/Region/Framework/Scenes/Scene.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'OpenSim') diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 2137b42..2a0bd3c 100755 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -1520,6 +1520,8 @@ namespace OpenSim.Region.Framework.Scenes m_log.Debug("[SCENE]: Persisting changed objects"); Backup(true); + m_log.Debug("[SCENE]: Closing scene"); + m_sceneGraph.Close(); base.Close(); @@ -3983,7 +3985,7 @@ namespace OpenSim.Region.Framework.Scenes if (!LoginsEnabled) { - reason = "Logins Disabled"; + reason = "Logins to this region are disabled"; return false; } -- cgit v1.1 From 41d21190b18e1b14fe4f56bde69e7e9a9ee60723 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Fri, 24 Feb 2017 01:31:32 +0000 Subject: Skip region restart messages and delays when there are no root agents --- .../RemoteController/RemoteAdminPlugin.cs | 40 ++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) (limited to 'OpenSim') diff --git a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs index 597b439..e5fbc69 100644 --- a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs +++ b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs @@ -359,6 +359,42 @@ namespace OpenSim.ApplicationPlugins.RemoteController notice = false; } + if (startupConfig.GetBoolean("SkipDelayOnEmptyRegion", false)) + { + m_log.Info("[RADMIN]: Counting affected avatars"); + int agents = 0; + + if (restartAll) + { + foreach (Scene s in m_application.SceneManager.Scenes) + { + foreach (ScenePresence sp in s.GetScenePresences()) + { + if (!sp.IsChildAgent) + agents++; + } + } + } + else + { + foreach (ScenePresence sp in rebootedScene.GetScenePresences()) + { + if (!sp.IsChildAgent) + agents++; + } + } + + m_log.InfoFormat("[RADMIN]: Avatars in region: {0}", agents); + + if (agents == 0) + { + m_log.Info("[RADMIN]: No avatars detected, shutting down without delay"); + + times.Clear(); + times.Add(0); + } + } + List restartList; if (restartAll) @@ -376,10 +412,10 @@ namespace OpenSim.ApplicationPlugins.RemoteController } catch (Exception e) { -// m_log.ErrorFormat("[RADMIN]: Restart region: failed: {0} {1}", e.Message, e.StackTrace); + m_log.ErrorFormat("[RADMIN]: Restart region: failed: {0} {1}", e.Message, e.StackTrace); responseData["rebooting"] = false; - throw e; + throw; } m_log.Info("[RADMIN]: Restart Region request complete"); -- cgit v1.1 From adcb1d8798c0404d0c9f894d1b33003557066123 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Fri, 24 Feb 2017 01:33:02 +0000 Subject: Allow setting the stats log file location in OpenSim.ini rather than app.exe.config --- OpenSim/Framework/Servers/ServerBase.cs | 17 +++++++++++++++++ OpenSim/Framework/Util.cs | 15 ++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) (limited to 'OpenSim') diff --git a/OpenSim/Framework/Servers/ServerBase.cs b/OpenSim/Framework/Servers/ServerBase.cs index 8965e71..f627ae6 100644 --- a/OpenSim/Framework/Servers/ServerBase.cs +++ b/OpenSim/Framework/Servers/ServerBase.cs @@ -57,6 +57,7 @@ namespace OpenSim.Framework.Servers protected OpenSimAppender m_consoleAppender; protected FileAppender m_logFileAppender; + protected FileAppender m_statsLogFileAppender; protected DateTime m_startuptime; protected string m_startupDirectory = Environment.CurrentDirectory; @@ -156,6 +157,10 @@ namespace OpenSim.Framework.Servers { m_logFileAppender = (FileAppender)appender; } + else if (appender.Name == "StatsLogFileAppender") + { + m_statsLogFileAppender = (FileAppender)appender; + } } if (null == m_consoleAppender) @@ -185,6 +190,18 @@ namespace OpenSim.Framework.Servers m_log.InfoFormat("[SERVER BASE]: Logging started to file {0}", m_logFileAppender.File); } + + if (m_statsLogFileAppender != null && startupConfig != null) + { + string cfgStatsFileName = startupConfig.GetString("StatsLogFile", null); + if (cfgStatsFileName != null) + { + m_statsLogFileAppender.File = cfgStatsFileName; + m_statsLogFileAppender.ActivateOptions(); + } + + m_log.InfoFormat("[SERVER BASE]: Stats Logging started to file {0}", m_statsLogFileAppender.File); + } } /// diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index 0037f4f..dcb660a 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -1190,7 +1190,7 @@ namespace OpenSim.Framework { foreach (IAppender appender in LogManager.GetRepository().GetAppenders()) { - if (appender is FileAppender) + if (appender is FileAppender && appender.Name == "LogFileAppender") { return ((FileAppender)appender).File; } @@ -1199,6 +1199,19 @@ namespace OpenSim.Framework return "./OpenSim.log"; } + public static string statsLogFile() + { + foreach (IAppender appender in LogManager.GetRepository().GetAppenders()) + { + if (appender is FileAppender && appender.Name == "StatsLogFileAppender") + { + return ((FileAppender)appender).File; + } + } + + return "./OpenSimStats.log"; + } + public static string logDir() { return Path.GetDirectoryName(logFile()); -- cgit v1.1 From 5109dc955fa25562675b7ae37a79bf5504f2e394 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Mon, 13 Mar 2017 12:48:55 +0000 Subject: Set a startup marker to let external scripts know the scene is up. --- OpenSim/Region/Framework/Scenes/Scene.cs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'OpenSim') diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 2a0bd3c..f4bf0b2 100755 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -1212,6 +1212,28 @@ namespace OpenSim.Region.Framework.Scenes StatsReporter.OnSendStatsResult += SendSimStatsPackets; StatsReporter.OnStatsIncorrect += m_sceneGraph.RecalculateStats; + IConfig restartConfig = config.Configs["RestartModule"]; + if (restartConfig != null) + { + string markerPath = restartConfig.GetString("MarkerPath", String.Empty); + + if (markerPath != String.Empty) + { + string path = Path.Combine(markerPath, RegionInfo.RegionID.ToString() + ".started"); + try + { + string pidstring = System.Diagnostics.Process.GetCurrentProcess().Id.ToString(); + FileStream fs = File.Create(path); + System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding(); + Byte[] buf = enc.GetBytes(pidstring); + fs.Write(buf, 0, buf.Length); + fs.Close(); + } + catch (Exception) + { + } + } + } } public Scene(RegionInfo regInfo) -- cgit v1.1 From 3e880cee45ea0d85297c51d3ed87490d47a5bd94 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Thu, 23 Mar 2017 23:00:14 +0000 Subject: Allow short-circuiting region restart delays of there are no users left --- .../CoreModules/World/Region/RestartModule.cs | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'OpenSim') diff --git a/OpenSim/Region/CoreModules/World/Region/RestartModule.cs b/OpenSim/Region/CoreModules/World/Region/RestartModule.cs index 8bac9e6..bf6592f 100644 --- a/OpenSim/Region/CoreModules/World/Region/RestartModule.cs +++ b/OpenSim/Region/CoreModules/World/Region/RestartModule.cs @@ -61,6 +61,8 @@ namespace OpenSim.Region.CoreModules.World.Region protected IDialogModule m_DialogModule = null; protected string m_MarkerPath = String.Empty; private int[] m_CurrentAlerts = null; + protected bool m_shortCircuitDelays = false; + protected bool m_rebootAll = false; public void Initialise(IConfigSource config) { @@ -69,6 +71,9 @@ namespace OpenSim.Region.CoreModules.World.Region { m_MarkerPath = restartConfig.GetString("MarkerPath", String.Empty); } + IConfig startupConfig = config.Configs["Startup"]; + m_shortCircuitDelays = startupConfig.GetBoolean("SkipDelayOnEmptyRegion", false); + m_rebootAll = startupConfig.GetBoolean("InworldRestartShutsDown", false); } public void AddRegion(Scene scene) @@ -250,6 +255,14 @@ namespace OpenSim.Region.CoreModules.World.Region private void OnTimer(object source, ElapsedEventArgs e) { int nextInterval = DoOneNotice(true); + if (m_shortCircuitDelays) + { + if (CountAgents() == 0) + { + m_Scene.RestartNow(); + return; + } + } SetTimer(nextInterval); } @@ -349,5 +362,35 @@ namespace OpenSim.Region.CoreModules.World.Region { } } + + int CountAgents() + { + m_log.Info("[RESTART MODULE]: Counting affected avatars"); + int agents = 0; + + if (m_rebootAll) + { + foreach (Scene s in SceneManager.Instance.Scenes) + { + foreach (ScenePresence sp in s.GetScenePresences()) + { + if (!sp.IsChildAgent) + agents++; + } + } + } + else + { + foreach (ScenePresence sp in m_Scene.GetScenePresences()) + { + if (!sp.IsChildAgent) + agents++; + } + } + + m_log.InfoFormat("[RESTART MODULE]: Avatars in region: {0}", agents); + + return agents; + } } } -- cgit v1.1 From 680231d7e79cae2636e9722ca12b79345fa2dbdd Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Thu, 23 Mar 2017 23:00:48 +0000 Subject: Make the MySqlGeneric layer transaction aware --- OpenSim/Data/MySQL/MySQLFramework.cs | 75 ++++++++---- OpenSim/Data/MySQL/MySQLGenericTableHandler.cs | 163 ++++++++++++++++--------- 2 files changed, 155 insertions(+), 83 deletions(-) (limited to 'OpenSim') diff --git a/OpenSim/Data/MySQL/MySQLFramework.cs b/OpenSim/Data/MySQL/MySQLFramework.cs index 34791cf..93662db 100644 --- a/OpenSim/Data/MySQL/MySQLFramework.cs +++ b/OpenSim/Data/MySQL/MySQLFramework.cs @@ -36,7 +36,7 @@ using MySql.Data.MySqlClient; namespace OpenSim.Data.MySQL { /// - /// A database interface class to a user profile storage system + /// Common code for a number of database modules /// public class MySqlFramework { @@ -44,14 +44,24 @@ namespace OpenSim.Data.MySQL log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - protected string m_connectionString; - protected object m_dbLock = new object(); + protected string m_connectionString = String.Empty; + protected MySqlTransaction m_trans = null; + // Constructor using a connection string. Instances constructed + // this way will open a new connection for each call. protected MySqlFramework(string connectionString) { m_connectionString = connectionString; } + // Constructor using a connection object. Instances constructed + // this way will use the connection object and never create + // new connections. + protected MySqlFramework(MySqlTransaction trans) + { + m_trans = trans; + } + ////////////////////////////////////////////////////////////// // // All non queries are funneled through one connection @@ -59,33 +69,48 @@ namespace OpenSim.Data.MySQL // protected int ExecuteNonQuery(MySqlCommand cmd) { - lock (m_dbLock) + if (m_trans == null) { using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) { - try - { - dbcon.Open(); - cmd.Connection = dbcon; + dbcon.Open(); + return ExecuteNonQueryWithConnection(cmd, dbcon); + } + } + else + { + return ExecuteNonQueryWithTransaction(cmd, m_trans); + } + } - try - { - return cmd.ExecuteNonQuery(); - } - catch (Exception e) - { - m_log.Error(e.Message, e); - m_log.Error(Environment.StackTrace.ToString()); - return 0; - } - } - catch (Exception e) - { - m_log.Error(e.Message, e); - return 0; - } + private int ExecuteNonQueryWithTransaction(MySqlCommand cmd, MySqlTransaction trans) + { + cmd.Transaction = trans; + return ExecuteNonQueryWithConnection(cmd, trans.Connection); + } + + private int ExecuteNonQueryWithConnection(MySqlCommand cmd, MySqlConnection dbcon) + { + try + { + cmd.Connection = dbcon; + + try + { + return cmd.ExecuteNonQuery(); } + catch (Exception e) + { + m_log.Error(e.Message, e); + m_log.Error(Environment.StackTrace.ToString()); + return 0; + } + } + catch (Exception e) + { + m_log.Error(e.Message, e); + return 0; } } } -} \ No newline at end of file +} diff --git a/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs b/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs index 6aae9c6..bd8bbd5 100644 --- a/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs +++ b/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs @@ -53,14 +53,27 @@ namespace OpenSim.Data.MySQL get { return GetType().Assembly; } } + public MySQLGenericTableHandler(MySqlTransaction trans, + string realm, string storeName) : base(trans) + { + m_Realm = realm; + + CommonConstruct(storeName); + } + public MySQLGenericTableHandler(string connectionString, string realm, string storeName) : base(connectionString) { m_Realm = realm; - m_connectionString = connectionString; + CommonConstruct(storeName); + } + + protected void CommonConstruct(string storeName) + { if (storeName != String.Empty) { + // We always use a new connection for any Migrations using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) { dbcon.Open(); @@ -111,6 +124,11 @@ namespace OpenSim.Data.MySQL public virtual T[] Get(string[] fields, string[] keys) { + return Get(fields, keys, String.Empty); + } + + public virtual T[] Get(string[] fields, string[] keys, string options) + { if (fields.Length != keys.Length) return new T[0]; @@ -126,8 +144,8 @@ namespace OpenSim.Data.MySQL string where = String.Join(" and ", terms.ToArray()); - string query = String.Format("select * from {0} where {1}", - m_Realm, where); + string query = String.Format("select * from {0} where {1} {2}", + m_Realm, where, options); cmd.CommandText = query; @@ -137,72 +155,92 @@ namespace OpenSim.Data.MySQL protected T[] DoQuery(MySqlCommand cmd) { + if (m_trans == null) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + return DoQueryWithConnection(cmd, dbcon); + } + } + else + { + return DoQueryWithTransaction(cmd, m_trans); + } + } + + protected T[] DoQueryWithTransaction(MySqlCommand cmd, MySqlTransaction trans) + { + cmd.Transaction = trans; + + return DoQueryWithConnection(cmd, trans.Connection); + } + + protected T[] DoQueryWithConnection(MySqlCommand cmd, MySqlConnection dbcon) + { List result = new List(); - using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + cmd.Connection = dbcon; + + using (IDataReader reader = cmd.ExecuteReader()) { - dbcon.Open(); - cmd.Connection = dbcon; + if (reader == null) + return new T[0]; - using (IDataReader reader = cmd.ExecuteReader()) - { - if (reader == null) - return new T[0]; + CheckColumnNames(reader); - CheckColumnNames(reader); + while (reader.Read()) + { + T row = new T(); - while (reader.Read()) + foreach (string name in m_Fields.Keys) { - T row = new T(); - - foreach (string name in m_Fields.Keys) + if (reader[name] is DBNull) { - if (reader[name] is DBNull) - { - continue; - } - if (m_Fields[name].FieldType == typeof(bool)) - { - int v = Convert.ToInt32(reader[name]); - m_Fields[name].SetValue(row, v != 0 ? true : false); - } - else if (m_Fields[name].FieldType == typeof(UUID)) - { - m_Fields[name].SetValue(row, DBGuid.FromDB(reader[name])); - } - else if (m_Fields[name].FieldType == typeof(int)) - { - int v = Convert.ToInt32(reader[name]); - m_Fields[name].SetValue(row, v); - } - else if (m_Fields[name].FieldType == typeof(uint)) - { - uint v = Convert.ToUInt32(reader[name]); - m_Fields[name].SetValue(row, v); - } - else - { - m_Fields[name].SetValue(row, reader[name]); - } + continue; } - - if (m_DataField != null) + if (m_Fields[name].FieldType == typeof(bool)) + { + int v = Convert.ToInt32(reader[name]); + m_Fields[name].SetValue(row, v != 0 ? true : false); + } + else if (m_Fields[name].FieldType == typeof(UUID)) + { + m_Fields[name].SetValue(row, DBGuid.FromDB(reader[name])); + } + else if (m_Fields[name].FieldType == typeof(int)) + { + int v = Convert.ToInt32(reader[name]); + m_Fields[name].SetValue(row, v); + } + else if (m_Fields[name].FieldType == typeof(uint)) + { + uint v = Convert.ToUInt32(reader[name]); + m_Fields[name].SetValue(row, v); + } + else { - Dictionary data = - new Dictionary(); + m_Fields[name].SetValue(row, reader[name]); + } + } - foreach (string col in m_ColumnNames) - { - data[col] = reader[col].ToString(); - if (data[col] == null) - data[col] = String.Empty; - } + if (m_DataField != null) + { + Dictionary data = + new Dictionary(); - m_DataField.SetValue(row, data); + foreach (string col in m_ColumnNames) + { + data[col] = reader[col].ToString(); + if (data[col] == null) + data[col] = String.Empty; } - result.Add(row); + m_DataField.SetValue(row, data); } + + result.Add(row); } } @@ -357,14 +395,23 @@ namespace OpenSim.Data.MySQL public object DoQueryScalar(MySqlCommand cmd) { - using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + if (m_trans == null) { - dbcon.Open(); - cmd.Connection = dbcon; + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + cmd.Connection = dbcon; + + return cmd.ExecuteScalar(); + } + } + else + { + cmd.Connection = m_trans.Connection; + cmd.Transaction = m_trans; return cmd.ExecuteScalar(); } } - } } -- cgit v1.1 From 75915bd0f3be487a0fa05e4a91d46b23b488ce82 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Fri, 31 Mar 2017 13:46:01 +0100 Subject: Add the ability to post to and act uponconfiguration in etcd Etcd is a distributed, highly available name-value store that is the basis of many clustered applications. However, it can also be run on a standalone machine. Using etcd allows live monitoring and possibly changing configuration while the sim is running. --- OpenSim/Region/Framework/Interfaces/IEtcdModule.cs | 37 ++++ OpenSim/Region/Framework/Scenes/Scene.cs | 47 ++++- .../Framework/Monitoring/EtcdMonitoringModule.cs | 195 +++++++++++++++++++++ 3 files changed, 276 insertions(+), 3 deletions(-) create mode 100644 OpenSim/Region/Framework/Interfaces/IEtcdModule.cs create mode 100644 OpenSim/Region/OptionalModules/Framework/Monitoring/EtcdMonitoringModule.cs (limited to 'OpenSim') diff --git a/OpenSim/Region/Framework/Interfaces/IEtcdModule.cs b/OpenSim/Region/Framework/Interfaces/IEtcdModule.cs new file mode 100644 index 0000000..123cb67 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IEtcdModule.cs @@ -0,0 +1,37 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; + +public interface IEtcdModule +{ + bool Store(string k, string v); + bool Store(string k, string v, int ttl); + string Get(string k); + void Watch(string k, Action callback); + void Delete(string k); +} diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index f4bf0b2..fd13821 100755 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -540,6 +540,9 @@ namespace OpenSim.Region.Framework.Scenes private Timer m_mapGenerationTimer = new Timer(); private bool m_generateMaptiles; + protected int m_lastHealth = -1; + protected int m_lastUsers = -1; + #endregion Fields #region Properties @@ -1234,6 +1237,8 @@ namespace OpenSim.Region.Framework.Scenes } } } + + StartTimerWatchdog(); } public Scene(RegionInfo regInfo) @@ -1504,6 +1509,14 @@ namespace OpenSim.Region.Framework.Scenes return; } + IEtcdModule etcd = RequestModuleInterface(); + if (etcd != null) + { + etcd.Delete("Health"); + etcd.Delete("HealthFlags"); + etcd.Delete("RootAgents"); + } + m_log.InfoFormat("[SCENE]: Closing down the single simulator: {0}", RegionInfo.RegionName); @@ -5536,23 +5549,24 @@ Label_GroupsDone: return 0; } - if ((Util.EnvironmentTickCountSubtract(m_lastFrameTick)) < 1000) + if ((Util.EnvironmentTickCountSubtract(m_lastFrameTick)) < 2000) { health+=1; flags |= 1; } - if (Util.EnvironmentTickCountSubtract(m_lastIncoming) < 1000) + if (Util.EnvironmentTickCountSubtract(m_lastIncoming) < 2000) { health+=1; flags |= 2; } - if (Util.EnvironmentTickCountSubtract(m_lastOutgoing) < 1000) + if (Util.EnvironmentTickCountSubtract(m_lastOutgoing) < 2000) { health+=1; flags |= 4; } + /* else { int pid = System.Diagnostics.Process.GetCurrentProcess().Id; @@ -5565,6 +5579,7 @@ proc.WaitForExit(); Thread.Sleep(1000); Environment.Exit(1); } + */ if (flags != 7) return health; @@ -6329,6 +6344,32 @@ Environment.Exit(1); public void TimerWatchdog(object sender, ElapsedEventArgs e) { CheckHeartbeat(); + + IEtcdModule etcd = RequestModuleInterface(); + int flags; + string message; + if (etcd != null) + { + int health = GetHealth(out flags, out message); + if (health != m_lastHealth) + { + m_lastHealth = health; + + etcd.Store("Health", health.ToString(), 300000); + etcd.Store("HealthFlags", flags.ToString(), 300000); + } + + int roots = 0; + foreach (ScenePresence sp in GetScenePresences()) + if (!sp.IsChildAgent && !sp.IsNPC) + roots++; + + if (m_lastUsers != roots) + { + m_lastUsers = roots; + etcd.Store("RootAgents", roots.ToString(), 300000); + } + } } /// This method deals with movement when an avatar is automatically moving (but this is distinct from the diff --git a/OpenSim/Region/OptionalModules/Framework/Monitoring/EtcdMonitoringModule.cs b/OpenSim/Region/OptionalModules/Framework/Monitoring/EtcdMonitoringModule.cs new file mode 100644 index 0000000..921bbfb --- /dev/null +++ b/OpenSim/Region/OptionalModules/Framework/Monitoring/EtcdMonitoringModule.cs @@ -0,0 +1,195 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using netcd; +using netcd.Serialization; +using netcd.Advanced; +using netcd.Advanced.Requests; + +namespace OpenSim.Region.OptionalModules.Framework.Monitoring +{ + /// + /// Allows to store monitoring data in etcd, a high availability + /// name-value store. + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "EtcdMonitoringModule")] + public class EtcdMonitoringModule : INonSharedRegionModule, IEtcdModule + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected Scene m_scene; + protected IEtcdClient m_client; + protected bool m_enabled = false; + protected string m_etcdBasePath = String.Empty; + protected bool m_appendRegionID = true; + + public string Name + { + get { return "EtcdMonitoringModule"; } + } + + public Type ReplaceableInterface + { + get { return null; } + } + + public void Initialise(IConfigSource source) + { + if (source.Configs["Etcd"] == null) + return; + + IConfig etcdConfig = source.Configs["Etcd"]; + + string etcdUrls = etcdConfig.GetString("EtcdUrls", String.Empty); + if (etcdUrls == String.Empty) + return; + + m_etcdBasePath = etcdConfig.GetString("BasePath", m_etcdBasePath); + m_appendRegionID = etcdConfig.GetBoolean("AppendRegionID", m_appendRegionID); + + if (!m_etcdBasePath.EndsWith("/")) + m_etcdBasePath += "/"; + + try + { + string[] endpoints = etcdUrls.Split(new char[] {','}); + List uris = new List(); + foreach (string endpoint in endpoints) + uris.Add(new Uri(endpoint.Trim())); + + m_client = new EtcdClient(uris.ToArray(), new DefaultSerializer(), new DefaultSerializer()); + } + catch (Exception e) + { + m_log.DebugFormat("[ETCD]: Error initializing connection: " + e.ToString()); + return; + } + + m_log.DebugFormat("[ETCD]: Etcd module configured"); + m_enabled = true; + } + + public void Close() + { + //m_client = null; + m_scene = null; + } + + public void AddRegion(Scene scene) + { + m_scene = scene; + + if (m_enabled) + { + if (m_appendRegionID) + m_etcdBasePath += m_scene.RegionInfo.RegionID.ToString() + "/"; + + m_log.DebugFormat("[ETCD]: Using base path {0} for all keys", m_etcdBasePath); + + try + { + m_client.Advanced.CreateDirectory(new CreateDirectoryRequest() {Key = m_etcdBasePath}); + } + catch (Exception e) + { + m_log.ErrorFormat("Exception trying to create base path {0}: " + e.ToString(), m_etcdBasePath); + } + + scene.RegisterModuleInterface(this); + } + } + + public void RemoveRegion(Scene scene) + { + } + + public void RegionLoaded(Scene scene) + { + } + + public bool Store(string k, string v) + { + return Store(k, v, 0); + } + + public bool Store(string k, string v, int ttl) + { + Response resp = m_client.Advanced.SetKey(new SetKeyRequest() { Key = m_etcdBasePath + k, Value = v, TimeToLive = ttl }); + + if (resp == null) + return false; + + if (resp.ErrorCode.HasValue) + { + m_log.DebugFormat("[ETCD]: Error {0} ({1}) storing {2} => {3}", resp.Cause, (int)resp.ErrorCode, m_etcdBasePath + k, v); + + return false; + } + + return true; + } + + public string Get(string k) + { + Response resp = m_client.Advanced.GetKey(new GetKeyRequest() { Key = m_etcdBasePath + k }); + + if (resp == null) + return String.Empty; + + if (resp.ErrorCode.HasValue) + { + m_log.DebugFormat("[ETCD]: Error {0} ({1}) getting {2}", resp.Cause, (int)resp.ErrorCode, m_etcdBasePath + k); + + return String.Empty; + } + + return resp.Node.Value; + } + + public void Delete(string k) + { + m_client.Advanced.DeleteKey(new DeleteKeyRequest() { Key = m_etcdBasePath + k }); + } + + public void Watch(string k, Action callback) + { + m_client.Advanced.WatchKey(new WatchKeyRequest() { Key = m_etcdBasePath + k, Callback = (x) => { callback(x.Node.Value); } }); + } + } +} -- cgit v1.1 From c0ce746e2d2709af56cd993e3de48eb06e5b7b64 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Fri, 31 Mar 2017 13:47:53 +0100 Subject: When determining if a sim can be shut down, ignore NPCs --- OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'OpenSim') diff --git a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs index e5fbc69..1ee2468 100644 --- a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs +++ b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs @@ -370,7 +370,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController { foreach (ScenePresence sp in s.GetScenePresences()) { - if (!sp.IsChildAgent) + if (!sp.IsChildAgent && !sp.IsNPC) agents++; } } @@ -379,7 +379,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController { foreach (ScenePresence sp in rebootedScene.GetScenePresences()) { - if (!sp.IsChildAgent) + if (!sp.IsChildAgent && !sp.IsNPC) agents++; } } -- cgit v1.1 From a6ecedb72a44dbf9c2b9ec4c8f51ec70fce06b78 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Fri, 31 Mar 2017 13:50:28 +0100 Subject: Also ignore NPCs in short-circuiting region restarts --- OpenSim/Region/CoreModules/World/Region/RestartModule.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'OpenSim') diff --git a/OpenSim/Region/CoreModules/World/Region/RestartModule.cs b/OpenSim/Region/CoreModules/World/Region/RestartModule.cs index bf6592f..bb3b860 100644 --- a/OpenSim/Region/CoreModules/World/Region/RestartModule.cs +++ b/OpenSim/Region/CoreModules/World/Region/RestartModule.cs @@ -374,7 +374,7 @@ namespace OpenSim.Region.CoreModules.World.Region { foreach (ScenePresence sp in s.GetScenePresences()) { - if (!sp.IsChildAgent) + if (!sp.IsChildAgent && !sp.IsNPC) agents++; } } @@ -383,7 +383,7 @@ namespace OpenSim.Region.CoreModules.World.Region { foreach (ScenePresence sp in m_Scene.GetScenePresences()) { - if (!sp.IsChildAgent) + if (!sp.IsChildAgent && !sp.IsNPC) agents++; } } -- cgit v1.1 From 59f7cf79c9925ea07a79d975bf480aba7b5d6498 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Fri, 31 Mar 2017 13:51:42 +0100 Subject: Prevent error spew when script send IM to HG users before HG is up --- .../Framework/UserManagement/UserManagementModule.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'OpenSim') diff --git a/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs b/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs index 51f973a..32cb5a3 100644 --- a/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs +++ b/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs @@ -957,9 +957,14 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement public virtual bool IsLocalGridUser(UUID uuid) { - UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, uuid); - if (account == null || (account != null && !account.LocalToGrid)) - return false; + lock (m_Scenes) + { + if (m_Scenes.Count == 0) + return true; + UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, uuid); + if (account == null || (account != null && !account.LocalToGrid)) + return false; + } return true; } -- cgit v1.1 From 6463ab7d795933ed6745b4e43cab7bc6ffe98c0a Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Fri, 31 Mar 2017 14:44:22 +0100 Subject: If a DNS resolution fails on an outbound request, simply allow it --- OpenSim/Framework/OutboundUrlFilter.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'OpenSim') diff --git a/OpenSim/Framework/OutboundUrlFilter.cs b/OpenSim/Framework/OutboundUrlFilter.cs index ee4707f..63ae361 100644 --- a/OpenSim/Framework/OutboundUrlFilter.cs +++ b/OpenSim/Framework/OutboundUrlFilter.cs @@ -212,7 +212,17 @@ namespace OpenSim.Framework // Check that we are permitted to make calls to this endpoint. bool foundIpv4Address = false; - IPAddress[] addresses = Dns.GetHostAddresses(url.Host); + IPAddress[] addresses = null; + + try + { + addresses = Dns.GetHostAddresses(url.Host); + } + catch + { + // If there is a DNS error, we can't stop the script! + return true; + } foreach (IPAddress addr in addresses) { @@ -253,4 +263,4 @@ namespace OpenSim.Framework return allowed; } } -} \ No newline at end of file +} -- cgit v1.1