29 using System.Collections.Generic;
30 using System.Diagnostics;
32 using System.Reflection;
34 using System.Text.RegularExpressions;
38 using OpenSim.Framework;
39 using OpenSim.Region.Framework.Interfaces;
40 using OpenSim.Region.Framework.Scenes;
103 [Extension(Path =
"/OpenSim/RegionModules", NodeName =
"RegionModule", Id =
"AutoBackupModule")]
106 private static readonly ILog m_log =
107 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
108 private readonly Dictionary<Guid, IScene> m_pendingSaves =
new Dictionary<Guid, IScene>(1);
110 private readonly Dictionary<IScene, AutoBackupModuleState> m_states =
111 new Dictionary<IScene, AutoBackupModuleState>(1);
112 private readonly Dictionary<Timer, List<IScene>> m_timerMap =
113 new Dictionary<Timer, List<IScene>>(1);
114 private readonly Dictionary<double, Timer> m_timers =
new Dictionary<double, Timer>(1);
116 private delegate T DefaultGetter<T>(
string settingName, T defaultValue);
117 private bool m_enabled;
119 private List<Scene> m_Scenes =
new List<Scene> ();
125 private bool m_closed;
127 private IConfigSource m_configSource;
132 public bool IsSharedModule
137 #region ISharedRegionModule Members
142 string IRegionModuleBase.Name
144 get {
return "AutoBackupModule"; }
150 Type IRegionModuleBase.ReplaceableInterface
159 void IRegionModuleBase.Initialise(IConfigSource source)
162 this.m_configSource = source;
163 IConfig moduleConfig = source.Configs[
"AutoBackupModule"];
164 if (moduleConfig == null)
166 this.m_enabled =
false;
171 this.m_enabled = moduleConfig.GetBoolean(
"AutoBackupModuleEnabled",
false);
174 m_log.Info(
"[AUTO BACKUP]: AutoBackupModule enabled");
183 this.m_defaultState.Timer = defTimer;
184 this.m_timers.Add(43200000, defTimer);
185 defTimer.Elapsed += this.HandleElapsed;
186 defTimer.AutoReset =
true;
189 AutoBackupModuleState abms = this.ParseConfig(null,
true);
190 m_log.Debug(
"[AUTO BACKUP]: Here is the default config:");
191 m_log.Debug(abms.ToString());
197 void IRegionModuleBase.Close()
205 this.StopAllTimers();
212 void IRegionModuleBase.AddRegion (
Scene scene)
214 if (!this.m_enabled) {
218 m_Scenes.Add (scene);
220 m_console = MainConsole.Instance;
222 m_console.Commands.AddCommand (
223 "AutoBackup",
false,
"dobackup",
225 "do backup.", DoBackup);
232 void IRegionModuleBase.RemoveRegion(
Scene scene)
238 m_Scenes.Remove (scene);
239 if (this.m_states.ContainsKey(scene))
241 AutoBackupModuleState abms = this.m_states[scene];
245 List<IScene> list = this.m_timerMap[timer];
251 this.m_timerMap.Remove(timer);
252 this.m_timers.Remove(timer.Interval);
255 this.m_states.Remove(scene);
264 void IRegionModuleBase.RegionLoaded(
Scene scene)
277 AutoBackupModuleState abms = this.ParseConfig(scene,
false);
278 m_log.Debug(
"[AUTO BACKUP]: Config for " + scene.RegionInfo.RegionName);
279 m_log.Debug((abms == null ?
"DEFAULT" : abms.ToString()));
281 m_states.Add(scene, abms);
287 void ISharedRegionModule.PostInitialise()
293 private void DoBackup (
string module,
string[] args)
295 if (args.Length != 2) {
296 MainConsole.Instance.OutputFormat (
"Usage: dobackup <regionname>");
300 string name = args [1];
302 foreach (
Scene s
in m_Scenes) {
303 string test = s.Name.ToString ();
310 MainConsole.Instance.OutputFormat (
"No such region {0}. Nothing to backup", name);
322 private AutoBackupModuleState ParseConfig(
IScene scene,
bool parseDefault)
327 AutoBackupModuleState state;
332 sRegionLabel =
"DEFAULT";
334 state = this.m_defaultState;
338 sRegionName = scene.RegionInfo.RegionName;
339 sRegionLabel = sRegionName;
345 IConfig regionConfig = (scene != null ? scene.Config.Configs[sRegionName] : null);
346 IConfig config = this.m_configSource.Configs[
"AutoBackupModule"];
350 state = this.m_defaultState;
354 bool tmpEnabled = ResolveBoolean(
"AutoBackup", this.m_defaultState.Enabled, config, regionConfig);
355 if (state == null && tmpEnabled != this.m_defaultState.Enabled)
358 state =
new AutoBackupModuleState();
363 state.Enabled = tmpEnabled;
367 if ((state == null && !this.m_defaultState.Enabled) || (state != null && !state.Enabled))
373 m_log.Info(
"[AUTO BACKUP]: Region " + sRegionLabel +
" is AutoBackup ENABLED.");
378 this.ResolveDouble(
"AutoBackupInterval", this.m_defaultState.IntervalMinutes,
379 config, regionConfig) * 60000.0;
380 if (state == null && interval != this.m_defaultState.IntervalMinutes * 60000.0)
382 state =
new AutoBackupModuleState();
385 if (this.m_timers.ContainsKey(interval))
389 state.Timer = this.m_timers[interval];
391 m_log.Debug(
"[AUTO BACKUP]: Reusing timer for " + interval +
" msec for region " +
397 if (interval <= 0.0 && state != null)
399 state.Enabled =
false;
404 state =
new AutoBackupModuleState();
409 this.m_timers.Add(interval, tim);
410 tim.Elapsed += this.HandleElapsed;
411 tim.AutoReset =
true;
420 if (this.m_timerMap.ContainsKey(state.Timer))
426 List<IScene> scns =
new List<IScene>(1);
433 if (this.m_timerMap.ContainsKey(
this.m_defaultState.Timer))
439 List<IScene> scns =
new List<IScene>(1);
446 bool tmpBusyCheck = ResolveBoolean(
"AutoBackupBusyCheck",
447 this.m_defaultState.BusyCheck, config, regionConfig);
448 if (state == null && tmpBusyCheck != this.m_defaultState.BusyCheck)
450 state =
new AutoBackupModuleState();
455 state.BusyCheck = tmpBusyCheck;
459 bool tmpSkipAssets = ResolveBoolean(
"AutoBackupSkipAssets",
460 this.m_defaultState.SkipAssets, config, regionConfig);
461 if (state == null && tmpSkipAssets != this.m_defaultState.SkipAssets)
463 state =
new AutoBackupModuleState();
468 state.SkipAssets = tmpSkipAssets;
472 int tmpKeepFilesForDays = ResolveInt(
"AutoBackupKeepFilesForDays",
473 this.m_defaultState.KeepFilesForDays, config, regionConfig);
474 if (state == null && tmpKeepFilesForDays != this.m_defaultState.KeepFilesForDays)
476 state =
new AutoBackupModuleState();
481 state.KeepFilesForDays = tmpKeepFilesForDays;
485 string stmpNamingType = ResolveString(
"AutoBackupNaming",
486 this.m_defaultState.NamingType.ToString(), config, regionConfig);
488 if (stmpNamingType.Equals(
"Time", StringComparison.CurrentCultureIgnoreCase))
490 tmpNamingType = NamingType.Time;
492 else if (stmpNamingType.Equals(
"Sequential", StringComparison.CurrentCultureIgnoreCase))
494 tmpNamingType = NamingType.Sequential;
496 else if (stmpNamingType.Equals(
"Overwrite", StringComparison.CurrentCultureIgnoreCase))
498 tmpNamingType = NamingType.Overwrite;
502 m_log.Warn(
"Unknown naming type specified for region " + sRegionLabel +
": " +
504 tmpNamingType = NamingType.Time;
507 if (state == null && tmpNamingType != this.m_defaultState.NamingType)
509 state =
new AutoBackupModuleState();
514 state.NamingType = tmpNamingType;
517 string tmpScript = ResolveString(
"AutoBackupScript",
518 this.m_defaultState.Script, config, regionConfig);
519 if (state == null && tmpScript != this.m_defaultState.Script)
521 state =
new AutoBackupModuleState();
526 state.Script = tmpScript;
529 string tmpBackupDir = ResolveString(
"AutoBackupDir",
".", config, regionConfig);
530 if (state == null && tmpBackupDir != this.m_defaultState.BackupDir)
532 state =
new AutoBackupModuleState();
537 state.BackupDir = tmpBackupDir;
539 if (state.BackupDir !=
".")
543 DirectoryInfo dirinfo =
new DirectoryInfo(state.BackupDir);
552 "[AUTO BACKUP]: BAD NEWS. You won't be able to save backups to directory " +
554 " because it doesn't exist or there's a permissions issue with it. Here's the exception.",
561 return m_defaultState;
574 private bool ResolveBoolean(
string settingName,
bool defaultValue, IConfig global, IConfig local)
578 return local.GetBoolean(settingName, global.GetBoolean(settingName, defaultValue));
582 return global.GetBoolean(settingName, defaultValue);
594 private double ResolveDouble(
string settingName,
double defaultValue, IConfig global, IConfig local)
598 return local.GetDouble(settingName, global.GetDouble(settingName, defaultValue));
602 return global.GetDouble(settingName, defaultValue);
614 private int ResolveInt(
string settingName,
int defaultValue, IConfig global, IConfig local)
618 return local.GetInt(settingName, global.GetInt(settingName, defaultValue));
622 return global.GetInt(settingName, defaultValue);
634 private string ResolveString(
string settingName,
string defaultValue, IConfig global, IConfig local)
638 return local.GetString(settingName, global.GetString(settingName, defaultValue));
642 return global.GetString(settingName, defaultValue);
651 private void HandleElapsed(
object sender, ElapsedEventArgs e)
665 bool heuristicsRun =
false;
666 bool heuristicsPassed =
false;
667 if (!this.m_timerMap.ContainsKey((
Timer) sender))
669 m_log.Debug(
"[AUTO BACKUP]: Code-up error: timerMap doesn't contain timer " + sender);
672 List<IScene> tmap = this.m_timerMap[(
Timer) sender];
673 if (tmap != null && tmap.Count > 0)
675 foreach (
IScene scene
in tmap)
677 AutoBackupModuleState state = this.m_states[scene];
678 bool heuristics = state.BusyCheck;
681 if ((heuristics && heuristicsRun && heuristicsPassed) || !heuristics)
683 this.DoRegionBackup(scene);
686 else if (heuristicsRun)
688 m_log.Info(
"[AUTO BACKUP]: Heuristics: too busy to backup " +
689 scene.RegionInfo.RegionName +
" right now.");
695 heuristicsPassed = this.RunHeuristics(scene);
696 heuristicsRun =
true;
697 if (!heuristicsPassed)
699 m_log.Info(
"[AUTO BACKUP]: Heuristics: too busy to backup " +
700 scene.RegionInfo.RegionName +
" right now.");
703 this.DoRegionBackup(scene);
707 this.RemoveOldFiles(state);
716 private void DoRegionBackup(
IScene scene)
721 m_log.Warn(
"[AUTO BACKUP]: Not backing up region " + scene.RegionInfo.RegionName +
726 AutoBackupModuleState state = this.m_states[scene];
731 if (savePath == null)
733 m_log.Warn(
"[AUTO BACKUP]: savePath is null in HandleElapsed");
736 Guid guid = Guid.NewGuid();
737 m_pendingSaves.Add(guid, scene);
738 state.LiveRequests.Add(guid, savePath);
739 ((
Scene) scene).EventManager.OnOarFileSaved +=
new EventManager.OarFileSaved(EventManager_OnOarFileSaved);
741 m_log.Info(
"[AUTO BACKUP]: Backing up region " + scene.RegionInfo.RegionName);
744 Dictionary<string, object>
options =
new Dictionary<string, object>();
746 if (state.SkipAssets)
747 options[
"noassets"] =
true;
749 iram.ArchiveRegion(savePath, guid,
options);
753 private void RemoveOldFiles(AutoBackupModuleState state)
756 if (state.KeepFilesForDays > 0)
758 string[] files = Directory.GetFiles(state.BackupDir,
"*.oar");
759 DateTime CuttOffDate = DateTime.Now.AddDays(0 - state.KeepFilesForDays);
761 foreach (
string file
in files)
765 FileInfo fi =
new FileInfo(file);
766 if (fi.CreationTime < CuttOffDate)
771 m_log.Error(
"[AUTO BACKUP]: Error deleting old backup file '" + file +
"': " + Ex.Message);
782 void EventManager_OnOarFileSaved(Guid guid,
string message)
785 if (m_pendingSaves.ContainsKey(guid))
787 AutoBackupModuleState abms = m_states[(m_pendingSaves[guid])];
788 ExecuteScript(abms.Script, abms.LiveRequests[guid]);
789 m_pendingSaves.Remove(guid);
790 abms.LiveRequests.Remove(guid);
797 private static string GetTimeString()
799 StringWriter sw =
new StringWriter();
801 DateTime now = DateTime.Now;
810 sw.Write(now.Minute);
812 sw.Write(now.Second);
815 string output = sw.ToString();
821 private bool RunHeuristics(
IScene region)
825 return this.RunTimeDilationHeuristic(region) && this.RunAgentLimitHeuristic(region);
829 m_log.Warn(
"[AUTO BACKUP]: Exception in RunHeuristics", e);
841 private bool RunTimeDilationHeuristic(
IScene region)
843 string regionName = region.RegionInfo.RegionName;
844 return region.TimeDilation >=
845 this.m_configSource.Configs[
"AutoBackupModule"].GetFloat(
846 regionName +
".AutoBackupDilationThreshold", 0.5f);
856 private bool RunAgentLimitHeuristic(
IScene region)
858 string regionName = region.RegionInfo.RegionName;
863 return scene.GetRootAgentCount() <=
864 this.m_configSource.Configs[
"AutoBackupModule"].GetInt(
865 regionName +
".AutoBackupAgentThreshold", 10);
867 catch (InvalidCastException ice)
870 "[AUTO BACKUP]: I NEED MAINTENANCE: IScene is not a Scene; can't get root agent count!",
884 private static void ExecuteScript(
string scriptName,
string savePath)
887 if (scriptName == null || scriptName.Length <= 0)
894 FileInfo fi =
new FileInfo(scriptName);
897 ProcessStartInfo psi =
new ProcessStartInfo(scriptName);
898 psi.Arguments = savePath;
899 psi.CreateNoWindow =
true;
900 Process proc = Process.Start(psi);
901 proc.ErrorDataReceived += HandleProcErrorDataReceived;
907 "Exception encountered when trying to run script for oar backup " + savePath, e);
916 private static void HandleProcErrorDataReceived(
object sender, DataReceivedEventArgs e)
918 m_log.Warn(
"ExecuteScript hook " + ((Process) sender).ProcessName +
919 " is yacking on stderr: " + e.Data);
925 private void StopAllTimers()
927 foreach (
Timer t
in this.m_timerMap.Keys)
931 this.m_closed =
true;
940 private static string GetNextFile(
string dirName,
string regionName)
942 FileInfo uniqueFile = null;
943 long biggestExistingFile = GetNextOarFileNumber(dirName, regionName);
944 biggestExistingFile++;
947 new FileInfo(dirName + Path.DirectorySeparatorChar + regionName +
"_" +
948 biggestExistingFile +
".oar");
949 return uniqueFile.FullName;
959 private static string BuildOarPath(
string regionName,
string baseDir,
NamingType naming)
961 FileInfo path = null;
964 case NamingType.Overwrite:
965 path =
new FileInfo(baseDir + Path.DirectorySeparatorChar + regionName +
".oar");
966 return path.FullName;
967 case NamingType.Time:
969 new FileInfo(baseDir + Path.DirectorySeparatorChar + regionName +
970 GetTimeString() +
".oar");
971 return path.FullName;
972 case NamingType.Sequential:
974 path =
new FileInfo(GetNextFile(baseDir, regionName));
975 return path.FullName;
977 m_log.Warn(
"VERY BAD: Unhandled case element " + naming);
990 private static long GetNextOarFileNumber(
string dirName,
string regionName)
994 DirectoryInfo di =
new DirectoryInfo(dirName);
995 FileInfo[] fi = di.GetFiles(regionName, SearchOption.TopDirectoryOnly);
996 Array.Sort(fi, (f1, f2) => StringComparer.CurrentCultureIgnoreCase.Compare(f1.Name, f2.Name));
998 if (fi.LongLength > 0)
1001 bool worked =
false;
1002 Regex reg =
new Regex(regionName +
"_([0-9])+" +
".oar");
1004 while (!worked && subtract <= fi.LongLength)
1007 string biggestFileName = fi[fi.LongLength - subtract].Name;
1008 MatchCollection matches = reg.Matches(biggestFileName);
1010 if (matches.Count > 0 && matches[0].Groups.Count > 0)
1014 long.TryParse(matches[0].Groups[1].Value, out l);
1018 catch (FormatException fe)
1021 "[AUTO BACKUP]: Error: Can't parse long value from file name to determine next OAR backup file number!",
AutoBackupModuleState: Auto-Backup state for one region (scene). If you use this class in any way out...
Interface to region archive functionality
bool Ready
Is this region ready for use?
System.Timers.Timer Timer
AutoBackupModule: save OAR region backups to disk periodically
NamingType
Choose between ways of naming the backup files that are generated.
Interactive OpenSim region server