aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Servers/ServerBase.cs
diff options
context:
space:
mode:
authorUbitUmarov2015-09-01 11:43:07 +0100
committerUbitUmarov2015-09-01 11:43:07 +0100
commitfb78b182520fc9bb0f971afd0322029c70278ea6 (patch)
treeb4e30d383938fdeef8c92d1d1c2f44bb61d329bd /OpenSim/Framework/Servers/ServerBase.cs
parentlixo (diff)
parentMantis #7713: fixed bug introduced by 1st MOSES patch. (diff)
downloadopensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.zip
opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.gz
opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.bz2
opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.xz
Merge remote-tracking branch 'os/master'
Diffstat (limited to 'OpenSim/Framework/Servers/ServerBase.cs')
-rw-r--r--OpenSim/Framework/Servers/ServerBase.cs1047
1 files changed, 1047 insertions, 0 deletions
diff --git a/OpenSim/Framework/Servers/ServerBase.cs b/OpenSim/Framework/Servers/ServerBase.cs
new file mode 100644
index 0000000..e403ba0
--- /dev/null
+++ b/OpenSim/Framework/Servers/ServerBase.cs
@@ -0,0 +1,1047 @@
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
28using System;
29using System.Collections.Generic;
30using System.Diagnostics;
31using System.IO;
32using System.Linq;
33using System.Reflection;
34using System.Text;
35using System.Text.RegularExpressions;
36using System.Threading;
37using log4net;
38using log4net.Appender;
39using log4net.Core;
40using log4net.Repository;
41using Nini.Config;
42using OpenSim.Framework.Console;
43using OpenSim.Framework.Monitoring;
44
45namespace 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
53 /// <summary>
54 /// Console to be used for any command line output. Can be null, in which case there should be no output.
55 /// </summary>
56 protected ICommandConsole m_console;
57
58 protected OpenSimAppender m_consoleAppender;
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
66 protected ServerStatsCollector m_serverStatsCollector;
67
68 /// <summary>
69 /// Server version information. Usually VersionInfo + information about git commit, operating system, etc.
70 /// </summary>
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
124 /// <summary>
125 /// Log information about the circumstances in which we're running (OpenSimulator version number, CLR details,
126 /// etc.).
127 /// </summary>
128 public void LogEnvironmentInformation()
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
190 /// <summary>
191 /// Register common commands once m_console has been set if it is going to be set
192 /// </summary>
193 public void RegisterCommonCommands()
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
376 ConsoleDisplayList cdl = new ConsoleDisplayList();
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
405 ConsoleDisplayList cdl = new ConsoleDisplayList();
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
571 /// <summary>
572 /// Change and load configuration file data.
573 /// </summary>
574 /// <param name="module"></param>
575 /// <param name="cmd"></param>
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
725 /// <summary>
726 /// Run an optional startup list of commands
727 /// </summary>
728 /// <param name="fileName"></param>
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
757 /// <summary>
758 /// Return a report about the uptime of this server
759 /// </summary>
760 /// <returns></returns>
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
778 /// <summary>
779 /// Enhance the version string with extra information if it's available.
780 /// </summary>
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 protected string GetVersionText()
875 {
876 return String.Format("Version: {0} (interface version {1})", m_version, VersionInfo.MajorInterfaceVersion);
877 }
878
879 /// <summary>
880 /// Get a report about the registered threads in this server.
881 /// </summary>
882 protected string GetThreadsReport()
883 {
884 // This should be a constant field.
885 string reportFormat = "{0,6} {1,35} {2,16} {3,13} {4,10} {5,30}";
886
887 StringBuilder sb = new StringBuilder();
888 Watchdog.ThreadWatchdogInfo[] threads = Watchdog.GetThreadsInfo();
889
890 sb.Append(threads.Length + " threads are being tracked:" + Environment.NewLine);
891
892 int timeNow = Environment.TickCount & Int32.MaxValue;
893
894 sb.AppendFormat(reportFormat, "ID", "NAME", "LAST UPDATE (MS)", "LIFETIME (MS)", "PRIORITY", "STATE");
895 sb.Append(Environment.NewLine);
896
897 foreach (Watchdog.ThreadWatchdogInfo twi in threads)
898 {
899 Thread t = twi.Thread;
900
901 sb.AppendFormat(
902 reportFormat,
903 t.ManagedThreadId,
904 t.Name,
905 timeNow - twi.LastTick,
906 timeNow - twi.FirstTick,
907 t.Priority,
908 t.ThreadState);
909
910 sb.Append("\n");
911 }
912
913 sb.Append("\n");
914
915 // For some reason mono 2.6.7 returns an empty threads set! Not going to confuse people by reporting
916 // zero active threads.
917 int totalThreads = Process.GetCurrentProcess().Threads.Count;
918 if (totalThreads > 0)
919 sb.AppendFormat("Total threads active: {0}\n\n", totalThreads);
920
921 sb.Append("Main threadpool (excluding script engine pools)\n");
922 sb.Append(GetThreadPoolReport());
923
924 return sb.ToString();
925 }
926
927 /// <summary>
928 /// Get a thread pool report.
929 /// </summary>
930 /// <returns></returns>
931 public static string GetThreadPoolReport()
932 {
933 string threadPoolUsed = null;
934 int maxThreads = 0;
935 int minThreads = 0;
936 int allocatedThreads = 0;
937 int inUseThreads = 0;
938 int waitingCallbacks = 0;
939 int completionPortThreads = 0;
940
941 StringBuilder sb = new StringBuilder();
942 if (Util.FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool)
943 {
944 STPInfo stpi = Util.GetSmartThreadPoolInfo();
945
946 // ROBUST currently leaves this the FireAndForgetMethod but never actually initializes the threadpool.
947 if (stpi != null)
948 {
949 threadPoolUsed = "SmartThreadPool";
950 maxThreads = stpi.MaxThreads;
951 minThreads = stpi.MinThreads;
952 inUseThreads = stpi.InUseThreads;
953 allocatedThreads = stpi.ActiveThreads;
954 waitingCallbacks = stpi.WaitingCallbacks;
955 }
956 }
957 else if (
958 Util.FireAndForgetMethod == FireAndForgetMethod.QueueUserWorkItem
959 || Util.FireAndForgetMethod == FireAndForgetMethod.UnsafeQueueUserWorkItem)
960 {
961 threadPoolUsed = "BuiltInThreadPool";
962 ThreadPool.GetMaxThreads(out maxThreads, out completionPortThreads);
963 ThreadPool.GetMinThreads(out minThreads, out completionPortThreads);
964 int availableThreads;
965 ThreadPool.GetAvailableThreads(out availableThreads, out completionPortThreads);
966 inUseThreads = maxThreads - availableThreads;
967 allocatedThreads = -1;
968 waitingCallbacks = -1;
969 }
970
971 if (threadPoolUsed != null)
972 {
973 sb.AppendFormat("Thread pool used : {0}\n", threadPoolUsed);
974 sb.AppendFormat("Max threads : {0}\n", maxThreads);
975 sb.AppendFormat("Min threads : {0}\n", minThreads);
976 sb.AppendFormat("Allocated threads : {0}\n", allocatedThreads < 0 ? "not applicable" : allocatedThreads.ToString());
977 sb.AppendFormat("In use threads : {0}\n", inUseThreads);
978 sb.AppendFormat("Work items waiting : {0}\n", waitingCallbacks < 0 ? "not available" : waitingCallbacks.ToString());
979 }
980 else
981 {
982 sb.AppendFormat("Thread pool not used\n");
983 }
984
985 return sb.ToString();
986 }
987
988 public virtual void HandleThreadsAbort(string module, string[] cmd)
989 {
990 if (cmd.Length != 3)
991 {
992 MainConsole.Instance.Output("Usage: threads abort <thread-id>");
993 return;
994 }
995
996 int threadId;
997 if (!int.TryParse(cmd[2], out threadId))
998 {
999 MainConsole.Instance.Output("ERROR: Thread id must be an integer");
1000 return;
1001 }
1002
1003 if (Watchdog.AbortThread(threadId))
1004 MainConsole.Instance.OutputFormat("Aborted thread with id {0}", threadId);
1005 else
1006 MainConsole.Instance.OutputFormat("ERROR - Thread with id {0} not found in managed threads", threadId);
1007 }
1008
1009 /// <summary>
1010 /// Console output is only possible if a console has been established.
1011 /// That is something that cannot be determined within this class. So
1012 /// all attempts to use the console MUST be verified.
1013 /// </summary>
1014 /// <param name="msg"></param>
1015 protected void Notice(string msg)
1016 {
1017 if (m_console != null)
1018 {
1019 m_console.Output(msg);
1020 }
1021 }
1022
1023 /// <summary>
1024 /// Console output is only possible if a console has been established.
1025 /// That is something that cannot be determined within this class. So
1026 /// all attempts to use the console MUST be verified.
1027 /// </summary>
1028 /// <param name="format"></param>
1029 /// <param name="components"></param>
1030 protected void Notice(string format, params object[] components)
1031 {
1032 if (m_console != null)
1033 m_console.OutputFormat(format, components);
1034 }
1035
1036 public virtual void Shutdown()
1037 {
1038 m_serverStatsCollector.Close();
1039 ShutdownSpecific();
1040 }
1041
1042 /// <summary>
1043 /// Should be overriden and referenced by descendents if they need to perform extra shutdown processing
1044 /// </summary>
1045 protected virtual void ShutdownSpecific() {}
1046 }
1047} \ No newline at end of file