From ec0d2c28fa04102ecbad4c5660efecbb970201dd Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Mon, 4 May 2009 20:19:21 +0000 Subject: Committing the changed tree --- OpenSim/Framework/Servers/BaseOpenSimServer.cs | 510 +++++++++++++++++++++++++ 1 file changed, 510 insertions(+) create mode 100644 OpenSim/Framework/Servers/BaseOpenSimServer.cs (limited to 'OpenSim/Framework/Servers/BaseOpenSimServer.cs') 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 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSim Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Timers; +using log4net; +using log4net.Appender; +using log4net.Core; +using log4net.Repository; +using OpenSim.Framework.Console; +using OpenSim.Framework.Servers; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Framework.Statistics; +using Timer=System.Timers.Timer; + +using OpenMetaverse; +using OpenMetaverse.StructuredData; + + +namespace OpenSim.Framework.Servers +{ + /// + /// Common base for the main OpenSimServers (user, grid, inventory, region, etc) + /// + public abstract class BaseOpenSimServer + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// This will control a periodic log printout of the current 'show stats' (if they are active) for this + /// server. + /// + private Timer m_periodicDiagnosticsTimer = new Timer(60 * 60 * 1000); + + protected CommandConsole m_console; + protected OpenSimAppender m_consoleAppender; + protected IAppender m_logFileAppender = null; + + /// + /// Time at which this server was started + /// + protected DateTime m_startuptime; + + /// + /// Record the initial startup directory for info purposes + /// + protected string m_startupDirectory = Environment.CurrentDirectory; + + /// + /// Server version information. Usually VersionInfo + information about svn revision, operating system, etc. + /// + protected string m_version; + + protected string m_pidFile = String.Empty; + + /// + /// Random uuid for private data + /// + protected string m_osSecret = String.Empty; + + protected BaseHttpServer m_httpServer; + public BaseHttpServer HttpServer + { + get { return m_httpServer; } + } + + /// + /// Holds the non-viewer statistics collection object for this service/server + /// + protected IStatsCollector m_stats; + + public BaseOpenSimServer() + { + m_startuptime = DateTime.Now; + m_version = VersionInfo.Version; + + // Random uuid for private data + m_osSecret = UUID.Random().ToString(); + + m_periodicDiagnosticsTimer.Elapsed += new ElapsedEventHandler(LogDiagnostics); + m_periodicDiagnosticsTimer.Enabled = true; + + // Add ourselves to thread monitoring. This thread will go on to become the console listening thread + Thread.CurrentThread.Name = "ConsoleThread"; + ThreadTracker.Add(Thread.CurrentThread); + + ILoggerRepository repository = LogManager.GetRepository(); + IAppender[] appenders = repository.GetAppenders(); + + foreach (IAppender appender in appenders) + { + if (appender.Name == "LogFileAppender") + { + m_logFileAppender = appender; + } + } + + } + + /// + /// Must be overriden by child classes for their own server specific startup behaviour. + /// + protected virtual void StartupSpecific() + { + if (m_console != null) + { + ILoggerRepository repository = LogManager.GetRepository(); + IAppender[] appenders = repository.GetAppenders(); + + foreach (IAppender appender in appenders) + { + if (appender.Name == "Console") + { + m_consoleAppender = (OpenSimAppender)appender; + break; + } + } + + if (null == m_consoleAppender) + { + Notice("No appender named Console found (see the log4net config file for this executable)!"); + } + else + { + m_consoleAppender.Console = m_console; + + // If there is no threshold set then the threshold is effectively everything. + if (null == m_consoleAppender.Threshold) + m_consoleAppender.Threshold = Level.All; + + Notice(String.Format("Console log level is {0}", m_consoleAppender.Threshold)); + } + + m_console.Commands.AddCommand("base", false, "quit", + "quit", + "Quit the application", HandleQuit); + + m_console.Commands.AddCommand("base", false, "shutdown", + "shutdown", + "Quit the application", HandleQuit); + + m_console.Commands.AddCommand("base", false, "set log level", + "set log level ", + "Set the console logging level", HandleLogLevel); + + m_console.Commands.AddCommand("base", false, "show info", + "show info", + "Show general information", HandleShow); + + m_console.Commands.AddCommand("base", false, "show stats", + "show stats", + "Show statistics", HandleShow); + + m_console.Commands.AddCommand("base", false, "show threads", + "show threads", + "Show thread status", HandleShow); + + m_console.Commands.AddCommand("base", false, "show uptime", + "show uptime", + "Show server uptime", HandleShow); + + m_console.Commands.AddCommand("base", false, "show version", + "show version", + "Show server version", HandleShow); + } + } + + /// + /// Should be overriden and referenced by descendents if they need to perform extra shutdown processing + /// + public virtual void ShutdownSpecific() {} + + /// + /// Provides a list of help topics that are available. Overriding classes should append their topics to the + /// information returned when the base method is called. + /// + /// + /// + /// A list of strings that represent different help topics on which more information is available + /// + protected virtual List GetHelpTopics() { return new List(); } + + /// + /// Print statistics to the logfile, if they are active + /// + protected void LogDiagnostics(object source, ElapsedEventArgs e) + { + StringBuilder sb = new StringBuilder("DIAGNOSTICS\n\n"); + sb.Append(GetUptimeReport()); + + if (m_stats != null) + { + sb.Append(m_stats.Report()); + } + + sb.Append(Environment.NewLine); + sb.Append(GetThreadsReport()); + + m_log.Debug(sb); + } + + /// + /// Get a report about the registered threads in this server. + /// + protected string GetThreadsReport() + { + StringBuilder sb = new StringBuilder(); + + List threads = ThreadTracker.GetThreads(); + if (threads == null) + { + sb.Append("Thread tracking is only enabled in DEBUG mode."); + } + else + { + sb.Append(threads.Count + " threads are being tracked:" + Environment.NewLine); + foreach (Thread t in threads) + { + if (t.IsAlive) + { + sb.Append( + "ID: " + t.ManagedThreadId + ", Name: " + t.Name + ", Alive: " + t.IsAlive + + ", Pri: " + t.Priority + ", State: " + t.ThreadState + Environment.NewLine); + } + else + { + try + { + sb.Append("ID: " + t.ManagedThreadId + ", Name: " + t.Name + ", DEAD" + Environment.NewLine); + } + catch + { + sb.Append("THREAD ERROR" + Environment.NewLine); + } + } + } + } + + return sb.ToString(); + } + + /// + /// Return a report about the uptime of this server + /// + /// + protected string GetUptimeReport() + { + StringBuilder sb = new StringBuilder(String.Format("Time now is {0}\n", DateTime.Now)); + sb.Append(String.Format("Server has been running since {0}, {1}\n", m_startuptime.DayOfWeek, m_startuptime)); + sb.Append(String.Format("That is an elapsed time of {0}\n", DateTime.Now - m_startuptime)); + + return sb.ToString(); + } + + /// + /// Performs initialisation of the scene, such as loading configuration from disk. + /// + public virtual void Startup() + { + m_log.Info("[STARTUP]: Beginning startup processing"); + + EnhanceVersionInformation(); + + m_log.Info("[STARTUP]: Version: " + m_version + "\n"); + + StartupSpecific(); + + TimeSpan timeTaken = DateTime.Now - m_startuptime; + + m_log.InfoFormat("[STARTUP]: Startup took {0}m {1}s", timeTaken.Minutes, timeTaken.Seconds); + } + + /// + /// Should be overriden and referenced by descendents if they need to perform extra shutdown processing + /// + public virtual void Shutdown() + { + ShutdownSpecific(); + + m_log.Info("[SHUTDOWN]: Shutdown processing on main thread complete. Exiting..."); + RemovePIDFile(); + + Environment.Exit(0); + } + + private void HandleQuit(string module, string[] args) + { + Shutdown(); + } + + private void HandleLogLevel(string module, string[] cmd) + { + if (null == m_consoleAppender) + { + Notice("No appender named Console found (see the log4net config file for this executable)!"); + return; + } + + string rawLevel = cmd[3]; + + ILoggerRepository repository = LogManager.GetRepository(); + Level consoleLevel = repository.LevelMap[rawLevel]; + + if (consoleLevel != null) + m_consoleAppender.Threshold = consoleLevel; + else + Notice( + String.Format( + "{0} is not a valid logging level. Valid logging levels are ALL, DEBUG, INFO, WARN, ERROR, FATAL, OFF", + rawLevel)); + + Notice(String.Format("Console log level is {0}", m_consoleAppender.Threshold)); + } + + /// + /// Show help information + /// + /// + protected virtual void ShowHelp(string[] helpArgs) + { + Notice(""); + + if (helpArgs.Length == 0) + { + Notice("set log level [level] - change the console logging level only. For example, off or debug."); + Notice("show info - show server information (e.g. startup path)."); + + if (m_stats != null) + Notice("show stats - show statistical information for this server"); + + Notice("show threads - list tracked threads"); + Notice("show uptime - show server startup time and uptime."); + Notice("show version - show server version."); + Notice(""); + + return; + } + } + + public virtual void HandleShow(string module, string[] cmd) + { + List args = new List(cmd); + + args.RemoveAt(0); + + string[] showParams = args.ToArray(); + + switch (showParams[0]) + { + case "info": + Notice("Version: " + m_version); + Notice("Startup directory: " + m_startupDirectory); + break; + + case "stats": + if (m_stats != null) + Notice(m_stats.Report()); + break; + + case "threads": + Notice(GetThreadsReport()); + break; + + case "uptime": + Notice(GetUptimeReport()); + break; + + case "version": + Notice( + String.Format( + "Version: {0} (interface version {1})", m_version, VersionInfo.MajorInterfaceVersion)); + break; + } + } + + /// + /// Console output is only possible if a console has been established. + /// That is something that cannot be determined within this class. So + /// all attempts to use the console MUST be verified. + /// + protected void Notice(string msg) + { + if (m_console != null) + { + m_console.Notice(msg); + } + } + + /// + /// Enhance the version string with extra information if it's available. + /// + protected void EnhanceVersionInformation() + { + string buildVersion = string.Empty; + + // Add subversion revision information if available + // Try file "svn_revision" in the current directory first, then the .svn info. + // This allows to make the revision available in simulators not running from the source tree. + // FIXME: Making an assumption about the directory we're currently in - we do this all over the place + // elsewhere as well + string svnRevisionFileName = "svn_revision"; + string svnFileName = ".svn/entries"; + string inputLine; + int strcmp; + + if (File.Exists(svnRevisionFileName)) + { + StreamReader RevisionFile = File.OpenText(svnRevisionFileName); + buildVersion = RevisionFile.ReadLine(); + buildVersion.Trim(); + RevisionFile.Close(); + } + + if (string.IsNullOrEmpty(buildVersion) && File.Exists(svnFileName)) + { + StreamReader EntriesFile = File.OpenText(svnFileName); + inputLine = EntriesFile.ReadLine(); + while (inputLine != null) + { + // using the dir svn revision at the top of entries file + strcmp = String.Compare(inputLine, "dir"); + if (strcmp == 0) + { + buildVersion = EntriesFile.ReadLine(); + break; + } + else + { + inputLine = EntriesFile.ReadLine(); + } + } + EntriesFile.Close(); + } + + m_version += string.IsNullOrEmpty(buildVersion) ? " " : ("." + buildVersion + " ").Substring(0, 6); + } + + protected void CreatePIDFile(string path) + { + try + { + string pidstring = System.Diagnostics.Process.GetCurrentProcess().Id.ToString(); + FileStream fs = File.Create(path); + System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding(); + Byte[] buf = enc.GetBytes(pidstring); + fs.Write(buf, 0, buf.Length); + fs.Close(); + m_pidFile = path; + } + catch (Exception) + { + } + } + + public string osSecret { + // Secret uuid for the simulator + get { return m_osSecret; } + + } + + public string StatReport(OSHttpRequest httpRequest) + { + return m_stats.XReport((DateTime.Now - m_startuptime).ToString() , m_version ); + } + + protected void RemovePIDFile() + { + if (m_pidFile != String.Empty) + { + try + { + File.Delete(m_pidFile); + m_pidFile = String.Empty; + } + catch (Exception) + { + } + } + } + } +} -- cgit v1.1