Skip to content
Snippets Groups Projects
Commit f5419fe4 authored by Niko Ehrenfeuchter's avatar Niko Ehrenfeuchter :keyboard:
Browse files

Use ATXSerializables library for service code.

This removes the XmlWrapper namespace and uses the DLL instead.

Fixes #21
parent abb3754b
No related branches found
No related tags found
No related merge requests found
......@@ -105,9 +105,6 @@
<Compile Include="SystemChecks.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="XmlWrapper\DriveToCheck.cs" />
<Compile Include="XmlWrapper\ServiceConfig.cs" />
<Compile Include="XmlWrapper\ServiceStatus.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
......@@ -150,6 +147,12 @@
<ItemGroup>
<None Include="Resources\BuildCommit.txt" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ATXSerializables\ATXSerializables.csproj">
<Project>{166D65D5-EE10-4364-8AA3-4D86BA5CE244}</Project>
<Name>ATXSerializables</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PreBuildEvent>PowerShell -NoProfile -ExecutionPolicy RemoteSigned $(SolutionDir)\Scripts\Prepare-Build.ps1 $(ProjectDir)</PreBuildEvent>
......
......@@ -9,7 +9,7 @@ using System.Timers;
using System.DirectoryServices.AccountManagement;
using System.Globalization;
using System.Management;
using AutoTx.XmlWrapper;
using ATXSerializables;
using RoboSharp;
namespace AutoTx
......
using System.Xml.Serialization;
namespace AutoTx.XmlWrapper
{
/// <summary>
/// Helper class for the nested SpaceMonitoring sections.
/// </summary>
public class DriveToCheck
{
[XmlElement("DriveName")]
public string DriveName { get; set; }
// the value is to be compared to System.IO.DriveInfo.TotalFreeSpace
// hence we use the same type (long) to avoid unnecessary casts later:
[XmlElement("SpaceThreshold")]
public long SpaceThreshold { get; set; }
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Xml.Serialization;
namespace AutoTx.XmlWrapper
{
/// <summary>
/// configuration class based on xml
/// </summary>
[Serializable]
public class ServiceConfig
{
[XmlIgnore] public string ValidationWarnings;
public ServiceConfig() {
ValidationWarnings = "";
// set values for the optional XML elements:
SmtpHost = "";
SmtpPort = 25;
SmtpUserCredential = "";
SmtpPasswortCredential = "";
EmailPrefix = "";
AdminEmailAdress = "";
AdminDebugEmailAdress = "";
GraceNotificationDelta = 720;
InterPacketGap = 0;
EnforceInheritedACLs = true;
}
#region required configuration parameters
/// <summary>
/// A human friendly name for the host, to be used in emails etc.
/// </summary>
public string HostAlias { get; set; }
/// <summary>
/// A human friendly name for the target, to be used in emails etc.
/// </summary>
public string DestinationAlias { get; set; }
/// <summary>
/// The base drive for the spooling directories (incoming and managed).
/// </summary>
public string SourceDrive { get; set; }
/// <summary>
/// The name of a directory on SourceDrive that is monitored for new files.
/// </summary>
public string IncomingDirectory { get; set; }
/// <summary>
/// The name of a marker file to be placed in all **sub**directories
/// inside the IncomingDirectory.
/// </summary>
public string MarkerFile { get; set; }
/// <summary>
/// A directory on SourceDrive to hold the three subdirectories "DONE",
/// "PROCESSING" and "UNMATCHED" used during and after transfers.
/// </summary>
public string ManagedDirectory { get; set; }
/// <summary>
/// Target path to transfer files to. Usually a UNC location.
/// </summary>
public string DestinationDirectory { get; set; }
/// <summary>
/// The name of a subdirectory in the DestinationDirectory to be used
/// to keep the temporary data of running transfers.
/// </summary>
public string TmpTransferDir { get; set; }
public string EmailFrom { get; set; }
public int ServiceTimer { get; set; }
public int MaxCpuUsage { get; set; }
public int MinAvailableMemory { get; set; }
public int AdminNotificationDelta { get; set; }
public int StorageNotificationDelta { get; set; }
/// <summary>
/// GracePeriod: number of days after data in the "DONE" location expires,
/// which will trigger a summary email to the admin address.
/// </summary>
public int GracePeriod { get; set; }
public bool SendAdminNotification { get; set; }
public bool SendTransferNotification { get; set; }
public bool Debug { get; set; }
[XmlArray]
[XmlArrayItem(ElementName = "DriveToCheck")]
public List<DriveToCheck> SpaceMonitoring { get; set; }
[XmlArray]
[XmlArrayItem(ElementName = "ProcessName")]
public List<string> BlacklistedProcesses { get; set; }
#endregion
#region optional configuration parameters
public string SmtpHost { get; set; }
public string SmtpUserCredential { get; set; }
public string SmtpPasswortCredential { get; set; }
public int SmtpPort { get; set; }
public string EmailPrefix { get; set; }
public string AdminEmailAdress { get; set; }
public string AdminDebugEmailAdress { get; set; }
public int GraceNotificationDelta { get; set; }
public int InterPacketGap { get; set; }
/// <summary>
/// EnforceInheritedACLs: whether to enforce ACL inheritance when moving files and
/// directories, see https://support.microsoft.com/en-us/help/320246 for more details.
/// </summary>
public bool EnforceInheritedACLs { get; set; }
#endregion
public static void Serialize(string file, ServiceConfig c) {
// the config is never meant to be written by us, therefore:
throw new SettingsPropertyIsReadOnlyException("The config file must not be written by the service!");
}
public static ServiceConfig Deserialize(string file) {
var xs = new XmlSerializer(typeof(ServiceConfig));
var reader = File.OpenText(file);
var config = (ServiceConfig) xs.Deserialize(reader);
reader.Close();
ValidateConfiguration(config);
return config;
}
private static void ValidateConfiguration(ServiceConfig c) {
if (string.IsNullOrEmpty(c.SourceDrive) ||
string.IsNullOrEmpty(c.IncomingDirectory) ||
string.IsNullOrEmpty(c.ManagedDirectory))
throw new ConfigurationErrorsException("mandatory parameter missing!");
if (c.SourceDrive.Substring(1) != @":\")
throw new ConfigurationErrorsException("SourceDrive must be a drive " +
@"letter followed by a colon and a backslash, e.g. 'D:\'!");
// make sure SourceDrive is a local (fixed) disk:
var driveInfo = new DriveInfo(c.SourceDrive);
if (driveInfo.DriveType != DriveType.Fixed)
throw new ConfigurationErrorsException("SourceDrive (" + c.SourceDrive +
") must be a local (fixed) drive, OS reports '" + driveInfo.DriveType + "')!");
// spooling directories: IncomingDirectory + ManagedDirectory
if (c.IncomingDirectory.StartsWith(@"\"))
throw new ConfigurationErrorsException("IncomingDirectory must not start with a backslash!");
if (c.ManagedDirectory.StartsWith(@"\"))
throw new ConfigurationErrorsException("ManagedDirectory must not start with a backslash!");
if (!Directory.Exists(c.DestinationDirectory))
throw new ConfigurationErrorsException("can't find destination: " + c.DestinationDirectory);
var tmpTransferPath = Path.Combine(c.DestinationDirectory, c.TmpTransferDir);
if (!Directory.Exists(tmpTransferPath))
throw new ConfigurationErrorsException("temporary transfer dir doesn't exist: " + tmpTransferPath);
if (c.ServiceTimer < 1000)
throw new ConfigurationErrorsException("ServiceTimer must not be smaller than 1000 ms!");
// NON-CRITICAL stuff just adds messages to ValidationWarnings:
// DestinationDirectory
if (!c.DestinationDirectory.StartsWith(@"\\"))
c.ValidationWarnings += " - <DestinationDirectory> is not a UNC path!\n";
}
public string Summary() {
var msg =
"HostAlias: " + HostAlias + "\n" +
"SourceDrive: " + SourceDrive + "\n" +
"IncomingDirectory: " + IncomingDirectory + "\n" +
"MarkerFile: " + MarkerFile + "\n" +
"ManagedDirectory: " + ManagedDirectory + "\n" +
"GracePeriod: " + GracePeriod + "\n" +
"DestinationDirectory: " + DestinationDirectory + "\n" +
"TmpTransferDir: " + TmpTransferDir + "\n" +
"EnforceInheritedACLs: " + EnforceInheritedACLs + "\n" +
"ServiceTimer: " + ServiceTimer + "\n" +
"InterPacketGap: " + InterPacketGap + "\n" +
"MaxCpuUsage: " + MaxCpuUsage + "\n" +
"MinAvailableMemory: " + MinAvailableMemory + "\n";
foreach (var processName in BlacklistedProcesses) {
msg += "BlacklistedProcess: " + processName + "\n";
}
foreach (var driveToCheck in SpaceMonitoring) {
msg += "Drive to check free space: " + driveToCheck.DriveName +
" (threshold: " + driveToCheck.SpaceThreshold + ")" + "\n";
}
if (string.IsNullOrEmpty(SmtpHost)) {
msg += "SmtpHost: ====== Not configured, disabling email! ======" + "\n";
} else {
msg +=
"SmtpHost: " + SmtpHost + "\n" +
"EmailFrom: " + EmailFrom + "\n" +
"AdminEmailAdress: " + AdminEmailAdress + "\n" +
"AdminDebugEmailAdress: " + AdminDebugEmailAdress + "\n" +
"StorageNotificationDelta: " + StorageNotificationDelta + "\n" +
"AdminNotificationDelta: " + AdminNotificationDelta + "\n" +
"GraceNotificationDelta: " + GraceNotificationDelta + "\n";
}
return msg;
}
}
}
\ No newline at end of file
using System;
using System.IO;
using System.Xml.Serialization;
namespace AutoTx.XmlWrapper
{
[Serializable]
public class ServiceStatus
{
[XmlIgnore] string _storageFile; // remember where we came from
[XmlIgnore] private ServiceConfig _config;
[XmlIgnore] public string ValidationWarnings;
private DateTime _lastStatusUpdate;
private DateTime _lastStorageNotification;
private DateTime _lastAdminNotification;
private DateTime _lastGraceNotification;
private string _limitReason;
string _currentTransferSrc;
string _currentTargetTmp;
bool _transferInProgress;
private bool _serviceSuspended;
private bool _cleanShutdown;
private long _currentTransferSize;
[XmlElement("LastStatusUpdate", DataType = "dateTime")]
public DateTime LastStatusUpdate {
get { return _lastStatusUpdate; }
set { _lastStatusUpdate = value; }
}
[XmlElement("LastStorageNotification", DataType = "dateTime")]
public DateTime LastStorageNotification {
get { return _lastStorageNotification; }
set {
_lastStorageNotification = value;
Serialize();
}
}
[XmlElement("LastAdminNotification", DataType = "dateTime")]
public DateTime LastAdminNotification {
get { return _lastAdminNotification; }
set {
_lastAdminNotification = value;
Serialize();
}
}
[XmlElement("LastGraceNotification", DataType = "dateTime")]
public DateTime LastGraceNotification {
get { return _lastGraceNotification; }
set {
_lastGraceNotification = value;
Serialize();
}
}
public string LimitReason {
get { return _limitReason; }
set {
_limitReason = value;
log("LimitReason was updated (" + value + "), calling Serialize()...");
Serialize();
}
}
public string CurrentTransferSrc {
get { return _currentTransferSrc; }
set {
_currentTransferSrc = value;
log("CurrentTransferSrc was updated (" + value + "), calling Serialize()...");
Serialize();
}
}
public string CurrentTargetTmp {
get { return _currentTargetTmp; }
set {
_currentTargetTmp = value;
log("CurrentTargetTmp was updated (" + value + "), calling Serialize()...");
Serialize();
}
}
public bool ServiceSuspended {
get { return _serviceSuspended; }
set {
_serviceSuspended = value;
log("ServiceSuspended was updated (" + value + "), calling Serialize()...");
Serialize();
}
}
public bool TransferInProgress {
get { return _transferInProgress; }
set {
_transferInProgress = value;
log("FilecopyFinished was updated (" + value + "), calling Serialize()...");
Serialize();
}
}
/// <summary>
/// Indicates whether the service was cleanly shut down (false while the service is running).
/// </summary>
public bool CleanShutdown {
get { return _cleanShutdown; }
set {
_cleanShutdown = value;
Serialize();
}
}
public long CurrentTransferSize {
get { return _currentTransferSize; }
set {
_currentTransferSize = value;
log("CurrentTransferSize was updated (" + value + "), calling Serialize()...");
Serialize();
}
}
public ServiceStatus() {
_currentTransferSrc = "";
_currentTargetTmp = "";
_transferInProgress = false;
}
public void Serialize() {
/* During de-serialization, the setter methods get called as well but
* we should not serialize until the deserialization has completed.
* As the storage file name will only be set after this, it is sufficient
* to test for this (plus, we can't serialize anyway without it).
*/
if (_storageFile == null) {
log("File name for XML serialization is not set, doing nothing.");
return;
}
// update the timestamp:
LastStatusUpdate = DateTime.Now;
try {
var xs = new XmlSerializer(GetType());
var writer = File.CreateText(_storageFile);
xs.Serialize(writer, this);
writer.Flush();
writer.Close();
}
catch (Exception ex) {
log("Error in Serialize(): " + ex.Message);
}
log("Finished serializing " + _storageFile);
}
static void log(string message) {
// use Console.WriteLine until proper logging is there (running as a system
// service means those messages will disappear):
Console.WriteLine(message);
/*
using (var sw = File.AppendText(@"C:\Tools\AutoTx\console.log")) {
sw.WriteLine(message);
}
*/
}
public static ServiceStatus Deserialize(string file, ServiceConfig config) {
ServiceStatus status;
var xs = new XmlSerializer(typeof(ServiceStatus));
try {
var reader = File.OpenText(file);
status = (ServiceStatus) xs.Deserialize(reader);
reader.Close();
}
catch (Exception) {
// if reading the status XML fails, we return an empty (new) one
status = new ServiceStatus();
}
status._config = config;
ValidateStatus(status);
// now set the storage filename:
status._storageFile = file;
return status;
}
private static void ValidateStatus(ServiceStatus s) {
// CurrentTransferSrc
if (s.CurrentTransferSrc.Length > 0
&& !Directory.Exists(s.CurrentTransferSrc)) {
s.ValidationWarnings += " - found non-existing source path of an unfinished " +
"transfer: " + s.CurrentTransferSrc + "\n";
s.CurrentTransferSrc = "";
}
// CurrentTargetTmp
var currentTargetTmpPath = Path.Combine(s._config.DestinationDirectory,
s._config.TmpTransferDir,
s.CurrentTargetTmp);
if (s.CurrentTargetTmp.Length > 0
&& !Directory.Exists(currentTargetTmpPath)) {
s.ValidationWarnings += " - found non-existing temporary path of an " +
"unfinished transfer: " + currentTargetTmpPath+ "\n";
s.CurrentTargetTmp = "";
}
}
public string Summary() {
return
"CurrentTransferSrc: " + CurrentTransferSrc + "\n" +
"CurrentTargetTmp: " + CurrentTargetTmp + "\n" +
"TransferInProgress: " + TransferInProgress + "\n" +
"CurrentTransferSize: " + CurrentTransferSize + "\n" +
"LastStatusUpdate: " +
LastStatusUpdate.ToString("yyyy-MM-dd HH:mm:ss") + "\n" +
"LastStorageNotification: " +
LastStorageNotification.ToString("yyyy-MM-dd HH:mm:ss") + "\n" +
"LastAdminNotification: " +
LastAdminNotification.ToString("yyyy-MM-dd HH:mm:ss") + "\n" +
"LastGraceNotification: " +
LastGraceNotification.ToString("yyyy-MM-dd HH:mm:ss") + "\n";
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment