|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Introduction
BackgroundFor logging on Windows we have Using the demo projectThe demo project contains the Syslogd.exe service. If you want to use it, you have to install the service by using the .NET tool installutil.exe which is located in the framework directory (watch version numbering). In my src project I use two post-build steps, one for uninstall, and one for installing. c:\windows\microsoft.net\framework\v2.0.50727\installutil.exe
/u "$(TargetPath)"
c:\windows\microsoft.net\framework\v2.0.50727\installutil.exe
"$(TargetPath)"
Note: The demo project contains a simple install.cmd that does install your service. Because services are not nice to debug(!), the project contains a simple Form which can When using installutil.exe for registering .NET services, the tool looks for any [RunInstaller(true)]
public class SyslogdInstaller : Installer
{
.......
}
SyslogdInstaller.csThis installer code is located in SyslogdInstaller.cs which does two things. It installs the .......
this.serviceProcessInstaller1.Account = ServiceAccount.LocalSystem;
this.serviceProcessInstaller1.Password = null;
this.serviceProcessInstaller1.Username = null;
.......
this.serviceInstaller1.ServiceName = settings.ServiceName;
this.serviceInstaller1.Description = settings.Description;
this.serviceInstaller1.DisplayName = settings.DisplayName;
this.serviceInstaller1.StartType = ServiceStartMode.Automatic;
Properties.SettingsMost of the properties in this project are configurable by its In Visual Studio you can use the nice properties editor:
When saving these settings, an XML file is saved (Syslogd.exe.config) which can be changed according to one's own wishes afterwards. <?xml version="1.0" encoding="utf-8" ?>
<configuration>
.....
<userSettings>
<Syslogd.Properties.Settings>
<setting name="Address" serializeAs="String">
<value>*</value>
</setting>
<setting name="Port" serializeAs="String">
<value>514</value>
</setting>
<setting name="ServiceName" serializeAs="String">
<value>Syslogd</value>
</setting>
<setting name="Description" serializeAs="String">
<value>Syslogd service logs syslog messages to
windows eventlog</value>
</setting>
<setting name="DisplayName" serializeAs="String">
<value>Syslogd service</value>
</setting>
<setting name="EventLog" serializeAs="String">
<value>Syslog</value>
</setting>
</Syslogd.Properties.Settings>
</userSettings>
</configuration>
SyslogdService.csThe ...
private Syslogd syslogd;
...
public SyslogdService()
{
...
syslogd = new Syslogd();
}
protected override void OnStart(string[] args)
{
syslogd.Start();
}
...
protected override void OnStop()
{
syslogd.Stop();
}
...
Syslogd.csWhen private void Worker()
{
Properties.Settings settings = new Properties.Settings();
m_EventLog = settings.EventLog;
IPAddress ipAddress = IPAddress.Any;
if(settings.Address!="*")
ipAddress = IPAddress.Parse(settings.Address);
IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, settings.Port);
m_socket = new Socket(
AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
m_socket.Bind(ipEndPoint);
// Recycling vars , i love it.
EndPoint endPoint = ipEndPoint;
// http://www.ietf.org/rfc/rfc3164.txt
// 4.1 syslog Message Parts
// The total length of the packet MUST be 1024 bytes or less.
byte[] buffer = new byte[1024];
while (m_running)
{
try
{
int intReceived = m_socket.ReceiveFrom(buffer, 0,
buffer.Length, SocketFlags.None, ref endPoint);
string strReceived = Encoding.ASCII.GetString(buffer,
0, intReceived);
Log(endPoint, strReceived);
}
catch (Exception exception)
{
EventLog myLog = new EventLog();
myLog.Source = settings.ServiceName;
if (!m_running)
myLog.WriteEntry("Stopping...",
EventLogEntryType.Information);
else
myLog.WriteEntry(exception.Message,
EventLogEntryType.Error);
myLog.Close();
myLog.Dispose();
}
}
}
Decoding Syslog PRIEvery ...
/*
* The PRI part MUST have three, four, or five characters and will be
* bound with angle brackets as the first and last characters. The PRI
* part starts with a leading "<" ('less-than' character), followed by a
* number, which is followed by a ">" ('greater-than' character).
*/
m_regex = new Regex("<([0-9]{1,3})>", RegexOptions.Compiled);
...
After decoding the decimal part of the /// <summary>
/// Facility according to http://www.ietf.org/rfc/rfc3164.txt
/// 4.1.1 PRI Part
/// </summary>
private enum FacilityEnum : int
{
kernel = 0, // kernel messages
user = 1, // user-level messages
mail = 2, // mail system
system = 3, // system daemons
security = 4, // security/authorization messages (note 1)
internally = 5, // messages generated internally by syslogd
printer = 6, // line printer subsystem
news = 7, // network news subsystem
uucp = 8, // UUCP subsystem
cron = 9, // clock daemon (note 2) changed to cron
security2 = 10, // security/authorization messages (note 1)
ftp = 11, // FTP daemon
ntp = 12, // NTP subsystem
audit = 13, // log audit (note 1)
alert = 14, // log alert (note 1)
clock2 = 15, // clock daemon (note 2)
local0 = 16, // local use 0 (local0)
local1 = 17, // local use 1 (local1)
local2 = 18, // local use 2 (local2)
local3 = 19, // local use 3 (local3)
local4 = 20, // local use 4 (local4)
local5 = 21, // local use 5 (local5)
local6 = 22, // local use 6 (local6)
local7 = 23, // local use 7 (local7)
}
/// <summary>
/// Severity according to http://www.ietf.org/rfc/rfc3164.txt
/// 4.1.1 PRI Part
/// </summary>
private enum SeverityEnum : int
{
emergency = 0, // Emergency: system is unusable
alert = 1, // Alert: action must be taken immediately
critical = 2, // Critical: critical conditions
error = 3, // Error: error conditions
warning = 4, // Warning: warning conditions
notice = 5, // Notice: normal but significant condition
info = 6, // Informational: informational messages
debug = 7, // Debug: debug-level messages
}
private struct Pri
{
public FacilityEnum Facility;
public SeverityEnum Severity;
public Pri(string strPri)
{
int intPri = Convert.ToInt32(strPri);
int intFacility = intPri >> 3;
int intSeverity = intPri & 0x7;
this.Facility = (FacilityEnum)Enum.Parse(typeof(FacilityEnum),
intFacility.ToString());
this.Severity = (SeverityEnum)Enum.Parse(typeof(SeverityEnum),
intSeverity.ToString());
}
public override string ToString()
{
return string.Format("{0}.{1}", this.Facility, this.Severity);
}
}
The first ...
Pri pri = new Pri(m_regex.Match(strReceived).Groups[1].Value);
...
Because /// <summary>
/// Evaluator is being used to translate every decimal Pri header in
/// a Syslog message to an 'Facility.Severity ' string.
/// </summary>
/// <param name="match">Any Pri header match in a message</param>
/// <returns />Translated Pri header to 'Facility.Severity '</returns>
private string evaluator(Match match)
{
Pri pri = new Pri(match.Groups[1].Value);
return pri.ToString()+" ";
}
private void Log(EndPoint endPoint, string strReceived)
{
...
string strMessage = string.Format("{0} : {1}",
endPoint, m_regex.Replace(strReceived, evaluator));
...
}
Translating Syslog Severity to EventLogEntryTypeWindows private EventLogEntryType Severity2EventLogEntryType(SeverityEnum Severity)
{
EventLogEntryType eventLogEntryType;
switch (Severity)
{
case SeverityEnum.emergency:
eventLogEntryType = EventLogEntryType.Error;
break;
case SeverityEnum.alert:
eventLogEntryType = EventLogEntryType.Error;
break;
case SeverityEnum.critical:
eventLogEntryType = EventLogEntryType.Error;
break;
case SeverityEnum.error:
eventLogEntryType = EventLogEntryType.Error;
break;
case SeverityEnum.warning:
eventLogEntryType = EventLogEntryType.Warning;
break;
case SeverityEnum.notice:
eventLogEntryType = EventLogEntryType.Information;
break;
case SeverityEnum.info:
eventLogEntryType = EventLogEntryType.Information;
break;
case SeverityEnum.debug:
eventLogEntryType = EventLogEntryType.Information;
break;
default: // ?
eventLogEntryType = EventLogEntryType.Error;
break;
}
return eventLogEntryType;
}
The logging in EventlogBinding this all together, logging IP-address, Port-number, private void Log(EndPoint endPoint, string strReceived)
{
Pri pri = new Pri(m_regex.Match(strReceived).Groups[1].Value);
EventLogEntryType eventLogEntryType =
Severity2EventLogEntryType(pri.Severity);
string strMessage = string.Format("{0} : {1}",
endPoint, m_regex.Replace(strReceived, evaluator));
EventLog myLog = new EventLog(m_EventLog);
myLog.Source = pri.ToString();
myLog.WriteEntry(strMessage, eventLogEntryType);
myLog.Close();
myLog.Dispose();
}
Points of InterestWhen building services which use the network on a Windows XP machine, you have to deal with the built-in Firewall. For opening up the internal firewall for the service, this piece of code is needed: private void FireWall()
{
...
Type NetFwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr", false);
INetFwMgr mgr = (INetFwMgr)Activator.CreateInstance(NetFwMgrType);
Type NetFwAuthorizedApplicationType =
Type.GetTypeFromProgID("HNetCfg.FwAuthorizedApplication", false);
INetFwAuthorizedApplication app =
(INetFwAuthorizedApplication)
Activator.CreateInstance(NetFwAuthorizedApplicationType);
app.Name = settings.ServiceName;
app.Enabled = true;
app.ProcessImageFileName = Assembly.GetExecutingAssembly().Location;
app.Scope = NET_FW_SCOPE_.NET_FW_SCOPE_ALL;
mgr.LocalPolicy.CurrentProfile.AuthorizedApplications.Add(app);
...
}
Don't forget that you have to make a reference to c:\windows\system32\hnetcfg.dll - it makes an Interop.NetFwTypeLib.dll Syslogd In actionI'm running a Dutch version of Windows XP, nice for all other programmers to see Services and
A sample
History
| ||||||||||||||||||||