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 /// /// Check every 2.5 hours /// private const int AUTO_CHECK_INTERVAL_IN_MINUTES = 150; /// /// Keep track when last update occured /// private static ISReadWrite s_ISReadWrite = new ISReadWrite("update"); /// /// Auto-Update Thread /// private static SingleThreadTimer timer = null; /// /// Online Version of the Service /// private static Version OnlineVersion = null; /// /// Retrieve the LocalFileNameNPath for SetupMini File (TEMP FOLDER) /// /// 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 /// /// Try to update the Service, if needed /// 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); } } /// /// Main Entry Point - Service Calls into here upon Start-Up /// 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(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); } /// /// Main Entry Point - Service Calls into here upon Stop /// internal static void StopAutoTimer_ServiceStopped() { if (timer != null) { timer.Stop(); timer = null; } } /// /// Fetch the Latest Version that is Online /// /// valid version object or null if error occured 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; } /// /// Quick Check to see if the Local Service is out of date /// /// 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 /// /// 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 /// /// /// 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); } } /// /// Get the LastUpdated() DT or DateTime.Min if none /// /// 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; } /// /// Set the LastUpdated() DT /// /// private static void SetLastUpdated(DateTime dt) { if (dt != null) { s_ISReadWrite.WriteToIS(dt.ToString()); } } /// /// Add A Delay variable to the Update Thread /// private class UpdateThreadParams { public int nDelay = 0; } /// /// Check for Updates and update the service if needed /// 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)); } } } } /// /// Launch the Update within a Thread() with specified Delay, /// Thread only launched if Delay > 0, otherwise just calls CheckForUpdatesAndUpdate() directly /// /// 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 } }