/* * 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.Threading; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Timers; using log4net; using NDesk.Options; using Nini.Config; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Framework.Console; using OpenSim.Framework.Servers; using OpenSim.Framework.Monitoring; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using OpenSim.Services.Interfaces; namespace OpenSim { /// /// Interactive OpenSim region server /// public class OpenSim : OpenSimBase { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); protected string m_startupCommandsFile; protected string m_shutdownCommandsFile; protected bool m_gui = false; protected string m_consoleType = "local"; protected uint m_consolePort = 0; /// /// Prompt to use for simulator command line. /// private string m_consolePrompt; /// /// Regex for parsing out special characters in the prompt. /// private Regex m_consolePromptRegex = new Regex(@"([^\\])\\(\w)", RegexOptions.Compiled); private string m_timedScript = "disabled"; private int m_timeInterval = 1200; private System.Timers.Timer m_scriptTimer; public OpenSim(IConfigSource configSource) : base(configSource) { } protected override void ReadExtraConfigSettings() { base.ReadExtraConfigSettings(); IConfig startupConfig = Config.Configs["Startup"]; IConfig networkConfig = Config.Configs["Network"]; int stpMinThreads = 2; int stpMaxThreads = 15; if (startupConfig != null) { m_startupCommandsFile = startupConfig.GetString("startup_console_commands_file", "startup_commands.txt"); m_shutdownCommandsFile = startupConfig.GetString("shutdown_console_commands_file", "shutdown_commands.txt"); if (startupConfig.GetString("console", String.Empty) == String.Empty) m_gui = startupConfig.GetBoolean("gui", false); else m_consoleType= startupConfig.GetString("console", String.Empty); if (networkConfig != null) m_consolePort = (uint)networkConfig.GetInt("console_port", 0); m_timedScript = startupConfig.GetString("timer_Script", "disabled"); if (m_timedScript != "disabled") { m_timeInterval = startupConfig.GetInt("timer_Interval", 1200); } string asyncCallMethodStr = startupConfig.GetString("async_call_method", String.Empty); FireAndForgetMethod asyncCallMethod; if (!String.IsNullOrEmpty(asyncCallMethodStr) && Utils.EnumTryParse(asyncCallMethodStr, out asyncCallMethod)) Util.FireAndForgetMethod = asyncCallMethod; stpMinThreads = startupConfig.GetInt("MinPoolThreads", 2 ); stpMaxThreads = startupConfig.GetInt("MaxPoolThreads", 25); m_consolePrompt = startupConfig.GetString("ConsolePrompt", @"Region (\R) "); } if (Util.FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool) Util.InitThreadPool(stpMinThreads, stpMaxThreads); m_log.Info("[OPENSIM MAIN]: Using async_call_method " + Util.FireAndForgetMethod); } private static Mono.Unix.UnixSignal[] signals; private Thread signal_thread = new Thread (delegate () { while (true) { // Wait for a signal to be delivered int index = Mono.Unix.UnixSignal.WaitAny (signals, -1); //Mono.Unix.Native.Signum signal = signals [index].Signum; MainConsole.Instance.RunCommand("shutdown"); } }); /// /// Performs initialisation of the scene, such as loading configuration from disk. /// protected override void StartupSpecific() { m_log.Info("===================================================================="); m_log.Info("========================= STARTING OPENSIM ========================="); m_log.Info("===================================================================="); if(!Util.IsWindows()) { try { // linux mac os specifics signals = new Mono.Unix.UnixSignal[] { new Mono.Unix.UnixSignal(Mono.Unix.Native.Signum.SIGTERM) }; signal_thread.Start(); } catch (Exception e) { m_log.Info("Could not set up UNIX signal handlers. SIGTERM will not"); m_log.InfoFormat("shut down gracefully: {0}", e.Message); m_log.Debug("Exception was: ", e); } } //m_log.InfoFormat("[OPENSIM MAIN]: GC Is Server GC: {0}", GCSettings.IsServerGC.ToString()); // http://msdn.microsoft.com/en-us/library/bb384202.aspx //GCSettings.LatencyMode = GCLatencyMode.Batch; //m_log.InfoFormat("[OPENSIM MAIN]: GC Latency Mode: {0}", GCSettings.LatencyMode.ToString()); if (m_gui) // Driven by external GUI { m_console = new CommandConsole("Region"); } else { switch (m_consoleType) { case "basic": m_console = new CommandConsole("Region"); break; case "rest": m_console = new RemoteConsole("Region"); ((RemoteConsole)m_console).ReadConfig(Config); break; default: m_console = new LocalConsole("Region", Config.Configs["Startup"]); break; } } MainConsole.Instance = m_console; LogEnvironmentInformation(); RegisterCommonAppenders(Config.Configs["Startup"]); RegisterConsoleCommands(); base.StartupSpecific(); MainServer.Instance.AddStreamHandler(new OpenSim.SimStatusHandler()); MainServer.Instance.AddStreamHandler(new OpenSim.XSimStatusHandler(this)); if (userStatsURI != String.Empty) MainServer.Instance.AddStreamHandler(new OpenSim.UXSimStatusHandler(this)); MainServer.Instance.AddStreamHandler(new OpenSim.SimRobotsHandler()); if (managedStatsURI != String.Empty) { string urlBase = String.Format("/{0}/", managedStatsURI); StatsManager.StatsPassword = managedStatsPassword; MainServer.Instance.AddHTTPHandler(urlBase, StatsManager.HandleStatsRequest); m_log.InfoFormat("[OPENSIM] Enabling remote managed stats fetch. URL = {0}", urlBase); } if (m_console is RemoteConsole) { if (m_consolePort == 0) { ((RemoteConsole)m_console).SetServer(m_httpServer); } else { ((RemoteConsole)m_console).SetServer(MainServer.GetHttpServer(m_consolePort)); } } // Hook up to the watchdog timer Watchdog.OnWatchdogTimeout += WatchdogTimeoutHandler; PrintFileToConsole("startuplogo.txt"); // For now, start at the 'root' level by default if (SceneManager.Scenes.Count == 1) // If there is only one region, select it ChangeSelectedRegion("region", new string[] {"change", "region", SceneManager.Scenes[0].RegionInfo.RegionName}); else ChangeSelectedRegion("region", new string[] {"change", "region", "root"}); //Run Startup Commands if (String.IsNullOrEmpty(m_startupCommandsFile)) { m_log.Info("[STARTUP]: No startup command script specified. Moving on..."); } else { RunCommandScript(m_startupCommandsFile); } // Start timer script (run a script every xx seconds) if (m_timedScript != "disabled") { m_scriptTimer = new System.Timers.Timer(); m_scriptTimer.Enabled = true; m_scriptTimer.Interval = m_timeInterval*1000; m_scriptTimer.Elapsed += RunAutoTimerScript; } } /// /// Register standard set of region console commands /// private void RegisterConsoleCommands() { MainServer.RegisterHttpConsoleCommands(m_console); m_console.Commands.AddCommand("Objects", false, "force update", "force update", "Force the update of all objects on clients", HandleForceUpdate); m_console.Commands.AddCommand("General", false, "change region", "change region ", "Change current console region", ChangeSelectedRegion); m_console.Commands.AddCommand("Archiving", false, "save xml", "save xml []", "Save a region's data in XML format", SaveXml); m_console.Commands.AddCommand("Archiving", false, "save xml2", "save xml2 []", "Save a region's data in XML2 format", SaveXml2); m_console.Commands.AddCommand("Archiving", false, "load xml", "load xml [ [-newUID [ ]]]", "Load a region's data from XML format", LoadXml); m_console.Commands.AddCommand("Archiving", false, "load xml2", "load xml2 []", "Load a region's data from XML2 format", LoadXml2); m_console.Commands.AddCommand("Archiving", false, "save prims xml2", "save prims xml2 [ ]", "Save named prim to XML2", SavePrimsXml2); m_console.Commands.AddCommand("Archiving", false, "load oar", "load oar [-m|--merge] [-s|--skip-assets]" + " [--default-user \"User Name\"]" + " [--force-terrain] [--force-parcels]" + " [--no-objects]" + " [--rotation degrees]" + " [--bounding-origin \"\"]" + " [--bounding-size \"\"]" + " [--displacement \"\"]" + " [-d|--debug]" + " []", "Load a region's data from an OAR archive.", "--merge will merge the OAR with the existing scene (suppresses terrain and parcel info loading).\n" + "--skip-assets will load the OAR but ignore the assets it contains.\n" + "--default-user will use this user for any objects with an owner whose UUID is not found in the grid.\n" + "--force-terrain forces the loading of terrain from the oar (undoes suppression done by --merge).\n" + "--force-parcels forces the loading of parcels from the oar (undoes suppression done by --merge).\n" + "--no-objects suppresses the addition of any objects (good for loading only the terrain).\n" + "--rotation specified rotation to be applied to the oar. Specified in degrees.\n" + "--bounding-origin will only place objects that after displacement and rotation fall within the bounding cube who's position starts at . Defaults to <0,0,0>.\n" + "--bounding-size specifies the size of the bounding cube. The default is the size of the destination region and cannot be larger than this.\n" + "--displacement will add this value to the position of every object loaded.\n" + "--debug forces the archiver to display messages about where each object is being placed.\n\n" + "The path can be either a filesystem location or a URI.\n" + " If this is not given then the command looks for an OAR named region.oar in the current directory." + " [--rotation-center \"\"] used to be an option, now it does nothing and will be removed soon." + "When an OAR is being loaded, operations are applied in this order:\n" + "1: Rotation (around the incoming OARs region center)\n" + "2: Cropping (a bounding cube with origin and size)\n" + "3: Displacement (setting offset coordinates within the destination region)", LoadOar); ; m_console.Commands.AddCommand("Archiving", false, "save oar", //"save oar [-v|--version=] [-p|--profile=] []", "save oar [-h|--home=] [--noassets] [--publish] [--perm=] [--all] []", "Save a region's data to an OAR archive.", // "-v|--version= generates scene objects as per older versions of the serialization (e.g. -v=0)" + Environment.NewLine "-h|--home= adds the url of the profile service to the saved user information.\n" + "--noassets stops assets being saved to the OAR.\n" + "--publish saves an OAR stripped of owner and last owner information.\n" + " on reload, the estate owner will be the owner of all objects\n" + " this is useful if you're making oars generally available that might be reloaded to the same grid from which you published\n" + "--perm= stops objects with insufficient permissions from being saved to the OAR.\n" + " can contain one or more of these characters: \"C\" = Copy, \"T\" = Transfer\n" + "--all saves all the regions in the simulator, instead of just the current region.\n" + "The OAR path must be a filesystem path." + " If this is not given then the oar is saved to region.oar in the current directory.", SaveOar); m_console.Commands.AddCommand("Objects", false, "edit scale", "edit scale ", "Change the scale of a named prim", HandleEditScale); m_console.Commands.AddCommand("Objects", false, "rotate scene", "rotate scene [centerX, centerY]", "Rotates all scene objects around centerX, centerY (default 128, 128) (please back up your region before using)", HandleRotateScene); m_console.Commands.AddCommand("Objects", false, "scale scene", "scale scene ", "Scales the scene objects (please back up your region before using)", HandleScaleScene); m_console.Commands.AddCommand("Objects", false, "translate scene", "translate scene xOffset yOffset zOffset", "translates the scene objects (please back up your region before using)", HandleTranslateScene); m_console.Commands.AddCommand("Users", false, "kick user", "kick user [--force] [message]", "Kick a user off the simulator", "The --force option will kick the user without any checks to see whether it's already in the process of closing\n" + "Only use this option if you are sure the avatar is inactive and a normal kick user operation does not removed them", KickUserCommand); m_console.Commands.AddCommand("Users", false, "show users", "show users [full]", "Show user data for users currently on the region", "Without the 'full' option, only users actually on the region are shown." + " With the 'full' option child agents of users in neighbouring regions are also shown.", HandleShow); m_console.Commands.AddCommand("Comms", false, "show connections", "show connections", "Show connection data", HandleShow); m_console.Commands.AddCommand("Comms", false, "show circuits", "show circuits", "Show agent circuit data", HandleShow); m_console.Commands.AddCommand("Comms", false, "show pending-objects", "show pending-objects", "Show # of objects on the pending queues of all scene viewers", HandleShow); m_console.Commands.AddCommand("General", false, "show modules", "show modules", "Show module data", HandleShow); m_console.Commands.AddCommand("Regions", false, "show regions", "show regions", "Show region data", HandleShow); m_console.Commands.AddCommand("Regions", false, "show ratings", "show ratings", "Show rating data", HandleShow); m_console.Commands.AddCommand("Objects", false, "backup", "backup", "Persist currently unsaved object changes immediately instead of waiting for the normal persistence call.", RunCommand); m_console.Commands.AddCommand("Regions", false, "create region", "create region [\"region name\"] ", "Create a new region.", "The settings for \"region name\" are read from . Paths specified with are relative to your Regions directory, unless an absolute path is given." + " If \"region name\" does not exist in , it will be added." + Environment.NewLine + "Without \"region name\", the first region found in will be created." + Environment.NewLine + "If does not exist, it will be created.", HandleCreateRegion); m_console.Commands.AddCommand("Regions", false, "restart", "restart", "Restart the currently selected region(s) in this instance", RunCommand); m_console.Commands.AddCommand("General", false, "command-script", "command-script