Introduction
In a domain with many servers, automatic updates using the Windows update agent have become standard. By these means, administrators can deploy new updates with a minimum effort to all servers, and the servers can handle the update process automatically by themselves, including a possible restart after the patch. Such automatic restarts can be unwanted, for instance, when running a long-term calculation on the given machine. A possible solution would be to configure the machine in such a way that it does not restart automatically, but that would leave your machine vulnerable until you log in and get the message that the machine has to be rebooted.
We propose as an alternative solution the creation of an instant message, which informs you that the server needs a reboot, enabling you to react quickly by logging on to the machine and deciding whether the restart is appropriate now or later.
Architecture
The goal of the service is the automatic generation of an email whenever the automatic update needs to reboot the server. To achieve this, it is necessary to notice that the Windows update agent has installed a patch and wants the machine to reboot. The agent writes its activities to the Windows event log, thus we can monitor the event log for new entries from the Windows update agent. The constraints on the implementation are the following: firstly, it has to be a Windows service to run without a user being logged on; secondly, it needs to monitor the event log to recognize events from the update agent; and thirdly, it has to send an email to a given address.
Using the Code
Windows Service
Visual Studio 2005 allows fast implementation of Windows Services. Create a new project and use the 'Windows Service' Template from the Visual C# / Windows category. Visual Studio will create a project body that runs a Windows Service: we just need to implement the functionality. In addition to the service functionality, we will add a Windows form for debugging purposes, since services can't be started from the studio. In the startup class, 'Program.cs', we add a differentiation between the debug and release mode to call the service automatically or from the Windows form depending on the running context.
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[] { new ServiceBody() };
#if DEBUG
System.Windows.Forms.Application.Run(new TestForm());
#else
ServiceBase.Run(ServicesToRun);
#endif
The functionality of the service class (called 'ServiceBody
') consists of starting the monitoring of the Windows event log. This is implemented by the function 'OnStart
' which is created by Visual Studio. In order to allow a certain degree of configurability, we first read some configuration info before starting the eventlog-watcher. In case of a reconfiguration of the running service, the service needs to be restarted. In order to apply changes without restart, the configuration needs to be read whenever an event is written to the event log. The next step consists of initializing the eventlog-watcher using the configuration values and starting it.
protected override void OnStart(string[] args)
{
Properties.Settings _mySettings =
new RestartReminderService.Properties.Settings();
_myLogger = new EventLogger(_mySettings.LogNameToWatch,
_mySettings.SourceNameToWatch,
_mySettings.MailServerName,
_mySettings.MailSender,
_mySettings.MailReceiver,
_mySettings.MailTitle);
_myLogger.StartLogging();
}
Eventlog Watcher
To recognize new entries in the event log, we first need to subscribe to the event that is fired when a new entry is created. The name of this event is EntryWrittenEventHandler
, and it can be found in the System.Diagnostic
namespace. It demands one parameter, the name of the function to be called when the event fires. I have named this function _myLog_EntryWritten
, yet any other name would do. Make sure that the event log receives event notification, by setting EnableRaisingEvents
to true
.
public void StartLogging()
{
EventLog _myLog = new EventLog(_logName);
_myLog.EntryWritten += new EntryWrittenEventHandler(_myLog_EntryWritten);
_myLog.EnableRaisingEvents = true;
}
The method _myLog_EntryWritten
needs to filter all the events fired from new entries to the event log, since the service should react exclusively to entries from the Windows update agent. The property Entry.Source
from the Eventargumentvariable
, which is passed on to _myLog_EntryWritten
, contains the source of the entry, enabling us to infer whether the entry is from the update agent or not. If it is, the mailer class is called, and receives the event message and the name of the machine, for the purpose of recognizing the updated machine when we get the mail.
void _myLog_EntryWritten(object sender, EntryWrittenEventArgs e)
{
if (e.Entry.Source.Equals(_logSource))
{
MailSender _myMail = new MailSender();
_myMail.SendMail(_smtpServer,
_from,
_to,
_title,
e.Entry.MachineName + ": " + e.Entry.Message);
}
}
The used parameters are written to state variables during the instantiation of our eventlog-watcher class.
Mail Sender
To send an email automatically, the System.Net.Mail
namespace must be included. It contains a class 'SmtpClient
' that can be used to send the email. To keep things simple, we do not authenticate ourselves to the server. If you don't have access to an SMTP-server without authentication, you will need to add the authentication to the code. There are just two lines to send the mail, and I have added a simple error logging function, in case something is mis-configured.
try
{
SmtpClient _mySmtpClient = new SmtpClient(smptServer);
_mySmtpClient.Send(from, to, title, message);
}
catch (Exception e)
{
logging.logger.ErrorRoutine(e,
"smtpServer: " + smptServer +
", from: " + from +
", to: " + to +
", title: " + title +
", message: " + message);
}
Installation
The installation of a service needs a class which is derived from the class 'Installer
'. I use a class from John Storer, modified to fit to the project.
[RunInstaller(true)]
public class WindowsServiceInstaller : Installer
{
public WindowsServiceInstaller()
{
ServiceProcessInstaller serviceProcessInstaller = new ServiceProcessInstaller();
ServiceInstaller serviceInstaller = new ServiceInstaller();
serviceProcessInstaller.Account = ServiceAccount.LocalSystem;
serviceProcessInstaller.Username = null;
serviceProcessInstaller.Password = null;
serviceInstaller.DisplayName = "RestartReminder";
serviceInstaller.StartType = ServiceStartMode.Automatic;
serviceInstaller.ServiceName = "RestartReminderService";
this.Installers.Add(serviceProcessInstaller);
this.Installers.Add(serviceInstaller);
}
}
Put the compiled project with a batch file for installation (and de-installation) to the server to monitor. Configure the service via editing the RestartReminderService.exe.config. You can set the mail server, your mail address, and so on. Finally, run the following batch installation file (for uninstalling, use the switch /u):
@ECHO OFF
REM The following directory is for .NET 2.0
set DOTNETFX2=%SystemRoot%\Microsoft.NET\Framework\v2.0.50727
set PATH=%PATH%;%DOTNETFX2%
echo Installing WindowsService...
echo ---------------------------------------------------
InstallUtil /i RestartReminderService.exe
echo ---------------------------------------------------
echo Done.
The process is now configured to run automatically after system startup. But it doesn't start up automatically after the installation, and therefore it needs to be started manually once.
Conclusion
The service introduced above is a very simple, straightforward one, which incorporates three aspects: Windows Service programming, eventlog-monitoring, and mailing. These aspects are used to inform the user that a server machine needs to be rebooted, because of patch-installation by the Windows update agent. Via configuration, this service is able to monitor and inform the user about any event source that writes entries in the event log.
The author would like to thank the following people for their great articles here on CodeProject that form the basis for his solution:
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.