From 85654f82a515df99d01dd2d2f3b619747a6cc5db Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Sat, 19 Feb 2011 12:25:04 -0500
Subject: First cut of AutoBackupModule; only compile-tested so far
---
.../Resources/OptionalModules.addin.xml | 1 +
.../World/AutoBackup/AutoBackupModule.cs | 540 +++++++++++++++++++++
2 files changed, 541 insertions(+)
create mode 100644 OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/Resources/OptionalModules.addin.xml b/OpenSim/Region/OptionalModules/Resources/OptionalModules.addin.xml
index 5eea286..8691343 100644
--- a/OpenSim/Region/OptionalModules/Resources/OptionalModules.addin.xml
+++ b/OpenSim/Region/OptionalModules/Resources/OptionalModules.addin.xml
@@ -13,5 +13,6 @@
+
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
new file mode 100644
index 0000000..ed21e41
--- /dev/null
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -0,0 +1,540 @@
+/*
+ * 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.IO;
+using System.Timers;
+using System.Diagnostics;
+using System.Reflection;
+using System.Collections.Generic;
+using log4net;
+using Nini;
+using Nini.Config;
+using OpenSim.Framework;
+using OpenSim.Region.Framework.Interfaces;
+
+
+/*
+ * Config Settings Documentation.
+ * EACH REGION in e.g. Regions/Regions.ini can have the following options:
+ * AutoBackup: True/False. Default: False. If True, activate auto backup functionality.
+ * This is the only required option for enabling auto-backup; the other options have sane defaults.
+ * If False, the auto-backup module becomes a no-op for the region, and all other AutoBackup* settings are ignored.
+ * AutoBackupInterval: Double, non-negative value. Default: 720 (12 hours).
+ * The number of minutes between each backup attempt.
+ * If a negative or zero value is given, it is equivalent to setting AutoBackup = False.
+ * AutoBackupBusyCheck: True/False. Default: True.
+ * If True, we will only take an auto-backup if a set of conditions are met.
+ * These conditions are heuristics to try and avoid taking a backup when the sim is busy.
+ * AutoBackupScript: String. Default: not specified (disabled).
+ * File path to an executable script or binary to run when an automatic backup is taken.
+ * The file should really be (Windows) an .exe or .bat, or (Linux/Mac) a shell script or binary.
+ * Trying to "run" directories, or things with weird file associations on Win32, might cause unexpected results!
+ * argv[1] of the executed file/script will be the file name of the generated OAR.
+ * If the process can't be spawned for some reason (file not found, no execute permission, etc), write a warning to the console.
+ * AutoBackupNaming: string. Default: Time.
+ * One of three strings (case insensitive):
+ * "Time": Current timestamp is appended to file name. An existing file will never be overwritten.
+ * "Sequential": A number is appended to the file name. So if RegionName_x.oar exists, we'll save to RegionName_{x+1}.oar next. An existing file will never be overwritten.
+ * "Overwrite": Always save to file named "${AutoBackupDir}/RegionName.oar", even if we have to overwrite an existing file.
+ * AutoBackupDir: String. Default: "." (the current directory).
+ * A directory (absolute or relative) where backups should be saved.
+ * */
+
+namespace OpenSim.Region.OptionalModules.World.AutoBackup
+{
+
+ public enum NamingType
+ {
+ TIME,
+ SEQUENTIAL,
+ OVERWRITE
+ };
+
+ public class AutoBackupModule : ISharedRegionModule, IRegionModuleBase
+ {
+
+ private static readonly ILog m_log =
+ LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ //AutoBackupModuleState: Auto-Backup state for one region (scene).
+ public class AutoBackupModuleState
+ {
+ private readonly IScene m_scene;
+ private bool m_enabled = false;
+ private NamingType m_naming = NamingType.TIME;
+ private Timer m_timer = null;
+ private bool m_busycheck = true;
+ private string m_script = null;
+ private string m_dir = ".";
+
+ public AutoBackupModuleState(IScene scene)
+ {
+ m_scene = scene;
+ if(scene == null)
+ throw new NullReferenceException("Required parameter missing for AutoBackupModuleState constructor");
+ }
+
+ public void SetEnabled(bool b)
+ {
+ m_enabled = b;
+ }
+
+ public bool GetEnabled()
+ {
+ return m_enabled;
+ }
+
+ public Timer GetTimer()
+ {
+ return m_timer;
+ }
+
+ public void SetTimer(Timer t)
+ {
+ m_timer = t;
+ }
+
+ public bool GetBusyCheck()
+ {
+ return m_busycheck;
+ }
+
+ public void SetBusyCheck(bool b)
+ {
+ m_busycheck = b;
+ }
+
+
+ public string GetScript()
+ {
+ return m_script;
+ }
+
+ public void SetScript(string s)
+ {
+ m_script = s;
+ }
+
+ public string GetBackupDir()
+ {
+ return m_dir;
+ }
+
+ public void SetBackupDir(string s)
+ {
+ m_dir = s;
+ }
+
+ public NamingType GetNamingType()
+ {
+ return m_naming;
+ }
+
+ public void SetNamingType(NamingType n)
+ {
+ m_naming = n;
+ }
+ }
+
+ //Save memory by setting low initial capacities. Minimizes impact in common cases of all regions using same interval, and instances hosting 1 ~ 4 regions.
+ //Also helps if you don't want AutoBackup at all
+ readonly Dictionary states = new Dictionary(4);
+ readonly Dictionary timers = new Dictionary(1);
+ readonly Dictionary> timerMap = new Dictionary>(1);
+
+ public AutoBackupModule ()
+ {
+
+ }
+
+ #region IRegionModuleBase implementation
+ void IRegionModuleBase.Initialise (Nini.Config.IConfigSource source)
+ {
+ //I have no overall config settings to care about.
+ }
+
+ void IRegionModuleBase.Close ()
+ {
+ //We don't want any timers firing while the sim's coming down; strange things may happen.
+ StopAllTimers();
+ }
+
+ void IRegionModuleBase.AddRegion (Framework.Scenes.Scene scene)
+ {
+ //NO-OP. Wait for the region to be loaded.
+ }
+
+ void IRegionModuleBase.RemoveRegion (Framework.Scenes.Scene scene)
+ {
+ AutoBackupModuleState abms = states[scene];
+ Timer timer = abms.GetTimer();
+ List list = timerMap[timer];
+ list.Remove(scene);
+ if(list.Count == 0)
+ {
+ timerMap.Remove(timer);
+ timers.Remove(timer.Interval);
+ timer.Close();
+ }
+ }
+
+ void IRegionModuleBase.RegionLoaded (Framework.Scenes.Scene scene)
+ {
+ //This really ought not to happen, but just in case, let's pretend it didn't...
+ if(scene == null)
+ return;
+
+ AutoBackupModuleState st = new AutoBackupModuleState(scene);
+ states.Add(scene, st);
+
+ //Read the config settings and set variables.
+ IConfig config = scene.Config.Configs[scene.RegionInfo.RegionName];
+ st.SetEnabled(config.GetBoolean("AutoBackup", false));
+ if(!st.GetEnabled()) //If you don't want AutoBackup, we stop.
+ return;
+
+ //Borrow an existing timer if one exists for the same interval; otherwise, make a new one.
+ double interval = config.GetDouble("AutoBackupInterval", 720);
+ if(timers.ContainsKey(interval))
+ {
+ st.SetTimer(timers[interval]);
+ }
+ else
+ {
+ st.SetTimer(new Timer(interval));
+ timers.Add(interval, st.GetTimer());
+ st.GetTimer().Elapsed += HandleElapsed;
+ }
+
+ //Add the current region to the list of regions tied to this timer.
+ if(timerMap.ContainsKey(st.GetTimer()))
+ {
+ timerMap[st.GetTimer()].Add(scene);
+ }
+ else
+ {
+ List scns = new List(1);
+ timerMap.Add(st.GetTimer(), scns);
+ }
+
+ st.SetBusyCheck(config.GetBoolean("AutoBackupBusyCheck", true));
+
+ //Set file naming algorithm
+ string namingtype = config.GetString("AutoBackupNaming", "Time");
+ if(namingtype.Equals("Time", StringComparison.CurrentCultureIgnoreCase))
+ {
+ st.SetNamingType(NamingType.TIME);
+ }
+ else if(namingtype.Equals("Sequential", StringComparison.CurrentCultureIgnoreCase))
+ {
+ st.SetNamingType(NamingType.SEQUENTIAL);
+ }
+ else if(namingtype.Equals("Overwrite", StringComparison.CurrentCultureIgnoreCase))
+ {
+ st.SetNamingType(NamingType.OVERWRITE);
+ }
+ else
+ {
+ m_log.Warn("Unknown naming type specified for region " + scene.RegionInfo.RegionName + ": " + namingtype);
+ st.SetNamingType(NamingType.TIME);
+ }
+
+ st.SetScript(config.GetString("AutoBackupScript", null));
+ st.SetBackupDir(config.GetString("AutoBackupDir", "."));
+
+ //Let's give the user *one* convenience and auto-mkdir
+ if(st.GetBackupDir() != ".")
+ {
+ try
+ {
+ DirectoryInfo dirinfo = new DirectoryInfo(st.GetBackupDir());
+ if(!dirinfo.Exists)
+ {
+ dirinfo.Create();
+ }
+ }
+ catch(Exception e)
+ {
+ m_log.Warn("BAD NEWS. You won't be able to save backups to directory " + st.GetBackupDir() +
+ " because it doesn't exist or there's a permissions issue with it. Here's the exception.", e);
+ }
+ }
+ }
+
+ void HandleElapsed (object sender, ElapsedEventArgs e)
+ {
+ bool heuristicsRun = false;
+ bool heuristicsPassed = false;
+ foreach(IScene scene in timerMap[(Timer)sender])
+ {
+ AutoBackupModuleState state = states[scene];
+ bool heuristics = state.GetBusyCheck();
+
+ //Fast path: heuristics are on; already ran em; and sim is fine; OR, no heuristics for the region.
+ if((heuristics && heuristicsRun && heuristicsPassed)
+ || !heuristics)
+ {
+ IRegionArchiverModule iram = scene.RequestModuleInterface();
+ string savePath = BuildOarPath(scene.RegionInfo.RegionName, state.GetBackupDir(), state.GetNamingType());
+ if(savePath == null)
+ {
+ m_log.Warn("savePath is null in HandleElapsed");
+ continue;
+ }
+ iram.ArchiveRegion(savePath, null);
+ ExecuteScript(state.GetScript(), savePath);
+ }
+ //Heuristics are on; ran but we're too busy -- keep going. Maybe another region will have heuristics off!
+ else if(heuristics && heuristicsRun && !heuristicsPassed)
+ {
+ continue;
+ }
+ //Logical Deduction: heuristics are on but haven't been run
+ else
+ {
+ heuristicsPassed = RunHeuristics();
+ heuristicsRun = true;
+ if(!heuristicsPassed)
+ continue;
+ }
+ }
+ }
+
+ string IRegionModuleBase.Name {
+ get {
+ return "AutoBackupModule";
+ }
+ }
+
+ Type IRegionModuleBase.ReplaceableInterface {
+ get {
+ return null;
+ }
+ }
+
+ #endregion
+ #region ISharedRegionModule implementation
+ void ISharedRegionModule.PostInitialise ()
+ {
+ //I don't care right now.
+ }
+
+ #endregion
+
+ //Is this even needed?
+ public bool IsSharedModule
+ {
+ get { return true; }
+ }
+
+ private string BuildOarPath(string regionName, string baseDir, NamingType naming)
+ {
+ FileInfo path = null;
+ switch(naming)
+ {
+ case NamingType.OVERWRITE:
+ path = new FileInfo(baseDir + Path.DirectorySeparatorChar + regionName);
+ return path.FullName;
+ case NamingType.TIME:
+ path = new FileInfo(baseDir + Path.DirectorySeparatorChar + regionName + GetTimeString() + ".oar");
+ return path.FullName;
+ case NamingType.SEQUENTIAL:
+ path = new FileInfo(baseDir + Path.DirectorySeparatorChar + regionName + "_" + GetNextFile(baseDir, regionName) + ".oar");
+ return path.FullName;
+ default:
+ m_log.Warn("VERY BAD: Unhandled case element " + naming.ToString());
+ break;
+ }
+
+ return path.FullName;
+ }
+
+ //Welcome to the TIME STRING. 4 CORNER INTEGERS, CUBES 4 QUAD MEMORY -- No 1 Integer God.
+ //(Terrible reference to )
+ //This format may turn out to be too unwieldy to keep...
+ //Besides, that's what ctimes are for. But then how do I name each file uniquely without using a GUID?
+ //Sequential numbers, right? Ugh. Almost makes TOO much sense.
+ private string GetTimeString()
+ {
+ StringWriter sw = new StringWriter();
+ sw.Write("_");
+ DateTime now = DateTime.Now;
+ sw.Write(now.Year);
+ sw.Write("y_");
+ sw.Write(now.Month);
+ sw.Write("M_");
+ sw.Write(now.Day);
+ sw.Write("d_");
+ sw.Write(now.Hour);
+ sw.Write("h_");
+ sw.Write(now.Minute);
+ sw.Write("m_");
+ sw.Write(now.Second);
+ sw.Write("s");
+ sw.Flush();
+ string output = sw.ToString();
+ sw.Close();
+ return output;
+ }
+
+ //Get the next logical file name
+ //I really shouldn't put fields here, but for now.... ;)
+ private string m_dirName = null;
+ private string m_regionName = null;
+ private string GetNextFile(string dirName, string regionName)
+ {
+ FileInfo uniqueFile = null;
+ m_dirName = dirName;
+ m_regionName = regionName;
+ long biggestExistingFile = HalfIntervalMaximize(1, FileExistsTest);
+ biggestExistingFile++; //We don't want to overwrite the biggest existing file; we want to write to the NEXT biggest.
+
+ uniqueFile = new FileInfo(m_dirName + Path.DirectorySeparatorChar + m_regionName + "_" + biggestExistingFile + ".oar");
+ if(uniqueFile.Exists)
+ {
+ //Congratulations, your strange deletion patterns fooled my half-interval search into picking an existing file!
+ //Now you get to pay the performance cost :)
+ uniqueFile = UniqueFileSearchLinear(biggestExistingFile);
+ }
+
+ return uniqueFile.FullName;
+ }
+
+ private bool RunHeuristics()
+ {
+ return true;
+ }
+
+ private void ExecuteScript(string scriptName, string savePath)
+ {
+ //Fast path out
+ if(scriptName == null || scriptName.Length <= 0)
+ return;
+
+ try
+ {
+ FileInfo fi = new FileInfo(scriptName);
+ if(fi.Exists)
+ {
+ ProcessStartInfo psi = new ProcessStartInfo(scriptName);
+ psi.Arguments = savePath;
+ psi.CreateNoWindow = true;
+ Process proc = Process.Start(psi);
+ proc.ErrorDataReceived += HandleProcErrorDataReceived;
+ }
+ }
+ catch(Exception e)
+ {
+ m_log.Warn("Exception encountered when trying to run script for oar backup " + savePath, e);
+ }
+ }
+
+ void HandleProcErrorDataReceived (object sender, DataReceivedEventArgs e)
+ {
+ m_log.Warn("ExecuteScript hook " + ((Process)sender).ProcessName + " is yacking on stderr: " + e.Data);
+ }
+
+ private void StopAllTimers()
+ {
+ foreach(Timer t in timerMap.Keys)
+ {
+ t.Close();
+ }
+ }
+
+ /* Find the largest value for which the predicate returns true.
+ * We use a bisection algorithm (half interval) to make the algorithm scalable.
+ * The worst-case complexity is about O(log(n)^2) in practice.
+ * Only for extremely small values (under 10) do you notice it taking more iterations than a linear search.
+ * The number of predicate invocations only hits a few hundred when the maximized value
+ * is in the tens of millions, so prepare for the predicate to be invoked between 10 and 100 times.
+ * And of course it is fantastic with powers of 2, which are densely packed in values under 100 anyway.
+ * The Predicate parameter must be a function that accepts a long and returns a bool.
+ * */
+ public long HalfIntervalMaximize(long start, Predicate pred)
+ {
+ long prev = start, curr = start, biggest = 0;
+
+ if(start < 0)
+ throw new IndexOutOfRangeException("Start value for HalfIntervalMaximize must be non-negative");
+
+ do
+ {
+ if(pred(curr))
+ {
+ if(curr > biggest)
+ {
+ biggest = curr;
+ }
+ prev = curr;
+ if(curr == 0)
+ {
+ //Special case because 0 * 2 = 0 :)
+ curr = 1;
+ }
+ else
+ {
+ //Look deeper
+ curr *= 2;
+ }
+ }
+ else
+ {
+ // We went too far, back off halfway
+ curr = (curr + prev) / 2;
+ }
+ }
+ while(curr - prev > 0);
+
+ return biggest;
+ }
+
+ public bool FileExistsTest(long num)
+ {
+ FileInfo test = new FileInfo(m_dirName + Path.DirectorySeparatorChar + m_regionName + "_" + num + ".oar");
+ return test.Exists;
+ }
+
+
+ //Very slow, hence why we try the HalfIntervalMaximize first!
+ public FileInfo UniqueFileSearchLinear(long start)
+ {
+ long l = start;
+ FileInfo retval = null;
+ do
+ {
+ retval = new FileInfo(m_dirName + Path.DirectorySeparatorChar + m_regionName + "_" + (l++) + ".oar");
+ }
+ while(retval.Exists);
+
+ return retval;
+ }
+}
+
+}
+
--
cgit v1.1
From dfa63ff0313610e1c2d262f7e660af94134159f6 Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Sat, 19 Feb 2011 20:15:38 -0500
Subject: Let GetNextFile do all the string-building work for SEQUENTIAL.
---
OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index ed21e41..f8d9060 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -364,7 +364,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
path = new FileInfo(baseDir + Path.DirectorySeparatorChar + regionName + GetTimeString() + ".oar");
return path.FullName;
case NamingType.SEQUENTIAL:
- path = new FileInfo(baseDir + Path.DirectorySeparatorChar + regionName + "_" + GetNextFile(baseDir, regionName) + ".oar");
+ path = new FileInfo(GetNextFile(baseDir, regionName));
return path.FullName;
default:
m_log.Warn("VERY BAD: Unhandled case element " + naming.ToString());
--
cgit v1.1
From 99e82602826e7d100d04a4bb229188be240db1ad Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Sat, 19 Feb 2011 22:08:19 -0500
Subject: Add [Modules] option for unconditionally disabling entire module
globally (for easy configuration)
---
.../World/AutoBackup/AutoBackupModule.cs | 25 +++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index f8d9060..7593b95 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -40,6 +40,9 @@ using OpenSim.Region.Framework.Interfaces;
/*
* Config Settings Documentation.
+ * At the TOP LEVEL, e.g. in OpenSim.ini, we have one option:
+ * In the [Modules] section:
+ * AutoBackupModule: True/False. Default: False. If True, use the auto backup module. Otherwise it will be disabled regardless of what settings are in Regions.ini!
* EACH REGION in e.g. Regions/Regions.ini can have the following options:
* AutoBackup: True/False. Default: False. If True, activate auto backup functionality.
* This is the only required option for enabling auto-backup; the other options have sane defaults.
@@ -166,6 +169,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
readonly Dictionary states = new Dictionary(4);
readonly Dictionary timers = new Dictionary(1);
readonly Dictionary> timerMap = new Dictionary>(1);
+ private bool m_Enabled = false; //Whether the shared module should be enabled at all. NOT the same as m_Enabled in AutoBackupModuleState!
public AutoBackupModule ()
{
@@ -175,11 +179,24 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
#region IRegionModuleBase implementation
void IRegionModuleBase.Initialise (Nini.Config.IConfigSource source)
{
- //I have no overall config settings to care about.
+ //Determine if we have been enabled at all in OpenSim.ini -- this is part and parcel of being an optional module
+ IConfig moduleConfig = source.Configs["Modules"];
+ if (moduleConfig != null)
+ {
+ m_Enabled = moduleConfig.GetBoolean("AutoBackupModule", false);
+ if (m_Enabled)
+ {
+ m_log.Info("[AUTO BACKUP MODULE]: AutoBackupModule enabled");
+ }
+
+ }
}
void IRegionModuleBase.Close ()
{
+ if(!m_Enabled)
+ return;
+
//We don't want any timers firing while the sim's coming down; strange things may happen.
StopAllTimers();
}
@@ -191,6 +208,9 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
void IRegionModuleBase.RemoveRegion (Framework.Scenes.Scene scene)
{
+ if(!m_Enabled)
+ return;
+
AutoBackupModuleState abms = states[scene];
Timer timer = abms.GetTimer();
List list = timerMap[timer];
@@ -205,6 +225,9 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
void IRegionModuleBase.RegionLoaded (Framework.Scenes.Scene scene)
{
+ if(!m_Enabled)
+ return;
+
//This really ought not to happen, but just in case, let's pretend it didn't...
if(scene == null)
return;
--
cgit v1.1
From 06a4810d210fcbfe1a859317698f791129ecdaf1 Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Sat, 19 Feb 2011 22:29:45 -0500
Subject: Fix config source by taking it from Initialize
---
OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index 7593b95..68cf219 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -169,6 +169,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
readonly Dictionary states = new Dictionary(4);
readonly Dictionary timers = new Dictionary(1);
readonly Dictionary> timerMap = new Dictionary>(1);
+ private IConfigSource m_configSource = null;
private bool m_Enabled = false; //Whether the shared module should be enabled at all. NOT the same as m_Enabled in AutoBackupModuleState!
public AutoBackupModule ()
@@ -180,6 +181,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
void IRegionModuleBase.Initialise (Nini.Config.IConfigSource source)
{
//Determine if we have been enabled at all in OpenSim.ini -- this is part and parcel of being an optional module
+ m_configSource = source;
IConfig moduleConfig = source.Configs["Modules"];
if (moduleConfig != null)
{
@@ -236,7 +238,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
states.Add(scene, st);
//Read the config settings and set variables.
- IConfig config = scene.Config.Configs[scene.RegionInfo.RegionName];
+ IConfig config = m_configSource.Configs[scene.RegionInfo.RegionName];
st.SetEnabled(config.GetBoolean("AutoBackup", false));
if(!st.GetEnabled()) //If you don't want AutoBackup, we stop.
return;
--
cgit v1.1
From 3c9bf5c476eb294748b53e0c65e7880c26c108ab Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Sat, 19 Feb 2011 22:47:05 -0500
Subject: Fix config source stuff harder (debug console prints only; no fix
yet)
---
.../OptionalModules/World/AutoBackup/AutoBackupModule.cs | 11 +++++++++++
1 file changed, 11 insertions(+)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index 68cf219..ebad12f 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -234,11 +234,22 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
if(scene == null)
return;
+ m_log.Info("[AUTO BACKUP MODULE]: RegionLoaded for region: " + scene.RegionInfo.RegionName);
+
AutoBackupModuleState st = new AutoBackupModuleState(scene);
states.Add(scene, st);
//Read the config settings and set variables.
IConfig config = m_configSource.Configs[scene.RegionInfo.RegionName];
+ if(config == null)
+ {
+ m_log.Warn("[AUTO BACKUP MODULE]: Can't get config settings! Here are the IConfigs available:");
+ foreach(IConfig c in m_configSource.Configs)
+ {
+ m_log.Warn("[AUTO BACKUP MODULE]: " + c.Name);
+ }
+ throw new NullReferenceException("This is debug code");
+ }
st.SetEnabled(config.GetBoolean("AutoBackup", false));
if(!st.GetEnabled()) //If you don't want AutoBackup, we stop.
return;
--
cgit v1.1
From e5c08a553c4cbf0485d7eb93fc891e002ae04254 Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Sat, 19 Feb 2011 22:55:32 -0500
Subject: More debug messages
---
.../OptionalModules/World/AutoBackup/AutoBackupModule.cs | 10 ++++++++++
1 file changed, 10 insertions(+)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index ebad12f..21e48df 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -248,6 +248,16 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
{
m_log.Warn("[AUTO BACKUP MODULE]: " + c.Name);
}
+
+ if(scene.Config != null)
+ {
+ m_log.Warn("[AUTO BACKUP MODULE]: And in scene.Config:");
+ IConfigSource tmp = scene.Config;
+ foreach(IConfig d in tmp.Configs)
+ {
+ m_log.Warn("[AUTO BACKUP MODULE]: " + d.Name);
+ }
+ }
throw new NullReferenceException("This is debug code");
}
st.SetEnabled(config.GetBoolean("AutoBackup", false));
--
cgit v1.1
From 7eac0af5590a450752a9fd2c57abe7d1a6d84c4c Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Sat, 19 Feb 2011 23:07:54 -0500
Subject: Fix an actual bug in the timer interval calculation (minutes -> msec)
---
.../World/AutoBackup/AutoBackupModule.cs | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index 21e48df..4899718 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -240,7 +240,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
states.Add(scene, st);
//Read the config settings and set variables.
- IConfig config = m_configSource.Configs[scene.RegionInfo.RegionName];
+ IConfig config = scene.Config.Configs["Startup"];
if(config == null)
{
m_log.Warn("[AUTO BACKUP MODULE]: Can't get config settings! Here are the IConfigs available:");
@@ -258,11 +258,17 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
m_log.Warn("[AUTO BACKUP MODULE]: " + d.Name);
}
}
- throw new NullReferenceException("This is debug code");
+ throw new NullReferenceException("This is debug code"); //This crashes the whole process -- not good
}
st.SetEnabled(config.GetBoolean("AutoBackup", false));
if(!st.GetEnabled()) //If you don't want AutoBackup, we stop.
+ {
return;
+ }
+ else
+ {
+ m_log.Info("[AUTO BACKUP MODULE]: Region " + scene.RegionInfo.RegionName + " is AutoBackup ENABLED.");
+ }
//Borrow an existing timer if one exists for the same interval; otherwise, make a new one.
double interval = config.GetDouble("AutoBackupInterval", 720);
@@ -272,7 +278,13 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
}
else
{
- st.SetTimer(new Timer(interval));
+ //0 or negative interval == do nothing.
+ if(interval <= 0.0)
+ {
+ st.SetEnabled(false);
+ return;
+ }
+ st.SetTimer(new Timer(interval * 60000)); //Milliseconds -> minutes
timers.Add(interval, st.GetTimer());
st.GetTimer().Elapsed += HandleElapsed;
}
--
cgit v1.1
From c2658c2f14d75c0c90f40c3673eb35677bcf2cb7 Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Sat, 19 Feb 2011 23:14:43 -0500
Subject: More relevant console messages, and maybe fix config problem
---
.../World/AutoBackup/AutoBackupModule.cs | 25 ++++++----------------
1 file changed, 6 insertions(+), 19 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index 4899718..4735620 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -234,35 +234,22 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
if(scene == null)
return;
- m_log.Info("[AUTO BACKUP MODULE]: RegionLoaded for region: " + scene.RegionInfo.RegionName);
-
AutoBackupModuleState st = new AutoBackupModuleState(scene);
states.Add(scene, st);
//Read the config settings and set variables.
- IConfig config = scene.Config.Configs["Startup"];
+ IConfig config = scene.Config.Configs["AutoBackupModule"];
if(config == null)
{
- m_log.Warn("[AUTO BACKUP MODULE]: Can't get config settings! Here are the IConfigs available:");
- foreach(IConfig c in m_configSource.Configs)
- {
- m_log.Warn("[AUTO BACKUP MODULE]: " + c.Name);
- }
-
- if(scene.Config != null)
- {
- m_log.Warn("[AUTO BACKUP MODULE]: And in scene.Config:");
- IConfigSource tmp = scene.Config;
- foreach(IConfig d in tmp.Configs)
- {
- m_log.Warn("[AUTO BACKUP MODULE]: " + d.Name);
- }
- }
- throw new NullReferenceException("This is debug code"); //This crashes the whole process -- not good
+ //No config settings for this, let's just give up.
+ st.SetEnabled(false);
+ m_log.Info("[AUTO BACKUP MODULE]: Region " + scene.RegionInfo.RegionName + " is NOT AutoBackup enabled.");
+ return;
}
st.SetEnabled(config.GetBoolean("AutoBackup", false));
if(!st.GetEnabled()) //If you don't want AutoBackup, we stop.
{
+ m_log.Info("[AUTO BACKUP MODULE]: Region " + scene.RegionInfo.RegionName + " is NOT AutoBackup enabled.");
return;
}
else
--
cgit v1.1
From 2da9bb3ca2fbd4294409b74733f30b79bd48e2ec Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Fri, 25 Feb 2011 08:31:10 -0500
Subject: Try to fix config one more time.
Note that the way we specify settings has changed significantly here.
---
.../World/AutoBackup/AutoBackupModule.cs | 25 ++++++++++++----------
1 file changed, 14 insertions(+), 11 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index 4735620..0869b0c 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -40,10 +40,13 @@ using OpenSim.Region.Framework.Interfaces;
/*
* Config Settings Documentation.
- * At the TOP LEVEL, e.g. in OpenSim.ini, we have one option:
+ * At the TOP LEVEL, e.g. in OpenSim.ini, we have the following options:
* In the [Modules] section:
* AutoBackupModule: True/False. Default: False. If True, use the auto backup module. Otherwise it will be disabled regardless of what settings are in Regions.ini!
- * EACH REGION in e.g. Regions/Regions.ini can have the following options:
+ * EACH REGION, in OpenSim.ini, can have the following settings under the [AutoBackupModule] section.
+ * VERY IMPORTANT: You must create the key name as follows: .
+ * Example: My region is named Foo.
+ * If I wanted to specify the "AutoBackupInterval" key below, I would name my key "Foo.AutoBackupInterval", under the [AutoBackupModule] section of OpenSim.ini.
* AutoBackup: True/False. Default: False. If True, activate auto backup functionality.
* This is the only required option for enabling auto-backup; the other options have sane defaults.
* If False, the auto-backup module becomes a no-op for the region, and all other AutoBackup* settings are ignored.
@@ -190,7 +193,6 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
{
m_log.Info("[AUTO BACKUP MODULE]: AutoBackupModule enabled");
}
-
}
}
@@ -234,19 +236,20 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
if(scene == null)
return;
+ string sRegionName = scene.RegionInfo.RegionName;
AutoBackupModuleState st = new AutoBackupModuleState(scene);
states.Add(scene, st);
//Read the config settings and set variables.
- IConfig config = scene.Config.Configs["AutoBackupModule"];
+ IConfig config = m_configSource.Configs["AutoBackupModule"];
if(config == null)
{
- //No config settings for this, let's just give up.
+ //No config settings for any regions, let's just give up.
st.SetEnabled(false);
m_log.Info("[AUTO BACKUP MODULE]: Region " + scene.RegionInfo.RegionName + " is NOT AutoBackup enabled.");
return;
}
- st.SetEnabled(config.GetBoolean("AutoBackup", false));
+ st.SetEnabled(config.GetBoolean(sRegionName + ".AutoBackup", false));
if(!st.GetEnabled()) //If you don't want AutoBackup, we stop.
{
m_log.Info("[AUTO BACKUP MODULE]: Region " + scene.RegionInfo.RegionName + " is NOT AutoBackup enabled.");
@@ -258,7 +261,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
}
//Borrow an existing timer if one exists for the same interval; otherwise, make a new one.
- double interval = config.GetDouble("AutoBackupInterval", 720);
+ double interval = config.GetDouble(sRegionName + ".AutoBackupInterval", 720);
if(timers.ContainsKey(interval))
{
st.SetTimer(timers[interval]);
@@ -287,10 +290,10 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
timerMap.Add(st.GetTimer(), scns);
}
- st.SetBusyCheck(config.GetBoolean("AutoBackupBusyCheck", true));
+ st.SetBusyCheck(config.GetBoolean(sRegionName + ".AutoBackupBusyCheck", true));
//Set file naming algorithm
- string namingtype = config.GetString("AutoBackupNaming", "Time");
+ string namingtype = config.GetString(sRegionName + ".AutoBackupNaming", "Time");
if(namingtype.Equals("Time", StringComparison.CurrentCultureIgnoreCase))
{
st.SetNamingType(NamingType.TIME);
@@ -309,8 +312,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
st.SetNamingType(NamingType.TIME);
}
- st.SetScript(config.GetString("AutoBackupScript", null));
- st.SetBackupDir(config.GetString("AutoBackupDir", "."));
+ st.SetScript(config.GetString(sRegionName + ".AutoBackupScript", null));
+ st.SetBackupDir(config.GetString(sRegionName + ".AutoBackupDir", "."));
//Let's give the user *one* convenience and auto-mkdir
if(st.GetBackupDir() != ".")
--
cgit v1.1
From 7fa8ed0c47b3b0a5c87cd27b2620aeef05a7fe86 Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Fri, 25 Feb 2011 08:48:02 -0500
Subject: Config works, but timer isn't firing. Hmm.
---
.../OptionalModules/World/AutoBackup/AutoBackupModule.cs | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index 0869b0c..68c8301 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -246,25 +246,26 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
{
//No config settings for any regions, let's just give up.
st.SetEnabled(false);
- m_log.Info("[AUTO BACKUP MODULE]: Region " + scene.RegionInfo.RegionName + " is NOT AutoBackup enabled.");
+ m_log.Info("[AUTO BACKUP MODULE]: Region " + sRegionName + " is NOT AutoBackup enabled.");
return;
}
st.SetEnabled(config.GetBoolean(sRegionName + ".AutoBackup", false));
if(!st.GetEnabled()) //If you don't want AutoBackup, we stop.
{
- m_log.Info("[AUTO BACKUP MODULE]: Region " + scene.RegionInfo.RegionName + " is NOT AutoBackup enabled.");
+ m_log.Info("[AUTO BACKUP MODULE]: Region " + sRegionName + " is NOT AutoBackup enabled.");
return;
}
else
{
- m_log.Info("[AUTO BACKUP MODULE]: Region " + scene.RegionInfo.RegionName + " is AutoBackup ENABLED.");
+ m_log.Info("[AUTO BACKUP MODULE]: Region " + sRegionName + " is AutoBackup ENABLED.");
}
//Borrow an existing timer if one exists for the same interval; otherwise, make a new one.
- double interval = config.GetDouble(sRegionName + ".AutoBackupInterval", 720);
+ double interval = config.GetDouble(sRegionName + ".AutoBackupInterval", 720) * 60000;
if(timers.ContainsKey(interval))
{
st.SetTimer(timers[interval]);
+ m_log.Debug("[AUTO BACKUP MODULE]: Reusing timer for " + interval + " msec for region " + sRegionName);
}
else
{
@@ -274,9 +275,10 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
st.SetEnabled(false);
return;
}
- st.SetTimer(new Timer(interval * 60000)); //Milliseconds -> minutes
+ st.SetTimer(new Timer(interval)); //Milliseconds -> minutes
timers.Add(interval, st.GetTimer());
st.GetTimer().Elapsed += HandleElapsed;
+ m_log.Debug("[AUTO BACKUP MODULE]: New timer for " + interval + " msec for region " + sRegionName);
}
//Add the current region to the list of regions tied to this timer.
--
cgit v1.1
From bb1f03abc6a3c12b4f511e4f212f5673c4d31dc4 Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Fri, 25 Feb 2011 08:57:48 -0500
Subject: Make timer auto-respawn.
---
.../Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index 68c8301..58b93c0 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -275,9 +275,11 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
st.SetEnabled(false);
return;
}
- st.SetTimer(new Timer(interval)); //Milliseconds -> minutes
- timers.Add(interval, st.GetTimer());
- st.GetTimer().Elapsed += HandleElapsed;
+ Timer tim = new Timer(interval);
+ st.SetTimer(tim); //Milliseconds -> minutes
+ timers.Add(interval, tim);
+ tim.Elapsed += HandleElapsed;
+ tim.AutoReset = True;
m_log.Debug("[AUTO BACKUP MODULE]: New timer for " + interval + " msec for region " + sRegionName);
}
@@ -351,6 +353,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
{
IRegionArchiverModule iram = scene.RequestModuleInterface();
string savePath = BuildOarPath(scene.RegionInfo.RegionName, state.GetBackupDir(), state.GetNamingType());
+ m_log.Debug("[AUTO BACKUP MODULE]: savePath = " + savePath);
if(savePath == null)
{
m_log.Warn("savePath is null in HandleElapsed");
--
cgit v1.1
From cb049aa15f64fec5ba4b08aedddf98b68ab5044e Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Fri, 25 Feb 2011 08:58:42 -0500
Subject: s/True/true
---
OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index 58b93c0..ba5e24b 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -279,7 +279,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
st.SetTimer(tim); //Milliseconds -> minutes
timers.Add(interval, tim);
tim.Elapsed += HandleElapsed;
- tim.AutoReset = True;
+ tim.AutoReset = true;
m_log.Debug("[AUTO BACKUP MODULE]: New timer for " + interval + " msec for region " + sRegionName);
}
--
cgit v1.1
From d3511ca592eff780e6d707680ac1d20a24b9fc58 Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Fri, 25 Feb 2011 09:03:11 -0500
Subject: Start the timer. (Could it be that simple?)
---
OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs | 1 +
1 file changed, 1 insertion(+)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index ba5e24b..77126c7 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -280,6 +280,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
timers.Add(interval, tim);
tim.Elapsed += HandleElapsed;
tim.AutoReset = true;
+ tim.Start();
m_log.Debug("[AUTO BACKUP MODULE]: New timer for " + interval + " msec for region " + sRegionName);
}
--
cgit v1.1
From 34b6904939dd551aafa68ea8e02dc5177d395b3b Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Sat, 26 Feb 2011 22:09:19 -0500
Subject: First working commit of AutoBackupModule.
It seems to do something! Heuristics are still TODO, but this is alpha 1.
---
.../World/AutoBackup/AutoBackupModule.cs | 34 +++++++++++++++-------
1 file changed, 23 insertions(+), 11 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index 77126c7..e3686ac 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -281,7 +281,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
tim.Elapsed += HandleElapsed;
tim.AutoReset = true;
tim.Start();
- m_log.Debug("[AUTO BACKUP MODULE]: New timer for " + interval + " msec for region " + sRegionName);
+ //m_log.Debug("[AUTO BACKUP MODULE]: New timer for " + interval + " msec for region " + sRegionName);
}
//Add the current region to the list of regions tied to this timer.
@@ -292,6 +292,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
else
{
List scns = new List(1);
+ scns.Add(scene);
timerMap.Add(st.GetTimer(), scns);
}
@@ -343,6 +344,10 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
{
bool heuristicsRun = false;
bool heuristicsPassed = false;
+ if(!timerMap.ContainsKey((Timer) sender))
+ {
+ m_log.Debug("Code-up error: timerMap doesn't contain timer " + sender.ToString());
+ }
foreach(IScene scene in timerMap[(Timer)sender])
{
AutoBackupModuleState state = states[scene];
@@ -352,16 +357,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
if((heuristics && heuristicsRun && heuristicsPassed)
|| !heuristics)
{
- IRegionArchiverModule iram = scene.RequestModuleInterface();
- string savePath = BuildOarPath(scene.RegionInfo.RegionName, state.GetBackupDir(), state.GetNamingType());
- m_log.Debug("[AUTO BACKUP MODULE]: savePath = " + savePath);
- if(savePath == null)
- {
- m_log.Warn("savePath is null in HandleElapsed");
- continue;
- }
- iram.ArchiveRegion(savePath, null);
- ExecuteScript(state.GetScript(), savePath);
+ doRegionBackup(scene);
}
//Heuristics are on; ran but we're too busy -- keep going. Maybe another region will have heuristics off!
else if(heuristics && heuristicsRun && !heuristicsPassed)
@@ -375,9 +371,25 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
heuristicsRun = true;
if(!heuristicsPassed)
continue;
+ doRegionBackup(scene);
}
}
}
+
+ void doRegionBackup(IScene scene)
+ {
+ AutoBackupModuleState state = states[scene];
+ IRegionArchiverModule iram = scene.RequestModuleInterface();
+ string savePath = BuildOarPath(scene.RegionInfo.RegionName, state.GetBackupDir(), state.GetNamingType());
+ //m_log.Debug("[AUTO BACKUP MODULE]: savePath = " + savePath);
+ if(savePath == null)
+ {
+ m_log.Warn("[AUTO BACKUP MODULE]: savePath is null in HandleElapsed");
+ return;
+ }
+ iram.ArchiveRegion(savePath, null);
+ ExecuteScript(state.GetScript(), savePath);
+ }
string IRegionModuleBase.Name {
get {
--
cgit v1.1
From a01c44e74de669ea2643b8bfe76a7e78ca4740a4 Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Mon, 28 Feb 2011 11:04:54 -0500
Subject: Be smarter about stopping timers. Cleanup formatting.
Use a boolean flag to tell timers that fire after IRegionModuleBase.Close() is called that they should not execute.
Also, I used MonoDevelop's auto-formatting feature to format the code uniformly. No guarantee about variable names though.
---
.../World/AutoBackup/AutoBackupModule.cs | 502 +++++++++------------
1 file changed, 225 insertions(+), 277 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index e3686ac..54b9b09 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -73,20 +73,19 @@ using OpenSim.Region.Framework.Interfaces;
namespace OpenSim.Region.OptionalModules.World.AutoBackup
{
-
+
public enum NamingType
{
TIME,
SEQUENTIAL,
OVERWRITE
- };
-
+ }
+
public class AutoBackupModule : ISharedRegionModule, IRegionModuleBase
{
-
- private static readonly ILog m_log =
- LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
+
+ private static readonly ILog m_log = LogManager.GetLogger (MethodBase.GetCurrentMethod ().DeclaringType);
+
//AutoBackupModuleState: Auto-Backup state for one region (scene).
public class AutoBackupModuleState
{
@@ -97,84 +96,87 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
private bool m_busycheck = true;
private string m_script = null;
private string m_dir = ".";
-
- public AutoBackupModuleState(IScene scene)
+
+ public AutoBackupModuleState (IScene scene)
{
m_scene = scene;
- if(scene == null)
- throw new NullReferenceException("Required parameter missing for AutoBackupModuleState constructor");
+ if (scene == null)
+ throw new NullReferenceException ("Required parameter missing for AutoBackupModuleState constructor");
}
-
- public void SetEnabled(bool b)
+
+ public void SetEnabled (bool b)
{
- m_enabled = b;
+ m_enabled = b;
}
-
- public bool GetEnabled()
+
+ public bool GetEnabled ()
{
- return m_enabled;
+ return m_enabled;
}
-
- public Timer GetTimer()
+
+ public Timer GetTimer ()
{
- return m_timer;
+ return m_timer;
}
-
- public void SetTimer(Timer t)
+
+ public void SetTimer (Timer t)
{
- m_timer = t;
+ m_timer = t;
}
-
- public bool GetBusyCheck()
+
+ public bool GetBusyCheck ()
{
- return m_busycheck;
+ return m_busycheck;
}
-
- public void SetBusyCheck(bool b)
+
+ public void SetBusyCheck (bool b)
{
- m_busycheck = b;
+ m_busycheck = b;
}
-
-
- public string GetScript()
+
+
+ public string GetScript ()
{
- return m_script;
+ return m_script;
}
-
- public void SetScript(string s)
+
+ public void SetScript (string s)
{
- m_script = s;
+ m_script = s;
}
-
- public string GetBackupDir()
+
+ public string GetBackupDir ()
{
- return m_dir;
+ return m_dir;
}
-
- public void SetBackupDir(string s)
+
+ public void SetBackupDir (string s)
{
- m_dir = s;
+ m_dir = s;
}
-
- public NamingType GetNamingType()
+
+ public NamingType GetNamingType ()
{
return m_naming;
}
-
- public void SetNamingType(NamingType n)
+
+ public void SetNamingType (NamingType n)
{
- m_naming = n;
+ m_naming = n;
}
}
-
+
//Save memory by setting low initial capacities. Minimizes impact in common cases of all regions using same interval, and instances hosting 1 ~ 4 regions.
//Also helps if you don't want AutoBackup at all
- readonly Dictionary states = new Dictionary(4);
- readonly Dictionary timers = new Dictionary(1);
- readonly Dictionary> timerMap = new Dictionary>(1);
+ readonly Dictionary states = new Dictionary (4);
+ readonly Dictionary timers = new Dictionary (1);
+ readonly Dictionary> timerMap = new Dictionary> (1);
private IConfigSource m_configSource = null;
- private bool m_Enabled = false; //Whether the shared module should be enabled at all. NOT the same as m_Enabled in AutoBackupModuleState!
-
+ private bool m_Enabled = false;
+ //Whether the shared module should be enabled at all. NOT the same as m_Enabled in AutoBackupModuleState!
+ private bool m_closed = false;
+ //True means IRegionModuleBase.Close() was called on us, and we should stop operation ASAP.
+ //Used to prevent elapsing timers after Close() is called from trying to start an autobackup while the sim is shutting down.
public AutoBackupModule ()
{
@@ -185,24 +187,22 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
{
//Determine if we have been enabled at all in OpenSim.ini -- this is part and parcel of being an optional module
m_configSource = source;
- IConfig moduleConfig = source.Configs["Modules"];
- if (moduleConfig != null)
- {
- m_Enabled = moduleConfig.GetBoolean("AutoBackupModule", false);
- if (m_Enabled)
- {
- m_log.Info("[AUTO BACKUP MODULE]: AutoBackupModule enabled");
- }
- }
+ IConfig moduleConfig = source.Configs["Modules"];
+ if (moduleConfig != null) {
+ m_Enabled = moduleConfig.GetBoolean ("AutoBackupModule", false);
+ if (m_Enabled) {
+ m_log.Info ("[AUTO BACKUP MODULE]: AutoBackupModule enabled");
+ }
+ }
}
void IRegionModuleBase.Close ()
{
- if(!m_Enabled)
+ if (!m_Enabled)
return;
//We don't want any timers firing while the sim's coming down; strange things may happen.
- StopAllTimers();
+ StopAllTimers ();
}
void IRegionModuleBase.AddRegion (Framework.Scenes.Scene scene)
@@ -212,327 +212,286 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
void IRegionModuleBase.RemoveRegion (Framework.Scenes.Scene scene)
{
- if(!m_Enabled)
+ if (!m_Enabled)
return;
AutoBackupModuleState abms = states[scene];
- Timer timer = abms.GetTimer();
+ Timer timer = abms.GetTimer ();
List list = timerMap[timer];
- list.Remove(scene);
- if(list.Count == 0)
- {
- timerMap.Remove(timer);
- timers.Remove(timer.Interval);
- timer.Close();
+ list.Remove (scene);
+ if (list.Count == 0) {
+ timerMap.Remove (timer);
+ timers.Remove (timer.Interval);
+ timer.Close ();
}
}
void IRegionModuleBase.RegionLoaded (Framework.Scenes.Scene scene)
{
- if(!m_Enabled)
+ if (!m_Enabled)
return;
//This really ought not to happen, but just in case, let's pretend it didn't...
- if(scene == null)
+ if (scene == null)
return;
string sRegionName = scene.RegionInfo.RegionName;
- AutoBackupModuleState st = new AutoBackupModuleState(scene);
- states.Add(scene, st);
+ AutoBackupModuleState st = new AutoBackupModuleState (scene);
+ states.Add (scene, st);
//Read the config settings and set variables.
IConfig config = m_configSource.Configs["AutoBackupModule"];
- if(config == null)
- {
+ if (config == null) {
//No config settings for any regions, let's just give up.
- st.SetEnabled(false);
- m_log.Info("[AUTO BACKUP MODULE]: Region " + sRegionName + " is NOT AutoBackup enabled.");
+ st.SetEnabled (false);
+ m_log.Info ("[AUTO BACKUP MODULE]: Region " + sRegionName + " is NOT AutoBackup enabled.");
return;
}
- st.SetEnabled(config.GetBoolean(sRegionName + ".AutoBackup", false));
- if(!st.GetEnabled()) //If you don't want AutoBackup, we stop.
- {
- m_log.Info("[AUTO BACKUP MODULE]: Region " + sRegionName + " is NOT AutoBackup enabled.");
+ st.SetEnabled (config.GetBoolean (sRegionName + ".AutoBackup", false));
+ //If you don't want AutoBackup, we stop.
+ if (!st.GetEnabled ()) {
+ m_log.Info ("[AUTO BACKUP MODULE]: Region " + sRegionName + " is NOT AutoBackup enabled.");
return;
- }
- else
- {
- m_log.Info("[AUTO BACKUP MODULE]: Region " + sRegionName + " is AutoBackup ENABLED.");
+ } else {
+ m_log.Info ("[AUTO BACKUP MODULE]: Region " + sRegionName + " is AutoBackup ENABLED.");
}
//Borrow an existing timer if one exists for the same interval; otherwise, make a new one.
- double interval = config.GetDouble(sRegionName + ".AutoBackupInterval", 720) * 60000;
- if(timers.ContainsKey(interval))
- {
- st.SetTimer(timers[interval]);
- m_log.Debug("[AUTO BACKUP MODULE]: Reusing timer for " + interval + " msec for region " + sRegionName);
- }
- else
- {
+ double interval = config.GetDouble (sRegionName + ".AutoBackupInterval", 720) * 60000;
+ if (timers.ContainsKey (interval)) {
+ st.SetTimer (timers[interval]);
+ m_log.Debug ("[AUTO BACKUP MODULE]: Reusing timer for " + interval + " msec for region " + sRegionName);
+ } else {
//0 or negative interval == do nothing.
- if(interval <= 0.0)
- {
- st.SetEnabled(false);
+ if (interval <= 0.0) {
+ st.SetEnabled (false);
return;
}
- Timer tim = new Timer(interval);
- st.SetTimer(tim); //Milliseconds -> minutes
- timers.Add(interval, tim);
- tim.Elapsed += HandleElapsed;
+ Timer tim = new Timer (interval);
+ st.SetTimer (tim);
+ //Milliseconds -> minutes
+ timers.Add (interval, tim);
+ tim.Elapsed += HandleElapsed;
tim.AutoReset = true;
- tim.Start();
+ tim.Start ();
//m_log.Debug("[AUTO BACKUP MODULE]: New timer for " + interval + " msec for region " + sRegionName);
}
//Add the current region to the list of regions tied to this timer.
- if(timerMap.ContainsKey(st.GetTimer()))
- {
- timerMap[st.GetTimer()].Add(scene);
- }
- else
- {
- List scns = new List(1);
- scns.Add(scene);
- timerMap.Add(st.GetTimer(), scns);
+ if (timerMap.ContainsKey (st.GetTimer ())) {
+ timerMap[st.GetTimer ()].Add (scene);
+ } else {
+ List scns = new List (1);
+ scns.Add (scene);
+ timerMap.Add (st.GetTimer (), scns);
}
- st.SetBusyCheck(config.GetBoolean(sRegionName + ".AutoBackupBusyCheck", true));
+ st.SetBusyCheck (config.GetBoolean (sRegionName + ".AutoBackupBusyCheck", true));
//Set file naming algorithm
- string namingtype = config.GetString(sRegionName + ".AutoBackupNaming", "Time");
- if(namingtype.Equals("Time", StringComparison.CurrentCultureIgnoreCase))
- {
- st.SetNamingType(NamingType.TIME);
- }
- else if(namingtype.Equals("Sequential", StringComparison.CurrentCultureIgnoreCase))
- {
- st.SetNamingType(NamingType.SEQUENTIAL);
- }
- else if(namingtype.Equals("Overwrite", StringComparison.CurrentCultureIgnoreCase))
- {
- st.SetNamingType(NamingType.OVERWRITE);
- }
- else
- {
- m_log.Warn("Unknown naming type specified for region " + scene.RegionInfo.RegionName + ": " + namingtype);
- st.SetNamingType(NamingType.TIME);
+ string namingtype = config.GetString (sRegionName + ".AutoBackupNaming", "Time");
+ if (namingtype.Equals ("Time", StringComparison.CurrentCultureIgnoreCase)) {
+ st.SetNamingType (NamingType.TIME);
+ } else if (namingtype.Equals ("Sequential", StringComparison.CurrentCultureIgnoreCase)) {
+ st.SetNamingType (NamingType.SEQUENTIAL);
+ } else if (namingtype.Equals ("Overwrite", StringComparison.CurrentCultureIgnoreCase)) {
+ st.SetNamingType (NamingType.OVERWRITE);
+ } else {
+ m_log.Warn ("Unknown naming type specified for region " + scene.RegionInfo.RegionName + ": " + namingtype);
+ st.SetNamingType (NamingType.TIME);
}
- st.SetScript(config.GetString(sRegionName + ".AutoBackupScript", null));
- st.SetBackupDir(config.GetString(sRegionName + ".AutoBackupDir", "."));
+ st.SetScript (config.GetString (sRegionName + ".AutoBackupScript", null));
+ st.SetBackupDir (config.GetString (sRegionName + ".AutoBackupDir", "."));
//Let's give the user *one* convenience and auto-mkdir
- if(st.GetBackupDir() != ".")
- {
- try
- {
- DirectoryInfo dirinfo = new DirectoryInfo(st.GetBackupDir());
- if(!dirinfo.Exists)
- {
- dirinfo.Create();
+ if (st.GetBackupDir () != ".") {
+ try {
+ DirectoryInfo dirinfo = new DirectoryInfo (st.GetBackupDir ());
+ if (!dirinfo.Exists) {
+ dirinfo.Create ();
}
- }
- catch(Exception e)
- {
- m_log.Warn("BAD NEWS. You won't be able to save backups to directory " + st.GetBackupDir() +
- " because it doesn't exist or there's a permissions issue with it. Here's the exception.", e);
+ } catch (Exception e) {
+ m_log.Warn ("BAD NEWS. You won't be able to save backups to directory " + st.GetBackupDir () + " because it doesn't exist or there's a permissions issue with it. Here's the exception.", e);
}
}
}
void HandleElapsed (object sender, ElapsedEventArgs e)
{
+ if (m_closed)
+ return;
bool heuristicsRun = false;
bool heuristicsPassed = false;
- if(!timerMap.ContainsKey((Timer) sender))
- {
- m_log.Debug("Code-up error: timerMap doesn't contain timer " + sender.ToString());
+ if (!timerMap.ContainsKey ((Timer)sender)) {
+ m_log.Debug ("Code-up error: timerMap doesn't contain timer " + sender.ToString ());
}
- foreach(IScene scene in timerMap[(Timer)sender])
- {
+ foreach (IScene scene in timerMap[(Timer)sender]) {
AutoBackupModuleState state = states[scene];
- bool heuristics = state.GetBusyCheck();
+ bool heuristics = state.GetBusyCheck ();
//Fast path: heuristics are on; already ran em; and sim is fine; OR, no heuristics for the region.
- if((heuristics && heuristicsRun && heuristicsPassed)
- || !heuristics)
- {
- doRegionBackup(scene);
- }
+ if ((heuristics && heuristicsRun && heuristicsPassed) || !heuristics) {
+ doRegionBackup (scene);
//Heuristics are on; ran but we're too busy -- keep going. Maybe another region will have heuristics off!
- else if(heuristics && heuristicsRun && !heuristicsPassed)
- {
+ } else if (heuristics && heuristicsRun && !heuristicsPassed) {
continue;
- }
//Logical Deduction: heuristics are on but haven't been run
- else
- {
- heuristicsPassed = RunHeuristics();
+ } else {
+ heuristicsPassed = RunHeuristics ();
heuristicsRun = true;
- if(!heuristicsPassed)
+ if (!heuristicsPassed)
continue;
- doRegionBackup(scene);
+ doRegionBackup (scene);
}
}
}
-
- void doRegionBackup(IScene scene)
+
+ void doRegionBackup (IScene scene)
{
AutoBackupModuleState state = states[scene];
- IRegionArchiverModule iram = scene.RequestModuleInterface();
- string savePath = BuildOarPath(scene.RegionInfo.RegionName, state.GetBackupDir(), state.GetNamingType());
+ IRegionArchiverModule iram = scene.RequestModuleInterface ();
+ string savePath = BuildOarPath (scene.RegionInfo.RegionName, state.GetBackupDir (), state.GetNamingType ());
//m_log.Debug("[AUTO BACKUP MODULE]: savePath = " + savePath);
- if(savePath == null)
- {
- m_log.Warn("[AUTO BACKUP MODULE]: savePath is null in HandleElapsed");
+ if (savePath == null) {
+ m_log.Warn ("[AUTO BACKUP MODULE]: savePath is null in HandleElapsed");
return;
}
- iram.ArchiveRegion(savePath, null);
- ExecuteScript(state.GetScript(), savePath);
+ iram.ArchiveRegion (savePath, null);
+ ExecuteScript (state.GetScript (), savePath);
}
string IRegionModuleBase.Name {
- get {
- return "AutoBackupModule";
- }
+ get { return "AutoBackupModule"; }
}
Type IRegionModuleBase.ReplaceableInterface {
- get {
- return null;
- }
+ get { return null; }
}
-
+
#endregion
#region ISharedRegionModule implementation
void ISharedRegionModule.PostInitialise ()
{
//I don't care right now.
}
-
+
#endregion
-
+
//Is this even needed?
- public bool IsSharedModule
- {
- get { return true; }
- }
-
- private string BuildOarPath(string regionName, string baseDir, NamingType naming)
+ public bool IsSharedModule {
+ get { return true; }
+ }
+
+ private string BuildOarPath (string regionName, string baseDir, NamingType naming)
{
FileInfo path = null;
- switch(naming)
- {
+ switch (naming) {
case NamingType.OVERWRITE:
- path = new FileInfo(baseDir + Path.DirectorySeparatorChar + regionName);
+ path = new FileInfo (baseDir + Path.DirectorySeparatorChar + regionName);
return path.FullName;
case NamingType.TIME:
- path = new FileInfo(baseDir + Path.DirectorySeparatorChar + regionName + GetTimeString() + ".oar");
+ path = new FileInfo (baseDir + Path.DirectorySeparatorChar + regionName + GetTimeString () + ".oar");
return path.FullName;
case NamingType.SEQUENTIAL:
- path = new FileInfo(GetNextFile(baseDir, regionName));
+ path = new FileInfo (GetNextFile (baseDir, regionName));
return path.FullName;
default:
- m_log.Warn("VERY BAD: Unhandled case element " + naming.ToString());
+ m_log.Warn ("VERY BAD: Unhandled case element " + naming.ToString ());
break;
}
return path.FullName;
}
-
+
//Welcome to the TIME STRING. 4 CORNER INTEGERS, CUBES 4 QUAD MEMORY -- No 1 Integer God.
//(Terrible reference to )
//This format may turn out to be too unwieldy to keep...
//Besides, that's what ctimes are for. But then how do I name each file uniquely without using a GUID?
//Sequential numbers, right? Ugh. Almost makes TOO much sense.
- private string GetTimeString()
+ private string GetTimeString ()
{
- StringWriter sw = new StringWriter();
- sw.Write("_");
+ StringWriter sw = new StringWriter ();
+ sw.Write ("_");
DateTime now = DateTime.Now;
- sw.Write(now.Year);
- sw.Write("y_");
- sw.Write(now.Month);
- sw.Write("M_");
- sw.Write(now.Day);
- sw.Write("d_");
- sw.Write(now.Hour);
- sw.Write("h_");
- sw.Write(now.Minute);
- sw.Write("m_");
- sw.Write(now.Second);
- sw.Write("s");
- sw.Flush();
- string output = sw.ToString();
- sw.Close();
+ sw.Write (now.Year);
+ sw.Write ("y_");
+ sw.Write (now.Month);
+ sw.Write ("M_");
+ sw.Write (now.Day);
+ sw.Write ("d_");
+ sw.Write (now.Hour);
+ sw.Write ("h_");
+ sw.Write (now.Minute);
+ sw.Write ("m_");
+ sw.Write (now.Second);
+ sw.Write ("s");
+ sw.Flush ();
+ string output = sw.ToString ();
+ sw.Close ();
return output;
}
-
+
//Get the next logical file name
//I really shouldn't put fields here, but for now.... ;)
private string m_dirName = null;
private string m_regionName = null;
- private string GetNextFile(string dirName, string regionName)
+ private string GetNextFile (string dirName, string regionName)
{
FileInfo uniqueFile = null;
m_dirName = dirName;
m_regionName = regionName;
- long biggestExistingFile = HalfIntervalMaximize(1, FileExistsTest);
- biggestExistingFile++; //We don't want to overwrite the biggest existing file; we want to write to the NEXT biggest.
-
- uniqueFile = new FileInfo(m_dirName + Path.DirectorySeparatorChar + m_regionName + "_" + biggestExistingFile + ".oar");
- if(uniqueFile.Exists)
- {
+ long biggestExistingFile = HalfIntervalMaximize (1, FileExistsTest);
+ biggestExistingFile++;
+ //We don't want to overwrite the biggest existing file; we want to write to the NEXT biggest.
+ uniqueFile = new FileInfo (m_dirName + Path.DirectorySeparatorChar + m_regionName + "_" + biggestExistingFile + ".oar");
+ if (uniqueFile.Exists) {
//Congratulations, your strange deletion patterns fooled my half-interval search into picking an existing file!
//Now you get to pay the performance cost :)
- uniqueFile = UniqueFileSearchLinear(biggestExistingFile);
+ uniqueFile = UniqueFileSearchLinear (biggestExistingFile);
}
return uniqueFile.FullName;
}
-
- private bool RunHeuristics()
+
+ private bool RunHeuristics ()
{
return true;
}
-
- private void ExecuteScript(string scriptName, string savePath)
+
+ private void ExecuteScript (string scriptName, string savePath)
{
//Fast path out
- if(scriptName == null || scriptName.Length <= 0)
+ if (scriptName == null || scriptName.Length <= 0)
return;
- try
- {
- FileInfo fi = new FileInfo(scriptName);
- if(fi.Exists)
- {
- ProcessStartInfo psi = new ProcessStartInfo(scriptName);
+ try {
+ FileInfo fi = new FileInfo (scriptName);
+ if (fi.Exists) {
+ ProcessStartInfo psi = new ProcessStartInfo (scriptName);
psi.Arguments = savePath;
psi.CreateNoWindow = true;
- Process proc = Process.Start(psi);
+ Process proc = Process.Start (psi);
proc.ErrorDataReceived += HandleProcErrorDataReceived;
}
- }
- catch(Exception e)
- {
- m_log.Warn("Exception encountered when trying to run script for oar backup " + savePath, e);
+ } catch (Exception e) {
+ m_log.Warn ("Exception encountered when trying to run script for oar backup " + savePath, e);
}
}
void HandleProcErrorDataReceived (object sender, DataReceivedEventArgs e)
{
- m_log.Warn("ExecuteScript hook " + ((Process)sender).ProcessName + " is yacking on stderr: " + e.Data);
+ m_log.Warn ("ExecuteScript hook " + ((Process)sender).ProcessName + " is yacking on stderr: " + e.Data);
}
-
- private void StopAllTimers()
+
+ private void StopAllTimers ()
{
- foreach(Timer t in timerMap.Keys)
- {
- t.Close();
+ foreach (Timer t in timerMap.Keys) {
+ t.Close ();
}
+ m_closed = true;
}
-
+
/* Find the largest value for which the predicate returns true.
* We use a bisection algorithm (half interval) to make the algorithm scalable.
* The worst-case complexity is about O(log(n)^2) in practice.
@@ -542,65 +501,54 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
* And of course it is fantastic with powers of 2, which are densely packed in values under 100 anyway.
* The Predicate parameter must be a function that accepts a long and returns a bool.
* */
- public long HalfIntervalMaximize(long start, Predicate pred)
+ public long HalfIntervalMaximize (long start, Predicate pred)
{
long prev = start, curr = start, biggest = 0;
- if(start < 0)
- throw new IndexOutOfRangeException("Start value for HalfIntervalMaximize must be non-negative");
+ if (start < 0)
+ throw new IndexOutOfRangeException ("Start value for HalfIntervalMaximize must be non-negative");
- do
- {
- if(pred(curr))
- {
- if(curr > biggest)
- {
+ do {
+ if (pred (curr)) {
+ if (curr > biggest) {
biggest = curr;
}
prev = curr;
- if(curr == 0)
- {
+ if (curr == 0) {
//Special case because 0 * 2 = 0 :)
curr = 1;
- }
- else
- {
+ } else {
//Look deeper
curr *= 2;
}
- }
- else
- {
+ } else {
// We went too far, back off halfway
curr = (curr + prev) / 2;
}
- }
- while(curr - prev > 0);
+ } while (curr - prev > 0);
return biggest;
}
-
- public bool FileExistsTest(long num)
+
+ public bool FileExistsTest (long num)
{
- FileInfo test = new FileInfo(m_dirName + Path.DirectorySeparatorChar + m_regionName + "_" + num + ".oar");
+ FileInfo test = new FileInfo (m_dirName + Path.DirectorySeparatorChar + m_regionName + "_" + num + ".oar");
return test.Exists;
}
-
-
+
+
//Very slow, hence why we try the HalfIntervalMaximize first!
- public FileInfo UniqueFileSearchLinear(long start)
+ public FileInfo UniqueFileSearchLinear (long start)
{
long l = start;
FileInfo retval = null;
- do
- {
- retval = new FileInfo(m_dirName + Path.DirectorySeparatorChar + m_regionName + "_" + (l++) + ".oar");
- }
- while(retval.Exists);
+ do {
+ retval = new FileInfo (m_dirName + Path.DirectorySeparatorChar + m_regionName + "_" + (l++) + ".oar");
+ } while (retval.Exists);
return retval;
}
-}
+ }
}
--
cgit v1.1
From 018645f9f843d1e807a69a63b7dd82c294885eff Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Mon, 28 Feb 2011 11:45:50 -0500
Subject: First pass at busy heuristics. Compile-tested only.
---
.../World/AutoBackup/AutoBackupModule.cs | 69 ++++++++++++++++++++--
1 file changed, 63 insertions(+), 6 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index 54b9b09..98127b7 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -35,7 +35,9 @@ using log4net;
using Nini;
using Nini.Config;
using OpenSim.Framework;
+using OpenSim.Framework.Statistics;
using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
/*
@@ -319,6 +321,12 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
void HandleElapsed (object sender, ElapsedEventArgs e)
{
+ //TODO?: heuristic thresholds are per-region, so we should probably run heuristics once per region
+ //XXX: Running heuristics once per region could add undue performance penalty for something that's supposed to
+ //check whether the region is too busy! Especially on sims with LOTS of regions.
+ //Alternative: make heuristics thresholds global to the module rather than per-region. Less flexible,
+ // but would allow us to be semantically correct while being easier on perf.
+ //Alternative 2: Run heuristics once per unique set of heuristics threshold parameters! Ay yi yi...
if (m_closed)
return;
bool heuristicsRun = false;
@@ -333,15 +341,18 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
//Fast path: heuristics are on; already ran em; and sim is fine; OR, no heuristics for the region.
if ((heuristics && heuristicsRun && heuristicsPassed) || !heuristics) {
doRegionBackup (scene);
- //Heuristics are on; ran but we're too busy -- keep going. Maybe another region will have heuristics off!
+ //Heuristics are on; ran but we're too busy -- keep going. Maybe another region will have heuristics off!
} else if (heuristics && heuristicsRun && !heuristicsPassed) {
+ m_log.Info ("[AUTO BACKUP MODULE]: Heuristics: too busy to backup " + scene.RegionInfo.RegionName + " right now.");
continue;
- //Logical Deduction: heuristics are on but haven't been run
+ //Logical Deduction: heuristics are on but haven't been run
} else {
- heuristicsPassed = RunHeuristics ();
+ heuristicsPassed = RunHeuristics (scene);
heuristicsRun = true;
- if (!heuristicsPassed)
+ if (!heuristicsPassed) {
+ m_log.Info ("[AUTO BACKUP MODULE]: Heuristics: too busy to backup " + scene.RegionInfo.RegionName + " right now.");
continue;
+ }
doRegionBackup (scene);
}
}
@@ -349,6 +360,12 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
void doRegionBackup (IScene scene)
{
+ if (scene.RegionStatus != RegionStatus.Up) {
+ //We won't backup a region that isn't operating normally.
+ m_log.Warn ("[AUTO BACKUP MODULE]: Not backing up region " + scene.RegionInfo.RegionName + " because its status is " + scene.RegionStatus.ToString ());
+ return;
+ }
+
AutoBackupModuleState state = states[scene];
IRegionArchiverModule iram = scene.RequestModuleInterface ();
string savePath = BuildOarPath (scene.RegionInfo.RegionName, state.GetBackupDir (), state.GetNamingType ());
@@ -454,9 +471,49 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
return uniqueFile.FullName;
}
- private bool RunHeuristics ()
+ /*
+ * Return value of true ==> not too busy; false ==> too busy to backup an OAR right now, or error.
+ * */
+ private bool RunHeuristics (IScene region)
+ {
+ try {
+ return RunTimeDilationHeuristic (region) && RunAgentLimitHeuristic (region);
+ } catch (Exception e) {
+ m_log.Warn ("[AUTO BACKUP MODULE]: Exception in RunHeuristics", e);
+ return false;
+ }
+ }
+
+ /*
+ * If the time dilation right at this instant is less than the threshold specified in AutoBackupDilationThreshold (default 0.5),
+ * then we return false and trip the busy heuristic's "too busy" path (i.e. don't save an OAR).
+ * AutoBackupDilationThreshold is a _LOWER BOUND_. Lower Time Dilation is bad, so if you go lower than our threshold, it's "too busy".
+ * Return value of "true" ==> not too busy. Return value of "false" ==> too busy!
+ * */
+ private bool RunTimeDilationHeuristic (IScene region)
{
- return true;
+ string regionName = region.RegionInfo.RegionName;
+ return region.TimeDilation >= m_configSource.Configs["AutoBackupModule"].GetFloat (regionName + ".AutoBackupDilationThreshold", 0.5f);
+ }
+
+ /*
+ * If the root agent count right at this instant is less than the threshold specified in AutoBackupAgentThreshold (default 10),
+ * then we return false and trip the busy heuristic's "too busy" path (i.e., don't save an OAR).
+ * AutoBackupAgentThreshold is an _UPPER BOUND_. Higher Agent Count is bad, so if you go higher than our threshold, it's "too busy".
+ * Return value of "true" ==> not too busy. Return value of "false" ==> too busy!
+ * */
+ private bool RunAgentLimitHeuristic (IScene region)
+ {
+ string regionName = region.RegionInfo.RegionName;
+ try {
+ Scene scene = (Scene)region;
+ //TODO: Why isn't GetRootAgentCount() a method in the IScene interface? Seems generally useful...
+ return scene.GetRootAgentCount () <= m_configSource.Configs["AutoBackupModule"].GetInt (regionName + ".AutoBackupAgentThreshold", 10);
+ } catch (InvalidCastException ice) {
+ m_log.Debug ("[AUTO BACKUP MODULE]: I NEED MAINTENANCE: IScene is not a Scene; can't get root agent count!");
+ return true;
+ //Non-obstructionist safest answer...
+ }
}
private void ExecuteScript (string scriptName, string savePath)
--
cgit v1.1
From b3c42e952f24a5c280a3691e1f75912e21c77323 Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Mon, 28 Feb 2011 11:54:07 -0500
Subject: Fix small bug with remove region; update settings docs.
Our impl of IRegionModuleBase.RemoveRegion didn't remove the scene from the states map.
---
.../OptionalModules/World/AutoBackup/AutoBackupModule.cs | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index 98127b7..7660342 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -71,6 +71,10 @@ using OpenSim.Region.Framework.Scenes;
* "Overwrite": Always save to file named "${AutoBackupDir}/RegionName.oar", even if we have to overwrite an existing file.
* AutoBackupDir: String. Default: "." (the current directory).
* A directory (absolute or relative) where backups should be saved.
+ * AutoBackupDilationThreshold: float. Default: 0.5. Lower bound on time dilation required for BusyCheck heuristics to pass.
+ * If the time dilation is below this value, don't take a backup right now.
+ * AutoBackupAgentThreshold: int. Default: 10. Upper bound on # of agents in region required for BusyCheck heuristics to pass.
+ * If the number of agents is greater than this value, don't take a backup right now.
* */
namespace OpenSim.Region.OptionalModules.World.AutoBackup
@@ -91,7 +95,6 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
//AutoBackupModuleState: Auto-Backup state for one region (scene).
public class AutoBackupModuleState
{
- private readonly IScene m_scene;
private bool m_enabled = false;
private NamingType m_naming = NamingType.TIME;
private Timer m_timer = null;
@@ -99,11 +102,9 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
private string m_script = null;
private string m_dir = ".";
- public AutoBackupModuleState (IScene scene)
+ public AutoBackupModuleState ()
{
- m_scene = scene;
- if (scene == null)
- throw new NullReferenceException ("Required parameter missing for AutoBackupModuleState constructor");
+
}
public void SetEnabled (bool b)
@@ -226,6 +227,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
timers.Remove (timer.Interval);
timer.Close ();
}
+ states.Remove(scene);
}
void IRegionModuleBase.RegionLoaded (Framework.Scenes.Scene scene)
@@ -238,7 +240,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
return;
string sRegionName = scene.RegionInfo.RegionName;
- AutoBackupModuleState st = new AutoBackupModuleState (scene);
+ AutoBackupModuleState st = new AutoBackupModuleState ();
states.Add (scene, st);
//Read the config settings and set variables.
--
cgit v1.1
From 4974a1ce69fb3a1d2937c7de7ba93079a918eb3a Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Mon, 11 Apr 2011 12:34:26 -0400
Subject: AutoBackup: Support region-independent settings too.
---
.../World/AutoBackup/AutoBackupModule.cs | 232 ++++++++++++++++-----
1 file changed, 175 insertions(+), 57 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index 7660342..37a2d97 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -49,6 +49,8 @@ using OpenSim.Region.Framework.Scenes;
* VERY IMPORTANT: You must create the key name as follows: .
* Example: My region is named Foo.
* If I wanted to specify the "AutoBackupInterval" key below, I would name my key "Foo.AutoBackupInterval", under the [AutoBackupModule] section of OpenSim.ini.
+ * Instead of specifying them on a per-region basis, you can also omit the region name to specify the default setting for all regions.
+ * Region-specific settings take precedence.
* AutoBackup: True/False. Default: False. If True, activate auto backup functionality.
* This is the only required option for enabling auto-backup; the other options have sane defaults.
* If False, the auto-backup module becomes a no-op for the region, and all other AutoBackup* settings are ignored.
@@ -121,6 +123,18 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
{
return m_timer;
}
+
+ public double GetIntervalMinutes ()
+ {
+ if(m_timer == null)
+ {
+ return -1.0;
+ }
+ else
+ {
+ return m_timer.Interval / 60000.0;
+ }
+ }
public void SetTimer (Timer t)
{
@@ -167,6 +181,19 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
{
m_naming = n;
}
+
+ public string ToString()
+ {
+ string retval = "";
+
+ retval += "[AUTO BACKUP]: AutoBackup: " + (GetEnabled() ? "ENABLED" : "DISABLED") + "\n";
+ retval += "[AUTO BACKUP]: Interval: " + GetIntervalMinutes() + " minutes" + "\n";
+ retval += "[AUTO BACKUP]: Do Busy Check: " + (GetBusyCheck() ? "Yes" : "No") + "\n";
+ retval += "[AUTO BACKUP]: Naming Type: " + GetNamingType().ToString() + "\n";
+ retval += "[AUTO BACKUP]: Backup Dir: " + GetBackupDir() + "\n";
+ retval += "[AUTO BACKUP]: Script: " + GetScript() + "\n";
+ return retval;
+ }
}
//Save memory by setting low initial capacities. Minimizes impact in common cases of all regions using same interval, and instances hosting 1 ~ 4 regions.
@@ -180,6 +207,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
private bool m_closed = false;
//True means IRegionModuleBase.Close() was called on us, and we should stop operation ASAP.
//Used to prevent elapsing timers after Close() is called from trying to start an autobackup while the sim is shutting down.
+ readonly AutoBackupModuleState defaultState = new AutoBackupModuleState();
+
public AutoBackupModule ()
{
@@ -194,9 +223,20 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
if (moduleConfig != null) {
m_Enabled = moduleConfig.GetBoolean ("AutoBackupModule", false);
if (m_Enabled) {
- m_log.Info ("[AUTO BACKUP MODULE]: AutoBackupModule enabled");
+ m_log.Info ("[AUTO BACKUP]: AutoBackupModule enabled");
}
}
+
+ Timer defTimer = new Timer(720 * 60000);
+ defaultState.SetTimer(defTimer);
+ timers.Add (720*60000, defTimer);
+ defTimer.Elapsed += HandleElapsed;
+ defTimer.AutoReset = true;
+ defTimer.Start ();
+
+ AutoBackupModuleState abms = ParseConfig(null, false);
+ m_log.Debug("[AUTO BACKUP]: Config for default");
+ m_log.Debug(abms.ToString());
}
void IRegionModuleBase.Close ()
@@ -238,87 +278,162 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
//This really ought not to happen, but just in case, let's pretend it didn't...
if (scene == null)
return;
-
- string sRegionName = scene.RegionInfo.RegionName;
- AutoBackupModuleState st = new AutoBackupModuleState ();
- states.Add (scene, st);
-
- //Read the config settings and set variables.
+
+ AutoBackupModuleState abms = ParseConfig(scene, true);
+ m_log.Debug("[AUTO BACKUP]: Config for " + scene.RegionInfo.RegionName);
+ m_log.Debug(abms.ToString());
+ }
+
+ AutoBackupModuleState ParseConfig (IScene scene, bool parseDefault)
+ {
+ string sRegionName;
+ string sRegionLabel;
+ string prepend;
+ AutoBackupModuleState state;
+
+ if(parseDefault)
+ {
+ sRegionName = null;
+ sRegionLabel = "DEFAULT";
+ prepend = "";
+ state = defaultState;
+ }
+ else
+ {
+ sRegionName = scene.RegionInfo.RegionName;
+ sRegionLabel = sRegionName;
+ prepend = sRegionName + ".";
+ state = null;
+ }
+
+ //Read the config settings and set variables.
IConfig config = m_configSource.Configs["AutoBackupModule"];
if (config == null) {
- //No config settings for any regions, let's just give up.
- st.SetEnabled (false);
- m_log.Info ("[AUTO BACKUP MODULE]: Region " + sRegionName + " is NOT AutoBackup enabled.");
- return;
+ state = defaultState; //defaultState would be disabled too if the section doesn't exist.
+ m_log.Info ("[AUTO BACKUP]: Region " + sRegionLabel + " is NOT AutoBackup enabled.");
+ return state;
+ }
+
+ bool tmpEnabled = config.GetBoolean (prepend + "AutoBackup", defaultState.GetEnabled());
+ if(state == null && tmpEnabled != defaultState.GetEnabled()) //Varies from default state
+ {
+ state = new AutoBackupModuleState();
+ state.SetEnabled (tmpEnabled);
}
- st.SetEnabled (config.GetBoolean (sRegionName + ".AutoBackup", false));
+
//If you don't want AutoBackup, we stop.
- if (!st.GetEnabled ()) {
- m_log.Info ("[AUTO BACKUP MODULE]: Region " + sRegionName + " is NOT AutoBackup enabled.");
- return;
+ if ((state == null && !defaultState.GetEnabled()) || !state.GetEnabled ()) {
+ m_log.Info ("[AUTO BACKUP]: Region " + sRegionLabel + " is NOT AutoBackup enabled.");
+ return state;
} else {
- m_log.Info ("[AUTO BACKUP MODULE]: Region " + sRegionName + " is AutoBackup ENABLED.");
+ m_log.Info ("[AUTO BACKUP]: Region " + sRegionLabel + " is AutoBackup ENABLED.");
}
//Borrow an existing timer if one exists for the same interval; otherwise, make a new one.
- double interval = config.GetDouble (sRegionName + ".AutoBackupInterval", 720) * 60000;
+ double interval = config.GetDouble (prepend + "AutoBackupInterval", defaultState.GetIntervalMinutes()) * 60000.0;
+ if(state == null && interval != defaultState.GetIntervalMinutes() * 60000.0)
+ {
+ state = new AutoBackupModuleState();
+ }
+
if (timers.ContainsKey (interval)) {
- st.SetTimer (timers[interval]);
- m_log.Debug ("[AUTO BACKUP MODULE]: Reusing timer for " + interval + " msec for region " + sRegionName);
+ if(state != null)
+ state.SetTimer (timers[interval]);
+ m_log.Debug ("[AUTO BACKUP]: Reusing timer for " + interval + " msec for region " + sRegionLabel);
} else {
//0 or negative interval == do nothing.
- if (interval <= 0.0) {
- st.SetEnabled (false);
- return;
+ if (interval <= 0.0 && state != null) {
+ state.SetEnabled (false);
+ return state;
}
Timer tim = new Timer (interval);
- st.SetTimer (tim);
+ state.SetTimer (tim);
//Milliseconds -> minutes
timers.Add (interval, tim);
tim.Elapsed += HandleElapsed;
tim.AutoReset = true;
tim.Start ();
- //m_log.Debug("[AUTO BACKUP MODULE]: New timer for " + interval + " msec for region " + sRegionName);
+ //m_log.Debug("[AUTO BACKUP]: New timer for " + interval + " msec for region " + sRegionName);
}
//Add the current region to the list of regions tied to this timer.
- if (timerMap.ContainsKey (st.GetTimer ())) {
- timerMap[st.GetTimer ()].Add (scene);
+ if (timerMap.ContainsKey (state.GetTimer ())) {
+ timerMap[state.GetTimer ()].Add (scene);
} else {
List scns = new List (1);
scns.Add (scene);
- timerMap.Add (st.GetTimer (), scns);
+ timerMap.Add (state.GetTimer (), scns);
+ }
+
+ bool tmpBusyCheck = config.GetBoolean (prepend + "AutoBackupBusyCheck", defaultState.GetBusyCheck());
+ if(state == null && tmpBusyCheck != defaultState.GetBusyCheck())
+ {
+ state = new AutoBackupModuleState();
}
- st.SetBusyCheck (config.GetBoolean (sRegionName + ".AutoBackupBusyCheck", true));
+ if(state != null)
+ {
+ state.SetBusyCheck (tmpBusyCheck);
+ }
//Set file naming algorithm
- string namingtype = config.GetString (sRegionName + ".AutoBackupNaming", "Time");
- if (namingtype.Equals ("Time", StringComparison.CurrentCultureIgnoreCase)) {
- st.SetNamingType (NamingType.TIME);
- } else if (namingtype.Equals ("Sequential", StringComparison.CurrentCultureIgnoreCase)) {
- st.SetNamingType (NamingType.SEQUENTIAL);
- } else if (namingtype.Equals ("Overwrite", StringComparison.CurrentCultureIgnoreCase)) {
- st.SetNamingType (NamingType.OVERWRITE);
+ string stmpNamingType = config.GetString (prepend + "AutoBackupNaming", defaultState.GetNamingType().ToString());
+ NamingType tmpNamingType;
+ if (stmpNamingType.Equals ("Time", StringComparison.CurrentCultureIgnoreCase)) {
+ tmpNamingType = NamingType.TIME;
+ } else if (stmpNamingType.Equals ("Sequential", StringComparison.CurrentCultureIgnoreCase)) {
+ tmpNamingType = NamingType.SEQUENTIAL;
+ } else if (stmpNamingType.Equals ("Overwrite", StringComparison.CurrentCultureIgnoreCase)) {
+ tmpNamingType = NamingType.OVERWRITE;
} else {
- m_log.Warn ("Unknown naming type specified for region " + scene.RegionInfo.RegionName + ": " + namingtype);
- st.SetNamingType (NamingType.TIME);
+ m_log.Warn ("Unknown naming type specified for region " + sRegionLabel + ": " + stmpNamingType);
+ tmpNamingType = NamingType.TIME;
}
- st.SetScript (config.GetString (sRegionName + ".AutoBackupScript", null));
- st.SetBackupDir (config.GetString (sRegionName + ".AutoBackupDir", "."));
+ if(state == null && tmpNamingType != defaultState.GetNamingType())
+ {
+ state = new AutoBackupModuleState();
+ }
- //Let's give the user *one* convenience and auto-mkdir
- if (st.GetBackupDir () != ".") {
- try {
- DirectoryInfo dirinfo = new DirectoryInfo (st.GetBackupDir ());
- if (!dirinfo.Exists) {
- dirinfo.Create ();
- }
- } catch (Exception e) {
- m_log.Warn ("BAD NEWS. You won't be able to save backups to directory " + st.GetBackupDir () + " because it doesn't exist or there's a permissions issue with it. Here's the exception.", e);
- }
+ if(state != null)
+ {
+ state.SetNamingType(tmpNamingType);
}
+
+ string tmpScript = config.GetString (prepend + "AutoBackupScript", defaultState.GetScript());
+ if(state == null && tmpScript != defaultState.GetScript())
+ {
+ state = new AutoBackupModuleState();
+ }
+
+ if(state != null)
+ {
+ state.SetScript (tmpScript);
+ }
+
+ string tmpBackupDir = config.GetString (prepend + "AutoBackupDir", ".");
+ if(state == null && tmpBackupDir != defaultState.GetBackupDir())
+ {
+ state = new AutoBackupModuleState();
+ }
+
+ if(state != null)
+ {
+ state.SetBackupDir (tmpBackupDir);
+ //Let's give the user *one* convenience and auto-mkdir
+ if (state.GetBackupDir () != ".") {
+ try {
+ DirectoryInfo dirinfo = new DirectoryInfo (state.GetBackupDir ());
+ if (!dirinfo.Exists) {
+ dirinfo.Create ();
+ }
+ } catch (Exception e) {
+ m_log.Warn ("BAD NEWS. You won't be able to save backups to directory " + state.GetBackupDir () + " because it doesn't exist or there's a permissions issue with it. Here's the exception.", e);
+ }
+ }
+ }
+
+ return state;
}
void HandleElapsed (object sender, ElapsedEventArgs e)
@@ -336,7 +451,10 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
if (!timerMap.ContainsKey ((Timer)sender)) {
m_log.Debug ("Code-up error: timerMap doesn't contain timer " + sender.ToString ());
}
- foreach (IScene scene in timerMap[(Timer)sender]) {
+
+ List tmap = timerMap[(Timer)sender];
+ if(tmap != null && tmap.Count > 0)
+ foreach (IScene scene in tmap) {
AutoBackupModuleState state = states[scene];
bool heuristics = state.GetBusyCheck ();
@@ -345,14 +463,14 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
doRegionBackup (scene);
//Heuristics are on; ran but we're too busy -- keep going. Maybe another region will have heuristics off!
} else if (heuristics && heuristicsRun && !heuristicsPassed) {
- m_log.Info ("[AUTO BACKUP MODULE]: Heuristics: too busy to backup " + scene.RegionInfo.RegionName + " right now.");
+ m_log.Info ("[AUTO BACKUP]: Heuristics: too busy to backup " + scene.RegionInfo.RegionName + " right now.");
continue;
//Logical Deduction: heuristics are on but haven't been run
} else {
heuristicsPassed = RunHeuristics (scene);
heuristicsRun = true;
if (!heuristicsPassed) {
- m_log.Info ("[AUTO BACKUP MODULE]: Heuristics: too busy to backup " + scene.RegionInfo.RegionName + " right now.");
+ m_log.Info ("[AUTO BACKUP]: Heuristics: too busy to backup " + scene.RegionInfo.RegionName + " right now.");
continue;
}
doRegionBackup (scene);
@@ -364,16 +482,16 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
{
if (scene.RegionStatus != RegionStatus.Up) {
//We won't backup a region that isn't operating normally.
- m_log.Warn ("[AUTO BACKUP MODULE]: Not backing up region " + scene.RegionInfo.RegionName + " because its status is " + scene.RegionStatus.ToString ());
+ m_log.Warn ("[AUTO BACKUP]: Not backing up region " + scene.RegionInfo.RegionName + " because its status is " + scene.RegionStatus.ToString ());
return;
}
AutoBackupModuleState state = states[scene];
IRegionArchiverModule iram = scene.RequestModuleInterface ();
string savePath = BuildOarPath (scene.RegionInfo.RegionName, state.GetBackupDir (), state.GetNamingType ());
- //m_log.Debug("[AUTO BACKUP MODULE]: savePath = " + savePath);
+ //m_log.Debug("[AUTO BACKUP]: savePath = " + savePath);
if (savePath == null) {
- m_log.Warn ("[AUTO BACKUP MODULE]: savePath is null in HandleElapsed");
+ m_log.Warn ("[AUTO BACKUP]: savePath is null in HandleElapsed");
return;
}
iram.ArchiveRegion (savePath, null);
@@ -481,7 +599,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
try {
return RunTimeDilationHeuristic (region) && RunAgentLimitHeuristic (region);
} catch (Exception e) {
- m_log.Warn ("[AUTO BACKUP MODULE]: Exception in RunHeuristics", e);
+ m_log.Warn ("[AUTO BACKUP]: Exception in RunHeuristics", e);
return false;
}
}
@@ -512,7 +630,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
//TODO: Why isn't GetRootAgentCount() a method in the IScene interface? Seems generally useful...
return scene.GetRootAgentCount () <= m_configSource.Configs["AutoBackupModule"].GetInt (regionName + ".AutoBackupAgentThreshold", 10);
} catch (InvalidCastException ice) {
- m_log.Debug ("[AUTO BACKUP MODULE]: I NEED MAINTENANCE: IScene is not a Scene; can't get root agent count!");
+ m_log.Debug ("[AUTO BACKUP]: I NEED MAINTENANCE: IScene is not a Scene; can't get root agent count!");
return true;
//Non-obstructionist safest answer...
}
--
cgit v1.1
From 4ad05fb01d038b31738438d02850972e79739f30 Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Mon, 11 Apr 2011 13:12:26 -0400
Subject: Fix some NREs on certain paths.
---
.../World/AutoBackup/AutoBackupModule.cs | 28 +++++++++++++++++-----
1 file changed, 22 insertions(+), 6 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index 37a2d97..3d156ca 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -324,6 +324,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
//If you don't want AutoBackup, we stop.
if ((state == null && !defaultState.GetEnabled()) || !state.GetEnabled ()) {
m_log.Info ("[AUTO BACKUP]: Region " + sRegionLabel + " is NOT AutoBackup enabled.");
+ state = defaultState;
return state;
} else {
m_log.Info ("[AUTO BACKUP]: Region " + sRegionLabel + " is AutoBackup ENABLED.");
@@ -346,6 +347,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
state.SetEnabled (false);
return state;
}
+ if(state == null)
+ state = new AutoBackupModuleState();
Timer tim = new Timer (interval);
state.SetTimer (tim);
//Milliseconds -> minutes
@@ -357,12 +360,25 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
}
//Add the current region to the list of regions tied to this timer.
- if (timerMap.ContainsKey (state.GetTimer ())) {
- timerMap[state.GetTimer ()].Add (scene);
- } else {
- List scns = new List (1);
- scns.Add (scene);
- timerMap.Add (state.GetTimer (), scns);
+ if(state != null)
+ {
+ if (timerMap.ContainsKey (state.GetTimer ())) {
+ timerMap[state.GetTimer ()].Add (scene);
+ } else {
+ List scns = new List (1);
+ scns.Add (scene);
+ timerMap.Add (state.GetTimer (), scns);
+ }
+ }
+ else
+ {
+ if(timerMap.ContainsKey(defaultState.GetTimer())) {
+ timerMap[defaultState.GetTimer()].Add(scene);
+ } else {
+ List scns = new List (1);
+ scns.Add(scene);
+ timerMap.Add(defaultState.GetTimer(), scns);
+ }
}
bool tmpBusyCheck = config.GetBoolean (prepend + "AutoBackupBusyCheck", defaultState.GetBusyCheck());
--
cgit v1.1
From 95a6ee0a3793b72deb29fe6e853d59afc2ca631e Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Mon, 11 Apr 2011 13:20:46 -0400
Subject: Fix NREs harder.
---
.../World/AutoBackup/AutoBackupModule.cs | 39 +++++++++++++---------
1 file changed, 23 insertions(+), 16 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index 3d156ca..e52e9cb 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -225,6 +225,10 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
if (m_Enabled) {
m_log.Info ("[AUTO BACKUP]: AutoBackupModule enabled");
}
+ else {
+ m_log.Info ("[AUTO BACKUP]: AutoBackupModule disabled");
+ return;
+ }
}
Timer defTimer = new Timer(720 * 60000);
@@ -360,24 +364,27 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
}
//Add the current region to the list of regions tied to this timer.
- if(state != null)
+ if(scene != null)
{
- if (timerMap.ContainsKey (state.GetTimer ())) {
- timerMap[state.GetTimer ()].Add (scene);
- } else {
- List scns = new List (1);
- scns.Add (scene);
- timerMap.Add (state.GetTimer (), scns);
+ if(state != null)
+ {
+ if (timerMap.ContainsKey (state.GetTimer ())) {
+ timerMap[state.GetTimer ()].Add (scene);
+ } else {
+ List scns = new List (1);
+ scns.Add (scene);
+ timerMap.Add (state.GetTimer (), scns);
+ }
}
- }
- else
- {
- if(timerMap.ContainsKey(defaultState.GetTimer())) {
- timerMap[defaultState.GetTimer()].Add(scene);
- } else {
- List scns = new List (1);
- scns.Add(scene);
- timerMap.Add(defaultState.GetTimer(), scns);
+ else
+ {
+ if(timerMap.ContainsKey(defaultState.GetTimer())) {
+ timerMap[defaultState.GetTimer()].Add(scene);
+ } else {
+ List scns = new List (1);
+ scns.Add(scene);
+ timerMap.Add(defaultState.GetTimer(), scns);
+ }
}
}
--
cgit v1.1
From 049bce4d003f6854868306c7d98f7b5a501cb047 Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Tue, 12 Apr 2011 01:14:21 -0400
Subject: Fixup the global defaults config parsing code.
---
.../World/AutoBackup/AutoBackupModule.cs | 40 ++++++++++++++--------
1 file changed, 25 insertions(+), 15 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index e52e9cb..364697b 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -238,7 +238,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
defTimer.AutoReset = true;
defTimer.Start ();
- AutoBackupModuleState abms = ParseConfig(null, false);
+ AutoBackupModuleState abms = ParseConfig(null, true);
m_log.Debug("[AUTO BACKUP]: Config for default");
m_log.Debug(abms.ToString());
}
@@ -262,16 +262,23 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
if (!m_Enabled)
return;
- AutoBackupModuleState abms = states[scene];
- Timer timer = abms.GetTimer ();
- List list = timerMap[timer];
- list.Remove (scene);
- if (list.Count == 0) {
- timerMap.Remove (timer);
- timers.Remove (timer.Interval);
- timer.Close ();
+ if(states.ContainsKey(scene))
+ {
+ AutoBackupModuleState abms = states[scene];
+
+ //Remove this scene out of the timer map list
+ Timer timer = abms.GetTimer ();
+ List list = timerMap[timer];
+ list.Remove (scene);
+
+ //Shut down the timer if this was the last scene for the timer
+ if (list.Count == 0) {
+ timerMap.Remove (timer);
+ timers.Remove (timer.Interval);
+ timer.Close ();
+ }
+ states.Remove(scene);
}
- states.Remove(scene);
}
void IRegionModuleBase.RegionLoaded (Framework.Scenes.Scene scene)
@@ -283,9 +290,9 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
if (scene == null)
return;
- AutoBackupModuleState abms = ParseConfig(scene, true);
+ AutoBackupModuleState abms = ParseConfig(scene, false);
m_log.Debug("[AUTO BACKUP]: Config for " + scene.RegionInfo.RegionName);
- m_log.Debug(abms.ToString());
+ m_log.Debug((abms == null ? "DEFAULT" : abms.ToString()));
}
AutoBackupModuleState ParseConfig (IScene scene, bool parseDefault)
@@ -322,13 +329,16 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
if(state == null && tmpEnabled != defaultState.GetEnabled()) //Varies from default state
{
state = new AutoBackupModuleState();
- state.SetEnabled (tmpEnabled);
+ }
+
+ if(state != null)
+ {
+ state.SetEnabled (tmpEnabled);
}
//If you don't want AutoBackup, we stop.
- if ((state == null && !defaultState.GetEnabled()) || !state.GetEnabled ()) {
+ if ((state == null && !defaultState.GetEnabled()) || (state != null && !state.GetEnabled ())) {
m_log.Info ("[AUTO BACKUP]: Region " + sRegionLabel + " is NOT AutoBackup enabled.");
- state = defaultState;
return state;
} else {
m_log.Info ("[AUTO BACKUP]: Region " + sRegionLabel + " is AutoBackup ENABLED.");
--
cgit v1.1
From 082fad6dd28513e38120c9d272aeed385de8208f Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Sat, 23 Apr 2011 18:29:13 -0400
Subject: Fix most issues raised by justincc:
http://opensimulator.org/mantis/view.php?id=5440
---
.../World/AutoBackup/AutoBackupModule.cs | 1509 ++++++++++----------
.../World/AutoBackup/AutoBackupModuleState.cs | 109 ++
2 files changed, 854 insertions(+), 764 deletions(-)
create mode 100644 OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index 364697b..a4dbea4 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -1,764 +1,745 @@
-/*
- * 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.IO;
-using System.Timers;
-using System.Diagnostics;
-using System.Reflection;
-using System.Collections.Generic;
-using log4net;
-using Nini;
-using Nini.Config;
-using OpenSim.Framework;
-using OpenSim.Framework.Statistics;
-using OpenSim.Region.Framework.Interfaces;
-using OpenSim.Region.Framework.Scenes;
-
-
-/*
- * Config Settings Documentation.
- * At the TOP LEVEL, e.g. in OpenSim.ini, we have the following options:
- * In the [Modules] section:
- * AutoBackupModule: True/False. Default: False. If True, use the auto backup module. Otherwise it will be disabled regardless of what settings are in Regions.ini!
- * EACH REGION, in OpenSim.ini, can have the following settings under the [AutoBackupModule] section.
- * VERY IMPORTANT: You must create the key name as follows: .
- * Example: My region is named Foo.
- * If I wanted to specify the "AutoBackupInterval" key below, I would name my key "Foo.AutoBackupInterval", under the [AutoBackupModule] section of OpenSim.ini.
- * Instead of specifying them on a per-region basis, you can also omit the region name to specify the default setting for all regions.
- * Region-specific settings take precedence.
- * AutoBackup: True/False. Default: False. If True, activate auto backup functionality.
- * This is the only required option for enabling auto-backup; the other options have sane defaults.
- * If False, the auto-backup module becomes a no-op for the region, and all other AutoBackup* settings are ignored.
- * AutoBackupInterval: Double, non-negative value. Default: 720 (12 hours).
- * The number of minutes between each backup attempt.
- * If a negative or zero value is given, it is equivalent to setting AutoBackup = False.
- * AutoBackupBusyCheck: True/False. Default: True.
- * If True, we will only take an auto-backup if a set of conditions are met.
- * These conditions are heuristics to try and avoid taking a backup when the sim is busy.
- * AutoBackupScript: String. Default: not specified (disabled).
- * File path to an executable script or binary to run when an automatic backup is taken.
- * The file should really be (Windows) an .exe or .bat, or (Linux/Mac) a shell script or binary.
- * Trying to "run" directories, or things with weird file associations on Win32, might cause unexpected results!
- * argv[1] of the executed file/script will be the file name of the generated OAR.
- * If the process can't be spawned for some reason (file not found, no execute permission, etc), write a warning to the console.
- * AutoBackupNaming: string. Default: Time.
- * One of three strings (case insensitive):
- * "Time": Current timestamp is appended to file name. An existing file will never be overwritten.
- * "Sequential": A number is appended to the file name. So if RegionName_x.oar exists, we'll save to RegionName_{x+1}.oar next. An existing file will never be overwritten.
- * "Overwrite": Always save to file named "${AutoBackupDir}/RegionName.oar", even if we have to overwrite an existing file.
- * AutoBackupDir: String. Default: "." (the current directory).
- * A directory (absolute or relative) where backups should be saved.
- * AutoBackupDilationThreshold: float. Default: 0.5. Lower bound on time dilation required for BusyCheck heuristics to pass.
- * If the time dilation is below this value, don't take a backup right now.
- * AutoBackupAgentThreshold: int. Default: 10. Upper bound on # of agents in region required for BusyCheck heuristics to pass.
- * If the number of agents is greater than this value, don't take a backup right now.
- * */
-
-namespace OpenSim.Region.OptionalModules.World.AutoBackup
-{
-
- public enum NamingType
- {
- TIME,
- SEQUENTIAL,
- OVERWRITE
- }
-
- public class AutoBackupModule : ISharedRegionModule, IRegionModuleBase
- {
-
- private static readonly ILog m_log = LogManager.GetLogger (MethodBase.GetCurrentMethod ().DeclaringType);
-
- //AutoBackupModuleState: Auto-Backup state for one region (scene).
- public class AutoBackupModuleState
- {
- private bool m_enabled = false;
- private NamingType m_naming = NamingType.TIME;
- private Timer m_timer = null;
- private bool m_busycheck = true;
- private string m_script = null;
- private string m_dir = ".";
-
- public AutoBackupModuleState ()
- {
-
- }
-
- public void SetEnabled (bool b)
- {
- m_enabled = b;
- }
-
- public bool GetEnabled ()
- {
- return m_enabled;
- }
-
- public Timer GetTimer ()
- {
- return m_timer;
- }
-
- public double GetIntervalMinutes ()
- {
- if(m_timer == null)
- {
- return -1.0;
- }
- else
- {
- return m_timer.Interval / 60000.0;
- }
- }
-
- public void SetTimer (Timer t)
- {
- m_timer = t;
- }
-
- public bool GetBusyCheck ()
- {
- return m_busycheck;
- }
-
- public void SetBusyCheck (bool b)
- {
- m_busycheck = b;
- }
-
-
- public string GetScript ()
- {
- return m_script;
- }
-
- public void SetScript (string s)
- {
- m_script = s;
- }
-
- public string GetBackupDir ()
- {
- return m_dir;
- }
-
- public void SetBackupDir (string s)
- {
- m_dir = s;
- }
-
- public NamingType GetNamingType ()
- {
- return m_naming;
- }
-
- public void SetNamingType (NamingType n)
- {
- m_naming = n;
- }
-
- public string ToString()
- {
- string retval = "";
-
- retval += "[AUTO BACKUP]: AutoBackup: " + (GetEnabled() ? "ENABLED" : "DISABLED") + "\n";
- retval += "[AUTO BACKUP]: Interval: " + GetIntervalMinutes() + " minutes" + "\n";
- retval += "[AUTO BACKUP]: Do Busy Check: " + (GetBusyCheck() ? "Yes" : "No") + "\n";
- retval += "[AUTO BACKUP]: Naming Type: " + GetNamingType().ToString() + "\n";
- retval += "[AUTO BACKUP]: Backup Dir: " + GetBackupDir() + "\n";
- retval += "[AUTO BACKUP]: Script: " + GetScript() + "\n";
- return retval;
- }
- }
-
- //Save memory by setting low initial capacities. Minimizes impact in common cases of all regions using same interval, and instances hosting 1 ~ 4 regions.
- //Also helps if you don't want AutoBackup at all
- readonly Dictionary states = new Dictionary (4);
- readonly Dictionary timers = new Dictionary (1);
- readonly Dictionary> timerMap = new Dictionary> (1);
- private IConfigSource m_configSource = null;
- private bool m_Enabled = false;
- //Whether the shared module should be enabled at all. NOT the same as m_Enabled in AutoBackupModuleState!
- private bool m_closed = false;
- //True means IRegionModuleBase.Close() was called on us, and we should stop operation ASAP.
- //Used to prevent elapsing timers after Close() is called from trying to start an autobackup while the sim is shutting down.
- readonly AutoBackupModuleState defaultState = new AutoBackupModuleState();
-
- public AutoBackupModule ()
- {
-
- }
-
- #region IRegionModuleBase implementation
- void IRegionModuleBase.Initialise (Nini.Config.IConfigSource source)
- {
- //Determine if we have been enabled at all in OpenSim.ini -- this is part and parcel of being an optional module
- m_configSource = source;
- IConfig moduleConfig = source.Configs["Modules"];
- if (moduleConfig != null) {
- m_Enabled = moduleConfig.GetBoolean ("AutoBackupModule", false);
- if (m_Enabled) {
- m_log.Info ("[AUTO BACKUP]: AutoBackupModule enabled");
- }
- else {
- m_log.Info ("[AUTO BACKUP]: AutoBackupModule disabled");
- return;
- }
- }
-
- Timer defTimer = new Timer(720 * 60000);
- defaultState.SetTimer(defTimer);
- timers.Add (720*60000, defTimer);
- defTimer.Elapsed += HandleElapsed;
- defTimer.AutoReset = true;
- defTimer.Start ();
-
- AutoBackupModuleState abms = ParseConfig(null, true);
- m_log.Debug("[AUTO BACKUP]: Config for default");
- m_log.Debug(abms.ToString());
- }
-
- void IRegionModuleBase.Close ()
- {
- if (!m_Enabled)
- return;
-
- //We don't want any timers firing while the sim's coming down; strange things may happen.
- StopAllTimers ();
- }
-
- void IRegionModuleBase.AddRegion (Framework.Scenes.Scene scene)
- {
- //NO-OP. Wait for the region to be loaded.
- }
-
- void IRegionModuleBase.RemoveRegion (Framework.Scenes.Scene scene)
- {
- if (!m_Enabled)
- return;
-
- if(states.ContainsKey(scene))
- {
- AutoBackupModuleState abms = states[scene];
-
- //Remove this scene out of the timer map list
- Timer timer = abms.GetTimer ();
- List list = timerMap[timer];
- list.Remove (scene);
-
- //Shut down the timer if this was the last scene for the timer
- if (list.Count == 0) {
- timerMap.Remove (timer);
- timers.Remove (timer.Interval);
- timer.Close ();
- }
- states.Remove(scene);
- }
- }
-
- void IRegionModuleBase.RegionLoaded (Framework.Scenes.Scene scene)
- {
- if (!m_Enabled)
- return;
-
- //This really ought not to happen, but just in case, let's pretend it didn't...
- if (scene == null)
- return;
-
- AutoBackupModuleState abms = ParseConfig(scene, false);
- m_log.Debug("[AUTO BACKUP]: Config for " + scene.RegionInfo.RegionName);
- m_log.Debug((abms == null ? "DEFAULT" : abms.ToString()));
- }
-
- AutoBackupModuleState ParseConfig (IScene scene, bool parseDefault)
- {
- string sRegionName;
- string sRegionLabel;
- string prepend;
- AutoBackupModuleState state;
-
- if(parseDefault)
- {
- sRegionName = null;
- sRegionLabel = "DEFAULT";
- prepend = "";
- state = defaultState;
- }
- else
- {
- sRegionName = scene.RegionInfo.RegionName;
- sRegionLabel = sRegionName;
- prepend = sRegionName + ".";
- state = null;
- }
-
- //Read the config settings and set variables.
- IConfig config = m_configSource.Configs["AutoBackupModule"];
- if (config == null) {
- state = defaultState; //defaultState would be disabled too if the section doesn't exist.
- m_log.Info ("[AUTO BACKUP]: Region " + sRegionLabel + " is NOT AutoBackup enabled.");
- return state;
- }
-
- bool tmpEnabled = config.GetBoolean (prepend + "AutoBackup", defaultState.GetEnabled());
- if(state == null && tmpEnabled != defaultState.GetEnabled()) //Varies from default state
- {
- state = new AutoBackupModuleState();
- }
-
- if(state != null)
- {
- state.SetEnabled (tmpEnabled);
- }
-
- //If you don't want AutoBackup, we stop.
- if ((state == null && !defaultState.GetEnabled()) || (state != null && !state.GetEnabled ())) {
- m_log.Info ("[AUTO BACKUP]: Region " + sRegionLabel + " is NOT AutoBackup enabled.");
- return state;
- } else {
- m_log.Info ("[AUTO BACKUP]: Region " + sRegionLabel + " is AutoBackup ENABLED.");
- }
-
- //Borrow an existing timer if one exists for the same interval; otherwise, make a new one.
- double interval = config.GetDouble (prepend + "AutoBackupInterval", defaultState.GetIntervalMinutes()) * 60000.0;
- if(state == null && interval != defaultState.GetIntervalMinutes() * 60000.0)
- {
- state = new AutoBackupModuleState();
- }
-
- if (timers.ContainsKey (interval)) {
- if(state != null)
- state.SetTimer (timers[interval]);
- m_log.Debug ("[AUTO BACKUP]: Reusing timer for " + interval + " msec for region " + sRegionLabel);
- } else {
- //0 or negative interval == do nothing.
- if (interval <= 0.0 && state != null) {
- state.SetEnabled (false);
- return state;
- }
- if(state == null)
- state = new AutoBackupModuleState();
- Timer tim = new Timer (interval);
- state.SetTimer (tim);
- //Milliseconds -> minutes
- timers.Add (interval, tim);
- tim.Elapsed += HandleElapsed;
- tim.AutoReset = true;
- tim.Start ();
- //m_log.Debug("[AUTO BACKUP]: New timer for " + interval + " msec for region " + sRegionName);
- }
-
- //Add the current region to the list of regions tied to this timer.
- if(scene != null)
- {
- if(state != null)
- {
- if (timerMap.ContainsKey (state.GetTimer ())) {
- timerMap[state.GetTimer ()].Add (scene);
- } else {
- List scns = new List (1);
- scns.Add (scene);
- timerMap.Add (state.GetTimer (), scns);
- }
- }
- else
- {
- if(timerMap.ContainsKey(defaultState.GetTimer())) {
- timerMap[defaultState.GetTimer()].Add(scene);
- } else {
- List scns = new List (1);
- scns.Add(scene);
- timerMap.Add(defaultState.GetTimer(), scns);
- }
- }
- }
-
- bool tmpBusyCheck = config.GetBoolean (prepend + "AutoBackupBusyCheck", defaultState.GetBusyCheck());
- if(state == null && tmpBusyCheck != defaultState.GetBusyCheck())
- {
- state = new AutoBackupModuleState();
- }
-
- if(state != null)
- {
- state.SetBusyCheck (tmpBusyCheck);
- }
-
- //Set file naming algorithm
- string stmpNamingType = config.GetString (prepend + "AutoBackupNaming", defaultState.GetNamingType().ToString());
- NamingType tmpNamingType;
- if (stmpNamingType.Equals ("Time", StringComparison.CurrentCultureIgnoreCase)) {
- tmpNamingType = NamingType.TIME;
- } else if (stmpNamingType.Equals ("Sequential", StringComparison.CurrentCultureIgnoreCase)) {
- tmpNamingType = NamingType.SEQUENTIAL;
- } else if (stmpNamingType.Equals ("Overwrite", StringComparison.CurrentCultureIgnoreCase)) {
- tmpNamingType = NamingType.OVERWRITE;
- } else {
- m_log.Warn ("Unknown naming type specified for region " + sRegionLabel + ": " + stmpNamingType);
- tmpNamingType = NamingType.TIME;
- }
-
- if(state == null && tmpNamingType != defaultState.GetNamingType())
- {
- state = new AutoBackupModuleState();
- }
-
- if(state != null)
- {
- state.SetNamingType(tmpNamingType);
- }
-
- string tmpScript = config.GetString (prepend + "AutoBackupScript", defaultState.GetScript());
- if(state == null && tmpScript != defaultState.GetScript())
- {
- state = new AutoBackupModuleState();
- }
-
- if(state != null)
- {
- state.SetScript (tmpScript);
- }
-
- string tmpBackupDir = config.GetString (prepend + "AutoBackupDir", ".");
- if(state == null && tmpBackupDir != defaultState.GetBackupDir())
- {
- state = new AutoBackupModuleState();
- }
-
- if(state != null)
- {
- state.SetBackupDir (tmpBackupDir);
- //Let's give the user *one* convenience and auto-mkdir
- if (state.GetBackupDir () != ".") {
- try {
- DirectoryInfo dirinfo = new DirectoryInfo (state.GetBackupDir ());
- if (!dirinfo.Exists) {
- dirinfo.Create ();
- }
- } catch (Exception e) {
- m_log.Warn ("BAD NEWS. You won't be able to save backups to directory " + state.GetBackupDir () + " because it doesn't exist or there's a permissions issue with it. Here's the exception.", e);
- }
- }
- }
-
- return state;
- }
-
- void HandleElapsed (object sender, ElapsedEventArgs e)
- {
- //TODO?: heuristic thresholds are per-region, so we should probably run heuristics once per region
- //XXX: Running heuristics once per region could add undue performance penalty for something that's supposed to
- //check whether the region is too busy! Especially on sims with LOTS of regions.
- //Alternative: make heuristics thresholds global to the module rather than per-region. Less flexible,
- // but would allow us to be semantically correct while being easier on perf.
- //Alternative 2: Run heuristics once per unique set of heuristics threshold parameters! Ay yi yi...
- if (m_closed)
- return;
- bool heuristicsRun = false;
- bool heuristicsPassed = false;
- if (!timerMap.ContainsKey ((Timer)sender)) {
- m_log.Debug ("Code-up error: timerMap doesn't contain timer " + sender.ToString ());
- }
-
- List tmap = timerMap[(Timer)sender];
- if(tmap != null && tmap.Count > 0)
- foreach (IScene scene in tmap) {
- AutoBackupModuleState state = states[scene];
- bool heuristics = state.GetBusyCheck ();
-
- //Fast path: heuristics are on; already ran em; and sim is fine; OR, no heuristics for the region.
- if ((heuristics && heuristicsRun && heuristicsPassed) || !heuristics) {
- doRegionBackup (scene);
- //Heuristics are on; ran but we're too busy -- keep going. Maybe another region will have heuristics off!
- } else if (heuristics && heuristicsRun && !heuristicsPassed) {
- m_log.Info ("[AUTO BACKUP]: Heuristics: too busy to backup " + scene.RegionInfo.RegionName + " right now.");
- continue;
- //Logical Deduction: heuristics are on but haven't been run
- } else {
- heuristicsPassed = RunHeuristics (scene);
- heuristicsRun = true;
- if (!heuristicsPassed) {
- m_log.Info ("[AUTO BACKUP]: Heuristics: too busy to backup " + scene.RegionInfo.RegionName + " right now.");
- continue;
- }
- doRegionBackup (scene);
- }
- }
- }
-
- void doRegionBackup (IScene scene)
- {
- if (scene.RegionStatus != RegionStatus.Up) {
- //We won't backup a region that isn't operating normally.
- m_log.Warn ("[AUTO BACKUP]: Not backing up region " + scene.RegionInfo.RegionName + " because its status is " + scene.RegionStatus.ToString ());
- return;
- }
-
- AutoBackupModuleState state = states[scene];
- IRegionArchiverModule iram = scene.RequestModuleInterface ();
- string savePath = BuildOarPath (scene.RegionInfo.RegionName, state.GetBackupDir (), state.GetNamingType ());
- //m_log.Debug("[AUTO BACKUP]: savePath = " + savePath);
- if (savePath == null) {
- m_log.Warn ("[AUTO BACKUP]: savePath is null in HandleElapsed");
- return;
- }
- iram.ArchiveRegion (savePath, null);
- ExecuteScript (state.GetScript (), savePath);
- }
-
- string IRegionModuleBase.Name {
- get { return "AutoBackupModule"; }
- }
-
- Type IRegionModuleBase.ReplaceableInterface {
- get { return null; }
- }
-
- #endregion
- #region ISharedRegionModule implementation
- void ISharedRegionModule.PostInitialise ()
- {
- //I don't care right now.
- }
-
- #endregion
-
- //Is this even needed?
- public bool IsSharedModule {
- get { return true; }
- }
-
- private string BuildOarPath (string regionName, string baseDir, NamingType naming)
- {
- FileInfo path = null;
- switch (naming) {
- case NamingType.OVERWRITE:
- path = new FileInfo (baseDir + Path.DirectorySeparatorChar + regionName);
- return path.FullName;
- case NamingType.TIME:
- path = new FileInfo (baseDir + Path.DirectorySeparatorChar + regionName + GetTimeString () + ".oar");
- return path.FullName;
- case NamingType.SEQUENTIAL:
- path = new FileInfo (GetNextFile (baseDir, regionName));
- return path.FullName;
- default:
- m_log.Warn ("VERY BAD: Unhandled case element " + naming.ToString ());
- break;
- }
-
- return path.FullName;
- }
-
- //Welcome to the TIME STRING. 4 CORNER INTEGERS, CUBES 4 QUAD MEMORY -- No 1 Integer God.
- //(Terrible reference to )
- //This format may turn out to be too unwieldy to keep...
- //Besides, that's what ctimes are for. But then how do I name each file uniquely without using a GUID?
- //Sequential numbers, right? Ugh. Almost makes TOO much sense.
- private string GetTimeString ()
- {
- StringWriter sw = new StringWriter ();
- sw.Write ("_");
- DateTime now = DateTime.Now;
- sw.Write (now.Year);
- sw.Write ("y_");
- sw.Write (now.Month);
- sw.Write ("M_");
- sw.Write (now.Day);
- sw.Write ("d_");
- sw.Write (now.Hour);
- sw.Write ("h_");
- sw.Write (now.Minute);
- sw.Write ("m_");
- sw.Write (now.Second);
- sw.Write ("s");
- sw.Flush ();
- string output = sw.ToString ();
- sw.Close ();
- return output;
- }
-
- //Get the next logical file name
- //I really shouldn't put fields here, but for now.... ;)
- private string m_dirName = null;
- private string m_regionName = null;
- private string GetNextFile (string dirName, string regionName)
- {
- FileInfo uniqueFile = null;
- m_dirName = dirName;
- m_regionName = regionName;
- long biggestExistingFile = HalfIntervalMaximize (1, FileExistsTest);
- biggestExistingFile++;
- //We don't want to overwrite the biggest existing file; we want to write to the NEXT biggest.
- uniqueFile = new FileInfo (m_dirName + Path.DirectorySeparatorChar + m_regionName + "_" + biggestExistingFile + ".oar");
- if (uniqueFile.Exists) {
- //Congratulations, your strange deletion patterns fooled my half-interval search into picking an existing file!
- //Now you get to pay the performance cost :)
- uniqueFile = UniqueFileSearchLinear (biggestExistingFile);
- }
-
- return uniqueFile.FullName;
- }
-
- /*
- * Return value of true ==> not too busy; false ==> too busy to backup an OAR right now, or error.
- * */
- private bool RunHeuristics (IScene region)
- {
- try {
- return RunTimeDilationHeuristic (region) && RunAgentLimitHeuristic (region);
- } catch (Exception e) {
- m_log.Warn ("[AUTO BACKUP]: Exception in RunHeuristics", e);
- return false;
- }
- }
-
- /*
- * If the time dilation right at this instant is less than the threshold specified in AutoBackupDilationThreshold (default 0.5),
- * then we return false and trip the busy heuristic's "too busy" path (i.e. don't save an OAR).
- * AutoBackupDilationThreshold is a _LOWER BOUND_. Lower Time Dilation is bad, so if you go lower than our threshold, it's "too busy".
- * Return value of "true" ==> not too busy. Return value of "false" ==> too busy!
- * */
- private bool RunTimeDilationHeuristic (IScene region)
- {
- string regionName = region.RegionInfo.RegionName;
- return region.TimeDilation >= m_configSource.Configs["AutoBackupModule"].GetFloat (regionName + ".AutoBackupDilationThreshold", 0.5f);
- }
-
- /*
- * If the root agent count right at this instant is less than the threshold specified in AutoBackupAgentThreshold (default 10),
- * then we return false and trip the busy heuristic's "too busy" path (i.e., don't save an OAR).
- * AutoBackupAgentThreshold is an _UPPER BOUND_. Higher Agent Count is bad, so if you go higher than our threshold, it's "too busy".
- * Return value of "true" ==> not too busy. Return value of "false" ==> too busy!
- * */
- private bool RunAgentLimitHeuristic (IScene region)
- {
- string regionName = region.RegionInfo.RegionName;
- try {
- Scene scene = (Scene)region;
- //TODO: Why isn't GetRootAgentCount() a method in the IScene interface? Seems generally useful...
- return scene.GetRootAgentCount () <= m_configSource.Configs["AutoBackupModule"].GetInt (regionName + ".AutoBackupAgentThreshold", 10);
- } catch (InvalidCastException ice) {
- m_log.Debug ("[AUTO BACKUP]: I NEED MAINTENANCE: IScene is not a Scene; can't get root agent count!");
- return true;
- //Non-obstructionist safest answer...
- }
- }
-
- private void ExecuteScript (string scriptName, string savePath)
- {
- //Fast path out
- if (scriptName == null || scriptName.Length <= 0)
- return;
-
- try {
- FileInfo fi = new FileInfo (scriptName);
- if (fi.Exists) {
- ProcessStartInfo psi = new ProcessStartInfo (scriptName);
- psi.Arguments = savePath;
- psi.CreateNoWindow = true;
- Process proc = Process.Start (psi);
- proc.ErrorDataReceived += HandleProcErrorDataReceived;
- }
- } catch (Exception e) {
- m_log.Warn ("Exception encountered when trying to run script for oar backup " + savePath, e);
- }
- }
-
- void HandleProcErrorDataReceived (object sender, DataReceivedEventArgs e)
- {
- m_log.Warn ("ExecuteScript hook " + ((Process)sender).ProcessName + " is yacking on stderr: " + e.Data);
- }
-
- private void StopAllTimers ()
- {
- foreach (Timer t in timerMap.Keys) {
- t.Close ();
- }
- m_closed = true;
- }
-
- /* Find the largest value for which the predicate returns true.
- * We use a bisection algorithm (half interval) to make the algorithm scalable.
- * The worst-case complexity is about O(log(n)^2) in practice.
- * Only for extremely small values (under 10) do you notice it taking more iterations than a linear search.
- * The number of predicate invocations only hits a few hundred when the maximized value
- * is in the tens of millions, so prepare for the predicate to be invoked between 10 and 100 times.
- * And of course it is fantastic with powers of 2, which are densely packed in values under 100 anyway.
- * The Predicate parameter must be a function that accepts a long and returns a bool.
- * */
- public long HalfIntervalMaximize (long start, Predicate pred)
- {
- long prev = start, curr = start, biggest = 0;
-
- if (start < 0)
- throw new IndexOutOfRangeException ("Start value for HalfIntervalMaximize must be non-negative");
-
- do {
- if (pred (curr)) {
- if (curr > biggest) {
- biggest = curr;
- }
- prev = curr;
- if (curr == 0) {
- //Special case because 0 * 2 = 0 :)
- curr = 1;
- } else {
- //Look deeper
- curr *= 2;
- }
- } else {
- // We went too far, back off halfway
- curr = (curr + prev) / 2;
- }
- } while (curr - prev > 0);
-
- return biggest;
- }
-
- public bool FileExistsTest (long num)
- {
- FileInfo test = new FileInfo (m_dirName + Path.DirectorySeparatorChar + m_regionName + "_" + num + ".oar");
- return test.Exists;
- }
-
-
- //Very slow, hence why we try the HalfIntervalMaximize first!
- public FileInfo UniqueFileSearchLinear (long start)
- {
- long l = start;
- FileInfo retval = null;
- do {
- retval = new FileInfo (m_dirName + Path.DirectorySeparatorChar + m_regionName + "_" + (l++) + ".oar");
- } while (retval.Exists);
-
- return retval;
- }
- }
-
-}
-
+#pragma warning disable 1587
+///
+/// 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.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Reflection;
+using System.Timers;
+using System.Text.RegularExpressions;
+using log4net;
+using Nini.Config;
+using OpenSim.Framework;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+
+///
+/// Config Settings Documentation.
+/// At the TOP LEVEL, e.g. in OpenSim.ini, we have the following options:
+/// EACH REGION, in OpenSim.ini, can have the following settings under the [AutoBackupModule] section.
+/// IMPORTANT: You may optionally specify the key name as follows for a per-region key: .
+/// Example: My region is named Foo.
+/// If I wanted to specify the "AutoBackupInterval" key just for this region, I would name my key "Foo.AutoBackupInterval", under the [AutoBackupModule] section of OpenSim.ini.
+/// Instead of specifying them on a per-region basis, you can also omit the region name to specify the default setting for all regions.
+/// Region-specific settings take precedence.
+///
+/// AutoBackupModuleEnabled: True/False. Default: False. If True, use the auto backup module. This setting does not support per-region basis.
+/// All other settings under [AutoBackupModule] are ignored if AutoBackupModuleEnabled is false, even per-region settings!
+/// AutoBackup: True/False. Default: False. If True, activate auto backup functionality.
+/// This is the only required option for enabling auto-backup; the other options have sane defaults.
+/// If False for a particular region, the auto-backup module becomes a no-op for the region, and all other AutoBackup* settings are ignored.
+/// If False globally (the default), only regions that specifically override this with "FooRegion.AutoBackup = true" will get AutoBackup functionality.
+/// AutoBackupInterval: Double, non-negative value. Default: 720 (12 hours).
+/// The number of minutes between each backup attempt.
+/// If a negative or zero value is given, it is equivalent to setting AutoBackup = False.
+/// AutoBackupBusyCheck: True/False. Default: True.
+/// If True, we will only take an auto-backup if a set of conditions are met.
+/// These conditions are heuristics to try and avoid taking a backup when the sim is busy.
+/// AutoBackupScript: String. Default: not specified (disabled).
+/// File path to an executable script or binary to run when an automatic backup is taken.
+/// The file should really be (Windows) an .exe or .bat, or (Linux/Mac) a shell script or binary.
+/// Trying to "run" directories, or things with weird file associations on Win32, might cause unexpected results!
+/// argv[1] of the executed file/script will be the file name of the generated OAR.
+/// If the process can't be spawned for some reason (file not found, no execute permission, etc), write a warning to the console.
+/// AutoBackupNaming: string. Default: Time.
+/// One of three strings (case insensitive):
+/// "Time": Current timestamp is appended to file name. An existing file will never be overwritten.
+/// "Sequential": A number is appended to the file name. So if RegionName_x.oar exists, we'll save to RegionName_{x+1}.oar next. An existing file will never be overwritten.
+/// "Overwrite": Always save to file named "${AutoBackupDir}/RegionName.oar", even if we have to overwrite an existing file.
+/// AutoBackupDir: String. Default: "." (the current directory).
+/// A directory (absolute or relative) where backups should be saved.
+/// AutoBackupDilationThreshold: float. Default: 0.5. Lower bound on time dilation required for BusyCheck heuristics to pass.
+/// If the time dilation is below this value, don't take a backup right now.
+/// AutoBackupAgentThreshold: int. Default: 10. Upper bound on # of agents in region required for BusyCheck heuristics to pass.
+/// If the number of agents is greater than this value, don't take a backup right now.
+///
+
+namespace OpenSim.Region.OptionalModules.World.AutoBackup
+{
+ public enum NamingType
+ {
+ Time,
+ Sequential,
+ Overwrite
+ }
+
+ public class AutoBackupModule : ISharedRegionModule
+ {
+ private static readonly ILog m_log =
+ LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ /// True means IRegionModuleBase.Close() was called on us, and we should stop operation ASAP.
+ /// Used to prevent elapsing timers after Close() is called from trying to start an autobackup while the sim is shutting down.
+ private readonly AutoBackupModuleState m_defaultState = new AutoBackupModuleState();
+
+ /// Save memory by setting low initial capacities. Minimizes impact in common cases of all regions using same interval, and instances hosting 1 ~ 4 regions.
+ /// Also helps if you don't want AutoBackup at all
+ private readonly Dictionary m_states =
+ new Dictionary(1);
+
+ private readonly Dictionary> m_timerMap =
+ new Dictionary>(1);
+
+ private readonly Dictionary m_timers = new Dictionary(1);
+
+ private bool m_enabled;
+
+ /// Whether the shared module should be enabled at all. NOT the same as m_Enabled in AutoBackupModuleState!
+ private bool m_closed;
+
+ private IConfigSource m_configSource;
+
+ public bool IsSharedModule
+ {
+ get { return true; }
+ }
+
+ #region ISharedRegionModule Members
+
+ string IRegionModuleBase.Name
+ {
+ get { return "AutoBackupModule"; }
+ }
+
+ Type IRegionModuleBase.ReplaceableInterface
+ {
+ get { return null; }
+ }
+
+ void IRegionModuleBase.Initialise(IConfigSource source)
+ {
+ /// Determine if we have been enabled at all in OpenSim.ini -- this is part and parcel of being an optional module
+ this.m_configSource = source;
+ IConfig moduleConfig = source.Configs["AutoBackupModule"];
+ if (moduleConfig == null)
+ {
+ this.m_enabled = false;
+ return;
+ }
+ else
+ {
+ this.m_enabled = moduleConfig.GetBoolean("AutoBackupModuleEnabled", false);
+ if (this.m_enabled)
+ {
+ m_log.Info("[AUTO BACKUP]: AutoBackupModule enabled");
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ Timer defTimer = new Timer(43200000);
+ this.m_defaultState.Timer = defTimer;
+ this.m_timers.Add(43200000, defTimer);
+ defTimer.Elapsed += this.HandleElapsed;
+ defTimer.AutoReset = true;
+ defTimer.Start();
+
+ AutoBackupModuleState abms = this.ParseConfig(null, true);
+ m_log.Debug("[AUTO BACKUP]: Here is the default config:");
+ m_log.Debug(abms.ToString());
+ }
+
+ void IRegionModuleBase.Close()
+ {
+ if (!this.m_enabled)
+ {
+ return;
+ }
+
+ /// We don't want any timers firing while the sim's coming down; strange things may happen.
+ this.StopAllTimers();
+ }
+
+ void IRegionModuleBase.AddRegion(Scene scene)
+ {
+ /// NO-OP. Wait for the region to be loaded.
+ }
+
+ void IRegionModuleBase.RemoveRegion(Scene scene)
+ {
+ if (!this.m_enabled)
+ {
+ return;
+ }
+
+ if (this.m_states.ContainsKey(scene))
+ {
+ AutoBackupModuleState abms = this.m_states[scene];
+
+ /// Remove this scene out of the timer map list
+ Timer timer = abms.Timer;
+ List list = this.m_timerMap[timer];
+ list.Remove(scene);
+
+ /// Shut down the timer if this was the last scene for the timer
+ if (list.Count == 0)
+ {
+ this.m_timerMap.Remove(timer);
+ this.m_timers.Remove(timer.Interval);
+ timer.Close();
+ }
+ this.m_states.Remove(scene);
+ }
+ }
+
+ void IRegionModuleBase.RegionLoaded(Scene scene)
+ {
+ if (!this.m_enabled)
+ {
+ return;
+ }
+
+ /// This really ought not to happen, but just in case, let's pretend it didn't...
+ if (scene == null)
+ {
+ return;
+ }
+
+ AutoBackupModuleState abms = this.ParseConfig(scene, false);
+ m_log.Debug("[AUTO BACKUP]: Config for " + scene.RegionInfo.RegionName);
+ m_log.Debug((abms == null ? "DEFAULT" : abms.ToString()));
+ }
+
+ void ISharedRegionModule.PostInitialise()
+ {
+ /// I don't care right now.
+ }
+
+ #endregion
+
+ private AutoBackupModuleState ParseConfig(IScene scene, bool parseDefault)
+ {
+ string sRegionName;
+ string sRegionLabel;
+ string prepend;
+ AutoBackupModuleState state;
+
+ if (parseDefault)
+ {
+ sRegionName = null;
+ sRegionLabel = "DEFAULT";
+ prepend = "";
+ state = this.m_defaultState;
+ }
+ else
+ {
+ sRegionName = scene.RegionInfo.RegionName;
+ sRegionLabel = sRegionName;
+ prepend = sRegionName + ".";
+ state = null;
+ }
+
+ /// Read the config settings and set variables.
+ IConfig config = this.m_configSource.Configs["AutoBackupModule"];
+ if (config == null)
+ {
+ /// defaultState would be disabled too if the section doesn't exist.
+ state = this.m_defaultState;
+ m_log.Info("[AUTO BACKUP]: Region " + sRegionLabel + " is NOT AutoBackup enabled.");
+ return state;
+ }
+
+ bool tmpEnabled = config.GetBoolean(prepend + "AutoBackup", this.m_defaultState.Enabled);
+ if (state == null && tmpEnabled != this.m_defaultState.Enabled)
+ //Varies from default state
+ {
+ state = new AutoBackupModuleState();
+ }
+
+ if (state != null)
+ {
+ state.Enabled = tmpEnabled;
+ }
+
+ /// If you don't want AutoBackup, we stop.
+ if ((state == null && !this.m_defaultState.Enabled) || (state != null && !state.Enabled))
+ {
+ m_log.Info("[AUTO BACKUP]: Region " + sRegionLabel + " is NOT AutoBackup enabled.");
+ return state;
+ }
+ else
+ {
+ m_log.Info("[AUTO BACKUP]: Region " + sRegionLabel + " is AutoBackup ENABLED.");
+ }
+
+ /// Borrow an existing timer if one exists for the same interval; otherwise, make a new one.
+ double interval =
+ config.GetDouble(prepend + "AutoBackupInterval", this.m_defaultState.IntervalMinutes)*
+ 60000.0;
+ if (state == null && interval != this.m_defaultState.IntervalMinutes*60000.0)
+ {
+ state = new AutoBackupModuleState();
+ }
+
+ if (this.m_timers.ContainsKey(interval))
+ {
+ if (state != null)
+ {
+ state.Timer = this.m_timers[interval];
+ }
+ m_log.Debug("[AUTO BACKUP]: Reusing timer for " + interval + " msec for region " +
+ sRegionLabel);
+ }
+ else
+ {
+ /// 0 or negative interval == do nothing.
+ if (interval <= 0.0 && state != null)
+ {
+ state.Enabled = false;
+ return state;
+ }
+ if (state == null)
+ {
+ state = new AutoBackupModuleState();
+ }
+ Timer tim = new Timer(interval);
+ state.Timer = tim;
+ //Milliseconds -> minutes
+ this.m_timers.Add(interval, tim);
+ tim.Elapsed += this.HandleElapsed;
+ tim.AutoReset = true;
+ tim.Start();
+ }
+
+ /// Add the current region to the list of regions tied to this timer.
+ if (scene != null)
+ {
+ if (state != null)
+ {
+ if (this.m_timerMap.ContainsKey(state.Timer))
+ {
+ this.m_timerMap[state.Timer].Add(scene);
+ }
+ else
+ {
+ List scns = new List(1);
+ scns.Add(scene);
+ this.m_timerMap.Add(state.Timer, scns);
+ }
+ }
+ else
+ {
+ if (this.m_timerMap.ContainsKey(this.m_defaultState.Timer))
+ {
+ this.m_timerMap[this.m_defaultState.Timer].Add(scene);
+ }
+ else
+ {
+ List scns = new List(1);
+ scns.Add(scene);
+ this.m_timerMap.Add(this.m_defaultState.Timer, scns);
+ }
+ }
+ }
+
+ bool tmpBusyCheck = config.GetBoolean(prepend + "AutoBackupBusyCheck",
+ this.m_defaultState.BusyCheck);
+ if (state == null && tmpBusyCheck != this.m_defaultState.BusyCheck)
+ {
+ state = new AutoBackupModuleState();
+ }
+
+ if (state != null)
+ {
+ state.BusyCheck = tmpBusyCheck;
+ }
+
+ /// Set file naming algorithm
+ string stmpNamingType = config.GetString(prepend + "AutoBackupNaming",
+ this.m_defaultState.NamingType.ToString());
+ NamingType tmpNamingType;
+ if (stmpNamingType.Equals("Time", StringComparison.CurrentCultureIgnoreCase))
+ {
+ tmpNamingType = NamingType.Time;
+ }
+ else if (stmpNamingType.Equals("Sequential", StringComparison.CurrentCultureIgnoreCase))
+ {
+ tmpNamingType = NamingType.Sequential;
+ }
+ else if (stmpNamingType.Equals("Overwrite", StringComparison.CurrentCultureIgnoreCase))
+ {
+ tmpNamingType = NamingType.Overwrite;
+ }
+ else
+ {
+ m_log.Warn("Unknown naming type specified for region " + sRegionLabel + ": " +
+ stmpNamingType);
+ tmpNamingType = NamingType.Time;
+ }
+
+ if (state == null && tmpNamingType != this.m_defaultState.NamingType)
+ {
+ state = new AutoBackupModuleState();
+ }
+
+ if (state != null)
+ {
+ state.NamingType = tmpNamingType;
+ }
+
+ string tmpScript = config.GetString(prepend + "AutoBackupScript",
+ this.m_defaultState.Script);
+ if (state == null && tmpScript != this.m_defaultState.Script)
+ {
+ state = new AutoBackupModuleState();
+ }
+
+ if (state != null)
+ {
+ state.Script = tmpScript;
+ }
+
+ string tmpBackupDir = config.GetString(prepend + "AutoBackupDir", ".");
+ if (state == null && tmpBackupDir != this.m_defaultState.BackupDir)
+ {
+ state = new AutoBackupModuleState();
+ }
+
+ if (state != null)
+ {
+ state.BackupDir = tmpBackupDir;
+ /// Let's give the user *one* convenience and auto-mkdir
+ if (state.BackupDir != ".")
+ {
+ try
+ {
+ DirectoryInfo dirinfo = new DirectoryInfo(state.BackupDir);
+ if (!dirinfo.Exists)
+ {
+ dirinfo.Create();
+ }
+ }
+ catch (Exception e)
+ {
+ m_log.Warn(
+ "BAD NEWS. You won't be able to save backups to directory " +
+ state.BackupDir +
+ " because it doesn't exist or there's a permissions issue with it. Here's the exception.",
+ e);
+ }
+ }
+ }
+
+ return state;
+ }
+
+ private void HandleElapsed(object sender, ElapsedEventArgs e)
+ {
+ /// TODO?: heuristic thresholds are per-region, so we should probably run heuristics once per region
+ /// XXX: Running heuristics once per region could add undue performance penalty for something that's supposed to
+ /// check whether the region is too busy! Especially on sims with LOTS of regions.
+ /// Alternative: make heuristics thresholds global to the module rather than per-region. Less flexible,
+ /// but would allow us to be semantically correct while being easier on perf.
+ /// Alternative 2: Run heuristics once per unique set of heuristics threshold parameters! Ay yi yi...
+ if (this.m_closed)
+ {
+ return;
+ }
+ bool heuristicsRun = false;
+ bool heuristicsPassed = false;
+ if (!this.m_timerMap.ContainsKey((Timer) sender))
+ {
+ m_log.Debug("Code-up error: timerMap doesn't contain timer " + sender);
+ }
+
+ List tmap = this.m_timerMap[(Timer) sender];
+ if (tmap != null && tmap.Count > 0)
+ {
+ foreach (IScene scene in tmap)
+ {
+ AutoBackupModuleState state = this.m_states[scene];
+ bool heuristics = state.BusyCheck;
+
+ /// Fast path: heuristics are on; already ran em; and sim is fine; OR, no heuristics for the region.
+ if ((heuristics && heuristicsRun && heuristicsPassed) || !heuristics)
+ {
+ this.DoRegionBackup(scene);
+ /// Heuristics are on; ran but we're too busy -- keep going. Maybe another region will have heuristics off!
+ }
+ else if (heuristicsRun)
+ {
+ m_log.Info("[AUTO BACKUP]: Heuristics: too busy to backup " +
+ scene.RegionInfo.RegionName + " right now.");
+ continue;
+ /// Logical Deduction: heuristics are on but haven't been run
+ }
+ else
+ {
+ heuristicsPassed = this.RunHeuristics(scene);
+ heuristicsRun = true;
+ if (!heuristicsPassed)
+ {
+ m_log.Info("[AUTO BACKUP]: Heuristics: too busy to backup " +
+ scene.RegionInfo.RegionName + " right now.");
+ continue;
+ }
+ this.DoRegionBackup(scene);
+ }
+ }
+ }
+ }
+
+ private void DoRegionBackup(IScene scene)
+ {
+ if (scene.RegionStatus != RegionStatus.Up)
+ {
+ /// We won't backup a region that isn't operating normally.
+ m_log.Warn("[AUTO BACKUP]: Not backing up region " + scene.RegionInfo.RegionName +
+ " because its status is " + scene.RegionStatus);
+ return;
+ }
+
+ AutoBackupModuleState state = this.m_states[scene];
+ IRegionArchiverModule iram = scene.RequestModuleInterface();
+ string savePath = BuildOarPath(scene.RegionInfo.RegionName,
+ state.BackupDir,
+ state.NamingType);
+ /// m_log.Debug("[AUTO BACKUP]: savePath = " + savePath);
+ if (savePath == null)
+ {
+ m_log.Warn("[AUTO BACKUP]: savePath is null in HandleElapsed");
+ return;
+ }
+ iram.ArchiveRegion(savePath, Guid.NewGuid(), null);
+ ExecuteScript(state.Script, savePath);
+ }
+
+ /// This format may turn out to be too unwieldy to keep...
+ /// Besides, that's what ctimes are for. But then how do I name each file uniquely without using a GUID?
+ /// Sequential numbers, right? Ugh. Almost makes TOO much sense.
+ private static string GetTimeString()
+ {
+ StringWriter sw = new StringWriter();
+ sw.Write("_");
+ DateTime now = DateTime.Now;
+ sw.Write(now.Year);
+ sw.Write("y_");
+ sw.Write(now.Month);
+ sw.Write("M_");
+ sw.Write(now.Day);
+ sw.Write("d_");
+ sw.Write(now.Hour);
+ sw.Write("h_");
+ sw.Write(now.Minute);
+ sw.Write("m_");
+ sw.Write(now.Second);
+ sw.Write("s");
+ sw.Flush();
+ string output = sw.ToString();
+ sw.Close();
+ return output;
+ }
+
+ ///
+ /// Return value of true ==> not too busy; false ==> too busy to backup an OAR right now, or error.
+ ///
+ private bool RunHeuristics(IScene region)
+ {
+ try
+ {
+ return this.RunTimeDilationHeuristic(region) && this.RunAgentLimitHeuristic(region);
+ }
+ catch (Exception e)
+ {
+ m_log.Warn("[AUTO BACKUP]: Exception in RunHeuristics", e);
+ return false;
+ }
+ }
+
+ ///
+ /// If the time dilation right at this instant is less than the threshold specified in AutoBackupDilationThreshold (default 0.5),
+ /// then we return false and trip the busy heuristic's "too busy" path (i.e. don't save an OAR).
+ /// AutoBackupDilationThreshold is a _LOWER BOUND_. Lower Time Dilation is bad, so if you go lower than our threshold, it's "too busy".
+ /// Return value of "true" ==> not too busy. Return value of "false" ==> too busy!
+ ///
+ private bool RunTimeDilationHeuristic(IScene region)
+ {
+ string regionName = region.RegionInfo.RegionName;
+ return region.TimeDilation >=
+ this.m_configSource.Configs["AutoBackupModule"].GetFloat(
+ regionName + ".AutoBackupDilationThreshold", 0.5f);
+ }
+
+ ///
+ /// If the root agent count right at this instant is less than the threshold specified in AutoBackupAgentThreshold (default 10),
+ /// then we return false and trip the busy heuristic's "too busy" path (i.e., don't save an OAR).
+ /// AutoBackupAgentThreshold is an _UPPER BOUND_. Higher Agent Count is bad, so if you go higher than our threshold, it's "too busy".
+ /// Return value of "true" ==> not too busy. Return value of "false" ==> too busy!
+ ///
+ private bool RunAgentLimitHeuristic(IScene region)
+ {
+ string regionName = region.RegionInfo.RegionName;
+ try
+ {
+ Scene scene = (Scene) region;
+ /// TODO: Why isn't GetRootAgentCount() a method in the IScene interface? Seems generally useful...
+ return scene.GetRootAgentCount() <=
+ this.m_configSource.Configs["AutoBackupModule"].GetInt(
+ regionName + ".AutoBackupAgentThreshold", 10);
+ }
+ catch (InvalidCastException ice)
+ {
+ m_log.Debug(
+ "[AUTO BACKUP]: I NEED MAINTENANCE: IScene is not a Scene; can't get root agent count!",
+ ice);
+ return true;
+ /// Non-obstructionist safest answer...
+ }
+ }
+
+ private static void ExecuteScript(string scriptName, string savePath)
+ {
+ //Fast path out
+ if (scriptName == null || scriptName.Length <= 0)
+ {
+ return;
+ }
+
+ try
+ {
+ FileInfo fi = new FileInfo(scriptName);
+ if (fi.Exists)
+ {
+ ProcessStartInfo psi = new ProcessStartInfo(scriptName);
+ psi.Arguments = savePath;
+ psi.CreateNoWindow = true;
+ Process proc = Process.Start(psi);
+ proc.ErrorDataReceived += HandleProcErrorDataReceived;
+ }
+ }
+ catch (Exception e)
+ {
+ m_log.Warn(
+ "Exception encountered when trying to run script for oar backup " + savePath, e);
+ }
+ }
+
+ private static void HandleProcErrorDataReceived(object sender, DataReceivedEventArgs e)
+ {
+ m_log.Warn("ExecuteScript hook " + ((Process) sender).ProcessName +
+ " is yacking on stderr: " + e.Data);
+ }
+
+ private void StopAllTimers()
+ {
+ foreach (Timer t in this.m_timerMap.Keys)
+ {
+ t.Close();
+ }
+ this.m_closed = true;
+ }
+
+ private static string GetNextFile(string dirName, string regionName)
+ {
+ FileInfo uniqueFile = null;
+ long biggestExistingFile = GetNextOarFileNumber(dirName, regionName);
+ biggestExistingFile++;
+ //We don't want to overwrite the biggest existing file; we want to write to the NEXT biggest.
+ uniqueFile =
+ new FileInfo(dirName + Path.DirectorySeparatorChar + regionName + "_" +
+ biggestExistingFile + ".oar");
+ return uniqueFile.FullName;
+ }
+
+ private static string BuildOarPath(string regionName, string baseDir, NamingType naming)
+ {
+ FileInfo path = null;
+ switch (naming)
+ {
+ case NamingType.Overwrite:
+ path = new FileInfo(baseDir + Path.DirectorySeparatorChar + regionName + ".oar");
+ return path.FullName;
+ case NamingType.Time:
+ path =
+ new FileInfo(baseDir + Path.DirectorySeparatorChar + regionName +
+ GetTimeString() + ".oar");
+ return path.FullName;
+ case NamingType.Sequential:
+ /// All codepaths in GetNextFile should return a file name ending in .oar
+ path = new FileInfo(GetNextFile(baseDir, regionName));
+ return path.FullName;
+ default:
+ m_log.Warn("VERY BAD: Unhandled case element " + naming);
+ break;
+ }
+
+ return null;
+ }
+
+ private static long GetNextOarFileNumber(string dirName, string regionName)
+ {
+ long retval = 1;
+
+ DirectoryInfo di = new DirectoryInfo(dirName);
+ FileInfo[] fi = di.GetFiles(regionName, SearchOption.TopDirectoryOnly);
+ Array.Sort(fi, (f1, f2) => StringComparer.CurrentCultureIgnoreCase.Compare(f1.Name, f2.Name));
+
+ if (fi.LongLength > 0)
+ {
+ long subtract = 1L;
+ bool worked = false;
+ Regex reg = new Regex(regionName + "_([0-9])+" + ".oar");
+
+ while (!worked && subtract <= fi.LongLength)
+ {
+ /// Pick the file with the last natural ordering
+ string biggestFileName = fi[fi.LongLength - subtract].Name;
+ MatchCollection matches = reg.Matches(biggestFileName);
+ long l = 1;
+ if (matches.Count > 0 && matches[0].Groups.Count > 0)
+ {
+ try
+ {
+ long.TryParse(matches[0].Groups[1].Value, out l);
+ retval = l;
+ worked = true;
+ }
+ catch (FormatException fe)
+ {
+ m_log.Warn(
+ "[AUTO BACKUP]: Error: Can't parse long value from file name to determine next OAR backup file number!",
+ fe);
+ subtract++;
+ }
+ }
+ else
+ {
+ subtract++;
+ }
+ }
+ }
+ return retval;
+ }
+ }
+}
+
+
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs
new file mode 100644
index 0000000..1b348af
--- /dev/null
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs
@@ -0,0 +1,109 @@
+#pragma warning disable 1587
+///
+/// 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.
+///
+
+namespace OpenSim.Region.OptionalModules.World.AutoBackup
+{
+ /// AutoBackupModuleState: Auto-Backup state for one region (scene).
+ public class AutoBackupModuleState
+ {
+ public AutoBackupModuleState()
+ {
+ this.Enabled = false;
+ this.BackupDir = ".";
+ this.BusyCheck = true;
+ this.Timer = null;
+ this.NamingType = NamingType.Time;
+ this.Script = null;
+ }
+
+ public bool Enabled
+ {
+ get;
+ set;
+ }
+
+ public System.Timers.Timer Timer
+ {
+ get;
+ set;
+ }
+
+ public double IntervalMinutes
+ {
+ get
+ {
+ if (this.Timer == null)
+ {
+ return -1.0;
+ }
+ else
+ {
+ return this.Timer.Interval / 60000.0;
+ }
+ }
+ }
+
+ public bool BusyCheck
+ {
+ get;
+ set;
+ }
+
+ public string Script
+ {
+ get;
+ set;
+ }
+
+ public string BackupDir
+ {
+ get;
+ set;
+ }
+
+ public NamingType NamingType
+ {
+ get;
+ set;
+ }
+
+ public new string ToString()
+ {
+ string retval = "";
+
+ retval += "[AUTO BACKUP]: AutoBackup: " + (Enabled ? "ENABLED" : "DISABLED") + "\n";
+ retval += "[AUTO BACKUP]: Interval: " + IntervalMinutes + " minutes" + "\n";
+ retval += "[AUTO BACKUP]: Do Busy Check: " + (BusyCheck ? "Yes" : "No") + "\n";
+ retval += "[AUTO BACKUP]: Naming Type: " + NamingType.ToString() + "\n";
+ retval += "[AUTO BACKUP]: Backup Dir: " + BackupDir + "\n";
+ retval += "[AUTO BACKUP]: Script: " + Script + "\n";
+ return retval;
+ }
+ }
+}
+
--
cgit v1.1
From 440d54a52158057a93d8bd08c5e46290e69d8f3f Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Sat, 23 Apr 2011 18:32:08 -0400
Subject: AutoBackup: Removed unneeded imports.
---
OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs | 1 -
1 file changed, 1 deletion(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index a4dbea4..7c0a5c6 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -29,7 +29,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
-using System.Globalization;
using System.IO;
using System.Reflection;
using System.Timers;
--
cgit v1.1
From c82c7e6ed9ecf4858d5afffa635013bd14e90bdf Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Tue, 26 Apr 2011 11:42:06 -0400
Subject: Wait for OnOarFileSaved event callback before executing script
We want to execute the (optional) user script after I/O is done on the oar.
I wasn't aware that ArchiveRegion is asynchronous -- now I am.
Should fully resolve comment 0018290 at
http://opensimulator.org/mantis/view.php?id=5440
---
.../World/AutoBackup/AutoBackupModule.cs | 23 ++++++++++++++--------
.../World/AutoBackup/AutoBackupModuleState.cs | 14 +++++++++++++
2 files changed, 29 insertions(+), 8 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index 7c0a5c6..bd4893c 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -94,18 +94,14 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
private static readonly ILog m_log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
- /// True means IRegionModuleBase.Close() was called on us, and we should stop operation ASAP.
- /// Used to prevent elapsing timers after Close() is called from trying to start an autobackup while the sim is shutting down.
- private readonly AutoBackupModuleState m_defaultState = new AutoBackupModuleState();
-
/// Save memory by setting low initial capacities. Minimizes impact in common cases of all regions using same interval, and instances hosting 1 ~ 4 regions.
/// Also helps if you don't want AutoBackup at all
+ private readonly Dictionary m_pendingSaves = new Dictionary(1);
+ private readonly AutoBackupModuleState m_defaultState = new AutoBackupModuleState();
private readonly Dictionary m_states =
new Dictionary(1);
-
private readonly Dictionary> m_timerMap =
new Dictionary>(1);
-
private readonly Dictionary m_timers = new Dictionary(1);
private bool m_enabled;
@@ -528,8 +524,19 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
m_log.Warn("[AUTO BACKUP]: savePath is null in HandleElapsed");
return;
}
- iram.ArchiveRegion(savePath, Guid.NewGuid(), null);
- ExecuteScript(state.Script, savePath);
+ Guid guid = Guid.NewGuid();
+ m_pendingSaves.Add(guid, scene);
+ state.LiveRequests.Add(guid, savePath);
+ ((Scene) scene).EventManager.OnOarFileSaved += new EventManager.OarFileSaved(EventManager_OnOarFileSaved);
+ iram.ArchiveRegion(savePath, guid, null);
+ }
+
+ void EventManager_OnOarFileSaved(Guid guid, string message)
+ {
+ AutoBackupModuleState abms = m_states[(m_pendingSaves[guid])];
+ ExecuteScript(abms.Script, abms.LiveRequests[guid]);
+ m_pendingSaves.Remove(guid);
+ abms.LiveRequests.Remove(guid);
}
/// This format may turn out to be too unwieldy to keep...
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs
index 1b348af..7fecfa4 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs
@@ -26,11 +26,17 @@
/// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
///
+using System;
+using System.Collections.Generic;
+
+
namespace OpenSim.Region.OptionalModules.World.AutoBackup
{
/// AutoBackupModuleState: Auto-Backup state for one region (scene).
public class AutoBackupModuleState
{
+ private Dictionary m_liveRequests = null;
+
public AutoBackupModuleState()
{
this.Enabled = false;
@@ -41,6 +47,14 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
this.Script = null;
}
+ public Dictionary LiveRequests
+ {
+ get {
+ return this.m_liveRequests ??
+ (this.m_liveRequests = new Dictionary(1));
+ }
+ }
+
public bool Enabled
{
get;
--
cgit v1.1
From 2aab033aaa41f2ebef96e725b76e0153a673b448 Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Mon, 2 May 2011 02:20:50 -0400
Subject: First pass at fixing justincc's feedback v2 (
http://opensimulator.org/mantis/view.php?id=5440 )
Fixing everything here (I think) except the per-region config. That's next.
---
.../World/AutoBackup/AutoBackupModule.cs | 1577 +++++++++++---------
.../World/AutoBackup/AutoBackupModuleState.cs | 59 +-
2 files changed, 868 insertions(+), 768 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index bd4893c..4a9615d 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -1,751 +1,848 @@
-#pragma warning disable 1587
-///
-/// 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.Diagnostics;
-using System.IO;
-using System.Reflection;
-using System.Timers;
-using System.Text.RegularExpressions;
-using log4net;
-using Nini.Config;
-using OpenSim.Framework;
-using OpenSim.Region.Framework.Interfaces;
-using OpenSim.Region.Framework.Scenes;
-
-///
-/// Config Settings Documentation.
-/// At the TOP LEVEL, e.g. in OpenSim.ini, we have the following options:
-/// EACH REGION, in OpenSim.ini, can have the following settings under the [AutoBackupModule] section.
-/// IMPORTANT: You may optionally specify the key name as follows for a per-region key: .
-/// Example: My region is named Foo.
-/// If I wanted to specify the "AutoBackupInterval" key just for this region, I would name my key "Foo.AutoBackupInterval", under the [AutoBackupModule] section of OpenSim.ini.
-/// Instead of specifying them on a per-region basis, you can also omit the region name to specify the default setting for all regions.
-/// Region-specific settings take precedence.
-///
-/// AutoBackupModuleEnabled: True/False. Default: False. If True, use the auto backup module. This setting does not support per-region basis.
-/// All other settings under [AutoBackupModule] are ignored if AutoBackupModuleEnabled is false, even per-region settings!
-/// AutoBackup: True/False. Default: False. If True, activate auto backup functionality.
-/// This is the only required option for enabling auto-backup; the other options have sane defaults.
-/// If False for a particular region, the auto-backup module becomes a no-op for the region, and all other AutoBackup* settings are ignored.
-/// If False globally (the default), only regions that specifically override this with "FooRegion.AutoBackup = true" will get AutoBackup functionality.
-/// AutoBackupInterval: Double, non-negative value. Default: 720 (12 hours).
-/// The number of minutes between each backup attempt.
-/// If a negative or zero value is given, it is equivalent to setting AutoBackup = False.
-/// AutoBackupBusyCheck: True/False. Default: True.
-/// If True, we will only take an auto-backup if a set of conditions are met.
-/// These conditions are heuristics to try and avoid taking a backup when the sim is busy.
-/// AutoBackupScript: String. Default: not specified (disabled).
-/// File path to an executable script or binary to run when an automatic backup is taken.
-/// The file should really be (Windows) an .exe or .bat, or (Linux/Mac) a shell script or binary.
-/// Trying to "run" directories, or things with weird file associations on Win32, might cause unexpected results!
-/// argv[1] of the executed file/script will be the file name of the generated OAR.
-/// If the process can't be spawned for some reason (file not found, no execute permission, etc), write a warning to the console.
-/// AutoBackupNaming: string. Default: Time.
-/// One of three strings (case insensitive):
-/// "Time": Current timestamp is appended to file name. An existing file will never be overwritten.
-/// "Sequential": A number is appended to the file name. So if RegionName_x.oar exists, we'll save to RegionName_{x+1}.oar next. An existing file will never be overwritten.
-/// "Overwrite": Always save to file named "${AutoBackupDir}/RegionName.oar", even if we have to overwrite an existing file.
-/// AutoBackupDir: String. Default: "." (the current directory).
-/// A directory (absolute or relative) where backups should be saved.
-/// AutoBackupDilationThreshold: float. Default: 0.5. Lower bound on time dilation required for BusyCheck heuristics to pass.
-/// If the time dilation is below this value, don't take a backup right now.
-/// AutoBackupAgentThreshold: int. Default: 10. Upper bound on # of agents in region required for BusyCheck heuristics to pass.
-/// If the number of agents is greater than this value, don't take a backup right now.
-///
-
-namespace OpenSim.Region.OptionalModules.World.AutoBackup
-{
- public enum NamingType
- {
- Time,
- Sequential,
- Overwrite
- }
-
- public class AutoBackupModule : ISharedRegionModule
- {
- private static readonly ILog m_log =
- LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
- /// Save memory by setting low initial capacities. Minimizes impact in common cases of all regions using same interval, and instances hosting 1 ~ 4 regions.
- /// Also helps if you don't want AutoBackup at all
- private readonly Dictionary m_pendingSaves = new Dictionary(1);
- private readonly AutoBackupModuleState m_defaultState = new AutoBackupModuleState();
- private readonly Dictionary m_states =
- new Dictionary(1);
- private readonly Dictionary> m_timerMap =
- new Dictionary>(1);
- private readonly Dictionary m_timers = new Dictionary(1);
-
- private bool m_enabled;
-
+/*
+ * 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.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Timers;
+using System.Text.RegularExpressions;
+using log4net;
+using Nini.Config;
+using OpenSim.Framework;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+
+namespace OpenSim.Region.OptionalModules.World.AutoBackup
+{
+ ///
+ /// Choose between ways of naming the backup files that are generated.
+ ///
+ /// Time: OARs are named by a timestamp.
+ /// Sequential: OARs are named by counting (Region_1.oar, Region_2.oar, etc.)
+ /// Overwrite: Only one file per region is created; it's overwritten each time a backup is made.
+ public enum NamingType
+ {
+ Time,
+ Sequential,
+ Overwrite
+ }
+
+ ///
+ /// AutoBackupModule: save OAR region backups to disk periodically
+ ///
+ ///
+ /// Config Settings Documentation.
+ /// At the TOP LEVEL, e.g. in OpenSim.ini, we have the following options:
+ /// EACH REGION, in OpenSim.ini, can have the following settings under the [AutoBackupModule] section.
+ /// IMPORTANT: You may optionally specify the key name as follows for a per-region key: [Region Name].[Key Name]
+ /// Example: My region is named Foo.
+ /// If I wanted to specify the "AutoBackupInterval" key just for this region, I would name my key "Foo.AutoBackupInterval", under the [AutoBackupModule] section of OpenSim.ini.
+ /// Instead of specifying them on a per-region basis, you can also omit the region name to specify the default setting for all regions.
+ /// Region-specific settings take precedence.
+ ///
+ /// AutoBackupModuleEnabled: True/False. Default: False. If True, use the auto backup module. This setting does not support per-region basis.
+ /// All other settings under [AutoBackupModule] are ignored if AutoBackupModuleEnabled is false, even per-region settings!
+ /// AutoBackup: True/False. Default: False. If True, activate auto backup functionality.
+ /// This is the only required option for enabling auto-backup; the other options have sane defaults.
+ /// If False for a particular region, the auto-backup module becomes a no-op for the region, and all other AutoBackup* settings are ignored.
+ /// If False globally (the default), only regions that specifically override this with "FooRegion.AutoBackup = true" will get AutoBackup functionality.
+ /// AutoBackupInterval: Double, non-negative value. Default: 720 (12 hours).
+ /// The number of minutes between each backup attempt.
+ /// If a negative or zero value is given, it is equivalent to setting AutoBackup = False.
+ /// AutoBackupBusyCheck: True/False. Default: True.
+ /// If True, we will only take an auto-backup if a set of conditions are met.
+ /// These conditions are heuristics to try and avoid taking a backup when the sim is busy.
+ /// AutoBackupScript: String. Default: not specified (disabled).
+ /// File path to an executable script or binary to run when an automatic backup is taken.
+ /// The file should really be (Windows) an .exe or .bat, or (Linux/Mac) a shell script or binary.
+ /// Trying to "run" directories, or things with weird file associations on Win32, might cause unexpected results!
+ /// argv[1] of the executed file/script will be the file name of the generated OAR.
+ /// If the process can't be spawned for some reason (file not found, no execute permission, etc), write a warning to the console.
+ /// AutoBackupNaming: string. Default: Time.
+ /// One of three strings (case insensitive):
+ /// "Time": Current timestamp is appended to file name. An existing file will never be overwritten.
+ /// "Sequential": A number is appended to the file name. So if RegionName_x.oar exists, we'll save to RegionName_{x+1}.oar next. An existing file will never be overwritten.
+ /// "Overwrite": Always save to file named "${AutoBackupDir}/RegionName.oar", even if we have to overwrite an existing file.
+ /// AutoBackupDir: String. Default: "." (the current directory).
+ /// A directory (absolute or relative) where backups should be saved.
+ /// AutoBackupDilationThreshold: float. Default: 0.5. Lower bound on time dilation required for BusyCheck heuristics to pass.
+ /// If the time dilation is below this value, don't take a backup right now.
+ /// AutoBackupAgentThreshold: int. Default: 10. Upper bound on # of agents in region required for BusyCheck heuristics to pass.
+ /// If the number of agents is greater than this value, don't take a backup right now
+ /// Save memory by setting low initial capacities. Minimizes impact in common cases of all regions using same interval, and instances hosting 1 ~ 4 regions.
+ /// Also helps if you don't want AutoBackup at all.
+ ///
+ public class AutoBackupModule : ISharedRegionModule
+ {
+ private static readonly ILog m_log =
+ LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+ private readonly Dictionary m_pendingSaves = new Dictionary(1);
+ private readonly AutoBackupModuleState m_defaultState = new AutoBackupModuleState();
+ private readonly Dictionary m_states =
+ new Dictionary(1);
+ private readonly Dictionary> m_timerMap =
+ new Dictionary>(1);
+ private readonly Dictionary m_timers = new Dictionary(1);
+
+ private bool m_enabled;
+
+ ///
/// Whether the shared module should be enabled at all. NOT the same as m_Enabled in AutoBackupModuleState!
- private bool m_closed;
-
- private IConfigSource m_configSource;
-
- public bool IsSharedModule
+ ///
+ private bool m_closed;
+
+ private IConfigSource m_configSource;
+
+ ///
+ /// Required by framework.
+ ///
+ public bool IsSharedModule
+ {
+ get { return true; }
+ }
+
+ #region ISharedRegionModule Members
+
+ ///
+ /// Identifies the module to the system.
+ ///
+ string IRegionModuleBase.Name
+ {
+ get { return "AutoBackupModule"; }
+ }
+
+ ///
+ /// We don't implement an interface, this is a single-use module.
+ ///
+ Type IRegionModuleBase.ReplaceableInterface
+ {
+ get { return null; }
+ }
+
+ ///
+ /// Called once in the lifetime of the module at startup.
+ ///
+ /// The input config source for OpenSim.ini.
+ void IRegionModuleBase.Initialise(IConfigSource source)
+ {
+ // Determine if we have been enabled at all in OpenSim.ini -- this is part and parcel of being an optional module
+ this.m_configSource = source;
+ IConfig moduleConfig = source.Configs["AutoBackupModule"];
+ if (moduleConfig == null)
+ {
+ this.m_enabled = false;
+ return;
+ }
+ else
+ {
+ this.m_enabled = moduleConfig.GetBoolean("AutoBackupModuleEnabled", false);
+ if (this.m_enabled)
+ {
+ m_log.Info("[AUTO BACKUP]: AutoBackupModule enabled");
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ Timer defTimer = new Timer(43200000);
+ this.m_defaultState.Timer = defTimer;
+ this.m_timers.Add(43200000, defTimer);
+ defTimer.Elapsed += this.HandleElapsed;
+ defTimer.AutoReset = true;
+ defTimer.Start();
+
+ AutoBackupModuleState abms = this.ParseConfig(null, true);
+ m_log.Debug("[AUTO BACKUP]: Here is the default config:");
+ m_log.Debug(abms.ToString());
+ }
+
+ ///
+ /// Called once at de-init (sim shutting down).
+ ///
+ void IRegionModuleBase.Close()
+ {
+ if (!this.m_enabled)
+ {
+ return;
+ }
+
+ // We don't want any timers firing while the sim's coming down; strange things may happen.
+ this.StopAllTimers();
+ }
+
+ ///
+ /// Currently a no-op for AutoBackup because we have to wait for region to be fully loaded.
+ ///
+ ///
+ void IRegionModuleBase.AddRegion(Scene scene)
+ {
+ }
+
+ ///
+ /// Here we just clean up some resources and stop the OAR backup (if any) for the given scene.
+ ///
+ /// The scene (region) to stop performing AutoBackup on.
+ void IRegionModuleBase.RemoveRegion(Scene scene)
+ {
+ if (!this.m_enabled)
+ {
+ return;
+ }
+
+ if (this.m_states.ContainsKey(scene))
+ {
+ AutoBackupModuleState abms = this.m_states[scene];
+
+ // Remove this scene out of the timer map list
+ Timer timer = abms.Timer;
+ List list = this.m_timerMap[timer];
+ list.Remove(scene);
+
+ // Shut down the timer if this was the last scene for the timer
+ if (list.Count == 0)
+ {
+ this.m_timerMap.Remove(timer);
+ this.m_timers.Remove(timer.Interval);
+ timer.Close();
+ }
+ this.m_states.Remove(scene);
+ }
+ }
+
+ ///
+ /// Most interesting/complex code paths in AutoBackup begin here.
+ /// We read lots of Nini config, maybe set a timer, add members to state tracking Dictionaries, etc.
+ ///
+ /// The scene to (possibly) perform AutoBackup on.
+ void IRegionModuleBase.RegionLoaded(Scene scene)
+ {
+ if (!this.m_enabled)
+ {
+ return;
+ }
+
+ // This really ought not to happen, but just in case, let's pretend it didn't...
+ if (scene == null)
+ {
+ return;
+ }
+
+ AutoBackupModuleState abms = this.ParseConfig(scene, false);
+ m_log.Debug("[AUTO BACKUP]: Config for " + scene.RegionInfo.RegionName);
+ m_log.Debug((abms == null ? "DEFAULT" : abms.ToString()));
+ }
+
+ ///
+ /// Currently a no-op.
+ ///
+ void ISharedRegionModule.PostInitialise()
+ {
+ }
+
+ #endregion
+
+ ///
+ /// Set up internal state for a given scene. Fairly complex code.
+ /// When this method returns, we've started auto-backup timers, put members in Dictionaries, and created a State object for this scene.
+ ///
+ /// The scene to look at.
+ /// Whether this call is intended to figure out what we consider the "default" config (applied to all regions unless overridden by per-region settings).
+ /// An AutoBackupModuleState contains most information you should need to know relevant to auto-backup, as applicable to a single region.
+ private AutoBackupModuleState ParseConfig(IScene scene, bool parseDefault)
+ {
+ string sRegionName;
+ string sRegionLabel;
+ string prepend;
+ AutoBackupModuleState state;
+
+ if (parseDefault)
+ {
+ sRegionName = null;
+ sRegionLabel = "DEFAULT";
+ prepend = "";
+ state = this.m_defaultState;
+ }
+ else
+ {
+ sRegionName = scene.RegionInfo.RegionName;
+ sRegionLabel = sRegionName;
+ prepend = sRegionName + ".";
+ state = null;
+ }
+
+ // Read the config settings and set variables.
+ IConfig config = this.m_configSource.Configs["AutoBackupModule"];
+ if (config == null)
+ {
+ // defaultState would be disabled too if the section doesn't exist.
+ state = this.m_defaultState;
+ m_log.Info("[AUTO BACKUP]: Region " + sRegionLabel + " is NOT AutoBackup enabled.");
+ return state;
+ }
+
+ bool tmpEnabled = config.GetBoolean(prepend + "AutoBackup", this.m_defaultState.Enabled);
+ if (state == null && tmpEnabled != this.m_defaultState.Enabled)
+ //Varies from default state
+ {
+ state = new AutoBackupModuleState();
+ }
+
+ if (state != null)
+ {
+ state.Enabled = tmpEnabled;
+ }
+
+ // If you don't want AutoBackup, we stop.
+ if ((state == null && !this.m_defaultState.Enabled) || (state != null && !state.Enabled))
+ {
+ return state;
+ }
+ else
+ {
+ m_log.Info("[AUTO BACKUP]: Region " + sRegionLabel + " is AutoBackup ENABLED.");
+ }
+
+ // Borrow an existing timer if one exists for the same interval; otherwise, make a new one.
+ double interval =
+ config.GetDouble(prepend + "AutoBackupInterval", this.m_defaultState.IntervalMinutes)*
+ 60000.0;
+ if (state == null && interval != this.m_defaultState.IntervalMinutes*60000.0)
+ {
+ state = new AutoBackupModuleState();
+ }
+
+ if (this.m_timers.ContainsKey(interval))
+ {
+ if (state != null)
+ {
+ state.Timer = this.m_timers[interval];
+ }
+ m_log.Debug("[AUTO BACKUP]: Reusing timer for " + interval + " msec for region " +
+ sRegionLabel);
+ }
+ else
+ {
+ // 0 or negative interval == do nothing.
+ if (interval <= 0.0 && state != null)
+ {
+ state.Enabled = false;
+ return state;
+ }
+ if (state == null)
+ {
+ state = new AutoBackupModuleState();
+ }
+ Timer tim = new Timer(interval);
+ state.Timer = tim;
+ //Milliseconds -> minutes
+ this.m_timers.Add(interval, tim);
+ tim.Elapsed += this.HandleElapsed;
+ tim.AutoReset = true;
+ tim.Start();
+ }
+
+ // Add the current region to the list of regions tied to this timer.
+ if (scene != null)
+ {
+ if (state != null)
+ {
+ if (this.m_timerMap.ContainsKey(state.Timer))
+ {
+ this.m_timerMap[state.Timer].Add(scene);
+ }
+ else
+ {
+ List scns = new List(1);
+ scns.Add(scene);
+ this.m_timerMap.Add(state.Timer, scns);
+ }
+ }
+ else
+ {
+ if (this.m_timerMap.ContainsKey(this.m_defaultState.Timer))
+ {
+ this.m_timerMap[this.m_defaultState.Timer].Add(scene);
+ }
+ else
+ {
+ List scns = new List(1);
+ scns.Add(scene);
+ this.m_timerMap.Add(this.m_defaultState.Timer, scns);
+ }
+ }
+ }
+
+ bool tmpBusyCheck = config.GetBoolean(prepend + "AutoBackupBusyCheck",
+ this.m_defaultState.BusyCheck);
+ if (state == null && tmpBusyCheck != this.m_defaultState.BusyCheck)
+ {
+ state = new AutoBackupModuleState();
+ }
+
+ if (state != null)
+ {
+ state.BusyCheck = tmpBusyCheck;
+ }
+
+ // Set file naming algorithm
+ string stmpNamingType = config.GetString(prepend + "AutoBackupNaming",
+ this.m_defaultState.NamingType.ToString());
+ NamingType tmpNamingType;
+ if (stmpNamingType.Equals("Time", StringComparison.CurrentCultureIgnoreCase))
+ {
+ tmpNamingType = NamingType.Time;
+ }
+ else if (stmpNamingType.Equals("Sequential", StringComparison.CurrentCultureIgnoreCase))
+ {
+ tmpNamingType = NamingType.Sequential;
+ }
+ else if (stmpNamingType.Equals("Overwrite", StringComparison.CurrentCultureIgnoreCase))
+ {
+ tmpNamingType = NamingType.Overwrite;
+ }
+ else
+ {
+ m_log.Warn("Unknown naming type specified for region " + sRegionLabel + ": " +
+ stmpNamingType);
+ tmpNamingType = NamingType.Time;
+ }
+
+ if (state == null && tmpNamingType != this.m_defaultState.NamingType)
+ {
+ state = new AutoBackupModuleState();
+ }
+
+ if (state != null)
+ {
+ state.NamingType = tmpNamingType;
+ }
+
+ string tmpScript = config.GetString(prepend + "AutoBackupScript",
+ this.m_defaultState.Script);
+ if (state == null && tmpScript != this.m_defaultState.Script)
+ {
+ state = new AutoBackupModuleState();
+ }
+
+ if (state != null)
+ {
+ state.Script = tmpScript;
+ }
+
+ string tmpBackupDir = config.GetString(prepend + "AutoBackupDir", ".");
+ if (state == null && tmpBackupDir != this.m_defaultState.BackupDir)
+ {
+ state = new AutoBackupModuleState();
+ }
+
+ if (state != null)
+ {
+ state.BackupDir = tmpBackupDir;
+ // Let's give the user some convenience and auto-mkdir
+ if (state.BackupDir != ".")
+ {
+ try
+ {
+ DirectoryInfo dirinfo = new DirectoryInfo(state.BackupDir);
+ if (!dirinfo.Exists)
+ {
+ dirinfo.Create();
+ }
+ }
+ catch (Exception e)
+ {
+ m_log.Warn(
+ "BAD NEWS. You won't be able to save backups to directory " +
+ state.BackupDir +
+ " because it doesn't exist or there's a permissions issue with it. Here's the exception.",
+ e);
+ }
+ }
+ }
+
+ return state;
+ }
+
+ ///
+ /// Called when any auto-backup timer expires. This starts the code path for actually performing a backup.
+ ///
+ ///
+ ///
+ private void HandleElapsed(object sender, ElapsedEventArgs e)
+ {
+ // TODO: heuristic thresholds are per-region, so we should probably run heuristics once per region
+ // XXX: Running heuristics once per region could add undue performance penalty for something that's supposed to
+ // check whether the region is too busy! Especially on sims with LOTS of regions.
+ // Alternative: make heuristics thresholds global to the module rather than per-region. Less flexible,
+ // but would allow us to be semantically correct while being easier on perf.
+ // Alternative 2: Run heuristics once per unique set of heuristics threshold parameters! Ay yi yi...
+ // Alternative 3: Don't support per-region heuristics at all; just accept them as a global only parameter.
+ // Since this is pretty experimental, I haven't decided which alternative makes the most sense.
+ if (this.m_closed)
+ {
+ return;
+ }
+ bool heuristicsRun = false;
+ bool heuristicsPassed = false;
+ if (!this.m_timerMap.ContainsKey((Timer) sender))
+ {
+ m_log.Debug("Code-up error: timerMap doesn't contain timer " + sender);
+ }
+
+ List tmap = this.m_timerMap[(Timer) sender];
+ if (tmap != null && tmap.Count > 0)
+ {
+ foreach (IScene scene in tmap)
+ {
+ AutoBackupModuleState state = this.m_states[scene];
+ bool heuristics = state.BusyCheck;
+
+ // Fast path: heuristics are on; already ran em; and sim is fine; OR, no heuristics for the region.
+ if ((heuristics && heuristicsRun && heuristicsPassed) || !heuristics)
+ {
+ this.DoRegionBackup(scene);
+ // Heuristics are on; ran but we're too busy -- keep going. Maybe another region will have heuristics off!
+ }
+ else if (heuristicsRun)
+ {
+ m_log.Info("[AUTO BACKUP]: Heuristics: too busy to backup " +
+ scene.RegionInfo.RegionName + " right now.");
+ continue;
+ // Logical Deduction: heuristics are on but haven't been run
+ }
+ else
+ {
+ heuristicsPassed = this.RunHeuristics(scene);
+ heuristicsRun = true;
+ if (!heuristicsPassed)
+ {
+ m_log.Info("[AUTO BACKUP]: Heuristics: too busy to backup " +
+ scene.RegionInfo.RegionName + " right now.");
+ continue;
+ }
+ this.DoRegionBackup(scene);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Save an OAR, register for the callback for when it's done, then call the AutoBackupScript (if applicable).
+ ///
+ ///
+ private void DoRegionBackup(IScene scene)
+ {
+ if (scene.RegionStatus != RegionStatus.Up)
+ {
+ // We won't backup a region that isn't operating normally.
+ m_log.Warn("[AUTO BACKUP]: Not backing up region " + scene.RegionInfo.RegionName +
+ " because its status is " + scene.RegionStatus);
+ return;
+ }
+
+ AutoBackupModuleState state = this.m_states[scene];
+ IRegionArchiverModule iram = scene.RequestModuleInterface();
+ string savePath = BuildOarPath(scene.RegionInfo.RegionName,
+ state.BackupDir,
+ state.NamingType);
+ if (savePath == null)
+ {
+ m_log.Warn("[AUTO BACKUP]: savePath is null in HandleElapsed");
+ return;
+ }
+ Guid guid = Guid.NewGuid();
+ m_pendingSaves.Add(guid, scene);
+ state.LiveRequests.Add(guid, savePath);
+ ((Scene) scene).EventManager.OnOarFileSaved += new EventManager.OarFileSaved(EventManager_OnOarFileSaved);
+ iram.ArchiveRegion(savePath, guid, null);
+ }
+
+ ///
+ /// Called by the Event Manager when the OnOarFileSaved event is fired.
+ ///
+ ///
+ ///
+ void EventManager_OnOarFileSaved(Guid guid, string message)
{
- get { return true; }
- }
-
- #region ISharedRegionModule Members
-
- string IRegionModuleBase.Name
- {
- get { return "AutoBackupModule"; }
- }
-
- Type IRegionModuleBase.ReplaceableInterface
- {
- get { return null; }
- }
-
- void IRegionModuleBase.Initialise(IConfigSource source)
- {
- /// Determine if we have been enabled at all in OpenSim.ini -- this is part and parcel of being an optional module
- this.m_configSource = source;
- IConfig moduleConfig = source.Configs["AutoBackupModule"];
- if (moduleConfig == null)
- {
- this.m_enabled = false;
- return;
- }
- else
- {
- this.m_enabled = moduleConfig.GetBoolean("AutoBackupModuleEnabled", false);
- if (this.m_enabled)
- {
- m_log.Info("[AUTO BACKUP]: AutoBackupModule enabled");
- }
- else
- {
- return;
- }
- }
-
- Timer defTimer = new Timer(43200000);
- this.m_defaultState.Timer = defTimer;
- this.m_timers.Add(43200000, defTimer);
- defTimer.Elapsed += this.HandleElapsed;
- defTimer.AutoReset = true;
- defTimer.Start();
-
- AutoBackupModuleState abms = this.ParseConfig(null, true);
- m_log.Debug("[AUTO BACKUP]: Here is the default config:");
- m_log.Debug(abms.ToString());
- }
-
- void IRegionModuleBase.Close()
- {
- if (!this.m_enabled)
- {
- return;
- }
-
- /// We don't want any timers firing while the sim's coming down; strange things may happen.
- this.StopAllTimers();
- }
-
- void IRegionModuleBase.AddRegion(Scene scene)
- {
- /// NO-OP. Wait for the region to be loaded.
- }
-
- void IRegionModuleBase.RemoveRegion(Scene scene)
- {
- if (!this.m_enabled)
- {
- return;
- }
-
- if (this.m_states.ContainsKey(scene))
- {
- AutoBackupModuleState abms = this.m_states[scene];
-
- /// Remove this scene out of the timer map list
- Timer timer = abms.Timer;
- List list = this.m_timerMap[timer];
- list.Remove(scene);
-
- /// Shut down the timer if this was the last scene for the timer
- if (list.Count == 0)
- {
- this.m_timerMap.Remove(timer);
- this.m_timers.Remove(timer.Interval);
- timer.Close();
- }
- this.m_states.Remove(scene);
- }
- }
-
- void IRegionModuleBase.RegionLoaded(Scene scene)
- {
- if (!this.m_enabled)
- {
- return;
- }
-
- /// This really ought not to happen, but just in case, let's pretend it didn't...
- if (scene == null)
- {
- return;
- }
-
- AutoBackupModuleState abms = this.ParseConfig(scene, false);
- m_log.Debug("[AUTO BACKUP]: Config for " + scene.RegionInfo.RegionName);
- m_log.Debug((abms == null ? "DEFAULT" : abms.ToString()));
- }
-
- void ISharedRegionModule.PostInitialise()
- {
- /// I don't care right now.
- }
-
- #endregion
-
- private AutoBackupModuleState ParseConfig(IScene scene, bool parseDefault)
- {
- string sRegionName;
- string sRegionLabel;
- string prepend;
- AutoBackupModuleState state;
-
- if (parseDefault)
- {
- sRegionName = null;
- sRegionLabel = "DEFAULT";
- prepend = "";
- state = this.m_defaultState;
- }
- else
- {
- sRegionName = scene.RegionInfo.RegionName;
- sRegionLabel = sRegionName;
- prepend = sRegionName + ".";
- state = null;
- }
-
- /// Read the config settings and set variables.
- IConfig config = this.m_configSource.Configs["AutoBackupModule"];
- if (config == null)
- {
- /// defaultState would be disabled too if the section doesn't exist.
- state = this.m_defaultState;
- m_log.Info("[AUTO BACKUP]: Region " + sRegionLabel + " is NOT AutoBackup enabled.");
- return state;
- }
-
- bool tmpEnabled = config.GetBoolean(prepend + "AutoBackup", this.m_defaultState.Enabled);
- if (state == null && tmpEnabled != this.m_defaultState.Enabled)
- //Varies from default state
- {
- state = new AutoBackupModuleState();
- }
-
- if (state != null)
- {
- state.Enabled = tmpEnabled;
- }
-
- /// If you don't want AutoBackup, we stop.
- if ((state == null && !this.m_defaultState.Enabled) || (state != null && !state.Enabled))
- {
- m_log.Info("[AUTO BACKUP]: Region " + sRegionLabel + " is NOT AutoBackup enabled.");
- return state;
- }
- else
- {
- m_log.Info("[AUTO BACKUP]: Region " + sRegionLabel + " is AutoBackup ENABLED.");
- }
-
- /// Borrow an existing timer if one exists for the same interval; otherwise, make a new one.
- double interval =
- config.GetDouble(prepend + "AutoBackupInterval", this.m_defaultState.IntervalMinutes)*
- 60000.0;
- if (state == null && interval != this.m_defaultState.IntervalMinutes*60000.0)
- {
- state = new AutoBackupModuleState();
- }
-
- if (this.m_timers.ContainsKey(interval))
- {
- if (state != null)
- {
- state.Timer = this.m_timers[interval];
- }
- m_log.Debug("[AUTO BACKUP]: Reusing timer for " + interval + " msec for region " +
- sRegionLabel);
- }
- else
- {
- /// 0 or negative interval == do nothing.
- if (interval <= 0.0 && state != null)
- {
- state.Enabled = false;
- return state;
- }
- if (state == null)
- {
- state = new AutoBackupModuleState();
- }
- Timer tim = new Timer(interval);
- state.Timer = tim;
- //Milliseconds -> minutes
- this.m_timers.Add(interval, tim);
- tim.Elapsed += this.HandleElapsed;
- tim.AutoReset = true;
- tim.Start();
- }
-
- /// Add the current region to the list of regions tied to this timer.
- if (scene != null)
- {
- if (state != null)
- {
- if (this.m_timerMap.ContainsKey(state.Timer))
- {
- this.m_timerMap[state.Timer].Add(scene);
- }
- else
- {
- List scns = new List(1);
- scns.Add(scene);
- this.m_timerMap.Add(state.Timer, scns);
- }
- }
- else
- {
- if (this.m_timerMap.ContainsKey(this.m_defaultState.Timer))
- {
- this.m_timerMap[this.m_defaultState.Timer].Add(scene);
- }
- else
- {
- List scns = new List(1);
- scns.Add(scene);
- this.m_timerMap.Add(this.m_defaultState.Timer, scns);
- }
- }
- }
-
- bool tmpBusyCheck = config.GetBoolean(prepend + "AutoBackupBusyCheck",
- this.m_defaultState.BusyCheck);
- if (state == null && tmpBusyCheck != this.m_defaultState.BusyCheck)
- {
- state = new AutoBackupModuleState();
- }
-
- if (state != null)
- {
- state.BusyCheck = tmpBusyCheck;
- }
-
- /// Set file naming algorithm
- string stmpNamingType = config.GetString(prepend + "AutoBackupNaming",
- this.m_defaultState.NamingType.ToString());
- NamingType tmpNamingType;
- if (stmpNamingType.Equals("Time", StringComparison.CurrentCultureIgnoreCase))
- {
- tmpNamingType = NamingType.Time;
- }
- else if (stmpNamingType.Equals("Sequential", StringComparison.CurrentCultureIgnoreCase))
- {
- tmpNamingType = NamingType.Sequential;
- }
- else if (stmpNamingType.Equals("Overwrite", StringComparison.CurrentCultureIgnoreCase))
- {
- tmpNamingType = NamingType.Overwrite;
- }
- else
+ // Ignore if the OAR save is being done by some other part of the system
+ if (m_pendingSaves.ContainsKey(guid))
{
- m_log.Warn("Unknown naming type specified for region " + sRegionLabel + ": " +
- stmpNamingType);
- tmpNamingType = NamingType.Time;
+ AutoBackupModuleState abms = m_states[(m_pendingSaves[guid])];
+ ExecuteScript(abms.Script, abms.LiveRequests[guid]);
+ m_pendingSaves.Remove(guid);
+ abms.LiveRequests.Remove(guid);
}
-
- if (state == null && tmpNamingType != this.m_defaultState.NamingType)
- {
- state = new AutoBackupModuleState();
- }
-
- if (state != null)
- {
- state.NamingType = tmpNamingType;
- }
-
- string tmpScript = config.GetString(prepend + "AutoBackupScript",
- this.m_defaultState.Script);
- if (state == null && tmpScript != this.m_defaultState.Script)
- {
- state = new AutoBackupModuleState();
- }
-
- if (state != null)
- {
- state.Script = tmpScript;
- }
-
- string tmpBackupDir = config.GetString(prepend + "AutoBackupDir", ".");
- if (state == null && tmpBackupDir != this.m_defaultState.BackupDir)
- {
- state = new AutoBackupModuleState();
- }
-
- if (state != null)
- {
- state.BackupDir = tmpBackupDir;
- /// Let's give the user *one* convenience and auto-mkdir
- if (state.BackupDir != ".")
- {
- try
- {
- DirectoryInfo dirinfo = new DirectoryInfo(state.BackupDir);
- if (!dirinfo.Exists)
- {
- dirinfo.Create();
- }
- }
- catch (Exception e)
- {
- m_log.Warn(
- "BAD NEWS. You won't be able to save backups to directory " +
- state.BackupDir +
- " because it doesn't exist or there's a permissions issue with it. Here's the exception.",
- e);
- }
- }
- }
-
- return state;
- }
-
- private void HandleElapsed(object sender, ElapsedEventArgs e)
- {
- /// TODO?: heuristic thresholds are per-region, so we should probably run heuristics once per region
- /// XXX: Running heuristics once per region could add undue performance penalty for something that's supposed to
- /// check whether the region is too busy! Especially on sims with LOTS of regions.
- /// Alternative: make heuristics thresholds global to the module rather than per-region. Less flexible,
- /// but would allow us to be semantically correct while being easier on perf.
- /// Alternative 2: Run heuristics once per unique set of heuristics threshold parameters! Ay yi yi...
- if (this.m_closed)
- {
- return;
- }
- bool heuristicsRun = false;
- bool heuristicsPassed = false;
- if (!this.m_timerMap.ContainsKey((Timer) sender))
- {
- m_log.Debug("Code-up error: timerMap doesn't contain timer " + sender);
- }
-
- List tmap = this.m_timerMap[(Timer) sender];
- if (tmap != null && tmap.Count > 0)
- {
- foreach (IScene scene in tmap)
- {
- AutoBackupModuleState state = this.m_states[scene];
- bool heuristics = state.BusyCheck;
-
- /// Fast path: heuristics are on; already ran em; and sim is fine; OR, no heuristics for the region.
- if ((heuristics && heuristicsRun && heuristicsPassed) || !heuristics)
- {
- this.DoRegionBackup(scene);
- /// Heuristics are on; ran but we're too busy -- keep going. Maybe another region will have heuristics off!
- }
- else if (heuristicsRun)
- {
- m_log.Info("[AUTO BACKUP]: Heuristics: too busy to backup " +
- scene.RegionInfo.RegionName + " right now.");
- continue;
- /// Logical Deduction: heuristics are on but haven't been run
- }
- else
- {
- heuristicsPassed = this.RunHeuristics(scene);
- heuristicsRun = true;
- if (!heuristicsPassed)
- {
- m_log.Info("[AUTO BACKUP]: Heuristics: too busy to backup " +
- scene.RegionInfo.RegionName + " right now.");
- continue;
- }
- this.DoRegionBackup(scene);
- }
- }
- }
- }
-
- private void DoRegionBackup(IScene scene)
- {
- if (scene.RegionStatus != RegionStatus.Up)
- {
- /// We won't backup a region that isn't operating normally.
- m_log.Warn("[AUTO BACKUP]: Not backing up region " + scene.RegionInfo.RegionName +
- " because its status is " + scene.RegionStatus);
- return;
- }
-
- AutoBackupModuleState state = this.m_states[scene];
- IRegionArchiverModule iram = scene.RequestModuleInterface();
- string savePath = BuildOarPath(scene.RegionInfo.RegionName,
- state.BackupDir,
- state.NamingType);
- /// m_log.Debug("[AUTO BACKUP]: savePath = " + savePath);
- if (savePath == null)
- {
- m_log.Warn("[AUTO BACKUP]: savePath is null in HandleElapsed");
- return;
- }
- Guid guid = Guid.NewGuid();
- m_pendingSaves.Add(guid, scene);
- state.LiveRequests.Add(guid, savePath);
- ((Scene) scene).EventManager.OnOarFileSaved += new EventManager.OarFileSaved(EventManager_OnOarFileSaved);
- iram.ArchiveRegion(savePath, guid, null);
- }
-
- void EventManager_OnOarFileSaved(Guid guid, string message)
- {
- AutoBackupModuleState abms = m_states[(m_pendingSaves[guid])];
- ExecuteScript(abms.Script, abms.LiveRequests[guid]);
- m_pendingSaves.Remove(guid);
- abms.LiveRequests.Remove(guid);
- }
-
- /// This format may turn out to be too unwieldy to keep...
+ }
+
+ /// This format may turn out to be too unwieldy to keep...
/// Besides, that's what ctimes are for. But then how do I name each file uniquely without using a GUID?
- /// Sequential numbers, right? Ugh. Almost makes TOO much sense.
- private static string GetTimeString()
- {
- StringWriter sw = new StringWriter();
- sw.Write("_");
- DateTime now = DateTime.Now;
- sw.Write(now.Year);
- sw.Write("y_");
- sw.Write(now.Month);
- sw.Write("M_");
- sw.Write(now.Day);
- sw.Write("d_");
- sw.Write(now.Hour);
- sw.Write("h_");
- sw.Write(now.Minute);
- sw.Write("m_");
- sw.Write(now.Second);
- sw.Write("s");
- sw.Flush();
- string output = sw.ToString();
- sw.Close();
- return output;
- }
-
- ///
- /// Return value of true ==> not too busy; false ==> too busy to backup an OAR right now, or error.
- ///
- private bool RunHeuristics(IScene region)
- {
- try
- {
- return this.RunTimeDilationHeuristic(region) && this.RunAgentLimitHeuristic(region);
- }
- catch (Exception e)
- {
- m_log.Warn("[AUTO BACKUP]: Exception in RunHeuristics", e);
- return false;
- }
- }
-
- ///
+ /// Sequential numbers, right? We support those, too!
+ private static string GetTimeString()
+ {
+ StringWriter sw = new StringWriter();
+ sw.Write("_");
+ DateTime now = DateTime.Now;
+ sw.Write(now.Year);
+ sw.Write("y_");
+ sw.Write(now.Month);
+ sw.Write("M_");
+ sw.Write(now.Day);
+ sw.Write("d_");
+ sw.Write(now.Hour);
+ sw.Write("h_");
+ sw.Write(now.Minute);
+ sw.Write("m_");
+ sw.Write(now.Second);
+ sw.Write("s");
+ sw.Flush();
+ string output = sw.ToString();
+ sw.Close();
+ return output;
+ }
+
+ /// Return value of true ==> not too busy; false ==> too busy to backup an OAR right now, or error.
+ private bool RunHeuristics(IScene region)
+ {
+ try
+ {
+ return this.RunTimeDilationHeuristic(region) && this.RunAgentLimitHeuristic(region);
+ }
+ catch (Exception e)
+ {
+ m_log.Warn("[AUTO BACKUP]: Exception in RunHeuristics", e);
+ return false;
+ }
+ }
+
+ ///
/// If the time dilation right at this instant is less than the threshold specified in AutoBackupDilationThreshold (default 0.5),
/// then we return false and trip the busy heuristic's "too busy" path (i.e. don't save an OAR).
/// AutoBackupDilationThreshold is a _LOWER BOUND_. Lower Time Dilation is bad, so if you go lower than our threshold, it's "too busy".
- /// Return value of "true" ==> not too busy. Return value of "false" ==> too busy!
- ///
- private bool RunTimeDilationHeuristic(IScene region)
- {
- string regionName = region.RegionInfo.RegionName;
- return region.TimeDilation >=
- this.m_configSource.Configs["AutoBackupModule"].GetFloat(
- regionName + ".AutoBackupDilationThreshold", 0.5f);
- }
-
- ///
+ ///
+ ///
+ /// Returns true if we're not too busy; false means we've got worse time dilation than the threshold.
+ private bool RunTimeDilationHeuristic(IScene region)
+ {
+ string regionName = region.RegionInfo.RegionName;
+ return region.TimeDilation >=
+ this.m_configSource.Configs["AutoBackupModule"].GetFloat(
+ regionName + ".AutoBackupDilationThreshold", 0.5f);
+ }
+
+ ///
/// If the root agent count right at this instant is less than the threshold specified in AutoBackupAgentThreshold (default 10),
/// then we return false and trip the busy heuristic's "too busy" path (i.e., don't save an OAR).
/// AutoBackupAgentThreshold is an _UPPER BOUND_. Higher Agent Count is bad, so if you go higher than our threshold, it's "too busy".
- /// Return value of "true" ==> not too busy. Return value of "false" ==> too busy!
- ///
- private bool RunAgentLimitHeuristic(IScene region)
- {
- string regionName = region.RegionInfo.RegionName;
- try
- {
- Scene scene = (Scene) region;
- /// TODO: Why isn't GetRootAgentCount() a method in the IScene interface? Seems generally useful...
- return scene.GetRootAgentCount() <=
- this.m_configSource.Configs["AutoBackupModule"].GetInt(
- regionName + ".AutoBackupAgentThreshold", 10);
- }
- catch (InvalidCastException ice)
- {
- m_log.Debug(
- "[AUTO BACKUP]: I NEED MAINTENANCE: IScene is not a Scene; can't get root agent count!",
- ice);
- return true;
- /// Non-obstructionist safest answer...
- }
- }
-
- private static void ExecuteScript(string scriptName, string savePath)
- {
- //Fast path out
- if (scriptName == null || scriptName.Length <= 0)
- {
- return;
- }
-
- try
- {
- FileInfo fi = new FileInfo(scriptName);
- if (fi.Exists)
- {
- ProcessStartInfo psi = new ProcessStartInfo(scriptName);
- psi.Arguments = savePath;
- psi.CreateNoWindow = true;
- Process proc = Process.Start(psi);
- proc.ErrorDataReceived += HandleProcErrorDataReceived;
- }
- }
- catch (Exception e)
- {
- m_log.Warn(
- "Exception encountered when trying to run script for oar backup " + savePath, e);
- }
- }
-
- private static void HandleProcErrorDataReceived(object sender, DataReceivedEventArgs e)
- {
- m_log.Warn("ExecuteScript hook " + ((Process) sender).ProcessName +
- " is yacking on stderr: " + e.Data);
- }
-
- private void StopAllTimers()
- {
- foreach (Timer t in this.m_timerMap.Keys)
- {
- t.Close();
- }
- this.m_closed = true;
- }
-
- private static string GetNextFile(string dirName, string regionName)
- {
- FileInfo uniqueFile = null;
- long biggestExistingFile = GetNextOarFileNumber(dirName, regionName);
- biggestExistingFile++;
- //We don't want to overwrite the biggest existing file; we want to write to the NEXT biggest.
- uniqueFile =
- new FileInfo(dirName + Path.DirectorySeparatorChar + regionName + "_" +
- biggestExistingFile + ".oar");
- return uniqueFile.FullName;
- }
-
- private static string BuildOarPath(string regionName, string baseDir, NamingType naming)
- {
- FileInfo path = null;
- switch (naming)
- {
- case NamingType.Overwrite:
- path = new FileInfo(baseDir + Path.DirectorySeparatorChar + regionName + ".oar");
- return path.FullName;
- case NamingType.Time:
- path =
- new FileInfo(baseDir + Path.DirectorySeparatorChar + regionName +
- GetTimeString() + ".oar");
- return path.FullName;
- case NamingType.Sequential:
- /// All codepaths in GetNextFile should return a file name ending in .oar
- path = new FileInfo(GetNextFile(baseDir, regionName));
- return path.FullName;
- default:
- m_log.Warn("VERY BAD: Unhandled case element " + naming);
- break;
- }
-
- return null;
- }
-
- private static long GetNextOarFileNumber(string dirName, string regionName)
- {
- long retval = 1;
-
- DirectoryInfo di = new DirectoryInfo(dirName);
- FileInfo[] fi = di.GetFiles(regionName, SearchOption.TopDirectoryOnly);
- Array.Sort(fi, (f1, f2) => StringComparer.CurrentCultureIgnoreCase.Compare(f1.Name, f2.Name));
-
- if (fi.LongLength > 0)
- {
- long subtract = 1L;
- bool worked = false;
- Regex reg = new Regex(regionName + "_([0-9])+" + ".oar");
-
- while (!worked && subtract <= fi.LongLength)
- {
- /// Pick the file with the last natural ordering
- string biggestFileName = fi[fi.LongLength - subtract].Name;
- MatchCollection matches = reg.Matches(biggestFileName);
- long l = 1;
- if (matches.Count > 0 && matches[0].Groups.Count > 0)
- {
- try
- {
- long.TryParse(matches[0].Groups[1].Value, out l);
- retval = l;
- worked = true;
- }
- catch (FormatException fe)
- {
- m_log.Warn(
- "[AUTO BACKUP]: Error: Can't parse long value from file name to determine next OAR backup file number!",
- fe);
- subtract++;
- }
- }
- else
- {
- subtract++;
- }
- }
- }
- return retval;
- }
- }
-}
-
-
+ ///
+ ///
+ /// Returns true if we're not too busy; false means we've got more agents on the sim than the threshold.
+ private bool RunAgentLimitHeuristic(IScene region)
+ {
+ string regionName = region.RegionInfo.RegionName;
+ try
+ {
+ Scene scene = (Scene) region;
+ // TODO: Why isn't GetRootAgentCount() a method in the IScene interface? Seems generally useful...
+ return scene.GetRootAgentCount() <=
+ this.m_configSource.Configs["AutoBackupModule"].GetInt(
+ regionName + ".AutoBackupAgentThreshold", 10);
+ }
+ catch (InvalidCastException ice)
+ {
+ m_log.Debug(
+ "[AUTO BACKUP]: I NEED MAINTENANCE: IScene is not a Scene; can't get root agent count!",
+ ice);
+ return true;
+ // Non-obstructionist safest answer...
+ }
+ }
+
+ ///
+ /// Run the script or executable specified by the "AutoBackupScript" config setting.
+ /// Of course this is a security risk if you let anyone modify OpenSim.ini and they want to run some nasty bash script.
+ /// But there are plenty of other nasty things that can be done with an untrusted OpenSim.ini, such as running high threat level scripting functions.
+ ///
+ ///
+ ///
+ private static void ExecuteScript(string scriptName, string savePath)
+ {
+ // Do nothing if there's no script.
+ if (scriptName == null || scriptName.Length <= 0)
+ {
+ return;
+ }
+
+ try
+ {
+ FileInfo fi = new FileInfo(scriptName);
+ if (fi.Exists)
+ {
+ ProcessStartInfo psi = new ProcessStartInfo(scriptName);
+ psi.Arguments = savePath;
+ psi.CreateNoWindow = true;
+ Process proc = Process.Start(psi);
+ proc.ErrorDataReceived += HandleProcErrorDataReceived;
+ }
+ }
+ catch (Exception e)
+ {
+ m_log.Warn(
+ "Exception encountered when trying to run script for oar backup " + savePath, e);
+ }
+ }
+
+ ///
+ /// Called if a running script process writes to stderr.
+ ///
+ ///
+ ///
+ private static void HandleProcErrorDataReceived(object sender, DataReceivedEventArgs e)
+ {
+ m_log.Warn("ExecuteScript hook " + ((Process) sender).ProcessName +
+ " is yacking on stderr: " + e.Data);
+ }
+
+ ///
+ /// Quickly stop all timers from firing.
+ ///
+ private void StopAllTimers()
+ {
+ foreach (Timer t in this.m_timerMap.Keys)
+ {
+ t.Close();
+ }
+ this.m_closed = true;
+ }
+
+ ///
+ /// Determine the next unique filename by number, for "Sequential" AutoBackupNamingType.
+ ///
+ ///
+ ///
+ ///
+ private static string GetNextFile(string dirName, string regionName)
+ {
+ FileInfo uniqueFile = null;
+ long biggestExistingFile = GetNextOarFileNumber(dirName, regionName);
+ biggestExistingFile++;
+ // We don't want to overwrite the biggest existing file; we want to write to the NEXT biggest.
+ uniqueFile =
+ new FileInfo(dirName + Path.DirectorySeparatorChar + regionName + "_" +
+ biggestExistingFile + ".oar");
+ return uniqueFile.FullName;
+ }
+
+ ///
+ /// Top-level method for creating an absolute path to an OAR backup file based on what naming scheme the user wants.
+ ///
+ /// Name of the region to save.
+ /// Absolute or relative path to the directory where the file should reside.
+ /// The naming scheme for the file name.
+ ///
+ private static string BuildOarPath(string regionName, string baseDir, NamingType naming)
+ {
+ FileInfo path = null;
+ switch (naming)
+ {
+ case NamingType.Overwrite:
+ path = new FileInfo(baseDir + Path.DirectorySeparatorChar + regionName + ".oar");
+ return path.FullName;
+ case NamingType.Time:
+ path =
+ new FileInfo(baseDir + Path.DirectorySeparatorChar + regionName +
+ GetTimeString() + ".oar");
+ return path.FullName;
+ case NamingType.Sequential:
+ // All codepaths in GetNextFile should return a file name ending in .oar
+ path = new FileInfo(GetNextFile(baseDir, regionName));
+ return path.FullName;
+ default:
+ m_log.Warn("VERY BAD: Unhandled case element " + naming);
+ break;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Helper function for Sequential file naming type (see BuildOarPath and GetNextFile).
+ ///
+ ///
+ ///
+ ///
+ private static long GetNextOarFileNumber(string dirName, string regionName)
+ {
+ long retval = 1;
+
+ DirectoryInfo di = new DirectoryInfo(dirName);
+ FileInfo[] fi = di.GetFiles(regionName, SearchOption.TopDirectoryOnly);
+ Array.Sort(fi, (f1, f2) => StringComparer.CurrentCultureIgnoreCase.Compare(f1.Name, f2.Name));
+
+ if (fi.LongLength > 0)
+ {
+ long subtract = 1L;
+ bool worked = false;
+ Regex reg = new Regex(regionName + "_([0-9])+" + ".oar");
+
+ while (!worked && subtract <= fi.LongLength)
+ {
+ // Pick the file with the last natural ordering
+ string biggestFileName = fi[fi.LongLength - subtract].Name;
+ MatchCollection matches = reg.Matches(biggestFileName);
+ long l = 1;
+ if (matches.Count > 0 && matches[0].Groups.Count > 0)
+ {
+ try
+ {
+ long.TryParse(matches[0].Groups[1].Value, out l);
+ retval = l;
+ worked = true;
+ }
+ catch (FormatException fe)
+ {
+ m_log.Warn(
+ "[AUTO BACKUP]: Error: Can't parse long value from file name to determine next OAR backup file number!",
+ fe);
+ subtract++;
+ }
+ }
+ else
+ {
+ subtract++;
+ }
+ }
+ }
+ return retval;
+ }
+ }
+}
+
+
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs
index 7fecfa4..2db718c 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModuleState.cs
@@ -1,30 +1,29 @@
-#pragma warning disable 1587
-///
-/// 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.
-///
+/*
+ * 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;
@@ -32,7 +31,11 @@ using System.Collections.Generic;
namespace OpenSim.Region.OptionalModules.World.AutoBackup
{
- /// AutoBackupModuleState: Auto-Backup state for one region (scene).
+ /// AutoBackupModuleState: Auto-Backup state for one region (scene).
+ /// If you use this class in any way outside of AutoBackupModule, you should treat the class as opaque.
+ /// Since it is not part of the framework, you really should not rely upon it outside of the AutoBackupModule implementation.
+ ///
+ ///
public class AutoBackupModuleState
{
private Dictionary m_liveRequests = null;
--
cgit v1.1
From 0995fedcaca9a921488929ee40f68c71fbba7a70 Mon Sep 17 00:00:00 2001
From: Sean McNamara
Date: Mon, 2 May 2011 04:32:31 -0400
Subject: AutoBackupModule: Implement per-region settings in Regions.ini.
---
.../World/AutoBackup/AutoBackupModule.cs | 291 +++++++++++++--------
1 file changed, 186 insertions(+), 105 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index 4a9615d..ce9a448 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -55,7 +55,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
///
/// AutoBackupModule: save OAR region backups to disk periodically
- ///
+ ///
///
/// Config Settings Documentation.
/// At the TOP LEVEL, e.g. in OpenSim.ini, we have the following options:
@@ -96,7 +96,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
/// AutoBackupAgentThreshold: int. Default: 10. Upper bound on # of agents in region required for BusyCheck heuristics to pass.
/// If the number of agents is greater than this value, don't take a backup right now
/// Save memory by setting low initial capacities. Minimizes impact in common cases of all regions using same interval, and instances hosting 1 ~ 4 regions.
- /// Also helps if you don't want AutoBackup at all.
+ /// Also helps if you don't want AutoBackup at all.
///
public class AutoBackupModule : ISharedRegionModule
{
@@ -110,17 +110,18 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
new Dictionary>(1);
private readonly Dictionary m_timers = new Dictionary(1);
+ private delegate T DefaultGetter(string settingName, T defaultValue);
private bool m_enabled;
- ///
- /// Whether the shared module should be enabled at all. NOT the same as m_Enabled in AutoBackupModuleState!
+ ///
+ /// Whether the shared module should be enabled at all. NOT the same as m_Enabled in AutoBackupModuleState!
///
private bool m_closed;
private IConfigSource m_configSource;
- ///
- /// Required by framework.
+ ///
+ /// Required by framework.
///
public bool IsSharedModule
{
@@ -129,25 +130,25 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
#region ISharedRegionModule Members
- ///
- /// Identifies the module to the system.
+ ///
+ /// Identifies the module to the system.
///
string IRegionModuleBase.Name
{
get { return "AutoBackupModule"; }
}
- ///
- /// We don't implement an interface, this is a single-use module.
+ ///
+ /// We don't implement an interface, this is a single-use module.
///
Type IRegionModuleBase.ReplaceableInterface
{
get { return null; }
}
- ///
- /// Called once in the lifetime of the module at startup.
- ///
+ ///
+ /// Called once in the lifetime of the module at startup.
+ ///
/// The input config source for OpenSim.ini.
void IRegionModuleBase.Initialise(IConfigSource source)
{
@@ -184,8 +185,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
m_log.Debug(abms.ToString());
}
- ///
- /// Called once at de-init (sim shutting down).
+ ///
+ /// Called once at de-init (sim shutting down).
///
void IRegionModuleBase.Close()
{
@@ -198,17 +199,17 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
this.StopAllTimers();
}
- ///
- /// Currently a no-op for AutoBackup because we have to wait for region to be fully loaded.
- ///
+ ///
+ /// Currently a no-op for AutoBackup because we have to wait for region to be fully loaded.
+ ///
///
void IRegionModuleBase.AddRegion(Scene scene)
{
}
- ///
- /// Here we just clean up some resources and stop the OAR backup (if any) for the given scene.
- ///
+ ///
+ /// Here we just clean up some resources and stop the OAR backup (if any) for the given scene.
+ ///
/// The scene (region) to stop performing AutoBackup on.
void IRegionModuleBase.RemoveRegion(Scene scene)
{
@@ -237,10 +238,10 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
}
}
- ///
- /// Most interesting/complex code paths in AutoBackup begin here.
- /// We read lots of Nini config, maybe set a timer, add members to state tracking Dictionaries, etc.
- ///
+ ///
+ /// Most interesting/complex code paths in AutoBackup begin here.
+ /// We read lots of Nini config, maybe set a timer, add members to state tracking Dictionaries, etc.
+ ///
/// The scene to (possibly) perform AutoBackup on.
void IRegionModuleBase.RegionLoaded(Scene scene)
{
@@ -260,8 +261,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
m_log.Debug((abms == null ? "DEFAULT" : abms.ToString()));
}
- ///
- /// Currently a no-op.
+ ///
+ /// Currently a no-op.
///
void ISharedRegionModule.PostInitialise()
{
@@ -269,12 +270,12 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
#endregion
- ///
- /// Set up internal state for a given scene. Fairly complex code.
- /// When this method returns, we've started auto-backup timers, put members in Dictionaries, and created a State object for this scene.
- ///
- /// The scene to look at.
- /// Whether this call is intended to figure out what we consider the "default" config (applied to all regions unless overridden by per-region settings).
+ ///
+ /// Set up internal state for a given scene. Fairly complex code.
+ /// When this method returns, we've started auto-backup timers, put members in Dictionaries, and created a State object for this scene.
+ ///
+ /// The scene to look at.
+ /// Whether this call is intended to figure out what we consider the "default" config (applied to all regions unless overridden by per-region settings).
/// An AutoBackupModuleState contains most information you should need to know relevant to auto-backup, as applicable to a single region.
private AutoBackupModuleState ParseConfig(IScene scene, bool parseDefault)
{
@@ -299,16 +300,16 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
}
// Read the config settings and set variables.
+ IConfig regionConfig = (scene != null ? scene.Config.Configs[sRegionName] : null);
IConfig config = this.m_configSource.Configs["AutoBackupModule"];
if (config == null)
{
// defaultState would be disabled too if the section doesn't exist.
state = this.m_defaultState;
- m_log.Info("[AUTO BACKUP]: Region " + sRegionLabel + " is NOT AutoBackup enabled.");
return state;
}
- bool tmpEnabled = config.GetBoolean(prepend + "AutoBackup", this.m_defaultState.Enabled);
+ bool tmpEnabled = ResolveBoolean("AutoBackup", this.m_defaultState.Enabled, config, regionConfig);
if (state == null && tmpEnabled != this.m_defaultState.Enabled)
//Varies from default state
{
@@ -332,8 +333,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
// Borrow an existing timer if one exists for the same interval; otherwise, make a new one.
double interval =
- config.GetDouble(prepend + "AutoBackupInterval", this.m_defaultState.IntervalMinutes)*
- 60000.0;
+ this.ResolveDouble("AutoBackupInterval", this.m_defaultState.IntervalMinutes,
+ config, regionConfig) * 60000.0;
if (state == null && interval != this.m_defaultState.IntervalMinutes*60000.0)
{
state = new AutoBackupModuleState();
@@ -400,8 +401,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
}
}
- bool tmpBusyCheck = config.GetBoolean(prepend + "AutoBackupBusyCheck",
- this.m_defaultState.BusyCheck);
+ bool tmpBusyCheck = ResolveBoolean("AutoBackupBusyCheck",
+ this.m_defaultState.BusyCheck, config, regionConfig);
if (state == null && tmpBusyCheck != this.m_defaultState.BusyCheck)
{
state = new AutoBackupModuleState();
@@ -413,8 +414,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
}
// Set file naming algorithm
- string stmpNamingType = config.GetString(prepend + "AutoBackupNaming",
- this.m_defaultState.NamingType.ToString());
+ string stmpNamingType = ResolveString("AutoBackupNaming",
+ this.m_defaultState.NamingType.ToString(), config, regionConfig);
NamingType tmpNamingType;
if (stmpNamingType.Equals("Time", StringComparison.CurrentCultureIgnoreCase))
{
@@ -445,8 +446,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
state.NamingType = tmpNamingType;
}
- string tmpScript = config.GetString(prepend + "AutoBackupScript",
- this.m_defaultState.Script);
+ string tmpScript = ResolveString("AutoBackupScript",
+ this.m_defaultState.Script, config, regionConfig);
if (state == null && tmpScript != this.m_defaultState.Script)
{
state = new AutoBackupModuleState();
@@ -457,7 +458,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
state.Script = tmpScript;
}
- string tmpBackupDir = config.GetString(prepend + "AutoBackupDir", ".");
+ string tmpBackupDir = ResolveString("AutoBackupDir", ".", config, regionConfig);
if (state == null && tmpBackupDir != this.m_defaultState.BackupDir)
{
state = new AutoBackupModuleState();
@@ -491,10 +492,90 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
return state;
}
- ///
- /// Called when any auto-backup timer expires. This starts the code path for actually performing a backup.
- ///
- ///
+ ///
+ /// Helper function for ParseConfig.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private bool ResolveBoolean(string settingName, bool defaultValue, IConfig global, IConfig local)
+ {
+ if(local != null)
+ {
+ return local.GetBoolean(settingName, global.GetBoolean(settingName, defaultValue));
+ }
+ else
+ {
+ return global.GetBoolean(settingName, defaultValue);
+ }
+ }
+
+ ///
+ /// Helper function for ParseConfig.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private double ResolveDouble(string settingName, double defaultValue, IConfig global, IConfig local)
+ {
+ if (local != null)
+ {
+ return local.GetDouble(settingName, global.GetDouble(settingName, defaultValue));
+ }
+ else
+ {
+ return global.GetDouble(settingName, defaultValue);
+ }
+ }
+
+ ///
+ /// Helper function for ParseConfig.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private int ResolveInt(string settingName, int defaultValue, IConfig global, IConfig local)
+ {
+ if (local != null)
+ {
+ return local.GetInt(settingName, global.GetInt(settingName, defaultValue));
+ }
+ else
+ {
+ return global.GetInt(settingName, defaultValue);
+ }
+ }
+
+ ///
+ /// Helper function for ParseConfig.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private string ResolveString(string settingName, string defaultValue, IConfig global, IConfig local)
+ {
+ if (local != null)
+ {
+ return local.GetString(settingName, global.GetString(settingName, defaultValue));
+ }
+ else
+ {
+ return global.GetString(settingName, defaultValue);
+ }
+ }
+
+ ///
+ /// Called when any auto-backup timer expires. This starts the code path for actually performing a backup.
+ ///
+ ///
///
private void HandleElapsed(object sender, ElapsedEventArgs e)
{
@@ -554,9 +635,9 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
}
}
- ///
- /// Save an OAR, register for the callback for when it's done, then call the AutoBackupScript (if applicable).
- ///
+ ///
+ /// Save an OAR, register for the callback for when it's done, then call the AutoBackupScript (if applicable).
+ ///
///
private void DoRegionBackup(IScene scene)
{
@@ -585,25 +666,25 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
iram.ArchiveRegion(savePath, guid, null);
}
- ///
- /// Called by the Event Manager when the OnOarFileSaved event is fired.
- ///
- ///
+ ///
+ /// Called by the Event Manager when the OnOarFileSaved event is fired.
+ ///
+ ///
///
void EventManager_OnOarFileSaved(Guid guid, string message)
- {
- // Ignore if the OAR save is being done by some other part of the system
- if (m_pendingSaves.ContainsKey(guid))
- {
- AutoBackupModuleState abms = m_states[(m_pendingSaves[guid])];
- ExecuteScript(abms.Script, abms.LiveRequests[guid]);
- m_pendingSaves.Remove(guid);
- abms.LiveRequests.Remove(guid);
- }
+ {
+ // Ignore if the OAR save is being done by some other part of the system
+ if (m_pendingSaves.ContainsKey(guid))
+ {
+ AutoBackupModuleState abms = m_states[(m_pendingSaves[guid])];
+ ExecuteScript(abms.Script, abms.LiveRequests[guid]);
+ m_pendingSaves.Remove(guid);
+ abms.LiveRequests.Remove(guid);
+ }
}
/// This format may turn out to be too unwieldy to keep...
- /// Besides, that's what ctimes are for. But then how do I name each file uniquely without using a GUID?
+ /// Besides, that's what ctimes are for. But then how do I name each file uniquely without using a GUID?
/// Sequential numbers, right? We support those, too!
private static string GetTimeString()
{
@@ -642,12 +723,12 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
}
}
- ///
- /// If the time dilation right at this instant is less than the threshold specified in AutoBackupDilationThreshold (default 0.5),
- /// then we return false and trip the busy heuristic's "too busy" path (i.e. don't save an OAR).
- /// AutoBackupDilationThreshold is a _LOWER BOUND_. Lower Time Dilation is bad, so if you go lower than our threshold, it's "too busy".
- ///
- ///
+ ///
+ /// If the time dilation right at this instant is less than the threshold specified in AutoBackupDilationThreshold (default 0.5),
+ /// then we return false and trip the busy heuristic's "too busy" path (i.e. don't save an OAR).
+ /// AutoBackupDilationThreshold is a _LOWER BOUND_. Lower Time Dilation is bad, so if you go lower than our threshold, it's "too busy".
+ ///
+ ///
/// Returns true if we're not too busy; false means we've got worse time dilation than the threshold.
private bool RunTimeDilationHeuristic(IScene region)
{
@@ -657,12 +738,12 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
regionName + ".AutoBackupDilationThreshold", 0.5f);
}
- ///
- /// If the root agent count right at this instant is less than the threshold specified in AutoBackupAgentThreshold (default 10),
- /// then we return false and trip the busy heuristic's "too busy" path (i.e., don't save an OAR).
- /// AutoBackupAgentThreshold is an _UPPER BOUND_. Higher Agent Count is bad, so if you go higher than our threshold, it's "too busy".
- ///
- ///
+ ///
+ /// If the root agent count right at this instant is less than the threshold specified in AutoBackupAgentThreshold (default 10),
+ /// then we return false and trip the busy heuristic's "too busy" path (i.e., don't save an OAR).
+ /// AutoBackupAgentThreshold is an _UPPER BOUND_. Higher Agent Count is bad, so if you go higher than our threshold, it's "too busy".
+ ///
+ ///
/// Returns true if we're not too busy; false means we've got more agents on the sim than the threshold.
private bool RunAgentLimitHeuristic(IScene region)
{
@@ -685,12 +766,12 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
}
}
- ///
- /// Run the script or executable specified by the "AutoBackupScript" config setting.
- /// Of course this is a security risk if you let anyone modify OpenSim.ini and they want to run some nasty bash script.
- /// But there are plenty of other nasty things that can be done with an untrusted OpenSim.ini, such as running high threat level scripting functions.
- ///
- ///
+ ///
+ /// Run the script or executable specified by the "AutoBackupScript" config setting.
+ /// Of course this is a security risk if you let anyone modify OpenSim.ini and they want to run some nasty bash script.
+ /// But there are plenty of other nasty things that can be done with an untrusted OpenSim.ini, such as running high threat level scripting functions.
+ ///
+ ///
///
private static void ExecuteScript(string scriptName, string savePath)
{
@@ -719,10 +800,10 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
}
}
- ///
- /// Called if a running script process writes to stderr.
- ///
- ///
+ ///
+ /// Called if a running script process writes to stderr.
+ ///
+ ///
///
private static void HandleProcErrorDataReceived(object sender, DataReceivedEventArgs e)
{
@@ -730,8 +811,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
" is yacking on stderr: " + e.Data);
}
- ///
- /// Quickly stop all timers from firing.
+ ///
+ /// Quickly stop all timers from firing.
///
private void StopAllTimers()
{
@@ -742,11 +823,11 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
this.m_closed = true;
}
- ///
- /// Determine the next unique filename by number, for "Sequential" AutoBackupNamingType.
- ///
- ///
- ///
+ ///
+ /// Determine the next unique filename by number, for "Sequential" AutoBackupNamingType.
+ ///
+ ///
+ ///
///
private static string GetNextFile(string dirName, string regionName)
{
@@ -760,12 +841,12 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
return uniqueFile.FullName;
}
- ///
- /// Top-level method for creating an absolute path to an OAR backup file based on what naming scheme the user wants.
- ///
- /// Name of the region to save.
- /// Absolute or relative path to the directory where the file should reside.
- /// The naming scheme for the file name.
+ ///
+ /// Top-level method for creating an absolute path to an OAR backup file based on what naming scheme the user wants.
+ ///
+ /// Name of the region to save.
+ /// Absolute or relative path to the directory where the file should reside.
+ /// The naming scheme for the file name.
///
private static string BuildOarPath(string regionName, string baseDir, NamingType naming)
{
@@ -792,11 +873,11 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
return null;
}
- ///
- /// Helper function for Sequential file naming type (see BuildOarPath and GetNextFile).
- ///
- ///
- ///
+ ///
+ /// Helper function for Sequential file naming type (see BuildOarPath and GetNextFile).
+ ///
+ ///
+ ///
///
private static long GetNextOarFileNumber(string dirName, string regionName)
{
--
cgit v1.1
From 8755a48cde6ee77f421bef07e8b95cf8b68a76ed Mon Sep 17 00:00:00 2001
From: Justin Clark-Casey (justincc)
Date: Fri, 6 May 2011 00:34:04 +0100
Subject: fix command display for debugging 'emergency-monitoring'
---
OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs b/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs
index db17d8f..bdebbfb 100644
--- a/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs
+++ b/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs
@@ -107,7 +107,7 @@ namespace OpenSim.Region.CoreModules.UDP.Linden
scene.AddCommand(
this, "emergency-monitoring",
- "Go on/off emergency monitoring mode",
+ "emergency-monitoring",
"Go on/off emergency monitoring mode",
"Go on/off emergency monitoring mode",
EmergencyMonitoring);
--
cgit v1.1
From 8ca793875318efc8db3339b25bf7fa5ddeeac218 Mon Sep 17 00:00:00 2001
From: BlueWall
Date: Sun, 1 May 2011 14:44:09 -0400
Subject: Adding ssl support
Adding ssl support for "Out of Band" applications such as the remote
admin module or Robust services
---
OpenSim/Region/ClientStack/RegionApplicationBase.cs | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/ClientStack/RegionApplicationBase.cs b/OpenSim/Region/ClientStack/RegionApplicationBase.cs
index ea1317a..6e3a58e 100644
--- a/OpenSim/Region/ClientStack/RegionApplicationBase.cs
+++ b/OpenSim/Region/ClientStack/RegionApplicationBase.cs
@@ -96,6 +96,22 @@ namespace OpenSim.Region.ClientStack
MainServer.Instance = m_httpServer;
+ // "OOB" Server
+ if (m_networkServersInfo.ssl_listener)
+ {
+ BaseHttpServer server = null;
+ server = new BaseHttpServer(
+ m_networkServersInfo.https_port, m_networkServersInfo.ssl_listener, m_networkServersInfo.cert_path,
+ m_networkServersInfo.cert_pass);
+ // Add the server to m_Servers
+ if(server != null)
+ {
+ m_log.InfoFormat("[REGION SERVER]: Starting HTTPS server on port {0}", server.Port);
+ MainServer.AddHttpServer(server);
+ server.Start();
+ }
+ }
+
base.StartupSpecific();
}
--
cgit v1.1
From e4e95706d52fad2bab4a725955449f6bdb523a29 Mon Sep 17 00:00:00 2001
From: BlueWall
Date: Mon, 2 May 2011 14:35:44 -0400
Subject: Add support for llRequestSecureURL() if ssl is enabled
---
.../CoreModules/Scripting/LSLHttp/UrlModule.cs | 61 ++++++++++++++++++++--
1 file changed, 56 insertions(+), 5 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs b/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs
index 9b565ed..a552a28 100644
--- a/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs
+++ b/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs
@@ -78,7 +78,9 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
private int m_TotalUrls = 100;
+ private uint https_port = 0;
private IHttpServer m_HttpServer = null;
+ private IHttpServer m_HttpsServer = null;
private string m_ExternalHostNameForLSL = "";
@@ -100,6 +102,11 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
public void Initialise(IConfigSource config)
{
m_ExternalHostNameForLSL = config.Configs["Network"].GetString("ExternalHostNameForLSL", System.Environment.MachineName);
+ bool ssl_enabled = config.Configs["Network"].GetBoolean("https_listener",false);
+ if (ssl_enabled)
+ {
+ https_port = (uint) config.Configs["Network"].GetInt("https_port",0);
+ }
}
public void PostInitialise()
@@ -113,6 +120,12 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
// There can only be one
//
m_HttpServer = MainServer.Instance;
+ //
+ // We can use the https if it is enabled
+ if (https_port > 0)
+ {
+ m_HttpsServer = MainServer.GetHttpServer(https_port);
+ }
}
scene.RegisterModuleInterface(this);
@@ -171,7 +184,40 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
{
UUID urlcode = UUID.Random();
- engine.PostScriptEvent(itemID, "http_request", new Object[] { urlcode.ToString(), "URL_REQUEST_DENIED", "" });
+ if (m_HttpsServer == null)
+ {
+ engine.PostScriptEvent(itemID, "http_request", new Object[] { urlcode.ToString(), "URL_REQUEST_DENIED", "" });
+ return urlcode;
+ }
+
+ lock (m_UrlMap)
+ {
+ if (m_UrlMap.Count >= m_TotalUrls)
+ {
+ engine.PostScriptEvent(itemID, "http_request", new Object[] { urlcode.ToString(), "URL_REQUEST_DENIED", "" });
+ return urlcode;
+ }
+ string url = "https://" + m_ExternalHostNameForLSL + ":" + m_HttpsServer.Port.ToString() + "/lslhttps/" + urlcode.ToString() + "/";
+
+ UrlData urlData = new UrlData();
+ urlData.hostID = host.UUID;
+ urlData.itemID = itemID;
+ urlData.engine = engine;
+ urlData.url = url;
+ urlData.urlcode = urlcode;
+ urlData.requests = new Dictionary();
+
+
+ m_UrlMap[url] = urlData;
+
+ string uri = "/lslhttps/" + urlcode.ToString() + "/";
+
+ m_HttpsServer.AddPollServiceHTTPHandler(uri,HandleHttpPoll,
+ new PollServiceEventArgs(HttpRequestHandler,HasEvents, GetEvents, NoEvents,
+ urlcode));
+
+ engine.PostScriptEvent(itemID, "http_request", new Object[] { urlcode.ToString(), "URL_REQUEST_GRANTED", url });
+ }
return urlcode;
}
@@ -345,7 +391,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
}
private Hashtable GetEvents(UUID requestID, UUID sessionID, string request)
{
- UrlData url = null;
+ UrlData url = null;
RequestData requestData = null;
lock (m_RequestMap)
@@ -391,11 +437,12 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
lock (request)
{
string uri = request["uri"].ToString();
-
+ bool is_ssl = uri.Contains("lslhttps");
+
try
{
Hashtable headers = (Hashtable)request["headers"];
-
+
// string uri_full = "http://" + m_ExternalHostNameForLSL + ":" + m_HttpServer.Port.ToString() + uri;// "/lslhttp/" + urlcode.ToString() + "/";
int pos1 = uri.IndexOf("/");// /lslhttp
@@ -409,7 +456,11 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
pathInfo = uri.Substring(pos3);
- UrlData url = m_UrlMap["http://" + m_ExternalHostNameForLSL + ":" + m_HttpServer.Port.ToString() + uri_tmp];
+ UrlData url = null;
+ if (!is_ssl)
+ url = m_UrlMap["http://" + m_ExternalHostNameForLSL + ":" + m_HttpServer.Port.ToString() + uri_tmp];
+ else
+ url = m_UrlMap["https://" + m_ExternalHostNameForLSL + ":" + m_HttpsServer.Port.ToString() + uri_tmp];
//for llGetHttpHeader support we need to store original URI here
//to make x-path-info / x-query-string / x-script-url / x-remote-ip headers
--
cgit v1.1
From fd44540c023e7df35308a40df9e61d7f9273eba4 Mon Sep 17 00:00:00 2001
From: Justin Clark-Casey (justincc)
Date: Fri, 6 May 2011 01:06:28 +0100
Subject: add descriptive explanations for region restart functionality
---
.../Region/CoreModules/World/Region/RestartModule.cs | 19 +++++++++++++------
.../Agent/UDP/Linden/LindenUDPInfoModule.cs | 1 -
2 files changed, 13 insertions(+), 7 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/CoreModules/World/Region/RestartModule.cs b/OpenSim/Region/CoreModules/World/Region/RestartModule.cs
index ab6a598..e983239 100644
--- a/OpenSim/Region/CoreModules/World/Region/RestartModule.cs
+++ b/OpenSim/Region/CoreModules/World/Region/RestartModule.cs
@@ -64,19 +64,26 @@ namespace OpenSim.Region.CoreModules.World.Region
public void AddRegion(Scene scene)
{
m_Scene = scene;
+
scene.RegisterModuleInterface(this);
MainConsole.Instance.Commands.AddCommand("RestartModule",
false, "region restart bluebox",
- "region restart bluebox