diff options
Diffstat (limited to 'OpenSim/Framework/Servers/BaseOpenSimServer.cs')
-rw-r--r-- | OpenSim/Framework/Servers/BaseOpenSimServer.cs | 510 |
1 files changed, 510 insertions, 0 deletions
diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs new file mode 100644 index 0000000..7ab5c33 --- /dev/null +++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs | |||
@@ -0,0 +1,510 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using System.Reflection; | ||
32 | using System.Text; | ||
33 | using System.Threading; | ||
34 | using System.Timers; | ||
35 | using log4net; | ||
36 | using log4net.Appender; | ||
37 | using log4net.Core; | ||
38 | using log4net.Repository; | ||
39 | using OpenSim.Framework.Console; | ||
40 | using OpenSim.Framework.Servers; | ||
41 | using OpenSim.Framework.Servers.HttpServer; | ||
42 | using OpenSim.Framework.Statistics; | ||
43 | using Timer=System.Timers.Timer; | ||
44 | |||
45 | using OpenMetaverse; | ||
46 | using OpenMetaverse.StructuredData; | ||
47 | |||
48 | |||
49 | namespace OpenSim.Framework.Servers | ||
50 | { | ||
51 | /// <summary> | ||
52 | /// Common base for the main OpenSimServers (user, grid, inventory, region, etc) | ||
53 | /// </summary> | ||
54 | public abstract class BaseOpenSimServer | ||
55 | { | ||
56 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
57 | |||
58 | /// <summary> | ||
59 | /// This will control a periodic log printout of the current 'show stats' (if they are active) for this | ||
60 | /// server. | ||
61 | /// </summary> | ||
62 | private Timer m_periodicDiagnosticsTimer = new Timer(60 * 60 * 1000); | ||
63 | |||
64 | protected CommandConsole m_console; | ||
65 | protected OpenSimAppender m_consoleAppender; | ||
66 | protected IAppender m_logFileAppender = null; | ||
67 | |||
68 | /// <summary> | ||
69 | /// Time at which this server was started | ||
70 | /// </summary> | ||
71 | protected DateTime m_startuptime; | ||
72 | |||
73 | /// <summary> | ||
74 | /// Record the initial startup directory for info purposes | ||
75 | /// </summary> | ||
76 | protected string m_startupDirectory = Environment.CurrentDirectory; | ||
77 | |||
78 | /// <summary> | ||
79 | /// Server version information. Usually VersionInfo + information about svn revision, operating system, etc. | ||
80 | /// </summary> | ||
81 | protected string m_version; | ||
82 | |||
83 | protected string m_pidFile = String.Empty; | ||
84 | |||
85 | /// <summary> | ||
86 | /// Random uuid for private data | ||
87 | /// </summary> | ||
88 | protected string m_osSecret = String.Empty; | ||
89 | |||
90 | protected BaseHttpServer m_httpServer; | ||
91 | public BaseHttpServer HttpServer | ||
92 | { | ||
93 | get { return m_httpServer; } | ||
94 | } | ||
95 | |||
96 | /// <summary> | ||
97 | /// Holds the non-viewer statistics collection object for this service/server | ||
98 | /// </summary> | ||
99 | protected IStatsCollector m_stats; | ||
100 | |||
101 | public BaseOpenSimServer() | ||
102 | { | ||
103 | m_startuptime = DateTime.Now; | ||
104 | m_version = VersionInfo.Version; | ||
105 | |||
106 | // Random uuid for private data | ||
107 | m_osSecret = UUID.Random().ToString(); | ||
108 | |||
109 | m_periodicDiagnosticsTimer.Elapsed += new ElapsedEventHandler(LogDiagnostics); | ||
110 | m_periodicDiagnosticsTimer.Enabled = true; | ||
111 | |||
112 | // Add ourselves to thread monitoring. This thread will go on to become the console listening thread | ||
113 | Thread.CurrentThread.Name = "ConsoleThread"; | ||
114 | ThreadTracker.Add(Thread.CurrentThread); | ||
115 | |||
116 | ILoggerRepository repository = LogManager.GetRepository(); | ||
117 | IAppender[] appenders = repository.GetAppenders(); | ||
118 | |||
119 | foreach (IAppender appender in appenders) | ||
120 | { | ||
121 | if (appender.Name == "LogFileAppender") | ||
122 | { | ||
123 | m_logFileAppender = appender; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | } | ||
128 | |||
129 | /// <summary> | ||
130 | /// Must be overriden by child classes for their own server specific startup behaviour. | ||
131 | /// </summary> | ||
132 | protected virtual void StartupSpecific() | ||
133 | { | ||
134 | if (m_console != null) | ||
135 | { | ||
136 | ILoggerRepository repository = LogManager.GetRepository(); | ||
137 | IAppender[] appenders = repository.GetAppenders(); | ||
138 | |||
139 | foreach (IAppender appender in appenders) | ||
140 | { | ||
141 | if (appender.Name == "Console") | ||
142 | { | ||
143 | m_consoleAppender = (OpenSimAppender)appender; | ||
144 | break; | ||
145 | } | ||
146 | } | ||
147 | |||
148 | if (null == m_consoleAppender) | ||
149 | { | ||
150 | Notice("No appender named Console found (see the log4net config file for this executable)!"); | ||
151 | } | ||
152 | else | ||
153 | { | ||
154 | m_consoleAppender.Console = m_console; | ||
155 | |||
156 | // If there is no threshold set then the threshold is effectively everything. | ||
157 | if (null == m_consoleAppender.Threshold) | ||
158 | m_consoleAppender.Threshold = Level.All; | ||
159 | |||
160 | Notice(String.Format("Console log level is {0}", m_consoleAppender.Threshold)); | ||
161 | } | ||
162 | |||
163 | m_console.Commands.AddCommand("base", false, "quit", | ||
164 | "quit", | ||
165 | "Quit the application", HandleQuit); | ||
166 | |||
167 | m_console.Commands.AddCommand("base", false, "shutdown", | ||
168 | "shutdown", | ||
169 | "Quit the application", HandleQuit); | ||
170 | |||
171 | m_console.Commands.AddCommand("base", false, "set log level", | ||
172 | "set log level <level>", | ||
173 | "Set the console logging level", HandleLogLevel); | ||
174 | |||
175 | m_console.Commands.AddCommand("base", false, "show info", | ||
176 | "show info", | ||
177 | "Show general information", HandleShow); | ||
178 | |||
179 | m_console.Commands.AddCommand("base", false, "show stats", | ||
180 | "show stats", | ||
181 | "Show statistics", HandleShow); | ||
182 | |||
183 | m_console.Commands.AddCommand("base", false, "show threads", | ||
184 | "show threads", | ||
185 | "Show thread status", HandleShow); | ||
186 | |||
187 | m_console.Commands.AddCommand("base", false, "show uptime", | ||
188 | "show uptime", | ||
189 | "Show server uptime", HandleShow); | ||
190 | |||
191 | m_console.Commands.AddCommand("base", false, "show version", | ||
192 | "show version", | ||
193 | "Show server version", HandleShow); | ||
194 | } | ||
195 | } | ||
196 | |||
197 | /// <summary> | ||
198 | /// Should be overriden and referenced by descendents if they need to perform extra shutdown processing | ||
199 | /// </summary> | ||
200 | public virtual void ShutdownSpecific() {} | ||
201 | |||
202 | /// <summary> | ||
203 | /// Provides a list of help topics that are available. Overriding classes should append their topics to the | ||
204 | /// information returned when the base method is called. | ||
205 | /// </summary> | ||
206 | /// | ||
207 | /// <returns> | ||
208 | /// A list of strings that represent different help topics on which more information is available | ||
209 | /// </returns> | ||
210 | protected virtual List<string> GetHelpTopics() { return new List<string>(); } | ||
211 | |||
212 | /// <summary> | ||
213 | /// Print statistics to the logfile, if they are active | ||
214 | /// </summary> | ||
215 | protected void LogDiagnostics(object source, ElapsedEventArgs e) | ||
216 | { | ||
217 | StringBuilder sb = new StringBuilder("DIAGNOSTICS\n\n"); | ||
218 | sb.Append(GetUptimeReport()); | ||
219 | |||
220 | if (m_stats != null) | ||
221 | { | ||
222 | sb.Append(m_stats.Report()); | ||
223 | } | ||
224 | |||
225 | sb.Append(Environment.NewLine); | ||
226 | sb.Append(GetThreadsReport()); | ||
227 | |||
228 | m_log.Debug(sb); | ||
229 | } | ||
230 | |||
231 | /// <summary> | ||
232 | /// Get a report about the registered threads in this server. | ||
233 | /// </summary> | ||
234 | protected string GetThreadsReport() | ||
235 | { | ||
236 | StringBuilder sb = new StringBuilder(); | ||
237 | |||
238 | List<Thread> threads = ThreadTracker.GetThreads(); | ||
239 | if (threads == null) | ||
240 | { | ||
241 | sb.Append("Thread tracking is only enabled in DEBUG mode."); | ||
242 | } | ||
243 | else | ||
244 | { | ||
245 | sb.Append(threads.Count + " threads are being tracked:" + Environment.NewLine); | ||
246 | foreach (Thread t in threads) | ||
247 | { | ||
248 | if (t.IsAlive) | ||
249 | { | ||
250 | sb.Append( | ||
251 | "ID: " + t.ManagedThreadId + ", Name: " + t.Name + ", Alive: " + t.IsAlive | ||
252 | + ", Pri: " + t.Priority + ", State: " + t.ThreadState + Environment.NewLine); | ||
253 | } | ||
254 | else | ||
255 | { | ||
256 | try | ||
257 | { | ||
258 | sb.Append("ID: " + t.ManagedThreadId + ", Name: " + t.Name + ", DEAD" + Environment.NewLine); | ||
259 | } | ||
260 | catch | ||
261 | { | ||
262 | sb.Append("THREAD ERROR" + Environment.NewLine); | ||
263 | } | ||
264 | } | ||
265 | } | ||
266 | } | ||
267 | |||
268 | return sb.ToString(); | ||
269 | } | ||
270 | |||
271 | /// <summary> | ||
272 | /// Return a report about the uptime of this server | ||
273 | /// </summary> | ||
274 | /// <returns></returns> | ||
275 | protected string GetUptimeReport() | ||
276 | { | ||
277 | StringBuilder sb = new StringBuilder(String.Format("Time now is {0}\n", DateTime.Now)); | ||
278 | sb.Append(String.Format("Server has been running since {0}, {1}\n", m_startuptime.DayOfWeek, m_startuptime)); | ||
279 | sb.Append(String.Format("That is an elapsed time of {0}\n", DateTime.Now - m_startuptime)); | ||
280 | |||
281 | return sb.ToString(); | ||
282 | } | ||
283 | |||
284 | /// <summary> | ||
285 | /// Performs initialisation of the scene, such as loading configuration from disk. | ||
286 | /// </summary> | ||
287 | public virtual void Startup() | ||
288 | { | ||
289 | m_log.Info("[STARTUP]: Beginning startup processing"); | ||
290 | |||
291 | EnhanceVersionInformation(); | ||
292 | |||
293 | m_log.Info("[STARTUP]: Version: " + m_version + "\n"); | ||
294 | |||
295 | StartupSpecific(); | ||
296 | |||
297 | TimeSpan timeTaken = DateTime.Now - m_startuptime; | ||
298 | |||
299 | m_log.InfoFormat("[STARTUP]: Startup took {0}m {1}s", timeTaken.Minutes, timeTaken.Seconds); | ||
300 | } | ||
301 | |||
302 | /// <summary> | ||
303 | /// Should be overriden and referenced by descendents if they need to perform extra shutdown processing | ||
304 | /// </summary> | ||
305 | public virtual void Shutdown() | ||
306 | { | ||
307 | ShutdownSpecific(); | ||
308 | |||
309 | m_log.Info("[SHUTDOWN]: Shutdown processing on main thread complete. Exiting..."); | ||
310 | RemovePIDFile(); | ||
311 | |||
312 | Environment.Exit(0); | ||
313 | } | ||
314 | |||
315 | private void HandleQuit(string module, string[] args) | ||
316 | { | ||
317 | Shutdown(); | ||
318 | } | ||
319 | |||
320 | private void HandleLogLevel(string module, string[] cmd) | ||
321 | { | ||
322 | if (null == m_consoleAppender) | ||
323 | { | ||
324 | Notice("No appender named Console found (see the log4net config file for this executable)!"); | ||
325 | return; | ||
326 | } | ||
327 | |||
328 | string rawLevel = cmd[3]; | ||
329 | |||
330 | ILoggerRepository repository = LogManager.GetRepository(); | ||
331 | Level consoleLevel = repository.LevelMap[rawLevel]; | ||
332 | |||
333 | if (consoleLevel != null) | ||
334 | m_consoleAppender.Threshold = consoleLevel; | ||
335 | else | ||
336 | Notice( | ||
337 | String.Format( | ||
338 | "{0} is not a valid logging level. Valid logging levels are ALL, DEBUG, INFO, WARN, ERROR, FATAL, OFF", | ||
339 | rawLevel)); | ||
340 | |||
341 | Notice(String.Format("Console log level is {0}", m_consoleAppender.Threshold)); | ||
342 | } | ||
343 | |||
344 | /// <summary> | ||
345 | /// Show help information | ||
346 | /// </summary> | ||
347 | /// <param name="helpArgs"></param> | ||
348 | protected virtual void ShowHelp(string[] helpArgs) | ||
349 | { | ||
350 | Notice(""); | ||
351 | |||
352 | if (helpArgs.Length == 0) | ||
353 | { | ||
354 | Notice("set log level [level] - change the console logging level only. For example, off or debug."); | ||
355 | Notice("show info - show server information (e.g. startup path)."); | ||
356 | |||
357 | if (m_stats != null) | ||
358 | Notice("show stats - show statistical information for this server"); | ||
359 | |||
360 | Notice("show threads - list tracked threads"); | ||
361 | Notice("show uptime - show server startup time and uptime."); | ||
362 | Notice("show version - show server version."); | ||
363 | Notice(""); | ||
364 | |||
365 | return; | ||
366 | } | ||
367 | } | ||
368 | |||
369 | public virtual void HandleShow(string module, string[] cmd) | ||
370 | { | ||
371 | List<string> args = new List<string>(cmd); | ||
372 | |||
373 | args.RemoveAt(0); | ||
374 | |||
375 | string[] showParams = args.ToArray(); | ||
376 | |||
377 | switch (showParams[0]) | ||
378 | { | ||
379 | case "info": | ||
380 | Notice("Version: " + m_version); | ||
381 | Notice("Startup directory: " + m_startupDirectory); | ||
382 | break; | ||
383 | |||
384 | case "stats": | ||
385 | if (m_stats != null) | ||
386 | Notice(m_stats.Report()); | ||
387 | break; | ||
388 | |||
389 | case "threads": | ||
390 | Notice(GetThreadsReport()); | ||
391 | break; | ||
392 | |||
393 | case "uptime": | ||
394 | Notice(GetUptimeReport()); | ||
395 | break; | ||
396 | |||
397 | case "version": | ||
398 | Notice( | ||
399 | String.Format( | ||
400 | "Version: {0} (interface version {1})", m_version, VersionInfo.MajorInterfaceVersion)); | ||
401 | break; | ||
402 | } | ||
403 | } | ||
404 | |||
405 | /// <summary> | ||
406 | /// Console output is only possible if a console has been established. | ||
407 | /// That is something that cannot be determined within this class. So | ||
408 | /// all attempts to use the console MUST be verified. | ||
409 | /// </summary> | ||
410 | protected void Notice(string msg) | ||
411 | { | ||
412 | if (m_console != null) | ||
413 | { | ||
414 | m_console.Notice(msg); | ||
415 | } | ||
416 | } | ||
417 | |||
418 | /// <summary> | ||
419 | /// Enhance the version string with extra information if it's available. | ||
420 | /// </summary> | ||
421 | protected void EnhanceVersionInformation() | ||
422 | { | ||
423 | string buildVersion = string.Empty; | ||
424 | |||
425 | // Add subversion revision information if available | ||
426 | // Try file "svn_revision" in the current directory first, then the .svn info. | ||
427 | // This allows to make the revision available in simulators not running from the source tree. | ||
428 | // FIXME: Making an assumption about the directory we're currently in - we do this all over the place | ||
429 | // elsewhere as well | ||
430 | string svnRevisionFileName = "svn_revision"; | ||
431 | string svnFileName = ".svn/entries"; | ||
432 | string inputLine; | ||
433 | int strcmp; | ||
434 | |||
435 | if (File.Exists(svnRevisionFileName)) | ||
436 | { | ||
437 | StreamReader RevisionFile = File.OpenText(svnRevisionFileName); | ||
438 | buildVersion = RevisionFile.ReadLine(); | ||
439 | buildVersion.Trim(); | ||
440 | RevisionFile.Close(); | ||
441 | } | ||
442 | |||
443 | if (string.IsNullOrEmpty(buildVersion) && File.Exists(svnFileName)) | ||
444 | { | ||
445 | StreamReader EntriesFile = File.OpenText(svnFileName); | ||
446 | inputLine = EntriesFile.ReadLine(); | ||
447 | while (inputLine != null) | ||
448 | { | ||
449 | // using the dir svn revision at the top of entries file | ||
450 | strcmp = String.Compare(inputLine, "dir"); | ||
451 | if (strcmp == 0) | ||
452 | { | ||
453 | buildVersion = EntriesFile.ReadLine(); | ||
454 | break; | ||
455 | } | ||
456 | else | ||
457 | { | ||
458 | inputLine = EntriesFile.ReadLine(); | ||
459 | } | ||
460 | } | ||
461 | EntriesFile.Close(); | ||
462 | } | ||
463 | |||
464 | m_version += string.IsNullOrEmpty(buildVersion) ? " " : ("." + buildVersion + " ").Substring(0, 6); | ||
465 | } | ||
466 | |||
467 | protected void CreatePIDFile(string path) | ||
468 | { | ||
469 | try | ||
470 | { | ||
471 | string pidstring = System.Diagnostics.Process.GetCurrentProcess().Id.ToString(); | ||
472 | FileStream fs = File.Create(path); | ||
473 | System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding(); | ||
474 | Byte[] buf = enc.GetBytes(pidstring); | ||
475 | fs.Write(buf, 0, buf.Length); | ||
476 | fs.Close(); | ||
477 | m_pidFile = path; | ||
478 | } | ||
479 | catch (Exception) | ||
480 | { | ||
481 | } | ||
482 | } | ||
483 | |||
484 | public string osSecret { | ||
485 | // Secret uuid for the simulator | ||
486 | get { return m_osSecret; } | ||
487 | |||
488 | } | ||
489 | |||
490 | public string StatReport(OSHttpRequest httpRequest) | ||
491 | { | ||
492 | return m_stats.XReport((DateTime.Now - m_startuptime).ToString() , m_version ); | ||
493 | } | ||
494 | |||
495 | protected void RemovePIDFile() | ||
496 | { | ||
497 | if (m_pidFile != String.Empty) | ||
498 | { | ||
499 | try | ||
500 | { | ||
501 | File.Delete(m_pidFile); | ||
502 | m_pidFile = String.Empty; | ||
503 | } | ||
504 | catch (Exception) | ||
505 | { | ||
506 | } | ||
507 | } | ||
508 | } | ||
509 | } | ||
510 | } | ||