mirror of
https://github.com/Jackett/Jackett.git
synced 2025-09-17 17:34:09 +02:00
Add back Windows Service functionality
This commit is contained in:
@@ -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
|
||||
|
@@ -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>
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
|
13
src/Jackett.Service.Windows/Jackett.Service.Windows.csproj
Normal file
13
src/Jackett.Service.Windows/Jackett.Service.Windows.csproj
Normal 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>
|
17
src/Jackett.Service.Windows/Program.cs
Normal file
17
src/Jackett.Service.Windows/Program.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
29
src/Jackett.Service.Windows/Service.cs
Normal file
29
src/Jackett.Service.Windows/Service.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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}
|
||||
|
Reference in New Issue
Block a user