diff --git a/.gitignore b/.gitignore index e84f8e1..1bbdfbd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,22 @@ -.vscode/ + __pycache__/ state/ + +*.egg-info/ + +build/build-exe-config.json + +install/*.exe + +*.pyo + +*.spec + +build/build-exe-pyinstaller-command.bat + +build/build/BAPSicle/ + +build/output/ diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..c96ed4c --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,12 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Python: Launch Server Standalone", + "type": "python", + "request": "launch", + "program": "./launch_standalone.py", + "console": "integratedTerminal" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 84a8833..025393e 100644 --- a/README.md +++ b/README.md @@ -1 +1,47 @@ -bapsicle +# Bapsicle +### a.k.a. The Next-Gen BAPS server + +!["BAPSicle logo, a pink melting ice lolly."](/dev/logo.png "BAPSicle Logo") + +Welcome! This is BAPS. More acurately, this is yet another attempt at a BAPS3 server. + +## Installing + +Just want to install BAPSicle? + +### Windows + +Currently there's just a batch script. Simply run ``install.bat`` as administrator. If you've just built BAPSicle youself, it'll be in ``/install`` folder. + +This will: +* Copy BAPSicle into ``C:\Program Files\BAPSicle`` +* Install BAPSicle.exe as a Windows Service with NSSM. +* If all goes well, open [http://localhost:5000](localhost:5000) for the server UI. + +### Linux + +Installed service for linux is comming soon. Testing is primarily on Ubuntu 20.04. Your milage with other distros will vary. + +### MacOS + +Support for MacOS will be the last to come, sorry about that. + +## Developing + +### Requirements + +* Python 3.7 (3.8 may also work, 3.9 is unlikely to.) +* Git (Obviously) + +### Running +To just run the server standaline without installing, run ``python ./launch_standalone.py``. + +### Building + +Currently mostly Windows focused. + +To build a BAPSicle.exe, run ``build\build-exe.py``. The resulting file will appear in ``build\output``. You can then use the install instructions above to install it, or just run it standalone. + +### Other bits + +Provided is a VScode debug config to let you debug live, as well as ``dev\install-githook.bat`` that will help git to clean your code up as you're committing! \ No newline at end of file diff --git a/__init__.py b/__init__.py index e69de29..cb45bae 100644 --- a/__init__.py +++ b/__init__.py @@ -0,0 +1,5 @@ +# see +import logging +logging.getLogger(__name__).addHandler(logging.NullHandler()) +logging.basicConfig(filename='bapsicle_log.log', level=logging.INFO) +logging.info('Started Logging') diff --git a/build/build-exe-config.template.json b/build/build-exe-config.template.json new file mode 100644 index 0000000..147e9f0 --- /dev/null +++ b/build/build-exe-config.template.json @@ -0,0 +1,77 @@ +{ + "version": "auto-py-to-exe-configuration_v1", + "pyinstallerOptions": [ + { + "optionDest": "noconfirm", + "value": true + }, + { + "optionDest": "filenames", + "value": "\\launch_standalone.py" + }, + { + "optionDest": "onefile", + "value": true + }, + { + "optionDest": "console", + "value": true + }, + { + "optionDest": "icon_file", + "value": "\\build\\icon.ico" + }, + { + "optionDest": "name", + "value": "BAPSicle" + }, + { + "optionDest": "ascii", + "value": false + }, + { + "optionDest": "clean_build", + "value": true + }, + { + "optionDest": "strip", + "value": false + }, + { + "optionDest": "noupx", + "value": false + }, + { + "optionDest": "uac_admin", + "value": true + }, + { + "optionDest": "uac_uiaccess", + "value": false + }, + { + "optionDest": "win_private_assemblies", + "value": false + }, + { + "optionDest": "win_no_prefer_redirects", + "value": false + }, + { + "optionDest": "bootloader_ignore_signals", + "value": false + }, + { + "optionDest": "datas", + "value": "\\templates;templates/" + }, + { + "optionDest": "datas", + "value": "\\ui-static;ui-static/" + } + ], + "nonPyinstallerOptions": { + "increaseRecursionLimit": false, + "manualArguments": "" + } +} \ No newline at end of file diff --git a/build/build-exe.bat b/build/build-exe.bat new file mode 100644 index 0000000..a2cf6e0 --- /dev/null +++ b/build/build-exe.bat @@ -0,0 +1,18 @@ +cd /D "%~dp0" +pip install -r requirements.txt +pip install -r requirements-windows.txt +pip install -e ..\ + +: Generate the json config in case you wanted to use the gui to regenerate the command below manually. +python generate-build-exe-config.py + +: auto-py-to-exe -c build-exe-config.json -o ../install + +python build-exe.py + +build-exe-pyinstaller-command.bat + +del *.spec /q + +echo "Output file should be located in 'output/' folder." +TIMEOUT 5 \ No newline at end of file diff --git a/build/build-exe.py b/build/build-exe.py new file mode 100644 index 0000000..d9dc3ea --- /dev/null +++ b/build/build-exe.py @@ -0,0 +1,43 @@ +import sys +import json + +file = open('build-exe-config.json', 'r') +config = json.loads(file.read()) +file.close() + +cmd_str = "pyinstaller " +json_dests = ["icon_file", "clean_build"] +pyi_dests = ["icon", "clean"] + +for option in config["pyinstallerOptions"]: + + option_dest = option["optionDest"] + + # The json is rather inconsistent :/ + if option_dest in json_dests: + print("in") + option_dest = pyi_dests[json_dests.index(option_dest)] + + option_dest = option_dest.replace("_", "-") + + if option_dest == "datas": + cmd_str += '--add-data "' + option["value"] + '" ' + elif option_dest == "filenames": + filename = option["value"] + elif option["value"] == True: + cmd_str += "--" + str(option_dest) + " " + elif option["value"] == False: + pass + else: + cmd_str += "--" + str(option_dest) + ' "' + str(option["value"]) + '" ' + + +command = open('build-exe-pyinstaller-command.bat', 'w') + +if filename == "": + print("No filename data was found in json file.") + command.write("") +else: + command.write(cmd_str + ' --distpath "output/" --workpath "build/" "' + filename + '"') + +command.close() diff --git a/build/generate-build-exe-config.py b/build/generate-build-exe-config.py new file mode 100644 index 0000000..5fc1e0d --- /dev/null +++ b/build/generate-build-exe-config.py @@ -0,0 +1,17 @@ +import json +import os + +dir_path = os.path.dirname(os.path.realpath(__file__)) +parent_path = os.path.dirname(dir_path) + +in_file = open('build-exe-config.template.json', 'r') +config = json.loads(in_file.read()) +in_file.close() + +for option in config["pyinstallerOptions"]: + if option["optionDest"] in ["datas", "filenames", "icon_file"]: + option["value"] = os.path.abspath(parent_path + option["value"]) + +out_file = open('build-exe-config.json', 'w') +out_file.write(json.dumps(config, indent=2)) +out_file.close() diff --git a/build/icon.ico b/build/icon.ico new file mode 100644 index 0000000..240c4fd Binary files /dev/null and b/build/icon.ico differ diff --git a/build/requirements-windows.txt b/build/requirements-windows.txt new file mode 100644 index 0000000..8af44a0 --- /dev/null +++ b/build/requirements-windows.txt @@ -0,0 +1,2 @@ +pywin32 +auto-py-to-exe \ No newline at end of file diff --git a/install/requirements.txt b/build/requirements.txt similarity index 68% rename from install/requirements.txt rename to build/requirements.txt index a6b9845..0e55fef 100644 --- a/install/requirements.txt +++ b/build/requirements.txt @@ -2,4 +2,5 @@ pygame==2.0.0.dev20 flask mutagen sounddevice -autopep8 \ No newline at end of file +autopep8 +setproctitle \ No newline at end of file diff --git a/dev/install-githook.bat b/dev/install-githook.bat new file mode 100644 index 0000000..1db15ca --- /dev/null +++ b/dev/install-githook.bat @@ -0,0 +1,2 @@ +cd %~dp0 +copy /Y ".\pre-commit" "..\.git\hooks\" \ No newline at end of file diff --git a/dev/logo.png b/dev/logo.png new file mode 100644 index 0000000..f8d1e66 Binary files /dev/null and b/dev/logo.png differ diff --git a/helpers/os_environment.py b/helpers/os_environment.py new file mode 100644 index 0000000..c1497c9 --- /dev/null +++ b/helpers/os_environment.py @@ -0,0 +1,42 @@ +import sys +import os + +# Check if we're running inside a pyinstaller bundled (it's an exe) + + +def isBundelled(): + return getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS') + + +def isWindows(): + return sys.platform.startswith('win32') + + +def isLinux(): + return sys.platform.startswith('linux') + + +def isMacOS(): + return sys.platform.startswith('darwin') + +# This must be used to that relative file paths resolve inside the bundled versions. + + +def resolve_local_file_path(relative_path): + """ Get absolute path to resource, works for dev and for PyInstaller """ + try: + # PyInstaller creates a temp folder and stores path in _MEIPASS + base_path = sys._MEIPASS + except Exception: + base_path = os.path.abspath(".") + + return os.path.join(base_path, relative_path) + +# Use this to resolve paths to resources not bundled within the bundled exe. + + +def resolve_external_file_path(relative_path): + if (not relative_path.startswith("/")): + relative_path = "/" + relative_path + # Pass through abspath to correct any /'s with \'s on Windows + return os.path.abspath(os.getcwd() + relative_path) diff --git a/install/SMWinservice.py b/install/SMWinservice.py deleted file mode 100644 index def79ca..0000000 --- a/install/SMWinservice.py +++ /dev/null @@ -1,98 +0,0 @@ -''' -SMWinservice -by Davide Mastromatteo - -Base class to create winservice in Python ------------------------------------------ - -Instructions: - -1. Just create a new class that inherits from this base class -2. Define into the new class the variables - _svc_name_ = "nameOfWinservice" - _svc_display_name_ = "name of the Winservice that will be displayed in scm" - _svc_description_ = "description of the Winservice that will be displayed in scm" -3. Override the three main methods: - def start(self) : if you need to do something at the service initialization. - A good idea is to put here the inizialization of the running condition - def stop(self) : if you need to do something just before the service is stopped. - A good idea is to put here the invalidation of the running condition - def main(self) : your actual run loop. Just create a loop based on your running condition -4. Define the entry point of your module calling the method "parse_command_line" of the new class -5. Enjoy -''' - -import socket - -import win32serviceutil - -import servicemanager -import win32event -import win32service - - -class SMWinservice(win32serviceutil.ServiceFramework): - '''Base class to create winservice in Python''' - - _svc_name_ = 'pythonService' - _svc_display_name_ = 'Python Service' - _svc_description_ = 'Python Service Description' - - @classmethod - def parse_command_line(cls): - ''' - ClassMethod to parse the command line - ''' - win32serviceutil.HandleCommandLine(cls) - - def __init__(self, args): - ''' - Constructor of the winservice - ''' - win32serviceutil.ServiceFramework.__init__(self, args) - self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) - socket.setdefaulttimeout(60) - - def SvcStop(self): - ''' - Called when the service is asked to stop - ''' - self.stop() - self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) - win32event.SetEvent(self.hWaitStop) - - def SvcDoRun(self): - ''' - Called when the service is asked to start - ''' - self.start() - servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, - servicemanager.PYS_SERVICE_STARTED, - (self._svc_name_, '')) - self.main() - - def start(self): - ''' - Override to add logic before the start - eg. running condition - ''' - pass - - def stop(self): - ''' - Override to add logic before the stop - eg. invalidating running condition - ''' - pass - - def main(self): - ''' - Main class to be ovverridden to add logic - ''' - pass - - -# entry point of the module: copy and paste into the new module -# ensuring you are calling the "parse_command_line" of the new created class -if __name__ == '__main__': - SMWinservice.parse_command_line() diff --git a/install/debug.bat b/install/debug.bat deleted file mode 100644 index a140f65..0000000 --- a/install/debug.bat +++ /dev/null @@ -1,2 +0,0 @@ -python C:\Users\matth\Documents\GitHub\bapsicle\install\windows_service.py debug -TIMEOUT 10 \ No newline at end of file diff --git a/install/install.bat b/install/install.bat index e6e4225..e582c70 100644 --- a/install/install.bat +++ b/install/install.bat @@ -1,12 +1,44 @@ -cd /D "%~dp0" -pip install -r requirements.txt -pip install -r requirements-windows.txt -pip install -e ..\ -python windows_service.py install +set install_path="C:\Program Files\BAPSicle" +set exe_name="BAPSicle.exe" +set exe_path=%install_path%\\%exe_name% +set service_name="BAPSicle" -mkdir "C:\Program Files\BAPSicle" -cd "C:\Program Files\BAPSicle\" -mkdir state +mkdir %install_path% +mkdir %install_path%\state -copy "C:\Program Files\Python37\Lib\site-packages\pywin32_system32\pywintypes37.dll" "C:\Program Files\Python37\Lib\site-packages\win32\" -TIMEOUT 10 \ No newline at end of file + +cd %~dp0\nssm +nssm stop %service_name% +nssm remove %service_name% confirm +sc.exe delete %service_name% + +cd %install_path% + + +copy /Y "%~dp0\uninstall.bat" . +copy /Y "%~dp0\..\build\output\%exe_name%" %exe_name% + +mkdir nssm +cd nssm +copy /Y "%~dp0\nssm\nssm.exe" . +nssm install %service_name% %exe_path% +nssm set %service_name% AppDirectory %install_path% +nssm set %service_name% AppExit Default Restart +nssm set %service_name% AppStopMethodConsole 5000 +nssm set %service_name% AppStopMethodWindow 5000 +nssm set %service_name% AppStopMethodThreads 5000 +nssm set %service_name% DisplayName "BAPSicle Server" +nssm set %service_name% Description "The next gen Broadcast and Presenting Suite server! Access settings on port 5000." +nssm set %service_name% ObjectName LocalSystem +nssm set %service_name% Start SERVICE_AUTO_START +nssm set %service_name% Type SERVICE_INTERACTIVE_PROCESS + +: usefull tools are edit and dump: + +: nssm edit %service_name% +: nssm dump %service_name% +nssm start %service_name% + +timeout 4 /nobreak + +explorer "http://localhost:5000/" diff --git a/install/nssm/ChangeLog.txt b/install/nssm/ChangeLog.txt new file mode 100644 index 0000000..6762ca8 --- /dev/null +++ b/install/nssm/ChangeLog.txt @@ -0,0 +1,257 @@ +Changes since 2.24 +------------------ + * Allow skipping kill_process_tree(). + + * NSSM can now sleep a configurable amount of time after + rotating output files. + + * NSSM can now rotate log files by calling CopyFile() + followed by SetEndOfFile(), allowing it to rotate files + which other processes hold open. + + * NSSM now sets the service environment before querying + parameters from the registry, so paths and arguments + can reference environment configured in AppEnvironment + or AppEnvironmentExtra. + +Changes since 2.23 +------------------ + * NSSM once again calls TerminateProcess() correctly. + +Changes since 2.22 +------------------ + * NSSM no longer clutters the event log with "The specified + procedure could not be found" on legacy Windows releases. + + * Fixed failure to set a local username to run the service. + +Changes since 2.21 +------------------ + * Existing services can now be managed using the GUI + or on the command line. + + * NSSM can now set the priority class and processor + affinity of the managed application. + + * NSSM can now apply an unconditional delay before + restarting the application. + + * NSSM can now optionally rotate existing files when + redirecting I/O. + + * Unqualified path names are now relative to the + application startup directory when redirecting I/O. + + * NSSM can now set the service display name, description, + startup type and log on details. + + * All services now receive a standard console window, + allowing them to read input correctly (if running in + interactive mode). + +Changes since 2.20 +------------------ + * Services installed from the GUI no longer have incorrect + AppParameters set in the registry. + +Changes since 2.19 +------------------ + * Services installed from the commandline without using the + GUI no longer have incorrect AppStopMethod* registry + entries set. + +Changes since 2.18 +------------------ + * Support AppEnvironmentExtra to append to the environment + instead of replacing it. + + * The GUI is significantly less sucky. + +Changes since 2.17 +------------------ + * Timeouts for each shutdown method can be configured in + the registry. + + * The GUI is slightly less sucky. + +Changes since 2.16 +------------------ + * NSSM can now redirect the service's I/O streams to any path + capable of being opened by CreateFile(). + + * Allow building on Visual Studio Express. + + * Silently ignore INTERROGATE control. + + * Try to send Control-C events to console applications when + shutting them down. + +Changes since 2.15 +------------------ + * Fixed case where NSSM could kill unrelated processes when + shutting down. + +Changes since 2.14 +------------------ + * NSSM is now translated into Italian. + + * Fixed GUI not allowing paths longer than 256 characters. + +Changes since 2.13 +------------------ + * Fixed default GUI language being French not English. + +Changes since 2.12 +------------------ + * Fixed failure to run on Windows 2000. + +Changes since 2.11 +------------------ + * NSSM is now translated into French. + + * Really ensure systems recovery actions can happen. + + The change supposedly introduced in v2.4 to allow service recovery + actions to be activated when the application exits gracefully with + a non-zero error code didn't actually work. + +Changes since 2.10 +------------------ + * Support AppEnvironment for compatibility with srvany. + +Changes since 2.9 +----------------- + * Fixed failure to compile messages.mc in paths containing spaces. + + * Fixed edge case with CreateProcess(). + + Correctly handle the case where the application executable is under + a path which contains space and an executable sharing the initial + part of that path (up to a space) exists. + +Changes since 2.8 +----------------- + * Fixed failure to run on Windows versions prior to Vista. + +Changes since 2.7 +----------------- + * Read Application, AppDirectory and AppParameters before each restart so + a change to any one doesn't require restarting NSSM itself. + + * Fixed messages not being sent to the event log correctly in some + cases. + + * Try to handle (strictly incorrect) quotes in AppDirectory. + + Windows directories aren't allowed to contain quotes so CreateProcess() + will fail if the AppDirectory is quoted. Note that it succeeds even if + Application itself is quoted as the application plus parameters are + interpreted as a command line. + + * Fixed failed to write full arguments to AppParameters when + installing a service. + + * Throttle restarts. + + Back off from restarting the application immediately if it starts + successfully but exits too soon. The default value of "too soon" is + 1500 milliseconds. This can be configured by adding a DWORD value + AppThrottle to the registry. + + Handle resume messages from the service console to restart the + application immediately even if it is throttled. + + * Try to kill the process tree gracefully. + + Before calling TerminateProcess() on all processes assocatiated with + the monitored application, enumerate all windows and threads and + post appropriate messages to them. If the application bothers to + listen for such messages it has a chance to shut itself down gracefully. + +Changes since 2.6 +----------------- + * Handle missing registry values. + + Warn if AppParameters is missing. Warn if AppDirectory is missing or + unset and choose a fallback directory. + First try to find the parent directory of the application. If that + fails, eg because the application path is just "notepad" or something, + start in the Windows directory. + + * Kill process tree when stopping service. + + Ensure that all child processes of the monitored application are + killed when the service stops by recursing through all running + processes and terminating those whose parent is the application + or one of its descendents. + +Changes since 2.5 +----------------- + * Removed incorrect ExpandEnvironmentStrings() error. + + A log_event() call was inadvertently left in the code causing an error + to be set to the eventlog saying that ExpandEnvironmentStrings() had + failed when it had actually succeeded. + +Changes since 2.4 +----------------- + * Allow use of REG_EXPAND_SZ values in the registry. + + * Don't suicide on exit status 0 by default. + + Suiciding when the application exits 0 will cause recovery actions to be + taken. Usually this is inappropriate. Only suicide if there is an + explicit AppExit value for 0 in the registry. + + Technically such behaviour could be abused to do something like run a + script after successful completion of a service but in most cases a + suicide is undesirable when no actual failure occurred. + + * Don't hang if startup parameters couldn't be determined. + Instead, signal that the service entered the STOPPED state. + Set START_PENDING state prior to actual startup. + +Changes since 2.3 +----------------- + * Ensure systems recovery actions can happen. + + In Windows versions earlier than Vista the service manager would only + consider a service failed (and hence eligible for recovery action) if + the service exited without setting its state to SERVICE_STOPPED, even if + it signalled an error exit code. + In Vista and later the service manager can be configured to treat a + graceful shutdown with error code as a failure but this is not the + default behaviour. + + Try to configure the service manager to use the new behaviour when + starting the service so users who set AppExit to Exit can use recovery + actions as expected. + + Also recognise the new AppExit option Suicide for use on pre-Vista + systems. When AppExit is Suicide don't stop the service but exit + inelegantly, which should be seen as a failure. + +Changes since 2.2 +----------------- + * Send properly formatted messages to the event log. + + * Fixed truncation of very long path lengths in the registry. + +Changes since 2.1 +----------------- + * Decide how to handle application exit. + + When the service exits with exit code n look in + HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppExit\, + falling back to the unnamed value if no such code is listed. Parse the + (string) value of this entry as follows: + + Restart: Start the application again (NSSM default). + Ignore: Do nothing (srvany default). + Exit: Stop the service. + +Changes since 2.0 +----------------- + * Added support for building a 64-bit executable. + + * Added project files for newer versions of Visual Studio. diff --git a/install/nssm/README.txt b/install/nssm/README.txt new file mode 100644 index 0000000..be6db69 --- /dev/null +++ b/install/nssm/README.txt @@ -0,0 +1,1051 @@ +NSSM: The Non-Sucking Service Manager +Version 2.24, 2014-08-31 + +NSSM is a service helper program similar to srvany and cygrunsrv. It can +start any application as an NT service and will restart the service if it +fails for any reason. + +NSSM also has a graphical service installer and remover. + +Full documentation can be found online at + + http://nssm.cc/ + +Since version 2.0, the GUI can be bypassed by entering all appropriate +options on the command line. + +Since version 2.1, NSSM can be compiled for x64 platforms. +Thanks Benjamin Mayrargue. + +Since version 2.2, NSSM can be configured to take different actions +based on the exit code of the managed application. + +Since version 2.3, NSSM logs to the Windows event log more elegantly. + +Since version 2.5, NSSM respects environment variables in its parameters. + +Since version 2.8, NSSM tries harder to shut down the managed application +gracefully and throttles restart attempts if the application doesn't run +for a minimum amount of time. + +Since version 2.11, NSSM respects srvany's AppEnvironment parameter. + +Since version 2.13, NSSM is translated into French. +Thanks François-Régis Tardy. + +Since version 2.15, NSSM is translated into Italian. +Thanks Riccardo Gusmeroli. + +Since version 2.17, NSSM can try to shut down console applications by +simulating a Control-C keypress. If they have installed a handler routine +they can clean up and shut down gracefully on receipt of the event. + +Since version 2.17, NSSM can redirect the managed application's I/O streams +to an arbitrary path. + +Since version 2.18, NSSM can be configured to wait a user-specified amount +of time for the application to exit when shutting down. + +Since version 2.19, many more service options can be configured with the +GUI installer as well as via the registry. + +Since version 2.19, NSSM can add to the service's environment by setting +AppEnvironmentExtra in place of or in addition to the srvany-compatible +AppEnvironment. + +Since version 2.22, NSSM can set the managed application's process priority +and CPU affinity. + +Since version 2.22, NSSM can apply an unconditional delay before restarting +an application which has exited. + +Since version 2.22, NSSM can rotate existing output files when redirecting I/O. + +Since version 2.22, NSSM can set service display name, description, startup +type, log on details and dependencies. + +Since version 2.22, NSSM can manage existing services. + +Since version 2.25, NSSM can execute commands in response to service events. + +Since version 2.25, NSSM can list services it manages. + +Since version 2.25, NSSM can dump the configuration of services it manages. + +Since version 2.25, NSSM can show the processes managed by a service. + + +Usage +----- +In the usage notes below, arguments to the program may be written in angle +brackets and/or square brackets. means you must insert the +appropriate string and [] means the string is optional. See the +examples below... + +Note that everywhere appears you may substitute the +service's display name. + + +Installation using the GUI +-------------------------- +To install a service, run + + nssm install + +You will be prompted to enter the full path to the application you wish +to run and any command line options to pass to that application. + +Use the system service manager (services.msc) to control advanced service +properties such as startup method and desktop interaction. NSSM may +support these options at a later time... + + +Installation using the command line +----------------------------------- +To install a service, run + + nssm install [] + +NSSM will then attempt to install a service which runs the named application +with the given options (if you specified any). + +Don't forget to enclose paths in "quotes" if they contain spaces! + +If you want to include quotes in the options you will need to """quote""" the +quotes. + + +Managing the service +-------------------- +NSSM will launch the application listed in the registry when you send it a +start signal and will terminate it when you send a stop signal. So far, so +much like srvany. But NSSM is the Non-Sucking service manager and can take +action if/when the application dies. + +With no configuration from you, NSSM will try to restart itself if it notices +that the application died but you didn't send it a stop signal. NSSM will +keep trying, pausing between each attempt, until the service is successfully +started or you send it a stop signal. + +NSSM will pause an increasingly longer time between subsequent restart attempts +if the service fails to start in a timely manner, up to a maximum of four +minutes. This is so it does not consume an excessive amount of CPU time trying +to start a failed application over and over again. If you identify the cause +of the failure and don't want to wait you can use the Windows service console +(where the service will be shown in Paused state) to send a continue signal to +NSSM and it will retry within a few seconds. + +By default, NSSM defines "a timely manner" to be within 1500 milliseconds. +You can change the threshold for the service by setting the number of +milliseconds as a REG_DWORD value in the registry at +HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppThrottle. + +Alternatively, NSSM can pause for a configurable amount of time before +attempting to restart the application even if it successfully ran for the +amount of time specified by AppThrottle. NSSM will consult the REG_DWORD value +at HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppRestartDelay +for the number of milliseconds to wait before attempting a restart. If +AppRestartDelay is set and the application is determined to be subject to +throttling, NSSM will pause the service for whichever is longer of the +configured restart delay and the calculated throttle period. + +If AppRestartDelay is missing or invalid, only throttling will be applied. + +NSSM will look in the registry under +HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppExit for +string (REG_EXPAND_SZ) values corresponding to the exit code of the application. +If the application exited with code 1, for instance, NSSM will look for a +string value under AppExit called "1" or, if it does not find it, will +fall back to the AppExit (Default) value. You can find out the exit code +for the application by consulting the system event log. NSSM will log the +exit code when the application exits. + +Based on the data found in the registry, NSSM will take one of three actions: + +If the value data is "Restart" NSSM will try to restart the application as +described above. This is its default behaviour. + +If the value data is "Ignore" NSSM will not try to restart the application +but will continue running itself. This emulates the (usually undesirable) +behaviour of srvany. The Windows Services console would show the service +as still running even though the application has exited. + +If the value data is "Exit" NSSM will exit gracefully. The Windows Services +console would show the service as stopped. If you wish to provide +finer-grained control over service recovery you should use this code and +edit the failure action manually. Please note that Windows versions prior +to Vista will not consider such an exit to be a failure. On older versions +of Windows you should use "Suicide" instead. + +If the value data is "Suicide" NSSM will simulate a crash and exit without +informing the service manager. This option should only be used for +pre-Vista systems where you wish to apply a service recovery action. Note +that if the monitored application exits with code 0, NSSM will only honour a +request to suicide if you explicitly configure a registry key for exit code 0. +If only the default action is set to Suicide NSSM will instead exit gracefully. + + +Application priority +-------------------- +NSSM can set the priority class of the managed application. NSSM will look in +the registry under HKLM\SYSTEM\CurrentControlSet\Services\\Parameters +for the REG_DWORD entry AppPriority. Valid values correspond to arguments to +SetPriorityClass(). If AppPriority() is missing or invalid the +application will be launched with normal priority. + + +Processor affinity +------------------ +NSSM can set the CPU affinity of the managed application. NSSM will look in +the registry under HKLM\SYSTEM\CurrentControlSet\Services\\Parameters +for the REG_SZ entry AppAffinity. It should specify a comma-separated listed +of zero-indexed processor IDs. A range of processors may optionally be +specified with a dash. No other characters are allowed in the string. + +For example, to specify the first; second; third and fifth CPUs, an appropriate +AppAffinity would be 0-2,4. + +If AppAffinity is missing or invalid, NSSM will not attempt to restrict the +application to specific CPUs. + +Note that the 64-bit version of NSSM can configure a maximum of 64 CPUs in this +way and that the 32-bit version can configure a maxium of 32 CPUs even when +running on 64-bit Windows. + + +Stopping the service +-------------------- +When stopping a service NSSM will attempt several different methods of killing +the monitored application, each of which can be disabled if necessary. + +First NSSM will attempt to generate a Control-C event and send it to the +application's console. Batch scripts or console applications may intercept +the event and shut themselves down gracefully. GUI applications do not have +consoles and will not respond to this method. + +Secondly NSSM will enumerate all windows created by the application and send +them a WM_CLOSE message, requesting a graceful exit. + +Thirdly NSSM will enumerate all threads created by the application and send +them a WM_QUIT message, requesting a graceful exit. Not all applications' +threads have message queues; those which do not will not respond to this +method. + +Finally NSSM will call TerminateProcess() to request that the operating +system forcibly terminate the application. TerminateProcess() cannot be +trapped or ignored, so in most circumstances the application will be killed. +However, there is no guarantee that it will have a chance to perform any +tidyup operations before it exits. + +Any or all of the methods above may be disabled. NSSM will look for the +HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppStopMethodSkip +registry value which should be of type REG_DWORD set to a bit field describing +which methods should not be applied. + + If AppStopMethodSkip includes 1, Control-C events will not be generated. + If AppStopMethodSkip includes 2, WM_CLOSE messages will not be posted. + If AppStopMethodSkip includes 4, WM_QUIT messages will not be posted. + If AppStopMethodSkip includes 8, TerminateProcess() will not be called. + +If, for example, you knew that an application did not respond to Control-C +events and did not have a thread message queue, you could set AppStopMethodSkip +to 5 and NSSM would not attempt to use those methods to stop the application. + +Take great care when including 8 in the value of AppStopMethodSkip. If NSSM +does not call TerminateProcess() it is possible that the application will not +exit when the service stops. + +By default NSSM will allow processes 1500ms to respond to each of the methods +described above before proceeding to the next one. The timeout can be +configured on a per-method basis by creating REG_DWORD entries in the +registry under HKLM\SYSTEM\CurrentControlSet\Services\\Parameters. + + AppStopMethodConsole + AppStopMethodWindow + AppStopMethodThreads + +Each value should be set to the number of milliseconds to wait. Please note +that the timeout applies to each process in the application's process tree, +so the actual time to shutdown may be longer than the sum of all configured +timeouts if the application spawns multiple subprocesses. + +To skip applying the above stop methods to all processes in the application's +process tree, applying them only to the original application process, set the +HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppKillProcessTree +registry value, which should be of type REG_DWORD, to 0. + + +Console window +-------------- +By default, NSSM will create a console window so that applications which +are capable of reading user input can do so - subject to the service being +allowed to interact with the desktop. + +Creation of the console can be suppressed by setting the integer (REG_DWORD) +HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppNoConsole +registry value to 1. + + +I/O redirection +--------------- +NSSM can redirect the managed application's I/O to any path capable of being +opened by CreateFile(). This enables, for example, capturing the log output +of an application which would otherwise only write to the console or accepting +input from a serial port. + +NSSM will look in the registry under +HKLM\SYSTEM\CurrentControlSet\Services\\Parameters for the keys +corresponding to arguments to CreateFile(). All are optional. If no path is +given for a particular stream it will not be redirected. If a path is given +but any of the other values are omitted they will be receive sensible defaults. + + AppStdin: Path to receive input. + AppStdout: Path to receive output. + AppStderr: Path to receive error output. + +Parameters for CreateFile() are providing with the "AppStdinShareMode", +"AppStdinCreationDisposition" and "AppStdinFlagsAndAttributes" values (and +analogously for stdout and stderr). + +In general, if you want the service to log its output, set AppStdout and +AppStderr to the same path, eg C:\Users\Public\service.log, and it should +work. Remember, however, that the path must be accessible to the user +running the service. + + +File rotation +------------- +When using I/O redirection, NSSM can rotate existing output files prior to +opening stdout and/or stderr. An existing file will be renamed with a +suffix based on the file's last write time, to millisecond precision. For +example, the file nssm.log might be rotated to nssm-20131221T113939.457.log. + +NSSM will look in the registry under +HKLM\SYSTEM\CurrentControlSet\Services\\Parameters for REG_DWORD +entries which control how rotation happens. + +If AppRotateFiles is missing or set to 0, rotation is disabled. Any non-zero +value enables rotation. + +If AppRotateSeconds is non-zero, a file will not be rotated if its last write +time is less than the given number of seconds in the past. + +If AppRotateBytes is non-zero, a file will not be rotated if it is smaller +than the given number of bytes. 64-bit file sizes can be handled by setting +a non-zero value of AppRotateBytesHigh. + +If AppRotateDelay is non-zero, NSSM will pause for the given number of +milliseconds after rotation. + +If AppStdoutCopyAndTruncate or AppStderrCopyAndTruncate are non-zero, the +stdout (or stderr respectively) file will be rotated by first taking a copy +of the file then truncating the original file to zero size. This allows +NSSM to rotate files which are held open by other processes, preventing the +usual MoveFile() from succeeding. Note that the copy process may take some +time if the file is large, and will temporarily consume twice as much disk +space as the original file. Note also that applications reading the log file +may not notice that the file size changed. Using this option in conjunction +with AppRotateDelay may help in that case. + +Rotation is independent of the CreateFile() parameters used to open the files. +They will be rotated regardless of whether NSSM would otherwise have appended +or replaced them. + +NSSM can also rotate files which hit the configured size threshold while the +service is running. Additionally, you can trigger an on-demand rotation by +running the command + + nssm rotate + +On-demand rotations will happen after the next line of data is read from +the managed application, regardless of the value of AppRotateBytes. Be aware +that if the application is not particularly verbose the rotation may not +happen for some time. + +To enable online and on-demand rotation, set AppRotateOnline to a non-zero +value. + +Note that online rotation requires NSSM to intercept the application's I/O +and create the output files on its behalf. This is more complex and +error-prone than simply redirecting the I/O streams before launching the +application. Therefore online rotation is not enabled by default. + + +Timestamping output +------------------- +When redirecting output, NSSM can prefix each line of output with a +millisecond-precision timestamp, for example: + + 2016-09-06 10:17:09.451 Pipeline main started + +To enable timestamp prefixing, set AppTimestampLog to a non-zero value. + +The prefix applies to both stdout and stderr. Prefixing requires +intercepting the application's I/O in the same way that online rotation +does. If log rotation and timestamp prefixing are both enabled, the +rotation will be online. + + +Environment variables +--------------------- +NSSM can replace or append to the managed application's environment. Two +multi-valued string (REG_MULTI_SZ) registry values are recognised under +HKLM\SYSTEM\CurrentControlSet\Services\\Parameters. + +AppEnvironment defines a list of environment variables which will override +the service's environment. AppEnvironmentExtra defines a list of +environment variables which will be added to the service's environment. + +Each entry in the list should be of the form KEY=VALUE. It is possible to +omit the VALUE but the = symbol is mandatory. + +Environment variables listed in both AppEnvironment and AppEnvironmentExtra +are subject to normal expansion, so it is possible, for example, to update the +system path by setting "PATH=C:\bin;%PATH%" in AppEnvironmentExtra. Variables +are expanded in the order in which they appear, so if you want to include the +value of one variable in another variable you should declare the dependency +first. + +Because variables defined in AppEnvironment override the existing +environment it is not possible to refer to any variables which were previously +defined. + +For example, the following AppEnvironment block: + + PATH=C:\Windows\System32;C:\Windows + PATH=C:\bin;%PATH% + +Would result in a PATH of "C:\bin;C:\Windows\System32;C:\Windows" as expected. + +Whereas the following AppEnvironment block: + + PATH=C:\bin;%PATH% + +Would result in a path containing only C:\bin and probably cause the +application to fail to start. + +Most people will want to use AppEnvironmentExtra exclusively. srvany only +supports AppEnvironment. + +As of version 2.25, NSSM parses AppEnvironment and AppEnvironmentExtra +itself, before reading any other registry values. As a result it is now +possible to refer to custom environment variables in Application, +AppDirectory and other parameters. + + +Merged service environment +-------------------------- +All Windows services can be passed additional environment variables by +creating a multi-valued string (REG_MULTI_SZ) registry value named +HLKM\SYSTEM\CurrentControlSet\Services\\Environment. + +The contents of this environment block will be merged into the system +environment before the service starts. + +Note, however, that the merged environment will be sorted alphabetically +before being processed. This means that in practice you cannot set, +for example, DIR=%PROGRAMFILES% in the Environment block because the +environment passed to the service will not have defined %PROGRAMFILES% +by the time it comes to define %DIR%. Environment variables defined in +AppEnvironmentExtra do not suffer from this limitation. + +As of version 2.25, NSSM can get and set the Environment block using +commands similar to: + + nssm get Environment + +It is worth reiterating that the Environment block is available to all +Windows services, not just NSSM services. + + +Service startup environment +--------------------------- +The environment NSSM passes to the application depends on how various +registry values are configured. The following flow describes how the +environment is modified. + +By default: + The service inherits the system environment. + +If \Environment is defined: + The contents of Environment are MERGED into the environment. + +If \Parameters\AppEnvironment is defined: + The service inherits the environment specified in AppEnvironment. + +If \Parameters\AppEnvironmentExtra is defined: + The contents of AppEnvironmentExtra are APPENDED to the environment. + +Note that AppEnvironment overrides the system environment and the +merged Environment block. Note also that AppEnvironmentExtra is +guaranteed to be appended to the startup environment if it is defined. + + +Event hooks +----------- +NSSM can run user-configurable commands in response to application events. +These commands are referred to as "hooks" below. + +All hooks are optional. Any hooks which are run will be launched with the +environment configured for the service. NSSM will place additional +variables into the environment which hooks can query to learn how and why +they were called. + +Hooks are categorised by Event and Action. Some hooks are run synchronously +and some are run asynchronously. Hooks prefixed with an *asterisk are run +synchronously. NSSM will wait for these hooks to complete before continuing +its work. Note, however, that ALL hooks are subject to a deadline after which +they will be killed, regardless of whether they are run asynchronously +or not. + + Event: Start - Triggered when the service is requested to start. + *Action: Pre - Called before NSSM attempts to launch the application. + Action: Post - Called after the application successfully starts. + + Event: Stop - Triggered when the service is requested to stop. + *Action: Pre - Called before NSSM attempts to kill the application. + + Event: Exit - Triggered when the application exits. + *Action: Post - Called after NSSM has cleaned up the application. + + Event: Rotate - Triggered when online log rotation is requested. + *Action: Pre - Called before NSSM rotates logs. + Action: Post - Called after NSSM rotates logs. + + Event: Power + Action: Change - Called when the system power status has changed. + Action: Resume - Called when the system has resumed from standby. + +Note that there is no Stop/Post hook. This is because Exit/Post is called +when the application exits, regardless of whether it did so in response to +a service shutdown request. Stop/Pre is only called before a graceful +shutdown attempt. + +NSSM sets the environment variable NSSM_HOOK_VERSION to a positive number. +Hooks can check the value of the number to determine which other environment +variables are available to them. + +If NSSM_HOOK_VERSION is 1 or greater, these variables are provided: + + NSSM_EXE - Path to NSSM itself. + NSSM_CONFIGURATION - Build information for the NSSM executable, + eg 64-bit debug. + NSSM_VERSION - Version of the NSSM executable. + NSSM_BUILD_DATE - Build date of NSSM. + NSSM_PID - Process ID of the running NSSM executable. + NSSM_DEADLINE - Deadline number of milliseconds after which NSSM will + kill the hook if it is still running. + NSSM_SERVICE_NAME - Name of the service controlled by NSSM. + NSSM_SERVICE_DISPLAYNAME - Display name of the service. + NSSM_COMMAND_LINE - Command line used to launch the application. + NSSM_APPLICATION_PID - Process ID of the primary application process. + May be blank if the process is not running. + NSSM_EVENT - Event class triggering the hook. + NSSM_ACTION - Event action triggering the hook. + NSSM_TRIGGER - Service control triggering the hook. May be blank if + the hook was not triggered by a service control, eg Exit/Post. + NSSM_LAST_CONTROL - Last service control handled by NSSM. + NSSM_START_REQUESTED_COUNT - Number of times the application was + requested to start. + NSSM_START_COUNT - Number of times the application successfully started. + NSSM_THROTTLE_COUNT - Number of times the application ran for less than + the throttle period. Reset to zero on successful start or when the + service is explicitly unpaused. + NSSM_EXIT_COUNT - Number of times the application exited. + NSSM_EXITCODE - Exit code of the application. May be blank if the + application is still running or has not started yet. + NSSM_RUNTIME - Number of milliseconds for which the NSSM executable has + been running. + NSSM_APPLICATION_RUNTIME - Number of milliseconds for which the + application has been running since it was last started. May be blank + if the application has not been started yet. + +Future versions of NSSM may provide more environment variables, in which +case NSSM_HOOK_VERSION will be set to a higher number. + +Hooks are configured by creating string (REG_EXPAND_SZ) values in the +registry named after the hook action and placed under +HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppEvents\. + +For example the service could be configured to restart when the system +resumes from standby by setting AppEvents\Power\Resume to: + + %NSSM_EXE% restart %NSSM_SERVICE_NAME% + +To set a hook on the command line, use + + nssm set AppEvents / + +Note that NSSM will abort the startup of the application if a Start/Pre hook +returns exit code of 99. + +A service will normally run hooks in the following order: + + Start/Pre + Start/Post + Stop/Pre + Exit/Post + +If the application crashes and is restarted by NSSM, the order might be: + + Start/Pre + Start/Post + Exit/Post + Start/Pre + Start/Post + Stop/Pre + Exit/Post + + +If NSSM is redirecting stdout or stderr it can be configured to redirect +the output of any hooks it runs. Set AppRedirectHooks to 1 to enable +that functionality. A hook can of course redirect its own I/O independently +of NSSM. + + +Managing services using the GUI +------------------------------- +NSSM can edit the settings of existing services with the same GUI that is +used to install them. Run + + nssm edit + +to bring up the GUI. + +NSSM offers limited editing capabilities for services other than those which +run NSSM itself. When NSSM is asked to edit a service which does not have +the App* registry settings described above, the GUI will allow editing only +system settings such as the service display name and description. + + +Managing services using the command line +---------------------------------------- +NSSM can retrieve or set individual service parameters from the command line. +In general the syntax is as follows, though see below for exceptions. + + nssm get + + nssm set + +Parameters can also be reset to their default values. + + nssm reset + +The parameter names recognised by NSSM are the same as the registry entry +names described above, eg AppDirectory. + +NSSM offers limited editing capabilities for Services other than those which +run NSSM itself. The parameters recognised are as follows: + + Description: Service description. + DisplayName: Service display name. + Environment: Service merged environment. + ImagePath: Path to the service executable. + ObjectName: User account which runs the service. + Name: Service key name. + Start: Service startup type. + Type: Service type. + +These correspond to the registry values under the service's key +HKLM\SYSTEM\CurrentControlSet\Services\. + + +Note that NSSM will concatenate all arguments passed on the command line +with spaces to form the value to set. Thus the following two invocations +would have the same effect. + + nssm set Description "NSSM managed service" + + nssm set Description NSSM managed service + + +Non-standard parameters +----------------------- +The AppEnvironment, AppEnvironmentExtra and Environment parameters +recognise an additional argument when querying the environment. The +following syntax will print all extra environment variables configured +for a service + + nssm get AppEnvironmentExtra + +whereas the syntax below will print only the value of the CLASSPATH +variable if it is configured in the environment block, or the empty string +if it is not configured. + + nssm get AppEnvironmentExtra CLASSPATH + +When setting an environment block, each variable should be specified as a +KEY=VALUE pair in separate command line arguments. For example: + + nssm set AppEnvironment CLASSPATH=C:\Classes TEMP=C:\Temp + +Alternatively the KEY can be prefixed with a + or - symbol to respectively +add or remove a pair from the block. + +The following two lines set CLASSPATH and TEMP: + + nssm set AppEnvironment CLASSPATH=C:\Classes + nssm set AppEnvironment +TEMP=C:\Temp + +If the key is already present, specifying +KEY will override the value +while preserving the order of keys: + + nssm set AppEnvironment +CLASSPATH=C:\NewClasses + +The following syntax removes a single variable from the block while +leaving any other variables in place. + + nssm set AppEnvironment -TEMP + +Specifying -KEY=VALUE will remove the variable only if the existing +value matches. + +The following syntax would not remove TEMP=C:\Temp + + nssm set AppEnvironment -TEMP=C:\Work\Temporary + +The + and - symbols are valid characters in environment variables. +The syntax :KEY=VALUE is equivalent to KEY=VALUE and can be used to +set variables which start with +/- or to explicitly reset the block in +a script: + + nssm set AppEnvironment :CLASSPATH=C:\Classes + nssm set AppEnvironment +TEMP=C:\Temp + + +The AppExit parameter requires an additional argument specifying the exit +code to get or set. The default action can be specified with the string +Default. + +For example, to get the default exit action for a service you should run + + nssm get AppExit Default + +To get the exit action when the application exits with exit code 2, run + + nssm get AppExit 2 + +Note that if no explicit action is configured for a specified exit code, +NSSM will print the default exit action. + +To set configure the service to stop when the application exits with an +exit code of 2, run + + nssm set AppExit 2 Exit + + +The AppPriority parameter is used to set the priority class of the +managed application. Valid priorities are as follows: + + REALTIME_PRIORITY_CLASS + HIGH_PRIORITY_CLASS + ABOVE_NORMAL_PRIORITY_CLASS + NORMAL_PRIORITY_CLASS + BELOW_NORMAL_PRIORITY_CLASS + IDLE_PRIORITY_CLASS + + +The DependOnGroup and DependOnService parameters are used to query or set +the dependencies for the service. When setting dependencies, each service +or service group (preceded with the + symbol) should be specified in +separate command line arguments. For example: + + nssm set DependOnService RpcSs LanmanWorkstation + +Alternatively the dependency name can be prefixed with a + or - symbol to +respectively add or remove a dependency. + +The following two lines set dependencies on RpcSs and LanmanWorkstation: + + nssm set DependOnService RpcSs + nssm set DependOnService +LanmanWorkstation + +The follwing syntax removes the dependency on RpcSs: + + nssm set DependOnService -RpcSs + +Service groups should, strictly speaking, be prefixed with the + symbol. +To specify a single dependency on a group, the + symbol can be prefixed +with the : symbol. + +The following lines are equivalent, and each set a dependency ONLY on +NetBIOSGroup: + + nssm set DependOnGroup NetBIOSGroup + nssm set DependOnGroup :NetBIOSGroup + nssm set DependOnGroup :+NetBIOSGroup + +Whereas these lines add to any existing dependencies: + + nssm set DependOnGroup +NetBIOSGroup + nssm set DependOnGroup ++NetBIOSGroup + + +The Name parameter can only be queried, not set. It returns the service's +registry key name. This may be useful to know if you take advantage of +the fact that you can substitute the service's display name anywhere where +the syntax calls for . + + +The ObjectName parameter requires an additional argument only when setting +a username. The additional argument is the password of the user. + +To retrieve the username, run + + nssm get ObjectName + +To set the username and password, run + + nssm set ObjectName + +Note that the rules of argument concatenation still apply. The following +invocation is valid and will have the expected effect. + + nssm set ObjectName correct horse battery staple + +The following well-known usernames do not need a password. The password +parameter can be omitted when using them: + + "LocalSystem" aka "System" aka "NT Authority\System" + "LocalService" aka "Local Service" aka "NT Authority\Local Service" + "NetworkService" aka "Network Service" aka "NT Authority\Network Service" + Virtual service account "NT Service\" + + +The Start parameter is used to query or set the startup type of the service. +Valid service startup types are as follows: + + SERVICE_AUTO_START: Automatic startup at boot. + SERVICE_DELAYED_START: Delayed startup at boot. + SERVICE_DEMAND_START: Manual service startup. + SERVICE_DISABLED: The service is disabled. + +Note that SERVICE_DELAYED_START is not supported on versions of Windows prior +to Vista. NSSM will set the service to automatic startup if delayed start is +unavailable. + + +The Type parameter is used to query or set the service type. NSSM recognises +all currently documented service types but will only allow setting one of two +types: + + SERVICE_WIN32_OWN_PROCESS: A standalone service. This is the default. + SERVICE_INTERACTIVE_PROCESS: A service which can interact with the desktop. + +Note that a service may only be configured as interactive if it runs under +the LocalSystem account. The safe way to configure an interactive service +is in two stages as follows. + + nssm reset ObjectName + nssm set Type SERVICE_INTERACTIVE_PROCESS + + +Controlling services using the command line +------------------------------------------- +NSSM offers rudimentary service control features. + + nssm start + + nssm restart + + nssm stop + + nssm status + + nssm statuscode + +The output of "nssm status" and "nssm statuscode" is a string +representing the service state, eg SERVICE_RUNNING. + +The exit code of "nssm status" will be 0 if the status was +succesfully retrieved. If the exit code is not zero there was +an error. + +The exit code of "nssm statuscode" will be the numeric value +of the service state, eg 4 for SERVICE_RUNNING. Zero is not a +valid service state code. If the exit code is zero there was +an error. + + +Removing services using the GUI +------------------------------- +NSSM can also remove services. Run + + nssm remove + +to remove a service. You will prompted for confirmation before the service +is removed. Try not to remove essential system services... + + +Removing service using the command line +--------------------------------------- +To remove a service without confirmation from the GUI, run + + nssm remove confirm + +Try not to remove essential system services... + + +Logging +------- +NSSM logs to the Windows event log. It registers itself as an event log source +and uses unique event IDs for each type of message it logs. New versions may +add event types but existing event IDs will never be changed. + +Because of the way NSSM registers itself you should be aware that you may not +be able to replace the NSSM binary if you have the event viewer open and that +running multiple instances of NSSM from different locations may be confusing if +they are not all the same version. + + +Listing managed services +------------------------ +The following command will print the names of all services managed by NSSM: + + nssm list + +To see all services on the system, not just NSSM's, use list all: + + nssm list all + + +Showing processes started by a service +-------------------------------------- +The following command will print the process ID and executable path of +processes started by a given service: + + nssm processes + +Note that if 32-bit NSSM is run on a 64-bit system running an older version of +Windows than Vista it will not be able to query the paths of 64-bit processes. + + +Exporting service configuration +------------------------------- +NSSM can dump commands which would recreate the configuration of a service. +The output can be pasted into a batch script to back up the service or +transfer to another computer. + + nssm dump + +Because the service configuration may contain characters which need to be +quoted or escaped from the command prompt, NSSM tries hard to produce +output which will work correctly when run as a script, by adding quotes +and caret escapes as appropriate. + +To facilitate copying a service, the dump command accepts a second +argument which specifies the name of the service to be used in the output. + + nssm dump + +Lines in the dump will reference the service while showing the +configuration of . + + +Example usage +------------- +To install an Unreal Tournament server: + + nssm install UT2004 c:\games\ut2004\system\ucc.exe server + +To run the server as the "games" user: + + nssm set UT2004 ObjectName games password + +To configure the server to log to a file: + + nssm set UT2004 AppStdout c:\games\ut2004\service.log + +To restrict the server to a single CPU: + + nssm set UT2004 AppAffinity 0 + +To remove the server: + + nssm remove UT2004 confirm + +To find out the service name of a service with a display name: + + nssm get "Background Intelligent Transfer Service" Name + + +Building NSSM from source +------------------------- +NSSM is known to compile with Visual Studio 2008 and later. Older Visual +Studio releases may or may not work if you install an appropriate SDK and +edit the nssm.vcproj and nssm.sln files to set a lower version number. +They are known not to work with default settings. + +NSSM will also compile with Visual Studio 2010 but the resulting executable +will not run on versions of Windows older than XP SP2. If you require +compatiblity with older Windows releases you should change the Platform +Toolset to v90 in the General section of the project's Configuration +Properties. + + +Credits +------- +Thanks to Bernard Loh for finding a bug with service recovery. +Thanks to Benjamin Mayrargue (www.softlion.com) for adding 64-bit support. +Thanks to Joel Reingold for spotting a command line truncation bug. +Thanks to Arve Knudsen for spotting that child processes of the monitored +application could be left running on service shutdown, and that a missing +registry value for AppDirectory confused NSSM. +Thanks to Peter Wagemans and Laszlo Keresztfalvi for suggesting throttling +restarts. +Thanks to Eugene Lifshitz for finding an edge case in CreateProcess() and for +advising how to build messages.mc correctly in paths containing spaces. +Thanks to Rob Sharp for pointing out that NSSM did not respect the +AppEnvironment registry value used by srvany. +Thanks to Szymon Nowak for help with Windows 2000 compatibility. +Thanks to François-Régis Tardy and Gildas le Nadan for French translation. +Thanks to Emilio Frini for spotting that French was inadvertently set as +the default language when the user's display language was not translated. +Thanks to Riccardo Gusmeroli and Marco Certelli for Italian translation. +Thanks to Eric Cheldelin for the inspiration to generate a Control-C event +on shutdown. +Thanks to Brian Baxter for suggesting how to escape quotes from the command +prompt. +Thanks to Russ Holmann for suggesting that the shutdown timeout be configurable. +Thanks to Paul Spause for spotting a bug with default registry entries. +Thanks to BUGHUNTER for spotting more GUI bugs. +Thanks to Doug Watson for suggesting file rotation. +Thanks to Арслан Сайдуганов for suggesting setting process priority. +Thanks to Robert Middleton for suggestion and draft implementation of process +affinity support. +Thanks to Andrew RedzMax for suggesting an unconditional restart delay. +Thanks to Bryan Senseman for noticing that applications with redirected stdout +and/or stderr which attempt to read from stdin would fail. +Thanks to Czenda Czendov for help with Visual Studio 2013 and Server 2012R2. +Thanks to Alessandro Gherardi for reporting and draft fix of the bug whereby +the second restart of the application would have a corrupted environment. +Thanks to Hadrien Kohl for suggesting to disable the console window's menu. +Thanks to Allen Vailliencourt for noticing bugs with configuring the service to +run under a local user account. +Thanks to Sam Townsend for noticing a regression with TerminateProcess(). +Thanks to Barrett Lewis for suggesting the option to skip terminating the +application's child processes. +Thanks to Miguel Angel Terrón for suggesting copy/truncate rotation. +Thanks to Yuriy Lesiuk for suggesting setting the environment before querying +the registry for parameters. +Thanks to Gerald Haider for noticing that installing a service with NSSM in a +path containing spaces was technically a security vulnerability. +Thanks to Scott Ware for reporting a crash saving the environment on XP 32-bit. +Thanks to Stefan and Michael Scherer for reporting a bug writing the event messages source. +Thanks to Paul Baxter for help with Visual Studio 2015. +Thanks to Mathias Breiner for help with Visual Studio and some registry fixes. +Thanks to David Bremner for general tidyups. +Thanks to Nabil Redmann for suggesting redirecting hooks' output. +Thanks to Bader Aldurai for suggesting the process tree. +Thanks to Christian Long for suggesting virtual accounts. +Thanks to Marcin Lewandowski for spotting a bug appending to large files. +Thanks to Nicolas Ducrocq for suggesting timestamping redirected output. +Thanks to Meang Akira Tanaka for suggestion and initial implementation of +the statuscode command. +Thanks to Kirill Kovalenko for reporting a crash with NANO server. +Thanks to Connor Reynolds for spotting a potential buffer overflow. + +Licence +------- +NSSM is public domain. You may unconditionally use it and/or its source code +for any purpose you wish. diff --git a/install/nssm/nssm.exe b/install/nssm/nssm.exe new file mode 100644 index 0000000..b81399d Binary files /dev/null and b/install/nssm/nssm.exe differ diff --git a/install/requirements-windows.txt b/install/requirements-windows.txt deleted file mode 100644 index afd24f6..0000000 --- a/install/requirements-windows.txt +++ /dev/null @@ -1 +0,0 @@ -pywin32 \ No newline at end of file diff --git a/install/uninstall.bat b/install/uninstall.bat new file mode 100644 index 0000000..d0dd0e0 --- /dev/null +++ b/install/uninstall.bat @@ -0,0 +1,15 @@ + +set service_name="BAPSicle" + +: We can't 'nssm stop because' we're about to delete it. +: The file will remain open, so you'll get access denied. +net stop %service_name% +sc delete %service_name% + +: We cd out of the folder, just in case we're about to delete +: out PWD. +cd \ +rmdir "C:\Program Files\BAPSicle\" /q /s + + +PAUSE \ No newline at end of file diff --git a/install/windows_service.py b/install/windows_service.py deleted file mode 100644 index f0df182..0000000 --- a/install/windows_service.py +++ /dev/null @@ -1,36 +0,0 @@ -from server import BAPSicleServer -from pathlib import Path -from SMWinservice import SMWinservice -import time -import multiprocessing - -import sys -sys.path.append("..\\") - - -class BAPScileAsAService(SMWinservice): - _svc_name_ = "BAPSicle" - _svc_display_name_ = "BAPSicle Server" - _svc_description_ = "BAPS development has been frozen for a while, but this new spike of progress is dripping." - - def start(self): - self.isrunning = True - self.server = multiprocessing.Process(target=BAPSicleServer).start() - - def stop(self): - print("stopping") - self.isrunning = False - try: - self.server.terminate() - self.server.join() - except: - pass - - def main(self): - while self.isrunning: - time.sleep(1) - print("BAPSicle is running.") - - -if __name__ == '__main__': - BAPScileAsAService.parse_command_line() diff --git a/launch_standalone.py b/launch_standalone.py new file mode 100644 index 0000000..2681aff --- /dev/null +++ b/launch_standalone.py @@ -0,0 +1,13 @@ +import multiprocessing +import time + +from server import BAPSicleServer + +if __name__ == '__main__': + # On Windows calling this function is necessary. + # Causes all kinds of loops if not present. + multiprocessing.freeze_support() + server = multiprocessing.Process(target=BAPSicleServer).start() + while True: + time.sleep(1) + pass diff --git a/player.py b/player.py index d119954..53126c5 100644 --- a/player.py +++ b/player.py @@ -1,10 +1,13 @@ -import pygame +from state_manager import StateManager +from mutagen.mp3 import MP3 +from pygame import mixer import time import json -from mutagen.mp3 import MP3 import copy +import os +import setproctitle -from state_manager import StateManager +os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide" class Player(): @@ -24,54 +27,54 @@ class Player(): def isInit(self): try: - pygame.mixer.music.get_busy() + mixer.music.get_busy() except: return False else: return True def isPlaying(self): - return bool(pygame.mixer.music.get_busy()) + return bool(mixer.music.get_busy()) def play(self): - pygame.mixer.music.play(0) + mixer.music.play(0) def pause(self): - pygame.mixer.music.pause() + mixer.music.pause() def unpause(self): - pygame.mixer.music.play(0, self.state.state["pos"]) + mixer.music.play(0, self.state.state["pos"]) def stop(self): - pygame.mixer.music.stop() + mixer.music.stop() def seek(self, pos): if self.isPlaying(): - pygame.mixer.music.play(0, pos) + mixer.music.play(0, pos) else: self.updateState(pos) def load(self, filename): if not self.isPlaying(): self.state.update("filename", filename) - pygame.mixer.music.load(filename) + mixer.music.load(filename) if ".mp3" in filename: song = MP3(filename) self.state.update("length", song.info.length) else: - self.state.update("length", pygame.mixer.Sound(filename).get_length()/1000) + self.state.update("length", mixer.Sound(filename).get_length()/1000) def quit(self): - pygame.mixer.quit() + mixer.quit() def output(self, name=None): self.quit() try: if name: - pygame.mixer.init(44100, -16, 1, 1024, devicename=name) + mixer.init(44100, -16, 1, 1024, devicename=name) else: - pygame.mixer.init(44100, -16, 1, 1024) + mixer.init(44100, -16, 1, 1024) except: return "FAIL:Failed to init mixer, check sound devices." else: @@ -84,7 +87,7 @@ class Player(): if (pos): self.state.update("pos", max(0, pos)) else: - self.state.update("pos", max(0, pygame.mixer.music.get_pos()/1000)) + self.state.update("pos", max(0, mixer.music.get_pos()/1000)) self.state.update("remaining", self.state.state["length"] - self.state.state["pos"]) def getDetails(self): @@ -93,6 +96,7 @@ class Player(): def __init__(self, channel, in_q, out_q): self.running = True + setproctitle.setproctitle("BAPSicle - Player " + str(channel)) self.state = StateManager("channel" + str(channel), self.__default_state) diff --git a/server.py b/server.py index e03680c..05d9001 100644 --- a/server.py +++ b/server.py @@ -3,6 +3,9 @@ import player from flask import Flask, render_template, send_from_directory, request import json import sounddevice as sd +import setproctitle + +setproctitle.setproctitle("BAPSicle - Server") class BAPSicleServer(): @@ -117,7 +120,6 @@ def seek(channel, pos): @app.route("/player//output/") def output(channel, name): channel_to_q[channel].put("OUTPUT:" + name) - channel_to_q[channel].put("LOAD:test"+str(channel)+".mp3") return ui_status() @@ -148,7 +150,6 @@ def startServer(): for channel in range(3): channel_to_q.append(multiprocessing.Queue()) channel_from_q.append(multiprocessing.Queue()) - # channel_to_q[-1].put_nowait("LOAD:test"+str(channel)+".mp3") channel_p.append( multiprocessing.Process( target=player.Player, diff --git a/state_manager.py b/state_manager.py index 139bdaa..e81cffd 100644 --- a/state_manager.py +++ b/state_manager.py @@ -1,5 +1,6 @@ import json import os +from helpers.os_environment import resolve_external_file_path class StateManager: @@ -7,7 +8,7 @@ class StateManager: __state = {} def __init__(self, name, default_state=None): - self.filepath = "C:\Program Files\BAPSicle\state\\" + name + ".json" + self.filepath = resolve_external_file_path("/state/" + name + ".json") if not os.path.isfile(self.filepath): self.log("No file found for " + self.filepath) try: