3 years agoTry to build PDB files even for releases. master
Iain Patterson [Tue, 16 May 2017 07:34:43 +0000 (08:34 +0100)]
Try to build PDB files even for releases.

3 years agoFix lockup on with 64 cores.
Iain Patterson [Tue, 16 May 2017 07:18:29 +0000 (08:18 +0100)]
Fix lockup on with 64 cores.

Thanks foi.

3 years agoSupport starting processes with a console on Windows 10.
Iain Patterson [Wed, 26 Apr 2017 12:55:18 +0000 (13:55 +0100)]
Support starting processes with a console on Windows 10.

By default we let the application inherit NSSM's console.  On Windows 10
Creators Update an application inheriting a console would fail to start with

Now we close our own console and call CreateProcess() with the
CREATE_NEW_CONSOLE flag if the application needs a console.

Unfortunately the ASCII art logo has to be sacrificed in the name of

3 years agoAdded --version command.
Iain Patterson [Fri, 22 Oct 2010 08:09:00 +0000 (09:09 +0100)]
Added --version command.

3 years agoFixed buffer overflow in GUI browse().
Iain Patterson [Mon, 6 Mar 2017 11:48:18 +0000 (11:48 +0000)]
Fixed buffer overflow in GUI browse().

Thanks Connor Reynolds.

4 years agoTweaks for NANO server.
Iain Patterson [Sun, 11 Sep 2016 21:59:35 +0000 (22:59 +0100)]
Tweaks for NANO server.

Try to detect if we're running in a Powershell remote session, in which
case we have neither a console nor a window station.  If that's the case
it's appropriate to print the usage message to stdout rather than
attempt to display a popup.

Use EnumServicesStatusEx() instead of EnumServicesStatus().  The latter
crashes on NANO when used in the same way that works correctly on other
versions of Windows.

Thanks Kirill Kovalenko.

4 years agoDon't leak hook startup handles.
Iain Patterson [Wed, 7 Sep 2016 14:12:42 +0000 (15:12 +0100)]
Don't leak hook startup handles.

When sharing I/O settings we dup the service's I/O handles then call
CreateProcess() to run the hook.  However we didn't explicitly close the
duplicated handles, so the subsequent CloseHandle() called by
cleanup_loggers() would hang, as the logging thread's ReadFile() won't
have completed due to the duplicated handle still being a potential

4 years agoTidy up French GUI.
Iain Patterson [Wed, 7 Sep 2016 07:37:38 +0000 (08:37 +0100)]
Tidy up French GUI.

4 years agoDocument setting hooks on the command line.
Iain Patterson [Wed, 7 Sep 2016 07:37:23 +0000 (08:37 +0100)]
Document setting hooks on the command line.

4 years agoAdded dump and processess commands to usage message.
Iain Patterson [Tue, 6 Sep 2016 12:29:27 +0000 (13:29 +0100)]
Added dump and processess commands to usage message.

4 years agoCommand to exit with state code.
Iain Patterson [Tue, 6 Sep 2016 12:22:06 +0000 (13:22 +0100)]
Command to exit with state code.

Use "nssm statuscode <servicename>" to set the exit code to the value of
dwCurrentState, eg 4 for running.  An exit code of 0 implies an error.

Thanks Meang Akira Tanaka.

4 years agoAlow prefixing log output with a timestamp.
Iain Patterson [Tue, 6 Sep 2016 09:30:21 +0000 (10:30 +0100)]
Alow prefixing log output with a timestamp.

When AppTimestampLog is set to 1 all output to stdout and stderr will be
prefixed with a millisecond-precision ISO8601 timestamp.

Timestamp prefixing implies online rotation mode.  It should be
considered experimental as it requires splitting input lines by LF with
no way of knowing for certain that the input stream actually is

Thanks Nicolas Ducrocq.

4 years agoEnsure logging threads exit.
Iain Patterson [Tue, 30 Aug 2016 07:37:00 +0000 (08:37 +0100)]
Ensure logging threads exit.

We need to close all handles to ensure that all logging threads are

Care must be taken to close the writing end of the logging pipe first so
that the logging thread can issue a final ReadFile() call.  Before the
thread exits it should close the reading end, however as a safety
measure we WaitForSingleObject() then close the reading end from the
main rotation thread.

4 years agoUse close_handle().
Iain Patterson [Tue, 30 Aug 2016 07:35:35 +0000 (08:35 +0100)]
Use close_handle().

Call new close_handle() function when tidying up trying to set up I/O.

4 years agoAdded close_handle().
Iain Patterson [Tue, 30 Aug 2016 07:34:19 +0000 (08:34 +0100)]
Added close_handle().

New function to close then zero a handle.

If we pass a second handle pointer, it will be set to the value of the
handle that was closed, or INVALID_HANDLE_VALUE.  We can inspect the
remembered handle to see if we need to wait, eg for a thread.

4 years agoInitialise hook command widget correctly.
Iain Patterson [Mon, 29 Aug 2016 10:01:37 +0000 (11:01 +0100)]
Initialise hook command widget correctly.

We weren't explicitly zeroing the GUI widget for entering the hook command.

4 years agoSeek to end of large files correctly.
Iain Patterson [Thu, 25 Aug 2016 10:41:09 +0000 (11:41 +0100)]
Seek to end of large files correctly.

The documentation for SetFilePointer() is potentially confusing.  When the
third argument is NULL, the second argument is treated as a 32-bit
offset to move the pointer.  When the third argument is not NULL, it is
a pointer to an unsigned integer representing the high order part of a
64-bit signed offset, and the second argument is treated as the
corresponding (signed) low order part.

The documentation states that "if you do not need the high order
32-bits" the third argument must be NULL, and indeed we were setting it
to NULL on the basis that we were moving 0 bytes from FILE_END and 0 can
most definitely be represented in 32-bits.

However in practice when seeking to the end of a file whose size cannot
be represented in 32-bits we actually need to use a 64-bit offset, even
though the 64-bit integer we use is 0.  So we must pass a pointer to a
zeroed high part.

Note that when SetFilePointer() succeeds the return value is the low
order part of the actual offset and the high order value is filled with
the high order offset, which has two important implications.  Firstly we
cannot simply declare our zero variable statically but must pass a new
value each time.  Secondly it is insufficient to check that the function
returned something other than INVALID_SET_FILE_POINTER, as we were
doing; instead we must treat that return value as success if and only
iff GetLastError() returns NO_ERROR.

Marcin Lewandowski noticed that large files were not always appended
correctly.  Because we were passing NULL as the third argument to
SetFilePointer() it returned INVALID_SET_FILE_POINTER and we didn't call

SetFilePointerEx() is supported on all versions of Windows we target,
and has a much simpler definition than SetFilePointer().  We now use
that instead.

4 years agoUse WideCharToMultiByte() correctly.
Iain Patterson [Wed, 17 Aug 2016 11:23:44 +0000 (12:23 +0100)]
Use WideCharToMultiByte() correctly.

4 years agoEnsure we call free_imports() when exiting.
Iain Patterson [Fri, 5 Aug 2016 09:22:38 +0000 (10:22 +0100)]
Ensure we call free_imports() when exiting.

4 years agoCRLF for utf8.* files.
Iain Patterson [Fri, 5 Aug 2016 09:20:24 +0000 (10:20 +0100)]
CRLF for utf8.* files.

4 years agoRespect throttle time when trying to start a service.
Iain Patterson [Tue, 2 Aug 2016 10:32:05 +0000 (11:32 +0100)]
Respect throttle time when trying to start a service.

If we manage the service, retrieve the AppThrottle milliseconds value
and return if the start control doesn't return in that time.

This prevents hanging waiting for a service which fails to start.

4 years agoHandle virtual accounts when dumping service config.
Iain Patterson [Mon, 1 Aug 2016 17:07:45 +0000 (18:07 +0100)]
Handle virtual accounts when dumping service config.

If we are copying the service we need to build the virtual service
account name for the new service.

4 years agoSupport virtual service accounts.
Iain Patterson [Mon, 1 Aug 2016 16:45:46 +0000 (17:45 +0100)]
Support virtual service accounts.

Specify the account name as "NT Service\<servicename>" or use the
virtual account radio button in the GUI to run the service as a virtual
service account.

Thanks Christian Long.

4 years agoUse UTF-8 functions when working with explicit encodings.
Iain Patterson [Thu, 28 Jul 2016 15:15:53 +0000 (16:15 +0100)]
Use UTF-8 functions when working with explicit encodings.

LSA functions explicitly operate on UTF-16 strings.
GetProcAddress() explicitly operates on multibyte strings.

4 years agoAdded UTF-8 functions.
Iain Patterson [Fri, 22 Jul 2016 14:47:52 +0000 (15:47 +0100)]
Added UTF-8 functions.

