/Aurora/Simulation/Base/SimulationBase.cs

http://github.com/aurora-sim/Aurora-Sim · C# · 604 lines · 406 code · 81 blank · 117 comment · 48 complexity · b698bea5bfc562532238b4b36fc81cdc MD5 · raw file

  1. /*
  2. * Copyright (c) Contributors, http://aurora-sim.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 Aurora-Sim 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. using Aurora.Framework;
  28. using Aurora.Framework.Configuration;
  29. using Aurora.Framework.ConsoleFramework;
  30. using Aurora.Framework.ModuleLoader;
  31. using Aurora.Framework.Modules;
  32. using Aurora.Framework.SceneInfo;
  33. using Aurora.Framework.Servers;
  34. using Aurora.Framework.Servers.HttpServer;
  35. using Aurora.Framework.Servers.HttpServer.Interfaces;
  36. using Aurora.Framework.Services;
  37. using Aurora.Framework.Services.ClassHelpers.Other;
  38. using Aurora.Framework.Utilities;
  39. using Nini.Config;
  40. using OpenMetaverse;
  41. using System;
  42. using System.Collections.Generic;
  43. using System.IO;
  44. using System.Timers;
  45. namespace Aurora.Simulation.Base
  46. {
  47. public class SimulationBase : ISimulationBase
  48. {
  49. protected string m_startupCommandsFile;
  50. protected string m_shutdownCommandsFile;
  51. protected string m_TimerScriptFileName = "disabled";
  52. protected int m_TimerScriptTime = 20;
  53. protected IHttpServer m_BaseHTTPServer;
  54. protected Timer m_TimerScriptTimer;
  55. protected ConfigurationLoader m_configurationLoader;
  56. /// <value>
  57. /// The config information passed into the Aurora server.
  58. /// </value>
  59. protected IConfigSource m_config;
  60. protected IConfigSource m_original_config;
  61. public IConfigSource ConfigSource
  62. {
  63. get { return m_config; }
  64. set { m_config = value; }
  65. }
  66. /// <summary>
  67. /// Server version information. Usually VersionInfo + information about git commit, operating system, etc.
  68. /// </summary>
  69. protected string m_version;
  70. public string Version
  71. {
  72. get { return m_version; }
  73. }
  74. protected IRegistryCore m_applicationRegistry = new RegistryCore();
  75. public IRegistryCore ApplicationRegistry
  76. {
  77. get { return m_applicationRegistry; }
  78. }
  79. protected AuroraEventManager m_eventManager = new AuroraEventManager();
  80. public AuroraEventManager EventManager
  81. {
  82. get { return m_eventManager; }
  83. }
  84. /// <summary>
  85. /// Time at which this server was started
  86. /// </summary>
  87. protected DateTime m_StartupTime;
  88. public DateTime StartupTime
  89. {
  90. get { return m_StartupTime; }
  91. }
  92. protected List<IApplicationPlugin> m_applicationPlugins = new List<IApplicationPlugin>();
  93. public IHttpServer HttpServer
  94. {
  95. get { return m_BaseHTTPServer; }
  96. }
  97. protected Dictionary<uint, IHttpServer> m_Servers =
  98. new Dictionary<uint, IHttpServer>();
  99. protected uint m_Port;
  100. public uint DefaultPort
  101. {
  102. get { return m_Port; }
  103. }
  104. protected string[] m_commandLineParameters = null;
  105. public string[] CommandLineParameters
  106. {
  107. get { return m_commandLineParameters; }
  108. }
  109. protected string m_pidFile = String.Empty;
  110. /// <summary>
  111. /// Do the initial setup for the application
  112. /// </summary>
  113. /// <param name="originalConfig"></param>
  114. /// <param name="configSource"></param>
  115. /// <param name="cmdParams"></param>
  116. /// <param name="configLoader"></param>
  117. public virtual void Initialize(IConfigSource originalConfig, IConfigSource configSource, string[] cmdParams,
  118. ConfigurationLoader configLoader)
  119. {
  120. m_commandLineParameters = cmdParams;
  121. m_StartupTime = DateTime.Now;
  122. m_version = VersionInfo.Version + " (" + Util.GetRuntimeInformation() + ")";
  123. m_original_config = originalConfig;
  124. m_config = configSource;
  125. m_configurationLoader = configLoader;
  126. // This thread will go on to become the console listening thread
  127. if (System.Threading.Thread.CurrentThread.Name != "ConsoleThread")
  128. System.Threading.Thread.CurrentThread.Name = "ConsoleThread";
  129. //Register the interface
  130. ApplicationRegistry.RegisterModuleInterface<ISimulationBase>(this);
  131. Configuration(configSource);
  132. InitializeModules();
  133. RegisterConsoleCommands();
  134. }
  135. /// <summary>
  136. /// Read the configuration
  137. /// </summary>
  138. /// <param name="configSource"></param>
  139. public virtual void Configuration(IConfigSource configSource)
  140. {
  141. IConfig startupConfig = m_config.Configs["Startup"];
  142. int stpMaxThreads = 15;
  143. if (startupConfig != null)
  144. {
  145. m_startupCommandsFile = startupConfig.GetString("startup_console_commands_file", "startup_commands.txt");
  146. m_shutdownCommandsFile = startupConfig.GetString("shutdown_console_commands_file",
  147. "shutdown_commands.txt");
  148. m_TimerScriptFileName = startupConfig.GetString("timer_Script", "disabled");
  149. m_TimerScriptTime = startupConfig.GetInt("timer_time", m_TimerScriptTime);
  150. string pidFile = startupConfig.GetString("PIDFile", String.Empty);
  151. if (pidFile != String.Empty)
  152. CreatePIDFile(pidFile);
  153. }
  154. IConfig SystemConfig = m_config.Configs["System"];
  155. if (SystemConfig != null)
  156. {
  157. string asyncCallMethodStr = SystemConfig.GetString("AsyncCallMethod", String.Empty);
  158. FireAndForgetMethod asyncCallMethod;
  159. if (!String.IsNullOrEmpty(asyncCallMethodStr) &&
  160. Utils.EnumTryParse(asyncCallMethodStr, out asyncCallMethod))
  161. Util.FireAndForgetMethod = asyncCallMethod;
  162. stpMaxThreads = SystemConfig.GetInt("MaxPoolThreads", 15);
  163. }
  164. if (Util.FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool)
  165. Util.InitThreadPool(stpMaxThreads);
  166. }
  167. /// <summary>
  168. /// Performs initialisation of the application, such as loading the HTTP server and modules
  169. /// </summary>
  170. public virtual void Startup()
  171. {
  172. MainConsole.Instance.Warn("====================================================================");
  173. MainConsole.Instance.Warn(
  174. string.Format("====================== STARTING AURORA ({0}) ======================",
  175. (IntPtr.Size == 4 ? "x86" : "x64")));
  176. MainConsole.Instance.Warn("====================================================================");
  177. MainConsole.Instance.Warn("[AuroraStartup]: Version: " + Version + "\n");
  178. SetUpHTTPServer();
  179. StartModules();
  180. //Has to be after Scene Manager startup
  181. AddPluginCommands();
  182. }
  183. public virtual ISimulationBase Copy()
  184. {
  185. return new SimulationBase();
  186. }
  187. /// <summary>
  188. /// Run the console now that we are all done with startup
  189. /// </summary>
  190. public virtual void Run()
  191. {
  192. //Start the prompt
  193. if (MainConsole.Instance != null)
  194. MainConsole.Instance.ReadConsole();
  195. }
  196. public virtual void AddPluginCommands()
  197. {
  198. }
  199. /// <summary>
  200. /// Get an HTTPServer on the given port. It will create one if one does not exist
  201. /// </summary>
  202. /// <param name="port"></param>
  203. /// <returns></returns>
  204. public IHttpServer GetHttpServer(uint port)
  205. {
  206. if ((port == m_Port || port == 0) && HttpServer != null)
  207. return HttpServer;
  208. bool useHTTPS = m_config.Configs["Network"].GetBoolean("use_https", false);
  209. IHttpServer server;
  210. if (m_Servers.TryGetValue(port, out server) && server.Secure == useHTTPS)
  211. return server;
  212. string hostName =
  213. m_config.Configs["Network"].GetString("HostName",
  214. "http" + (useHTTPS ? "s" : "") + "://" + Utilities.GetExternalIp());
  215. uint threadCount = m_config.Configs["Network"].GetUInt("HttpThreadCount", 5);
  216. //Clean it up a bit
  217. if (hostName.StartsWith("http://") || hostName.StartsWith("https://"))
  218. hostName = hostName.Replace("https://", "").Replace("http://", "");
  219. if (hostName.EndsWith("/"))
  220. hostName = hostName.Remove(hostName.Length - 1, 1);
  221. server = new BaseHttpServer(port, hostName, useHTTPS, threadCount);
  222. try
  223. {
  224. server.Start();
  225. }
  226. catch (Exception)
  227. {
  228. //Remove the server from the list
  229. m_Servers.Remove(port);
  230. //Then pass the exception upwards
  231. throw;
  232. }
  233. return (m_Servers[port] = server);
  234. }
  235. /// <summary>
  236. /// Set up the base HTTP server
  237. /// </summary>
  238. public virtual void SetUpHTTPServer()
  239. {
  240. m_Port = m_config.Configs["Network"].GetUInt("http_listener_port", 9000);
  241. m_BaseHTTPServer = GetHttpServer(m_Port);
  242. MainServer.Instance = m_BaseHTTPServer;
  243. }
  244. public virtual void InitializeModules()
  245. {
  246. m_applicationPlugins = AuroraModuleLoader.PickupModules<IApplicationPlugin>();
  247. foreach (IApplicationPlugin plugin in m_applicationPlugins)
  248. plugin.PreStartup(this);
  249. }
  250. /// <summary>
  251. /// Start the application modules
  252. /// </summary>
  253. public virtual void StartModules()
  254. {
  255. foreach (IApplicationPlugin plugin in m_applicationPlugins)
  256. plugin.Initialize(this);
  257. foreach (IApplicationPlugin plugin in m_applicationPlugins)
  258. plugin.PostInitialise();
  259. foreach (IApplicationPlugin plugin in m_applicationPlugins)
  260. plugin.Start();
  261. foreach (IApplicationPlugin plugin in m_applicationPlugins)
  262. plugin.PostStart();
  263. }
  264. /// <summary>
  265. /// Close all the Application Plugins
  266. /// </summary>
  267. public virtual void CloseModules()
  268. {
  269. foreach (IApplicationPlugin plugin in m_applicationPlugins)
  270. plugin.Close();
  271. }
  272. /// <summary>
  273. /// Run the commands given now that startup is complete
  274. /// </summary>
  275. public void RunStartupCommands()
  276. {
  277. //Draw the file on the console
  278. PrintFileToConsole("startuplogo.txt");
  279. //Run Startup Commands
  280. if (!String.IsNullOrEmpty(m_startupCommandsFile))
  281. RunCommandScript(m_startupCommandsFile);
  282. // Start timer script (run a script every xx seconds)
  283. if (m_TimerScriptFileName != "disabled")
  284. {
  285. Timer newtimername = new Timer {Enabled = true, Interval = m_TimerScriptTime*60*1000};
  286. newtimername.Elapsed += RunAutoTimerScript;
  287. }
  288. }
  289. /// <summary>
  290. /// Opens a file and uses it as input to the console command parser.
  291. /// </summary>
  292. /// <param name="fileName">name of file to use as input to the console</param>
  293. private void PrintFileToConsole(string fileName)
  294. {
  295. if (File.Exists(fileName))
  296. {
  297. StreamReader readFile = File.OpenText(fileName);
  298. string currentLine;
  299. while ((currentLine = readFile.ReadLine()) != null)
  300. {
  301. MainConsole.Instance.Info("[!]" + currentLine);
  302. }
  303. }
  304. }
  305. /// <summary>
  306. /// Timer to run a specific text file as console commands.
  307. /// Configured in in the main .ini file
  308. /// </summary>
  309. /// <param name="sender"></param>
  310. /// <param name="e"></param>
  311. private void RunAutoTimerScript(object sender, EventArgs e)
  312. {
  313. RunCommandScript(m_TimerScriptFileName);
  314. }
  315. #region Console Commands
  316. /// <summary>
  317. /// Register standard set of region console commands
  318. /// </summary>
  319. public virtual void RegisterConsoleCommands()
  320. {
  321. if (MainConsole.Instance == null)
  322. return;
  323. MainConsole.Instance.Commands.AddCommand("quit", "quit", "Quit the application", HandleQuit, false, true);
  324. MainConsole.Instance.Commands.AddCommand("shutdown", "shutdown", "Quit the application", HandleQuit, false, true);
  325. MainConsole.Instance.Commands.AddCommand("show info", "show info",
  326. "Show server information (e.g. startup path)", HandleShowInfo, false, true);
  327. MainConsole.Instance.Commands.AddCommand("show version", "show version", "Show server version",
  328. HandleShowVersion, false, true);
  329. MainConsole.Instance.Commands.AddCommand("reload config", "reload config", "Reloads .ini file configuration",
  330. HandleConfigRefresh, false, true);
  331. MainConsole.Instance.Commands.AddCommand("set timer script interval", "set timer script interval",
  332. "Set the interval for the timer script (in minutes).",
  333. HandleTimerScriptTime, false, true);
  334. MainConsole.Instance.Commands.AddCommand("force GC", "force GC", "Forces garbage collection.", HandleForceGC, false, true);
  335. MainConsole.Instance.Commands.AddCommand("run configurator", "run configurator", "Runs Aurora.Configurator.",
  336. runConfig, false, true);
  337. }
  338. private void HandleQuit(IScene scene, string[] args)
  339. {
  340. Shutdown(true);
  341. }
  342. /// <summary>
  343. /// Run an optional startup list of commands
  344. /// </summary>
  345. /// <param name="fileName"></param>
  346. public virtual void RunCommandScript(string fileName)
  347. {
  348. if (File.Exists(fileName))
  349. {
  350. MainConsole.Instance.Info("[COMMANDFILE]: Running " + fileName);
  351. List<string> commands = new List<string>();
  352. using (StreamReader readFile = File.OpenText(fileName))
  353. {
  354. string currentCommand;
  355. while ((currentCommand = readFile.ReadLine()) != null)
  356. {
  357. if (currentCommand != String.Empty)
  358. {
  359. commands.Add(currentCommand);
  360. }
  361. }
  362. }
  363. foreach (string currentCommand in commands)
  364. {
  365. MainConsole.Instance.Info("[COMMANDFILE]: Running '" + currentCommand + "'");
  366. MainConsole.Instance.RunCommand(currentCommand);
  367. }
  368. }
  369. }
  370. public virtual void HandleForceGC(IScene scene, string[] cmd)
  371. {
  372. GC.Collect();
  373. MainConsole.Instance.Warn("Garbage collection finished");
  374. }
  375. public virtual void runConfig(IScene scene, string[] cmd)
  376. {
  377. BaseApplication.Configure(true);
  378. }
  379. public virtual void HandleTimerScriptTime(IScene scene, string[] cmd)
  380. {
  381. if (cmd.Length != 5)
  382. {
  383. MainConsole.Instance.Warn("[CONSOLE]: Timer Interval command did not have enough parameters.");
  384. return;
  385. }
  386. MainConsole.Instance.Warn("[CONSOLE]: Set Timer Interval to " + cmd[4]);
  387. m_TimerScriptTime = int.Parse(cmd[4]);
  388. m_TimerScriptTimer.Enabled = false;
  389. m_TimerScriptTimer.Interval = m_TimerScriptTime*60*1000;
  390. m_TimerScriptTimer.Enabled = true;
  391. }
  392. public virtual void HandleConfigRefresh(IScene scene, string[] cmd)
  393. {
  394. //Rebuild the configs
  395. m_config = m_configurationLoader.LoadConfigSettings(m_original_config);
  396. foreach (IApplicationPlugin plugin in m_applicationPlugins)
  397. plugin.ReloadConfiguration(m_config);
  398. string hostName =
  399. m_config.Configs["Network"].GetString("HostName", "http://127.0.0.1");
  400. //Clean it up a bit
  401. // these are doing nothing??
  402. hostName.Replace("http://", "");
  403. hostName.Replace("https://", "");
  404. if (hostName.EndsWith("/"))
  405. hostName = hostName.Remove(hostName.Length - 1, 1);
  406. foreach (IHttpServer server in m_Servers.Values)
  407. {
  408. server.HostName = hostName;
  409. }
  410. MainConsole.Instance.Info("Finished reloading configuration.");
  411. }
  412. public virtual void HandleShowInfo(IScene scene, string[] cmd)
  413. {
  414. MainConsole.Instance.Info("Version: " + m_version);
  415. MainConsole.Instance.Info("Startup directory: " + Environment.CurrentDirectory);
  416. }
  417. public virtual void HandleShowVersion(IScene scene, string[] cmd)
  418. {
  419. MainConsole.Instance.Info(
  420. String.Format(
  421. "Version: {0}", m_version));
  422. }
  423. #endregion
  424. /// <summary>
  425. /// Should be overriden and referenced by descendents if they need to perform extra shutdown processing
  426. /// Performs any last-minute sanity checking and shuts down the region server
  427. /// </summary>
  428. public virtual void Shutdown(bool close)
  429. {
  430. try
  431. {
  432. try
  433. {
  434. RemovePIDFile();
  435. if (m_shutdownCommandsFile != String.Empty)
  436. {
  437. RunCommandScript(m_shutdownCommandsFile);
  438. }
  439. }
  440. catch
  441. {
  442. //It doesn't matter, just shut down
  443. }
  444. try
  445. {
  446. //Close out all the modules
  447. CloseModules();
  448. }
  449. catch
  450. {
  451. //Just shut down already
  452. }
  453. try
  454. {
  455. //Close the thread pool
  456. Util.CloseThreadPool();
  457. }
  458. catch
  459. {
  460. //Just shut down already
  461. }
  462. try
  463. {
  464. //Stop the HTTP server(s)
  465. foreach (IHttpServer server in m_Servers.Values)
  466. {
  467. server.Stop();
  468. }
  469. }
  470. catch
  471. {
  472. //Again, just shut down
  473. }
  474. if (close)
  475. MainConsole.Instance.Info("[SHUTDOWN]: Terminating");
  476. MainConsole.Instance.Info("[SHUTDOWN]: Shutdown processing on main thread complete. " +
  477. (close ? " Exiting..." : ""));
  478. if (close)
  479. Environment.Exit(0);
  480. }
  481. catch
  482. {
  483. }
  484. }
  485. /// <summary>
  486. /// Write the PID file to the hard drive
  487. /// </summary>
  488. /// <param name="path"></param>
  489. protected void CreatePIDFile(string path)
  490. {
  491. try
  492. {
  493. string pidstring = System.Diagnostics.Process.GetCurrentProcess().Id.ToString();
  494. FileStream fs = File.Create(path);
  495. System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
  496. Byte[] buf = enc.GetBytes(pidstring);
  497. fs.Write(buf, 0, buf.Length);
  498. fs.Close();
  499. m_pidFile = path;
  500. }
  501. catch (Exception)
  502. {
  503. }
  504. }
  505. /// <summary>
  506. /// Delete the PID file now that we are done running
  507. /// </summary>
  508. protected void RemovePIDFile()
  509. {
  510. if (m_pidFile != String.Empty)
  511. {
  512. try
  513. {
  514. File.Delete(m_pidFile);
  515. m_pidFile = String.Empty;
  516. }
  517. catch (Exception)
  518. {
  519. }
  520. }
  521. }
  522. }
  523. }