Program.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. using MediaBrowser.Model.Logging;
  2. using MediaBrowser.Server.Implementations;
  3. using Microsoft.Win32;
  4. using System;
  5. using System.Diagnostics;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Runtime.InteropServices;
  9. using System.Text;
  10. using System.Threading;
  11. using System.Threading.Tasks;
  12. using Emby.Common.Implementations.EnvironmentInfo;
  13. using Emby.Common.Implementations.IO;
  14. using Emby.Common.Implementations.Logging;
  15. using Emby.Common.Implementations.Networking;
  16. using Emby.Drawing;
  17. using Emby.Server.Core;
  18. using Emby.Server.Core.Browser;
  19. using Emby.Server.Implementations.IO;
  20. using MediaBrowser.Common.Net;
  21. using Emby.Server.IO;
  22. namespace Emby.Server
  23. {
  24. public class Program
  25. {
  26. private static ApplicationHost _appHost;
  27. private static ILogger _logger;
  28. private static bool _isRunningAsService = false;
  29. private static bool _canRestartService = false;
  30. private static bool _appHostDisposed;
  31. [DllImport("kernel32.dll", SetLastError = true)]
  32. static extern bool SetDllDirectory(string lpPathName);
  33. /// <summary>
  34. /// Defines the entry point of the application.
  35. /// </summary>
  36. public static void Main(string[] args)
  37. {
  38. var options = new StartupOptions();
  39. _isRunningAsService = options.ContainsOption("-service");
  40. if (_isRunningAsService)
  41. {
  42. //_canRestartService = CanRestartWindowsService();
  43. }
  44. var currentProcess = Process.GetCurrentProcess();
  45. var applicationPath = currentProcess.MainModule.FileName;
  46. //var architecturePath = Path.Combine(Path.GetDirectoryName(applicationPath), Environment.Is64BitProcess ? "x64" : "x86");
  47. //Wand.SetMagickCoderModulePath(architecturePath);
  48. //var success = SetDllDirectory(architecturePath);
  49. var appPaths = CreateApplicationPaths(applicationPath, _isRunningAsService);
  50. var logManager = new NlogManager(appPaths.LogDirectoryPath, "server");
  51. logManager.ReloadLogger(LogSeverity.Debug);
  52. logManager.AddConsoleOutput();
  53. var logger = _logger = logManager.GetLogger("Main");
  54. ApplicationHost.LogEnvironmentInfo(logger, appPaths, true);
  55. AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
  56. if (IsAlreadyRunning(applicationPath, currentProcess))
  57. {
  58. logger.Info("Shutting down because another instance of Emby Server is already running.");
  59. return;
  60. }
  61. if (PerformUpdateIfNeeded(appPaths, logger))
  62. {
  63. logger.Info("Exiting to perform application update.");
  64. return;
  65. }
  66. try
  67. {
  68. RunApplication(appPaths, logManager, _isRunningAsService, options);
  69. }
  70. finally
  71. {
  72. OnServiceShutdown();
  73. }
  74. }
  75. /// <summary>
  76. /// Determines whether [is already running] [the specified current process].
  77. /// </summary>
  78. /// <param name="applicationPath">The application path.</param>
  79. /// <param name="currentProcess">The current process.</param>
  80. /// <returns><c>true</c> if [is already running] [the specified current process]; otherwise, <c>false</c>.</returns>
  81. private static bool IsAlreadyRunning(string applicationPath, Process currentProcess)
  82. {
  83. var duplicate = Process.GetProcesses().FirstOrDefault(i =>
  84. {
  85. try
  86. {
  87. if (currentProcess.Id == i.Id)
  88. {
  89. return false;
  90. }
  91. }
  92. catch (Exception)
  93. {
  94. return false;
  95. }
  96. try
  97. {
  98. //_logger.Info("Module: {0}", i.MainModule.FileName);
  99. if (string.Equals(applicationPath, i.MainModule.FileName, StringComparison.OrdinalIgnoreCase))
  100. {
  101. return true;
  102. }
  103. return false;
  104. }
  105. catch (Exception)
  106. {
  107. return false;
  108. }
  109. });
  110. if (duplicate != null)
  111. {
  112. _logger.Info("Found a duplicate process. Giving it time to exit.");
  113. if (!duplicate.WaitForExit(30000))
  114. {
  115. _logger.Info("The duplicate process did not exit.");
  116. return true;
  117. }
  118. }
  119. if (!_isRunningAsService)
  120. {
  121. return false;
  122. }
  123. return false;
  124. }
  125. /// <summary>
  126. /// Creates the application paths.
  127. /// </summary>
  128. /// <param name="applicationPath">The application path.</param>
  129. /// <param name="runAsService">if set to <c>true</c> [run as service].</param>
  130. /// <returns>ServerApplicationPaths.</returns>
  131. private static ServerApplicationPaths CreateApplicationPaths(string applicationPath, bool runAsService)
  132. {
  133. var resourcesPath = Path.GetDirectoryName(applicationPath);
  134. if (runAsService)
  135. {
  136. var systemPath = Path.GetDirectoryName(applicationPath);
  137. var programDataPath = Path.GetDirectoryName(systemPath);
  138. return new ServerApplicationPaths(programDataPath, applicationPath, resourcesPath);
  139. }
  140. return new ServerApplicationPaths(ApplicationPathHelper.GetProgramDataPath(applicationPath), applicationPath, resourcesPath);
  141. }
  142. /// <summary>
  143. /// Gets a value indicating whether this instance can self restart.
  144. /// </summary>
  145. /// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value>
  146. public static bool CanSelfRestart
  147. {
  148. get
  149. {
  150. if (_isRunningAsService)
  151. {
  152. return _canRestartService;
  153. }
  154. else
  155. {
  156. return true;
  157. }
  158. }
  159. }
  160. /// <summary>
  161. /// Gets a value indicating whether this instance can self update.
  162. /// </summary>
  163. /// <value><c>true</c> if this instance can self update; otherwise, <c>false</c>.</value>
  164. public static bool CanSelfUpdate
  165. {
  166. get
  167. {
  168. if (_isRunningAsService)
  169. {
  170. return _canRestartService;
  171. }
  172. else
  173. {
  174. return true;
  175. }
  176. }
  177. }
  178. private static readonly TaskCompletionSource<bool> ApplicationTaskCompletionSource = new TaskCompletionSource<bool>();
  179. /// <summary>
  180. /// Runs the application.
  181. /// </summary>
  182. /// <param name="appPaths">The app paths.</param>
  183. /// <param name="logManager">The log manager.</param>
  184. /// <param name="runService">if set to <c>true</c> [run service].</param>
  185. /// <param name="options">The options.</param>
  186. private static void RunApplication(ServerApplicationPaths appPaths, ILogManager logManager, bool runService, StartupOptions options)
  187. {
  188. var fileSystem = new ManagedFileSystem(logManager.GetLogger("FileSystem"), true, true, true);
  189. fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
  190. var imageEncoder = new NullImageEncoder();
  191. _appHost = new CoreAppHost(appPaths,
  192. logManager,
  193. options,
  194. fileSystem,
  195. new PowerManagement(),
  196. "emby.windows.zip",
  197. new EnvironmentInfo(),
  198. imageEncoder,
  199. new CoreSystemEvents(),
  200. new MemoryStreamFactory(),
  201. new NetworkManager(logManager.GetLogger("NetworkManager")),
  202. GenerateCertificate,
  203. () => "EmbyUser");
  204. var initProgress = new Progress<double>();
  205. if (!runService)
  206. {
  207. // Not crazy about this but it's the only way to suppress ffmpeg crash dialog boxes
  208. SetErrorMode(ErrorModes.SEM_FAILCRITICALERRORS | ErrorModes.SEM_NOALIGNMENTFAULTEXCEPT |
  209. ErrorModes.SEM_NOGPFAULTERRORBOX | ErrorModes.SEM_NOOPENFILEERRORBOX);
  210. }
  211. var task = _appHost.Init(initProgress);
  212. Task.WaitAll(task);
  213. task = task.ContinueWith(new Action<Task>(a => _appHost.RunStartupTasks()), TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.AttachedToParent);
  214. if (runService)
  215. {
  216. StartService(logManager);
  217. }
  218. else
  219. {
  220. Task.WaitAll(task);
  221. task = ApplicationTaskCompletionSource.Task;
  222. Task.WaitAll(task);
  223. }
  224. }
  225. private static void GenerateCertificate(string certPath, string certHost)
  226. {
  227. //CertificateGenerator.CreateSelfSignCertificatePfx(certPath, certHost, _logger);
  228. }
  229. /// <summary>
  230. /// Starts the service.
  231. /// </summary>
  232. private static void StartService(ILogManager logManager)
  233. {
  234. }
  235. /// <summary>
  236. /// Handles the Disposed event of the service control.
  237. /// </summary>
  238. /// <param name="sender">The source of the event.</param>
  239. /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
  240. static void service_Disposed(object sender, EventArgs e)
  241. {
  242. ApplicationTaskCompletionSource.SetResult(true);
  243. OnServiceShutdown();
  244. }
  245. private static void OnServiceShutdown()
  246. {
  247. _logger.Info("Shutting down");
  248. DisposeAppHost();
  249. }
  250. /// <summary>
  251. /// Handles the UnhandledException event of the CurrentDomain control.
  252. /// </summary>
  253. /// <param name="sender">The source of the event.</param>
  254. /// <param name="e">The <see cref="UnhandledExceptionEventArgs"/> instance containing the event data.</param>
  255. static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
  256. {
  257. var exception = (Exception)e.ExceptionObject;
  258. new UnhandledExceptionWriter(_appHost.ServerConfigurationManager.ApplicationPaths, _logger, _appHost.LogManager).Log(exception);
  259. if (!_isRunningAsService)
  260. {
  261. ShowMessageBox("Unhandled exception: " + exception.Message);
  262. }
  263. if (!Debugger.IsAttached)
  264. {
  265. Environment.Exit(Marshal.GetHRForException(exception));
  266. }
  267. }
  268. /// <summary>
  269. /// Performs the update if needed.
  270. /// </summary>
  271. /// <param name="appPaths">The app paths.</param>
  272. /// <param name="logger">The logger.</param>
  273. /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
  274. private static bool PerformUpdateIfNeeded(ServerApplicationPaths appPaths, ILogger logger)
  275. {
  276. // Look for the existence of an update archive
  277. var updateArchive = Path.Combine(appPaths.TempUpdatePath, "MBServer" + ".zip");
  278. if (File.Exists(updateArchive))
  279. {
  280. logger.Info("An update is available from {0}", updateArchive);
  281. // Update is there - execute update
  282. try
  283. {
  284. //var serviceName = _isRunningAsService ? BackgroundService.GetExistingServiceName() : string.Empty;
  285. //new ApplicationUpdater().UpdateApplication(appPaths, updateArchive, logger, serviceName);
  286. // And just let the app exit so it can update
  287. return true;
  288. }
  289. catch (Exception e)
  290. {
  291. logger.ErrorException("Error starting updater.", e);
  292. ShowMessageBox(string.Format("Error attempting to update application.\n\n{0}\n\n{1}", e.GetType().Name, e.Message));
  293. }
  294. }
  295. return false;
  296. }
  297. private static void ShowMessageBox(string msg)
  298. {
  299. }
  300. public static void Shutdown()
  301. {
  302. if (_isRunningAsService)
  303. {
  304. ShutdownWindowsService();
  305. }
  306. else
  307. {
  308. DisposeAppHost();
  309. ShutdownWindowsApplication();
  310. }
  311. }
  312. public static void Restart()
  313. {
  314. DisposeAppHost();
  315. if (_isRunningAsService)
  316. {
  317. RestartWindowsService();
  318. }
  319. else
  320. {
  321. //_logger.Info("Hiding server notify icon");
  322. //_serverNotifyIcon.Visible = false;
  323. _logger.Info("Starting new instance");
  324. //Application.Restart();
  325. Process.Start(_appHost.ServerConfigurationManager.ApplicationPaths.ApplicationPath);
  326. ShutdownWindowsApplication();
  327. }
  328. }
  329. private static void DisposeAppHost()
  330. {
  331. if (!_appHostDisposed)
  332. {
  333. _logger.Info("Disposing app host");
  334. _appHostDisposed = true;
  335. _appHost.Dispose();
  336. }
  337. }
  338. private static void ShutdownWindowsApplication()
  339. {
  340. //_logger.Info("Calling Application.Exit");
  341. //Application.Exit();
  342. _logger.Info("Calling Environment.Exit");
  343. Environment.Exit(0);
  344. _logger.Info("Calling ApplicationTaskCompletionSource.SetResult");
  345. ApplicationTaskCompletionSource.SetResult(true);
  346. }
  347. private static void ShutdownWindowsService()
  348. {
  349. }
  350. private static void RestartWindowsService()
  351. {
  352. }
  353. private static bool CanRestartWindowsService()
  354. {
  355. return false;
  356. }
  357. /// <summary>
  358. /// Sets the error mode.
  359. /// </summary>
  360. /// <param name="uMode">The u mode.</param>
  361. /// <returns>ErrorModes.</returns>
  362. [DllImport("kernel32.dll")]
  363. static extern ErrorModes SetErrorMode(ErrorModes uMode);
  364. /// <summary>
  365. /// Enum ErrorModes
  366. /// </summary>
  367. [Flags]
  368. public enum ErrorModes : uint
  369. {
  370. /// <summary>
  371. /// The SYSTE m_ DEFAULT
  372. /// </summary>
  373. SYSTEM_DEFAULT = 0x0,
  374. /// <summary>
  375. /// The SE m_ FAILCRITICALERRORS
  376. /// </summary>
  377. SEM_FAILCRITICALERRORS = 0x0001,
  378. /// <summary>
  379. /// The SE m_ NOALIGNMENTFAULTEXCEPT
  380. /// </summary>
  381. SEM_NOALIGNMENTFAULTEXCEPT = 0x0004,
  382. /// <summary>
  383. /// The SE m_ NOGPFAULTERRORBOX
  384. /// </summary>
  385. SEM_NOGPFAULTERRORBOX = 0x0002,
  386. /// <summary>
  387. /// The SE m_ NOOPENFILEERRORBOX
  388. /// </summary>
  389. SEM_NOOPENFILEERRORBOX = 0x8000
  390. }
  391. }
  392. }