We want to use UTF-8 everywhere.  To that end we use new functions that
will convert strings between UTF-8 and UTF-16.

Before doing anything at all we set the console, if we have one, to
codepage UTF-8.  Before exiting we restore the console codepage to
whatever it was before.

4 years agoNTSTATUS returns STATUS_SUCCESS on success.
Iain Patterson [Thu, 28 Jul 2016 15:14:29 +0000 (16:14 +0100)]
NTSTATUS returns STATUS_SUCCESS on success.

Technically it isn't correct to check for failure with if (status).

4 years agoAllow listing all services.
Iain Patterson [Wed, 27 Jul 2016 10:54:29 +0000 (11:54 +0100)]
Allow listing all services.

To list all services, not just those managed by NSSM, use:

    nssm list all

4 years agoAllow waiting for debugger.
Iain Patterson [Mon, 25 Jul 2016 13:47:32 +0000 (14:47 +0100)]
Allow waiting for debugger.

If the service is started with the single argument "debug" eg by setting
the Start parameters of the service in services.msc, we delayed startup
in service_main() until the await_debugger variable is set to false.

Attach to nssm.exe with your debugger of choice, set a breakpoint within
that function and poke the variable when ready.

Beware that the behaviour of the service manager when service_main
doesn't complete operation promptly is undefined.

4 years agoUse QueryFullProcessImageName() if available.
Iain Patterson [Wed, 27 Jul 2016 11:21:01 +0000 (12:21 +0100)]
Use QueryFullProcessImageName() if available.

A 32-bit process running on 64-bit Windows can't read the image name of
a 64-bit process, so printing the process tree will fail.  However, on
Windows Vista or later we have QueryFullProcessImageName() which is able
to retrieve the path.

4 years agoBe quieter loading imports.
Iain Patterson [Wed, 27 Jul 2016 10:47:16 +0000 (11:47 +0100)]
Be quieter loading imports.

There's no need to log an event if we couldn't load a missing DLL.

There's no need to go to the trouble of converting to a wide buffer if
we aren't then going to log an event.

4 years agoCommand to show processes started by the service.
Iain Patterson [Mon, 25 Jul 2016 16:11:59 +0000 (17:11 +0100)]
Command to show processes started by the service.

Print process tree with PID and module path.

Thanks Bader Aldurai.

4 years agoAdded get_debug_token().
Iain Patterson [Mon, 25 Jul 2016 16:23:52 +0000 (17:23 +0100)]
Added get_debug_token().

New function to obtain SeDebugPrivilege privilege.

4 years agoAbstract kill_process_tree().
Iain Patterson [Mon, 25 Jul 2016 13:52:19 +0000 (14:52 +0100)]
Abstract kill_process_tree().

The function is now walk_process_tree() and instead of killing a process
it invokes the supplied callback function.  When killing a process we
pass a pointer to kill_process().

This will allow performing operations things other than killing
processes on the tree.

4 years agoLet the dump command copy a service.
Iain Patterson [Thu, 28 Jul 2016 14:53:52 +0000 (15:53 +0100)]
Let the dump command copy a service.

If an optional second argument is given to dump, the name of the service
in the output will be set to the argument's value.  In any case, an
"install" line will be written, replacing the Application dump line.

Thus a service can be copied by running:

    nssm dump <servicename> <newservicename> | %COMSPEC% /k

4 years agoCommand to dump service configuration.
Iain Patterson [Fri, 15 Jul 2016 14:09:58 +0000 (15:09 +0100)]
Command to dump service configuration.

Output commands which can be used to recreate the service configuration

    nssm dump <servicename>

Commands are quoted and escaped as best we can manage.

4 years agoDon't mangle the input to split_hook_name().
Iain Patterson [Fri, 22 Jul 2016 14:37:32 +0000 (15:37 +0100)]
Don't mangle the input to split_hook_name().

4 years agoDefine for default parameter string.
Iain Patterson [Fri, 22 Jul 2016 14:46:16 +0000 (15:46 +0100)]
Define for default parameter string.

4 years agoExport hook strings.
Iain Patterson [Fri, 22 Jul 2016 13:25:37 +0000 (14:25 +0100)]
Export hook strings.

4 years agoAllow adding or removing individual dependencies.
Iain Patterson [Sat, 23 Jul 2016 11:29:35 +0000 (12:29 +0100)]
Allow adding or removing individual dependencies.

