1151 lines
51 KiB
C#
1151 lines
51 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.ServiceProcess;
|
|
using System.Reflection;
|
|
using PlutoServer.MSL.Connectors;
|
|
//using TCMPortMapper;
|
|
using System.Net;
|
|
using Pluto.Api;
|
|
using System.Xml;
|
|
using System.IO;
|
|
using System.Text;
|
|
using Sdaleo.Systems.Advantage;
|
|
using Sdaleo;
|
|
using Sdaleo.Systems.SQLServer;
|
|
using Yaulw.File;
|
|
using Yaulw.Thread;
|
|
using TCMPortMapper;
|
|
using Yaulw.Assembly;
|
|
|
|
namespace PlutoServer.MSL
|
|
{
|
|
public partial class PlutoService : ServiceBase
|
|
{
|
|
//Portmapper singleton instance
|
|
PortMapper pMapper = PortMapper.SharedInstance;
|
|
|
|
#region Internal Consts
|
|
|
|
// Hard-coded in * Internal channel always uses these ports
|
|
internal const int INTERNAL_CHANNEL_PORT = 1945;
|
|
internal const int EXTERNAL_MINIMUM_PORT_NUMBER = 49000;
|
|
internal const int EXTERNAL_MAXIMUM_PORT_NUMBER = 50000;
|
|
|
|
// Hard-coded in * Registry Strings / Settings
|
|
internal const string LOCAL_MACHINE_SUBKEY = "Software\\MSLMobile";
|
|
internal const string LOCAL_MACHINE_EXTERNALPORT = "ExternalPort";
|
|
internal const string LOCAL_MACHINE_HOSTGUID = "HostGuid";
|
|
internal const string LOCAL_MACHINE_INITIALSETUPDT = "InitialSetup";
|
|
|
|
|
|
// Threshold used to determine if system just rebooted, and service just
|
|
// started. We need to wait a little because we need to make sure SQLServer
|
|
// and Advantage Server are up
|
|
internal const int NUMBER_OF_MINUTES_SERVICE_UP_THRESHOLD = 5;
|
|
internal const int NUMBER_OF_MINUTES_SYSTEM_UP_THRESHOLD = 10;
|
|
|
|
// Threshold used to query WhatIsMyIP (they allow every 5 Minutes)
|
|
internal const int NUMBER_OF_MINUTES_TO_QUERY_FOR_EXTERNAL_IP = 15;
|
|
|
|
// Use this host to check, if we are on the McKesson Network
|
|
internal const string MCK_HOST_TO_CHECK_ON_MCK_NETWORK = "ndh1fs01.mckesson.com";
|
|
|
|
#endregion
|
|
|
|
#region Internal Statics (Important for getting Information from Pluto Service)
|
|
|
|
/// <summary>
|
|
/// Service Start Time
|
|
/// </summary>
|
|
internal static DateTime ServiceStartDT = DateTime.MinValue;
|
|
|
|
/// <summary>
|
|
/// Did the service statup on the McKesson Network? Initialized upon Service Start
|
|
/// </summary>
|
|
internal static bool IsOnMcKessonNetwork { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Are the External IP and Port Loaded (and set)
|
|
/// </summary>
|
|
internal static bool IsExtIPandExtPortSet { get { return s_PlutoService.InitialExternalIPConfigurationIsSet; } }
|
|
|
|
/// <summary>
|
|
/// LocalIPAddress, automatically set by our Connectivity Thread
|
|
/// </summary>
|
|
internal static IPAddress InternalIP { get; private set; }
|
|
|
|
/// <summary>
|
|
/// ExternalIPAddress, automatically set by our Connectivity Thread
|
|
/// updated upon service startup and then at interval NUMBER_OF_MINUTES_TO_QUERY_FOR_EXTERNAL_IP
|
|
/// </summary>
|
|
internal static IPAddress ExternalIP { get; private set; }
|
|
|
|
/// <summary>
|
|
/// External Port, automatically set by our Connectivity Thread (Radomly or from the Registry)
|
|
/// LOCAL_MACHINE_EXTERNALPORT
|
|
/// </summary>
|
|
internal static uint ExternalPort { get; private set; }
|
|
|
|
/// <summary>
|
|
/// There is no need for us to update the server if the Host GUID doesn't exist, hence
|
|
/// we need to at start up know whether or not to automatically update or not, or whether to
|
|
/// just wait
|
|
/// </summary>
|
|
internal static RegistrationWrapper.HostGUIDstate state = RegistrationWrapper.HostGUIDstate.no_connectivity;
|
|
|
|
/// <summary>
|
|
/// Quick Check to get the local IP Address
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
internal static IPAddress IPFetchLocalIPAddress
|
|
{
|
|
get
|
|
{
|
|
IPAddress IP = Yaulw.Net.IPHostHelper.GetFirstLocalIPAddress();
|
|
if (IP == IPAddress.None)
|
|
MSLSpecific.Logger.Error("No Local IP Address Found using IP {0} as Local Address", IPAddress.None);
|
|
return IP;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reloads the External IP, and Port Configuration,
|
|
/// by setting ReloadExternalIPandPortConfiguration to true
|
|
/// </summary>
|
|
internal static bool ReloadExtIPandPortConfiguration(int Port)
|
|
{
|
|
if (Port > 0 && Port != ExternalPort)
|
|
{
|
|
s_PlutoService.DeactivateROExternalChannelIfItHasBeenActivated(false);
|
|
|
|
// Set the Port in the Registry
|
|
Yaulw.Registry.RegKey.SetKey<int>(Yaulw.Registry.HKEYRoot.LocalMachine, LOCAL_MACHINE_SUBKEY, LOCAL_MACHINE_EXTERNALPORT, Port);
|
|
|
|
// Reload the Ports
|
|
IPAddress extIP = IPAddress.None;
|
|
uint nPort = 0;
|
|
|
|
// A user could already have the router pointed to us while we are still set on another port,
|
|
// then decide to change port. We can check via PlutoCheck if this new port is actually pointing to us already
|
|
// without the Pluto check we will see it as used and jump to another port (unexpected behavior for the user)
|
|
if (s_PlutoService.Retrieve_ExternalIP_ExternalPort(true, true, true, out extIP, out nPort))
|
|
{
|
|
ExternalIP = extIP;
|
|
ExternalPort = nPort;
|
|
s_PlutoService.LastExternalIPCheckRun = DateTime.Now;
|
|
|
|
// Re-initialize the McKesson's Mobile Gateway
|
|
RegistrationWrapper.UpdateServerIntExt(InternalIP, ExternalIP, ExternalPort);
|
|
|
|
// Re-activate the Channel
|
|
s_PlutoService.ActivateROInternalChannelIfHasNotBeenActivated();
|
|
|
|
// If the port is now what the user asked for than we are good to go
|
|
bool bSuccess = (nPort == Port);
|
|
if (!bSuccess)
|
|
MSLSpecific.Logger.Error("ReloadExtIPandPortConfiguration Failed nPort:{0} Port:{1}", nPort, Port);
|
|
return bSuccess;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Members
|
|
|
|
/// <summary>
|
|
/// Check if this is the first time the network got enabled
|
|
/// </summary>
|
|
private bool FirstTimeNetworkConnected = true;
|
|
|
|
/// <summary>
|
|
/// Was the Internal Channel Activated?
|
|
/// </summary>
|
|
private bool ROInternalHasBeenActivated = false;
|
|
|
|
/// <summary>
|
|
/// Was the External Channel Activated?
|
|
/// </summary>
|
|
private bool ROExternalHasBeenActivated = false;
|
|
|
|
/// <summary>
|
|
/// Did a System Reboot just occur?
|
|
/// </summary>
|
|
private bool SystemJustStarted_SoUseServiceThreshold = false;
|
|
private bool ServiceUpTimeReached = false;
|
|
private bool SystemThresholdNotReached = true;
|
|
|
|
/// <summary>
|
|
/// Has the DataStore been read in? Try up to Number of Times
|
|
/// </summary>
|
|
private bool DataStoreHasBeenRead = false;
|
|
private uint Number_Of_Tries_to_Read_DataStore = 3;
|
|
private bool DataStoreReadErroredOut = false;
|
|
|
|
/// <summary>
|
|
/// Has the PortMapper been started?
|
|
/// </summary>
|
|
private bool IsPortMapperRunning = false;
|
|
|
|
/// <summary>
|
|
/// Is the Logger Setup?
|
|
/// </summary>
|
|
private bool LoggerIsSetup = false;
|
|
|
|
/// <summary>
|
|
/// Try to Read in External IP configuration. Try up to Number of Times
|
|
/// </summary>
|
|
private bool InitialExternalIPConfigurationIsSet = false;
|
|
private uint Number_Of_Tries_to_Read_ExternalIP_Configuration = 3;
|
|
private bool InitialExternalIPRetrieveErrorerOut = false;
|
|
|
|
/// <summary>
|
|
/// What is my IP only allows us to check external IP every NUMBER_OF_MINUTES_TO_QUERY_FOR_EXTERNAL_IP
|
|
/// </summary>
|
|
private DateTime LastExternalIPCheckRun = DateTime.MinValue;
|
|
|
|
/// <summary>
|
|
/// Our Connectivity Timer Thread
|
|
/// </summary>
|
|
private SingleThreadTimer connectivityTimer = null;
|
|
|
|
/// <summary>
|
|
/// Allows us to skip cycles in the Connectivity Thread
|
|
/// </summary>
|
|
private uint nConnectivitySkipCounter = 0;
|
|
|
|
/// <summary>
|
|
/// If for some reason the network goes down, we should re-initialize
|
|
/// everything as if nothing happened
|
|
/// </summary>
|
|
private bool NetworkOutageDetected = false;
|
|
|
|
/// <summary>
|
|
/// When we start up we have no knowledge whether we can connect to the
|
|
/// registrat/gateway server. Also if it goes down. If we detect a switch from
|
|
/// no to exist we force an external UpdateServer Refresh
|
|
/// *by default this is true* because at startup we want to do this also
|
|
/// </summary>
|
|
private bool SwitchFromNoConnectivityToConnectivityOccured = true;
|
|
|
|
/// <summary>
|
|
/// Send the service version upon Service start-up
|
|
/// </summary>
|
|
private bool ServiceStartVersionSent = false;
|
|
|
|
#endregion
|
|
|
|
#region Construction
|
|
|
|
/// <summary>
|
|
/// Singleton Instance of the Service
|
|
/// </summary>
|
|
private static PlutoService s_PlutoService = null;
|
|
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
public PlutoService()
|
|
{
|
|
InitializeComponent();
|
|
|
|
// Log to the Application System Log
|
|
this.EventLog.Log = "Application";
|
|
s_PlutoService = this;
|
|
|
|
// Handle all unexpected errors
|
|
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unhandled Domain Exception
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
|
{
|
|
Exception ex = (Exception)e.ExceptionObject;
|
|
String msg = "UnhandledException Occured: " + ex.Message + "\n\n" + ex.InnerException.Message;
|
|
AppLogError(msg);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Windows Event Logging
|
|
|
|
/// <summary>
|
|
/// Log into the Event Viewer as well as the Service Log (if possible)
|
|
/// </summary>
|
|
/// <param name="errMsg"></param>
|
|
internal static void AppLogError(string errMsg)
|
|
{
|
|
s_PlutoService.EventLog.WriteEntry(errMsg, EventLogEntryType.Error);
|
|
if (MSLSpecific.Logger != null)
|
|
MSLSpecific.Logger.Error(errMsg);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Log into the Event Viewer as well as the Service Log (if possible)
|
|
/// </summary>
|
|
/// <param name="errMsg"></param>
|
|
internal static void AppLogInfo(string infoMsg)
|
|
{
|
|
s_PlutoService.EventLog.WriteEntry(infoMsg, EventLogEntryType.Information);
|
|
if (MSLSpecific.Logger != null)
|
|
MSLSpecific.Logger.Info(infoMsg);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Log into the Event Viewer as well as the Service Log (if possible)
|
|
/// </summary>
|
|
/// <param name="errMsg"></param>
|
|
internal static void AppLogWarning(string warnMsg)
|
|
{
|
|
s_PlutoService.EventLog.WriteEntry(warnMsg, EventLogEntryType.Warning);
|
|
if (MSLSpecific.Logger != null)
|
|
MSLSpecific.Logger.Info(warnMsg);
|
|
}
|
|
|
|
#endregion
|
|
|
|
// usefull for debugging Service in visual studio
|
|
#if DEBUG
|
|
internal void DebugStart(string[] args)
|
|
{
|
|
DEBUGGING_ONLY.DEBUGSTEP_INTO();
|
|
OnStart(args);
|
|
}
|
|
#endif
|
|
|
|
#region System / Service Up Time
|
|
|
|
/// <summary>
|
|
/// Get Service UpTime
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private TimeSpan GetServiceUpTime()
|
|
{
|
|
return (DateTime.Now - ServiceStartDT);
|
|
}
|
|
|
|
/// <summary>
|
|
/// For Service Handling, see if the system has been up long enough
|
|
/// Once it becomes true, it will always return true
|
|
/// </summary>
|
|
private bool HasServiceBeenUpLongEnough()
|
|
{
|
|
if (!ServiceUpTimeReached)
|
|
ServiceUpTimeReached = GetServiceUpTime().TotalMinutes >= NUMBER_OF_MINUTES_SERVICE_UP_THRESHOLD;
|
|
return ServiceUpTimeReached;
|
|
}
|
|
|
|
/// <summary>
|
|
/// For Error Handling, see if the system has been up till this threshold
|
|
/// Once it becomes false, will always return false
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private bool IsSystemBeenUpWithinMaxThreshold()
|
|
{
|
|
if (SystemThresholdNotReached)
|
|
SystemThresholdNotReached = (Yaulw.Installer.Common.GetSystemUpTime().TotalMinutes <= NUMBER_OF_MINUTES_SYSTEM_UP_THRESHOLD);
|
|
return SystemThresholdNotReached;
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Service Start-Up
|
|
/// </summary>
|
|
/// <param name="args"></param>
|
|
protected override void OnStart(string[] args)
|
|
{
|
|
try
|
|
{
|
|
// Service Started
|
|
// System just started, so make sure that service is timing out a little to make
|
|
// sure everything is up and running
|
|
SystemJustStarted_SoUseServiceThreshold = IsSystemBeenUpWithinMaxThreshold();
|
|
AppLogInfo(String.Format("MSLMobile Api Service version:'{0}' started up at:'{1}'. System Reboot detected:'{2}'", AssemblyW.GetAssemblyVersion(AssemblyW.AssemblyST.Entry).ToString(), DateTime.Now.ToLongTimeString(), SystemJustStarted_SoUseServiceThreshold));
|
|
ServiceStartDT = DateTime.Now;
|
|
|
|
// Are we on the McKesson Network?
|
|
IsOnMcKessonNetwork = Yaulw.Net.IPHostHelper.CanResolveHost(PlutoService.MCK_HOST_TO_CHECK_ON_MCK_NETWORK, true);
|
|
if (IsOnMcKessonNetwork)
|
|
AppLogInfo("McKesson Network Detected");
|
|
else
|
|
AppLogInfo("Regular Network Detected");
|
|
|
|
// Load the AppConfig
|
|
if(Configuration.Load(false))
|
|
AppLogInfo("Using the .config File as main configuration");
|
|
else
|
|
AppLogInfo("Using hard-coded in values as main configuration");
|
|
|
|
// Load the Determine External IP Url
|
|
Yaulw.Net.IPHostHelper.WHAT_IS_MY_IP_AUTOMATION_URL = Configuration.DETERMINE_EXTERNAL_IP_ADDRESS_URL;
|
|
AppLogInfo(String.Format("Using '{0}' as our determine external IP address url", Configuration.DETERMINE_EXTERNAL_IP_ADDRESS_URL));
|
|
|
|
// Internet / Network Connectivity Timer
|
|
// * Ran into issues when computer is rebooted, we need certain things working before the service can start,
|
|
// * therefore decided to put DataStore Read(), AutoUpdate, RO Connection into a Connection Timer,
|
|
// that keeps checking every 10 seconds if we have network connectivity * Enables / Disables RO Channels as needed, just in case *
|
|
AppLogInfo("MSLMobile Api Service starting connectivity timer.");
|
|
connectivityTimer = new SingleThreadTimer(ConnectivityHandler, (uint)TimeSpan.FromSeconds(10).TotalMilliseconds, true);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
AppLogError(String.Format("Service OnStart() Error thrown. Fatal. Can't continue: {0}", e.Message));
|
|
this.Stop();
|
|
}
|
|
}
|
|
|
|
#region Private Connectivity Timed Thread (runs every 10 Seconds)
|
|
|
|
/// <summary>
|
|
/// Single Threaded Connectivity Handler (Handles Auto-Update, ReadInDataStore(), and RO Channels)
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void ConnectivityHandler(object sender, Yaulw.Thread.SingleThreadTimer.STElapsedEventArgs e)
|
|
{
|
|
// System just started, we won't do anyting in here until our
|
|
// Service Startup 'NUMBER_OF_MINUTES_SERVICE_UP_THRESHOLD' timeout of is reached
|
|
if (SystemJustStarted_SoUseServiceThreshold && !HasServiceBeenUpLongEnough())
|
|
return;
|
|
|
|
// If the skip counter is set, use it to skip iterations
|
|
if (nConnectivitySkipCounter > 0)
|
|
{
|
|
nConnectivitySkipCounter--;
|
|
return;
|
|
}
|
|
|
|
// Check Connectivity,
|
|
// ~withouth basic networking being up there is nothing really to do here
|
|
bool bHasConnection = Yaulw.Net.IPHostHelper.HasConnection();
|
|
if (!bHasConnection)
|
|
{
|
|
DeactivateROChannelsIfTheyHaveBeenActivated(true);
|
|
return;
|
|
}
|
|
|
|
// Initialize Internal IP
|
|
if (InternalIP == null)
|
|
InternalIP = PlutoService.IPFetchLocalIPAddress;
|
|
|
|
// Always set up the Logger First (Needed for all subsequent Calls)
|
|
if (!LoggerIsSetup)
|
|
{
|
|
AppLogInfo("Setting up Logger");
|
|
MSLSpecific.Setup_Logger();
|
|
LoggerIsSetup = true;
|
|
}
|
|
|
|
// Check To See if we need to update, This is our first time that
|
|
// the system has been up long enough and that we have a network connection
|
|
if (FirstTimeNetworkConnected)
|
|
{
|
|
AppLogInfo("System has been up long enough. Starting Initialization.");
|
|
// Upon Every Startup of the Service, let Auto-Update know to update the service when needed
|
|
#if !DEBUG
|
|
AppLogInfo("Starting Auto-Update Timer.");
|
|
AutoUpdate.StartAutoTimer_ServiceStarted();
|
|
#endif
|
|
FirstTimeNetworkConnected = false;
|
|
// just to make sure wait another cycle * due to Auto-Update * Feature
|
|
return;
|
|
}
|
|
|
|
// * Check to Make sure the System has been up long enough *
|
|
// Calling ReadInDataStore() requires Advantage and/or SQL Server to be up
|
|
// and running and hence, we can't do it right at service start when the
|
|
// server just rebooted, we wait 5 of service uptime to make sure after a server reboot,
|
|
// that the services needed are up
|
|
if (!FirstTimeNetworkConnected)
|
|
{
|
|
// If Network disconnection occured, we should try to re-read in everything,
|
|
// to make sure we are doing the best we can
|
|
if (NetworkOutageDetected)
|
|
{
|
|
AppLogInfo("Network Outage Detected - Rereading Data and External IP settings");
|
|
|
|
// Re-read in Data Store
|
|
DataStoreHasBeenRead = false;
|
|
DataStoreReadErroredOut = false;
|
|
|
|
// Re-initialize initial ExternalIP Retrival
|
|
InitialExternalIPConfigurationIsSet = false;
|
|
InitialExternalIPRetrieveErrorerOut = false;
|
|
}
|
|
|
|
// First step try to load the DataBase Cache
|
|
if (!LoadInDataStore())
|
|
return;
|
|
|
|
// Second is to do is to figgure out the external IP and Port Settings
|
|
if (!Initialize_ExternalIP_And_ExternalPort())
|
|
return;
|
|
|
|
// Lastly, activate any of the RO Channels
|
|
ActivateROChannelsIfTheyHaveNotBeenActivated();
|
|
|
|
// There is no need to call update Server automatically,
|
|
// if the host guid doesn't exist, the MSLSpecific calls would
|
|
// have to call RegisterNewPractice First (in case of no connectivity
|
|
// we can't call registration server anyways, so may as well time out
|
|
// for 5 minutes and try again)
|
|
switch (state)
|
|
{
|
|
case RegistrationWrapper.HostGUIDstate.no_connectivity:
|
|
{
|
|
state = RegistrationWrapper.DoesServerHostGuidExist();
|
|
if (state == RegistrationWrapper.HostGUIDstate.no_connectivity)
|
|
{
|
|
MSLSpecific.Logger.Info("Communication to Mobile Gateway Server could not be established, trying again in 5 minutes.");
|
|
// try again in 5 minutes...
|
|
nConnectivitySkipCounter = 30;
|
|
// force a UpdateServerIntExt right away once connectivity is restored
|
|
SwitchFromNoConnectivityToConnectivityOccured = true;
|
|
return;
|
|
}
|
|
else if (state == RegistrationWrapper.HostGUIDstate.exists)
|
|
{
|
|
goto case RegistrationWrapper.HostGUIDstate.exists;
|
|
}
|
|
break;
|
|
}
|
|
case RegistrationWrapper.HostGUIDstate.exists:
|
|
{
|
|
// Try to force the UpdateServerIntExt Right away,
|
|
// once a switch from no connectivity to connectivity occurs
|
|
if (SwitchFromNoConnectivityToConnectivityOccured &&
|
|
InitialExternalIPConfigurationIsSet)
|
|
{
|
|
RegistrationWrapper.UpdateServerIntExt(InternalIP, ExternalIP, ExternalPort);
|
|
SwitchFromNoConnectivityToConnectivityOccured = false;
|
|
}
|
|
else if (SwitchFromNoConnectivityToConnectivityOccured)
|
|
{
|
|
// We must also initialize internal, in case external was never set
|
|
// i.e. we are on the McKesson Network or WIFI only mode
|
|
RegistrationWrapper.UpdateServerInternal(InternalIP);
|
|
SwitchFromNoConnectivityToConnectivityOccured = false;
|
|
}
|
|
|
|
// Let the Gateway know upon service start-up what version we are running
|
|
// (only do this once per service start), so after a fresh install, we know what they are running
|
|
if (!ServiceStartVersionSent)
|
|
{
|
|
Version LocalVersion = Yaulw.Assembly.AssemblyW.GetAssemblyVersion(Yaulw.Assembly.AssemblyW.AssemblyST.Entry);
|
|
RegistrationWrapper.ServerHasBeenUpdated(LocalVersion.ToString());
|
|
ServiceStartVersionSent = true;
|
|
}
|
|
|
|
// Handle Internal Address Changes every 10 seconds
|
|
HandleLocalIPAddressChange();
|
|
|
|
// Handle External Address Change every 5 minutes
|
|
HandleExternalIPAddressChange();
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Connectivity Important Handlers
|
|
|
|
/// <summary>
|
|
/// First_LoadInDataStore() First thing to do, we should connect to the data store,
|
|
/// we try up to 3 times, should be enough. Each time has 1 minute.
|
|
/// </summary>
|
|
/// <param name="bHasConnection">true, if network connection is up</param>
|
|
/// <returns>True for the Caller to continue, false otherwise</returns>
|
|
private bool LoadInDataStore()
|
|
{
|
|
if (!DataStoreHasBeenRead && !DataStoreReadErroredOut)
|
|
{
|
|
AppLogInfo("Trying to load the DataStore");
|
|
DataStoreHasBeenRead = DBCache.ReadInDataStore();
|
|
if (DataStoreHasBeenRead)
|
|
{
|
|
// This is good news!
|
|
AppLogInfo("DataStore has been read successfully or DataStore is empty");
|
|
return true;
|
|
}
|
|
else if (!DataStoreHasBeenRead && Number_Of_Tries_to_Read_DataStore > 0)
|
|
{
|
|
// We are still erroring out, try again in a minute, up to Number_Of_Tries_to_Read_DataStore
|
|
// Skip six cycles, aka. 60 seconds for each try
|
|
Number_Of_Tries_to_Read_DataStore--;
|
|
nConnectivitySkipCounter = 6;
|
|
|
|
// Don't allow to continue until this is complete
|
|
return false;
|
|
}
|
|
else if (!DataStoreHasBeenRead && Number_Of_Tries_to_Read_DataStore == 0)
|
|
{
|
|
DataStoreReadErroredOut = true;
|
|
AppLogWarning("Failed to read a single DataStore item. Mobile Api Service may not be in a good state. Contact support if problem persist.");
|
|
|
|
// Failed to many times, warn the user but nothing totally critical yet
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// always continue, once we reach here
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Trues to retrieve and set the External IP, and calculatest a possible Port To Use,
|
|
/// we try up to 3 times, should be enough. Each time has 1 minute.
|
|
/// </summary>
|
|
/// <param name="bHasConnection">true, if network connection is up</param>
|
|
/// <returns>True for the Caller to continue, false otherwise</returns>
|
|
private bool Initialize_ExternalIP_And_ExternalPort()
|
|
{
|
|
try
|
|
{
|
|
#if DEBUG
|
|
if (!InitialExternalIPConfigurationIsSet && !InitialExternalIPRetrieveErrorerOut)
|
|
#else
|
|
if (!IsOnMcKessonNetwork && !InitialExternalIPConfigurationIsSet && !InitialExternalIPRetrieveErrorerOut)
|
|
#endif
|
|
{
|
|
AppLogInfo("Trying to load External IP Configuration");
|
|
IPAddress extIP = IPAddress.None;
|
|
uint Port = 0;
|
|
|
|
// During initialization/startup we just will only check that the port is not in use via
|
|
// Port Check. A pluto check is overkill and doesn't make sense. Only a port check should be enough.
|
|
// Whether this get's called when network card is down or upon Service Startup, we will be fine
|
|
// by auto-calculating the port and not pluto checking
|
|
if (Retrieve_ExternalIP_ExternalPort(true, true, false, out extIP, out Port))
|
|
{
|
|
InitialExternalIPConfigurationIsSet = true;
|
|
ExternalIP = extIP;
|
|
ExternalPort = Port;
|
|
LastExternalIPCheckRun = DateTime.Now;
|
|
AppLogInfo(String.Format("Loaded initial External IP:{0} and Port:{1} at {2}", extIP, ExternalPort, LastExternalIPCheckRun));
|
|
return true;
|
|
}
|
|
else if (!InitialExternalIPConfigurationIsSet && Number_Of_Tries_to_Read_ExternalIP_Configuration > 0)
|
|
{
|
|
// We are still erroring out, try again in a minute, up to Number_Of_Tries_to_Read_ExternalIP_Configuration
|
|
// Skip six cycles, aka. 60 seconds for each try
|
|
Number_Of_Tries_to_Read_ExternalIP_Configuration--;
|
|
nConnectivitySkipCounter = 6;
|
|
|
|
// Don't allow to continue until this is complete
|
|
return false;
|
|
}
|
|
else if (!InitialExternalIPConfigurationIsSet && Number_Of_Tries_to_Read_ExternalIP_Configuration == 0)
|
|
{
|
|
// Failed too many times, we can work arround this via *Wifi Only*
|
|
InitialExternalIPRetrieveErrorerOut = true;
|
|
AppLogWarning("Failed to load External IP Configuration for now. Will try again in 5 minutes");
|
|
|
|
// Failed to many times, warn the user but nothing totally critical yet
|
|
LastExternalIPCheckRun = DateTime.Now;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// always continue, once we reach here
|
|
return true;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
MSLSpecific.Logger.Error("Something is seriously wrong with Initialize_ExternalIP_And_Port.", e);
|
|
MSLSpecific.Logger.Fatal("Dev has to look into this. Program may not be able to recover.", e);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle Local IP Address Change
|
|
/// </summary>
|
|
private void HandleLocalIPAddressChange()
|
|
{
|
|
IPAddress intIP = IPAddress.None;
|
|
bool bChangeOccured = Yaulw.Net.IPHostHelper.AreIPAddressesDifferent(InternalIP, PlutoService.IPFetchLocalIPAddress, out intIP);
|
|
if (bChangeOccured && Yaulw.Net.IPHostHelper.IsValidIPv4Address(intIP, false))
|
|
{
|
|
InternalIP = intIP;
|
|
if (InitialExternalIPConfigurationIsSet)
|
|
{
|
|
// Update the McKesson's Mobile Gateway
|
|
RegistrationWrapper.UpdateServerIntExt(InternalIP, ExternalIP, ExternalPort);
|
|
}
|
|
else
|
|
{
|
|
// Update the McKesson's Mobile Gateway
|
|
RegistrationWrapper.UpdateServerInternal(InternalIP);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle External IP Address Change
|
|
/// </summary>
|
|
private void HandleExternalIPAddressChange()
|
|
{
|
|
// What is my ip automation rules allow every NUMBER_OF_MINUTES_TO_QUERY_FOR_EXTERNAL_IP minutes to check, so that is what'll do
|
|
TimeSpan ts = DateTime.Now - LastExternalIPCheckRun;
|
|
bool bTimePassed = (ts.TotalMilliseconds >= TimeSpan.FromMinutes(NUMBER_OF_MINUTES_TO_QUERY_FOR_EXTERNAL_IP).TotalMilliseconds);
|
|
|
|
// Also Check that we are not updating between the hours of 2am to 6am
|
|
DateTime dt2 = DateTime.Now.ToLocalTime();
|
|
TimeSpan ts2 = dt2.TimeOfDay;
|
|
bool bIsBetween2amAnd6am = (ts2.TotalHours >= 2.0) && (ts2.TotalHours <= 6.0);
|
|
if (bIsBetween2amAnd6am && bTimePassed)
|
|
bTimePassed = false;
|
|
|
|
// We need to automatically keep track of the external IP on External Networks
|
|
// and update it with the McKesson's Mobile Gateway, as needed
|
|
#if DEBUG
|
|
if (bTimePassed)
|
|
#else
|
|
if (!IsOnMcKessonNetwork && bTimePassed)
|
|
#endif
|
|
{
|
|
// Retrieve both Port and IP configuration to set the initial configuration
|
|
if (!InitialExternalIPConfigurationIsSet)
|
|
{
|
|
IPAddress extIP = IPAddress.None;
|
|
uint Port = 0;
|
|
|
|
// For some reason the InitialExternalIPConfiguration was never set, which means
|
|
// the port also was never calculated / used. Because we need an external ip to check the external port.
|
|
// try to retrieve the external ip and do a port check , don't force multiple checks and don't do a pluto check on
|
|
// the port (that will only be done via ReloadExIPandPortConfiguration())
|
|
if (Retrieve_ExternalIP_ExternalPort(false, true, false, out extIP, out Port))
|
|
{
|
|
InitialExternalIPConfigurationIsSet = true;
|
|
ExternalIP = extIP;
|
|
ExternalPort = Port;
|
|
LastExternalIPCheckRun = DateTime.Now;
|
|
AppLogInfo(String.Format("Loaded External IP:{0} and Port:{1} at {2}", extIP, ExternalPort, LastExternalIPCheckRun));
|
|
|
|
// Initialize the McKesson's Mobile Gateway
|
|
RegistrationWrapper.UpdateServerIntExt(InternalIP, ExternalIP, ExternalPort);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We only check if the External IP has changed. The Port will only change if the user
|
|
// modifies the registry and reboots the server or the user calls ReloadExtIPandPortConfiguration()
|
|
// via the Diagnose Mobile Tool. Hence we don't want to do any port checking here at all
|
|
IPAddress extIP = Yaulw.Net.IPHostHelper.GetExternalIP();
|
|
if (extIP != IPAddress.None)
|
|
{
|
|
bool bChangeOccured = Yaulw.Net.IPHostHelper.AreIPAddressesDifferent(ExternalIP, extIP, out extIP);
|
|
if (bChangeOccured && Yaulw.Net.IPHostHelper.IsValidIPv4Address(extIP, false))
|
|
{
|
|
ExternalIP = extIP;
|
|
LastExternalIPCheckRun = DateTime.Now;
|
|
AppLogInfo(String.Format("Updated External IP:{0} at {1}", extIP, LastExternalIPCheckRun));
|
|
|
|
// Update the McKesson's Mobile Gateway
|
|
RegistrationWrapper.UpdateServerIntExt(InternalIP, ExternalIP, ExternalPort);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Helper Functions
|
|
|
|
/// <summary>
|
|
/// Core logic to retrieve External IP and Port to use (should be called only when not on McKesson Network)
|
|
/// </summary>
|
|
/// <param name="ExternalIP">External Host IP</param>
|
|
/// <param name="bForceMultipleExternalChecks">set to true during initialization of the service, to check more than once, to make sure</param>
|
|
/// <param name="bCheckPort">if set to true, will perform Port Sockets Checks to make sure the port isn't used, if false won't do any port checking and just use what is given * should be set to true upon initialization *</param>
|
|
/// <param name="bPlutoCheckPort">if bCheckPort is set to true and we encounter an open port will perform a pluto check on it and see if it's hostguid matches ours, if so, decides we can use this port * RARE Reason this should ever be set to true *</param>
|
|
/// <param name="Port">Random Port that can be used</param>
|
|
/// <returns>true if all of our parameters were passed out and valid, false otherwise</returns>
|
|
private bool Retrieve_ExternalIP_ExternalPort(bool bForceMultipleExternalChecks, bool bCheckPort, bool bPlutoCheckPort, out IPAddress ExternalIP, out uint Port)
|
|
{
|
|
ExternalIP = IPAddress.None;
|
|
Port = 0;
|
|
|
|
// First Try, is to use the Port that is either random or saved in the registry,
|
|
// if that fails (which the odds are pretty low), we fall back to a second try,
|
|
// which will be random for sure. But just in case, let's do it a 3rd Random time,
|
|
// if bForceMultipleExternalChecks is set to true.
|
|
int nTrys = bForceMultipleExternalChecks ? 3 : 2;
|
|
|
|
// Try to Fetch the External IP, this should be easy, one would think
|
|
bool bFetchedExternalIP = false;
|
|
for (int n = 0; n < nTrys && !bFetchedExternalIP; n++)
|
|
{
|
|
// Try to get the external IP (should work most of the time, if we have internet)
|
|
// But once we have it, we shouldn't have to try 3 times
|
|
if (!bFetchedExternalIP)
|
|
{
|
|
ExternalIP = Yaulw.Net.IPHostHelper.GetExternalIP();
|
|
if (ExternalIP != IPAddress.None)
|
|
{
|
|
bFetchedExternalIP = true;
|
|
MSLSpecific.Logger.Info("External IP Fetched:{0}", ExternalIP.ToString());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Try to find a suitable Port, by trying to
|
|
// Get the Port Number (should always work), but we
|
|
// need to make sure that the port is free to use
|
|
bool bFoundFreePort = false;
|
|
for (int n = 0; n < nTrys && !bFoundFreePort && bFetchedExternalIP && bCheckPort; n++)
|
|
{
|
|
Port = (uint)GetRandomOrSavedPortNumber(n > 0);
|
|
MSLSpecific.Logger.Info("Checking Port:{0} to use", Port);
|
|
|
|
// Check to see if the port is open via external IP
|
|
bool bIsReachable = RegistrationWrapper.IsServerReachable(ExternalIP, Port);
|
|
if (bIsReachable)
|
|
{
|
|
if (bPlutoCheckPort)
|
|
{
|
|
string FoundHostGuid = RegistrationWrapper.RetrieveHostGUIDForServer(ExternalIP, Port);
|
|
if (!String.IsNullOrEmpty(FoundHostGuid))
|
|
{
|
|
// for whatever miracelous reason this is us (maybe thru registry)
|
|
if (String.Compare(FoundHostGuid, MSLSpecific.GetHostGUID().ToString(), true) == 0)
|
|
bFoundFreePort = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Noting Reachable, assume we can use it. (DiagnoseMobile Tool can later make changes, if needed)
|
|
bFoundFreePort = true;
|
|
}
|
|
|
|
if(bFoundFreePort)
|
|
MSLSpecific.Logger.Info("Port:{0} free to use", Port);
|
|
else
|
|
MSLSpecific.Logger.Info("Port:{0} not free to use", Port);
|
|
}
|
|
|
|
// Yeah, we have a very likely way that our configuration that should
|
|
// work 99% of the time without issue
|
|
bool bWeCanUseThisConfig = false;
|
|
if (bCheckPort && bFetchedExternalIP && bFoundFreePort)
|
|
bWeCanUseThisConfig = true;
|
|
else if (!bCheckPort && bFetchedExternalIP)
|
|
bWeCanUseThisConfig = true;
|
|
if (bWeCanUseThisConfig)
|
|
MSLSpecific.Logger.Info("Found valid External IP:{0} and Port:{1} to use", ExternalIP, Port);
|
|
else
|
|
MSLSpecific.Logger.Error("Haven't found a valid External IP:{0} and Port:{1} to use", ExternalIP, Port);
|
|
return bWeCanUseThisConfig;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieve a Random Port Number * Auto Generated and retrieved/stored in Registry *
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private int GetRandomOrSavedPortNumber(bool bForceRandomNumberCreation)
|
|
{
|
|
int SavedPort = 0;
|
|
if (!bForceRandomNumberCreation)
|
|
SavedPort = Yaulw.Registry.RegKey.GetKey<int>(Yaulw.Registry.HKEYRoot.LocalMachine, LOCAL_MACHINE_SUBKEY, LOCAL_MACHINE_EXTERNALPORT, 0);
|
|
|
|
if (SavedPort <= 0 || SavedPort < EXTERNAL_MINIMUM_PORT_NUMBER || SavedPort > EXTERNAL_MAXIMUM_PORT_NUMBER)
|
|
{
|
|
Random random = new Random((int)DateTime.Now.Ticks);
|
|
int nRand = random.Next(EXTERNAL_MINIMUM_PORT_NUMBER, EXTERNAL_MAXIMUM_PORT_NUMBER);
|
|
SavedPort = random.Next(EXTERNAL_MINIMUM_PORT_NUMBER, EXTERNAL_MAXIMUM_PORT_NUMBER);
|
|
Yaulw.Registry.RegKey.SetKey<int>(Yaulw.Registry.HKEYRoot.LocalMachine, LOCAL_MACHINE_SUBKEY, LOCAL_MACHINE_EXTERNALPORT, SavedPort);
|
|
}
|
|
return SavedPort;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private RO Channel Helpers
|
|
|
|
/// <summary>
|
|
/// Activate all RO Channels
|
|
/// </summary>
|
|
private void ActivateROChannelsIfTheyHaveNotBeenActivated()
|
|
{
|
|
ActivateROInternalChannelIfHasNotBeenActivated();
|
|
ActivateROExternalChannelIfHasNotBeenActivated();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deactivate all RO Channels
|
|
/// </summary>
|
|
private void DeactivateROChannelsIfTheyHaveBeenActivated(bool bNetworkOutage)
|
|
{
|
|
DeactivateROInternalChannelIfItHasBeenActivated(bNetworkOutage);
|
|
DeactivateROExternalChannelIfItHasBeenActivated(bNetworkOutage);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private RO Internal Channel Helper
|
|
|
|
/// <summary>
|
|
/// Activate Internal
|
|
/// </summary>
|
|
private void ActivateROInternalChannelIfHasNotBeenActivated()
|
|
{
|
|
if (!ROInternalHasBeenActivated)
|
|
{
|
|
SetInternalPort(INTERNAL_CHANNEL_PORT);
|
|
MSLTcpServerChannelInternal.Activate();
|
|
ROInternalHasBeenActivated = true;
|
|
AppLogInfo("MSLMobile Api Service Internal Channel Activated.");
|
|
NetworkOutageDetected = false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deactivate Internal
|
|
/// </summary>
|
|
private void DeactivateROInternalChannelIfItHasBeenActivated(bool bNetworkOutage)
|
|
{
|
|
if (ROInternalHasBeenActivated)
|
|
{
|
|
MSLTcpServerChannelInternal.Deactivate();
|
|
ROInternalHasBeenActivated = false;
|
|
AppLogInfo("MSLMobile Api Service Internal Channel Deactivated.");
|
|
NetworkOutageDetected = bNetworkOutage;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private RO External Channel Helper
|
|
|
|
/// <summary>
|
|
/// Activate External
|
|
/// </summary>
|
|
private void ActivateROExternalChannelIfHasNotBeenActivated()
|
|
{
|
|
if (!ROExternalHasBeenActivated && InitialExternalIPConfigurationIsSet)
|
|
{
|
|
SetExternalPort((int)ExternalPort);
|
|
MSLTcpServerChannelExternal.Activate();
|
|
ROExternalHasBeenActivated = true;
|
|
AppLogInfo(String.Format("MSLMobile Api Service External Channel on Port:{0} Activated.", ExternalPort));
|
|
NetworkOutageDetected = false;
|
|
StartThePortMapper();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deactivate External
|
|
/// </summary>
|
|
private void DeactivateROExternalChannelIfItHasBeenActivated(bool bNetworkOutage)
|
|
{
|
|
if (ROExternalHasBeenActivated)
|
|
{
|
|
StopThePortMapper();
|
|
MSLTcpServerChannelExternal.Deactivate();
|
|
ROExternalHasBeenActivated = false;
|
|
AppLogInfo(String.Format("MSLMobile Api Service External Channel on Port:{0} Deactivated.", ExternalPort));
|
|
NetworkOutageDetected = bNetworkOutage;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Port Mapper
|
|
|
|
/// <summary>
|
|
/// Launch Port Mapper if not already running
|
|
/// </summary>
|
|
private void StartThePortMapper()
|
|
{
|
|
#if DEBUG
|
|
if (!IsPortMapperRunning)
|
|
#else
|
|
if (!IsOnMcKessonNetwork && !IsPortMapperRunning)
|
|
#endif
|
|
{
|
|
try
|
|
{
|
|
AppLogInfo("Trying to start PortMapper");
|
|
Guid hostGuid = MSLSpecific.GetHostGUID();
|
|
|
|
// We have already done the port calculation,
|
|
// just pass in one port, that should be mapped * always *
|
|
//string minPort = ExternalPort.ToString();
|
|
//string maxPort = ExternalPort.ToString();
|
|
string externalPortToMap = ExternalPort.ToString();
|
|
string portMapDescription = "MSLMobile-" + ExternalPort.ToString();
|
|
|
|
// Call PORT FORWARDING Start method passing in the guid
|
|
BindPortMapperEvent();
|
|
//pMapper.Start(hostGuid.ToString(), minPort, maxPort, portMapDescription, LOCAL_MACHINE_SUBKEY, MSLSpecific.Logger);
|
|
pMapper.Start(ExternalIP, externalPortToMap, portMapDescription, MSLSpecific.Logger);
|
|
AppLogInfo("PortMapper Started.");
|
|
IsPortMapperRunning = true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
AppLogError(String.Format("Error when attempting to start PortMapper: {0}. PortMapper Stopped.", ex.Message));
|
|
UnBindPortMapperEvent();
|
|
pMapper.Stop();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stop Port Mapper if running
|
|
/// </summary>
|
|
private void StopThePortMapper()
|
|
{
|
|
// Stop the Port Mapper, if for any reason it started
|
|
AppLogInfo("PortMapper stopping.");
|
|
if (IsPortMapperRunning)
|
|
{
|
|
AppLogInfo("PortMapper stopped.");
|
|
UnBindPortMapperEvent();
|
|
pMapper.Stop();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Port Mapper Event Bindings
|
|
|
|
private void BindPortMapperEvent()
|
|
{
|
|
pMapper.DidFinishSearchForRouter += new PortMapper.PMDidFinishSearchForRouter(pMapper_DidFinishSearchForRouter);
|
|
pMapper.ExternalIPAddressDidChange += new PortMapper.PMExternalIPAddressDidChange(pMapper_ExternalIPAddressDidChange);
|
|
pMapper.CanAddPortMapping += new PortMapper.PMCanAddPortMapping(pMapper_CanAddPortMapping);
|
|
}
|
|
|
|
private void UnBindPortMapperEvent()
|
|
{
|
|
pMapper.DidFinishSearchForRouter -= new PortMapper.PMDidFinishSearchForRouter(pMapper_DidFinishSearchForRouter);
|
|
pMapper.ExternalIPAddressDidChange -= new PortMapper.PMExternalIPAddressDidChange(pMapper_ExternalIPAddressDidChange);
|
|
pMapper.CanAddPortMapping -= new PortMapper.PMCanAddPortMapping(pMapper_CanAddPortMapping);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Port Mapper Event Handlers
|
|
|
|
void pMapper_DidFinishSearchForRouter(PortMapper sender)
|
|
{
|
|
AppLogInfo("MSLMobile Api Add port mapping.");
|
|
pMapper.AddPortMapping();
|
|
}
|
|
|
|
void pMapper_ExternalIPAddressDidChange(PortMapper sender, IPAddress ip)
|
|
{
|
|
if (Yaulw.Net.IPHostHelper.IsValidIPv4Address(ip, false))
|
|
{
|
|
IPAddress extIP = IPAddress.None;
|
|
bool bChangeOccured = Yaulw.Net.IPHostHelper.AreIPAddressesDifferent(ExternalIP, ip, out extIP);
|
|
if (bChangeOccured)
|
|
{
|
|
// We probably should call McKesson's Mobile Gateway?
|
|
// IF the router actually sends us the external IP! then yes, otherwise, to be discussed
|
|
ExternalIP = extIP;
|
|
RegistrationWrapper.UpdateServerIntExt(InternalIP, ExternalIP, ExternalPort);
|
|
//Let's log this for now
|
|
MSLSpecific.Logger.Info("PMapper_ExternalIPAddress Changed Occured OldIP:{0} NewIP:{1}", ExternalIP, extIP);
|
|
}
|
|
}
|
|
}
|
|
|
|
void pMapper_CanAddPortMapping(PortMapper sender)
|
|
{
|
|
//This is fired after an external ip address change is detected and resolved
|
|
MSLSpecific.Logger.Info("PMapper_CanAddPortMapping Occured Port:{0} ExternalIP:{1}", sender.ExternalPortToMap, sender.ExternalIPAddress);
|
|
pMapper.AddPortMapping();
|
|
}
|
|
|
|
#endregion
|
|
|
|
// usefull for debugging Service in visual studio
|
|
#if DEBUG
|
|
internal void DebugStop() { OnStop(); }
|
|
#endif
|
|
|
|
#region OnStop
|
|
|
|
///// <summary>
|
|
///// Stop Now (let's try this)
|
|
///// </summary>
|
|
internal static void StopNow()
|
|
{
|
|
s_PlutoService.Stop();
|
|
}
|
|
|
|
/// <summary>
|
|
/// SCM OnStop() call-in
|
|
/// </summary>
|
|
protected override void OnStop()
|
|
{
|
|
// Stop the Connectivity timer
|
|
try
|
|
{
|
|
if (connectivityTimer != null)
|
|
{
|
|
connectivityTimer.Stop();
|
|
connectivityTimer = null;
|
|
AppLogInfo("End Connectivity Timer stopped.");
|
|
}
|
|
}
|
|
catch (Exception) { /* ignore */ }
|
|
|
|
// Deactivate RO Channels
|
|
try
|
|
{
|
|
DeactivateROChannelsIfTheyHaveBeenActivated(false);
|
|
AppLogInfo("End DeactivateROChannelsIfTheyHaveBeenActivated.");
|
|
}
|
|
catch (Exception) { /* ignore */ }
|
|
|
|
#if !DEBUG
|
|
// Disable Auto-Update Timer
|
|
try
|
|
{
|
|
AutoUpdate.StopAutoTimer_ServiceStopped();
|
|
AppLogInfo("End Auto-Update Timer stopped.");
|
|
}
|
|
catch (Exception) { /* ignore */ }
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Force close this process (to make sure we are dead),
|
|
/// we can't have open handles remaining open due to auto-update
|
|
/// However, doing this means an error gets thrown in the SCM when the user manually
|
|
/// stops the service!, so thie bool let's us know that it is us who is trying to do it.
|
|
/// Last Resort, Try to kill the process to make sure the whole service is down.
|
|
/// </summary>
|
|
internal static void KillCurrentProccess()
|
|
{
|
|
try
|
|
{
|
|
var p = System.Diagnostics.Process.GetCurrentProcess();
|
|
if (p != null)
|
|
p.Kill();
|
|
}
|
|
catch (Exception) { /* ignore */ }
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|