ServicesServerBase.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  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 OpenSimulator 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 System;
  28. using System.Collections.Generic;
  29. using System.IO;
  30. using System.Reflection;
  31. using System.Threading;
  32. using System.Text;
  33. using System.Xml;
  34. using OpenSim.Framework;
  35. using OpenSim.Framework.Console;
  36. using OpenSim.Framework.Monitoring;
  37. using OpenSim.Framework.Servers;
  38. using log4net;
  39. using log4net.Config;
  40. using log4net.Appender;
  41. using log4net.Core;
  42. using log4net.Repository;
  43. using Nini.Config;
  44. namespace OpenSim.Server.Base
  45. {
  46. public class ServicesServerBase : ServerBase
  47. {
  48. // Logger
  49. //
  50. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  51. // Command line args
  52. //
  53. protected string[] m_Arguments;
  54. protected string m_configDirectory = ".";
  55. // Run flag
  56. //
  57. private bool m_Running = true;
  58. // Handle all the automagical stuff
  59. //
  60. public ServicesServerBase(string prompt, string[] args) : base()
  61. {
  62. // Save raw arguments
  63. m_Arguments = args;
  64. // Read command line
  65. ArgvConfigSource argvConfig = new ArgvConfigSource(args);
  66. argvConfig.AddSwitch("Startup", "console", "c");
  67. argvConfig.AddSwitch("Startup", "logfile", "l");
  68. argvConfig.AddSwitch("Startup", "inifile", "i");
  69. argvConfig.AddSwitch("Startup", "prompt", "p");
  70. argvConfig.AddSwitch("Startup", "logconfig", "g");
  71. // Automagically create the ini file name
  72. string fileName = "";
  73. if (Assembly.GetEntryAssembly() != null)
  74. fileName = Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location);
  75. string iniFile = fileName + ".ini";
  76. string logConfig = null;
  77. IConfig startupConfig = argvConfig.Configs["Startup"];
  78. if (startupConfig != null)
  79. {
  80. // Check if a file name was given on the command line
  81. iniFile = startupConfig.GetString("inifile", iniFile);
  82. // Check if a prompt was given on the command line
  83. prompt = startupConfig.GetString("prompt", prompt);
  84. // Check for a Log4Net config file on the command line
  85. logConfig =startupConfig.GetString("logconfig", logConfig);
  86. }
  87. Config = ReadConfigSource(iniFile);
  88. List<string> sources = new List<string>();
  89. sources.Add(iniFile);
  90. int sourceIndex = 1;
  91. while (AddIncludes(Config, sources))
  92. {
  93. for ( ; sourceIndex < sources.Count ; ++sourceIndex)
  94. {
  95. IConfigSource s = ReadConfigSource(sources[sourceIndex]);
  96. Config.Merge(s);
  97. }
  98. }
  99. // Merge OpSys env vars
  100. Console.WriteLine("[CONFIG]: Loading environment variables for Config");
  101. Util.MergeEnvironmentToConfig(Config);
  102. // Merge the configuration from the command line into the loaded file
  103. Config.Merge(argvConfig);
  104. Config.ReplaceKeyValues();
  105. // Refresh the startupConfig post merge
  106. if (Config.Configs["Startup"] != null)
  107. {
  108. startupConfig = Config.Configs["Startup"];
  109. }
  110. if (startupConfig != null)
  111. {
  112. m_configDirectory = startupConfig.GetString("ConfigDirectory", m_configDirectory);
  113. prompt = startupConfig.GetString("Prompt", prompt);
  114. }
  115. // Allow derived classes to load config before the console is opened.
  116. ReadConfig();
  117. // Create main console
  118. string consoleType = "local";
  119. if (startupConfig != null)
  120. consoleType = startupConfig.GetString("console", consoleType);
  121. if (consoleType == "basic")
  122. MainConsole.Instance = new CommandConsole(prompt);
  123. else if (consoleType == "rest")
  124. MainConsole.Instance = new RemoteConsole(prompt);
  125. else if (consoleType == "mock")
  126. MainConsole.Instance = new MockConsole();
  127. else if (consoleType == "local")
  128. MainConsole.Instance = new LocalConsole(prompt, startupConfig);
  129. MainConsole.Instance.ReadConfig(Config);
  130. m_console = MainConsole.Instance;
  131. if (logConfig != null)
  132. {
  133. FileInfo cfg = new FileInfo(logConfig);
  134. XmlConfigurator.Configure(cfg);
  135. }
  136. else
  137. {
  138. XmlConfigurator.Configure();
  139. }
  140. LogEnvironmentInformation();
  141. RegisterCommonAppenders(startupConfig);
  142. if (startupConfig.GetString("PIDFile", String.Empty) != String.Empty)
  143. {
  144. CreatePIDFile(startupConfig.GetString("PIDFile"));
  145. }
  146. RegisterCommonCommands();
  147. RegisterCommonComponents(Config);
  148. // Allow derived classes to perform initialization that
  149. // needs to be done after the console has opened
  150. Initialise();
  151. }
  152. public bool Running
  153. {
  154. get { return m_Running; }
  155. }
  156. private bool DoneShutdown = false;
  157. public virtual int Run()
  158. {
  159. Watchdog.Enabled = true;
  160. MemoryWatchdog.Enabled = true;
  161. while (m_Running)
  162. {
  163. try
  164. {
  165. MainConsole.Instance.Prompt();
  166. }
  167. catch (Exception e)
  168. {
  169. m_log.ErrorFormat("Command error: {0}", e);
  170. }
  171. }
  172. if (!DoneShutdown)
  173. {
  174. DoneShutdown = true;
  175. MainServer.Stop();
  176. MemoryWatchdog.Enabled = false;
  177. Watchdog.Enabled = false;
  178. WorkManager.Stop();
  179. RemovePIDFile();
  180. }
  181. return 0;
  182. }
  183. protected override void ShutdownSpecific()
  184. {
  185. if(!m_Running)
  186. return;
  187. m_Running = false;
  188. m_log.Info("[CONSOLE] Quitting");
  189. base.ShutdownSpecific();
  190. if (!DoneShutdown)
  191. {
  192. DoneShutdown = true;
  193. MainServer.Stop();
  194. MemoryWatchdog.Enabled = false;
  195. Watchdog.Enabled = false;
  196. WorkManager.Stop();
  197. RemovePIDFile();
  198. Util.StopThreadPool();
  199. Environment.Exit(0);
  200. }
  201. }
  202. protected virtual void ReadConfig()
  203. {
  204. }
  205. protected virtual void Initialise()
  206. {
  207. }
  208. /// <summary>
  209. /// Adds the included files as ini configuration files
  210. /// </summary>
  211. /// <param name="sources">List of URL strings or filename strings</param>
  212. private bool AddIncludes(IConfigSource configSource, List<string> sources)
  213. {
  214. bool sourcesAdded = false;
  215. //loop over config sources
  216. foreach (IConfig config in configSource.Configs)
  217. {
  218. // Look for Include-* in the key name
  219. string[] keys = config.GetKeys();
  220. foreach (string k in keys)
  221. {
  222. if (k.StartsWith("Include-"))
  223. {
  224. // read the config file to be included.
  225. string file = config.GetString(k);
  226. if (IsUri(file))
  227. {
  228. if (!sources.Contains(file))
  229. {
  230. sourcesAdded = true;
  231. sources.Add(file);
  232. }
  233. }
  234. else
  235. {
  236. string basepath = Path.GetFullPath(m_configDirectory);
  237. // Resolve relative paths with wildcards
  238. string chunkWithoutWildcards = file;
  239. string chunkWithWildcards = string.Empty;
  240. int wildcardIndex = file.IndexOfAny(new char[] { '*', '?' });
  241. if (wildcardIndex != -1)
  242. {
  243. chunkWithoutWildcards = file.Substring(0, wildcardIndex);
  244. chunkWithWildcards = file.Substring(wildcardIndex);
  245. }
  246. string path = Path.Combine(basepath, chunkWithoutWildcards);
  247. path = Path.GetFullPath(path) + chunkWithWildcards;
  248. string[] paths = Util.Glob(path);
  249. // If the include path contains no wildcards, then warn the user that it wasn't found.
  250. if (wildcardIndex == -1 && paths.Length == 0)
  251. {
  252. Console.WriteLine("[CONFIG]: Could not find include file {0}", path);
  253. }
  254. else
  255. {
  256. foreach (string p in paths)
  257. {
  258. if (!sources.Contains(p))
  259. {
  260. sourcesAdded = true;
  261. sources.Add(p);
  262. }
  263. }
  264. }
  265. }
  266. }
  267. }
  268. }
  269. return sourcesAdded;
  270. }
  271. /// <summary>
  272. /// Check if we can convert the string to a URI
  273. /// </summary>
  274. /// <param name="file">String uri to the remote resource</param>
  275. /// <returns>true if we can convert the string to a Uri object</returns>
  276. bool IsUri(string file)
  277. {
  278. Uri configUri;
  279. return Uri.TryCreate(file, UriKind.Absolute,
  280. out configUri) && configUri.Scheme == Uri.UriSchemeHttp;
  281. }
  282. IConfigSource ReadConfigSource(string iniFile)
  283. {
  284. // Find out of the file name is a URI and remote load it if possible.
  285. // Load it as a local file otherwise.
  286. Uri configUri;
  287. IConfigSource s = null;
  288. try
  289. {
  290. if (Uri.TryCreate(iniFile, UriKind.Absolute, out configUri) &&
  291. configUri.Scheme == Uri.UriSchemeHttp)
  292. {
  293. XmlReader r = XmlReader.Create(iniFile);
  294. s = new XmlConfigSource(r);
  295. }
  296. else
  297. {
  298. s = new IniConfigSource(iniFile);
  299. }
  300. }
  301. catch (Exception e)
  302. {
  303. System.Console.WriteLine("Error reading from config source. {0}", e.Message);
  304. Environment.Exit(1);
  305. }
  306. return s;
  307. }
  308. }
  309. }