Specifying a DependOnService or DependOnGroup argument as +<dependency>
will add the dependency to the list.  Specifying it as -<dependency>
will remove it from the list.  The dependency may also be given as
:<dependency> to make scripting more legible.

Groups should technically be prefixed with a + symbol.  NSSM will follow
the rules above even when the + prefix is used.

4 years agoAdded append_to/remove_from_dependencies().
Iain Patterson [Fri, 22 Jul 2016 16:51:19 +0000 (17:51 +0100)]
Added append_to/remove_from_dependencies().

4 years agoAllow adding or removing individual environment variables.
Iain Patterson [Thu, 21 Jul 2016 16:23:21 +0000 (17:23 +0100)]
Allow adding or removing individual environment variables.

Specifying an environment string as +KEY=VALUE will append KEY=VALUE to
the existing block, or create a new one if none was previously
configured.  If KEY is already present, +KEY=VALUE will override it in

Specifying -KEY=VALUE will remove KEY=VALUE from the existing block.

Specifying -KEY will remove KEY regardless of its value.

Removing the last key will delete the whole block.

Specifying :KEY=VALUE is equivalent to KEY=VALUE, ie it creates a new
block with only KEY=VALUE present.  Its main purpose is to make scripts
easier to read.

    nssm set <servicename> AppEnvironment :FIRST=one
    nssm set <servicename> AppEnvironment +SECOND=two
    nssm set <servicename> AppEnvironment +THIRD=two

4 years agoDon't leak memory in get_service_dependencies().
Iain Patterson [Thu, 21 Jul 2016 16:09:32 +0000 (17:09 +0100)]
Don't leak memory in get_service_dependencies().

Ensure we free qsc when there are no dependencies.
Ensure we free qsc when we couldn't allocate a buffer.
Free the buffer and zero its pointer's size when there are no

4 years agoSome notes on the environment and registry.
Iain Patterson [Fri, 22 Jul 2016 14:43:08 +0000 (15:43 +0100)]
Some notes on the environment and registry.

Documentation is good!

4 years agoAdded append_to/remove_from_environment_block().
Iain Patterson [Thu, 21 Jul 2016 14:18:53 +0000 (15:18 +0100)]
Added append_to/remove_from_environment_block().

New functions to add an environment variable to an existing environment
block or to remove a variable (specified by KEY=VALUE or just KEY) from
a block.

A new block is always returned.  It will have length 2 (NULL NULL) if
the requested action was a no-op.

4 years agoFunctions to work with null delimited lists.
Iain Patterson [Fri, 22 Jul 2016 14:29:48 +0000 (15:29 +0100)]
Functions to work with null delimited lists.

copy_double_null() duplicates a null delimited list.

append_to_double_null() adds an entry to a list, or replaces an entry
which matches a given substring of the new value.

remove_from_double_null() removes an entry from a list, possibly only if
it matches a given substring.

4 years agoHandle second parameter of unformat_double_null().
Iain Patterson [Fri, 22 Jul 2016 11:05:44 +0000 (12:05 +0100)]
Handle second parameter of unformat_double_null().

The length of the formatted buffer is supposed to be a count of
characters NOT including the trailing NULL.

4 years agoAdded enumerate_registry_values().
Iain Patterson [Fri, 22 Jul 2016 14:47:11 +0000 (15:47 +0100)]
Added enumerate_registry_values().

New function to retrieve values under a key.

4 years agoCorrect return code from setting_get_priority().
Iain Patterson [Sat, 23 Jul 2016 11:01:52 +0000 (12:01 +0100)]
Correct return code from setting_get_priority().

The function was returning 1 when filling the destination buffer with
the default value.  It should return 0 when the value is default and 1
when it has been set by the administrator.

4 years agoOpen registry with read/write when setting parameters.
Iain Patterson [Thu, 21 Jul 2016 09:49:02 +0000 (10:49 +0100)]
Open registry with read/write when setting parameters.

We may need to query existing settings so we need KEY_READ as well as
KEY_WRITE access.

4 years agoEnsure we have the right key for querying Environment.
Iain Patterson [Fri, 22 Jul 2016 13:20:07 +0000 (14:20 +0100)]
Ensure we have the right key for querying Environment.

The Environment value is under the service key not our subkey.
If we're querying it we should expect that the service definition exists
in the registry.

