Merge pull request #40 from NzbDrone/single-instance

Single instance
pull/2/head
Keivan Beigi 11 years ago
commit 8f44009a4e

1
.gitignore vendored

@ -116,3 +116,4 @@ _tests/
wix/*.msi
wix/*.wixobj
wix/*.wixpdb
setup/Output/

@ -0,0 +1 @@
inno\ISCC.exe nzbdrone.iss

@ -0,0 +1,336 @@
; *** Inno Setup version 5.5.3+ English messages ***
;
; To download user-contributed translations of this file, go to:
; http://www.jrsoftware.org/files/istrans/
;
; Note: When translating this text, do not add periods (.) to the end of
; messages that didn't have them already, because on those messages Inno
; Setup adds the periods automatically (appending a period would result in
; two periods being displayed).
[LangOptions]
; The following three entries are very important. Be sure to read and
; understand the '[LangOptions] section' topic in the help file.
LanguageName=English
LanguageID=$0409
LanguageCodePage=0
; If the language you are translating to requires special font faces or
; sizes, uncomment any of the following entries and change them accordingly.
;DialogFontName=
;DialogFontSize=8
;WelcomeFontName=Verdana
;WelcomeFontSize=12
;TitleFontName=Arial
;TitleFontSize=29
;CopyrightFontName=Arial
;CopyrightFontSize=8
[Messages]
; *** Application titles
SetupAppTitle=Setup
SetupWindowTitle=Setup - %1
UninstallAppTitle=Uninstall
UninstallAppFullTitle=%1 Uninstall
; *** Misc. common
InformationTitle=Information
ConfirmTitle=Confirm
ErrorTitle=Error
; *** SetupLdr messages
SetupLdrStartupMessage=This will install %1. Do you wish to continue?
LdrCannotCreateTemp=Unable to create a temporary file. Setup aborted
LdrCannotExecTemp=Unable to execute file in the temporary directory. Setup aborted
; *** Startup error messages
LastErrorMessage=%1.%n%nError %2: %3
SetupFileMissing=The file %1 is missing from the installation directory. Please correct the problem or obtain a new copy of the program.
SetupFileCorrupt=The setup files are corrupted. Please obtain a new copy of the program.
SetupFileCorruptOrWrongVer=The setup files are corrupted, or are incompatible with this version of Setup. Please correct the problem or obtain a new copy of the program.
InvalidParameter=An invalid parameter was passed on the command line:%n%n%1
SetupAlreadyRunning=Setup is already running.
WindowsVersionNotSupported=This program does not support the version of Windows your computer is running.
WindowsServicePackRequired=This program requires %1 Service Pack %2 or later.
NotOnThisPlatform=This program will not run on %1.
OnlyOnThisPlatform=This program must be run on %1.
OnlyOnTheseArchitectures=This program can only be installed on versions of Windows designed for the following processor architectures:%n%n%1
MissingWOW64APIs=The version of Windows you are running does not include functionality required by Setup to perform a 64-bit installation. To correct this problem, please install Service Pack %1.
WinVersionTooLowError=This program requires %1 version %2 or later.
WinVersionTooHighError=This program cannot be installed on %1 version %2 or later.
AdminPrivilegesRequired=You must be logged in as an administrator when installing this program.
PowerUserPrivilegesRequired=You must be logged in as an administrator or as a member of the Power Users group when installing this program.
SetupAppRunningError=Setup has detected that %1 is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit.
UninstallAppRunningError=Uninstall has detected that %1 is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit.
; *** Misc. errors
ErrorCreatingDir=Setup was unable to create the directory "%1"
ErrorTooManyFilesInDir=Unable to create a file in the directory "%1" because it contains too many files
; *** Setup common messages
ExitSetupTitle=Exit Setup
ExitSetupMessage=Setup is not complete. If you exit now, the program will not be installed.%n%nYou may run Setup again at another time to complete the installation.%n%nExit Setup?
AboutSetupMenuItem=&About Setup...
AboutSetupTitle=About Setup
AboutSetupMessage=%1 version %2%n%3%n%n%1 home page:%n%4
AboutSetupNote=
TranslatorNote=
; *** Buttons
ButtonBack=< &Back
ButtonNext=&Next >
ButtonInstall=&Install
ButtonOK=OK
ButtonCancel=Cancel
ButtonYes=&Yes
ButtonYesToAll=Yes to &All
ButtonNo=&No
ButtonNoToAll=N&o to All
ButtonFinish=&Finish
ButtonBrowse=&Browse...
ButtonWizardBrowse=B&rowse...
ButtonNewFolder=&Make New Folder
; *** "Select Language" dialog messages
SelectLanguageTitle=Select Setup Language
SelectLanguageLabel=Select the language to use during the installation:
; *** Common wizard text
ClickNext=Click Next to continue, or Cancel to exit Setup.
BeveledLabel=
BrowseDialogTitle=Browse For Folder
BrowseDialogLabel=Select a folder in the list below, then click OK.
NewFolderName=New Folder
; *** "Welcome" wizard page
WelcomeLabel1=Welcome to the [name] Setup Wizard
WelcomeLabel2=This will install [name/ver] on your computer.%n%nIt is recommended that you close all other applications before continuing.
; *** "Password" wizard page
WizardPassword=Password
PasswordLabel1=This installation is password protected.
PasswordLabel3=Please provide the password, then click Next to continue. Passwords are case-sensitive.
PasswordEditLabel=&Password:
IncorrectPassword=The password you entered is not correct. Please try again.
; *** "License Agreement" wizard page
WizardLicense=License Agreement
LicenseLabel=Please read the following important information before continuing.
LicenseLabel3=Please read the following License Agreement. You must accept the terms of this agreement before continuing with the installation.
LicenseAccepted=I &accept the agreement
LicenseNotAccepted=I &do not accept the agreement
; *** "Information" wizard pages
WizardInfoBefore=Information
InfoBeforeLabel=Please read the following important information before continuing.
InfoBeforeClickLabel=When you are ready to continue with Setup, click Next.
WizardInfoAfter=Information
InfoAfterLabel=Please read the following important information before continuing.
InfoAfterClickLabel=When you are ready to continue with Setup, click Next.
; *** "User Information" wizard page
WizardUserInfo=User Information
UserInfoDesc=Please enter your information.
UserInfoName=&User Name:
UserInfoOrg=&Organization:
UserInfoSerial=&Serial Number:
UserInfoNameRequired=You must enter a name.
; *** "Select Destination Location" wizard page
WizardSelectDir=Select Destination Location
SelectDirDesc=Where should [name] be installed?
SelectDirLabel3=Setup will install [name] into the following folder.
SelectDirBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse.
DiskSpaceMBLabel=At least [mb] MB of free disk space is required.
CannotInstallToNetworkDrive=Setup cannot install to a network drive.
CannotInstallToUNCPath=Setup cannot install to a UNC path.
InvalidPath=You must enter a full path with drive letter; for example:%n%nC:\APP%n%nor a UNC path in the form:%n%n\\server\share
InvalidDrive=The drive or UNC share you selected does not exist or is not accessible. Please select another.
DiskSpaceWarningTitle=Not Enough Disk Space
DiskSpaceWarning=Setup requires at least %1 KB of free space to install, but the selected drive only has %2 KB available.%n%nDo you want to continue anyway?
DirNameTooLong=The folder name or path is too long.
InvalidDirName=The folder name is not valid.
BadDirName32=Folder names cannot include any of the following characters:%n%n%1
DirExistsTitle=Folder Exists
DirExists=The folder:%n%n%1%n%nalready exists. Would you like to install to that folder anyway?
DirDoesntExistTitle=Folder Does Not Exist
DirDoesntExist=The folder:%n%n%1%n%ndoes not exist. Would you like the folder to be created?
; *** "Select Components" wizard page
WizardSelectComponents=Select Components
SelectComponentsDesc=Which components should be installed?
SelectComponentsLabel2=Select the components you want to install; clear the components you do not want to install. Click Next when you are ready to continue.
FullInstallation=Full installation
; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language)
CompactInstallation=Compact installation
CustomInstallation=Custom installation
NoUninstallWarningTitle=Components Exist
NoUninstallWarning=Setup has detected that the following components are already installed on your computer:%n%n%1%n%nDeselecting these components will not uninstall them.%n%nWould you like to continue anyway?
ComponentSize1=%1 KB
ComponentSize2=%1 MB
ComponentsDiskSpaceMBLabel=Current selection requires at least [mb] MB of disk space.
; *** "Select Additional Tasks" wizard page
WizardSelectTasks=Select Additional Tasks
SelectTasksDesc=Which additional tasks should be performed?
SelectTasksLabel2=Select the additional tasks you would like Setup to perform while installing [name], then click Next.
; *** "Select Start Menu Folder" wizard page
WizardSelectProgramGroup=Select Start Menu Folder
SelectStartMenuFolderDesc=Where should Setup place the program's shortcuts?
SelectStartMenuFolderLabel3=Setup will create the program's shortcuts in the following Start Menu folder.
SelectStartMenuFolderBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse.
MustEnterGroupName=You must enter a folder name.
GroupNameTooLong=The folder name or path is too long.
InvalidGroupName=The folder name is not valid.
BadGroupName=The folder name cannot include any of the following characters:%n%n%1
NoProgramGroupCheck2=&Don't create a Start Menu folder
; *** "Ready to Install" wizard page
WizardReady=Ready to Install
ReadyLabel1=Setup is now ready to begin installing [name] on your computer.
ReadyLabel2a=Click Install to continue with the installation, or click Back if you want to review or change any settings.
ReadyLabel2b=Click Install to continue with the installation.
ReadyMemoUserInfo=User information:
ReadyMemoDir=Destination location:
ReadyMemoType=Setup type:
ReadyMemoComponents=Selected components:
ReadyMemoGroup=Start Menu folder:
ReadyMemoTasks=Additional tasks:
; *** "Preparing to Install" wizard page
WizardPreparing=Preparing to Install
PreparingDesc=Setup is preparing to install [name] on your computer.
PreviousInstallNotCompleted=The installation/removal of a previous program was not completed. You will need to restart your computer to complete that installation.%n%nAfter restarting your computer, run Setup again to complete the installation of [name].
CannotContinue=Setup cannot continue. Please click Cancel to exit.
ApplicationsFound=The following applications are using files that need to be updated by Setup. It is recommended that you allow Setup to automatically close these applications.
ApplicationsFound2=The following applications are using files that need to be updated by Setup. It is recommended that you allow Setup to automatically close these applications. After the installation has completed, Setup will attempt to restart the applications.
CloseApplications=&Automatically close the applications
DontCloseApplications=&Do not close the applications
ErrorCloseApplications=Setup was unable to automatically close all applications. It is recommended that you close all applications using files that need to be updated by Setup before continuing.
; *** "Installing" wizard page
WizardInstalling=Installing
InstallingLabel=Please wait while Setup installs [name] on your computer.
; *** "Setup Completed" wizard page
FinishedHeadingLabel=Completing the [name] Setup Wizard
FinishedLabelNoIcons=Setup has finished installing [name] on your computer.
FinishedLabel=Setup has finished installing [name] on your computer. The application may be launched by selecting the installed icons.
ClickFinish=Click Finish to exit Setup.
FinishedRestartLabel=To complete the installation of [name], Setup must restart your computer. Would you like to restart now?
FinishedRestartMessage=To complete the installation of [name], Setup must restart your computer.%n%nWould you like to restart now?
ShowReadmeCheck=Yes, I would like to view the README file
YesRadio=&Yes, restart the computer now
NoRadio=&No, I will restart the computer later
; used for example as 'Run MyProg.exe'
RunEntryExec=Run %1
; used for example as 'View Readme.txt'
RunEntryShellExec=View %1
; *** "Setup Needs the Next Disk" stuff
ChangeDiskTitle=Setup Needs the Next Disk
SelectDiskLabel2=Please insert Disk %1 and click OK.%n%nIf the files on this disk can be found in a folder other than the one displayed below, enter the correct path or click Browse.
PathLabel=&Path:
FileNotInDir2=The file "%1" could not be located in "%2". Please insert the correct disk or select another folder.
SelectDirectoryLabel=Please specify the location of the next disk.
; *** Installation phase messages
SetupAborted=Setup was not completed.%n%nPlease correct the problem and run Setup again.
EntryAbortRetryIgnore=Click Retry to try again, Ignore to proceed anyway, or Abort to cancel installation.
; *** Installation status messages
StatusClosingApplications=Closing applications...
StatusCreateDirs=Creating directories...
StatusExtractFiles=Extracting files...
StatusCreateIcons=Creating shortcuts...
StatusCreateIniEntries=Creating INI entries...
StatusCreateRegistryEntries=Creating registry entries...
StatusRegisterFiles=Registering files...
StatusSavingUninstall=Saving uninstall information...
StatusRunProgram=Finishing installation...
StatusRestartingApplications=Restarting applications...
StatusRollback=Rolling back changes...
; *** Misc. errors
ErrorInternal2=Internal error: %1
ErrorFunctionFailedNoCode=%1 failed
ErrorFunctionFailed=%1 failed; code %2
ErrorFunctionFailedWithMessage=%1 failed; code %2.%n%3
ErrorExecutingProgram=Unable to execute file:%n%1
; *** Registry errors
ErrorRegOpenKey=Error opening registry key:%n%1\%2
ErrorRegCreateKey=Error creating registry key:%n%1\%2
ErrorRegWriteKey=Error writing to registry key:%n%1\%2
; *** INI errors
ErrorIniEntry=Error creating INI entry in file "%1".
; *** File copying errors
FileAbortRetryIgnore=Click Retry to try again, Ignore to skip this file (not recommended), or Abort to cancel installation.
FileAbortRetryIgnore2=Click Retry to try again, Ignore to proceed anyway (not recommended), or Abort to cancel installation.
SourceIsCorrupted=The source file is corrupted
SourceDoesntExist=The source file "%1" does not exist
ExistingFileReadOnly=The existing file is marked as read-only.%n%nClick Retry to remove the read-only attribute and try again, Ignore to skip this file, or Abort to cancel installation.
ErrorReadingExistingDest=An error occurred while trying to read the existing file:
FileExists=The file already exists.%n%nWould you like Setup to overwrite it?
ExistingFileNewer=The existing file is newer than the one Setup is trying to install. It is recommended that you keep the existing file.%n%nDo you want to keep the existing file?
ErrorChangingAttr=An error occurred while trying to change the attributes of the existing file:
ErrorCreatingTemp=An error occurred while trying to create a file in the destination directory:
ErrorReadingSource=An error occurred while trying to read the source file:
ErrorCopying=An error occurred while trying to copy a file:
ErrorReplacingExistingFile=An error occurred while trying to replace the existing file:
ErrorRestartReplace=RestartReplace failed:
ErrorRenamingTemp=An error occurred while trying to rename a file in the destination directory:
ErrorRegisterServer=Unable to register the DLL/OCX: %1
ErrorRegSvr32Failed=RegSvr32 failed with exit code %1
ErrorRegisterTypeLib=Unable to register the type library: %1
; *** Post-installation errors
ErrorOpeningReadme=An error occurred while trying to open the README file.
ErrorRestartingComputer=Setup was unable to restart the computer. Please do this manually.
; *** Uninstaller messages
UninstallNotFound=File "%1" does not exist. Cannot uninstall.
UninstallOpenError=File "%1" could not be opened. Cannot uninstall
UninstallUnsupportedVer=The uninstall log file "%1" is in a format not recognized by this version of the uninstaller. Cannot uninstall
UninstallUnknownEntry=An unknown entry (%1) was encountered in the uninstall log
ConfirmUninstall=Are you sure you want to completely remove %1 and all of its components?
UninstallOnlyOnWin64=This installation can only be uninstalled on 64-bit Windows.
OnlyAdminCanUninstall=This installation can only be uninstalled by a user with administrative privileges.
UninstallStatusLabel=Please wait while %1 is removed from your computer.
UninstalledAll=%1 was successfully removed from your computer.
UninstalledMost=%1 uninstall complete.%n%nSome elements could not be removed. These can be removed manually.
UninstalledAndNeedsRestart=To complete the uninstallation of %1, your computer must be restarted.%n%nWould you like to restart now?
UninstallDataCorrupted="%1" file is corrupted. Cannot uninstall
; *** Uninstallation phase messages
ConfirmDeleteSharedFileTitle=Remove Shared File?
ConfirmDeleteSharedFile2=The system indicates that the following shared file is no longer in use by any programs. Would you like for Uninstall to remove this shared file?%n%nIf any programs are still using this file and it is removed, those programs may not function properly. If you are unsure, choose No. Leaving the file on your system will not cause any harm.
SharedFileNameLabel=File name:
SharedFileLocationLabel=Location:
WizardUninstalling=Uninstall Status
StatusUninstalling=Uninstalling %1...
; *** Shutdown block reasons
ShutdownBlockReasonInstallingApp=Installing %1.
ShutdownBlockReasonUninstallingApp=Uninstalling %1.
; The custom messages below aren't used by Setup itself, but if you make
; use of them in your scripts, you'll want to translate them.
[CustomMessages]
NameAndVersion=%1 version %2
AdditionalIcons=Additional icons:
CreateDesktopIcon=Create a &desktop icon
CreateQuickLaunchIcon=Create a &Quick Launch icon
ProgramOnTheWeb=%1 on the Web
UninstallProgram=Uninstall %1
LaunchProgram=Launch %1
AssocFileExtension=&Associate %1 with the %2 file extension
AssocingFileExtension=Associating %1 with the %2 file extension...
AutoStartProgramGroupDescription=Startup:
AutoStartProgram=Automatically start %1
AddonHostProgramNotFound=%1 could not be located in the folder you selected.%n%nDo you want to continue anyway?

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

@ -0,0 +1,52 @@
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define AppName "NzbDrone"
#define AppPublisher "NzbDrone"
#define AppURL "http://www.nzbdrone.com/"
#define AppExeName "NzbDrone.exe"
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{56C1065D-3523-4025-B76D-6F73F67F7F71}
AppName={#AppName}
AppVersion={%BUILD_NUMBER|1.0.0}
AppPublisher={#AppPublisher}
AppPublisherURL={#AppURL}
AppSupportURL={#AppURL}
AppUpdatesURL={#AppURL}
DefaultDirName={commonprograms}\{#AppName}\bin
DisableDirPage=yes
DefaultGroupName={#AppName}
DisableProgramGroupPage=yes
OutputBaseFilename=NzbDroneSetup
SolidCompression=yes
AppCopyright=Creative Commons 3.0 License
AllowUNCPath=False
UninstallDisplayIcon={app}\NzbDrone.exe
DisableReadyPage=True
CompressionThreads=2
Compression=lzma2/normal
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Files]
Source: "C:\Dropbox\Git\NzbDrone\_output\NzbDrone.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Dropbox\Git\NzbDrone\_output\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]
Name: "{group}\{#AppName}"; Filename: "{app}\{#AppExeName}"
Name: "{commondesktop}\{#AppName}"; Filename: "{app}\{#AppExeName}"; Tasks: desktopicon
[Run]
Filename: "{app}\nzbdrone.console.exe"; Parameters: "/i"; Flags: waituntilterminated
[UninstallRun]
Filename: "{app}\nzbdrone.console.exe"; Parameters: "/u"; Flags: waituntilterminated skipifdoesntexist

@ -18,7 +18,7 @@ namespace NzbDrone.App.Test
[TestFixture]
public class ContainerFixture : TestBase
{
StartupArguments args = new StartupArguments("first", "second");
StartupContext args = new StartupContext("first", "second");
[Test]
public void should_be_able_to_resolve_indexers()

@ -60,6 +60,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="ContainerFixture.cs" />
<Compile Include="NzbDroneProcessServiceFixture.cs" />
<Compile Include="RouterTest.cs" />
<Compile Include="MonitoringProviderTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

@ -0,0 +1,91 @@
using System.Collections.Generic;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Model;
using NzbDrone.Common.Processes;
using NzbDrone.Host;
using NzbDrone.Test.Common;
namespace NzbDrone.App.Test
{
[TestFixture]
public class NzbDroneProcessServiceFixture : TestBase<SingleInstancePolicy>
{
private const int CURRENT_PROCESS_ID = 5;
[SetUp]
public void Setup()
{
Mocker.GetMock<IProcessProvider>().Setup(c => c.GetCurrentProcess())
.Returns(new ProcessInfo() { Id = CURRENT_PROCESS_ID });
}
[Test]
public void should_continue_if_only_instance()
{
Mocker.GetMock<IProcessProvider>()
.Setup(c => c.FindProcessByName(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME))
.Returns(new List<ProcessInfo>());
Mocker.GetMock<IProcessProvider>().Setup(c => c.FindProcessByName(ProcessProvider.NZB_DRONE_PROCESS_NAME))
.Returns(new List<ProcessInfo>
{
new ProcessInfo{Id = CURRENT_PROCESS_ID}
});
Subject.PreventStartIfAlreadyRunning();
Mocker.GetMock<IBrowserService>().Verify(c => c.LaunchWebUI(), Times.Never());
}
[Test]
public void should_enforce_if_another_console_is_running()
{
Mocker.GetMock<IProcessProvider>()
.Setup(c => c.FindProcessByName(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME))
.Returns(new List<ProcessInfo>
{
new ProcessInfo{Id = 10}
});
Mocker.GetMock<IProcessProvider>().Setup(c => c.FindProcessByName(ProcessProvider.NZB_DRONE_PROCESS_NAME))
.Returns(new List<ProcessInfo>
{
new ProcessInfo{Id = CURRENT_PROCESS_ID}
});
Assert.Throws<TerminateApplicationException>(() => Subject.PreventStartIfAlreadyRunning());
Mocker.GetMock<IBrowserService>().Verify(c => c.LaunchWebUI(), Times.Once());
ExceptionVerification.ExpectedWarns(1);
}
[Test]
public void should_return_false_if_another_gui_is_running()
{
Mocker.GetMock<IProcessProvider>()
.Setup(c => c.FindProcessByName(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME))
.Returns(new List<ProcessInfo>
{
new ProcessInfo{Id = CURRENT_PROCESS_ID}
});
Mocker.GetMock<IProcessProvider>().Setup(c => c.FindProcessByName(ProcessProvider.NZB_DRONE_PROCESS_NAME))
.Returns(new List<ProcessInfo>
{
new ProcessInfo{Id = 10}
});
Assert.Throws<TerminateApplicationException>(() => Subject.PreventStartIfAlreadyRunning());
Mocker.GetMock<IBrowserService>().Verify(c => c.LaunchWebUI(), Times.Once());
ExceptionVerification.ExpectedWarns(1);
}
}
}

@ -68,7 +68,7 @@ namespace NzbDrone.App.Test
serviceProvider.Setup(c => c.ServiceExist(It.IsAny<string>())).Returns(true);
serviceProvider.Setup(c => c.GetStatus(It.IsAny<string>())).Returns(ServiceControllerStatus.StartPending);
Subject.Route();
Subject.Route(ApplicationModes.Service);
serviceProvider.Verify(c => c.Run(It.IsAny<ServiceBase>()), Times.Once());
}

@ -23,7 +23,7 @@ namespace NzbDrone.Automation.Test
public AutomationTest()
{
new StartupArguments();
new StartupContext();
LogManager.Configuration = new LoggingConfiguration();
var consoleTarget = new ConsoleTarget { Layout = "${level}: ${message} ${exception}" };

@ -35,9 +35,9 @@ namespace NzbDrone.Common.Test
[Test]
public void should_use_path_from_arg_if_provided()
{
var args = new StartupArguments("-data=\"c:\\users\\test\\\"");
var args = new StartupContext("-data=\"c:\\users\\test\\\"");
Mocker.SetConstant<IStartupArguments>(args);
Mocker.SetConstant<IStartupContext>(args);
Subject.AppDataFolder.Should().Be("c:\\users\\test\\");
}
}

@ -11,7 +11,7 @@ namespace NzbDrone.Common.Test.EnvironmentTests
[Test]
public void empty_array_should_return_empty_flags()
{
var args = new StartupArguments(new string[0]);
var args = new StartupContext(new string[0]);
args.Flags.Should().BeEmpty();
}
@ -21,7 +21,7 @@ namespace NzbDrone.Common.Test.EnvironmentTests
[TestCase(" /t ")]
public void should_parse_single_flag(string arg)
{
var args = new StartupArguments(new[] { arg });
var args = new StartupContext(new[] { arg });
args.Flags.Should().HaveCount(1);
args.Flags.Contains("t").Should().BeTrue();
}
@ -32,7 +32,7 @@ namespace NzbDrone.Common.Test.EnvironmentTests
[TestCase(" /key=\"value\"")]
public void should_parse_args_with_alues(string arg)
{
var args = new StartupArguments(new[] { arg });
var args = new StartupContext(new[] { arg });
args.Args.Should().HaveCount(1);
args.Args["key"].Should().Be("value");
}

@ -15,7 +15,7 @@ namespace NzbDrone.Common.Test
[SetUp]
public void setup()
{
Mocker.SetConstant(MainAppContainerBuilder.BuildContainer(new StartupArguments()));
Mocker.SetConstant(MainAppContainerBuilder.BuildContainer(new StartupContext()));
}
[Test]

@ -15,7 +15,7 @@ namespace NzbDrone.Common.Composition
public IContainer Container { get; private set; }
protected ContainerBuilderBase(IStartupArguments args, params string[] assemblies)
protected ContainerBuilderBase(IStartupContext args, params string[] assemblies)
{

@ -24,9 +24,9 @@ namespace NzbDrone.Common
Console.WriteLine();
Console.WriteLine(" Usage: {0} <command> ", Process.GetCurrentProcess().MainModule.ModuleName);
Console.WriteLine(" Commands:");
Console.WriteLine(" /{0} Install the application as a Windows Service ({1}).", StartupArguments.INSTALL_SERVICE, ServiceProvider.NZBDRONE_SERVICE_NAME);
Console.WriteLine(" /{0} Uninstall already installed Windows Service ({1}).", StartupArguments.UNINSTALL_SERVICE, ServiceProvider.NZBDRONE_SERVICE_NAME);
Console.WriteLine(" /{0} Don't open NzbDrone in a browser", StartupArguments.NO_BROWSER);
Console.WriteLine(" /{0} Install the application as a Windows Service ({1}).", StartupContext.INSTALL_SERVICE, ServiceProvider.NZBDRONE_SERVICE_NAME);
Console.WriteLine(" /{0} Uninstall already installed Windows Service ({1}).", StartupContext.UNINSTALL_SERVICE, ServiceProvider.NZBDRONE_SERVICE_NAME);
Console.WriteLine(" /{0} Don't open NzbDrone in a browser", StartupContext.NO_BROWSER);
Console.WriteLine(" <No Arguments> Run application in console mode.");
}

@ -22,7 +22,7 @@ namespace NzbDrone.Common.EnvironmentInfo
private readonly Environment.SpecialFolder DATA_SPECIAL_FOLDER = Environment.SpecialFolder.CommonApplicationData;
public AppFolderInfo(IDiskProvider diskProvider, IStartupArguments startupArguments)
public AppFolderInfo(IDiskProvider diskProvider, IStartupContext startupContext)
{
_diskProvider = diskProvider;
@ -33,9 +33,9 @@ namespace NzbDrone.Common.EnvironmentInfo
_logger = NzbDroneLogger.GetLogger(this);
if (startupArguments.Args.ContainsKey(StartupArguments.APPDATA))
if (startupContext.Args.ContainsKey(StartupContext.APPDATA))
{
AppDataFolder = startupArguments.Args[StartupArguments.APPDATA];
AppDataFolder = startupContext.Args[StartupContext.APPDATA];
}
else
{

@ -2,6 +2,7 @@
using System.Diagnostics;
using System.IO;
using System.Security.Principal;
using System.ServiceProcess;
using NLog;
namespace NzbDrone.Common.EnvironmentInfo
@ -11,15 +12,21 @@ namespace NzbDrone.Common.EnvironmentInfo
{
bool IsUserInteractive { get; }
bool IsAdmin { get; }
bool IsWindowsService { get; }
}
public class RuntimeInfo : IRuntimeInfo
{
private readonly Logger _logger;
public RuntimeInfo(Logger logger)
public RuntimeInfo(Logger logger, IServiceProvider serviceProvider)
{
_logger = logger;
IsWindowsService = !IsUserInteractive &&
OsInfo.IsWindows &&
serviceProvider.ServiceExist(ServiceProvider.NZBDRONE_SERVICE_NAME) &&
serviceProvider.GetStatus(ServiceProvider.NZBDRONE_SERVICE_NAME) == ServiceControllerStatus.StartPending;
}
public bool IsUserInteractive
@ -30,6 +37,8 @@ namespace NzbDrone.Common.EnvironmentInfo
static RuntimeInfo()
{
IsProduction = InternalIsProduction();
}
public bool IsAdmin
@ -49,6 +58,8 @@ namespace NzbDrone.Common.EnvironmentInfo
}
}
public bool IsWindowsService { get; private set; }
private static readonly string ProcessName = Process.GetCurrentProcess().ProcessName.ToLower();
public static bool IsProduction { get; private set; }

@ -2,21 +2,23 @@
namespace NzbDrone.Common.EnvironmentInfo
{
public interface IStartupArguments
public interface IStartupContext
{
HashSet<string> Flags { get; }
Dictionary<string, string> Args { get; }
bool InstallService { get; }
bool UninstallService { get; }
}
public class StartupArguments : IStartupArguments
public class StartupContext : IStartupContext
{
public const string APPDATA = "data";
public const string NO_BROWSER = "nobrowser";
public const string INSTALL_SERVICE = "i";
public const string UNINSTALL_SERVICE = "u";
internal const string INSTALL_SERVICE = "i";
internal const string UNINSTALL_SERVICE = "u";
public const string HELP = "?";
public StartupArguments(params string[] args)
public StartupContext(params string[] args)
{
Flags = new HashSet<string>();
Args = new Dictionary<string, string>();
@ -40,5 +42,22 @@ namespace NzbDrone.Common.EnvironmentInfo
public HashSet<string> Flags { get; private set; }
public Dictionary<string, string> Args { get; private set; }
public bool InstallService
{
get
{
return Flags.Contains(INSTALL_SERVICE);
}
}
public bool UninstallService
{
get
{
return Flags.Contains(UNINSTALL_SERVICE);
}
}
}
}

@ -9,9 +9,9 @@ namespace NzbDrone.Common.Instrumentation
{
public static class LogTargets
{
public static void Register(IStartupArguments startupArguments, bool updateApp, bool inConsole)
public static void Register(IStartupContext startupContext, bool updateApp, bool inConsole)
{
var appFolderInfo = new AppFolderInfo(new DiskProvider(), startupArguments);
var appFolderInfo = new AppFolderInfo(new DiskProvider(), startupContext);
LogManager.Configuration = new LoggingConfiguration();
@ -24,7 +24,7 @@ namespace NzbDrone.Common.Instrumentation
}
else
{
if (inConsole && (OsInfo.IsLinux || new RuntimeInfo(null).IsUserInteractive))
if (inConsole && (OsInfo.IsLinux || new RuntimeInfo(null, new ServiceProvider()).IsUserInteractive))
{
RegisterConsole();
}

@ -86,7 +86,7 @@
<Compile Include="EnsureThat\Param.cs" />
<Compile Include="EnsureThat\Resources\ExceptionMessages.Designer.cs" />
<Compile Include="EnvironmentInfo\BuildInfo.cs" />
<Compile Include="EnvironmentInfo\StartupArguments.cs" />
<Compile Include="EnvironmentInfo\StartupContext.cs" />
<Compile Include="EnvironmentInfo\RuntimeInfo.cs" />
<Compile Include="EnvironmentInfo\OsInfo.cs" />
<Compile Include="Exceptions\NzbDroneException.cs" />

@ -15,10 +15,12 @@ namespace NzbDrone.Common.Processes
{
ProcessInfo GetCurrentProcess();
ProcessInfo GetProcessById(int id);
List<ProcessInfo> FindProcessByName(string name);
void OpenDefaultBrowser(string url);
void WaitForExit(Process process);
void SetPriority(int processId, ProcessPriorityClass priority);
void KillAll(string processName);
void Kill(int processId);
bool Exists(string processName);
ProcessPriorityClass GetCurrentProcessPriority();
Process Start(string path, string args = null, Action<string> onOutputDataReceived = null, Action<string> onErrorDataReceived = null);
@ -74,6 +76,11 @@ namespace NzbDrone.Common.Processes
return processInfo;
}
public List<ProcessInfo> FindProcessByName(string name)
{
return Process.GetProcessesByName(name).Select(ConvertToProcessInfo).Where(c => c != null).ToList();
}
public void OpenDefaultBrowser(string url)
{
Logger.Info("Opening URL [{0}]", url);
@ -213,23 +220,29 @@ namespace NzbDrone.Common.Processes
process.Refresh();
ProcessInfo processInfo = null;
try
{
if (process.Id <= 0 || process.HasExited) return null;
if (process.Id <= 0) return null;
processInfo = new ProcessInfo();
processInfo.Id = process.Id;
processInfo.Name = process.ProcessName;
processInfo.StartPath = GetExeFileName(process);
return new ProcessInfo
if (process.HasExited)
{
Id = process.Id,
StartPath = GetExeFileName(process),
Name = process.ProcessName
};
processInfo = null;
}
}
catch (Win32Exception)
catch (Win32Exception e)
{
Logger.Warn("Coudn't get process info for " + process.ProcessName);
Logger.WarnException("Couldn't get process info for " + process.ProcessName, e);
}
return null;
return processInfo;
}
private static string GetExeFileName(Process process)
@ -242,7 +255,7 @@ namespace NzbDrone.Common.Processes
return process.Modules.Cast<ProcessModule>().FirstOrDefault(module => module.ModuleName.ToLower().EndsWith(".exe")).FileName;
}
private void Kill(int processId)
public void Kill(int processId)
{
var process = Process.GetProcesses().FirstOrDefault(p => p.Id == processId);

@ -1,5 +1,4 @@
using System;
using System.Threading;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Instrumentation;
@ -15,23 +14,18 @@ namespace NzbDrone.Console
{
try
{
var startupArgs = new StartupArguments(args);
var startupArgs = new StartupContext(args);
LogTargets.Register(startupArgs, false, true);
Bootstrap.Start(startupArgs, new ConsoleAlerts());
}
catch (TerminateApplicationException)
{
}
catch (Exception e)
{
System.Console.WriteLine("");
System.Console.WriteLine("");
Logger.FatalException("EPIC FAIL!", e);
System.Console.WriteLine("Press any key to exit...");
System.Console.ReadLine();
}
while (true)
{
Thread.Sleep(10 * 60);
}
}
}
}

@ -17,7 +17,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests
[SetUp]
public void Setup()
{
Mocker.SetConstant<IAppFolderInfo>(new AppFolderInfo(new DiskProvider(), Mocker.Resolve<IStartupArguments>()));
Mocker.SetConstant<IAppFolderInfo>(new AppFolderInfo(new DiskProvider(), Mocker.Resolve<IStartupContext>()));
}
[Test]

@ -1,8 +1,6 @@
using System;
using System.ServiceProcess;
using System.ServiceProcess;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Processes;
using NzbDrone.Core.Configuration;
using NzbDrone.Host.Owin;
@ -10,6 +8,7 @@ namespace NzbDrone.Host
{
public interface INzbDroneServiceFactory
{
bool IsServiceStopped { get; }
ServiceBase Build();
void Start();
}
@ -19,20 +18,20 @@ namespace NzbDrone.Host
private readonly IConfigFileProvider _configFileProvider;
private readonly IRuntimeInfo _runtimeInfo;
private readonly IHostController _hostController;
private readonly IProcessProvider _processProvider;
private readonly PriorityMonitor _priorityMonitor;
private readonly IStartupArguments _startupArguments;
private readonly IStartupContext _startupContext;
private readonly IBrowserService _browserService;
private readonly Logger _logger;
public NzbDroneServiceFactory(IConfigFileProvider configFileProvider, IHostController hostController, IRuntimeInfo runtimeInfo,
IProcessProvider processProvider, PriorityMonitor priorityMonitor, IStartupArguments startupArguments, Logger logger)
public NzbDroneServiceFactory(IConfigFileProvider configFileProvider, IHostController hostController,
IRuntimeInfo runtimeInfo, PriorityMonitor priorityMonitor, IStartupContext startupContext, IBrowserService browserService, Logger logger)
{
_configFileProvider = configFileProvider;
_hostController = hostController;
_runtimeInfo = runtimeInfo;
_processProvider = processProvider;
_priorityMonitor = priorityMonitor;
_startupArguments = startupArguments;
_startupContext = startupContext;
_browserService = browserService;
_logger = logger;
}
@ -45,19 +44,10 @@ namespace NzbDrone.Host
{
_hostController.StartServer();
if (!_startupArguments.Flags.Contains(StartupArguments.NO_BROWSER) &&
_runtimeInfo.IsUserInteractive &&
_configFileProvider.LaunchBrowser)
if (!_startupContext.Flags.Contains(StartupContext.NO_BROWSER)
&& _configFileProvider.LaunchBrowser)
{
try
{
_logger.Info("Starting default browser. {0}", _hostController.AppUrl);
_processProvider.OpenDefaultBrowser(_hostController.AppUrl);
}
catch (Exception e)
{
_logger.ErrorException("Failed to open URL in default browser.", e);
}
_browserService.LaunchWebUI();
}
_priorityMonitor.Start();
@ -68,8 +58,11 @@ namespace NzbDrone.Host
_logger.Info("Attempting to stop application.");
_hostController.StopServer();
_logger.Info("Application has finished stop routine.");
IsServiceStopped = true;
}
public bool IsServiceStopped { get; private set; }
public ServiceBase Build()
{
return this;

@ -1,4 +1,7 @@
using System.Reflection;
using System;
using System.Reflection;
using System.Threading;
using NLog;
using NzbDrone.Common.Composition;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Instrumentation;
@ -9,27 +12,131 @@ namespace NzbDrone.Host
{
public static class Bootstrap
{
public static IContainer Start(StartupArguments args, IUserAlert userAlert)
private static IContainer _container;
private static readonly Logger Logger = NzbDroneLogger.GetLogger();
public static void Start(StartupContext startupContext, IUserAlert userAlert, Action<IContainer> startCallback = null)
{
var logger = NzbDroneLogger.GetLogger();
try
{
GlobalExceptionHandlers.Register();
IgnoreCertErrorPolicy.Register();
Logger.Info("Starting NzbDrone Console. Version {0}", Assembly.GetExecutingAssembly().GetName().Version);
if (!PlatformValidation.IsValidate(userAlert))
{
throw new TerminateApplicationException("Missing system requirements");
}
GlobalExceptionHandlers.Register();
IgnoreCertErrorPolicy.Register();
_container = MainAppContainerBuilder.BuildContainer(startupContext);
logger.Info("Starting NzbDrone Console. Version {0}", Assembly.GetExecutingAssembly().GetName().Version);
var appMode = GetApplicationMode(startupContext);
Start(appMode);
if (startCallback != null)
{
startCallback(_container);
}
SpinToExit(appMode);
}
catch (TerminateApplicationException e)
{
Logger.Info(e.Message);
}
}
if (!PlatformValidation.IsValidate(userAlert))
private static void Start(ApplicationModes applicationModes)
{
if (!IsInUtilityMode(applicationModes))
{
throw new TerminateApplicationException();
EnsureSingleInstance(applicationModes == ApplicationModes.Service);
}
var container = MainAppContainerBuilder.BuildContainer(args);
DbFactory.RegisterDatabase(_container);
_container.Resolve<Router>().Route(applicationModes);
}
DbFactory.RegisterDatabase(container);
container.Resolve<Router>().Route();
private static void SpinToExit(ApplicationModes applicationModes)
{
if (IsInUtilityMode(applicationModes))
{
return;
}
var serviceFactory = _container.Resolve<INzbDroneServiceFactory>();
return container;
while (!serviceFactory.IsServiceStopped)
{
Thread.Sleep(1000);
}
}
private static void EnsureSingleInstance(bool isService)
{
var instancePolicy = _container.Resolve<ISingleInstancePolicy>();
if (isService)
{
instancePolicy.KillAllOtherInstance();
}
else
{
instancePolicy.PreventStartIfAlreadyRunning();
}
}
private static ApplicationModes GetApplicationMode(StartupContext startupContext)
{
if (startupContext.Flags.Contains(StartupContext.HELP))
{
return ApplicationModes.Help;
}
if (!OsInfo.IsLinux && startupContext.InstallService)
{
return ApplicationModes.InstallService;
}
if (!OsInfo.IsLinux && startupContext.UninstallService)
{
return ApplicationModes.UninstallService;
}
if (_container.Resolve<IRuntimeInfo>().IsWindowsService)
{
return ApplicationModes.Service;
}
return ApplicationModes.Interactive;
}
private static bool IsInUtilityMode(ApplicationModes applicationMode)
{
switch (applicationMode)
{
case ApplicationModes.InstallService:
case ApplicationModes.UninstallService:
case ApplicationModes.Help:
{
return true;
}
default:
{
return false;
}
}
}
}
}

@ -0,0 +1,50 @@
using System;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Processes;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Host
{
public interface IBrowserService
{
void LaunchWebUI();
}
public class BrowserService : IBrowserService
{
private readonly IProcessProvider _processProvider;
private readonly IConfigFileProvider _configFileProvider;
private readonly IRuntimeInfo _runtimeInfo;
private readonly Logger _logger;
public BrowserService(IProcessProvider processProvider, IConfigFileProvider configFileProvider, IRuntimeInfo runtimeInfo, Logger logger)
{
_processProvider = processProvider;
_configFileProvider = configFileProvider;
_runtimeInfo = runtimeInfo;
_logger = logger;
}
public void LaunchWebUI()
{
var url = string.Format("http://localhost:{0}", _configFileProvider.Port);
try
{
if (_runtimeInfo.IsUserInteractive)
{
_logger.Info("Starting default browser. {0}", url);
_processProvider.OpenDefaultBrowser(url);
}
else
{
_logger.Debug("none-interactive runtime. Won't attempt to open browser.");
}
}
catch (Exception e)
{
_logger.ErrorException("Couldn't open default browser to " + url, e);
}
}
}
}

@ -10,12 +10,12 @@ namespace NzbDrone.Host
{
public class MainAppContainerBuilder : ContainerBuilderBase
{
public static IContainer BuildContainer(StartupArguments args)
public static IContainer BuildContainer(StartupContext args)
{
return new MainAppContainerBuilder(args).Container;
}
private MainAppContainerBuilder(StartupArguments args)
private MainAppContainerBuilder(StartupContext args)
: base(args, "NzbDrone.Host", "NzbDrone.Common", "NzbDrone.Core", "NzbDrone.Api", "NzbDrone.SignalR")
{

@ -116,6 +116,8 @@
</Compile>
<Compile Include="AccessControl\FirewallAdapter.cs" />
<Compile Include="AccessControl\UrlAclAdapter.cs" />
<Compile Include="BrowserService.cs" />
<Compile Include="NzbDroneProcessService.cs" />
<Compile Include="IUserAlert.cs" />
<Compile Include="Owin\NlogTextWriter.cs" />
<Compile Include="Owin\OwinServiceProvider.cs" />

@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Processes;
namespace NzbDrone.Host
{
public interface ISingleInstancePolicy
{
void PreventStartIfAlreadyRunning();
void KillAllOtherInstance();
}
public class SingleInstancePolicy : ISingleInstancePolicy
{
private readonly IProcessProvider _processProvider;
private readonly IBrowserService _browserService;
private readonly Logger _logger;
public SingleInstancePolicy(IProcessProvider processProvider, IBrowserService browserService, Logger logger)
{
_processProvider = processProvider;
_browserService = browserService;
_logger = logger;
}
public void PreventStartIfAlreadyRunning()
{
if (IsAlreadyRunning())
{
_logger.Warn("Another instance of NzbDrone is already running.");
_browserService.LaunchWebUI();
throw new TerminateApplicationException("Another instance is already running");
}
}
public void KillAllOtherInstance()
{
foreach (var processId in GetOtherNzbDroneProcessIds())
{
_processProvider.Kill(processId);
}
}
private bool IsAlreadyRunning()
{
return GetOtherNzbDroneProcessIds().Any();
}
private List<int> GetOtherNzbDroneProcessIds()
{
var currentId = _processProvider.GetCurrentProcess().Id;
var consoleIds = _processProvider.FindProcessByName(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME)
.Select(c => c.Id);
var winformIds = _processProvider.FindProcessByName(ProcessProvider.NZB_DRONE_PROCESS_NAME).Select(c => c.Id);
var otherProcesses = consoleIds.Union(winformIds).Except(new[] { currentId }).ToList();
if (otherProcesses.Any())
{
_logger.Info("{0} instance(s) of NzbDrone are running", otherProcesses.Count);
}
return otherProcesses;
}
}
}

@ -2,7 +2,6 @@
{
public interface IHostController
{
string AppUrl { get; }
void StartServer();
void StopServer();
}

@ -107,11 +107,6 @@ namespace NzbDrone.Host.Owin
}
}
public string AppUrl
{
get { return string.Format("http://localhost:{0}", _configFileProvider.Port); }
}
public void StopServer()
{
if (_host == null) return;

@ -1,7 +1,5 @@
using System.ServiceProcess;
using NLog;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Host
{
@ -9,28 +7,18 @@ namespace NzbDrone.Host
{
private readonly INzbDroneServiceFactory _nzbDroneServiceFactory;
private readonly IServiceProvider _serviceProvider;
private readonly IStartupArguments _startupArguments;
private readonly IConsoleService _consoleService;
private readonly IRuntimeInfo _runtimeInfo;
private readonly Logger _logger;
public Router(INzbDroneServiceFactory nzbDroneServiceFactory, IServiceProvider serviceProvider, IStartupArguments startupArguments,
IConsoleService consoleService, IRuntimeInfo runtimeInfo, Logger logger)
public Router(INzbDroneServiceFactory nzbDroneServiceFactory, IServiceProvider serviceProvider,
IConsoleService consoleService, Logger logger)
{
_nzbDroneServiceFactory = nzbDroneServiceFactory;
_serviceProvider = serviceProvider;
_startupArguments = startupArguments;
_consoleService = consoleService;
_runtimeInfo = runtimeInfo;
_logger = logger;
}
public void Route()
{
var appMode = GetApplicationMode();
Route(appMode);
}
public void Route(ApplicationModes applicationModes)
{
_logger.Info("Application mode: {0}", applicationModes);
@ -86,32 +74,6 @@ namespace NzbDrone.Host
}
}
private ApplicationModes GetApplicationMode()
{
if (!_runtimeInfo.IsUserInteractive &&
OsInfo.IsWindows &&
_serviceProvider.ServiceExist(ServiceProvider.NZBDRONE_SERVICE_NAME) &&
_serviceProvider.GetStatus(ServiceProvider.NZBDRONE_SERVICE_NAME) == ServiceControllerStatus.StartPending)
{
return ApplicationModes.Service;
}
if (_startupArguments.Flags.Contains(StartupArguments.HELP))
{
return ApplicationModes.Help;
}
if (!OsInfo.IsLinux && _startupArguments.Flags.Contains(StartupArguments.INSTALL_SERVICE))
{
return ApplicationModes.InstallService;
}
if (!OsInfo.IsLinux && _startupArguments.Flags.Contains(StartupArguments.UNINSTALL_SERVICE))
{
return ApplicationModes.UninstallService;
}
return ApplicationModes.Interactive;
}
}
}

@ -4,5 +4,9 @@ namespace NzbDrone.Host
{
public class TerminateApplicationException : ApplicationException
{
public TerminateApplicationException(string reason)
: base("Application is being terminated. Reason : " + reason)
{
}
}
}

@ -50,7 +50,7 @@ namespace NzbDrone.Integration.Test
public IntegrationTest()
{
new StartupArguments();
new StartupContext();
LogManager.Configuration = new LoggingConfiguration();
var consoleTarget = new ConsoleTarget { Layout = "${level}: ${message} ${exception}" };

@ -12,7 +12,7 @@ namespace NzbDrone.Test.Common
protected static void InitLogging()
{
new StartupArguments();
new StartupContext();
TestLogger = LogManager.GetLogger("TestLogger");

@ -93,7 +93,7 @@ namespace NzbDrone.Test.Common
Mocker.SetConstant(LogManager.GetLogger("TestLogger"));
Mocker.SetConstant<IStartupArguments>(new StartupArguments(new string[0]));
Mocker.SetConstant<IStartupContext>(new StartupContext(new string[0]));
LogManager.ReconfigExistingLoggers();

@ -32,7 +32,7 @@ namespace NzbDrone.Update.Test
Subject.Start(AppType.Service, targetFolder);
Mocker.GetMock<IProcessProvider>().Verify(c => c.SpawnNewProcess("c:\\NzbDrone\\NzbDrone.Console.exe", StartupArguments.NO_BROWSER), Times.Once());
Mocker.GetMock<IProcessProvider>().Verify(c => c.SpawnNewProcess("c:\\NzbDrone\\NzbDrone.Console.exe", StartupContext.NO_BROWSER), Times.Once());
ExceptionVerification.ExpectedWarns(1);
}

@ -28,7 +28,7 @@ namespace NzbDrone.Update
{
try
{
var startupArgument = new StartupArguments(args);
var startupArgument = new StartupContext(args);
LogTargets.Register(startupArgument, true, true);
Console.WriteLine("Starting NzbDrone Update Client");

@ -5,15 +5,15 @@ namespace NzbDrone.Update
{
public class UpdateContainerBuilder : ContainerBuilderBase
{
private UpdateContainerBuilder(IStartupArguments startupArguments)
: base(startupArguments, "NzbDrone.Update", "NzbDrone.Common")
private UpdateContainerBuilder(IStartupContext startupContext)
: base(startupContext, "NzbDrone.Update", "NzbDrone.Common")
{
}
public static IContainer Build(IStartupArguments startupArguments)
public static IContainer Build(IStartupContext startupContext)
{
return new UpdateContainerBuilder(startupArguments).Container;
return new UpdateContainerBuilder(startupContext).Container;
}
}
}

@ -73,7 +73,7 @@ namespace NzbDrone.Update.UpdateEngine
_logger.Info("Starting {0}", fileName);
var path = Path.Combine(installationFolder, fileName);
_processProvider.SpawnNewProcess(path, StartupArguments.NO_BROWSER);
_processProvider.SpawnNewProcess(path, StartupContext.NO_BROWSER);
}
}
}

@ -68,6 +68,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.SignalR.Ow
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "External", "External", "{F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}"
EndProject
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "NzbDrone.Setup", "NzbDrone.Setup\NzbDrone.Setup.wixproj", "{F5958838-EBE5-4A76-A27F-5AFA9A8D7818}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
@ -170,6 +172,10 @@ Global
{2B8C6DAD-4D85-41B1-83FD-248D9F347522}.Debug|x86.Build.0 = Debug|x86
{2B8C6DAD-4D85-41B1-83FD-248D9F347522}.Release|x86.ActiveCfg = Release|x86
{2B8C6DAD-4D85-41B1-83FD-248D9F347522}.Release|x86.Build.0 = Release|x86
{F5958838-EBE5-4A76-A27F-5AFA9A8D7818}.Debug|x86.ActiveCfg = Debug|x86
{F5958838-EBE5-4A76-A27F-5AFA9A8D7818}.Debug|x86.Build.0 = Debug|x86
{F5958838-EBE5-4A76-A27F-5AFA9A8D7818}.Release|x86.ActiveCfg = Release|x86
{F5958838-EBE5-4A76-A27F-5AFA9A8D7818}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -191,9 +197,9 @@ Global
{3DCA7B58-B8B3-49AC-9D9E-56F4A0460976} = {486ADF86-DD89-4E19-B805-9D94F19800D9}
{95C11A9E-56ED-456A-8447-2C89C1139266} = {486ADF86-DD89-4E19-B805-9D94F19800D9}
{D12F7F2F-8A3C-415F-88FA-6DD061A84869} = {486ADF86-DD89-4E19-B805-9D94F19800D9}
{1B9A82C4-BCA1-4834-A33E-226F17BE070B} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}
{2B8C6DAD-4D85-41B1-83FD-248D9F347522} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}
{F6FC6BE7-0847-4817-A1ED-223DC647C3D7} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}
{1B9A82C4-BCA1-4834-A33E-226F17BE070B} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EnterpriseLibraryConfigurationToolBinariesPath = packages\Unity.2.1.505.0\lib\NET35;packages\Unity.2.1.505.2\lib\NET35

@ -2,8 +2,7 @@
using System.ComponentModel;
using System.Windows.Forms;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Processes;
using NzbDrone.Host.Owin;
using NzbDrone.Host;
namespace NzbDrone.SysTray
{
@ -14,16 +13,14 @@ namespace NzbDrone.SysTray
public class SystemTrayApp : Form, ISystemTrayApp
{
private readonly IProcessProvider _processProvider;
private readonly IHostController _hostController;
private readonly IBrowserService _browserService;
private readonly NotifyIcon _trayIcon = new NotifyIcon();
private readonly ContextMenu _trayMenu = new ContextMenu();
public SystemTrayApp(IProcessProvider processProvider, IHostController hostController)
public SystemTrayApp(IBrowserService browserService)
{
_processProvider = processProvider;
_hostController = hostController;
_browserService = browserService;
}
@ -84,7 +81,7 @@ namespace NzbDrone.SysTray
{
try
{
_processProvider.OpenDefaultBrowser(_hostController.AppUrl);
_browserService.LaunchWebUI();
}
catch (Exception)
{

@ -10,22 +10,22 @@ namespace NzbDrone
{
public static class WindowsApp
{
private static readonly Logger Logger = NzbDroneLogger.GetLogger();
private static readonly Logger Logger = NzbDroneLogger.GetLogger();
public static void Main(string[] args)
{
try
{
var startupArgs = new StartupArguments(args);
var startupArgs = new StartupContext(args);
LogTargets.Register(startupArgs, false, true);
var container = Bootstrap.Start(startupArgs, new MessageBoxUserAlert());
container.Register<ISystemTrayApp, SystemTrayApp>();
container.Resolve<ISystemTrayApp>().Start();
}
catch (TerminateApplicationException)
{
Bootstrap.Start(startupArgs, new MessageBoxUserAlert(), container =>
{
container.Register<ISystemTrayApp, SystemTrayApp>();
var trayApp = container.Resolve<ISystemTrayApp>();
trayApp.Start();
});
}
catch (Exception e)
{
@ -34,5 +34,7 @@ namespace NzbDrone
MessageBox.Show(text: message, buttons: MessageBoxButtons.OK, icon: MessageBoxIcon.Error, caption: "Epic Fail!");
}
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
<copyright file="app.config" company="Outercurve Foundation">
Copyright (c) 2004, Outercurve Foundation.
This software is released under Microsoft Reciprocal License (MS-RL).
The license and further copyright text can be found in the file
LICENSE.TXT at the root directory of the distribution.
</copyright>
-->
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0" />
<supportedRuntime version="v2.0.50727" />
</startup>
</configuration>

Binary file not shown.

Binary file not shown.

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
<copyright file="app.config" company="Outercurve Foundation">
Copyright (c) 2004, Outercurve Foundation.
This software is released under Microsoft Reciprocal License (MS-RL).
The license and further copyright text can be found in the file
LICENSE.TXT at the root directory of the distribution.
</copyright>
-->
<configuration>
<appSettings>
<add key="extensions" value="WixIIsExtension;WixUtilExtension;WixVSExtension"/>
</appSettings>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0" />
<supportedRuntime version="v2.0.50727" />
</startup>
</configuration>

Binary file not shown.

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
<copyright file="app.config" company="Outercurve Foundation">
Copyright (c) 2004, Outercurve Foundation.
This software is released under Microsoft Reciprocal License (MS-RL).
The license and further copyright text can be found in the file
LICENSE.TXT at the root directory of the distribution.
</copyright>
-->
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0" />
<supportedRuntime version="v2.0.50727" />
</startup>
</configuration>

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -1,44 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="257311fe-7c96-49df-8863-40930831d296" Name="NzbDrone" Language="1033" Version="$(env.BUILD_NUMBER)" Manufacturer="NzbDrone" UpgradeCode="56833D74-A480-4CA2-B562-5A018B3A0F99">
<Package Id="*"
Description="NzbDrone"
InstallerVersion="200"
Compressed="yes"
InstallPrivileges="elevated"
InstallScope="perUser"
Platform="x86"
Manufacturer="NzbDrone"
/>
<UIRef Id="WixUI_Minimal" />
<UIRef Id="WixUI_ErrorProgressText" />
<Media Id="1" Cabinet="nzbdrone.cab" EmbedCab="yes" CompressionLevel="high" />
<Property Id="ARPPRODUCTICON" Value="APP_ICON" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="CommonAppDataFolder" Name="ProgramData">
<Directory Id="ROOT_DIR" Name="NzbDrone">
<Directory Id="BIN_DIR" Name="bin">
<Component Id="BIN_COMP" DiskId="1" Guid="417fd784-e2f5-435d-92d1-220c140d0042">
<RemoveFolder Id="BIN_DIR" On="uninstall" />
</Component>
</Directory>
</Directory>
</Directory>
</Directory>
<MajorUpgrade AllowDowngrades="no" AllowSameVersionUpgrades="yes" MigrateFeatures="yes" Schedule="afterInstallValidate" DowngradeErrorMessage="Newer version of Nzbdrone is already installed." />
<Feature Id="Core" Title="NzbDrone Core" Description="NzbDrone Core" Level="1" Display='expand'>
<ComponentRef Id="BIN_COMP" />
<ComponentGroupRef Id="OUTPUT_GROUP" />
</Feature>
<PropertyRef Id="NETFRAMEWORK40FULL" />
<Property Id="INSTALL_WINDOWS_SERVICE" Value="1" />
<Condition Message="This application requires .NET Framework 4.0 or later. Please install the .NET Framework then run this installer again.">NETFRAMEWORK40FULL</Condition>
<Icon Id="APP_ICON" SourceFile="..\_output\NzbDrone.exe" />
<InstallExecuteSequence>
<InstallInitialize />
<!-- <Custom Action="START_ACTION" After="InstallFiles" /> -->
<InstallFinalize />
</InstallExecuteSequence>
<CustomAction Id="START_ACTION" FileKey="NzbDrone.Console.exe" ExeCommand="" Execute="deferred" Impersonate="no" Return="asyncNoWait" />
</Product>
</Wix>

@ -1,5 +0,0 @@
bin\heat.exe dir ..\_output\ -out _output.wxs -cg OUTPUT_GROUP -dr BIN_DIR -gg -scom -srd -sfrag -sreg -suid -svb6
"bin\candle.exe" nzbdrone.wxs _output.wxs -ext WixNetFxExtension -ext WixUIExtension
"bin\light.exe" nzbdrone.wixobj _output.wixobj -out "nzbdrone.msi" -ext WixNetFxExtension -ext WixUIExtension -b ..\_output
Loading…
Cancel
Save