Files
ARM64/IPMonitor/Services/MainService.cs
Sean a82da5bc77
All checks were successful
Build .NET Project / build (push) Successful in 4m51s
Fix the reboot process in IPMonitor
2025-12-20 18:47:07 -05:00

401 lines
15 KiB
C#
Executable File

using System.Diagnostics;
using System.Net;
using System.Text;
using System.Web;
using MarketData;
using MarketData.Configuration;
using MarketData.Integration;
using MarketData.Utils;
using Microsoft.Extensions.Configuration;
namespace IPMonitor
{
public class MainService : IMainService
{
/// <summary>
/// This is the main entry point.
/// Arguments: /FORCE:true|false if true forces a refresh on ZoneEdit.
/// Force an update to ZoneEdit at the top of every hour
/// cron every hour
/// DOTNET_ROOT=/opt/dotnet
/// CRON_DIR_IPMONITOR=/opt/MarketData/IPMonitor
/// 0 * * * * cd $CRON_DIR_IPMONITOR ; /opt/MarketData/IPMonitor/ipmonitor /FORCE:true > /dev/null 2>&1
/// cron 10 minute intervals except top of hour
/// 10,20,30,40,50 * * * * cd $CRON_DIR_IPMONITOR ; /opt/MarketData/IPMonitor/ipmonitor > /dev/null 2>&1
/// </summary>
/// <param name="args"></param>
/// <param name="configuration"></param>
public void RunService(String[] args,IConfiguration configuration)
{
Profiler profiler=new Profiler();
DateTime currentDate = DateTime.Now;
GlobalConfig.Instance.Configuration = configuration; // This call sets up configuration stuff so it needs to be first.
string arg = "ipmonitor";
CommandArgs commandArgs = new CommandArgs(args);
if(!CreateLogging(arg))
{
Console.WriteLine("CreateLogging returned false.");
return;
}
try
{
MDTrace.WriteLine(LogLevel.DEBUG,$"[RunService] Started @ {Utility.DateTimeToStringYYYYHMMHDDHHMMSSTT(currentDate)} in {Directory.GetCurrentDirectory()}");
MDTrace.WriteLine(LogLevel.DEBUG,$"[RunService] Argument {arg}");
bool force=false;
if(commandArgs.Has("REBOOT") && true==commandArgs.Coalesce<bool>("REBOOT"))
{
MDTrace.WriteLine(LogLevel.DEBUG,"IPMONITOR REBOOT true.");
Reboot();
return;
}
else if(commandArgs.Has("FORCE"))
{
force=commandArgs.Coalesce<bool>("FORCE");
MDTrace.WriteLine(LogLevel.DEBUG,$"FORCE={force}");
}
VerifyNetwork();
String ipAddress=GetPublicIPAddress();
UpdateIPAddress(ipAddress,force);
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception:{0}",exception.ToString()));
}
finally
{
MDTrace.WriteLine(LogLevel.DEBUG,$"Done, total took {profiler.End()} (ms)");
}
}
private static bool CreateLogging(String task)
{
if(String.IsNullOrEmpty(task))return false;
task=task.ToLower();
MDTrace.LogLevel = LogLevel.DEBUG;
String logFolder = "/logs";
DateTime currentDate=DateTime.Now;
String strLogFile = "marketdata_" + task + ".log";
String currentWorkingDirectory = Directory.GetCurrentDirectory();
Console.WriteLine($"Current directory is {currentWorkingDirectory}");
Utility.EnsureLogFolder(currentWorkingDirectory+logFolder);
Utility.ExpireLogs(currentWorkingDirectory+logFolder,1);
Trace.Listeners.Remove("Default");
Console.WriteLine($"Adding Trace Listener :{currentWorkingDirectory+logFolder+"/"+strLogFile}");
Trace.Listeners.Add(new TextWriterTraceListener(currentWorkingDirectory+logFolder+"/"+strLogFile));
MDTrace.WriteLine($"Trace Listener added.");
Utility.ShowLogs(currentWorkingDirectory + logFolder);
return true;
}
/// <summary>
/// Verified that we are connected to the internet.
/// The method will retry for 5 minutes and then issue a reboot
/// </summary>
public static void VerifyNetwork()
{
int MAX_RETRIES=10;
int TIMEOUT_BETWEEN_ATTEMPTS=30000;
bool isNetworkAvailable=false;
try
{
for(int index=0;index<MAX_RETRIES && !isNetworkAvailable;index++)
{
MDTrace.WriteLine(LogLevel.DEBUG,"Checking network status...");
isNetworkAvailable=NetworkStatus.IsNetworkAvailable();
if(!isNetworkAvailable)
{
MDTrace.WriteLine(LogLevel.DEBUG,$"The Network seems to be disconnected. Will retry after {TIMEOUT_BETWEEN_ATTEMPTS} (ms) ");
try{Thread.Sleep(TIMEOUT_BETWEEN_ATTEMPTS);}catch{;}
continue;
}
else
{
MDTrace.WriteLine(LogLevel.DEBUG,"The network is connected.");
}
}
if(!isNetworkAvailable)
{
MDTrace.WriteLine(LogLevel.DEBUG,$"IPMonitor has determined that the network has been down for {(TIMEOUT_BETWEEN_ATTEMPTS*MAX_RETRIES)/60000} minutes, rebooting");
Reboot();
}
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,"IPMonitor encountered an issue checking the status of the network.");
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return;
}
}
/// <summary>
/// If REBOOT is specified in the command line arguments then a reboot will be initiated
/// </summary>
public static void Reboot()
{
MDTrace.WriteLine(LogLevel.DEBUG,"Initiating reboot process.");
Utility.Reboot();
}
/// <summary>
/// Retains a record of current ip address in ipaddress.txt file and updates that ip address to ZoneEdit for DNS
/// </summary>
/// <param name="ipAddress"></param>
public static void UpdateIPAddress(String ipAddress,bool force)
{
try
{
String strPathFileName="ipaddress.txt";
if(null==ipAddress)return;
if(force)
{
File.Delete(GetPathFileName(strPathFileName));
}
if(!File.Exists(GetPathFileName(strPathFileName)))
{
WriteFile(GetPathFileName(strPathFileName), ipAddress);
ZoneEditResponses zoneEditResponses=UpdateZoneEditRecord(ipAddress);
MDTrace.WriteLine(LogLevel.DEBUG,$"IPAddress {ipAddress}. ZoneEditUpdated={zoneEditResponses.IsSuccess()}");
SendSMSEmail(String.Format("IPAddress {0}. ZoneEditUpdated={1}",ipAddress,zoneEditResponses.IsSuccess()));
if(!zoneEditResponses.IsSuccess())
{
File.Delete(GetPathFileName(strPathFileName));
}
}
else
{
String currentIPAddress=ReadFile(GetPathFileName(strPathFileName));
if(null!=currentIPAddress && !currentIPAddress.Equals(ipAddress))
{
WriteFile(GetPathFileName(strPathFileName),ipAddress);
ZoneEditResponses zoneEditResponses=UpdateZoneEditRecord(ipAddress);
SendSMSEmail(String.Format("IPAddress {0}. ZoneEditUpdated={1}",ipAddress,zoneEditResponses.IsSuccess()));
}
else if(null!=currentIPAddress && currentIPAddress.Equals(ipAddress))
{
MDTrace.WriteLine(LogLevel.DEBUG,$"Public IP:{ipAddress} matches latest fetched IP:{currentIPAddress}");
}
else if(null==currentIPAddress)
{
SendSMSEmail("IPMonitor "+ipAddress+ ". IPMonitor encountered an issue reading the IPAddress file.");
}
}
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception:{0}",exception.ToString()));
}
}
/// <summary>
/// Retrieves the public IP Address.
/// </summary>
/// <param name="ipAddress"></param>
public static String GetPublicIPAddress()
{
int MAX_RETRIES=5;
int TIMEOUT_BETWEEN_ATTEMPTS=30000;
String request="http://checkip.dyndns.org/";
try
{
String address = null;
for(int index=0;index<MAX_RETRIES && null==address;index++)
{
MDTrace.WriteLine(LogLevel.DEBUG,$"Requesting IPAddress from {request}");
// if(!NetworkStatus.IsNetworkAvailable())
// {
// MDTrace.WriteLine(LogLevel.DEBUG,$"The Network seems to be disconnected. Will retry after {TIMEOUT_BETWEEN_ATTEMPTS} (ms) ");
// try{Thread.Sleep(TIMEOUT_BETWEEN_ATTEMPTS);}catch{;}
// continue;
// }
HttpNetResponse response = HttpNetRequest.GetRequestNoEncodingV7(request);
if(!response.Success)
{
MDTrace.WriteLine(LogLevel.DEBUG,$"Request {request} failed with {response.StatusCode}. Will retry after {TIMEOUT_BETWEEN_ATTEMPTS} (ms) ");
try{Thread.Sleep(TIMEOUT_BETWEEN_ATTEMPTS);}catch{;}
continue;
}
address = response.ResponseString;
if(String.IsNullOrEmpty(address))
{
MDTrace.WriteLine(LogLevel.DEBUG,$"Request {request} received an empty response. Will retry after {TIMEOUT_BETWEEN_ATTEMPTS} (ms) ");
try{Thread.Sleep(TIMEOUT_BETWEEN_ATTEMPTS);}catch{;}
continue;
}
}
if(null==address)
{
MDTrace.WriteLine(LogLevel.DEBUG,$"Unable to obtain IP Address. Attempts:{MAX_RETRIES}");
return null;
}
try
{
int first = address.IndexOf("Address: ") + 9;
int last = address.LastIndexOf("</body>");
address = address.Substring(first, last - first);
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Request succeeded: {0} -> {1}",request,address));
return address;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception parsing address: {0}",exception.ToString()));
return null;
}
}
catch(Exception exception)
{
String message = String.Format("Exception:{0}",exception.ToString());
MDTrace.WriteLine(LogLevel.DEBUG,message);
SendSMSEmail("IPMonitor encountered an issue retrieving public IPAddress.");
return null;
}
}
/// <summary>
/// Updates ZoneEdit with the given ipaddress.
/// </summary>
/// <param name="ipAddress"></param>
/// <returns>ZoneEditResponses</returns>
public static ZoneEditResponses UpdateZoneEditRecord(String ipAddress)
{
HttpClient client = default;
HttpNetResponse httpNetResponse=default;
ZoneEditResponses zoneEditResponses = new ZoneEditResponses();
try
{
client = new HttpClient();
String user=GlobalConfig.Instance.Configuration["zoneedit_user"];
String password=GlobalConfig.Instance.Configuration["zoneedit_password"];
String host=GlobalConfig.Instance.Configuration["zoneedit_host"];
String strRequest=null;
StringBuilder sb=new StringBuilder();
sb = new StringBuilder();
sb.Append("https://");
sb.Append("dynamic.zoneedit.com/auth/dynamic.html");
sb.Append("?host=").Append(HttpUtility.UrlEncode(host));
sb.Append("&dnsto=").Append(HttpUtility.UrlEncode(ipAddress));
strRequest=sb.ToString();
httpNetResponse = HttpNetRequest.GetRequestNoEncodingZoneEdit(strRequest,user,password);
if(httpNetResponse.Success)
{
zoneEditResponses = new ZoneEditResponses(httpNetResponse.ResponseString);
}
foreach(ZoneEditResponse zoneEditResponse in zoneEditResponses)
{
MDTrace.WriteLine(LogLevel.DEBUG,$"Text:{zoneEditResponse.Text} Zone:{zoneEditResponse.Zone}");
}
return zoneEditResponses;
}
finally
{
if(default!=client)
{
client.Dispose();
}
if(default!=httpNetResponse)
{
httpNetResponse.Dispose();
}
}
}
/// <summary>
/// Writes the given ip address into the specified file
/// </summary>
/// <param name="strPathFileName"></param>
/// <param name="ipAddress"></param>
public static void WriteFile(String strPathFileName,String ipAddress)
{
try
{
if(File.Exists(strPathFileName))File.Delete(strPathFileName);
using FileStream fileStream=new FileStream(strPathFileName,FileMode.Create,FileAccess.Write,FileShare.Read);
using StreamWriter streamWriter=new StreamWriter(fileStream);
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Creating address file:{0}",strPathFileName));
streamWriter.WriteLine(ipAddress);
streamWriter.Flush();
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception:{0}",exception.ToString()));
return;
}
}
/// <summary>
/// Reads a string from the specified file
/// </summary>
/// <param name="strPathFileName"></param>
/// <param name="ipAddress"></param>
public static String ReadFile(String strPathFileName)
{
try
{
using FileStream fileStream=new FileStream(strPathFileName,FileMode.Open,FileAccess.ReadWrite,FileShare.Read);
using StreamReader streamReader=new StreamReader(fileStream);
String item=null;
item=streamReader.ReadLine();
return item;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception:{0}",exception.ToString()));
return null;
}
}
/// <summary>
/// Determines the fully qualified path file name for the provided file based on current directory and working folder
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public static String GetPathFileName(String fileName)
{
String currentWorkingDirectory = Directory.GetCurrentDirectory();
String workingFolder = GlobalConfig.Instance.Configuration["working_folder"];
if(workingFolder.StartsWith("/"))
{
String fullPath = currentWorkingDirectory+workingFolder;
if(!Directory.Exists(fullPath))Directory.CreateDirectory(fullPath);
return fullPath+"/"+fileName;
}
else
{
String fullPath = currentWorkingDirectory+"/"+workingFolder;
if(!Directory.Exists(fullPath))Directory.CreateDirectory(fullPath);
return fullPath+"/"+fileName;
}
}
/// <summary>
/// Sends an email.
/// </summary>
/// <param name="message"></param>
public static void SendSMSEmail(string message)
{
String smsSMTPAddress = GlobalConfig.Instance.Configuration["sms_smtpaddress"];
String smsUserName = GlobalConfig.Instance.Configuration["sms_smsusername"];
String smsPassword = GlobalConfig.Instance.Configuration["sms_smspassword"];
String[] smsRecipients = GlobalConfig.Instance.Configuration["sms_smsrecipients"].Split(',');
bool sendNotifications = bool.Parse(GlobalConfig.Instance.Configuration["send_notifications"]);
if(!sendNotifications)
{
MDTrace.WriteLine(LogLevel.DEBUG,$"Not sending Message '{message}' to {GlobalConfig.Instance.Configuration["sms_smsrecipients"]} because notifications are disabled.");
return;
}
SMSClient.SendSMSEmail(message, smsUserName, smsRecipients, smsSMTPAddress, smsUserName, smsPassword);
}
}
}