Add back Windows Service functionality

This commit is contained in:
flightlevel
2018-06-10 12:33:16 +10:00
parent 80d78a027b
commit 53162b4bd3
9 changed files with 188 additions and 42 deletions

View File

@@ -6,6 +6,7 @@ using Jackett.Common.Models.Config;
using Jackett.Common.Services;
using Jackett.Common.Services.Interfaces;
using Jackett.Common.Utils.Clients;
using Microsoft.AspNetCore.Hosting;
using NLog;
using NLog.Config;
using NLog.Targets;
@@ -19,8 +20,10 @@ namespace Jackett.Server
public static class Helper
{
public static IContainer ApplicationContainer { get; set; }
public static IApplicationLifetime applicationLifetime;
private static bool _automapperInitialised = false;
public static ConsoleOptions ConsoleOptions { get; set; }
public static void Initialize()
{
@@ -32,13 +35,13 @@ namespace Jackett.Server
_automapperInitialised = true;
}
ProcessRuntimeSettings();
ProcessSettings();
//Load the indexers
ServerService.Initalize();
}
private static void ProcessRuntimeSettings()
private static void ProcessSettings()
{
RuntimeSettings runtimeSettings = ServerConfiguration.RuntimeSettings;
@@ -74,11 +77,83 @@ namespace Jackett.Server
Logger.Info("Jackett Data will be stored in: " + runtimeSettings.CustomDataFolder);
}
// Use Proxy
if (runtimeSettings.ProxyConnection != null)
{
Logger.Info("Proxy enabled. " + runtimeSettings.ProxyConnection);
}
if (ConsoleOptions.Install || ConsoleOptions.Uninstall || ConsoleOptions.StartService || ConsoleOptions.StopService || ConsoleOptions.ReserveUrls)
{
bool isWindows = Environment.OSVersion.Platform == PlatformID.Win32NT;
if (!isWindows)
{
Logger.Error($"ReserveUrls and service arguments only apply to Windows, please remove them from your start arguments");
Environment.Exit(1);
}
}
/* ====== Actions ===== */
// Install service
if (ConsoleOptions.Install)
{
Logger.Info("Initiating Jackett service install");
ServiceConfigService.Install();
Environment.Exit(1);
}
// Uninstall service
if (ConsoleOptions.Uninstall)
{
Logger.Info("Initiating Jackett service uninstall");
ServiceConfigService.Uninstall();
Environment.Exit(1);
}
// Start Service
if (ConsoleOptions.StartService)
{
if (!ServiceConfigService.ServiceRunning())
{
Logger.Info("Initiating Jackett service start");
ServiceConfigService.Start();
}
Environment.Exit(1);
}
// Stop Service
if (ConsoleOptions.StopService)
{
if (ServiceConfigService.ServiceRunning())
{
Logger.Info("Initiating Jackett service stop");
ServiceConfigService.Stop();
}
Environment.Exit(1);
}
// Reserve urls
if (ConsoleOptions.ReserveUrls)
{
Logger.Info("Initiating ReserveUrls");
ServerService.ReserveUrls(doInstall: true);
Environment.Exit(1);
}
}
public static void RestartWebHost()
{
Logger.Info("Restart of the web application host (not process) initiated");
Program.isWebHostRestart = true;
applicationLifetime.StopApplication();
}
public static void StopWebHost()
{
Logger.Info("Jackett is being stopped");
applicationLifetime.StopApplication();
}
public static IConfigurationService ConfigService
@@ -97,6 +172,14 @@ namespace Jackett.Server
}
}
public static IServiceConfigService ServiceConfigService
{
get
{
return ApplicationContainer.Resolve<IServiceConfigService>();
}
}
public static ServerConfig ServerConfiguration
{
get

View File

@@ -34,6 +34,7 @@
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="2.1.0" />
<PackageReference Include="System.ServiceProcess.ServiceController" Version="4.5.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.0" />
</ItemGroup>

View File

@@ -21,15 +21,17 @@ namespace Jackett.Server
public static class Program
{
public static IConfiguration Configuration { get; set; }
private static RuntimeSettings Settings { get; set; }
public static bool isWebHostRestart = false;
public static void Main(string[] args)
{
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
var parser = new Parser();
var optionsResult = parser.ParseArguments<ConsoleOptions>(args);
var commandLineParser = new Parser();
var optionsResult = commandLineParser.ParseArguments<ConsoleOptions>(args);
var runtimeDictionary = new Dictionary<string, string>();
ConsoleOptions consoleOptions = new ConsoleOptions();
optionsResult.WithNotParsed(errors =>
{
@@ -41,8 +43,6 @@ namespace Jackett.Server
return;
});
var runtimeDictionary = new Dictionary<string, string>();
ConsoleOptions consoleOptions = new ConsoleOptions();
optionsResult.WithParsed(options =>
{
if (string.IsNullOrEmpty(options.Client))
@@ -56,6 +56,8 @@ namespace Jackett.Server
runtimeDictionary = GetValues(Settings);
});
Helper.ConsoleOptions = consoleOptions;
var builder = new ConfigurationBuilder();
builder.AddInMemoryCollection(runtimeDictionary);
@@ -111,7 +113,12 @@ namespace Jackett.Server
tempContainer.Dispose();
tempContainer = null;
//TODO Handle scenario where user changes the port in the UI and we need to restart the WebHost with new port
do
{
isWebHostRestart = false;
CreateWebHostBuilder(args, url).Build().Run();
} while (isWebHostRestart);
}
public static Dictionary<string, string> GetValues(object obj)

View File

@@ -1,28 +1,27 @@
using NLog;
using System;
using System.Collections.Specialized;
using System.Configuration.Install;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using Jackett.Common.Services.Interfaces;
namespace Jackett.Services
namespace Jackett.Server.Services
{
public class ServiceConfigService : IServiceConfigService
{
private const string NAME = "Jackett";
private const string DESCRIPTION = "Additional indexers for Sonarr";
private const string DESCRIPTION = "API Support for your favorite torrent trackers";
private const string SERVICEEXE = "JackettService.exe";
private IConfigurationService configService;
private IProcessService processService;
private Logger logger;
public ServiceConfigService(IConfigurationService c, Logger l)
public ServiceConfigService(IConfigurationService c, IProcessService p, Logger l)
{
configService = c;
processService = p;
logger = l;
}
@@ -65,30 +64,17 @@ namespace Jackett.Services
}
else
{
var installer = new ServiceProcessInstaller
{
Account = ServiceAccount.LocalSystem
};
var serviceInstaller = new ServiceInstaller();
var exePath = Path.Combine(configService.ApplicationFolder(), SERVICEEXE);
if (!File.Exists(exePath) && Debugger.IsAttached)
{
exePath = Path.Combine(configService.ApplicationFolder(), "..\\..\\..\\Jackett.Service\\bin\\Debug", SERVICEEXE);
}
string[] cmdline = { @"/assemblypath=" + exePath};
string arg = $"create {NAME} start= auto binpath= \"{exePath}\" DisplayName= {NAME}";
var context = new InstallContext("jackettservice_install.log", cmdline);
serviceInstaller.Context = context;
serviceInstaller.DisplayName = NAME;
serviceInstaller.ServiceName = NAME;
serviceInstaller.Description = DESCRIPTION;
serviceInstaller.StartType = ServiceStartMode.Automatic;
serviceInstaller.Parent = installer;
processService.StartProcessAndLog("sc.exe", arg, true);
serviceInstaller.Install(new ListDictionary());
processService.StartProcessAndLog("sc.exe", $"description {NAME} \"{DESCRIPTION}\"", true);
}
}
@@ -96,11 +82,7 @@ namespace Jackett.Services
{
RemoveService();
var serviceInstaller = new ServiceInstaller();
var context = new InstallContext("jackettservice_uninstall.log", null);
serviceInstaller.Context = context;
serviceInstaller.ServiceName = NAME;
serviceInstaller.Uninstall(null);
processService.StartProcessAndLog("sc.exe", $"delete {NAME}", true);
logger.Info("The service was uninstalled.");
}
@@ -120,12 +102,18 @@ namespace Jackett.Services
service.Refresh();
if (service.Status == ServiceControllerStatus.Stopped)
{
logger.Info("Service stopped.");
else
logger.Error("Failed to stop the service");
}
else
{
logger.Error("Failed to stop the service");
}
}
else
{
logger.Warn("The service was already stopped");
}
}
}
}

