/* * 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 OpenSimulator 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.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime; using System.Text; using System.Text.RegularExpressions; using System.Threading; using log4net; using log4net.Appender; using log4net.Core; using log4net.Repository; using Nini.Config; using OpenSim.Framework.Console; using OpenSim.Framework.Monitoring; namespace OpenSim.Framework.Servers { public class ServerBase { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); public IConfigSource Config { get; protected set; } /// /// Console to be used for any command line output. Can be null, in which case there should be no output. /// protected ICommandConsole m_console; protected OpenSimAppender m_consoleAppender; protected FileAppender m_logFileAppender; protected FileAppender m_statsLogFileAppender; protected DateTime m_startuptime; protected string m_startupDirectory = Environment.CurrentDirectory; protected string m_pidFile = String.Empty; protected ServerStatsCollector m_serverStatsCollector; /// /// Server version information. Usually VersionInfo + information about git commit, operating system, etc. /// protected string m_version; public ServerBase() { m_startuptime = DateTime.Now; m_version = VersionInfo.Version; EnhanceVersionInformation(); } protected void CreatePIDFile(string path) { if (File.Exists(path)) m_log.Error($"[SERVER BASE]: Previous pid file {path} still exists on startup. Possibly previously unclean shutdown."); try { string pidstring = System.Diagnostics.Process.GetCurrentProcess().Id.ToString(); using (FileStream fs = File.Create(path)) { Byte[] buf = Encoding.ASCII.GetBytes(pidstring); fs.Write(buf, 0, buf.Length); } m_pidFile = path; m_log.InfoFormat("[SERVER BASE]: Created pid file {0}", m_pidFile); } catch (Exception e) { m_log.Warn(string.Format("[SERVER BASE]: Could not create PID file at {0} ", path), e); } } protected void RemovePIDFile() { if (!string.IsNullOrEmpty(m_pidFile)) { try { File.Delete(m_pidFile); } catch (Exception e) { m_log.Error($"[SERVER BASE]: Error whilst removing {m_pidFile}", e); } m_pidFile = String.Empty; } } /// /// Log information about the circumstances in which we're running (OpenSimulator version number, CLR details, /// etc.). /// public void LogEnvironmentInformation() { // FIXME: This should be done down in ServerBase but we need to sort out and refactor the log4net // XmlConfigurator calls first accross servers. m_log.Info($"[SERVER BASE]: Starting in {m_startupDirectory}"); m_log.Info($"[SERVER BASE]: OpenSimulator version: {m_version}"); // clr version potentially is more confusing than helpful, since it doesn't tell us if we're running under Mono/MS .NET and // the clr version number doesn't match the project version number under Mono. //m_log.Info("[STARTUP]: Virtual machine runtime version: " + Environment.Version + Environment.NewLine); m_log.Info( $"[SERVER BASE]: Operating system version: {Environment.OSVersion}, .NET platform {Util.RuntimePlatformStr}, {(Environment.Is64BitProcess ? "64" : "32")}-bit"); } public void RegisterCommonAppenders(IConfig startupConfig) { ILoggerRepository repository = LogManager.GetRepository(); IAppender[] appenders = repository.GetAppenders(); foreach (IAppender appender in appenders) { if (appender.Name == "Console") { m_consoleAppender = (OpenSimAppender)appender; } else if (appender.Name == "LogFileAppender") { m_logFileAppender = (FileAppender)appender; } else if (appender.Name == "StatsLogFileAppender") { m_statsLogFileAppender = (FileAppender)appender; } } if (null == m_consoleAppender) { Notice("No appender named Console found (see the log4net config file for this executable)!"); } else { // FIXME: This should be done through an interface rather than casting. m_consoleAppender.Console = (ConsoleBase)m_console; // If there is no threshold set then the threshold is effectively everything. if (m_consoleAppender.Threshold is null) m_consoleAppender.Threshold = Level.All; //Notice($"Console log level is {m_consoleAppender.Threshold}"); } if (m_logFileAppender != null && startupConfig != null) { string cfgFileName = startupConfig.GetString("logfile", null); if (cfgFileName != null) { m_logFileAppender.File = cfgFileName; m_logFileAppender.ActivateOptions(); } m_log.InfoFormat("[SERVER BASE]: Logging started to file {0}", m_logFileAppender.File); } if (m_statsLogFileAppender != null && startupConfig != null) { string cfgStatsFileName = startupConfig.GetString("StatsLogFile", null); if (cfgStatsFileName != null) { m_statsLogFileAppender.File = cfgStatsFileName; m_statsLogFileAppender.ActivateOptions(); } m_log.InfoFormat("[SERVER BASE]: Stats Logging started to file {0}", m_statsLogFileAppender.File); } } /// /// Register common commands once m_console has been set if it is going to be set /// public void RegisterCommonCommands() { if (m_console == null) return; m_console.Commands.AddCommand( "General", false, "show info", "show info", "Show general information about the server", HandleShow); m_console.Commands.AddCommand( "General", false, "show version", "show version", "Show server version", HandleShow); m_console.Commands.AddCommand( "General", false, "show uptime", "show uptime", "Show server uptime", HandleShow); m_console.Commands.AddCommand( "General", false, "get log level", "get log level", "Get the current console logging level", (mod, cmd) => ShowLogLevel()); m_console.Commands.AddCommand( "General", false, "set log level", "set log level ", "Set the console logging level for this session.", HandleSetLogLevel); m_console.Commands.AddCommand( "General", false, "config set", "config set
", "Set a config option. In most cases this is not useful since changed parameters are not dynamically reloaded. Neither do changed parameters persist - you will have to change a config file manually and restart.", HandleConfig); m_console.Commands.AddCommand( "General", false, "config get", "config get [
] []", "Synonym for config show", HandleConfig); m_console.Commands.AddCommand( "General", false, "config show", "config show [
] []", "Show config information", "If neither section nor field are specified, then the whole current configuration is printed." + Environment.NewLine + "If a section is given but not a field, then all fields in that section are printed.", HandleConfig); m_console.Commands.AddCommand( "General", false, "config save", "config save ", "Save current configuration to a file at the given path", HandleConfig); m_console.Commands.AddCommand( "General", false, "command-script", "command-script