4 years agoRegQueryValueEx() returns error code directly.
Iain Patterson [Fri, 22 Jul 2016 13:06:32 +0000 (14:06 +0100)]
RegQueryValueEx() returns error code directly.

Formatting an error message should use the return value not

4 years agoAdded quote().
Iain Patterson [Fri, 15 Jul 2016 14:08:48 +0000 (15:08 +0100)]
Added quote().

New function to quote strings for passing to the command prompt.
Protect metacharacters with ^ when quoting.  Always quote when
metacharacters are present even if we wouldn't otherwise.

4 years agoAdded nssm_exe().
Iain Patterson [Fri, 15 Jul 2016 13:27:31 +0000 (14:27 +0100)]
Added nssm_exe().

New function to retrieve argv[0].

4 years agoRedirect hooks' output.
Iain Patterson [Thu, 14 Jul 2016 16:44:14 +0000 (17:44 +0100)]
Redirect hooks' output.

If AppRedirectHook is 1 and stdout and/or stderr are being redirected,
also redirect the output of hooks.

Doing so requires using a logging thread and so implies online rotation
if log rotation is used.

Thanks Nabil Redmann.

4 years agoAdded dup_handle().
Iain Patterson [Thu, 14 Jul 2016 16:07:28 +0000 (17:07 +0100)]
Added dup_handle().

New wrapper around DuplicateHandle().

4 years agoDavid Bremner committed some fixes.
Iain Patterson [Tue, 5 Jul 2016 09:05:08 +0000 (10:05 +0100)]
David Bremner committed some fixes.

4 years agoCreateFile returns INVALID_HANDLE_VALUE on failure https://msdn.microsoft.com/en...
David Bremner [Mon, 4 Jul 2016 21:47:25 +0000 (14:47 -0700)]
CreateFile returns INVALID_HANDLE_VALUE on failure https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx

4 years agoCreateToolhelp32Snapshot returns INVALID_HANDLE_VALUE on failure https://msdn.microso...
David Bremner [Mon, 4 Jul 2016 21:36:19 +0000 (14:36 -0700)]
CreateToolhelp32Snapshot returns INVALID_HANDLE_VALUE on failure https://msdn.microsoft.com/en-us/library/windows/desktop/ms682489(v=vs.85).aspx

4 years agoservice->passwordlen is a count of TCHARs, fix SecureZeroMemory for UNICODE
David Bremner [Mon, 4 Jul 2016 21:35:06 +0000 (14:35 -0700)]
service->passwordlen is a count of TCHARs, fix SecureZeroMemory for UNICODE

4 years agomove misplaced return
David Bremner [Mon, 4 Jul 2016 21:32:04 +0000 (14:32 -0700)]
move misplaced return

4 years agoremove dead breaks and returns
David Bremner [Mon, 4 Jul 2016 21:31:26 +0000 (14:31 -0700)]
remove dead breaks and returns

4 years agoTry to create messages at install time.
Iain Patterson [Fri, 17 Jun 2016 14:38:00 +0000 (15:38 +0100)]
Try to create messages at install time.

If the service was never run under a privileged account we might not
have been able to set the message source in the registry.

Set the message source when installing the service, as we are guaranteed
to have the necessary rights.

4 years agoCompiler food.
Iain Patterson [Thu, 21 Apr 2016 09:18:53 +0000 (10:18 +0100)]
Compiler food.

SetConsoleCtrlHandler() returns BOOL which is not bool.

4 years agoAdded list command to enumerate NSSM services.
Iain Patterson [Wed, 20 Apr 2016 21:25:36 +0000 (22:25 +0100)]
Added list command to enumerate NSSM services.

Use "nssm list" to print all services which are managed by NSSM.

4 years agoTechnically we should stop ignoring Control-C.
Iain Patterson [Thu, 31 Mar 2016 07:34:33 +0000 (08:34 +0100)]
Technically we should stop ignoring Control-C.

Before signalling our application we implement a null Control-C handler
so we don't get killed ourselves when signalling the process group.  We
should probably restore the default behaviour after signalling.

4 years agoDon't ignore must_exist in open_registry().
Iain Patterson [Sun, 20 Mar 2016 09:20:40 +0000 (09:20 +0000)]
Don't ignore must_exist in open_registry().

A typo meant that we were passing true to the overloaded open_registry()
which also takes an HKEY as a parameter.

As a result we would end up spamming ERROR_FILE_NOT_FOUND when updating
a service via the GUI if there were no hooks to update, ironically in a
section of code whose purpose was to check that it didn't need to do or
report anything.

