OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
ServerBase.cs
Go to the documentation of this file.
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 
28 using System;
29 using System.Collections.Generic;
30 using System.Diagnostics;
31 using System.IO;
32 using System.Linq;
33 using System.Reflection;
34 using System.Text;
35 using System.Text.RegularExpressions;
36 using System.Threading;
37 using log4net;
38 using log4net.Appender;
39 using log4net.Core;
40 using log4net.Repository;
41 using Nini.Config;
42 using OpenSim.Framework.Console;
43 using OpenSim.Framework.Monitoring;
44 
45 namespace OpenSim.Framework.Servers
46 {
47  public class ServerBase
48  {
49  private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
50 
51  public IConfigSource Config { get; protected set; }
52 
57 
59  protected FileAppender m_logFileAppender;
60 
61  protected DateTime m_startuptime;
62  protected string m_startupDirectory = Environment.CurrentDirectory;
63 
64  protected string m_pidFile = String.Empty;
65 
67 
71  protected string m_version;
72 
73  public ServerBase()
74  {
75  m_startuptime = DateTime.Now;
76  m_version = VersionInfo.Version;
77  EnhanceVersionInformation();
78  }
79 
80  protected void CreatePIDFile(string path)
81  {
82  if (File.Exists(path))
83  m_log.ErrorFormat(
84  "[SERVER BASE]: Previous pid file {0} still exists on startup. Possibly previously unclean shutdown.",
85  path);
86 
87  try
88  {
89  string pidstring = System.Diagnostics.Process.GetCurrentProcess().Id.ToString();
90 
91  using (FileStream fs = File.Create(path))
92  {
93  Byte[] buf = Encoding.ASCII.GetBytes(pidstring);
94  fs.Write(buf, 0, buf.Length);
95  }
96 
97  m_pidFile = path;
98 
99  m_log.InfoFormat("[SERVER BASE]: Created pid file {0}", m_pidFile);
100  }
101  catch (Exception e)
102  {
103  m_log.Warn(string.Format("[SERVER BASE]: Could not create PID file at {0} ", path), e);
104  }
105  }
106 
107  protected void RemovePIDFile()
108  {
109  if (m_pidFile != String.Empty)
110  {
111  try
112  {
113  File.Delete(m_pidFile);
114  }
115  catch (Exception e)
116  {
117  m_log.Error(string.Format("[SERVER BASE]: Error whilst removing {0} ", m_pidFile), e);
118  }
119 
120  m_pidFile = String.Empty;
121  }
122  }
123 
129  {
130  // FIXME: This should be done down in ServerBase but we need to sort out and refactor the log4net
131  // XmlConfigurator calls first accross servers.
132  m_log.InfoFormat("[SERVER BASE]: Starting in {0}", m_startupDirectory);
133 
134  m_log.InfoFormat("[SERVER BASE]: OpenSimulator version: {0}", m_version);
135 
136  // clr version potentially is more confusing than helpful, since it doesn't tell us if we're running under Mono/MS .NET and
137  // the clr version number doesn't match the project version number under Mono.
138  //m_log.Info("[STARTUP]: Virtual machine runtime version: " + Environment.Version + Environment.NewLine);
139  m_log.InfoFormat(
140  "[SERVER BASE]: Operating system version: {0}, .NET platform {1}, {2}-bit",
141  Environment.OSVersion, Environment.OSVersion.Platform, Util.Is64BitProcess() ? "64" : "32");
142  }
143 
144  public void RegisterCommonAppenders(IConfig startupConfig)
145  {
146  ILoggerRepository repository = LogManager.GetRepository();
147  IAppender[] appenders = repository.GetAppenders();
148 
149  foreach (IAppender appender in appenders)
150  {
151  if (appender.Name == "Console")
152  {
153  m_consoleAppender = (OpenSimAppender)appender;
154  }
155  else if (appender.Name == "LogFileAppender")
156  {
157  m_logFileAppender = (FileAppender)appender;
158  }
159  }
160 
161  if (null == m_consoleAppender)
162  {
163  Notice("No appender named Console found (see the log4net config file for this executable)!");
164  }
165  else
166  {
167  // FIXME: This should be done through an interface rather than casting.
168  m_consoleAppender.Console = (ConsoleBase)m_console;
169 
170  // If there is no threshold set then the threshold is effectively everything.
171  if (null == m_consoleAppender.Threshold)
172  m_consoleAppender.Threshold = Level.All;
173 
174  Notice(String.Format("Console log level is {0}", m_consoleAppender.Threshold));
175  }
176 
177  if (m_logFileAppender != null && startupConfig != null)
178  {
179  string cfgFileName = startupConfig.GetString("LogFile", null);
180  if (cfgFileName != null)
181  {
182  m_logFileAppender.File = cfgFileName;
183  m_logFileAppender.ActivateOptions();
184  }
185 
186  m_log.InfoFormat("[SERVER BASE]: Logging started to file {0}", m_logFileAppender.File);
187  }
188  }
189 
194  {
195  if (m_console == null)
196  return;
197 
198  m_console.Commands.AddCommand(
199  "General", false, "show info", "show info", "Show general information about the server", HandleShow);
200 
201  m_console.Commands.AddCommand(
202  "General", false, "show version", "show version", "Show server version", HandleShow);
203 
204  m_console.Commands.AddCommand(
205  "General", false, "show uptime", "show uptime", "Show server uptime", HandleShow);
206 
207  m_console.Commands.AddCommand(
208  "General", false, "get log level", "get log level", "Get the current console logging level",
209  (mod, cmd) => ShowLogLevel());
210 
211  m_console.Commands.AddCommand(
212  "General", false, "set log level", "set log level <level>",
213  "Set the console logging level for this session.", HandleSetLogLevel);
214 
215  m_console.Commands.AddCommand(
216  "General", false, "config set",
217  "config set <section> <key> <value>",
218  "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);
219 
220  m_console.Commands.AddCommand(
221  "General", false, "config get",
222  "config get [<section>] [<key>]",
223  "Synonym for config show",
224  HandleConfig);
225 
226  m_console.Commands.AddCommand(
227  "General", false, "config show",
228  "config show [<section>] [<key>]",
229  "Show config information",
230  "If neither section nor field are specified, then the whole current configuration is printed." + Environment.NewLine
231  + "If a section is given but not a field, then all fields in that section are printed.",
232  HandleConfig);
233 
234  m_console.Commands.AddCommand(
235  "General", false, "config save",
236  "config save <path>",
237  "Save current configuration to a file at the given path", HandleConfig);
238 
239  m_console.Commands.AddCommand(
240  "General", false, "command-script",
241  "command-script <script>",
242  "Run a command script from file", HandleScript);
243 
244  m_console.Commands.AddCommand(
245  "General", false, "show threads",
246  "show threads",
247  "Show thread status", HandleShow);
248 
249  m_console.Commands.AddCommand(
250  "Debug", false, "threads abort",
251  "threads abort <thread-id>",
252  "Abort a managed thread. Use \"show threads\" to find possible threads.", HandleThreadsAbort);
253 
254  m_console.Commands.AddCommand(
255  "General", false, "threads show",
256  "threads show",
257  "Show thread status. Synonym for \"show threads\"",
258  (string module, string[] args) => Notice(GetThreadsReport()));
259 
260  m_console.Commands.AddCommand (
261  "Debug", false, "debug comms set",
262  "debug comms set serialosdreq true|false",
263  "Set comms parameters. For debug purposes.",
264  HandleDebugCommsSet);
265 
266  m_console.Commands.AddCommand (
267  "Debug", false, "debug comms status",
268  "debug comms status",
269  "Show current debug comms parameters.",
270  HandleDebugCommsStatus);
271 
272  m_console.Commands.AddCommand (
273  "Debug", false, "debug threadpool set",
274  "debug threadpool set worker|iocp min|max <n>",
275  "Set threadpool parameters. For debug purposes.",
276  HandleDebugThreadpoolSet);
277 
278  m_console.Commands.AddCommand (
279  "Debug", false, "debug threadpool status",
280  "debug threadpool status",
281  "Show current debug threadpool parameters.",
282  HandleDebugThreadpoolStatus);
283 
284  m_console.Commands.AddCommand(
285  "Debug", false, "debug threadpool level",
286  "debug threadpool level 0.." + Util.MAX_THREADPOOL_LEVEL,
287  "Turn on logging of activity in the main thread pool.",
288  "Log levels:\n"
289  + " 0 = no logging\n"
290  + " 1 = only first line of stack trace; don't log common threads\n"
291  + " 2 = full stack trace; don't log common threads\n"
292  + " 3 = full stack trace, including common threads\n",
293  HandleDebugThreadpoolLevel);
294 
295 // m_console.Commands.AddCommand(
296 // "Debug", false, "show threadpool calls active",
297 // "show threadpool calls active",
298 // "Show details about threadpool calls that are still active (currently waiting or in progress)",
299 // HandleShowThreadpoolCallsActive);
300 
301  m_console.Commands.AddCommand(
302  "Debug", false, "show threadpool calls complete",
303  "show threadpool calls complete",
304  "Show details about threadpool calls that have been completed.",
305  HandleShowThreadpoolCallsComplete);
306 
307  m_console.Commands.AddCommand(
308  "Debug", false, "force gc",
309  "force gc",
310  "Manually invoke runtime garbage collection. For debugging purposes",
311  HandleForceGc);
312 
313  m_console.Commands.AddCommand(
314  "General", false, "quit",
315  "quit",
316  "Quit the application", (mod, args) => Shutdown());
317 
318  m_console.Commands.AddCommand(
319  "General", false, "shutdown",
320  "shutdown",
321  "Quit the application", (mod, args) => Shutdown());
322 
323  ChecksManager.RegisterConsoleCommands(m_console);
324  StatsManager.RegisterConsoleCommands(m_console);
325  }
326 
327  public void RegisterCommonComponents(IConfigSource configSource)
328  {
329  IConfig networkConfig = configSource.Configs["Network"];
330 
331  if (networkConfig != null)
332  {
333  WebUtil.SerializeOSDRequestsPerEndpoint = networkConfig.GetBoolean("SerializeOSDRequests", false);
334  }
335 
336  m_serverStatsCollector = new ServerStatsCollector();
337  m_serverStatsCollector.Initialise(configSource);
338  m_serverStatsCollector.Start();
339  }
340 
341  private void HandleDebugCommsStatus(string module, string[] args)
342  {
343  Notice("serialosdreq is {0}", WebUtil.SerializeOSDRequestsPerEndpoint);
344  }
345 
346  private void HandleDebugCommsSet(string module, string[] args)
347  {
348  if (args.Length != 5)
349  {
350  Notice("Usage: debug comms set serialosdreq true|false");
351  return;
352  }
353 
354  if (args[3] != "serialosdreq")
355  {
356  Notice("Usage: debug comms set serialosdreq true|false");
357  return;
358  }
359 
360  bool setSerializeOsdRequests;
361 
362  if (!ConsoleUtil.TryParseConsoleBool(m_console, args[4], out setSerializeOsdRequests))
363  return;
364 
365  WebUtil.SerializeOSDRequestsPerEndpoint = setSerializeOsdRequests;
366 
367  Notice("serialosdreq is now {0}", setSerializeOsdRequests);
368  }
369 
370  private void HandleShowThreadpoolCallsActive(string module, string[] args)
371  {
372  List<KeyValuePair<string, int>> calls = Util.GetFireAndForgetCallsInProgress().ToList();
373  calls.Sort((kvp1, kvp2) => kvp2.Value.CompareTo(kvp1.Value));
374  int namedCalls = 0;
375 
377  foreach (KeyValuePair<string, int> kvp in calls)
378  {
379  if (kvp.Value > 0)
380  {
381  cdl.AddRow(kvp.Key, kvp.Value);
382  namedCalls += kvp.Value;
383  }
384  }
385 
386  cdl.AddRow("TOTAL NAMED", namedCalls);
387 
388  long allQueuedCalls = Util.TotalQueuedFireAndForgetCalls;
389  long allRunningCalls = Util.TotalRunningFireAndForgetCalls;
390 
391  cdl.AddRow("TOTAL QUEUED", allQueuedCalls);
392  cdl.AddRow("TOTAL RUNNING", allRunningCalls);
393  cdl.AddRow("TOTAL ANONYMOUS", allQueuedCalls + allRunningCalls - namedCalls);
394  cdl.AddRow("TOTAL ALL", allQueuedCalls + allRunningCalls);
395 
396  MainConsole.Instance.Output(cdl.ToString());
397  }
398 
399  private void HandleShowThreadpoolCallsComplete(string module, string[] args)
400  {
401  List<KeyValuePair<string, int>> calls = Util.GetFireAndForgetCallsMade().ToList();
402  calls.Sort((kvp1, kvp2) => kvp2.Value.CompareTo(kvp1.Value));
403  int namedCallsMade = 0;
404 
406  foreach (KeyValuePair<string, int> kvp in calls)
407  {
408  cdl.AddRow(kvp.Key, kvp.Value);
409  namedCallsMade += kvp.Value;
410  }
411 
412  cdl.AddRow("TOTAL NAMED", namedCallsMade);
413 
414  long allCallsMade = Util.TotalFireAndForgetCallsMade;
415  cdl.AddRow("TOTAL ANONYMOUS", allCallsMade - namedCallsMade);
416  cdl.AddRow("TOTAL ALL", allCallsMade);
417 
418  MainConsole.Instance.Output(cdl.ToString());
419  }
420 
421  private void HandleDebugThreadpoolStatus(string module, string[] args)
422  {
423  int workerThreads, iocpThreads;
424 
425  ThreadPool.GetMinThreads(out workerThreads, out iocpThreads);
426  Notice("Min worker threads: {0}", workerThreads);
427  Notice("Min IOCP threads: {0}", iocpThreads);
428 
429  ThreadPool.GetMaxThreads(out workerThreads, out iocpThreads);
430  Notice("Max worker threads: {0}", workerThreads);
431  Notice("Max IOCP threads: {0}", iocpThreads);
432 
433  ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads);
434  Notice("Available worker threads: {0}", workerThreads);
435  Notice("Available IOCP threads: {0}", iocpThreads);
436  }
437 
438  private void HandleDebugThreadpoolSet(string module, string[] args)
439  {
440  if (args.Length != 6)
441  {
442  Notice("Usage: debug threadpool set worker|iocp min|max <n>");
443  return;
444  }
445 
446  int newThreads;
447 
448  if (!ConsoleUtil.TryParseConsoleInt(m_console, args[5], out newThreads))
449  return;
450 
451  string poolType = args[3];
452  string bound = args[4];
453 
454  bool fail = false;
455  int workerThreads, iocpThreads;
456 
457  if (poolType == "worker")
458  {
459  if (bound == "min")
460  {
461  ThreadPool.GetMinThreads(out workerThreads, out iocpThreads);
462 
463  if (!ThreadPool.SetMinThreads(newThreads, iocpThreads))
464  fail = true;
465  }
466  else
467  {
468  ThreadPool.GetMaxThreads(out workerThreads, out iocpThreads);
469 
470  if (!ThreadPool.SetMaxThreads(newThreads, iocpThreads))
471  fail = true;
472  }
473  }
474  else
475  {
476  if (bound == "min")
477  {
478  ThreadPool.GetMinThreads(out workerThreads, out iocpThreads);
479 
480  if (!ThreadPool.SetMinThreads(workerThreads, newThreads))
481  fail = true;
482  }
483  else
484  {
485  ThreadPool.GetMaxThreads(out workerThreads, out iocpThreads);
486 
487  if (!ThreadPool.SetMaxThreads(workerThreads, newThreads))
488  fail = true;
489  }
490  }
491 
492  if (fail)
493  {
494  Notice("ERROR: Could not set {0} {1} threads to {2}", poolType, bound, newThreads);
495  }
496  else
497  {
498  int minWorkerThreads, maxWorkerThreads, minIocpThreads, maxIocpThreads;
499 
500  ThreadPool.GetMinThreads(out minWorkerThreads, out minIocpThreads);
501  ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxIocpThreads);
502 
503  Notice("Min worker threads now {0}", minWorkerThreads);
504  Notice("Min IOCP threads now {0}", minIocpThreads);
505  Notice("Max worker threads now {0}", maxWorkerThreads);
506  Notice("Max IOCP threads now {0}", maxIocpThreads);
507  }
508  }
509 
510  private static void HandleDebugThreadpoolLevel(string module, string[] cmdparams)
511  {
512  if (cmdparams.Length < 4)
513  {
514  MainConsole.Instance.Output("Usage: debug threadpool level 0.." + Util.MAX_THREADPOOL_LEVEL);
515  return;
516  }
517 
518  string rawLevel = cmdparams[3];
519  int newLevel;
520 
521  if (!int.TryParse(rawLevel, out newLevel))
522  {
523  MainConsole.Instance.OutputFormat("{0} is not a valid debug level", rawLevel);
524  return;
525  }
526 
527  if (newLevel < 0 || newLevel > Util.MAX_THREADPOOL_LEVEL)
528  {
529  MainConsole.Instance.OutputFormat("{0} is outside the valid debug level range of 0.." + Util.MAX_THREADPOOL_LEVEL, newLevel);
530  return;
531  }
532 
533  Util.LogThreadPool = newLevel;
534  MainConsole.Instance.OutputFormat("LogThreadPool set to {0}", newLevel);
535  }
536 
537  private void HandleForceGc(string module, string[] args)
538  {
539  Notice("Manually invoking runtime garbage collection");
540  GC.Collect();
541  }
542 
543  public virtual void HandleShow(string module, string[] cmd)
544  {
545  List<string> args = new List<string>(cmd);
546 
547  args.RemoveAt(0);
548 
549  string[] showParams = args.ToArray();
550 
551  switch (showParams[0])
552  {
553  case "info":
554  ShowInfo();
555  break;
556 
557  case "version":
558  Notice(GetVersionText());
559  break;
560 
561  case "uptime":
562  Notice(GetUptimeReport());
563  break;
564 
565  case "threads":
566  Notice(GetThreadsReport());
567  break;
568  }
569  }
570 
576  private void HandleConfig(string module, string[] cmd)
577  {
578  List<string> args = new List<string>(cmd);
579  args.RemoveAt(0);
580  string[] cmdparams = args.ToArray();
581 
582  if (cmdparams.Length > 0)
583  {
584  string firstParam = cmdparams[0].ToLower();
585 
586  switch (firstParam)
587  {
588  case "set":
589  if (cmdparams.Length < 4)
590  {
591  Notice("Syntax: config set <section> <key> <value>");
592  Notice("Example: config set ScriptEngine.DotNetEngine NumberOfScriptThreads 5");
593  }
594  else
595  {
596  IConfig c;
597  IConfigSource source = new IniConfigSource();
598  c = source.AddConfig(cmdparams[1]);
599  if (c != null)
600  {
601  string _value = String.Join(" ", cmdparams, 3, cmdparams.Length - 3);
602  c.Set(cmdparams[2], _value);
603  Config.Merge(source);
604 
605  Notice("In section [{0}], set {1} = {2}", c.Name, cmdparams[2], _value);
606  }
607  }
608  break;
609 
610  case "get":
611  case "show":
612  if (cmdparams.Length == 1)
613  {
614  foreach (IConfig config in Config.Configs)
615  {
616  Notice("[{0}]", config.Name);
617  string[] keys = config.GetKeys();
618  foreach (string key in keys)
619  Notice(" {0} = {1}", key, config.GetString(key));
620  }
621  }
622  else if (cmdparams.Length == 2 || cmdparams.Length == 3)
623  {
624  IConfig config = Config.Configs[cmdparams[1]];
625  if (config == null)
626  {
627  Notice("Section \"{0}\" does not exist.",cmdparams[1]);
628  break;
629  }
630  else
631  {
632  if (cmdparams.Length == 2)
633  {
634  Notice("[{0}]", config.Name);
635  foreach (string key in config.GetKeys())
636  Notice(" {0} = {1}", key, config.GetString(key));
637  }
638  else
639  {
640  Notice(
641  "config get {0} {1} : {2}",
642  cmdparams[1], cmdparams[2], config.GetString(cmdparams[2]));
643  }
644  }
645  }
646  else
647  {
648  Notice("Syntax: config {0} [<section>] [<key>]", firstParam);
649  Notice("Example: config {0} ScriptEngine.DotNetEngine NumberOfScriptThreads", firstParam);
650  }
651 
652  break;
653 
654  case "save":
655  if (cmdparams.Length < 2)
656  {
657  Notice("Syntax: config save <path>");
658  return;
659  }
660 
661  string path = cmdparams[1];
662  Notice("Saving configuration file: {0}", path);
663 
664  if (Config is IniConfigSource)
665  {
666  IniConfigSource iniCon = (IniConfigSource)Config;
667  iniCon.Save(path);
668  }
669  else if (Config is XmlConfigSource)
670  {
671  XmlConfigSource xmlCon = (XmlConfigSource)Config;
672  xmlCon.Save(path);
673  }
674 
675  break;
676  }
677  }
678  }
679 
680  private void HandleSetLogLevel(string module, string[] cmd)
681  {
682  if (cmd.Length != 4)
683  {
684  Notice("Usage: set log level <level>");
685  return;
686  }
687 
688  if (null == m_consoleAppender)
689  {
690  Notice("No appender named Console found (see the log4net config file for this executable)!");
691  return;
692  }
693 
694  string rawLevel = cmd[3];
695 
696  ILoggerRepository repository = LogManager.GetRepository();
697  Level consoleLevel = repository.LevelMap[rawLevel];
698 
699  if (consoleLevel != null)
700  m_consoleAppender.Threshold = consoleLevel;
701  else
702  Notice(
703  "{0} is not a valid logging level. Valid logging levels are ALL, DEBUG, INFO, WARN, ERROR, FATAL, OFF",
704  rawLevel);
705 
706  ShowLogLevel();
707  }
708 
709  private void ShowLogLevel()
710  {
711  Notice("Console log level is {0}", m_consoleAppender.Threshold);
712  }
713 
714  protected virtual void HandleScript(string module, string[] parms)
715  {
716  if (parms.Length != 2)
717  {
718  Notice("Usage: command-script <path-to-script");
719  return;
720  }
721 
722  RunCommandScript(parms[1]);
723  }
724 
729  protected void RunCommandScript(string fileName)
730  {
731  if (m_console == null)
732  return;
733 
734  if (File.Exists(fileName))
735  {
736  m_log.Info("[SERVER BASE]: Running " + fileName);
737 
738  using (StreamReader readFile = File.OpenText(fileName))
739  {
740  string currentCommand;
741  while ((currentCommand = readFile.ReadLine()) != null)
742  {
743  currentCommand = currentCommand.Trim();
744  if (!(currentCommand == ""
745  || currentCommand.StartsWith(";")
746  || currentCommand.StartsWith("//")
747  || currentCommand.StartsWith("#")))
748  {
749  m_log.Info("[SERVER BASE]: Running '" + currentCommand + "'");
750  m_console.RunCommand(currentCommand);
751  }
752  }
753  }
754  }
755  }
756 
761  protected string GetUptimeReport()
762  {
763  StringBuilder sb = new StringBuilder(String.Format("Time now is {0}\n", DateTime.Now));
764  sb.Append(String.Format("Server has been running since {0}, {1}\n", m_startuptime.DayOfWeek, m_startuptime));
765  sb.Append(String.Format("That is an elapsed time of {0}\n", DateTime.Now - m_startuptime));
766 
767  return sb.ToString();
768  }
769 
770  protected void ShowInfo()
771  {
772  Notice(GetVersionText());
773  Notice("Startup directory: " + m_startupDirectory);
774  if (null != m_consoleAppender)
775  Notice(String.Format("Console log level: {0}", m_consoleAppender.Threshold));
776  }
777 
781  protected void EnhanceVersionInformation()
782  {
783  string buildVersion = string.Empty;
784 
785  // The subversion information is deprecated and will be removed at a later date
786  // Add subversion revision information if available
787  // Try file "svn_revision" in the current directory first, then the .svn info.
788  // This allows to make the revision available in simulators not running from the source tree.
789  // FIXME: Making an assumption about the directory we're currently in - we do this all over the place
790  // elsewhere as well
791  string gitDir = "../.git/";
792  string gitRefPointerPath = gitDir + "HEAD";
793 
794  string svnRevisionFileName = "svn_revision";
795  string svnFileName = ".svn/entries";
796  string manualVersionFileName = ".version";
797  string inputLine;
798  int strcmp;
799 
800  if (File.Exists(manualVersionFileName))
801  {
802  using (StreamReader CommitFile = File.OpenText(manualVersionFileName))
803  buildVersion = CommitFile.ReadLine();
804 
805  m_version += buildVersion ?? "";
806  }
807  else if (File.Exists(gitRefPointerPath))
808  {
809 // m_log.DebugFormat("[SERVER BASE]: Found {0}", gitRefPointerPath);
810 
811  string rawPointer = "";
812 
813  using (StreamReader pointerFile = File.OpenText(gitRefPointerPath))
814  rawPointer = pointerFile.ReadLine();
815 
816 // m_log.DebugFormat("[SERVER BASE]: rawPointer [{0}]", rawPointer);
817 
818  Match m = Regex.Match(rawPointer, "^ref: (.+)$");
819 
820  if (m.Success)
821  {
822 // m_log.DebugFormat("[SERVER BASE]: Matched [{0}]", m.Groups[1].Value);
823 
824  string gitRef = m.Groups[1].Value;
825  string gitRefPath = gitDir + gitRef;
826  if (File.Exists(gitRefPath))
827  {
828 // m_log.DebugFormat("[SERVER BASE]: Found gitRefPath [{0}]", gitRefPath);
829 
830  using (StreamReader refFile = File.OpenText(gitRefPath))
831  {
832  string gitHash = refFile.ReadLine();
833  m_version += gitHash.Substring(0, 7);
834  }
835  }
836  }
837  }
838  else
839  {
840  // Remove the else logic when subversion mirror is no longer used
841  if (File.Exists(svnRevisionFileName))
842  {
843  StreamReader RevisionFile = File.OpenText(svnRevisionFileName);
844  buildVersion = RevisionFile.ReadLine();
845  buildVersion.Trim();
846  RevisionFile.Close();
847  }
848 
849  if (string.IsNullOrEmpty(buildVersion) && File.Exists(svnFileName))
850  {
851  StreamReader EntriesFile = File.OpenText(svnFileName);
852  inputLine = EntriesFile.ReadLine();
853  while (inputLine != null)
854  {
855  // using the dir svn revision at the top of entries file
856  strcmp = String.Compare(inputLine, "dir");
857  if (strcmp == 0)
858  {
859  buildVersion = EntriesFile.ReadLine();
860  break;
861  }
862  else
863  {
864  inputLine = EntriesFile.ReadLine();
865  }
866  }
867  EntriesFile.Close();
868  }
869 
870  m_version += string.IsNullOrEmpty(buildVersion) ? " " : ("." + buildVersion + " ").Substring(0, 6);
871  }
872  }
873 
874  public string GetVersionText()
875  {
876  return String.Format("Version: {0} (SIMULATION/{1} - SIMULATION/{2})",
878  }
879 
883  protected string GetThreadsReport()
884  {
885  // This should be a constant field.
886  string reportFormat = "{0,6} {1,35} {2,16} {3,13} {4,10} {5,30}";
887 
888  StringBuilder sb = new StringBuilder();
889  Watchdog.ThreadWatchdogInfo[] threads = Watchdog.GetThreadsInfo();
890 
891  sb.Append(threads.Length + " threads are being tracked:" + Environment.NewLine);
892 
893  int timeNow = Environment.TickCount & Int32.MaxValue;
894 
895  sb.AppendFormat(reportFormat, "ID", "NAME", "LAST UPDATE (MS)", "LIFETIME (MS)", "PRIORITY", "STATE");
896  sb.Append(Environment.NewLine);
897 
898  foreach (Watchdog.ThreadWatchdogInfo twi in threads)
899  {
900  Thread t = twi.Thread;
901 
902  sb.AppendFormat(
903  reportFormat,
904  t.ManagedThreadId,
905  t.Name,
906  timeNow - twi.LastTick,
907  timeNow - twi.FirstTick,
908  t.Priority,
909  t.ThreadState);
910 
911  sb.Append("\n");
912  }
913 
914  sb.Append("\n");
915 
916  // For some reason mono 2.6.7 returns an empty threads set! Not going to confuse people by reporting
917  // zero active threads.
918  int totalThreads = Process.GetCurrentProcess().Threads.Count;
919  if (totalThreads > 0)
920  sb.AppendFormat("Total threads active: {0}\n\n", totalThreads);
921 
922  sb.Append("Main threadpool (excluding script engine pools)\n");
923  sb.Append(GetThreadPoolReport());
924 
925  return sb.ToString();
926  }
927 
932  public static string GetThreadPoolReport()
933  {
934  string threadPoolUsed = null;
935  int maxThreads = 0;
936  int minThreads = 0;
937  int allocatedThreads = 0;
938  int inUseThreads = 0;
939  int waitingCallbacks = 0;
940  int completionPortThreads = 0;
941 
942  StringBuilder sb = new StringBuilder();
943  if (Util.FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool)
944  {
945  STPInfo stpi = Util.GetSmartThreadPoolInfo();
946 
947  // ROBUST currently leaves this the FireAndForgetMethod but never actually initializes the threadpool.
948  if (stpi != null)
949  {
950  threadPoolUsed = "SmartThreadPool";
951  maxThreads = stpi.MaxThreads;
952  minThreads = stpi.MinThreads;
953  inUseThreads = stpi.InUseThreads;
954  allocatedThreads = stpi.ActiveThreads;
955  waitingCallbacks = stpi.WaitingCallbacks;
956  }
957  }
958  else if (
959  Util.FireAndForgetMethod == FireAndForgetMethod.QueueUserWorkItem
960  || Util.FireAndForgetMethod == FireAndForgetMethod.UnsafeQueueUserWorkItem)
961  {
962  threadPoolUsed = "BuiltInThreadPool";
963  ThreadPool.GetMaxThreads(out maxThreads, out completionPortThreads);
964  ThreadPool.GetMinThreads(out minThreads, out completionPortThreads);
965  int availableThreads;
966  ThreadPool.GetAvailableThreads(out availableThreads, out completionPortThreads);
967  inUseThreads = maxThreads - availableThreads;
968  allocatedThreads = -1;
969  waitingCallbacks = -1;
970  }
971 
972  if (threadPoolUsed != null)
973  {
974  sb.AppendFormat("Thread pool used : {0}\n", threadPoolUsed);
975  sb.AppendFormat("Max threads : {0}\n", maxThreads);
976  sb.AppendFormat("Min threads : {0}\n", minThreads);
977  sb.AppendFormat("Allocated threads : {0}\n", allocatedThreads < 0 ? "not applicable" : allocatedThreads.ToString());
978  sb.AppendFormat("In use threads : {0}\n", inUseThreads);
979  sb.AppendFormat("Work items waiting : {0}\n", waitingCallbacks < 0 ? "not available" : waitingCallbacks.ToString());
980  }
981  else
982  {
983  sb.AppendFormat("Thread pool not used\n");
984  }
985 
986  return sb.ToString();
987  }
988 
989  public virtual void HandleThreadsAbort(string module, string[] cmd)
990  {
991  if (cmd.Length != 3)
992  {
993  MainConsole.Instance.Output("Usage: threads abort <thread-id>");
994  return;
995  }
996 
997  int threadId;
998  if (!int.TryParse(cmd[2], out threadId))
999  {
1000  MainConsole.Instance.Output("ERROR: Thread id must be an integer");
1001  return;
1002  }
1003 
1004  if (Watchdog.AbortThread(threadId))
1005  MainConsole.Instance.OutputFormat("Aborted thread with id {0}", threadId);
1006  else
1007  MainConsole.Instance.OutputFormat("ERROR - Thread with id {0} not found in managed threads", threadId);
1008  }
1009 
1016  protected void Notice(string msg)
1017  {
1018  if (m_console != null)
1019  {
1020  m_console.Output(msg);
1021  }
1022  }
1023 
1031  protected void Notice(string format, params object[] components)
1032  {
1033  if (m_console != null)
1034  m_console.OutputFormat(format, components);
1035  }
1036 
1037  public virtual void Shutdown()
1038  {
1039  m_serverStatsCollector.Close();
1040  ShutdownSpecific();
1041  }
1042 
1046  protected virtual void ShutdownSpecific() {}
1047  }
1048 }
Used to generated a formatted table for the console.
void OutputFormat(string format, params object[] components)
ServerStatsCollector m_serverStatsCollector
Definition: ServerBase.cs:66
virtual void HandleScript(string module, string[] parms)
Definition: ServerBase.cs:714
static bool TryParseConsoleInt(ICommandConsole console, string rawConsoleInt, out int i)
Convert a console input to an int, automatically complaining if a console is given.
Definition: ConsoleUtil.cs:185
FireAndForgetMethod
The method used by Util.FireAndForget for asynchronously firing events
Definition: Util.cs:92
virtual void HandleThreadsAbort(string module, string[] cmd)
Definition: ServerBase.cs:989
virtual void ShutdownSpecific()
Should be overriden and referenced by descendents if they need to perform extra shutdown processing ...
Definition: ServerBase.cs:1046
Class for delivering SmartThreadPool statistical information
Definition: Util.cs:109
static bool TryParseConsoleBool(ICommandConsole console, string rawConsoleString, out bool b)
Convert a console input to a bool, automatically complaining if a console is given.
Definition: ConsoleUtil.cs:165
void RunCommandScript(string fileName)
Run an optional startup list of commands
Definition: ServerBase.cs:729
static readonly float SimulationServiceVersionSupportedMin
Definition: VersionInfo.cs:89
void RegisterCommonComponents(IConfigSource configSource)
Definition: ServerBase.cs:327
void EnhanceVersionInformation()
Enhance the version string with extra information if it's available.
Definition: ServerBase.cs:781
OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString key
Definition: ICM_Api.cs:31
string m_version
Server version information. Usually VersionInfo + information about git commit, operating system...
Definition: ServerBase.cs:71
void RegisterCommonAppenders(IConfig startupConfig)
Definition: ServerBase.cs:144
void RegisterCommonCommands()
Register common commands once m_console has been set if it is going to be set
Definition: ServerBase.cs:193
Writes log information out onto the console
static readonly float SimulationServiceVersionSupportedMax
Definition: VersionInfo.cs:90
virtual void HandleShow(string module, string[] cmd)
Definition: ServerBase.cs:543
static ICommandConsole Instance
Definition: MainConsole.cs:35
void Notice(string format, params object[] components)
Console output is only possible if a console has been established. That is something that cannot be d...
Definition: ServerBase.cs:1031
static string GetThreadPoolReport()
Get a thread pool report.
Definition: ServerBase.cs:932
static string Version
Definition: VersionInfo.cs:50
void LogEnvironmentInformation()
Log information about the circumstances in which we're running (OpenSimulator version number...
Definition: ServerBase.cs:128
string GetUptimeReport()
Return a report about the uptime of this server
Definition: ServerBase.cs:761
ICommandConsole m_console
Console to be used for any command line output. Can be null, in which case there should be no output...
Definition: ServerBase.cs:56
void Notice(string msg)
Console output is only possible if a console has been established. That is something that cannot be d...
Definition: ServerBase.cs:1016
string GetThreadsReport()
Get a report about the registered threads in this server.
Definition: ServerBase.cs:883