aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs')
-rw-r--r--OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs718
1 files changed, 208 insertions, 510 deletions
diff --git a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
index 79b80f8..eab6705 100644
--- a/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
+++ b/OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs
@@ -59,23 +59,20 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
59 /// </summary> 59 /// </summary>
60 /// <remarks> 60 /// <remarks>
61 /// Config Settings Documentation. 61 /// Config Settings Documentation.
62 /// Each configuration setting can be specified in two places: OpenSim.ini or Regions.ini. 62 /// Configuration setting can be specified in two places: OpenSim.ini and/or Regions.ini.
63 /// If specified in Regions.ini, the settings should be within the region's section name.
64 /// If specified in OpenSim.ini, the settings should be within the [AutoBackupModule] section.
65 /// Region-specific settings take precedence.
66 /// 63 ///
67 /// AutoBackupModuleEnabled: True/False. Default: False. If True, use the auto backup module. This setting does not support per-region basis. 64 /// OpenSim.ini only settings section [AutoBackupModule]
68 /// All other settings under [AutoBackupModule] are ignored if AutoBackupModuleEnabled is false, even per-region settings! 65 /// AutoBackupModuleEnabled: True/False. Default: False. If True, use the auto backup module.
69 /// AutoBackup: True/False. Default: False. If True, activate auto backup functionality. 66 /// if false module is disable and all rest is ignored
70 /// This is the only required option for enabling auto-backup; the other options have sane defaults.
71 /// If False for a particular region, the auto-backup module becomes a no-op for the region, and all other AutoBackup* settings are ignored.
72 /// If False globally (the default), only regions that specifically override it in Regions.ini will get AutoBackup functionality.
73 /// AutoBackupInterval: Double, non-negative value. Default: 720 (12 hours). 67 /// AutoBackupInterval: Double, non-negative value. Default: 720 (12 hours).
74 /// The number of minutes between each backup attempt. 68 /// The number of minutes between each backup attempt.
75 /// If a negative or zero value is given, it is equivalent to setting AutoBackup = False. 69 ///
76 /// AutoBackupBusyCheck: True/False. Default: True. 70 /// Next can be set on OpenSim.ini, as default, and or per region in Regions.ini
77 /// If True, we will only take an auto-backup if a set of conditions are met. 71 /// Region-specific settings take precedence.
78 /// These conditions are heuristics to try and avoid taking a backup when the sim is busy. 72 ///
73 /// AutoBackup: True/False. Default: False. If True, activate auto backup functionality.
74 /// controls backup per region, with default optionaly set on OpenSim.ini
75
79 /// AutoBackupSkipAssets 76 /// AutoBackupSkipAssets
80 /// If true, assets are not saved to the oar file. Considerably reduces impact on simulator when backing up. Intended for when assets db is backed up separately 77 /// If true, assets are not saved to the oar file. Considerably reduces impact on simulator when backing up. Intended for when assets db is backed up separately
81 /// AutoBackupKeepFilesForDays 78 /// AutoBackupKeepFilesForDays
@@ -93,36 +90,22 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
93 /// "Overwrite": Always save to file named "${AutoBackupDir}/RegionName.oar", even if we have to overwrite an existing file. 90 /// "Overwrite": Always save to file named "${AutoBackupDir}/RegionName.oar", even if we have to overwrite an existing file.
94 /// AutoBackupDir: String. Default: "." (the current directory). 91 /// AutoBackupDir: String. Default: "." (the current directory).
95 /// A directory (absolute or relative) where backups should be saved. 92 /// A directory (absolute or relative) where backups should be saved.
96 /// AutoBackupDilationThreshold: float. Default: 0.5. Lower bound on time dilation required for BusyCheck heuristics to pass.
97 /// If the time dilation is below this value, don't take a backup right now.
98 /// AutoBackupAgentThreshold: int. Default: 10. Upper bound on # of agents in region required for BusyCheck heuristics to pass.
99 /// If the number of agents is greater than this value, don't take a backup right now
100 /// Save memory by setting low initial capacities. Minimizes impact in common cases of all regions using same interval, and instances hosting 1 ~ 4 regions.
101 /// Also helps if you don't want AutoBackup at all.
102 /// </remarks> 93 /// </remarks>
103 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AutoBackupModule")] 94 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AutoBackupModule")]
104 public class AutoBackupModule : ISharedRegionModule 95 public class AutoBackupModule : ISharedRegionModule
105 { 96 {
106 private static readonly ILog m_log = 97 private static readonly ILog m_log =
107 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 98 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
108 private readonly Dictionary<Guid, IScene> m_pendingSaves = new Dictionary<Guid, IScene>(1);
109 private readonly AutoBackupModuleState m_defaultState = new AutoBackupModuleState(); 99 private readonly AutoBackupModuleState m_defaultState = new AutoBackupModuleState();
110 private readonly Dictionary<IScene, AutoBackupModuleState> m_states = 100 private readonly Dictionary<IScene, AutoBackupModuleState> m_states =
111 new Dictionary<IScene, AutoBackupModuleState>(1); 101 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);
115 102
116 private delegate T DefaultGetter<T>(string settingName, T defaultValue); 103 private delegate T DefaultGetter<T>(string settingName, T defaultValue);
117 private bool m_enabled; 104 private bool m_enabled;
118 private ICommandConsole m_console; 105 private ICommandConsole m_console;
119 private List<Scene> m_Scenes = new List<Scene> (); 106 private List<Scene> m_Scenes = new List<Scene> ();
120 107 private Timer m_masterTimer;
121 108 private bool m_busy;
122 /// <summary>
123 /// Whether the shared module should be enabled at all. NOT the same as m_Enabled in AutoBackupModuleState!
124 /// </summary>
125 private bool m_closed;
126 109
127 private IConfigSource m_configSource; 110 private IConfigSource m_configSource;
128 111
@@ -159,36 +142,34 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
159 void IRegionModuleBase.Initialise(IConfigSource source) 142 void IRegionModuleBase.Initialise(IConfigSource source)
160 { 143 {
161 // Determine if we have been enabled at all in OpenSim.ini -- this is part and parcel of being an optional module 144 // Determine if we have been enabled at all in OpenSim.ini -- this is part and parcel of being an optional module
162 this.m_configSource = source; 145 m_configSource = source;
163 IConfig moduleConfig = source.Configs["AutoBackupModule"]; 146 IConfig moduleConfig = source.Configs["AutoBackupModule"];
164 if (moduleConfig == null) 147 if (moduleConfig == null)
165 { 148 {
166 this.m_enabled = false; 149 m_enabled = false;
167 return; 150 return;
168 } 151 }
169 else
170 {
171 this.m_enabled = moduleConfig.GetBoolean("AutoBackupModuleEnabled", false);
172 if (this.m_enabled)
173 {
174 m_log.Info("[AUTO BACKUP]: AutoBackupModule enabled");
175 }
176 else
177 {
178 return;
179 }
180 }
181 152
182 Timer defTimer = new Timer(43200000); 153 m_enabled = moduleConfig.GetBoolean("AutoBackupModuleEnabled", false);
183 this.m_defaultState.Timer = defTimer; 154 if(!m_enabled)
184 this.m_timers.Add(43200000, defTimer); 155 return;
185 defTimer.Elapsed += this.HandleElapsed; 156
186 defTimer.AutoReset = true; 157 m_log.Info("[AUTO BACKUP]: AutoBackupModule enabled");
187 defTimer.Start(); 158 m_masterTimer = new Timer(43200000);
159 m_masterTimer.Elapsed += HandleElapsed;
160 m_masterTimer.AutoReset = false;
161
162 ParseDefaultConfig();
163 m_log.Debug("[AUTO BACKUP]: Default config:");
164 m_log.Debug(m_defaultState.ToString());
188 165
189 AutoBackupModuleState abms = this.ParseConfig(null, true); 166 m_console = MainConsole.Instance;
190 m_log.Debug("[AUTO BACKUP]: Here is the default config:"); 167
191 m_log.Debug(abms.ToString()); 168 m_console.Commands.AddCommand (
169 "AutoBackup", true, "dooarbackup",
170 "dooarbackup <regionName>",
171 "do single region backup into a oar. Identical to save oar but using AutoBackup settings for name etc", DoBackup);
172 m_busy = true;
192 } 173 }
193 174
194 /// <summary> 175 /// <summary>
@@ -196,13 +177,11 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
196 /// </summary> 177 /// </summary>
197 void IRegionModuleBase.Close() 178 void IRegionModuleBase.Close()
198 { 179 {
199 if (!this.m_enabled) 180 if (!m_enabled)
200 {
201 return; 181 return;
202 }
203 182
204 // We don't want any timers firing while the sim's coming down; strange things may happen. 183 // We don't want any timers firing while the sim's coming down; strange things may happen.
205 this.StopAllTimers(); 184 m_masterTimer.Dispose();
206 } 185 }
207 186
208 /// <summary> 187 /// <summary>
@@ -211,18 +190,11 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
211 /// <param name="scene"></param> 190 /// <param name="scene"></param>
212 void IRegionModuleBase.AddRegion (Scene scene) 191 void IRegionModuleBase.AddRegion (Scene scene)
213 { 192 {
214 if (!this.m_enabled) { 193 if (!m_enabled)
215 return; 194 return;
216 }
217 lock (m_Scenes) {
218 m_Scenes.Add (scene);
219 }
220 m_console = MainConsole.Instance;
221 195
222 m_console.Commands.AddCommand ( 196 lock (m_Scenes)
223 "AutoBackup", false, "dobackup", 197 m_Scenes.Add (scene);
224 "dobackup",
225 "do backup.", DoBackup);
226 } 198 }
227 199
228 /// <summary> 200 /// <summary>
@@ -231,28 +203,14 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
231 /// <param name="scene">The scene (region) to stop performing AutoBackup on.</param> 203 /// <param name="scene">The scene (region) to stop performing AutoBackup on.</param>
232 void IRegionModuleBase.RemoveRegion(Scene scene) 204 void IRegionModuleBase.RemoveRegion(Scene scene)
233 { 205 {
234 if (!this.m_enabled) 206 if (m_enabled)
235 {
236 return; 207 return;
237 }
238 m_Scenes.Remove (scene);
239 if (this.m_states.ContainsKey(scene))
240 {
241 AutoBackupModuleState abms = this.m_states[scene];
242
243 // Remove this scene out of the timer map list
244 Timer timer = abms.Timer;
245 List<IScene> list = this.m_timerMap[timer];
246 list.Remove(scene);
247 208
248 // Shut down the timer if this was the last scene for the timer 209 lock(m_Scenes)
249 if (list.Count == 0) 210 {
250 { 211 if (m_states.ContainsKey(scene))
251 this.m_timerMap.Remove(timer); 212 m_states.Remove(scene);
252 this.m_timers.Remove(timer.Interval); 213 m_Scenes.Remove(scene);
253 timer.Close();
254 }
255 this.m_states.Remove(scene);
256 } 214 }
257 } 215 }
258 216
@@ -263,22 +221,29 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
263 /// <param name="scene">The scene to (possibly) perform AutoBackup on.</param> 221 /// <param name="scene">The scene to (possibly) perform AutoBackup on.</param>
264 void IRegionModuleBase.RegionLoaded(Scene scene) 222 void IRegionModuleBase.RegionLoaded(Scene scene)
265 { 223 {
266 if (!this.m_enabled) 224 if (!m_enabled)
267 {
268 return; 225 return;
269 }
270 226
271 // This really ought not to happen, but just in case, let's pretend it didn't... 227 // This really ought not to happen, but just in case, let's pretend it didn't...
272 if (scene == null) 228 if (scene == null)
273 {
274 return; 229 return;
275 }
276 230
277 AutoBackupModuleState abms = this.ParseConfig(scene, false); 231 AutoBackupModuleState abms = ParseConfig(scene);
278 m_log.Debug("[AUTO BACKUP]: Config for " + scene.RegionInfo.RegionName); 232 if(abms == null)
279 m_log.Debug((abms == null ? "DEFAULT" : abms.ToString())); 233 {
234 m_log.Debug("[AUTO BACKUP]: Config for " + scene.RegionInfo.RegionName);
235 m_log.Debug("DEFAULT");
236 abms = new AutoBackupModuleState(m_defaultState);
237 }
238 else
239 {
240 m_log.Debug("[AUTO BACKUP]: Config for " + scene.RegionInfo.RegionName);
241 m_log.Debug(abms.ToString());
242 }
280 243
281 m_states.Add(scene, abms); 244 m_states.Add(scene, abms);
245 m_busy = false;
246 m_masterTimer.Start();
282 } 247 }
283 248
284 /// <summary> 249 /// <summary>
@@ -286,280 +251,193 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
286 /// </summary> 251 /// </summary>
287 void ISharedRegionModule.PostInitialise() 252 void ISharedRegionModule.PostInitialise()
288 { 253 {
254
289 } 255 }
290 256
291 #endregion 257 #endregion
292 258
293 private void DoBackup (string module, string[] args) 259 private void DoBackup (string module, string[] args)
294 { 260 {
295 if (args.Length != 2) { 261 if (!m_enabled)
296 MainConsole.Instance.OutputFormat ("Usage: dobackup <regionname>"); 262 return;
263
264 if(m_busy)
265 {
266 MainConsole.Instance.OutputFormat ("Already doing a backup, please try later");
267 return;
268 }
269
270 if (args.Length != 2)
271 {
272 MainConsole.Instance.OutputFormat ("Usage: dooarbackup <regionname>");
297 return; 273 return;
298 } 274 }
275
276 m_busy = true;
277
299 bool found = false; 278 bool found = false;
300 string name = args [1]; 279 string name = args [1];
301 lock (m_Scenes) { 280 Scene[] scenes;
302 foreach (Scene s in m_Scenes) { 281 lock (m_Scenes)
303 string test = s.Name.ToString (); 282 scenes = m_Scenes.ToArray();
304 if (test == name) { 283
284 if(scenes == null)
285 return;
286
287 Scene s;
288 try
289 {
290 for(int i = 0; i < scenes.Length; i++)
291 {
292 s = scenes[i];
293 if (s.Name == name)
294 {
305 found = true; 295 found = true;
306 DoRegionBackup (s); 296 m_masterTimer.Stop();
297 DoRegionBackup(s);
298 break;
307 } 299 }
308 } 300 }
309 if (!found) { 301 } catch { }
302
303 if (!found)
310 MainConsole.Instance.OutputFormat ("No such region {0}. Nothing to backup", name); 304 MainConsole.Instance.OutputFormat ("No such region {0}. Nothing to backup", name);
311 } 305 m_busy = false;
312 }
313 } 306 }
314 307
315 /// <summary> 308 private void ParseDefaultConfig()
316 /// Set up internal state for a given scene. Fairly complex code.
317 /// When this method returns, we've started auto-backup timers, put members in Dictionaries, and created a State object for this scene.
318 /// </summary>
319 /// <param name="scene">The scene to look at.</param>
320 /// <param name="parseDefault">Whether this call is intended to figure out what we consider the "default" config (applied to all regions unless overridden by per-region settings).</param>
321 /// <returns>An AutoBackupModuleState contains most information you should need to know relevant to auto-backup, as applicable to a single region.</returns>
322 private AutoBackupModuleState ParseConfig(IScene scene, bool parseDefault)
323 { 309 {
324 string sRegionName; 310 IConfig config = m_configSource.Configs["AutoBackupModule"];
325 string sRegionLabel; 311 if (config == null)
326// string prepend; 312 return;
327 AutoBackupModuleState state;
328 313
329 if (parseDefault) 314 // Borrow an existing timer if one exists for the same interval; otherwise, make a new one.
330 { 315 double interval = config.GetDouble("AutoBackupInterval", 720);
331 sRegionName = null; 316 interval *= 60000.0;
332 sRegionLabel = "DEFAULT"; 317 m_masterTimer.Interval = interval;
333// prepend = "";
334 state = this.m_defaultState;
335 }
336 else
337 {
338 sRegionName = scene.RegionInfo.RegionName;
339 sRegionLabel = sRegionName;
340// prepend = sRegionName + ".";
341 state = null;
342 }
343 318
344 // Read the config settings and set variables. 319 m_defaultState.Enabled = config.GetBoolean("AutoBackup", m_defaultState.Enabled);
345 IConfig regionConfig = (scene != null ? scene.Config.Configs[sRegionName] : null);
346 IConfig config = this.m_configSource.Configs["AutoBackupModule"];
347 if (config == null)
348 {
349 // defaultState would be disabled too if the section doesn't exist.
350 state = this.m_defaultState;
351 return state;
352 }
353 320
354 bool tmpEnabled = ResolveBoolean("AutoBackup", this.m_defaultState.Enabled, config, regionConfig); 321 // Included Option To Skip Assets
355 if (state == null && tmpEnabled != this.m_defaultState.Enabled) 322 m_defaultState.SkipAssets = config.GetBoolean("AutoBackupSkipAssets",m_defaultState.SkipAssets);
356 //Varies from default state
357 {
358 state = new AutoBackupModuleState();
359 }
360 323
361 if (state != null) 324 // How long to keep backup files in days, 0 Disables this feature
362 { 325 m_defaultState.KeepFilesForDays = config.GetInt("AutoBackupKeepFilesForDays",m_defaultState.KeepFilesForDays);
363 state.Enabled = tmpEnabled;
364 }
365 326
366 // If you don't want AutoBackup, we stop. 327 // Set file naming algorithm
367 if ((state == null && !this.m_defaultState.Enabled) || (state != null && !state.Enabled)) 328 string stmpNamingType = config.GetString("AutoBackupNaming", m_defaultState.NamingType.ToString());
368 { 329 NamingType tmpNamingType;
369 return state; 330 if (stmpNamingType.Equals("Time", StringComparison.CurrentCultureIgnoreCase))
370 } 331 tmpNamingType = NamingType.Time;
332 else if (stmpNamingType.Equals("Sequential", StringComparison.CurrentCultureIgnoreCase))
333 tmpNamingType = NamingType.Sequential;
334 else if (stmpNamingType.Equals("Overwrite", StringComparison.CurrentCultureIgnoreCase))
335 tmpNamingType = NamingType.Overwrite;
371 else 336 else
372 { 337 {
373 m_log.Info("[AUTO BACKUP]: Region " + sRegionLabel + " is AutoBackup ENABLED."); 338 m_log.Warn("Unknown naming type specified for Default");
339 tmpNamingType = NamingType.Time;
374 } 340 }
341 m_defaultState.NamingType = tmpNamingType;
375 342
376 // Borrow an existing timer if one exists for the same interval; otherwise, make a new one. 343 m_defaultState.Script = config.GetString("AutoBackupScript", m_defaultState.Script);
377 double interval =
378 this.ResolveDouble("AutoBackupInterval", this.m_defaultState.IntervalMinutes,
379 config, regionConfig) * 60000.0;
380 if (state == null && interval != this.m_defaultState.IntervalMinutes * 60000.0)
381 {
382 state = new AutoBackupModuleState();
383 }
384 344
385 if (this.m_timers.ContainsKey(interval)) 345 string backupDir = config.GetString("AutoBackupDir", ".");
346 if (backupDir != ".")
386 { 347 {
387 if (state != null) 348 try
388 { 349 {
389 state.Timer = this.m_timers[interval]; 350 DirectoryInfo dirinfo = new DirectoryInfo(backupDir);
351 if (!dirinfo.Exists)
352 dirinfo.Create();
390 } 353 }
391 m_log.Debug("[AUTO BACKUP]: Reusing timer for " + interval + " msec for region " + 354 catch (Exception e)
392 sRegionLabel);
393 }
394 else
395 {
396 // 0 or negative interval == do nothing.
397 if (interval <= 0.0 && state != null)
398 { 355 {
399 state.Enabled = false; 356 m_log.Warn(
400 return state; 357 "[AUTO BACKUP]: BAD NEWS. You won't be able to save backups to directory " +
401 } 358 backupDir +
402 if (state == null) 359 " because it doesn't exist or there's a permissions issue with it. Here's the exception.",
403 { 360 e);
404 state = new AutoBackupModuleState();
405 } 361 }
406 Timer tim = new Timer(interval);
407 state.Timer = tim;
408 //Milliseconds -> minutes
409 this.m_timers.Add(interval, tim);
410 tim.Elapsed += this.HandleElapsed;
411 tim.AutoReset = true;
412 tim.Start();
413 } 362 }
363 m_defaultState.BackupDir = backupDir;
364 }
414 365
415 // Add the current region to the list of regions tied to this timer. 366 /// <summary>
416 if (scene != null) 367 /// Set up internal state for a given scene. Fairly complex code.
417 { 368 /// When this method returns, we've started auto-backup timers, put members in Dictionaries, and created a State object for this scene.
418 if (state != null) 369 /// </summary>
419 { 370 /// <param name="scene">The scene to look at.</param>
420 if (this.m_timerMap.ContainsKey(state.Timer)) 371 /// <param name="parseDefault">Whether this call is intended to figure out what we consider the "default" config (applied to all regions unless overridden by per-region settings).</param>
421 { 372 /// <returns>An AutoBackupModuleState contains most information you should need to know relevant to auto-backup, as applicable to a single region.</returns>
422 this.m_timerMap[state.Timer].Add(scene); 373 private AutoBackupModuleState ParseConfig(IScene scene)
423 } 374 {
424 else 375 if(scene == null)
425 { 376 return null;
426 List<IScene> scns = new List<IScene>(1);
427 scns.Add(scene);
428 this.m_timerMap.Add(state.Timer, scns);
429 }
430 }
431 else
432 {
433 if (this.m_timerMap.ContainsKey(this.m_defaultState.Timer))
434 {
435 this.m_timerMap[this.m_defaultState.Timer].Add(scene);
436 }
437 else
438 {
439 List<IScene> scns = new List<IScene>(1);
440 scns.Add(scene);
441 this.m_timerMap.Add(this.m_defaultState.Timer, scns);
442 }
443 }
444 }
445 377
446 bool tmpBusyCheck = ResolveBoolean("AutoBackupBusyCheck", 378 string sRegionName;
447 this.m_defaultState.BusyCheck, config, regionConfig); 379 string sRegionLabel;
448 if (state == null && tmpBusyCheck != this.m_defaultState.BusyCheck) 380 AutoBackupModuleState state = null;
449 {
450 state = new AutoBackupModuleState();
451 }
452 381
453 if (state != null) 382 sRegionName = scene.RegionInfo.RegionName;
454 { 383 sRegionLabel = sRegionName;
455 state.BusyCheck = tmpBusyCheck;
456 }
457 384
458 // Included Option To Skip Assets 385 // Read the config settings and set variables.
459 bool tmpSkipAssets = ResolveBoolean("AutoBackupSkipAssets", 386 IConfig regionConfig = scene.Config.Configs[sRegionName];
460 this.m_defaultState.SkipAssets, config, regionConfig); 387 if (regionConfig == null)
461 if (state == null && tmpSkipAssets != this.m_defaultState.SkipAssets) 388 return null;
462 {
463 state = new AutoBackupModuleState();
464 }
465 389
466 if (state != null) 390 state = new AutoBackupModuleState();
467 {
468 state.SkipAssets = tmpSkipAssets;
469 }
470 391
471 // How long to keep backup files in days, 0 Disables this feature 392 state.Enabled = regionConfig.GetBoolean("AutoBackup", m_defaultState.Enabled);
472 int tmpKeepFilesForDays = ResolveInt("AutoBackupKeepFilesForDays",
473 this.m_defaultState.KeepFilesForDays, config, regionConfig);
474 if (state == null && tmpKeepFilesForDays != this.m_defaultState.KeepFilesForDays)
475 {
476 state = new AutoBackupModuleState();
477 }
478 393
479 if (state != null) 394 // Included Option To Skip Assets
480 { 395 state.SkipAssets = regionConfig.GetBoolean("AutoBackupSkipAssets", m_defaultState.SkipAssets);
481 state.KeepFilesForDays = tmpKeepFilesForDays; 396
482 } 397 // How long to keep backup files in days, 0 Disables this feature
398 state.KeepFilesForDays = regionConfig.GetInt("AutoBackupKeepFilesForDays", m_defaultState.KeepFilesForDays);
483 399
484 // Set file naming algorithm 400 // Set file naming algorithm
485 string stmpNamingType = ResolveString("AutoBackupNaming", 401 string stmpNamingType = regionConfig.GetString("AutoBackupNaming", m_defaultState.NamingType.ToString());
486 this.m_defaultState.NamingType.ToString(), config, regionConfig);
487 NamingType tmpNamingType; 402 NamingType tmpNamingType;
488 if (stmpNamingType.Equals("Time", StringComparison.CurrentCultureIgnoreCase)) 403 if (stmpNamingType.Equals("Time", StringComparison.CurrentCultureIgnoreCase))
489 {
490 tmpNamingType = NamingType.Time; 404 tmpNamingType = NamingType.Time;
491 }
492 else if (stmpNamingType.Equals("Sequential", StringComparison.CurrentCultureIgnoreCase)) 405 else if (stmpNamingType.Equals("Sequential", StringComparison.CurrentCultureIgnoreCase))
493 {
494 tmpNamingType = NamingType.Sequential; 406 tmpNamingType = NamingType.Sequential;
495 }
496 else if (stmpNamingType.Equals("Overwrite", StringComparison.CurrentCultureIgnoreCase)) 407 else if (stmpNamingType.Equals("Overwrite", StringComparison.CurrentCultureIgnoreCase))
497 {
498 tmpNamingType = NamingType.Overwrite; 408 tmpNamingType = NamingType.Overwrite;
499 }
500 else 409 else
501 { 410 {
502 m_log.Warn("Unknown naming type specified for region " + sRegionLabel + ": " + 411 m_log.Warn("Unknown naming type specified for region " + sRegionLabel + ": " +
503 stmpNamingType); 412 stmpNamingType);
504 tmpNamingType = NamingType.Time; 413 tmpNamingType = NamingType.Time;
505 } 414 }
415 m_defaultState.NamingType = tmpNamingType;
506 416
507 if (state == null && tmpNamingType != this.m_defaultState.NamingType) 417 state.Script = regionConfig.GetString("AutoBackupScript", m_defaultState.Script);
508 {
509 state = new AutoBackupModuleState();
510 }
511
512 if (state != null)
513 {
514 state.NamingType = tmpNamingType;
515 }
516 418
517 string tmpScript = ResolveString("AutoBackupScript", 419 string tmpBackupDir = regionConfig.GetString("AutoBackupDir", ".");
518 this.m_defaultState.Script, config, regionConfig); 420 // Let's give the user some convenience and auto-mkdir
519 if (state == null && tmpScript != this.m_defaultState.Script) 421 if (tmpBackupDir != "." && tmpBackupDir != m_defaultState.BackupDir)
520 { 422 {
521 state = new AutoBackupModuleState(); 423 try
522 }
523
524 if (state != null)
525 {
526 state.Script = tmpScript;
527 }
528
529 string tmpBackupDir = ResolveString("AutoBackupDir", ".", config, regionConfig);
530 if (state == null && tmpBackupDir != this.m_defaultState.BackupDir)
531 {
532 state = new AutoBackupModuleState();
533 }
534
535 if (state != null)
536 {
537 state.BackupDir = tmpBackupDir;
538 // Let's give the user some convenience and auto-mkdir
539 if (state.BackupDir != ".")
540 { 424 {
541 try 425 DirectoryInfo dirinfo = new DirectoryInfo(state.BackupDir);
426 if (!dirinfo.Exists)
542 { 427 {
543 DirectoryInfo dirinfo = new DirectoryInfo(state.BackupDir); 428 dirinfo.Create();
544 if (!dirinfo.Exists)
545 {
546 dirinfo.Create();
547 }
548 }
549 catch (Exception e)
550 {
551 m_log.Warn(
552 "[AUTO BACKUP]: BAD NEWS. You won't be able to save backups to directory " +
553 state.BackupDir +
554 " because it doesn't exist or there's a permissions issue with it. Here's the exception.",
555 e);
556 } 429 }
557 } 430 }
431 catch (Exception e)
432 {
433 m_log.Warn(
434 "[AUTO BACKUP]: BAD NEWS. You won't be able to save backups to directory " +
435 state.BackupDir +
436 " because it doesn't exist or there's a permissions issue with it:",
437 e);
438 }
558 } 439 }
559 440 state.BackupDir = tmpBackupDir;
560 if(state == null)
561 return m_defaultState;
562
563 return state; 441 return state;
564 } 442 }
565 443
@@ -584,128 +462,27 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
584 } 462 }
585 463
586 /// <summary> 464 /// <summary>
587 /// Helper function for ParseConfig.
588 /// </summary>
589 /// <param name="settingName"></param>
590 /// <param name="defaultValue"></param>
591 /// <param name="global"></param>
592 /// <param name="local"></param>
593 /// <returns></returns>
594 private double ResolveDouble(string settingName, double defaultValue, IConfig global, IConfig local)
595 {
596 if (local != null)
597 {
598 return local.GetDouble(settingName, global.GetDouble(settingName, defaultValue));
599 }
600 else
601 {
602 return global.GetDouble(settingName, defaultValue);
603 }
604 }
605
606 /// <summary>
607 /// Helper function for ParseConfig.
608 /// </summary>
609 /// <param name="settingName"></param>
610 /// <param name="defaultValue"></param>
611 /// <param name="global"></param>
612 /// <param name="local"></param>
613 /// <returns></returns>
614 private int ResolveInt(string settingName, int defaultValue, IConfig global, IConfig local)
615 {
616 if (local != null)
617 {
618 return local.GetInt(settingName, global.GetInt(settingName, defaultValue));
619 }
620 else
621 {
622 return global.GetInt(settingName, defaultValue);
623 }
624 }
625
626 /// <summary>
627 /// Helper function for ParseConfig.
628 /// </summary>
629 /// <param name="settingName"></param>
630 /// <param name="defaultValue"></param>
631 /// <param name="global"></param>
632 /// <param name="local"></param>
633 /// <returns></returns>
634 private string ResolveString(string settingName, string defaultValue, IConfig global, IConfig local)
635 {
636 if (local != null)
637 {
638 return local.GetString(settingName, global.GetString(settingName, defaultValue));
639 }
640 else
641 {
642 return global.GetString(settingName, defaultValue);
643 }
644 }
645
646 /// <summary>
647 /// Called when any auto-backup timer expires. This starts the code path for actually performing a backup. 465 /// Called when any auto-backup timer expires. This starts the code path for actually performing a backup.
648 /// </summary> 466 /// </summary>
649 /// <param name="sender"></param> 467 /// <param name="sender"></param>
650 /// <param name="e"></param> 468 /// <param name="e"></param>
651 private void HandleElapsed(object sender, ElapsedEventArgs e) 469 private void HandleElapsed(object sender, ElapsedEventArgs e)
652 { 470 {
653 // TODO: heuristic thresholds are per-region, so we should probably run heuristics once per region 471 if (!m_enabled || m_busy)
654 // XXX: Running heuristics once per region could add undue performance penalty for something that's supposed to
655 // check whether the region is too busy! Especially on sims with LOTS of regions.
656 // Alternative: make heuristics thresholds global to the module rather than per-region. Less flexible,
657 // but would allow us to be semantically correct while being easier on perf.
658 // Alternative 2: Run heuristics once per unique set of heuristics threshold parameters! Ay yi yi...
659 // Alternative 3: Don't support per-region heuristics at all; just accept them as a global only parameter.
660 // Since this is pretty experimental, I haven't decided which alternative makes the most sense.
661 if (this.m_closed)
662 {
663 return; 472 return;
664 } 473
665 bool heuristicsRun = false; 474 m_busy = true;
666 bool heuristicsPassed = false; 475 foreach (IScene scene in m_Scenes)
667 if (!this.m_timerMap.ContainsKey((Timer) sender))
668 { 476 {
669 m_log.Debug("[AUTO BACKUP]: Code-up error: timerMap doesn't contain timer " + sender); 477 if (!m_enabled)
478 return;
479 DoRegionBackup(scene);
670 } 480 }
671 481
672 List<IScene> tmap = this.m_timerMap[(Timer) sender]; 482 if (m_enabled)
673 if (tmap != null && tmap.Count > 0)
674 { 483 {
675 foreach (IScene scene in tmap) 484 m_masterTimer.Start();
676 { 485 m_busy = false;
677 AutoBackupModuleState state = this.m_states[scene];
678 bool heuristics = state.BusyCheck;
679
680 // Fast path: heuristics are on; already ran em; and sim is fine; OR, no heuristics for the region.
681 if ((heuristics && heuristicsRun && heuristicsPassed) || !heuristics)
682 {
683 this.DoRegionBackup(scene);
684 // Heuristics are on; ran but we're too busy -- keep going. Maybe another region will have heuristics off!
685 }
686 else if (heuristicsRun)
687 {
688 m_log.Info("[AUTO BACKUP]: Heuristics: too busy to backup " +
689 scene.RegionInfo.RegionName + " right now.");
690 continue;
691 // Logical Deduction: heuristics are on but haven't been run
692 }
693 else
694 {
695 heuristicsPassed = this.RunHeuristics(scene);
696 heuristicsRun = true;
697 if (!heuristicsPassed)
698 {
699 m_log.Info("[AUTO BACKUP]: Heuristics: too busy to backup " +
700 scene.RegionInfo.RegionName + " right now.");
701 continue;
702 }
703 this.DoRegionBackup(scene);
704 }
705
706 // Remove Old Backups
707 this.RemoveOldFiles(state);
708 }
709 } 486 }
710 } 487 }
711 488
@@ -723,8 +500,19 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
723 return; 500 return;
724 } 501 }
725 502
726 AutoBackupModuleState state = this.m_states[scene]; 503 m_busy = true;
504
505 AutoBackupModuleState state;
506 if(!m_states.TryGetValue(scene, out state))
507 return;
508
509 if(state == null || !state.Enabled)
510 return;
511
727 IRegionArchiverModule iram = scene.RequestModuleInterface<IRegionArchiverModule>(); 512 IRegionArchiverModule iram = scene.RequestModuleInterface<IRegionArchiverModule>();
513 if(iram == null)
514 return;
515
728 string savePath = BuildOarPath(scene.RegionInfo.RegionName, 516 string savePath = BuildOarPath(scene.RegionInfo.RegionName,
729 state.BackupDir, 517 state.BackupDir,
730 state.NamingType); 518 state.NamingType);
@@ -733,11 +521,8 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
733 m_log.Warn("[AUTO BACKUP]: savePath is null in HandleElapsed"); 521 m_log.Warn("[AUTO BACKUP]: savePath is null in HandleElapsed");
734 return; 522 return;
735 } 523 }
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);
740 524
525 Guid guid = Guid.NewGuid();
741 m_log.Info("[AUTO BACKUP]: Backing up region " + scene.RegionInfo.RegionName); 526 m_log.Info("[AUTO BACKUP]: Backing up region " + scene.RegionInfo.RegionName);
742 527
743 // Must pass options, even if dictionary is empty! 528 // Must pass options, even if dictionary is empty!
@@ -747,6 +532,7 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
747 options["noassets"] = true; 532 options["noassets"] = true;
748 533
749 iram.ArchiveRegion(savePath, guid, options); 534 iram.ArchiveRegion(savePath, guid, options);
535 ExecuteScript(state.Script, savePath);
750 } 536 }
751 537
752 // For the given state, remove backup files older than the states KeepFilesForDays property 538 // For the given state, remove backup files older than the states KeepFilesForDays property
@@ -774,23 +560,6 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
774 } 560 }
775 } 561 }
776 562
777 /// <summary>
778 /// Called by the Event Manager when the OnOarFileSaved event is fired.
779 /// </summary>
780 /// <param name="guid"></param>
781 /// <param name="message"></param>
782 void EventManager_OnOarFileSaved(Guid guid, string message)
783 {
784 // Ignore if the OAR save is being done by some other part of the system
785 if (m_pendingSaves.ContainsKey(guid))
786 {
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);
791 }
792 }
793
794 /// <summary>This format may turn out to be too unwieldy to keep... 563 /// <summary>This format may turn out to be too unwieldy to keep...
795 /// Besides, that's what ctimes are for. But then how do I name each file uniquely without using a GUID? 564 /// Besides, that's what ctimes are for. But then how do I name each file uniquely without using a GUID?
796 /// Sequential numbers, right? We support those, too!</summary> 565 /// Sequential numbers, right? We support those, too!</summary>
@@ -817,63 +586,6 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
817 return output; 586 return output;
818 } 587 }
819 588
820 /// <summary>Return value of true ==> not too busy; false ==> too busy to backup an OAR right now, or error.</summary>
821 private bool RunHeuristics(IScene region)
822 {
823 try
824 {
825 return this.RunTimeDilationHeuristic(region) && this.RunAgentLimitHeuristic(region);
826 }
827 catch (Exception e)
828 {
829 m_log.Warn("[AUTO BACKUP]: Exception in RunHeuristics", e);
830 return false;
831 }
832 }
833
834 /// <summary>
835 /// If the time dilation right at this instant is less than the threshold specified in AutoBackupDilationThreshold (default 0.5),
836 /// then we return false and trip the busy heuristic's "too busy" path (i.e. don't save an OAR).
837 /// AutoBackupDilationThreshold is a _LOWER BOUND_. Lower Time Dilation is bad, so if you go lower than our threshold, it's "too busy".
838 /// </summary>
839 /// <param name="region"></param>
840 /// <returns>Returns true if we're not too busy; false means we've got worse time dilation than the threshold.</returns>
841 private bool RunTimeDilationHeuristic(IScene region)
842 {
843 string regionName = region.RegionInfo.RegionName;
844 return region.TimeDilation >=
845 this.m_configSource.Configs["AutoBackupModule"].GetFloat(
846 regionName + ".AutoBackupDilationThreshold", 0.5f);
847 }
848
849 /// <summary>
850 /// If the root agent count right at this instant is less than the threshold specified in AutoBackupAgentThreshold (default 10),
851 /// then we return false and trip the busy heuristic's "too busy" path (i.e., don't save an OAR).
852 /// AutoBackupAgentThreshold is an _UPPER BOUND_. Higher Agent Count is bad, so if you go higher than our threshold, it's "too busy".
853 /// </summary>
854 /// <param name="region"></param>
855 /// <returns>Returns true if we're not too busy; false means we've got more agents on the sim than the threshold.</returns>
856 private bool RunAgentLimitHeuristic(IScene region)
857 {
858 string regionName = region.RegionInfo.RegionName;
859 try
860 {
861 Scene scene = (Scene) region;
862 // TODO: Why isn't GetRootAgentCount() a method in the IScene interface? Seems generally useful...
863 return scene.GetRootAgentCount() <=
864 this.m_configSource.Configs["AutoBackupModule"].GetInt(
865 regionName + ".AutoBackupAgentThreshold", 10);
866 }
867 catch (InvalidCastException ice)
868 {
869 m_log.Debug(
870 "[AUTO BACKUP]: I NEED MAINTENANCE: IScene is not a Scene; can't get root agent count!",
871 ice);
872 return true;
873 // Non-obstructionist safest answer...
874 }
875 }
876
877 /// <summary> 589 /// <summary>
878 /// Run the script or executable specified by the "AutoBackupScript" config setting. 590 /// Run the script or executable specified by the "AutoBackupScript" config setting.
879 /// Of course this is a security risk if you let anyone modify OpenSim.ini and they want to run some nasty bash script. 591 /// Of course this is a security risk if you let anyone modify OpenSim.ini and they want to run some nasty bash script.
@@ -920,18 +632,6 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
920 } 632 }
921 633
922 /// <summary> 634 /// <summary>
923 /// Quickly stop all timers from firing.
924 /// </summary>
925 private void StopAllTimers()
926 {
927 foreach (Timer t in this.m_timerMap.Keys)
928 {
929 t.Close();
930 }
931 this.m_closed = true;
932 }
933
934 /// <summary>
935 /// Determine the next unique filename by number, for "Sequential" AutoBackupNamingType. 635 /// Determine the next unique filename by number, for "Sequential" AutoBackupNamingType.
936 /// </summary> 636 /// </summary>
937 /// <param name="dirName"></param> 637 /// <param name="dirName"></param>
@@ -1033,5 +733,3 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup
1033 } 733 }
1034 } 734 }
1035} 735}
1036
1037