Thanks Igor Zenkov and James Gleason.

4 years agoNot an error if no hook found in the registry.
Iain Patterson [Tue, 1 Mar 2016 10:22:28 +0000 (10:22 +0000)]
Not an error if no hook found in the registry.

Hooks are optional so we shouldn't log an error if one wasn't found.

Thanks Mathias Breiner.

4 years agoAlternative open_registry_key() signature.
Iain Patterson [Tue, 1 Mar 2016 10:19:13 +0000 (10:19 +0000)]
Alternative open_registry_key() signature.

Allow passing a pointer to an HKEY and having the function return the
result of RegCreateKeyEx()/RegOpenKeyEx().  Existing functions wrap the
new invocation so no code changes are needed.

The new function can be used to handle ERROR_FILE_NOT_FOUND as success
when must_exist is false.

4 years agoHelp git figure out how to diff .mc and .rc files.
Iain Patterson [Sun, 28 Feb 2016 09:01:09 +0000 (09:01 +0000)]
Help git figure out how to diff .mc and .rc files.

Visual Studio wants .mc and .rc files to be UTF-16LE, which git thinks
are binary.  Use .gitattributes to force treating them as text only when
diffing, explicitly not handling them as text when submitting as they
must retain their encoding to keep Visual Studio happy.

Thanks to Mathias Breiner for proposing the use of .gitattributes in the
repo.  I had the rules in .git/info/exclude in my working copy for a
long time and forgot about the issue!

4 years agoUse CRLF consistently.
Iain Patterson [Sun, 28 Feb 2016 08:59:11 +0000 (08:59 +0000)]
Use CRLF consistently.

The mismatch of line endings has long been an annoyance.

Thanks Mathias Breiner for advocating finally doing something about it.

4 years agoUse service_registry_path() in create_exit_action().
Iain Patterson [Sun, 28 Feb 2016 08:46:49 +0000 (08:46 +0000)]
Use service_registry_path() in create_exit_action().

As a result of registry function refactoring we were constructing the
default path to AppExit incorrectly.  Using the function created for the
purpose works correctly, unsurprisingly.

Thanks Igor Zenkov.

4 years agoWorkaround for Resource Compiler warnings/errors under VS2010.
Mathias Breiner [Thu, 11 Feb 2016 18:31:26 +0000 (19:31 +0100)]
Workaround for Resource Compiler warnings/errors under VS2010.

VS2010 RC.exe yielded lots of warnings and some errors when including some (unnecessary) platform header files via nssm.h. So now including only what is really required.
This includes a workaround for error RC2247 found here: http://stackoverflow.com/a/18317658

4 years agoFix compilation on VS2015.
Iain Patterson [Tue, 23 Feb 2016 13:41:28 +0000 (13:41 +0000)]
Fix compilation on VS2015.

Paul Baxter and Mathias Breiner independently reported fixes to get the
project to compile on Visual Studio 2015.

4 years agoEventMessageFile should be unquoted.
Iain Patterson [Mon, 22 Feb 2016 13:15:44 +0000 (13:15 +0000)]
EventMessageFile should be unquoted.

We were writing a quoted message source path to the registry.
Stefan and Michael Scherer point out that it should be unquoted.

4 years agoCompiler food.
Iain Patterson [Wed, 17 Feb 2016 11:39:00 +0000 (11:39 +0000)]
Compiler food.

4 years agoFix crash on Windows XP.
Iain Patterson [Wed, 17 Feb 2016 09:42:16 +0000 (09:42 +0000)]
Fix crash on Windows XP.

The pointer returned by GetEnvironmentStrings() on Unicode versions of
Windows XP was to the raw environment block, not a copy as described in
the documentation.  Retrieving it into service->initial_env and keeping
it around for the lifetime of the service could cause a crash.  Instead
we now copy the memory and immediately call FreeEnvironmentStrings().

Thanks Scott Ware.

5 years agoClean up Parameters properly.
Iain Patterson [Sun, 8 Mar 2015 17:24:58 +0000 (17:24 +0000)]
Clean up Parameters properly.

If create_parameters() failed to write Application, AppParameters or
AppDirectory it would try to clean up by deleting the Parameters key.
The method used to do so was flawed.  It would attempt to delete the key
defined by the NSSM_REGISTRY macro but that macro contains a printf
control string and is not suitable for standalone use.

