PageRenderTime 50ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Procon/Updater.cs

#
C# | 330 lines | 204 code | 74 blank | 52 comment | 27 complexity | 87b3d08ea35b09cd58c1d2b9d7a0477c MD5 | raw file
Possible License(s): GPL-2.0
  1. // Copyright 2011 Geoffrey 'Phogue' Green
  2. //
  3. // http://www.phogue.net
  4. //
  5. // This file is part of Procon 2.
  6. //
  7. // Procon 2 is free software: you can redistribute it and/or modify
  8. // it under the terms of the GNU General Public License as published by
  9. // the Free Software Foundation, either version 3 of the License, or
  10. // (at your option) any later version.
  11. //
  12. // Procon 2 is distributed in the hope that it will be useful,
  13. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. // GNU General Public License for more details.
  16. //
  17. // You should have received a copy of the GNU General Public License
  18. // along with Procon 2. If not, see <http://www.gnu.org/licenses/>.
  19. using System;
  20. using System.Linq;
  21. using System.Collections.Generic;
  22. using System.Windows.Forms;
  23. using System.IO;
  24. using System.Threading;
  25. using System.Reflection;
  26. using System.Diagnostics;
  27. using System.Xml;
  28. using System.Drawing;
  29. using Ionic.Zip;
  30. using System.Text;
  31. namespace Procon {
  32. public class Updater {
  33. // Note: This exe cannot import Procon.Core.dll, so some defines need to be placed here as well.
  34. public static readonly string PROCON_EXE = "Procon.exe";
  35. public static readonly string PROCON_PDB = "Procon.pdb";
  36. public static readonly string PROCON_CORE_DLL = "Procon.Core.dll";
  37. public static readonly string PROCON_UI_EXE = "Procon.UI.exe";
  38. public static readonly string PROCON_CONSOLE_EXE = "Procon.Console.exe";
  39. public static readonly string UPDATE_LOG = "update.log";
  40. public static readonly string PROCON_DIRECTORY = AppDomain.CurrentDomain.BaseDirectory;
  41. public static readonly string PROCON_DIRECTORY_PROCON_EXE = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Updater.PROCON_EXE);
  42. public static readonly string PROCON_DIRECTORY_PROCON_PDB = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Updater.PROCON_PDB);
  43. public static readonly string PROCON_DIRECTORY_PROCON_UI_EXE = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Updater.PROCON_UI_EXE);
  44. public static readonly string PROCON_DIRECTORY_PROCON_CONSOLE_EXE = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Updater.PROCON_CONSOLE_EXE);
  45. public static readonly string PROCON_DIRECTORY_PROCON_CORE_DLL = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Updater.PROCON_CORE_DLL);
  46. public static readonly string CONFIGS_DIRECTORY = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configs");
  47. public static readonly string CONFIGS_BACKUP_DIRECTORY = Path.Combine(Updater.CONFIGS_DIRECTORY, "Backups");
  48. public static readonly string UPDATES_DIRECTORY = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Updates");
  49. public static readonly string UPDATES_DIRECTORY_PROCON_CORE_DLL = Path.Combine(UPDATES_DIRECTORY, Updater.PROCON_CORE_DLL);
  50. private TextWriter Writer { get; set; }
  51. public Updater() {
  52. this.Writer = new StreamWriter(Updater.UPDATE_LOG);
  53. }
  54. public Updater Execute() {
  55. this.Writer.WriteLine("Updater initialized");
  56. // #1
  57. if (this.CheckUpdatesDirectory() == true) {
  58. // #2
  59. this.CreateRequiredDirectories();
  60. // #3
  61. this.CreateConfigBackup();
  62. // #4
  63. this.TerminateProcess();
  64. // #5
  65. this.MoveDirectoryContents(Updater.UPDATES_DIRECTORY);
  66. // #6 Remove the updates directory
  67. this.DeleteDirectory(Updater.UPDATES_DIRECTORY);
  68. }
  69. return this;
  70. }
  71. public Updater Shutdown() {
  72. this.Writer.WriteLine("Closing Updater");
  73. this.Writer.Flush();
  74. this.Writer.Close();
  75. return this;
  76. }
  77. #region Step #1 - Checking if update is required
  78. /// <summary>
  79. /// Checks if the updates directory exists, logging the result
  80. /// </summary>
  81. /// <returns>true - dir exists, false otherwise</returns>
  82. protected bool CheckUpdatesDirectory() {
  83. bool updatesDirectoryExists = false;
  84. if ((updatesDirectoryExists = Directory.Exists(Updater.UPDATES_DIRECTORY)) == true) {
  85. this.Writer.WriteLine("Updates directory exists, beginning update");
  86. }
  87. else {
  88. this.Writer.WriteLine("Updates directory does not exists");
  89. }
  90. return updatesDirectoryExists;
  91. }
  92. #endregion
  93. #region Step #2 - Creating required directories
  94. /// <summary>
  95. /// Loops through all required directories the update needs
  96. ///
  97. /// This is so the update can forego checks and error logging later.
  98. /// </summary>
  99. protected void CreateRequiredDirectories() {
  100. List<string> directories = new List<string>() {
  101. Updater.CONFIGS_DIRECTORY,
  102. Updater.CONFIGS_BACKUP_DIRECTORY
  103. };
  104. foreach (string directory in directories) {
  105. this.CreateDirectory(directory);
  106. }
  107. }
  108. #endregion
  109. #region Step #3 - Backing up configs (Procon update only, not normal package install)
  110. protected void CreateConfigBackup() {
  111. try {
  112. if (File.Exists(Updater.PROCON_DIRECTORY_PROCON_CORE_DLL) == true && File.Exists(Updater.UPDATES_DIRECTORY_PROCON_CORE_DLL) == true) {
  113. this.Writer.WriteLine("Creating config backup");
  114. FileVersionInfo currentFv = FileVersionInfo.GetVersionInfo(Updater.PROCON_DIRECTORY_PROCON_CORE_DLL);
  115. FileVersionInfo updatedFv = FileVersionInfo.GetVersionInfo(Updater.UPDATES_DIRECTORY_PROCON_CORE_DLL);
  116. string zipFileName = String.Format("{0}_to_{1}_backup.zip", currentFv.FileVersion, updatedFv.FileVersion);
  117. using (ZipFile zip = new ZipFile()) {
  118. DirectoryInfo configsDirectory = new DirectoryInfo(Updater.CONFIGS_DIRECTORY);
  119. foreach (FileInfo config in configsDirectory.GetFiles("*.cfg")) {
  120. this.Writer.WriteLine("\tAdding {0} to archive", config.FullName);
  121. zip.AddFile(config.FullName);
  122. }
  123. foreach (DirectoryInfo directory in configsDirectory.GetDirectories()) {
  124. if (String.Compare(directory.FullName, Updater.CONFIGS_BACKUP_DIRECTORY, true) != 0) {
  125. this.Writer.WriteLine("\tAdding {0} to archive", directory.FullName);
  126. zip.AddDirectory(directory.FullName);
  127. // Add files from directory?
  128. }
  129. }
  130. this.Writer.WriteLine("\tSaving archive to {0}", Path.Combine(Updater.CONFIGS_BACKUP_DIRECTORY, zipFileName));
  131. zip.Save(Path.Combine(Updater.CONFIGS_BACKUP_DIRECTORY, zipFileName));
  132. }
  133. }
  134. }
  135. catch (Exception e) {
  136. this.Writer.WriteLine("\tERROR: {0}", e.Message);
  137. }
  138. }
  139. #endregion
  140. #region Step #4 - Closing open instances of any executables in "/Updates" and "/"
  141. /// <summary>
  142. /// Terminates all executables running in "/" that will be replaced by executables in the updates directory.
  143. /// </summary>
  144. protected void TerminateProcess() {
  145. List<string> localExecutables = this.DiscoverExecutables(Updater.PROCON_DIRECTORY);
  146. List<string> updatesExecutables = this.DiscoverExecutables(Updater.UPDATES_DIRECTORY);
  147. // This is so the updater does not attempt suicide.
  148. localExecutables.Remove(Updater.PROCON_DIRECTORY_PROCON_EXE);
  149. // Close Procon.exe
  150. // Close Procon.UI.exe
  151. // Close Procon.Console.exe
  152. // Or any other executables we create later..
  153. foreach (string exe in localExecutables.Where(x => updatesExecutables.Select(y => Path.GetFileName(y)).Contains(Path.GetFileName(x)))) {
  154. foreach (Process process in Process.GetProcesses()) {
  155. try {
  156. if (string.Compare(exe, Path.GetFullPath(process.MainModule.FileName), true) == 0) {
  157. try {
  158. this.Writer.WriteLine("Killing process {1} at {0}", process.Id, exe);
  159. process.Kill();
  160. }
  161. catch (Exception e) {
  162. this.Writer.WriteLine("\tERROR: {0}", e.Message);
  163. }
  164. }
  165. }
  166. catch (Exception) { }
  167. }
  168. }
  169. }
  170. #endregion
  171. #region Step #5 - Moving and deleting the files from updates directory
  172. protected void MoveDirectoryContents(string path) {
  173. this.Writer.WriteLine("Moving contents of directory {0}", path);
  174. if (Directory.Exists(path) == true) {
  175. foreach (string file in Directory.GetFiles(path)) {
  176. if (String.Compare(Path.GetFileName(file), Updater.PROCON_EXE) == 0 || String.Compare(Path.GetFileName(file), Updater.PROCON_PDB) == 0 || String.Compare(Path.GetFileName(file), "Ionic.Zip.Reduced.dll") == 0) {
  177. this.Writer.WriteLine("Ignoring file {0} (Updater file)", file);
  178. this.DeleteFile(file);
  179. }
  180. else {
  181. string destinationFile = file.Remove(file.LastIndexOf("Updates" + Path.DirectorySeparatorChar), ("Updates" + Path.DirectorySeparatorChar).Length);
  182. this.DeleteFile(destinationFile);
  183. this.MoveFile(file, destinationFile);
  184. }
  185. }
  186. foreach (string directory in Directory.GetDirectories(path)) {
  187. this.MoveDirectoryContents(directory);
  188. this.DeleteDirectory(directory);
  189. }
  190. }
  191. }
  192. #endregion
  193. #region File I/O with logging
  194. /// <summary>
  195. /// Creates a directory, logging the progress and errors (if any)
  196. /// </summary>
  197. /// <param name="path">The path to create</param>
  198. protected void CreateDirectory(string path) {
  199. if (Directory.Exists(path) == false) {
  200. this.Writer.WriteLine("Creating directory: {0}", path);
  201. try {
  202. Directory.CreateDirectory(path);
  203. }
  204. catch (Exception e) {
  205. this.Writer.WriteLine("\tERROR: {0}", e.Message);
  206. }
  207. }
  208. }
  209. /// <summary>
  210. /// Finds all .exe files at a given path
  211. /// </summary>
  212. /// <param name="path">The path to search for .exe's</param>
  213. /// <returns>A list of fullname exe files</returns>
  214. protected List<string> DiscoverExecutables(string path) {
  215. List<string> executables = new List<string>();
  216. try {
  217. if (Directory.Exists(path) == true) {
  218. executables = new List<string>(Directory.GetFiles(path, "*.exe"));
  219. }
  220. }
  221. catch (Exception e) {
  222. this.Writer.WriteLine("\tERROR: {0}", e.Message);
  223. }
  224. return executables;
  225. }
  226. protected void DeleteDirectory(string path) {
  227. this.Writer.WriteLine("Deleting directory {0}", path);
  228. try {
  229. Directory.Delete(path);
  230. }
  231. catch (Exception e) {
  232. this.Writer.WriteLine("\tERROR: {0}", e.Message);
  233. }
  234. }
  235. protected void DeleteFile(string file) {
  236. this.Writer.WriteLine("Deleting file {0}", file);
  237. try {
  238. File.Delete(file);
  239. }
  240. catch (Exception e) {
  241. this.Writer.WriteLine("\tERROR: {0}", e.Message);
  242. }
  243. }
  244. protected void MoveFile(string file, string destination) {
  245. this.Writer.WriteLine("Moving {0} to {1}", file, destination);
  246. try {
  247. this.CreateDirectory(Path.GetDirectoryName(destination));
  248. File.Move(file, destination);
  249. }
  250. catch (Exception e) {
  251. this.Writer.WriteLine("\tERROR: {0}", e.Message);
  252. }
  253. }
  254. #endregion
  255. }
  256. }