mirror of https://github.com/winsw/winsw
372 lines
11 KiB
C#
Executable File
372 lines
11 KiB
C#
Executable File
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Data;
|
|
using System.Diagnostics;
|
|
using System.Runtime.InteropServices;
|
|
using System.ServiceProcess;
|
|
using System.Text;
|
|
using System.IO;
|
|
using System.Net;
|
|
using WMI;
|
|
using System.Xml;
|
|
using System.Threading;
|
|
using Microsoft.Win32;
|
|
namespace winsw
|
|
{
|
|
/// <summary>
|
|
/// In-memory representation of the configuration file.
|
|
/// </summary>
|
|
public class ServiceDescriptor
|
|
{
|
|
private readonly XmlDocument dom = new XmlDocument();
|
|
|
|
/// <summary>
|
|
/// Where did we find the configuration file?
|
|
///
|
|
/// This string is "c:\abc\def\ghi" when the configuration XML is "c:\abc\def\ghi.xml"
|
|
/// </summary>
|
|
public readonly string BasePath;
|
|
/// <summary>
|
|
/// The file name portion of the configuration file.
|
|
///
|
|
/// In the above example, this would be "ghi".
|
|
/// </summary>
|
|
public readonly string BaseName;
|
|
|
|
public static string ExecutablePath
|
|
{
|
|
get
|
|
{
|
|
// this returns the executable name as given by the calling process, so
|
|
// it needs to be absolutized.
|
|
string p = Environment.GetCommandLineArgs()[0];
|
|
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, p);
|
|
|
|
}
|
|
}
|
|
|
|
public ServiceDescriptor()
|
|
{
|
|
// find co-located configuration xml. We search up to the ancestor directories to simplify debugging,
|
|
// as well as trimming off ".vshost" suffix (which is used during debugging)
|
|
string p = ExecutablePath;
|
|
string baseName = Path.GetFileNameWithoutExtension(p);
|
|
if (baseName.EndsWith(".vshost")) baseName = baseName.Substring(0, baseName.Length - 7);
|
|
while (true)
|
|
{
|
|
p = Path.GetDirectoryName(p);
|
|
if (File.Exists(Path.Combine(p, baseName + ".xml")))
|
|
break;
|
|
}
|
|
|
|
// register the base directory as environment variable so that future expansions can refer to this.
|
|
Environment.SetEnvironmentVariable("BASE", p);
|
|
|
|
BaseName = baseName;
|
|
BasePath = Path.Combine(p, BaseName);
|
|
|
|
dom.Load(BasePath + ".xml");
|
|
}
|
|
|
|
private string SingleElement(string tagName)
|
|
{
|
|
var n = dom.SelectSingleNode("//" + tagName);
|
|
if (n == null) throw new InvalidDataException("<" + tagName + "> is missing in configuration XML");
|
|
return Environment.ExpandEnvironmentVariables(n.InnerText);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Path to the executable.
|
|
/// </summary>
|
|
public string Executable
|
|
{
|
|
get
|
|
{
|
|
return SingleElement("executable");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Optionally specify a different Path to an executable to shutdown the service.
|
|
/// </summary>
|
|
public string StopExecutable
|
|
{
|
|
get
|
|
{
|
|
return AppendTags("stopexecutable");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Arguments or multiple optional argument elements which overrule the arguments element.
|
|
/// </summary>
|
|
public string Arguments
|
|
{
|
|
get
|
|
{
|
|
string arguments = AppendTags("argument");
|
|
|
|
if (arguments == null)
|
|
{
|
|
var tagName = "arguments";
|
|
var argumentsNode = dom.SelectSingleNode("//" + tagName);
|
|
|
|
if (argumentsNode == null)
|
|
{
|
|
if (AppendTags("startargument") == null)
|
|
{
|
|
throw new InvalidDataException("<" + tagName + "> is missing in configuration XML");
|
|
}
|
|
else
|
|
{
|
|
return "";
|
|
}
|
|
}
|
|
|
|
return Environment.ExpandEnvironmentVariables(argumentsNode.InnerText);
|
|
}
|
|
else
|
|
{
|
|
return arguments;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Multiple optional startargument elements.
|
|
/// </summary>
|
|
public string Startarguments
|
|
{
|
|
get
|
|
{
|
|
return AppendTags("startargument");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Multiple optional stopargument elements.
|
|
/// </summary>
|
|
public string Stoparguments
|
|
{
|
|
get
|
|
{
|
|
return AppendTags("stopargument");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Combines the contents of all the elements of the given name,
|
|
/// or return null if no element exists.
|
|
/// </summary>
|
|
private string AppendTags(string tagName)
|
|
{
|
|
XmlNode argumentNode = dom.SelectSingleNode("//" + tagName);
|
|
|
|
if (argumentNode == null)
|
|
{
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
string arguments = "";
|
|
|
|
foreach (XmlNode argument in dom.SelectNodes("//" + tagName))
|
|
{
|
|
arguments += " " + argument.InnerText;
|
|
}
|
|
|
|
return Environment.ExpandEnvironmentVariables(arguments);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// LogDirectory is the service wrapper executable directory or the optionally specified logpath element.
|
|
/// </summary>
|
|
public string LogDirectory
|
|
{
|
|
get
|
|
{
|
|
XmlNode loggingNode = dom.SelectSingleNode("//logpath");
|
|
|
|
if (loggingNode != null)
|
|
{
|
|
return loggingNode.InnerText;
|
|
}
|
|
else
|
|
{
|
|
return Path.GetDirectoryName(ExecutablePath);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Logmode to 'reset', 'rotate' once or 'append' [default] the out.log and err.log files.
|
|
/// </summary>
|
|
public string Logmode
|
|
{
|
|
get
|
|
{
|
|
XmlNode logmodeNode = dom.SelectSingleNode("//logmode");
|
|
|
|
if (logmodeNode == null)
|
|
{
|
|
return "append";
|
|
}
|
|
else
|
|
{
|
|
return logmodeNode.InnerText;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Optionally specified depend services that must start before this service starts.
|
|
/// </summary>
|
|
public string[] ServiceDependencies
|
|
{
|
|
get
|
|
{
|
|
System.Collections.ArrayList serviceDependencies = new System.Collections.ArrayList();
|
|
|
|
foreach (XmlNode depend in dom.SelectNodes("//depend"))
|
|
{
|
|
serviceDependencies.Add(depend.InnerText);
|
|
}
|
|
|
|
return (string[])serviceDependencies.ToArray(typeof(string));
|
|
}
|
|
}
|
|
|
|
public string Id
|
|
{
|
|
get
|
|
{
|
|
return SingleElement("id");
|
|
}
|
|
}
|
|
|
|
public string Caption
|
|
{
|
|
get
|
|
{
|
|
return SingleElement("name");
|
|
}
|
|
}
|
|
|
|
public string Description
|
|
{
|
|
get
|
|
{
|
|
return SingleElement("description");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// True if the service should when finished on shutdown.
|
|
/// </summary>
|
|
public bool BeepOnShutdown
|
|
{
|
|
get
|
|
{
|
|
return dom.SelectSingleNode("//beeponshutdown") != null;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// The estimated time required for a pending stop operation, in milliseconds (default 15 secs).
|
|
/// Before the specified amount of time has elapsed, the service should make its next call to the SetServiceStatus function
|
|
/// with either an incremented checkPoint value or a change in currentState. (see http://msdn.microsoft.com/en-us/library/ms685996.aspx)
|
|
/// </summary>
|
|
public int WaitHint
|
|
{
|
|
get
|
|
{
|
|
XmlNode waithintNode = dom.SelectSingleNode("//waithint");
|
|
|
|
if (waithintNode == null)
|
|
{
|
|
return 15000;
|
|
}
|
|
else
|
|
{
|
|
return int.Parse(waithintNode.InnerText);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// The time, in milliseconds (default 1 sec), before the service should make its next call to the SetServiceStatus function
|
|
/// with an incremented checkPoint value.
|
|
/// Do not wait longer than the wait hint. A good interval is one-tenth of the wait hint but not less than 1 second and not more than 10 seconds.
|
|
/// </summary>
|
|
public int SleepTime
|
|
{
|
|
get
|
|
{
|
|
XmlNode sleeptimeNode = dom.SelectSingleNode("//sleeptime");
|
|
|
|
if (sleeptimeNode == null)
|
|
{
|
|
return 1000;
|
|
}
|
|
else
|
|
{
|
|
return int.Parse(sleeptimeNode.InnerText);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// True if the service can interact with the desktop.
|
|
/// </summary>
|
|
public bool Interactive
|
|
{
|
|
get
|
|
{
|
|
return dom.SelectSingleNode("//interactive") != null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Environment variable overrides
|
|
/// </summary>
|
|
public Dictionary<string, string> EnvironmentVariables
|
|
{
|
|
get
|
|
{
|
|
Dictionary<string, string> map = new Dictionary<string, string>();
|
|
foreach (XmlNode n in dom.SelectNodes("//env"))
|
|
{
|
|
string key = n.Attributes["name"].Value;
|
|
string value = Environment.ExpandEnvironmentVariables(n.Attributes["value"].Value);
|
|
map[key] = value;
|
|
|
|
Environment.SetEnvironmentVariable(key, value);
|
|
}
|
|
return map;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// List of downloads to be performed by the wrapper before starting
|
|
/// a service.
|
|
/// </summary>
|
|
public List<Download> Downloads
|
|
{
|
|
get
|
|
{
|
|
List<Download> r = new List<Download>();
|
|
foreach (XmlNode n in dom.SelectNodes("//download"))
|
|
{
|
|
r.Add(new Download(n));
|
|
}
|
|
return r;
|
|
}
|
|
}
|
|
}
|
|
}
|