diff options
Diffstat (limited to 'OpenSim/Region/OptionalModules/World/AutoBackup')
-rw-r--r-- | OpenSim/Region/OptionalModules/World/AutoBackup/AutoBackupModule.cs | 502 |
1 files changed, 225 insertions, 277 deletions
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; | |||
73 | 73 | ||
74 | namespace OpenSim.Region.OptionalModules.World.AutoBackup | 74 | namespace OpenSim.Region.OptionalModules.World.AutoBackup |
75 | { | 75 | { |
76 | 76 | ||
77 | public enum NamingType | 77 | public enum NamingType |
78 | { | 78 | { |
79 | TIME, | 79 | TIME, |
80 | SEQUENTIAL, | 80 | SEQUENTIAL, |
81 | OVERWRITE | 81 | OVERWRITE |
82 | }; | 82 | } |
83 | 83 | ||
84 | public class AutoBackupModule : ISharedRegionModule, IRegionModuleBase | 84 | public class AutoBackupModule : ISharedRegionModule, IRegionModuleBase |
85 | { | 85 | { |
86 | 86 | ||
87 | private static readonly ILog m_log = | 87 | private static readonly ILog m_log = LogManager.GetLogger (MethodBase.GetCurrentMethod ().DeclaringType); |
88 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 88 | |
89 | |||
90 | //AutoBackupModuleState: Auto-Backup state for one region (scene). | 89 | //AutoBackupModuleState: Auto-Backup state for one region (scene). |
91 | public class AutoBackupModuleState | 90 | public class AutoBackupModuleState |
92 | { | 91 | { |
@@ -97,84 +96,87 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup | |||
97 | private bool m_busycheck = true; | 96 | private bool m_busycheck = true; |
98 | private string m_script = null; | 97 | private string m_script = null; |
99 | private string m_dir = "."; | 98 | private string m_dir = "."; |
100 | 99 | ||
101 | public AutoBackupModuleState(IScene scene) | 100 | public AutoBackupModuleState (IScene scene) |
102 | { | 101 | { |
103 | m_scene = scene; | 102 | m_scene = scene; |
104 | if(scene == null) | 103 | if (scene == null) |
105 | throw new NullReferenceException("Required parameter missing for AutoBackupModuleState constructor"); | 104 | throw new NullReferenceException ("Required parameter missing for AutoBackupModuleState constructor"); |
106 | } | 105 | } |
107 | 106 | ||
108 | public void SetEnabled(bool b) | 107 | public void SetEnabled (bool b) |
109 | { | 108 | { |
110 | m_enabled = b; | 109 | m_enabled = b; |
111 | } | 110 | } |
112 | 111 | ||
113 | public bool GetEnabled() | 112 | public bool GetEnabled () |
114 | { | 113 | { |
115 | return m_enabled; | 114 | return m_enabled; |
116 | } | 115 | } |
117 | 116 | ||
118 | public Timer GetTimer() | 117 | public Timer GetTimer () |
119 | { | 118 | { |
120 | return m_timer; | 119 | return m_timer; |
121 | } | 120 | } |
122 | 121 | ||
123 | public void SetTimer(Timer t) | 122 | public void SetTimer (Timer t) |
124 | { | 123 | { |
125 | m_timer = t; | 124 | m_timer = t; |
126 | } | 125 | } |
127 | 126 | ||
128 | public bool GetBusyCheck() | 127 | public bool GetBusyCheck () |
129 | { | 128 | { |
130 | return m_busycheck; | 129 | return m_busycheck; |
131 | } | 130 | } |
132 | 131 | ||
133 | public void SetBusyCheck(bool b) | 132 | public void SetBusyCheck (bool b) |
134 | { | 133 | { |
135 | m_busycheck = b; | 134 | m_busycheck = b; |
136 | } | 135 | } |
137 | 136 | ||
138 | 137 | ||
139 | public string GetScript() | 138 | public string GetScript () |
140 | { | 139 | { |
141 | return m_script; | 140 | return m_script; |
142 | } | 141 | } |
143 | 142 | ||
144 | public void SetScript(string s) | 143 | public void SetScript (string s) |
145 | { | 144 | { |
146 | m_script = s; | 145 | m_script = s; |
147 | } | 146 | } |
148 | 147 | ||
149 | public string GetBackupDir() | 148 | public string GetBackupDir () |
150 | { | 149 | { |
151 | return m_dir; | 150 | return m_dir; |
152 | } | 151 | } |
153 | 152 | ||
154 | public void SetBackupDir(string s) | 153 | public void SetBackupDir (string s) |
155 | { | 154 | { |
156 | m_dir = s; | 155 | m_dir = s; |
157 | } | 156 | } |
158 | 157 | ||
159 | public NamingType GetNamingType() | 158 | public NamingType GetNamingType () |
160 | { | 159 | { |
161 | return m_naming; | 160 | return m_naming; |
162 | } | 161 | } |
163 | 162 | ||
164 | public void SetNamingType(NamingType n) | 163 | public void SetNamingType (NamingType n) |
165 | { | 164 | { |
166 | m_naming = n; | 165 | m_naming = n; |
167 | } | 166 | } |
168 | } | 167 | } |
169 | 168 | ||
170 | //Save memory by setting low initial capacities. Minimizes impact in common cases of all regions using same interval, and instances hosting 1 ~ 4 regions. | 169 | //Save memory by setting low initial capacities. Minimizes impact in common cases of all regions using same interval, and instances hosting 1 ~ 4 regions. |
171 | //Also helps if you don't want AutoBackup at all | 170 | //Also helps if you don't want AutoBackup at all |
172 | readonly Dictionary<IScene, AutoBackupModuleState> states = new Dictionary<IScene, AutoBackupModuleState>(4); | 171 | readonly Dictionary<IScene, AutoBackupModuleState> states = new Dictionary<IScene, AutoBackupModuleState> (4); |
173 | readonly Dictionary<double, Timer> timers = new Dictionary<double, Timer>(1); | 172 | readonly Dictionary<double, Timer> timers = new Dictionary<double, Timer> (1); |
174 | readonly Dictionary<Timer, List<IScene>> timerMap = new Dictionary<Timer, List<IScene>>(1); | 173 | readonly Dictionary<Timer, List<IScene>> timerMap = new Dictionary<Timer, List<IScene>> (1); |
175 | private IConfigSource m_configSource = null; | 174 | private IConfigSource m_configSource = null; |
176 | private bool m_Enabled = false; //Whether the shared module should be enabled at all. NOT the same as m_Enabled in AutoBackupModuleState! | 175 | private bool m_Enabled = false; |
177 | 176 | //Whether the shared module should be enabled at all. NOT the same as m_Enabled in AutoBackupModuleState! | |
177 | private bool m_closed = false; | ||
178 | //True means IRegionModuleBase.Close() was called on us, and we should stop operation ASAP. | ||
179 | //Used to prevent elapsing timers after Close() is called from trying to start an autobackup while the sim is shutting down. | ||
178 | public AutoBackupModule () | 180 | public AutoBackupModule () |
179 | { | 181 | { |
180 | 182 | ||
@@ -185,24 +187,22 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup | |||
185 | { | 187 | { |
186 | //Determine if we have been enabled at all in OpenSim.ini -- this is part and parcel of being an optional module | 188 | //Determine if we have been enabled at all in OpenSim.ini -- this is part and parcel of being an optional module |
187 | m_configSource = source; | 189 | m_configSource = source; |
188 | IConfig moduleConfig = source.Configs["Modules"]; | 190 | IConfig moduleConfig = source.Configs["Modules"]; |
189 | if (moduleConfig != null) | 191 | if (moduleConfig != null) { |
190 | { | 192 | m_Enabled = moduleConfig.GetBoolean ("AutoBackupModule", false); |
191 | m_Enabled = moduleConfig.GetBoolean("AutoBackupModule", false); | 193 | if (m_Enabled) { |
192 | if (m_Enabled) | 194 | m_log.Info ("[AUTO BACKUP MODULE]: AutoBackupModule enabled"); |
193 | { | 195 | } |
194 | m_log.Info("[AUTO BACKUP MODULE]: AutoBackupModule enabled"); | 196 | } |
195 | } | ||
196 | } | ||
197 | } | 197 | } |
198 | 198 | ||
199 | void IRegionModuleBase.Close () | 199 | void IRegionModuleBase.Close () |
200 | { | 200 | { |
201 | if(!m_Enabled) | 201 | if (!m_Enabled) |
202 | return; | 202 | return; |
203 | 203 | ||
204 | //We don't want any timers firing while the sim's coming down; strange things may happen. | 204 | //We don't want any timers firing while the sim's coming down; strange things may happen. |
205 | StopAllTimers(); | 205 | StopAllTimers (); |
206 | } | 206 | } |
207 | 207 | ||
208 | void IRegionModuleBase.AddRegion (Framework.Scenes.Scene scene) | 208 | void IRegionModuleBase.AddRegion (Framework.Scenes.Scene scene) |
@@ -212,327 +212,286 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup | |||
212 | 212 | ||
213 | void IRegionModuleBase.RemoveRegion (Framework.Scenes.Scene scene) | 213 | void IRegionModuleBase.RemoveRegion (Framework.Scenes.Scene scene) |
214 | { | 214 | { |
215 | if(!m_Enabled) | 215 | if (!m_Enabled) |
216 | return; | 216 | return; |
217 | 217 | ||
218 | AutoBackupModuleState abms = states[scene]; | 218 | AutoBackupModuleState abms = states[scene]; |
219 | Timer timer = abms.GetTimer(); | 219 | Timer timer = abms.GetTimer (); |
220 | List<IScene> list = timerMap[timer]; | 220 | List<IScene> list = timerMap[timer]; |
221 | list.Remove(scene); | 221 | list.Remove (scene); |
222 | if(list.Count == 0) | 222 | if (list.Count == 0) { |
223 | { | 223 | timerMap.Remove (timer); |
224 | timerMap.Remove(timer); | 224 | timers.Remove (timer.Interval); |
225 | timers.Remove(timer.Interval); | 225 | timer.Close (); |
226 | timer.Close(); | ||
227 | } | 226 | } |
228 | } | 227 | } |
229 | 228 | ||
230 | void IRegionModuleBase.RegionLoaded (Framework.Scenes.Scene scene) | 229 | void IRegionModuleBase.RegionLoaded (Framework.Scenes.Scene scene) |
231 | { | 230 | { |
232 | if(!m_Enabled) | 231 | if (!m_Enabled) |
233 | return; | 232 | return; |
234 | 233 | ||
235 | //This really ought not to happen, but just in case, let's pretend it didn't... | 234 | //This really ought not to happen, but just in case, let's pretend it didn't... |
236 | if(scene == null) | 235 | if (scene == null) |
237 | return; | 236 | return; |
238 | 237 | ||
239 | string sRegionName = scene.RegionInfo.RegionName; | 238 | string sRegionName = scene.RegionInfo.RegionName; |
240 | AutoBackupModuleState st = new AutoBackupModuleState(scene); | 239 | AutoBackupModuleState st = new AutoBackupModuleState (scene); |
241 | states.Add(scene, st); | 240 | states.Add (scene, st); |
242 | 241 | ||
243 | //Read the config settings and set variables. | 242 | //Read the config settings and set variables. |
244 | IConfig config = m_configSource.Configs["AutoBackupModule"]; | 243 | IConfig config = m_configSource.Configs["AutoBackupModule"]; |
245 | if(config == null) | 244 | if (config == null) { |
246 | { | ||
247 | //No config settings for any regions, let's just give up. | 245 | //No config settings for any regions, let's just give up. |
248 | st.SetEnabled(false); | 246 | st.SetEnabled (false); |
249 | m_log.Info("[AUTO BACKUP MODULE]: Region " + sRegionName + " is NOT AutoBackup enabled."); | 247 | m_log.Info ("[AUTO BACKUP MODULE]: Region " + sRegionName + " is NOT AutoBackup enabled."); |
250 | return; | 248 | return; |
251 | } | 249 | } |
252 | st.SetEnabled(config.GetBoolean(sRegionName + ".AutoBackup", false)); | 250 | st.SetEnabled (config.GetBoolean (sRegionName + ".AutoBackup", false)); |
253 | if(!st.GetEnabled()) //If you don't want AutoBackup, we stop. | 251 | //If you don't want AutoBackup, we stop. |
254 | { | 252 | if (!st.GetEnabled ()) { |
255 | m_log.Info("[AUTO BACKUP MODULE]: Region " + sRegionName + " is NOT AutoBackup enabled."); | 253 | m_log.Info ("[AUTO BACKUP MODULE]: Region " + sRegionName + " is NOT AutoBackup enabled."); |
256 | return; | 254 | return; |
257 | } | 255 | } else { |
258 | else | 256 | m_log.Info ("[AUTO BACKUP MODULE]: Region " + sRegionName + " is AutoBackup ENABLED."); |
259 | { | ||
260 | m_log.Info("[AUTO BACKUP MODULE]: Region " + sRegionName + " is AutoBackup ENABLED."); | ||
261 | } | 257 | } |
262 | 258 | ||
263 | //Borrow an existing timer if one exists for the same interval; otherwise, make a new one. | 259 | //Borrow an existing timer if one exists for the same interval; otherwise, make a new one. |
264 | double interval = config.GetDouble(sRegionName + ".AutoBackupInterval", 720) * 60000; | 260 | double interval = config.GetDouble (sRegionName + ".AutoBackupInterval", 720) * 60000; |
265 | if(timers.ContainsKey(interval)) | 261 | if (timers.ContainsKey (interval)) { |
266 | { | 262 | st.SetTimer (timers[interval]); |
267 | st.SetTimer(timers[interval]); | 263 | m_log.Debug ("[AUTO BACKUP MODULE]: Reusing timer for " + interval + " msec for region " + sRegionName); |
268 | m_log.Debug("[AUTO BACKUP MODULE]: Reusing timer for " + interval + " msec for region " + sRegionName); | 264 | } else { |
269 | } | ||
270 | else | ||
271 | { | ||
272 | //0 or negative interval == do nothing. | 265 | //0 or negative interval == do nothing. |
273 | if(interval <= 0.0) | 266 | if (interval <= 0.0) { |
274 | { | 267 | st.SetEnabled (false); |
275 | st.SetEnabled(false); | ||
276 | return; | 268 | return; |
277 | } | 269 | } |
278 | Timer tim = new Timer(interval); | 270 | Timer tim = new Timer (interval); |
279 | st.SetTimer(tim); //Milliseconds -> minutes | 271 | st.SetTimer (tim); |
280 | timers.Add(interval, tim); | 272 | //Milliseconds -> minutes |
281 | tim.Elapsed += HandleElapsed; | 273 | timers.Add (interval, tim); |
274 | tim.Elapsed += HandleElapsed; | ||
282 | tim.AutoReset = true; | 275 | tim.AutoReset = true; |
283 | tim.Start(); | 276 | tim.Start (); |
284 | //m_log.Debug("[AUTO BACKUP MODULE]: New timer for " + interval + " msec for region " + sRegionName); | 277 | //m_log.Debug("[AUTO BACKUP MODULE]: New timer for " + interval + " msec for region " + sRegionName); |
285 | } | 278 | } |
286 | 279 | ||
287 | //Add the current region to the list of regions tied to this timer. | 280 | //Add the current region to the list of regions tied to this timer. |
288 | if(timerMap.ContainsKey(st.GetTimer())) | 281 | if (timerMap.ContainsKey (st.GetTimer ())) { |
289 | { | 282 | timerMap[st.GetTimer ()].Add (scene); |
290 | timerMap[st.GetTimer()].Add(scene); | 283 | } else { |
291 | } | 284 | List<IScene> scns = new List<IScene> (1); |
292 | else | 285 | scns.Add (scene); |
293 | { | 286 | timerMap.Add (st.GetTimer (), scns); |
294 | List<IScene> scns = new List<IScene>(1); | ||
295 | scns.Add(scene); | ||
296 | timerMap.Add(st.GetTimer(), scns); | ||
297 | } | 287 | } |
298 | 288 | ||
299 | st.SetBusyCheck(config.GetBoolean(sRegionName + ".AutoBackupBusyCheck", true)); | 289 | st.SetBusyCheck (config.GetBoolean (sRegionName + ".AutoBackupBusyCheck", true)); |
300 | 290 | ||
301 | //Set file naming algorithm | 291 | //Set file naming algorithm |
302 | string namingtype = config.GetString(sRegionName + ".AutoBackupNaming", "Time"); | 292 | string namingtype = config.GetString (sRegionName + ".AutoBackupNaming", "Time"); |
303 | if(namingtype.Equals("Time", StringComparison.CurrentCultureIgnoreCase)) | 293 | if (namingtype.Equals ("Time", StringComparison.CurrentCultureIgnoreCase)) { |
304 | { | 294 | st.SetNamingType (NamingType.TIME); |
305 | st.SetNamingType(NamingType.TIME); | 295 | } else if (namingtype.Equals ("Sequential", StringComparison.CurrentCultureIgnoreCase)) { |
306 | } | 296 | st.SetNamingType (NamingType.SEQUENTIAL); |
307 | else if(namingtype.Equals("Sequential", StringComparison.CurrentCultureIgnoreCase)) | 297 | } else if (namingtype.Equals ("Overwrite", StringComparison.CurrentCultureIgnoreCase)) { |
308 | { | 298 | st.SetNamingType (NamingType.OVERWRITE); |
309 | st.SetNamingType(NamingType.SEQUENTIAL); | 299 | } else { |
310 | } | 300 | m_log.Warn ("Unknown naming type specified for region " + scene.RegionInfo.RegionName + ": " + namingtype); |
311 | else if(namingtype.Equals("Overwrite", StringComparison.CurrentCultureIgnoreCase)) | 301 | st.SetNamingType (NamingType.TIME); |
312 | { | ||
313 | st.SetNamingType(NamingType.OVERWRITE); | ||
314 | } | ||
315 | else | ||
316 | { | ||
317 | m_log.Warn("Unknown naming type specified for region " + scene.RegionInfo.RegionName + ": " + namingtype); | ||
318 | st.SetNamingType(NamingType.TIME); | ||
319 | } | 302 | } |
320 | 303 | ||
321 | st.SetScript(config.GetString(sRegionName + ".AutoBackupScript", null)); | 304 | st.SetScript (config.GetString (sRegionName + ".AutoBackupScript", null)); |
322 | st.SetBackupDir(config.GetString(sRegionName + ".AutoBackupDir", ".")); | 305 | st.SetBackupDir (config.GetString (sRegionName + ".AutoBackupDir", ".")); |
323 | 306 | ||
324 | //Let's give the user *one* convenience and auto-mkdir | 307 | //Let's give the user *one* convenience and auto-mkdir |
325 | if(st.GetBackupDir() != ".") | 308 | if (st.GetBackupDir () != ".") { |
326 | { | 309 | try { |
327 | try | 310 | DirectoryInfo dirinfo = new DirectoryInfo (st.GetBackupDir ()); |
328 | { | 311 | if (!dirinfo.Exists) { |
329 | DirectoryInfo dirinfo = new DirectoryInfo(st.GetBackupDir()); | 312 | dirinfo.Create (); |
330 | if(!dirinfo.Exists) | ||
331 | { | ||
332 | dirinfo.Create(); | ||
333 | } | 313 | } |
334 | } | 314 | } catch (Exception e) { |
335 | catch(Exception e) | 315 | 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); |
336 | { | ||
337 | m_log.Warn("BAD NEWS. You won't be able to save backups to directory " + st.GetBackupDir() + | ||
338 | " because it doesn't exist or there's a permissions issue with it. Here's the exception.", e); | ||
339 | } | 316 | } |
340 | } | 317 | } |
341 | } | 318 | } |
342 | 319 | ||
343 | void HandleElapsed (object sender, ElapsedEventArgs e) | 320 | void HandleElapsed (object sender, ElapsedEventArgs e) |
344 | { | 321 | { |
322 | if (m_closed) | ||
323 | return; | ||
345 | bool heuristicsRun = false; | 324 | bool heuristicsRun = false; |
346 | bool heuristicsPassed = false; | 325 | bool heuristicsPassed = false; |
347 | if(!timerMap.ContainsKey((Timer) sender)) | 326 | if (!timerMap.ContainsKey ((Timer)sender)) { |
348 | { | 327 | m_log.Debug ("Code-up error: timerMap doesn't contain timer " + sender.ToString ()); |
349 | m_log.Debug("Code-up error: timerMap doesn't contain timer " + sender.ToString()); | ||
350 | } | 328 | } |
351 | foreach(IScene scene in timerMap[(Timer)sender]) | 329 | foreach (IScene scene in timerMap[(Timer)sender]) { |
352 | { | ||
353 | AutoBackupModuleState state = states[scene]; | 330 | AutoBackupModuleState state = states[scene]; |
354 | bool heuristics = state.GetBusyCheck(); | 331 | bool heuristics = state.GetBusyCheck (); |
355 | 332 | ||
356 | //Fast path: heuristics are on; already ran em; and sim is fine; OR, no heuristics for the region. | 333 | //Fast path: heuristics are on; already ran em; and sim is fine; OR, no heuristics for the region. |
357 | if((heuristics && heuristicsRun && heuristicsPassed) | 334 | if ((heuristics && heuristicsRun && heuristicsPassed) || !heuristics) { |
358 | || !heuristics) | 335 | doRegionBackup (scene); |
359 | { | ||
360 | doRegionBackup(scene); | ||
361 | } | ||
362 | //Heuristics are on; ran but we're too busy -- keep going. Maybe another region will have heuristics off! | 336 | //Heuristics are on; ran but we're too busy -- keep going. Maybe another region will have heuristics off! |
363 | else if(heuristics && heuristicsRun && !heuristicsPassed) | 337 | } else if (heuristics && heuristicsRun && !heuristicsPassed) { |
364 | { | ||
365 | continue; | 338 | continue; |
366 | } | ||
367 | //Logical Deduction: heuristics are on but haven't been run | 339 | //Logical Deduction: heuristics are on but haven't been run |
368 | else | 340 | } else { |
369 | { | 341 | heuristicsPassed = RunHeuristics (); |
370 | heuristicsPassed = RunHeuristics(); | ||
371 | heuristicsRun = true; | 342 | heuristicsRun = true; |
372 | if(!heuristicsPassed) | 343 | if (!heuristicsPassed) |
373 | continue; | 344 | continue; |
374 | doRegionBackup(scene); | 345 | doRegionBackup (scene); |
375 | } | 346 | } |
376 | } | 347 | } |
377 | } | 348 | } |
378 | 349 | ||
379 | void doRegionBackup(IScene scene) | 350 | void doRegionBackup (IScene scene) |
380 | { | 351 | { |
381 | AutoBackupModuleState state = states[scene]; | 352 | AutoBackupModuleState state = states[scene]; |
382 | IRegionArchiverModule iram = scene.RequestModuleInterface<IRegionArchiverModule>(); | 353 | IRegionArchiverModule iram = scene.RequestModuleInterface<IRegionArchiverModule> (); |
383 | string savePath = BuildOarPath(scene.RegionInfo.RegionName, state.GetBackupDir(), state.GetNamingType()); | 354 | string savePath = BuildOarPath (scene.RegionInfo.RegionName, state.GetBackupDir (), state.GetNamingType ()); |
384 | //m_log.Debug("[AUTO BACKUP MODULE]: savePath = " + savePath); | 355 | //m_log.Debug("[AUTO BACKUP MODULE]: savePath = " + savePath); |
385 | if(savePath == null) | 356 | if (savePath == null) { |
386 | { | 357 | m_log.Warn ("[AUTO BACKUP MODULE]: savePath is null in HandleElapsed"); |
387 | m_log.Warn("[AUTO BACKUP MODULE]: savePath is null in HandleElapsed"); | ||
388 | return; | 358 | return; |
389 | } | 359 | } |
390 | iram.ArchiveRegion(savePath, null); | 360 | iram.ArchiveRegion (savePath, null); |
391 | ExecuteScript(state.GetScript(), savePath); | 361 | ExecuteScript (state.GetScript (), savePath); |
392 | } | 362 | } |
393 | 363 | ||
394 | string IRegionModuleBase.Name { | 364 | string IRegionModuleBase.Name { |
395 | get { | 365 | get { return "AutoBackupModule"; } |
396 | return "AutoBackupModule"; | ||
397 | } | ||
398 | } | 366 | } |
399 | 367 | ||
400 | Type IRegionModuleBase.ReplaceableInterface { | 368 | Type IRegionModuleBase.ReplaceableInterface { |
401 | get { | 369 | get { return null; } |
402 | return null; | ||
403 | } | ||
404 | } | 370 | } |
405 | 371 | ||
406 | #endregion | 372 | #endregion |
407 | #region ISharedRegionModule implementation | 373 | #region ISharedRegionModule implementation |
408 | void ISharedRegionModule.PostInitialise () | 374 | void ISharedRegionModule.PostInitialise () |
409 | { | 375 | { |
410 | //I don't care right now. | 376 | //I don't care right now. |
411 | } | 377 | } |
412 | 378 | ||
413 | #endregion | 379 | #endregion |
414 | 380 | ||
415 | //Is this even needed? | 381 | //Is this even needed? |
416 | public bool IsSharedModule | 382 | public bool IsSharedModule { |
417 | { | 383 | get { return true; } |
418 | get { return true; } | 384 | } |
419 | } | 385 | |
420 | 386 | private string BuildOarPath (string regionName, string baseDir, NamingType naming) | |
421 | private string BuildOarPath(string regionName, string baseDir, NamingType naming) | ||
422 | { | 387 | { |
423 | FileInfo path = null; | 388 | FileInfo path = null; |
424 | switch(naming) | 389 | switch (naming) { |
425 | { | ||
426 | case NamingType.OVERWRITE: | 390 | case NamingType.OVERWRITE: |
427 | path = new FileInfo(baseDir + Path.DirectorySeparatorChar + regionName); | 391 | path = new FileInfo (baseDir + Path.DirectorySeparatorChar + regionName); |
428 | return path.FullName; | 392 | return path.FullName; |
429 | case NamingType.TIME: | 393 | case NamingType.TIME: |
430 | path = new FileInfo(baseDir + Path.DirectorySeparatorChar + regionName + GetTimeString() + ".oar"); | 394 | path = new FileInfo (baseDir + Path.DirectorySeparatorChar + regionName + GetTimeString () + ".oar"); |
431 | return path.FullName; | 395 | return path.FullName; |
432 | case NamingType.SEQUENTIAL: | 396 | case NamingType.SEQUENTIAL: |
433 | path = new FileInfo(GetNextFile(baseDir, regionName)); | 397 | path = new FileInfo (GetNextFile (baseDir, regionName)); |
434 | return path.FullName; | 398 | return path.FullName; |
435 | default: | 399 | default: |
436 | m_log.Warn("VERY BAD: Unhandled case element " + naming.ToString()); | 400 | m_log.Warn ("VERY BAD: Unhandled case element " + naming.ToString ()); |
437 | break; | 401 | break; |
438 | } | 402 | } |
439 | 403 | ||
440 | return path.FullName; | 404 | return path.FullName; |
441 | } | 405 | } |
442 | 406 | ||
443 | //Welcome to the TIME STRING. 4 CORNER INTEGERS, CUBES 4 QUAD MEMORY -- No 1 Integer God. | 407 | //Welcome to the TIME STRING. 4 CORNER INTEGERS, CUBES 4 QUAD MEMORY -- No 1 Integer God. |
444 | //(Terrible reference to <timecube.com>) | 408 | //(Terrible reference to <timecube.com>) |
445 | //This format may turn out to be too unwieldy to keep... | 409 | //This format may turn out to be too unwieldy to keep... |
446 | //Besides, that's what ctimes are for. But then how do I name each file uniquely without using a GUID? | 410 | //Besides, that's what ctimes are for. But then how do I name each file uniquely without using a GUID? |
447 | //Sequential numbers, right? Ugh. Almost makes TOO much sense. | 411 | //Sequential numbers, right? Ugh. Almost makes TOO much sense. |
448 | private string GetTimeString() | 412 | private string GetTimeString () |
449 | { | 413 | { |
450 | StringWriter sw = new StringWriter(); | 414 | StringWriter sw = new StringWriter (); |
451 | sw.Write("_"); | 415 | sw.Write ("_"); |
452 | DateTime now = DateTime.Now; | 416 | DateTime now = DateTime.Now; |
453 | sw.Write(now.Year); | 417 | sw.Write (now.Year); |
454 | sw.Write("y_"); | 418 | sw.Write ("y_"); |
455 | sw.Write(now.Month); | 419 | sw.Write (now.Month); |
456 | sw.Write("M_"); | 420 | sw.Write ("M_"); |
457 | sw.Write(now.Day); | 421 | sw.Write (now.Day); |
458 | sw.Write("d_"); | 422 | sw.Write ("d_"); |
459 | sw.Write(now.Hour); | 423 | sw.Write (now.Hour); |
460 | sw.Write("h_"); | 424 | sw.Write ("h_"); |
461 | sw.Write(now.Minute); | 425 | sw.Write (now.Minute); |
462 | sw.Write("m_"); | 426 | sw.Write ("m_"); |
463 | sw.Write(now.Second); | 427 | sw.Write (now.Second); |
464 | sw.Write("s"); | 428 | sw.Write ("s"); |
465 | sw.Flush(); | 429 | sw.Flush (); |
466 | string output = sw.ToString(); | 430 | string output = sw.ToString (); |
467 | sw.Close(); | 431 | sw.Close (); |
468 | return output; | 432 | return output; |
469 | } | 433 | } |
470 | 434 | ||
471 | //Get the next logical file name | 435 | //Get the next logical file name |
472 | //I really shouldn't put fields here, but for now.... ;) | 436 | //I really shouldn't put fields here, but for now.... ;) |
473 | private string m_dirName = null; | 437 | private string m_dirName = null; |
474 | private string m_regionName = null; | 438 | private string m_regionName = null; |
475 | private string GetNextFile(string dirName, string regionName) | 439 | private string GetNextFile (string dirName, string regionName) |
476 | { | 440 | { |
477 | FileInfo uniqueFile = null; | 441 | FileInfo uniqueFile = null; |
478 | m_dirName = dirName; | 442 | m_dirName = dirName; |
479 | m_regionName = regionName; | 443 | m_regionName = regionName; |
480 | long biggestExistingFile = HalfIntervalMaximize(1, FileExistsTest); | 444 | long biggestExistingFile = HalfIntervalMaximize (1, FileExistsTest); |
481 | biggestExistingFile++; //We don't want to overwrite the biggest existing file; we want to write to the NEXT biggest. | 445 | biggestExistingFile++; |
482 | 446 | //We don't want to overwrite the biggest existing file; we want to write to the NEXT biggest. | |
483 | uniqueFile = new FileInfo(m_dirName + Path.DirectorySeparatorChar + m_regionName + "_" + biggestExistingFile + ".oar"); | 447 | uniqueFile = new FileInfo (m_dirName + Path.DirectorySeparatorChar + m_regionName + "_" + biggestExistingFile + ".oar"); |
484 | if(uniqueFile.Exists) | 448 | if (uniqueFile.Exists) { |
485 | { | ||
486 | //Congratulations, your strange deletion patterns fooled my half-interval search into picking an existing file! | 449 | //Congratulations, your strange deletion patterns fooled my half-interval search into picking an existing file! |
487 | //Now you get to pay the performance cost :) | 450 | //Now you get to pay the performance cost :) |
488 | uniqueFile = UniqueFileSearchLinear(biggestExistingFile); | 451 | uniqueFile = UniqueFileSearchLinear (biggestExistingFile); |
489 | } | 452 | } |
490 | 453 | ||
491 | return uniqueFile.FullName; | 454 | return uniqueFile.FullName; |
492 | } | 455 | } |
493 | 456 | ||
494 | private bool RunHeuristics() | 457 | private bool RunHeuristics () |
495 | { | 458 | { |
496 | return true; | 459 | return true; |
497 | } | 460 | } |
498 | 461 | ||
499 | private void ExecuteScript(string scriptName, string savePath) | 462 | private void ExecuteScript (string scriptName, string savePath) |
500 | { | 463 | { |
501 | //Fast path out | 464 | //Fast path out |
502 | if(scriptName == null || scriptName.Length <= 0) | 465 | if (scriptName == null || scriptName.Length <= 0) |
503 | return; | 466 | return; |
504 | 467 | ||
505 | try | 468 | try { |
506 | { | 469 | FileInfo fi = new FileInfo (scriptName); |
507 | FileInfo fi = new FileInfo(scriptName); | 470 | if (fi.Exists) { |
508 | if(fi.Exists) | 471 | ProcessStartInfo psi = new ProcessStartInfo (scriptName); |
509 | { | ||
510 | ProcessStartInfo psi = new ProcessStartInfo(scriptName); | ||
511 | psi.Arguments = savePath; | 472 | psi.Arguments = savePath; |
512 | psi.CreateNoWindow = true; | 473 | psi.CreateNoWindow = true; |
513 | Process proc = Process.Start(psi); | 474 | Process proc = Process.Start (psi); |
514 | proc.ErrorDataReceived += HandleProcErrorDataReceived; | 475 | proc.ErrorDataReceived += HandleProcErrorDataReceived; |
515 | } | 476 | } |
516 | } | 477 | } catch (Exception e) { |
517 | catch(Exception e) | 478 | m_log.Warn ("Exception encountered when trying to run script for oar backup " + savePath, e); |
518 | { | ||
519 | m_log.Warn("Exception encountered when trying to run script for oar backup " + savePath, e); | ||
520 | } | 479 | } |
521 | } | 480 | } |
522 | 481 | ||
523 | void HandleProcErrorDataReceived (object sender, DataReceivedEventArgs e) | 482 | void HandleProcErrorDataReceived (object sender, DataReceivedEventArgs e) |
524 | { | 483 | { |
525 | m_log.Warn("ExecuteScript hook " + ((Process)sender).ProcessName + " is yacking on stderr: " + e.Data); | 484 | m_log.Warn ("ExecuteScript hook " + ((Process)sender).ProcessName + " is yacking on stderr: " + e.Data); |
526 | } | 485 | } |
527 | 486 | ||
528 | private void StopAllTimers() | 487 | private void StopAllTimers () |
529 | { | 488 | { |
530 | foreach(Timer t in timerMap.Keys) | 489 | foreach (Timer t in timerMap.Keys) { |
531 | { | 490 | t.Close (); |
532 | t.Close(); | ||
533 | } | 491 | } |
492 | m_closed = true; | ||
534 | } | 493 | } |
535 | 494 | ||
536 | /* Find the largest value for which the predicate returns true. | 495 | /* Find the largest value for which the predicate returns true. |
537 | * We use a bisection algorithm (half interval) to make the algorithm scalable. | 496 | * We use a bisection algorithm (half interval) to make the algorithm scalable. |
538 | * The worst-case complexity is about O(log(n)^2) in practice. | 497 | * The worst-case complexity is about O(log(n)^2) in practice. |
@@ -542,65 +501,54 @@ namespace OpenSim.Region.OptionalModules.World.AutoBackup | |||
542 | * And of course it is fantastic with powers of 2, which are densely packed in values under 100 anyway. | 501 | * And of course it is fantastic with powers of 2, which are densely packed in values under 100 anyway. |
543 | * The Predicate<long> parameter must be a function that accepts a long and returns a bool. | 502 | * The Predicate<long> parameter must be a function that accepts a long and returns a bool. |
544 | * */ | 503 | * */ |
545 | public long HalfIntervalMaximize(long start, Predicate<long> pred) | 504 | public long HalfIntervalMaximize (long start, Predicate<long> pred) |
546 | { | 505 | { |
547 | long prev = start, curr = start, biggest = 0; | 506 | long prev = start, curr = start, biggest = 0; |
548 | 507 | ||
549 | if(start < 0) | 508 | if (start < 0) |
550 | throw new IndexOutOfRangeException("Start value for HalfIntervalMaximize must be non-negative"); | 509 | throw new IndexOutOfRangeException ("Start value for HalfIntervalMaximize must be non-negative"); |
551 | 510 | ||
552 | do | 511 | do { |
553 | { | 512 | if (pred (curr)) { |
554 | if(pred(curr)) | 513 | if (curr > biggest) { |
555 | { | ||
556 | if(curr > biggest) | ||
557 | { | ||
558 | biggest = curr; | 514 | biggest = curr; |
559 | } | 515 | } |
560 | prev = curr; | 516 | prev = curr; |
561 | if(curr == 0) | 517 | if (curr == 0) { |
562 | { | ||
563 | //Special case because 0 * 2 = 0 :) | 518 | //Special case because 0 * 2 = 0 :) |
564 | curr = 1; | 519 | curr = 1; |
565 | } | 520 | } else { |
566 | else | ||
567 | { | ||
568 | //Look deeper | 521 | //Look deeper |
569 | curr *= 2; | 522 | curr *= 2; |
570 | } | 523 | } |
571 | } | 524 | } else { |
572 | else | ||
573 | { | ||
574 | // We went too far, back off halfway | 525 | // We went too far, back off halfway |
575 | curr = (curr + prev) / 2; | 526 | curr = (curr + prev) / 2; |
576 | } | 527 | } |
577 | } | 528 | } while (curr - prev > 0); |
578 | while(curr - prev > 0); | ||
579 | 529 | ||
580 | return biggest; | 530 | return biggest; |
581 | } | 531 | } |
582 | 532 | ||
583 | public bool FileExistsTest(long num) | 533 | public bool FileExistsTest (long num) |
584 | { | 534 | { |
585 | FileInfo test = new FileInfo(m_dirName + Path.DirectorySeparatorChar + m_regionName + "_" + num + ".oar"); | 535 | FileInfo test = new FileInfo (m_dirName + Path.DirectorySeparatorChar + m_regionName + "_" + num + ".oar"); |
586 | return test.Exists; | 536 | return test.Exists; |
587 | } | 537 | } |
588 | 538 | ||
589 | 539 | ||
590 | //Very slow, hence why we try the HalfIntervalMaximize first! | 540 | //Very slow, hence why we try the HalfIntervalMaximize first! |
591 | public FileInfo UniqueFileSearchLinear(long start) | 541 | public FileInfo UniqueFileSearchLinear (long start) |
592 | { | 542 | { |
593 | long l = start; | 543 | long l = start; |
594 | FileInfo retval = null; | 544 | FileInfo retval = null; |
595 | do | 545 | do { |
596 | { | 546 | retval = new FileInfo (m_dirName + Path.DirectorySeparatorChar + m_regionName + "_" + (l++) + ".oar"); |
597 | retval = new FileInfo(m_dirName + Path.DirectorySeparatorChar + m_regionName + "_" + (l++) + ".oar"); | 547 | } while (retval.Exists); |
598 | } | ||
599 | while(retval.Exists); | ||
600 | 548 | ||
601 | return retval; | 549 | return retval; |
602 | } | 550 | } |
603 | } | 551 | } |
604 | 552 | ||
605 | } | 553 | } |
606 | 554 | ||