View File

@@ -81,6 +81,7 @@ namespace Jackett.Server
builder.RegisterType<SecuityService>().As<ISecuityService>();
builder.RegisterType<ServerService>().As<IServerService>();
builder.RegisterType<ProtectionService>().As<IProtectionService>();
builder.RegisterType<ServiceConfigService>().As<IServiceConfigService>();
IContainer container = builder.Build();
Helper.ApplicationContainer = container;
@@ -94,7 +95,7 @@ namespace Jackett.Server
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime applicationLifetime)
{
applicationLifetime.ApplicationStopping.Register(OnShutdown);
Helper.applicationLifetime = applicationLifetime;
app.UseResponseCompression();
app.UseDeveloperExceptionPage();

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
<PropertyGroup>
<OutputType>Exe</OutputType>
<AssemblyName>JackettService</AssemblyName>
<TargetFramework>net461</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.Compatibility" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Jackett.Server\Jackett.Server.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,17 @@
using System.ServiceProcess;
namespace Jackett.Service.Windows
{
internal static class Program
{
private static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Service()
};
ServiceBase.Run(ServicesToRun);
}
}
}

View File

@@ -0,0 +1,29 @@
using Jackett.Server;
using System.ServiceProcess;
using System.Threading;
using System.Threading.Tasks;
namespace Jackett.Service.Windows
{
public class Service : ServiceBase
{
private CancellationTokenSource tokenSource = new CancellationTokenSource();
protected override void OnStart(string[] args)
{
CancellationToken token = tokenSource.Token;
Task.Run(async () =>
{
//Registering callback that would cancel downloading
token.Register(() => Helper.StopWebHost());
Jackett.Server.Program.Main(new string[0]);
}, token);
}
protected override void OnStop()
{
tokenSource.Cancel(true);
}
}
}