5 years agoIncorrect capitalisation of AppStderrCopyAndTruncate.
Iain Patterson [Sun, 8 Mar 2015 17:21:53 +0000 (17:21 +0000)]
Incorrect capitalisation of AppStderrCopyAndTruncate.

5 years agoDescribe startup environment in the README.
Iain Patterson [Sun, 8 Mar 2015 17:21:09 +0000 (17:21 +0000)]
Describe startup environment in the README.

5 years agoAbstracted open_registry().
Iain Patterson [Sun, 8 Mar 2015 16:45:55 +0000 (16:45 +0000)]
Abstracted open_registry().

Added service_registry_path() to construct a registry path for a service
which may contain the Parameters subkey and optional sub-subkey.  If no
Parameters subkey is needed the constructed path references the native
service parameters.

The open_registry() function is now a wrapper around the new
open_registry_key() function which opens an arbitrary key.

The new open_service_resgistry() function also wraps open_registry_key()
to access native parameters.

5 years agoUse nssm_imagepath().
Iain Patterson [Mon, 2 Mar 2015 11:03:59 +0000 (11:03 +0000)]
Use nssm_imagepath().

We were calling GetModuleFileName() in a number of places.  Use
nssm_imagepath() instead.

Ensure that a quoted path is used for the service image path when
creating the service, thus avoiding a theoretical security vulnerability
whereby the service manager could be tricked into launching the wrong

When setting the path to NSSM in the environment for event hooks we
still use the unquoted path, as environment variables are typically

Thanks Gerald Haider.

5 years agoAdded nssm_imagepath() and nssm_unquoted_imagepath().
Iain Patterson [Mon, 2 Mar 2015 11:02:06 +0000 (11:02 +0000)]
Added nssm_imagepath() and nssm_unquoted_imagepath().

Functions to retrieve the path to nssm.exe both with and without path

5 years agoSave the environment.
Iain Patterson [Tue, 24 Feb 2015 13:37:17 +0000 (13:37 +0000)]
Save the environment.

Before the previous two commits we used to read the environment from the
registry and set it using an optimisation which modified the retrieved
block in-place.

Now we need to set the environment twice after querying the registry -
once in get_parameters() and once just before CreateProcess(), which
means we can't use the in-place optimisation and must copy the block
before operating on it.

Additionally, nssm_hook() cleans the environment so we have to reinstate
it immediately prior to launching the application.

5 years agoCompiler food.
Iain Patterson [Mon, 23 Feb 2015 21:47:01 +0000 (21:47 +0000)]
Compiler food.

Missing header from previous commit.

5 years agoSet the environment before querying the registry.
Iain Patterson [Mon, 23 Feb 2015 21:22:22 +0000 (21:22 +0000)]
Set the environment before querying the registry.

Handle AppEnvironment and AppEnvironmentExtra before querying registry
parameters.  Doing so allows paths and arguments passed to the service
to reference such environment variables.

Thanks Yuriy Lesiuk.

5 years agoFormatting.
Iain Patterson [Mon, 23 Feb 2015 21:17:58 +0000 (21:17 +0000)]

6 years agoStart service in a new thread so we don't block stop controls.
Iain Patterson [Fri, 2 Jan 2015 16:59:28 +0000 (16:59 +0000)]
Start service in a new thread so we don't block stop controls.

6 years agoRun hooks in response to certain events.
Iain Patterson [Thu, 1 Jan 2015 19:01:54 +0000 (19:01 +0000)]
Run hooks in response to certain events.

User-configurable hooks will be run before and/or after certain events,
eg after the application starts successfully.

Hooks will run with the environment of the application, with additional
environment variables providing information about the service status and
the hook which has been called.

6 years agoDon't create registry keys needlessly.
Iain Patterson [Fri, 2 Jan 2015 13:16:54 +0000 (13:16 +0000)]
Don't create registry keys needlessly.

Don't log an error when trying to access a registry key which doesn't
exist when it's expected that it might not be present.

Don't open a key with RegCreateKeyEx() when we are potentially going to
delete a value.  Instead try RegOpenKeyEx() and return success if it
already isn't present.

6 years agoFixed permissions check in open_registry().
Iain Patterson [Fri, 2 Jan 2015 11:31:19 +0000 (11:31 +0000)]
Fixed permissions check in open_registry().

We were checking for the presence of KEY_WRITE in the SAM to decide
whether to call RegCreateKeyEx() or RegOpenKeyEx().  KEY_WRITE is an
the function was incorrectly calling RegCreateKeyEx() even when
RegOpenKeyEx() would be more appropriate.

