317 lines
13 KiB
C#
317 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Yaulw.File;
|
|
using System.IO;
|
|
using Yaulw.Thread;
|
|
using System.Diagnostics;
|
|
|
|
namespace PlutoServer.MSL
|
|
{
|
|
internal static class AutoUpdate
|
|
{
|
|
#region Private Statics
|
|
|
|
/// <summary>
|
|
/// Check every 2.5 hours
|
|
/// </summary>
|
|
private const int AUTO_CHECK_INTERVAL_IN_MINUTES = 150;
|
|
|
|
/// <summary>
|
|
/// Keep track when last update occured
|
|
/// </summary>
|
|
private static ISReadWrite s_ISReadWrite = new ISReadWrite("update");
|
|
|
|
/// <summary>
|
|
/// Auto-Update Thread
|
|
/// </summary>
|
|
private static SingleThreadTimer timer = null;
|
|
|
|
/// <summary>
|
|
/// Online Version of the Service
|
|
/// </summary>
|
|
private static Version OnlineVersion = null;
|
|
|
|
/// <summary>
|
|
/// Retrieve the LocalFileNameNPath for SetupMini File (TEMP FOLDER)
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private static string MiniSetupFileNameNPath
|
|
{
|
|
get
|
|
{
|
|
string LocalFileNameNPath = Yaulw.Tools.PathNaming.PathEndsWithSlash(Path.GetTempPath()) + "SetupMini.exe";
|
|
return LocalFileNameNPath;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Construction
|
|
|
|
static AutoUpdate()
|
|
{
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region internal Methods
|
|
|
|
/// <summary>
|
|
/// Try to update the Service, if needed
|
|
/// </summary>
|
|
internal static void TryToUpdateIfNeeded(int nDelay, bool bCheckDTStamp)
|
|
{
|
|
// Default Auto Update with 15 sec delay
|
|
if (nDelay < 0)
|
|
nDelay = 15;
|
|
|
|
PlutoService.AppLogInfo(String.Format("TryToUpdateIfNeeded called at {0} with delay {1} and CheckDT {2}", DateTime.Now.ToLongTimeString(), nDelay.ToString(), bCheckDTStamp));
|
|
if (bCheckDTStamp)
|
|
{
|
|
DateTime dtLastUpdated = GetLastUpdated();
|
|
TimeSpan ts = DateTime.Now - dtLastUpdated;
|
|
if (ts.TotalDays >= 1.0)
|
|
{
|
|
PlutoService.AppLogInfo(String.Format("LaunchUpdateThread called with Delay {0}", nDelay));
|
|
LaunchUpdateThread(nDelay);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PlutoService.AppLogInfo(String.Format("LaunchUpdateThread called with Delay {0}", nDelay));
|
|
LaunchUpdateThread(nDelay);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Main Entry Point - Service Calls into here upon Start-Up
|
|
/// </summary>
|
|
internal static void StartAutoTimer_ServiceStarted()
|
|
{
|
|
// Important Sanity Clean-up, this way we know that the service
|
|
// always downloads the latest setup and runs the latest setup
|
|
if (File.Exists(MiniSetupFileNameNPath))
|
|
File.Delete(MiniSetupFileNameNPath);
|
|
|
|
// Right at Service Startup Check if we have tried to update
|
|
// within the last day, if we have not, try to update.
|
|
// NOTE: Don't do this if we just got installed for the very first time
|
|
// the service seems to hang when doing that during the install of
|
|
// lytec/medisoft. (not known what the root cause is), but let's
|
|
// just avoid it alltogether. (We could potentially add a timeout here
|
|
// and just check way way later), not sure what the best course of action
|
|
// is at this point.
|
|
DateTime dt = Yaulw.Registry.RegKey.GetKey<DateTime>(Yaulw.Registry.HKEYRoot.LocalMachine, PlutoService.LOCAL_MACHINE_SUBKEY, PlutoService.LOCAL_MACHINE_INITIALSETUPDT, DateTime.MinValue);
|
|
TimeSpan ts = DateTime.Now - dt;
|
|
|
|
// If the ts is < 10 Minutes we must have just gotten installed, hence, we shouldn't
|
|
// try to update the service and just let it start (let the autoUpdate Handler below or Diagnose Mobile handle the initial updating).
|
|
if (ts.TotalMinutes > 10)
|
|
TryToUpdateIfNeeded(0, false);
|
|
|
|
// Additionally, we want to start a Timer Thread,
|
|
// that Randomly checks for new updates, once every few hours, but only
|
|
// in specific circumstances will it actually perform the update. ( check every 2.5 hours )
|
|
timer = new SingleThreadTimer(AutoUpdateHandler, (uint)TimeSpan.FromMinutes(AUTO_CHECK_INTERVAL_IN_MINUTES).TotalMilliseconds, true);
|
|
// * for debugging *:
|
|
//SingleThreadTimer timer = new SingleThreadTimer(STElapsedEventHandler, (uint)TimeSpan.FromSeconds(30).TotalMilliseconds, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Main Entry Point - Service Calls into here upon Stop
|
|
/// </summary>
|
|
internal static void StopAutoTimer_ServiceStopped()
|
|
{
|
|
if (timer != null)
|
|
{
|
|
timer.Stop();
|
|
timer = null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fetch the Latest Version that is Online
|
|
/// </summary>
|
|
/// <returns>valid version object or null if error occured</returns>
|
|
internal static Version FetchOnlineVerion()
|
|
{
|
|
string fetchVersion = Yaulw.Net.WCHelper.ScreenScrapeFromURL(Configuration.CHECK_VERSION_URL);
|
|
if (!String.IsNullOrEmpty(fetchVersion))
|
|
{
|
|
fetchVersion = fetchVersion.Replace("\n", "");
|
|
fetchVersion = fetchVersion.Replace("\r", "");
|
|
fetchVersion = fetchVersion.Trim();
|
|
|
|
try
|
|
{
|
|
Version version = new Version(fetchVersion);
|
|
return version;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
PlutoService.AppLogError(String.Format("The url:{0} to check the Mobile version could not be parsed: {1}", Configuration.CHECK_VERSION_URL, e.Message));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PlutoService.AppLogError(String.Format("The url:{0} to check the Mobile version could not be accessed", Configuration.CHECK_VERSION_URL));
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Quick Check to see if the Local Service is out of date
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
internal static bool IsLocalServiceVersionOutOfDate()
|
|
{
|
|
OnlineVersion = FetchOnlineVerion();
|
|
Version LocalVersion = Yaulw.Assembly.AssemblyW.GetAssemblyVersion(Yaulw.Assembly.AssemblyW.AssemblyST.Entry);
|
|
if (OnlineVersion != null && LocalVersion != null)
|
|
{
|
|
if (OnlineVersion.Major == LocalVersion.Major && OnlineVersion > LocalVersion)
|
|
{
|
|
PlutoService.AppLogInfo("Newer Mobile Api Service Found Online");
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
PlutoService.AppLogInfo("Mobile Api Service is already up to date");
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Methods / Helpers
|
|
|
|
/// <summary>
|
|
/// Called Randomly every 2.5 Hours, will call Update only if called in the hours of 2 am - 6am AND
|
|
/// the TimeSpan of the last update has been over a day
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private static void AutoUpdateHandler(object sender, Yaulw.Thread.SingleThreadTimer.STElapsedEventArgs e)
|
|
{
|
|
DateTime dt = e.SignalTime.ToLocalTime();
|
|
TimeSpan ts = dt.TimeOfDay;
|
|
bool bIsBetween2amAnd6am = (ts.TotalHours >= 2.0) && (ts.TotalHours <= 6.0);
|
|
|
|
// Only Check between the hours of 2am to 6am
|
|
// And if we haven't updated today yet
|
|
if (bIsBetween2amAnd6am)
|
|
{
|
|
// Launch update without a thread, already inside a thread
|
|
TryToUpdateIfNeeded(0, true);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the LastUpdated() DT or DateTime.Min if none
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private static DateTime GetLastUpdated()
|
|
{
|
|
string s = s_ISReadWrite.ReadFromIS();
|
|
DateTime dt = DateTime.MinValue;
|
|
if (!String.IsNullOrEmpty(s) && DateTime.TryParse(s, out dt))
|
|
return dt;
|
|
else
|
|
return DateTime.MinValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the LastUpdated() DT
|
|
/// </summary>
|
|
/// <param name="dt"></param>
|
|
private static void SetLastUpdated(DateTime dt)
|
|
{
|
|
if (dt != null)
|
|
{
|
|
s_ISReadWrite.WriteToIS(dt.ToString());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add A Delay variable to the Update Thread
|
|
/// </summary>
|
|
private class UpdateThreadParams
|
|
{
|
|
public int nDelay = 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check for Updates and update the service if needed
|
|
/// </summary>
|
|
private static void CheckForUpdatesAndUpdate(object o)
|
|
{
|
|
int nSleepInMiliseconds = 0;
|
|
if (o != null)
|
|
{
|
|
UpdateThreadParams p = (UpdateThreadParams)o;
|
|
nSleepInMiliseconds = p.nDelay;
|
|
}
|
|
|
|
PlutoService.AppLogInfo(string.Format("CheckForUpdatesAndUpdate called with nDelay {0}", nSleepInMiliseconds));
|
|
if(nSleepInMiliseconds > 0)
|
|
System.Threading.Thread.Sleep(nSleepInMiliseconds);
|
|
|
|
// Update this service, if Online Version is bigger than Local Version
|
|
if (IsLocalServiceVersionOutOfDate())
|
|
{
|
|
if (Yaulw.Net.WCHelper.DownloadFileFromURL(Configuration.DOWNLOAD_MINISETUP_URL, MiniSetupFileNameNPath, true))
|
|
{
|
|
// * Important * sanity check. Kumar/Jeff found this. State can happen that we can reach the
|
|
// version url but don't actually download the file which then causes the service to crash when
|
|
// calling PSetupSpwan (might as well also make sure that the new file is equal to the file we expect)
|
|
if (File.Exists(MiniSetupFileNameNPath))
|
|
{
|
|
// The Setup will also try to auto-close this service, so first update the dt then spawn the setup.exe
|
|
SetLastUpdated(DateTime.Now);
|
|
PlutoService.AppLogInfo(String.Format("Updating to newer Mobile Api Service '{0}' via '{1}'", OnlineVersion.ToString(), MiniSetupFileNameNPath));
|
|
|
|
//No longer needed - ServiceStartVersionSent (handled in PlutoService.MSL.cs) will take care of this for us
|
|
//RegistrationWrapper.ServerHasBeenUpdated(OnlineVersion.ToString());
|
|
|
|
// The auto-update is also forcing a close (however we will also try to committ suicide)
|
|
Yaulw.Installer.Common.PSetupSpwan(MiniSetupFileNameNPath, "", false);
|
|
|
|
// * Double Knock-out * Force the Service to close here a kill itself here as well, don't know if this even get's called
|
|
// but might as well make sure and double punch
|
|
//PlutoService.StopNow();
|
|
//PlutoService.KillCurrentProccess();
|
|
}
|
|
else
|
|
{
|
|
PlutoService.AppLogInfo(String.Format("Failed to download the file to update the service, please download and install manually: '{0}'", Configuration.DOWNLOAD_MINISETUP_URL));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Launch the Update within a Thread() with specified Delay,
|
|
/// Thread only launched if Delay > 0, otherwise just calls CheckForUpdatesAndUpdate() directly
|
|
/// </summary>
|
|
/// <param name="nDelay"></param>
|
|
internal static void LaunchUpdateThread(int nDelayInSeconds)
|
|
{
|
|
if (nDelayInSeconds > 0)
|
|
{
|
|
UpdateThreadParams p = new UpdateThreadParams() { nDelay = (int)TimeSpan.FromSeconds(nDelayInSeconds).TotalMilliseconds };
|
|
Yaulw.Thread.TStarter.StartParameterizedThread(CheckForUpdatesAndUpdate, p, "CheckForUpdate_Thread", System.Threading.ApartmentState.MTA, true, System.Threading.ThreadPriority.Normal);
|
|
}
|
|
else
|
|
{
|
|
CheckForUpdatesAndUpdate(null);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|