View File

@@ -39,7 +39,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".NET Core", ".NET Core", "{
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Executables", "Executables", "{AA50F785-12B8-4669-8D4F-EAFB49258E60}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jackett.Server", "Jackett.Server\Jackett.Server.csproj", "{84182782-EDBC-4342-ADA6-72B7694D0862}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jackett.Server", "Jackett.Server\Jackett.Server.csproj", "{84182782-EDBC-4342-ADA6-72B7694D0862}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jackett.Service.Windows", "Jackett.Service.Windows\Jackett.Service.Windows.csproj", "{45EA874E-4FF6-4D35-AE62-668EDA5E910D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -87,6 +89,10 @@ Global
{84182782-EDBC-4342-ADA6-72B7694D0862}.Debug|Any CPU.Build.0 = Debug|Any CPU
{84182782-EDBC-4342-ADA6-72B7694D0862}.Release|Any CPU.ActiveCfg = Release|Any CPU
{84182782-EDBC-4342-ADA6-72B7694D0862}.Release|Any CPU.Build.0 = Release|Any CPU
{45EA874E-4FF6-4D35-AE62-668EDA5E910D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{45EA874E-4FF6-4D35-AE62-668EDA5E910D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{45EA874E-4FF6-4D35-AE62-668EDA5E910D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{45EA874E-4FF6-4D35-AE62-668EDA5E910D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -105,6 +111,7 @@ Global
{FF8B9A1B-AE7E-4F14-9C37-DA861D034738} = {AA50F785-12B8-4669-8D4F-EAFB49258E60}
{6A06EC9B-AF21-4DE8-9B50-BC7E3C2C78B9} = {AA50F785-12B8-4669-8D4F-EAFB49258E60}
{84182782-EDBC-4342-ADA6-72B7694D0862} = {6A06EC9B-AF21-4DE8-9B50-BC7E3C2C78B9}
{45EA874E-4FF6-4D35-AE62-668EDA5E910D} = {FF8B9A1B-AE7E-4F14-9C37-DA861D034738}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {54BC4102-8B85-49C1-BA12-257D941D1B97}