6 years agoDon't time out waiting for service status change.
Iain Patterson [Wed, 31 Dec 2014 14:33:57 +0000 (14:33 +0000)]
Don't time out waiting for service status change.

Wait for service to start (or fail to start) or stop instead of timing
out after five seconds.

An NSSM service which doesn't start properly and temporarily enters
paused state may end up reporting running state in response to a stop
control.  Therefore we now consider SERVICE_RUNNING to be a valid

6 years agoRegisterPowerSettingNotification is unnecessary.
Iain Patterson [Wed, 31 Dec 2014 15:11:05 +0000 (15:11 +0000)]
RegisterPowerSettingNotification is unnecessary.

We were importing RegisterPowerSettingNotification but not actually
calling it.  This didn't matter because including
SERVICE_ACCEPT_POWEREVENT in the accepted controls list is sufficient to
receive power notifications.

6 years agoRestrict acceptance of service controls.
Iain Patterson [Wed, 31 Dec 2014 13:16:24 +0000 (13:16 +0000)]
Restrict acceptance of service controls.

Don't accept a pause/continue control unless the service is marked as
Don't accept any control until startup is pending.
Don't accept any control after processing a stop control.

6 years agoMore StringFileInfo parameters.
Iain Patterson [Wed, 31 Dec 2014 13:15:10 +0000 (13:15 +0000)]
More StringFileInfo parameters.

The CompanyName and OriginalFileName parameters are mandatory according
to the spec, so we now provide them.

6 years agoRefactor kill functions to be independent of services.
Iain Patterson [Tue, 30 Dec 2014 11:05:12 +0000 (11:05 +0000)]
Refactor kill functions to be independent of services.

Removed most explicit references to service data in kill functions so
they can be used to kill arbitrary process trees.

The function to wait for service status change has been renamed
await_single_handle() to reflect that it can wait for any single object
not just a service's process handle.

6 years agoSingle waiting function.
Iain Patterson [Mon, 15 Dec 2014 22:50:38 +0000 (22:50 +0000)]
Single waiting function.

Use a single function to wait for service status to change, regardless
of if we are waiting for the service to run for a minimum period of time
to be unthrottled or for it to shut down..

6 years agoAllow slow service startup again.
Iain Patterson [Mon, 15 Dec 2014 14:05:55 +0000 (14:05 +0000)]
Allow slow service startup again.

Previously we enforced a maximum delay in reporting to the service
manager that the service was started, and waited for the application to
become unthrottled in the background.

Users may prefer that the service not report a started state until the
application is unthrottled.  Therefore we now loop and increment the
status checkpoint and wait hint to satisfy the service manager that we
aren't hung.

Thanks Tom Saul.

6 years agoTypo millisencondes.
Iain Patterson [Mon, 15 Dec 2014 22:54:58 +0000 (22:54 +0000)]
Typo millisencondes.

6 years agoDon't try to operate on freed data structure.
Iain Patterson [Mon, 15 Dec 2014 13:58:39 +0000 (13:58 +0000)]
Don't try to operate on freed data structure.

We were calling HeapFree() on the logger structure then trying to
call CloseHandle() on one of its elements.

6 years agoCopy/truncate file rotation.
Iain Patterson [Thu, 18 Sep 2014 07:28:21 +0000 (08:28 +0100)]
Copy/truncate file rotation.

Some processes, notably Logstash, open files for reading without setting
the FILE_SHARE_READ share mode.  If NSSM is using such a file for I/O
redirection and it tries to rotate the file, its MoveFile() call will

If the new (REG_DWORD) value(s) AppStdoutCopyAndTruncate and/or
AppStderrCopyAndTruncate are non-zero, NSSM will instead try to rotate
the file(s) by calling CopyFile() to create a copy of the file then
calling SetEndOfFile() to truncate the original file to zero size.
Doing so allows the rotation to succeed at the cost of time and disk
space to create a full copy of the file.

If the new (REG_DWORD) value AppRotateDelay is non-zero, NSSM will sleep
for the given number of milliseconds after rotation, regardless of the
rotation method used.  Testing with Logstash specifically has shown that
introducing a delay of 1000ms may be sufficient to avoid an issue
whereby the reading application loses the final line of input from the
old file due to not noticing that it has been truncated.

Thanks Miguel Angel TerrĂ³n.